Skip to content
Snippets Groups Projects
Commit f5ef38b2 authored by Tony CABAYE's avatar Tony CABAYE Committed by Florent Torregrosa
Browse files

Issue #3476384 by tocab, grimreaper, maboy: Replace Core offcanvas by...

Issue #3476384 by tocab, grimreaper, maboy: Replace Core offcanvas by Bootstrap's one + fix modal buttons not updated + fix offcanvas does not close after form submission
parent 5eb8e72f
No related branches found
No related tags found
1 merge request!229Issue by tocab, grimreaper, maboy: Replace Core offcanvas by Bootstrap's one +...
......@@ -5,6 +5,14 @@
* @see core/misc/dialog/off-canvas.form.css
*/
.offcanvas-end {
left: auto !important;
}
.offcanvas-bottom {
top: auto !important;
}
/* Remove form-text input styles applied on description. */
#drupal-off-canvas:not(.drupal-off-canvas-reset) .description.form-text,
#drupal-off-canvas-wrapper .description.form-text {
......
......@@ -30,8 +30,8 @@
const $dialog = $context.closest('#drupal-modal');
if ($dialog.length) {
const dialogSettings = $dialog.closest('.modal').data('settings');
if (dialogSettings && dialogSettings.drupalAutoButtons) {
const drupalAutoButtons = $dialog.data('drupal-auto-buttons');
if (drupalAutoButtons) {
$dialog.trigger('dialogButtonsChange');
}
}
......@@ -102,16 +102,57 @@
dialogUrlAjax.execute();
};
function openOffCanvasDialog(ajax, response, status) {
if (!response.selector) {
return false;
}
let $dialog = $(response.selector);
if (!$dialog.length) {
$dialog = $(
`<div id="${response.selector.replace(
/^#/,
'',
)}" class="offcanvas" tabindex="-1" role="dialog"></div>`,
)
.appendTo('body');
}
// Set up the wrapper, if there isn't one.
if (!ajax.wrapper) {
ajax.wrapper = $dialog.attr('id');
}
// Use the ajax.js insert command to populate the dialog contents.
response.command = 'insert';
response.method = 'html';
if (
response.dialogOptions.modalDialogWrapBody === undefined ||
response.dialogOptions.modalDialogWrapBody === true ||
response.dialogOptions.modalDialogWrapBody === 'true'
) {
response.data = `<div class="offcanvas-body">${response.data}</div>`;
}
ajax.commands.insert(ajax, response, status);
// Open the dialog itself.
response.dialogOptions = response.dialogOptions || {};
const dialog = Drupal.uiSuiteOffCanvas($dialog.get(0), response.dialogOptions);
if (response.dialogOptions.modal) {
dialog.showModal();
} else {
dialog.show();
}
// Add the standard Drupal class for buttons for style consistency.
$dialog.parent().find('.ui-dialog-buttonset').addClass('form-actions');
};
Drupal.AjaxCommands.prototype.coreOpenDialog =
Drupal.AjaxCommands.prototype.openDialog;
Drupal.AjaxCommands.prototype.openDialog = (ajax, response, status) => {
if (ajax.dialogRenderer === 'off_canvas') {
return Drupal.AjaxCommands.prototype.coreOpenDialog(
ajax,
response,
status,
);
return openOffCanvasDialog(ajax, response, status);
}
if (!response.selector) {
......@@ -167,6 +208,7 @@
response.dialogOptions.drupalAutoButtons =
!!response.dialogOptions.drupalAutoButtons;
}
if (
!response.dialogOptions.buttons &&
response.dialogOptions.drupalAutoButtons
......@@ -174,7 +216,7 @@
response.dialogOptions.buttons =
Drupal.behaviors.dialog.prepareDialogButtons($dialog);
}
$dialog.data('drupal-auto-buttons', response.dialogOptions.drupalAutoButtons)
// Bind dialogButtonsChange.
$dialog.on('dialogButtonsChange', () => {
const buttons = Drupal.behaviors.dialog.prepareDialogButtons($dialog);
......@@ -199,7 +241,11 @@
Drupal.AjaxCommands.prototype.closeDialog = (ajax, response, status) => {
const $dialog = $(response.selector);
if ($dialog.length) {
Drupal.uiSuiteDialog($dialog.get(0)).close();
if ($dialog.hasClass('offcanvas')) {
Drupal.uiSuiteOffCanvas($dialog.get(0)).close();
} else {
Drupal.uiSuiteDialog($dialog.get(0)).close();
}
}
$dialog.off('dialogButtonsChange');
......@@ -214,9 +260,9 @@
};
// eslint-disable-next-line
$(window).on("dialog:aftercreate", (e, dialog, $element, settings) => {
$(window).on('dialog:aftercreate', (e, dialog, $element, settings) => {
// eslint-disable-next-line
$element.on("click.dialog", ".dialog-cancel", (e) => {
$element.on('click.dialog', '.dialog-cancel', (e) => {
dialog.close('cancel');
e.preventDefault();
e.stopPropagation();
......
......@@ -91,6 +91,10 @@
const modalFooter = $(
'<div class="modal-footer"><div class="ui-dialog-buttonpane"></div><div class="ui-dialog-buttonset d-flex justify-content-end flex-grow-1"></div>',
);
const $footer = $('.modal-dialog .modal-content .modal-footer', $element);
if ($footer.length > 0) {
$($footer).find('.ui-dialog-buttonset').empty();
}
// eslint-disable-next-line func-names
$.each(buttons, function () {
......@@ -118,15 +122,13 @@
) {
$(button).addClass(classes.join(' '));
}
$(modalFooter).find('.ui-dialog-buttonset').append(button);
if ($footer.length > 0) {
$($footer).find('.ui-dialog-buttonset').append(button);
} else {
$(modalFooter).find('.ui-dialog-buttonset').append(button);
}
});
if (
$('.modal-dialog .modal-content .modal-footer', $element).length > 0
) {
$('.modal-dialog .modal-content .modal-footer', $element).remove();
}
if ($(modalFooter).html().length > 0) {
if ($(modalFooter).html().length > 0 && $footer.length === 0) {
$(modalFooter).appendTo($('.modal-dialog .modal-content', $element));
}
}
......@@ -198,11 +200,13 @@
}
function closeDialog(value) {
domElement.dispatchEvent(new DrupalDialogEvent('beforeclose', dialog));
if ($element.modal !== undefined) {
$element.modal('hide');
}
dialog.returnValue = value;
dialog.open = false;
domElement.dispatchEvent(new DrupalDialogEvent('afterclose', dialog));
}
dialog.updateButtons = (buttons) => {
......
/**
* @file
* Dialog API inspired by HTML5 dialog element.
*
* @see http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#the-dialog-element
*/
(($, Drupal, drupalSettings) => {
/**
* Default dialog options.
*
* @type {object}
*
* @prop {bool} [autoOpen=true]
* @prop {bool} [autoResize=undefined]
* @prop {bool} [backdrop=undefined]
* @prop {object} [classes=undefined] TODO
* @prop {function} close
* @prop {string} [dialogClasses='']
* @prop {string} [dialogHeadingLevel=5]
* @prop {string} [dialogShowHeader=true]
* @prop {string} [dialogShowHeaderTitle=true]
* @prop {string} [dialogStatic=false]
* @prop {bool} [drupalAutoButtons=undefined]
* @prop {bool} [drupalOffCanvasPosition='side']
* @prop {bool} [resizable=undefined]
* @prop {string} [title=undefined]
* @prop {string} [width=undefined]
*/
drupalSettings.offCanvas = {
autoOpen: true,
autoResize: undefined,
backdrop: undefined,
classes: undefined,
close: function close(event) {
Drupal.uiSuiteDialog(event.target).close();
Drupal.detachBehaviors(event.target, null, "unload");
},
dialogHeadingLevel: 5,
dialogShowHeader: true,
dialogShowHeaderTitle: true,
dialogStatic: false,
drupalAutoButtons: undefined,
drupalOffCanvasPosition: "side",
resizable: undefined,
title: undefined,
width: undefined,
};
/**
* @typedef {object} Drupal.dialog~dialogDefinition
*
* @prop {boolean} open
* Is the dialog open or not.
* @prop {*} returnValue
* Return value of the dialog.
* @prop {function} show
* Method to display the dialog on the page.
* @prop {function} showModal
* Method to display the dialog as a modal on the page.
* @prop {function} close
* Method to hide the dialog from the page.
*/
/**
* Polyfill HTML5 dialog element with jQueryUI.
*
* @param {HTMLElement} element
* The element that holds the dialog.
* @param {object} options
* jQuery UI options to be passed to the dialog.
*
* @return {Drupal.dialog~dialogDefinition}
* The dialog instance.
*/
Drupal.uiSuiteOffCanvas = (element, options) => {
let undef;
const $element = $(element);
const domElement = $element.get(0);
const dialog = {
open: false,
returnValue: undef,
};
options = $.extend({}, drupalSettings.offCanvas, options);
function settingIsTrue(setting) {
return setting !== undefined && (setting === true || setting === "true");
}
function openDialog(settings) {
settings = $.extend({}, options, settings);
const event = new DrupalDialogEvent("beforecreate", dialog, settings);
domElement.dispatchEvent(event);
dialog.open = true;
settings = event.settings;
// Position
if (settings.drupalOffCanvasPosition === "side") {
$element.addClass("offcanvas-end");
} else if (settings.drupalOffCanvasPosition === "top") {
$element.addClass("offcanvas-top");
} else if (settings.drupalOffCanvasPosition === "bottom") {
$element.addClass("offcanvas-bottom");
}
// Classes
if (settings.classes) {
if (settings.classes['ui-dialog']) {
$element.addClass(settings.classes['ui-dialog']);
}
if (settings.classes['ui-dialog-content']) {
$('.offcanvas-body', $element).addClass(settings.classes['ui-dialog-content']);
}
}
// The modal dialog header.
if (settingIsTrue(settings.dialogShowHeader)) {
let modalHeader = '<div class="offcanvas-header">';
const heading = settings.dialogHeadingLevel;
if (settingIsTrue(settings.dialogShowHeaderTitle)) {
modalHeader += `<h${heading} class="offcanvas-title" id="offcanvasLabel">${settings.title}</h${heading}>`;
}
modalHeader += `<button type="button" class="close btn-close" data-bs-dismiss="offcanvas" aria-label="${Drupal.t(
"Close",
)}"></button></div>`;
$(modalHeader).prependTo($element);
}
if (settingIsTrue(settings.dialogStatic)) {
$element.attr("data-bs-backdrop", "static");
}
if (!settingIsTrue(settings.backdrop)) {
$element.attr("data-bs-scroll", "true");
}
if ($element.offcanvas !== undefined) {
$element.offcanvas(settings);
$element.offcanvas("show");
}
if (settings.width) {
$element.css(
"--bs-offcanvas-width",
typeof settings.width === "number"
? `${settings.width}px`
: settings.width,
);
}
if ($element.resizable !== undefined && settings.resizable) {
$element.resizable({
handles: "w",
});
}
domElement.dispatchEvent(
new DrupalDialogEvent("aftercreate", dialog, settings),
);
}
function closeDialog(value) {
if ($element.modal !== undefined) {
$element.offcanvas("hide");
}
dialog.returnValue = value;
dialog.open = false;
}
dialog.show = () => {
openDialog({ backdrop: false });
};
dialog.showModal = () => {
openDialog({ backdrop: true });
};
dialog.close = () => {
closeDialog({});
};
$element.on("hide.bs.offcanvas", () => {
domElement.dispatchEvent(new DrupalDialogEvent("beforeclose", dialog));
});
$element.on("hidden.bs.offcanvas", () => {
domElement.dispatchEvent(new DrupalDialogEvent("afterclose", dialog));
});
return dialog;
};
Drupal.behaviors.offCanvasEvents = {};
})(jQuery, Drupal, drupalSettings);
......@@ -52,6 +52,12 @@ drupal.dialog.ajax:
- core/drupal.ajax
drupal.dialog.off_canvas:
js:
assets/js/misc/dialog/dialog.off-canvas.js: {}
dependencies:
- core/jquery
- core/drupal
- core/drupalSettings
css:
component:
assets/css/form/off-canvas.button.css: {}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment