annotate core/misc/dialog/off-canvas.es6.js @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 129ea1e6d783
children
rev   line source
Chris@14 1 /**
Chris@14 2 * @file
Chris@14 3 * Drupal's off-canvas library.
Chris@14 4 */
Chris@14 5
Chris@14 6 (($, Drupal, debounce, displace) => {
Chris@14 7 /**
Chris@14 8 * Off-canvas dialog implementation using jQuery Dialog.
Chris@14 9 *
Chris@14 10 * Transforms the regular dialogs created using Drupal.dialog when the dialog
Chris@14 11 * element equals '#drupal-off-canvas' into an side-loading dialog.
Chris@14 12 *
Chris@14 13 * @namespace
Chris@14 14 */
Chris@14 15 Drupal.offCanvas = {
Chris@17 16 /**
Chris@17 17 * Storage for position information about the tray.
Chris@17 18 *
Chris@17 19 * @type {?String}
Chris@17 20 */
Chris@17 21 position: null,
Chris@17 22
Chris@17 23 /**
Chris@17 24 * The minimum height of the tray when opened at the top of the page.
Chris@17 25 *
Chris@17 26 * @type {Number}
Chris@17 27 */
Chris@17 28 minimumHeight: 30,
Chris@14 29
Chris@14 30 /**
Chris@14 31 * The minimum width to use body displace needs to match the width at which
Chris@14 32 * the tray will be 100% width. @see core/misc/dialog/off-canvas.css
Chris@14 33 *
Chris@14 34 * @type {Number}
Chris@14 35 */
Chris@14 36 minDisplaceWidth: 768,
Chris@14 37
Chris@14 38 /**
Chris@14 39 * Wrapper used to position off-canvas dialog.
Chris@14 40 *
Chris@14 41 * @type {jQuery}
Chris@14 42 */
Chris@14 43 $mainCanvasWrapper: $('[data-off-canvas-main-canvas]'),
Chris@14 44
Chris@14 45 /**
Chris@14 46 * Determines if an element is an off-canvas dialog.
Chris@14 47 *
Chris@14 48 * @param {jQuery} $element
Chris@14 49 * The dialog element.
Chris@14 50 *
Chris@14 51 * @return {bool}
Chris@14 52 * True this is currently an off-canvas dialog.
Chris@14 53 */
Chris@14 54 isOffCanvas($element) {
Chris@14 55 return $element.is('#drupal-off-canvas');
Chris@14 56 },
Chris@14 57
Chris@14 58 /**
Chris@14 59 * Remove off-canvas dialog events.
Chris@14 60 *
Chris@14 61 * @param {jQuery} $element
Chris@14 62 * The target element.
Chris@14 63 */
Chris@14 64 removeOffCanvasEvents($element) {
Chris@14 65 $element.off('.off-canvas');
Chris@14 66 $(document).off('.off-canvas');
Chris@14 67 $(window).off('.off-canvas');
Chris@14 68 },
Chris@14 69
Chris@14 70 /**
Chris@14 71 * Handler fired before an off-canvas dialog has been opened.
Chris@14 72 *
Chris@14 73 * @param {Object} settings
Chris@14 74 * Settings related to the composition of the dialog.
Chris@14 75 *
Chris@14 76 * @return {undefined}
Chris@14 77 */
Chris@14 78 beforeCreate({ settings, $element }) {
Chris@14 79 // Clean up previous dialog event handlers.
Chris@14 80 Drupal.offCanvas.removeOffCanvasEvents($element);
Chris@14 81
Chris@14 82 $('body').addClass('js-off-canvas-dialog-open');
Chris@14 83 // @see http://api.jqueryui.com/position/
Chris@14 84 settings.position = {
Chris@14 85 my: 'left top',
Chris@14 86 at: `${Drupal.offCanvas.getEdge()} top`,
Chris@14 87 of: window,
Chris@14 88 };
Chris@14 89
Chris@14 90 /**
Chris@17 91 * Applies initial height and with to dialog based depending on position.
Chris@14 92 * @see http://api.jqueryui.com/dialog for all dialog options.
Chris@14 93 */
Chris@17 94 const position = settings.drupalOffCanvasPosition;
Chris@17 95 const height = position === 'side' ? $(window).height() : settings.height;
Chris@17 96 const width = position === 'side' ? settings.width : '100%';
Chris@17 97 settings.height = height;
Chris@17 98 settings.width = width;
Chris@14 99 },
Chris@14 100
Chris@14 101 /**
Chris@14 102 * Handler fired after an off-canvas dialog has been closed.
Chris@14 103 *
Chris@14 104 * @return {undefined}
Chris@14 105 */
Chris@14 106 beforeClose({ $element }) {
Chris@14 107 $('body').removeClass('js-off-canvas-dialog-open');
Chris@14 108 // Remove all *.off-canvas events
Chris@14 109 Drupal.offCanvas.removeOffCanvasEvents($element);
Chris@17 110 Drupal.offCanvas.resetPadding();
Chris@14 111 },
Chris@14 112
Chris@14 113 /**
Chris@14 114 * Handler fired when an off-canvas dialog has been opened.
Chris@14 115 *
Chris@14 116 * @param {jQuery} $element
Chris@14 117 * The off-canvas dialog element.
Chris@14 118 * @param {Object} settings
Chris@14 119 * Settings related to the composition of the dialog.
Chris@14 120 *
Chris@14 121 * @return {undefined}
Chris@14 122 */
Chris@14 123 afterCreate({ $element, settings }) {
Chris@14 124 const eventData = { settings, $element, offCanvasDialog: this };
Chris@14 125
Chris@14 126 $element
Chris@17 127 .on(
Chris@17 128 'dialogContentResize.off-canvas',
Chris@17 129 eventData,
Chris@17 130 Drupal.offCanvas.handleDialogResize,
Chris@17 131 )
Chris@17 132 .on(
Chris@17 133 'dialogContentResize.off-canvas',
Chris@17 134 eventData,
Chris@17 135 Drupal.offCanvas.bodyPadding,
Chris@17 136 );
Chris@14 137
Chris@17 138 Drupal.offCanvas
Chris@17 139 .getContainer($element)
Chris@17 140 .attr(`data-offset-${Drupal.offCanvas.getEdge()}`, '');
Chris@14 141
Chris@14 142 $(window)
Chris@17 143 .on(
Chris@17 144 'resize.off-canvas',
Chris@17 145 eventData,
Chris@17 146 debounce(Drupal.offCanvas.resetSize, 100),
Chris@17 147 )
Chris@14 148 .trigger('resize.off-canvas');
Chris@14 149 },
Chris@14 150
Chris@14 151 /**
Chris@14 152 * Toggle classes based on title existence.
Chris@14 153 * Called with Drupal.offCanvas.afterCreate.
Chris@14 154 *
Chris@14 155 * @param {Object} settings
Chris@14 156 * Settings related to the composition of the dialog.
Chris@14 157 *
Chris@14 158 * @return {undefined}
Chris@14 159 */
Chris@14 160 render({ settings }) {
Chris@17 161 $(
Chris@17 162 '.ui-dialog-off-canvas, .ui-dialog-off-canvas .ui-dialog-titlebar',
Chris@17 163 ).toggleClass('ui-dialog-empty-title', !settings.title);
Chris@14 164 },
Chris@14 165
Chris@14 166 /**
Chris@14 167 * Adjusts the dialog on resize.
Chris@14 168 *
Chris@14 169 * @param {jQuery.Event} event
Chris@14 170 * The event triggered.
Chris@14 171 * @param {object} event.data
Chris@14 172 * Data attached to the event.
Chris@14 173 */
Chris@14 174 handleDialogResize(event) {
Chris@14 175 const $element = event.data.$element;
Chris@14 176 const $container = Drupal.offCanvas.getContainer($element);
Chris@14 177
Chris@17 178 const $offsets = $container.find(
Chris@17 179 '> :not(#drupal-off-canvas, .ui-resizable-handle)',
Chris@17 180 );
Chris@14 181 let offset = 0;
Chris@14 182
Chris@14 183 // Let scroll element take all the height available.
Chris@14 184 $element.css({ height: 'auto' });
Chris@14 185 const modalHeight = $container.height();
Chris@14 186
Chris@14 187 $offsets.each((i, e) => {
Chris@14 188 offset += $(e).outerHeight();
Chris@14 189 });
Chris@14 190
Chris@14 191 // Take internal padding into account.
Chris@14 192 const scrollOffset = $element.outerHeight() - $element.height();
Chris@14 193 $element.height(modalHeight - offset - scrollOffset);
Chris@14 194 },
Chris@14 195
Chris@14 196 /**
Chris@14 197 * Resets the size of the dialog.
Chris@14 198 *
Chris@14 199 * @param {jQuery.Event} event
Chris@14 200 * The event triggered.
Chris@14 201 * @param {object} event.data
Chris@14 202 * Data attached to the event.
Chris@14 203 */
Chris@14 204 resetSize(event) {
Chris@14 205 const $element = event.data.$element;
Chris@14 206 const container = Drupal.offCanvas.getContainer($element);
Chris@17 207 const position = event.data.settings.drupalOffCanvasPosition;
Chris@14 208
Chris@17 209 // Only remove the `data-offset-*` attribute if the value previously
Chris@17 210 // exists and the orientation is changing.
Chris@17 211 if (Drupal.offCanvas.position && Drupal.offCanvas.position !== position) {
Chris@17 212 container.removeAttr(`data-offset-${Drupal.offCanvas.position}`);
Chris@17 213 }
Chris@17 214 // Set a minimum height on $element
Chris@17 215 if (position === 'top') {
Chris@17 216 $element.css('min-height', `${Drupal.offCanvas.minimumHeight}px`);
Chris@17 217 }
Chris@17 218
Chris@17 219 displace();
Chris@17 220
Chris@17 221 const offsets = displace.offsets;
Chris@17 222
Chris@17 223 const topPosition =
Chris@17 224 position === 'side' && offsets.top !== 0 ? `+${offsets.top}` : '';
Chris@14 225 const adjustedOptions = {
Chris@14 226 // @see http://api.jqueryui.com/position/
Chris@14 227 position: {
Chris@14 228 my: `${Drupal.offCanvas.getEdge()} top`,
Chris@14 229 at: `${Drupal.offCanvas.getEdge()} top${topPosition}`,
Chris@14 230 of: window,
Chris@14 231 },
Chris@14 232 };
Chris@14 233
Chris@17 234 const height =
Chris@17 235 position === 'side'
Chris@17 236 ? `${$(window).height() - (offsets.top + offsets.bottom)}px`
Chris@17 237 : event.data.settings.height;
Chris@14 238 container.css({
Chris@14 239 position: 'fixed',
Chris@17 240 height,
Chris@14 241 });
Chris@14 242
Chris@14 243 $element
Chris@14 244 .dialog('option', adjustedOptions)
Chris@14 245 .trigger('dialogContentResize.off-canvas');
Chris@17 246
Chris@17 247 Drupal.offCanvas.position = position;
Chris@14 248 },
Chris@14 249
Chris@14 250 /**
Chris@14 251 * Adjusts the body padding when the dialog is resized.
Chris@14 252 *
Chris@14 253 * @param {jQuery.Event} event
Chris@14 254 * The event triggered.
Chris@14 255 * @param {object} event.data
Chris@14 256 * Data attached to the event.
Chris@14 257 */
Chris@14 258 bodyPadding(event) {
Chris@17 259 const position = event.data.settings.drupalOffCanvasPosition;
Chris@17 260 if (
Chris@17 261 position === 'side' &&
Chris@17 262 $('body').outerWidth() < Drupal.offCanvas.minDisplaceWidth
Chris@17 263 ) {
Chris@14 264 return;
Chris@14 265 }
Chris@17 266 Drupal.offCanvas.resetPadding();
Chris@14 267 const $element = event.data.$element;
Chris@14 268 const $container = Drupal.offCanvas.getContainer($element);
Chris@14 269 const $mainCanvasWrapper = Drupal.offCanvas.$mainCanvasWrapper;
Chris@14 270
Chris@14 271 const width = $container.outerWidth();
Chris@17 272 const mainCanvasPadding = $mainCanvasWrapper.css(
Chris@17 273 `padding-${Drupal.offCanvas.getEdge()}`,
Chris@17 274 );
Chris@17 275 if (position === 'side' && width !== mainCanvasPadding) {
Chris@17 276 $mainCanvasWrapper.css(
Chris@17 277 `padding-${Drupal.offCanvas.getEdge()}`,
Chris@17 278 `${width}px`,
Chris@17 279 );
Chris@14 280 $container.attr(`data-offset-${Drupal.offCanvas.getEdge()}`, width);
Chris@14 281 displace();
Chris@14 282 }
Chris@17 283
Chris@17 284 const height = $container.outerHeight();
Chris@17 285 if (position === 'top') {
Chris@17 286 $mainCanvasWrapper.css('padding-top', `${height}px`);
Chris@17 287 $container.attr('data-offset-top', height);
Chris@17 288 displace();
Chris@17 289 }
Chris@14 290 },
Chris@14 291
Chris@14 292 /**
Chris@14 293 * The HTML element that surrounds the dialog.
Chris@14 294 * @param {HTMLElement} $element
Chris@14 295 * The dialog element.
Chris@14 296 *
Chris@14 297 * @return {HTMLElement}
Chris@14 298 * The containing element.
Chris@14 299 */
Chris@14 300 getContainer($element) {
Chris@14 301 return $element.dialog('widget');
Chris@14 302 },
Chris@14 303
Chris@14 304 /**
Chris@14 305 * The edge of the screen that the dialog should appear on.
Chris@14 306 *
Chris@14 307 * @return {string}
Chris@14 308 * The edge the tray will be shown on, left or right.
Chris@14 309 */
Chris@14 310 getEdge() {
Chris@14 311 return document.documentElement.dir === 'rtl' ? 'left' : 'right';
Chris@14 312 },
Chris@17 313
Chris@17 314 /**
Chris@17 315 * Resets main canvas wrapper and toolbar padding / margin.
Chris@17 316 */
Chris@17 317 resetPadding() {
Chris@17 318 Drupal.offCanvas.$mainCanvasWrapper.css(
Chris@17 319 `padding-${Drupal.offCanvas.getEdge()}`,
Chris@17 320 0,
Chris@17 321 );
Chris@17 322 Drupal.offCanvas.$mainCanvasWrapper.css('padding-top', 0);
Chris@17 323 displace();
Chris@17 324 },
Chris@14 325 };
Chris@14 326
Chris@14 327 /**
Chris@14 328 * Attaches off-canvas dialog behaviors.
Chris@14 329 *
Chris@14 330 * @type {Drupal~behavior}
Chris@14 331 *
Chris@14 332 * @prop {Drupal~behaviorAttach} attach
Chris@14 333 * Attaches event listeners for off-canvas dialogs.
Chris@14 334 */
Chris@14 335 Drupal.behaviors.offCanvasEvents = {
Chris@14 336 attach: () => {
Chris@17 337 $(window)
Chris@17 338 .once('off-canvas')
Chris@17 339 .on({
Chris@17 340 'dialog:beforecreate': (event, dialog, $element, settings) => {
Chris@17 341 if (Drupal.offCanvas.isOffCanvas($element)) {
Chris@17 342 Drupal.offCanvas.beforeCreate({ dialog, $element, settings });
Chris@17 343 }
Chris@17 344 },
Chris@17 345 'dialog:aftercreate': (event, dialog, $element, settings) => {
Chris@17 346 if (Drupal.offCanvas.isOffCanvas($element)) {
Chris@17 347 Drupal.offCanvas.render({ dialog, $element, settings });
Chris@17 348 Drupal.offCanvas.afterCreate({ $element, settings });
Chris@17 349 }
Chris@17 350 },
Chris@17 351 'dialog:beforeclose': (event, dialog, $element) => {
Chris@17 352 if (Drupal.offCanvas.isOffCanvas($element)) {
Chris@17 353 Drupal.offCanvas.beforeClose({ dialog, $element });
Chris@17 354 }
Chris@17 355 },
Chris@17 356 });
Chris@14 357 },
Chris@14 358 };
Chris@14 359 })(jQuery, Drupal, Drupal.debounce, Drupal.displace);