Loading core/misc/displace.es6.js +88 −45 Original line number Diff line number Diff line Loading @@ -23,25 +23,66 @@ * * @event drupalViewportOffsetChange */ (function ($, Drupal, debounce) { /** * @name Drupal.displace.offsets * * @type {Drupal~displaceOffset} */ let offsets = { top: 0, const cache = { right: 0, bottom: 0, left: 0, bottom: 0, top: 0, }; /** * The prefix used for the css custom variable name. * * @type {string} */ const cssVarPrefix = '--drupal-displace-offset'; const documentStyle = document.documentElement.style; const offsetKeys = Object.keys(cache); /** * The object with accessors that update the CSS variable on value update. * * @type {Drupal~displaceOffset} */ const offsetProps = {}; offsetKeys.forEach((edge) => { offsetProps[edge] = { // Show this property when using Object.keys(). enumerable: true, get() { return cache[edge]; }, set(value) { // Only update the CSS custom variable when the value changed. if (value !== cache[edge]) { documentStyle.setProperty(`${cssVarPrefix}-${edge}`, `${value}px`); } cache[edge] = value; }, }; }); /** * Current value of the size of margins on the page. * * This property is read-only and the object is sealed to prevent key name * modifications since key names are used to dynamically construct CSS custom * variable names. * * @name Drupal.displace.offsets * * @type {Drupal~displaceOffset} */ const offsets = Object.seal(Object.defineProperties({}, offsetProps)); /** * Calculates displacement for element based on its dimensions and placement. * * @param {HTMLElement} el * The jQuery element whose dimensions and placement will be measured. * The element whose dimensions and placement will be measured. * * @param {string} edge * The name of the edge of the viewport that the element is associated Loading Loading @@ -139,33 +180,23 @@ return edgeOffset; } /** * Determines the viewport offsets. * * @return {Drupal~displaceOffset} * An object whose keys are the for sides an element -- top, right, bottom * and left. The value of each key is the viewport displacement distance for * that edge. */ function calculateOffsets() { return { top: calculateOffset('top'), right: calculateOffset('right'), bottom: calculateOffset('bottom'), left: calculateOffset('left'), }; } /** * Informs listeners of the current offset dimensions. * * Corresponding CSS custom variables are also updated. * Corresponding CSS custom variables names are: * - `--drupal-displace-offset-top` * - `--drupal-displace-offset-right` * - `--drupal-displace-offset-bottom` * - `--drupal-displace-offset-left` * * @function Drupal.displace * * @prop {Drupal~displaceOffset} offsets * * @param {bool} [broadcast] * When true or undefined, causes the recalculated offsets values to be * broadcast to listeners. * @param {bool} [broadcast=true] * When true, causes the recalculated offsets values to be * broadcast to listeners. If none is given, defaults to true. * * @return {Drupal~displaceOffset} * An object whose keys are the for sides an element -- top, right, bottom Loading @@ -174,10 +205,20 @@ * * @fires event:drupalViewportOffsetChange */ function displace(broadcast) { offsets = calculateOffsets(); Drupal.displace.offsets = offsets; if (typeof broadcast === 'undefined' || broadcast) { function displace(broadcast = true) { const newOffsets = {}; // Getting the offset and setting the offset needs to be separated because // of performance concerns. Only do DOM/style reading happening here. offsetKeys.forEach((edge) => { newOffsets[edge] = calculateOffset(edge); }); // Once we have all the values, write to the DOM/style. offsetKeys.forEach((edge) => { // Updating the value in place also update Drupal.displace.offsets. offsets[edge] = newOffsets[edge]; }); if (broadcast) { $(document).trigger('drupalViewportOffsetChange', offsets); } return offsets; Loading @@ -195,7 +236,6 @@ return; } this.displaceProcessed = true; $(window).on('resize.drupalDisplace', debounce(displace, 200)); }, }; Loading @@ -206,19 +246,22 @@ * @ignore */ Drupal.displace = displace; $.extend(Drupal.displace, { /** * Expose offsets to other scripts to avoid having to recalculate offsets. * * @ignore */ offsets, Object.defineProperty(Drupal.displace, 'offsets', { value: offsets, // Make sure other scripts don't replace this object. writable: false, }); /** * Expose method to compute a single edge offsets. * * @ignore */ calculateOffset, }); Drupal.displace.calculateOffset = calculateOffset; })(jQuery, Drupal, Drupal.debounce); core/misc/displace.js +41 −20 Original line number Diff line number Diff line Loading @@ -6,12 +6,35 @@ **/ (function ($, Drupal, debounce) { let offsets = { top: 0, const cache = { right: 0, left: 0, bottom: 0, left: 0 top: 0 }; const cssVarPrefix = '--drupal-displace-offset'; const documentStyle = document.documentElement.style; const offsetKeys = Object.keys(cache); const offsetProps = {}; offsetKeys.forEach(edge => { offsetProps[edge] = { enumerable: true, get() { return cache[edge]; }, set(value) { if (value !== cache[edge]) { documentStyle.setProperty(`${cssVarPrefix}-${edge}`, `${value}px`); } cache[edge] = value; } }; }); const offsets = Object.seal(Object.defineProperties({}, offsetProps)); function getRawOffset(el, edge) { const $el = $(el); Loading Loading @@ -69,20 +92,17 @@ return edgeOffset; } function calculateOffsets() { return { top: calculateOffset('top'), right: calculateOffset('right'), bottom: calculateOffset('bottom'), left: calculateOffset('left') }; } function displace(broadcast) { offsets = calculateOffsets(); Drupal.displace.offsets = offsets; function displace() { let broadcast = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; const newOffsets = {}; offsetKeys.forEach(edge => { newOffsets[edge] = calculateOffset(edge); }); offsetKeys.forEach(edge => { offsets[edge] = newOffsets[edge]; }); if (typeof broadcast === 'undefined' || broadcast) { if (broadcast) { $(document).trigger('drupalViewportOffsetChange', offsets); } Loading @@ -101,8 +121,9 @@ }; Drupal.displace = displace; $.extend(Drupal.displace, { offsets, calculateOffset Object.defineProperty(Drupal.displace, 'offsets', { value: offsets, writable: false }); Drupal.displace.calculateOffset = calculateOffset; })(jQuery, Drupal, Drupal.debounce); No newline at end of file core/modules/system/tests/modules/js_displace/js_displace.info.yml 0 → 100644 +5 −0 Original line number Diff line number Diff line name: 'JS Displace Tests' type: module description: 'Support module for testing Drupal.displace() JavaScript API.' package: Testing version: VERSION core/modules/system/tests/modules/js_displace/js_displace.module 0 → 100644 +13 −0 Original line number Diff line number Diff line <?php /** * @file * Functions to support testing Drupal.displace() JavaScript API. */ /** * Implements hook_preprocess_html(). */ function js_displace_preprocess_html(&$variables) { $variables['#attached']['library'][] = 'core/drupal.displace'; } core/tests/Drupal/Nightwatch/Tests/jsDisplace.js 0 → 100644 +126 −0 Original line number Diff line number Diff line const testValues = { top: 200, right: 110, bottom: 145, left: 310, }; const testElements = ` <div data-offset-top style=" background-color: red; height: 110px; left: 0; position: fixed; right: 0; top: 90px; width: 100%;" ></div> <div data-offset-right style=" background-color: blue; bottom: 0; height: 100%; position: fixed; right: 10px; top: 0; width: 100px;" ></div> <div data-offset-bottom style=" background-color: yellow; bottom: 45px; height: 100px; left: 0; position: fixed; right: 0; width: 100%;" ></div> <div data-offset-left style=" background-color: orange; bottom: 0; height: 100%; left: 10px; position: fixed; top: 0; width: 300px;" ></div> `; module.exports = { '@tags': ['core'], before(browser) { browser.drupalInstall().drupalInstallModule('js_displace'); }, after(browser) { browser.drupalUninstall(); }, 'Test Drupal.displace() JavaScript API': (browser) => { browser .drupalRelativeURL('/') .waitForElementVisible('body') .execute( // eslint-disable-next-line func-names, prefer-arrow-callback, no-shadow function (testValues, testElements) { const testElementsContainer = document.createElement('div'); testElementsContainer.innerHTML = testElements; document.body.append(testElementsContainer); const displaceOutput = Drupal.displace(); return ( displaceOutput.top === testValues.top && displaceOutput.right === testValues.right && displaceOutput.bottom === testValues.bottom && displaceOutput.left === testValues.left ); }, [testValues, testElements], (result) => { browser.assert.ok( result.value, 'Drupal.displace() JS returns proper offsets for all edges.', ); }, ) .execute( // eslint-disable-next-line func-names, prefer-arrow-callback, no-shadow function (testValues) { const rootStyles = getComputedStyle(document.documentElement); const topOffsetStyle = rootStyles.getPropertyValue( '--drupal-displace-offset-top', ); const rightOffsetStyle = rootStyles.getPropertyValue( '--drupal-displace-offset-right', ); const bottomOffsetStyle = rootStyles.getPropertyValue( '--drupal-displace-offset-bottom', ); const leftOffsetStyle = rootStyles.getPropertyValue( '--drupal-displace-offset-left', ); return ( topOffsetStyle === `${testValues.top}px` && rightOffsetStyle === `${testValues.right}px` && bottomOffsetStyle === `${testValues.bottom}px` && leftOffsetStyle === `${testValues.left}px` ); }, [testValues], (result) => { browser.assert.ok( result.value, 'Drupal.displace() properly sets CSS variables.', ); }, ); }, }; Loading
core/misc/displace.es6.js +88 −45 Original line number Diff line number Diff line Loading @@ -23,25 +23,66 @@ * * @event drupalViewportOffsetChange */ (function ($, Drupal, debounce) { /** * @name Drupal.displace.offsets * * @type {Drupal~displaceOffset} */ let offsets = { top: 0, const cache = { right: 0, bottom: 0, left: 0, bottom: 0, top: 0, }; /** * The prefix used for the css custom variable name. * * @type {string} */ const cssVarPrefix = '--drupal-displace-offset'; const documentStyle = document.documentElement.style; const offsetKeys = Object.keys(cache); /** * The object with accessors that update the CSS variable on value update. * * @type {Drupal~displaceOffset} */ const offsetProps = {}; offsetKeys.forEach((edge) => { offsetProps[edge] = { // Show this property when using Object.keys(). enumerable: true, get() { return cache[edge]; }, set(value) { // Only update the CSS custom variable when the value changed. if (value !== cache[edge]) { documentStyle.setProperty(`${cssVarPrefix}-${edge}`, `${value}px`); } cache[edge] = value; }, }; }); /** * Current value of the size of margins on the page. * * This property is read-only and the object is sealed to prevent key name * modifications since key names are used to dynamically construct CSS custom * variable names. * * @name Drupal.displace.offsets * * @type {Drupal~displaceOffset} */ const offsets = Object.seal(Object.defineProperties({}, offsetProps)); /** * Calculates displacement for element based on its dimensions and placement. * * @param {HTMLElement} el * The jQuery element whose dimensions and placement will be measured. * The element whose dimensions and placement will be measured. * * @param {string} edge * The name of the edge of the viewport that the element is associated Loading Loading @@ -139,33 +180,23 @@ return edgeOffset; } /** * Determines the viewport offsets. * * @return {Drupal~displaceOffset} * An object whose keys are the for sides an element -- top, right, bottom * and left. The value of each key is the viewport displacement distance for * that edge. */ function calculateOffsets() { return { top: calculateOffset('top'), right: calculateOffset('right'), bottom: calculateOffset('bottom'), left: calculateOffset('left'), }; } /** * Informs listeners of the current offset dimensions. * * Corresponding CSS custom variables are also updated. * Corresponding CSS custom variables names are: * - `--drupal-displace-offset-top` * - `--drupal-displace-offset-right` * - `--drupal-displace-offset-bottom` * - `--drupal-displace-offset-left` * * @function Drupal.displace * * @prop {Drupal~displaceOffset} offsets * * @param {bool} [broadcast] * When true or undefined, causes the recalculated offsets values to be * broadcast to listeners. * @param {bool} [broadcast=true] * When true, causes the recalculated offsets values to be * broadcast to listeners. If none is given, defaults to true. * * @return {Drupal~displaceOffset} * An object whose keys are the for sides an element -- top, right, bottom Loading @@ -174,10 +205,20 @@ * * @fires event:drupalViewportOffsetChange */ function displace(broadcast) { offsets = calculateOffsets(); Drupal.displace.offsets = offsets; if (typeof broadcast === 'undefined' || broadcast) { function displace(broadcast = true) { const newOffsets = {}; // Getting the offset and setting the offset needs to be separated because // of performance concerns. Only do DOM/style reading happening here. offsetKeys.forEach((edge) => { newOffsets[edge] = calculateOffset(edge); }); // Once we have all the values, write to the DOM/style. offsetKeys.forEach((edge) => { // Updating the value in place also update Drupal.displace.offsets. offsets[edge] = newOffsets[edge]; }); if (broadcast) { $(document).trigger('drupalViewportOffsetChange', offsets); } return offsets; Loading @@ -195,7 +236,6 @@ return; } this.displaceProcessed = true; $(window).on('resize.drupalDisplace', debounce(displace, 200)); }, }; Loading @@ -206,19 +246,22 @@ * @ignore */ Drupal.displace = displace; $.extend(Drupal.displace, { /** * Expose offsets to other scripts to avoid having to recalculate offsets. * * @ignore */ offsets, Object.defineProperty(Drupal.displace, 'offsets', { value: offsets, // Make sure other scripts don't replace this object. writable: false, }); /** * Expose method to compute a single edge offsets. * * @ignore */ calculateOffset, }); Drupal.displace.calculateOffset = calculateOffset; })(jQuery, Drupal, Drupal.debounce);
core/misc/displace.js +41 −20 Original line number Diff line number Diff line Loading @@ -6,12 +6,35 @@ **/ (function ($, Drupal, debounce) { let offsets = { top: 0, const cache = { right: 0, left: 0, bottom: 0, left: 0 top: 0 }; const cssVarPrefix = '--drupal-displace-offset'; const documentStyle = document.documentElement.style; const offsetKeys = Object.keys(cache); const offsetProps = {}; offsetKeys.forEach(edge => { offsetProps[edge] = { enumerable: true, get() { return cache[edge]; }, set(value) { if (value !== cache[edge]) { documentStyle.setProperty(`${cssVarPrefix}-${edge}`, `${value}px`); } cache[edge] = value; } }; }); const offsets = Object.seal(Object.defineProperties({}, offsetProps)); function getRawOffset(el, edge) { const $el = $(el); Loading Loading @@ -69,20 +92,17 @@ return edgeOffset; } function calculateOffsets() { return { top: calculateOffset('top'), right: calculateOffset('right'), bottom: calculateOffset('bottom'), left: calculateOffset('left') }; } function displace(broadcast) { offsets = calculateOffsets(); Drupal.displace.offsets = offsets; function displace() { let broadcast = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; const newOffsets = {}; offsetKeys.forEach(edge => { newOffsets[edge] = calculateOffset(edge); }); offsetKeys.forEach(edge => { offsets[edge] = newOffsets[edge]; }); if (typeof broadcast === 'undefined' || broadcast) { if (broadcast) { $(document).trigger('drupalViewportOffsetChange', offsets); } Loading @@ -101,8 +121,9 @@ }; Drupal.displace = displace; $.extend(Drupal.displace, { offsets, calculateOffset Object.defineProperty(Drupal.displace, 'offsets', { value: offsets, writable: false }); Drupal.displace.calculateOffset = calculateOffset; })(jQuery, Drupal, Drupal.debounce); No newline at end of file
core/modules/system/tests/modules/js_displace/js_displace.info.yml 0 → 100644 +5 −0 Original line number Diff line number Diff line name: 'JS Displace Tests' type: module description: 'Support module for testing Drupal.displace() JavaScript API.' package: Testing version: VERSION
core/modules/system/tests/modules/js_displace/js_displace.module 0 → 100644 +13 −0 Original line number Diff line number Diff line <?php /** * @file * Functions to support testing Drupal.displace() JavaScript API. */ /** * Implements hook_preprocess_html(). */ function js_displace_preprocess_html(&$variables) { $variables['#attached']['library'][] = 'core/drupal.displace'; }
core/tests/Drupal/Nightwatch/Tests/jsDisplace.js 0 → 100644 +126 −0 Original line number Diff line number Diff line const testValues = { top: 200, right: 110, bottom: 145, left: 310, }; const testElements = ` <div data-offset-top style=" background-color: red; height: 110px; left: 0; position: fixed; right: 0; top: 90px; width: 100%;" ></div> <div data-offset-right style=" background-color: blue; bottom: 0; height: 100%; position: fixed; right: 10px; top: 0; width: 100px;" ></div> <div data-offset-bottom style=" background-color: yellow; bottom: 45px; height: 100px; left: 0; position: fixed; right: 0; width: 100%;" ></div> <div data-offset-left style=" background-color: orange; bottom: 0; height: 100%; left: 10px; position: fixed; top: 0; width: 300px;" ></div> `; module.exports = { '@tags': ['core'], before(browser) { browser.drupalInstall().drupalInstallModule('js_displace'); }, after(browser) { browser.drupalUninstall(); }, 'Test Drupal.displace() JavaScript API': (browser) => { browser .drupalRelativeURL('/') .waitForElementVisible('body') .execute( // eslint-disable-next-line func-names, prefer-arrow-callback, no-shadow function (testValues, testElements) { const testElementsContainer = document.createElement('div'); testElementsContainer.innerHTML = testElements; document.body.append(testElementsContainer); const displaceOutput = Drupal.displace(); return ( displaceOutput.top === testValues.top && displaceOutput.right === testValues.right && displaceOutput.bottom === testValues.bottom && displaceOutput.left === testValues.left ); }, [testValues, testElements], (result) => { browser.assert.ok( result.value, 'Drupal.displace() JS returns proper offsets for all edges.', ); }, ) .execute( // eslint-disable-next-line func-names, prefer-arrow-callback, no-shadow function (testValues) { const rootStyles = getComputedStyle(document.documentElement); const topOffsetStyle = rootStyles.getPropertyValue( '--drupal-displace-offset-top', ); const rightOffsetStyle = rootStyles.getPropertyValue( '--drupal-displace-offset-right', ); const bottomOffsetStyle = rootStyles.getPropertyValue( '--drupal-displace-offset-bottom', ); const leftOffsetStyle = rootStyles.getPropertyValue( '--drupal-displace-offset-left', ); return ( topOffsetStyle === `${testValues.top}px` && rightOffsetStyle === `${testValues.right}px` && bottomOffsetStyle === `${testValues.bottom}px` && leftOffsetStyle === `${testValues.left}px` ); }, [testValues], (result) => { browser.assert.ok( result.value, 'Drupal.displace() properly sets CSS variables.', ); }, ); }, };