@expressivecss/expressive 0.7.0 → 0.9.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 (122) hide show
  1. package/CHANGELOG.md +1313 -1
  2. package/README.md +76 -30
  3. package/dist/css/expressive.css +4896 -1777
  4. package/dist/css/expressive.css.map +1 -1
  5. package/dist/css/expressive.min.css +1 -1
  6. package/dist/css/expressive.min.css.map +1 -1
  7. package/dist/fonts/LICENSE-material-symbols +93 -0
  8. package/dist/fonts/LICENSE-noto-sans +93 -0
  9. package/dist/fonts/LICENSE-roboto +93 -0
  10. package/dist/fonts/material-symbols-outlined.woff2 +0 -0
  11. package/dist/fonts/material-symbols-rounded.woff2 +0 -0
  12. package/dist/fonts/material-symbols-sharp.woff2 +0 -0
  13. package/dist/fonts/noto-sans-latin-400.woff2 +0 -0
  14. package/dist/fonts/noto-sans-latin-500.woff2 +0 -0
  15. package/dist/fonts/roboto-latin-400.woff2 +0 -0
  16. package/dist/fonts/roboto-latin-500.woff2 +0 -0
  17. package/dist/js/expressive.cjs +1673 -1338
  18. package/dist/js/expressive.cjs.map +4 -4
  19. package/dist/js/expressive.js +1673 -1338
  20. package/dist/js/expressive.js.map +4 -4
  21. package/dist/js/expressive.min.js +13 -13
  22. package/dist/js/expressive.min.js.map +4 -4
  23. package/dist/js/expressive.mjs +1673 -1338
  24. package/dist/js/expressive.mjs.map +4 -4
  25. package/dist/types/behaviors/bottomSheets.d.ts +19 -11
  26. package/dist/types/behaviors/sheetDrag.d.ts +40 -0
  27. package/dist/types/behaviors/sideSheets.d.ts +2 -9
  28. package/dist/types/components/appBar.d.ts +26 -0
  29. package/dist/types/components/autocomplete.d.ts +15 -3
  30. package/dist/types/components/buttonGroup.d.ts +39 -0
  31. package/dist/types/components/buttons.d.ts +5 -1
  32. package/dist/types/components/cards.d.ts +11 -13
  33. package/dist/types/components/carousel.d.ts +87 -53
  34. package/dist/types/components/chips.d.ts +21 -1
  35. package/dist/types/components/expandingCard.d.ts +31 -0
  36. package/dist/types/components/index.d.ts +23 -20
  37. package/dist/types/components/lightbox.d.ts +1 -0
  38. package/dist/types/components/menu.d.ts +11 -1
  39. package/dist/types/components/{sidenav.d.ts → navigationDrawer.d.ts} +16 -16
  40. package/dist/types/components/registry.d.ts +18 -8
  41. package/dist/types/components/slider.d.ts +26 -85
  42. package/dist/types/components/snackbar.d.ts +20 -5
  43. package/dist/types/components/tabs.d.ts +12 -0
  44. package/dist/types/components/timepicker.d.ts +12 -10
  45. package/dist/types/components/tooltip.d.ts +1 -0
  46. package/dist/types/core/utils.d.ts +64 -53
  47. package/dist/types/index.d.ts +8 -9
  48. package/package.json +39 -3
  49. package/src/sass/README.md +61 -11
  50. package/src/sass/abstracts/_breakpoints.scss +17 -19
  51. package/src/sass/abstracts/_mixins.scss +155 -21
  52. package/src/sass/abstracts/_variables.scss +69 -5
  53. package/src/sass/base/_fonts.scss +63 -0
  54. package/src/sass/base/_global.scss +6 -24
  55. package/src/sass/base/_grid.scss +15 -12
  56. package/src/sass/base/_normalize.scss +34 -38
  57. package/src/sass/base/_typography.scss +4 -18
  58. package/src/sass/components/_badges.scss +3 -5
  59. package/src/sass/components/_banners.scss +298 -0
  60. package/src/sass/components/_bottom-app-bar.scss +180 -0
  61. package/src/sass/components/_bottom-sheet.scss +12 -4
  62. package/src/sass/components/_button-groups.scss +358 -0
  63. package/src/sass/components/_buttons.scss +245 -92
  64. package/src/sass/components/_cards.scss +514 -78
  65. package/src/sass/components/_carousel.scss +562 -68
  66. package/src/sass/components/_chips.scss +64 -5
  67. package/src/sass/components/_datepicker.scss +24 -24
  68. package/src/sass/components/_dialog.scss +17 -11
  69. package/src/sass/components/_drag-handle.scss +130 -0
  70. package/src/sass/components/_expanding-card.scss +226 -0
  71. package/src/sass/components/_fab-menu.scss +232 -0
  72. package/src/sass/components/_floating-sheet.scss +86 -0
  73. package/src/sass/components/_icon-buttons.scss +172 -0
  74. package/src/sass/components/_icons-material-design.scss +22 -10
  75. package/src/sass/components/_index.scss +22 -6
  76. package/src/sass/components/_list.scss +16 -13
  77. package/src/sass/components/_loading-indicator.scss +106 -0
  78. package/src/sass/components/_menu.scss +335 -61
  79. package/src/sass/components/_navbar.scss +245 -45
  80. package/src/sass/components/_navigation-bar.scss +22 -11
  81. package/src/sass/components/{_sidenav.scss → _navigation-drawer.scss} +123 -33
  82. package/src/sass/components/_navigation-rail.scss +31 -23
  83. package/src/sass/components/_page-footer.scss +19 -16
  84. package/src/sass/components/_pagination.scss +24 -6
  85. package/src/sass/components/_panes.scss +52 -11
  86. package/src/sass/components/{_preloader.scss → _progress.scss} +11 -3
  87. package/src/sass/components/_scrim.scss +37 -0
  88. package/src/sass/components/_search.scss +259 -0
  89. package/src/sass/components/_segmented-buttons.scss +188 -0
  90. package/src/sass/components/_side-sheet.scss +2 -2
  91. package/src/sass/components/_snackbar.scss +20 -15
  92. package/src/sass/components/_split-button.scss +196 -0
  93. package/src/sass/components/_tabs.scss +7 -8
  94. package/src/sass/components/_timepicker.scss +67 -58
  95. package/src/sass/components/_toolbar.scss +206 -80
  96. package/src/sass/components/_transitions.scss +8 -1
  97. package/src/sass/components/forms/_checkboxes.scss +49 -12
  98. package/src/sass/components/forms/_fieldset.scss +4 -4
  99. package/src/sass/components/forms/_file-input.scss +8 -1
  100. package/src/sass/components/forms/_index.scss +1 -1
  101. package/src/sass/components/forms/_input-fields.scss +50 -44
  102. package/src/sass/components/forms/_radio-buttons.scss +42 -10
  103. package/src/sass/components/forms/_select.scss +38 -1
  104. package/src/sass/components/forms/{_range.scss → _slider.scss} +8 -9
  105. package/src/sass/components/forms/_switches.scss +6 -7
  106. package/src/sass/expressive.scss +5 -0
  107. package/src/sass/tokens/_index.scss +4 -1
  108. package/src/sass/tokens/_reference.scss +70 -61
  109. package/src/sass/tokens/_state.scss +24 -0
  110. package/src/sass/tokens/_theme.scss +6 -3
  111. package/src/sass/tokens/_vibrant.scss +76 -0
  112. package/src/sass/utilities/_colors.scss +14 -1
  113. package/src/sass/utilities/_helpers.scss +5 -5
  114. package/src/sass/utilities/_visibility.scss +48 -26
  115. package/dist/types/behaviors/waves.d.ts +0 -15
  116. package/dist/types/components/parallax.d.ts +0 -16
  117. package/dist/types/components/range.d.ts +0 -43
  118. package/dist/types/core/bounding.d.ts +0 -6
  119. package/dist/types/core/edges.d.ts +0 -6
  120. package/src/sass/components/_parallax.scss +0 -104
  121. package/src/sass/components/_pulse.scss +0 -37
  122. package/src/sass/components/_slider.scss +0 -129
@@ -1,45 +1,5 @@
1
1
  // src/ts/core/utils.ts
