summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorScott Reeves2017-08-14 16:05:48 -0400
committerScott Reeves2017-08-14 16:05:54 -0400
commit3704c99104f3aff47d314066f90254631ccff322 (patch)
tree1ba06428c2a2fffd1ed06ff1e5459cd0ab0ddadc
parent621aa0b5b84b959b5442fe51c726cf620204dd6e (diff)
Issue #2830882 by tedbow, droplet, drpal, Adita, tim.plunkett, markcarver, Wim Leers, nod_: Introduce Drupal.offCanvas, mirrored after Drupal.dialog, to avoid Settings Tray using Drupal.Dialog in unintended ways
(cherry picked from commit aa76f9674fb5e0e179a5af9f32149d49672fed4b)
-rw-r--r--core/modules/outside_in/js/off-canvas.es6.js333
-rw-r--r--core/modules/outside_in/js/off-canvas.js179
2 files changed, 315 insertions, 197 deletions
diff --git a/core/modules/outside_in/js/off-canvas.es6.js b/core/modules/outside_in/js/off-canvas.es6.js
index 480561a..c711c2b 100644
--- a/core/modules/outside_in/js/off-canvas.es6.js
+++ b/core/modules/outside_in/js/off-canvas.es6.js
@@ -9,101 +9,220 @@
* @private
*/
-(function ($, Drupal, debounce, displace) {
- // The minimum width to use body displace needs to match the width at which
- // the tray will be %100 width. @see outside_in.module.css
- const minDisplaceWidth = 768;
-
+(($, Drupal, debounce, displace) => {
/**
- * The edge of the screen that the dialog should appear on.
+ * Off-canvas dialog implementation using jQuery Dialog.
+ *
+ * Transforms the regular dialogs created using Drupal.dialog when the dialog
+ * element equals '#drupal-off-canvas' into an side-loading dialog.
*
- * @type {string}
+ * @namespace
*/
- const edge = document.documentElement.dir === 'rtl' ? 'left' : 'right';
+ Drupal.offCanvas = {
- const $mainCanvasWrapper = $('[data-off-canvas-main-canvas]');
+ /**
+ * The minimum width to use body displace needs to match the width at which
+ * the tray will be %100 width. @see outside_in.module.css
+ * @type {Number}
+ */
+ minDisplaceWidth: 768,
- /**
- * Resets the size of the dialog.
- *
- * @param {jQuery.Event} event
- * The event triggered.
- */
- function resetSize(event) {
- const offsets = displace.offsets;
- const $element = event.data.$element;
- const $widget = $element.dialog('widget');
- const $elementScroll = $element.scrollTop();
+ /**
+ * Wrapper used to position off-canvas dialog.
+ * @type {jQuery}
+ */
+ $mainCanvasWrapper: $('[data-off-canvas-main-canvas]'),
- const adjustedOptions = {
+ /**
+ * Determines if an element is an off-canvas dialog.
+ *
+ * @param {jQuery} $element
+ * The dialog element.
+ * @return {bool}
+ * True this is currently an off-canvas dialog.
+ */
+ isOffCanvas($element) {
+ return $element.is('#drupal-off-canvas');
+ },
+
+ /**
+ * Handler fired before an off-canvas dialog has been opened.
+ * @param {Object} settings
+ * Settings related to the composition of the dialog.
+ * @return {undefined}
+ */
+ beforeCreate({ settings }) {
+ $('body').addClass('js-tray-open');
+ settings.dialogClass += ' ui-dialog-off-canvas';
// @see http://api.jqueryui.com/position/
- position: {
- my: `${edge} top`,
- at: `${edge} top${offsets.top !== 0 ? `+${offsets.top}` : ''}`,
+ settings.position = {
+ my: 'left top',
+ at: `${Drupal.offCanvas.getEdge()} top`,
of: window,
- },
- };
+ };
- $widget.css({
- position: 'fixed',
- height: `${$(window).height() - (offsets.top + offsets.bottom)}px`,
- });
+ /**
+ * Applies initial height to dialog based on window height.
+ * @see http://api.jqueryui.com/dialog for all dialog options.
+ */
+ settings.height = $(window).height();
+ },
- $element
- .dialog('option', adjustedOptions)
- .trigger('dialogContentResize.off-canvas');
+ /**
+ * Handler fired after an off-canvas dialog has been closed.
+ * @return {undefined}
+ */
+ beforeClose() {
+ $('body').removeClass('js-tray-open');
+ // Remove all *.off-canvas events
+ $(document).off('.off-canvas');
+ $(window).off('.off-canvas');
+ Drupal.offCanvas.$mainCanvasWrapper.css(`padding-${Drupal.offCanvas.getEdge()}`, 0);
+ },
- // Reset the elements scroll position after being repositioned.
- $element.scrollTop($elementScroll);
- }
+ /**
+ * Handler fired when an off-canvas dialog has been opened.
+ * @param {jQuery} $element
+ * The off-canvas dialog element.
+ * @param {Object} settings
+ * Settings related to the composition of the dialog.
+ * @return {undefined}
+ */
+ afterCreate({ $element, settings }) {
+ const eventData = { settings, $element, offCanvasDialog: this };
- /**
- * Adjusts the dialog on resize.
- *
- * @param {jQuery.Event} event
- * The event triggered.
- */
- function handleDialogResize(event) {
- const $element = event.data.$element;
- const $widget = $element.dialog('widget');
-
- const $offsets = $widget.find('> :not(#drupal-off-canvas, .ui-resizable-handle)');
- let offset = 0;
- let modalHeight;
-
- // Let scroll element take all the height available.
- $element.css({ height: 'auto' });
- modalHeight = $widget.height();
- $offsets.each(function () {
- offset += $(this).outerHeight();
- });
-
- // Take internal padding into account.
- const scrollOffset = $element.outerHeight() - $element.height();
- $element.height(modalHeight - offset - scrollOffset);
- }
+ $element
+ .on('dialogresize.off-canvas', eventData, debounce(Drupal.offCanvas.bodyPadding, 100))
+ .on('dialogContentResize.off-canvas', eventData, Drupal.offCanvas.handleDialogResize)
+ .on('dialogContentResize.off-canvas', eventData, debounce(Drupal.offCanvas.bodyPadding, 100))
+ .trigger('dialogresize.off-canvas');
- /**
- * Adjusts the body padding when the dialog is resized.
- *
- * @param {jQuery.Event} event
- * The event triggered.
- */
- function bodyPadding(event) {
- if ($('body').outerWidth() < minDisplaceWidth) {
- return;
- }
- const $element = event.data.$element;
- const $widget = $element.dialog('widget');
-
- const width = $widget.outerWidth();
- const mainCanvasPadding = $mainCanvasWrapper.css(`padding-${edge}`);
- if (width !== mainCanvasPadding) {
- $mainCanvasWrapper.css(`padding-${edge}`, `${width}px`);
- $widget.attr(`data-offset-${edge}`, width);
- displace();
- }
- }
+ Drupal.offCanvas.getContainer($element).attr(`data-offset-${Drupal.offCanvas.getEdge()}`, '');
+
+ $(window)
+ .on('resize.off-canvas scroll.off-canvas', eventData, debounce(Drupal.offCanvas.resetSize, 100))
+ .trigger('resize.off-canvas');
+ },
+
+ /**
+ * Toggle classes based on title existence.
+ * Called with Drupal.offCanvas.afterCreate.
+ * @param {Object} settings
+ * Settings related to the composition of the dialog.
+ * @return {undefined}
+ */
+ render({ settings }) {
+ $('.ui-dialog-off-canvas, .ui-dialog-off-canvas .ui-dialog-titlebar').toggleClass('ui-dialog-empty-title', !settings.title);
+ },
+
+ /**
+ * Adjusts the dialog on resize.
+ *
+ * @param {jQuery.Event} event
+ * The event triggered.
+ * @param {object} event.data
+ * Data attached to the event.
+ */
+ handleDialogResize(event) {
+ const $element = event.data.$element;
+ const $container = Drupal.offCanvas.getContainer($element);
+
+ const $offsets = $container.find('> :not(#drupal-off-canvas, .ui-resizable-handle)');
+ let offset = 0;
+
+ // Let scroll element take all the height available.
+ $element.css({ height: 'auto' });
+ const modalHeight = $container.height();
+
+ $offsets.each((i, e) => {
+ offset += $(e).outerHeight();
+ });
+
+ // Take internal padding into account.
+ const scrollOffset = $element.outerHeight() - $element.height();
+ $element.height(modalHeight - offset - scrollOffset);
+ },
+
+ /**
+ * Resets the size of the dialog.
+ *
+ * @param {jQuery.Event} event
+ * The event triggered.
+ * @param {object} event.data
+ * Data attached to the event.
+ */
+ resetSize(event) {
+ const offsets = displace.offsets;
+ const $element = event.data.$element;
+ const container = Drupal.offCanvas.getContainer($element);
+
+ const topPosition = (offsets.top !== 0 ? `+${offsets.top}` : '');
+ const adjustedOptions = {
+ // @see http://api.jqueryui.com/position/
+ position: {
+ my: `${Drupal.offCanvas.getEdge()} top`,
+ at: `${Drupal.offCanvas.getEdge()} top${topPosition}`,
+ of: window,
+ },
+ };
+
+ container.css({
+ position: 'fixed',
+ height: `${$(window).height() - (offsets.top + offsets.bottom)}px`,
+ });
+
+ $element
+ .dialog('option', adjustedOptions)
+ .trigger('dialogContentResize.off-canvas');
+ },
+
+ /**
+ * Adjusts the body padding when the dialog is resized.
+ *
+ * @param {jQuery.Event} event
+ * The event triggered.
+ * @param {object} event.data
+ * Data attached to the event.
+ */
+ bodyPadding(event) {
+ if ($('body').outerWidth() < Drupal.offCanvas.minDisplaceWidth) {
+ return;
+ }
+ const $element = event.data.$element;
+ const $container = Drupal.offCanvas.getContainer($element);
+ const $mainCanvasWrapper = Drupal.offCanvas.$mainCanvasWrapper;
+
+ const width = $container.outerWidth();
+ const mainCanvasPadding = $mainCanvasWrapper.css(`padding-${Drupal.offCanvas.getEdge()}`);
+ if (width !== mainCanvasPadding) {
+ $mainCanvasWrapper.css(`padding-${Drupal.offCanvas.getEdge()}`, `${width}px`);
+ $container.attr(`data-offset-${Drupal.offCanvas.getEdge()}`, width);
+ displace();
+ }
+ },
+
+ /**
+ * The HTML element that surrounds the dialog.
+ * @param {HTMLElement} $element
+ * The dialog element.
+ *
+ * @return {HTMLElement}
+ * The containing element.
+ */
+ getContainer($element) {
+ return $element.dialog('widget');
+ },
+
+ /**
+ * The edge of the screen that the dialog should appear on.
+ *
+ * @return {string}
+ * The edge the tray will be shown on, left or right.
+ */
+ getEdge() {
+ return document.documentElement.dir === 'rtl' ? 'left' : 'right';
+ },
+ };
/**
* Attaches off-canvas dialog behaviors.
@@ -114,51 +233,25 @@
* Attaches event listeners for off-canvas dialogs.
*/
Drupal.behaviors.offCanvasEvents = {
- attach() {
+ attach: () => {
$(window).once('off-canvas').on({
- 'dialog:aftercreate': function (event, dialog, $element, settings) {
- if ($element.is('#drupal-off-canvas')) {
- const eventData = { settings, $element };
- $('.ui-dialog-off-canvas, .ui-dialog-off-canvas .ui-dialog-titlebar').toggleClass('ui-dialog-empty-title', !settings.title);
-
- $element
- .on('dialogresize.off-canvas', eventData, debounce(bodyPadding, 100))
- .on('dialogContentResize.off-canvas', eventData, handleDialogResize)
- .on('dialogContentResize.off-canvas', eventData, debounce(bodyPadding, 100))
- .trigger('dialogresize.off-canvas');
-
- $element.dialog('widget').attr(`data-offset-${edge}`, '');
-
- $(window)
- .on('resize.off-canvas scroll.off-canvas', eventData, debounce(resetSize, 100))
- .trigger('resize.off-canvas');
+ 'dialog:beforecreate': (event, dialog, $element, settings) => {
+ if (Drupal.offCanvas.isOffCanvas($element)) {
+ Drupal.offCanvas.beforeCreate({ dialog, $element, settings });
}
},
- 'dialog:beforecreate': function (event, dialog, $element, settings) {
- if ($element.is('#drupal-off-canvas')) {
- $('body').addClass('js-tray-open');
- // @see http://api.jqueryui.com/position/
- settings.position = {
- my: 'left top',
- at: `${edge} top`,
- of: window,
- };
- settings.dialogClass += ' ui-dialog-off-canvas';
- // Applies initial height to dialog based on window height.
- // See http://api.jqueryui.com/dialog for all dialog options.
- settings.height = $(window).height();
+ 'dialog:aftercreate': (event, dialog, $element, settings) => {
+ if (Drupal.offCanvas.isOffCanvas($element)) {
+ Drupal.offCanvas.render({ dialog, $element, settings });
+ Drupal.offCanvas.afterCreate({ $element, settings });
}
},
- 'dialog:beforeclose': function (event, dialog, $element) {
- if ($element.is('#drupal-off-canvas')) {
- $('body').removeClass('js-tray-open');
- // Remove all *.off-canvas events
- $(document).off('.off-canvas');
- $(window).off('.off-canvas');
- $mainCanvasWrapper.css(`padding-${edge}`, 0);
+ 'dialog:beforeclose': (event, dialog, $element) => {
+ if (Drupal.offCanvas.isOffCanvas($element)) {
+ Drupal.offCanvas.beforeClose({ dialog, $element });
}
},
});
},
};
-}(jQuery, Drupal, Drupal.debounce, Drupal.displace));
+})(jQuery, Drupal, Drupal.debounce, Drupal.displace);
diff --git a/core/modules/outside_in/js/off-canvas.js b/core/modules/outside_in/js/off-canvas.js
index 85a9e53..f5f65a4 100644
--- a/core/modules/outside_in/js/off-canvas.js
+++ b/core/modules/outside_in/js/off-canvas.js
@@ -6,106 +6,131 @@
**/
(function ($, Drupal, debounce, displace) {
- var minDisplaceWidth = 768;
+ Drupal.offCanvas = {
+ minDisplaceWidth: 768,
- var edge = document.documentElement.dir === 'rtl' ? 'left' : 'right';
+ $mainCanvasWrapper: $('[data-off-canvas-main-canvas]'),
- var $mainCanvasWrapper = $('[data-off-canvas-main-canvas]');
+ isOffCanvas: function isOffCanvas($element) {
+ return $element.is('#drupal-off-canvas');
+ },
+ beforeCreate: function beforeCreate(_ref) {
+ var settings = _ref.settings;
- function resetSize(event) {
- var offsets = displace.offsets;
- var $element = event.data.$element;
- var $widget = $element.dialog('widget');
- var $elementScroll = $element.scrollTop();
+ $('body').addClass('js-tray-open');
+ settings.dialogClass += ' ui-dialog-off-canvas';
- var adjustedOptions = {
- position: {
- my: edge + ' top',
- at: edge + ' top' + (offsets.top !== 0 ? '+' + offsets.top : ''),
+ settings.position = {
+ my: 'left top',
+ at: Drupal.offCanvas.getEdge() + ' top',
of: window
- }
- };
+ };
- $widget.css({
- position: 'fixed',
- height: $(window).height() - (offsets.top + offsets.bottom) + 'px'
- });
+ settings.height = $(window).height();
+ },
+ beforeClose: function beforeClose() {
+ $('body').removeClass('js-tray-open');
- $element.dialog('option', adjustedOptions).trigger('dialogContentResize.off-canvas');
+ $(document).off('.off-canvas');
+ $(window).off('.off-canvas');
+ Drupal.offCanvas.$mainCanvasWrapper.css('padding-' + Drupal.offCanvas.getEdge(), 0);
+ },
+ afterCreate: function afterCreate(_ref2) {
+ var $element = _ref2.$element,
+ settings = _ref2.settings;
- $element.scrollTop($elementScroll);
- }
+ var eventData = { settings: settings, $element: $element, offCanvasDialog: this };
- function handleDialogResize(event) {
- var $element = event.data.$element;
- var $widget = $element.dialog('widget');
+ $element.on('dialogresize.off-canvas', eventData, debounce(Drupal.offCanvas.bodyPadding, 100)).on('dialogContentResize.off-canvas', eventData, Drupal.offCanvas.handleDialogResize).on('dialogContentResize.off-canvas', eventData, debounce(Drupal.offCanvas.bodyPadding, 100)).trigger('dialogresize.off-canvas');
- var $offsets = $widget.find('> :not(#drupal-off-canvas, .ui-resizable-handle)');
- var offset = 0;
- var modalHeight = void 0;
+ Drupal.offCanvas.getContainer($element).attr('data-offset-' + Drupal.offCanvas.getEdge(), '');
- $element.css({ height: 'auto' });
- modalHeight = $widget.height();
- $offsets.each(function () {
- offset += $(this).outerHeight();
- });
+ $(window).on('resize.off-canvas scroll.off-canvas', eventData, debounce(Drupal.offCanvas.resetSize, 100)).trigger('resize.off-canvas');
+ },
+ render: function render(_ref3) {
+ var settings = _ref3.settings;
- var scrollOffset = $element.outerHeight() - $element.height();
- $element.height(modalHeight - offset - scrollOffset);
- }
+ $('.ui-dialog-off-canvas, .ui-dialog-off-canvas .ui-dialog-titlebar').toggleClass('ui-dialog-empty-title', !settings.title);
+ },
+ handleDialogResize: function handleDialogResize(event) {
+ var $element = event.data.$element;
+ var $container = Drupal.offCanvas.getContainer($element);
- function bodyPadding(event) {
- if ($('body').outerWidth() < minDisplaceWidth) {
- return;
- }
- var $element = event.data.$element;
- var $widget = $element.dialog('widget');
-
- var width = $widget.outerWidth();
- var mainCanvasPadding = $mainCanvasWrapper.css('padding-' + edge);
- if (width !== mainCanvasPadding) {
- $mainCanvasWrapper.css('padding-' + edge, width + 'px');
- $widget.attr('data-offset-' + edge, width);
- displace();
+ var $offsets = $container.find('> :not(#drupal-off-canvas, .ui-resizable-handle)');
+ var offset = 0;
+
+ $element.css({ height: 'auto' });
+ var modalHeight = $container.height();
+
+ $offsets.each(function (i, e) {
+ offset += $(e).outerHeight();
+ });
+
+ var scrollOffset = $element.outerHeight() - $element.height();
+ $element.height(modalHeight - offset - scrollOffset);
+ },
+ resetSize: function resetSize(event) {
+ var offsets = displace.offsets;
+ var $element = event.data.$element;
+ var container = Drupal.offCanvas.getContainer($element);
+
+ var topPosition = offsets.top !== 0 ? '+' + offsets.top : '';
+ var adjustedOptions = {
+ position: {
+ my: Drupal.offCanvas.getEdge() + ' top',
+ at: Drupal.offCanvas.getEdge() + ' top' + topPosition,
+ of: window
+ }
+ };
+
+ container.css({
+ position: 'fixed',
+ height: $(window).height() - (offsets.top + offsets.bottom) + 'px'
+ });
+
+ $element.dialog('option', adjustedOptions).trigger('dialogContentResize.off-canvas');
+ },
+ bodyPadding: function bodyPadding(event) {
+ if ($('body').outerWidth() < Drupal.offCanvas.minDisplaceWidth) {
+ return;
+ }
+ var $element = event.data.$element;
+ var $container = Drupal.offCanvas.getContainer($element);
+ var $mainCanvasWrapper = Drupal.offCanvas.$mainCanvasWrapper;
+
+ var width = $container.outerWidth();
+ var mainCanvasPadding = $mainCanvasWrapper.css('padding-' + Drupal.offCanvas.getEdge());
+ if (width !== mainCanvasPadding) {
+ $mainCanvasWrapper.css('padding-' + Drupal.offCanvas.getEdge(), width + 'px');
+ $container.attr('data-offset-' + Drupal.offCanvas.getEdge(), width);
+ displace();
+ }
+ },
+ getContainer: function getContainer($element) {
+ return $element.dialog('widget');
+ },
+ getEdge: function getEdge() {
+ return document.documentElement.dir === 'rtl' ? 'left' : 'right';
}
- }
+ };
Drupal.behaviors.offCanvasEvents = {
attach: function attach() {
$(window).once('off-canvas').on({
- 'dialog:aftercreate': function dialogAftercreate(event, dialog, $element, settings) {
- if ($element.is('#drupal-off-canvas')) {
- var eventData = { settings: settings, $element: $element };
- $('.ui-dialog-off-canvas, .ui-dialog-off-canvas .ui-dialog-titlebar').toggleClass('ui-dialog-empty-title', !settings.title);
-
- $element.on('dialogresize.off-canvas', eventData, debounce(bodyPadding, 100)).on('dialogContentResize.off-canvas', eventData, handleDialogResize).on('dialogContentResize.off-canvas', eventData, debounce(bodyPadding, 100)).trigger('dialogresize.off-canvas');
-
- $element.dialog('widget').attr('data-offset-' + edge, '');
-
- $(window).on('resize.off-canvas scroll.off-canvas', eventData, debounce(resetSize, 100)).trigger('resize.off-canvas');
+ 'dialog:beforecreate': function dialogBeforecreate(event, dialog, $element, settings) {
+ if (Drupal.offCanvas.isOffCanvas($element)) {
+ Drupal.offCanvas.beforeCreate({ dialog: dialog, $element: $element, settings: settings });
}
},
- 'dialog:beforecreate': function dialogBeforecreate(event, dialog, $element, settings) {
- if ($element.is('#drupal-off-canvas')) {
- $('body').addClass('js-tray-open');
-
- settings.position = {
- my: 'left top',
- at: edge + ' top',
- of: window
- };
- settings.dialogClass += ' ui-dialog-off-canvas';
-
- settings.height = $(window).height();
+ 'dialog:aftercreate': function dialogAftercreate(event, dialog, $element, settings) {
+ if (Drupal.offCanvas.isOffCanvas($element)) {
+ Drupal.offCanvas.render({ dialog: dialog, $element: $element, settings: settings });
+ Drupal.offCanvas.afterCreate({ $element: $element, settings: settings });
}
},
'dialog:beforeclose': function dialogBeforeclose(event, dialog, $element) {
- if ($element.is('#drupal-off-canvas')) {
- $('body').removeClass('js-tray-open');
-
- $(document).off('.off-canvas');
- $(window).off('.off-canvas');
- $mainCanvasWrapper.css('padding-' + edge, 0);
+ if (Drupal.offCanvas.isOffCanvas($element)) {
+ Drupal.offCanvas.beforeClose({ dialog: dialog, $element: $element });
}
}
});