comparison core/misc/displace.es6.js @ 0:c75dbcec494b

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