2
2
  var _Utils = class _Utils {
3
- /**
4
- * Detects when a key is pressed.
5
- * @param e Event instance.
6
- */
7
- static docHandleKeydown(e) {
8
- _Utils.keyDown = true;
9
- if (_Utils._navigationKeys.has(e.key)) {
10
- _Utils.tabPressed = true;
11
- }
12
- }
13
- /**
14
- * Detects when a key is released.
15
- * @param e Event instance.
16
- */
17
- static docHandleKeyup(e) {
18
- _Utils.keyDown = false;
19
- if (_Utils._navigationKeys.has(e.key)) {
20
- _Utils.tabPressed = false;
21
- }
22
- }
23
- /**
24
- * Detects when document is focused.
25
- * @param e Event instance.
26
- */
27
- /* eslint-disabled as of required event type condition check */
28
- /* eslint-disable-next-line */
29
- static docHandleFocus(e) {
30
- if (_Utils.keyDown) {
31
- document.body.classList.add("keyboard-focused");
32
- }
33
- }
34
- /**
35
- * Detects when document is not focused.
36
- * @param e Event instance.
37
- */
38
- /* eslint-disabled as of required event type condition check */
39
- /* eslint-disable-next-line */
40
- static docHandleBlur(e) {
41
- document.body.classList.remove("keyboard-focused");
42
- }
43
3
  /**
44
4
  * Run `callback` once the document is parsed.
45
5
  *
@@ -57,13 +17,46 @@ var _Utils = class _Utils {
57
17
  }
58
18
  }
59
19
  /**
60
- * Generates a unique string identifier.
20
+ * Generates a unique string identifier. Every caller prefixes the result and
21
+ * uses it as an element id, and a UUID is valid in that position.
22
+ *
23
+ * `randomUUID` is exposed only in secure contexts, and a dev server on a
24
+ * plain-HTTP LAN address - the usual way a page is opened on a phone - is
25
+ * not one. Without the fallback every component that stamps an id would
26
+ * throw there. The ids only have to be unique within one document, so the
27
+ * fallback does not need to be cryptographic.
61
28
  */
62
29
  static guid() {
63
- const s4 = () => {
64
- return Math.floor((1 + Math.random()) * 65536).toString(16).substring(1);
65
- };
66
- return s4() + s4() + "-" + s4() + "-" + s4() + "-" + s4() + "-" + s4() + s4() + s4();
30
+ return globalThis.crypto?.randomUUID?.() ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
31
+ }
32
+ /**
33
+ * Where an element created at runtime has to be appended to stay in the same
34
+ * tree as `el`. A stylesheet adopted into a shadow root cannot match a node
35
+ * in the document, so a portal that lands on `document.body` is unstyled
36
+ * whenever its origin lives in a shadow root (adr/0002).
37
+ *
38
+ * Light-DOM elements still get `document.body`, which is what escapes an
39
+ * ancestor's `overflow: hidden` and stacking context. Inside a shadow root
40
+ * both hazards come back: the host's own ancestors still form the containing
41
+ * block, so a transformed ancestor re-anchors `position: fixed` and a
42
+ * positioned one re-anchors `position: absolute`.
43
+ */
44
+ static portalRoot(el) {
45
+ const root = el.getRootNode();
46
+ return root.host ? root : document.body;
47
+ }
48
+ /**
49
+ * `getElementById` against the tree `el` lives in. The mirror of
50
+ * `portalRoot`: an id in a shadow root is invisible to
51
+ * `document.getElementById`, so a trigger and its target inside the same
52
+ * root could not find each other at all.
53
+ *
54
+ * Falls back to the document for a detached `el`, whose root is a plain
55
+ * element with no lookup of its own.
56
+ */
57
+ static getElementById(el, id) {
58
+ const root = el.getRootNode();
59
+ return root.getElementById ? root.getElementById(id) : document.getElementById(id);
67
60
  }
68
61
  /**
69
62
  * Checks for exceeded edges
@@ -160,43 +153,50 @@ var _Utils = class _Utils {
160
153
  * Retrieves document scroll postion from top.
161
154
  */
162
155
  static getDocumentScrollTop() {
163
- return window.scrollY || document.documentElement.scrollTop || document.body.scrollTop || 0;
156
+ return window.scrollY;
164
157
  }
165
158
  /**
166
159
  * Retrieves document scroll postion from left.
167
160
  */
168
161
  static getDocumentScrollLeft() {
169
- return window.scrollX || document.documentElement.scrollLeft || document.body.scrollLeft || 0;
162
+ return window.scrollX;
170
163
  }
171
164
  /**
172
- * Fires the given function after a certain ammount of time.
173
- * @param func Function to be fired.
174
- * @param wait Wait time.
175
- * @param options Additional options.
165
+ * Trailing-edge throttle: run `func` now, then at most once per `wait` ms
166
+ * with the most recent arguments.
167
+ *
168
+ * This used to carry its underscore ancestor's whole configuration - leading
169
+ * and trailing switches, a result passthrough - and both call sites are
170
+ * resize handlers passing nothing but a function and a delay.
171
+ *
172
+ * Assign the return value once. `x = Utils.throttle(fn, 200)` is right;
173
+ * wrapping it in an arrow builds a fresh closure per event and never calls
174
+ * it, which is how resize handling was dead in three components.
176
175
  */
177
- static throttle(func, wait, options = {}) {
178
- let lastArgs = null, result, timeout = null, previous = 0;
179
- const later = () => {
180
- previous = options?.leading === false ? 0 : Date.now();
181
- timeout = null;
182
- result = func(...lastArgs ?? []);
183
- lastArgs = null;
184
- };
176
+ static throttle(func, wait) {
177
+ let timeout = null;
178
+ let lastArgs = null;
179
+ let previous = 0;
185
180
  return (...args) => {
186
181
  const now = Date.now();
187
- if (!previous && options?.leading === false) previous = now;
188
182
  const remaining = wait - (now - previous);
189
- lastArgs = args;
190
183
  if (remaining <= 0) {
191
184
  clearTimeout(timeout);
192
185
  timeout = null;
193
186
  previous = now;
194
187
  lastArgs = null;
195
- result = func(...args);
196
- } else if (!timeout && options?.trailing !== false) {
197
- timeout = setTimeout(later, remaining);
188
+ func(...args);
189
+ } else if (!timeout) {
190
+ lastArgs = args;
191
+ timeout = setTimeout(() => {
192
+ previous = Date.now();
193
+ timeout = null;
194
+ func(...lastArgs ?? []);
195
+ lastArgs = null;
196
+ }, remaining);
197
+ } else {
198
+ lastArgs = args;
198
199
  }
199
- return result;
200
200
  };
201
201
  }
202
202
  /**
@@ -213,7 +213,7 @@ var _Utils = class _Utils {
213
213
  * Renders a button with optional callback function
214
214
  */
215
215
  static createButton(container, text, className = [], visibility = true, callback = null) {
216
- className = className.concat(["waves-effect", "text"]);
216
+ className = className.concat(["text"]);
217
217
  const button = document.createElement("button");
218
218
  button.className = className.join(" ");
219
219
  button.style.visibility = visibility ? "visible" : "hidden";
@@ -223,7 +223,7 @@ var _Utils = class _Utils {
223
223
  if (typeof callback === "function") {
224
224
  button.addEventListener("click", callback);
225
225
  button.addEventListener("keypress", (e) => {
226
- if (_Utils.keys.ENTER.includes(e.key)) callback(e);
226
+ if (e.key === _Utils.keys.ENTER) callback(e);
227
227
  });
228
228
  }
229
229
  container.append(button);
@@ -302,36 +302,22 @@ var _Utils = class _Utils {
302
302
  };
303
303
  }
304
304
  };
305
- /** Specifies wether tab is pressed or not. */
306
- _Utils.tabPressed = false;
307
- /** Specifies wether there is a key pressed. */
308
- _Utils.keyDown = false;
309
305
  /**
310
- * Key maps.
306
+ * Key maps. One `KeyboardEvent.key` value each: the second entries these used
307
+ * to carry (`Esc`, `Up`, `Del`, ...) were IE and Edge Legacy spellings, and
308
+ * the support baseline excludes both.
311
309
  */
312
310
  _Utils.keys = {
313
- TAB: ["Tab"],
314
- ENTER: ["Enter"],
315
- ESC: ["Escape", "Esc"],
316
- BACKSPACE: ["Backspace"],
317
- ARROW_UP: ["ArrowUp", "Up"],
318
- ARROW_DOWN: ["ArrowDown", "Down"],
319
- ARROW_LEFT: ["ArrowLeft", "Left"],
320
- ARROW_RIGHT: ["ArrowRight", "Right"],
321
- DELETE: ["Delete", "Del"]
311
+ TAB: "Tab",
312
+ ENTER: "Enter",
313
+ ESC: "Escape",
314
+ BACKSPACE: "Backspace",
315
+ ARROW_UP: "ArrowUp",
316
+ ARROW_DOWN: "ArrowDown",
317
+ ARROW_LEFT: "ArrowLeft",
318
+ ARROW_RIGHT: "ArrowRight",
319
+ DELETE: "Delete"
322
320
  };
323
- /**
324
- * Keys that count as keyboard navigation, precomputed.
325
- *
326
- * These handlers run at capture phase on the document for every keystroke on
327
- * the page, and used to rebuild three arrays and spread them into a fourth
328
- * on each one.
329
- */
330
- _Utils._navigationKeys = /* @__PURE__ */ new Set([
331
- ..._Utils.keys.TAB,
332
- ..._Utils.keys.ARROW_DOWN,
333
- ..._Utils.keys.ARROW_UP
334
- ]);
335
321
  var Utils = _Utils;
336
322
 
337
323
  // src/ts/core/component.ts
@@ -394,47 +380,63 @@ var _defaults = {
394
380
  onOpen: null,
395
381
  onClose: null
396
382
  };
397
- var CARDS_SELECTOR = "article:has(> aside)";
383
+ var CARDS_SELECTOR = 'article:has(> aside[id]:not([id=""])):not(:has(> aside ~ aside)):has(> button.card-reveal-trigger[type="button"], > :not(aside) button.card-reveal-trigger[type="button"])';
384
+ var REVEAL_INITIALIZED_CLASS = "card-reveal-initialized";
385
+ function hasAccessibleName(trigger) {
386
+ const label = trigger.getAttribute("aria-label");
387
+ if (label?.trim()) return true;
388
+ const labelledBy = trigger.getAttribute("aria-labelledby");
389
+ if (labelledBy) {
390
+ const referencedText = labelledBy.trim().split(/\s+/).map((id) => trigger.ownerDocument.getElementById(id)?.textContent ?? "").join(" ").trim();
391
+ if (referencedText) return true;
392
+ }
393
+ const content = trigger.cloneNode(true);
394
+ content.querySelectorAll('[aria-hidden="true"]').forEach((node) => node.remove());
395
+ return Boolean(content.textContent?.trim());
396
+ }
397
+ function isUsableCardTrigger(trigger, card, panel) {
398
+ return trigger.closest("article") === card && !panel.contains(trigger) && !trigger.disabled && trigger.getAttribute("aria-disabled") !== "true" && hasAccessibleName(trigger);
399
+ }
400
+ function isCardReveal(el) {
401
+ if (el.tagName !== "ARTICLE") return false;
402
+ const panels = el.querySelectorAll(":scope > aside");
403
+ if (panels.length !== 1 || !panels[0].id) return false;
404
+ return Array.from(
405
+ el.querySelectorAll('button.card-reveal-trigger[type="button"]')
406
+ ).some((trigger) => isUsableCardTrigger(trigger, el, panels[0]));
407
+ }
398
408
  var Cards = class _Cards extends Component {
399
- constructor(el, options) {
409
+ constructor(el, options = {}) {
400
410
  super(el, options, _Cards);
401
411
  this.isOpen = false;
412
+ this._ownsRevealInitializedClass = false;
413
+ this._initialAriaControls = /* @__PURE__ */ new Map();
414
+ this._lastActivator = null;
402
415
  this._setupEventHandlers = () => {
403
- this._activators.forEach((el) => {
404
- el.addEventListener("click", this._handleClickInteraction);
405
- el.addEventListener("keypress", this._handleKeypressEvent);
416
+ this._activators.forEach((activator) => {
417
+ activator.addEventListener("click", this._handleClickInteraction);
406
418
  });
419
+ this.el.addEventListener("keydown", this._handleKeydownEvent);
407
420
  };
408
421
  this._removeEventHandlers = () => {
409
- this._activators.forEach((el) => {
410
- el.removeEventListener("click", this._handleClickInteraction);
411
- el.removeEventListener("keypress", this._handleKeypressEvent);
422
+ this._activators.forEach((activator) => {
423
+ activator.removeEventListener("click", this._handleClickInteraction);
412
424
  });
425
+ this.el.removeEventListener("keydown", this._handleKeydownEvent);
413
426
  };
414
- this._handleClickInteraction = () => {
427
+ this._handleClickInteraction = (event) => {
428
+ this._lastActivator = event.currentTarget;
415
429
  this._handleRevealEvent();
416
430
  };
417
- this._handleKeypressEvent = (e) => {
418
- if (Utils.keys.ENTER.includes(e.key)) {
419
- this._handleRevealEvent();
420
- }
431
+ this._handleKeydownEvent = (event) => {
432
+ const target = event.target instanceof Element ? event.target.closest("article") : null;
433
+ if (target !== this.el || event.key !== "Escape" || !this.isOpen) return;
434
+ event.preventDefault();
435
+ this.close();
421
436
  };
422
437
  this._handleRevealEvent = () => {
423
- this._activators.forEach((el) => el.tabIndex = -1);
424
- this.open();
425
- };
426
- this._setupRevealCloseEventHandlers = () => {
427
- this.cardRevealClose.addEventListener("click", this.close);
428
- this.cardRevealClose.addEventListener("keypress", this._handleKeypressCloseEvent);
429
- };
430
- this._removeRevealCloseEventHandlers = () => {
431
- this.cardRevealClose.removeEventListener("click", this.close);
432
- this.cardRevealClose.removeEventListener("keypress", this._handleKeypressCloseEvent);
433
- };
434
- this._handleKeypressCloseEvent = (e) => {
435
- if (Utils.keys.ENTER.includes(e.key)) {
436
- this.close();
437
- }
438
+ if (this.isOpen) this.close();
439
+ else this.open();
438
440
  };
439
441
  /**
440
442
  * Show card reveal.
@@ -442,44 +444,56 @@ var Cards = class _Cards extends Component {
442
444
  this.open = () => {
443
445
  if (this.isOpen || !this.cardReveal) return;
444
446
  this.isOpen = true;
445
- this.cardReveal.ariaExpanded = "true";
446
- if (this.cardRevealClose) this.cardRevealClose.tabIndex = 0;
447
+ this.cardReveal.classList.add("open");
448
+ this.cardReveal.inert = false;
449
+ this._activators.forEach((activator) => activator.ariaExpanded = "true");
447
450
  if (typeof this.options.onOpen === "function") {
448
- this.options.onOpen.call(this);
451
+ this.options.onOpen.call(this, this.el);
449
452
  }
450
- if (this.cardRevealClose) this._setupRevealCloseEventHandlers();
451
453
  };
452
454
  /**
453
455
  * Hide card reveal.
454
456
  */
455
457
  this.close = () => {
456
458
  if (!this.isOpen || !this.cardReveal) return;
459
+ const focusWasInside = this.cardReveal.contains(document.activeElement);
457
460
  this.isOpen = false;
458
- this.cardReveal.ariaExpanded = "false";
459
- this._activators.forEach((el) => el.tabIndex = 0);
460
- if (this.cardRevealClose) this.cardRevealClose.tabIndex = -1;
461
+ this.cardReveal.classList.remove("open");
462
+ this.cardReveal.inert = true;
463
+ this._activators.forEach((activator) => activator.ariaExpanded = "false");
461
464
  if (typeof this.options.onClose === "function") {
462
- this.options.onClose.call(this);
465
+ this.options.onClose.call(this, this.el);
466
+ }
467
+ if (focusWasInside) {
468
+ (this._lastActivator ?? this._activators[0])?.focus();
463
469
  }
464
- if (this.cardRevealClose) this._removeRevealCloseEventHandlers();
465
470
  };
466
471
  this.el["Expressive_Cards"] = this;
467
472
  this.options = {
468
473
  ..._Cards.defaults,
469
474
  ...options
470
475
  };
471
- this._activators = [];
472
- this.cardReveal = this.el.querySelector(":scope > aside");
476
+ const cardReveals = this.el.querySelectorAll(":scope > aside");
477
+ const cardReveal = cardReveals.length === 1 ? cardReveals[0] : null;
478
+ const activators = cardReveal ? Array.from(
479
+ this.el.querySelectorAll(
480
+ 'button.card-reveal-trigger[type="button"]'
481
+ )
482
+ ).filter(
483
+ (activator) => isUsableCardTrigger(activator, this.el, cardReveal)
484
+ ) : [];
485
+ this.cardReveal = cardReveal?.id && activators.length > 0 ? cardReveal : null;
486
+ this._activators = this.cardReveal ? activators : [];
473
487
  if (this.cardReveal) {
474
- this._activators = Array.from(this.el.querySelectorAll(".activator"));
475
- this._activators.forEach((el2) => {
476
- if (el2) el2.tabIndex = 0;
488
+ this._ownsRevealInitializedClass = !this.el.classList.contains(REVEAL_INITIALIZED_CLASS);
489
+ this.el.classList.add(REVEAL_INITIALIZED_CLASS);
490
+ this._activators.forEach((activator) => {
491
+ this._initialAriaControls.set(activator, activator.getAttribute("aria-controls"));
492
+ activator.ariaExpanded = "false";
493
+ activator.setAttribute("aria-controls", this.cardReveal.id);
477
494
  });
478
- this.cardRevealClose = this.cardReveal.querySelector(
479
- ":scope > :is(h1, h2, h3, h4, h5, h6)"
480
- );
481
- if (this.cardRevealClose) this.cardRevealClose.tabIndex = -1;
482
- this.cardReveal.ariaExpanded = "false";
495
+ this.cardReveal.classList.remove("open");
496
+ this.cardReveal.inert = true;
483
497
  this._setupEventHandlers();
484
498
  }
485
499
  }
@@ -502,13 +516,28 @@ var Cards = class _Cards extends Component {
502
516
  */
503
517
  destroy() {
504
518
  this._removeEventHandlers();
505
- if (this.cardRevealClose) this._removeRevealCloseEventHandlers();
519
+ if (this.cardReveal) {
520
+ this.cardReveal.classList.remove("open");
521
+ this.cardReveal.inert = false;
522
+ if (this._ownsRevealInitializedClass) this.el.classList.remove(REVEAL_INITIALIZED_CLASS);
523
+ }
524
+ this._activators.forEach((activator) => {
525
+ activator.removeAttribute("aria-expanded");
526
+ const initialAriaControls = this._initialAriaControls.get(activator);
527
+ if (initialAriaControls === null) activator.removeAttribute("aria-controls");
528
+ else if (initialAriaControls !== void 0) {
529
+ activator.setAttribute("aria-controls", initialAriaControls);
530
+ }
531
+ });
532
+ this.isOpen = false;
506
533
  this._activators = [];
534
+ this._initialAriaControls.clear();
535
+ this._lastActivator = null;
507
536
  this.el["Expressive_Cards"] = void 0;
508
537
  }
509
538
  static Init() {
510
539
  Utils.onDocumentReady(() => {
511
- const cards = document.querySelectorAll(CARDS_SELECTOR);
540
+ const cards = Array.from(document.querySelectorAll(CARDS_SELECTOR)).filter(isCardReveal);
512
541
  cards.forEach((el) => {
513
542
  if (el && el["Expressive_Cards"] == void 0) this.init(el);
514
543
  });
@@ -516,8 +545,173 @@ var Cards = class _Cards extends Component {
516
545
  }
517
546
  };
518
547
 
519
- // src/ts/components/menu.ts
548
+ // src/ts/components/expandingCard.ts
520
549
  var _defaults2 = {
550
+ onOpen: null,
551
+ onClose: null
552
+ };
553
+ var EXPANDING_CARD_SELECTOR = "article.expanding-card:has(> dialog.expanding-card-dialog)";
554
+ var ExpandingCard = class _ExpandingCard extends Component {
555
+ constructor(el, options) {
556
+ super(el, options, _ExpandingCard);
557
+ this.isOpen = false;
558
+ this.closeGeneration = 0;
559
+ this._setupEventHandlers = () => {
560
+ this.trigger?.addEventListener("click", this.open);
561
+ this.trigger?.addEventListener("keypress", this._handleTriggerKeypress);
562
+ this.closeButton?.addEventListener("click", this.close);
563
+ this.dialog?.addEventListener("cancel", this._handleCancel);
564
+ this.dialog?.addEventListener("close", this._handleNativeClose);
565
+ };
566
+ this._removeEventHandlers = () => {
567
+ this.trigger?.removeEventListener("click", this.open);
568
+ this.trigger?.removeEventListener("keypress", this._handleTriggerKeypress);
569
+ this.closeButton?.removeEventListener("click", this.close);
570
+ this.dialog?.removeEventListener("cancel", this._handleCancel);
571
+ this.dialog?.removeEventListener("close", this._handleNativeClose);
572
+ };
573
+ this._handleTriggerKeypress = (event) => {
574
+ if (event.key === Utils.keys.ENTER || event.key === " ") {
575
+ event.preventDefault();
576
+ this.open();
577
+ }
578
+ };
579
+ this._handleCancel = (event) => {
580
+ event.preventDefault();
581
+ this.close();
582
+ };
583
+ this._handleNativeClose = () => {
584
+ if (this.dialog.open) return;
585
+ this.closeGeneration++;
586
+ this.isOpen = false;
587
+ this.trigger.ariaExpanded = "false";
588
+ this.dialog.ariaExpanded = "false";
589
+ this.dialog.classList.remove("expanded");
590
+ };
591
+ this._setTransitionOrigin = () => {
592
+ if (!this.dialog) return;
593
+ const card = this.el.getBoundingClientRect();
594
+ const media = this.el.querySelector(
595
+ ":scope > figure, :scope > img"
596
+ );
597
+ const mediaRect = media?.getBoundingClientRect();
598
+ this.dialog.style.setProperty(
599
+ "--md-comp-expanding-card-from-top",
600
+ `${Math.max(0, card.top)}px`
601
+ );
602
+ this.dialog.style.setProperty(
603
+ "--md-comp-expanding-card-from-right",
604
+ `${Math.max(0, window.innerWidth - card.right)}px`
605
+ );
606
+ this.dialog.style.setProperty(
607
+ "--md-comp-expanding-card-from-bottom",
608
+ `${Math.max(0, window.innerHeight - card.bottom)}px`
609
+ );
610
+ this.dialog.style.setProperty(
611
+ "--md-comp-expanding-card-from-left",
612
+ `${Math.max(0, card.left)}px`
613
+ );
614
+ this.dialog.style.setProperty(
615
+ "--md-comp-expanding-card-from-media-height",
616
+ `${Math.max(0, mediaRect?.height ?? 0)}px`
617
+ );
618
+ };
619
+ this.open = () => {
620
+ if (this.isOpen || !this.dialog || !this.trigger) return;
621
+ this.closeGeneration++;
622
+ this._setTransitionOrigin();
623
+ this.isOpen = true;
624
+ this.trigger.ariaExpanded = "true";
625
+ this.dialog.ariaExpanded = "true";
626
+ this.dialog.showModal();
627
+ void this.dialog.offsetWidth;
628
+ this.dialog.classList.add("expanded");
629
+ this.closeButton?.focus();
630
+ if (typeof this.options.onOpen === "function") {
631
+ this.options.onOpen.call(this, this.el);
632
+ }
633
+ };
634
+ this.close = () => {
635
+ if (!this.isOpen || !this.dialog || !this.trigger) return;
636
+ const generation = ++this.closeGeneration;
637
+ this._setTransitionOrigin();
638
+ this.isOpen = false;
639
+ this.trigger.ariaExpanded = "false";
640
+ this.dialog.ariaExpanded = "false";
641
+ this.dialog.classList.remove("expanded");
642
+ if (typeof this.options.onClose === "function") {
643
+ this.options.onClose.call(this, this.el);
644
+ }
645
+ const finish = () => {
646
+ if (generation !== this.closeGeneration || this.isOpen || !this.dialog.open) return;
647
+ this.dialog.close();
648
+ if (this.dialog.isConnected && this.trigger.isConnected) this.trigger.focus();
649
+ };
650
+ const reducedMotion = window.matchMedia(
651
+ "(prefers-reduced-motion: reduce)"
652
+ ).matches;
653
+ const transitions = reducedMotion ? [] : (this.dialog.getAnimations?.() ?? []).filter((animation) => animation.transitionProperty === "clip-path");
654
+ if (transitions.length === 0) finish();
655
+ else void Promise.allSettled(transitions.map((animation) => animation.finished)).then(finish);
656
+ };
657
+ this.el["Expressive_ExpandingCard"] = this;
658
+ this.options = {
659
+ ..._ExpandingCard.defaults,
660
+ ...options
661
+ };
662
+ this.dialog = this.el.querySelector(
663
+ ":scope > dialog.expanding-card-dialog"
664
+ );
665
+ this.trigger = this.el.querySelector(
666
+ ":scope > .expanding-card-trigger, :scope > figure > .expanding-card-trigger"
667
+ );
668
+ this.closeButton = this.dialog?.querySelector(".expanding-card-close") ?? null;
669
+ if (this.trigger && this.dialog) {
670
+ this.trigger.ariaExpanded = "false";
671
+ if (this.dialog.id)
672
+ this.trigger.setAttribute("aria-controls", this.dialog.id);
673
+ if (!this.trigger.matches("a[href], button, input, select, textarea")) {
674
+ this.trigger.tabIndex = 0;
675
+ this.trigger.setAttribute("role", "button");
676
+ }
677
+ this.dialog.ariaExpanded = "false";
678
+ this._setupEventHandlers();
679
+ }
680
+ }
681
+ static get defaults() {
682
+ return _defaults2;
683
+ }
684
+ static init(els, options) {
685
+ return super.init(els, options, _ExpandingCard);
686
+ }
687
+ static getInstance(el) {
688
+ return el["Expressive_ExpandingCard"];
689
+ }
690
+ destroy() {
691
+ this._removeEventHandlers();
692
+ this.closeGeneration++;
693
+ this.isOpen = false;
694
+ if (this.trigger) this.trigger.ariaExpanded = "false";
695
+ if (this.dialog) this.dialog.ariaExpanded = "false";
696
+ this.dialog?.classList.remove("expanded");
697
+ if (this.dialog?.open) this.dialog.close();
698
+ this.el["Expressive_ExpandingCard"] = void 0;
699
+ }
700
+ static Init() {
701
+ Utils.onDocumentReady(() => {
702
+ document.querySelectorAll(EXPANDING_CARD_SELECTOR).forEach((el) => {
703
+ if (el && el["Expressive_ExpandingCard"] == void 0) {
704
+ this.init(el);
705
+ }
706
+ });
707
+ });
708
+ }
709
+ };
710
+
711
+ // src/ts/components/menu.ts
712
+ var MIN_SURFACE_WIDTH = 112;
713
+ var MAX_SURFACE_WIDTH = 280;
714
+ var _defaults3 = {
521
715
  alignment: "left",
522
716
  autoFocus: true,
523
717
  constrainWidth: true,
@@ -533,9 +727,12 @@ var _defaults2 = {
533
727
  onCloseEnd: null,
534
728
  onItemClick: null
535
729
  };
730
+ var SPACE = " ";
536
731
  var _Menu = class _Menu extends Component {
537
732
  constructor(el, options) {
538
733
  super(el, options, _Menu);
734
+ this._pendingTimers = /* @__PURE__ */ new Set();
735
+ this._transition = 0;
539
736
  this._handleClick = (e) => {
540
737
  e.preventDefault();
541
738
  if (this.isOpen) {
@@ -569,7 +766,7 @@ var _Menu = class _Menu extends Component {
569
766
  if (this.options.closeOnClick && this.menuEl?.contains(target) && !this.isTouchMoving) {
570
767
  this.close();
571
768
  } else if (!this.menuEl?.contains(target)) {
572
- setTimeout(() => {
769
+ this._schedule(() => {
573
770
  if (this.isOpen) {
574
771
  this.close();
575
772
  }
@@ -578,8 +775,8 @@ var _Menu = class _Menu extends Component {
578
775
  this.isTouchMoving = false;
579
776
  };
580
777
  this._handleTriggerKeydown = (e) => {
581
- const arrowDownOrEnter = Utils.keys.ARROW_DOWN.includes(e.key) || Utils.keys.ENTER.includes(e.key);
582
- if (arrowDownOrEnter && !this.isOpen) {
778
+ const opensMenu = e.key === SPACE || e.key === Utils.keys.ENTER || e.key === Utils.keys.ARROW_DOWN || e.key === Utils.keys.ARROW_UP;
779
+ if (opensMenu && !this.isOpen) {
583
780
  e.preventDefault();
584
781
  this.open();
585
782
  }
@@ -593,13 +790,18 @@ var _Menu = class _Menu extends Component {
593
790
  this._handleMenuClick = (e) => {
594
791
  const target = e.target;
595
792
  const li = target.closest("li");
793
+ if (li?.matches('.disabled, [aria-disabled="true"]')) {
794
+ e.preventDefault();
795
+ e.stopPropagation();
796
+ return;
797
+ }
596
798
  if (li && this._isSubmenuTriggerClick(target)) {
597
799
  e.preventDefault();
598
800
  e.stopPropagation();
599
801
  this._toggleSubmenu(li);
600
802
  return;
601
803
  }
602
- if (typeof this.options.onItemClick === "function") {
804
+ if (li && typeof this.options.onItemClick === "function") {
603
805
  this.options.onItemClick.call(this, li);
604
806
  }
605
807
  };
@@ -612,11 +814,11 @@ var _Menu = class _Menu extends Component {
612
814
  }
613
815
  };
614
816
  this._handleMenuKeydown = (e) => {
615
- const arrowUpOrDown = Utils.keys.ARROW_DOWN.includes(e.key) || Utils.keys.ARROW_UP.includes(e.key);
616
- if (Utils.keys.TAB.includes(e.key)) {
817
+ const arrowUpOrDown = e.key === Utils.keys.ARROW_DOWN || e.key === Utils.keys.ARROW_UP;
818
+ if (e.key === Utils.keys.TAB) {
617
819
  e.preventDefault();
618
820
  this.close();
619
- } else if (Utils.keys.ARROW_RIGHT.includes(e.key) && this.isOpen) {
821
+ } else if (e.key === Utils.keys.ARROW_RIGHT && this.isOpen) {
620
822
  const li = this._focusedRow(e);
621
823
  if (li?.querySelector(":scope > menu")) {
622
824
  e.preventDefault();
@@ -626,7 +828,7 @@ var _Menu = class _Menu extends Component {
626
828
  const first = li.querySelector(":scope > menu > li");
627
829
  first?.focus();
628
830
  }
629
- } else if (Utils.keys.ARROW_LEFT.includes(e.key) && this.isOpen) {
831
+ } else if (e.key === Utils.keys.ARROW_LEFT && this.isOpen) {
630
832
  const submenu = this._containingSubmenu(e);
631
833
  if (submenu) {
632
834
  e.preventDefault();
@@ -637,7 +839,7 @@ var _Menu = class _Menu extends Component {
637
839
  }
638
840
  } else if (arrowUpOrDown && this.isOpen) {
639
841
  e.preventDefault();
640
- const direction = Utils.keys.ARROW_DOWN.includes(e.key) ? 1 : -1;
842
+ const direction = e.key === Utils.keys.ARROW_DOWN ? 1 : -1;
641
843
  const list = this._containingSubmenu(e) ?? this.menuEl;
642
844
  let newFocusedIndex = this.focusedIndex;
643
845
  let hasFoundNewIndex = false;
@@ -649,17 +851,17 @@ var _Menu = class _Menu extends Component {
649
851
  }
650
852
  } while (newFocusedIndex < list.children.length && newFocusedIndex >= 0);
651
853
  if (hasFoundNewIndex) {
652
- if (this.focusedIndex >= 0)
653
- list.children[this.focusedIndex]?.classList.remove("active");
854
+ if (this.focusedIndex >= 0) list.children[this.focusedIndex]?.classList.remove("active");
654
855
  this.focusedIndex = newFocusedIndex;
655
856
  const item = list.children[this.focusedIndex];
656
857
  item.classList.add("active");
657
858
  item.focus({ preventScroll: true });
658
859
  }
659
- } else if (Utils.keys.ENTER.includes(e.key) && this.isOpen) {
860
+ } else if ((e.key === Utils.keys.ENTER || e.key === SPACE) && this.isOpen) {
660
861
  const li = this._focusedRow(e);
862
+ e.preventDefault();
863
+ if (li?.matches('.disabled, [aria-disabled="true"]')) return;
661
864
  if (li?.querySelector(":scope > menu")) {
662
- e.preventDefault();
663
865
  this._toggleSubmenu(li);
664
866
  return;
665
867
  }
@@ -670,7 +872,7 @@ var _Menu = class _Menu extends Component {
670
872
  } else if (focusedElement instanceof HTMLElement) {
671
873
  focusedElement.click();
672
874
  }
673
- } else if (Utils.keys.ESC.includes(e.key) && this.isOpen) {
875
+ } else if (e.key === Utils.keys.ESC && this.isOpen) {
674
876
  e.preventDefault();
675
877
  const openSub = this.menuEl.querySelector("li.open");
676
878
  if (openSub) {
@@ -684,11 +886,12 @@ var _Menu = class _Menu extends Component {
684
886
  const keyText = e.key.toLowerCase();
685
887
  const isLetter = /[a-zA-Z0-9-_]/.test(keyText);
686
888
  const specialKeys = [
687
- ...Utils.keys.ARROW_DOWN,
688
- ...Utils.keys.ARROW_UP,
689
- ...Utils.keys.ENTER,
690
- ...Utils.keys.ESC,
691
- ...Utils.keys.TAB
889
+ Utils.keys.ARROW_DOWN,
890
+ Utils.keys.ARROW_UP,
891
+ Utils.keys.ENTER,
892
+ Utils.keys.ESC,
893
+ Utils.keys.TAB,
894
+ SPACE
692
895
  ];
693
896
  if (isLetter && !specialKeys.includes(e.key)) {
694
897
  this.filterQuery.push(keyText);
@@ -717,16 +920,18 @@ var _Menu = class _Menu extends Component {
717
920
  * Open menu.
718
921
  */
719
922
  this.open = () => {
720
- if (this.isOpen) return;
923
+ if (this.isOpen || this.el["Expressive_Menu"] !== this) return;
924
+ const transition = this._clearPendingTimers();
721
925
  this.isOpen = true;
722
926
  if (typeof this.options.onOpenStart === "function") {
723
927
  this.options.onOpenStart.call(this, this.el);
724
928
  }
929
+ if (transition !== this._transition) return;
725
930
  this._resetMenuStyles();
726
931
  this.menuEl.style.display = "block";
727
932
  this._placeMenu();
933
+ this._schedule(() => this._setupTemporaryEventHandlers(), 0);
728
934
  this._animateIn();
729
- setTimeout(() => this._setupTemporaryEventHandlers(), 0);
730
935
  this.el.ariaExpanded = "true";
731
936
  };
732
937
  /**
@@ -734,18 +939,20 @@ var _Menu = class _Menu extends Component {
734
939
  */
735
940
  this.close = () => {
736
941
  if (!this.isOpen) return;
942
+ const transition = this._clearPendingTimers();
737
943
  this.isOpen = false;
738
944
  this.focusedIndex = -1;
739
945
  this._closeSubmenus();
740
946
  if (typeof this.options.onCloseStart === "function") {
741
947
  this.options.onCloseStart.call(this, this.el);
742
948
  }
949
+ if (transition !== this._transition) return;
743
950
  this._animateOut();
744
951
  this._removeTemporaryEventHandlers();
952
+ this.el.ariaExpanded = "false";
745
953
  if (this.options.autoFocus) {
746
954
  this.el.focus();
747
955
  }
748
- this.el.ariaExpanded = "false";
749
956
  };
750
957
  /**
751
958
  * While menu is open, you can recalculate its dimensions if its contents have changed.
@@ -759,7 +966,7 @@ var _Menu = class _Menu extends Component {
759
966
  this.el["Expressive_Menu"] = this;
760
967
  _Menu._menus.push(this);
761
968
  this.id = Utils.getIdFromTrigger(el);
762
- this.menuEl = document.getElementById(this.id);
969
+ this.menuEl = Utils.getElementById(el, this.id);
763
970
  this.options = {
764
971
  ..._Menu.defaults,
765
972
  ...options
@@ -770,13 +977,17 @@ var _Menu = class _Menu extends Component {
770
977
  this.focusedIndex = -1;
771
978
  this.filterQuery = [];
772
979
  this.el.ariaExpanded = "false";
980
+ if (!this.el.hasAttribute("aria-haspopup")) this.el.setAttribute("aria-haspopup", "menu");
981
+ if (this.id && !this.el.hasAttribute("aria-controls"))
982
+ this.el.setAttribute("aria-controls", this.id);
773
983
  this._moveMenuToElement();
984
+ this._setupAccessibility();
774
985
  this._makeMenuFocusable();
775
986
  this._setupSubmenus();
776
987
  this._setupEventHandlers();
777
988
  }
778
989
  static get defaults() {
779
- return _defaults2;
990
+ return _defaults3;
780
991
  }
781
992
  /**
782
993
  * Initializes instances of Menu.
@@ -790,6 +1001,9 @@ var _Menu = class _Menu extends Component {
790
1001
  return el["Expressive_Menu"];
791
1002
  }
792
1003
  destroy() {
1004
+ this._clearPendingTimers();
1005
+ this.isOpen = false;
1006
+ this.el.ariaExpanded = "false";
793
1007
  clearTimeout(this.filterTimeout);
794
1008
  this._resetMenuStyles();
795
1009
  this._removeEventHandlers();
@@ -826,7 +1040,9 @@ var _Menu = class _Menu extends Component {
826
1040
  }
827
1041
  _setupTemporaryEventHandlers() {
828
1042
  document.body.addEventListener("click", this._handleDocumentClick);
829
- document.body.addEventListener("touchmove", this._handleDocumentTouchmove, { passive: true });
1043
+ document.body.addEventListener("touchmove", this._handleDocumentTouchmove, {
1044
+ passive: true
1045
+ });
830
1046
  this.menuEl?.addEventListener("keydown", this._handleMenuKeydown);
831
1047
  window.addEventListener("resize", this._handleWindowResize);
832
1048
  }
@@ -836,6 +1052,33 @@ var _Menu = class _Menu extends Component {
836
1052
  this.menuEl?.removeEventListener("keydown", this._handleMenuKeydown);
837
1053
  window.removeEventListener("resize", this._handleWindowResize);
838
1054
  }
1055
+ _setupAccessibility() {
1056
+ if (!this.menuEl) return;
1057
+ if (!this.menuEl.hasAttribute("role")) this.menuEl.setAttribute("role", "menu");
1058
+ const menus = [this.menuEl, ...this.menuEl.querySelectorAll("menu")];
1059
+ menus.forEach((menu) => {
1060
+ if (!menu.hasAttribute("role")) menu.setAttribute("role", "menu");
1061
+ if (menu.getAttribute("role") !== "menu") return;
1062
+ menu.querySelectorAll(":scope > li").forEach((li) => {
1063
+ if (li.classList.contains("divider")) {
1064
+ if (!li.hasAttribute("role")) li.setAttribute("role", "separator");
1065
+ return;
1066
+ }
1067
+ if (li.classList.contains("gap")) {
1068
+ li.setAttribute("aria-hidden", "true");
1069
+ return;
1070
+ }
1071
+ if (li.classList.contains("label") || li.classList.contains("optgroup")) {
1072
+ if (!li.hasAttribute("role")) li.setAttribute("role", "presentation");
1073
+ return;
1074
+ }
1075
+ if (!li.hasAttribute("role")) li.setAttribute("role", "menuitem");
1076
+ if (li.classList.contains("disabled") && !li.hasAttribute("aria-disabled")) {
1077
+ li.setAttribute("aria-disabled", "true");
1078
+ }
1079
+ });
1080
+ });
1081
+ }
839
1082
  _isSubmenuTriggerClick(target) {
840
1083
  if (!this.menuEl) return false;
841
1084
  const li = target.closest("li");
@@ -922,7 +1165,7 @@ var _Menu = class _Menu extends Component {
922
1165
  _makeMenuFocusable() {
923
1166
  if (!this.menuEl) return;
924
1167
  this.menuEl.popover = "";
925
- this.menuEl.tabIndex = 0;
1168
+ this.menuEl.tabIndex = -1;
926
1169
  this.menuEl.querySelectorAll(":scope li, :scope > hr").forEach((el) => {
927
1170
  if (el instanceof HTMLHRElement || el.classList.contains("divider") || el.classList.contains("gap") || el.classList.contains("label") || el.classList.contains("optgroup")) {
928
1171
  el.setAttribute("tabindex", "-1");
@@ -931,6 +1174,15 @@ var _Menu = class _Menu extends Component {
931
1174
  if (!el.getAttribute("tabindex")) el.setAttribute("tabindex", "0");
932
1175
  });
933
1176
  }
1177
+ _focusInitialItem() {
1178
+ if (!this.menuEl || !this.options.autoFocus) return;
1179
+ const first = Array.from(this.menuEl.children).find(
1180
+ (child) => child instanceof HTMLElement && child.tabIndex !== -1
1181
+ );
1182
+ if (!first) return;
1183
+ this.focusedIndex = Array.from(this.menuEl.children).indexOf(first);
1184
+ first.focus({ preventScroll: true });
1185
+ }
934
1186
  _focusFocusedItem() {
935
1187
  if (this.focusedIndex >= 0 && this.focusedIndex < this.menuEl.children.length && this.options.autoFocus) {
936
1188
  this.menuEl.children[this.focusedIndex].focus({
@@ -1014,18 +1266,32 @@ var _Menu = class _Menu extends Component {
1014
1266
  width: idealWidth
1015
1267
  };
1016
1268
  }
1269
+ _schedule(callback, delay) {
1270
+ const timer = setTimeout(() => {
1271
+ this._pendingTimers.delete(timer);
1272
+ callback();
1273
+ }, delay);
1274
+ this._pendingTimers.add(timer);
1275
+ }
1276
+ _clearPendingTimers() {
1277
+ for (const timer of this._pendingTimers) clearTimeout(timer);
1278
+ this._pendingTimers.clear();
1279
+ return ++this._transition;
1280
+ }
1017
1281
  _animateIn() {
1282
+ const transition = this._transition;
1018
1283
  const duration = this.options.inDuration;
1019
1284
  this.menuEl.style.transition = "none";
1020
1285
  this.menuEl.style.opacity = "0";
1021
1286
  this.menuEl.style.transform = "scale(0.85)";
1022
- setTimeout(() => {
1287
+ this._schedule(() => {
1023
1288
  this.menuEl.style.transition = `opacity ${duration}ms ease, transform ${duration}ms ease`;
1024
1289
  this.menuEl.style.opacity = "1";
1025
1290
  this.menuEl.style.transform = "scale(1)";
1026
1291
  }, 1);
1027
- setTimeout(() => {
1028
- if (this.options.autoFocus) this.menuEl.focus();
1292
+ this._schedule(() => {
1293
+ this._focusInitialItem();
1294
+ if (transition !== this._transition) return;
1029
1295
  if (typeof this.options.onOpenEnd === "function") this.options.onOpenEnd.call(this, this.el);
1030
1296
  }, duration);
1031
1297
  }
@@ -1034,7 +1300,7 @@ var _Menu = class _Menu extends Component {
1034
1300
  this.menuEl.style.transition = `opacity ${duration}ms ease, transform ${duration}ms ease`;
1035
1301
  this.menuEl.style.opacity = "0";
1036
1302
  this.menuEl.style.transform = "scale(0.85)";
1037
- setTimeout(() => {
1303
+ this._schedule(() => {
1038
1304
  this._resetMenuStyles();
1039
1305
  if (typeof this.options.onCloseEnd === "function")
1040
1306
  this.options.onCloseEnd.call(this, this.el);
@@ -1042,11 +1308,11 @@ var _Menu = class _Menu extends Component {
1042
1308
  }
1043
1309
  _getClosestAncestor(el, condition) {
1044
1310
  let ancestor = el.parentNode;
1045
- while (ancestor !== null && ancestor !== document) {
1046
- if (condition(ancestor)) {
1311
+ while (ancestor !== null && ancestor !== void 0 && ancestor !== document) {
1312
+ if (ancestor.nodeType === Node.ELEMENT_NODE && condition(ancestor)) {
1047
1313
  return ancestor;
1048
1314
  }
1049
- ancestor = ancestor.parentElement;
1315
+ ancestor = ancestor.parentNode ?? ancestor.host;
1050
1316
  }
1051
1317
  return null;
1052
1318
  }
@@ -1058,12 +1324,14 @@ var _Menu = class _Menu extends Component {
1058
1324
  }
1059
1325
  );
1060
1326
  if (!closestOverflowParent) {
1061
- closestOverflowParent = !!this.menuEl.offsetParent ? this.menuEl.offsetParent : this.menuEl.parentNode;
1327
+ const parent = !!this.menuEl.offsetParent ? this.menuEl.offsetParent : this.menuEl.parentNode;
1328
+ closestOverflowParent = parent.host ?? parent;
1062
1329
  }
1063
1330
  if (getComputedStyle(closestOverflowParent).position === "static")
1064
1331
  closestOverflowParent.style.position = "relative";
1065
1332
  const natural = this.menuEl.getBoundingClientRect().width;
1066
- const idealWidth = this.options.constrainWidth ? this.el.getBoundingClientRect().width : Math.min(280, Math.max(112, natural));
1333
+ const triggerWidth = this.el.getBoundingClientRect().width;
1334
+ const idealWidth = this.options.constrainWidth && triggerWidth >= MIN_SURFACE_WIDTH ? triggerWidth : Math.min(MAX_SURFACE_WIDTH, Math.max(MIN_SURFACE_WIDTH, natural));
1067
1335
  this.menuEl.style.width = idealWidth + "px";
1068
1336
  const positionInfo = this._getMenuPosition(closestOverflowParent);
1069
1337
  this.menuEl.style.left = positionInfo.x + "px";
@@ -1077,7 +1345,7 @@ _Menu._menus = [];
1077
1345
  var Menu = _Menu;
1078
1346
 
1079
1347
  // src/ts/components/autocomplete.ts
1080
- var _defaults3 = {
1348
+ var _defaults4 = {
1081
1349
  data: [],
1082
1350
  // Autocomplete data set
1083
1351
  onAutocomplete: null,
@@ -1107,7 +1375,7 @@ var _Autocomplete = class _Autocomplete extends Component {
1107
1375
  constructor(el, options) {
1108
1376
  super(el, options, _Autocomplete);
1109
1377
  this._handleInputBlur = () => {
1110
- if (!this._mousedown) {
1378
+ if (!this._pointerDown) {
1111
1379
  this.close();
1112
1380
  this._resetAutocomplete();
1113
1381
  }
@@ -1116,9 +1384,9 @@ var _Autocomplete = class _Autocomplete extends Component {
1116
1384
  if (e.type === "keyup") _Autocomplete._keydown = false;
1117
1385
  this.count = 0;
1118
1386
  const actualValue = this.el.value.toLocaleLowerCase();
1119
- if (Utils.keys.ENTER.includes(e.key) || Utils.keys.ARROW_UP.includes(e.key) || Utils.keys.ARROW_DOWN.includes(e.key))
1387
+ if (e.key === Utils.keys.ENTER || e.key === Utils.keys.ARROW_UP || e.key === Utils.keys.ARROW_DOWN)
1120
1388
  return;
1121
- if (this.oldVal !== actualValue && Utils.tabPressed) {
1389
+ if (this.oldVal !== actualValue && this.el.matches(":focus-visible")) {
1122
1390
  this.open();
1123
1391
  }
1124
1392
  this._inputChangeDetection(actualValue);
@@ -1142,7 +1410,7 @@ var _Autocomplete = class _Autocomplete extends Component {
1142
1410
  this._handleInputKeydown = (e) => {
1143
1411
  _Autocomplete._keydown = true;
1144
1412
  const numItems = this.container.querySelectorAll("li").length;
1145
- if (Utils.keys.ENTER.includes(e.key) && this.activeIndex >= 0) {
1413
+ if (e.key === Utils.keys.ENTER && this.activeIndex >= 0) {
1146
1414
  const liElement = this.container.querySelectorAll("li")[this.activeIndex];
1147
1415
  if (liElement) {
1148
1416
  this.selectOption(liElement.getAttribute("data-id"));
@@ -1150,15 +1418,14 @@ var _Autocomplete = class _Autocomplete extends Component {
1150
1418
  }
1151
1419
  return;
1152
1420
  }
1153
- if (Utils.keys.ARROW_UP.includes(e.key) || Utils.keys.ARROW_DOWN.includes(e.key)) {
1421
+ if (e.key === Utils.keys.ARROW_UP || e.key === Utils.keys.ARROW_DOWN) {
1154
1422
  e.preventDefault();
1155
- if (Utils.keys.ARROW_UP.includes(e.key) && this.activeIndex > 0) this.activeIndex--;
1156
- if (Utils.keys.ARROW_DOWN.includes(e.key) && this.activeIndex < numItems - 1)
1423
+ if (e.key === Utils.keys.ARROW_UP && this.activeIndex > 0) this.activeIndex--;
1424
+ if (e.key === Utils.keys.ARROW_DOWN && this.activeIndex < numItems - 1)
1157
1425
  this.activeIndex++;
1158
- this.$active?.classList.remove("active");
1426
+ this._setActive(null);
1159
1427
  if (this.activeIndex >= 0) {
1160
- this.$active = this.container.querySelectorAll("li")[this.activeIndex];
1161
- this.$active?.classList.add("active");
1428
+ this._setActive(this.container.querySelectorAll("li")[this.activeIndex]);
1162
1429
  this.container.children[this.activeIndex].scrollIntoView({
1163
1430
  behavior: "smooth",
1164
1431
  block: "nearest",
@@ -1170,11 +1437,17 @@ var _Autocomplete = class _Autocomplete extends Component {
1170
1437
  this._handleInputClick = () => {
1171
1438
  this.open();
1172
1439
  };
1173
- this._handleContainerMousedownAndTouchstart = () => {
1174
- this._mousedown = true;
1440
+ // The flag says "a press is in flight inside the option list", so that a blur
1441
+ // caused by that press does not close the list out from under it. Without the
1442
+ // cancel half, a cancelled gesture left the flag set and the next blur could
1443
+ // not close the list at all.
1444
+ this._handleContainerPointerDown = (e) => {
1445
+ if (!e.isPrimary) return;
1446
+ if (e.pointerType === "mouse" && e.button !== 0) return;
1447
+ this._pointerDown = true;
1175
1448
  };
1176
- this._handleContainerMouseupAndTouchend = () => {
1177
- this._mousedown = false;
1449
+ this._handleContainerPointerUp = () => {
1450
+ this._pointerDown = false;
1178
1451
  };
1179
1452
  /**
1180
1453
  * Show autocomplete.
@@ -1187,7 +1460,9 @@ var _Autocomplete = class _Autocomplete extends Component {
1187
1460
  this._renderMenu();
1188
1461
  }
1189
1462
  if (!this.menu.isOpen) {
1190
- setTimeout(() => {
1463
+ if (this._openTimer !== void 0) clearTimeout(this._openTimer);
1464
+ this._openTimer = setTimeout(() => {
1465
+ this._openTimer = void 0;
1191
1466
  this.menu.open();
1192
1467
  }, 0);
1193
1468
  } else this.menu.recalculateDimensions();
@@ -1210,12 +1485,32 @@ var _Autocomplete = class _Autocomplete extends Component {
1210
1485
  this.selectedValues = this.selectedValues || this.options.selected.map((value) => ({ id: value })) || [];
1211
1486
  this.menuItems = this.options.data || [];
1212
1487
  this.$active = null;
1213
- this._mousedown = false;
1488
+ this._pointerDown = false;
1214
1489
  this._setupMenu();
1215
1490
  this._setupEventHandlers();
1216
1491
  }
1492
+ /**
1493
+ * The active entry, for both the eye and the screen reader. The class alone
1494
+ * was invisible to assistive technology; aria-activedescendant on the input
1495
+ * is what reports the move.
1496
+ *
1497
+ * Deliberately does not touch aria-selected. Active and selected are two
1498
+ * different things - in a multi-select the checkboxes track real choices,
1499
+ * and folding the highlight into aria-selected would announce a committed
1500
+ * choice as unselected the moment the user arrowed past it.
1501
+ */
1502
+ _setActive(item) {
1503
+ this.$active?.classList.remove("active");
1504
+ this.$active = item;
1505
+ if (item) {
1506
+ item.classList.add("active");
1507
+ this.el.setAttribute("aria-activedescendant", item.id);
1508
+ } else {
1509
+ this.el.removeAttribute("aria-activedescendant");
1510
+ }
1511
+ }
1217
1512
  static get defaults() {
1218
- return _defaults3;
1513
+ return _defaults4;
1219
1514
  }
1220
1515
  /**
1221
1516
  * Initializes instances of Autocomplete.
@@ -1241,6 +1536,10 @@ var _Autocomplete = class _Autocomplete extends Component {
1241
1536
  return _Autocomplete._loadingTemplate.cloneNode(true);
1242
1537
  }
1243
1538
  destroy() {
1539
+ if (this._openTimer !== void 0) {
1540
+ clearTimeout(this._openTimer);
1541
+ this._openTimer = void 0;
1542
+ }
1244
1543
  this._removeEventHandlers();
1245
1544
  this._removeMenu();
1246
1545
  this.el["Expressive_Autocomplete"] = void 0;
@@ -1251,12 +1550,9 @@ var _Autocomplete = class _Autocomplete extends Component {
1251
1550
  this.el.addEventListener("focus", this._handleInputFocus);
1252
1551
  this.el.addEventListener("keydown", this._handleInputKeydown);
1253
1552
  this.el.addEventListener("click", this._handleInputClick);
1254
- this.container.addEventListener("mousedown", this._handleContainerMousedownAndTouchstart);
1255
- this.container.addEventListener("mouseup", this._handleContainerMouseupAndTouchend);
1256
- if (typeof window.ontouchstart !== "undefined") {
1257
- this.container.addEventListener("touchstart", this._handleContainerMousedownAndTouchstart);
1258
- this.container.addEventListener("touchend", this._handleContainerMouseupAndTouchend);
1259
- }
1553
+ this.container.addEventListener("pointerdown", this._handleContainerPointerDown);
1554
+ this.container.addEventListener("pointerup", this._handleContainerPointerUp);
1555
+ this.container.addEventListener("pointercancel", this._handleContainerPointerUp);
1260
1556
  }
1261
1557
  _removeEventHandlers() {
1262
1558
  this.el.removeEventListener("blur", this._handleInputBlur);
@@ -1264,12 +1560,9 @@ var _Autocomplete = class _Autocomplete extends Component {
1264
1560
  this.el.removeEventListener("focus", this._handleInputFocus);
1265
1561
  this.el.removeEventListener("keydown", this._handleInputKeydown);
1266
1562
  this.el.removeEventListener("click", this._handleInputClick);
1267
- this.container.removeEventListener("mousedown", this._handleContainerMousedownAndTouchstart);
1268
- this.container.removeEventListener("mouseup", this._handleContainerMouseupAndTouchend);
1269
- if (typeof window.ontouchstart !== "undefined") {
1270
- this.container.removeEventListener("touchstart", this._handleContainerMousedownAndTouchstart);
1271
- this.container.removeEventListener("touchend", this._handleContainerMouseupAndTouchend);
1272
- }
1563
+ this.container.removeEventListener("pointerdown", this._handleContainerPointerDown);
1564
+ this.container.removeEventListener("pointerup", this._handleContainerPointerUp);
1565
+ this.container.removeEventListener("pointercancel", this._handleContainerPointerUp);
1273
1566
  }
1274
1567
  _setupMenu() {
1275
1568
  this.container = document.createElement("menu");
@@ -1277,6 +1570,10 @@ var _Autocomplete = class _Autocomplete extends Component {
1277
1570
  this.container.id = `autocomplete-options-${Utils.guid()}`;
1278
1571
  this.container.classList.add("autocomplete-content");
1279
1572
  this.container.ariaExpanded = "true";
1573
+ this.container.setAttribute("role", "listbox");
1574
+ this.el.setAttribute("role", "combobox");
1575
+ this.el.setAttribute("aria-autocomplete", "list");
1576
+ this.el.setAttribute("aria-controls", this.container.id);
1280
1577
  this.el.setAttribute("data-target", this.container.id);
1281
1578
  this.menuItems.forEach((menuItem) => {
1282
1579
  const itemElement = this._createMenuItem(menuItem);
@@ -1315,14 +1612,14 @@ var _Autocomplete = class _Autocomplete extends Component {
1315
1612
  }
1316
1613
  _resetCurrentElementPosition() {
1317
1614
  this.activeIndex = -1;
1318
- this.$active?.classList.remove("active");
1615
+ this._setActive(null);
1319
1616
  }
1320
1617
  _resetAutocomplete() {
1321
1618
  this.container.replaceChildren();
1322
1619
  this._resetCurrentElementPosition();
1323
1620
  this.oldVal = null;
1324
1621
  this.isOpen = false;
1325
- this._mousedown = false;
1622
+ this._pointerDown = false;
1326
1623
  }
1327
1624
  _highlightPartialText(input, label) {
1328
1625
  const start = label.toLocaleLowerCase().indexOf("" + input.toLocaleLowerCase());
@@ -1334,6 +1631,10 @@ var _Autocomplete = class _Autocomplete extends Component {
1334
1631
  }
1335
1632
  _createMenuItem(entry) {
1336
1633
  const item = document.createElement("li");
1634
+ item.id = `autocomplete-option-${Utils.guid()}`;
1635
+ item.setAttribute("role", "option");
1636
+ const isSelected = this.selectedValues.some((sel) => sel.id === entry.id);
1637
+ item.setAttribute("aria-selected", isSelected ? "true" : "false");
1337
1638
  item.setAttribute("data-id", entry.id);
1338
1639
  item.setAttribute(
1339
1640
  "style",
@@ -1346,7 +1647,7 @@ var _Autocomplete = class _Autocomplete extends Component {
1346
1647
  selection.style.textAlign = "center";
1347
1648
  const checkbox = document.createElement("input");
1348
1649
  checkbox.type = "checkbox";
1349
- checkbox.checked = this.selectedValues.some((sel) => sel.id === entry.id);
1650
+ checkbox.checked = isSelected;
1350
1651
  const spacer = document.createElement("span");
1351
1652
  spacer.style.paddingLeft = "21px";
1352
1653
  selection.append(checkbox, spacer);
@@ -1492,7 +1793,7 @@ _Autocomplete._loadingTemplate = null;
1492
1793
  var Autocomplete = _Autocomplete;
1493
1794
 
1494
1795
  // src/ts/components/chips.ts
1495
- var _defaults4 = {
1796
+ var _defaults5 = {
1496
1797
  data: [],
1497
1798
  placeholder: "",
1498
1799
  secondaryPlaceholder: "",
@@ -1501,6 +1802,7 @@ var _defaults4 = {
1501
1802
  autocompleteOnly: false,
1502
1803
  limit: Infinity,
1503
1804
  allowUserInput: false,
1805
+ i18n: { remove: "Remove" },
1504
1806
  onChipAdd: null,
1505
1807
  onChipSelect: null,
1506
1808
  onChipDelete: null
@@ -1510,7 +1812,7 @@ var _Chips = class _Chips extends Component {
1510
1812
  super(el, options, _Chips);
1511
1813
  this._handleChipClick = (e) => {
1512
1814
  const _chip = e.target.closest(".chip");
1513
- const clickedClose = e.target.classList.contains("close");
1815
+ const clickedClose = !!e.target.closest(".close");
1514
1816
  if (_chip) {
1515
1817
  const index = this._chips.indexOf(_chip);
1516
1818
  if (index < 0) return;
@@ -1532,7 +1834,7 @@ var _Chips = class _Chips extends Component {
1532
1834
  };
1533
1835
  this._handleInputKeydown = (e) => {
1534
1836
  _Chips._keydown = true;
1535
- if (Utils.keys.ENTER.includes(e.key)) {
1837
+ if (e.key === Utils.keys.ENTER) {
1536
1838
  if (this.hasAutocomplete && this.autocomplete && this.autocomplete.isOpen) {
1537
1839
  return;
1538
1840
  }
@@ -1541,7 +1843,7 @@ var _Chips = class _Chips extends Component {
1541
1843
  this.addChip({ id: this._input.value });
1542
1844
  }
1543
1845
  this._input.value = "";
1544
- } else if ((Utils.keys.BACKSPACE.includes(e.key) || Utils.keys.ARROW_LEFT.includes(e.key)) && this._input.value === "" && this.chipsData.length) {
1846
+ } else if ((e.key === Utils.keys.BACKSPACE || e.key === Utils.keys.ARROW_LEFT) && this._input.value === "" && this.chipsData.length) {
1545
1847
  e.preventDefault();
1546
1848
  this.selectChip(this.chipsData.length - 1);
1547
1849
  }
@@ -1551,6 +1853,7 @@ var _Chips = class _Chips extends Component {
1551
1853
  ..._Chips.defaults,
1552
1854
  ...options
1553
1855
  };
1856
+ if (options?.i18n) this.options.i18n = { ..._Chips.defaults.i18n, ...options.i18n };
1554
1857
  this.el.classList.add("chips");
1555
1858
  this.chipsData = [];
1556
1859
  this._chips = [];
@@ -1566,8 +1869,22 @@ var _Chips = class _Chips extends Component {
1566
1869
  }
1567
1870
  this._setupLabel();
1568
1871
  }
1872
+ /**
1873
+ * Selection is drawn by a class now, not by `:focus`, so it does not clear
1874
+ * itself when focus moves - every path that changes the selection has to go
1875
+ * through here or the class and the field drift apart.
1876
+ *
1877
+ * Deliberately no `aria-selected`: the chip is not an option in a listbox,
1878
+ * and putting the attribute on an element with no such role is the same
1879
+ * defect the semantics standard exists to remove.
1880
+ */
1881
+ _setSelected(chip) {
1882
+ this._selectedChip?.classList.remove("selected");
1883
+ this._selectedChip = chip;
1884
+ chip?.classList.add("selected");
1885
+ }
1569
1886
  static get defaults() {
1570
- return _defaults4;
1887
+ return _defaults5;
1571
1888
  }
1572
1889
  /**
1573
1890
  * Initializes instances of Chips.
@@ -1617,28 +1934,30 @@ var _Chips = class _Chips extends Component {
1617
1934
  if (tag === "INPUT" || tag === "TEXTAREA" || !chipsKeydown) return;
1618
1935
  const currChips = chips["Expressive_Chips"];
1619
1936
  if (!currChips) return;
1620
- if (Utils.keys.BACKSPACE.includes(e.key) || Utils.keys.DELETE.includes(e.key)) {
1937
+ if (e.key === Utils.keys.BACKSPACE || e.key === Utils.keys.DELETE) {
1621
1938
  e.preventDefault();
1622
1939
  let selectIndex = currChips.chipsData.length;
1623
1940
  if (currChips._selectedChip) {
1624
1941
  const index = currChips._chips.indexOf(currChips._selectedChip);
1625
1942
  currChips.deleteChip(index);
1626
- currChips._selectedChip = null;
1943
+ currChips._setSelected(null);
1627
1944
  selectIndex = Math.max(index - 1, 0);
1628
1945
  }
1629
1946
  if (currChips.chipsData.length) currChips.selectChip(selectIndex);
1630
1947
  else currChips._input.focus();
1631
- } else if (Utils.keys.ARROW_LEFT.includes(e.key)) {
1948
+ } else if (e.key === Utils.keys.ARROW_LEFT) {
1632
1949
  if (currChips._selectedChip) {
1633
1950
  const selectIndex = currChips._chips.indexOf(currChips._selectedChip) - 1;
1634
1951
  if (selectIndex < 0) return;
1635
1952
  currChips.selectChip(selectIndex);
1636
1953
  }
1637
- } else if (Utils.keys.ARROW_RIGHT.includes(e.key)) {
1954
+ } else if (e.key === Utils.keys.ARROW_RIGHT) {
1638
1955
  if (currChips._selectedChip) {
1639
1956
  const selectIndex = currChips._chips.indexOf(currChips._selectedChip) + 1;
1640
- if (selectIndex >= currChips.chipsData.length) currChips._input.focus();
1641
- else currChips.selectChip(selectIndex);
1957
+ if (selectIndex >= currChips.chipsData.length) {
1958
+ currChips._setSelected(null);
1959
+ currChips._input.focus();
1960
+ } else currChips.selectChip(selectIndex);
1642
1961
  }
1643
1962
  }
1644
1963
  }
@@ -1649,25 +1968,31 @@ var _Chips = class _Chips extends Component {
1649
1968
  if (!_Chips._keydown && document.hidden) {
1650
1969
  const chips = e.target.closest(".chips");
1651
1970
  const currChips = chips?.["Expressive_Chips"];
1652
- if (currChips) currChips._selectedChip = null;
1971
+ if (currChips) currChips._setSelected(null);
1653
1972
  }
1654
1973
  }
1655
1974
  _renderChip(chip) {
1656
1975
  if (!chip.id) return;
1657
- const renderedChip = document.createElement("div");
1976
+ const label = chip.text || chip.id;
1977
+ const renderedChip = document.createElement("span");
1658
1978
  renderedChip.classList.add("chip");
1659
- renderedChip.innerText = chip.text || chip.id;
1979
+ renderedChip.innerText = label;
1660
1980
  if (chip.image) {
1661
1981
  const img = document.createElement("img");
1662
1982
  img.setAttribute("src", chip.image);
1663
1983
  renderedChip.insertBefore(img, renderedChip.firstChild);
1664
1984
  }
1665
1985
  if (this.options.allowUserInput) {
1666
- renderedChip.setAttribute("tabindex", "0");
1667
- const closeIcon = document.createElement("i");
1668
- closeIcon.classList.add(this.options.closeIconClass, "close");
1986
+ const closeButton = document.createElement("button");
1987
+ closeButton.type = "button";
1988
+ closeButton.classList.add("close");
1989
+ closeButton.setAttribute("aria-label", `${this.options.i18n.remove} ${label}`);
1990
+ const closeIcon = document.createElement("span");
1991
+ closeIcon.classList.add(this.options.closeIconClass);
1992
+ closeIcon.setAttribute("aria-hidden", "true");
1669
1993
  closeIcon.innerText = "close";
1670
- renderedChip.appendChild(closeIcon);
1994
+ closeButton.appendChild(closeIcon);
1995
+ renderedChip.appendChild(closeButton);
1671
1996
  }
1672
1997
  return renderedChip;
1673
1998
  }
@@ -1742,6 +2067,7 @@ var _Chips = class _Chips extends Component {
1742
2067
  deleteChip(chipIndex) {
1743
2068
  const chip = this._chips[chipIndex];
1744
2069
  if (!chip) return;
2070
+ if (this._selectedChip === chip) this._setSelected(null);
1745
2071
  chip.remove();
1746
2072
  this._chips.splice(chipIndex, 1);
1747
2073
  this.chipsData.splice(chipIndex, 1);
@@ -1757,8 +2083,8 @@ var _Chips = class _Chips extends Component {
1757
2083
  selectChip(chipIndex) {
1758
2084
  const chip = this._chips[chipIndex];
1759
2085
  if (!chip) return;
1760
- this._selectedChip = chip;
1761
- chip.focus();
2086
+ this._setSelected(chip);
2087
+ chip.querySelector(".close")?.focus();
1762
2088
  if (typeof this.options.onChipSelect === "function") {
1763
2089
  this.options.onChipSelect(this.el, chip);
1764
2090
  }
@@ -1769,7 +2095,7 @@ var _Chips = class _Chips extends Component {
1769
2095
  chips.forEach((el) => {
1770
2096
  el.addEventListener("click", (e) => {
1771
2097
  if (el["Expressive_Chips"]) return;
1772
- if (e.target.classList.contains("close")) {
2098
+ if (e.target.closest(".close")) {
1773
2099
  const chip = e.target.closest(".chip");
1774
2100
  if (chip) chip.remove();
1775
2101
  }
@@ -1781,97 +2107,93 @@ var _Chips = class _Chips extends Component {
1781
2107
  _Chips._keydown = false;
1782
2108
  var Chips = _Chips;
1783
2109
 
1784
- // src/ts/components/range.ts
1785
- var _defaults5 = {};
1786
- var Range = class _Range extends Component {
2110
+ // src/ts/components/slider.ts
2111
+ var _defaults6 = {};
2112
+ var Slider = class _Slider extends Component {
1787
2113
  constructor(el, options) {
1788
- super(el, options, _Range);
2114
+ super(el, options, _Slider);
1789
2115
  this._handleRangeChange = () => {
1790
2116
  this._clampDual();
1791
2117
  this._sync();
1792
2118
  this.thumb.classList.add("active");
1793
2119
  };
1794
- this._handleRangeMousedownTouchstart = (e) => {
1795
- this._mousedown = true;
2120
+ this._handleRangePointerDown = (e) => {
2121
+ if (!e.isPrimary) return;
2122
+ if (e.pointerType === "mouse" && e.button !== 0) return;
2123
+ this._pointerDown = true;
1796
2124
  this.el.classList.add("active");
1797
2125
  this._clampDual();
1798
2126
  this._sync();
1799
- if (e.type !== "input") {
1800
- this.thumb.classList.add("active");
1801
- }
2127
+ this.thumb.classList.add("active");
1802
2128
  };
1803
- this._handleRangeInputMousemoveTouchmove = () => {
1804
- if (this._mousedown) {
2129
+ this._handleRangeInputOrMove = () => {
2130
+ if (this._pointerDown) {
1805
2131
  this._clampDual();
1806
2132
  this._sync();
1807
2133
  this.thumb.classList.add("active");
1808
2134
  }
1809
2135
  };
1810
- this._handleRangeMouseupTouchend = () => {
1811
- this._mousedown = false;
2136
+ this._handleRangePointerUp = (e) => {
2137
+ if (!e.isPrimary) return;
2138
+ this._pointerDown = false;
1812
2139
  this.el.classList.remove("active");
1813
2140
  };
1814
- this._handleRangeBlurMouseoutTouchleave = () => {
1815
- if (!this._mousedown) {
2141
+ this._handleRangeRelease = () => {
2142
+ if (!this._pointerDown) {
1816
2143
  this.thumb.classList.remove("active");
1817
2144
  }
1818
2145
  };
1819
- this.el["Expressive_Range"] = this;
2146
+ this.el["Expressive_Slider"] = this;
1820
2147
  this.options = {
1821
- ..._Range.defaults,
2148
+ ..._Slider.defaults,
1822
2149
  ...options
1823
2150
  };
1824
- this._mousedown = false;
2151
+ this._pointerDown = false;
1825
2152
  this._setupThumb();
1826
2153
  this._sync();
1827
2154
  this._setupEventHandlers();
1828
2155
  }
1829
2156
  static get defaults() {
1830
- return _defaults5;
2157
+ return _defaults6;
1831
2158
  }
1832
2159
  /**
1833
- * Initializes instances of Range.
2160
+ * Initializes instances of Slider.
1834
2161
  * @param els HTML elements.
1835
2162
  * @param options Component options.
1836
2163
  */
1837
2164
  static init(els, options = {}) {
1838
- return super.init(els, options, _Range);
2165
+ return super.init(els, options, _Slider);
1839
2166
  }
1840
2167
  static getInstance(el) {
1841
- return el["Expressive_Range"];
2168
+ return el["Expressive_Slider"];
1842
2169
  }
1843
2170
  destroy() {
1844
2171
  this._removeEventHandlers();
1845
2172
  this._removeThumb();
1846
- this.el["Expressive_Range"] = void 0;
2173
+ this.el["Expressive_Slider"] = void 0;
1847
2174
  }
2175
+ // Eleven listeners used to cover four stages, because each one was written
2176
+ // twice - once for mouse, once for touch. One of the touch spellings,
2177
+ // `touchleave`, was never implemented by any browser at all.
1848
2178
  _setupEventHandlers() {
1849
2179
  this.el.addEventListener("change", this._handleRangeChange);
1850
- this.el.addEventListener("mousedown", this._handleRangeMousedownTouchstart);
1851
- this.el.addEventListener("touchstart", this._handleRangeMousedownTouchstart);
1852
- this.el.addEventListener("input", this._handleRangeInputMousemoveTouchmove);
1853
- this.el.addEventListener("mousemove", this._handleRangeInputMousemoveTouchmove);
1854
- this.el.addEventListener("touchmove", this._handleRangeInputMousemoveTouchmove, {
1855
- passive: true
1856
- });
1857
- this.el.addEventListener("mouseup", this._handleRangeMouseupTouchend);
1858
- this.el.addEventListener("touchend", this._handleRangeMouseupTouchend);
1859
- this.el.addEventListener("blur", this._handleRangeBlurMouseoutTouchleave);
1860
- this.el.addEventListener("mouseout", this._handleRangeBlurMouseoutTouchleave);
1861
- this.el.addEventListener("touchleave", this._handleRangeBlurMouseoutTouchleave);
2180
+ this.el.addEventListener("pointerdown", this._handleRangePointerDown);
2181
+ this.el.addEventListener("input", this._handleRangeInputOrMove);
2182
+ this.el.addEventListener("pointermove", this._handleRangeInputOrMove, { passive: true });
2183
+ this.el.addEventListener("pointerup", this._handleRangePointerUp);
2184
+ this.el.addEventListener("pointercancel", this._handleRangePointerUp);
2185
+ this.el.addEventListener("blur", this._handleRangeRelease);
2186
+ this.el.addEventListener("mouseout", this._handleRangeRelease);
1862
2187
  }
1863
2188
  _removeEventHandlers() {
1864
2189
  this.el.removeEventListener("change", this._handleRangeChange);
1865
- this.el.removeEventListener("mousedown", this._handleRangeMousedownTouchstart);
1866
- this.el.removeEventListener("touchstart", this._handleRangeMousedownTouchstart);
1867
- this.el.removeEventListener("input", this._handleRangeInputMousemoveTouchmove);
1868
- this.el.removeEventListener("mousemove", this._handleRangeInputMousemoveTouchmove);
1869
- this.el.removeEventListener("touchmove", this._handleRangeInputMousemoveTouchmove);
1870
- this.el.removeEventListener("mouseup", this._handleRangeMouseupTouchend);
1871
- this.el.removeEventListener("touchend", this._handleRangeMouseupTouchend);
1872
- this.el.removeEventListener("blur", this._handleRangeBlurMouseoutTouchleave);
1873
- this.el.removeEventListener("mouseout", this._handleRangeBlurMouseoutTouchleave);
1874
- this.el.removeEventListener("touchleave", this._handleRangeBlurMouseoutTouchleave);
2190
+ this.el.removeEventListener("pointerdown", this._handleRangePointerDown);
2191
+ this.el.removeEventListener("input", this._handleRangeInputOrMove);
2192
+ this.el.removeEventListener("pointermove", this._handleRangeInputOrMove);
2193
+ this.el.removeEventListener("pointerup", this._handleRangePointerUp);
2194
+ this.el.removeEventListener("pointercancel", this._handleRangePointerUp);
2195
+ this.el.removeEventListener("blur", this._handleRangeRelease);
2196
+ this.el.removeEventListener("mouseout", this._handleRangeRelease);
1875
2197
  }
1876
2198
  _setupThumb() {
1877
2199
  this.thumb = document.createElement("span");
@@ -1888,7 +2210,7 @@ var Range = class _Range extends Component {
1888
2210
  return !!this.el.closest(".vertical");
1889
2211
  }
1890
2212
  _host() {
1891
- return this.el.closest(".range, .range-field, label");
2213
+ return this.el.closest(".slider, .range, .range-field, label");
1892
2214
  }
1893
2215
  _clampDual() {
1894
2216
  const host = this._host();
@@ -1943,7 +2265,7 @@ var Range = class _Range extends Component {
1943
2265
  */
1944
2266
  static Init() {
1945
2267
  Utils.onDocumentReady(() => {
1946
- _Range.init(document.querySelectorAll("input[type=range]"), {});
2268
+ _Slider.init(document.querySelectorAll("input[type=range]"), {});
1947
2269
  });
1948
2270
  }
1949
2271
  };
@@ -2016,169 +2338,144 @@ var _Dialogs = class _Dialogs {
2016
2338
  _Dialogs._down = null;
2017
2339
  var Dialogs = _Dialogs;
2018
2340
 
2019
- // src/ts/behaviors/bottomSheets.ts
2020
- var _BottomSheets = class _BottomSheets {
2021
- static Init() {
2022
- Utils.onDocumentReady(() => {
2023
- document.addEventListener("pointerdown", _BottomSheets._onDown, { passive: true });
2024
- document.addEventListener("pointermove", _BottomSheets._onMove, { passive: true });
2025
- document.addEventListener("pointerup", _BottomSheets._onUp, { passive: true });
2026
- document.addEventListener("pointercancel", _BottomSheets._onCancel, { passive: true });
2027
- });
2028
- }
2029
- static _onDown(event) {
2341
+ // src/ts/behaviors/sheetDrag.ts
2342
+ var DISMISS_DISTANCE = 96;
2343
+ var DISMISS_VELOCITY = 0.5;
2344
+ var DRAG_SLOP = 4;
2345
+ function installSheetDrag(config) {
2346
+ const coord = (event) => config.axis === "x" ? event.clientX : event.clientY;
2347
+ let drag = null;
2348
+ const sheetFor = (target) => {
2349
+ if (!(target instanceof Element)) return null;
2350
+ const dialog = target.closest(config.selector);
2351
+ return dialog instanceof HTMLDialogElement ? dialog : null;
2352
+ };
2353
+ const reset = (dialog) => {
2354
+ dialog.style.transition = "";
2355
+ dialog.style.setProperty(config.property, "0px");
2356
+ };
2357
+ const onDown = (event) => {
2358
+ config.onPress?.();
2030
2359
  if (!event.isPrimary) return;
2031
2360
  if (event.pointerType === "mouse" && event.button !== 0) return;
2032
- const dialog = _BottomSheets._sheet(event.target);
2361
+ const dialog = sheetFor(event.target);
2033
2362
  if (!dialog?.open) {
2034
- _BottomSheets._drag = null;
2363
+ drag = null;
2035
2364
  return;
2036
2365
  }
2037
- if (!_BottomSheets._inHandle(event, dialog)) {
2038
- _BottomSheets._drag = null;
2366
+ if (!config.inHandle(event, dialog)) {
2367
+ drag = null;
2039
2368
  return;
2040
2369
  }
2041
2370
  dialog.style.transition = "none";
2042
- _BottomSheets._drag = {
2371
+ drag = {
2043
2372
  dialog,
2044
2373
  pointerId: event.pointerId,
2045
- startY: event.clientY,
2046
- lastY: event.clientY,
2374
+ direction: config.direction(dialog),
2375
+ start: coord(event),
2376
+ last: coord(event),
2047
2377
  lastT: Date.now(),
2048
2378
  shift: 0
2049
2379
  };
2050
- }
2051
- static _onMove(event) {
2052
- const drag = _BottomSheets._drag;
2380
+ };
2381
+ const onMove = (event) => {
2053
2382
  if (!drag || drag.pointerId !== event.pointerId) return;
2054
- const shift = Math.max(0, event.clientY - drag.startY);
2055
- drag.shift = shift;
2056
- drag.lastY = event.clientY;
2383
+ const delta = coord(event) - drag.start;
2384
+ drag.shift = drag.direction === 1 ? Math.max(0, delta) : Math.min(0, delta);
2385
+ drag.last = coord(event);
2057
2386
  drag.lastT = Date.now();
2058
- drag.dialog.style.setProperty("--md-comp-bottom-sheet-shift", `${shift}px`);
2059
- }
2060
- static _onUp(event) {
2061
- const drag = _BottomSheets._drag;
2062
- _BottomSheets._drag = null;
2063
- if (!drag || drag.pointerId !== event.pointerId) return;
2064
- const dialog = drag.dialog;
2065
- dialog.style.transition = "";
2066
- const dt = Math.max(1, Date.now() - drag.lastT);
2067
- const velocity = (event.clientY - drag.lastY) / dt;
2068
- const dismiss = drag.shift > 96 || velocity > 0.5;
2069
- if (dismiss && dialog.open) {
2070
- dialog.close();
2071
- }
2072
- dialog.style.setProperty("--md-comp-bottom-sheet-shift", "0px");
2073
- }
2074
- static _onCancel() {
2075
- const drag = _BottomSheets._drag;
2076
- _BottomSheets._drag = null;
2077
- if (!drag) return;
2078
- drag.dialog.style.transition = "";
2079
- drag.dialog.style.setProperty("--md-comp-bottom-sheet-shift", "0px");
2080
- }
2081
- static _sheet(target) {
2082
- if (!(target instanceof Element)) return null;
2083
- const dialog = target.closest("dialog.bottom-sheet, dialog.bottom");
2084
- return dialog instanceof HTMLDialogElement ? dialog : null;
2387
+ drag.dialog.style.setProperty(config.property, `${drag.shift}px`);
2388
+ };
2389
+ const onUp = (event) => {
2390
+ const held = drag;
2391
+ if (!held || held.pointerId !== event.pointerId) return;
2392
+ drag = null;
2393
+ held.dialog.style.transition = "";
2394
+ const dt = Math.max(1, Date.now() - held.lastT);
2395
+ const velocity = (coord(event) - held.last) / dt;
2396
+ config.onRelease?.(Math.abs(held.shift) > DRAG_SLOP);
2397
+ const dismiss = held.direction === 1 ? held.shift > DISMISS_DISTANCE || velocity > DISMISS_VELOCITY : held.shift < -DISMISS_DISTANCE || velocity < -DISMISS_VELOCITY;
2398
+ if (dismiss && held.dialog.open) held.dialog.close();
2399
+ held.dialog.style.setProperty(config.property, "0px");
2400
+ };
2401
+ const onCancel = () => {
2402
+ config.onRelease?.(false);
2403
+ const held = drag;
2404
+ drag = null;
2405
+ if (held) reset(held.dialog);
2406
+ };
2407
+ Utils.onDocumentReady(() => {
2408
+ document.addEventListener("pointerdown", onDown, { passive: true });
2409
+ document.addEventListener("pointermove", onMove, { passive: true });
2410
+ document.addEventListener("pointerup", onUp, { passive: true });
2411
+ document.addEventListener("pointercancel", onCancel, { passive: true });
2412
+ });
2413
+ }
2414
+
2415
+ // src/ts/behaviors/bottomSheets.ts
2416
+ var SHEET_SELECTOR = "dialog.bottom-sheet, dialog.bottom";
2417
+ var _BottomSheets = class _BottomSheets {
2418
+ static Init() {
2419
+ installSheetDrag({
2420
+ selector: SHEET_SELECTOR,
2421
+ axis: "y",
2422
+ property: "--md-comp-bottom-sheet-shift",
2423
+ direction: () => 1,
2424
+ inHandle: (event, dialog) => {
2425
+ const target = event.target;
2426
+ if (target instanceof Element && target.closest(".handle")) return true;
2427
+ const rect = dialog.getBoundingClientRect();
2428
+ return event.clientY >= rect.top && event.clientY <= rect.top + 48;
2429
+ },
2430
+ onPress: () => {
2431
+ _BottomSheets._dragged = false;
2432
+ },
2433
+ onRelease: (dragged) => {
2434
+ _BottomSheets._dragged = dragged;
2435
+ }
2436
+ });
2437
+ Utils.onDocumentReady(() => {
2438
+ document.addEventListener("click", _BottomSheets._onClick);
2439
+ });
2085
2440
  }
2086
- static _inHandle(event, dialog) {
2441
+ static _onClick(event) {
2087
2442
  const target = event.target;
2088
- if (target instanceof Element && target.closest(".handle")) return true;
2089
- const rect = dialog.getBoundingClientRect();
2090
- return event.clientY >= rect.top && event.clientY <= rect.top + 48;
2443
+ if (!(target instanceof Element)) return;
2444
+ const handle = target.closest(".handle, .drag-handle");
2445
+ if (!(handle instanceof HTMLButtonElement)) return;
2446
+ if (event.detail !== 0 && _BottomSheets._dragged) return;
2447
+ const dialog = handle.closest(SHEET_SELECTOR);
2448
+ if (dialog instanceof HTMLDialogElement && dialog.open) dialog.close();
2091
2449
  }
2092
2450
  };
2093
- _BottomSheets._drag = null;
2451
+ /**
2452
+ * Whether the last pointer sequence moved. A drag ends with a `click` on the
2453
+ * element it started on, so without this the tail of a drag that snapped back
2454
+ * would dismiss the sheet the drag had just declined to dismiss.
2455
+ */
2456
+ _BottomSheets._dragged = false;
2094
2457
  var BottomSheets = _BottomSheets;
2095
2458
 
2096
2459
  // src/ts/behaviors/sideSheets.ts
2097
- var _SideSheets = class _SideSheets {
2460
+ var startDocked = (dialog) => dialog.classList.contains("left") || dialog.classList.contains("left-sheet") || dialog.classList.contains("start");
2461
+ var SideSheets = class {
2098
2462
  static Init() {
2099
- Utils.onDocumentReady(() => {
2100
- document.addEventListener("pointerdown", _SideSheets._onDown, { passive: true });
2101
- document.addEventListener("pointermove", _SideSheets._onMove, { passive: true });
2102
- document.addEventListener("pointerup", _SideSheets._onUp, { passive: true });
2103
- document.addEventListener("pointercancel", _SideSheets._onCancel, { passive: true });
2463
+ installSheetDrag({
2464
+ selector: "dialog.side-sheet, dialog.right, dialog.right-sheet, dialog.left, dialog.left-sheet",
2465
+ axis: "x",
2466
+ property: "--md-comp-side-sheet-shift",
2467
+ // A start-docked sheet leaves toward the start edge, so its dismissal
2468
+ // direction is the negative one.
2469
+ direction: (dialog) => startDocked(dialog) ? -1 : 1,
2470
+ inHandle: (event, dialog) => {
2471
+ const target = event.target;
2472
+ if (target instanceof Element && target.closest("header")) return true;
2473
+ const rect = dialog.getBoundingClientRect();
2474
+ return startDocked(dialog) ? event.clientX >= rect.right - 24 && event.clientX <= rect.right : event.clientX >= rect.left && event.clientX <= rect.left + 24;
2475
+ }
2104
2476
  });
2105
2477
  }
2106
- static _onDown(event) {
2107
- if (!event.isPrimary) return;
2108
- if (event.pointerType === "mouse" && event.button !== 0) return;
2109
- const dialog = _SideSheets._sheet(event.target);
2110
- if (!dialog?.open) {
2111
- _SideSheets._drag = null;
2112
- return;
2113
- }
2114
- if (!_SideSheets._inHandle(event, dialog)) {
2115
- _SideSheets._drag = null;
2116
- return;
2117
- }
2118
- dialog.style.transition = "none";
2119
- _SideSheets._drag = {
2120
- dialog,
2121
- pointerId: event.pointerId,
2122
- startX: event.clientX,
2123
- lastX: event.clientX,
2124
- lastT: Date.now(),
2125
- shift: 0,
2126
- startDocked: _SideSheets._startDocked(dialog)
2127
- };
2128
- }
2129
- static _onMove(event) {
2130
- const drag = _SideSheets._drag;
2131
- if (!drag || drag.pointerId !== event.pointerId) return;
2132
- const delta = event.clientX - drag.startX;
2133
- const shift = drag.startDocked ? Math.min(0, delta) : Math.max(0, delta);
2134
- drag.shift = shift;
2135
- drag.lastX = event.clientX;
2136
- drag.lastT = Date.now();
2137
- drag.dialog.style.setProperty("--md-comp-side-sheet-shift", `${shift}px`);
2138
- }
2139
- static _onUp(event) {
2140
- const drag = _SideSheets._drag;
2141
- _SideSheets._drag = null;
2142
- if (!drag || drag.pointerId !== event.pointerId) return;
2143
- const dialog = drag.dialog;
2144
- dialog.style.transition = "";
2145
- const dt = Math.max(1, Date.now() - drag.lastT);
2146
- const velocity = (event.clientX - drag.lastX) / dt;
2147
- const dismiss = drag.startDocked ? drag.shift < -96 || velocity < -0.5 : drag.shift > 96 || velocity > 0.5;
2148
- if (dismiss && dialog.open) {
2149
- dialog.close();
2150
- }
2151
- dialog.style.setProperty("--md-comp-side-sheet-shift", "0px");
2152
- }
2153
- static _onCancel() {
2154
- const drag = _SideSheets._drag;
2155
- _SideSheets._drag = null;
2156
- if (!drag) return;
2157
- drag.dialog.style.transition = "";
2158
- drag.dialog.style.setProperty("--md-comp-side-sheet-shift", "0px");
2159
- }
2160
- static _sheet(target) {
2161
- if (!(target instanceof Element)) return null;
2162
- const dialog = target.closest(
2163
- "dialog.side-sheet, dialog.right, dialog.right-sheet, dialog.left, dialog.left-sheet"
2164
- );
2165
- return dialog instanceof HTMLDialogElement ? dialog : null;
2166
- }
2167
- static _startDocked(dialog) {
2168
- return dialog.classList.contains("left") || dialog.classList.contains("left-sheet") || dialog.classList.contains("start");
2169
- }
2170
- static _inHandle(event, dialog) {
2171
- const target = event.target;
2172
- if (target instanceof Element && target.closest("header")) return true;
2173
- const rect = dialog.getBoundingClientRect();
2174
- if (_SideSheets._startDocked(dialog)) {
2175
- return event.clientX >= rect.right - 24 && event.clientX <= rect.right;
2176
- }
2177
- return event.clientX >= rect.left && event.clientX <= rect.left + 24;
2178
- }
2179
2478
  };
2180
- _SideSheets._drag = null;
2181
- var SideSheets = _SideSheets;
2182
2479
 
2183
2480
  // src/ts/behaviors/forms.ts
2184
2481
  var Forms = class _Forms {
@@ -2234,7 +2531,7 @@ var Forms = class _Forms {
2234
2531
  document.addEventListener("keyup", (e) => {
2235
2532
  const target = e.target;
2236
2533
  if (target instanceof HTMLInputElement && ["radio", "checkbox"].includes(target.type)) {
2237
- if (Utils.keys.TAB.includes(e.key)) {
2534
+ if (e.key === Utils.keys.TAB) {
2238
2535
  target.classList.add("tabbed");
2239
2536
  target.addEventListener("blur", () => target.classList.remove("tabbed"), {
2240
2537
  once: true
@@ -2272,61 +2569,89 @@ var Forms = class _Forms {
2272
2569
  }
2273
2570
  };
2274
2571
 
2275
- // src/ts/behaviors/waves.ts
2276
- var Waves = class _Waves {
2277
- static _offset(el) {
2278
- const box = el.getBoundingClientRect();
2279
- const docElem = document.documentElement;
2280
- return {
2281
- top: box.top + window.pageYOffset - docElem.clientTop,
2282
- left: box.left + window.pageXOffset - docElem.clientLeft
2283
- };
2284
- }
2285
- // https://phoenix-dx.com/css-techniques-for-material-ripple-effect/
2286
- static renderWaveEffect(targetElement, position = null, color = null) {
2287
- const isCentered = position === null;
2288
- const duration = 500;
2289
- let animationFrame, animationStart;
2290
- const animationStep = function(timestamp) {
2291
- if (!animationStart) {
2292
- animationStart = timestamp;
2293
- }
2294
- const frame = timestamp - animationStart;
2295
- if (frame < duration) {
2296
- const easing = frame / duration * (2 - frame / duration);
2297
- const circle = isCentered ? "circle at 50% 50%" : `circle at ${position.x}px ${position.y}px`;
2298
- const waveColor = `rgba(${color?.r || 0}, ${color?.g || 0}, ${color?.b || 0}, ${0.3 * (1 - easing)})`;
2299
- const stop = 90 * easing + "%";
2300
- targetElement.style.backgroundImage = "radial-gradient(" + circle + ", " + waveColor + " " + stop + ", transparent " + stop + ")";
2301
- animationFrame = window.requestAnimationFrame(animationStep);
2302
- } else {
2303
- targetElement.style.backgroundImage = "none";
2304
- window.cancelAnimationFrame(animationFrame);
2572
+ // src/ts/components/appBar.ts
2573
+ var _defaults7 = {};
2574
+ var AppBar = class _AppBar extends Component {
2575
+ constructor(el, options) {
2576
+ super(el, options, _AppBar);
2577
+ this._observer = null;
2578
+ this._sentinel = null;
2579
+ this._input = null;
2580
+ this._view = null;
2581
+ this._suppressSearchOpen = false;
2582
+ this._onIntersect = (entries) => {
2583
+ const entry = entries[0];
2584
+ if (!entry) return;
2585
+ this.el.classList.toggle("collapsed", !entry.isIntersecting);
2586
+ };
2587
+ this._onSearchViewClose = () => {
2588
+ this._suppressSearchOpen = true;
2589
+ };
2590
+ this._onSearchFocus = () => {
2591
+ if (!this._view) return;
2592
+ if (this._suppressSearchOpen) {
2593
+ this._suppressSearchOpen = false;
2594
+ return;
2305
2595
  }
2596
+ if (this._view instanceof HTMLDialogElement) {
2597
+ if (!this._view.open) this._view.showModal();
2598
+ return;
2599
+ }
2600
+ this._view.hidden = false;
2601
+ this._input?.setAttribute("aria-expanded", "true");
2306
2602
  };
2307
- animationFrame = window.requestAnimationFrame(animationStep);
2603
+ this.el["Expressive_AppBar"] = this;
2604
+ this.options = { ..._AppBar.defaults, ...options };
2605
+ this._setupCollapse();
2606
+ this._setupSearch();
2308
2607
  }
2309
- static Init() {
2310
- Utils.onDocumentReady(() => {
2311
- document.body.addEventListener("click", (e) => {
2312
- const trigger = e.target;
2313
- const el = trigger.closest(".waves-effect");
2314
- if (el && el.contains(trigger)) {
2315
- const isCircular = el.classList.contains("waves-circle");
2316
- const offset = _Waves._offset(el);
2317
- const x = e.pageX - offset.left;
2318
- const y = e.pageY - offset.top;
2319
- let color = null;
2320
- if (el.classList.contains("waves-light")) color = { r: 255, g: 255, b: 255 };
2321
- _Waves.renderWaveEffect(el, isCircular ? null : { x, y }, color);
2322
- }
2323
- });
2324
- });
2608
+ static get defaults() {
2609
+ return _defaults7;
2610
+ }
2611
+ static init(els, options = {}) {
2612
+ return super.init(els, options, _AppBar);
2613
+ }
2614
+ static getInstance(el) {
2615
+ return el["Expressive_AppBar"];
2616
+ }
2617
+ destroy() {
2618
+ this._observer?.disconnect();
2619
+ this._observer = null;
2620
+ this._sentinel?.remove();
2621
+ this._sentinel = null;
2622
+ this._input?.removeEventListener("focus", this._onSearchFocus);
2623
+ this._view?.removeEventListener("close", this._onSearchViewClose);
2624
+ this.el.classList.remove("collapsed");
2625
+ this.el["Expressive_AppBar"] = void 0;
2626
+ }
2627
+ _setupCollapse() {
2628
+ if (!this.el.classList.contains("medium") && !this.el.classList.contains("large")) {
2629
+ return;
2630
+ }
2631
+ if (typeof IntersectionObserver === "undefined") return;
2632
+ this._sentinel = document.createElement("span");
2633
+ this._sentinel.setAttribute("aria-hidden", "true");
2634
+ this._sentinel.style.cssText = "display:block;width:100%;height:1px;margin-top:-1px;pointer-events:none;visibility:hidden";
2635
+ this.el.insertAdjacentElement("beforebegin", this._sentinel);
2636
+ this._observer = new IntersectionObserver(this._onIntersect);
2637
+ this._observer.observe(this._sentinel);
2638
+ }
2639
+ _setupSearch() {
2640
+ this._input = this.el.querySelector('input[type="search"]');
2641
+ if (!this._input) return;
2642
+ const id = this._input.getAttribute("aria-controls");
2643
+ if (id) this._view = document.getElementById(id);
2644
+ if (!this._view) this._view = this.el.querySelector(".search-view");
2645
+ if (!this._view) return;
2646
+ this._input.addEventListener("focus", this._onSearchFocus);
2647
+ if (this._view instanceof HTMLDialogElement) {
2648
+ this._view.addEventListener("close", this._onSearchViewClose);
2649
+ }
2325
2650
  }
2326
2651
  };
2327
2652
 
2328
2653
  // src/ts/components/buttons.ts
2329
- var _defaults6 = {
2654
+ var _defaults8 = {
2330
2655
  direction: "top",
2331
2656
  hoverEnabled: true,
2332
2657
  toolbarEnabled: false
@@ -2361,13 +2686,13 @@ var FloatingActionButton = class _FloatingActionButton extends Component {
2361
2686
  this._toggle();
2362
2687
  };
2363
2688
  this._handleAnchorKeydown = (e) => {
2364
- if (Utils.keys.ENTER.includes(e.key) || e.key === " ") {
2689
+ if (e.key === Utils.keys.ENTER || e.key === " ") {
2365
2690
  e.preventDefault();
2366
2691
  this._toggle();
2367
2692
  }
2368
2693
  };
2369
2694
  this._handleDocumentKeydown = (e) => {
2370
- if (Utils.keys.ESC.includes(e.key)) this.close();
2695
+ if (e.key === Utils.keys.ESC) this.close();
2371
2696
  };
2372
2697
  this._handleDocumentClick = (e) => {
2373
2698
  const target = e.target;
@@ -2387,22 +2712,19 @@ var FloatingActionButton = class _FloatingActionButton extends Component {
2387
2712
  };
2388
2713
  this.isOpen = false;
2389
2714
  this._anchor = this.el.querySelector(":scope > a, :scope > button");
2390
- this._menu = this.el.querySelector("ul, menu");
2391
2715
  this.el.classList.add(`direction-${this.options.direction}`);
2392
2716
  if (clickToToggle) this.el.classList.add("click-to-toggle");
2393
2717
  if (toolbar) this.el.classList.add("toolbar");
2394
2718
  if (this._anchor) {
2395
- this._anchor.setAttribute("aria-haspopup", "menu");
2396
2719
  this._anchor.setAttribute("aria-expanded", "false");
2397
2720
  if (this._anchor instanceof HTMLAnchorElement && !this._anchor.hasAttribute("href") && this._anchor.tabIndex < 0) {
2398
2721
  this._anchor.tabIndex = 0;
2399
2722
  }
2400
2723
  }
2401
- if (this._menu) this._menu.setAttribute("role", "menu");
2402
2724
  this._setupEventHandlers();
2403
2725
  }
2404
2726
  static get defaults() {
2405
- return _defaults6;
2727
+ return _defaults8;
2406
2728
  }
2407
2729
  static init(els, options = {}) {
2408
2730
  return super.init(els, options, _FloatingActionButton);
@@ -2439,17 +2761,240 @@ var FloatingActionButton = class _FloatingActionButton extends Component {
2439
2761
  }
2440
2762
  };
2441
2763
 
2764
+ // src/ts/components/buttonGroup.ts
2765
+ var _defaults9 = {};
2766
+ var VALID_SELECTION_MODES = /* @__PURE__ */ new Set(["single", "multiple"]);
2767
+ var PRESSED_CLASS = "button-group-pressed";
2768
+ var MAX_ICON_COMPRESSION = 0.15;
2769
+ var ButtonGroup = class _ButtonGroup extends Component {
2770
+ constructor(el, options = {}) {
2771
+ super(el, options, _ButtonGroup);
2772
+ this._pressedStyles = /* @__PURE__ */ new Map();
2773
+ this._releaseStyles = /* @__PURE__ */ new Map();
2774
+ this._releaseTimer = null;
2775
+ this._originalPressed = /* @__PURE__ */ new Map();
2776
+ this._activeItem = null;
2777
+ this._handleClick = (event) => {
2778
+ if (!this._selectionMode) return;
2779
+ const item = this._itemFromEvent(event);
2780
+ if (!(item instanceof HTMLButtonElement) || this._isDisabled(item)) return;
2781
+ this._rememberPressed(item);
2782
+ const willSelect = item.getAttribute("aria-pressed") !== "true";
2783
+ const selectedCount = this._toggleItems.filter(
2784
+ (button) => button.getAttribute("aria-pressed") === "true"
2785
+ ).length;
2786
+ if (!willSelect && this.el.hasAttribute("data-selection-required") && selectedCount === 1) {
2787
+ return;
2788
+ }
2789
+ if (this._selectionMode === "single" && willSelect) {
2790
+ this._toggleItems.forEach((button) => {
2791
+ this._rememberPressed(button);
2792
+ button.setAttribute("aria-pressed", "false");
2793
+ });
2794
+ }
2795
+ item.setAttribute("aria-pressed", willSelect ? "true" : "false");
2796
+ };
2797
+ this._handlePointerDown = (event) => {
2798
+ if (event.button !== 0) return;
2799
+ const item = this._itemFromEvent(event);
2800
+ if (item && !this._isDisabled(item)) this._startPress(item);
2801
+ };
2802
+ this._handleKeyDown = (event) => {
2803
+ if (event.repeat || event.key !== " " && event.key !== "Enter") return;
2804
+ const item = this._itemFromEvent(event);
2805
+ if (item && !this._isDisabled(item)) this._startPress(item);
2806
+ };
2807
+ this._handleKeyUp = (event) => {
2808
+ if (event.key === " " || event.key === "Enter") this._resetPress();
2809
+ };
2810
+ this._handleFocusOut = (event) => {
2811
+ if (event.target === this._activeItem) this._resetPress();
2812
+ };
2813
+ this._resetPress = () => {
2814
+ this._activeItem?.classList.remove(PRESSED_CLASS);
2815
+ this._activeItem = null;
2816
+ if (!this._pressedStyles.size) return;
2817
+ this._finishRelease();
2818
+ this._releaseStyles = new Map(this._pressedStyles);
2819
+ for (const [item, style] of this._releaseStyles) {
2820
+ item.style.width = `${style.baseWidth}px`;
2821
+ }
2822
+ this._pressedStyles.clear();
2823
+ this._releaseTimer = window.setTimeout(this._finishRelease, 200);
2824
+ };
2825
+ this._finishRelease = () => {
2826
+ if (this._releaseTimer !== null) window.clearTimeout(this._releaseTimer);
2827
+ this._releaseTimer = null;
2828
+ for (const [item, style] of this._releaseStyles) item.style.width = style.width;
2829
+ this._releaseStyles.clear();
2830
+ };
2831
+ this.el["Expressive_ButtonGroup"] = this;
2832
+ this.options = { ..._ButtonGroup.defaults, ...options };
2833
+ if (this._selectionMode) {
2834
+ this._normalizeSelection();
2835
+ this.el.addEventListener("click", this._handleClick, true);
2836
+ }
2837
+ this.el.addEventListener("pointerdown", this._handlePointerDown);
2838
+ this.el.addEventListener("keydown", this._handleKeyDown);
2839
+ this.el.addEventListener("focusout", this._handleFocusOut);
2840
+ document.addEventListener("pointerup", this._resetPress);
2841
+ document.addEventListener("pointercancel", this._resetPress);
2842
+ document.addEventListener("keyup", this._handleKeyUp);
2843
+ window.addEventListener("blur", this._resetPress);
2844
+ }
2845
+ static get defaults() {
2846
+ return _defaults9;
2847
+ }
2848
+ static init(els, options = {}) {
2849
+ return super.init(els, options, _ButtonGroup);
2850
+ }
2851
+ static getInstance(el) {
2852
+ return el["Expressive_ButtonGroup"];
2853
+ }
2854
+ destroy() {
2855
+ this._resetPress();
2856
+ this._finishRelease();
2857
+ this.el.removeEventListener("click", this._handleClick, true);
2858
+ this.el.removeEventListener("pointerdown", this._handlePointerDown);
2859
+ this.el.removeEventListener("keydown", this._handleKeyDown);
2860
+ this.el.removeEventListener("focusout", this._handleFocusOut);
2861
+ document.removeEventListener("pointerup", this._resetPress);
2862
+ document.removeEventListener("pointercancel", this._resetPress);
2863
+ document.removeEventListener("keyup", this._handleKeyUp);
2864
+ window.removeEventListener("blur", this._resetPress);
2865
+ for (const [item, value] of this._originalPressed) {
2866
+ if (value === null) item.removeAttribute("aria-pressed");
2867
+ else item.setAttribute("aria-pressed", value);
2868
+ }
2869
+ this._originalPressed.clear();
2870
+ this.el["Expressive_ButtonGroup"] = void 0;
2871
+ }
2872
+ get _selectionMode() {
2873
+ const mode = this.el.dataset.selection;
2874
+ return VALID_SELECTION_MODES.has(mode) ? mode : null;
2875
+ }
2876
+ get _items() {
2877
+ return Array.from(this.el.children).filter(
2878
+ (child) => child instanceof HTMLButtonElement || child instanceof HTMLAnchorElement && child.classList.contains("button")
2879
+ );
2880
+ }
2881
+ get _toggleItems() {
2882
+ return this._items.filter(
2883
+ (item) => item instanceof HTMLButtonElement
2884
+ );
2885
+ }
2886
+ _normalizeSelection() {
2887
+ const items = this._toggleItems;
2888
+ items.forEach((item) => {
2889
+ this._rememberPressed(item);
2890
+ if (!["true", "false"].includes(item.getAttribute("aria-pressed") || "")) {
2891
+ item.setAttribute("aria-pressed", "false");
2892
+ }
2893
+ });
2894
+ if (this._selectionMode === "single") {
2895
+ const selected = items.filter((item) => item.getAttribute("aria-pressed") === "true");
2896
+ selected.slice(1).forEach((item) => item.setAttribute("aria-pressed", "false"));
2897
+ }
2898
+ if (this.el.hasAttribute("data-selection-required") && !items.some((item) => item.getAttribute("aria-pressed") === "true")) {
2899
+ items.find((item) => !this._isDisabled(item))?.setAttribute("aria-pressed", "true");
2900
+ }
2901
+ }
2902
+ _itemFromEvent(event) {
2903
+ if (!(event.target instanceof Element)) return null;
2904
+ const item = event.target.closest("button, a.button");
2905
+ return item instanceof HTMLElement && item.parentElement === this.el ? item : null;
2906
+ }
2907
+ _isDisabled(item) {
2908
+ return item instanceof HTMLButtonElement && item.disabled || item.getAttribute("aria-disabled") === "true" || item.classList.contains("disabled");
2909
+ }
2910
+ _rememberPressed(item) {
2911
+ if (!this._originalPressed.has(item)) {
2912
+ this._originalPressed.set(item, item.getAttribute("aria-pressed"));
2913
+ }
2914
+ }
2915
+ _startPress(item) {
2916
+ if (this._activeItem) return;
2917
+ this._finishRelease();
2918
+ const items = this._items;
2919
+ const activeIndex = items.indexOf(item);
2920
+ if (activeIndex < 0) return;
2921
+ this._activeItem = item;
2922
+ item.classList.add(PRESSED_CLASS);
2923
+ if (this.el.classList.contains("connected") || items.length < 2) return;
2924
+ const rects = items.map((candidate) => candidate.getBoundingClientRect());
2925
+ const activeWidth = rects[activeIndex].width;
2926
+ if (activeWidth <= 0) return;
2927
+ const parsedMultiplier = Number.parseFloat(
2928
+ getComputedStyle(this.el).getPropertyValue(
2929
+ "--md-comp-button-group-pressed-item-width-multiplier"
2930
+ )
2931
+ );
2932
+ const multiplier = Number.isFinite(parsedMultiplier) ? parsedMultiplier : 0.15;
2933
+ const desiredGrowth = activeWidth * multiplier;
2934
+ const neighborIndexes = [activeIndex - 1, activeIndex + 1].filter(
2935
+ (index) => index >= 0 && index < items.length
2936
+ );
2937
+ const compression = /* @__PURE__ */ new Map();
2938
+ if (neighborIndexes.length === 1) {
2939
+ const index = neighborIndexes[0];
2940
+ compression.set(
2941
+ index,
2942
+ Math.min(desiredGrowth, this._compressionCapacity(items[index], rects[index]))
2943
+ );
2944
+ } else {
2945
+ const half = desiredGrowth / 2;
2946
+ for (const index of neighborIndexes) {
2947
+ compression.set(
2948
+ index,
2949
+ Math.min(half, this._compressionCapacity(items[index], rects[index]))
2950
+ );
2951
+ }
2952
+ let remaining = desiredGrowth - [...compression.values()].reduce((sum, value) => sum + value, 0);
2953
+ for (const index of neighborIndexes) {
2954
+ if (remaining <= 0) break;
2955
+ const capacity = this._compressionCapacity(items[index], rects[index]) - compression.get(index);
2956
+ const extra = Math.min(remaining, capacity);
2957
+ compression.set(index, compression.get(index) + extra);
2958
+ remaining -= extra;
2959
+ }
2960
+ }
2961
+ const actualGrowth = [...compression.values()].reduce((sum, value) => sum + value, 0);
2962
+ if (actualGrowth <= 0) return;
2963
+ items.forEach((candidate, index) => {
2964
+ this._pressedStyles.set(candidate, {
2965
+ width: candidate.style.width,
2966
+ baseWidth: rects[index].width
2967
+ });
2968
+ candidate.style.width = `${rects[index].width}px`;
2969
+ });
2970
+ void this.el.offsetWidth;
2971
+ items.forEach((candidate, index) => {
2972
+ const adjustment = index === activeIndex ? actualGrowth : -(compression.get(index) || 0);
2973
+ candidate.style.width = `${rects[index].width + adjustment}px`;
2974
+ });
2975
+ }
2976
+ _compressionCapacity(item, rect) {
2977
+ const minimumWidth = item.classList.contains("icon-button") ? rect.width * (1 - MAX_ICON_COMPRESSION) : rect.height;
2978
+ return Math.max(0, rect.width - minimumWidth);
2979
+ }
2980
+ };
2981
+
2442
2982
  // src/ts/components/carousel.ts
2443
- var _defaults7 = {
2983
+ var _defaults10 = {
2444
2984
  duration: 200,
2445
- dist: -100,
2446
- shift: 0,
2447
- padding: 0,
2448
- numVisible: 5,
2449
2985
  fullWidth: false,
2450
2986
  indicators: false,
2451
2987
  noWrap: false,
2452
- onCycleTo: null
2988
+ interval: 0,
2989
+ height: null,
2990
+ onCycleTo: null,
2991
+ i18n: {
2992
+ carousel: "Carousel",
2993
+ item: "Item",
2994
+ of: "of",
2995
+ indicators: "Slides",
2996
+ slide: "Slide"
2997
+ }
2453
2998
  };
2454
2999
  var _Carousel = class _Carousel extends Component {
2455
3000
  constructor(el, options) {
@@ -2460,101 +3005,112 @@ var _Carousel = class _Carousel extends Component {
2460
3005
  this._ignoreScroll = false;
2461
3006
  this._trackRaf = null;
2462
3007
  this._started = false;
3008
+ this._vertical = false;
3009
+ this._resizeObserver = null;
3010
+ this._usesWindowResize = false;
3011
+ this._dragPointerId = null;
3012
+ this._dragStartX = 0;
3013
+ this._dragStartY = 0;
3014
+ this._dragStartScroll = 0;
3015
+ this._suppressClick = false;
3016
+ this._suppressClickTimeout = null;
3017
+ this._generatedContainerRole = false;
3018
+ this._generatedContainerLabel = false;
3019
+ this._generatedContainerDescription = false;
3020
+ this._generatedWideLayout = false;
3021
+ this._generatedFixedHeight = false;
3022
+ this._authoredInlineHeight = null;
3023
+ this._autoAdvanceTimer = null;
3024
+ this._autoAdvances = false;
3025
+ this._autoPaused = false;
3026
+ this._hovered = false;
3027
+ this._focused = false;
3028
+ this._generatedItemTabIndexes = /* @__PURE__ */ new Set();
3029
+ this._generatedItemLabels = /* @__PURE__ */ new Set();
3030
+ this._generatedItemDescriptions = /* @__PURE__ */ new Set();
3031
+ this._generatedItemSizes = /* @__PURE__ */ new Set();
3032
+ this._authoredItemSizes = /* @__PURE__ */ new Set();
3033
+ this._itemA11yOriginal = /* @__PURE__ */ new Map();
2463
3034
  this._handleThrottledResize = Utils.throttle(() => this._handleResize(), 200);
2464
- this._handleCarouselTap = (e) => {
2465
- if (e.pointerType === "mouse" && e.button !== 0) return;
2466
- if (e.target instanceof HTMLElement && e.target.tagName === "IMG") {
2467
- e.preventDefault();
3035
+ this._handleAutoAdvanceState = (e) => {
3036
+ if (e.type === "mouseenter") this._hovered = true;
3037
+ else if (e.type === "mouseleave") this._hovered = false;
3038
+ else if (e.type === "focusin") this._focused = true;
3039
+ else if (e.type === "focusout") this._focused = false;
3040
+ this._syncAutoAdvance();
3041
+ };
3042
+ this._tick = () => {
3043
+ this._autoAdvanceTimer = null;
3044
+ this._advance();
3045
+ this._syncAutoAdvance();
3046
+ };
3047
+ this._advance = () => {
3048
+ const next = this.center + 1;
3049
+ if (next < this.count) {
3050
+ this.set(next);
3051
+ return;
3052
+ }
3053
+ if (this.options.noWrap) {
3054
+ this._autoAdvances = false;
3055
+ this._syncAutoAdvance();
3056
+ return;
2468
3057
  }
3058
+ this.set(0);
3059
+ };
3060
+ this._handleTrackPointerDown = (e) => {
3061
+ if (e.pointerType !== "mouse" || e.button !== 0) return;
2469
3062
  this.pressed = true;
2470
3063
  this.dragged = false;
2471
- this.verticalDragged = false;
2472
- this.reference = e.clientX;
2473
- this.referenceY = e.clientY;
2474
- this.velocity = this.amplitude = 0;
2475
- this.frame = this.offset;
2476
- this.timestamp = Date.now();
3064
+ this._suppressClick = false;
3065
+ this._dragPointerId = e.pointerId;
3066
+ this._dragStartX = e.clientX;
3067
+ this._dragStartY = e.clientY;
3068
+ this._dragStartScroll = this._vertical ? this._scroller.scrollTop : this._scroller.scrollLeft;
2477
3069
  try {
2478
- this.el.setPointerCapture(e.pointerId);
3070
+ this._scroller.setPointerCapture(e.pointerId);
2479
3071
  } catch {
2480
3072
  }
2481
- if (this._trackRaf !== null) cancelAnimationFrame(this._trackRaf);
2482
- const loop = () => {
2483
- this._track();
2484
- if (this.pressed) this._trackRaf = requestAnimationFrame(loop);
2485
- else this._trackRaf = null;
2486
- };
2487
- this._trackRaf = requestAnimationFrame(loop);
2488
- };
2489
- this._handleCarouselDrag = (e) => {
2490
- if (!this.pressed) return;
2491
- const delta = this.reference - e.clientX;
2492
- const deltaY = Math.abs(this.referenceY - e.clientY);
2493
- if (deltaY < 30 && !this.verticalDragged) {
2494
- if (delta > 2 || delta < -2) {
2495
- this.dragged = true;
2496
- this.reference = e.clientX;
2497
- this._scroll(this.offset + delta);
2498
- }
2499
- } else if (this.dragged) {
2500
- e.preventDefault();
2501
- } else {
2502
- this.verticalDragged = true;
3073
+ };
3074
+ this._handleTrackPointerMove = (e) => {
3075
+ if (!this.pressed || e.pointerId !== this._dragPointerId) return;
3076
+ const deltaX = e.clientX - this._dragStartX;
3077
+ const deltaY = e.clientY - this._dragStartY;
3078
+ const primaryDelta = this._vertical ? deltaY : deltaX;
3079
+ const crossDelta = this._vertical ? deltaX : deltaY;
3080
+ if (!this.dragged) {
3081
+ if (Math.abs(primaryDelta) < 6 || Math.abs(primaryDelta) <= Math.abs(crossDelta)) return;
3082
+ this.dragged = true;
3083
+ this.el.classList.add("dragging");
2503
3084
  }
2504
- if (this.dragged) e.preventDefault();
3085
+ if (this._vertical) this._scroller.scrollTop = this._dragStartScroll - deltaY;
3086
+ else this._scroller.scrollLeft = this._dragStartScroll - deltaX;
3087
+ e.preventDefault();
2505
3088
  };
2506
- this._handleCarouselRelease = (e) => {
2507
- if (!this.pressed) return;
3089
+ this._handleTrackPointerUp = (e) => {
3090
+ if (!this.pressed || e.pointerId !== this._dragPointerId) return;
2508
3091
  this.pressed = false;
2509
- if (this._trackRaf !== null) {
2510
- cancelAnimationFrame(this._trackRaf);
2511
- this._trackRaf = null;
2512
- }
3092
+ this._dragPointerId = null;
3093
+ this.el.classList.remove("dragging");
2513
3094
  try {
2514
- this.el.releasePointerCapture(e.pointerId);
3095
+ this._scroller.releasePointerCapture(e.pointerId);
2515
3096
  } catch {
2516
3097
  }
2517
- this.target = this.offset;
2518
- if (this.velocity > 10 || this.velocity < -10) {
2519
- this.amplitude = 0.9 * this.velocity;
2520
- this.target = this.offset + this.amplitude;
2521
- }
2522
- this.target = Math.round(this.target / this.dim) * this.dim;
2523
- if (this.noWrap) {
2524
- if (this.target >= this.dim * (this.count - 1)) {
2525
- this.target = this.dim * (this.count - 1);
2526
- } else if (this.target < 0) {
2527
- this.target = 0;
2528
- }
2529
- }
2530
- this.amplitude = this.target - this.offset;
2531
- this.timestamp = Date.now();
2532
- requestAnimationFrame(this._autoScroll);
2533
- if (this.dragged) e.preventDefault();
3098
+ if (!this.dragged) return;
3099
+ this._suppressClick = true;
3100
+ window.clearTimeout(this._suppressClickTimeout);
3101
+ this._suppressClickTimeout = setTimeout(() => {
3102
+ this._suppressClick = false;
3103
+ }, 0);
3104
+ this._syncActive(this._nearestIndex(), true);
3105
+ };
3106
+ this._handleTrackClick = (e) => {
3107
+ if (!this._suppressClick) return;
3108
+ this._suppressClick = false;
3109
+ e.preventDefault();
3110
+ e.stopImmediatePropagation();
2534
3111
  };
2535
- this._handleCarouselClick = (e) => {
2536
- if (this.dragged) {
2537
- e.preventDefault();
2538
- e.stopPropagation();
2539
- return;
2540
- }
2541
- const clickedElem = e.target.closest(".carousel-item");
2542
- if (!clickedElem || !this.el.contains(clickedElem)) return;
2543
- const clickedIndex = this.images.indexOf(clickedElem);
2544
- const diff = this._wrap(this.center) - clickedIndex;
2545
- if (diff !== 0) {
2546
- e.preventDefault();
2547
- e.stopPropagation();
2548
- }
2549
- if (clickedIndex < 0) {
2550
- if (e.clientX - e.target.getBoundingClientRect().left > this.el.clientWidth / 2) {
2551
- this.next();
2552
- } else {
2553
- this.prev();
2554
- }
2555
- } else {
2556
- this._cycleTo(clickedIndex);
2557
- }
3112
+ this._handleTrackDragStart = (e) => {
3113
+ if (e.target.closest("img, picture, video")) e.preventDefault();
2558
3114
  };
2559
3115
  this._handleIndicatorClick = (e) => {
2560
3116
  e.stopPropagation();
@@ -2567,112 +3123,107 @@ var _Carousel = class _Carousel extends Component {
2567
3123
  this._handleKeydown = (e) => {
2568
3124
  const target = e.target;
2569
3125
  if (target.closest('input, textarea, select, [contenteditable="true"]')) return;
2570
- if (Utils.keys.ARROW_RIGHT.includes(e.key)) {
3126
+ const forward = this._vertical ? e.key === "ArrowDown" : e.key === Utils.keys.ARROW_RIGHT;
3127
+ const backward = this._vertical ? e.key === "ArrowUp" : e.key === Utils.keys.ARROW_LEFT;
3128
+ if (e.key === "Home") {
3129
+ e.preventDefault();
3130
+ this.set(0);
3131
+ this._focusItem(0);
3132
+ } else if (e.key === "End") {
3133
+ e.preventDefault();
3134
+ this.set(this.count - 1);
3135
+ this._focusItem(this.count - 1);
3136
+ } else if (forward) {
2571
3137
  e.preventDefault();
2572
3138
  this.next();
2573
- } else if (Utils.keys.ARROW_LEFT.includes(e.key)) {
3139
+ this._focusItem(this.center);
3140
+ } else if (backward) {
2574
3141
  e.preventDefault();
2575
3142
  this.prev();
3143
+ this._focusItem(this.center);
2576
3144
  }
2577
3145
  };
2578
- this._handleFlatScroll = () => {
2579
- if (this._ignoreScroll) return;
2580
- const index = this._nearestIndex();
2581
- if (index !== this.center) this._syncActive(index, this.dragged);
3146
+ this._handleTrackScroll = () => {
3147
+ if (!this.el.classList.contains("scrolling")) this.el.classList.add("scrolling");
3148
+ window.clearTimeout(this.scrollingTimeout);
3149
+ this.scrollingTimeout = setTimeout(() => this.el.classList.remove("scrolling"), 120);
3150
+ if (this._trackRaf !== null) return;
3151
+ this._trackRaf = requestAnimationFrame(() => {
3152
+ this._trackRaf = null;
3153
+ this._updateParallax();
3154
+ if (this._ignoreScroll) return;
3155
+ const index = this._nearestIndex();
3156
+ if (index !== this.center) this._syncActive(index, this.dragged);
3157
+ });
2582
3158
  };
2583
3159
  this._handleResize = () => {
2584
3160
  if (!this.images.length) return;
2585
- if (this._flat) {
2586
- this._scrollToIndex(this.center, false);
2587
- return;
2588
- }
2589
- if (this.options.fullWidth) {
2590
- const firstItem = this.images[0];
2591
- if (!firstItem) return;
2592
- this.itemWidth = firstItem.clientWidth;
2593
- this.dim = this.itemWidth * 2 + this.options.padding;
2594
- this.offset = this.center * 2 * this.itemWidth;
2595
- this.target = this.offset;
2596
- } else {
2597
- this._scroll();
2598
- }
2599
- };
2600
- this._track = () => {
2601
- const now = Date.now();
2602
- const elapsed = now - this.timestamp;
2603
- const delta = this.offset - this.frame;
2604
- const v = 1e3 * delta / (1 + elapsed);
2605
- this.timestamp = now;
2606
- this.frame = this.offset;
2607
- this.velocity = 0.8 * v + 0.2 * this.velocity;
2608
- };
2609
- this._autoScroll = () => {
2610
- if (!this.amplitude) return;
2611
- const elapsed = Date.now() - this.timestamp;
2612
- const delta = this.amplitude * Math.exp(-elapsed / this.options.duration);
2613
- if (delta > 2 || delta < -2) {
2614
- this._scroll(this.target - delta);
2615
- requestAnimationFrame(this._autoScroll);
2616
- } else {
2617
- this._scroll(this.target);
2618
- }
3161
+ this._syncAdaptiveMode();
3162
+ this._syncLayoutRoles(this.center);
3163
+ this._scrollToIndex(this.center, false);
3164
+ this._updateParallax();
2619
3165
  };
2620
3166
  this.el["Expressive_Carousel"] = this;
2621
3167
  this.options = {
2622
3168
  ..._Carousel.defaults,
2623
3169
  ...options
2624
3170
  };
2625
- this._flat = this.options.fullWidth || this.el.classList.contains("flat");
2626
- if (this._flat) {
3171
+ if (options?.i18n) {
3172
+ this.options.i18n = { ..._Carousel.defaults.i18n, ...options.i18n };
3173
+ }
3174
+ if (this.options.fullWidth || this.el.classList.contains("flat")) {
2627
3175
  this.el.classList.add("flat");
2628
3176
  this.options.fullWidth = true;
2629
- this.options.dist = 0;
2630
- }
2631
- if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
2632
- this.options.duration = 1;
2633
- if (!this._flat) this.options.dist = 0;
2634
3177
  }
3178
+ this._vertical = this.el.classList.contains("full-screen");
3179
+ const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
3180
+ if (reducedMotion) this.options.duration = 1;
2635
3181
  this.pressed = false;
2636
3182
  this.dragged = false;
2637
- this.offset = this.target = 0;
2638
3183
  this.images = this._collectItems();
2639
3184
  if (!this.images.length) {
2640
3185
  console.error("Carousel: no .carousel-item elements to show");
2641
3186
  return;
2642
3187
  }
3188
+ this.images.forEach((item) => {
3189
+ this._itemA11yOriginal.set(item, {
3190
+ ariaHidden: item.getAttribute("aria-hidden"),
3191
+ tabIndex: item.getAttribute("tabindex")
3192
+ });
3193
+ if (item.hasAttribute("data-carousel-size")) this._authoredItemSizes.add(item);
3194
+ });
2643
3195
  this.hasMultipleSlides = this.images.length > 1;
2644
3196
  this.showIndicators = this.options.indicators && this.hasMultipleSlides;
2645
- this.noWrap = this.options.noWrap || !this.hasMultipleSlides || this._flat;
2646
3197
  this.count = this.images.length;
2647
- this.options.numVisible = Math.min(this.count, this.options.numVisible);
3198
+ this._autoAdvances = this.options.interval > 0 && this.hasMultipleSlides && !reducedMotion;
2648
3199
  this.images.forEach((item) => item.classList.add("carousel-item"));
2649
- if (this._flat) this._wrapTrack();
2650
- const firstItem = this.images[0];
2651
- this.itemWidth = firstItem.clientWidth || this.el.clientWidth || 1;
2652
- this.itemHeight = firstItem.clientHeight || this.el.clientHeight || 1;
2653
- this.dim = this.itemWidth * 2 + this.options.padding || 1;
3200
+ if (this.options.height !== null) {
3201
+ this._generatedFixedHeight = !this.el.classList.contains("fixed-height");
3202
+ this._authoredInlineHeight = this.el.style.getPropertyValue("--carousel-height") || null;
3203
+ this.el.classList.add("fixed-height");
3204
+ this.el.style.setProperty("--carousel-height", `${this.options.height}px`);
3205
+ }
3206
+ this._wrapTrack();
3207
+ this._syncAdaptiveMode();
2654
3208
  if (this.showIndicators) this._setupIndicators();
2655
- this.el.tabIndex = this.el.tabIndex >= 0 ? this.el.tabIndex : 0;
2656
- this.el.setAttribute("aria-roledescription", "carousel");
3209
+ this._setupAccessibility();
2657
3210
  this._setupEventHandlers();
2658
3211
  this._started = true;
2659
- if (this._flat) {
2660
- const start = Math.max(
2661
- 0,
2662
- this.images.findIndex((el2) => el2.classList.contains("active"))
2663
- );
2664
- this.center = start;
2665
- this._syncActive(start, false);
2666
- this._scrollToIndex(start, false);
2667
- } else {
2668
- this._scroll(this.offset);
2669
- }
3212
+ const start = Math.max(
3213
+ 0,
3214
+ this.images.findIndex((el2) => el2.classList.contains("active"))
3215
+ );
3216
+ this.center = start;
3217
+ this._syncActive(start, false);
3218
+ this._updateParallax();
3219
+ this._scrollToIndex(start, false);
3220
+ this._syncAutoAdvance();
2670
3221
  }
2671
3222
  get _scroller() {
2672
3223
  return this._trackEl ?? this.el;
2673
3224
  }
2674
3225
  static get defaults() {
2675
- return _defaults7;
3226
+ return _defaults10;
2676
3227
  }
2677
3228
  static init(els, options = {}) {
2678
3229
  return super.init(els, options, _Carousel);
@@ -2697,6 +3248,39 @@ var _Carousel = class _Carousel extends Component {
2697
3248
  return el.tagName !== "NAV";
2698
3249
  });
2699
3250
  }
3251
+ _setupAccessibility() {
3252
+ if (!this.el.hasAttribute("role")) {
3253
+ this.el.setAttribute("role", "region");
3254
+ this._generatedContainerRole = true;
3255
+ }
3256
+ if (!this.el.hasAttribute("aria-label") && !this.el.hasAttribute("aria-labelledby")) {
3257
+ this.el.setAttribute("aria-label", this.options.i18n.carousel);
3258
+ this._generatedContainerLabel = true;
3259
+ }
3260
+ if (!this.el.hasAttribute("aria-roledescription")) {
3261
+ this.el.setAttribute("aria-roledescription", "carousel");
3262
+ this._generatedContainerDescription = true;
3263
+ }
3264
+ const naturallyFocusable = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
3265
+ this.images.forEach((item, index) => {
3266
+ if (!item.hasAttribute("aria-roledescription")) {
3267
+ item.setAttribute("aria-roledescription", "slide");
3268
+ this._generatedItemDescriptions.add(item);
3269
+ }
3270
+ if (!item.hasAttribute("aria-label") && !item.hasAttribute("aria-labelledby")) {
3271
+ const mediaName = item.querySelector("img[alt]")?.getAttribute("alt")?.trim();
3272
+ const textName = item.textContent?.replace(/\s+/g, " ").trim();
3273
+ const name = mediaName || textName;
3274
+ const position = `${this.options.i18n.item} ${index + 1} ${this.options.i18n.of} ${this.count}`;
3275
+ item.setAttribute("aria-label", name ? `${name}, ${position}` : position);
3276
+ this._generatedItemLabels.add(item);
3277
+ }
3278
+ if (!item.matches(naturallyFocusable) && !item.querySelector(naturallyFocusable)) {
3279
+ item.tabIndex = 0;
3280
+ this._generatedItemTabIndexes.add(item);
3281
+ }
3282
+ });
3283
+ }
2700
3284
  _setupIndicators() {
2701
3285
  const existing = Array.from(this.el.children).find(
2702
3286
  (el) => el instanceof HTMLElement && el.classList.contains("indicators")
@@ -2704,7 +3288,7 @@ var _Carousel = class _Carousel extends Component {
2704
3288
  this._indicators = existing ?? document.createElement("nav");
2705
3289
  this._ownIndicators = !existing;
2706
3290
  this._indicators.classList.add("indicators");
2707
- this._indicators.setAttribute("aria-label", "Slides");
3291
+ this._indicators.setAttribute("aria-label", this.options.i18n.indicators);
2708
3292
  this._indicators.replaceChildren();
2709
3293
  this.images.forEach((_, i) => {
2710
3294
  const button = document.createElement("button");
@@ -2714,158 +3298,124 @@ var _Carousel = class _Carousel extends Component {
2714
3298
  button.classList.add("active");
2715
3299
  button.setAttribute("aria-current", "true");
2716
3300
  }
2717
- button.setAttribute("aria-label", `Slide ${i + 1}`);
3301
+ button.setAttribute("aria-label", `${this.options.i18n.slide} ${i + 1}`);
2718
3302
  this._indicators.appendChild(button);
2719
3303
  });
2720
3304
  if (this._ownIndicators) this.el.appendChild(this._indicators);
2721
- if (this._flat) {
2722
- this.el.querySelector(".carousel-fixed-item")?.classList.add("with-indicators");
2723
- }
3305
+ this.el.querySelector(".carousel-fixed-item")?.classList.add("with-indicators");
2724
3306
  }
2725
3307
  _setupEventHandlers() {
2726
3308
  this.el.addEventListener("keydown", this._handleKeydown);
3309
+ if (this._autoAdvances) {
3310
+ this.el.addEventListener("mouseenter", this._handleAutoAdvanceState);
3311
+ this.el.addEventListener("mouseleave", this._handleAutoAdvanceState);
3312
+ this.el.addEventListener("focusin", this._handleAutoAdvanceState);
3313
+ this.el.addEventListener("focusout", this._handleAutoAdvanceState);
3314
+ document.addEventListener("visibilitychange", this._handleAutoAdvanceState);
3315
+ }
2727
3316
  if (this.showIndicators && this._indicators) {
2728
3317
  this._indicators.addEventListener("click", this._handleIndicatorClick);
2729
3318
  }
2730
- _Carousel._live.add(this);
2731
- if (!_Carousel._resizeListening) {
2732
- window.addEventListener("resize", _Carousel._onResize, { passive: true });
2733
- _Carousel._resizeListening = true;
3319
+ if (typeof ResizeObserver !== "undefined") {
3320
+ this._resizeObserver = new ResizeObserver(() => this._handleThrottledResize());
3321
+ this._resizeObserver.observe(this.el);
2734
3322
  }
2735
- if (this._flat) {
2736
- this._scroller.addEventListener("scroll", this._handleFlatScroll, { passive: true });
2737
- return;
3323
+ this._usesWindowResize = !this._resizeObserver || this.el.classList.contains("full-screen");
3324
+ if (this._usesWindowResize) {
3325
+ _Carousel._live.add(this);
3326
+ if (!_Carousel._resizeListening) {
3327
+ window.addEventListener("resize", _Carousel._onResize, {
3328
+ passive: true
3329
+ });
3330
+ _Carousel._resizeListening = true;
3331
+ }
2738
3332
  }
2739
- this.el.addEventListener("pointerdown", this._handleCarouselTap);
2740
- this.el.addEventListener("pointermove", this._handleCarouselDrag);
2741
- this.el.addEventListener("pointerup", this._handleCarouselRelease);
2742
- this.el.addEventListener("pointercancel", this._handleCarouselRelease);
2743
- this.el.addEventListener("click", this._handleCarouselClick);
3333
+ this._scroller.addEventListener("scroll", this._handleTrackScroll, {
3334
+ passive: true
3335
+ });
3336
+ this._scroller.addEventListener("pointerdown", this._handleTrackPointerDown);
3337
+ this._scroller.addEventListener("pointermove", this._handleTrackPointerMove);
3338
+ this._scroller.addEventListener("pointerup", this._handleTrackPointerUp);
3339
+ this._scroller.addEventListener("pointercancel", this._handleTrackPointerUp);
3340
+ this._scroller.addEventListener("click", this._handleTrackClick, true);
3341
+ this._scroller.addEventListener("dragstart", this._handleTrackDragStart);
2744
3342
  }
2745
3343
  _removeEventHandlers() {
2746
3344
  this.el.removeEventListener("keydown", this._handleKeydown);
3345
+ this.el.removeEventListener("mouseenter", this._handleAutoAdvanceState);
3346
+ this.el.removeEventListener("mouseleave", this._handleAutoAdvanceState);
3347
+ this.el.removeEventListener("focusin", this._handleAutoAdvanceState);
3348
+ this.el.removeEventListener("focusout", this._handleAutoAdvanceState);
3349
+ document.removeEventListener("visibilitychange", this._handleAutoAdvanceState);
2747
3350
  if (this._indicators) {
2748
3351
  this._indicators.removeEventListener("click", this._handleIndicatorClick);
2749
3352
  }
2750
- this._scroller.removeEventListener("scroll", this._handleFlatScroll);
2751
- this.el.removeEventListener("pointerdown", this._handleCarouselTap);
2752
- this.el.removeEventListener("pointermove", this._handleCarouselDrag);
2753
- this.el.removeEventListener("pointerup", this._handleCarouselRelease);
2754
- this.el.removeEventListener("pointercancel", this._handleCarouselRelease);
2755
- this.el.removeEventListener("click", this._handleCarouselClick);
2756
- _Carousel._live.delete(this);
2757
- if (_Carousel._live.size === 0 && _Carousel._resizeListening) {
2758
- window.removeEventListener("resize", _Carousel._onResize);
2759
- _Carousel._resizeListening = false;
3353
+ this._scroller.removeEventListener("scroll", this._handleTrackScroll);
3354
+ this._scroller.removeEventListener("pointerdown", this._handleTrackPointerDown);
3355
+ this._scroller.removeEventListener("pointermove", this._handleTrackPointerMove);
3356
+ this._scroller.removeEventListener("pointerup", this._handleTrackPointerUp);
3357
+ this._scroller.removeEventListener("pointercancel", this._handleTrackPointerUp);
3358
+ this._scroller.removeEventListener("click", this._handleTrackClick, true);
3359
+ this._scroller.removeEventListener("dragstart", this._handleTrackDragStart);
3360
+ this._resizeObserver?.disconnect();
3361
+ this._resizeObserver = null;
3362
+ if (this._usesWindowResize) {
3363
+ _Carousel._live.delete(this);
3364
+ if (_Carousel._live.size === 0 && _Carousel._resizeListening) {
3365
+ window.removeEventListener("resize", _Carousel._onResize);
3366
+ _Carousel._resizeListening = false;
3367
+ }
2760
3368
  }
2761
3369
  }
2762
3370
  _teardown() {
2763
3371
  this._removeEventHandlers();
3372
+ this._autoAdvances = false;
3373
+ this._syncAutoAdvance();
2764
3374
  if (this._trackRaf !== null) cancelAnimationFrame(this._trackRaf);
2765
3375
  window.clearTimeout(this.scrollingTimeout);
3376
+ window.clearTimeout(this._suppressClickTimeout);
2766
3377
  if (this._ownIndicators) this._indicators?.remove();
2767
3378
  this._unwrapTrack();
2768
3379
  this.images.forEach((el) => {
2769
- el.style.transform = "";
2770
- el.style.zIndex = "";
2771
- el.style.opacity = "";
2772
- el.style.visibility = "";
3380
+ el.style.removeProperty("--md-comp-carousel-item-parallax");
3381
+ if (this._generatedItemLabels.has(el)) el.removeAttribute("aria-label");
3382
+ if (this._generatedItemDescriptions.has(el)) el.removeAttribute("aria-roledescription");
3383
+ if (this._generatedItemSizes.has(el)) el.removeAttribute("data-carousel-size");
3384
+ const original = this._itemA11yOriginal.get(el);
3385
+ if (original?.ariaHidden === null) el.removeAttribute("aria-hidden");
3386
+ else if (original) el.setAttribute("aria-hidden", original.ariaHidden);
3387
+ if (original?.tabIndex === null) el.removeAttribute("tabindex");
3388
+ else if (original) el.setAttribute("tabindex", original.tabIndex);
2773
3389
  });
2774
- }
2775
- _xpos(e) {
2776
- if ("clientX" in e) return e.clientX;
2777
- return 0;
2778
- }
2779
- _ypos(e) {
2780
- if ("clientY" in e) return e.clientY;
2781
- return 0;
2782
- }
2783
- _wrap(x) {
2784
- return x >= this.count ? x % this.count : x < 0 ? this._wrap(this.count + x % this.count) : x;
2785
- }
2786
- _scroll(x = 0) {
2787
- if (!this.el.classList.contains("scrolling")) {
2788
- this.el.classList.add("scrolling");
2789
- }
2790
- window.clearTimeout(this.scrollingTimeout);
2791
- this.scrollingTimeout = setTimeout(() => {
2792
- this.el.classList.remove("scrolling");
2793
- }, this.options.duration);
2794
- this.offset = typeof x === "number" ? x : this.offset;
2795
- this.center = Math.floor((this.offset + this.dim / 2) / this.dim);
2796
- const half = this.count >> 1;
2797
- const delta = this.offset - this.center * this.dim;
2798
- const dir = delta < 0 ? 1 : -1;
2799
- const tween = -dir * delta * 2 / this.dim;
2800
- const lastCenter = this.center;
2801
- const numVisibleOffset = 1 / this.options.numVisible;
2802
- let alignment;
2803
- let centerTweenedOpacity;
2804
- if (this.options.fullWidth) {
2805
- alignment = "translateX(0)";
2806
- centerTweenedOpacity = 1;
2807
- } else {
2808
- alignment = "translateX(" + (this.el.clientWidth - this.itemWidth) / 2 + "px) ";
2809
- alignment += "translateY(" + (this.el.clientHeight - this.itemHeight) / 2 + "px)";
2810
- centerTweenedOpacity = 1 - numVisibleOffset * tween;
2811
- }
2812
- this._paintIndicators(this.center);
2813
- if (!this.noWrap || this.center >= 0 && this.center < this.count) {
2814
- const el = this.images[this._wrap(this.center)];
2815
- if (el && !el.classList.contains("active")) {
2816
- this.el.querySelector(".carousel-item.active")?.classList.remove("active");
2817
- el.classList.add("active");
2818
- }
2819
- }
2820
- for (let i = 1; i <= half; ++i) {
2821
- let zTranslation;
2822
- let tweenedOpacity;
2823
- if (this.options.fullWidth) {
2824
- zTranslation = this.options.dist;
2825
- tweenedOpacity = i === half && delta < 0 ? 1 - tween : 1;
2826
- } else {
2827
- zTranslation = this.options.dist * (i * 2 + tween * dir);
2828
- tweenedOpacity = 1 - numVisibleOffset * (i * 2 + tween * dir);
2829
- }
2830
- if (!this.noWrap || this.center + i < this.count) {
2831
- const el = this.images[this._wrap(this.center + i)];
2832
- const transformString = `${alignment} translateX(${this.options.shift + (this.dim * i - delta) / 2}px) translateZ(${zTranslation}px)`;
2833
- this._updateItemStyle(el, tweenedOpacity, -i, transformString);
2834
- }
2835
- if (this.options.fullWidth) {
2836
- zTranslation = this.options.dist;
2837
- tweenedOpacity = i === half && delta > 0 ? 1 - tween : 1;
2838
- } else {
2839
- zTranslation = this.options.dist * (i * 2 - tween * dir);
2840
- tweenedOpacity = 1 - numVisibleOffset * (i * 2 - tween * dir);
2841
- }
2842
- if (!this.noWrap || this.center - i >= 0) {
2843
- const el = this.images[this._wrap(this.center - i)];
2844
- const transformString = `${alignment} translateX(${-this.options.shift + (-this.dim * i - delta) / 2}px) translateZ(${zTranslation}px)`;
2845
- this._updateItemStyle(el, tweenedOpacity, -i, transformString);
2846
- }
2847
- }
2848
- if (!this.noWrap || this.center >= 0 && this.center < this.count) {
2849
- const el = this.images[this._wrap(this.center)];
2850
- const transformString = `${alignment} translateX(${-delta / 2}px) translateX(${dir * this.options.shift * tween}px) translateZ(${this.options.dist * tween}px)`;
2851
- this._updateItemStyle(el, centerTweenedOpacity, 0, transformString);
2852
- }
2853
- const currItem = this.images[this._wrap(this.center)];
2854
- this._syncA11y(this._wrap(this.center));
2855
- if (lastCenter !== this.center && typeof this.options.onCycleTo === "function") {
2856
- this.options.onCycleTo.call(this, currItem, this.dragged);
2857
- }
2858
- if (typeof this.oneTimeCallback === "function") {
2859
- this.oneTimeCallback.call(this, currItem, this.dragged);
2860
- this.oneTimeCallback = null;
3390
+ if (this._generatedContainerRole) this.el.removeAttribute("role");
3391
+ if (this._generatedContainerLabel) this.el.removeAttribute("aria-label");
3392
+ if (this._generatedContainerDescription) this.el.removeAttribute("aria-roledescription");
3393
+ if (this._generatedFixedHeight) this.el.classList.remove("fixed-height");
3394
+ if (this.options.height !== null) {
3395
+ if (this._authoredInlineHeight === null) this.el.style.removeProperty("--carousel-height");
3396
+ else this.el.style.setProperty("--carousel-height", this._authoredInlineHeight);
3397
+ }
3398
+ if (this._generatedWideLayout) {
3399
+ this.el.classList.remove(
3400
+ "multi-wide",
3401
+ "multi-large",
3402
+ "multi-extra-large",
3403
+ "full-screen-horizontal"
3404
+ );
2861
3405
  }
2862
3406
  }
2863
- _updateItemStyle(el, opacity, zIndex, transform) {
2864
- if (!el) return;
2865
- el.style.transform = transform;
2866
- el.style.zIndex = zIndex.toString();
2867
- el.style.opacity = opacity.toString();
2868
- el.style.visibility = "visible";
3407
+ /** Start or stop the timer to match the current pause state. */
3408
+ _syncAutoAdvance() {
3409
+ const run = this._autoAdvances && !this._autoPaused && !this._hovered && !this._focused && !document.hidden;
3410
+ if (run && this._autoAdvanceTimer === null) {
3411
+ this._autoAdvanceTimer = setTimeout(
3412
+ this._tick,
3413
+ this.options.duration + this.options.interval
3414
+ );
3415
+ } else if (!run && this._autoAdvanceTimer !== null) {
3416
+ clearTimeout(this._autoAdvanceTimer);
3417
+ this._autoAdvanceTimer = null;
3418
+ }
2869
3419
  }
2870
3420
  _paintIndicators(center) {
2871
3421
  if (!this.showIndicators || !this._indicators) return;
@@ -2877,18 +3427,12 @@ var _Carousel = class _Carousel extends Component {
2877
3427
  else el.removeAttribute("aria-current");
2878
3428
  });
2879
3429
  }
2880
- _syncA11y(index) {
2881
- this.images.forEach((el, i) => {
2882
- if (i === index) el.removeAttribute("aria-hidden");
2883
- else el.setAttribute("aria-hidden", "true");
2884
- });
2885
- }
2886
3430
  _syncActive(index, dragged) {
2887
3431
  const prev = this.center;
2888
3432
  this.center = index;
2889
3433
  this.images.forEach((el, i) => el.classList.toggle("active", i === index));
2890
3434
  this._paintIndicators(index);
2891
- this._syncA11y(index);
3435
+ this._syncLayoutRoles(index);
2892
3436
  const curr = this.images[index];
2893
3437
  if (prev !== index && typeof this.options.onCycleTo === "function") {
2894
3438
  this.options.onCycleTo.call(this, curr, dragged);
@@ -2898,6 +3442,81 @@ var _Carousel = class _Carousel extends Component {
2898
3442
  this.oneTimeCallback = null;
2899
3443
  }
2900
3444
  }
3445
+ _syncLayoutRoles(index) {
3446
+ const setSize = (item, size) => {
3447
+ if (this._authoredItemSizes.has(item)) return;
3448
+ item.setAttribute("data-carousel-size", size);
3449
+ this._generatedItemSizes.add(item);
3450
+ };
3451
+ this.images.forEach((item) => setSize(item, "large"));
3452
+ if (this.el.classList.contains("flat") || this.el.classList.contains("uncontained") || this.el.classList.contains("full-screen") && this._vertical) {
3453
+ return;
3454
+ }
3455
+ if (this.el.classList.contains("hero") || this.el.classList.contains("full-screen-horizontal")) {
3456
+ const next = this.images[index + 1];
3457
+ if (next) setSize(next, "small");
3458
+ if (this.el.classList.contains("center-aligned")) {
3459
+ const previous = this.images[index - 1];
3460
+ if (previous) setSize(previous, "small");
3461
+ }
3462
+ return;
3463
+ }
3464
+ const width = this.el.clientWidth;
3465
+ const desiredLargeCount = width >= 1600 ? 4 : width >= 1200 ? 3 : width >= 600 ? 2 : 1;
3466
+ const largeCount = Math.min(desiredLargeCount, Math.max(1, this.count - 2));
3467
+ this.el.classList.remove("multi-wide", "multi-large", "multi-extra-large");
3468
+ if (largeCount === 2) this.el.classList.add("multi-wide");
3469
+ else if (largeCount === 3) this.el.classList.add("multi-large");
3470
+ else if (largeCount >= 4) this.el.classList.add("multi-extra-large");
3471
+ this._generatedWideLayout = this._generatedWideLayout || largeCount > 1;
3472
+ for (let offset = 1; offset < largeCount; offset += 1) {
3473
+ const item = this.images[index + offset];
3474
+ if (item) setSize(item, "large");
3475
+ }
3476
+ const medium = this.images[index + largeCount];
3477
+ const small = this.images[index + largeCount + 1];
3478
+ if (medium) setSize(medium, "medium");
3479
+ if (small) setSize(small, "small");
3480
+ }
3481
+ _syncAdaptiveMode() {
3482
+ if (!this.el.classList.contains("full-screen")) return;
3483
+ const landscape = window.matchMedia("(orientation: landscape)").matches;
3484
+ const horizontal = this.el.clientWidth >= 840 || landscape;
3485
+ const wasVertical = this._vertical;
3486
+ this._vertical = !horizontal;
3487
+ this.el.classList.toggle("full-screen-horizontal", horizontal);
3488
+ this._generatedWideLayout = this._generatedWideLayout || horizontal;
3489
+ if (this._started && wasVertical !== this._vertical) {
3490
+ this._scroller.scrollTo({ left: 0, top: 0, behavior: "auto" });
3491
+ }
3492
+ }
3493
+ _focusItem(index) {
3494
+ const item = this.images[index];
3495
+ if (!item) return;
3496
+ const focusable = item.matches(
3497
+ 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
3498
+ ) ? item : item.querySelector(
3499
+ 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
3500
+ );
3501
+ (focusable ?? item).focus({ preventScroll: true });
3502
+ }
3503
+ _updateParallax() {
3504
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
3505
+ this.images.forEach(
3506
+ (item) => item.style.setProperty("--md-comp-carousel-item-parallax", "0px")
3507
+ );
3508
+ return;
3509
+ }
3510
+ const scrollerRect = this._scroller.getBoundingClientRect();
3511
+ const viewportCenter = this._vertical ? scrollerRect.top + scrollerRect.height / 2 : scrollerRect.left + scrollerRect.width / 2;
3512
+ const viewportSize = Math.max(1, this._vertical ? scrollerRect.height : scrollerRect.width);
3513
+ this.images.forEach((item) => {
3514
+ const rect = item.getBoundingClientRect();
3515
+ const itemCenter = this._vertical ? rect.top + rect.height / 2 : rect.left + rect.width / 2;
3516
+ const progress = Math.max(-1, Math.min(1, (itemCenter - viewportCenter) / viewportSize));
3517
+ item.style.setProperty("--md-comp-carousel-item-parallax", `${progress * -24}px`);
3518
+ });
3519
+ }
2901
3520
  _wrapTrack() {
2902
3521
  const track = document.createElement("div");
2903
3522
  track.className = "carousel-track";
@@ -2912,11 +3531,16 @@ var _Carousel = class _Carousel extends Component {
2912
3531
  this._trackEl = null;
2913
3532
  }
2914
3533
  _nearestIndex() {
2915
- const left = this._scroller.scrollLeft;
3534
+ const centered = this.el.classList.contains("center-aligned");
3535
+ const position = this._vertical ? this._scroller.scrollTop : this._scroller.scrollLeft;
3536
+ const viewportCenter = position + (this._vertical ? this._scroller.clientHeight : this._scroller.clientWidth) / 2;
2916
3537
  let best = 0;
2917
3538
  let bestDist = Infinity;
2918
3539
  this.images.forEach((el, i) => {
2919
- const dist = Math.abs(el.offsetLeft - left);
3540
+ const start = this._vertical ? el.offsetTop : el.offsetLeft;
3541
+ const size = this._vertical ? el.offsetHeight : el.offsetWidth;
3542
+ const target = centered ? start + size / 2 : start;
3543
+ const dist = Math.abs(target - (centered ? viewportCenter : position));
2920
3544
  if (dist < bestDist) {
2921
3545
  bestDist = dist;
2922
3546
  best = i;
@@ -2927,13 +3551,24 @@ var _Carousel = class _Carousel extends Component {
2927
3551
  _scrollToIndex(n, smooth = true) {
2928
3552
  const el = this.images[n];
2929
3553
  if (!el) return;
2930
- const left = el.offsetLeft;
2931
- if (Math.abs(this._scroller.scrollLeft - left) < 1) {
3554
+ const centered = this.el.classList.contains("center-aligned");
3555
+ const style = getComputedStyle(this._scroller);
3556
+ const inlinePadding = Number.parseFloat(style.paddingInlineStart) || 0;
3557
+ const blockPadding = Number.parseFloat(style.paddingBlockStart) || 0;
3558
+ const left = centered ? el.offsetLeft - (this._scroller.clientWidth - el.offsetWidth) / 2 : el.offsetLeft - inlinePadding;
3559
+ const top = el.offsetTop - blockPadding;
3560
+ const current = this._vertical ? this._scroller.scrollTop : this._scroller.scrollLeft;
3561
+ const target = this._vertical ? top : left;
3562
+ if (Math.abs(current - target) < 1) {
2932
3563
  this._syncActive(n, false);
2933
3564
  return;
2934
3565
  }
2935
3566
  this._ignoreScroll = true;
2936
- this._scroller.scrollTo({ left, behavior: smooth ? "smooth" : "auto" });
3567
+ this._scroller.scrollTo({
3568
+ left: this._vertical ? 0 : left,
3569
+ top: this._vertical ? top : 0,
3570
+ behavior: smooth ? "smooth" : "auto"
3571
+ });
2937
3572
  const done = () => {
2938
3573
  this._ignoreScroll = false;
2939
3574
  };
@@ -2941,63 +3576,38 @@ var _Carousel = class _Carousel extends Component {
2941
3576
  window.setTimeout(done, this.options.duration + 350);
2942
3577
  }
2943
3578
  _cycleTo(n, callback = null) {
2944
- if (this._flat) {
2945
- if (typeof callback === "function") this.oneTimeCallback = callback;
2946
- this._syncActive(n, false);
2947
- this._scrollToIndex(n);
2948
- return;
2949
- }
2950
- let diff = this.center % this.count - n;
2951
- if (!this.noWrap) {
2952
- if (diff < 0) {
2953
- if (Math.abs(diff + this.count) < Math.abs(diff)) {
2954
- diff += this.count;
2955
- }
2956
- } else if (diff > 0) {
2957
- if (Math.abs(diff - this.count) < diff) {
2958
- diff -= this.count;
2959
- }
2960
- }
2961
- }
2962
- this.target = this.dim * Math.round(this.offset / this.dim);
2963
- if (diff < 0) {
2964
- this.target += this.dim * Math.abs(diff);
2965
- } else if (diff > 0) {
2966
- this.target -= this.dim * diff;
2967
- }
2968
- if (typeof callback === "function") {
2969
- this.oneTimeCallback = callback;
2970
- }
2971
- if (this.offset !== this.target) {
2972
- this.amplitude = this.target - this.offset;
2973
- this.timestamp = Date.now();
2974
- requestAnimationFrame(this._autoScroll);
2975
- }
3579
+ if (typeof callback === "function") this.oneTimeCallback = callback;
3580
+ this._syncActive(n, false);
3581
+ this._scrollToIndex(n);
3582
+ }
3583
+ /**
3584
+ * Stop auto-advance until start() is called. Without an `interval` there is
3585
+ * nothing to stop and this does nothing.
3586
+ */
3587
+ pause() {
3588
+ this._autoPaused = true;
3589
+ this._syncAutoAdvance();
3590
+ }
3591
+ /** Resume auto-advance after pause(). */
3592
+ start() {
3593
+ this._autoPaused = false;
3594
+ this._syncAutoAdvance();
2976
3595
  }
2977
3596
  next(n = 1) {
2978
3597
  if (n === void 0 || isNaN(n)) n = 1;
2979
- let index = this.center + n;
2980
- if (index >= this.count || index < 0) {
2981
- if (this.noWrap) return;
2982
- index = this._wrap(index);
2983
- }
3598
+ const index = this.center + n;
3599
+ if (index >= this.count || index < 0) return;
2984
3600
  this._cycleTo(index);
2985
3601
  }
2986
3602
  prev(n = 1) {
2987
3603
  if (n === void 0 || isNaN(n)) n = 1;
2988
- let index = this.center - n;
2989
- if (index >= this.count || index < 0) {
2990
- if (this.noWrap) return;
2991
- index = this._wrap(index);
2992
- }
3604
+ const index = this.center - n;
3605
+ if (index >= this.count || index < 0) return;
2993
3606
  this._cycleTo(index);
2994
3607
  }
2995
3608
  set(n, callback) {
2996
3609
  if (n === void 0 || isNaN(n)) n = 0;
2997
- if (n > this.count || n < 0) {
2998
- if (this.noWrap) return;
2999
- n = this._wrap(n);
3000
- }
3610
+ if (n >= this.count || n < 0) return;
3001
3611
  this._cycleTo(n, callback);
3002
3612
  }
3003
3613
  };
@@ -3009,7 +3619,7 @@ _Carousel._onResize = Utils.throttle(() => {
3009
3619
  var Carousel = _Carousel;
3010
3620
 
3011
3621
  // src/ts/components/characterCounter.ts
3012
- var _defaults8 = Object.freeze({});
3622
+ var _defaults11 = Object.freeze({});
3013
3623
  var CharacterCounter = class _CharacterCounter extends Component {
3014
3624
  constructor(el, options) {
3015
3625
  super(el, {}, _CharacterCounter);
@@ -3034,7 +3644,7 @@ var CharacterCounter = class _CharacterCounter extends Component {
3034
3644
  this._setupEventHandlers();
3035
3645
  }
3036
3646
  static get defaults() {
3037
- return _defaults8;
3647
+ return _defaults11;
3038
3648
  }
3039
3649
  /**
3040
3650
  * Initializes instances of CharacterCounter.
@@ -3063,6 +3673,8 @@ var CharacterCounter = class _CharacterCounter extends Component {
3063
3673
  _setupCounter() {
3064
3674
  this.counterEl = document.createElement("span");
3065
3675
  this.counterEl.classList.add("character-counter");
3676
+ this.counterEl.setAttribute("aria-live", "polite");
3677
+ this.counterEl.setAttribute("aria-atomic", "true");
3066
3678
  this.counterEl.style.float = "right";
3067
3679
  this.counterEl.style.fontSize = "12px";
3068
3680
  this.counterEl.style.height = "1";
@@ -3084,7 +3696,7 @@ var CharacterCounter = class _CharacterCounter extends Component {
3084
3696
  };
3085
3697
 
3086
3698
  // src/ts/components/select.ts
3087
- var _defaults9 = {
3699
+ var _defaults12 = {
3088
3700
  classes: "",
3089
3701
  menuOptions: {}
3090
3702
  };
@@ -3133,7 +3745,7 @@ var FormSelect = class _FormSelect extends Component {
3133
3745
  this._setupEventHandlers();
3134
3746
  }
3135
3747
  static get defaults() {
3136
- return _defaults9;
3748
+ return _defaults12;
3137
3749
  }
3138
3750
  /**
3139
3751
  * Initializes instances of FormSelect.
@@ -3264,7 +3876,7 @@ var FormSelect = class _FormSelect extends Component {
3264
3876
  _buildInput() {
3265
3877
  this.input = document.createElement("input");
3266
3878
  this.input.id = "select-input-" + Utils.guid();
3267
- this.input.classList.add("menu-trigger");
3879
+ this.input.classList.add("menu-trigger", "no-autoinit");
3268
3880
  this.input.type = "text";
3269
3881
  this.input.readOnly = true;
3270
3882
  this.input.setAttribute("data-target", this.menuEl.id);
@@ -3304,10 +3916,8 @@ var FormSelect = class _FormSelect extends Component {
3304
3916
  menuOptions.onOpenEnd = () => {
3305
3917
  const selectedOption = this.menuEl.querySelector(".selected");
3306
3918
  if (selectedOption) {
3307
- Utils.keyDown = true;
3308
3919
  this.menu.focusedIndex = [...selectedOption.parentNode.children].indexOf(selectedOption);
3309
3920
  this.menu._focusFocusedItem();
3310
- Utils.keyDown = false;
3311
3921
  if (this.menu.isScrollable) {
3312
3922
  let scrollOffset = selectedOption.getBoundingClientRect().top - this.menuEl.getBoundingClientRect().top;
3313
3923
  scrollOffset -= this.menuEl.clientHeight / 2;
@@ -3507,7 +4117,7 @@ var FormSelect = class _FormSelect extends Component {
3507
4117
  };
3508
4118
 
3509
4119
  // src/ts/plugins/dockedDisplayPlugin.ts
3510
- var _defaults10 = {
4120
+ var _defaults13 = {
3511
4121
  margin: 5,
3512
4122
  transition: 10,
3513
4123
  duration: 250,
@@ -3556,7 +4166,7 @@ var DockedDisplayPlugin = class _DockedDisplayPlugin {
3556
4166
  };
3557
4167
  this.el = el;
3558
4168
  this.options = {
3559
- ..._defaults10,
4169
+ ..._defaults13,
3560
4170
  ...options
3561
4171
  };
3562
4172
  this.container = document.createElement("div");
@@ -3577,7 +4187,7 @@ var DockedDisplayPlugin = class _DockedDisplayPlugin {
3577
4187
  };
3578
4188
 
3579
4189
  // src/ts/components/datepicker.ts
3580
- var _defaults11 = {
4190
+ var _defaults14 = {
3581
4191
  // the default output format for the input field value
3582
4192
  format: "mmm dd, yyyy",
3583
4193
  // Used to create date object from current input string
@@ -3702,7 +4312,7 @@ var _Datepicker = class _Datepicker extends Component {
3702
4312
  if (this.options.onInputInteraction) this.options.onInputInteraction.call(this);
3703
4313
  };
3704
4314
  this._handleInputKeydown = (e) => {
3705
- if (Utils.keys.ENTER.includes(e.key)) {
4315
+ if (e.key === Utils.keys.ENTER) {
3706
4316
  e.preventDefault();
3707
4317
  this._batchDraws(() => {
3708
4318
  this.setDateFromInput(e.target);
@@ -3842,7 +4452,7 @@ var _Datepicker = class _Datepicker extends Component {
3842
4452
  }
3843
4453
  }
3844
4454
  static get defaults() {
3845
- return _defaults11;
4455
+ return _defaults14;
3846
4456
  }
3847
4457
  /**
3848
4458
  * Initializes instances of Datepicker.
@@ -4425,11 +5035,11 @@ var _Datepicker = class _Datepicker extends Component {
4425
5035
  const monthSelect = this.calendarEl.querySelector(".orig-select-month");
4426
5036
  FormSelect.init(yearSelect, {
4427
5037
  classes: "select-year",
4428
- menuOptions: { container: document.body, constrainWidth: false }
5038
+ menuOptions: { container: Utils.portalRoot(this.el), constrainWidth: false }
4429
5039
  });
4430
5040
  FormSelect.init(monthSelect, {
4431
5041
  classes: "select-month",
4432
- menuOptions: { container: document.body, constrainWidth: false }
5042
+ menuOptions: { container: Utils.portalRoot(this.el), constrainWidth: false }
4433
5043
  });
4434
5044
  yearSelect.addEventListener("change", this._handleYearChange);
4435
5045
  monthSelect.addEventListener("change", this._handleMonthChange);
@@ -4560,7 +5170,7 @@ _Datepicker._template = `
4560
5170
  var Datepicker = _Datepicker;
4561
5171
 
4562
5172
  // src/ts/components/lightbox.ts
4563
- var _defaults12 = {
5173
+ var _defaults15 = {
4564
5174
  inDuration: 275,
4565
5175
  outDuration: 200,
4566
5176
  onOpenStart: null,
@@ -4575,7 +5185,7 @@ var Lightbox = class _Lightbox extends Component {
4575
5185
  this._handleLightboxToggle();
4576
5186
  };
4577
5187
  this._handleLightboxKeypress = (e) => {
4578
- if (Utils.keys.ENTER.includes(e.key)) {
5188
+ if (e.key === Utils.keys.ENTER) {
4579
5189
  this._handleLightboxToggle();
4580
5190
  }
4581
5191
  };
@@ -4590,7 +5200,7 @@ var Lightbox = class _Lightbox extends Component {
4590
5200
  if (this.overlayActive) this.close();
4591
5201
  };
4592
5202
  this._handleWindowEscape = (e) => {
4593
- if (Utils.keys.ESC.includes(e.key) && this.doneAnimating && this.overlayActive) this.close();
5203
+ if (e.key === Utils.keys.ESC && this.doneAnimating && this.overlayActive) this.close();
4594
5204
  };
4595
5205
  /**
4596
5206
  * Open lightbox.
@@ -4679,7 +5289,7 @@ var Lightbox = class _Lightbox extends Component {
4679
5289
  this._setupEventHandlers();
4680
5290
  }
4681
5291
  static get defaults() {
4682
- return _defaults12;
5292
+ return _defaults15;
4683
5293
  }
4684
5294
  /**
4685
5295
  * Initializes instances of Lightbox.
@@ -4709,13 +5319,13 @@ var Lightbox = class _Lightbox extends Component {
4709
5319
  _makeAncestorsOverflowVisible() {
4710
5320
  this._changedAncestorList = [];
4711
5321
  let ancestor = this.placeholder.parentNode;
4712
- while (ancestor !== null && ancestor !== document) {
5322
+ while (ancestor !== null && ancestor !== void 0 && ancestor !== document) {
4713
5323
  const curr = ancestor;
4714
- if (curr.style.overflow !== "visible") {
5324
+ if (curr.style && curr.style.overflow !== "visible") {
5325
+ this._changedAncestorList.push([curr, curr.style.overflow]);
4715
5326
  curr.style.overflow = "visible";
4716
- this._changedAncestorList.push(curr);
4717
5327
  }
4718
- ancestor = ancestor.parentNode;
5328
+ ancestor = ancestor.parentNode ?? ancestor.host;
4719
5329
  }
4720
5330
  }
4721
5331
  _offset(el) {
@@ -4779,7 +5389,7 @@ var Lightbox = class _Lightbox extends Component {
4779
5389
  if (this.originInlineStyles) this.el.setAttribute("style", this.originInlineStyles);
4780
5390
  this.el.classList.remove("active");
4781
5391
  this.doneAnimating = true;
4782
- this._changedAncestorList.forEach((anchestor) => anchestor.style.overflow = "");
5392
+ this._changedAncestorList.forEach(([ancestor, overflow]) => ancestor.style.overflow = overflow);
4783
5393
  if (typeof this.options.onCloseEnd === "function")
4784
5394
  this.options.onCloseEnd.call(this, this.el);
4785
5395
  }, duration);
@@ -4789,7 +5399,7 @@ var Lightbox = class _Lightbox extends Component {
4789
5399
  this._photoCaption = document.createElement("div");
4790
5400
  this._photoCaption.classList.add("lightbox-caption");
4791
5401
  this._photoCaption.innerText = this.caption;
4792
- document.body.append(this._photoCaption);
5402
+ Utils.portalRoot(this.el).append(this._photoCaption);
4793
5403
  this._photoCaption.style.display = "inline";
4794
5404
  this._photoCaption.style.transition = "none";
4795
5405
  this._photoCaption.style.opacity = "0";
@@ -4843,37 +5453,9 @@ var Lightbox = class _Lightbox extends Component {
4843
5453
  }
4844
5454
  };
4845
5455
 
4846
- // src/ts/components/parallax.ts
4847
- var _defaults13 = {};
4848
- var Parallax = class _Parallax extends Component {
4849
- constructor(el, options) {
4850
- super(el, options, _Parallax);
4851
- this.el["Expressive_Parallax"] = this;
4852
- this.options = {
4853
- ..._Parallax.defaults,
4854
- ...options
4855
- };
4856
- if (!this.el.querySelector("img, video")) {
4857
- console.error("Parallax: no <img> or <video> to move inside the .parallax element");
4858
- }
4859
- }
4860
- static get defaults() {
4861
- return _defaults13;
4862
- }
4863
- static init(els, options = {}) {
4864
- return super.init(els, options, _Parallax);
4865
- }
4866
- static getInstance(el) {
4867
- return el["Expressive_Parallax"];
4868
- }
4869
- destroy() {
4870
- this.el["Expressive_Parallax"] = void 0;
4871
- }
4872
- };
4873
-
4874
5456
  // src/ts/components/scrollspy.ts
4875
5457
  var _defaultGetActiveElement = (id) => `a[href="#${id}"]`;
4876
- var _defaults14 = {
5458
+ var _defaults16 = {
4877
5459
  scrollOffset: 200,
4878
5460
  activeClass: "active",
4879
5461
  getActiveElement: _defaultGetActiveElement,
@@ -4896,7 +5478,7 @@ var _ScrollSpy = class _ScrollSpy extends Component {
4896
5478
  _ScrollSpy._observer?.observe(this.el);
4897
5479
  }
4898
5480
  static get defaults() {
4899
- return _defaults14;
5481
+ return _defaults16;
4900
5482
  }
4901
5483
  static init(els, options = {}) {
4902
5484
  return super.init(els, options, _ScrollSpy);
@@ -5017,8 +5599,8 @@ _ScrollSpy._onIntersect = (entries) => {
5017
5599
  };
5018
5600
  var ScrollSpy = _ScrollSpy;
5019
5601
 
5020
- // src/ts/components/sidenav.ts
5021
- var _defaults15 = {
5602
+ // src/ts/components/navigationDrawer.ts
5603
+ var _defaults17 = {
5022
5604
  edge: "left",
5023
5605
  draggable: true,
5024
5606
  dragTargetWidth: "10px",
@@ -5027,17 +5609,17 @@ var _defaults15 = {
5027
5609
  onCloseStart: null,
5028
5610
  onCloseEnd: null
5029
5611
  };
5030
- var LARGE_UP = "(width >= 993px)";
5031
- var _Sidenav = class _Sidenav extends Component {
5612
+ var EXPANDED_UP = "(width >= 840px)";
5613
+ var _NavigationDrawer = class _NavigationDrawer extends Component {
5032
5614
  constructor(el, options) {
5033
- super(el, options, _Sidenav);
5615
+ super(el, options, _NavigationDrawer);
5034
5616
  /**
5035
5617
  * Opens the overlay drawer. No-op while the sidenav is docked
5036
- * (sidenav-fixed at the large breakpoint).
5618
+ * (navigation-drawer-fixed at the Expanded breakpoint).
5037
5619
  */
5038
5620
  this.open = () => {
5039
5621
  if (this._isCurrentlyFixed() || this.isOpen || !this._dialog) return;
5040
- _Sidenav._sidenavs.forEach((other) => {
5622
+ _NavigationDrawer._sidenavs.forEach((other) => {
5041
5623
  if (other !== this && other.isOpen) other.close();
5042
5624
  });
5043
5625
  if (typeof this.options.onOpenStart === "function") {
@@ -5089,7 +5671,7 @@ var _Sidenav = class _Sidenav extends Component {
5089
5671
  this._handleCloseTriggerClick = (e) => {
5090
5672
  const target = e.target;
5091
5673
  if (!(target instanceof Element)) return;
5092
- const closeTrigger = target.closest(".sidenav-close");
5674
+ const closeTrigger = target.closest(".sidenav-close, .navigation-drawer-close");
5093
5675
  if (closeTrigger && !this._isCurrentlyFixed()) {
5094
5676
  this.close();
5095
5677
  }
@@ -5133,14 +5715,14 @@ var _Sidenav = class _Sidenav extends Component {
5133
5715
  this.isDragged = false;
5134
5716
  this._verticallyScrolling = false;
5135
5717
  };
5136
- this.el["Expressive_Sidenav"] = this;
5718
+ this.el["Expressive_NavigationDrawer"] = this;
5137
5719
  this.options = {
5138
- ..._Sidenav.defaults,
5720
+ ..._NavigationDrawer.defaults,
5139
5721
  ...options
5140
5722
  };
5141
5723
  this.id = this.el.id;
5142
5724
  this.isOpen = false;
5143
- this.isFixed = this.el.classList.contains("sidenav-fixed");
5725
+ this.isFixed = this.el.classList.contains("navigation-drawer-fixed") || this.el.classList.contains("sidenav-fixed");
5144
5726
  this.isDragged = false;
5145
5727
  this.dragTarget = null;
5146
5728
  this._dialog = null;
@@ -5152,27 +5734,27 @@ var _Sidenav = class _Sidenav extends Component {
5152
5734
  this._width = 0;
5153
5735
  this._initialScrollTop = 0;
5154
5736
  this._verticallyScrolling = false;
5155
- this._mql = window.matchMedia(LARGE_UP);
5737
+ this._mql = window.matchMedia(EXPANDED_UP);
5156
5738
  this._setupDialog();
5157
5739
  this._createDragTarget();
5158
5740
  this._setupClasses();
5159
5741
  this._setupEventHandlers();
5160
5742
  this._syncTriggers();
5161
- _Sidenav._sidenavs.push(this);
5743
+ _NavigationDrawer._sidenavs.push(this);
5162
5744
  }
5163
5745
  static get defaults() {
5164
- return _defaults15;
5746
+ return _defaults17;
5165
5747
  }
5166
5748
  /**
5167
- * Initializes instances of Sidenav.
5749
+ * Initializes instances of NavigationDrawer.
5168
5750
  * @param els HTML elements.
5169
5751
  * @param options Component options.
5170
5752
  */
5171
5753
  static init(els, options = {}) {
5172
- return super.init(els, options, _Sidenav);
5754
+ return super.init(els, options, _NavigationDrawer);
5173
5755
  }
5174
5756
  static getInstance(el) {
5175
- return el["Expressive_Sidenav"];
5757
+ return el["Expressive_NavigationDrawer"];
5176
5758
  }
5177
5759
  destroy() {
5178
5760
  this._removeEventHandlers();
@@ -5185,10 +5767,10 @@ var _Sidenav = class _Sidenav extends Component {
5185
5767
  this._clearShift();
5186
5768
  this._unwrapDialog();
5187
5769
  this._removeDragTarget();
5188
- this.el["Expressive_Sidenav"] = void 0;
5189
- const index = _Sidenav._sidenavs.indexOf(this);
5770
+ this.el["Expressive_NavigationDrawer"] = void 0;
5771
+ const index = _NavigationDrawer._sidenavs.indexOf(this);
5190
5772
  if (index >= 0) {
5191
- _Sidenav._sidenavs.splice(index, 1);
5773
+ _NavigationDrawer._sidenavs.splice(index, 1);
5192
5774
  }
5193
5775
  }
5194
5776
  _beginClose() {
@@ -5219,6 +5801,11 @@ var _Sidenav = class _Sidenav extends Component {
5219
5801
  }
5220
5802
  const dialog = document.createElement("dialog");
5221
5803
  dialog.classList.add("sidenav-overlay");
5804
+ const named = this.el.closest("[aria-label], [aria-labelledby]") ?? this.el;
5805
+ const label = named.getAttribute("aria-label");
5806
+ const labelledBy = named.getAttribute("aria-labelledby");
5807
+ if (label) dialog.setAttribute("aria-label", label);
5808
+ else if (labelledBy) dialog.setAttribute("aria-labelledby", labelledBy);
5222
5809
  this.el.replaceWith(dialog);
5223
5810
  dialog.appendChild(this.el);
5224
5811
  this._dialog = dialog;
@@ -5236,7 +5823,7 @@ var _Sidenav = class _Sidenav extends Component {
5236
5823
  dragTarget.classList.add("drag-target");
5237
5824
  dragTarget.style.width = this.options.dragTargetWidth;
5238
5825
  if (this.isFixed) dragTarget.dataset.fixed = "";
5239
- document.body.appendChild(dragTarget);
5826
+ Utils.portalRoot(this.el).appendChild(dragTarget);
5240
5827
  this.dragTarget = dragTarget;
5241
5828
  }
5242
5829
  _removeDragTarget() {
@@ -5251,8 +5838,8 @@ var _Sidenav = class _Sidenav extends Component {
5251
5838
  }
5252
5839
  }
5253
5840
  _setupEventHandlers() {
5254
- if (_Sidenav._sidenavs.length === 0) {
5255
- document.body.addEventListener("click", _Sidenav._handleTriggerClick);
5841
+ if (_NavigationDrawer._sidenavs.length === 0) {
5842
+ document.body.addEventListener("click", _NavigationDrawer._handleTriggerClick);
5256
5843
  }
5257
5844
  const passive = { passive: true };
5258
5845
  this.dragTarget?.addEventListener("touchmove", this._handleDragTargetDrag, passive);
@@ -5264,8 +5851,8 @@ var _Sidenav = class _Sidenav extends Component {
5264
5851
  this._mql.addEventListener("change", this._onBreakpointChange);
5265
5852
  }
5266
5853
  _removeEventHandlers() {
5267
- if (_Sidenav._sidenavs.length === 1) {
5268
- document.body.removeEventListener("click", _Sidenav._handleTriggerClick);
5854
+ if (_NavigationDrawer._sidenavs.length === 1) {
5855
+ document.body.removeEventListener("click", _NavigationDrawer._handleTriggerClick);
5269
5856
  }
5270
5857
  this.dragTarget?.removeEventListener("touchmove", this._handleDragTargetDrag);
5271
5858
  this.dragTarget?.removeEventListener("touchend", this._handleDragTargetRelease);
@@ -5278,10 +5865,10 @@ var _Sidenav = class _Sidenav extends Component {
5278
5865
  static _handleTriggerClick(e) {
5279
5866
  const target = e.target;
5280
5867
  if (!(target instanceof Element)) return;
5281
- const trigger = target.closest(".sidenav-trigger");
5868
+ const trigger = target.closest(".sidenav-trigger, .navigation-drawer-trigger");
5282
5869
  if (!trigger || !(trigger instanceof HTMLElement)) return;
5283
5870
  const sidenavId = Utils.getIdFromTrigger(trigger);
5284
- const sidenavInstance = document.getElementById(sidenavId)?.["Expressive_Sidenav"];
5871
+ const sidenavInstance = Utils.getElementById(trigger, sidenavId)?.["Expressive_NavigationDrawer"];
5285
5872
  if (sidenavInstance) {
5286
5873
  sidenavInstance.open();
5287
5874
  e.preventDefault();
@@ -5335,7 +5922,7 @@ var _Sidenav = class _Sidenav extends Component {
5335
5922
  }
5336
5923
  _syncTriggers() {
5337
5924
  if (!this.id) return;
5338
- document.querySelectorAll(".sidenav-trigger").forEach((trigger) => {
5925
+ document.querySelectorAll(".sidenav-trigger, .navigation-drawer-trigger").forEach((trigger) => {
5339
5926
  if (!(trigger instanceof HTMLElement)) return;
5340
5927
  if (Utils.getIdFromTrigger(trigger) !== this.id) return;
5341
5928
  trigger.setAttribute("aria-expanded", this.isOpen ? "true" : "false");
@@ -5343,17 +5930,17 @@ var _Sidenav = class _Sidenav extends Component {
5343
5930
  });
5344
5931
  }
5345
5932
  };
5346
- _Sidenav._sidenavs = [];
5347
- var Sidenav = _Sidenav;
5933
+ _NavigationDrawer._sidenavs = [];
5934
+ var NavigationDrawer = _NavigationDrawer;
5348
5935
 
5349
5936
  // src/ts/components/navigationRail.ts
5350
- var _defaults16 = {
5937
+ var _defaults18 = {
5351
5938
  onOpenStart: null,
5352
5939
  onOpenEnd: null,
5353
5940
  onCloseStart: null,
5354
5941
  onCloseEnd: null
5355
5942
  };
5356
- var COMPACT = "(width < 601px)";
5943
+ var COMPACT = "(width < 600px)";
5357
5944
  var NavigationRail = class _NavigationRail extends Component {
5358
5945
  constructor(el, options) {
5359
5946
  super(el, options, _NavigationRail);
@@ -5423,7 +6010,7 @@ var NavigationRail = class _NavigationRail extends Component {
5423
6010
  document.addEventListener("keydown", this._onKeyDown);
5424
6011
  }
5425
6012
  static get defaults() {
5426
- return _defaults16;
6013
+ return _defaults18;
5427
6014
  }
5428
6015
  static init(els, options = {}) {
5429
6016
  return super.init(els, options, _NavigationRail);
@@ -5452,309 +6039,8 @@ var NavigationRail = class _NavigationRail extends Component {
5452
6039
  }
5453
6040
  };
5454
6041
 
5455
- // src/ts/components/slider.ts
5456
- var _defaults17 = {
5457
- indicators: true,
5458
- height: 400,
5459
- duration: 500,
5460
- interval: 6e3,
5461
- pauseOnFocus: true,
5462
- pauseOnHover: true,
5463
- indicatorLabelFunc: null
5464
- // Function which will generate a label for the indicators (ARIA)
5465
- };
5466
- var Slider = class _Slider extends Component {
5467
- constructor(el, options) {
5468
- super(el, options, _Slider);
5469
- // Nobody is watching a backgrounded tab. Browsers clamp the timer rather
5470
- // than stopping it, so without this the slider keeps cycling slides - and
5471
- // running their transitions - out of sight.
5472
- this._handleVisibilityChange = () => {
5473
- if (document.hidden) {
5474
- if (this.interval != null) this._pause(true);
5475
- } else if (this.eventPause && !(this.options.pauseOnHover && this._hovered) && !(this.options.pauseOnFocus && this._focused)) {
5476
- this.start();
5477
- }
5478
- };
5479
- this._handleIndicatorClick = (e) => {
5480
- const el = e.target.parentElement;
5481
- const currIndex = [...el.parentNode.children].indexOf(el);
5482
- this._focusCurrent = true;
5483
- this.set(currIndex);
5484
- };
5485
- this._handleAutoPauseHover = () => {
5486
- this._hovered = true;
5487
- if (this.interval != null) {
5488
- this._pause(true);
5489
- }
5490
- };
5491
- this._handleAutoPauseFocus = () => {
5492
- this._focused = true;
5493
- if (this.interval != null) {
5494
- this._pause(true);
5495
- }
5496
- };
5497
- this._handleAutoStartHover = () => {
5498
- this._hovered = false;
5499
- if (!(this.options.pauseOnFocus && this._focused) && this.eventPause) {
5500
- this.start();
5501
- }
5502
- };
5503
- this._handleAutoStartFocus = () => {
5504
- this._focused = false;
5505
- if (!(this.options.pauseOnHover && this._hovered) && this.eventPause) {
5506
- this.start();
5507
- }
5508
- };
5509
- this._handleInterval = () => {
5510
- const activeElem = this._slider.querySelector(".active");
5511
- let newActiveIndex = [...activeElem.parentNode.children].indexOf(activeElem);
5512
- if (this._slides.length === newActiveIndex + 1)
5513
- newActiveIndex = 0;
5514
- else newActiveIndex += 1;
5515
- this.set(newActiveIndex);
5516
- };
5517
- /**
5518
- * Pause slider autoslide.
5519
- */
5520
- this.pause = () => {
5521
- this._pause(false);
5522
- };
5523
- /**
5524
- * Start slider autoslide.
5525
- */
5526
- this.start = () => {
5527
- clearInterval(this.interval);
5528
- this.interval = setInterval(
5529
- this._handleInterval,
5530
- this.options.duration + this.options.interval
5531
- );
5532
- this.eventPause = false;
5533
- };
5534
- /**
5535
- * Move to next slider.
5536
- */
5537
- this.next = () => {
5538
- let newIndex = this.activeIndex + 1;
5539
- if (newIndex >= this._slides.length) newIndex = 0;
5540
- else if (newIndex < 0) newIndex = this._slides.length - 1;
5541
- this.set(newIndex);
5542
- };
5543
- /**
5544
- * Move to prev slider.
5545
- */
5546
- this.prev = () => {
5547
- let newIndex = this.activeIndex - 1;
5548
- if (newIndex >= this._slides.length) newIndex = 0;
5549
- else if (newIndex < 0) newIndex = this._slides.length - 1;
5550
- this.set(newIndex);
5551
- };
5552
- this.el["Expressive_Slider"] = this;
5553
- this.options = {
5554
- ..._Slider.defaults,
5555
- ...options
5556
- };
5557
- this.interval = null;
5558
- this.eventPause = false;
5559
- this._hovered = false;
5560
- this._focused = false;
5561
- this._focusCurrent = false;
5562
- this._slider = this.el.querySelector(".slides");
5563
- this._slides = Array.from(this._slider.querySelectorAll("li"));
5564
- this.activeIndex = this._slides.findIndex((li) => li.classList.contains("active"));
5565
- if (this.activeIndex !== -1) {
5566
- this._activeSlide = this._slides[this.activeIndex];
5567
- }
5568
- this._setSliderHeight();
5569
- if (this._slider.hasAttribute("id")) this._sliderId = this._slider.getAttribute("id");
5570
- else {
5571
- this._sliderId = "slider-" + Utils.guid();
5572
- this._slider.setAttribute("id", this._sliderId);
5573
- }
5574
- const placeholderBase64 = "data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
5575
- this._slides.forEach((slide) => {
5576
- const img = slide.querySelector("img");
5577
- if (img) {
5578
- if (img.src !== placeholderBase64) {
5579
- img.style.backgroundImage = "url(" + img.src + ")";
5580
- img.src = placeholderBase64;
5581
- }
5582
- }
5583
- if (!slide.hasAttribute("tabindex")) slide.setAttribute("tabindex", "-1");
5584
- slide.style.visibility = "hidden";
5585
- });
5586
- this._setupIndicators();
5587
- if (this._activeSlide) {
5588
- this._activeSlide.style.display = "block";
5589
- this._activeSlide.style.visibility = "visible";
5590
- } else {
5591
- this.activeIndex = 0;
5592
- this._slides[0].classList.add("active");
5593
- this._slides[0].style.visibility = "visible";
5594
- this._activeSlide = this._slides[0];
5595
- this._animateSlide(this._slides[0], true);
5596
- if (this.options.indicators) {
5597
- this._indicators[this.activeIndex].children[0].classList.add("active");
5598
- }
5599
- }
5600
- this._setupEventHandlers();
5601
- this.start();
5602
- }
5603
- static get defaults() {
5604
- return _defaults17;
5605
- }
5606
- /**
5607
- * Initializes instances of Slider.
5608
- * @param els HTML elements.
5609
- * @param options Component options.
5610
- */
5611
- static init(els, options = {}) {
5612
- return super.init(els, options, _Slider);
5613
- }
5614
- static getInstance(el) {
5615
- return el["Expressive_Slider"];
5616
- }
5617
- destroy() {
5618
- this.pause();
5619
- this._removeIndicators();
5620
- this._removeEventHandlers();
5621
- this.el["Expressive_Slider"] = void 0;
5622
- }
5623
- _setupEventHandlers() {
5624
- if (this.options.pauseOnFocus) {
5625
- this.el.addEventListener("focusin", this._handleAutoPauseFocus);
5626
- this.el.addEventListener("focusout", this._handleAutoStartFocus);
5627
- }
5628
- if (this.options.pauseOnHover) {
5629
- this.el.addEventListener("mouseenter", this._handleAutoPauseHover);
5630
- this.el.addEventListener("mouseleave", this._handleAutoStartHover);
5631
- }
5632
- if (this.options.indicators) {
5633
- this._indicators.forEach((el) => {
5634
- el.addEventListener("click", this._handleIndicatorClick);
5635
- });
5636
- }
5637
- document.addEventListener("visibilitychange", this._handleVisibilityChange);
5638
- }
5639
- _removeEventHandlers() {
5640
- if (this.options.pauseOnFocus) {
5641
- this.el.removeEventListener("focusin", this._handleAutoPauseFocus);
5642
- this.el.removeEventListener("focusout", this._handleAutoStartFocus);
5643
- }
5644
- if (this.options.pauseOnHover) {
5645
- this.el.removeEventListener("mouseenter", this._handleAutoPauseHover);
5646
- this.el.removeEventListener("mouseleave", this._handleAutoStartHover);
5647
- }
5648
- if (this.options.indicators) {
5649
- this._indicators.forEach((el) => {
5650
- el.removeEventListener("click", this._handleIndicatorClick);
5651
- });
5652
- }
5653
- document.removeEventListener("visibilitychange", this._handleVisibilityChange);
5654
- }
5655
- _animateSlide(slide, isDirectionIn) {
5656
- let dx = 0, dy = 0;
5657
- slide.style.opacity = isDirectionIn ? "0" : "1";
5658
- setTimeout(() => {
5659
- slide.style.transition = `opacity ${this.options.duration}ms ease`;
5660
- slide.style.opacity = isDirectionIn ? "1" : "0";
5661
- }, 1);
5662
- const caption = slide.querySelector(".caption");
5663
- if (!caption) return;
5664
- if (caption.classList.contains("center-align")) dy = -100;
5665
- else if (caption.classList.contains("right-align")) dx = 100;
5666
- else if (caption.classList.contains("left-align")) dx = -100;
5667
- caption.style.opacity = isDirectionIn ? "0" : "1";
5668
- caption.style.transform = isDirectionIn ? `translate(${dx}px, ${dy}px)` : `translate(0, 0)`;
5669
- setTimeout(() => {
5670
- caption.style.transition = `opacity ${this.options.duration}ms ease, transform ${this.options.duration}ms ease`;
5671
- caption.style.opacity = isDirectionIn ? "1" : "0";
5672
- caption.style.transform = isDirectionIn ? `translate(0, 0)` : `translate(${dx}px, ${dy}px)`;
5673
- }, this.options.duration);
5674
- }
5675
- _setSliderHeight() {
5676
- if (!this.el.classList.contains("fullscreen")) {
5677
- if (this.options.indicators) {
5678
- this.el.style.height = this.options.height + 40 + "px";
5679
- } else {
5680
- this.el.style.height = this.options.height + "px";
5681
- }
5682
- this._slider.style.height = this.options.height + "px";
5683
- }
5684
- }
5685
- _setupIndicators() {
5686
- if (this.options.indicators) {
5687
- const ul = document.createElement("ul");
5688
- ul.classList.add("indicators");
5689
- const arrLi = [];
5690
- this._slides.forEach((el, i) => {
5691
- const label = this.options.indicatorLabelFunc ? this.options.indicatorLabelFunc.call(this, i + 1, i === 0) : `${i + 1}`;
5692
- const li = document.createElement("li");
5693
- li.classList.add("indicator-item");
5694
- const button = document.createElement("button");
5695
- button.type = "button";
5696
- button.classList.add("indicator-item-btn");
5697
- button.setAttribute("aria-label", label);
5698
- button.setAttribute("aria-controls", this._sliderId);
5699
- li.appendChild(button);
5700
- arrLi.push(li);
5701
- ul.append(li);
5702
- });
5703
- this.el.append(ul);
5704
- this._indicators = arrLi;
5705
- }
5706
- }
5707
- _removeIndicators() {
5708
- this.el.querySelector("ul.indicators").remove();
5709
- }
5710
- set(index) {
5711
- if (index >= this._slides.length) index = 0;
5712
- else if (index < 0) index = this._slides.length - 1;
5713
- if (this.activeIndex === index) return;
5714
- this._activeSlide = this._slides[this.activeIndex];
5715
- const _caption = this._activeSlide.querySelector(".caption");
5716
- this._activeSlide.classList.remove("active");
5717
- this._slides.forEach((slide) => slide.style.visibility = "visible");
5718
- this._activeSlide.style.opacity = "0";
5719
- setTimeout(() => {
5720
- this._slides.forEach((slide) => {
5721
- if (slide.classList.contains("active")) return;
5722
- slide.style.opacity = "0";
5723
- slide.style.transform = "translate(0, 0)";
5724
- slide.style.visibility = "hidden";
5725
- });
5726
- }, this.options.duration);
5727
- if (_caption) _caption.style.opacity = "0";
5728
- if (this.options.indicators) {
5729
- const activeIndicator = this._indicators[this.activeIndex].children[0];
5730
- const nextIndicator = this._indicators[index].children[0];
5731
- activeIndicator.classList.remove("active");
5732
- nextIndicator.classList.add("active");
5733
- if (typeof this.options.indicatorLabelFunc === "function") {
5734
- activeIndicator.ariaLabel = this.options.indicatorLabelFunc.call(
5735
- this,
5736
- this.activeIndex,
5737
- false
5738
- );
5739
- nextIndicator.ariaLabel = this.options.indicatorLabelFunc.call(this, index, true);
5740
- }
5741
- }
5742
- this._animateSlide(this._slides[index], true);
5743
- this._slides[index].classList.add("active");
5744
- this.activeIndex = index;
5745
- if (this.interval != null) {
5746
- this.start();
5747
- }
5748
- }
5749
- _pause(fromEvent) {
5750
- clearInterval(this.interval);
5751
- this.eventPause = fromEvent;
5752
- this.interval = null;
5753
- }
5754
- };
5755
-
5756
6042
  // src/ts/components/tabs.ts
5757
- var _defaults18 = {
6043
+ var _defaults19 = {
5758
6044
  duration: 300,
5759
6045
  onShow: null,
5760
6046
  swipeable: false,
@@ -5783,12 +6069,10 @@ var Tabs = class _Tabs extends Component {
5783
6069
  return;
5784
6070
  }
5785
6071
  if (tabLink.hasAttribute("target")) return;
5786
- this._activeTabLink.classList.remove("active");
5787
6072
  const _oldContent = this._content;
5788
- this._activeTabLink = tabLink;
5789
6073
  if (tabLink.hash) this._content = document.querySelector(tabLink.hash);
5790
6074
  this._tabLinks = this._queryTabLinks();
5791
- this._activeTabLink.classList.add("active");
6075
+ this._setActiveTabLink(tabLink);
5792
6076
  const prevIndex = this._index;
5793
6077
  this._index = Math.max(Array.from(this._tabLinks).indexOf(tabLink), 0);
5794
6078
  if (this.options.swipeable) {
@@ -5832,7 +6116,7 @@ var Tabs = class _Tabs extends Component {
5832
6116
  this._setupEventHandlers();
5833
6117
  }
5834
6118
  static get defaults() {
5835
- return _defaults18;
6119
+ return _defaults19;
5836
6120
  }
5837
6121
  /**
5838
6122
  * Initializes instances of Tabs.
@@ -5883,6 +6167,26 @@ var Tabs = class _Tabs extends Component {
5883
6167
  this._indicator.style.left = this._calcLeftPos(this._activeTabLink) + "px";
5884
6168
  this._indicator.style.right = this._calcRightPos(this._activeTabLink) + "px";
5885
6169
  }
6170
+ /**
6171
+ * Which tab is current, for the eye and for assistive technology.
6172
+ *
6173
+ * The class alone is a colour. `aria-current` is the part a screen reader
6174
+ * reads, and because tabs are navigation rather than a tablist (rule 2 -
6175
+ * there is no keyboard model here to back a tablist), "current" is exactly
6176
+ * the right word for it. Its *value* changes as the user clicks, which makes
6177
+ * maintaining it the component's job, not the author's - see CONTEXT.md on
6178
+ * static semantics versus dynamic state. Writing it once in the markup and
6179
+ * never moving it left the old tab announcing itself as current.
6180
+ */
6181
+ _setActiveTabLink(tabLink) {
6182
+ Array.from(this._tabLinks).forEach((a) => {
6183
+ a.classList.remove("active");
6184
+ a.removeAttribute("aria-current");
6185
+ });
6186
+ this._activeTabLink = tabLink;
6187
+ tabLink.classList.add("active");
6188
+ tabLink.setAttribute("aria-current", "page");
6189
+ }
5886
6190
  _setupActiveTabLink() {
5887
6191
  this._activeTabLink = Array.from(this._tabLinks).find(
5888
6192
  (a) => a.getAttribute("href") === location.hash
@@ -5894,8 +6198,7 @@ var Tabs = class _Tabs extends Component {
5894
6198
  }
5895
6199
  this._activeTabLink = activeTabLink;
5896
6200
  }
5897
- Array.from(this._tabLinks).forEach((a) => a.classList.remove("active"));
5898
- this._activeTabLink.classList.add("active");
6201
+ this._setActiveTabLink(this._activeTabLink);
5899
6202
  this._index = Math.max(Array.from(this._tabLinks).indexOf(this._activeTabLink), 0);
5900
6203
  if (this._activeTabLink && this._activeTabLink.hash) {
5901
6204
  this._content = document.querySelector(this._activeTabLink.hash);
@@ -6000,7 +6303,7 @@ var Tabs = class _Tabs extends Component {
6000
6303
  };
6001
6304
 
6002
6305
  // src/ts/components/timepicker.ts
6003
- var _defaults19 = {
6306
+ var _defaults20 = {
6004
6307
  dialRadius: 135,
6005
6308
  outerRadius: 105,
6006
6309
  innerRadius: 70,
@@ -6034,6 +6337,8 @@ var _defaults19 = {
6034
6337
  var _Timepicker = class _Timepicker extends Component {
6035
6338
  constructor(el, options) {
6036
6339
  super(el, options, _Timepicker);
6340
+ /** Which pointer is dragging the clock hand; a second finger is ignored. */
6341
+ this._dragPointerId = null;
6037
6342
  /** Whether the dial (ticks + SVG + AM/PM buttons) has been built yet. */
6038
6343
  this._clockBuilt = false;
6039
6344
  this._handleInputClick = () => {
@@ -6043,7 +6348,7 @@ var _Timepicker = class _Timepicker extends Component {
6043
6348
  if (this.displayPlugin) this.displayPlugin.show();
6044
6349
  };
6045
6350
  this._handleInputKeydown = (e) => {
6046
- if (Utils.keys.ENTER.includes(e.key)) {
6351
+ if (e.key === Utils.keys.ENTER) {
6047
6352
  e.preventDefault();
6048
6353
  this._ensureClockBuilt();
6049
6354
  this.inputHours.focus();
@@ -6051,13 +6356,9 @@ var _Timepicker = class _Timepicker extends Component {
6051
6356
  if (this.displayPlugin) this.displayPlugin.show();
6052
6357
  }
6053
6358
  };
6054
- this._handleTimeInputEnterKey = (e) => {
6055
- if (Utils.keys.ENTER.includes(e.key)) {
6056
- e.preventDefault();
6057
- this._inputFromTextField();
6058
- }
6059
- };
6060
6359
  this._handleClockClickStart = (e) => {
6360
+ if (!e.isPrimary) return;
6361
+ if (e.pointerType === "mouse" && e.button !== 0) return;
6061
6362
  e.preventDefault();
6062
6363
  this._ensureClockBuilt();
6063
6364
  const clockPlateBR = this.plate.getBoundingClientRect();
@@ -6065,30 +6366,30 @@ var _Timepicker = class _Timepicker extends Component {
6065
6366
  this.x0 = offset.x + this.options.dialRadius;
6066
6367
  this.y0 = offset.y + this.options.dialRadius;
6067
6368
  this.moved = false;
6068
- const clickPos = _Timepicker._Pos(e);
6069
- this.dx = clickPos.x - this.x0;
6070
- this.dy = clickPos.y - this.y0;
6369
+ this._dragPointerId = e.pointerId;
6370
+ this.dx = e.clientX - this.x0;
6371
+ this.dy = e.clientY - this.y0;
6071
6372
  this.setHand(this.dx, this.dy, false);
6072
- document.addEventListener("mousemove", this._handleDocumentClickMove);
6073
- document.addEventListener("touchmove", this._handleDocumentClickMove);
6074
- document.addEventListener("mouseup", this._handleDocumentClickEnd);
6075
- document.addEventListener("touchend", this._handleDocumentClickEnd);
6373
+ document.addEventListener("pointermove", this._handleDocumentClickMove);
6374
+ document.addEventListener("pointerup", this._handleDocumentClickEnd);
6375
+ document.addEventListener("pointercancel", this._handleDocumentClickCancel);
6076
6376
  };
6077
6377
  this._handleDocumentClickMove = (e) => {
6378
+ if (e.pointerId !== this._dragPointerId) return;
6078
6379
  e.preventDefault();
6079
- const clickPos = _Timepicker._Pos(e);
6080
- const x = clickPos.x - this.x0;
6081
- const y = clickPos.y - this.y0;
6380
+ const x = e.clientX - this.x0;
6381
+ const y = e.clientY - this.y0;
6082
6382
  this.moved = true;
6083
6383
  this.setHand(x, y, false);
6084
6384
  };
6085
6385
  this._handleDocumentClickEnd = (e) => {
6386
+ if (e.pointerId !== this._dragPointerId) return;
6387
+ this._dragPointerId = null;
6086
6388
  e.preventDefault();
6087
- document.removeEventListener("mouseup", this._handleDocumentClickEnd);
6088
- document.removeEventListener("touchend", this._handleDocumentClickEnd);
6089
- const clickPos = _Timepicker._Pos(e);
6090
- const x = clickPos.x - this.x0;
6091
- const y = clickPos.y - this.y0;
6389
+ document.removeEventListener("pointerup", this._handleDocumentClickEnd);
6390
+ document.removeEventListener("pointercancel", this._handleDocumentClickCancel);
6391
+ const x = e.clientX - this.x0;
6392
+ const y = e.clientY - this.y0;
6092
6393
  if (this.moved && x === this.dx && y === this.dy) {
6093
6394
  this.setHand(x, y);
6094
6395
  }
@@ -6103,14 +6404,27 @@ var _Timepicker = class _Timepicker extends Component {
6103
6404
  if (typeof this.options.onSelect === "function") {
6104
6405
  this.options.onSelect.call(this, this.hours, this.minutes);
6105
6406
  }
6106
- document.removeEventListener("mousemove", this._handleDocumentClickMove);
6107
- document.removeEventListener("touchmove", this._handleDocumentClickMove);
6407
+ document.removeEventListener("pointermove", this._handleDocumentClickMove);
6408
+ };
6409
+ /**
6410
+ * A cancelled pointer is not a completed selection. The browser or the OS
6411
+ * took the gesture over, so this only lets go of the drag - it must not run
6412
+ * the pointer-up path, which advances hours to minutes, fires `onSelect`,
6413
+ * and under `autoSubmit` closes the picker on a time the user never chose.
6414
+ */
6415
+ this._handleDocumentClickCancel = (e) => {
6416
+ if (e.pointerId !== this._dragPointerId) return;
6417
+ this._dragPointerId = null;
6418
+ this.moved = false;
6419
+ document.removeEventListener("pointermove", this._handleDocumentClickMove);
6420
+ document.removeEventListener("pointerup", this._handleDocumentClickEnd);
6421
+ document.removeEventListener("pointercancel", this._handleDocumentClickCancel);
6108
6422
  };
6109
6423
  this._handleAmPmClick = (e) => {
6110
6424
  this._handleAmPmInteraction(e.target);
6111
6425
  };
6112
6426
  this._handleAmPmKeypress = (e) => {
6113
- if (Utils.keys.ENTER.includes(e.key)) {
6427
+ if (e.key === Utils.keys.ENTER) {
6114
6428
  this._handleAmPmInteraction(e.target);
6115
6429
  }
6116
6430
  };
@@ -6125,8 +6439,6 @@ var _Timepicker = class _Timepicker extends Component {
6125
6439
  */
6126
6440
  this.showView = (view, delay = null) => {
6127
6441
  this._ensureClockBuilt();
6128
- if (view === "minutes" && getComputedStyle(this.hoursView).visibility === "visible") {
6129
- }
6130
6442
  const isHours = view === "hours", nextView = isHours ? this.hoursView : this.minutesView, hideView = isHours ? this.minutesView : this.hoursView;
6131
6443
  this.currentView = view;
6132
6444
  hideView.classList.add("timepicker-dial-out");
@@ -6198,7 +6510,7 @@ var _Timepicker = class _Timepicker extends Component {
6198
6510
  }
6199
6511
  }
6200
6512
  static get defaults() {
6201
- return _defaults19;
6513
+ return _defaults20;
6202
6514
  }
6203
6515
  /**
6204
6516
  * Initializes instances of Timepicker.
@@ -6215,15 +6527,6 @@ var _Timepicker = class _Timepicker extends Component {
6215
6527
  const svgNS = "http://www.w3.org/2000/svg";
6216
6528
  return document.createElementNS(svgNS, name);
6217
6529
  }
6218
- static _Pos(e) {
6219
- if (e.type.startsWith("touch") && e.targetTouches.length >= 1) {
6220
- return {
6221
- x: e.targetTouches[0].clientX,
6222
- y: e.targetTouches[0].clientY
6223
- };
6224
- }
6225
- return { x: e.clientX, y: e.clientY };
6226
- }
6227
6530
  static getInstance(el) {
6228
6531
  return el["Expressive_Timepicker"];
6229
6532
  }
@@ -6236,8 +6539,7 @@ var _Timepicker = class _Timepicker extends Component {
6236
6539
  _setupEventHandlers() {
6237
6540
  this.el.addEventListener("click", this._handleInputClick);
6238
6541
  this.el.addEventListener("keydown", this._handleInputKeydown);
6239
- this.plate.addEventListener("mousedown", this._handleClockClickStart);
6240
- this.plate.addEventListener("touchstart", this._handleClockClickStart);
6542
+ this.plate.addEventListener("pointerdown", this._handleClockClickStart);
6241
6543
  this.digitalClock.addEventListener("keyup", this._inputFromTextField);
6242
6544
  this.inputHours.addEventListener("focus", () => this.showView("hours"));
6243
6545
  this.inputHours.addEventListener("focusout", () => this.formatHours());
@@ -6247,10 +6549,9 @@ var _Timepicker = class _Timepicker extends Component {
6247
6549
  _removeEventHandlers() {
6248
6550
  this.el.removeEventListener("click", this._handleInputClick);
6249
6551
  this.el.removeEventListener("keydown", this._handleInputKeydown);
6250
- document.removeEventListener("mousemove", this._handleDocumentClickMove);
6251
- document.removeEventListener("touchmove", this._handleDocumentClickMove);
6252
- document.removeEventListener("mouseup", this._handleDocumentClickEnd);
6253
- document.removeEventListener("touchend", this._handleDocumentClickEnd);
6552
+ document.removeEventListener("pointermove", this._handleDocumentClickMove);
6553
+ document.removeEventListener("pointerup", this._handleDocumentClickEnd);
6554
+ document.removeEventListener("pointercancel", this._handleDocumentClickCancel);
6254
6555
  }
6255
6556
  _insertHTMLIntoDOM() {
6256
6557
  const template = document.createElement("template");
@@ -6279,15 +6580,6 @@ var _Timepicker = class _Timepicker extends Component {
6279
6580
  this.footer = this.containerEl.querySelector(".timepicker-footer");
6280
6581
  this.amOrPm = "PM";
6281
6582
  }
6282
- /*private _createButton(text: string, visibility: string): HTMLButtonElement {
6283
- const button = document.createElement('button');
6284
- button.classList.add('waves-effect', 'text');
6285
- button.style.visibility = visibility;
6286
- button.type = 'button';
6287
- button.tabIndex = -1;
6288
- button.innerText = text;
6289
- return button;
6290
- }*/
6291
6583
  _pickerSetup() {
6292
6584
  Utils.createButton(
6293
6585
  this.footer,
@@ -6568,7 +6860,7 @@ _Timepicker._template = `<div class="timepicker-container">
6568
6860
  var Timepicker = _Timepicker;
6569
6861
 
6570
6862
  // src/ts/components/snackbar.ts
6571
- var _defaults20 = {
6863
+ var _defaults21 = {
6572
6864
  text: "",
6573
6865
  action: "",
6574
6866
  onAction: null,
@@ -6578,7 +6870,8 @@ var _defaults20 = {
6578
6870
  outDuration: 375,
6579
6871
  classes: "",
6580
6872
  completeCallback: null,
6581
- activationPercent: 0.8
6873
+ activationPercent: 0.8,
6874
+ root: null
6582
6875
  };
6583
6876
  var _Snackbar = class _Snackbar {
6584
6877
  constructor(options = {}) {
@@ -6601,8 +6894,10 @@ var _Snackbar = class _Snackbar {
6601
6894
  }
6602
6895
  _Snackbar._snackbars.length = 0;
6603
6896
  }
6604
- if (!_Snackbar._container || !_Snackbar._container.isConnected) {
6605
- _Snackbar._createContainer();
6897
+ const root = Utils.portalRoot(this.options.root ?? document.body);
6898
+ if (!_Snackbar._container || !_Snackbar._container.isConnected || _Snackbar._container.parentNode !== root) {
6899
+ if (_Snackbar._container) _Snackbar._removeContainer();
6900
+ _Snackbar._createContainer(root);
6606
6901
  }
6607
6902
  _Snackbar._snackbars.push(this);
6608
6903
  const snackbarElement = this._createSnackbar();
@@ -6612,30 +6907,31 @@ var _Snackbar = class _Snackbar {
6612
6907
  this._setTimer();
6613
6908
  }
6614
6909
  static get defaults() {
6615
- return _defaults20;
6910
+ return _defaults21;
6616
6911
  }
6617
6912
  static getInstance(el) {
6618
6913
  return el["Expressive_Snackbar"];
6619
6914
  }
6620
- static _createContainer() {
6915
+ static _createContainer(root = document.body) {
6621
6916
  const container = document.createElement("div");
6622
6917
  container.setAttribute("id", "snackbar-container");
6623
- container.addEventListener("touchstart", _Snackbar._onDragStart);
6624
- container.addEventListener("touchmove", _Snackbar._onDragMove);
6625
- container.addEventListener("touchend", _Snackbar._onDragEnd);
6626
- container.addEventListener("mousedown", _Snackbar._onDragStart);
6627
- document.addEventListener("mousemove", _Snackbar._onDragMove);
6628
- document.addEventListener("mouseup", _Snackbar._onDragEnd);
6629
- document.body.appendChild(container);
6918
+ container.addEventListener("pointerdown", _Snackbar._onDragStart);
6919
+ document.addEventListener("pointermove", _Snackbar._onDragMove);
6920
+ document.addEventListener("pointerup", _Snackbar._onDragEnd);
6921
+ document.addEventListener("pointercancel", _Snackbar._onDragCancel);
6922
+ root.appendChild(container);
6630
6923
  _Snackbar._container = container;
6631
6924
  }
6632
6925
  static _removeContainer() {
6633
- document.removeEventListener("mousemove", _Snackbar._onDragMove);
6634
- document.removeEventListener("mouseup", _Snackbar._onDragEnd);
6926
+ document.removeEventListener("pointermove", _Snackbar._onDragMove);
6927
+ document.removeEventListener("pointerup", _Snackbar._onDragEnd);
6928
+ document.removeEventListener("pointercancel", _Snackbar._onDragCancel);
6635
6929
  _Snackbar._container.remove();
6636
6930
  _Snackbar._container = null;
6637
6931
  }
6638
6932
  static _onDragStart(e) {
6933
+ if (!e.isPrimary) return;
6934
+ if (e.pointerType === "mouse" && e.button !== 0) return;
6639
6935
  const target = e.target;
6640
6936
  if (target && !target.closest("button, a") && target.closest(".snackbar")) {
6641
6937
  const snackbarElem = target.closest(".snackbar");
@@ -6646,17 +6942,19 @@ var _Snackbar = class _Snackbar {
6646
6942
  _Snackbar._draggedSnackbar = snackbar;
6647
6943
  snackbar.el.classList.add("panning");
6648
6944
  snackbar.el.style.transition = "";
6649
- snackbar.startingXPos = _Snackbar._xPos(e);
6945
+ _Snackbar._dragPointerId = e.pointerId;
6946
+ snackbar.startingXPos = e.clientX;
6650
6947
  snackbar.time = Date.now();
6651
- snackbar.xPos = _Snackbar._xPos(e);
6948
+ snackbar.xPos = e.clientX;
6652
6949
  }
6653
6950
  }
6654
6951
  static _onDragMove(e) {
6952
+ if (_Snackbar._dragPointerId !== e.pointerId) return;
6655
6953
  if (!!_Snackbar._draggedSnackbar) {
6656
6954
  e.preventDefault();
6657
6955
  const snackbar = _Snackbar._draggedSnackbar;
6658
- snackbar.deltaX = Math.abs(snackbar.xPos - _Snackbar._xPos(e));
6659
- snackbar.xPos = _Snackbar._xPos(e);
6956
+ snackbar.deltaX = Math.abs(snackbar.xPos - e.clientX);
6957
+ snackbar.xPos = e.clientX;
6660
6958
  snackbar.velocityX = snackbar.deltaX / (Date.now() - snackbar.time);
6661
6959
  snackbar.time = Date.now();
6662
6960
  const totalDeltaX = snackbar.xPos - snackbar.startingXPos;
@@ -6665,7 +6963,9 @@ var _Snackbar = class _Snackbar {
6665
6963
  snackbar.el.style.opacity = (1 - Math.abs(totalDeltaX / activationDistance)).toString();
6666
6964
  }
6667
6965
  }
6668
- static _onDragEnd() {
6966
+ static _onDragEnd(e) {
6967
+ if (_Snackbar._dragPointerId !== e.pointerId) return;
6968
+ _Snackbar._dragPointerId = null;
6669
6969
  if (!!_Snackbar._draggedSnackbar) {
6670
6970
  const snackbar = _Snackbar._draggedSnackbar;
6671
6971
  snackbar.panning = false;
@@ -6685,11 +6985,23 @@ var _Snackbar = class _Snackbar {
6685
6985
  _Snackbar._draggedSnackbar = null;
6686
6986
  }
6687
6987
  }
6688
- static _xPos(e) {
6689
- if (e.type.startsWith("touch") && e.targetTouches.length >= 1) {
6690
- return e.targetTouches[0].clientX;
6691
- }
6692
- return e.clientX;
6988
+ /**
6989
+ * A cancelled pointer is not a short swipe. Snap back and resume the timer
6990
+ * without consulting the distance travelled - the browser took the gesture
6991
+ * over, so the movement so far says nothing about what the user wanted.
6992
+ */
6993
+ static _onDragCancel(e) {
6994
+ if (_Snackbar._dragPointerId !== e.pointerId) return;
6995
+ _Snackbar._dragPointerId = null;
6996
+ const snackbar = _Snackbar._draggedSnackbar;
6997
+ if (!snackbar) return;
6998
+ snackbar.panning = false;
6999
+ snackbar.el.classList.remove("panning");
7000
+ snackbar.el.style.transition = "transform .2s, opacity .2s";
7001
+ snackbar.el.style.transform = "";
7002
+ snackbar.el.style.opacity = "";
7003
+ snackbar._resumeTimer();
7004
+ _Snackbar._draggedSnackbar = null;
6693
7005
  }
6694
7006
  /**
6695
7007
  * dismiss all snackbars.
@@ -6700,7 +7012,7 @@ var _Snackbar = class _Snackbar {
6700
7012
  }
6701
7013
  }
6702
7014
  _createSnackbar() {
6703
- let snackbar = this.options.snackbarId ? document.getElementById(this.options.snackbarId) : document.createElement("div");
7015
+ let snackbar = this.options.snackbarId ? Utils.getElementById(this.options.root ?? document.body, this.options.snackbarId) : document.createElement("div");
6704
7016
  if (snackbar instanceof HTMLTemplateElement) {
6705
7017
  const node = snackbar.content.cloneNode(true);
6706
7018
  snackbar = node.firstElementChild;
@@ -6822,13 +7134,15 @@ var _Snackbar = class _Snackbar {
6822
7134
  }, this.options.outDuration);
6823
7135
  }
6824
7136
  };
7137
+ /** Which pointer owns the drag in flight; a second finger is ignored. */
7138
+ _Snackbar._dragPointerId = null;
6825
7139
  _Snackbar._snackbars = [];
6826
7140
  _Snackbar._container = null;
6827
7141
  _Snackbar._draggedSnackbar = null;
6828
7142
  var Snackbar = _Snackbar;
6829
7143
 
6830
7144
  // src/ts/components/tooltip.ts
6831
- var _defaults21 = {
7145
+ var _defaults22 = {
6832
7146
  exitDelay: 200,
6833
7147
  enterDelay: 0,
6834
7148
  text: "",
@@ -6873,8 +7187,10 @@ var Tooltip = class _Tooltip extends Component {
6873
7187
  this.isFocused = false;
6874
7188
  this.close();
6875
7189
  };
7190
+ // Focus opens the tooltip only when it arrived by keyboard: a pointer press
7191
+ // on the trigger also focuses it, and _handleMouseEnter already covers that.
6876
7192
  this._handleFocus = () => {
6877
- if (Utils.tabPressed) {
7193
+ if (this.el.matches(":focus-visible")) {
6878
7194
  this.isFocused = true;
6879
7195
  this.open(false);
6880
7196
  }
@@ -6896,7 +7212,7 @@ var Tooltip = class _Tooltip extends Component {
6896
7212
  this._setupEventHandlers();
6897
7213
  }
6898
7214
  static get defaults() {
6899
- return _defaults21;
7215
+ return _defaults22;
6900
7216
  }
6901
7217
  /**
6902
7218
  * Initializes instances of Tooltip.
@@ -6910,6 +7226,12 @@ var Tooltip = class _Tooltip extends Component {
6910
7226
  return el["Expressive_Tooltip"];
6911
7227
  }
6912
7228
  destroy() {
7229
+ clearTimeout(this._enterDelayTimeout);
7230
+ clearTimeout(this._exitDelayTimeout);
7231
+ clearTimeout(this._animationTimeout);
7232
+ this.isOpen = false;
7233
+ this.isHovered = false;
7234
+ this.isFocused = false;
6913
7235
  if (this.el.getAttribute("aria-describedby") === this.tooltipEl.id) {
6914
7236
  this.el.removeAttribute("aria-describedby");
6915
7237
  }
@@ -6923,7 +7245,7 @@ var Tooltip = class _Tooltip extends Component {
6923
7245
  this.tooltipEl.id = `tooltip-${Utils.guid()}`;
6924
7246
  this.tooltipEl.setAttribute("role", "tooltip");
6925
7247
  this.el.setAttribute("aria-describedby", this.tooltipEl.id);
6926
- const tooltipContentEl = this.options.tooltipId ? document.getElementById(this.options.tooltipId) : document.createElement("div");
7248
+ const tooltipContentEl = this.options.tooltipId ? Utils.getElementById(this.el, this.options.tooltipId) : document.createElement("div");
6927
7249
  if (this.options.tooltipId) {
6928
7250
  this.tooltipEl.classList.add("rich");
6929
7251
  }
@@ -6931,7 +7253,7 @@ var Tooltip = class _Tooltip extends Component {
6931
7253
  tooltipContentEl.classList.add("tooltip-content");
6932
7254
  this._setTooltipContent(tooltipContentEl);
6933
7255
  this.tooltipEl.appendChild(tooltipContentEl);
6934
- document.body.appendChild(this.tooltipEl);
7256
+ Utils.portalRoot(this.el).appendChild(this.tooltipEl);
6935
7257
  }
6936
7258
  _setTooltipContent(tooltipContentEl) {
6937
7259
  if (this.options.tooltipId) return;
@@ -7001,32 +7323,15 @@ var Tooltip = class _Tooltip extends Component {
7001
7323
  tooltip.style.left = newCoordinates.x + "px";
7002
7324
  }
7003
7325
  _repositionWithinScreen(x, y, width, height) {
7004
- const scrollLeft = Utils.getDocumentScrollLeft();
7005
- const scrollTop = Utils.getDocumentScrollTop();
7006
- let newX = x - scrollLeft;
7007
- let newY = y - scrollTop;
7008
- const bounding = {
7009
- left: newX,
7010
- top: newY,
7326
+ return Utils._repositionWithinScreen(
7327
+ x,
7328
+ y,
7011
7329
  width,
7012
- height
7013
- };
7014
- const offset = this.options.margin + this.options.transitionMovement;
7015
- const edges = Utils.checkWithinContainer(document.body, bounding, offset);
7016
- if (edges.left) {
7017
- newX = offset;
7018
- } else if (edges.right) {
7019
- newX -= newX + width - window.innerWidth;
7020
- }
7021
- if (edges.top) {
7022
- newY = offset;
7023
- } else if (edges.bottom) {
7024
- newY -= newY + height - window.innerHeight;
7025
- }
7026
- return {
7027
- x: newX + scrollLeft,
7028
- y: newY + scrollTop
7029
- };
7330
+ height,
7331
+ this.options.margin,
7332
+ this.options.transitionMovement,
7333
+ "center"
7334
+ );
7030
7335
  }
7031
7336
  _animateIn() {
7032
7337
  this._positionTooltip();
@@ -7035,7 +7340,8 @@ var Tooltip = class _Tooltip extends Component {
7035
7340
  this.tooltipEl.style.transition = `
7036
7341
  transform ${duration}ms ease-out,
7037
7342
  opacity ${duration}ms ease-out`;
7038
- setTimeout(() => {
7343
+ clearTimeout(this._animationTimeout);
7344
+ this._animationTimeout = setTimeout(() => {
7039
7345
  this.tooltipEl.style.transform = `translateX(${this.xMovement}px) translateY(${this.yMovement}px)`;
7040
7346
  this.tooltipEl.style.opacity = (this.options.opacity || 1).toString();
7041
7347
  }, 1);
@@ -7045,7 +7351,8 @@ var Tooltip = class _Tooltip extends Component {
7045
7351
  this.tooltipEl.style.transition = `
7046
7352
  transform ${duration}ms ease-out,
7047
7353
  opacity ${duration}ms ease-out`;
7048
- setTimeout(() => {
7354
+ clearTimeout(this._animationTimeout);
7355
+ this._animationTimeout = setTimeout(() => {
7049
7356
  this.tooltipEl.style.transform = `translateX(0px) translateY(0px)`;
7050
7357
  this.tooltipEl.style.opacity = "0";
7051
7358
  }, 1);
@@ -7070,79 +7377,107 @@ var Tooltip = class _Tooltip extends Component {
7070
7377
 
7071
7378
  // src/ts/components/registry.ts
7072
7379
  var AUTO_INIT_COMPONENTS = {
7073
- Autocomplete: { component: Autocomplete, selector: ".autocomplete" },
7380
+ AppBar: {
7381
+ component: AppBar,
7382
+ selector: "header.medium, header.large, header:has(> :is(nav:not(.tabs, .navigation-bar, .navigation-rail), .bar) > :is(.search-bar, .searchbar))"
7383
+ },
7384
+ Autocomplete: {
7385
+ component: Autocomplete,
7386
+ selector: ".autocomplete"
7387
+ },
7388
+ ButtonGroup: {
7389
+ component: ButtonGroup,
7390
+ selector: ".button-group"
7391
+ },
7074
7392
  // Shared with Cards.Init() - see CARDS_SELECTOR. This entry used to read
7075
7393
  // '.cards', which no card markup uses.
7076
- Cards: { component: Cards, selector: CARDS_SELECTOR },
7394
+ Cards: { component: Cards, selector: CARDS_SELECTOR, filter: isCardReveal },
7395
+ ExpandingCard: {
7396
+ component: ExpandingCard,
7397
+ selector: EXPANDING_CARD_SELECTOR
7398
+ },
7077
7399
  Carousel: { component: Carousel, selector: ".carousel" },
7078
7400
  Chips: { component: Chips, selector: ".chips" },
7079
- Datepicker: { component: Datepicker, selector: ".datepicker" },
7401
+ Datepicker: {
7402
+ component: Datepicker,
7403
+ selector: ".datepicker, .date-picker"
7404
+ },
7080
7405
  Menu: { component: Menu, selector: ".menu-trigger" },
7081
7406
  Lightbox: { component: Lightbox, selector: ".lightboxed" },
7082
- Parallax: { component: Parallax, selector: ".parallax" },
7083
7407
  ScrollSpy: { component: ScrollSpy, selector: ".scrollspy" },
7084
7408
  FormSelect: { component: FormSelect, selector: "select" },
7085
- Sidenav: { component: Sidenav, selector: ".sidenav" },
7086
- NavigationRail: { component: NavigationRail, selector: ".navigation-rail" },
7409
+ NavigationDrawer: {
7410
+ component: NavigationDrawer,
7411
+ selector: ".sidenav, .navigation-drawer"
7412
+ },
7413
+ NavigationRail: {
7414
+ component: NavigationRail,
7415
+ selector: ".navigation-rail"
7416
+ },
7087
7417
  Tabs: { component: Tabs, selector: ".tabs" },
7088
- Timepicker: { component: Timepicker, selector: ".timepicker" },
7418
+ Timepicker: {
7419
+ component: Timepicker,
7420
+ selector: ".timepicker, .time-picker"
7421
+ },
7089
7422
  Tooltip: { component: Tooltip, selector: ".tooltipped" },
7090
7423
  FloatingActionButton: {
7091
7424
  component: FloatingActionButton,
7092
- selector: ".fixed-action-btn"
7425
+ selector: ".fab, .fixed-action-btn, .fab-menu"
7093
7426
  }
7094
7427
  };
7095
7428
  function AutoInit(context = document.body, options) {
7096
7429
  for (const name of Object.keys(AUTO_INIT_COMPONENTS)) {
7097
- const { component, selector } = AUTO_INIT_COMPONENTS[name];
7430
+ const { component, selector, filter } = AUTO_INIT_COMPONENTS[name];
7098
7431
  const els = context.querySelectorAll(`:is(${selector}):not(.no-autoinit)`);
7099
- component.init(els, options?.[name] ?? {});
7432
+ if (filter) {
7433
+ els.forEach((el) => {
7434
+ if (filter(el)) component.init(el, options?.[name] ?? {});
7435
+ });
7436
+ } else {
7437
+ component.init(els, options?.[name] ?? {});
7438
+ }
7100
7439
  }
7101
7440
  }
7102
7441
 
7103
7442
  // src/ts/index.ts
7104
- var version = "0.7.0";
7105
- if (typeof document !== "undefined") {
7106
- document.addEventListener("keydown", Utils.docHandleKeydown, true);
7107
- document.addEventListener("keyup", Utils.docHandleKeyup, true);
7108
- document.addEventListener("focus", Utils.docHandleFocus, true);
7109
- document.addEventListener("blur", Utils.docHandleBlur, true);
7110
- }
7443
+ var version = "0.9.0";
7111
7444
  Forms.Init();
7112
7445
  Chips.Init();
7113
- Waves.Init();
7114
- Range.Init();
7446
+ Slider.Init();
7115
7447
  Cards.Init();
7448
+ ExpandingCard.Init();
7116
7449
  Dialogs.Init();
7117
7450
  BottomSheets.Init();
7118
7451
  SideSheets.Init();
7119
7452
  export {
7453
+ AppBar,
7120
7454
  AutoInit,
7121
7455
  Autocomplete,
7122
7456
  BottomSheets,
7457
+ ButtonGroup,
7123
7458
  Cards,
7124
7459
  Carousel,
7125
7460
  CharacterCounter,
7126
7461
  Chips,
7127
7462
  Datepicker,
7128
7463
  Dialogs,
7464
+ ExpandingCard,
7129
7465
  FloatingActionButton,
7130
7466
  FormSelect,
7131
7467
  Forms,
7132
7468
  Lightbox,
7133
7469
  Menu,
7470
+ NavigationDrawer,
7134
7471
  NavigationRail,
7135
- Parallax,
7136
- Range,
7472
+ Slider as Range,
7137
7473
  ScrollSpy,
7138
7474
  SideSheets,
7139
- Sidenav,
7475
+ NavigationDrawer as Sidenav,
7140
7476
  Slider,
7141
7477
  Snackbar,
7142
7478
  Tabs,
7143
7479
  Timepicker,
7144
7480
  Tooltip,
7145
- Waves,
7146
7481
  version
7147
7482
  };
7148
7483
  //# sourceMappingURL=expressive.mjs.map