@expressivecss/expressive 0.7.0 → 0.8.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 (105) hide show
  1. package/CHANGELOG.md +1266 -1
  2. package/README.md +14 -11
  3. package/dist/css/expressive.css +4215 -1668
  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/js/expressive.cjs +1231 -1253
  8. package/dist/js/expressive.cjs.map +4 -4
  9. package/dist/js/expressive.js +1231 -1253
  10. package/dist/js/expressive.js.map +4 -4
  11. package/dist/js/expressive.min.js +12 -12
  12. package/dist/js/expressive.min.js.map +4 -4
  13. package/dist/js/expressive.mjs +1231 -1253
  14. package/dist/js/expressive.mjs.map +4 -4
  15. package/dist/types/behaviors/bottomSheets.d.ts +19 -11
  16. package/dist/types/behaviors/sheetDrag.d.ts +40 -0
  17. package/dist/types/behaviors/sideSheets.d.ts +2 -9
  18. package/dist/types/components/autocomplete.d.ts +15 -3
  19. package/dist/types/components/buttons.d.ts +5 -1
  20. package/dist/types/components/carousel.d.ts +87 -53
  21. package/dist/types/components/chips.d.ts +21 -1
  22. package/dist/types/components/expandingCard.d.ts +31 -0
  23. package/dist/types/components/index.d.ts +21 -20
  24. package/dist/types/components/lightbox.d.ts +1 -0
  25. package/dist/types/components/menu.d.ts +7 -1
  26. package/dist/types/components/{sidenav.d.ts → navigationDrawer.d.ts} +16 -16
  27. package/dist/types/components/registry.d.ts +8 -8
  28. package/dist/types/components/slider.d.ts +26 -85
  29. package/dist/types/components/snackbar.d.ts +20 -5
  30. package/dist/types/components/tabs.d.ts +12 -0
  31. package/dist/types/components/timepicker.d.ts +12 -9
  32. package/dist/types/core/utils.d.ts +64 -53
  33. package/dist/types/index.d.ts +8 -9
  34. package/package.json +15 -1
  35. package/src/sass/README.md +61 -11
  36. package/src/sass/abstracts/_breakpoints.scss +17 -19
  37. package/src/sass/abstracts/_mixins.scss +103 -21
  38. package/src/sass/abstracts/_variables.scss +50 -5
  39. package/src/sass/base/_global.scss +6 -24
  40. package/src/sass/base/_grid.scss +15 -12
  41. package/src/sass/base/_normalize.scss +34 -38
  42. package/src/sass/base/_typography.scss +4 -18
  43. package/src/sass/components/_badges.scss +2 -3
  44. package/src/sass/components/_banners.scss +298 -0
  45. package/src/sass/components/_bottom-app-bar.scss +180 -0
  46. package/src/sass/components/_bottom-sheet.scss +12 -4
  47. package/src/sass/components/_button-groups.scss +186 -0
  48. package/src/sass/components/_buttons.scss +245 -92
  49. package/src/sass/components/_cards.scss +494 -73
  50. package/src/sass/components/_carousel.scss +562 -68
  51. package/src/sass/components/_chips.scss +64 -5
  52. package/src/sass/components/_datepicker.scss +24 -24
  53. package/src/sass/components/_dialog.scss +17 -11
  54. package/src/sass/components/_drag-handle.scss +130 -0
  55. package/src/sass/components/_expanding-card.scss +260 -0
  56. package/src/sass/components/_fab-menu.scss +232 -0
  57. package/src/sass/components/_floating-sheet.scss +86 -0
  58. package/src/sass/components/_icon-buttons.scss +194 -0
  59. package/src/sass/components/_icons-material-design.scss +16 -4
  60. package/src/sass/components/_index.scss +22 -6
  61. package/src/sass/components/_list.scss +16 -13
  62. package/src/sass/components/_loading-indicator.scss +106 -0
  63. package/src/sass/components/_menu.scss +335 -61
  64. package/src/sass/components/_navbar.scss +71 -24
  65. package/src/sass/components/_navigation-bar.scss +13 -10
  66. package/src/sass/components/{_sidenav.scss → _navigation-drawer.scss} +123 -33
  67. package/src/sass/components/_navigation-rail.scss +22 -22
  68. package/src/sass/components/_page-footer.scss +19 -16
  69. package/src/sass/components/_pagination.scss +24 -6
  70. package/src/sass/components/_panes.scss +52 -11
  71. package/src/sass/components/{_preloader.scss → _progress.scss} +11 -3
  72. package/src/sass/components/_scrim.scss +37 -0
  73. package/src/sass/components/_search.scss +259 -0
  74. package/src/sass/components/_segmented-buttons.scss +188 -0
  75. package/src/sass/components/_side-sheet.scss +2 -2
  76. package/src/sass/components/_snackbar.scss +20 -15
  77. package/src/sass/components/_split-button.scss +196 -0
  78. package/src/sass/components/_tabs.scss +7 -8
  79. package/src/sass/components/_timepicker.scss +67 -58
  80. package/src/sass/components/_toolbar.scss +206 -80
  81. package/src/sass/components/forms/_checkboxes.scss +49 -12
  82. package/src/sass/components/forms/_fieldset.scss +4 -4
  83. package/src/sass/components/forms/_file-input.scss +8 -1
  84. package/src/sass/components/forms/_index.scss +1 -1
  85. package/src/sass/components/forms/_input-fields.scss +50 -44
  86. package/src/sass/components/forms/_radio-buttons.scss +42 -10
  87. package/src/sass/components/forms/_select.scss +0 -1
  88. package/src/sass/components/forms/{_range.scss → _slider.scss} +8 -9
  89. package/src/sass/components/forms/_switches.scss +6 -7
  90. package/src/sass/tokens/_index.scss +4 -1
  91. package/src/sass/tokens/_reference.scss +70 -61
  92. package/src/sass/tokens/_state.scss +24 -0
  93. package/src/sass/tokens/_theme.scss +6 -3
  94. package/src/sass/tokens/_vibrant.scss +76 -0
  95. package/src/sass/utilities/_colors.scss +14 -1
  96. package/src/sass/utilities/_helpers.scss +5 -5
  97. package/src/sass/utilities/_visibility.scss +48 -26
  98. package/dist/types/behaviors/waves.d.ts +0 -15
  99. package/dist/types/components/parallax.d.ts +0 -16
  100. package/dist/types/components/range.d.ts +0 -43
  101. package/dist/types/core/bounding.d.ts +0 -6
  102. package/dist/types/core/edges.d.ts +0 -6
  103. package/src/sass/components/_parallax.scss +0 -104
  104. package/src/sass/components/_pulse.scss +0 -37
  105. package/src/sass/components/_slider.scss +0 -129
@@ -29,68 +29,28 @@ var Expressive = (() => {
29
29
  Chips: () => Chips,
30
30
  Datepicker: () => Datepicker,
31
31
  Dialogs: () => Dialogs,
32
+ ExpandingCard: () => ExpandingCard,
32
33
  FloatingActionButton: () => FloatingActionButton,
33
34
  FormSelect: () => FormSelect,
34
35
  Forms: () => Forms,
35
36
  Lightbox: () => Lightbox,
36
37
  Menu: () => Menu,
38
+ NavigationDrawer: () => NavigationDrawer,
37
39
  NavigationRail: () => NavigationRail,
38
- Parallax: () => Parallax,
39
- Range: () => Range,
40
+ Range: () => Slider,
40
41
  ScrollSpy: () => ScrollSpy,
41
42
  SideSheets: () => SideSheets,
42
- Sidenav: () => Sidenav,
43
+ Sidenav: () => NavigationDrawer,
43
44
  Slider: () => Slider,
44
45
  Snackbar: () => Snackbar,
45
46
  Tabs: () => Tabs,
46
47
  Timepicker: () => Timepicker,
47
48
  Tooltip: () => Tooltip,
48
- Waves: () => Waves,
49
49
  version: () => version
50
50
  });
51
51
 
52
52
  // src/ts/core/utils.ts
