Initial Drupal 11 with DDEV setup
This commit is contained in:
303
web/core/misc/dialog/dialog.ajax.js
Normal file
303
web/core/misc/dialog/dialog.ajax.js
Normal file
@ -0,0 +1,303 @@
|
||||
/**
|
||||
* @file
|
||||
* Extends the Drupal AJAX functionality to integrate the dialog API.
|
||||
*/
|
||||
|
||||
(function ($, Drupal, { focusable }) {
|
||||
/**
|
||||
* Initialize dialogs for Ajax purposes.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*
|
||||
* @prop {Drupal~behaviorAttach} attach
|
||||
* Attaches the behaviors for dialog ajax functionality.
|
||||
*/
|
||||
Drupal.behaviors.dialog = {
|
||||
attach(context, settings) {
|
||||
// Provide a known 'drupal-modal' DOM element for Drupal-based modal
|
||||
// dialogs. Non-modal dialogs are responsible for creating their own
|
||||
// elements, since there can be multiple non-modal dialogs at a time.
|
||||
if (!document.querySelector('#drupal-modal')) {
|
||||
// Add 'ui-front' jQuery UI class so jQuery UI widgets like autocomplete
|
||||
// sit on top of dialogs. For more information see
|
||||
// http://api.jqueryui.com/theming/stacking-elements/.
|
||||
document.body.insertAdjacentHTML(
|
||||
'beforeend',
|
||||
'<div id="drupal-modal" class="ui-front" style="display:none"></div>',
|
||||
);
|
||||
}
|
||||
|
||||
// Special behaviors specific when attaching content within a dialog.
|
||||
// These behaviors usually fire after a validation error inside a dialog.
|
||||
if (context !== document) {
|
||||
const dialog = context.closest('.ui-dialog-content');
|
||||
if (dialog) {
|
||||
// Remove and replace the dialog buttons with those from the new form.
|
||||
if ($(dialog).dialog('option', 'drupalAutoButtons')) {
|
||||
// Trigger an event to detect/sync changes to buttons.
|
||||
dialog.dispatchEvent(new CustomEvent('dialogButtonsChange'));
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
// Account for pre-existing focus handling that may have already moved
|
||||
// the focus inside the dialog.
|
||||
if (!dialog.contains(document.activeElement)) {
|
||||
// Move focus to the first focusable element in the next event loop
|
||||
// to allow dialog buttons to be changed first.
|
||||
$(dialog).dialog('instance')._focusedElement = null;
|
||||
$(dialog).dialog('instance')._focusTabbable();
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
const originalClose = settings.dialog.close;
|
||||
// Overwrite the close method to remove the dialog on closing.
|
||||
settings.dialog.close = function (event, ...args) {
|
||||
originalClose.apply(settings.dialog, [event, ...args]);
|
||||
// Check if the opener element is inside an AJAX container.
|
||||
const $element = $(event.target);
|
||||
const ajaxContainer = $element.data('uiDialog')
|
||||
? $element
|
||||
.data('uiDialog')
|
||||
.opener.closest('[data-drupal-ajax-container]')
|
||||
: [];
|
||||
|
||||
// If the opener element was in an ajax container, and focus is on the
|
||||
// body element, we can assume focus was lost. To recover, focus is
|
||||
// moved to the first focusable element in the container.
|
||||
if (
|
||||
ajaxContainer.length &&
|
||||
(document.activeElement === document.body ||
|
||||
$(document.activeElement).not(':visible'))
|
||||
) {
|
||||
const focusableChildren = focusable(ajaxContainer[0]);
|
||||
if (focusableChildren.length > 0) {
|
||||
setTimeout(() => {
|
||||
focusableChildren[0].focus();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
$(event.target).remove();
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Scan a dialog for any primary buttons and move them to the button area.
|
||||
*
|
||||
* @param {jQuery} $dialog
|
||||
* A jQuery object containing the element that is the dialog target.
|
||||
*
|
||||
* @return {Array}
|
||||
* An array of buttons that need to be added to the button area.
|
||||
*/
|
||||
prepareDialogButtons($dialog) {
|
||||
const buttons = [];
|
||||
const buttonSelectors =
|
||||
'.form-actions input[type=submit], .form-actions a.button, .form-actions a.action-link';
|
||||
const buttonElements = $dialog[0].querySelectorAll(buttonSelectors);
|
||||
|
||||
buttonElements.forEach((button) => {
|
||||
button.style.display = 'none';
|
||||
buttons.push({
|
||||
text: button.innerHTML || button.getAttribute('value'),
|
||||
class: button.getAttribute('class'),
|
||||
'data-once': button.dataset.once,
|
||||
click(e) {
|
||||
if (button.tagName === 'A') {
|
||||
button.click();
|
||||
} else {
|
||||
['mousedown', 'mouseup', 'click'].forEach((event) =>
|
||||
button.dispatchEvent(new MouseEvent(event)),
|
||||
);
|
||||
}
|
||||
e.preventDefault();
|
||||
},
|
||||
});
|
||||
});
|
||||
return buttons;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Command to open a dialog.
|
||||
*
|
||||
* @param {Drupal.Ajax} ajax
|
||||
* The Drupal Ajax object.
|
||||
* @param {object} response
|
||||
* Object holding the server response.
|
||||
* @param {number} [status]
|
||||
* The HTTP status code.
|
||||
*
|
||||
* @return {boolean|undefined}
|
||||
* Returns false if there was no selector property in the response object.
|
||||
*/
|
||||
Drupal.AjaxCommands.prototype.openDialog = function (ajax, response, status) {
|
||||
if (!response.selector) {
|
||||
return false;
|
||||
}
|
||||
let dialog = document.querySelector(response.selector);
|
||||
if (!dialog) {
|
||||
// Create the element if needed.
|
||||
dialog = document.createElement('div');
|
||||
dialog.id = response.selector.replace(/^#/, '');
|
||||
dialog.classList.add('ui-front');
|
||||
document.body.appendChild(dialog);
|
||||
}
|
||||
// Set up the wrapper, if there isn't one.
|
||||
if (!ajax.wrapper) {
|
||||
ajax.wrapper = dialog.id;
|
||||
}
|
||||
|
||||
// Use the ajax.js insert command to populate the dialog contents.
|
||||
response.command = 'insert';
|
||||
response.method = 'html';
|
||||
ajax.commands.insert(ajax, response, status);
|
||||
|
||||
// Move the buttons to the jQuery UI dialog buttons area.
|
||||
response.dialogOptions = response.dialogOptions || {};
|
||||
if (typeof response.dialogOptions.drupalAutoButtons === 'undefined') {
|
||||
response.dialogOptions.drupalAutoButtons = true;
|
||||
} else if (response.dialogOptions.drupalAutoButtons === 'false') {
|
||||
response.dialogOptions.drupalAutoButtons = false;
|
||||
} else {
|
||||
response.dialogOptions.drupalAutoButtons =
|
||||
!!response.dialogOptions.drupalAutoButtons;
|
||||
}
|
||||
if (
|
||||
!response.dialogOptions.buttons &&
|
||||
response.dialogOptions.drupalAutoButtons
|
||||
) {
|
||||
response.dialogOptions.buttons =
|
||||
Drupal.behaviors.dialog.prepareDialogButtons($(dialog));
|
||||
}
|
||||
|
||||
const dialogButtonsChange = () => {
|
||||
const buttons = Drupal.behaviors.dialog.prepareDialogButtons($(dialog));
|
||||
$(dialog).dialog('option', 'buttons', buttons);
|
||||
};
|
||||
|
||||
// Bind dialogButtonsChange.
|
||||
dialog.addEventListener('dialogButtonsChange', dialogButtonsChange);
|
||||
dialog.addEventListener('dialog:beforeclose', (event) => {
|
||||
dialog.removeEventListener('dialogButtonsChange', dialogButtonsChange);
|
||||
});
|
||||
|
||||
// Open the dialog itself.
|
||||
const createdDialog = Drupal.dialog(dialog, response.dialogOptions);
|
||||
if (response.dialogOptions.modal) {
|
||||
createdDialog.showModal();
|
||||
} else {
|
||||
createdDialog.show();
|
||||
}
|
||||
|
||||
// Add the standard Drupal class for buttons for style consistency.
|
||||
dialog.parentElement
|
||||
?.querySelector('.ui-dialog-buttonset')
|
||||
?.classList.add('form-actions');
|
||||
};
|
||||
|
||||
/**
|
||||
* Command to close a dialog.
|
||||
*
|
||||
* If no selector is given, it defaults to trying to close the modal.
|
||||
*
|
||||
* @param {Drupal.Ajax} [ajax]
|
||||
* The ajax object.
|
||||
* @param {object} response
|
||||
* Object holding the server response.
|
||||
* @param {string} response.selector
|
||||
* The selector of the dialog.
|
||||
* @param {boolean} response.persist
|
||||
* Whether to persist the dialog element or not.
|
||||
* @param {number} [status]
|
||||
* The HTTP status code.
|
||||
*/
|
||||
Drupal.AjaxCommands.prototype.closeDialog = function (
|
||||
ajax,
|
||||
response,
|
||||
status,
|
||||
) {
|
||||
const dialog = document.querySelector(response.selector);
|
||||
if (dialog) {
|
||||
Drupal.dialog(dialog).close();
|
||||
if (!response.persist) {
|
||||
dialog.remove();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Command to set a dialog property.
|
||||
*
|
||||
* JQuery UI specific way of setting dialog options.
|
||||
*
|
||||
* @param {Drupal.Ajax} [ajax]
|
||||
* The Drupal Ajax object.
|
||||
* @param {object} response
|
||||
* Object holding the server response.
|
||||
* @param {string} response.selector
|
||||
* Selector for the dialog element.
|
||||
* @param {string} response.optionsName
|
||||
* Name of a key to set.
|
||||
* @param {string} response.optionValue
|
||||
* Value to set.
|
||||
* @param {number} [status]
|
||||
* The HTTP status code.
|
||||
*/
|
||||
Drupal.AjaxCommands.prototype.setDialogOption = function (
|
||||
ajax,
|
||||
response,
|
||||
status,
|
||||
) {
|
||||
const dialog = document.querySelector(response.selector);
|
||||
if (dialog) {
|
||||
$(dialog).dialog('option', response.optionName, response.optionValue);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Binds a listener on dialog creation to handle the cancel link.
|
||||
*
|
||||
* @param {DrupalDialogEvent} e
|
||||
* The event triggered.
|
||||
* @param {Drupal.dialog~dialogDefinition} dialog
|
||||
* The dialog instance.
|
||||
* @param {object} [settings]
|
||||
* Dialog settings.
|
||||
*/
|
||||
window.addEventListener('dialog:aftercreate', (event) => {
|
||||
const dialog = event.dialog;
|
||||
const cancelButton = event.target.querySelector('.dialog-cancel');
|
||||
const cancelClick = (e) => {
|
||||
dialog.close('cancel');
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
};
|
||||
cancelButton?.removeEventListener('click', cancelClick);
|
||||
cancelButton?.addEventListener('click', cancelClick);
|
||||
});
|
||||
|
||||
/**
|
||||
* Ajax command to open URL in a modal dialog.
|
||||
*
|
||||
* @param {Drupal.Ajax} [ajax]
|
||||
* An Ajax object.
|
||||
* @param {object} response
|
||||
* The Ajax response.
|
||||
*/
|
||||
Drupal.AjaxCommands.prototype.openModalDialogWithUrl = function (
|
||||
ajax,
|
||||
response,
|
||||
) {
|
||||
const dialogOptions = response.dialogOptions || {};
|
||||
const elementSettings = {
|
||||
progress: { type: 'throbber' },
|
||||
dialogType: 'modal',
|
||||
dialog: dialogOptions,
|
||||
url: response.url,
|
||||
httpMethod: 'GET',
|
||||
};
|
||||
Drupal.ajax(elementSettings).execute();
|
||||
};
|
||||
})(jQuery, Drupal, window.tabbable);
|
||||
Reference in New Issue
Block a user