@carbon/ibmdotcom-utilities 2.10.0-canary.9273569646.0 → 2.10.0-canary.9274459085.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,20 +21,32 @@ var StickyHeader = /*#__PURE__*/function () {
21
21
  function StickyHeader() {
22
22
  _classCallCheck(this, StickyHeader);
23
23
  this.ownerDocument = root.document;
24
- this._banner = undefined;
25
- this._cumulativeHeight = 0;
26
- this._hasBanner = false;
27
- this._lastScrollPosition = 0;
28
- this._leadspaceWithSearch = undefined;
29
- this._leadspaceSearchBar = undefined;
30
- this._leadspaceWithSearchStickyThreshold = 0;
31
- this._localeModal = undefined;
32
- this._masthead = undefined;
33
- this._mastheadL0 = undefined;
34
- this._mastheadL1 = undefined;
35
- this._tableOfContents = undefined;
36
- this._tableOfContentsInnerBar = undefined;
37
- this._tableOfContentsLayout = undefined;
24
+ this._state = {
25
+ cumulativeOffset: 0,
26
+ hasBanner: false,
27
+ leadspaceSearchThreshold: 0,
28
+ mastheadL0IsActive: false,
29
+ mastheadL1IsActive: false,
30
+ maxScrollaway: 0,
31
+ scrollPosPrevious: 0,
32
+ scrollPos: 0,
33
+ searchIsAtTop: false,
34
+ tocShouldStick: false,
35
+ tocIsAtTop: false,
36
+ tocIsAtSearch: false
37
+ };
38
+ this._elements = {
39
+ banner: undefined,
40
+ leadspaceSearch: undefined,
41
+ leadspaceSearchBar: undefined,
42
+ leadspaceSearchInput: undefined,
43
+ localeModal: undefined,
44
+ masthead: undefined,
45
+ mastheadL0: undefined,
46
+ mastheadL1: undefined,
47
+ tableOfContents: undefined,
48
+ tableOfContentsInnerBar: undefined
49
+ };
38
50
  this._throttled = false;
39
51
  this._resizeObserver = new ResizeObserver(this._handleResize.bind(this));
40
52
  root.addEventListener('scroll', this._throttledHandler.bind(this));
@@ -46,7 +58,7 @@ var StickyHeader = /*#__PURE__*/function () {
46
58
  return _createClass(StickyHeader, [{
47
59
  key: "height",
48
60
  get: function get() {
49
- return this._cumulativeHeight;
61
+ return this._state.cumulativeOffset;
50
62
  }
51
63
 
52
64
  /**
@@ -67,73 +79,71 @@ var StickyHeader = /*#__PURE__*/function () {
67
79
  return true;
68
80
  }
69
81
  }
82
+
83
+ /**
84
+ * Stores references to TOC sub-elements that are relevant to current viewport
85
+ * dimensions.
86
+ */
70
87
  }, {
71
- key: "_tableOfContentsStickyUpdate",
72
- value: function _tableOfContentsStickyUpdate() {
73
- var toc = this._tableOfContents;
88
+ key: "_updateTableOfContentsRefs",
89
+ value: function _updateTableOfContentsRefs() {
90
+ var toc = this._elements.tableOfContents;
74
91
  var tocRoot = toc.shadowRoot;
75
- this._tableOfContentsInnerBar = tocRoot.querySelector(".".concat(prefix, "--tableofcontents__navbar"));
76
- if (window.innerWidth > gridBreakpoint) {
77
- if (toc.layout === 'horizontal') {
78
- this._tableOfContentsLayout = 'horizontal';
79
- } else {
80
- this._tableOfContentsInnerBar = tocRoot.querySelector(".".concat(c4dPrefix, "-ce--table-of-contents__items-container"));
81
- }
82
- }
92
+ this._elements.tableOfContentsInnerBar = tocRoot.querySelector(window.innerWidth >= gridBreakpoint && (toc === null || toc === void 0 ? void 0 : toc.layout) !== 'horizontal' ? ".".concat(c4dPrefix, "-ce--table-of-contents__items-container") : ".".concat(prefix, "--tableofcontents__navbar"));
83
93
  }
84
94
  }, {
85
95
  key: "banner",
86
96
  set: function set(component) {
87
97
  if (this._validateComponent(component, "".concat(c4dPrefix, "-global-banner"))) {
88
- this._banner = component;
89
- this.hasBanner = true;
90
- if (this._masthead) {
91
- this._masthead.setAttribute('with-banner', '');
98
+ this._elements.banner = component;
99
+ this._state.hasBanner = true;
100
+ if (this._elements.masthead) {
101
+ this._elements.masthead.setAttribute('with-banner', '');
92
102
  }
93
- this._calculateCumulativeHeight();
103
+ this._manageStickyElements();
94
104
  }
95
105
  }
96
106
  }, {
97
- key: "leadspaceWithSearch",
107
+ key: "leadspaceSearch",
98
108
  set: function set(component) {
99
109
  if (this._validateComponent(component, "".concat(c4dPrefix, "-leadspace-with-search"))) {
100
- this._leadspaceWithSearch = component;
110
+ this._elements.leadspaceSearch = component;
101
111
  var leadspaceSearchBar = component.shadowRoot.querySelector(".".concat(prefix, "--search-container"));
102
- this._leadspaceSearchBar = leadspaceSearchBar;
103
- this._leadspaceWithSearchInput = component.querySelector("".concat(c4dPrefix, "-search-with-typeahead"));
104
- this._leadspaceWithSearchStickyThreshold = parseInt(window.getComputedStyle(leadspaceSearchBar).paddingBottom) - 16;
105
- this._calculateCumulativeHeight();
112
+ this._elements.leadspaceSearchBar = leadspaceSearchBar;
113
+ this._elements.leadspaceSearchInput = component.querySelector("".concat(c4dPrefix, "-search-with-typeahead"));
114
+ this._state.leadspaceSearchThreshold = parseInt(window.getComputedStyle(leadspaceSearchBar).paddingBottom) - 16;
115
+ this._manageStickyElements();
106
116
  }
107
117
  }
108
118
  }, {
109
119
  key: "localeModal",
110
120
  set: function set(component) {
111
121
  if (this._validateComponent(component, "".concat(c4dPrefix, "-locale-modal"))) {
112
- this._localeModal = component;
113
- this._calculateCumulativeHeight();
122
+ this._elements.localeModal = component;
123
+ this._manageStickyElements();
114
124
  }
115
125
  }
116
126
  }, {
117
127
  key: "masthead",
118
128
  set: function set(component) {
119
129
  if (this._validateComponent(component, "".concat(c4dPrefix, "-masthead"))) {
120
- this._masthead = component;
121
- if (this._banner) {
122
- this._masthead.setAttribute('with-banner', '');
130
+ this._elements.masthead = component;
131
+ if (this._elements.banner) {
132
+ this._elements.masthead.setAttribute('with-banner', '');
123
133
  }
124
- this._mastheadL0 = component.shadowRoot.querySelector(".".concat(prefix, "--masthead__l0"));
125
- this._mastheadL1 = component.querySelector("".concat(c4dPrefix, "-masthead-l1"));
126
- this._calculateCumulativeHeight();
134
+ this._elements.mastheadL0 = component.shadowRoot.querySelector(".".concat(prefix, "--masthead__l0"));
135
+ this._elements.mastheadL1 = component.querySelector("".concat(c4dPrefix, "-masthead-l1"));
136
+ this._manageStickyElements();
127
137
  }
128
138
  }
129
139
  }, {
130
140
  key: "tableOfContents",
131
141
  set: function set(component) {
132
142
  if (this._validateComponent(component, "".concat(c4dPrefix, "-table-of-contents"))) {
133
- this._tableOfContents = component;
134
- this._tableOfContentsStickyUpdate();
135
- this._resizeObserver.observe(this._tableOfContents);
136
- this._calculateCumulativeHeight();
143
+ this._elements.tableOfContents = component;
144
+ this._updateTableOfContentsRefs();
145
+ this._resizeObserver.observe(this._elements.tableOfContents);
146
+ this._manageStickyElements();
137
147
  }
138
148
  }
139
149
 
@@ -146,7 +156,7 @@ var StickyHeader = /*#__PURE__*/function () {
146
156
  var _this = this;
147
157
  if (!this._throttled) {
148
158
  this._throttled = true;
149
- this._calculateCumulativeHeight();
159
+ this._manageStickyElements();
150
160
  setTimeout(function () {
151
161
  _this._throttled = false;
152
162
  }, 20);
@@ -155,95 +165,197 @@ var StickyHeader = /*#__PURE__*/function () {
155
165
  }, {
156
166
  key: "_handleResize",
157
167
  value: function _handleResize() {
158
- var hasBanner = this._hasBanner,
159
- masthead = this._masthead,
160
- toc = this._tableOfContents,
161
- tocLayout = this._tableOfContentsLayout,
162
- leadspaceSearchBar = this._leadspaceSearchBar;
168
+ var hasBanner = this._state._hasBanner;
169
+ var _this$_elements = this._elements,
170
+ masthead = _this$_elements.masthead,
171
+ toc = _this$_elements.tableOfContents,
172
+ leadspaceSearchBar = _this$_elements.leadspaceSearchBar;
163
173
  if (toc && masthead) {
164
- this._tableOfContentsStickyUpdate();
165
- if (window.innerWidth >= gridBreakpoint && tocLayout !== 'horizontal' && !hasBanner) {
166
- masthead.style.top = '0';
174
+ this._updateTableOfContentsRefs();
175
+ if (window.innerWidth >= gridBreakpoint && toc.layout !== 'horizontal' && !hasBanner) {
176
+ masthead.style.insetBlockStart = '0';
167
177
  } else {
168
- // This has to happen after the tocStickyUpdate method.
169
- var tocInner = this._tableOfContentsInnerBar;
178
+ // This has to happen after the _updateTableOfContentsRefs method.
179
+ var tocInner = this._elements.tableOfContentsInnerBar;
170
180
  if (masthead.offsetTop === 0) {
171
- tocInner.style.top = "".concat(masthead.offsetHeight, "px");
181
+ tocInner.style.insetBlockStart = "".concat(masthead.offsetHeight, "px");
172
182
  }
173
183
  }
174
- this._calculateCumulativeHeight();
184
+ this._manageStickyElements();
175
185
  }
176
186
  if (leadspaceSearchBar) {
177
- this._leadspaceWithSearchStickyThreshold = parseInt(window.getComputedStyle(leadspaceSearchBar).paddingBottom) - 16;
187
+ this._state.leadspaceSearchThreshold = parseInt(window.getComputedStyle(leadspaceSearchBar).paddingBottom) - 16;
178
188
  }
179
189
  }
190
+
191
+ /**
192
+ * Handles the banner given the current scroll position.
193
+ */
180
194
  }, {
181
- key: "_calculateCumulativeHeight",
182
- value: function _calculateCumulativeHeight() {
183
- var _StickyHeader$global = StickyHeader.global,
184
- oldY = _StickyHeader$global._lastScrollPosition,
185
- banner = _StickyHeader$global._banner,
186
- masthead = _StickyHeader$global._masthead,
187
- mastheadL0 = _StickyHeader$global._mastheadL0,
188
- mastheadL1 = _StickyHeader$global._mastheadL1,
189
- localeModal = _StickyHeader$global._localeModal,
190
- toc = _StickyHeader$global._tableOfContents,
191
- tocInner = _StickyHeader$global._tableOfContentsInnerBar,
192
- leadspaceSearch = _StickyHeader$global._leadspaceWithSearch,
193
- leadspaceSearchBar = _StickyHeader$global._leadspaceSearchBar,
194
- leadspaceSearchInput = _StickyHeader$global._leadspaceWithSearchInput,
195
- leadspaceSearchThreshold = _StickyHeader$global._leadspaceWithSearchStickyThreshold;
196
- var customPropertyName = this.constructor.customPropertyName;
197
- if (localeModal && localeModal.hasAttribute('open')) {
198
- return;
199
- }
200
- var newY = window.scrollY;
201
- this._lastScrollPosition = Math.max(0, newY);
195
+ key: "_handleBanner",
196
+ value: function _handleBanner() {
197
+ var banner = this._elements.banner;
198
+ var scrollPos = this._state.scrollPos;
199
+ this._state.cumulativeOffset += Math.max(banner.offsetHeight - scrollPos, 0);
200
+ }
202
201
 
203
- /**
204
- * maxScrollaway is a calculated value matching the height of all components
205
- * that are allowed to hide above the viewport.
206
- *
207
- * We should only have one sticky header showing as the page scrolls down.
208
- *
209
- * Items that stick, in order
210
- * - L0
211
- * - L1
212
- * - The TOC in horizontal bar form
213
- * - The leadspace with search (if no TOC)
214
- */
215
- var maxScrollaway = 0;
202
+ /**
203
+ * Handles the masthead given the current scroll position.
204
+ */
205
+ }, {
206
+ key: "_handleMasthead",
207
+ value: function _handleMasthead() {
208
+ var masthead = this._elements.masthead;
209
+ masthead.style.transition = 'none';
210
+ masthead.style.insetBlockStart = "".concat(this._state.cumulativeOffset, "px");
216
211
 
217
- // Calculate maxScrollaway values based on TOC positon
218
- var tocIsAtTop = false;
219
- var tocShouldStick = false;
220
- if (tocInner) {
221
- tocIsAtTop = tocInner.getBoundingClientRect().top <= (masthead ? masthead.offsetTop + masthead.offsetHeight : 0) + 1;
222
- tocShouldStick = toc.layout === 'horizontal' || window.innerWidth < gridBreakpoint;
223
- if (masthead && tocIsAtTop && (tocShouldStick || mastheadL1)) {
224
- maxScrollaway += masthead.offsetHeight;
225
- if (mastheadL1 && !tocShouldStick) {
226
- maxScrollaway -= mastheadL1.offsetHeight;
227
- }
228
- } else if (mastheadL0 && mastheadL1) {
229
- maxScrollaway += mastheadL0.offsetHeight;
212
+ // Masthead always sticks, therefore always add its height.
213
+ this._state.cumulativeOffset += masthead.offsetHeight;
214
+ }
215
+
216
+ /**
217
+ * Handles the table of contents given the current scroll position.
218
+ */
219
+ }, {
220
+ key: "_handleToc",
221
+ value: function _handleToc() {
222
+ var tableOfContentsInnerBar = this._elements.tableOfContentsInnerBar;
223
+ var tocShouldStick = this._state.tocShouldStick;
224
+ tableOfContentsInnerBar.style.transition = 'none';
225
+ tableOfContentsInnerBar.style.insetBlockStart = "".concat(this._state.cumulativeOffset, "px");
226
+ var tocIsStuck = Math.round(tableOfContentsInnerBar.getBoundingClientRect().top) <= this._state.cumulativeOffset + 1;
227
+ if (tocShouldStick && tocIsStuck) {
228
+ this._state.cumulativeOffset += tableOfContentsInnerBar.offsetHeight;
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Handles the leadspace search given the current scroll position.
234
+ */
235
+ }, {
236
+ key: "_handleLeadspaceSearch",
237
+ value: function _handleLeadspaceSearch() {
238
+ var _this$_elements2 = this._elements,
239
+ leadspaceSearch = _this$_elements2.leadspaceSearch,
240
+ leadspaceSearchBar = _this$_elements2.leadspaceSearchBar,
241
+ leadspaceSearchInput = _this$_elements2.leadspaceSearchInput;
242
+ var leadspaceSearchThreshold = this._state.leadspaceSearchThreshold;
243
+ var searchShouldBeSticky = leadspaceSearch.getBoundingClientRect().bottom <= leadspaceSearchThreshold;
244
+ var searchIsSticky = leadspaceSearch.hasAttribute('sticky-search');
245
+ if (searchShouldBeSticky) {
246
+ if (!searchIsSticky) {
247
+ leadspaceSearch.style.paddingBottom = "".concat(leadspaceSearchBar.offsetHeight, "px");
248
+ leadspaceSearch.setAttribute('sticky-search', '');
249
+ leadspaceSearchInput.setAttribute('large', '');
250
+ window.requestAnimationFrame(function () {
251
+ leadspaceSearchBar.style.transitionDuration = '110ms';
252
+ leadspaceSearchBar.style.transform = 'translateY(0)';
253
+ });
230
254
  }
255
+ leadspaceSearchBar.style.insetBlockStart = "".concat(this._state.cumulativeOffset, "px");
256
+ this._state.cumulativeOffset += leadspaceSearchBar.offsetHeight;
257
+ } else if (searchIsSticky) {
258
+ leadspaceSearch.style.paddingBottom = '';
259
+ leadspaceSearch.removeAttribute('sticky-search');
260
+ leadspaceSearchInput.removeAttribute('large');
261
+ leadspaceSearchBar.style.transitionDuration = '';
262
+ leadspaceSearchBar.style.transform = '';
263
+ leadspaceSearchBar.style.insetBlockStart = '';
231
264
  }
265
+ }
232
266
 
233
- // Calculate maxScrollaway values based on leadspace search position
234
- if (!tocInner && leadspaceSearchBar) {
235
- var searchIsAtTop = leadspaceSearchBar.getBoundingClientRect().top <= (masthead ? masthead.offsetTop + masthead.offsetHeight : 0) + 1;
236
- if (masthead && searchIsAtTop) {
237
- maxScrollaway += masthead.offsetHeight;
267
+ /**
268
+ * Calculates a value matching the height of all components that are allowed
269
+ * to hide above the viewport.
270
+ *
271
+ * Adding an item's height to this value indicates we expect it to be hidden
272
+ * above the viewport.
273
+ *
274
+ * Items that stick, in order
275
+ * - L0
276
+ * - L1
277
+ * - The TOC in horizontal bar form
278
+ * - The leadspace with search (if no TOC)
279
+ */
280
+ }, {
281
+ key: "_calculateMaxScrollaway",
282
+ value: function _calculateMaxScrollaway() {
283
+ var _this$_elements3 = this._elements,
284
+ masthead = _this$_elements3.masthead,
285
+ mastheadL0 = _this$_elements3.mastheadL0,
286
+ mastheadL1 = _this$_elements3.mastheadL1,
287
+ tableOfContents = _this$_elements3.tableOfContents,
288
+ tableOfContentsInnerBar = _this$_elements3.tableOfContentsInnerBar,
289
+ leadspaceSearchBar = _this$_elements3.leadspaceSearchBar;
290
+
291
+ // Reset the value before performing any further calculations.
292
+ this._state.maxScrollaway = 0;
293
+
294
+ // Collect conditions we may want to test for to make logic easier to read.
295
+ this._state.tocShouldStick = tableOfContents ? tableOfContents.layout === 'horizontal' || window.innerWidth < gridBreakpoint : false;
296
+ this._state.tocIsAtTop = tableOfContentsInnerBar ? tableOfContentsInnerBar.getBoundingClientRect().top <= this.height + 1 : false;
297
+ this._state.searchIsAtTop = leadspaceSearchBar ? leadspaceSearchBar.getBoundingClientRect().top <= this.height + 1 : false;
298
+ this._state.tocIsAtSearch = leadspaceSearchBar && tableOfContentsInnerBar ? tableOfContentsInnerBar.getBoundingClientRect().top <= leadspaceSearchBar.getBoundingClientRect().bottom : false;
299
+ this._state.mastheadL0IsActive = Boolean(masthead === null || masthead === void 0 ? void 0 : masthead.querySelector('[expanded]'));
300
+ this._state.mastheadL1IsActive = mastheadL1 && mastheadL1.hasAttribute('active');
301
+ var _this$_state = this._state,
302
+ tocShouldStick = _this$_state.tocShouldStick,
303
+ tocIsAtTop = _this$_state.tocIsAtTop,
304
+ searchIsAtTop = _this$_state.searchIsAtTop,
305
+ tocIsAtSearch = _this$_state.tocIsAtSearch,
306
+ mastheadL0IsActive = _this$_state.mastheadL0IsActive,
307
+ mastheadL1IsActive = _this$_state.mastheadL1IsActive;
308
+
309
+ // Begin calculating maxScrollAway.
310
+
311
+ // If L0 is open, lock it to the top of the page.
312
+ if (mastheadL0 && mastheadL0IsActive) {
313
+ this._state.maxScrollaway = 0;
314
+ }
315
+ // If L1 is open, lock it to the top of the page.
316
+ else if (mastheadL1IsActive && mastheadL0) {
317
+ this._state.maxScrollaway = mastheadL0.offsetHeight;
318
+ } else {
319
+ // In cases where we have both an eligible ToC and leadspace search, we want
320
+ // the ToC to take precedence. Scroll away leadspace search.
321
+ if (searchIsAtTop && tocIsAtSearch && tocShouldStick) {
322
+ this._state.maxScrollaway += leadspaceSearchBar.offsetHeight;
323
+ }
324
+
325
+ // Scroll away entire masthead if either ToC or leadspace search is eligible
326
+ // to be the stuck element (unless L1 is open). Otherwise, scroll away the
327
+ // L0 if we have an L1.
328
+ if (searchIsAtTop || tocIsAtTop && tocShouldStick) {
329
+ if (masthead) {
330
+ this._state.maxScrollaway += masthead.offsetHeight;
331
+ }
332
+ } else if (masthead && mastheadL0 && mastheadL1) {
333
+ this._state.maxScrollaway += mastheadL0.offsetHeight;
238
334
  }
239
335
  }
336
+ }
337
+
338
+ /**
339
+ * Positions sticky elements. Does so by checking the scroll position and where
340
+ * tracked elements are in relation to it, then applying the correct styles to
341
+ * each element in succession to ensure that only one element is stuck to the
342
+ * top of the page, and all other elements that have been scrolled past can be
343
+ * revealed when scrolling back up.
344
+ */
345
+ }, {
346
+ key: "_positionElements",
347
+ value: function _positionElements() {
348
+ var _this$_elements4 = this._elements,
349
+ banner = _this$_elements4.banner,
350
+ masthead = _this$_elements4.masthead,
351
+ tocInner = _this$_elements4.tableOfContentsInnerBar,
352
+ leadspaceSearchBar = _this$_elements4.leadspaceSearchBar;
353
+ var oldY = this._state.scrollPosPrevious;
240
354
 
241
355
  /**
242
- * Cumulative offset is a calculated value used to set the `top` property of
243
- * components that stick to the top of the viewport.
244
- *
245
- * This value is equal to the difference between the previous scrollY and
246
- * the current scrollY values, but is positively and negatively limited.
356
+ * Reset to a value that is equal to the difference between the previous
357
+ * scrollY and the current scrollY values, but is positively and negatively
358
+ * limited.
247
359
  *
248
360
  * Positive limit: 0
249
361
  * all elements visible, starting at the top of the viewport.
@@ -253,55 +365,58 @@ var StickyHeader = /*#__PURE__*/function () {
253
365
  * with the elements that should be visible starting at the top of the
254
366
  * viewport.
255
367
  */
256
- var cumulativeOffset = Math.max(Math.min((masthead ? masthead.offsetTop : 0) + oldY - newY, 0), maxScrollaway * -1);
368
+ this._state.cumulativeOffset = Math.max(Math.min((masthead ? masthead.offsetTop : 0) + oldY - this._state.scrollPos, 0), this._state.maxScrollaway * -1);
369
+
370
+ /**
371
+ * Handle each potentially sticky element in the order we expect them to
372
+ * appear on the page. Important to do this sequentially for
373
+ * cumulativeOffset to be correctly calculated by the time each of these
374
+ * methods accesses it.
375
+ *
376
+ * To-do: One idea for improving this so the execution order doesn't matter
377
+ * is to collect our elements into an array ordered by document position,
378
+ * then loop over that array and execute a corresponding handler method.
379
+ */
257
380
  if (banner) {
258
- cumulativeOffset += Math.max(banner.offsetHeight - newY, 0);
381
+ this._handleBanner();
259
382
  }
260
383
  if (masthead) {
261
- masthead.style.transition = 'none';
262
- masthead.style.top = "".concat(cumulativeOffset, "px");
263
- cumulativeOffset += masthead.offsetHeight;
384
+ this._handleMasthead();
385
+ }
386
+ if (leadspaceSearchBar) {
387
+ this._handleLeadspaceSearch();
264
388
  }
265
389
  if (tocInner) {
266
- tocInner.style.transition = 'none';
267
- tocInner.style.top = "".concat(cumulativeOffset, "px");
268
- tocShouldStick = toc.layout === 'horizontal' || window.innerWidth < gridBreakpoint;
269
- var tocIsStuck = Math.round(tocInner.getBoundingClientRect().top) <= cumulativeOffset + 1;
270
- if (tocShouldStick && tocIsStuck) {
271
- cumulativeOffset += tocInner.offsetHeight;
272
- }
390
+ this._handleToc();
273
391
  }
274
- if (!tocInner && leadspaceSearchBar) {
275
- var searchShouldBeSticky = leadspaceSearch.getBoundingClientRect().bottom <= leadspaceSearchThreshold;
276
- var searchIsSticky = leadspaceSearch.hasAttribute('sticky-search');
277
- if (searchShouldBeSticky) {
278
- if (!searchIsSticky) {
279
- leadspaceSearch.style.paddingBottom = "".concat(leadspaceSearchBar.offsetHeight, "px");
280
- leadspaceSearch.setAttribute('sticky-search', '');
281
- leadspaceSearchInput.setAttribute('large', '');
282
- window.requestAnimationFrame(function () {
283
- leadspaceSearchBar.style.transitionDuration = '110ms';
284
- leadspaceSearchBar.style.transform = 'translateY(0)';
285
- });
286
- }
287
- leadspaceSearchBar.style.top = "".concat(cumulativeOffset, "px");
288
- cumulativeOffset += leadspaceSearchBar.offsetHeight;
289
- }
290
- if (!searchShouldBeSticky && searchIsSticky) {
291
- leadspaceSearch.removeAttribute('sticky-search');
292
- leadspaceSearch.style.paddingBottom = '';
293
- leadspaceSearchBar.style.top = '';
294
- leadspaceSearchBar.style.transitionDuration = '';
295
- leadspaceSearchBar.style.transform = '';
296
- leadspaceSearchInput.removeAttribute('large');
297
- }
392
+ }
393
+
394
+ /**
395
+ * Manages which elements are stuck and where they are positioned. We should
396
+ * only have one element stuck to the top of the viewport as the page scrolls
397
+ * down.
398
+ */
399
+ }, {
400
+ key: "_manageStickyElements",
401
+ value: function _manageStickyElements() {
402
+ var localeModal = this._elements.localeModal;
403
+ var scrollPosPrevious = this._state.scrollPos;
404
+
405
+ // Exit early if locale modal is open.
406
+ if (localeModal && localeModal.hasAttribute('open')) {
407
+ return;
298
408
  }
299
409
 
300
- // Set internal property for use in scripts
301
- this._cumulativeHeight = cumulativeOffset;
410
+ // Store scroll positions.
411
+ this._state.scrollPosPrevious = scrollPosPrevious;
412
+ this._state.scrollPos = Math.max(0, window.scrollY);
413
+
414
+ // Given the current state, calculate how elements should be positioned.
415
+ this._calculateMaxScrollaway();
416
+ this._positionElements();
302
417
 
303
418
  // Set custom property for use in stylesheets
304
- root.document.documentElement.style.setProperty(customPropertyName, "".concat(this._cumulativeHeight, "px"));
419
+ root.document.documentElement.style.setProperty(this.constructor.customPropertyName, "".concat(this._state.cumulativeOffset, "px"));
305
420
  }
306
421
  }], [{
307
422
  key: "global",
@@ -26,20 +26,32 @@ var StickyHeader = /*#__PURE__*/function () {
26
26
  function StickyHeader() {
27
27
  _classCallCheck(this, StickyHeader);
28
28
  this.ownerDocument = _windowOrGlobal.default.document;
29
- this._banner = undefined;
30
- this._cumulativeHeight = 0;
31
- this._hasBanner = false;
32
- this._lastScrollPosition = 0;
33
- this._leadspaceWithSearch = undefined;
34
- this._leadspaceSearchBar = undefined;
35
- this._leadspaceWithSearchStickyThreshold = 0;
36
- this._localeModal = undefined;
37
- this._masthead = undefined;
38
- this._mastheadL0 = undefined;
39
- this._mastheadL1 = undefined;
40
- this._tableOfContents = undefined;
41
- this._tableOfContentsInnerBar = undefined;
42
- this._tableOfContentsLayout = undefined;
29
+ this._state = {
30
+ cumulativeOffset: 0,
31
+ hasBanner: false,
32
+ leadspaceSearchThreshold: 0,
33
+ mastheadL0IsActive: false,
34
+ mastheadL1IsActive: false,
35
+ maxScrollaway: 0,
36
+ scrollPosPrevious: 0,
37
+ scrollPos: 0,
38
+ searchIsAtTop: false,
39
+ tocShouldStick: false,
40
+ tocIsAtTop: false,
41
+ tocIsAtSearch: false
42
+ };
43
+ this._elements = {
44
+ banner: undefined,
45
+ leadspaceSearch: undefined,
46
+ leadspaceSearchBar: undefined,
47
+ leadspaceSearchInput: undefined,
48
+ localeModal: undefined,
49
+ masthead: undefined,
50
+ mastheadL0: undefined,
51
+ mastheadL1: undefined,
52
+ tableOfContents: undefined,
53
+ tableOfContentsInnerBar: undefined
54
+ };
43
55
  this._throttled = false;
44
56
  this._resizeObserver = new ResizeObserver(this._handleResize.bind(this));
45
57
  _windowOrGlobal.default.addEventListener('scroll', this._throttledHandler.bind(this));
@@ -51,7 +63,7 @@ var StickyHeader = /*#__PURE__*/function () {
51
63
  return _createClass(StickyHeader, [{
52
64
  key: "height",
53
65
  get: function get() {
54
- return this._cumulativeHeight;
66
+ return this._state.cumulativeOffset;
55
67
  }
56
68
 
57
69
  /**
@@ -72,73 +84,71 @@ var StickyHeader = /*#__PURE__*/function () {
72
84
  return true;
73
85
  }
74
86
  }
87
+
88
+ /**
89
+ * Stores references to TOC sub-elements that are relevant to current viewport
90
+ * dimensions.
91
+ */
75
92
  }, {
76
- key: "_tableOfContentsStickyUpdate",
77
- value: function _tableOfContentsStickyUpdate() {
78
- var toc = this._tableOfContents;
93
+ key: "_updateTableOfContentsRefs",
94
+ value: function _updateTableOfContentsRefs() {
95
+ var toc = this._elements.tableOfContents;
79
96
  var tocRoot = toc.shadowRoot;
80
- this._tableOfContentsInnerBar = tocRoot.querySelector(".".concat(prefix, "--tableofcontents__navbar"));
81
- if (window.innerWidth > gridBreakpoint) {
82
- if (toc.layout === 'horizontal') {
83
- this._tableOfContentsLayout = 'horizontal';
84
- } else {
85
- this._tableOfContentsInnerBar = tocRoot.querySelector(".".concat(c4dPrefix, "-ce--table-of-contents__items-container"));
86
- }
87
- }
97
+ this._elements.tableOfContentsInnerBar = tocRoot.querySelector(window.innerWidth >= gridBreakpoint && (toc === null || toc === void 0 ? void 0 : toc.layout) !== 'horizontal' ? ".".concat(c4dPrefix, "-ce--table-of-contents__items-container") : ".".concat(prefix, "--tableofcontents__navbar"));
88
98
  }
89
99
  }, {
90
100
  key: "banner",
91
101
  set: function set(component) {
92
102
  if (this._validateComponent(component, "".concat(c4dPrefix, "-global-banner"))) {
93
- this._banner = component;
94
- this.hasBanner = true;
95
- if (this._masthead) {
96
- this._masthead.setAttribute('with-banner', '');
103
+ this._elements.banner = component;
104
+ this._state.hasBanner = true;
105
+ if (this._elements.masthead) {
106
+ this._elements.masthead.setAttribute('with-banner', '');
97
107
  }
98
- this._calculateCumulativeHeight();
108
+ this._manageStickyElements();
99
109
  }
100
110
  }
101
111
  }, {
102
- key: "leadspaceWithSearch",
112
+ key: "leadspaceSearch",
103
113
  set: function set(component) {
104
114
  if (this._validateComponent(component, "".concat(c4dPrefix, "-leadspace-with-search"))) {
105
- this._leadspaceWithSearch = component;
115
+ this._elements.leadspaceSearch = component;
106
116
  var leadspaceSearchBar = component.shadowRoot.querySelector(".".concat(prefix, "--search-container"));
107
- this._leadspaceSearchBar = leadspaceSearchBar;
108
- this._leadspaceWithSearchInput = component.querySelector("".concat(c4dPrefix, "-search-with-typeahead"));
109
- this._leadspaceWithSearchStickyThreshold = parseInt(window.getComputedStyle(leadspaceSearchBar).paddingBottom) - 16;
110
- this._calculateCumulativeHeight();
117
+ this._elements.leadspaceSearchBar = leadspaceSearchBar;
118
+ this._elements.leadspaceSearchInput = component.querySelector("".concat(c4dPrefix, "-search-with-typeahead"));
119
+ this._state.leadspaceSearchThreshold = parseInt(window.getComputedStyle(leadspaceSearchBar).paddingBottom) - 16;
120
+ this._manageStickyElements();
111
121
  }
112
122
  }
113
123
  }, {
114
124
  key: "localeModal",
115
125
  set: function set(component) {
116
126
  if (this._validateComponent(component, "".concat(c4dPrefix, "-locale-modal"))) {
117
- this._localeModal = component;
118
- this._calculateCumulativeHeight();
127
+ this._elements.localeModal = component;
128
+ this._manageStickyElements();
119
129
  }
120
130
  }
121
131
  }, {
122
132
  key: "masthead",
123
133
  set: function set(component) {
124
134
  if (this._validateComponent(component, "".concat(c4dPrefix, "-masthead"))) {
125
- this._masthead = component;
126
- if (this._banner) {
127
- this._masthead.setAttribute('with-banner', '');
135
+ this._elements.masthead = component;
136
+ if (this._elements.banner) {
137
+ this._elements.masthead.setAttribute('with-banner', '');
128
138
  }
129
- this._mastheadL0 = component.shadowRoot.querySelector(".".concat(prefix, "--masthead__l0"));
130
- this._mastheadL1 = component.querySelector("".concat(c4dPrefix, "-masthead-l1"));
131
- this._calculateCumulativeHeight();
139
+ this._elements.mastheadL0 = component.shadowRoot.querySelector(".".concat(prefix, "--masthead__l0"));
140
+ this._elements.mastheadL1 = component.querySelector("".concat(c4dPrefix, "-masthead-l1"));
141
+ this._manageStickyElements();
132
142
  }
133
143
  }
134
144
  }, {
135
145
  key: "tableOfContents",
136
146
  set: function set(component) {
137
147
  if (this._validateComponent(component, "".concat(c4dPrefix, "-table-of-contents"))) {
138
- this._tableOfContents = component;
139
- this._tableOfContentsStickyUpdate();
140
- this._resizeObserver.observe(this._tableOfContents);
141
- this._calculateCumulativeHeight();
148
+ this._elements.tableOfContents = component;
149
+ this._updateTableOfContentsRefs();
150
+ this._resizeObserver.observe(this._elements.tableOfContents);
151
+ this._manageStickyElements();
142
152
  }
143
153
  }
144
154
 
@@ -151,7 +161,7 @@ var StickyHeader = /*#__PURE__*/function () {
151
161
  var _this = this;
152
162
  if (!this._throttled) {
153
163
  this._throttled = true;
154
- this._calculateCumulativeHeight();
164
+ this._manageStickyElements();
155
165
  setTimeout(function () {
156
166
  _this._throttled = false;
157
167
  }, 20);
@@ -160,95 +170,197 @@ var StickyHeader = /*#__PURE__*/function () {
160
170
  }, {
161
171
  key: "_handleResize",
162
172
  value: function _handleResize() {
163
- var hasBanner = this._hasBanner,
164
- masthead = this._masthead,
165
- toc = this._tableOfContents,
166
- tocLayout = this._tableOfContentsLayout,
167
- leadspaceSearchBar = this._leadspaceSearchBar;
173
+ var hasBanner = this._state._hasBanner;
174
+ var _this$_elements = this._elements,
175
+ masthead = _this$_elements.masthead,
176
+ toc = _this$_elements.tableOfContents,
177
+ leadspaceSearchBar = _this$_elements.leadspaceSearchBar;
168
178
  if (toc && masthead) {
169
- this._tableOfContentsStickyUpdate();
170
- if (window.innerWidth >= gridBreakpoint && tocLayout !== 'horizontal' && !hasBanner) {
171
- masthead.style.top = '0';
179
+ this._updateTableOfContentsRefs();
180
+ if (window.innerWidth >= gridBreakpoint && toc.layout !== 'horizontal' && !hasBanner) {
181
+ masthead.style.insetBlockStart = '0';
172
182
  } else {
173
- // This has to happen after the tocStickyUpdate method.
174
- var tocInner = this._tableOfContentsInnerBar;
183
+ // This has to happen after the _updateTableOfContentsRefs method.
184
+ var tocInner = this._elements.tableOfContentsInnerBar;
175
185
  if (masthead.offsetTop === 0) {
176
- tocInner.style.top = "".concat(masthead.offsetHeight, "px");
186
+ tocInner.style.insetBlockStart = "".concat(masthead.offsetHeight, "px");
177
187
  }
178
188
  }
179
- this._calculateCumulativeHeight();
189
+ this._manageStickyElements();
180
190
  }
181
191
  if (leadspaceSearchBar) {
182
- this._leadspaceWithSearchStickyThreshold = parseInt(window.getComputedStyle(leadspaceSearchBar).paddingBottom) - 16;
192
+ this._state.leadspaceSearchThreshold = parseInt(window.getComputedStyle(leadspaceSearchBar).paddingBottom) - 16;
183
193
  }
184
194
  }
195
+
196
+ /**
197
+ * Handles the banner given the current scroll position.
198
+ */
185
199
  }, {
186
- key: "_calculateCumulativeHeight",
187
- value: function _calculateCumulativeHeight() {
188
- var _StickyHeader$global = StickyHeader.global,
189
- oldY = _StickyHeader$global._lastScrollPosition,
190
- banner = _StickyHeader$global._banner,
191
- masthead = _StickyHeader$global._masthead,
192
- mastheadL0 = _StickyHeader$global._mastheadL0,
193
- mastheadL1 = _StickyHeader$global._mastheadL1,
194
- localeModal = _StickyHeader$global._localeModal,
195
- toc = _StickyHeader$global._tableOfContents,
196
- tocInner = _StickyHeader$global._tableOfContentsInnerBar,
197
- leadspaceSearch = _StickyHeader$global._leadspaceWithSearch,
198
- leadspaceSearchBar = _StickyHeader$global._leadspaceSearchBar,
199
- leadspaceSearchInput = _StickyHeader$global._leadspaceWithSearchInput,
200
- leadspaceSearchThreshold = _StickyHeader$global._leadspaceWithSearchStickyThreshold;
201
- var customPropertyName = this.constructor.customPropertyName;
202
- if (localeModal && localeModal.hasAttribute('open')) {
203
- return;
204
- }
205
- var newY = window.scrollY;
206
- this._lastScrollPosition = Math.max(0, newY);
200
+ key: "_handleBanner",
201
+ value: function _handleBanner() {
202
+ var banner = this._elements.banner;
203
+ var scrollPos = this._state.scrollPos;
204
+ this._state.cumulativeOffset += Math.max(banner.offsetHeight - scrollPos, 0);
205
+ }
207
206
 
208
- /**
209
- * maxScrollaway is a calculated value matching the height of all components
210
- * that are allowed to hide above the viewport.
211
- *
212
- * We should only have one sticky header showing as the page scrolls down.
213
- *
214
- * Items that stick, in order
215
- * - L0
216
- * - L1
217
- * - The TOC in horizontal bar form
218
- * - The leadspace with search (if no TOC)
219
- */
220
- var maxScrollaway = 0;
207
+ /**
208
+ * Handles the masthead given the current scroll position.
209
+ */
210
+ }, {
211
+ key: "_handleMasthead",
212
+ value: function _handleMasthead() {
213
+ var masthead = this._elements.masthead;
214
+ masthead.style.transition = 'none';
215
+ masthead.style.insetBlockStart = "".concat(this._state.cumulativeOffset, "px");
221
216
 
222
- // Calculate maxScrollaway values based on TOC positon
223
- var tocIsAtTop = false;
224
- var tocShouldStick = false;
225
- if (tocInner) {
226
- tocIsAtTop = tocInner.getBoundingClientRect().top <= (masthead ? masthead.offsetTop + masthead.offsetHeight : 0) + 1;
227
- tocShouldStick = toc.layout === 'horizontal' || window.innerWidth < gridBreakpoint;
228
- if (masthead && tocIsAtTop && (tocShouldStick || mastheadL1)) {
229
- maxScrollaway += masthead.offsetHeight;
230
- if (mastheadL1 && !tocShouldStick) {
231
- maxScrollaway -= mastheadL1.offsetHeight;
232
- }
233
- } else if (mastheadL0 && mastheadL1) {
234
- maxScrollaway += mastheadL0.offsetHeight;
217
+ // Masthead always sticks, therefore always add its height.
218
+ this._state.cumulativeOffset += masthead.offsetHeight;
219
+ }
220
+
221
+ /**
222
+ * Handles the table of contents given the current scroll position.
223
+ */
224
+ }, {
225
+ key: "_handleToc",
226
+ value: function _handleToc() {
227
+ var tableOfContentsInnerBar = this._elements.tableOfContentsInnerBar;
228
+ var tocShouldStick = this._state.tocShouldStick;
229
+ tableOfContentsInnerBar.style.transition = 'none';
230
+ tableOfContentsInnerBar.style.insetBlockStart = "".concat(this._state.cumulativeOffset, "px");
231
+ var tocIsStuck = Math.round(tableOfContentsInnerBar.getBoundingClientRect().top) <= this._state.cumulativeOffset + 1;
232
+ if (tocShouldStick && tocIsStuck) {
233
+ this._state.cumulativeOffset += tableOfContentsInnerBar.offsetHeight;
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Handles the leadspace search given the current scroll position.
239
+ */
240
+ }, {
241
+ key: "_handleLeadspaceSearch",
242
+ value: function _handleLeadspaceSearch() {
243
+ var _this$_elements2 = this._elements,
244
+ leadspaceSearch = _this$_elements2.leadspaceSearch,
245
+ leadspaceSearchBar = _this$_elements2.leadspaceSearchBar,
246
+ leadspaceSearchInput = _this$_elements2.leadspaceSearchInput;
247
+ var leadspaceSearchThreshold = this._state.leadspaceSearchThreshold;
248
+ var searchShouldBeSticky = leadspaceSearch.getBoundingClientRect().bottom <= leadspaceSearchThreshold;
249
+ var searchIsSticky = leadspaceSearch.hasAttribute('sticky-search');
250
+ if (searchShouldBeSticky) {
251
+ if (!searchIsSticky) {
252
+ leadspaceSearch.style.paddingBottom = "".concat(leadspaceSearchBar.offsetHeight, "px");
253
+ leadspaceSearch.setAttribute('sticky-search', '');
254
+ leadspaceSearchInput.setAttribute('large', '');
255
+ window.requestAnimationFrame(function () {
256
+ leadspaceSearchBar.style.transitionDuration = '110ms';
257
+ leadspaceSearchBar.style.transform = 'translateY(0)';
258
+ });
235
259
  }
260
+ leadspaceSearchBar.style.insetBlockStart = "".concat(this._state.cumulativeOffset, "px");
261
+ this._state.cumulativeOffset += leadspaceSearchBar.offsetHeight;
262
+ } else if (searchIsSticky) {
263
+ leadspaceSearch.style.paddingBottom = '';
264
+ leadspaceSearch.removeAttribute('sticky-search');
265
+ leadspaceSearchInput.removeAttribute('large');
266
+ leadspaceSearchBar.style.transitionDuration = '';
267
+ leadspaceSearchBar.style.transform = '';
268
+ leadspaceSearchBar.style.insetBlockStart = '';
236
269
  }
270
+ }
237
271
 
238
- // Calculate maxScrollaway values based on leadspace search position
239
- if (!tocInner && leadspaceSearchBar) {
240
- var searchIsAtTop = leadspaceSearchBar.getBoundingClientRect().top <= (masthead ? masthead.offsetTop + masthead.offsetHeight : 0) + 1;
241
- if (masthead && searchIsAtTop) {
242
- maxScrollaway += masthead.offsetHeight;
272
+ /**
273
+ * Calculates a value matching the height of all components that are allowed
274
+ * to hide above the viewport.
275
+ *
276
+ * Adding an item's height to this value indicates we expect it to be hidden
277
+ * above the viewport.
278
+ *
279
+ * Items that stick, in order
280
+ * - L0
281
+ * - L1
282
+ * - The TOC in horizontal bar form
283
+ * - The leadspace with search (if no TOC)
284
+ */
285
+ }, {
286
+ key: "_calculateMaxScrollaway",
287
+ value: function _calculateMaxScrollaway() {
288
+ var _this$_elements3 = this._elements,
289
+ masthead = _this$_elements3.masthead,
290
+ mastheadL0 = _this$_elements3.mastheadL0,
291
+ mastheadL1 = _this$_elements3.mastheadL1,
292
+ tableOfContents = _this$_elements3.tableOfContents,
293
+ tableOfContentsInnerBar = _this$_elements3.tableOfContentsInnerBar,
294
+ leadspaceSearchBar = _this$_elements3.leadspaceSearchBar;
295
+
296
+ // Reset the value before performing any further calculations.
297
+ this._state.maxScrollaway = 0;
298
+
299
+ // Collect conditions we may want to test for to make logic easier to read.
300
+ this._state.tocShouldStick = tableOfContents ? tableOfContents.layout === 'horizontal' || window.innerWidth < gridBreakpoint : false;
301
+ this._state.tocIsAtTop = tableOfContentsInnerBar ? tableOfContentsInnerBar.getBoundingClientRect().top <= this.height + 1 : false;
302
+ this._state.searchIsAtTop = leadspaceSearchBar ? leadspaceSearchBar.getBoundingClientRect().top <= this.height + 1 : false;
303
+ this._state.tocIsAtSearch = leadspaceSearchBar && tableOfContentsInnerBar ? tableOfContentsInnerBar.getBoundingClientRect().top <= leadspaceSearchBar.getBoundingClientRect().bottom : false;
304
+ this._state.mastheadL0IsActive = Boolean(masthead === null || masthead === void 0 ? void 0 : masthead.querySelector('[expanded]'));
305
+ this._state.mastheadL1IsActive = mastheadL1 && mastheadL1.hasAttribute('active');
306
+ var _this$_state = this._state,
307
+ tocShouldStick = _this$_state.tocShouldStick,
308
+ tocIsAtTop = _this$_state.tocIsAtTop,
309
+ searchIsAtTop = _this$_state.searchIsAtTop,
310
+ tocIsAtSearch = _this$_state.tocIsAtSearch,
311
+ mastheadL0IsActive = _this$_state.mastheadL0IsActive,
312
+ mastheadL1IsActive = _this$_state.mastheadL1IsActive;
313
+
314
+ // Begin calculating maxScrollAway.
315
+
316
+ // If L0 is open, lock it to the top of the page.
317
+ if (mastheadL0 && mastheadL0IsActive) {
318
+ this._state.maxScrollaway = 0;
319
+ }
320
+ // If L1 is open, lock it to the top of the page.
321
+ else if (mastheadL1IsActive && mastheadL0) {
322
+ this._state.maxScrollaway = mastheadL0.offsetHeight;
323
+ } else {
324
+ // In cases where we have both an eligible ToC and leadspace search, we want
325
+ // the ToC to take precedence. Scroll away leadspace search.
326
+ if (searchIsAtTop && tocIsAtSearch && tocShouldStick) {
327
+ this._state.maxScrollaway += leadspaceSearchBar.offsetHeight;
328
+ }
329
+
330
+ // Scroll away entire masthead if either ToC or leadspace search is eligible
331
+ // to be the stuck element (unless L1 is open). Otherwise, scroll away the
332
+ // L0 if we have an L1.
333
+ if (searchIsAtTop || tocIsAtTop && tocShouldStick) {
334
+ if (masthead) {
335
+ this._state.maxScrollaway += masthead.offsetHeight;
336
+ }
337
+ } else if (masthead && mastheadL0 && mastheadL1) {
338
+ this._state.maxScrollaway += mastheadL0.offsetHeight;
243
339
  }
244
340
  }
341
+ }
342
+
343
+ /**
344
+ * Positions sticky elements. Does so by checking the scroll position and where
345
+ * tracked elements are in relation to it, then applying the correct styles to
346
+ * each element in succession to ensure that only one element is stuck to the
347
+ * top of the page, and all other elements that have been scrolled past can be
348
+ * revealed when scrolling back up.
349
+ */
350
+ }, {
351
+ key: "_positionElements",
352
+ value: function _positionElements() {
353
+ var _this$_elements4 = this._elements,
354
+ banner = _this$_elements4.banner,
355
+ masthead = _this$_elements4.masthead,
356
+ tocInner = _this$_elements4.tableOfContentsInnerBar,
357
+ leadspaceSearchBar = _this$_elements4.leadspaceSearchBar;
358
+ var oldY = this._state.scrollPosPrevious;
245
359
 
246
360
  /**
247
- * Cumulative offset is a calculated value used to set the `top` property of
248
- * components that stick to the top of the viewport.
249
- *
250
- * This value is equal to the difference between the previous scrollY and
251
- * the current scrollY values, but is positively and negatively limited.
361
+ * Reset to a value that is equal to the difference between the previous
362
+ * scrollY and the current scrollY values, but is positively and negatively
363
+ * limited.
252
364
  *
253
365
  * Positive limit: 0
254
366
  * all elements visible, starting at the top of the viewport.
@@ -258,55 +370,58 @@ var StickyHeader = /*#__PURE__*/function () {
258
370
  * with the elements that should be visible starting at the top of the
259
371
  * viewport.
260
372
  */
261
- var cumulativeOffset = Math.max(Math.min((masthead ? masthead.offsetTop : 0) + oldY - newY, 0), maxScrollaway * -1);
373
+ this._state.cumulativeOffset = Math.max(Math.min((masthead ? masthead.offsetTop : 0) + oldY - this._state.scrollPos, 0), this._state.maxScrollaway * -1);
374
+
375
+ /**
376
+ * Handle each potentially sticky element in the order we expect them to
377
+ * appear on the page. Important to do this sequentially for
378
+ * cumulativeOffset to be correctly calculated by the time each of these
379
+ * methods accesses it.
380
+ *
381
+ * To-do: One idea for improving this so the execution order doesn't matter
382
+ * is to collect our elements into an array ordered by document position,
383
+ * then loop over that array and execute a corresponding handler method.
384
+ */
262
385
  if (banner) {
263
- cumulativeOffset += Math.max(banner.offsetHeight - newY, 0);
386
+ this._handleBanner();
264
387
  }
265
388
  if (masthead) {
266
- masthead.style.transition = 'none';
267
- masthead.style.top = "".concat(cumulativeOffset, "px");
268
- cumulativeOffset += masthead.offsetHeight;
389
+ this._handleMasthead();
390
+ }
391
+ if (leadspaceSearchBar) {
392
+ this._handleLeadspaceSearch();
269
393
  }
270
394
  if (tocInner) {
271
- tocInner.style.transition = 'none';
272
- tocInner.style.top = "".concat(cumulativeOffset, "px");
273
- tocShouldStick = toc.layout === 'horizontal' || window.innerWidth < gridBreakpoint;
274
- var tocIsStuck = Math.round(tocInner.getBoundingClientRect().top) <= cumulativeOffset + 1;
275
- if (tocShouldStick && tocIsStuck) {
276
- cumulativeOffset += tocInner.offsetHeight;
277
- }
395
+ this._handleToc();
278
396
  }
279
- if (!tocInner && leadspaceSearchBar) {
280
- var searchShouldBeSticky = leadspaceSearch.getBoundingClientRect().bottom <= leadspaceSearchThreshold;
281
- var searchIsSticky = leadspaceSearch.hasAttribute('sticky-search');
282
- if (searchShouldBeSticky) {
283
- if (!searchIsSticky) {
284
- leadspaceSearch.style.paddingBottom = "".concat(leadspaceSearchBar.offsetHeight, "px");
285
- leadspaceSearch.setAttribute('sticky-search', '');
286
- leadspaceSearchInput.setAttribute('large', '');
287
- window.requestAnimationFrame(function () {
288
- leadspaceSearchBar.style.transitionDuration = '110ms';
289
- leadspaceSearchBar.style.transform = 'translateY(0)';
290
- });
291
- }
292
- leadspaceSearchBar.style.top = "".concat(cumulativeOffset, "px");
293
- cumulativeOffset += leadspaceSearchBar.offsetHeight;
294
- }
295
- if (!searchShouldBeSticky && searchIsSticky) {
296
- leadspaceSearch.removeAttribute('sticky-search');
297
- leadspaceSearch.style.paddingBottom = '';
298
- leadspaceSearchBar.style.top = '';
299
- leadspaceSearchBar.style.transitionDuration = '';
300
- leadspaceSearchBar.style.transform = '';
301
- leadspaceSearchInput.removeAttribute('large');
302
- }
397
+ }
398
+
399
+ /**
400
+ * Manages which elements are stuck and where they are positioned. We should
401
+ * only have one element stuck to the top of the viewport as the page scrolls
402
+ * down.
403
+ */
404
+ }, {
405
+ key: "_manageStickyElements",
406
+ value: function _manageStickyElements() {
407
+ var localeModal = this._elements.localeModal;
408
+ var scrollPosPrevious = this._state.scrollPos;
409
+
410
+ // Exit early if locale modal is open.
411
+ if (localeModal && localeModal.hasAttribute('open')) {
412
+ return;
303
413
  }
304
414
 
305
- // Set internal property for use in scripts
306
- this._cumulativeHeight = cumulativeOffset;
415
+ // Store scroll positions.
416
+ this._state.scrollPosPrevious = scrollPosPrevious;
417
+ this._state.scrollPos = Math.max(0, window.scrollY);
418
+
419
+ // Given the current state, calculate how elements should be positioned.
420
+ this._calculateMaxScrollaway();
421
+ this._positionElements();
307
422
 
308
423
  // Set custom property for use in stylesheets
309
- _windowOrGlobal.default.document.documentElement.style.setProperty(customPropertyName, "".concat(this._cumulativeHeight, "px"));
424
+ _windowOrGlobal.default.document.documentElement.style.setProperty(this.constructor.customPropertyName, "".concat(this._state.cumulativeOffset, "px"));
310
425
  }
311
426
  }], [{
312
427
  key: "global",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@carbon/ibmdotcom-utilities",
3
3
  "description": "Carbon for IBM.com Utilities",
4
- "version": "2.10.0-canary.9273569646.0+666acd0",
4
+ "version": "2.10.0-canary.9274459085.0+365c9b6",
5
5
  "license": "Apache-2.0",
6
6
  "main": "lib/index.js",
7
7
  "module": "es/index.js",
@@ -88,5 +88,5 @@
88
88
  "rollup-plugin-sizes": "^1.0.4",
89
89
  "whatwg-fetch": "^2.0.3"
90
90
  },
91
- "gitHead": "666acd0a6307f8db0c73a1d7fc54597727f156e8"
91
+ "gitHead": "365c9b684bfc0e59785009c05a0ebe05cc0b6df9"
92
92
  }