@carbon/ibmdotcom-utilities 2.10.0 → 2.11.0-rc.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.
Files changed (33) hide show
  1. package/es/utilities/StickyHeader/StickyHeader.js +380 -163
  2. package/es/utilities/focuswrap/focuswrap.js +4 -4
  3. package/es/utilities/ipcinfoCookie/ipcinfoCookie.js +3 -3
  4. package/es/utilities/settings/settings.js +2 -2
  5. package/lib/utilities/StickyHeader/StickyHeader.js +381 -164
  6. package/lib/utilities/StickyHeader/index.js +1 -1
  7. package/lib/utilities/altlangs/altlangs.js +1 -1
  8. package/lib/utilities/altlangs/index.js +1 -1
  9. package/lib/utilities/calculateTotalWidth/index.js +1 -1
  10. package/lib/utilities/decodeString/index.js +1 -1
  11. package/lib/utilities/deprecate/index.js +1 -1
  12. package/lib/utilities/escaperegexp/index.js +1 -1
  13. package/lib/utilities/featureflag/index.js +1 -1
  14. package/lib/utilities/focuswrap/focuswrap.js +4 -4
  15. package/lib/utilities/ipcinfoCookie/index.js +1 -1
  16. package/lib/utilities/ipcinfoCookie/ipcinfoCookie.js +4 -4
  17. package/lib/utilities/loadNonLatinPlex/index.js +1 -1
  18. package/lib/utilities/markdownToHtml/index.js +1 -1
  19. package/lib/utilities/markdownToHtml/markdownToHtml.js +1 -1
  20. package/lib/utilities/on/index.js +1 -1
  21. package/lib/utilities/parseAspectRatio/index.js +1 -1
  22. package/lib/utilities/removeHtmlTagEntities/index.js +1 -1
  23. package/lib/utilities/removeHtmlTagEntities/removeHtmlTagEntities.js +1 -1
  24. package/lib/utilities/sameHeight/index.js +1 -1
  25. package/lib/utilities/serialize/index.js +1 -1
  26. package/lib/utilities/settings/index.js +1 -1
  27. package/lib/utilities/settings/settings.js +2 -2
  28. package/lib/utilities/smoothScroll/index.js +1 -1
  29. package/lib/utilities/stripHTML/index.js +1 -1
  30. package/lib/utilities/uniqueid/index.js +1 -1
  31. package/package.json +2 -2
  32. package/umd/ibmdotcom-utilities.js +34 -29
  33. package/umd/ibmdotcom-utilities.min.js +3 -3
@@ -7,11 +7,11 @@ exports.default = void 0;
7
7
  var _layout = require("@carbon/layout");
8
8
  var _windowOrGlobal = _interopRequireDefault(require("window-or-global"));
9
9
  var _settings = _interopRequireDefault(require("../settings/settings.js"));
10
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
11
11
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
12
- function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
13
- function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
14
- function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
12
+ function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
13
+ function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
14
+ function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
15
15
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
16
16
  function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /**
17
17
  * Copyright IBM Corp. 2016, 2024
@@ -21,25 +21,39 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
21
21
  */
22
22
  var prefix = _settings.default.prefix,
23
23
  c4dPrefix = _settings.default.stablePrefix;
24
+ var ddsPrefix = 'dds';
25
+ var bxPrefix = 'bx';
24
26
  var gridBreakpoint = parseFloat(_layout.breakpoints.lg.width) * _layout.baseFontSize;
