Updated to Drupal 8.5. Core Media not yet in use.
[yaffs-website] / web / core / misc / dialog / off-canvas.es6.js
diff --git a/web/core/misc/dialog/off-canvas.es6.js b/web/core/misc/dialog/off-canvas.es6.js
new file mode 100644 (file)
index 0000000..0068e44
--- /dev/null
@@ -0,0 +1,273 @@
+/**
+ * @file
+ * Drupal's off-canvas library.
+ */
+
+(($, Drupal, debounce, displace) => {
+  /**
+   * 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.
+   *
+   * @namespace
+   */
+  Drupal.offCanvas = {
+
+    /**
+     * The minimum width to use body displace needs to match the width at which
+     * the tray will be 100% width. @see core/misc/dialog/off-canvas.css
+     *
+     * @type {Number}
+     */
+    minDisplaceWidth: 768,
+
+    /**
+     * Wrapper used to position off-canvas dialog.
+     *
+     * @type {jQuery}
+     */
+    $mainCanvasWrapper: $('[data-off-canvas-main-canvas]'),
+
+    /**
+     * 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');
+    },
+
+    /**
+     * Remove off-canvas dialog events.
+     *
+     * @param {jQuery} $element
+     *   The target element.
+     */
+    removeOffCanvasEvents($element) {
+      $element.off('.off-canvas');
+      $(document).off('.off-canvas');
+      $(window).off('.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, $element }) {
+      // Clean up previous dialog event handlers.
+      Drupal.offCanvas.removeOffCanvasEvents($element);
+
+      $('body').addClass('js-off-canvas-dialog-open');
+      // @see http://api.jqueryui.com/position/
+      settings.position = {
+        my: 'left top',
+        at: `${Drupal.offCanvas.getEdge()} top`,
+        of: window,
+      };
+
+      /**
+       * Applies initial height to dialog based on window height.
+       * @see http://api.jqueryui.com/dialog for all dialog options.
+       */
+      settings.height = $(window).height();
+    },
+
+    /**
+     * Handler fired after an off-canvas dialog has been closed.
+     *
+     * @return {undefined}
+     */
+    beforeClose({ $element }) {
+      $('body').removeClass('js-off-canvas-dialog-open');
+      // Remove all *.off-canvas events
+      Drupal.offCanvas.removeOffCanvasEvents($element);
+
+      Drupal.offCanvas.$mainCanvasWrapper.css(`padding-${Drupal.offCanvas.getEdge()}`, 0);
+    },
+
+    /**
+     * 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 };
+
+      $element
+        .on('dialogContentResize.off-canvas', eventData, Drupal.offCanvas.handleDialogResize)
+        .on('dialogContentResize.off-canvas', eventData, Drupal.offCanvas.bodyPadding);
+
+      Drupal.offCanvas.getContainer($element).attr(`data-offset-${Drupal.offCanvas.getEdge()}`, '');
+
+      $(window)
+        .on('resize.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.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches event listeners for off-canvas dialogs.
+   */
+  Drupal.behaviors.offCanvasEvents = {
+    attach: () => {
+      $(window).once('off-canvas').on({
+        'dialog:beforecreate': (event, dialog, $element, settings) => {
+          if (Drupal.offCanvas.isOffCanvas($element)) {
+            Drupal.offCanvas.beforeCreate({ dialog, $element, settings });
+          }
+        },
+        'dialog:aftercreate': (event, dialog, $element, settings) => {
+          if (Drupal.offCanvas.isOffCanvas($element)) {
+            Drupal.offCanvas.render({ dialog, $element, settings });
+            Drupal.offCanvas.afterCreate({ $element, settings });
+          }
+        },
+        'dialog:beforeclose': (event, dialog, $element) => {
+          if (Drupal.offCanvas.isOffCanvas($element)) {
+            Drupal.offCanvas.beforeClose({ dialog, $element });
+          }
+        },
+      });
+    },
+  };
+})(jQuery, Drupal, Drupal.debounce, Drupal.displace);