53
53
  var _Utils = class _Utils {
54
- /**
55
- * Detects when a key is pressed.
56
- * @param e Event instance.
57
- */
58
- static docHandleKeydown(e) {
59
- _Utils.keyDown = true;
60
- if (_Utils._navigationKeys.has(e.key)) {
61
- _Utils.tabPressed = true;
62
- }
63
- }
64
- /**
65
- * Detects when a key is released.
66
- * @param e Event instance.
67
- */
68
- static docHandleKeyup(e) {
69
- _Utils.keyDown = false;
70
- if (_Utils._navigationKeys.has(e.key)) {
71
- _Utils.tabPressed = false;
72
- }
73
- }
74
- /**
75
- * Detects when document is focused.
76
- * @param e Event instance.
77
- */
78
- /* eslint-disabled as of required event type condition check */
79
- /* eslint-disable-next-line */
80
- static docHandleFocus(e) {
81
- if (_Utils.keyDown) {
82
- document.body.classList.add("keyboard-focused");
83
- }
84
- }
85
- /**
86
- * Detects when document is not focused.
87
- * @param e Event instance.
88
- */
89
- /* eslint-disabled as of required event type condition check */
90
- /* eslint-disable-next-line */
91
- static docHandleBlur(e) {
92
- document.body.classList.remove("keyboard-focused");
93
- }
94
54
  /**
95
55
  * Run `callback` once the document is parsed.
96
56
  *
@@ -108,13 +68,46 @@ var Expressive = (() => {
108
68
  }
109
69
  }
110
70
  /**
111
- * Generates a unique string identifier.
71
+ * Generates a unique string identifier. Every caller prefixes the result and
72
+ * uses it as an element id, and a UUID is valid in that position.
73
+ *
74
+ * `randomUUID` is exposed only in secure contexts, and a dev server on a
75
+ * plain-HTTP LAN address - the usual way a page is opened on a phone - is
76
+ * not one. Without the fallback every component that stamps an id would
77
+ * throw there. The ids only have to be unique within one document, so the
78
+ * fallback does not need to be cryptographic.
112
79
  */
113
80
  static guid() {
114
- const s4 = () => {
115
- return Math.floor((1 + Math.random()) * 65536).toString(16).substring(1);
116
- };
117
- return s4() + s4() + "-" + s4() + "-" + s4() + "-" + s4() + "-" + s4() + s4() + s4();
81
+ return globalThis.crypto?.randomUUID?.() ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
82
+ }
83
+ /**
84
+ * Where an element created at runtime has to be appended to stay in the same
85
+ * tree as `el`. A stylesheet adopted into a shadow root cannot match a node
86
+ * in the document, so a portal that lands on `document.body` is unstyled
87
+ * whenever its origin lives in a shadow root (adr/0002).
88
+ *
89
+ * Light-DOM elements still get `document.body`, which is what escapes an
90
+ * ancestor's `overflow: hidden` and stacking context. Inside a shadow root
91
+ * both hazards come back: the host's own ancestors still form the containing
92
+ * block, so a transformed ancestor re-anchors `position: fixed` and a
93
+ * positioned one re-anchors `position: absolute`.
94
+ */
95
+ static portalRoot(el) {
96
+ const root = el.getRootNode();
97
+ return root.host ? root : document.body;
98
+ }
99
+ /**
100
+ * `getElementById` against the tree `el` lives in. The mirror of
101
+ * `portalRoot`: an id in a shadow root is invisible to
102
+ * `document.getElementById`, so a trigger and its target inside the same
103
+ * root could not find each other at all.
104
+ *
105
+ * Falls back to the document for a detached `el`, whose root is a plain
106
+ * element with no lookup of its own.
107
+ */
108
+ static getElementById(el, id) {
109
+ const root = el.getRootNode();
110
+ return root.getElementById ? root.getElementById(id) : document.getElementById(id);
118
111
  }
119
112
  /**
120
113
  * Checks for exceeded edges
@@ -211,43 +204,50 @@ var Expressive = (() => {
211
204
  * Retrieves document scroll postion from top.
212
205
  */
213
206
  static getDocumentScrollTop() {
214
- return window.scrollY || document.documentElement.scrollTop || document.body.scrollTop || 0;
207
+ return window.scrollY;
215
208
  }
216
209
  /**
217
210
  * Retrieves document scroll postion from left.
218
211
  */
219
212
  static getDocumentScrollLeft() {
220
- return window.scrollX || document.documentElement.scrollLeft || document.body.scrollLeft || 0;
213
+ return window.scrollX;
221
214
  }
222
215
  /**
223
- * Fires the given function after a certain ammount of time.
224
- * @param func Function to be fired.
225
- * @param wait Wait time.
226
- * @param options Additional options.
216
+ * Trailing-edge throttle: run `func` now, then at most once per `wait` ms
217
+ * with the most recent arguments.
218
+ *
219
+ * This used to carry its underscore ancestor's whole configuration - leading
220
+ * and trailing switches, a result passthrough - and both call sites are
221
+ * resize handlers passing nothing but a function and a delay.
222
+ *
223
+ * Assign the return value once. `x = Utils.throttle(fn, 200)` is right;
224
+ * wrapping it in an arrow builds a fresh closure per event and never calls
225
+ * it, which is how resize handling was dead in three components.
227
226
  */
228
- static throttle(func, wait, options = {}) {
229
- let lastArgs = null, result, timeout = null, previous = 0;
230
- const later = () => {
231
- previous = options?.leading === false ? 0 : Date.now();
232
- timeout = null;
233
- result = func(...lastArgs ?? []);
234
- lastArgs = null;
235
- };
227
+ static throttle(func, wait) {
228
+ let timeout = null;
229
+ let lastArgs = null;
230
+ let previous = 0;
236
231
  return (...args) => {
237
232
  const now = Date.now();
238
- if (!previous && options?.leading === false) previous = now;
239
233
  const remaining = wait - (now - previous);
240
- lastArgs = args;
241
234
  if (remaining <= 0) {
242
235
  clearTimeout(timeout);
243
236
  timeout = null;
244
237
  previous = now;
245
238
  lastArgs = null;
246
- result = func(...args);
247
- } else if (!timeout && options?.trailing !== false) {
248
- timeout = setTimeout(later, remaining);
239
+ func(...args);
240
+ } else if (!timeout) {
241
+ lastArgs = args;
242
+ timeout = setTimeout(() => {
243
+ previous = Date.now();
244
+ timeout = null;
245
+ func(...lastArgs ?? []);
246
+ lastArgs = null;
247
+ }, remaining);
248
+ } else {
249
+ lastArgs = args;
249
250
  }
250
- return result;
251
251
  };
252
252
  }
253
253
  /**
@@ -264,7 +264,7 @@ var Expressive = (() => {
264
264
  * Renders a button with optional callback function
265
265
  */
266
266
  static createButton(container, text, className = [], visibility = true, callback = null) {
267
- className = className.concat(["waves-effect", "text"]);
267
+ className = className.concat(["text"]);
268
268
  const button = document.createElement("button");
269
269
  button.className = className.join(" ");
270
270
  button.style.visibility = visibility ? "visible" : "hidden";
@@ -274,7 +274,7 @@ var Expressive = (() => {
274
274
  if (typeof callback === "function") {
275
275
  button.addEventListener("click", callback);
276
276
  button.addEventListener("keypress", (e) => {
277
- if (_Utils.keys.ENTER.includes(e.key)) callback(e);
277
+ if (e.key === _Utils.keys.ENTER) callback(e);
278
278
  });
279
279
  }
280
280
  container.append(button);
@@ -353,36 +353,22 @@ var Expressive = (() => {
353
353
  };
354
354
  }
355
355
  };
356
- /** Specifies wether tab is pressed or not. */
357
- _Utils.tabPressed = false;
358
- /** Specifies wether there is a key pressed. */
359
- _Utils.keyDown = false;
360
356
  /**
361
- * Key maps.
357
+ * Key maps. One `KeyboardEvent.key` value each: the second entries these used
358
+ * to carry (`Esc`, `Up`, `Del`, ...) were IE and Edge Legacy spellings, and
359
+ * the support baseline excludes both.
362
360
  */
363
361
  _Utils.keys = {
364
- TAB: ["Tab"],
365
- ENTER: ["Enter"],
366
- ESC: ["Escape", "Esc"],
367
- BACKSPACE: ["Backspace"],
368
- ARROW_UP: ["ArrowUp", "Up"],
369
- ARROW_DOWN: ["ArrowDown", "Down"],
370
- ARROW_LEFT: ["ArrowLeft", "Left"],
371
- ARROW_RIGHT: ["ArrowRight", "Right"],
372
- DELETE: ["Delete", "Del"]
362
+ TAB: "Tab",
363
+ ENTER: "Enter",
364
+ ESC: "Escape",
365
+ BACKSPACE: "Backspace",
366
+ ARROW_UP: "ArrowUp",
367
+ ARROW_DOWN: "ArrowDown",
368
+ ARROW_LEFT: "ArrowLeft",
369
+ ARROW_RIGHT: "ArrowRight",
370
+ DELETE: "Delete"
373
371
  };
374
- /**
375
- * Keys that count as keyboard navigation, precomputed.
376
- *
377
- * These handlers run at capture phase on the document for every keystroke on
378
- * the page, and used to rebuild three arrays and spread them into a fourth
379
- * on each one.
380
- */
381
- _Utils._navigationKeys = /* @__PURE__ */ new Set([
382
- ..._Utils.keys.TAB,
383
- ..._Utils.keys.ARROW_DOWN,
384
- ..._Utils.keys.ARROW_UP
385
- ]);
386
372
  var Utils = _Utils;
387
373
 
388
374
  // src/ts/core/component.ts
@@ -466,13 +452,14 @@ var Expressive = (() => {
466
452
  this._handleRevealEvent();
467
453
  };
468
454
  this._handleKeypressEvent = (e) => {
469
- if (Utils.keys.ENTER.includes(e.key)) {
455
+ if (e.key === Utils.keys.ENTER || e.key === " ") {
456
+ e.preventDefault();
470
457
  this._handleRevealEvent();
471
458
  }
472
459
  };
473
460
  this._handleRevealEvent = () => {
474
- this._activators.forEach((el) => el.tabIndex = -1);
475
- this.open();
461
+ if (this.isOpen) this.close();
462
+ else this.open();
476
463
  };
477
464
  this._setupRevealCloseEventHandlers = () => {
478
465
  this.cardRevealClose.addEventListener("click", this.close);
@@ -483,7 +470,7 @@ var Expressive = (() => {
483
470
  this.cardRevealClose.removeEventListener("keypress", this._handleKeypressCloseEvent);
484
471
  };
485
472
  this._handleKeypressCloseEvent = (e) => {
486
- if (Utils.keys.ENTER.includes(e.key)) {
473
+ if (e.key === Utils.keys.ENTER) {
487
474
  this.close();
488
475
  }
489
476
  };
@@ -494,6 +481,7 @@ var Expressive = (() => {
494
481
  if (this.isOpen || !this.cardReveal) return;
495
482
  this.isOpen = true;
496
483
  this.cardReveal.ariaExpanded = "true";
484
+ this._activators.forEach((el) => el.ariaExpanded = "true");
497
485
  if (this.cardRevealClose) this.cardRevealClose.tabIndex = 0;
498
486
  if (typeof this.options.onOpen === "function") {
499
487
  this.options.onOpen.call(this);
@@ -507,7 +495,7 @@ var Expressive = (() => {
507
495
  if (!this.isOpen || !this.cardReveal) return;
508
496
  this.isOpen = false;
509
497
  this.cardReveal.ariaExpanded = "false";
510
- this._activators.forEach((el) => el.tabIndex = 0);
498
+ this._activators.forEach((el) => el.ariaExpanded = "false");
511
499
  if (this.cardRevealClose) this.cardRevealClose.tabIndex = -1;
512
500
  if (typeof this.options.onClose === "function") {
513
501
  this.options.onClose.call(this);
@@ -522,9 +510,18 @@ var Expressive = (() => {
522
510
  this._activators = [];
523
511
  this.cardReveal = this.el.querySelector(":scope > aside");
524
512
  if (this.cardReveal) {
525
- this._activators = Array.from(this.el.querySelectorAll(".activator"));
513
+ this._activators = Array.from(
514
+ this.el.querySelectorAll(".activator")
515
+ ).filter(
516
+ (activator) => !this.cardReveal.contains(activator)
517
+ );
526
518
  this._activators.forEach((el2) => {
527
- if (el2) el2.tabIndex = 0;
519
+ if (!el2) return;
520
+ el2.tabIndex = 0;
521
+ el2.ariaExpanded = "false";
522
+ if (!el2.matches("a[href], button, input, select, textarea")) {
523
+ el2.setAttribute("role", "button");
524
+ }
528
525
  });
529
526
  this.cardRevealClose = this.cardReveal.querySelector(
530
527
  ":scope > :is(h1, h2, h3, h4, h5, h6)"
@@ -567,8 +564,171 @@ var Expressive = (() => {
567
564
  }
568
565
  };
569
566
 
570
- // src/ts/components/menu.ts
567
+ // src/ts/components/expandingCard.ts
571
568
  var _defaults2 = {
569
+ onOpen: null,
570
+ onClose: null
571
+ };
572
+ var MOTION_DURATION = 500;
573
+ var EXPANDING_CARD_SELECTOR = "article.expanding-card:has(> dialog.expanding-card-dialog)";
574
+ var ExpandingCard = class _ExpandingCard extends Component {
575
+ constructor(el, options) {
576
+ super(el, options, _ExpandingCard);
577
+ this.isOpen = false;
578
+ this.closeTimer = null;
579
+ this._setupEventHandlers = () => {
580
+ this.trigger?.addEventListener("click", this.open);
581
+ this.trigger?.addEventListener("keypress", this._handleTriggerKeypress);
582
+ this.closeButton?.addEventListener("click", this.close);
583
+ this.dialog?.addEventListener("cancel", this._handleCancel);
584
+ this.dialog?.addEventListener("close", this._handleNativeClose);
585
+ };
586
+ this._removeEventHandlers = () => {
587
+ this.trigger?.removeEventListener("click", this.open);
588
+ this.trigger?.removeEventListener("keypress", this._handleTriggerKeypress);
589
+ this.closeButton?.removeEventListener("click", this.close);
590
+ this.dialog?.removeEventListener("cancel", this._handleCancel);
591
+ this.dialog?.removeEventListener("close", this._handleNativeClose);
592
+ };
593
+ this._handleTriggerKeypress = (event) => {
594
+ if (event.key === Utils.keys.ENTER || event.key === " ") {
595
+ event.preventDefault();
596
+ this.open();
597
+ }
598
+ };
599
+ this._handleCancel = (event) => {
600
+ event.preventDefault();
601
+ this.close();
602
+ };
603
+ this._handleNativeClose = () => {
604
+ if (!this.isOpen) return;
605
+ this.isOpen = false;
606
+ this.trigger.ariaExpanded = "false";
607
+ this.dialog.ariaExpanded = "false";
608
+ this.dialog.classList.remove("expanded");
609
+ };
610
+ this._setTransitionOrigin = () => {
611
+ if (!this.dialog) return;
612
+ const card = this.el.getBoundingClientRect();
613
+ const media = this.el.querySelector(
614
+ ":scope > figure, :scope > img"
615
+ );
616
+ const mediaRect = media?.getBoundingClientRect();
617
+ this.dialog.style.setProperty(
618
+ "--md-comp-expanding-card-from-top",
619
+ `${Math.max(0, card.top)}px`
620
+ );
621
+ this.dialog.style.setProperty(
622
+ "--md-comp-expanding-card-from-right",
623
+ `${Math.max(0, window.innerWidth - card.right)}px`
624
+ );
625
+ this.dialog.style.setProperty(
626
+ "--md-comp-expanding-card-from-bottom",
627
+ `${Math.max(0, window.innerHeight - card.bottom)}px`
628
+ );
629
+ this.dialog.style.setProperty(
630
+ "--md-comp-expanding-card-from-left",
631
+ `${Math.max(0, card.left)}px`
632
+ );
633
+ this.dialog.style.setProperty(
634
+ "--md-comp-expanding-card-from-media-height",
635
+ `${Math.max(0, mediaRect?.height ?? 0)}px`
636
+ );
637
+ };
638
+ this.open = () => {
639
+ if (this.isOpen || !this.dialog || !this.trigger) return;
640
+ if (this.closeTimer !== null) {
641
+ window.clearTimeout(this.closeTimer);
642
+ this.closeTimer = null;
643
+ }
644
+ this._setTransitionOrigin();
645
+ this.isOpen = true;
646
+ this.trigger.ariaExpanded = "true";
647
+ this.dialog.ariaExpanded = "true";
648
+ this.dialog.showModal();
649
+ void this.dialog.offsetWidth;
650
+ this.dialog.classList.add("expanded");
651
+ this.closeButton?.focus();
652
+ if (typeof this.options.onOpen === "function") {
653
+ this.options.onOpen.call(this, this.el);
654
+ }
655
+ };
656
+ this.close = () => {
657
+ if (!this.isOpen || !this.dialog || !this.trigger) return;
658
+ this._setTransitionOrigin();
659
+ this.isOpen = false;
660
+ this.trigger.ariaExpanded = "false";
661
+ this.dialog.ariaExpanded = "false";
662
+ this.dialog.classList.remove("expanded");
663
+ if (typeof this.options.onClose === "function") {
664
+ this.options.onClose.call(this, this.el);
665
+ }
666
+ const finish = () => {
667
+ this.closeTimer = null;
668
+ if (this.dialog.open) this.dialog.close();
669
+ this.trigger.focus();
670
+ };
671
+ const reducedMotion = window.matchMedia(
672
+ "(prefers-reduced-motion: reduce)"
673
+ ).matches;
674
+ if (reducedMotion) finish();
675
+ else this.closeTimer = window.setTimeout(finish, MOTION_DURATION);
676
+ };
677
+ this.el["Expressive_ExpandingCard"] = this;
678
+ this.options = {
679
+ ..._ExpandingCard.defaults,
680
+ ...options
681
+ };
682
+ this.dialog = this.el.querySelector(
683
+ ":scope > dialog.expanding-card-dialog"
684
+ );
685
+ this.trigger = this.el.querySelector(
686
+ ":scope > .expanding-card-trigger, :scope > figure > .expanding-card-trigger"
687
+ );
688
+ this.closeButton = this.dialog?.querySelector(".expanding-card-close") ?? null;
689
+ if (this.trigger && this.dialog) {
690
+ this.trigger.ariaExpanded = "false";
691
+ if (this.dialog.id)
692
+ this.trigger.setAttribute("aria-controls", this.dialog.id);
693
+ if (!this.trigger.matches("a[href], button, input, select, textarea")) {
694
+ this.trigger.tabIndex = 0;
695
+ this.trigger.setAttribute("role", "button");
696
+ }
697
+ this.dialog.ariaExpanded = "false";
698
+ this._setupEventHandlers();
699
+ }
700
+ }
701
+ static get defaults() {
702
+ return _defaults2;
703
+ }
704
+ static init(els, options) {
705
+ return super.init(els, options, _ExpandingCard);
706
+ }
707
+ static getInstance(el) {
708
+ return el["Expressive_ExpandingCard"];
709
+ }
710
+ destroy() {
711
+ this._removeEventHandlers();
712
+ if (this.closeTimer !== null) window.clearTimeout(this.closeTimer);
713
+ this.dialog?.classList.remove("expanded");
714
+ if (this.dialog?.open) this.dialog.close();
715
+ this.el["Expressive_ExpandingCard"] = void 0;
716
+ }
717
+ static Init() {
718
+ Utils.onDocumentReady(() => {
719
+ document.querySelectorAll(EXPANDING_CARD_SELECTOR).forEach((el) => {
720
+ if (el && el["Expressive_ExpandingCard"] == void 0) {
721
+ this.init(el);
722
+ }
723
+ });
724
+ });
725
+ }
726
+ };
727
+
728
+ // src/ts/components/menu.ts
729
+ var MIN_SURFACE_WIDTH = 112;
730
+ var MAX_SURFACE_WIDTH = 280;
731
+ var _defaults3 = {
572
732
  alignment: "left",
573
733
  autoFocus: true,
574
734
  constrainWidth: true,
@@ -584,6 +744,7 @@ var Expressive = (() => {
584
744
  onCloseEnd: null,
585
745
  onItemClick: null
586
746
  };
747
+ var SPACE = " ";
587
748
  var _Menu = class _Menu extends Component {
588
749
  constructor(el, options) {
589
750
  super(el, options, _Menu);
@@ -629,8 +790,8 @@ var Expressive = (() => {
629
790
  this.isTouchMoving = false;
630
791
  };
631
792
  this._handleTriggerKeydown = (e) => {
632
- const arrowDownOrEnter = Utils.keys.ARROW_DOWN.includes(e.key) || Utils.keys.ENTER.includes(e.key);
633
- if (arrowDownOrEnter && !this.isOpen) {
793
+ const opensMenu = e.key === SPACE || e.key === Utils.keys.ENTER || e.key === Utils.keys.ARROW_DOWN || e.key === Utils.keys.ARROW_UP;
794
+ if (opensMenu && !this.isOpen) {
634
795
  e.preventDefault();
635
796
  this.open();
636
797
  }
@@ -644,13 +805,18 @@ var Expressive = (() => {
644
805
  this._handleMenuClick = (e) => {
645
806
  const target = e.target;
646
807
  const li = target.closest("li");
808
+ if (li?.matches('.disabled, [aria-disabled="true"]')) {
809
+ e.preventDefault();
810
+ e.stopPropagation();
811
+ return;
812
+ }
647
813
  if (li && this._isSubmenuTriggerClick(target)) {
648
814
  e.preventDefault();
649
815
  e.stopPropagation();
650
816
  this._toggleSubmenu(li);
651
817
  return;
652
818
  }
653
- if (typeof this.options.onItemClick === "function") {
819
+ if (li && typeof this.options.onItemClick === "function") {
654
820
  this.options.onItemClick.call(this, li);
655
821
  }
656
822
  };
@@ -663,11 +829,11 @@ var Expressive = (() => {
663
829
  }
664
830
  };
665
831
  this._handleMenuKeydown = (e) => {
666
- const arrowUpOrDown = Utils.keys.ARROW_DOWN.includes(e.key) || Utils.keys.ARROW_UP.includes(e.key);
667
- if (Utils.keys.TAB.includes(e.key)) {
832
+ const arrowUpOrDown = e.key === Utils.keys.ARROW_DOWN || e.key === Utils.keys.ARROW_UP;
833
+ if (e.key === Utils.keys.TAB) {
668
834
  e.preventDefault();
669
835
  this.close();
670
- } else if (Utils.keys.ARROW_RIGHT.includes(e.key) && this.isOpen) {
836
+ } else if (e.key === Utils.keys.ARROW_RIGHT && this.isOpen) {
671
837
  const li = this._focusedRow(e);
672
838
  if (li?.querySelector(":scope > menu")) {
673
839
  e.preventDefault();
@@ -677,7 +843,7 @@ var Expressive = (() => {
677
843
  const first = li.querySelector(":scope > menu > li");
678
844
  first?.focus();
679
845
  }
680
- } else if (Utils.keys.ARROW_LEFT.includes(e.key) && this.isOpen) {
846
+ } else if (e.key === Utils.keys.ARROW_LEFT && this.isOpen) {
681
847
  const submenu = this._containingSubmenu(e);
682
848
  if (submenu) {
683
849
  e.preventDefault();
@@ -688,7 +854,7 @@ var Expressive = (() => {
688
854
  }
689
855
  } else if (arrowUpOrDown && this.isOpen) {
690
856
  e.preventDefault();
691
- const direction = Utils.keys.ARROW_DOWN.includes(e.key) ? 1 : -1;
857
+ const direction = e.key === Utils.keys.ARROW_DOWN ? 1 : -1;
692
858
  const list = this._containingSubmenu(e) ?? this.menuEl;
693
859
  let newFocusedIndex = this.focusedIndex;
694
860
  let hasFoundNewIndex = false;
@@ -700,17 +866,17 @@ var Expressive = (() => {
700
866
  }
701
867
  } while (newFocusedIndex < list.children.length && newFocusedIndex >= 0);
702
868
  if (hasFoundNewIndex) {
703
- if (this.focusedIndex >= 0)
704
- list.children[this.focusedIndex]?.classList.remove("active");
869
+ if (this.focusedIndex >= 0) list.children[this.focusedIndex]?.classList.remove("active");
705
870
  this.focusedIndex = newFocusedIndex;
706
871
  const item = list.children[this.focusedIndex];
707
872
  item.classList.add("active");
708
873
  item.focus({ preventScroll: true });
709
874
  }
710
- } else if (Utils.keys.ENTER.includes(e.key) && this.isOpen) {
875
+ } else if ((e.key === Utils.keys.ENTER || e.key === SPACE) && this.isOpen) {
711
876
  const li = this._focusedRow(e);
877
+ e.preventDefault();
878
+ if (li?.matches('.disabled, [aria-disabled="true"]')) return;
712
879
  if (li?.querySelector(":scope > menu")) {
713
- e.preventDefault();
714
880
  this._toggleSubmenu(li);
715
881
  return;
716
882
  }
@@ -721,7 +887,7 @@ var Expressive = (() => {
721
887
  } else if (focusedElement instanceof HTMLElement) {
722
888
  focusedElement.click();
723
889
  }
724
- } else if (Utils.keys.ESC.includes(e.key) && this.isOpen) {
890
+ } else if (e.key === Utils.keys.ESC && this.isOpen) {
725
891
  e.preventDefault();
726
892
  const openSub = this.menuEl.querySelector("li.open");
727
893
  if (openSub) {
@@ -735,11 +901,12 @@ var Expressive = (() => {
735
901
  const keyText = e.key.toLowerCase();
736
902
  const isLetter = /[a-zA-Z0-9-_]/.test(keyText);
737
903
  const specialKeys = [
738
- ...Utils.keys.ARROW_DOWN,
739
- ...Utils.keys.ARROW_UP,
740
- ...Utils.keys.ENTER,
741
- ...Utils.keys.ESC,
742
- ...Utils.keys.TAB
904
+ Utils.keys.ARROW_DOWN,
905
+ Utils.keys.ARROW_UP,
906
+ Utils.keys.ENTER,
907
+ Utils.keys.ESC,
908
+ Utils.keys.TAB,
909
+ SPACE
743
910
  ];
744
911
  if (isLetter && !specialKeys.includes(e.key)) {
745
912
  this.filterQuery.push(keyText);
@@ -810,7 +977,7 @@ var Expressive = (() => {
810
977
  this.el["Expressive_Menu"] = this;
811
978
  _Menu._menus.push(this);
812
979
  this.id = Utils.getIdFromTrigger(el);
813
- this.menuEl = document.getElementById(this.id);
980
+ this.menuEl = Utils.getElementById(el, this.id);
814
981
  this.options = {
815
982
  ..._Menu.defaults,
816
983
  ...options
@@ -821,13 +988,17 @@ var Expressive = (() => {
821
988
  this.focusedIndex = -1;
822
989
  this.filterQuery = [];
823
990
  this.el.ariaExpanded = "false";
991
+ if (!this.el.hasAttribute("aria-haspopup")) this.el.setAttribute("aria-haspopup", "menu");
992
+ if (this.id && !this.el.hasAttribute("aria-controls"))
993
+ this.el.setAttribute("aria-controls", this.id);
824
994
  this._moveMenuToElement();
995
+ this._setupAccessibility();
825
996
  this._makeMenuFocusable();
826
997
  this._setupSubmenus();
827
998
  this._setupEventHandlers();
828
999
  }
829
1000
  static get defaults() {
830
- return _defaults2;
1001
+ return _defaults3;
831
1002
  }
832
1003
  /**
833
1004
  * Initializes instances of Menu.
@@ -877,7 +1048,9 @@ var Expressive = (() => {
877
1048
  }
878
1049
  _setupTemporaryEventHandlers() {
879
1050
  document.body.addEventListener("click", this._handleDocumentClick);
880
- document.body.addEventListener("touchmove", this._handleDocumentTouchmove, { passive: true });
1051
+ document.body.addEventListener("touchmove", this._handleDocumentTouchmove, {
1052
+ passive: true
1053
+ });
881
1054
  this.menuEl?.addEventListener("keydown", this._handleMenuKeydown);
882
1055
  window.addEventListener("resize", this._handleWindowResize);
883
1056
  }
@@ -887,6 +1060,33 @@ var Expressive = (() => {
887
1060
  this.menuEl?.removeEventListener("keydown", this._handleMenuKeydown);
888
1061
  window.removeEventListener("resize", this._handleWindowResize);
889
1062
  }
1063
+ _setupAccessibility() {
1064
+ if (!this.menuEl) return;
1065
+ if (!this.menuEl.hasAttribute("role")) this.menuEl.setAttribute("role", "menu");
1066
+ const menus = [this.menuEl, ...this.menuEl.querySelectorAll("menu")];
1067
+ menus.forEach((menu) => {
1068
+ if (!menu.hasAttribute("role")) menu.setAttribute("role", "menu");
1069
+ if (menu.getAttribute("role") !== "menu") return;
1070
+ menu.querySelectorAll(":scope > li").forEach((li) => {
1071
+ if (li.classList.contains("divider")) {
1072
+ if (!li.hasAttribute("role")) li.setAttribute("role", "separator");
1073
+ return;
1074
+ }
1075
+ if (li.classList.contains("gap")) {
1076
+ li.setAttribute("aria-hidden", "true");
1077
+ return;
1078
+ }
1079
+ if (li.classList.contains("label") || li.classList.contains("optgroup")) {
1080
+ if (!li.hasAttribute("role")) li.setAttribute("role", "presentation");
1081
+ return;
1082
+ }
1083
+ if (!li.hasAttribute("role")) li.setAttribute("role", "menuitem");
1084
+ if (li.classList.contains("disabled") && !li.hasAttribute("aria-disabled")) {
1085
+ li.setAttribute("aria-disabled", "true");
1086
+ }
1087
+ });
1088
+ });
1089
+ }
890
1090
  _isSubmenuTriggerClick(target) {
891
1091
  if (!this.menuEl) return false;
892
1092
  const li = target.closest("li");
@@ -973,7 +1173,7 @@ var Expressive = (() => {
973
1173
  _makeMenuFocusable() {
974
1174
  if (!this.menuEl) return;
975
1175
  this.menuEl.popover = "";
976
- this.menuEl.tabIndex = 0;
1176
+ this.menuEl.tabIndex = -1;
977
1177
  this.menuEl.querySelectorAll(":scope li, :scope > hr").forEach((el) => {
978
1178
  if (el instanceof HTMLHRElement || el.classList.contains("divider") || el.classList.contains("gap") || el.classList.contains("label") || el.classList.contains("optgroup")) {
979
1179
  el.setAttribute("tabindex", "-1");
@@ -982,6 +1182,15 @@ var Expressive = (() => {
982
1182
  if (!el.getAttribute("tabindex")) el.setAttribute("tabindex", "0");
983
1183
  });
984
1184
  }
1185
+ _focusInitialItem() {
1186
+ if (!this.menuEl || !this.options.autoFocus) return;
1187
+ const first = Array.from(this.menuEl.children).find(
1188
+ (child) => child instanceof HTMLElement && child.tabIndex !== -1
1189
+ );
1190
+ if (!first) return;
1191
+ this.focusedIndex = Array.from(this.menuEl.children).indexOf(first);
1192
+ first.focus({ preventScroll: true });
1193
+ }
985
1194
  _focusFocusedItem() {
986
1195
  if (this.focusedIndex >= 0 && this.focusedIndex < this.menuEl.children.length && this.options.autoFocus) {
987
1196
  this.menuEl.children[this.focusedIndex].focus({
@@ -1076,7 +1285,7 @@ var Expressive = (() => {
1076
1285
  this.menuEl.style.transform = "scale(1)";
1077
1286
  }, 1);
1078
1287
  setTimeout(() => {
1079
- if (this.options.autoFocus) this.menuEl.focus();
1288
+ this._focusInitialItem();
1080
1289
  if (typeof this.options.onOpenEnd === "function") this.options.onOpenEnd.call(this, this.el);
1081
1290
  }, duration);
1082
1291
  }
@@ -1093,11 +1302,11 @@ var Expressive = (() => {
1093
1302
  }
1094
1303
  _getClosestAncestor(el, condition) {
1095
1304
  let ancestor = el.parentNode;
1096
- while (ancestor !== null && ancestor !== document) {
1097
- if (condition(ancestor)) {
1305
+ while (ancestor !== null && ancestor !== void 0 && ancestor !== document) {
1306
+ if (ancestor.nodeType === Node.ELEMENT_NODE && condition(ancestor)) {
1098
1307
  return ancestor;
1099
1308
  }
1100
- ancestor = ancestor.parentElement;
1309
+ ancestor = ancestor.parentNode ?? ancestor.host;
1101
1310
  }
1102
1311
  return null;
1103
1312
  }
@@ -1109,12 +1318,14 @@ var Expressive = (() => {
1109
1318
  }
1110
1319
  );
1111
1320
  if (!closestOverflowParent) {
1112
- closestOverflowParent = !!this.menuEl.offsetParent ? this.menuEl.offsetParent : this.menuEl.parentNode;
1321
+ const parent = !!this.menuEl.offsetParent ? this.menuEl.offsetParent : this.menuEl.parentNode;
1322
+ closestOverflowParent = parent.host ?? parent;
1113
1323
  }
1114
1324
  if (getComputedStyle(closestOverflowParent).position === "static")
1115
1325
  closestOverflowParent.style.position = "relative";
1116
1326
  const natural = this.menuEl.getBoundingClientRect().width;
1117
- const idealWidth = this.options.constrainWidth ? this.el.getBoundingClientRect().width : Math.min(280, Math.max(112, natural));
1327
+ const triggerWidth = this.el.getBoundingClientRect().width;
1328
+ const idealWidth = this.options.constrainWidth && triggerWidth >= MIN_SURFACE_WIDTH ? triggerWidth : Math.min(MAX_SURFACE_WIDTH, Math.max(MIN_SURFACE_WIDTH, natural));
1118
1329
  this.menuEl.style.width = idealWidth + "px";
1119
1330
  const positionInfo = this._getMenuPosition(closestOverflowParent);
1120
1331
  this.menuEl.style.left = positionInfo.x + "px";
@@ -1128,7 +1339,7 @@ var Expressive = (() => {
1128
1339
  var Menu = _Menu;
1129
1340
 
1130
1341
  // src/ts/components/autocomplete.ts
1131
- var _defaults3 = {
1342
+ var _defaults4 = {
1132
1343
  data: [],
1133
1344
  // Autocomplete data set
1134
1345
  onAutocomplete: null,
@@ -1158,7 +1369,7 @@ var Expressive = (() => {
1158
1369
  constructor(el, options) {
1159
1370
  super(el, options, _Autocomplete);
1160
1371
  this._handleInputBlur = () => {
1161
- if (!this._mousedown) {
1372
+ if (!this._pointerDown) {
1162
1373
  this.close();
1163
1374
  this._resetAutocomplete();
1164
1375
  }
@@ -1167,9 +1378,9 @@ var Expressive = (() => {
1167
1378
  if (e.type === "keyup") _Autocomplete._keydown = false;
1168
1379
  this.count = 0;
1169
1380
  const actualValue = this.el.value.toLocaleLowerCase();
1170
- if (Utils.keys.ENTER.includes(e.key) || Utils.keys.ARROW_UP.includes(e.key) || Utils.keys.ARROW_DOWN.includes(e.key))
1381
+ if (e.key === Utils.keys.ENTER || e.key === Utils.keys.ARROW_UP || e.key === Utils.keys.ARROW_DOWN)
1171
1382
  return;
1172
- if (this.oldVal !== actualValue && Utils.tabPressed) {
1383
+ if (this.oldVal !== actualValue && this.el.matches(":focus-visible")) {
1173
1384
  this.open();
1174
1385
  }
1175
1386
  this._inputChangeDetection(actualValue);
@@ -1193,7 +1404,7 @@ var Expressive = (() => {
1193
1404
  this._handleInputKeydown = (e) => {
1194
1405
  _Autocomplete._keydown = true;
1195
1406
  const numItems = this.container.querySelectorAll("li").length;
1196
- if (Utils.keys.ENTER.includes(e.key) && this.activeIndex >= 0) {
1407
+ if (e.key === Utils.keys.ENTER && this.activeIndex >= 0) {
1197
1408
  const liElement = this.container.querySelectorAll("li")[this.activeIndex];
1198
1409
  if (liElement) {
1199
1410
  this.selectOption(liElement.getAttribute("data-id"));
@@ -1201,15 +1412,14 @@ var Expressive = (() => {
1201
1412
  }
1202
1413
  return;
1203
1414
  }
1204
- if (Utils.keys.ARROW_UP.includes(e.key) || Utils.keys.ARROW_DOWN.includes(e.key)) {
1415
+ if (e.key === Utils.keys.ARROW_UP || e.key === Utils.keys.ARROW_DOWN) {
1205
1416
  e.preventDefault();
1206
- if (Utils.keys.ARROW_UP.includes(e.key) && this.activeIndex > 0) this.activeIndex--;
1207
- if (Utils.keys.ARROW_DOWN.includes(e.key) && this.activeIndex < numItems - 1)
1417
+ if (e.key === Utils.keys.ARROW_UP && this.activeIndex > 0) this.activeIndex--;
1418
+ if (e.key === Utils.keys.ARROW_DOWN && this.activeIndex < numItems - 1)
1208
1419
  this.activeIndex++;
1209
- this.$active?.classList.remove("active");
1420
+ this._setActive(null);
1210
1421
  if (this.activeIndex >= 0) {
1211
- this.$active = this.container.querySelectorAll("li")[this.activeIndex];
1212
- this.$active?.classList.add("active");
1422
+ this._setActive(this.container.querySelectorAll("li")[this.activeIndex]);
1213
1423
  this.container.children[this.activeIndex].scrollIntoView({
1214
1424
  behavior: "smooth",
1215
1425
  block: "nearest",
@@ -1221,11 +1431,17 @@ var Expressive = (() => {
1221
1431
  this._handleInputClick = () => {
1222
1432
  this.open();
1223
1433
  };
1224
- this._handleContainerMousedownAndTouchstart = () => {
1225
- this._mousedown = true;
1434
+ // The flag says "a press is in flight inside the option list", so that a blur
1435
+ // caused by that press does not close the list out from under it. Without the
1436
+ // cancel half, a cancelled gesture left the flag set and the next blur could
1437
+ // not close the list at all.
1438
+ this._handleContainerPointerDown = (e) => {
1439
+ if (!e.isPrimary) return;
1440
+ if (e.pointerType === "mouse" && e.button !== 0) return;
1441
+ this._pointerDown = true;
1226
1442
  };
1227
- this._handleContainerMouseupAndTouchend = () => {
1228
- this._mousedown = false;
1443
+ this._handleContainerPointerUp = () => {
1444
+ this._pointerDown = false;
1229
1445
  };
1230
1446
  /**
1231
1447
  * Show autocomplete.
@@ -1238,7 +1454,9 @@ var Expressive = (() => {
1238
1454
  this._renderMenu();
1239
1455
  }
1240
1456
  if (!this.menu.isOpen) {
1241
- setTimeout(() => {
1457
+ if (this._openTimer !== void 0) clearTimeout(this._openTimer);
1458
+ this._openTimer = setTimeout(() => {
1459
+ this._openTimer = void 0;
1242
1460
  this.menu.open();
1243
1461
  }, 0);
1244
1462
  } else this.menu.recalculateDimensions();
@@ -1261,12 +1479,32 @@ var Expressive = (() => {
1261
1479
  this.selectedValues = this.selectedValues || this.options.selected.map((value) => ({ id: value })) || [];
1262
1480
  this.menuItems = this.options.data || [];
1263
1481
  this.$active = null;
1264
- this._mousedown = false;
1482
+ this._pointerDown = false;
1265
1483
  this._setupMenu();
1266
1484
  this._setupEventHandlers();
1267
1485
  }
1486
+ /**
1487
+ * The active entry, for both the eye and the screen reader. The class alone
1488
+ * was invisible to assistive technology; aria-activedescendant on the input
1489
+ * is what reports the move.
1490
+ *
1491
+ * Deliberately does not touch aria-selected. Active and selected are two
1492
+ * different things - in a multi-select the checkboxes track real choices,
1493
+ * and folding the highlight into aria-selected would announce a committed
1494
+ * choice as unselected the moment the user arrowed past it.
1495
+ */
1496
+ _setActive(item) {
1497
+ this.$active?.classList.remove("active");
1498
+ this.$active = item;
1499
+ if (item) {
1500
+ item.classList.add("active");
1501
+ this.el.setAttribute("aria-activedescendant", item.id);
1502
+ } else {
1503
+ this.el.removeAttribute("aria-activedescendant");
1504
+ }
1505
+ }
1268
1506
  static get defaults() {
1269
- return _defaults3;
1507
+ return _defaults4;
1270
1508
  }
1271
1509
  /**
1272
1510
  * Initializes instances of Autocomplete.
@@ -1292,6 +1530,10 @@ var Expressive = (() => {
1292
1530
  return _Autocomplete._loadingTemplate.cloneNode(true);
1293
1531
  }
1294
1532
  destroy() {
1533
+ if (this._openTimer !== void 0) {
1534
+ clearTimeout(this._openTimer);
1535
+ this._openTimer = void 0;
1536
+ }
1295
1537
  this._removeEventHandlers();
1296
1538
  this._removeMenu();
1297
1539
  this.el["Expressive_Autocomplete"] = void 0;
@@ -1302,12 +1544,9 @@ var Expressive = (() => {
1302
1544
  this.el.addEventListener("focus", this._handleInputFocus);
1303
1545
  this.el.addEventListener("keydown", this._handleInputKeydown);
1304
1546
  this.el.addEventListener("click", this._handleInputClick);
1305
- this.container.addEventListener("mousedown", this._handleContainerMousedownAndTouchstart);
1306
- this.container.addEventListener("mouseup", this._handleContainerMouseupAndTouchend);
1307
- if (typeof window.ontouchstart !== "undefined") {
1308
- this.container.addEventListener("touchstart", this._handleContainerMousedownAndTouchstart);
1309
- this.container.addEventListener("touchend", this._handleContainerMouseupAndTouchend);
1310
- }
1547
+ this.container.addEventListener("pointerdown", this._handleContainerPointerDown);
1548
+ this.container.addEventListener("pointerup", this._handleContainerPointerUp);
1549
+ this.container.addEventListener("pointercancel", this._handleContainerPointerUp);
1311
1550
  }
1312
1551
  _removeEventHandlers() {
1313
1552
  this.el.removeEventListener("blur", this._handleInputBlur);
@@ -1315,12 +1554,9 @@ var Expressive = (() => {
1315
1554
  this.el.removeEventListener("focus", this._handleInputFocus);
1316
1555
  this.el.removeEventListener("keydown", this._handleInputKeydown);
1317
1556
  this.el.removeEventListener("click", this._handleInputClick);
1318
- this.container.removeEventListener("mousedown", this._handleContainerMousedownAndTouchstart);
1319
- this.container.removeEventListener("mouseup", this._handleContainerMouseupAndTouchend);
1320
- if (typeof window.ontouchstart !== "undefined") {
1321
- this.container.removeEventListener("touchstart", this._handleContainerMousedownAndTouchstart);
1322
- this.container.removeEventListener("touchend", this._handleContainerMouseupAndTouchend);
1323
- }
1557
+ this.container.removeEventListener("pointerdown", this._handleContainerPointerDown);
1558
+ this.container.removeEventListener("pointerup", this._handleContainerPointerUp);
1559
+ this.container.removeEventListener("pointercancel", this._handleContainerPointerUp);
1324
1560
  }
1325
1561
  _setupMenu() {
1326
1562
  this.container = document.createElement("menu");
@@ -1328,6 +1564,10 @@ var Expressive = (() => {
1328
1564
  this.container.id = `autocomplete-options-${Utils.guid()}`;
1329
1565
  this.container.classList.add("autocomplete-content");
1330
1566
  this.container.ariaExpanded = "true";
1567
+ this.container.setAttribute("role", "listbox");
1568
+ this.el.setAttribute("role", "combobox");
1569
+ this.el.setAttribute("aria-autocomplete", "list");
1570
+ this.el.setAttribute("aria-controls", this.container.id);
1331
1571
  this.el.setAttribute("data-target", this.container.id);
1332
1572
  this.menuItems.forEach((menuItem) => {
1333
1573
  const itemElement = this._createMenuItem(menuItem);
@@ -1366,14 +1606,14 @@ var Expressive = (() => {
1366
1606
  }
1367
1607
  _resetCurrentElementPosition() {
1368
1608
  this.activeIndex = -1;
1369
- this.$active?.classList.remove("active");
1609
+ this._setActive(null);
1370
1610
  }
1371
1611
  _resetAutocomplete() {
1372
1612
  this.container.replaceChildren();
1373
1613
  this._resetCurrentElementPosition();
1374
1614
  this.oldVal = null;
1375
1615
  this.isOpen = false;
1376
- this._mousedown = false;
1616
+ this._pointerDown = false;
1377
1617
  }
1378
1618
  _highlightPartialText(input, label) {
1379
1619
  const start = label.toLocaleLowerCase().indexOf("" + input.toLocaleLowerCase());
@@ -1385,6 +1625,10 @@ var Expressive = (() => {
1385
1625
  }
1386
1626
  _createMenuItem(entry) {
1387
1627
  const item = document.createElement("li");
1628
+ item.id = `autocomplete-option-${Utils.guid()}`;
1629
+ item.setAttribute("role", "option");
1630
+ const isSelected = this.selectedValues.some((sel) => sel.id === entry.id);
1631
+ item.setAttribute("aria-selected", isSelected ? "true" : "false");
1388
1632
  item.setAttribute("data-id", entry.id);
1389
1633
  item.setAttribute(
1390
1634
  "style",
@@ -1397,7 +1641,7 @@ var Expressive = (() => {
1397
1641
  selection.style.textAlign = "center";
1398
1642
  const checkbox = document.createElement("input");
1399
1643
  checkbox.type = "checkbox";
1400
- checkbox.checked = this.selectedValues.some((sel) => sel.id === entry.id);
1644
+ checkbox.checked = isSelected;
1401
1645
  const spacer = document.createElement("span");
1402
1646
  spacer.style.paddingLeft = "21px";
1403
1647
  selection.append(checkbox, spacer);
@@ -1543,7 +1787,7 @@ var Expressive = (() => {
1543
1787
  var Autocomplete = _Autocomplete;
1544
1788
 
1545
1789
  // src/ts/components/chips.ts
1546
- var _defaults4 = {
1790
+ var _defaults5 = {
1547
1791
  data: [],
1548
1792
  placeholder: "",
1549
1793
  secondaryPlaceholder: "",
@@ -1552,6 +1796,7 @@ var Expressive = (() => {
1552
1796
  autocompleteOnly: false,
1553
1797
  limit: Infinity,
1554
1798
  allowUserInput: false,
1799
+ i18n: { remove: "Remove" },
1555
1800
  onChipAdd: null,
1556
1801
  onChipSelect: null,
1557
1802
  onChipDelete: null
@@ -1561,7 +1806,7 @@ var Expressive = (() => {
1561
1806
  super(el, options, _Chips);
1562
1807
  this._handleChipClick = (e) => {
1563
1808
  const _chip = e.target.closest(".chip");
1564
- const clickedClose = e.target.classList.contains("close");
1809
+ const clickedClose = !!e.target.closest(".close");
1565
1810
  if (_chip) {
1566
1811
  const index = this._chips.indexOf(_chip);
1567
1812
  if (index < 0) return;
@@ -1583,7 +1828,7 @@ var Expressive = (() => {
1583
1828
  };
1584
1829
  this._handleInputKeydown = (e) => {
1585
1830
  _Chips._keydown = true;
1586
- if (Utils.keys.ENTER.includes(e.key)) {
1831
+ if (e.key === Utils.keys.ENTER) {
1587
1832
  if (this.hasAutocomplete && this.autocomplete && this.autocomplete.isOpen) {
1588
1833
  return;
1589
1834
  }
@@ -1592,7 +1837,7 @@ var Expressive = (() => {
1592
1837
  this.addChip({ id: this._input.value });
1593
1838
  }
1594
1839
  this._input.value = "";
1595
- } else if ((Utils.keys.BACKSPACE.includes(e.key) || Utils.keys.ARROW_LEFT.includes(e.key)) && this._input.value === "" && this.chipsData.length) {
1840
+ } else if ((e.key === Utils.keys.BACKSPACE || e.key === Utils.keys.ARROW_LEFT) && this._input.value === "" && this.chipsData.length) {
1596
1841
  e.preventDefault();
1597
1842
  this.selectChip(this.chipsData.length - 1);
1598
1843
  }
@@ -1602,6 +1847,7 @@ var Expressive = (() => {
1602
1847
  ..._Chips.defaults,
1603
1848
  ...options
1604
1849
  };
1850
+ if (options?.i18n) this.options.i18n = { ..._Chips.defaults.i18n, ...options.i18n };
1605
1851
  this.el.classList.add("chips");
1606
1852
  this.chipsData = [];
1607
1853
  this._chips = [];
@@ -1617,8 +1863,22 @@ var Expressive = (() => {
1617
1863
  }
1618
1864
  this._setupLabel();
1619
1865
  }
1866
+ /**
1867
+ * Selection is drawn by a class now, not by `:focus`, so it does not clear
1868
+ * itself when focus moves - every path that changes the selection has to go
1869
+ * through here or the class and the field drift apart.
1870
+ *
1871
+ * Deliberately no `aria-selected`: the chip is not an option in a listbox,
1872
+ * and putting the attribute on an element with no such role is the same
1873
+ * defect the semantics standard exists to remove.
1874
+ */
1875
+ _setSelected(chip) {
1876
+ this._selectedChip?.classList.remove("selected");
1877
+ this._selectedChip = chip;
1878
+ chip?.classList.add("selected");
1879
+ }
1620
1880
  static get defaults() {
1621
- return _defaults4;
1881
+ return _defaults5;
1622
1882
  }
1623
1883
  /**
1624
1884
  * Initializes instances of Chips.
@@ -1668,28 +1928,30 @@ var Expressive = (() => {
1668
1928
  if (tag === "INPUT" || tag === "TEXTAREA" || !chipsKeydown) return;
1669
1929
  const currChips = chips["Expressive_Chips"];
1670
1930
  if (!currChips) return;
1671
- if (Utils.keys.BACKSPACE.includes(e.key) || Utils.keys.DELETE.includes(e.key)) {
1931
+ if (e.key === Utils.keys.BACKSPACE || e.key === Utils.keys.DELETE) {
1672
1932
  e.preventDefault();
1673
1933
  let selectIndex = currChips.chipsData.length;
1674
1934
  if (currChips._selectedChip) {
1675
1935
  const index = currChips._chips.indexOf(currChips._selectedChip);
1676
1936
  currChips.deleteChip(index);
1677
- currChips._selectedChip = null;
1937
+ currChips._setSelected(null);
1678
1938
  selectIndex = Math.max(index - 1, 0);
1679
1939
  }
1680
1940
  if (currChips.chipsData.length) currChips.selectChip(selectIndex);
1681
1941
  else currChips._input.focus();
1682
- } else if (Utils.keys.ARROW_LEFT.includes(e.key)) {
1942
+ } else if (e.key === Utils.keys.ARROW_LEFT) {
1683
1943
  if (currChips._selectedChip) {
1684
1944
  const selectIndex = currChips._chips.indexOf(currChips._selectedChip) - 1;
1685
1945
  if (selectIndex < 0) return;
1686
1946
  currChips.selectChip(selectIndex);
1687
1947
  }
1688
- } else if (Utils.keys.ARROW_RIGHT.includes(e.key)) {
1948
+ } else if (e.key === Utils.keys.ARROW_RIGHT) {
1689
1949
  if (currChips._selectedChip) {
1690
1950
  const selectIndex = currChips._chips.indexOf(currChips._selectedChip) + 1;
1691
- if (selectIndex >= currChips.chipsData.length) currChips._input.focus();
1692
- else currChips.selectChip(selectIndex);
1951
+ if (selectIndex >= currChips.chipsData.length) {
1952
+ currChips._setSelected(null);
1953
+ currChips._input.focus();
1954
+ } else currChips.selectChip(selectIndex);
1693
1955
  }
1694
1956
  }
1695
1957
  }
@@ -1700,25 +1962,31 @@ var Expressive = (() => {
1700
1962
  if (!_Chips._keydown && document.hidden) {
1701
1963
  const chips = e.target.closest(".chips");
1702
1964
  const currChips = chips?.["Expressive_Chips"];
1703
- if (currChips) currChips._selectedChip = null;
1965
+ if (currChips) currChips._setSelected(null);
1704
1966
  }
1705
1967
  }
1706
1968
  _renderChip(chip) {
1707
1969
  if (!chip.id) return;
1708
- const renderedChip = document.createElement("div");
1970
+ const label = chip.text || chip.id;
1971
+ const renderedChip = document.createElement("span");
1709
1972
  renderedChip.classList.add("chip");
1710
- renderedChip.innerText = chip.text || chip.id;
1973
+ renderedChip.innerText = label;
1711
1974
  if (chip.image) {
1712
1975
  const img = document.createElement("img");
1713
1976
  img.setAttribute("src", chip.image);
1714
1977
  renderedChip.insertBefore(img, renderedChip.firstChild);
1715
1978
  }
1716
1979
  if (this.options.allowUserInput) {
1717
- renderedChip.setAttribute("tabindex", "0");
1718
- const closeIcon = document.createElement("i");
1719
- closeIcon.classList.add(this.options.closeIconClass, "close");
1980
+ const closeButton = document.createElement("button");
1981
+ closeButton.type = "button";
1982
+ closeButton.classList.add("close");
1983
+ closeButton.setAttribute("aria-label", `${this.options.i18n.remove} ${label}`);
1984
+ const closeIcon = document.createElement("span");
1985
+ closeIcon.classList.add(this.options.closeIconClass);
1986
+ closeIcon.setAttribute("aria-hidden", "true");
1720
1987
  closeIcon.innerText = "close";
1721
- renderedChip.appendChild(closeIcon);
1988
+ closeButton.appendChild(closeIcon);
1989
+ renderedChip.appendChild(closeButton);
1722
1990
  }
1723
1991
  return renderedChip;
1724
1992
  }
@@ -1793,6 +2061,7 @@ var Expressive = (() => {
1793
2061
  deleteChip(chipIndex) {
1794
2062
  const chip = this._chips[chipIndex];
1795
2063
  if (!chip) return;
2064
+ if (this._selectedChip === chip) this._setSelected(null);
1796
2065
  chip.remove();
1797
2066
  this._chips.splice(chipIndex, 1);
1798
2067
  this.chipsData.splice(chipIndex, 1);
@@ -1808,8 +2077,8 @@ var Expressive = (() => {
1808
2077
  selectChip(chipIndex) {
1809
2078
  const chip = this._chips[chipIndex];
1810
2079
  if (!chip) return;
1811
- this._selectedChip = chip;
1812
- chip.focus();
2080
+ this._setSelected(chip);
2081
+ chip.querySelector(".close")?.focus();
1813
2082
  if (typeof this.options.onChipSelect === "function") {
1814
2083
  this.options.onChipSelect(this.el, chip);
1815
2084
  }
@@ -1820,7 +2089,7 @@ var Expressive = (() => {
1820
2089
  chips.forEach((el) => {
1821
2090
  el.addEventListener("click", (e) => {
1822
2091
  if (el["Expressive_Chips"]) return;
1823
- if (e.target.classList.contains("close")) {
2092
+ if (e.target.closest(".close")) {
1824
2093
  const chip = e.target.closest(".chip");
1825
2094
  if (chip) chip.remove();
1826
2095
  }
@@ -1832,97 +2101,93 @@ var Expressive = (() => {
1832
2101
  _Chips._keydown = false;
1833
2102
  var Chips = _Chips;
1834
2103
 
1835
- // src/ts/components/range.ts
1836
- var _defaults5 = {};
1837
- var Range = class _Range extends Component {
2104
+ // src/ts/components/slider.ts
2105
+ var _defaults6 = {};
2106
+ var Slider = class _Slider extends Component {
1838
2107
  constructor(el, options) {
1839
- super(el, options, _Range);
2108
+ super(el, options, _Slider);
1840
2109
  this._handleRangeChange = () => {
1841
2110
  this._clampDual();
1842
2111
  this._sync();
1843
2112
  this.thumb.classList.add("active");
1844
2113
  };
1845
- this._handleRangeMousedownTouchstart = (e) => {
1846
- this._mousedown = true;
2114
+ this._handleRangePointerDown = (e) => {
2115
+ if (!e.isPrimary) return;
2116
+ if (e.pointerType === "mouse" && e.button !== 0) return;
2117
+ this._pointerDown = true;
1847
2118
  this.el.classList.add("active");
1848
2119
  this._clampDual();
1849
2120
  this._sync();
1850
- if (e.type !== "input") {
1851
- this.thumb.classList.add("active");
1852
- }
2121
+ this.thumb.classList.add("active");
1853
2122
  };
1854
- this._handleRangeInputMousemoveTouchmove = () => {
1855
- if (this._mousedown) {
2123
+ this._handleRangeInputOrMove = () => {
2124
+ if (this._pointerDown) {
1856
2125
  this._clampDual();
1857
2126
  this._sync();
1858
2127
  this.thumb.classList.add("active");
1859
2128
  }
1860
2129
  };
1861
- this._handleRangeMouseupTouchend = () => {
1862
- this._mousedown = false;
2130
+ this._handleRangePointerUp = (e) => {
2131
+ if (!e.isPrimary) return;
2132
+ this._pointerDown = false;
1863
2133
  this.el.classList.remove("active");
1864
2134
  };
1865
- this._handleRangeBlurMouseoutTouchleave = () => {
1866
- if (!this._mousedown) {
2135
+ this._handleRangeRelease = () => {
2136
+ if (!this._pointerDown) {
1867
2137
  this.thumb.classList.remove("active");
1868
2138
  }
1869
2139
  };
1870
- this.el["Expressive_Range"] = this;
2140
+ this.el["Expressive_Slider"] = this;
1871
2141
  this.options = {
1872
- ..._Range.defaults,
2142
+ ..._Slider.defaults,
1873
2143
  ...options
1874
2144
  };
1875
- this._mousedown = false;
2145
+ this._pointerDown = false;
1876
2146
  this._setupThumb();
1877
2147
  this._sync();
1878
2148
  this._setupEventHandlers();
1879
2149
  }
1880
2150
  static get defaults() {
1881
- return _defaults5;
2151
+ return _defaults6;
1882
2152
  }
1883
2153
  /**
1884
- * Initializes instances of Range.
2154
+ * Initializes instances of Slider.
1885
2155
  * @param els HTML elements.
1886
2156
  * @param options Component options.
1887
2157
  */
1888
2158
  static init(els, options = {}) {
1889
- return super.init(els, options, _Range);
2159
+ return super.init(els, options, _Slider);
1890
2160
  }
1891
2161
  static getInstance(el) {
1892
- return el["Expressive_Range"];
2162
+ return el["Expressive_Slider"];
1893
2163
  }
1894
2164
  destroy() {
1895
2165
  this._removeEventHandlers();
1896
2166
  this._removeThumb();
1897
- this.el["Expressive_Range"] = void 0;
2167
+ this.el["Expressive_Slider"] = void 0;
1898
2168
  }
2169
+ // Eleven listeners used to cover four stages, because each one was written
2170
+ // twice - once for mouse, once for touch. One of the touch spellings,
2171
+ // `touchleave`, was never implemented by any browser at all.
1899
2172
  _setupEventHandlers() {
1900
2173
  this.el.addEventListener("change", this._handleRangeChange);
1901
- this.el.addEventListener("mousedown", this._handleRangeMousedownTouchstart);
1902
- this.el.addEventListener("touchstart", this._handleRangeMousedownTouchstart);
1903
- this.el.addEventListener("input", this._handleRangeInputMousemoveTouchmove);
1904
- this.el.addEventListener("mousemove", this._handleRangeInputMousemoveTouchmove);
1905
- this.el.addEventListener("touchmove", this._handleRangeInputMousemoveTouchmove, {
1906
- passive: true
1907
- });
1908
- this.el.addEventListener("mouseup", this._handleRangeMouseupTouchend);
1909
- this.el.addEventListener("touchend", this._handleRangeMouseupTouchend);
1910
- this.el.addEventListener("blur", this._handleRangeBlurMouseoutTouchleave);
1911
- this.el.addEventListener("mouseout", this._handleRangeBlurMouseoutTouchleave);
1912
- this.el.addEventListener("touchleave", this._handleRangeBlurMouseoutTouchleave);
2174
+ this.el.addEventListener("pointerdown", this._handleRangePointerDown);
2175
+ this.el.addEventListener("input", this._handleRangeInputOrMove);
2176
+ this.el.addEventListener("pointermove", this._handleRangeInputOrMove, { passive: true });
2177
+ this.el.addEventListener("pointerup", this._handleRangePointerUp);
2178
+ this.el.addEventListener("pointercancel", this._handleRangePointerUp);
2179
+ this.el.addEventListener("blur", this._handleRangeRelease);
2180
+ this.el.addEventListener("mouseout", this._handleRangeRelease);
1913
2181
  }
1914
2182
  _removeEventHandlers() {
1915
2183
  this.el.removeEventListener("change", this._handleRangeChange);
1916
- this.el.removeEventListener("mousedown", this._handleRangeMousedownTouchstart);
1917
- this.el.removeEventListener("touchstart", this._handleRangeMousedownTouchstart);
1918
- this.el.removeEventListener("input", this._handleRangeInputMousemoveTouchmove);
1919
- this.el.removeEventListener("mousemove", this._handleRangeInputMousemoveTouchmove);
1920
- this.el.removeEventListener("touchmove", this._handleRangeInputMousemoveTouchmove);
1921
- this.el.removeEventListener("mouseup", this._handleRangeMouseupTouchend);
1922
- this.el.removeEventListener("touchend", this._handleRangeMouseupTouchend);
1923
- this.el.removeEventListener("blur", this._handleRangeBlurMouseoutTouchleave);
1924
- this.el.removeEventListener("mouseout", this._handleRangeBlurMouseoutTouchleave);
1925
- this.el.removeEventListener("touchleave", this._handleRangeBlurMouseoutTouchleave);
2184
+ this.el.removeEventListener("pointerdown", this._handleRangePointerDown);
2185
+ this.el.removeEventListener("input", this._handleRangeInputOrMove);
2186
+ this.el.removeEventListener("pointermove", this._handleRangeInputOrMove);
2187
+ this.el.removeEventListener("pointerup", this._handleRangePointerUp);
2188
+ this.el.removeEventListener("pointercancel", this._handleRangePointerUp);
2189
+ this.el.removeEventListener("blur", this._handleRangeRelease);
2190
+ this.el.removeEventListener("mouseout", this._handleRangeRelease);
1926
2191
  }
1927
2192
  _setupThumb() {
1928
2193
  this.thumb = document.createElement("span");
@@ -1939,7 +2204,7 @@ var Expressive = (() => {
1939
2204
  return !!this.el.closest(".vertical");
1940
2205
  }
1941
2206
  _host() {
1942
- return this.el.closest(".range, .range-field, label");
2207
+ return this.el.closest(".slider, .range, .range-field, label");
1943
2208
  }
1944
2209
  _clampDual() {
1945
2210
  const host = this._host();
@@ -1994,7 +2259,7 @@ var Expressive = (() => {
1994
2259
  */
1995
2260
  static Init() {
1996
2261
  Utils.onDocumentReady(() => {
1997
- _Range.init(document.querySelectorAll("input[type=range]"), {});
2262
+ _Slider.init(document.querySelectorAll("input[type=range]"), {});
1998
2263
  });
1999
2264
  }
2000
2265
  };
@@ -2067,169 +2332,144 @@ var Expressive = (() => {
2067
2332
  _Dialogs._down = null;
2068
2333
  var Dialogs = _Dialogs;
2069
2334
 
2070
- // src/ts/behaviors/bottomSheets.ts
2071
- var _BottomSheets = class _BottomSheets {
2072
- static Init() {
2073
- Utils.onDocumentReady(() => {
2074
- document.addEventListener("pointerdown", _BottomSheets._onDown, { passive: true });
2075
- document.addEventListener("pointermove", _BottomSheets._onMove, { passive: true });
2076
- document.addEventListener("pointerup", _BottomSheets._onUp, { passive: true });
2077
- document.addEventListener("pointercancel", _BottomSheets._onCancel, { passive: true });
2078
- });
2079
- }
2080
- static _onDown(event) {
2335
+ // src/ts/behaviors/sheetDrag.ts
2336
+ var DISMISS_DISTANCE = 96;
2337
+ var DISMISS_VELOCITY = 0.5;
2338
+ var DRAG_SLOP = 4;
2339
+ function installSheetDrag(config) {
2340
+ const coord = (event) => config.axis === "x" ? event.clientX : event.clientY;
2341
+ let drag = null;
2342
+ const sheetFor = (target) => {
2343
+ if (!(target instanceof Element)) return null;
2344
+ const dialog = target.closest(config.selector);
2345
+ return dialog instanceof HTMLDialogElement ? dialog : null;
2346
+ };
2347
+ const reset = (dialog) => {
2348
+ dialog.style.transition = "";
2349
+ dialog.style.setProperty(config.property, "0px");
2350
+ };
2351
+ const onDown = (event) => {
2352
+ config.onPress?.();
2081
2353
  if (!event.isPrimary) return;
2082
2354
  if (event.pointerType === "mouse" && event.button !== 0) return;
2083
- const dialog = _BottomSheets._sheet(event.target);
2355
+ const dialog = sheetFor(event.target);
2084
2356
  if (!dialog?.open) {
2085
- _BottomSheets._drag = null;
2357
+ drag = null;
2086
2358
  return;
2087
2359
  }
2088
- if (!_BottomSheets._inHandle(event, dialog)) {
2089
- _BottomSheets._drag = null;
2360
+ if (!config.inHandle(event, dialog)) {
2361
+ drag = null;
2090
2362
  return;
2091
2363
  }
2092
2364
  dialog.style.transition = "none";
2093
- _BottomSheets._drag = {
2365
+ drag = {
2094
2366
  dialog,
2095
2367
  pointerId: event.pointerId,
2096
- startY: event.clientY,
2097
- lastY: event.clientY,
2368
+ direction: config.direction(dialog),
2369
+ start: coord(event),
2370
+ last: coord(event),
2098
2371
  lastT: Date.now(),
2099
2372
  shift: 0
2100
2373
  };
2101
- }
2102
- static _onMove(event) {
2103
- const drag = _BottomSheets._drag;
2374
+ };
2375
+ const onMove = (event) => {
2104
2376
  if (!drag || drag.pointerId !== event.pointerId) return;
2105
- const shift = Math.max(0, event.clientY - drag.startY);
2106
- drag.shift = shift;
2107
- drag.lastY = event.clientY;
2377
+ const delta = coord(event) - drag.start;
2378
+ drag.shift = drag.direction === 1 ? Math.max(0, delta) : Math.min(0, delta);
2379
+ drag.last = coord(event);
2108
2380
  drag.lastT = Date.now();
2109
- drag.dialog.style.setProperty("--md-comp-bottom-sheet-shift", `${shift}px`);
2110
- }
2111
- static _onUp(event) {
2112
- const drag = _BottomSheets._drag;
2113
- _BottomSheets._drag = null;
2114
- if (!drag || drag.pointerId !== event.pointerId) return;
2115
- const dialog = drag.dialog;
2116
- dialog.style.transition = "";
2117
- const dt = Math.max(1, Date.now() - drag.lastT);
2118
- const velocity = (event.clientY - drag.lastY) / dt;
2119
- const dismiss = drag.shift > 96 || velocity > 0.5;
2120
- if (dismiss && dialog.open) {
2121
- dialog.close();
2122
- }
2123
- dialog.style.setProperty("--md-comp-bottom-sheet-shift", "0px");
2124
- }
2125
- static _onCancel() {
2126
- const drag = _BottomSheets._drag;
2127
- _BottomSheets._drag = null;
2128
- if (!drag) return;
2129
- drag.dialog.style.transition = "";
2130
- drag.dialog.style.setProperty("--md-comp-bottom-sheet-shift", "0px");
2131
- }
2132
- static _sheet(target) {
2133
- if (!(target instanceof Element)) return null;
2134
- const dialog = target.closest("dialog.bottom-sheet, dialog.bottom");
2135
- return dialog instanceof HTMLDialogElement ? dialog : null;
2381
+ drag.dialog.style.setProperty(config.property, `${drag.shift}px`);
2382
+ };
2383
+ const onUp = (event) => {
2384
+ const held = drag;
2385
+ if (!held || held.pointerId !== event.pointerId) return;
2386
+ drag = null;
2387
+ held.dialog.style.transition = "";
2388
+ const dt = Math.max(1, Date.now() - held.lastT);
2389
+ const velocity = (coord(event) - held.last) / dt;
2390
+ config.onRelease?.(Math.abs(held.shift) > DRAG_SLOP);
2391
+ const dismiss = held.direction === 1 ? held.shift > DISMISS_DISTANCE || velocity > DISMISS_VELOCITY : held.shift < -DISMISS_DISTANCE || velocity < -DISMISS_VELOCITY;
2392
+ if (dismiss && held.dialog.open) held.dialog.close();
2393
+ held.dialog.style.setProperty(config.property, "0px");
2394
+ };
2395
+ const onCancel = () => {
2396
+ config.onRelease?.(false);
2397
+ const held = drag;
2398
+ drag = null;
2399
+ if (held) reset(held.dialog);
2400
+ };
2401
+ Utils.onDocumentReady(() => {
2402
+ document.addEventListener("pointerdown", onDown, { passive: true });
2403
+ document.addEventListener("pointermove", onMove, { passive: true });
2404
+ document.addEventListener("pointerup", onUp, { passive: true });
2405
+ document.addEventListener("pointercancel", onCancel, { passive: true });
2406
+ });
2407
+ }
2408
+
2409
+ // src/ts/behaviors/bottomSheets.ts
2410
+ var SHEET_SELECTOR = "dialog.bottom-sheet, dialog.bottom";
2411
+ var _BottomSheets = class _BottomSheets {
2412
+ static Init() {
2413
+ installSheetDrag({
2414
+ selector: SHEET_SELECTOR,
2415
+ axis: "y",
2416
+ property: "--md-comp-bottom-sheet-shift",
2417
+ direction: () => 1,
2418
+ inHandle: (event, dialog) => {
2419
+ const target = event.target;
2420
+ if (target instanceof Element && target.closest(".handle")) return true;
2421
+ const rect = dialog.getBoundingClientRect();
2422
+ return event.clientY >= rect.top && event.clientY <= rect.top + 48;
2423
+ },
2424
+ onPress: () => {
2425
+ _BottomSheets._dragged = false;
2426
+ },
2427
+ onRelease: (dragged) => {
2428
+ _BottomSheets._dragged = dragged;
2429
+ }
2430
+ });
2431
+ Utils.onDocumentReady(() => {
2432
+ document.addEventListener("click", _BottomSheets._onClick);
2433
+ });
2136
2434
  }
2137
- static _inHandle(event, dialog) {
2435
+ static _onClick(event) {
2138
2436
  const target = event.target;
2139
- if (target instanceof Element && target.closest(".handle")) return true;
2140
- const rect = dialog.getBoundingClientRect();
2141
- return event.clientY >= rect.top && event.clientY <= rect.top + 48;
2437
+ if (!(target instanceof Element)) return;
2438
+ const handle = target.closest(".handle, .drag-handle");
2439
+ if (!(handle instanceof HTMLButtonElement)) return;
2440
+ if (event.detail !== 0 && _BottomSheets._dragged) return;
2441
+ const dialog = handle.closest(SHEET_SELECTOR);
2442
+ if (dialog instanceof HTMLDialogElement && dialog.open) dialog.close();
2142
2443
  }
2143
2444
  };
2144
- _BottomSheets._drag = null;
2445
+ /**
2446
+ * Whether the last pointer sequence moved. A drag ends with a `click` on the
2447
+ * element it started on, so without this the tail of a drag that snapped back
2448
+ * would dismiss the sheet the drag had just declined to dismiss.
2449
+ */
2450
+ _BottomSheets._dragged = false;
2145
2451
  var BottomSheets = _BottomSheets;
2146
2452
 
2147
2453
  // src/ts/behaviors/sideSheets.ts
2148
- var _SideSheets = class _SideSheets {
2454
+ var startDocked = (dialog) => dialog.classList.contains("left") || dialog.classList.contains("left-sheet") || dialog.classList.contains("start");
2455
+ var SideSheets = class {
2149
2456
  static Init() {
2150
- Utils.onDocumentReady(() => {
2151
- document.addEventListener("pointerdown", _SideSheets._onDown, { passive: true });
2152
- document.addEventListener("pointermove", _SideSheets._onMove, { passive: true });
2153
- document.addEventListener("pointerup", _SideSheets._onUp, { passive: true });
2154
- document.addEventListener("pointercancel", _SideSheets._onCancel, { passive: true });
2457
+ installSheetDrag({
2458
+ selector: "dialog.side-sheet, dialog.right, dialog.right-sheet, dialog.left, dialog.left-sheet",
2459
+ axis: "x",
2460
+ property: "--md-comp-side-sheet-shift",
2461
+ // A start-docked sheet leaves toward the start edge, so its dismissal
2462
+ // direction is the negative one.
2463
+ direction: (dialog) => startDocked(dialog) ? -1 : 1,
2464
+ inHandle: (event, dialog) => {
2465
+ const target = event.target;
2466
+ if (target instanceof Element && target.closest("header")) return true;
2467
+ const rect = dialog.getBoundingClientRect();
2468
+ return startDocked(dialog) ? event.clientX >= rect.right - 24 && event.clientX <= rect.right : event.clientX >= rect.left && event.clientX <= rect.left + 24;
2469
+ }
2155
2470
  });
2156
2471
  }
2157
- static _onDown(event) {
2158
- if (!event.isPrimary) return;
2159
- if (event.pointerType === "mouse" && event.button !== 0) return;
2160
- const dialog = _SideSheets._sheet(event.target);
2161
- if (!dialog?.open) {
2162
- _SideSheets._drag = null;
2163
- return;
2164
- }
2165
- if (!_SideSheets._inHandle(event, dialog)) {
2166
- _SideSheets._drag = null;
2167
- return;
2168
- }
2169
- dialog.style.transition = "none";
2170
- _SideSheets._drag = {
2171
- dialog,
2172
- pointerId: event.pointerId,
2173
- startX: event.clientX,
2174
- lastX: event.clientX,
2175
- lastT: Date.now(),
2176
- shift: 0,
2177
- startDocked: _SideSheets._startDocked(dialog)
2178
- };
2179
- }
2180
- static _onMove(event) {
2181
- const drag = _SideSheets._drag;
2182
- if (!drag || drag.pointerId !== event.pointerId) return;
2183
- const delta = event.clientX - drag.startX;
2184
- const shift = drag.startDocked ? Math.min(0, delta) : Math.max(0, delta);
2185
- drag.shift = shift;
2186
- drag.lastX = event.clientX;
2187
- drag.lastT = Date.now();
2188
- drag.dialog.style.setProperty("--md-comp-side-sheet-shift", `${shift}px`);
2189
- }
2190
- static _onUp(event) {
2191
- const drag = _SideSheets._drag;
2192
- _SideSheets._drag = null;
2193
- if (!drag || drag.pointerId !== event.pointerId) return;
2194
- const dialog = drag.dialog;
2195
- dialog.style.transition = "";
2196
- const dt = Math.max(1, Date.now() - drag.lastT);
2197
- const velocity = (event.clientX - drag.lastX) / dt;
2198
- const dismiss = drag.startDocked ? drag.shift < -96 || velocity < -0.5 : drag.shift > 96 || velocity > 0.5;
2199
- if (dismiss && dialog.open) {
2200
- dialog.close();
2201
- }
2202
- dialog.style.setProperty("--md-comp-side-sheet-shift", "0px");
2203
- }
2204
- static _onCancel() {
2205
- const drag = _SideSheets._drag;
2206
- _SideSheets._drag = null;
2207
- if (!drag) return;
2208
- drag.dialog.style.transition = "";
2209
- drag.dialog.style.setProperty("--md-comp-side-sheet-shift", "0px");
2210
- }
2211
- static _sheet(target) {
2212
- if (!(target instanceof Element)) return null;
2213
- const dialog = target.closest(
2214
- "dialog.side-sheet, dialog.right, dialog.right-sheet, dialog.left, dialog.left-sheet"
2215
- );
2216
- return dialog instanceof HTMLDialogElement ? dialog : null;
2217
- }
2218
- static _startDocked(dialog) {
2219
- return dialog.classList.contains("left") || dialog.classList.contains("left-sheet") || dialog.classList.contains("start");
2220
- }
2221
- static _inHandle(event, dialog) {
2222
- const target = event.target;
2223
- if (target instanceof Element && target.closest("header")) return true;
2224
- const rect = dialog.getBoundingClientRect();
2225
- if (_SideSheets._startDocked(dialog)) {
2226
- return event.clientX >= rect.right - 24 && event.clientX <= rect.right;
2227
- }
2228
- return event.clientX >= rect.left && event.clientX <= rect.left + 24;
2229
- }
2230
2472
  };
2231
- _SideSheets._drag = null;
2232
- var SideSheets = _SideSheets;
2233
2473
 
2234
2474
  // src/ts/behaviors/forms.ts
2235
2475
  var Forms = class _Forms {
@@ -2285,7 +2525,7 @@ var Expressive = (() => {
2285
2525
  document.addEventListener("keyup", (e) => {
2286
2526
  const target = e.target;
2287
2527
  if (target instanceof HTMLInputElement && ["radio", "checkbox"].includes(target.type)) {
2288
- if (Utils.keys.TAB.includes(e.key)) {
2528
+ if (e.key === Utils.keys.TAB) {
2289
2529
  target.classList.add("tabbed");
2290
2530
  target.addEventListener("blur", () => target.classList.remove("tabbed"), {
2291
2531
  once: true
@@ -2323,61 +2563,8 @@ var Expressive = (() => {
2323
2563
  }
2324
2564
  };
2325
2565
 
2326
- // src/ts/behaviors/waves.ts
2327
- var Waves = class _Waves {
2328
- static _offset(el) {
2329
- const box = el.getBoundingClientRect();
2330
- const docElem = document.documentElement;
2331
- return {
2332
- top: box.top + window.pageYOffset - docElem.clientTop,
2333
- left: box.left + window.pageXOffset - docElem.clientLeft
2334
- };
2335
- }
2336
- // https://phoenix-dx.com/css-techniques-for-material-ripple-effect/
2337
- static renderWaveEffect(targetElement, position = null, color = null) {
2338
- const isCentered = position === null;
2339
- const duration = 500;
2340
- let animationFrame, animationStart;
2341
- const animationStep = function(timestamp) {
2342
- if (!animationStart) {
2343
- animationStart = timestamp;
2344
- }
2345
- const frame = timestamp - animationStart;
2346
- if (frame < duration) {
2347
- const easing = frame / duration * (2 - frame / duration);
2348
- const circle = isCentered ? "circle at 50% 50%" : `circle at ${position.x}px ${position.y}px`;
2349
- const waveColor = `rgba(${color?.r || 0}, ${color?.g || 0}, ${color?.b || 0}, ${0.3 * (1 - easing)})`;
2350
- const stop = 90 * easing + "%";
2351
- targetElement.style.backgroundImage = "radial-gradient(" + circle + ", " + waveColor + " " + stop + ", transparent " + stop + ")";
2352
- animationFrame = window.requestAnimationFrame(animationStep);
2353
- } else {
2354
- targetElement.style.backgroundImage = "none";
2355
- window.cancelAnimationFrame(animationFrame);
2356
- }
2357
- };
2358
- animationFrame = window.requestAnimationFrame(animationStep);
2359
- }
2360
- static Init() {
2361
- Utils.onDocumentReady(() => {
2362
- document.body.addEventListener("click", (e) => {
2363
- const trigger = e.target;
2364
- const el = trigger.closest(".waves-effect");
2365
- if (el && el.contains(trigger)) {
2366
- const isCircular = el.classList.contains("waves-circle");
2367
- const offset = _Waves._offset(el);
2368
- const x = e.pageX - offset.left;
2369
- const y = e.pageY - offset.top;
2370
- let color = null;
2371
- if (el.classList.contains("waves-light")) color = { r: 255, g: 255, b: 255 };
2372
- _Waves.renderWaveEffect(el, isCircular ? null : { x, y }, color);
2373
- }
2374
- });
2375
- });
2376
- }
2377
- };
2378
-
2379
2566
  // src/ts/components/buttons.ts
2380
- var _defaults6 = {
2567
+ var _defaults7 = {
2381
2568
  direction: "top",
2382
2569
  hoverEnabled: true,
2383
2570
  toolbarEnabled: false
@@ -2412,13 +2599,13 @@ var Expressive = (() => {
2412
2599
  this._toggle();
2413
2600
  };
2414
2601
  this._handleAnchorKeydown = (e) => {
2415
- if (Utils.keys.ENTER.includes(e.key) || e.key === " ") {
2602
+ if (e.key === Utils.keys.ENTER || e.key === " ") {
2416
2603
  e.preventDefault();
2417
2604
  this._toggle();
2418
2605
  }
2419
2606
  };
2420
2607
  this._handleDocumentKeydown = (e) => {
2421
- if (Utils.keys.ESC.includes(e.key)) this.close();
2608
+ if (e.key === Utils.keys.ESC) this.close();
2422
2609
  };
2423
2610
  this._handleDocumentClick = (e) => {
2424
2611
  const target = e.target;
@@ -2438,22 +2625,19 @@ var Expressive = (() => {
2438
2625
  };
2439
2626
  this.isOpen = false;
2440
2627
  this._anchor = this.el.querySelector(":scope > a, :scope > button");
2441
- this._menu = this.el.querySelector("ul, menu");
2442
2628
  this.el.classList.add(`direction-${this.options.direction}`);
2443
2629
  if (clickToToggle) this.el.classList.add("click-to-toggle");
2444
2630
  if (toolbar) this.el.classList.add("toolbar");
2445
2631
  if (this._anchor) {
2446
- this._anchor.setAttribute("aria-haspopup", "menu");
2447
2632
  this._anchor.setAttribute("aria-expanded", "false");
2448
2633
  if (this._anchor instanceof HTMLAnchorElement && !this._anchor.hasAttribute("href") && this._anchor.tabIndex < 0) {
2449
2634
  this._anchor.tabIndex = 0;
2450
2635
  }
2451
2636
  }
2452
- if (this._menu) this._menu.setAttribute("role", "menu");
2453
2637
  this._setupEventHandlers();
2454
2638
  }
2455
2639
  static get defaults() {
2456
- return _defaults6;
2640
+ return _defaults7;
2457
2641
  }
2458
2642
  static init(els, options = {}) {
2459
2643
  return super.init(els, options, _FloatingActionButton);
@@ -2491,16 +2675,21 @@ var Expressive = (() => {
2491
2675
  };
2492
2676
 
2493
2677
  // src/ts/components/carousel.ts
2494
- var _defaults7 = {
2678
+ var _defaults8 = {
2495
2679
  duration: 200,
2496
- dist: -100,
2497
- shift: 0,
2498
- padding: 0,
2499
- numVisible: 5,
2500
2680
  fullWidth: false,
2501
2681
  indicators: false,
2502
2682
  noWrap: false,
2503
- onCycleTo: null
2683
+ interval: 0,
2684
+ height: null,
2685
+ onCycleTo: null,
2686
+ i18n: {
2687
+ carousel: "Carousel",
2688
+ item: "Item",
2689
+ of: "of",
2690
+ indicators: "Slides",
2691
+ slide: "Slide"
2692
+ }
2504
2693
  };
2505
2694
  var _Carousel = class _Carousel extends Component {
2506
2695
  constructor(el, options) {
@@ -2511,101 +2700,112 @@ var Expressive = (() => {
2511
2700
  this._ignoreScroll = false;
2512
2701
  this._trackRaf = null;
2513
2702
  this._started = false;
2703
+ this._vertical = false;
2704
+ this._resizeObserver = null;
2705
+ this._usesWindowResize = false;
2706
+ this._dragPointerId = null;
2707
+ this._dragStartX = 0;
2708
+ this._dragStartY = 0;
2709
+ this._dragStartScroll = 0;
2710
+ this._suppressClick = false;
2711
+ this._suppressClickTimeout = null;
2712
+ this._generatedContainerRole = false;
2713
+ this._generatedContainerLabel = false;
2714
+ this._generatedContainerDescription = false;
2715
+ this._generatedWideLayout = false;
2716
+ this._generatedFixedHeight = false;
2717
+ this._authoredInlineHeight = null;
2718
+ this._autoAdvanceTimer = null;
2719
+ this._autoAdvances = false;
2720
+ this._autoPaused = false;
2721
+ this._hovered = false;
2722
+ this._focused = false;
2723
+ this._generatedItemTabIndexes = /* @__PURE__ */ new Set();
2724
+ this._generatedItemLabels = /* @__PURE__ */ new Set();
2725
+ this._generatedItemDescriptions = /* @__PURE__ */ new Set();
2726
+ this._generatedItemSizes = /* @__PURE__ */ new Set();
2727
+ this._authoredItemSizes = /* @__PURE__ */ new Set();
2728
+ this._itemA11yOriginal = /* @__PURE__ */ new Map();
2514
2729
  this._handleThrottledResize = Utils.throttle(() => this._handleResize(), 200);
2515
- this._handleCarouselTap = (e) => {
2516
- if (e.pointerType === "mouse" && e.button !== 0) return;
2517
- if (e.target instanceof HTMLElement && e.target.tagName === "IMG") {
2518
- e.preventDefault();
2730
+ this._handleAutoAdvanceState = (e) => {
2731
+ if (e.type === "mouseenter") this._hovered = true;
2732
+ else if (e.type === "mouseleave") this._hovered = false;
2733
+ else if (e.type === "focusin") this._focused = true;
2734
+ else if (e.type === "focusout") this._focused = false;
2735
+ this._syncAutoAdvance();
2736
+ };
2737
+ this._tick = () => {
2738
+ this._autoAdvanceTimer = null;
2739
+ this._advance();
2740
+ this._syncAutoAdvance();
2741
+ };
2742
+ this._advance = () => {
2743
+ const next = this.center + 1;
2744
+ if (next < this.count) {
2745
+ this.set(next);
2746
+ return;
2747
+ }
2748
+ if (this.options.noWrap) {
2749
+ this._autoAdvances = false;
2750
+ this._syncAutoAdvance();
2751
+ return;
2519
2752
  }
2753
+ this.set(0);
2754
+ };
2755
+ this._handleTrackPointerDown = (e) => {
2756
+ if (e.pointerType !== "mouse" || e.button !== 0) return;
2520
2757
  this.pressed = true;
2521
2758
  this.dragged = false;
2522
- this.verticalDragged = false;
2523
- this.reference = e.clientX;
2524
- this.referenceY = e.clientY;
2525
- this.velocity = this.amplitude = 0;
2526
- this.frame = this.offset;
2527
- this.timestamp = Date.now();
2759
+ this._suppressClick = false;
2760
+ this._dragPointerId = e.pointerId;
2761
+ this._dragStartX = e.clientX;
2762
+ this._dragStartY = e.clientY;
2763
+ this._dragStartScroll = this._vertical ? this._scroller.scrollTop : this._scroller.scrollLeft;
2528
2764
  try {
2529
- this.el.setPointerCapture(e.pointerId);
2765
+ this._scroller.setPointerCapture(e.pointerId);
2530
2766
  } catch {
2531
2767
  }
2532
- if (this._trackRaf !== null) cancelAnimationFrame(this._trackRaf);
2533
- const loop = () => {
2534
- this._track();
2535
- if (this.pressed) this._trackRaf = requestAnimationFrame(loop);
2536
- else this._trackRaf = null;
2537
- };
2538
- this._trackRaf = requestAnimationFrame(loop);
2539
- };
2540
- this._handleCarouselDrag = (e) => {
2541
- if (!this.pressed) return;
2542
- const delta = this.reference - e.clientX;
2543
- const deltaY = Math.abs(this.referenceY - e.clientY);
2544
- if (deltaY < 30 && !this.verticalDragged) {
2545
- if (delta > 2 || delta < -2) {
2546
- this.dragged = true;
2547
- this.reference = e.clientX;
2548
- this._scroll(this.offset + delta);
2549
- }
2550
- } else if (this.dragged) {
2551
- e.preventDefault();
2552
- } else {
2553
- this.verticalDragged = true;
2768
+ };
2769
+ this._handleTrackPointerMove = (e) => {
2770
+ if (!this.pressed || e.pointerId !== this._dragPointerId) return;
2771
+ const deltaX = e.clientX - this._dragStartX;
2772
+ const deltaY = e.clientY - this._dragStartY;
2773
+ const primaryDelta = this._vertical ? deltaY : deltaX;
2774
+ const crossDelta = this._vertical ? deltaX : deltaY;
2775
+ if (!this.dragged) {
2776
+ if (Math.abs(primaryDelta) < 6 || Math.abs(primaryDelta) <= Math.abs(crossDelta)) return;
2777
+ this.dragged = true;
2778
+ this.el.classList.add("dragging");
2554
2779
  }
2555
- if (this.dragged) e.preventDefault();
2780
+ if (this._vertical) this._scroller.scrollTop = this._dragStartScroll - deltaY;
2781
+ else this._scroller.scrollLeft = this._dragStartScroll - deltaX;
2782
+ e.preventDefault();
2556
2783
  };
2557
- this._handleCarouselRelease = (e) => {
2558
- if (!this.pressed) return;
2784
+ this._handleTrackPointerUp = (e) => {
2785
+ if (!this.pressed || e.pointerId !== this._dragPointerId) return;
2559
2786
  this.pressed = false;
2560
- if (this._trackRaf !== null) {
2561
- cancelAnimationFrame(this._trackRaf);
2562
- this._trackRaf = null;
2563
- }
2787
+ this._dragPointerId = null;
2788
+ this.el.classList.remove("dragging");
2564
2789
  try {
2565
- this.el.releasePointerCapture(e.pointerId);
2790
+ this._scroller.releasePointerCapture(e.pointerId);
2566
2791
  } catch {
2567
2792
  }
2568
- this.target = this.offset;
2569
- if (this.velocity > 10 || this.velocity < -10) {
2570
- this.amplitude = 0.9 * this.velocity;
2571
- this.target = this.offset + this.amplitude;
2572
- }
2573
- this.target = Math.round(this.target / this.dim) * this.dim;
2574
- if (this.noWrap) {
2575
- if (this.target >= this.dim * (this.count - 1)) {
2576
- this.target = this.dim * (this.count - 1);
2577
- } else if (this.target < 0) {
2578
- this.target = 0;
2579
- }
2580
- }
2581
- this.amplitude = this.target - this.offset;
2582
- this.timestamp = Date.now();
2583
- requestAnimationFrame(this._autoScroll);
2584
- if (this.dragged) e.preventDefault();
2793
+ if (!this.dragged) return;
2794
+ this._suppressClick = true;
2795
+ window.clearTimeout(this._suppressClickTimeout);
2796
+ this._suppressClickTimeout = setTimeout(() => {
2797
+ this._suppressClick = false;
2798
+ }, 0);
2799
+ this._syncActive(this._nearestIndex(), true);
2800
+ };
2801
+ this._handleTrackClick = (e) => {
2802
+ if (!this._suppressClick) return;
2803
+ this._suppressClick = false;
2804
+ e.preventDefault();
2805
+ e.stopImmediatePropagation();
2585
2806
  };
2586
- this._handleCarouselClick = (e) => {
2587
- if (this.dragged) {
2588
- e.preventDefault();
2589
- e.stopPropagation();
2590
- return;
2591
- }
2592
- const clickedElem = e.target.closest(".carousel-item");
2593
- if (!clickedElem || !this.el.contains(clickedElem)) return;
2594
- const clickedIndex = this.images.indexOf(clickedElem);
2595
- const diff = this._wrap(this.center) - clickedIndex;
2596
- if (diff !== 0) {
2597
- e.preventDefault();
2598
- e.stopPropagation();
2599
- }
2600
- if (clickedIndex < 0) {
2601
- if (e.clientX - e.target.getBoundingClientRect().left > this.el.clientWidth / 2) {
2602
- this.next();
2603
- } else {
2604
- this.prev();
2605
- }
2606
- } else {
2607
- this._cycleTo(clickedIndex);
2608
- }
2807
+ this._handleTrackDragStart = (e) => {
2808
+ if (e.target.closest("img, picture, video")) e.preventDefault();
2609
2809
  };
2610
2810
  this._handleIndicatorClick = (e) => {
2611
2811
  e.stopPropagation();
@@ -2618,112 +2818,107 @@ var Expressive = (() => {
2618
2818
  this._handleKeydown = (e) => {
2619
2819
  const target = e.target;
2620
2820
  if (target.closest('input, textarea, select, [contenteditable="true"]')) return;
2621
- if (Utils.keys.ARROW_RIGHT.includes(e.key)) {
2821
+ const forward = this._vertical ? e.key === "ArrowDown" : e.key === Utils.keys.ARROW_RIGHT;
2822
+ const backward = this._vertical ? e.key === "ArrowUp" : e.key === Utils.keys.ARROW_LEFT;
2823
+ if (e.key === "Home") {
2824
+ e.preventDefault();
2825
+ this.set(0);
2826
+ this._focusItem(0);
2827
+ } else if (e.key === "End") {
2828
+ e.preventDefault();
2829
+ this.set(this.count - 1);
2830
+ this._focusItem(this.count - 1);
2831
+ } else if (forward) {
2622
2832
  e.preventDefault();
2623
2833
  this.next();
2624
- } else if (Utils.keys.ARROW_LEFT.includes(e.key)) {
2834
+ this._focusItem(this.center);
2835
+ } else if (backward) {
2625
2836
  e.preventDefault();
2626
2837
  this.prev();
2838
+ this._focusItem(this.center);
2627
2839
  }
2628
2840
  };
2629
- this._handleFlatScroll = () => {
2630
- if (this._ignoreScroll) return;
2631
- const index = this._nearestIndex();
2632
- if (index !== this.center) this._syncActive(index, this.dragged);
2841
+ this._handleTrackScroll = () => {
2842
+ if (!this.el.classList.contains("scrolling")) this.el.classList.add("scrolling");
2843
+ window.clearTimeout(this.scrollingTimeout);
2844
+ this.scrollingTimeout = setTimeout(() => this.el.classList.remove("scrolling"), 120);
2845
+ if (this._trackRaf !== null) return;
2846
+ this._trackRaf = requestAnimationFrame(() => {
2847
+ this._trackRaf = null;
2848
+ this._updateParallax();
2849
+ if (this._ignoreScroll) return;
2850
+ const index = this._nearestIndex();
2851
+ if (index !== this.center) this._syncActive(index, this.dragged);
2852
+ });
2633
2853
  };
2634
2854
  this._handleResize = () => {
2635
2855
  if (!this.images.length) return;
2636
- if (this._flat) {
2637
- this._scrollToIndex(this.center, false);
2638
- return;
2639
- }
2640
- if (this.options.fullWidth) {
2641
- const firstItem = this.images[0];
2642
- if (!firstItem) return;
2643
- this.itemWidth = firstItem.clientWidth;
2644
- this.dim = this.itemWidth * 2 + this.options.padding;
2645
- this.offset = this.center * 2 * this.itemWidth;
2646
- this.target = this.offset;
2647
- } else {
2648
- this._scroll();
2649
- }
2650
- };
2651
- this._track = () => {
2652
- const now = Date.now();
2653
- const elapsed = now - this.timestamp;
2654
- const delta = this.offset - this.frame;
2655
- const v = 1e3 * delta / (1 + elapsed);
2656
- this.timestamp = now;
2657
- this.frame = this.offset;
2658
- this.velocity = 0.8 * v + 0.2 * this.velocity;
2659
- };
2660
- this._autoScroll = () => {
2661
- if (!this.amplitude) return;
2662
- const elapsed = Date.now() - this.timestamp;
2663
- const delta = this.amplitude * Math.exp(-elapsed / this.options.duration);
2664
- if (delta > 2 || delta < -2) {
2665
- this._scroll(this.target - delta);
2666
- requestAnimationFrame(this._autoScroll);
2667
- } else {
2668
- this._scroll(this.target);
2669
- }
2856
+ this._syncAdaptiveMode();
2857
+ this._syncLayoutRoles(this.center);
2858
+ this._scrollToIndex(this.center, false);
2859
+ this._updateParallax();
2670
2860
  };
2671
2861
  this.el["Expressive_Carousel"] = this;
2672
2862
  this.options = {
2673
2863
  ..._Carousel.defaults,
2674
2864
  ...options
2675
2865
  };
2676
- this._flat = this.options.fullWidth || this.el.classList.contains("flat");
2677
- if (this._flat) {
2866
+ if (options?.i18n) {
2867
+ this.options.i18n = { ..._Carousel.defaults.i18n, ...options.i18n };
2868
+ }
2869
+ if (this.options.fullWidth || this.el.classList.contains("flat")) {
2678
2870
  this.el.classList.add("flat");
2679
2871
  this.options.fullWidth = true;
2680
- this.options.dist = 0;
2681
- }
2682
- if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
2683
- this.options.duration = 1;
2684
- if (!this._flat) this.options.dist = 0;
2685
2872
  }
2873
+ this._vertical = this.el.classList.contains("full-screen");
2874
+ const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
2875
+ if (reducedMotion) this.options.duration = 1;
2686
2876
  this.pressed = false;
2687
2877
  this.dragged = false;
2688
- this.offset = this.target = 0;
2689
2878
  this.images = this._collectItems();
2690
2879
  if (!this.images.length) {
2691
2880
  console.error("Carousel: no .carousel-item elements to show");
2692
2881
  return;
2693
2882
  }
2883
+ this.images.forEach((item) => {
2884
+ this._itemA11yOriginal.set(item, {
2885
+ ariaHidden: item.getAttribute("aria-hidden"),
2886
+ tabIndex: item.getAttribute("tabindex")
2887
+ });
2888
+ if (item.hasAttribute("data-carousel-size")) this._authoredItemSizes.add(item);
2889
+ });
2694
2890
  this.hasMultipleSlides = this.images.length > 1;
2695
2891
  this.showIndicators = this.options.indicators && this.hasMultipleSlides;
2696
- this.noWrap = this.options.noWrap || !this.hasMultipleSlides || this._flat;
2697
2892
  this.count = this.images.length;
2698
- this.options.numVisible = Math.min(this.count, this.options.numVisible);
2893
+ this._autoAdvances = this.options.interval > 0 && this.hasMultipleSlides && !reducedMotion;
2699
2894
  this.images.forEach((item) => item.classList.add("carousel-item"));
2700
- if (this._flat) this._wrapTrack();
2701
- const firstItem = this.images[0];
2702
- this.itemWidth = firstItem.clientWidth || this.el.clientWidth || 1;
2703
- this.itemHeight = firstItem.clientHeight || this.el.clientHeight || 1;
2704
- this.dim = this.itemWidth * 2 + this.options.padding || 1;
2895
+ if (this.options.height !== null) {
2896
+ this._generatedFixedHeight = !this.el.classList.contains("fixed-height");
2897
+ this._authoredInlineHeight = this.el.style.getPropertyValue("--carousel-height") || null;
2898
+ this.el.classList.add("fixed-height");
2899
+ this.el.style.setProperty("--carousel-height", `${this.options.height}px`);
2900
+ }
2901
+ this._wrapTrack();
2902
+ this._syncAdaptiveMode();
2705
2903
  if (this.showIndicators) this._setupIndicators();
2706
- this.el.tabIndex = this.el.tabIndex >= 0 ? this.el.tabIndex : 0;
2707
- this.el.setAttribute("aria-roledescription", "carousel");
2904
+ this._setupAccessibility();
2708
2905
  this._setupEventHandlers();
2709
2906
  this._started = true;
2710
- if (this._flat) {
2711
- const start = Math.max(
2712
- 0,
2713
- this.images.findIndex((el2) => el2.classList.contains("active"))
2714
- );
2715
- this.center = start;
2716
- this._syncActive(start, false);
2717
- this._scrollToIndex(start, false);
2718
- } else {
2719
- this._scroll(this.offset);
2720
- }
2907
+ const start = Math.max(
2908
+ 0,
2909
+ this.images.findIndex((el2) => el2.classList.contains("active"))
2910
+ );
2911
+ this.center = start;
2912
+ this._syncActive(start, false);
2913
+ this._updateParallax();
2914
+ this._scrollToIndex(start, false);
2915
+ this._syncAutoAdvance();
2721
2916
  }
2722
2917
  get _scroller() {
2723
2918
  return this._trackEl ?? this.el;
2724
2919
  }
2725
2920
  static get defaults() {
2726
- return _defaults7;
2921
+ return _defaults8;
2727
2922
  }
2728
2923
  static init(els, options = {}) {
2729
2924
  return super.init(els, options, _Carousel);
@@ -2748,6 +2943,39 @@ var Expressive = (() => {
2748
2943
  return el.tagName !== "NAV";
2749
2944
  });
2750
2945
  }
2946
+ _setupAccessibility() {
2947
+ if (!this.el.hasAttribute("role")) {
2948
+ this.el.setAttribute("role", "region");
2949
+ this._generatedContainerRole = true;
2950
+ }
2951
+ if (!this.el.hasAttribute("aria-label") && !this.el.hasAttribute("aria-labelledby")) {
2952
+ this.el.setAttribute("aria-label", this.options.i18n.carousel);
2953
+ this._generatedContainerLabel = true;
2954
+ }
2955
+ if (!this.el.hasAttribute("aria-roledescription")) {
2956
+ this.el.setAttribute("aria-roledescription", "carousel");
2957
+ this._generatedContainerDescription = true;
2958
+ }
2959
+ const naturallyFocusable = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
2960
+ this.images.forEach((item, index) => {
2961
+ if (!item.hasAttribute("aria-roledescription")) {
2962
+ item.setAttribute("aria-roledescription", "slide");
2963
+ this._generatedItemDescriptions.add(item);
2964
+ }
2965
+ if (!item.hasAttribute("aria-label") && !item.hasAttribute("aria-labelledby")) {
2966
+ const mediaName = item.querySelector("img[alt]")?.getAttribute("alt")?.trim();
2967
+ const textName = item.textContent?.replace(/\s+/g, " ").trim();
2968
+ const name = mediaName || textName;
2969
+ const position = `${this.options.i18n.item} ${index + 1} ${this.options.i18n.of} ${this.count}`;
2970
+ item.setAttribute("aria-label", name ? `${name}, ${position}` : position);
2971
+ this._generatedItemLabels.add(item);
2972
+ }
2973
+ if (!item.matches(naturallyFocusable) && !item.querySelector(naturallyFocusable)) {
2974
+ item.tabIndex = 0;
2975
+ this._generatedItemTabIndexes.add(item);
2976
+ }
2977
+ });
2978
+ }
2751
2979
  _setupIndicators() {
2752
2980
  const existing = Array.from(this.el.children).find(
2753
2981
  (el) => el instanceof HTMLElement && el.classList.contains("indicators")
@@ -2755,7 +2983,7 @@ var Expressive = (() => {
2755
2983
  this._indicators = existing ?? document.createElement("nav");
2756
2984
  this._ownIndicators = !existing;
2757
2985
  this._indicators.classList.add("indicators");
2758
- this._indicators.setAttribute("aria-label", "Slides");
2986
+ this._indicators.setAttribute("aria-label", this.options.i18n.indicators);
2759
2987
  this._indicators.replaceChildren();
2760
2988
  this.images.forEach((_, i) => {
2761
2989
  const button = document.createElement("button");
@@ -2765,158 +2993,124 @@ var Expressive = (() => {
2765
2993
  button.classList.add("active");
2766
2994
  button.setAttribute("aria-current", "true");
2767
2995
  }
2768
- button.setAttribute("aria-label", `Slide ${i + 1}`);
2996
+ button.setAttribute("aria-label", `${this.options.i18n.slide} ${i + 1}`);
2769
2997
  this._indicators.appendChild(button);
2770
2998
  });
2771
2999
  if (this._ownIndicators) this.el.appendChild(this._indicators);
2772
- if (this._flat) {
2773
- this.el.querySelector(".carousel-fixed-item")?.classList.add("with-indicators");
2774
- }
3000
+ this.el.querySelector(".carousel-fixed-item")?.classList.add("with-indicators");
2775
3001
  }
2776
3002
  _setupEventHandlers() {
2777
3003
  this.el.addEventListener("keydown", this._handleKeydown);
3004
+ if (this._autoAdvances) {
3005
+ this.el.addEventListener("mouseenter", this._handleAutoAdvanceState);
3006
+ this.el.addEventListener("mouseleave", this._handleAutoAdvanceState);
3007
+ this.el.addEventListener("focusin", this._handleAutoAdvanceState);
3008
+ this.el.addEventListener("focusout", this._handleAutoAdvanceState);
3009
+ document.addEventListener("visibilitychange", this._handleAutoAdvanceState);
3010
+ }
2778
3011
  if (this.showIndicators && this._indicators) {
2779
3012
  this._indicators.addEventListener("click", this._handleIndicatorClick);
2780
3013
  }
2781
- _Carousel._live.add(this);
2782
- if (!_Carousel._resizeListening) {
2783
- window.addEventListener("resize", _Carousel._onResize, { passive: true });
2784
- _Carousel._resizeListening = true;
3014
+ if (typeof ResizeObserver !== "undefined") {
3015
+ this._resizeObserver = new ResizeObserver(() => this._handleThrottledResize());
3016
+ this._resizeObserver.observe(this.el);
2785
3017
  }
2786
- if (this._flat) {
2787
- this._scroller.addEventListener("scroll", this._handleFlatScroll, { passive: true });
2788
- return;
3018
+ this._usesWindowResize = !this._resizeObserver || this.el.classList.contains("full-screen");
3019
+ if (this._usesWindowResize) {
3020
+ _Carousel._live.add(this);
3021
+ if (!_Carousel._resizeListening) {
3022
+ window.addEventListener("resize", _Carousel._onResize, {
3023
+ passive: true
3024
+ });
3025
+ _Carousel._resizeListening = true;
3026
+ }
2789
3027
  }
2790
- this.el.addEventListener("pointerdown", this._handleCarouselTap);
2791
- this.el.addEventListener("pointermove", this._handleCarouselDrag);
2792
- this.el.addEventListener("pointerup", this._handleCarouselRelease);
2793
- this.el.addEventListener("pointercancel", this._handleCarouselRelease);
2794
- this.el.addEventListener("click", this._handleCarouselClick);
3028
+ this._scroller.addEventListener("scroll", this._handleTrackScroll, {
3029
+ passive: true
3030
+ });
3031
+ this._scroller.addEventListener("pointerdown", this._handleTrackPointerDown);
3032
+ this._scroller.addEventListener("pointermove", this._handleTrackPointerMove);
3033
+ this._scroller.addEventListener("pointerup", this._handleTrackPointerUp);
3034
+ this._scroller.addEventListener("pointercancel", this._handleTrackPointerUp);
3035
+ this._scroller.addEventListener("click", this._handleTrackClick, true);
3036
+ this._scroller.addEventListener("dragstart", this._handleTrackDragStart);
2795
3037
  }
2796
3038
  _removeEventHandlers() {
2797
3039
  this.el.removeEventListener("keydown", this._handleKeydown);
3040
+ this.el.removeEventListener("mouseenter", this._handleAutoAdvanceState);
3041
+ this.el.removeEventListener("mouseleave", this._handleAutoAdvanceState);
3042
+ this.el.removeEventListener("focusin", this._handleAutoAdvanceState);
3043
+ this.el.removeEventListener("focusout", this._handleAutoAdvanceState);
3044
+ document.removeEventListener("visibilitychange", this._handleAutoAdvanceState);
2798
3045
  if (this._indicators) {
2799
3046
  this._indicators.removeEventListener("click", this._handleIndicatorClick);
2800
3047
  }
2801
- this._scroller.removeEventListener("scroll", this._handleFlatScroll);
2802
- this.el.removeEventListener("pointerdown", this._handleCarouselTap);
2803
- this.el.removeEventListener("pointermove", this._handleCarouselDrag);
2804
- this.el.removeEventListener("pointerup", this._handleCarouselRelease);
2805
- this.el.removeEventListener("pointercancel", this._handleCarouselRelease);
2806
- this.el.removeEventListener("click", this._handleCarouselClick);
2807
- _Carousel._live.delete(this);
2808
- if (_Carousel._live.size === 0 && _Carousel._resizeListening) {
2809
- window.removeEventListener("resize", _Carousel._onResize);
2810
- _Carousel._resizeListening = false;
3048
+ this._scroller.removeEventListener("scroll", this._handleTrackScroll);
3049
+ this._scroller.removeEventListener("pointerdown", this._handleTrackPointerDown);
3050
+ this._scroller.removeEventListener("pointermove", this._handleTrackPointerMove);
3051
+ this._scroller.removeEventListener("pointerup", this._handleTrackPointerUp);
3052
+ this._scroller.removeEventListener("pointercancel", this._handleTrackPointerUp);
3053
+ this._scroller.removeEventListener("click", this._handleTrackClick, true);
3054
+ this._scroller.removeEventListener("dragstart", this._handleTrackDragStart);
3055
+ this._resizeObserver?.disconnect();
3056
+ this._resizeObserver = null;
3057
+ if (this._usesWindowResize) {
3058
+ _Carousel._live.delete(this);
3059
+ if (_Carousel._live.size === 0 && _Carousel._resizeListening) {
3060
+ window.removeEventListener("resize", _Carousel._onResize);
3061
+ _Carousel._resizeListening = false;
3062
+ }
2811
3063
  }
2812
3064
  }
2813
3065
  _teardown() {
2814
3066
  this._removeEventHandlers();
3067
+ this._autoAdvances = false;
3068
+ this._syncAutoAdvance();
2815
3069
  if (this._trackRaf !== null) cancelAnimationFrame(this._trackRaf);
2816
3070
  window.clearTimeout(this.scrollingTimeout);
3071
+ window.clearTimeout(this._suppressClickTimeout);
2817
3072
  if (this._ownIndicators) this._indicators?.remove();
2818
3073
  this._unwrapTrack();
2819
3074
  this.images.forEach((el) => {
2820
- el.style.transform = "";
2821
- el.style.zIndex = "";
2822
- el.style.opacity = "";
2823
- el.style.visibility = "";
3075
+ el.style.removeProperty("--md-comp-carousel-item-parallax");
3076
+ if (this._generatedItemLabels.has(el)) el.removeAttribute("aria-label");
3077
+ if (this._generatedItemDescriptions.has(el)) el.removeAttribute("aria-roledescription");
3078
+ if (this._generatedItemSizes.has(el)) el.removeAttribute("data-carousel-size");
3079
+ const original = this._itemA11yOriginal.get(el);
3080
+ if (original?.ariaHidden === null) el.removeAttribute("aria-hidden");
3081
+ else if (original) el.setAttribute("aria-hidden", original.ariaHidden);
3082
+ if (original?.tabIndex === null) el.removeAttribute("tabindex");
3083
+ else if (original) el.setAttribute("tabindex", original.tabIndex);
2824
3084
  });
2825
- }
2826
- _xpos(e) {
2827
- if ("clientX" in e) return e.clientX;
2828
- return 0;
2829
- }
2830
- _ypos(e) {
2831
- if ("clientY" in e) return e.clientY;
2832
- return 0;
2833
- }
2834
- _wrap(x) {
2835
- return x >= this.count ? x % this.count : x < 0 ? this._wrap(this.count + x % this.count) : x;
2836
- }
2837
- _scroll(x = 0) {
2838
- if (!this.el.classList.contains("scrolling")) {
2839
- this.el.classList.add("scrolling");
2840
- }
2841
- window.clearTimeout(this.scrollingTimeout);
2842
- this.scrollingTimeout = setTimeout(() => {
2843
- this.el.classList.remove("scrolling");
2844
- }, this.options.duration);
2845
- this.offset = typeof x === "number" ? x : this.offset;
2846
- this.center = Math.floor((this.offset + this.dim / 2) / this.dim);
2847
- const half = this.count >> 1;
2848
- const delta = this.offset - this.center * this.dim;
2849
- const dir = delta < 0 ? 1 : -1;
2850
- const tween = -dir * delta * 2 / this.dim;
2851
- const lastCenter = this.center;
2852
- const numVisibleOffset = 1 / this.options.numVisible;
2853
- let alignment;
2854
- let centerTweenedOpacity;
2855
- if (this.options.fullWidth) {
2856
- alignment = "translateX(0)";
2857
- centerTweenedOpacity = 1;
2858
- } else {
2859
- alignment = "translateX(" + (this.el.clientWidth - this.itemWidth) / 2 + "px) ";
2860
- alignment += "translateY(" + (this.el.clientHeight - this.itemHeight) / 2 + "px)";
2861
- centerTweenedOpacity = 1 - numVisibleOffset * tween;
2862
- }
2863
- this._paintIndicators(this.center);
2864
- if (!this.noWrap || this.center >= 0 && this.center < this.count) {
2865
- const el = this.images[this._wrap(this.center)];
2866
- if (el && !el.classList.contains("active")) {
2867
- this.el.querySelector(".carousel-item.active")?.classList.remove("active");
2868
- el.classList.add("active");
2869
- }
2870
- }
2871
- for (let i = 1; i <= half; ++i) {
2872
- let zTranslation;
2873
- let tweenedOpacity;
2874
- if (this.options.fullWidth) {
2875
- zTranslation = this.options.dist;
2876
- tweenedOpacity = i === half && delta < 0 ? 1 - tween : 1;
2877
- } else {
2878
- zTranslation = this.options.dist * (i * 2 + tween * dir);
2879
- tweenedOpacity = 1 - numVisibleOffset * (i * 2 + tween * dir);
2880
- }
2881
- if (!this.noWrap || this.center + i < this.count) {
2882
- const el = this.images[this._wrap(this.center + i)];
2883
- const transformString = `${alignment} translateX(${this.options.shift + (this.dim * i - delta) / 2}px) translateZ(${zTranslation}px)`;
2884
- this._updateItemStyle(el, tweenedOpacity, -i, transformString);
2885
- }
2886
- if (this.options.fullWidth) {
2887
- zTranslation = this.options.dist;
2888
- tweenedOpacity = i === half && delta > 0 ? 1 - tween : 1;
2889
- } else {
2890
- zTranslation = this.options.dist * (i * 2 - tween * dir);
2891
- tweenedOpacity = 1 - numVisibleOffset * (i * 2 - tween * dir);
2892
- }
2893
- if (!this.noWrap || this.center - i >= 0) {
2894
- const el = this.images[this._wrap(this.center - i)];
2895
- const transformString = `${alignment} translateX(${-this.options.shift + (-this.dim * i - delta) / 2}px) translateZ(${zTranslation}px)`;
2896
- this._updateItemStyle(el, tweenedOpacity, -i, transformString);
2897
- }
2898
- }
2899
- if (!this.noWrap || this.center >= 0 && this.center < this.count) {
2900
- const el = this.images[this._wrap(this.center)];
2901
- const transformString = `${alignment} translateX(${-delta / 2}px) translateX(${dir * this.options.shift * tween}px) translateZ(${this.options.dist * tween}px)`;
2902
- this._updateItemStyle(el, centerTweenedOpacity, 0, transformString);
2903
- }
2904
- const currItem = this.images[this._wrap(this.center)];
2905
- this._syncA11y(this._wrap(this.center));
2906
- if (lastCenter !== this.center && typeof this.options.onCycleTo === "function") {
2907
- this.options.onCycleTo.call(this, currItem, this.dragged);
2908
- }
2909
- if (typeof this.oneTimeCallback === "function") {
2910
- this.oneTimeCallback.call(this, currItem, this.dragged);
2911
- this.oneTimeCallback = null;
3085
+ if (this._generatedContainerRole) this.el.removeAttribute("role");
3086
+ if (this._generatedContainerLabel) this.el.removeAttribute("aria-label");
3087
+ if (this._generatedContainerDescription) this.el.removeAttribute("aria-roledescription");
3088
+ if (this._generatedFixedHeight) this.el.classList.remove("fixed-height");
3089
+ if (this.options.height !== null) {
3090
+ if (this._authoredInlineHeight === null) this.el.style.removeProperty("--carousel-height");
3091
+ else this.el.style.setProperty("--carousel-height", this._authoredInlineHeight);
3092
+ }
3093
+ if (this._generatedWideLayout) {
3094
+ this.el.classList.remove(
3095
+ "multi-wide",
3096
+ "multi-large",
3097
+ "multi-extra-large",
3098
+ "full-screen-horizontal"
3099
+ );
2912
3100
  }
2913
3101
  }
2914
- _updateItemStyle(el, opacity, zIndex, transform) {
2915
- if (!el) return;
2916
- el.style.transform = transform;
2917
- el.style.zIndex = zIndex.toString();
2918
- el.style.opacity = opacity.toString();
2919
- el.style.visibility = "visible";
3102
+ /** Start or stop the timer to match the current pause state. */
3103
+ _syncAutoAdvance() {
3104
+ const run = this._autoAdvances && !this._autoPaused && !this._hovered && !this._focused && !document.hidden;
3105
+ if (run && this._autoAdvanceTimer === null) {
3106
+ this._autoAdvanceTimer = setTimeout(
3107
+ this._tick,
3108
+ this.options.duration + this.options.interval
3109
+ );
3110
+ } else if (!run && this._autoAdvanceTimer !== null) {
3111
+ clearTimeout(this._autoAdvanceTimer);
3112
+ this._autoAdvanceTimer = null;
3113
+ }
2920
3114
  }
2921
3115
  _paintIndicators(center) {
2922
3116
  if (!this.showIndicators || !this._indicators) return;
@@ -2928,18 +3122,12 @@ var Expressive = (() => {
2928
3122
  else el.removeAttribute("aria-current");
2929
3123
  });
2930
3124
  }
2931
- _syncA11y(index) {
2932
- this.images.forEach((el, i) => {
2933
- if (i === index) el.removeAttribute("aria-hidden");
2934
- else el.setAttribute("aria-hidden", "true");
2935
- });
2936
- }
2937
3125
  _syncActive(index, dragged) {
2938
3126
  const prev = this.center;
2939
3127
  this.center = index;
2940
3128
  this.images.forEach((el, i) => el.classList.toggle("active", i === index));
2941
3129
  this._paintIndicators(index);
2942
- this._syncA11y(index);
3130
+ this._syncLayoutRoles(index);
2943
3131
  const curr = this.images[index];
2944
3132
  if (prev !== index && typeof this.options.onCycleTo === "function") {
2945
3133
  this.options.onCycleTo.call(this, curr, dragged);
@@ -2949,6 +3137,81 @@ var Expressive = (() => {
2949
3137
  this.oneTimeCallback = null;
2950
3138
  }
2951
3139
  }
3140
+ _syncLayoutRoles(index) {
3141
+ const setSize = (item, size) => {
3142
+ if (this._authoredItemSizes.has(item)) return;
3143
+ item.setAttribute("data-carousel-size", size);
3144
+ this._generatedItemSizes.add(item);
3145
+ };
3146
+ this.images.forEach((item) => setSize(item, "large"));
3147
+ if (this.el.classList.contains("flat") || this.el.classList.contains("uncontained") || this.el.classList.contains("full-screen") && this._vertical) {
3148
+ return;
3149
+ }
3150
+ if (this.el.classList.contains("hero") || this.el.classList.contains("full-screen-horizontal")) {
3151
+ const next = this.images[index + 1];
3152
+ if (next) setSize(next, "small");
3153
+ if (this.el.classList.contains("center-aligned")) {
3154
+ const previous = this.images[index - 1];
3155
+ if (previous) setSize(previous, "small");
3156
+ }
3157
+ return;
3158
+ }
3159
+ const width = this.el.clientWidth;
3160
+ const desiredLargeCount = width >= 1600 ? 4 : width >= 1200 ? 3 : width >= 600 ? 2 : 1;
3161
+ const largeCount = Math.min(desiredLargeCount, Math.max(1, this.count - 2));
3162
+ this.el.classList.remove("multi-wide", "multi-large", "multi-extra-large");
3163
+ if (largeCount === 2) this.el.classList.add("multi-wide");
3164
+ else if (largeCount === 3) this.el.classList.add("multi-large");
3165
+ else if (largeCount >= 4) this.el.classList.add("multi-extra-large");
3166
+ this._generatedWideLayout = this._generatedWideLayout || largeCount > 1;
3167
+ for (let offset = 1; offset < largeCount; offset += 1) {
3168
+ const item = this.images[index + offset];
3169
+ if (item) setSize(item, "large");
3170
+ }
3171
+ const medium = this.images[index + largeCount];
3172
+ const small = this.images[index + largeCount + 1];
3173
+ if (medium) setSize(medium, "medium");
3174
+ if (small) setSize(small, "small");
3175
+ }
3176
+ _syncAdaptiveMode() {
3177
+ if (!this.el.classList.contains("full-screen")) return;
3178
+ const landscape = window.matchMedia("(orientation: landscape)").matches;
3179
+ const horizontal = this.el.clientWidth >= 840 || landscape;
3180
+ const wasVertical = this._vertical;
3181
+ this._vertical = !horizontal;
3182
+ this.el.classList.toggle("full-screen-horizontal", horizontal);
3183
+ this._generatedWideLayout = this._generatedWideLayout || horizontal;
3184
+ if (this._started && wasVertical !== this._vertical) {
3185
+ this._scroller.scrollTo({ left: 0, top: 0, behavior: "auto" });
3186
+ }
3187
+ }
3188
+ _focusItem(index) {
3189
+ const item = this.images[index];
3190
+ if (!item) return;
3191
+ const focusable = item.matches(
3192
+ 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
3193
+ ) ? item : item.querySelector(
3194
+ 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
3195
+ );
3196
+ (focusable ?? item).focus({ preventScroll: true });
3197
+ }
3198
+ _updateParallax() {
3199
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
3200
+ this.images.forEach(
3201
+ (item) => item.style.setProperty("--md-comp-carousel-item-parallax", "0px")
3202
+ );
3203
+ return;
3204
+ }
3205
+ const scrollerRect = this._scroller.getBoundingClientRect();
3206
+ const viewportCenter = this._vertical ? scrollerRect.top + scrollerRect.height / 2 : scrollerRect.left + scrollerRect.width / 2;
3207
+ const viewportSize = Math.max(1, this._vertical ? scrollerRect.height : scrollerRect.width);
3208
+ this.images.forEach((item) => {
3209
+ const rect = item.getBoundingClientRect();
3210
+ const itemCenter = this._vertical ? rect.top + rect.height / 2 : rect.left + rect.width / 2;
3211
+ const progress = Math.max(-1, Math.min(1, (itemCenter - viewportCenter) / viewportSize));
3212
+ item.style.setProperty("--md-comp-carousel-item-parallax", `${progress * -24}px`);
3213
+ });
3214
+ }
2952
3215
  _wrapTrack() {
2953
3216
  const track = document.createElement("div");
2954
3217
  track.className = "carousel-track";
@@ -2963,11 +3226,16 @@ var Expressive = (() => {
2963
3226
  this._trackEl = null;
2964
3227
  }
2965
3228
  _nearestIndex() {
2966
- const left = this._scroller.scrollLeft;
3229
+ const centered = this.el.classList.contains("center-aligned");
3230
+ const position = this._vertical ? this._scroller.scrollTop : this._scroller.scrollLeft;
3231
+ const viewportCenter = position + (this._vertical ? this._scroller.clientHeight : this._scroller.clientWidth) / 2;
2967
3232
  let best = 0;
2968
3233
  let bestDist = Infinity;
2969
3234
  this.images.forEach((el, i) => {
2970
- const dist = Math.abs(el.offsetLeft - left);
3235
+ const start = this._vertical ? el.offsetTop : el.offsetLeft;
3236
+ const size = this._vertical ? el.offsetHeight : el.offsetWidth;
3237
+ const target = centered ? start + size / 2 : start;
3238
+ const dist = Math.abs(target - (centered ? viewportCenter : position));
2971
3239
  if (dist < bestDist) {
2972
3240
  bestDist = dist;
2973
3241
  best = i;
@@ -2978,13 +3246,24 @@ var Expressive = (() => {
2978
3246
  _scrollToIndex(n, smooth = true) {
2979
3247
  const el = this.images[n];
2980
3248
  if (!el) return;
2981
- const left = el.offsetLeft;
2982
- if (Math.abs(this._scroller.scrollLeft - left) < 1) {
3249
+ const centered = this.el.classList.contains("center-aligned");
3250
+ const style = getComputedStyle(this._scroller);
3251
+ const inlinePadding = Number.parseFloat(style.paddingInlineStart) || 0;
3252
+ const blockPadding = Number.parseFloat(style.paddingBlockStart) || 0;
3253
+ const left = centered ? el.offsetLeft - (this._scroller.clientWidth - el.offsetWidth) / 2 : el.offsetLeft - inlinePadding;
3254
+ const top = el.offsetTop - blockPadding;
3255
+ const current = this._vertical ? this._scroller.scrollTop : this._scroller.scrollLeft;
3256
+ const target = this._vertical ? top : left;
3257
+ if (Math.abs(current - target) < 1) {
2983
3258
  this._syncActive(n, false);
2984
3259
  return;
2985
3260
  }
2986
3261
  this._ignoreScroll = true;
2987
- this._scroller.scrollTo({ left, behavior: smooth ? "smooth" : "auto" });
3262
+ this._scroller.scrollTo({
3263
+ left: this._vertical ? 0 : left,
3264
+ top: this._vertical ? top : 0,
3265
+ behavior: smooth ? "smooth" : "auto"
3266
+ });
2988
3267
  const done = () => {
2989
3268
  this._ignoreScroll = false;
2990
3269
  };
@@ -2992,63 +3271,38 @@ var Expressive = (() => {
2992
3271
  window.setTimeout(done, this.options.duration + 350);
2993
3272
  }
2994
3273
  _cycleTo(n, callback = null) {
2995
- if (this._flat) {
2996
- if (typeof callback === "function") this.oneTimeCallback = callback;
2997
- this._syncActive(n, false);
2998
- this._scrollToIndex(n);
2999
- return;
3000
- }
3001
- let diff = this.center % this.count - n;
3002
- if (!this.noWrap) {
3003
- if (diff < 0) {
3004
- if (Math.abs(diff + this.count) < Math.abs(diff)) {
3005
- diff += this.count;
3006
- }
3007
- } else if (diff > 0) {
3008
- if (Math.abs(diff - this.count) < diff) {
3009
- diff -= this.count;
3010
- }
3011
- }
3012
- }
3013
- this.target = this.dim * Math.round(this.offset / this.dim);
3014
- if (diff < 0) {
3015
- this.target += this.dim * Math.abs(diff);
3016
- } else if (diff > 0) {
3017
- this.target -= this.dim * diff;
3018
- }
3019
- if (typeof callback === "function") {
3020
- this.oneTimeCallback = callback;
3021
- }
3022
- if (this.offset !== this.target) {
3023
- this.amplitude = this.target - this.offset;
3024
- this.timestamp = Date.now();
3025
- requestAnimationFrame(this._autoScroll);
3026
- }
3274
+ if (typeof callback === "function") this.oneTimeCallback = callback;
3275
+ this._syncActive(n, false);
3276
+ this._scrollToIndex(n);
3277
+ }
3278
+ /**
3279
+ * Stop auto-advance until start() is called. Without an `interval` there is
3280
+ * nothing to stop and this does nothing.
3281
+ */
3282
+ pause() {
3283
+ this._autoPaused = true;
3284
+ this._syncAutoAdvance();
3285
+ }
3286
+ /** Resume auto-advance after pause(). */
3287
+ start() {
3288
+ this._autoPaused = false;
3289
+ this._syncAutoAdvance();
3027
3290
  }
3028
3291
  next(n = 1) {
3029
3292
  if (n === void 0 || isNaN(n)) n = 1;
3030
- let index = this.center + n;
3031
- if (index >= this.count || index < 0) {
3032
- if (this.noWrap) return;
3033
- index = this._wrap(index);
3034
- }
3293
+ const index = this.center + n;
3294
+ if (index >= this.count || index < 0) return;
3035
3295
  this._cycleTo(index);
3036
3296
  }
3037
3297
  prev(n = 1) {
3038
3298
  if (n === void 0 || isNaN(n)) n = 1;
3039
- let index = this.center - n;
3040
- if (index >= this.count || index < 0) {
3041
- if (this.noWrap) return;
3042
- index = this._wrap(index);
3043
- }
3299
+ const index = this.center - n;
3300
+ if (index >= this.count || index < 0) return;
3044
3301
  this._cycleTo(index);
3045
3302
  }
3046
3303
  set(n, callback) {
3047
3304
  if (n === void 0 || isNaN(n)) n = 0;
3048
- if (n > this.count || n < 0) {
3049
- if (this.noWrap) return;
3050
- n = this._wrap(n);
3051
- }
3305
+ if (n >= this.count || n < 0) return;
3052
3306
  this._cycleTo(n, callback);
3053
3307
  }
3054
3308
  };
@@ -3060,7 +3314,7 @@ var Expressive = (() => {
3060
3314
  var Carousel = _Carousel;
3061
3315
 
3062
3316
  // src/ts/components/characterCounter.ts
3063
- var _defaults8 = Object.freeze({});
3317
+ var _defaults9 = Object.freeze({});
3064
3318
  var CharacterCounter = class _CharacterCounter extends Component {
3065
3319
  constructor(el, options) {
3066
3320
  super(el, {}, _CharacterCounter);
@@ -3085,7 +3339,7 @@ var Expressive = (() => {
3085
3339
  this._setupEventHandlers();
3086
3340
  }
3087
3341
  static get defaults() {
3088
- return _defaults8;
3342
+ return _defaults9;
3089
3343
  }
3090
3344
  /**
3091
3345
  * Initializes instances of CharacterCounter.
@@ -3114,6 +3368,8 @@ var Expressive = (() => {
3114
3368
  _setupCounter() {
3115
3369
  this.counterEl = document.createElement("span");
3116
3370
  this.counterEl.classList.add("character-counter");
3371
+ this.counterEl.setAttribute("aria-live", "polite");
3372
+ this.counterEl.setAttribute("aria-atomic", "true");
3117
3373
  this.counterEl.style.float = "right";
3118
3374
  this.counterEl.style.fontSize = "12px";
3119
3375
  this.counterEl.style.height = "1";
@@ -3135,7 +3391,7 @@ var Expressive = (() => {
3135
3391
  };
3136
3392
 
3137
3393
  // src/ts/components/select.ts
3138
- var _defaults9 = {
3394
+ var _defaults10 = {
3139
3395
  classes: "",
3140
3396
  menuOptions: {}
3141
3397
  };
@@ -3184,7 +3440,7 @@ var Expressive = (() => {
3184
3440
  this._setupEventHandlers();
3185
3441
  }
3186
3442
  static get defaults() {
3187
- return _defaults9;
3443
+ return _defaults10;
3188
3444
  }
3189
3445
  /**
3190
3446
  * Initializes instances of FormSelect.
@@ -3355,10 +3611,8 @@ var Expressive = (() => {
3355
3611
  menuOptions.onOpenEnd = () => {
3356
3612
  const selectedOption = this.menuEl.querySelector(".selected");
3357
3613
  if (selectedOption) {
3358
- Utils.keyDown = true;
3359
3614
  this.menu.focusedIndex = [...selectedOption.parentNode.children].indexOf(selectedOption);
3360
3615
  this.menu._focusFocusedItem();
3361
- Utils.keyDown = false;
3362
3616
  if (this.menu.isScrollable) {
3363
3617
  let scrollOffset = selectedOption.getBoundingClientRect().top - this.menuEl.getBoundingClientRect().top;
3364
3618
  scrollOffset -= this.menuEl.clientHeight / 2;
@@ -3558,7 +3812,7 @@ var Expressive = (() => {
3558
3812
  };
3559
3813
 
3560
3814
  // src/ts/plugins/dockedDisplayPlugin.ts
3561
- var _defaults10 = {
3815
+ var _defaults11 = {
3562
3816
  margin: 5,
3563
3817
  transition: 10,
3564
3818
  duration: 250,
@@ -3607,7 +3861,7 @@ var Expressive = (() => {
3607
3861
  };
3608
3862
  this.el = el;
3609
3863
  this.options = {
3610
- ..._defaults10,
3864
+ ..._defaults11,
3611
3865
  ...options
3612
3866
  };
3613
3867
  this.container = document.createElement("div");
@@ -3628,7 +3882,7 @@ var Expressive = (() => {
3628
3882
  };
3629
3883
 
3630
3884
  // src/ts/components/datepicker.ts
3631
- var _defaults11 = {
3885
+ var _defaults12 = {
3632
3886
  // the default output format for the input field value
3633
3887
  format: "mmm dd, yyyy",
3634
3888
  // Used to create date object from current input string
@@ -3753,7 +4007,7 @@ var Expressive = (() => {
3753
4007
  if (this.options.onInputInteraction) this.options.onInputInteraction.call(this);
3754
4008
  };
3755
4009
  this._handleInputKeydown = (e) => {
3756
- if (Utils.keys.ENTER.includes(e.key)) {
4010
+ if (e.key === Utils.keys.ENTER) {
3757
4011
  e.preventDefault();
3758
4012
  this._batchDraws(() => {
3759
4013
  this.setDateFromInput(e.target);
@@ -3893,7 +4147,7 @@ var Expressive = (() => {
3893
4147
  }
3894
4148
  }
3895
4149
  static get defaults() {
3896
- return _defaults11;
4150
+ return _defaults12;
3897
4151
  }
3898
4152
  /**
3899
4153
  * Initializes instances of Datepicker.
@@ -4476,11 +4730,11 @@ var Expressive = (() => {
4476
4730
  const monthSelect = this.calendarEl.querySelector(".orig-select-month");
4477
4731
  FormSelect.init(yearSelect, {
4478
4732
  classes: "select-year",
4479
- menuOptions: { container: document.body, constrainWidth: false }
4733
+ menuOptions: { container: Utils.portalRoot(this.el), constrainWidth: false }
4480
4734
  });
4481
4735
  FormSelect.init(monthSelect, {
4482
4736
  classes: "select-month",
4483
- menuOptions: { container: document.body, constrainWidth: false }
4737
+ menuOptions: { container: Utils.portalRoot(this.el), constrainWidth: false }
4484
4738
  });
4485
4739
  yearSelect.addEventListener("change", this._handleYearChange);
4486
4740
  monthSelect.addEventListener("change", this._handleMonthChange);
@@ -4611,7 +4865,7 @@ var Expressive = (() => {
4611
4865
  var Datepicker = _Datepicker;
4612
4866
 
4613
4867
  // src/ts/components/lightbox.ts
4614
- var _defaults12 = {
4868
+ var _defaults13 = {
4615
4869
  inDuration: 275,
4616
4870
  outDuration: 200,
4617
4871
  onOpenStart: null,
@@ -4626,7 +4880,7 @@ var Expressive = (() => {
4626
4880
  this._handleLightboxToggle();
4627
4881
  };
4628
4882
  this._handleLightboxKeypress = (e) => {
4629
- if (Utils.keys.ENTER.includes(e.key)) {
4883
+ if (e.key === Utils.keys.ENTER) {
4630
4884
  this._handleLightboxToggle();
4631
4885
  }
4632
4886
  };
@@ -4641,7 +4895,7 @@ var Expressive = (() => {
4641
4895
  if (this.overlayActive) this.close();
4642
4896
  };
4643
4897
  this._handleWindowEscape = (e) => {
4644
- if (Utils.keys.ESC.includes(e.key) && this.doneAnimating && this.overlayActive) this.close();
4898
+ if (e.key === Utils.keys.ESC && this.doneAnimating && this.overlayActive) this.close();
4645
4899
  };
4646
4900
  /**
4647
4901
  * Open lightbox.
@@ -4730,7 +4984,7 @@ var Expressive = (() => {
4730
4984
  this._setupEventHandlers();
4731
4985
  }
4732
4986
  static get defaults() {
4733
- return _defaults12;
4987
+ return _defaults13;
4734
4988
  }
4735
4989
  /**
4736
4990
  * Initializes instances of Lightbox.
@@ -4760,13 +5014,13 @@ var Expressive = (() => {
4760
5014
  _makeAncestorsOverflowVisible() {
4761
5015
  this._changedAncestorList = [];
4762
5016
  let ancestor = this.placeholder.parentNode;
4763
- while (ancestor !== null && ancestor !== document) {
5017
+ while (ancestor !== null && ancestor !== void 0 && ancestor !== document) {
4764
5018
  const curr = ancestor;
4765
- if (curr.style.overflow !== "visible") {
5019
+ if (curr.style && curr.style.overflow !== "visible") {
5020
+ this._changedAncestorList.push([curr, curr.style.overflow]);
4766
5021
  curr.style.overflow = "visible";
4767
- this._changedAncestorList.push(curr);
4768
5022
  }
4769
- ancestor = ancestor.parentNode;
5023
+ ancestor = ancestor.parentNode ?? ancestor.host;
4770
5024
  }
4771
5025
  }
4772
5026
  _offset(el) {
@@ -4830,7 +5084,7 @@ var Expressive = (() => {
4830
5084
  if (this.originInlineStyles) this.el.setAttribute("style", this.originInlineStyles);
4831
5085
  this.el.classList.remove("active");
4832
5086
  this.doneAnimating = true;
4833
- this._changedAncestorList.forEach((anchestor) => anchestor.style.overflow = "");
5087
+ this._changedAncestorList.forEach(([ancestor, overflow]) => ancestor.style.overflow = overflow);
4834
5088
  if (typeof this.options.onCloseEnd === "function")
4835
5089
  this.options.onCloseEnd.call(this, this.el);
4836
5090
  }, duration);
@@ -4840,7 +5094,7 @@ var Expressive = (() => {
4840
5094
  this._photoCaption = document.createElement("div");
4841
5095
  this._photoCaption.classList.add("lightbox-caption");
4842
5096
  this._photoCaption.innerText = this.caption;
4843
- document.body.append(this._photoCaption);
5097
+ Utils.portalRoot(this.el).append(this._photoCaption);
4844
5098
  this._photoCaption.style.display = "inline";
4845
5099
  this._photoCaption.style.transition = "none";
4846
5100
  this._photoCaption.style.opacity = "0";
@@ -4894,34 +5148,6 @@ var Expressive = (() => {
4894
5148
  }
4895
5149
  };
4896
5150
 
4897
- // src/ts/components/parallax.ts
4898
- var _defaults13 = {};
4899
- var Parallax = class _Parallax extends Component {
4900
- constructor(el, options) {
4901
- super(el, options, _Parallax);
4902
- this.el["Expressive_Parallax"] = this;
4903
- this.options = {
4904
- ..._Parallax.defaults,
4905
- ...options
4906
- };
4907
- if (!this.el.querySelector("img, video")) {
4908
- console.error("Parallax: no <img> or <video> to move inside the .parallax element");
4909
- }
4910
- }
4911
- static get defaults() {
4912
- return _defaults13;
4913
- }
4914
- static init(els, options = {}) {
4915
- return super.init(els, options, _Parallax);
4916
- }
4917
- static getInstance(el) {
4918
- return el["Expressive_Parallax"];
4919
- }
4920
- destroy() {
4921
- this.el["Expressive_Parallax"] = void 0;
4922
- }
4923
- };
4924
-
4925
5151
  // src/ts/components/scrollspy.ts
4926
5152
  var _defaultGetActiveElement = (id) => `a[href="#${id}"]`;
4927
5153
  var _defaults14 = {
@@ -5068,7 +5294,7 @@ var Expressive = (() => {
5068
5294
  };
5069
5295
  var ScrollSpy = _ScrollSpy;
5070
5296
 
5071
- // src/ts/components/sidenav.ts
5297
+ // src/ts/components/navigationDrawer.ts
5072
5298
  var _defaults15 = {
5073
5299
  edge: "left",
5074
5300
  draggable: true,
@@ -5078,17 +5304,17 @@ var Expressive = (() => {
5078
5304
  onCloseStart: null,
5079
5305
  onCloseEnd: null
5080
5306
  };
5081
- var LARGE_UP = "(width >= 993px)";
5082
- var _Sidenav = class _Sidenav extends Component {
5307
+ var EXPANDED_UP = "(width >= 840px)";
5308
+ var _NavigationDrawer = class _NavigationDrawer extends Component {
5083
5309
  constructor(el, options) {
5084
- super(el, options, _Sidenav);
5310
+ super(el, options, _NavigationDrawer);
5085
5311
  /**
5086
5312
  * Opens the overlay drawer. No-op while the sidenav is docked
5087
- * (sidenav-fixed at the large breakpoint).
5313
+ * (navigation-drawer-fixed at the Expanded breakpoint).
5088
5314
  */
5089
5315
  this.open = () => {
5090
5316
  if (this._isCurrentlyFixed() || this.isOpen || !this._dialog) return;
5091
- _Sidenav._sidenavs.forEach((other) => {
5317
+ _NavigationDrawer._sidenavs.forEach((other) => {
5092
5318
  if (other !== this && other.isOpen) other.close();
5093
5319
  });
5094
5320
  if (typeof this.options.onOpenStart === "function") {
@@ -5140,7 +5366,7 @@ var Expressive = (() => {
5140
5366
  this._handleCloseTriggerClick = (e) => {
5141
5367
  const target = e.target;
5142
5368
  if (!(target instanceof Element)) return;
5143
- const closeTrigger = target.closest(".sidenav-close");
5369
+ const closeTrigger = target.closest(".sidenav-close, .navigation-drawer-close");
5144
5370
  if (closeTrigger && !this._isCurrentlyFixed()) {
5145
5371
  this.close();
5146
5372
  }
@@ -5184,14 +5410,14 @@ var Expressive = (() => {
5184
5410
  this.isDragged = false;
5185
5411
  this._verticallyScrolling = false;
5186
5412
  };
5187
- this.el["Expressive_Sidenav"] = this;
5413
+ this.el["Expressive_NavigationDrawer"] = this;
5188
5414
  this.options = {
5189
- ..._Sidenav.defaults,
5415
+ ..._NavigationDrawer.defaults,
5190
5416
  ...options
5191
5417
  };
5192
5418
  this.id = this.el.id;
5193
5419
  this.isOpen = false;
5194
- this.isFixed = this.el.classList.contains("sidenav-fixed");
5420
+ this.isFixed = this.el.classList.contains("navigation-drawer-fixed") || this.el.classList.contains("sidenav-fixed");
5195
5421
  this.isDragged = false;
5196
5422
  this.dragTarget = null;
5197
5423
  this._dialog = null;
@@ -5203,27 +5429,27 @@ var Expressive = (() => {
5203
5429
  this._width = 0;
5204
5430
  this._initialScrollTop = 0;
5205
5431
  this._verticallyScrolling = false;
5206
- this._mql = window.matchMedia(LARGE_UP);
5432
+ this._mql = window.matchMedia(EXPANDED_UP);
5207
5433
  this._setupDialog();
5208
5434
  this._createDragTarget();
5209
5435
  this._setupClasses();
5210
5436
  this._setupEventHandlers();
5211
5437
  this._syncTriggers();
5212
- _Sidenav._sidenavs.push(this);
5438
+ _NavigationDrawer._sidenavs.push(this);
5213
5439
  }
5214
5440
  static get defaults() {
5215
5441
  return _defaults15;
5216
5442
  }
5217
5443
  /**
5218
- * Initializes instances of Sidenav.
5444
+ * Initializes instances of NavigationDrawer.
5219
5445
  * @param els HTML elements.
5220
5446
  * @param options Component options.
5221
5447
  */
5222
5448
  static init(els, options = {}) {
5223
- return super.init(els, options, _Sidenav);
5449
+ return super.init(els, options, _NavigationDrawer);
5224
5450
  }
5225
5451
  static getInstance(el) {
5226
- return el["Expressive_Sidenav"];
5452
+ return el["Expressive_NavigationDrawer"];
5227
5453
  }
5228
5454
  destroy() {
5229
5455
  this._removeEventHandlers();
@@ -5236,10 +5462,10 @@ var Expressive = (() => {
5236
5462
  this._clearShift();
5237
5463
  this._unwrapDialog();
5238
5464
  this._removeDragTarget();
5239
- this.el["Expressive_Sidenav"] = void 0;
5240
- const index = _Sidenav._sidenavs.indexOf(this);
5465
+ this.el["Expressive_NavigationDrawer"] = void 0;
5466
+ const index = _NavigationDrawer._sidenavs.indexOf(this);
5241
5467
  if (index >= 0) {
5242
- _Sidenav._sidenavs.splice(index, 1);
5468
+ _NavigationDrawer._sidenavs.splice(index, 1);
5243
5469
  }
5244
5470
  }
5245
5471
  _beginClose() {
@@ -5270,6 +5496,11 @@ var Expressive = (() => {
5270
5496
  }
5271
5497
  const dialog = document.createElement("dialog");
5272
5498
  dialog.classList.add("sidenav-overlay");
5499
+ const named = this.el.closest("[aria-label], [aria-labelledby]") ?? this.el;
5500
+ const label = named.getAttribute("aria-label");
5501
+ const labelledBy = named.getAttribute("aria-labelledby");
5502
+ if (label) dialog.setAttribute("aria-label", label);
5503
+ else if (labelledBy) dialog.setAttribute("aria-labelledby", labelledBy);
5273
5504
  this.el.replaceWith(dialog);
5274
5505
  dialog.appendChild(this.el);
5275
5506
  this._dialog = dialog;
@@ -5287,7 +5518,7 @@ var Expressive = (() => {
5287
5518
  dragTarget.classList.add("drag-target");
5288
5519
  dragTarget.style.width = this.options.dragTargetWidth;
5289
5520
  if (this.isFixed) dragTarget.dataset.fixed = "";
5290
- document.body.appendChild(dragTarget);
5521
+ Utils.portalRoot(this.el).appendChild(dragTarget);
5291
5522
  this.dragTarget = dragTarget;
5292
5523
  }
5293
5524
  _removeDragTarget() {
@@ -5302,8 +5533,8 @@ var Expressive = (() => {
5302
5533
  }
5303
5534
  }
5304
5535
  _setupEventHandlers() {
5305
- if (_Sidenav._sidenavs.length === 0) {
5306
- document.body.addEventListener("click", _Sidenav._handleTriggerClick);
5536
+ if (_NavigationDrawer._sidenavs.length === 0) {
5537
+ document.body.addEventListener("click", _NavigationDrawer._handleTriggerClick);
5307
5538
  }
5308
5539
  const passive = { passive: true };
5309
5540
  this.dragTarget?.addEventListener("touchmove", this._handleDragTargetDrag, passive);
@@ -5315,8 +5546,8 @@ var Expressive = (() => {
5315
5546
  this._mql.addEventListener("change", this._onBreakpointChange);
5316
5547
  }
5317
5548
  _removeEventHandlers() {
5318
- if (_Sidenav._sidenavs.length === 1) {
5319
- document.body.removeEventListener("click", _Sidenav._handleTriggerClick);
5549
+ if (_NavigationDrawer._sidenavs.length === 1) {
5550
+ document.body.removeEventListener("click", _NavigationDrawer._handleTriggerClick);
5320
5551
  }
5321
5552
  this.dragTarget?.removeEventListener("touchmove", this._handleDragTargetDrag);
5322
5553
  this.dragTarget?.removeEventListener("touchend", this._handleDragTargetRelease);
@@ -5329,10 +5560,10 @@ var Expressive = (() => {
5329
5560
  static _handleTriggerClick(e) {
5330
5561
  const target = e.target;
5331
5562
  if (!(target instanceof Element)) return;
5332
- const trigger = target.closest(".sidenav-trigger");
5563
+ const trigger = target.closest(".sidenav-trigger, .navigation-drawer-trigger");
5333
5564
  if (!trigger || !(trigger instanceof HTMLElement)) return;
5334
5565
  const sidenavId = Utils.getIdFromTrigger(trigger);
5335
- const sidenavInstance = document.getElementById(sidenavId)?.["Expressive_Sidenav"];
5566
+ const sidenavInstance = Utils.getElementById(trigger, sidenavId)?.["Expressive_NavigationDrawer"];
5336
5567
  if (sidenavInstance) {
5337
5568
  sidenavInstance.open();
5338
5569
  e.preventDefault();
@@ -5386,7 +5617,7 @@ var Expressive = (() => {
5386
5617
  }
5387
5618
  _syncTriggers() {
5388
5619
  if (!this.id) return;
5389
- document.querySelectorAll(".sidenav-trigger").forEach((trigger) => {
5620
+ document.querySelectorAll(".sidenav-trigger, .navigation-drawer-trigger").forEach((trigger) => {
5390
5621
  if (!(trigger instanceof HTMLElement)) return;
5391
5622
  if (Utils.getIdFromTrigger(trigger) !== this.id) return;
5392
5623
  trigger.setAttribute("aria-expanded", this.isOpen ? "true" : "false");
@@ -5394,8 +5625,8 @@ var Expressive = (() => {
5394
5625
  });
5395
5626
  }
5396
5627
  };
5397
- _Sidenav._sidenavs = [];
5398
- var Sidenav = _Sidenav;
5628
+ _NavigationDrawer._sidenavs = [];
5629
+ var NavigationDrawer = _NavigationDrawer;
5399
5630
 
5400
5631
  // src/ts/components/navigationRail.ts
5401
5632
  var _defaults16 = {
@@ -5404,7 +5635,7 @@ var Expressive = (() => {
5404
5635
  onCloseStart: null,
5405
5636
  onCloseEnd: null
5406
5637
  };
5407
- var COMPACT = "(width < 601px)";
5638
+ var COMPACT = "(width < 600px)";
5408
5639
  var NavigationRail = class _NavigationRail extends Component {
5409
5640
  constructor(el, options) {
5410
5641
  super(el, options, _NavigationRail);
@@ -5503,309 +5734,8 @@ var Expressive = (() => {
5503
5734
  }
5504
5735
  };
5505
5736
 
5506
- // src/ts/components/slider.ts
5507
- var _defaults17 = {
5508
- indicators: true,
5509
- height: 400,
5510
- duration: 500,
5511
- interval: 6e3,
5512
- pauseOnFocus: true,
5513
- pauseOnHover: true,
5514
- indicatorLabelFunc: null
5515
- // Function which will generate a label for the indicators (ARIA)
5516
- };
5517
- var Slider = class _Slider extends Component {
5518
- constructor(el, options) {
5519
- super(el, options, _Slider);
5520
- // Nobody is watching a backgrounded tab. Browsers clamp the timer rather
5521
- // than stopping it, so without this the slider keeps cycling slides - and
5522
- // running their transitions - out of sight.
5523
- this._handleVisibilityChange = () => {
5524
- if (document.hidden) {
5525
- if (this.interval != null) this._pause(true);
5526
- } else if (this.eventPause && !(this.options.pauseOnHover && this._hovered) && !(this.options.pauseOnFocus && this._focused)) {
5527
- this.start();
5528
- }
5529
- };
5530
- this._handleIndicatorClick = (e) => {
5531
- const el = e.target.parentElement;
5532
- const currIndex = [...el.parentNode.children].indexOf(el);
5533
- this._focusCurrent = true;
5534
- this.set(currIndex);
5535
- };
5536
- this._handleAutoPauseHover = () => {
5537
- this._hovered = true;
5538
- if (this.interval != null) {
5539
- this._pause(true);
5540
- }
5541
- };
5542
- this._handleAutoPauseFocus = () => {
5543
- this._focused = true;
5544
- if (this.interval != null) {
5545
- this._pause(true);
5546
- }
5547
- };
5548
- this._handleAutoStartHover = () => {
5549
- this._hovered = false;
5550
- if (!(this.options.pauseOnFocus && this._focused) && this.eventPause) {
5551
- this.start();
5552
- }
5553
- };
5554
- this._handleAutoStartFocus = () => {
5555
- this._focused = false;
5556
- if (!(this.options.pauseOnHover && this._hovered) && this.eventPause) {
5557
- this.start();
5558
- }
5559
- };
5560
- this._handleInterval = () => {
5561
- const activeElem = this._slider.querySelector(".active");
5562
- let newActiveIndex = [...activeElem.parentNode.children].indexOf(activeElem);
5563
- if (this._slides.length === newActiveIndex + 1)
5564
- newActiveIndex = 0;
5565
- else newActiveIndex += 1;
5566
- this.set(newActiveIndex);
5567
- };
5568
- /**
5569
- * Pause slider autoslide.
5570
- */
5571
- this.pause = () => {
5572
- this._pause(false);
5573
- };
5574
- /**
5575
- * Start slider autoslide.
5576
- */
5577
- this.start = () => {
5578
- clearInterval(this.interval);
5579
- this.interval = setInterval(
5580
- this._handleInterval,
5581
- this.options.duration + this.options.interval
5582
- );
5583
- this.eventPause = false;
5584
- };
5585
- /**
5586
- * Move to next slider.
5587
- */
5588
- this.next = () => {
5589
- let newIndex = this.activeIndex + 1;
5590
- if (newIndex >= this._slides.length) newIndex = 0;
5591
- else if (newIndex < 0) newIndex = this._slides.length - 1;
5592
- this.set(newIndex);
5593
- };
5594
- /**
5595
- * Move to prev slider.
5596
- */
5597
- this.prev = () => {
5598
- let newIndex = this.activeIndex - 1;
5599
- if (newIndex >= this._slides.length) newIndex = 0;
5600
- else if (newIndex < 0) newIndex = this._slides.length - 1;
5601
- this.set(newIndex);
5602
- };
5603
- this.el["Expressive_Slider"] = this;
5604
- this.options = {
5605
- ..._Slider.defaults,
5606
- ...options
5607
- };
5608
- this.interval = null;
5609
- this.eventPause = false;
5610
- this._hovered = false;
5611
- this._focused = false;
5612
- this._focusCurrent = false;
5613
- this._slider = this.el.querySelector(".slides");
5614
- this._slides = Array.from(this._slider.querySelectorAll("li"));
5615
- this.activeIndex = this._slides.findIndex((li) => li.classList.contains("active"));
5616
- if (this.activeIndex !== -1) {
5617
- this._activeSlide = this._slides[this.activeIndex];
5618
- }
5619
- this._setSliderHeight();
5620
- if (this._slider.hasAttribute("id")) this._sliderId = this._slider.getAttribute("id");
5621
- else {
5622
- this._sliderId = "slider-" + Utils.guid();
5623
- this._slider.setAttribute("id", this._sliderId);
5624
- }
5625
- const placeholderBase64 = "data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
5626
- this._slides.forEach((slide) => {
5627
- const img = slide.querySelector("img");
5628
- if (img) {
5629
- if (img.src !== placeholderBase64) {
5630
- img.style.backgroundImage = "url(" + img.src + ")";
5631
- img.src = placeholderBase64;
5632
- }
5633
- }
5634
- if (!slide.hasAttribute("tabindex")) slide.setAttribute("tabindex", "-1");
5635
- slide.style.visibility = "hidden";
5636
- });
5637
- this._setupIndicators();
5638
- if (this._activeSlide) {
5639
- this._activeSlide.style.display = "block";
5640
- this._activeSlide.style.visibility = "visible";
5641
- } else {
5642
- this.activeIndex = 0;
5643
- this._slides[0].classList.add("active");
5644
- this._slides[0].style.visibility = "visible";
5645
- this._activeSlide = this._slides[0];
5646
- this._animateSlide(this._slides[0], true);
5647
- if (this.options.indicators) {
5648
- this._indicators[this.activeIndex].children[0].classList.add("active");
5649
- }
5650
- }
5651
- this._setupEventHandlers();
5652
- this.start();
5653
- }
5654
- static get defaults() {
5655
- return _defaults17;
5656
- }
5657
- /**
5658
- * Initializes instances of Slider.
5659
- * @param els HTML elements.
5660
- * @param options Component options.
5661
- */
5662
- static init(els, options = {}) {
5663
- return super.init(els, options, _Slider);
5664
- }
5665
- static getInstance(el) {
5666
- return el["Expressive_Slider"];
5667
- }
5668
- destroy() {
5669
- this.pause();
5670
- this._removeIndicators();
5671
- this._removeEventHandlers();
5672
- this.el["Expressive_Slider"] = void 0;
5673
- }
5674
- _setupEventHandlers() {
5675
- if (this.options.pauseOnFocus) {
5676
- this.el.addEventListener("focusin", this._handleAutoPauseFocus);
5677
- this.el.addEventListener("focusout", this._handleAutoStartFocus);
5678
- }
5679
- if (this.options.pauseOnHover) {
5680
- this.el.addEventListener("mouseenter", this._handleAutoPauseHover);
5681
- this.el.addEventListener("mouseleave", this._handleAutoStartHover);
5682
- }
5683
- if (this.options.indicators) {
5684
- this._indicators.forEach((el) => {
5685
- el.addEventListener("click", this._handleIndicatorClick);
5686
- });
5687
- }
5688
- document.addEventListener("visibilitychange", this._handleVisibilityChange);
5689
- }
5690
- _removeEventHandlers() {
5691
- if (this.options.pauseOnFocus) {
5692
- this.el.removeEventListener("focusin", this._handleAutoPauseFocus);
5693
- this.el.removeEventListener("focusout", this._handleAutoStartFocus);
5694
- }
5695
- if (this.options.pauseOnHover) {
5696
- this.el.removeEventListener("mouseenter", this._handleAutoPauseHover);
5697
- this.el.removeEventListener("mouseleave", this._handleAutoStartHover);
5698
- }
5699
- if (this.options.indicators) {
5700
- this._indicators.forEach((el) => {
5701
- el.removeEventListener("click", this._handleIndicatorClick);
5702
- });
5703
- }
5704
- document.removeEventListener("visibilitychange", this._handleVisibilityChange);
5705
- }
5706
- _animateSlide(slide, isDirectionIn) {
5707
- let dx = 0, dy = 0;
5708
- slide.style.opacity = isDirectionIn ? "0" : "1";
5709
- setTimeout(() => {
5710
- slide.style.transition = `opacity ${this.options.duration}ms ease`;
5711
- slide.style.opacity = isDirectionIn ? "1" : "0";
5712
- }, 1);
5713
- const caption = slide.querySelector(".caption");
5714
- if (!caption) return;
5715
- if (caption.classList.contains("center-align")) dy = -100;
5716
- else if (caption.classList.contains("right-align")) dx = 100;
5717
- else if (caption.classList.contains("left-align")) dx = -100;
5718
- caption.style.opacity = isDirectionIn ? "0" : "1";
5719
- caption.style.transform = isDirectionIn ? `translate(${dx}px, ${dy}px)` : `translate(0, 0)`;
5720
- setTimeout(() => {
5721
- caption.style.transition = `opacity ${this.options.duration}ms ease, transform ${this.options.duration}ms ease`;
5722
- caption.style.opacity = isDirectionIn ? "1" : "0";
5723
- caption.style.transform = isDirectionIn ? `translate(0, 0)` : `translate(${dx}px, ${dy}px)`;
5724
- }, this.options.duration);
5725
- }
5726
- _setSliderHeight() {
5727
- if (!this.el.classList.contains("fullscreen")) {
5728
- if (this.options.indicators) {
5729
- this.el.style.height = this.options.height + 40 + "px";
5730
- } else {
5731
- this.el.style.height = this.options.height + "px";
5732
- }
5733
- this._slider.style.height = this.options.height + "px";
5734
- }
5735
- }
5736
- _setupIndicators() {
5737
- if (this.options.indicators) {
5738
- const ul = document.createElement("ul");
5739
- ul.classList.add("indicators");
5740
- const arrLi = [];
5741
- this._slides.forEach((el, i) => {
5742
- const label = this.options.indicatorLabelFunc ? this.options.indicatorLabelFunc.call(this, i + 1, i === 0) : `${i + 1}`;
5743
- const li = document.createElement("li");
5744
- li.classList.add("indicator-item");
5745
- const button = document.createElement("button");
5746
- button.type = "button";
5747
- button.classList.add("indicator-item-btn");
5748
- button.setAttribute("aria-label", label);
5749
- button.setAttribute("aria-controls", this._sliderId);
5750
- li.appendChild(button);
5751
- arrLi.push(li);
5752
- ul.append(li);
5753
- });
5754
- this.el.append(ul);
5755
- this._indicators = arrLi;
5756
- }
5757
- }
5758
- _removeIndicators() {
5759
- this.el.querySelector("ul.indicators").remove();
5760
- }
5761
- set(index) {
5762
- if (index >= this._slides.length) index = 0;
5763
- else if (index < 0) index = this._slides.length - 1;
5764
- if (this.activeIndex === index) return;
5765
- this._activeSlide = this._slides[this.activeIndex];
5766
- const _caption = this._activeSlide.querySelector(".caption");
5767
- this._activeSlide.classList.remove("active");
5768
- this._slides.forEach((slide) => slide.style.visibility = "visible");
5769
- this._activeSlide.style.opacity = "0";
5770
- setTimeout(() => {
5771
- this._slides.forEach((slide) => {
5772
- if (slide.classList.contains("active")) return;
5773
- slide.style.opacity = "0";
5774
- slide.style.transform = "translate(0, 0)";
5775
- slide.style.visibility = "hidden";
5776
- });
5777
- }, this.options.duration);
5778
- if (_caption) _caption.style.opacity = "0";
5779
- if (this.options.indicators) {
5780
- const activeIndicator = this._indicators[this.activeIndex].children[0];
5781
- const nextIndicator = this._indicators[index].children[0];
5782
- activeIndicator.classList.remove("active");
5783
- nextIndicator.classList.add("active");
5784
- if (typeof this.options.indicatorLabelFunc === "function") {
5785
- activeIndicator.ariaLabel = this.options.indicatorLabelFunc.call(
5786
- this,
5787
- this.activeIndex,
5788
- false
5789
- );
5790
- nextIndicator.ariaLabel = this.options.indicatorLabelFunc.call(this, index, true);
5791
- }
5792
- }
5793
- this._animateSlide(this._slides[index], true);
5794
- this._slides[index].classList.add("active");
5795
- this.activeIndex = index;
5796
- if (this.interval != null) {
5797
- this.start();
5798
- }
5799
- }
5800
- _pause(fromEvent) {
5801
- clearInterval(this.interval);
5802
- this.eventPause = fromEvent;
5803
- this.interval = null;
5804
- }
5805
- };
5806
-
5807
5737
  // src/ts/components/tabs.ts
5808
- var _defaults18 = {
5738
+ var _defaults17 = {
5809
5739
  duration: 300,
5810
5740
  onShow: null,
5811
5741
  swipeable: false,
@@ -5834,12 +5764,10 @@ var Expressive = (() => {
5834
5764
  return;
5835
5765
  }
5836
5766
  if (tabLink.hasAttribute("target")) return;
5837
- this._activeTabLink.classList.remove("active");
5838
5767
  const _oldContent = this._content;
5839
- this._activeTabLink = tabLink;
5840
5768
  if (tabLink.hash) this._content = document.querySelector(tabLink.hash);
5841
5769
  this._tabLinks = this._queryTabLinks();
5842
- this._activeTabLink.classList.add("active");
5770
+ this._setActiveTabLink(tabLink);
5843
5771
  const prevIndex = this._index;
5844
5772
  this._index = Math.max(Array.from(this._tabLinks).indexOf(tabLink), 0);
5845
5773
  if (this.options.swipeable) {
@@ -5883,7 +5811,7 @@ var Expressive = (() => {
5883
5811
  this._setupEventHandlers();
5884
5812
  }
5885
5813
  static get defaults() {
5886
- return _defaults18;
5814
+ return _defaults17;
5887
5815
  }
5888
5816
  /**
5889
5817
  * Initializes instances of Tabs.
@@ -5934,6 +5862,26 @@ var Expressive = (() => {
5934
5862
  this._indicator.style.left = this._calcLeftPos(this._activeTabLink) + "px";
5935
5863
  this._indicator.style.right = this._calcRightPos(this._activeTabLink) + "px";
5936
5864
  }
5865
+ /**
5866
+ * Which tab is current, for the eye and for assistive technology.
5867
+ *
5868
+ * The class alone is a colour. `aria-current` is the part a screen reader
5869
+ * reads, and because tabs are navigation rather than a tablist (rule 2 -
5870
+ * there is no keyboard model here to back a tablist), "current" is exactly
5871
+ * the right word for it. Its *value* changes as the user clicks, which makes
5872
+ * maintaining it the component's job, not the author's - see CONTEXT.md on
5873
+ * static semantics versus dynamic state. Writing it once in the markup and
5874
+ * never moving it left the old tab announcing itself as current.
5875
+ */
5876
+ _setActiveTabLink(tabLink) {
5877
+ Array.from(this._tabLinks).forEach((a) => {
5878
+ a.classList.remove("active");
5879
+ a.removeAttribute("aria-current");
5880
+ });
5881
+ this._activeTabLink = tabLink;
5882
+ tabLink.classList.add("active");
5883
+ tabLink.setAttribute("aria-current", "page");
5884
+ }
5937
5885
  _setupActiveTabLink() {
5938
5886
  this._activeTabLink = Array.from(this._tabLinks).find(
5939
5887
  (a) => a.getAttribute("href") === location.hash
@@ -5945,8 +5893,7 @@ var Expressive = (() => {
5945
5893
  }
5946
5894
  this._activeTabLink = activeTabLink;
5947
5895
  }
5948
- Array.from(this._tabLinks).forEach((a) => a.classList.remove("active"));
5949
- this._activeTabLink.classList.add("active");
5896
+ this._setActiveTabLink(this._activeTabLink);
5950
5897
  this._index = Math.max(Array.from(this._tabLinks).indexOf(this._activeTabLink), 0);
5951
5898
  if (this._activeTabLink && this._activeTabLink.hash) {
5952
5899
  this._content = document.querySelector(this._activeTabLink.hash);
@@ -6051,7 +5998,7 @@ var Expressive = (() => {
6051
5998
  };
6052
5999
 
6053
6000
  // src/ts/components/timepicker.ts
6054
- var _defaults19 = {
6001
+ var _defaults18 = {
6055
6002
  dialRadius: 135,
6056
6003
  outerRadius: 105,
6057
6004
  innerRadius: 70,
@@ -6085,6 +6032,8 @@ var Expressive = (() => {
6085
6032
  var _Timepicker = class _Timepicker extends Component {
6086
6033
  constructor(el, options) {
6087
6034
  super(el, options, _Timepicker);
6035
+ /** Which pointer is dragging the clock hand; a second finger is ignored. */
6036
+ this._dragPointerId = null;
6088
6037
  /** Whether the dial (ticks + SVG + AM/PM buttons) has been built yet. */
6089
6038
  this._clockBuilt = false;
6090
6039
  this._handleInputClick = () => {
@@ -6094,7 +6043,7 @@ var Expressive = (() => {
6094
6043
  if (this.displayPlugin) this.displayPlugin.show();
6095
6044
  };
6096
6045
  this._handleInputKeydown = (e) => {
6097
- if (Utils.keys.ENTER.includes(e.key)) {
6046
+ if (e.key === Utils.keys.ENTER) {
6098
6047
  e.preventDefault();
6099
6048
  this._ensureClockBuilt();
6100
6049
  this.inputHours.focus();
@@ -6103,12 +6052,14 @@ var Expressive = (() => {
6103
6052
  }
6104
6053
  };
6105
6054
  this._handleTimeInputEnterKey = (e) => {
6106
- if (Utils.keys.ENTER.includes(e.key)) {
6055
+ if (e.key === Utils.keys.ENTER) {
6107
6056
  e.preventDefault();
6108
6057
  this._inputFromTextField();
6109
6058
  }
6110
6059
  };
6111
6060
  this._handleClockClickStart = (e) => {
6061
+ if (!e.isPrimary) return;
6062
+ if (e.pointerType === "mouse" && e.button !== 0) return;
6112
6063
  e.preventDefault();
6113
6064
  this._ensureClockBuilt();
6114
6065
  const clockPlateBR = this.plate.getBoundingClientRect();
@@ -6116,30 +6067,30 @@ var Expressive = (() => {
6116
6067
  this.x0 = offset.x + this.options.dialRadius;
6117
6068
  this.y0 = offset.y + this.options.dialRadius;
6118
6069
  this.moved = false;
6119
- const clickPos = _Timepicker._Pos(e);
6120
- this.dx = clickPos.x - this.x0;
6121
- this.dy = clickPos.y - this.y0;
6070
+ this._dragPointerId = e.pointerId;
6071
+ this.dx = e.clientX - this.x0;
6072
+ this.dy = e.clientY - this.y0;
6122
6073
  this.setHand(this.dx, this.dy, false);
6123
- document.addEventListener("mousemove", this._handleDocumentClickMove);
6124
- document.addEventListener("touchmove", this._handleDocumentClickMove);
6125
- document.addEventListener("mouseup", this._handleDocumentClickEnd);
6126
- document.addEventListener("touchend", this._handleDocumentClickEnd);
6074
+ document.addEventListener("pointermove", this._handleDocumentClickMove);
6075
+ document.addEventListener("pointerup", this._handleDocumentClickEnd);
6076
+ document.addEventListener("pointercancel", this._handleDocumentClickCancel);
6127
6077
  };
6128
6078
  this._handleDocumentClickMove = (e) => {
6079
+ if (e.pointerId !== this._dragPointerId) return;
6129
6080
  e.preventDefault();
6130
- const clickPos = _Timepicker._Pos(e);
6131
- const x = clickPos.x - this.x0;
6132
- const y = clickPos.y - this.y0;
6081
+ const x = e.clientX - this.x0;
6082
+ const y = e.clientY - this.y0;
6133
6083
  this.moved = true;
6134
6084
  this.setHand(x, y, false);
6135
6085
  };
6136
6086
  this._handleDocumentClickEnd = (e) => {
6087
+ if (e.pointerId !== this._dragPointerId) return;
6088
+ this._dragPointerId = null;
6137
6089
  e.preventDefault();
6138
- document.removeEventListener("mouseup", this._handleDocumentClickEnd);
6139
- document.removeEventListener("touchend", this._handleDocumentClickEnd);
6140
- const clickPos = _Timepicker._Pos(e);
6141
- const x = clickPos.x - this.x0;
6142
- const y = clickPos.y - this.y0;
6090
+ document.removeEventListener("pointerup", this._handleDocumentClickEnd);
6091
+ document.removeEventListener("pointercancel", this._handleDocumentClickCancel);
6092
+ const x = e.clientX - this.x0;
6093
+ const y = e.clientY - this.y0;
6143
6094
  if (this.moved && x === this.dx && y === this.dy) {
6144
6095
  this.setHand(x, y);
6145
6096
  }
@@ -6154,14 +6105,27 @@ var Expressive = (() => {
6154
6105
  if (typeof this.options.onSelect === "function") {
6155
6106
  this.options.onSelect.call(this, this.hours, this.minutes);
6156
6107
  }
6157
- document.removeEventListener("mousemove", this._handleDocumentClickMove);
6158
- document.removeEventListener("touchmove", this._handleDocumentClickMove);
6108
+ document.removeEventListener("pointermove", this._handleDocumentClickMove);
6109
+ };
6110
+ /**
6111
+ * A cancelled pointer is not a completed selection. The browser or the OS
6112
+ * took the gesture over, so this only lets go of the drag - it must not run
6113
+ * the pointer-up path, which advances hours to minutes, fires `onSelect`,
6114
+ * and under `autoSubmit` closes the picker on a time the user never chose.
6115
+ */
6116
+ this._handleDocumentClickCancel = (e) => {
6117
+ if (e.pointerId !== this._dragPointerId) return;
6118
+ this._dragPointerId = null;
6119
+ this.moved = false;
6120
+ document.removeEventListener("pointermove", this._handleDocumentClickMove);
6121
+ document.removeEventListener("pointerup", this._handleDocumentClickEnd);
6122
+ document.removeEventListener("pointercancel", this._handleDocumentClickCancel);
6159
6123
  };
6160
6124
  this._handleAmPmClick = (e) => {
6161
6125
  this._handleAmPmInteraction(e.target);
6162
6126
  };
6163
6127
  this._handleAmPmKeypress = (e) => {
6164
- if (Utils.keys.ENTER.includes(e.key)) {
6128
+ if (e.key === Utils.keys.ENTER) {
6165
6129
  this._handleAmPmInteraction(e.target);
6166
6130
  }
6167
6131
  };
@@ -6176,8 +6140,6 @@ var Expressive = (() => {
6176
6140
  */
6177
6141
  this.showView = (view, delay = null) => {
6178
6142
  this._ensureClockBuilt();
6179
- if (view === "minutes" && getComputedStyle(this.hoursView).visibility === "visible") {
6180
- }
6181
6143
  const isHours = view === "hours", nextView = isHours ? this.hoursView : this.minutesView, hideView = isHours ? this.minutesView : this.hoursView;
6182
6144
  this.currentView = view;
6183
6145
  hideView.classList.add("timepicker-dial-out");
@@ -6249,7 +6211,7 @@ var Expressive = (() => {
6249
6211
  }
6250
6212
  }
6251
6213
  static get defaults() {
6252
- return _defaults19;
6214
+ return _defaults18;
6253
6215
  }
6254
6216
  /**
6255
6217
  * Initializes instances of Timepicker.
@@ -6266,15 +6228,6 @@ var Expressive = (() => {
6266
6228
  const svgNS = "http://www.w3.org/2000/svg";
6267
6229
  return document.createElementNS(svgNS, name);
6268
6230
  }
6269
- static _Pos(e) {
6270
- if (e.type.startsWith("touch") && e.targetTouches.length >= 1) {
6271
- return {
6272
- x: e.targetTouches[0].clientX,
6273
- y: e.targetTouches[0].clientY
6274
- };
6275
- }
6276
- return { x: e.clientX, y: e.clientY };
6277
- }
6278
6231
  static getInstance(el) {
6279
6232
  return el["Expressive_Timepicker"];
6280
6233
  }
@@ -6287,8 +6240,7 @@ var Expressive = (() => {
6287
6240
  _setupEventHandlers() {
6288
6241
  this.el.addEventListener("click", this._handleInputClick);
6289
6242
  this.el.addEventListener("keydown", this._handleInputKeydown);
6290
- this.plate.addEventListener("mousedown", this._handleClockClickStart);
6291
- this.plate.addEventListener("touchstart", this._handleClockClickStart);
6243
+ this.plate.addEventListener("pointerdown", this._handleClockClickStart);
6292
6244
  this.digitalClock.addEventListener("keyup", this._inputFromTextField);
6293
6245
  this.inputHours.addEventListener("focus", () => this.showView("hours"));
6294
6246
  this.inputHours.addEventListener("focusout", () => this.formatHours());
@@ -6298,10 +6250,9 @@ var Expressive = (() => {
6298
6250
  _removeEventHandlers() {
6299
6251
  this.el.removeEventListener("click", this._handleInputClick);
6300
6252
  this.el.removeEventListener("keydown", this._handleInputKeydown);
6301
- document.removeEventListener("mousemove", this._handleDocumentClickMove);
6302
- document.removeEventListener("touchmove", this._handleDocumentClickMove);
6303
- document.removeEventListener("mouseup", this._handleDocumentClickEnd);
6304
- document.removeEventListener("touchend", this._handleDocumentClickEnd);
6253
+ document.removeEventListener("pointermove", this._handleDocumentClickMove);
6254
+ document.removeEventListener("pointerup", this._handleDocumentClickEnd);
6255
+ document.removeEventListener("pointercancel", this._handleDocumentClickCancel);
6305
6256
  }
6306
6257
  _insertHTMLIntoDOM() {
6307
6258
  const template = document.createElement("template");
@@ -6330,15 +6281,6 @@ var Expressive = (() => {
6330
6281
  this.footer = this.containerEl.querySelector(".timepicker-footer");
6331
6282
  this.amOrPm = "PM";
6332
6283
  }
6333
- /*private _createButton(text: string, visibility: string): HTMLButtonElement {
6334
- const button = document.createElement('button');
6335
- button.classList.add('waves-effect', 'text');
6336
- button.style.visibility = visibility;
6337
- button.type = 'button';
6338
- button.tabIndex = -1;
6339
- button.innerText = text;
6340
- return button;
6341
- }*/
6342
6284
  _pickerSetup() {
6343
6285
  Utils.createButton(
6344
6286
  this.footer,
@@ -6619,7 +6561,7 @@ var Expressive = (() => {
6619
6561
  var Timepicker = _Timepicker;
6620
6562
 
6621
6563
  // src/ts/components/snackbar.ts
6622
- var _defaults20 = {
6564
+ var _defaults19 = {
6623
6565
  text: "",
6624
6566
  action: "",
6625
6567
  onAction: null,
@@ -6629,7 +6571,8 @@ var Expressive = (() => {
6629
6571
  outDuration: 375,
6630
6572
  classes: "",
6631
6573
  completeCallback: null,
6632
- activationPercent: 0.8
6574
+ activationPercent: 0.8,
6575
+ root: null
6633
6576
  };
6634
6577
  var _Snackbar = class _Snackbar {
6635
6578
  constructor(options = {}) {
@@ -6652,8 +6595,10 @@ var Expressive = (() => {
6652
6595
  }
6653
6596
  _Snackbar._snackbars.length = 0;
6654
6597
  }
6655
- if (!_Snackbar._container || !_Snackbar._container.isConnected) {
6656
- _Snackbar._createContainer();
6598
+ const root = Utils.portalRoot(this.options.root ?? document.body);
6599
+ if (!_Snackbar._container || !_Snackbar._container.isConnected || _Snackbar._container.parentNode !== root) {
6600
+ if (_Snackbar._container) _Snackbar._removeContainer();
6601
+ _Snackbar._createContainer(root);
6657
6602
  }
6658
6603
  _Snackbar._snackbars.push(this);
6659
6604
  const snackbarElement = this._createSnackbar();
@@ -6663,30 +6608,31 @@ var Expressive = (() => {
6663
6608
  this._setTimer();
6664
6609
  }
6665
6610
  static get defaults() {
6666
- return _defaults20;
6611
+ return _defaults19;
6667
6612
  }
6668
6613
  static getInstance(el) {
6669
6614
  return el["Expressive_Snackbar"];
6670
6615
  }
6671
- static _createContainer() {
6616
+ static _createContainer(root = document.body) {
6672
6617
  const container = document.createElement("div");
6673
6618
  container.setAttribute("id", "snackbar-container");
6674
- container.addEventListener("touchstart", _Snackbar._onDragStart);
6675
- container.addEventListener("touchmove", _Snackbar._onDragMove);
6676
- container.addEventListener("touchend", _Snackbar._onDragEnd);
6677
- container.addEventListener("mousedown", _Snackbar._onDragStart);
6678
- document.addEventListener("mousemove", _Snackbar._onDragMove);
6679
- document.addEventListener("mouseup", _Snackbar._onDragEnd);
6680
- document.body.appendChild(container);
6619
+ container.addEventListener("pointerdown", _Snackbar._onDragStart);
6620
+ document.addEventListener("pointermove", _Snackbar._onDragMove);
6621
+ document.addEventListener("pointerup", _Snackbar._onDragEnd);
6622
+ document.addEventListener("pointercancel", _Snackbar._onDragCancel);
6623
+ root.appendChild(container);
6681
6624
  _Snackbar._container = container;
6682
6625
  }
6683
6626
  static _removeContainer() {
6684
- document.removeEventListener("mousemove", _Snackbar._onDragMove);
6685
- document.removeEventListener("mouseup", _Snackbar._onDragEnd);
6627
+ document.removeEventListener("pointermove", _Snackbar._onDragMove);
6628
+ document.removeEventListener("pointerup", _Snackbar._onDragEnd);
6629
+ document.removeEventListener("pointercancel", _Snackbar._onDragCancel);
6686
6630
  _Snackbar._container.remove();
6687
6631
  _Snackbar._container = null;
6688
6632
  }
6689
6633
  static _onDragStart(e) {
6634
+ if (!e.isPrimary) return;
6635
+ if (e.pointerType === "mouse" && e.button !== 0) return;
6690
6636
  const target = e.target;
6691
6637
  if (target && !target.closest("button, a") && target.closest(".snackbar")) {
6692
6638
  const snackbarElem = target.closest(".snackbar");
@@ -6697,17 +6643,19 @@ var Expressive = (() => {
6697
6643
  _Snackbar._draggedSnackbar = snackbar;
6698
6644
  snackbar.el.classList.add("panning");
6699
6645
  snackbar.el.style.transition = "";
6700
- snackbar.startingXPos = _Snackbar._xPos(e);
6646
+ _Snackbar._dragPointerId = e.pointerId;
6647
+ snackbar.startingXPos = e.clientX;
6701
6648
  snackbar.time = Date.now();
6702
- snackbar.xPos = _Snackbar._xPos(e);
6649
+ snackbar.xPos = e.clientX;
6703
6650
  }
6704
6651
  }
6705
6652
  static _onDragMove(e) {
6653
+ if (_Snackbar._dragPointerId !== e.pointerId) return;
6706
6654
  if (!!_Snackbar._draggedSnackbar) {
6707
6655
  e.preventDefault();
6708
6656
  const snackbar = _Snackbar._draggedSnackbar;
6709
- snackbar.deltaX = Math.abs(snackbar.xPos - _Snackbar._xPos(e));
6710
- snackbar.xPos = _Snackbar._xPos(e);
6657
+ snackbar.deltaX = Math.abs(snackbar.xPos - e.clientX);
6658
+ snackbar.xPos = e.clientX;
6711
6659
  snackbar.velocityX = snackbar.deltaX / (Date.now() - snackbar.time);
6712
6660
  snackbar.time = Date.now();
6713
6661
  const totalDeltaX = snackbar.xPos - snackbar.startingXPos;
@@ -6716,7 +6664,9 @@ var Expressive = (() => {
6716
6664
  snackbar.el.style.opacity = (1 - Math.abs(totalDeltaX / activationDistance)).toString();
6717
6665
  }
6718
6666
  }
6719
- static _onDragEnd() {
6667
+ static _onDragEnd(e) {
6668
+ if (_Snackbar._dragPointerId !== e.pointerId) return;
6669
+ _Snackbar._dragPointerId = null;
6720
6670
  if (!!_Snackbar._draggedSnackbar) {
6721
6671
  const snackbar = _Snackbar._draggedSnackbar;
6722
6672
  snackbar.panning = false;
@@ -6736,11 +6686,23 @@ var Expressive = (() => {
6736
6686
  _Snackbar._draggedSnackbar = null;
6737
6687
  }
6738
6688
  }
6739
- static _xPos(e) {
6740
- if (e.type.startsWith("touch") && e.targetTouches.length >= 1) {
6741
- return e.targetTouches[0].clientX;
6742
- }
6743
- return e.clientX;
6689
+ /**
6690
+ * A cancelled pointer is not a short swipe. Snap back and resume the timer
6691
+ * without consulting the distance travelled - the browser took the gesture
6692
+ * over, so the movement so far says nothing about what the user wanted.
6693
+ */
6694
+ static _onDragCancel(e) {
6695
+ if (_Snackbar._dragPointerId !== e.pointerId) return;
6696
+ _Snackbar._dragPointerId = null;
6697
+ const snackbar = _Snackbar._draggedSnackbar;
6698
+ if (!snackbar) return;
6699
+ snackbar.panning = false;
6700
+ snackbar.el.classList.remove("panning");
6701
+ snackbar.el.style.transition = "transform .2s, opacity .2s";
6702
+ snackbar.el.style.transform = "";
6703
+ snackbar.el.style.opacity = "";
6704
+ snackbar._resumeTimer();
6705
+ _Snackbar._draggedSnackbar = null;
6744
6706
  }
6745
6707
  /**
6746
6708
  * dismiss all snackbars.
@@ -6751,7 +6713,7 @@ var Expressive = (() => {
6751
6713
  }
6752
6714
  }
6753
6715
  _createSnackbar() {
6754
- let snackbar = this.options.snackbarId ? document.getElementById(this.options.snackbarId) : document.createElement("div");
6716
+ let snackbar = this.options.snackbarId ? Utils.getElementById(this.options.root ?? document.body, this.options.snackbarId) : document.createElement("div");
6755
6717
  if (snackbar instanceof HTMLTemplateElement) {
6756
6718
  const node = snackbar.content.cloneNode(true);
6757
6719
  snackbar = node.firstElementChild;
@@ -6873,13 +6835,15 @@ var Expressive = (() => {
6873
6835
  }, this.options.outDuration);
6874
6836
  }
6875
6837
  };
6838
+ /** Which pointer owns the drag in flight; a second finger is ignored. */
6839
+ _Snackbar._dragPointerId = null;
6876
6840
  _Snackbar._snackbars = [];
6877
6841
  _Snackbar._container = null;
6878
6842
  _Snackbar._draggedSnackbar = null;
6879
6843
  var Snackbar = _Snackbar;
6880
6844
 
6881
6845
  // src/ts/components/tooltip.ts
6882
- var _defaults21 = {
6846
+ var _defaults20 = {
6883
6847
  exitDelay: 200,
6884
6848
  enterDelay: 0,
6885
6849
  text: "",
@@ -6924,8 +6888,10 @@ var Expressive = (() => {
6924
6888
  this.isFocused = false;
6925
6889
  this.close();
6926
6890
  };
6891
+ // Focus opens the tooltip only when it arrived by keyboard: a pointer press
6892
+ // on the trigger also focuses it, and _handleMouseEnter already covers that.
6927
6893
  this._handleFocus = () => {
6928
- if (Utils.tabPressed) {
6894
+ if (this.el.matches(":focus-visible")) {
6929
6895
  this.isFocused = true;
6930
6896
  this.open(false);
6931
6897
  }
@@ -6947,7 +6913,7 @@ var Expressive = (() => {
6947
6913
  this._setupEventHandlers();
6948
6914
  }
6949
6915
  static get defaults() {
6950
- return _defaults21;
6916
+ return _defaults20;
6951
6917
  }
6952
6918
  /**
6953
6919
  * Initializes instances of Tooltip.
@@ -6974,7 +6940,7 @@ var Expressive = (() => {
6974
6940
  this.tooltipEl.id = `tooltip-${Utils.guid()}`;
6975
6941
  this.tooltipEl.setAttribute("role", "tooltip");
6976
6942
  this.el.setAttribute("aria-describedby", this.tooltipEl.id);
6977
- const tooltipContentEl = this.options.tooltipId ? document.getElementById(this.options.tooltipId) : document.createElement("div");
6943
+ const tooltipContentEl = this.options.tooltipId ? Utils.getElementById(this.el, this.options.tooltipId) : document.createElement("div");
6978
6944
  if (this.options.tooltipId) {
6979
6945
  this.tooltipEl.classList.add("rich");
6980
6946
  }
@@ -6982,7 +6948,7 @@ var Expressive = (() => {
6982
6948
  tooltipContentEl.classList.add("tooltip-content");
6983
6949
  this._setTooltipContent(tooltipContentEl);
6984
6950
  this.tooltipEl.appendChild(tooltipContentEl);
6985
- document.body.appendChild(this.tooltipEl);
6951
+ Utils.portalRoot(this.el).appendChild(this.tooltipEl);
6986
6952
  }
6987
6953
  _setTooltipContent(tooltipContentEl) {
6988
6954
  if (this.options.tooltipId) return;
@@ -7121,26 +7087,44 @@ var Expressive = (() => {
7121
7087
 
7122
7088
  // src/ts/components/registry.ts
7123
7089
  var AUTO_INIT_COMPONENTS = {
7124
- Autocomplete: { component: Autocomplete, selector: ".autocomplete" },
7090
+ Autocomplete: {
7091
+ component: Autocomplete,
7092
+ selector: ".autocomplete"
7093
+ },
7125
7094
  // Shared with Cards.Init() - see CARDS_SELECTOR. This entry used to read
7126
7095
  // '.cards', which no card markup uses.
7127
7096
  Cards: { component: Cards, selector: CARDS_SELECTOR },
7097
+ ExpandingCard: {
7098
+ component: ExpandingCard,
7099
+ selector: EXPANDING_CARD_SELECTOR
7100
+ },
7128
7101
  Carousel: { component: Carousel, selector: ".carousel" },
7129
7102
  Chips: { component: Chips, selector: ".chips" },
7130
- Datepicker: { component: Datepicker, selector: ".datepicker" },
7103
+ Datepicker: {
7104
+ component: Datepicker,
7105
+ selector: ".datepicker, .date-picker"
7106
+ },
7131
7107
  Menu: { component: Menu, selector: ".menu-trigger" },
7132
7108
  Lightbox: { component: Lightbox, selector: ".lightboxed" },
7133
- Parallax: { component: Parallax, selector: ".parallax" },
7134
7109
  ScrollSpy: { component: ScrollSpy, selector: ".scrollspy" },
7135
7110
  FormSelect: { component: FormSelect, selector: "select" },
7136
- Sidenav: { component: Sidenav, selector: ".sidenav" },
7137
- NavigationRail: { component: NavigationRail, selector: ".navigation-rail" },
7111
+ NavigationDrawer: {
7112
+ component: NavigationDrawer,
7113
+ selector: ".sidenav, .navigation-drawer"
7114
+ },
7115
+ NavigationRail: {
7116
+ component: NavigationRail,
7117
+ selector: ".navigation-rail"
7118
+ },
7138
7119
  Tabs: { component: Tabs, selector: ".tabs" },
7139
- Timepicker: { component: Timepicker, selector: ".timepicker" },
7120
+ Timepicker: {
7121
+ component: Timepicker,
7122
+ selector: ".timepicker, .time-picker"
7123
+ },
7140
7124
  Tooltip: { component: Tooltip, selector: ".tooltipped" },
7141
7125
  FloatingActionButton: {
7142
7126
  component: FloatingActionButton,
7143
- selector: ".fixed-action-btn"
7127
+ selector: ".fab, .fixed-action-btn, .fab-menu"
7144
7128
  }
7145
7129
  };
7146
7130
  function AutoInit(context = document.body, options) {
@@ -7152,18 +7136,12 @@ var Expressive = (() => {
7152
7136
  }
7153
7137
 
7154
7138
  // src/ts/index.ts
7155
- var version = "0.7.0";
7156
- if (typeof document !== "undefined") {
7157
- document.addEventListener("keydown", Utils.docHandleKeydown, true);
7158
- document.addEventListener("keyup", Utils.docHandleKeyup, true);
7159
- document.addEventListener("focus", Utils.docHandleFocus, true);
7160
- document.addEventListener("blur", Utils.docHandleBlur, true);
7161
- }
7139
+ var version = "0.8.0";
7162
7140
  Forms.Init();
7163
7141
  Chips.Init();
7164
- Waves.Init();
7165
- Range.Init();
7142
+ Slider.Init();
7166
7143
  Cards.Init();
7144
+ ExpandingCard.Init();
7167
7145
  Dialogs.Init();
7168
7146
  BottomSheets.Init();
7169
7147
  SideSheets.Init();