summaryrefslogtreecommitdiffstatshomepage
path: root/core/misc/displace.js
diff options
context:
space:
mode:
Diffstat (limited to 'core/misc/displace.js')
-rw-r--r--core/misc/displace.js200
1 files changed, 169 insertions, 31 deletions
diff --git a/core/misc/displace.js b/core/misc/displace.js
index ac1902b7130..6e673d2c949 100644
--- a/core/misc/displace.js
+++ b/core/misc/displace.js
@@ -1,58 +1,125 @@
/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
+ * @file
+ * Manages elements that can offset the size of the viewport.
+ *
+ * Measures and reports viewport offset dimensions from elements like the
+ * toolbar that can potentially displace the positioning of other elements.
+ */
+/**
+ * @typedef {object} Drupal~displaceOffset
+ *
+ * @prop {number} top
+ * @prop {number} left
+ * @prop {number} right
+ * @prop {number} bottom
+ */
+
+/**
+ * Triggers when layout of the page changes.
+ *
+ * This is used to position fixed element on the page during page resize and
+ * Toolbar toggling.
+ *
+ * @event drupalViewportOffsetChange
+ */
(function ($, Drupal, debounce) {
+ /**
+ *
+ * @type {Drupal~displaceOffset}
+ */
const cache = {
right: 0,
left: 0,
bottom: 0,
- top: 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 => {
+ 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 element whose dimensions and placement will be measured.
+ *
+ * @param {string} edge
+ * The name of the edge of the viewport that the element is associated
+ * with.
+ *
+ * @return {number}
+ * The viewport displacement distance for the requested edge.
+ */
function getRawOffset(el, edge) {
const $el = $(el);
const documentElement = document.documentElement;
let displacement = 0;
const horizontal = edge === 'left' || edge === 'right';
+ // Get the offset of the element itself.
let placement = $el.offset()[horizontal ? 'left' : 'top'];
- placement -= window[`scroll${horizontal ? 'X' : 'Y'}`] || document.documentElement[`scroll${horizontal ? 'Left' : 'Top'}`] || 0;
-
+ // Subtract scroll distance from placement to get the distance
+ // to the edge of the viewport.
+ placement -=
+ window[`scroll${horizontal ? 'X' : 'Y'}`] ||
+ document.documentElement[`scroll${horizontal ? 'Left' : 'Top'}`] ||
+ 0;
+ // Find the displacement value according to the edge.
switch (edge) {
+ // Left and top elements displace as a sum of their own offset value
+ // plus their size.
case 'top':
+ // Total displacement is the sum of the elements placement and size.
displacement = placement + $el.outerHeight();
break;
case 'left':
+ // Total displacement is the sum of the elements placement and size.
displacement = placement + $el.outerWidth();
break;
+ // Right and bottom elements displace according to their left and
+ // top offset. Their size isn't important.
case 'bottom':
displacement = documentElement.clientHeight - placement;
break;
@@ -64,66 +131,137 @@
default:
displacement = 0;
}
-
return displacement;
}
+ /**
+ * Gets a specific edge's offset.
+ *
+ * Any element with the attribute data-offset-{edge} e.g. data-offset-top will
+ * be considered in the viewport offset calculations. If the attribute has a
+ * numeric value, that value will be used. If no value is provided, one will
+ * be calculated using the element's dimensions and placement.
+ *
+ * @function Drupal.displace.calculateOffset
+ *
+ * @param {string} edge
+ * The name of the edge to calculate. Can be 'top', 'right',
+ * 'bottom' or 'left'.
+ *
+ * @return {number}
+ * The viewport displacement distance for the requested edge.
+ */
function calculateOffset(edge) {
let edgeOffset = 0;
- const displacingElements = document.querySelectorAll(`[data-offset-${edge}]`);
+ const displacingElements = document.querySelectorAll(
+ `[data-offset-${edge}]`,
+ );
const n = displacingElements.length;
-
for (let i = 0; i < n; i++) {
const el = displacingElements[i];
-
+ // If the element is not visible, do consider its dimensions.
if (el.style.display === 'none') {
continue;
}
-
+ // If the offset data attribute contains a displacing value, use it.
let displacement = parseInt(el.getAttribute(`data-offset-${edge}`), 10);
-
+ // If the element's offset data attribute exits
+ // but is not a valid number then get the displacement
+ // dimensions directly from the element.
+ // eslint-disable-next-line no-restricted-globals
if (isNaN(displacement)) {
displacement = getRawOffset(el, edge);
}
-
+ // If the displacement value is larger than the current value for this
+ // edge, use the displacement value.
edgeOffset = Math.max(edgeOffset, displacement);
}
return edgeOffset;
}
- function displace() {
- let broadcast = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
+ /**
+ * 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=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
+ * and left. The value of each key is the viewport displacement distance for
+ * that edge.
+ *
+ * @fires event:drupalViewportOffsetChange
+ */
+ function displace(broadcast = true) {
const newOffsets = {};
- offsetKeys.forEach(edge => {
+ // 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);
});
- offsetKeys.forEach(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;
}
+ /**
+ * Registers a resize handler on the window.
+ *
+ * @type {Drupal~behavior}
+ */
Drupal.behaviors.drupalDisplace = {
attach() {
+ // Mark this behavior as processed on the first pass.
if (this.displaceProcessed) {
return;
}
-
this.displaceProcessed = true;
$(window).on('resize.drupalDisplace', debounce(displace, 200));
- }
-
+ },
};
+
+ /**
+ * Assign the displace function to a property of the Drupal global object.
+ *
+ * @ignore
+ */
Drupal.displace = displace;
+
+ /**
+ * Expose offsets to other scripts to avoid having to recalculate offsets.
+ *
+ * @ignore
+ */
Object.defineProperty(Drupal.displace, 'offsets', {
value: offsets,
- writable: false
+ // Make sure other scripts don't replace this object.
+ writable: false,
});
+
+ /**
+ * Expose method to compute a single edge offsets.
+ *
+ * @ignore
+ */
Drupal.displace.calculateOffset = calculateOffset;
-})(jQuery, Drupal, Drupal.debounce); \ No newline at end of file
+})(jQuery, Drupal, Drupal.debounce);