25
27
  var StickyHeader = /*#__PURE__*/function () {
26
28
  function StickyHeader() {
27
29
  _classCallCheck(this, StickyHeader);
28
30
  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;
31
+ this._state = {
32
+ cumulativeOffset: 0,
33
+ hasBanner: false,
34
+ leadspaceSearchThreshold: 0,
35
+ mastheadL0IsActive: false,
36
+ mastheadL1IsActive: false,
37
+ maxScrollaway: 0,
38
+ scrollPosPrevious: 0,
39
+ scrollPos: 0,
40
+ searchIsAtTop: false,
41
+ tocShouldStick: false,
42
+ tocIsAtTop: false,
43
+ tocIsAtSearch: false
44
+ };
45
+ this._elements = {
46
+ banner: undefined,
47
+ leadspaceSearch: undefined,
48
+ leadspaceSearchBar: undefined,
49
+ leadspaceSearchInput: undefined,
50
+ localeModal: undefined,
51
+ masthead: undefined,
52
+ mastheadL0: undefined,
53
+ mastheadL1: undefined,
54
+ tableOfContents: undefined,
55
+ tableOfContentsInnerBar: undefined
56
+ };
43
57
  this._throttled = false;
44
58
  this._resizeObserver = new ResizeObserver(this._handleResize.bind(this));
45
59
  _windowOrGlobal.default.addEventListener('scroll', this._throttledHandler.bind(this));
@@ -51,7 +65,7 @@ var StickyHeader = /*#__PURE__*/function () {
51
65
  return _createClass(StickyHeader, [{
52
66
  key: "height",
53
67
  get: function get() {
54
- return this._cumulativeHeight;
68
+ return this._state.cumulativeOffset;
55
69
  }
56
70
 
57
71
  /**
@@ -67,78 +81,176 @@ var StickyHeader = /*#__PURE__*/function () {
67
81
  value: function _validateComponent(component, expected) {
68
82
  var received = component.tagName.toLowerCase();
69
83
  if (received !== expected) {
70
- throw new TypeError("".concat(expected, " expected, ").concat(received, " provided"));
84
+ // TODO: don't check for v1/v2 compatibility after v1 EOL.
85
+ if (received.split('-').splice(1).join('-') !== expected.split('-').splice(1).join('-')) {
86
+ throw new TypeError("".concat(expected, " expected, ").concat(received, " provided"));
87
+ } else {
88
+ var message = ["Mixed prefixes detected.\n", "expected ".concat(expected, ", found ").concat(received, ".")];
89
+ console.warn(message.join(''));
90
+ return true;
91
+ }
71
92
  } else {
72
93
  return true;
73
94
  }
74
95
  }
96
+
97
+ /**
98
+ * Helper method to query for either C4IBM v1.x or v2.x sub-elements;
99
+ *
100
+ * @param {*} element The C4IBM element.
101
+ * @param {*} v1Func The querying function to run if using a C4IBM v1.x element.
102
+ * @param {*} v2Func The querying function to run if using a C4IBM v2.x element.
103
+ */
75
104
  }, {
76
- key: "_tableOfContentsStickyUpdate",
77
- value: function _tableOfContentsStickyUpdate() {
78
- var toc = this._tableOfContents;
105
+ key: "_updateRefsV1orV2",
106
+ value: function _updateRefsV1orV2(element, v1Func, v2Func) {
107
+ var elementPrefix = element.tagName.toLowerCase().split('-')[0];
108
+ if (elementPrefix === ddsPrefix) {
109
+ v1Func.bind(this)();
110
+ } else if (elementPrefix === c4dPrefix) {
111
+ v2Func.bind(this)();
112
+ } else {
113
+ throw new Error("\n Could not find sub-elements for ".concat(element.tagName.toLowerCase(), ".\n "));
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Temporary method to find v1 leadspace sub-elements.
119
+ */
120
+ }, {
121
+ key: "_updateLeadspaceRefsV1",
122
+ value: function _updateLeadspaceRefsV1() {
123
+ var leadspaceSearch = this._elements.leadspaceSearch;
124
+ this._elements.leadspaceSearchBar = leadspaceSearch.shadowRoot.querySelector(".".concat(bxPrefix, "--search-container"));
125
+ this._elements.leadspaceSearchInput = leadspaceSearch.querySelector("".concat(ddsPrefix, "-search-with-typeahead"));
126
+ }
127
+
128
+ /**
129
+ * Temporary method to find v2 leadspace sub-elements.
130
+ */
131
+ }, {
132
+ key: "_updateLeadspaceRefsV2",
133
+ value: function _updateLeadspaceRefsV2() {
134
+ var leadspaceSearch = this._elements.leadspaceSearch;
135
+ this._elements.leadspaceSearchBar = leadspaceSearch.shadowRoot.querySelector(".".concat(prefix, "--search-container"));
136
+ this._elements.leadspaceSearchInput = leadspaceSearch.querySelector("".concat(c4dPrefix, "-search-with-typeahead"));
137
+ }
138
+
139
+ /**
140
+ * Temporary method to find v1 masthead sub-elements.
141
+ */
142
+ }, {
143
+ key: "_updateMastheadRefsV1",
144
+ value: function _updateMastheadRefsV1() {
145
+ var masthead = this._elements.masthead;
146
+ this._elements.mastheadL0 = masthead.shadowRoot.querySelector(".".concat(bxPrefix, "--masthead__l0"));
147
+ this._elements.mastheadL1 = masthead.querySelector("".concat(ddsPrefix, "-masthead-l1"));
148
+ }
149
+
150
+ /**
151
+ * Temporary method to find v2 masthead sub-elements.
152
+ */
153
+ }, {
154
+ key: "_updateMastheadRefsV2",
155
+ value: function _updateMastheadRefsV2() {
156
+ var masthead = this._elements.masthead;
157
+ this._elements.mastheadL0 = masthead.shadowRoot.querySelector(".".concat(prefix, "--masthead__l0"));
158
+ this._elements.mastheadL1 = masthead.querySelector("".concat(c4dPrefix, "-masthead-l1"));
159
+ }
160
+
161
+ /**
162
+ * Temporary method to find v1 table of contents sub-elements.
163
+ */
164
+ }, {
165
+ key: "_updateTableOfContentsRefsV1",
166
+ value: function _updateTableOfContentsRefsV1() {
167
+ var toc = this._elements.tableOfContents;
79
168
  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"));
169
+ var selectors = {
170
+ desktop: {
171
+ vertical: ".".concat(ddsPrefix, "-ce--table-of-contents__items-container"),
172
+ horizontal: ".".concat(bxPrefix, "--tableofcontents__navbar")
173
+ },
174
+ mobile: {
175
+ vertical: ".".concat(bxPrefix, "--tableofcontents__sidebar"),
176
+ horizontal: ".".concat(bxPrefix, "--tableofcontents__navbar")
86
177
  }
87
- }
178
+ };
179
+ var viewportDimension = window.innerWidth >= gridBreakpoint ? 'desktop' : 'mobile';
180
+ this._elements.tableOfContentsInnerBar = tocRoot.querySelector(selectors[viewportDimension][toc.layout || 'vertical']);
181
+ }
182
+
183
+ /**
184
+ * Temporary method to find v2 table of contents sub-elements.
185
+ */
186
+ }, {
187
+ key: "_updateTableOfContentsRefsV2",
188
+ value: function _updateTableOfContentsRefsV2() {
189
+ var toc = this._elements.tableOfContents;
190
+ var tocRoot = toc.shadowRoot;
191
+ 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"));
192
+ }
193
+
194
+ /**
195
+ * Stores references to TOC sub-elements that are relevant to current viewport
196
+ * dimensions.
197
+ */
198
+ }, {
199
+ key: "_updateTableOfContentsRefs",
200
+ value: function _updateTableOfContentsRefs() {
201
+ var toc = this._elements.tableOfContents;
202
+ this._updateRefsV1orV2(toc, this._updateTableOfContentsRefsV1, this._updateTableOfContentsRefsV2);
88
203
  }
89
204
  }, {
90
205
  key: "banner",
91
206
  set: function set(component) {
92
207
  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', '');
208
+ this._elements.banner = component;
209
+ this._state.hasBanner = true;
210
+ if (this._elements.masthead) {
211
+ this._elements.masthead.setAttribute('with-banner', '');
97
212
  }
98
- this._calculateCumulativeHeight();
213
+ this._manageStickyElements();
99
214
  }
100
215
  }
101
216
  }, {
102
- key: "leadspaceWithSearch",
217
+ key: "leadspaceSearch",
103
218
  set: function set(component) {
104
219
  if (this._validateComponent(component, "".concat(c4dPrefix, "-leadspace-with-search"))) {
105
- this._leadspaceWithSearch = component;
106
- 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();
220
+ this._elements.leadspaceSearch = component;
221
+ this._updateRefsV1orV2(component, this._updateLeadspaceRefsV1, this._updateLeadspaceRefsV2);
222
+ this._state.leadspaceSearchThreshold = parseInt(window.getComputedStyle(this._elements.leadspaceSearchBar).paddingBottom) - 16;
223
+ this._manageStickyElements();
111
224
  }
112
225
  }
113
226
  }, {
114
227
  key: "localeModal",
115
228
  set: function set(component) {
116
229
  if (this._validateComponent(component, "".concat(c4dPrefix, "-locale-modal"))) {
117
- this._localeModal = component;
118
- this._calculateCumulativeHeight();
230
+ this._elements.localeModal = component;
231
+ this._manageStickyElements();
119
232
  }
120
233
  }
121
234
  }, {
122
235
  key: "masthead",
123
236
  set: function set(component) {
124
237
  if (this._validateComponent(component, "".concat(c4dPrefix, "-masthead"))) {
125
- this._masthead = component;
126
- if (this._banner) {
127
- this._masthead.setAttribute('with-banner', '');
238
+ this._elements.masthead = component;
239
+ if (this._elements.banner) {
240
+ this._elements.masthead.setAttribute('with-banner', '');
128
241
  }
129
- this._mastheadL0 = component.shadowRoot.querySelector(".".concat(prefix, "--masthead__l0"));
130
- this._mastheadL1 = component.querySelector("".concat(c4dPrefix, "-masthead-l1"));
131
- this._calculateCumulativeHeight();
242
+ this._updateRefsV1orV2(component, this._updateMastheadRefsV1, this._updateMastheadRefsV2);
243
+ this._manageStickyElements();
132
244
  }
133
245
  }
134
246
  }, {
135
247
  key: "tableOfContents",
136
248
  set: function set(component) {
137
249
  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();
250
+ this._elements.tableOfContents = component;
251
+ this._updateTableOfContentsRefs();
252
+ this._resizeObserver.observe(this._elements.tableOfContents);
253
+ this._manageStickyElements();
142
254
  }
143
255
  }
144
256
 
@@ -151,7 +263,7 @@ var StickyHeader = /*#__PURE__*/function () {
151
263
  var _this = this;
152
264
  if (!this._throttled) {
153
265
  this._throttled = true;
154
- this._calculateCumulativeHeight();
266
+ this._manageStickyElements();
155
267
  setTimeout(function () {
156
268
  _this._throttled = false;
157
269
  }, 20);
@@ -160,95 +272,197 @@ var StickyHeader = /*#__PURE__*/function () {
160
272
  }, {
161
273
  key: "_handleResize",
162
274
  value: function _handleResize() {
163
- var hasBanner = this._hasBanner,
164
- masthead = this._masthead,
165
- toc = this._tableOfContents,
166
- tocLayout = this._tableOfContentsLayout,
167
- leadspaceSearchBar = this._leadspaceSearchBar;
275
+ var hasBanner = this._state._hasBanner;
276
+ var _this$_elements = this._elements,
277
+ masthead = _this$_elements.masthead,
278
+ toc = _this$_elements.tableOfContents,
279
+ leadspaceSearchBar = _this$_elements.leadspaceSearchBar;
168
280
  if (toc && masthead) {
169
- this._tableOfContentsStickyUpdate();
170
- if (window.innerWidth >= gridBreakpoint && tocLayout !== 'horizontal' && !hasBanner) {
171
- masthead.style.top = '0';
281
+ this._updateTableOfContentsRefs();
282
+ if (window.innerWidth >= gridBreakpoint && toc.layout !== 'horizontal' && !hasBanner) {
283
+ masthead.style.insetBlockStart = '0';
172
284
  } else {
173
- // This has to happen after the tocStickyUpdate method.
174
- var tocInner = this._tableOfContentsInnerBar;
285
+ // This has to happen after the _updateTableOfContentsRefs method.
286
+ var tocInner = this._elements.tableOfContentsInnerBar;
175
287
  if (masthead.offsetTop === 0) {
176
- tocInner.style.top = "".concat(masthead.offsetHeight, "px");
288
+ tocInner.style.insetBlockStart = "".concat(masthead.offsetHeight, "px");
177
289
  }
178
290
  }
179
- this._calculateCumulativeHeight();
291
+ this._manageStickyElements();
180
292
  }
181
293
  if (leadspaceSearchBar) {
182
- this._leadspaceWithSearchStickyThreshold = parseInt(window.getComputedStyle(leadspaceSearchBar).paddingBottom) - 16;
294
+ this._state.leadspaceSearchThreshold = parseInt(window.getComputedStyle(leadspaceSearchBar).paddingBottom) - 16;
183
295
  }
184
296
  }
297
+
298
+ /**
299
+ * Handles the banner given the current scroll position.
300
+ */
185
301
  }, {
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);
302
+ key: "_handleBanner",
303
+ value: function _handleBanner() {
304
+ var banner = this._elements.banner;
305
+ var scrollPos = this._state.scrollPos;
306
+ this._state.cumulativeOffset += Math.max(banner.offsetHeight - scrollPos, 0);
307
+ }
207
308
 
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;
309
+ /**
310
+ * Handles the masthead given the current scroll position.
311
+ */
312
+ }, {
313
+ key: "_handleMasthead",
314
+ value: function _handleMasthead() {
315
+ var masthead = this._elements.masthead;
316
+ masthead.style.transition = 'none';
317
+ masthead.style.insetBlockStart = "".concat(this._state.cumulativeOffset, "px");
221
318
 
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;
319
+ // Masthead always sticks, therefore always add its height.
320
+ this._state.cumulativeOffset += masthead.offsetHeight;
321
+ }
322
+
323
+ /**
324
+ * Handles the table of contents given the current scroll position.
325
+ */
326
+ }, {
327
+ key: "_handleToc",
328
+ value: function _handleToc() {
329
+ var tableOfContentsInnerBar = this._elements.tableOfContentsInnerBar;
330
+ var tocShouldStick = this._state.tocShouldStick;
331
+ tableOfContentsInnerBar.style.transition = 'none';
332
+ tableOfContentsInnerBar.style.insetBlockStart = "".concat(this._state.cumulativeOffset, "px");
333
+ var tocIsStuck = Math.round(tableOfContentsInnerBar.getBoundingClientRect().top) <= this._state.cumulativeOffset + 1;
334
+ if (tocShouldStick && tocIsStuck) {
335
+ this._state.cumulativeOffset += tableOfContentsInnerBar.offsetHeight;
336
+ }
337
+ }
338
+
339
+ /**
340
+ * Handles the leadspace search given the current scroll position.
341
+ */
342
+ }, {
343
+ key: "_handleLeadspaceSearch",
344
+ value: function _handleLeadspaceSearch() {
345
+ var _this$_elements2 = this._elements,
346
+ leadspaceSearch = _this$_elements2.leadspaceSearch,
347
+ leadspaceSearchBar = _this$_elements2.leadspaceSearchBar,
348
+ leadspaceSearchInput = _this$_elements2.leadspaceSearchInput;
349
+ var leadspaceSearchThreshold = this._state.leadspaceSearchThreshold;
350
+ var searchShouldBeSticky = leadspaceSearch.getBoundingClientRect().bottom <= leadspaceSearchThreshold;
351
+ var searchIsSticky = leadspaceSearch.hasAttribute('sticky-search');
352
+ if (searchShouldBeSticky) {
353
+ if (!searchIsSticky) {
354
+ leadspaceSearch.style.paddingBottom = "".concat(leadspaceSearchBar.offsetHeight, "px");
355
+ leadspaceSearch.setAttribute('sticky-search', '');
356
+ leadspaceSearchInput.setAttribute('large', '');
357
+ window.requestAnimationFrame(function () {
358
+ leadspaceSearchBar.style.transitionDuration = '110ms';
359
+ leadspaceSearchBar.style.transform = 'translateY(0)';
360
+ });
235
361
  }
362
+ leadspaceSearchBar.style.insetBlockStart = "".concat(this._state.cumulativeOffset, "px");
363
+ this._state.cumulativeOffset += leadspaceSearchBar.offsetHeight;
364
+ } else if (searchIsSticky) {
365
+ leadspaceSearch.style.paddingBottom = '';
366
+ leadspaceSearch.removeAttribute('sticky-search');
367
+ leadspaceSearchInput.removeAttribute('large');
368
+ leadspaceSearchBar.style.transitionDuration = '';
369
+ leadspaceSearchBar.style.transform = '';
370
+ leadspaceSearchBar.style.insetBlockStart = '';
371
+ }
372
+ }
373
+
374
+ /**
375
+ * Calculates a value matching the height of all components that are allowed
376
+ * to hide above the viewport.
377
+ *
378
+ * Adding an item's height to this value indicates we expect it to be hidden
379
+ * above the viewport.
380
+ *
381
+ * Items that stick, in order
382
+ * - L0
383
+ * - L1
384
+ * - The TOC in horizontal bar form
385
+ * - The leadspace with search (if no TOC)
386
+ */
387
+ }, {
388
+ key: "_calculateMaxScrollaway",
389
+ value: function _calculateMaxScrollaway() {
390
+ var _this$_elements3 = this._elements,
391
+ masthead = _this$_elements3.masthead,
392
+ mastheadL0 = _this$_elements3.mastheadL0,
393
+ mastheadL1 = _this$_elements3.mastheadL1,
394
+ tableOfContents = _this$_elements3.tableOfContents,
395
+ tableOfContentsInnerBar = _this$_elements3.tableOfContentsInnerBar,
396
+ leadspaceSearchBar = _this$_elements3.leadspaceSearchBar;
397
+
398
+ // Reset the value before performing any further calculations.
399
+ this._state.maxScrollaway = 0;
400
+
401
+ // Collect conditions we may want to test for to make logic easier to read.
402
+ this._state.tocShouldStick = tableOfContents ? tableOfContents.layout === 'horizontal' || window.innerWidth < gridBreakpoint : false;
403
+ this._state.tocIsAtTop = tableOfContentsInnerBar ? tableOfContentsInnerBar.getBoundingClientRect().top <= this.height + 1 : false;
404
+ this._state.searchIsAtTop = leadspaceSearchBar ? leadspaceSearchBar.getBoundingClientRect().top <= this.height + 1 : false;
405
+ this._state.tocIsAtSearch = leadspaceSearchBar && tableOfContentsInnerBar ? tableOfContentsInnerBar.getBoundingClientRect().top <= leadspaceSearchBar.getBoundingClientRect().bottom : false;
406
+ this._state.mastheadL0IsActive = Boolean(masthead === null || masthead === void 0 ? void 0 : masthead.querySelector('[expanded]'));
407
+ this._state.mastheadL1IsActive = mastheadL1 && mastheadL1.hasAttribute('active');
408
+ var _this$_state = this._state,
409
+ tocShouldStick = _this$_state.tocShouldStick,
410
+ tocIsAtTop = _this$_state.tocIsAtTop,
411
+ searchIsAtTop = _this$_state.searchIsAtTop,
412
+ tocIsAtSearch = _this$_state.tocIsAtSearch,
413
+ mastheadL0IsActive = _this$_state.mastheadL0IsActive,
414
+ mastheadL1IsActive = _this$_state.mastheadL1IsActive;
415
+
416
+ // Begin calculating maxScrollAway.
417
+
418
+ // If L0 is open, lock it to the top of the page.
419
+ if (mastheadL0 && mastheadL0IsActive) {
420
+ this._state.maxScrollaway = 0;
236
421
  }
422
+ // If L1 is open, lock it to the top of the page.
423
+ else if (mastheadL1IsActive && mastheadL0) {
424
+ this._state.maxScrollaway = mastheadL0.offsetHeight;
425
+ } else {
426
+ // In cases where we have both an eligible ToC and leadspace search, we want
427
+ // the ToC to take precedence. Scroll away leadspace search.
428
+ if (searchIsAtTop && tocIsAtSearch && tocShouldStick) {
429
+ this._state.maxScrollaway += leadspaceSearchBar.offsetHeight;
430
+ }
237
431
 
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;
432
+ // Scroll away entire masthead if either ToC or leadspace search is eligible
433
+ // to be the stuck element (unless L1 is open). Otherwise, scroll away the
434
+ // L0 if we have an L1.
435
+ if (searchIsAtTop || tocIsAtTop && tocShouldStick) {
436
+ if (masthead) {
437
+ this._state.maxScrollaway += masthead.offsetHeight;
438
+ }
439
+ } else if (masthead && mastheadL0 && mastheadL1) {
440
+ this._state.maxScrollaway += mastheadL0.offsetHeight;
243
441
  }
244
442
  }
443
+ }
444
+
445
+ /**
446
+ * Positions sticky elements. Does so by checking the scroll position and where
447
+ * tracked elements are in relation to it, then applying the correct styles to
448
+ * each element in succession to ensure that only one element is stuck to the
449
+ * top of the page, and all other elements that have been scrolled past can be
450
+ * revealed when scrolling back up.
451
+ */
452
+ }, {
453
+ key: "_positionElements",
454
+ value: function _positionElements() {
455
+ var _this$_elements4 = this._elements,
456
+ banner = _this$_elements4.banner,
457
+ masthead = _this$_elements4.masthead,
458
+ tocInner = _this$_elements4.tableOfContentsInnerBar,
459
+ leadspaceSearchBar = _this$_elements4.leadspaceSearchBar;
460
+ var oldY = this._state.scrollPosPrevious;
245
461
 
246
462
  /**
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.
463
+ * Reset to a value that is equal to the difference between the previous
464
+ * scrollY and the current scrollY values, but is positively and negatively
465
+ * limited.
252
466
  *
253
467
  * Positive limit: 0
254
468
  * all elements visible, starting at the top of the viewport.
@@ -258,55 +472,58 @@ var StickyHeader = /*#__PURE__*/function () {
258
472
  * with the elements that should be visible starting at the top of the
259
473
  * viewport.
260
474
  */
261
- var cumulativeOffset = Math.max(Math.min((masthead ? masthead.offsetTop : 0) + oldY - newY, 0), maxScrollaway * -1);
475
+ this._state.cumulativeOffset = Math.max(Math.min((masthead ? masthead.offsetTop : 0) + oldY - this._state.scrollPos, 0), this._state.maxScrollaway * -1);
476
+
477
+ /**
478
+ * Handle each potentially sticky element in the order we expect them to
479
+ * appear on the page. Important to do this sequentially for
480
+ * cumulativeOffset to be correctly calculated by the time each of these
481
+ * methods accesses it.
482
+ *
483
+ * To-do: One idea for improving this so the execution order doesn't matter
484
+ * is to collect our elements into an array ordered by document position,
485
+ * then loop over that array and execute a corresponding handler method.
486
+ */
262
487
  if (banner) {
263
- cumulativeOffset += Math.max(banner.offsetHeight - newY, 0);
488
+ this._handleBanner();
264
489
  }
265
490
  if (masthead) {
266
- masthead.style.transition = 'none';
267
- masthead.style.top = "".concat(cumulativeOffset, "px");
268
- cumulativeOffset += masthead.offsetHeight;
491
+ this._handleMasthead();
492
+ }
493
+ if (leadspaceSearchBar) {
494
+ this._handleLeadspaceSearch();
269
495
  }
270
496
  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
- }
497
+ this._handleToc();
278
498
  }
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
- }
499
+ }
500
+
501
+ /**
502
+ * Manages which elements are stuck and where they are positioned. We should
503
+ * only have one element stuck to the top of the viewport as the page scrolls
504
+ * down.
505
+ */
506
+ }, {
507
+ key: "_manageStickyElements",
508
+ value: function _manageStickyElements() {
509
+ var localeModal = this._elements.localeModal;
510
+ var scrollPosPrevious = this._state.scrollPos;
511
+
512
+ // Exit early if locale modal is open.
513
+ if (localeModal && localeModal.hasAttribute('open')) {
514
+ return;
303
515
  }
304
516
 
305
- // Set internal property for use in scripts
306
- this._cumulativeHeight = cumulativeOffset;
517
+ // Store scroll positions.
518
+ this._state.scrollPosPrevious = scrollPosPrevious;
519
+ this._state.scrollPos = Math.max(0, window.scrollY);
520
+
521
+ // Given the current state, calculate how elements should be positioned.
522
+ this._calculateMaxScrollaway();
523
+ this._positionElements();
307
524
 
308
525
  // Set custom property for use in stylesheets
309
- _windowOrGlobal.default.document.documentElement.style.setProperty(customPropertyName, "".concat(this._cumulativeHeight, "px"));
526
+ _windowOrGlobal.default.document.documentElement.style.setProperty(this.constructor.customPropertyName, "".concat(this._state.cumulativeOffset, "px"));
310
527
  }
311
528
  }], [{
312
529
  key: "global",