annotate core/misc/displace.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@0 1 /**
Chris@0 2 * @file
Chris@0 3 * Manages elements that can offset the size of the viewport.
Chris@0 4 *
Chris@0 5 * Measures and reports viewport offset dimensions from elements like the
Chris@0 6 * toolbar that can potentially displace the positioning of other elements.
Chris@0 7 */
Chris@0 8
Chris@0 9 /**
Chris@0 10 * @typedef {object} Drupal~displaceOffset
Chris@0 11 *
Chris@0 12 * @prop {number} top
Chris@0 13 * @prop {number} left
Chris@0 14 * @prop {number} right
Chris@0 15 * @prop {number} bottom
Chris@0 16 */
Chris@0 17
Chris@0 18 /**
Chris@0 19 * Triggers when layout of the page changes.
Chris@0 20 *
Chris@0 21 * This is used to position fixed element on the page during page resize and
Chris@0 22 * Toolbar toggling.
Chris@0 23 *
Chris@0 24 * @event drupalViewportOffsetChange
Chris@0 25 */
Chris@0 26
Chris@17 27 (function($, Drupal, debounce) {
Chris@0 28 /**
Chris@0 29 * @name Drupal.displace.offsets
Chris@0 30 *
Chris@0 31 * @type {Drupal~displaceOffset}
Chris@0 32 */
Chris@0 33 let offsets = {
Chris@0 34 top: 0,
Chris@0 35 right: 0,
Chris@0 36 bottom: 0,
Chris@0 37 left: 0,
Chris@0 38 };
Chris@0 39
Chris@0 40 /**
Chris@0 41 * Calculates displacement for element based on its dimensions and placement.
Chris@0 42 *
Chris@0 43 * @param {HTMLElement} el
Chris@0 44 * The jQuery element whose dimensions and placement will be measured.
Chris@0 45 *
Chris@0 46 * @param {string} edge
Chris@0 47 * The name of the edge of the viewport that the element is associated
Chris@0 48 * with.
Chris@0 49 *
Chris@0 50 * @return {number}
Chris@0 51 * The viewport displacement distance for the requested edge.
Chris@0 52 */
Chris@0 53 function getRawOffset(el, edge) {
Chris@0 54 const $el = $(el);
Chris@0 55 const documentElement = document.documentElement;
Chris@0 56 let displacement = 0;
Chris@17 57 const horizontal = edge === 'left' || edge === 'right';
Chris@0 58 // Get the offset of the element itself.
Chris@0 59 let placement = $el.offset()[horizontal ? 'left' : 'top'];
Chris@0 60 // Subtract scroll distance from placement to get the distance
Chris@0 61 // to the edge of the viewport.
Chris@17 62 placement -=
Chris@17 63 window[`scroll${horizontal ? 'X' : 'Y'}`] ||
Chris@17 64 document.documentElement[`scroll${horizontal ? 'Left' : 'Top'}`] ||
Chris@17 65 0;
Chris@0 66 // Find the displacement value according to the edge.
Chris@0 67 switch (edge) {
Chris@0 68 // Left and top elements displace as a sum of their own offset value
Chris@0 69 // plus their size.
Chris@0 70 case 'top':
Chris@0 71 // Total displacement is the sum of the elements placement and size.
Chris@0 72 displacement = placement + $el.outerHeight();
Chris@0 73 break;
Chris@0 74
Chris@0 75 case 'left':
Chris@0 76 // Total displacement is the sum of the elements placement and size.
Chris@0 77 displacement = placement + $el.outerWidth();
Chris@0 78 break;
Chris@0 79
Chris@0 80 // Right and bottom elements displace according to their left and
Chris@0 81 // top offset. Their size isn't important.
Chris@0 82 case 'bottom':
Chris@0 83 displacement = documentElement.clientHeight - placement;
Chris@0 84 break;
Chris@0 85
Chris@0 86 case 'right':
Chris@0 87 displacement = documentElement.clientWidth - placement;
Chris@0 88 break;
Chris@0 89
Chris@0 90 default:
Chris@0 91 displacement = 0;
Chris@0 92 }
Chris@0 93 return displacement;
Chris@0 94 }
Chris@0 95
Chris@0 96 /**
Chris@17 97 * Gets a specific edge's offset.
Chris@17 98 *
Chris@17 99 * Any element with the attribute data-offset-{edge} e.g. data-offset-top will
Chris@17 100 * be considered in the viewport offset calculations. If the attribute has a
Chris@17 101 * numeric value, that value will be used. If no value is provided, one will
Chris@17 102 * be calculated using the element's dimensions and placement.
Chris@17 103 *
Chris@17 104 * @function Drupal.displace.calculateOffset
Chris@17 105 *
Chris@17 106 * @param {string} edge
Chris@17 107 * The name of the edge to calculate. Can be 'top', 'right',
Chris@17 108 * 'bottom' or 'left'.
Chris@17 109 *
Chris@17 110 * @return {number}
Chris@17 111 * The viewport displacement distance for the requested edge.
Chris@17 112 */
Chris@17 113 function calculateOffset(edge) {
Chris@17 114 let edgeOffset = 0;
Chris@17 115 const displacingElements = document.querySelectorAll(
Chris@17 116 `[data-offset-${edge}]`,
Chris@17 117 );
Chris@17 118 const n = displacingElements.length;
Chris@17 119 for (let i = 0; i < n; i++) {
Chris@17 120 const el = displacingElements[i];
Chris@17 121 // If the element is not visible, do consider its dimensions.
Chris@17 122 if (el.style.display === 'none') {
Chris@17 123 continue;
Chris@17 124 }
Chris@17 125 // If the offset data attribute contains a displacing value, use it.
Chris@17 126 let displacement = parseInt(el.getAttribute(`data-offset-${edge}`), 10);
Chris@17 127 // If the element's offset data attribute exits
Chris@17 128 // but is not a valid number then get the displacement
Chris@17 129 // dimensions directly from the element.
Chris@17 130 // eslint-disable-next-line no-restricted-globals
Chris@17 131 if (isNaN(displacement)) {
Chris@17 132 displacement = getRawOffset(el, edge);
Chris@17 133 }
Chris@17 134 // If the displacement value is larger than the current value for this
Chris@17 135 // edge, use the displacement value.
Chris@17 136 edgeOffset = Math.max(edgeOffset, displacement);
Chris@17 137 }
Chris@17 138
Chris@17 139 return edgeOffset;
Chris@17 140 }
Chris@17 141
Chris@17 142 /**
Chris@17 143 * Determines the viewport offsets.
Chris@17 144 *
Chris@17 145 * @return {Drupal~displaceOffset}
Chris@17 146 * An object whose keys are the for sides an element -- top, right, bottom
Chris@17 147 * and left. The value of each key is the viewport displacement distance for
Chris@17 148 * that edge.
Chris@17 149 */
Chris@17 150 function calculateOffsets() {
Chris@17 151 return {
Chris@17 152 top: calculateOffset('top'),
Chris@17 153 right: calculateOffset('right'),
Chris@17 154 bottom: calculateOffset('bottom'),
Chris@17 155 left: calculateOffset('left'),
Chris@17 156 };
Chris@17 157 }
Chris@17 158
Chris@17 159 /**
Chris@17 160 * Informs listeners of the current offset dimensions.
Chris@17 161 *
Chris@17 162 * @function Drupal.displace
Chris@17 163 *
Chris@17 164 * @prop {Drupal~displaceOffset} offsets
Chris@17 165 *
Chris@17 166 * @param {bool} [broadcast]
Chris@17 167 * When true or undefined, causes the recalculated offsets values to be
Chris@17 168 * broadcast to listeners.
Chris@17 169 *
Chris@17 170 * @return {Drupal~displaceOffset}
Chris@17 171 * An object whose keys are the for sides an element -- top, right, bottom
Chris@17 172 * and left. The value of each key is the viewport displacement distance for
Chris@17 173 * that edge.
Chris@17 174 *
Chris@17 175 * @fires event:drupalViewportOffsetChange
Chris@17 176 */
Chris@17 177 function displace(broadcast) {
Chris@17 178 offsets = calculateOffsets();
Chris@17 179 Drupal.displace.offsets = offsets;
Chris@17 180 if (typeof broadcast === 'undefined' || broadcast) {
Chris@17 181 $(document).trigger('drupalViewportOffsetChange', offsets);
Chris@17 182 }
Chris@17 183 return offsets;
Chris@17 184 }
Chris@17 185
Chris@17 186 /**
Chris@17 187 * Registers a resize handler on the window.
Chris@17 188 *
Chris@17 189 * @type {Drupal~behavior}
Chris@17 190 */
Chris@17 191 Drupal.behaviors.drupalDisplace = {
Chris@17 192 attach() {
Chris@17 193 // Mark this behavior as processed on the first pass.
Chris@17 194 if (this.displaceProcessed) {
Chris@17 195 return;
Chris@17 196 }
Chris@17 197 this.displaceProcessed = true;
Chris@17 198
Chris@17 199 $(window).on('resize.drupalDisplace', debounce(displace, 200));
Chris@17 200 },
Chris@17 201 };
Chris@17 202
Chris@17 203 /**
Chris@0 204 * Assign the displace function to a property of the Drupal global object.
Chris@0 205 *
Chris@0 206 * @ignore
Chris@0 207 */
Chris@0 208 Drupal.displace = displace;
Chris@0 209 $.extend(Drupal.displace, {
Chris@0 210 /**
Chris@0 211 * Expose offsets to other scripts to avoid having to recalculate offsets.
Chris@0 212 *
Chris@0 213 * @ignore
Chris@0 214 */
Chris@0 215 offsets,
Chris@0 216
Chris@0 217 /**
Chris@0 218 * Expose method to compute a single edge offsets.
Chris@0 219 *
Chris@0 220 * @ignore
Chris@0 221 */
Chris@0 222 calculateOffset,
Chris@0 223 });
Chris@17 224 })(jQuery, Drupal, Drupal.debounce);