@expressivecss/expressive 0.2.0 → 0.4.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 (80) hide show
  1. package/README.md +8 -8
  2. package/dist/css/expressive.css +1915 -1566
  3. package/dist/css/expressive.css.map +1 -1
  4. package/dist/css/expressive.min.css +1 -1
  5. package/dist/css/expressive.min.css.map +1 -1
  6. package/dist/js/expressive.cjs +1338 -1528
  7. package/dist/js/expressive.cjs.map +4 -4
  8. package/dist/js/expressive.js +1338 -1528
  9. package/dist/js/expressive.js.map +4 -4
  10. package/dist/js/expressive.min.js +10 -20
  11. package/dist/js/expressive.min.js.map +4 -4
  12. package/dist/js/expressive.mjs +1338 -1529
  13. package/dist/js/expressive.mjs.map +4 -4
  14. package/dist/types/components/autocomplete.d.ts +2 -9
  15. package/dist/types/components/buttons.d.ts +19 -43
  16. package/dist/types/components/cards.d.ts +3 -6
  17. package/dist/types/components/carousel.d.ts +42 -48
  18. package/dist/types/components/characterCounter.d.ts +3 -5
  19. package/dist/types/components/chips.d.ts +4 -4
  20. package/dist/types/components/collapsible.d.ts +28 -23
  21. package/dist/types/components/datepicker.d.ts +2 -8
  22. package/dist/types/components/dropdown.d.ts +11 -2
  23. package/dist/types/components/index.d.ts +0 -1
  24. package/dist/types/components/lightbox.d.ts +2 -2
  25. package/dist/types/components/parallax.d.ts +7 -37
  26. package/dist/types/components/pushpin.d.ts +2 -2
  27. package/dist/types/components/range.d.ts +2 -2
  28. package/dist/types/components/registry.d.ts +0 -4
  29. package/dist/types/components/scrollspy.d.ts +26 -50
  30. package/dist/types/components/select.d.ts +20 -6
  31. package/dist/types/components/sidenav.d.ts +40 -55
  32. package/dist/types/components/slider.d.ts +2 -2
  33. package/dist/types/components/tabs.d.ts +2 -2
  34. package/dist/types/components/tapTarget.d.ts +25 -26
  35. package/dist/types/components/timepicker.d.ts +2 -4
  36. package/dist/types/components/tooltip.d.ts +2 -2
  37. package/dist/types/core/component.d.ts +4 -4
  38. package/dist/types/index.d.ts +1 -1
  39. package/package.json +2 -2
  40. package/src/sass/README.md +3 -6
  41. package/src/sass/abstracts/_breakpoints.scss +0 -16
  42. package/src/sass/abstracts/_variables.scss +0 -21
  43. package/src/sass/base/_grid.scss +0 -19
  44. package/src/sass/components/_badges.scss +6 -5
  45. package/src/sass/components/_breadcrumb.scss +93 -16
  46. package/src/sass/components/_buttons.scss +212 -145
  47. package/src/sass/components/_cards.scss +50 -123
  48. package/src/sass/components/_carousel.scss +128 -63
  49. package/src/sass/components/_chips.scss +1 -1
  50. package/src/sass/components/_collapsible.scss +158 -60
  51. package/src/sass/components/_collection.scss +146 -80
  52. package/src/sass/components/_datepicker.scss +1 -15
  53. package/src/sass/components/_dropdown.scss +110 -14
  54. package/src/sass/components/_icons-material-design.scss +77 -4
  55. package/src/sass/components/_index.scss +1 -0
  56. package/src/sass/components/_lightbox.scss +4 -0
  57. package/src/sass/components/_modal.scss +13 -31
  58. package/src/sass/components/_navbar.scss +24 -6
  59. package/src/sass/components/_page-footer.scss +134 -15
  60. package/src/sass/components/_pagination.scss +131 -25
  61. package/src/sass/components/_parallax.scss +96 -21
  62. package/src/sass/components/_preloader.scss +145 -300
  63. package/src/sass/components/_scrollspy.scss +18 -0
  64. package/src/sass/components/_sidenav.scss +230 -119
  65. package/src/sass/components/_slider.scss +8 -0
  66. package/src/sass/components/_table_of_contents.scss +30 -14
  67. package/src/sass/components/_tabs.scss +54 -35
  68. package/src/sass/components/_tapTarget.scss +128 -45
  69. package/src/sass/components/_toast.scss +10 -11
  70. package/src/sass/components/_toolbar.scss +2 -2
  71. package/src/sass/components/_tooltip.scss +4 -9
  72. package/src/sass/components/_transitions.scss +0 -5
  73. package/src/sass/components/forms/_index.scss +0 -5
  74. package/src/sass/components/forms/_input-fields.scss +49 -52
  75. package/src/sass/components/forms/_select.scss +175 -60
  76. package/src/sass/components/forms/_switches.scss +21 -8
  77. package/src/sass/utilities/_helpers.scss +1 -3
  78. package/src/sass/utilities/_typescale.scss +0 -5
  79. package/src/sass/utilities/_visibility.scss +2 -2
  80. package/dist/types/components/modal.d.ts +0 -45
@@ -1,10 +1,3 @@
1
- var __typeError = (msg) => {
2
- throw TypeError(msg);
3
- };
4
- var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
5
- var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
6
- var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
7
-
8
1
  // src/ts/core/utils.ts
9
2
  var _Utils = class _Utils {
10
3
  /**
@@ -220,7 +213,7 @@ var _Utils = class _Utils {
220
213
  * Renders a button with optional callback function
221
214
  */
222
215
  static createButton(container, text, className = [], visibility = true, callback = null) {
223
- className = className.concat(["btn", "waves-effect", "text"]);
216
+ className = className.concat(["waves-effect", "text"]);
224
217
  const button = document.createElement("button");
225
218
  button.className = className.join(" ");
226
219
  button.style.visibility = visibility ? "visible" : "hidden";
@@ -399,11 +392,9 @@ var Component = class {
399
392
  // src/ts/components/cards.ts
400
393
  var _defaults = {
401
394
  onOpen: null,
402
- onClose: null,
403
- inDuration: 225,
404
- outDuration: 300
395
+ onClose: null
405
396
  };
406
- var CARDS_SELECTOR = ".card, article:has(> aside), article:has(.card-reveal)";
397
+ var CARDS_SELECTOR = "article:has(> aside)";
407
398
  var Cards = class _Cards extends Component {
408
399
  constructor(el, options) {
409
400
  super(el, options, _Cards);
@@ -449,16 +440,10 @@ var Cards = class _Cards extends Component {
449
440
  * Show card reveal.
450
441
  */
451
442
  this.open = () => {
452
- if (this.isOpen) return;
443
+ if (this.isOpen || !this.cardReveal) return;
453
444
  this.isOpen = true;
454
- this.el.style.overflow = "hidden";
455
- this.cardReveal.style.display = "block";
456
445
  this.cardReveal.ariaExpanded = "true";
457
446
  if (this.cardRevealClose) this.cardRevealClose.tabIndex = 0;
458
- setTimeout(() => {
459
- this.cardReveal.style.transition = `transform ${this.options.outDuration}ms ease`;
460
- this.cardReveal.style.transform = "translateY(-100%)";
461
- }, 1);
462
447
  if (typeof this.options.onOpen === "function") {
463
448
  this.options.onOpen.call(this);
464
449
  }
@@ -468,17 +453,11 @@ var Cards = class _Cards extends Component {
468
453
  * Hide card reveal.
469
454
  */
470
455
  this.close = () => {
471
- if (!this.isOpen) return;
456
+ if (!this.isOpen || !this.cardReveal) return;
472
457
  this.isOpen = false;
473
- this.cardReveal.style.transition = `transform ${this.options.inDuration}ms ease`;
474
- this.cardReveal.style.transform = "translateY(0)";
475
- setTimeout(() => {
476
- this.cardReveal.style.display = "none";
477
- this.cardReveal.ariaExpanded = "false";
478
- this._activators.forEach((el) => el.tabIndex = 0);
479
- if (this.cardRevealClose) this.cardRevealClose.tabIndex = -1;
480
- this.el.style.overflow = this.initialOverflow;
481
- }, this.options.inDuration);
458
+ this.cardReveal.ariaExpanded = "false";
459
+ this._activators.forEach((el) => el.tabIndex = 0);
460
+ if (this.cardRevealClose) this.cardRevealClose.tabIndex = -1;
482
461
  if (typeof this.options.onClose === "function") {
483
462
  this.options.onClose.call(this);
484
463
  }
@@ -490,15 +469,14 @@ var Cards = class _Cards extends Component {
490
469
  ...options
491
470
  };
492
471
  this._activators = [];
493
- this.cardReveal = this.el.querySelector(":scope > aside, .card-reveal");
472
+ this.cardReveal = this.el.querySelector(":scope > aside");
494
473
  if (this.cardReveal) {
495
- this.initialOverflow = getComputedStyle(this.el).overflow;
496
474
  this._activators = Array.from(this.el.querySelectorAll(".activator"));
497
475
  this._activators.forEach((el2) => {
498
476
  if (el2) el2.tabIndex = 0;
499
477
  });
500
478
  this.cardRevealClose = this.cardReveal.querySelector(
501
- ".card-title, :scope > :is(h1, h2, h3, h4, h5, h6)"
479
+ ":scope > :is(h1, h2, h3, h4, h5, h6)"
502
480
  );
503
481
  if (this.cardRevealClose) this.cardRevealClose.tabIndex = -1;
504
482
  this.cardReveal.ariaExpanded = "false";
@@ -575,7 +553,7 @@ var _Dropdown = class _Dropdown extends Component {
575
553
  this.close();
576
554
  return;
577
555
  }
578
- const leaveToDropdownContent = !!toEl.closest(".dropdown-content, menu");
556
+ const leaveToDropdownContent = !!(this.dropdownEl && toEl && this.dropdownEl.contains(toEl));
579
557
  let leaveToActiveDropdownTrigger = false;
580
558
  const closestTrigger = toEl.closest(".dropdown-trigger");
581
559
  if (closestTrigger && !!closestTrigger["Expressive_Dropdown"] && closestTrigger["Expressive_Dropdown"].isOpen) {
@@ -587,9 +565,10 @@ var _Dropdown = class _Dropdown extends Component {
587
565
  };
588
566
  this._handleDocumentClick = (e) => {
589
567
  const target = e.target;
590
- if (this.options.closeOnClick && target.closest(".dropdown-content, menu") && !this.isTouchMoving) {
568
+ if (this._isSubmenuTriggerClick(target)) return;
569
+ if (this.options.closeOnClick && this.dropdownEl?.contains(target) && !this.isTouchMoving) {
591
570
  this.close();
592
- } else if (!target.closest(".dropdown-content, menu")) {
571
+ } else if (!this.dropdownEl?.contains(target)) {
593
572
  setTimeout(() => {
594
573
  if (this.isOpen) {
595
574
  this.close();
@@ -607,14 +586,29 @@ var _Dropdown = class _Dropdown extends Component {
607
586
  };
608
587
  this._handleDocumentTouchmove = (e) => {
609
588
  const target = e.target;
610
- if (target.closest(".dropdown-content, menu")) {
589
+ if (this.dropdownEl?.contains(target)) {
611
590
  this.isTouchMoving = true;
612
591
  }
613
592
  };
614
593
  this._handleDropdownClick = (e) => {
594
+ const target = e.target;
595
+ const li = target.closest("li");
596
+ if (li && this._isSubmenuTriggerClick(target)) {
597
+ e.preventDefault();
598
+ e.stopPropagation();
599
+ this._toggleSubmenu(li);
600
+ return;
601
+ }
615
602
  if (typeof this.options.onItemClick === "function") {
616
- const itemEl = e.target.closest("li");
617
- this.options.onItemClick.call(this, itemEl);
603
+ this.options.onItemClick.call(this, li);
604
+ }
605
+ };
606
+ this._handleSubmenuAlign = (e) => {
607
+ const target = e.target;
608
+ if (!(target instanceof Element)) return;
609
+ const li = target.closest("li");
610
+ if (li?.querySelector(":scope > menu") && this.dropdownEl?.contains(li)) {
611
+ this._alignSubmenu(li);
618
612
  }
619
613
  };
620
614
  this._handleDropdownKeydown = (e) => {
@@ -622,37 +616,70 @@ var _Dropdown = class _Dropdown extends Component {
622
616
  if (Utils.keys.TAB.includes(e.key)) {
623
617
  e.preventDefault();
624
618
  this.close();
619
+ } else if (Utils.keys.ARROW_RIGHT.includes(e.key) && this.isOpen) {
620
+ const li = this._focusedRow(e);
621
+ if (li?.querySelector(":scope > menu")) {
622
+ e.preventDefault();
623
+ li.classList.add("open");
624
+ this._alignSubmenu(li);
625
+ this._syncSubmenuAria(li, true);
626
+ const first = li.querySelector(":scope > menu > li");
627
+ first?.focus();
628
+ }
629
+ } else if (Utils.keys.ARROW_LEFT.includes(e.key) && this.isOpen) {
630
+ const submenu = this._containingSubmenu(e);
631
+ if (submenu) {
632
+ e.preventDefault();
633
+ const parentLi = submenu.parentElement;
634
+ parentLi.classList.remove("open", "submenu-start");
635
+ this._syncSubmenuAria(parentLi, false);
636
+ parentLi.focus();
637
+ }
625
638
  } else if (arrowUpOrDown && this.isOpen) {
626
639
  e.preventDefault();
627
640
  const direction = Utils.keys.ARROW_DOWN.includes(e.key) ? 1 : -1;
641
+ const list = this._containingSubmenu(e) ?? this.dropdownEl;
628
642
  let newFocusedIndex = this.focusedIndex;
629
643
  let hasFoundNewIndex = false;
630
644
  do {
631
645
  newFocusedIndex = newFocusedIndex + direction;
632
- if (!!this.dropdownEl.children[newFocusedIndex] && this.dropdownEl.children[newFocusedIndex].tabIndex !== -1) {
646
+ if (!!list.children[newFocusedIndex] && list.children[newFocusedIndex].tabIndex !== -1) {
633
647
  hasFoundNewIndex = true;
634
648
  break;
635
649
  }
636
- } while (newFocusedIndex < this.dropdownEl.children.length && newFocusedIndex >= 0);
650
+ } while (newFocusedIndex < list.children.length && newFocusedIndex >= 0);
637
651
  if (hasFoundNewIndex) {
638
652
  if (this.focusedIndex >= 0)
639
- this.dropdownEl.children[this.focusedIndex].classList.remove("active");
653
+ list.children[this.focusedIndex]?.classList.remove("active");
640
654
  this.focusedIndex = newFocusedIndex;
641
- this._focusFocusedItem();
655
+ const item = list.children[this.focusedIndex];
656
+ item.classList.add("active");
657
+ item.focus({ preventScroll: true });
642
658
  }
643
659
  } else if (Utils.keys.ENTER.includes(e.key) && this.isOpen) {
644
- const focusedElement = this.dropdownEl.children[this.focusedIndex];
660
+ const li = this._focusedRow(e);
661
+ if (li?.querySelector(":scope > menu")) {
662
+ e.preventDefault();
663
+ this._toggleSubmenu(li);
664
+ return;
665
+ }
666
+ const focusedElement = li ?? this.dropdownEl.children[this.focusedIndex];
645
667
  const activatableElement = focusedElement?.querySelector("a, button");
646
668
  if (!!activatableElement) {
647
669
  activatableElement.click();
648
- } else if (!!focusedElement) {
649
- if (focusedElement instanceof HTMLElement) {
650
- focusedElement.click();
651
- }
670
+ } else if (focusedElement instanceof HTMLElement) {
671
+ focusedElement.click();
652
672
  }
653
673
  } else if (Utils.keys.ESC.includes(e.key) && this.isOpen) {
654
674
  e.preventDefault();
655
- this.close();
675
+ const openSub = this.dropdownEl.querySelector("li.open");
676
+ if (openSub) {
677
+ openSub.classList.remove("open", "submenu-start");
678
+ this._syncSubmenuAria(openSub, false);
679
+ openSub.focus();
680
+ } else {
681
+ this.close();
682
+ }
656
683
  }
657
684
  const keyText = e.key.toLowerCase();
658
685
  const isLetter = /[a-zA-Z0-9-_]/.test(keyText);
@@ -666,7 +693,8 @@ var _Dropdown = class _Dropdown extends Component {
666
693
  if (isLetter && !specialKeys.includes(e.key)) {
667
694
  this.filterQuery.push(keyText);
668
695
  const string = this.filterQuery.join("");
669
- const newOptionEl = Array.from(this.dropdownEl.querySelectorAll("li")).find(
696
+ const list = this._containingSubmenu(e) ?? this.dropdownEl;
697
+ const newOptionEl = Array.from(list.querySelectorAll(":scope > li")).find(
670
698
  (el) => el.textContent.trim().toLowerCase().indexOf(string) === 0
671
699
  );
672
700
  if (newOptionEl) {
@@ -708,6 +736,7 @@ var _Dropdown = class _Dropdown extends Component {
708
736
  if (!this.isOpen) return;
709
737
  this.isOpen = false;
710
738
  this.focusedIndex = -1;
739
+ this._closeSubmenus();
711
740
  if (typeof this.options.onCloseStart === "function") {
712
741
  this.options.onCloseStart.call(this, this.el);
713
742
  }
@@ -731,7 +760,6 @@ var _Dropdown = class _Dropdown extends Component {
731
760
  _Dropdown._dropdowns.push(this);
732
761
  this.id = Utils.getIdFromTrigger(el);
733
762
  this.dropdownEl = document.getElementById(this.id);
734
- this.dropdownEl?.classList.add("dropdown-content");
735
763
  this.options = {
736
764
  ..._Dropdown.defaults,
737
765
  ...options
@@ -744,6 +772,7 @@ var _Dropdown = class _Dropdown extends Component {
744
772
  this.el.ariaExpanded = "false";
745
773
  this._moveDropdownToElement();
746
774
  this._makeDropdownFocusable();
775
+ this._setupSubmenus();
747
776
  this._setupEventHandlers();
748
777
  }
749
778
  static get defaults() {
@@ -772,6 +801,9 @@ var _Dropdown = class _Dropdown extends Component {
772
801
  _setupEventHandlers() {
773
802
  this.el.addEventListener("keydown", this._handleTriggerKeydown);
774
803
  this.dropdownEl?.addEventListener("click", this._handleDropdownClick);
804
+ if (this.dropdownEl?.querySelector(":scope > li > menu")) {
805
+ this.dropdownEl.addEventListener("mouseover", this._handleSubmenuAlign);
806
+ }
775
807
  if (this.options.hover) {
776
808
  this.el.addEventListener("mouseenter", this._handleMouseEnter);
777
809
  this.el.addEventListener("mouseleave", this._handleMouseLeave);
@@ -783,6 +815,7 @@ var _Dropdown = class _Dropdown extends Component {
783
815
  _removeEventHandlers() {
784
816
  this.el.removeEventListener("keydown", this._handleTriggerKeydown);
785
817
  this.dropdownEl?.removeEventListener("click", this._handleDropdownClick);
818
+ this.dropdownEl?.removeEventListener("mouseover", this._handleSubmenuAlign);
786
819
  if (this.options.hover) {
787
820
  this.el.removeEventListener("mouseenter", this._handleMouseEnter);
788
821
  this.el.removeEventListener("mouseleave", this._handleMouseLeave);
@@ -803,6 +836,68 @@ var _Dropdown = class _Dropdown extends Component {
803
836
  this.dropdownEl?.removeEventListener("keydown", this._handleDropdownKeydown);
804
837
  window.removeEventListener("resize", this._handleWindowResize);
805
838
  }
839
+ _isSubmenuTriggerClick(target) {
840
+ if (!this.dropdownEl) return false;
841
+ const li = target.closest("li");
842
+ if (!li || !this.dropdownEl.contains(li)) return false;
843
+ const submenu = li.querySelector(":scope > menu");
844
+ if (!submenu) return false;
845
+ return !submenu.contains(target);
846
+ }
847
+ _setupSubmenus() {
848
+ this.dropdownEl?.querySelectorAll("li").forEach((li) => {
849
+ if (!li.querySelector(":scope > menu")) return;
850
+ const trigger = li.querySelector(":scope > a, :scope > button, :scope > span") ?? li;
851
+ trigger.setAttribute("aria-haspopup", "menu");
852
+ trigger.setAttribute("aria-expanded", "false");
853
+ });
854
+ }
855
+ _toggleSubmenu(li) {
856
+ const open = !li.classList.contains("open");
857
+ li.parentElement?.querySelectorAll(":scope > li.open").forEach((other) => {
858
+ if (other !== li) {
859
+ other.classList.remove("open", "submenu-start");
860
+ this._syncSubmenuAria(other, false);
861
+ }
862
+ });
863
+ li.classList.toggle("open", open);
864
+ if (open) this._alignSubmenu(li);
865
+ this._syncSubmenuAria(li, open);
866
+ }
867
+ _syncSubmenuAria(li, open) {
868
+ const trigger = li.querySelector(":scope > a, :scope > button, :scope > span") ?? li;
869
+ trigger.setAttribute("aria-expanded", open ? "true" : "false");
870
+ }
871
+ _alignSubmenu(li) {
872
+ const menu = li.querySelector(":scope > menu");
873
+ if (!menu) return;
874
+ const wasHidden = getComputedStyle(menu).display === "none";
875
+ if (wasHidden) menu.style.display = "block";
876
+ li.classList.remove("submenu-start");
877
+ const rect = menu.getBoundingClientRect();
878
+ if (rect.right > window.innerWidth - 8) li.classList.add("submenu-start");
879
+ if (wasHidden) menu.style.display = "";
880
+ }
881
+ _closeSubmenus() {
882
+ this.dropdownEl?.querySelectorAll("li.open").forEach((li) => {
883
+ li.classList.remove("open", "submenu-start");
884
+ this._syncSubmenuAria(li, false);
885
+ });
886
+ }
887
+ _containingSubmenu(e) {
888
+ const target = e.target;
889
+ if (!(target instanceof Element)) return null;
890
+ const submenu = target.closest("li > menu");
891
+ if (submenu instanceof HTMLElement && this.dropdownEl?.contains(submenu)) return submenu;
892
+ return null;
893
+ }
894
+ _focusedRow(e) {
895
+ const target = e.target;
896
+ if (!(target instanceof Element)) return null;
897
+ const li = target.closest("li");
898
+ if (li instanceof HTMLElement && this.dropdownEl?.contains(li)) return li;
899
+ return null;
900
+ }
806
901
  _resetDropdownStyles() {
807
902
  this.dropdownEl.style.display = "";
808
903
  this._resetDropdownPositioningStyles();
@@ -831,7 +926,7 @@ var _Dropdown = class _Dropdown extends Component {
831
926
  if (!this.dropdownEl) return;
832
927
  this.dropdownEl.popover = "";
833
928
  this.dropdownEl.tabIndex = 0;
834
- Array.from(this.dropdownEl.children).forEach((el) => {
929
+ this.dropdownEl.querySelectorAll(":scope li, :scope > hr").forEach((el) => {
835
930
  if (el instanceof HTMLHRElement || el.classList.contains("divider")) {
836
931
  el.setAttribute("tabindex", "-1");
837
932
  return;
@@ -1179,10 +1274,10 @@ var _Autocomplete = class _Autocomplete extends Component {
1179
1274
  }
1180
1275
  }
1181
1276
  _setupDropdown() {
1182
- this.container = document.createElement("ul");
1277
+ this.container = document.createElement("menu");
1183
1278
  this.container.style.maxHeight = this.options.maxDropDownHeight;
1184
1279
  this.container.id = `autocomplete-options-${Utils.guid()}`;
1185
- this.container.classList.add("autocomplete-content", "dropdown-content");
1280
+ this.container.classList.add("autocomplete-content");
1186
1281
  this.container.ariaExpanded = "true";
1187
1282
  this.el.setAttribute("data-target", this.container.id);
1188
1283
  this.menuItems.forEach((menuItem) => {
@@ -1363,19 +1458,6 @@ var _Autocomplete = class _Autocomplete extends Component {
1363
1458
  this._updateSelectedInfo();
1364
1459
  this._triggerChanged();
1365
1460
  }
1366
- /**
1367
- * Sets selected values.
1368
- * @deprecated @see https://github.com/materializecss/materialize/issues/552
1369
- * @param entries
1370
- */
1371
- setValues(entries) {
1372
- this.selectedValues = entries;
1373
- this._updateSelectedInfo();
1374
- if (!this.options.isMultiSelect) {
1375
- this._refreshInputText();
1376
- }
1377
- this._triggerChanged();
1378
- }
1379
1461
  /**
1380
1462
  * Select a specific autocomplete option via id-property.
1381
1463
  * @param id The id of a data-entry.
@@ -1401,14 +1483,6 @@ var _Autocomplete = class _Autocomplete extends Component {
1401
1483
  this._updateSelectedInfo();
1402
1484
  this._triggerChanged();
1403
1485
  }
1404
- selectOptions(ids) {
1405
- const entries = this.menuItems.filter(
1406
- (item) => !(ids.indexOf(item.id) === -1)
1407
- );
1408
- if (!entries) return;
1409
- this.selectedValues = entries;
1410
- this._renderDropdown();
1411
- }
1412
1486
  };
1413
1487
  /**
1414
1488
  * The three-dot loading indicator, parsed once and cloned thereafter.
@@ -1425,7 +1499,7 @@ var _defaults4 = {
1425
1499
  data: [],
1426
1500
  placeholder: "",
1427
1501
  secondaryPlaceholder: "",
1428
- closeIconClass: "material-icons",
1502
+ closeIconClass: "material-symbols",
1429
1503
  autocompleteOptions: {},
1430
1504
  autocompleteOnly: false,
1431
1505
  limit: Infinity,
@@ -1488,7 +1562,7 @@ var _Chips = class _Chips extends Component {
1488
1562
  this._renderChips();
1489
1563
  }
1490
1564
  if (this.options.allowUserInput) {
1491
- this.el.classList.add("input-field");
1565
+ this.el.classList.add("field");
1492
1566
  this._setupInput();
1493
1567
  this._setupEventHandlers();
1494
1568
  this.el.append(this._input);
@@ -2064,83 +2138,76 @@ var _defaults6 = {
2064
2138
  var FloatingActionButton = class _FloatingActionButton extends Component {
2065
2139
  constructor(el, options) {
2066
2140
  super(el, options, _FloatingActionButton);
2067
- /** Toolbar-mode backdrop, kept so closing can take it back out. */
2068
- this._backdrop = null;
2069
- this._handleFABClick = () => {
2070
- this._handleFABToggle();
2071
- };
2072
- this._handleFABKeyPress = (e) => {
2073
- if (Utils.keys.ENTER.includes(e.key)) {
2074
- this._handleFABToggle();
2075
- }
2076
- };
2077
- this._handleFABToggle = () => {
2078
- if (this.isOpen) {
2079
- this.close();
2080
- } else {
2081
- this.open();
2082
- }
2083
- };
2084
- this._handleDocumentClick = (e) => {
2085
- const elem = e.target;
2086
- if (elem !== this._menu) this.close();
2087
- };
2088
2141
  /**
2089
2142
  * Open FAB.
2090
2143
  */
2091
2144
  this.open = () => {
2092
2145
  if (this.isOpen) return;
2093
- if (this.options.toolbarEnabled) this._animateInToolbar();
2094
- else this._animateInFAB();
2095
2146
  this.isOpen = true;
2147
+ this.el.classList.add("active");
2148
+ this._anchor?.setAttribute("aria-expanded", "true");
2149
+ document.addEventListener("click", this._handleDocumentClick);
2150
+ document.addEventListener("keydown", this._handleDocumentKeydown);
2096
2151
  };
2097
2152
  /**
2098
2153
  * Close FAB.
2099
2154
  */
2100
2155
  this.close = () => {
2101
2156
  if (!this.isOpen) return;
2102
- if (this.options.toolbarEnabled) {
2103
- window.removeEventListener("scroll", this.close, true);
2104
- document.body.removeEventListener("click", this._handleDocumentClick, true);
2105
- this._animateOutToolbar();
2106
- } else {
2107
- this._animateOutFAB();
2108
- }
2109
2157
  this.isOpen = false;
2158
+ this.el.classList.remove("active");
2159
+ this._anchor?.setAttribute("aria-expanded", "false");
2160
+ document.removeEventListener("click", this._handleDocumentClick);
2161
+ document.removeEventListener("keydown", this._handleDocumentKeydown);
2162
+ };
2163
+ this._handleFABClick = (e) => {
2164
+ e.stopPropagation();
2165
+ this._toggle();
2166
+ };
2167
+ this._handleAnchorKeydown = (e) => {
2168
+ if (Utils.keys.ENTER.includes(e.key) || e.key === " ") {
2169
+ e.preventDefault();
2170
+ this._toggle();
2171
+ }
2172
+ };
2173
+ this._handleDocumentKeydown = (e) => {
2174
+ if (Utils.keys.ESC.includes(e.key)) this.close();
2175
+ };
2176
+ this._handleDocumentClick = (e) => {
2177
+ const target = e.target;
2178
+ if (!(target instanceof Node) || this.el.contains(target)) return;
2179
+ this.close();
2110
2180
  };
2111
2181
  this.el["Expressive_FloatingActionButton"] = this;
2182
+ const fromMarkup = this._directionFromClass();
2183
+ const toolbar = el.classList.contains("toolbar") || options.toolbarEnabled === true;
2184
+ const clickToToggle = el.classList.contains("click-to-toggle") || options.hoverEnabled === false;
2112
2185
  this.options = {
2113
2186
  ..._FloatingActionButton.defaults,
2114
- ...options
2187
+ ...options,
2188
+ direction: options.direction ?? fromMarkup ?? _FloatingActionButton.defaults.direction,
2189
+ hoverEnabled: !clickToToggle,
2190
+ toolbarEnabled: toolbar
2115
2191
  };
2116
2192
  this.isOpen = false;
2117
- this._anchor = this.el.querySelector("a, button");
2118
- this._menu = this.el.querySelector("ul");
2119
- this._floatingBtns = Array.from(this.el.querySelectorAll("ul .btn-floating"));
2120
- this._floatingBtnsReverse = this._floatingBtns.reverse();
2121
- this.offsetY = 0;
2122
- this.offsetX = 0;
2193
+ this._anchor = this.el.querySelector(":scope > a, :scope > button");
2194
+ this._menu = this.el.querySelector("ul, menu");
2123
2195
  this.el.classList.add(`direction-${this.options.direction}`);
2124
- if (this._anchor) this._anchor.tabIndex = 0;
2125
- if (this._menu) this._menu.ariaExpanded = "false";
2126
- if (this.options.direction === "top")
2127
- this.offsetY = 40;
2128
- else if (this.options.direction === "right")
2129
- this.offsetX = -40;
2130
- else if (this.options.direction === "bottom")
2131
- this.offsetY = -40;
2132
- else
2133
- this.offsetX = 40;
2196
+ if (clickToToggle) this.el.classList.add("click-to-toggle");
2197
+ if (toolbar) this.el.classList.add("toolbar");
2198
+ if (this._anchor) {
2199
+ this._anchor.setAttribute("aria-haspopup", "menu");
2200
+ this._anchor.setAttribute("aria-expanded", "false");
2201
+ if (this._anchor instanceof HTMLAnchorElement && !this._anchor.hasAttribute("href") && this._anchor.tabIndex < 0) {
2202
+ this._anchor.tabIndex = 0;
2203
+ }
2204
+ }
2205
+ if (this._menu) this._menu.setAttribute("role", "menu");
2134
2206
  this._setupEventHandlers();
2135
2207
  }
2136
2208
  static get defaults() {
2137
2209
  return _defaults6;
2138
2210
  }
2139
- /**
2140
- * Initializes instances of FloatingActionButton.
2141
- * @param els HTML elements.
2142
- * @param options Component options.
2143
- */
2144
2211
  static init(els, options = {}) {
2145
2212
  return super.init(els, options, _FloatingActionButton);
2146
2213
  }
@@ -2148,229 +2215,109 @@ var FloatingActionButton = class _FloatingActionButton extends Component {
2148
2215
  return el["Expressive_FloatingActionButton"];
2149
2216
  }
2150
2217
  destroy() {
2151
- this._removeEventHandlers();
2152
2218
  this.close();
2219
+ this._removeEventHandlers();
2153
2220
  this.el["Expressive_FloatingActionButton"] = void 0;
2154
2221
  }
2222
+ _directionFromClass() {
2223
+ const match = [...this.el.classList].find((c) => c.startsWith("direction-"));
2224
+ const value = match?.slice("direction-".length);
2225
+ if (value === "top" || value === "right" || value === "bottom" || value === "left") {
2226
+ return value;
2227
+ }
2228
+ return null;
2229
+ }
2155
2230
  _setupEventHandlers() {
2156
- if (this.options.hoverEnabled && !this.options.toolbarEnabled) {
2157
- this.el.addEventListener("mouseenter", this.open);
2158
- this.el.addEventListener("mouseleave", this.close);
2159
- } else {
2160
- this.el.addEventListener("click", this._handleFABClick);
2231
+ this.el.addEventListener("click", this._handleFABClick);
2232
+ if (this._anchor instanceof HTMLAnchorElement && !this._anchor.hasAttribute("href")) {
2233
+ this._anchor.addEventListener("keydown", this._handleAnchorKeydown);
2161
2234
  }
2162
- this.el.addEventListener("keypress", this._handleFABKeyPress);
2163
2235
  }
2164
2236
  _removeEventHandlers() {
2165
- if (this.options.hoverEnabled && !this.options.toolbarEnabled) {
2166
- this.el.removeEventListener("mouseenter", this.open);
2167
- this.el.removeEventListener("mouseleave", this.close);
2168
- } else {
2169
- this.el.removeEventListener("click", this._handleFABClick);
2170
- }
2171
- this.el.removeEventListener("keypress", this._handleFABKeyPress);
2172
- }
2173
- _animateInFAB() {
2174
- this.el.classList.add("active");
2175
- if (this._menu) this._menu.ariaExpanded = "true";
2176
- const delayIncrement = 40;
2177
- const duration = 275;
2178
- this._floatingBtnsReverse.forEach((el, index) => {
2179
- const delay = delayIncrement * index;
2180
- el.style.transition = "none";
2181
- el.style.opacity = "0";
2182
- el.style.transform = `translate(${this.offsetX}px, ${this.offsetY}px) scale(0.4)`;
2183
- setTimeout(() => {
2184
- el.style.opacity = "0.4";
2185
- setTimeout(() => {
2186
- el.style.transition = `opacity ${duration}ms ease, transform ${duration}ms ease`;
2187
- el.style.opacity = "1";
2188
- el.style.transform = "translate(0, 0) scale(1)";
2189
- el.tabIndex = 0;
2190
- }, 1);
2191
- }, delay);
2192
- });
2193
- }
2194
- _animateOutFAB() {
2195
- const duration = 175;
2196
- setTimeout(() => {
2197
- this.el.classList.remove("active");
2198
- if (this._menu) this._menu.ariaExpanded = "false";
2199
- }, duration);
2200
- this._floatingBtnsReverse.forEach((el) => {
2201
- el.style.transition = `opacity ${duration}ms ease, transform ${duration}ms ease`;
2202
- el.style.opacity = "0";
2203
- el.style.transform = `translate(${this.offsetX}px, ${this.offsetY}px) scale(0.4)`;
2204
- el.tabIndex = -1;
2205
- });
2237
+ this.el.removeEventListener("click", this._handleFABClick);
2238
+ this._anchor?.removeEventListener("keydown", this._handleAnchorKeydown);
2206
2239
  }
2207
- _animateInToolbar() {
2208
- const windowWidth = window.innerWidth;
2209
- const windowHeight = window.innerHeight;
2210
- const btnRect = this.el.getBoundingClientRect();
2211
- const fabColor = getComputedStyle(this._anchor).backgroundColor;
2212
- const backdrop = document.createElement("div");
2213
- backdrop.classList.add("fab-backdrop");
2214
- backdrop.style.backgroundColor = fabColor;
2215
- this._anchor.append(backdrop);
2216
- this._backdrop = backdrop;
2217
- const backdropWidth = backdrop.clientWidth;
2218
- const scaleFactor = backdropWidth > 0 ? windowWidth / backdropWidth : 1;
2219
- this.offsetX = btnRect.left - windowWidth / 2 + btnRect.width / 2;
2220
- this.offsetY = windowHeight - btnRect.bottom;
2221
- this.btnBottom = btnRect.bottom;
2222
- this.btnLeft = btnRect.left;
2223
- this.btnWidth = btnRect.width;
2224
- this.el.classList.add("active");
2225
- this.el.style.textAlign = "center";
2226
- this.el.style.width = "100%";
2227
- this.el.style.bottom = "0";
2228
- this.el.style.left = "0";
2229
- this.el.style.transform = "translateX(" + this.offsetX + "px)";
2230
- this.el.style.transition = "none";
2231
- if (this._menu) this._menu.ariaExpanded = "true";
2232
- this._anchor.style.transform = `translateY(${this.offsetY}px)`;
2233
- this._anchor.style.transition = "none";
2234
- setTimeout(() => {
2235
- this.el.style.transform = "";
2236
- this.el.style.transition = "transform .2s cubic-bezier(0.550, 0.085, 0.680, 0.530), background-color 0s linear .2s";
2237
- this._anchor.style.overflow = "visible";
2238
- this._anchor.style.transform = "";
2239
- this._anchor.style.transition = "transform .2s";
2240
- setTimeout(() => {
2241
- this.el.style.overflow = "hidden";
2242
- this.el.style.backgroundColor = fabColor;
2243
- backdrop.style.transform = "scale(" + scaleFactor + ")";
2244
- backdrop.style.transition = "transform .2s cubic-bezier(0.550, 0.055, 0.675, 0.190)";
2245
- this._menu.querySelectorAll("li > a").forEach((a) => {
2246
- a.style.opacity = "1";
2247
- a.tabIndex = 0;
2248
- });
2249
- window.addEventListener("scroll", this.close, true);
2250
- document.body.addEventListener("click", this._handleDocumentClick, true);
2251
- }, 100);
2252
- }, 0);
2253
- }
2254
- /**
2255
- * Reverse {@link _animateInToolbar}.
2256
- *
2257
- * Closing used to only unbind the listeners, leaving the FAB stretched
2258
- * across the viewport with its backdrop still in the DOM - and a fresh
2259
- * backdrop appended on every subsequent open.
2260
- */
2261
- _animateOutToolbar() {
2262
- const backdrop = this._backdrop;
2263
- this._backdrop = null;
2264
- this._menu?.querySelectorAll("li > a").forEach((a) => {
2265
- a.style.opacity = "";
2266
- a.tabIndex = -1;
2267
- });
2268
- if (backdrop) {
2269
- backdrop.style.transition = "transform .2s";
2270
- backdrop.style.transform = "scale(0)";
2271
- }
2272
- this.el.style.overflow = "";
2273
- this.el.style.backgroundColor = "";
2274
- this.el.style.transition = "transform .2s";
2275
- this.el.style.transform = `translateX(${this.offsetX}px)`;
2276
- this._anchor.style.transition = "transform .2s";
2277
- this._anchor.style.transform = `translateY(${this.offsetY}px)`;
2278
- setTimeout(() => {
2279
- backdrop?.remove();
2280
- this.el.classList.remove("active");
2281
- if (this._menu) this._menu.ariaExpanded = "false";
2282
- this.el.style.textAlign = "";
2283
- this.el.style.width = "";
2284
- this.el.style.bottom = "";
2285
- this.el.style.left = "";
2286
- this.el.style.transform = "";
2287
- this.el.style.transition = "";
2288
- this._anchor.style.overflow = "";
2289
- this._anchor.style.transform = "";
2290
- this._anchor.style.transition = "";
2291
- }, 200);
2240
+ _toggle() {
2241
+ if (this.isOpen) this.close();
2242
+ else this.open();
2292
2243
  }
2293
2244
  };
2294
2245
 
2295
2246
  // src/ts/components/carousel.ts
2296
2247
  var _defaults7 = {
2297
2248
  duration: 200,
2298
- // ms
2299
2249
  dist: -100,
2300
- // zoom scale TODO: make this more intuitive as an option
2301
2250
  shift: 0,
2302
- // spacing for center image
2303
2251
  padding: 0,
2304
- // Padding between non center items
2305
2252
  numVisible: 5,
2306
- // Number of visible items in carousel
2307
2253
  fullWidth: false,
2308
- // Change to full width styles
2309
2254
  indicators: false,
2310
- // Toggle indicators
2311
2255
  noWrap: false,
2312
- // Don't wrap around and cycle through items.
2313
2256
  onCycleTo: null
2314
- // Callback for when a new slide is cycled to.
2315
2257
  };
2316
- var Carousel = class _Carousel extends Component {
2258
+ var _Carousel = class _Carousel extends Component {
2317
2259
  constructor(el, options) {
2318
2260
  super(el, options, _Carousel);
2319
- /** The index of the center carousel item. */
2320
2261
  this.center = 0;
2321
- // Built once per instance. Creating the throttled function inside the
2322
- // handler - as this used to - produced a fresh closure per resize event and
2323
- // never actually called it, so resizing did nothing at all.
2262
+ this._ownIndicators = false;
2263
+ this._trackEl = null;
2264
+ this._ignoreScroll = false;
2265
+ this._trackRaf = null;
2266
+ this._started = false;
2324
2267
  this._handleThrottledResize = Utils.throttle(() => this._handleResize(), 200);
2325
2268
  this._handleCarouselTap = (e) => {
2326
- if (e.type === "mousedown" && e.target.tagName === "IMG") {
2269
+ if (e.pointerType === "mouse" && e.button !== 0) return;
2270
+ if (e.target instanceof HTMLElement && e.target.tagName === "IMG") {
2327
2271
  e.preventDefault();
2328
2272
  }
2329
2273
  this.pressed = true;
2330
2274
  this.dragged = false;
2331
2275
  this.verticalDragged = false;
2332
- this.reference = this._xpos(e);
2333
- this.referenceY = this._ypos(e);
2276
+ this.reference = e.clientX;
2277
+ this.referenceY = e.clientY;
2334
2278
  this.velocity = this.amplitude = 0;
2335
2279
  this.frame = this.offset;
2336
2280
  this.timestamp = Date.now();
2337
- clearInterval(this.ticker);
2338
- this.ticker = setInterval(this._track, 100);
2281
+ try {
2282
+ this.el.setPointerCapture(e.pointerId);
2283
+ } catch {
2284
+ }
2285
+ if (this._trackRaf !== null) cancelAnimationFrame(this._trackRaf);
2286
+ const loop = () => {
2287
+ this._track();
2288
+ if (this.pressed) this._trackRaf = requestAnimationFrame(loop);
2289
+ else this._trackRaf = null;
2290
+ };
2291
+ this._trackRaf = requestAnimationFrame(loop);
2339
2292
  };
2340
2293
  this._handleCarouselDrag = (e) => {
2341
- let x, y, delta, deltaY;
2342
- if (this.pressed) {
2343
- x = this._xpos(e);
2344
- y = this._ypos(e);
2345
- delta = this.reference - x;
2346
- deltaY = Math.abs(this.referenceY - y);
2347
- if (deltaY < 30 && !this.verticalDragged) {
2348
- if (delta > 2 || delta < -2) {
2349
- this.dragged = true;
2350
- this.reference = x;
2351
- this._scroll(this.offset + delta);
2352
- }
2353
- } else if (this.dragged) {
2354
- e.preventDefault();
2355
- e.stopPropagation();
2356
- return false;
2357
- } else {
2358
- this.verticalDragged = true;
2294
+ if (!this.pressed) return;
2295
+ const delta = this.reference - e.clientX;
2296
+ const deltaY = Math.abs(this.referenceY - e.clientY);
2297
+ if (deltaY < 30 && !this.verticalDragged) {
2298
+ if (delta > 2 || delta < -2) {
2299
+ this.dragged = true;
2300
+ this.reference = e.clientX;
2301
+ this._scroll(this.offset + delta);
2359
2302
  }
2360
- }
2361
- if (this.dragged) {
2303
+ } else if (this.dragged) {
2362
2304
  e.preventDefault();
2363
- e.stopPropagation();
2364
- return false;
2305
+ } else {
2306
+ this.verticalDragged = true;
2365
2307
  }
2308
+ if (this.dragged) e.preventDefault();
2366
2309
  };
2367
2310
  this._handleCarouselRelease = (e) => {
2368
- if (this.pressed) {
2369
- this.pressed = false;
2370
- } else {
2371
- return;
2311
+ if (!this.pressed) return;
2312
+ this.pressed = false;
2313
+ if (this._trackRaf !== null) {
2314
+ cancelAnimationFrame(this._trackRaf);
2315
+ this._trackRaf = null;
2316
+ }
2317
+ try {
2318
+ this.el.releasePointerCapture(e.pointerId);
2319
+ } catch {
2372
2320
  }
2373
- clearInterval(this.ticker);
2374
2321
  this.target = this.offset;
2375
2322
  if (this.velocity > 10 || this.velocity < -10) {
2376
2323
  this.amplitude = 0.9 * this.velocity;
@@ -2387,86 +2334,91 @@ var Carousel = class _Carousel extends Component {
2387
2334
  this.amplitude = this.target - this.offset;
2388
2335
  this.timestamp = Date.now();
2389
2336
  requestAnimationFrame(this._autoScroll);
2337
+ if (this.dragged) e.preventDefault();
2338
+ };
2339
+ this._handleCarouselClick = (e) => {
2390
2340
  if (this.dragged) {
2391
2341
  e.preventDefault();
2392
2342
  e.stopPropagation();
2343
+ return;
2393
2344
  }
2394
- return false;
2395
- };
2396
- this._handleCarouselClick = (e) => {
2397
- if (this.dragged) {
2345
+ const clickedElem = e.target.closest(".carousel-item");
2346
+ if (!clickedElem || !this.el.contains(clickedElem)) return;
2347
+ const clickedIndex = this.images.indexOf(clickedElem);
2348
+ const diff = this._wrap(this.center) - clickedIndex;
2349
+ if (diff !== 0) {
2398
2350
  e.preventDefault();
2399
2351
  e.stopPropagation();
2400
- return false;
2401
- } else if (!this.options.fullWidth) {
2402
- const clickedElem = e.target.closest(".carousel-item");
2403
- if (!clickedElem) return;
2404
- const clickedIndex = [...clickedElem.parentNode.children].indexOf(clickedElem);
2405
- const diff = this._wrap(this.center) - clickedIndex;
2406
- if (diff !== 0) {
2407
- e.preventDefault();
2408
- e.stopPropagation();
2409
- }
2410
- if (clickedIndex < 0) {
2411
- if (e.clientX - e.target.getBoundingClientRect().left > this.el.clientWidth / 2) {
2412
- this.next();
2413
- } else {
2414
- this.prev();
2415
- }
2352
+ }
2353
+ if (clickedIndex < 0) {
2354
+ if (e.clientX - e.target.getBoundingClientRect().left > this.el.clientWidth / 2) {
2355
+ this.next();
2416
2356
  } else {
2417
- this._cycleTo(clickedIndex);
2357
+ this.prev();
2418
2358
  }
2359
+ } else {
2360
+ this._cycleTo(clickedIndex);
2419
2361
  }
2420
2362
  };
2421
2363
  this._handleIndicatorClick = (e) => {
2422
2364
  e.stopPropagation();
2423
- this._handleIndicatorInteraction(e);
2365
+ const indicator = e.target.closest(".indicator-item");
2366
+ if (!indicator || !this._indicators.contains(indicator)) return;
2367
+ const index = [...this._indicators.children].indexOf(indicator);
2368
+ if (index < 0) return;
2369
+ this._cycleTo(index);
2424
2370
  };
2425
- this._handleIndicatorKeyPress = (e) => {
2426
- e.stopPropagation();
2427
- if (Utils.keys.ENTER.includes(e.key)) {
2428
- this._handleIndicatorInteraction(e);
2371
+ this._handleKeydown = (e) => {
2372
+ const target = e.target;
2373
+ if (target.closest('input, textarea, select, [contenteditable="true"]')) return;
2374
+ if (Utils.keys.ARROW_RIGHT.includes(e.key)) {
2375
+ e.preventDefault();
2376
+ this.next();
2377
+ } else if (Utils.keys.ARROW_LEFT.includes(e.key)) {
2378
+ e.preventDefault();
2379
+ this.prev();
2429
2380
  }
2430
2381
  };
2431
- this._handleIndicatorInteraction = (e) => {
2432
- const indicator = e.target.closest(".indicator-item");
2433
- if (indicator) {
2434
- const index = [...indicator.parentNode.children].indexOf(indicator);
2435
- this._cycleTo(index);
2436
- }
2382
+ this._handleFlatScroll = () => {
2383
+ if (this._ignoreScroll) return;
2384
+ const index = this._nearestIndex();
2385
+ if (index !== this.center) this._syncActive(index, this.dragged);
2437
2386
  };
2438
2387
  this._handleResize = () => {
2388
+ if (!this.images.length) return;
2389
+ if (this._flat) {
2390
+ this._scrollToIndex(this.center, false);
2391
+ return;
2392
+ }
2439
2393
  if (this.options.fullWidth) {
2440
- const firstItem = this.el.querySelector(".carousel-item");
2441
- const activeItem = this.el.querySelector(".carousel-item.active");
2442
- if (!firstItem || !activeItem) return;
2394
+ const firstItem = this.images[0];
2395
+ if (!firstItem) return;
2443
2396
  this.itemWidth = firstItem.clientWidth;
2444
- this.imageHeight = activeItem.clientHeight;
2445
2397
  this.dim = this.itemWidth * 2 + this.options.padding;
2446
2398
  this.offset = this.center * 2 * this.itemWidth;
2447
2399
  this.target = this.offset;
2448
- this._setCarouselHeight(true);
2449
2400
  } else {
2450
2401
  this._scroll();
2451
2402
  }
2452
2403
  };
2453
2404
  this._track = () => {
2454
- const now = Date.now(), elapsed = now - this.timestamp, delta = this.offset - this.frame, v = 1e3 * delta / (1 + elapsed);
2405
+ const now = Date.now();
2406
+ const elapsed = now - this.timestamp;
2407
+ const delta = this.offset - this.frame;
2408
+ const v = 1e3 * delta / (1 + elapsed);
2455
2409
  this.timestamp = now;
2456
2410
  this.frame = this.offset;
2457
2411
  this.velocity = 0.8 * v + 0.2 * this.velocity;
2458
2412
  };
2459
2413
  this._autoScroll = () => {
2460
- let elapsed, delta;
2461
- if (this.amplitude) {
2462
- elapsed = Date.now() - this.timestamp;
2463
- delta = this.amplitude * Math.exp(-elapsed / this.options.duration);
2464
- if (delta > 2 || delta < -2) {
2465
- this._scroll(this.target - delta);
2466
- requestAnimationFrame(this._autoScroll);
2467
- } else {
2468
- this._scroll(this.target);
2469
- }
2414
+ if (!this.amplitude) return;
2415
+ const elapsed = Date.now() - this.timestamp;
2416
+ const delta = this.amplitude * Math.exp(-elapsed / this.options.duration);
2417
+ if (delta > 2 || delta < -2) {
2418
+ this._scroll(this.target - delta);
2419
+ requestAnimationFrame(this._autoScroll);
2420
+ } else {
2421
+ this._scroll(this.target);
2470
2422
  }
2471
2423
  };
2472
2424
  this.el["Expressive_Carousel"] = this;
@@ -2474,56 +2426,58 @@ var Carousel = class _Carousel extends Component {
2474
2426
  ..._Carousel.defaults,
2475
2427
  ...options
2476
2428
  };
2477
- this.hasMultipleSlides = this.el.querySelectorAll(".carousel-item").length > 1;
2478
- this.showIndicators = this.options.indicators && this.hasMultipleSlides;
2479
- this.noWrap = this.options.noWrap || !this.hasMultipleSlides;
2429
+ this._flat = this.options.fullWidth || this.el.classList.contains("flat");
2430
+ if (this._flat) {
2431
+ this.el.classList.add("flat");
2432
+ this.options.fullWidth = true;
2433
+ this.options.dist = 0;
2434
+ }
2435
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
2436
+ this.options.duration = 1;
2437
+ if (!this._flat) this.options.dist = 0;
2438
+ }
2480
2439
  this.pressed = false;
2481
2440
  this.dragged = false;
2482
2441
  this.offset = this.target = 0;
2483
- this.images = [];
2484
- const firstItem = this.el.querySelector(".carousel-item");
2485
- if (!firstItem) {
2442
+ this.images = this._collectItems();
2443
+ if (!this.images.length) {
2486
2444
  console.error("Carousel: no .carousel-item elements to show");
2487
2445
  return;
2488
2446
  }
2489
- this.itemWidth = firstItem.clientWidth;
2490
- this.itemHeight = firstItem.clientHeight;
2491
- this.dim = this.itemWidth * 2 + this.options.padding || 1;
2492
- if (this.options.fullWidth) {
2493
- this.options.dist = 0;
2494
- this._setCarouselHeight();
2495
- if (this.showIndicators) {
2496
- this.el.querySelector(".carousel-fixed-item")?.classList.add("with-indicators");
2497
- }
2498
- }
2499
- this._indicators = document.createElement("ul");
2500
- this._indicators.classList.add("indicators");
2501
- this.el.querySelectorAll(".carousel-item").forEach((item, i) => {
2502
- this.images.push(item);
2503
- if (this.showIndicators) {
2504
- const indicator = document.createElement("li");
2505
- indicator.classList.add("indicator-item");
2506
- indicator.tabIndex = 0;
2507
- if (i === 0) {
2508
- indicator.classList.add("active");
2509
- }
2510
- this._indicators.appendChild(indicator);
2511
- }
2512
- });
2513
- if (this.showIndicators) this.el.appendChild(this._indicators);
2447
+ this.hasMultipleSlides = this.images.length > 1;
2448
+ this.showIndicators = this.options.indicators && this.hasMultipleSlides;
2449
+ this.noWrap = this.options.noWrap || !this.hasMultipleSlides || this._flat;
2514
2450
  this.count = this.images.length;
2515
2451
  this.options.numVisible = Math.min(this.count, this.options.numVisible);
2452
+ this.images.forEach((item) => item.classList.add("carousel-item"));
2453
+ if (this._flat) this._wrapTrack();
2454
+ const firstItem = this.images[0];
2455
+ this.itemWidth = firstItem.clientWidth || this.el.clientWidth || 1;
2456
+ this.itemHeight = firstItem.clientHeight || this.el.clientHeight || 1;
2457
+ this.dim = this.itemWidth * 2 + this.options.padding || 1;
2458
+ if (this.showIndicators) this._setupIndicators();
2459
+ this.el.tabIndex = this.el.tabIndex >= 0 ? this.el.tabIndex : 0;
2460
+ this.el.setAttribute("aria-roledescription", "carousel");
2516
2461
  this._setupEventHandlers();
2517
- this._scroll(this.offset);
2462
+ this._started = true;
2463
+ if (this._flat) {
2464
+ const start = Math.max(
2465
+ 0,
2466
+ this.images.findIndex((el2) => el2.classList.contains("active"))
2467
+ );
2468
+ this.center = start;
2469
+ this._syncActive(start, false);
2470
+ this._scrollToIndex(start, false);
2471
+ } else {
2472
+ this._scroll(this.offset);
2473
+ }
2474
+ }
2475
+ get _scroller() {
2476
+ return this._trackEl ?? this.el;
2518
2477
  }
2519
2478
  static get defaults() {
2520
2479
  return _defaults7;
2521
2480
  }
2522
- /**
2523
- * Initializes instances of Carousel.
2524
- * @param els HTML elements.
2525
- * @param options Component options.
2526
- */
2527
2481
  static init(els, options = {}) {
2528
2482
  return super.init(els, options, _Carousel);
2529
2483
  }
@@ -2531,81 +2485,104 @@ var Carousel = class _Carousel extends Component {
2531
2485
  return el["Expressive_Carousel"];
2532
2486
  }
2533
2487
  destroy() {
2534
- this._removeEventHandlers();
2488
+ if (this._started) this._teardown();
2535
2489
  this.el["Expressive_Carousel"] = void 0;
2536
2490
  }
2491
+ _collectItems() {
2492
+ const explicit = Array.from(this.el.children).filter(
2493
+ (el) => el instanceof HTMLElement && el.classList.contains("carousel-item")
2494
+ );
2495
+ if (explicit.length) return explicit;
2496
+ return Array.from(this.el.children).filter((el) => {
2497
+ if (!(el instanceof HTMLElement)) return false;
2498
+ if (el.classList.contains("indicators") || el.classList.contains("carousel-fixed-item")) {
2499
+ return false;
2500
+ }
2501
+ return el.tagName !== "NAV";
2502
+ });
2503
+ }
2504
+ _setupIndicators() {
2505
+ const existing = Array.from(this.el.children).find(
2506
+ (el) => el instanceof HTMLElement && el.classList.contains("indicators")
2507
+ );
2508
+ this._indicators = existing ?? document.createElement("nav");
2509
+ this._ownIndicators = !existing;
2510
+ this._indicators.classList.add("indicators");
2511
+ this._indicators.setAttribute("aria-label", "Slides");
2512
+ this._indicators.replaceChildren();
2513
+ this.images.forEach((_, i) => {
2514
+ const button = document.createElement("button");
2515
+ button.type = "button";
2516
+ button.classList.add("indicator-item");
2517
+ if (i === 0) {
2518
+ button.classList.add("active");
2519
+ button.setAttribute("aria-current", "true");
2520
+ }
2521
+ button.setAttribute("aria-label", `Slide ${i + 1}`);
2522
+ this._indicators.appendChild(button);
2523
+ });
2524
+ if (this._ownIndicators) this.el.appendChild(this._indicators);
2525
+ if (this._flat) {
2526
+ this.el.querySelector(".carousel-fixed-item")?.classList.add("with-indicators");
2527
+ }
2528
+ }
2537
2529
  _setupEventHandlers() {
2538
- if (typeof window.ontouchstart !== "undefined") {
2539
- this.el.addEventListener("touchstart", this._handleCarouselTap);
2540
- this.el.addEventListener("touchmove", this._handleCarouselDrag);
2541
- this.el.addEventListener("touchend", this._handleCarouselRelease);
2542
- }
2543
- this.el.addEventListener("mousedown", this._handleCarouselTap);
2544
- this.el.addEventListener("mousemove", this._handleCarouselDrag);
2545
- this.el.addEventListener("mouseup", this._handleCarouselRelease);
2546
- this.el.addEventListener("mouseleave", this._handleCarouselRelease);
2547
- this.el.addEventListener("click", this._handleCarouselClick);
2530
+ this.el.addEventListener("keydown", this._handleKeydown);
2548
2531
  if (this.showIndicators && this._indicators) {
2549
- this._indicators.querySelectorAll(".indicator-item").forEach((el) => {
2550
- el.addEventListener("click", this._handleIndicatorClick);
2551
- el.addEventListener("keypress", this._handleIndicatorKeyPress);
2552
- });
2532
+ this._indicators.addEventListener("click", this._handleIndicatorClick);
2533
+ }
2534
+ _Carousel._live.add(this);
2535
+ if (!_Carousel._resizeListening) {
2536
+ window.addEventListener("resize", _Carousel._onResize, { passive: true });
2537
+ _Carousel._resizeListening = true;
2553
2538
  }
2554
- window.addEventListener("resize", this._handleThrottledResize);
2539
+ if (this._flat) {
2540
+ this._scroller.addEventListener("scroll", this._handleFlatScroll, { passive: true });
2541
+ return;
2542
+ }
2543
+ this.el.addEventListener("pointerdown", this._handleCarouselTap);
2544
+ this.el.addEventListener("pointermove", this._handleCarouselDrag);
2545
+ this.el.addEventListener("pointerup", this._handleCarouselRelease);
2546
+ this.el.addEventListener("pointercancel", this._handleCarouselRelease);
2547
+ this.el.addEventListener("click", this._handleCarouselClick);
2555
2548
  }
2556
2549
  _removeEventHandlers() {
2557
- if (typeof window.ontouchstart !== "undefined") {
2558
- this.el.removeEventListener("touchstart", this._handleCarouselTap);
2559
- this.el.removeEventListener("touchmove", this._handleCarouselDrag);
2560
- this.el.removeEventListener("touchend", this._handleCarouselRelease);
2561
- }
2562
- this.el.removeEventListener("mousedown", this._handleCarouselTap);
2563
- this.el.removeEventListener("mousemove", this._handleCarouselDrag);
2564
- this.el.removeEventListener("mouseup", this._handleCarouselRelease);
2565
- this.el.removeEventListener("mouseleave", this._handleCarouselRelease);
2550
+ this.el.removeEventListener("keydown", this._handleKeydown);
2551
+ if (this._indicators) {
2552
+ this._indicators.removeEventListener("click", this._handleIndicatorClick);
2553
+ }
2554
+ this._scroller.removeEventListener("scroll", this._handleFlatScroll);
2555
+ this.el.removeEventListener("pointerdown", this._handleCarouselTap);
2556
+ this.el.removeEventListener("pointermove", this._handleCarouselDrag);
2557
+ this.el.removeEventListener("pointerup", this._handleCarouselRelease);
2558
+ this.el.removeEventListener("pointercancel", this._handleCarouselRelease);
2566
2559
  this.el.removeEventListener("click", this._handleCarouselClick);
2567
- if (this.showIndicators && this._indicators) {
2568
- this._indicators.querySelectorAll(".indicator-item").forEach((el) => {
2569
- el.removeEventListener("click", this._handleIndicatorClick);
2570
- });
2560
+ _Carousel._live.delete(this);
2561
+ if (_Carousel._live.size === 0 && _Carousel._resizeListening) {
2562
+ window.removeEventListener("resize", _Carousel._onResize);
2563
+ _Carousel._resizeListening = false;
2571
2564
  }
2572
- window.removeEventListener("resize", this._handleThrottledResize);
2573
2565
  }
2574
- _setCarouselHeight(imageOnly = false) {
2575
- const firstSlide = this.el.querySelector(".carousel-item.active") ? this.el.querySelector(".carousel-item.active") : this.el.querySelector(".carousel-item");
2576
- const firstImage = firstSlide.querySelector("img");
2577
- if (firstImage) {
2578
- if (firstImage.complete) {
2579
- const imageHeight = firstImage.clientHeight;
2580
- if (imageHeight > 0) {
2581
- this.el.style.height = imageHeight + "px";
2582
- } else {
2583
- const naturalWidth = firstImage.naturalWidth;
2584
- const naturalHeight = firstImage.naturalHeight;
2585
- const adjustedHeight = this.el.clientWidth / naturalWidth * naturalHeight;
2586
- this.el.style.height = adjustedHeight + "px";
2587
- }
2588
- } else {
2589
- firstImage.addEventListener("load", () => {
2590
- this.el.style.height = firstImage.offsetHeight + "px";
2591
- });
2592
- }
2593
- } else if (!imageOnly) {
2594
- const slideHeight = firstSlide.clientHeight;
2595
- this.el.style.height = slideHeight + "px";
2596
- }
2566
+ _teardown() {
2567
+ this._removeEventHandlers();
2568
+ if (this._trackRaf !== null) cancelAnimationFrame(this._trackRaf);
2569
+ window.clearTimeout(this.scrollingTimeout);
2570
+ if (this._ownIndicators) this._indicators?.remove();
2571
+ this._unwrapTrack();
2572
+ this.images.forEach((el) => {
2573
+ el.style.transform = "";
2574
+ el.style.zIndex = "";
2575
+ el.style.opacity = "";
2576
+ el.style.visibility = "";
2577
+ });
2597
2578
  }
2598
2579
  _xpos(e) {
2599
- if (e.type.startsWith("touch") && e.targetTouches.length >= 1) {
2600
- return e.targetTouches[0].clientX;
2601
- }
2602
- return e.clientX;
2580
+ if ("clientX" in e) return e.clientX;
2581
+ return 0;
2603
2582
  }
2604
2583
  _ypos(e) {
2605
- if (e.type.startsWith("touch") && e.targetTouches.length >= 1) {
2606
- return e.targetTouches[0].clientY;
2607
- }
2608
- return e.clientY;
2584
+ if ("clientY" in e) return e.clientY;
2585
+ return 0;
2609
2586
  }
2610
2587
  _wrap(x) {
2611
2588
  return x >= this.count ? x % this.count : x < 0 ? this._wrap(this.count + x % this.count) : x;
@@ -2614,18 +2591,20 @@ var Carousel = class _Carousel extends Component {
2614
2591
  if (!this.el.classList.contains("scrolling")) {
2615
2592
  this.el.classList.add("scrolling");
2616
2593
  }
2617
- if (this.scrollingTimeout != null) {
2618
- clearTimeout(this.scrollingTimeout);
2619
- }
2594
+ window.clearTimeout(this.scrollingTimeout);
2620
2595
  this.scrollingTimeout = setTimeout(() => {
2621
2596
  this.el.classList.remove("scrolling");
2622
2597
  }, this.options.duration);
2623
2598
  this.offset = typeof x === "number" ? x : this.offset;
2624
2599
  this.center = Math.floor((this.offset + this.dim / 2) / this.dim);
2625
- const half = this.count >> 1, delta = this.offset - this.center * this.dim, dir = delta < 0 ? 1 : -1, tween = -dir * delta * 2 / this.dim;
2626
- let i, el, alignment, zTranslation, tweenedOpacity, centerTweenedOpacity;
2600
+ const half = this.count >> 1;
2601
+ const delta = this.offset - this.center * this.dim;
2602
+ const dir = delta < 0 ? 1 : -1;
2603
+ const tween = -dir * delta * 2 / this.dim;
2627
2604
  const lastCenter = this.center;
2628
2605
  const numVisibleOffset = 1 / this.options.numVisible;
2606
+ let alignment;
2607
+ let centerTweenedOpacity;
2629
2608
  if (this.options.fullWidth) {
2630
2609
  alignment = "translateX(0)";
2631
2610
  centerTweenedOpacity = 1;
@@ -2634,26 +2613,17 @@ var Carousel = class _Carousel extends Component {
2634
2613
  alignment += "translateY(" + (this.el.clientHeight - this.itemHeight) / 2 + "px)";
2635
2614
  centerTweenedOpacity = 1 - numVisibleOffset * tween;
2636
2615
  }
2637
- if (this.showIndicators) {
2638
- const diff = this.center % this.count;
2639
- const activeIndicator = this._indicators.querySelector(".indicator-item.active");
2640
- const activeIndicatorIndex = [...activeIndicator.parentNode.children].indexOf(
2641
- activeIndicator
2642
- );
2643
- if (activeIndicatorIndex !== diff) {
2644
- activeIndicator.classList.remove("active");
2645
- const pos = diff < 0 ? this.count + diff : diff;
2646
- this._indicators.querySelectorAll(".indicator-item")[pos].classList.add("active");
2647
- }
2648
- }
2616
+ this._paintIndicators(this.center);
2649
2617
  if (!this.noWrap || this.center >= 0 && this.center < this.count) {
2650
- el = this.images[this._wrap(this.center)];
2651
- if (!el.classList.contains("active")) {
2618
+ const el = this.images[this._wrap(this.center)];
2619
+ if (el && !el.classList.contains("active")) {
2652
2620
  this.el.querySelector(".carousel-item.active")?.classList.remove("active");
2653
2621
  el.classList.add("active");
2654
2622
  }
2655
2623
  }
2656
- for (i = 1; i <= half; ++i) {
2624
+ for (let i = 1; i <= half; ++i) {
2625
+ let zTranslation;
2626
+ let tweenedOpacity;
2657
2627
  if (this.options.fullWidth) {
2658
2628
  zTranslation = this.options.dist;
2659
2629
  tweenedOpacity = i === half && delta < 0 ? 1 - tween : 1;
@@ -2662,7 +2632,7 @@ var Carousel = class _Carousel extends Component {
2662
2632
  tweenedOpacity = 1 - numVisibleOffset * (i * 2 + tween * dir);
2663
2633
  }
2664
2634
  if (!this.noWrap || this.center + i < this.count) {
2665
- el = this.images[this._wrap(this.center + i)];
2635
+ const el = this.images[this._wrap(this.center + i)];
2666
2636
  const transformString = `${alignment} translateX(${this.options.shift + (this.dim * i - delta) / 2}px) translateZ(${zTranslation}px)`;
2667
2637
  this._updateItemStyle(el, tweenedOpacity, -i, transformString);
2668
2638
  }
@@ -2674,32 +2644,113 @@ var Carousel = class _Carousel extends Component {
2674
2644
  tweenedOpacity = 1 - numVisibleOffset * (i * 2 - tween * dir);
2675
2645
  }
2676
2646
  if (!this.noWrap || this.center - i >= 0) {
2677
- el = this.images[this._wrap(this.center - i)];
2647
+ const el = this.images[this._wrap(this.center - i)];
2678
2648
  const transformString = `${alignment} translateX(${-this.options.shift + (-this.dim * i - delta) / 2}px) translateZ(${zTranslation}px)`;
2679
2649
  this._updateItemStyle(el, tweenedOpacity, -i, transformString);
2680
2650
  }
2681
2651
  }
2682
2652
  if (!this.noWrap || this.center >= 0 && this.center < this.count) {
2683
- el = this.images[this._wrap(this.center)];
2653
+ const el = this.images[this._wrap(this.center)];
2684
2654
  const transformString = `${alignment} translateX(${-delta / 2}px) translateX(${dir * this.options.shift * tween}px) translateZ(${this.options.dist * tween}px)`;
2685
2655
  this._updateItemStyle(el, centerTweenedOpacity, 0, transformString);
2686
2656
  }
2687
- const _currItem = this.el.querySelectorAll(".carousel-item")[this._wrap(this.center)];
2657
+ const currItem = this.images[this._wrap(this.center)];
2658
+ this._syncA11y(this._wrap(this.center));
2688
2659
  if (lastCenter !== this.center && typeof this.options.onCycleTo === "function") {
2689
- this.options.onCycleTo.call(this, _currItem, this.dragged);
2660
+ this.options.onCycleTo.call(this, currItem, this.dragged);
2690
2661
  }
2691
2662
  if (typeof this.oneTimeCallback === "function") {
2692
- this.oneTimeCallback.call(this, _currItem, this.dragged);
2663
+ this.oneTimeCallback.call(this, currItem, this.dragged);
2693
2664
  this.oneTimeCallback = null;
2694
2665
  }
2695
2666
  }
2696
2667
  _updateItemStyle(el, opacity, zIndex, transform) {
2668
+ if (!el) return;
2697
2669
  el.style.transform = transform;
2698
2670
  el.style.zIndex = zIndex.toString();
2699
2671
  el.style.opacity = opacity.toString();
2700
2672
  el.style.visibility = "visible";
2701
2673
  }
2674
+ _paintIndicators(center) {
2675
+ if (!this.showIndicators || !this._indicators) return;
2676
+ const pos = (center % this.count + this.count) % this.count;
2677
+ this._indicators.querySelectorAll(".indicator-item").forEach((el, i) => {
2678
+ const on = i === pos;
2679
+ el.classList.toggle("active", on);
2680
+ if (on) el.setAttribute("aria-current", "true");
2681
+ else el.removeAttribute("aria-current");
2682
+ });
2683
+ }
2684
+ _syncA11y(index) {
2685
+ this.images.forEach((el, i) => {
2686
+ if (i === index) el.removeAttribute("aria-hidden");
2687
+ else el.setAttribute("aria-hidden", "true");
2688
+ });
2689
+ }
2690
+ _syncActive(index, dragged) {
2691
+ const prev = this.center;
2692
+ this.center = index;
2693
+ this.images.forEach((el, i) => el.classList.toggle("active", i === index));
2694
+ this._paintIndicators(index);
2695
+ this._syncA11y(index);
2696
+ const curr = this.images[index];
2697
+ if (prev !== index && typeof this.options.onCycleTo === "function") {
2698
+ this.options.onCycleTo.call(this, curr, dragged);
2699
+ }
2700
+ if (typeof this.oneTimeCallback === "function") {
2701
+ this.oneTimeCallback.call(this, curr, dragged);
2702
+ this.oneTimeCallback = null;
2703
+ }
2704
+ }
2705
+ _wrapTrack() {
2706
+ const track = document.createElement("div");
2707
+ track.className = "carousel-track";
2708
+ this.images[0].before(track);
2709
+ this.images.forEach((el) => track.appendChild(el));
2710
+ this._trackEl = track;
2711
+ }
2712
+ _unwrapTrack() {
2713
+ if (!this._trackEl) return;
2714
+ this.images.forEach((el) => this.el.insertBefore(el, this._trackEl));
2715
+ this._trackEl.remove();
2716
+ this._trackEl = null;
2717
+ }
2718
+ _nearestIndex() {
2719
+ const left = this._scroller.scrollLeft;
2720
+ let best = 0;
2721
+ let bestDist = Infinity;
2722
+ this.images.forEach((el, i) => {
2723
+ const dist = Math.abs(el.offsetLeft - left);
2724
+ if (dist < bestDist) {
2725
+ bestDist = dist;
2726
+ best = i;
2727
+ }
2728
+ });
2729
+ return best;
2730
+ }
2731
+ _scrollToIndex(n, smooth = true) {
2732
+ const el = this.images[n];
2733
+ if (!el) return;
2734
+ const left = el.offsetLeft;
2735
+ if (Math.abs(this._scroller.scrollLeft - left) < 1) {
2736
+ this._syncActive(n, false);
2737
+ return;
2738
+ }
2739
+ this._ignoreScroll = true;
2740
+ this._scroller.scrollTo({ left, behavior: smooth ? "smooth" : "auto" });
2741
+ const done = () => {
2742
+ this._ignoreScroll = false;
2743
+ };
2744
+ this._scroller.addEventListener("scrollend", done, { once: true });
2745
+ window.setTimeout(done, this.options.duration + 350);
2746
+ }
2702
2747
  _cycleTo(n, callback = null) {
2748
+ if (this._flat) {
2749
+ if (typeof callback === "function") this.oneTimeCallback = callback;
2750
+ this._syncActive(n, false);
2751
+ this._scrollToIndex(n);
2752
+ return;
2753
+ }
2703
2754
  let diff = this.center % this.count - n;
2704
2755
  if (!this.noWrap) {
2705
2756
  if (diff < 0) {
@@ -2727,14 +2778,8 @@ var Carousel = class _Carousel extends Component {
2727
2778
  requestAnimationFrame(this._autoScroll);
2728
2779
  }
2729
2780
  }
2730
- /**
2731
- * Move carousel to next slide or go forward a given amount of slides.
2732
- * @param n How many times the carousel slides.
2733
- */
2734
2781
  next(n = 1) {
2735
- if (n === void 0 || isNaN(n)) {
2736
- n = 1;
2737
- }
2782
+ if (n === void 0 || isNaN(n)) n = 1;
2738
2783
  let index = this.center + n;
2739
2784
  if (index >= this.count || index < 0) {
2740
2785
  if (this.noWrap) return;
@@ -2742,14 +2787,8 @@ var Carousel = class _Carousel extends Component {
2742
2787
  }
2743
2788
  this._cycleTo(index);
2744
2789
  }
2745
- /**
2746
- * Move carousel to previous slide or go back a given amount of slides.
2747
- * @param n How many times the carousel slides.
2748
- */
2749
2790
  prev(n = 1) {
2750
- if (n === void 0 || isNaN(n)) {
2751
- n = 1;
2752
- }
2791
+ if (n === void 0 || isNaN(n)) n = 1;
2753
2792
  let index = this.center - n;
2754
2793
  if (index >= this.count || index < 0) {
2755
2794
  if (this.noWrap) return;
@@ -2757,15 +2796,8 @@ var Carousel = class _Carousel extends Component {
2757
2796
  }
2758
2797
  this._cycleTo(index);
2759
2798
  }
2760
- /**
2761
- * Move carousel to nth slide.
2762
- * @param n Index of slide.
2763
- * @param callback "onCycleTo" optional callback.
2764
- */
2765
2799
  set(n, callback) {
2766
- if (n === void 0 || isNaN(n)) {
2767
- n = 0;
2768
- }
2800
+ if (n === void 0 || isNaN(n)) n = 0;
2769
2801
  if (n > this.count || n < 0) {
2770
2802
  if (this.noWrap) return;
2771
2803
  n = this._wrap(n);
@@ -2773,6 +2805,12 @@ var Carousel = class _Carousel extends Component {
2773
2805
  this._cycleTo(n, callback);
2774
2806
  }
2775
2807
  };
2808
+ _Carousel._live = /* @__PURE__ */ new Set();
2809
+ _Carousel._resizeListening = false;
2810
+ _Carousel._onResize = Utils.throttle(() => {
2811
+ _Carousel._live.forEach((c) => c._handleResize());
2812
+ }, 200);
2813
+ var Carousel = _Carousel;
2776
2814
 
2777
2815
  // src/ts/components/characterCounter.ts
2778
2816
  var _defaults8 = Object.freeze({});
@@ -2855,84 +2893,55 @@ var _defaults9 = {
2855
2893
  onOpenStart: null,
2856
2894
  onOpenEnd: null,
2857
2895
  onCloseStart: null,
2858
- onCloseEnd: null,
2859
- inDuration: 300,
2860
- outDuration: 300
2896
+ onCloseEnd: null
2861
2897
  };
2862
2898
  var Collapsible = class _Collapsible extends Component {
2863
2899
  constructor(el, options) {
2864
2900
  super(el, options, _Collapsible);
2865
- this._handleCollapsibleClick = (e) => {
2866
- const header = e.target.closest(".collapsible-header");
2867
- if (e.target && header) {
2868
- const collapsible = header.closest(".collapsible");
2869
- if (collapsible !== this.el) return;
2870
- const li = header.closest("li");
2871
- const isActive = li.classList.contains("active");
2872
- const index = [...li.parentNode.children].indexOf(li);
2873
- if (isActive) this.close(index);
2874
- else this.open(index);
2875
- }
2876
- };
2877
- this._handleCollapsibleKeydown = (e) => {
2878
- if (Utils.keys.ENTER.includes(e.key)) {
2879
- this._handleCollapsibleClick(e);
2880
- }
2881
- };
2901
+ this._items = [];
2902
+ this._endTimers = [];
2903
+ this._wrappedBodies = [];
2882
2904
  /**
2883
2905
  * Open collapsible section.
2884
- * @param n Nth section to open.
2906
+ * @param index 0-based index of the section to open.
2885
2907
  */
2886
2908
  this.open = (index) => {
2887
- const listItems = Array.from(this.el.children).filter((c) => c.tagName === "LI");
2888
- const li = listItems[index];
2889
- if (li && !li.classList.contains("active")) {
2890
- if (typeof this.options.onOpenStart === "function") {
2891
- this.options.onOpenStart.call(this, li);
2892
- }
2893
- if (this.options.accordion) {
2894
- const activeLis = listItems.filter((li2) => li2.classList.contains("active"));
2895
- activeLis.forEach((activeLi) => {
2896
- const index2 = listItems.indexOf(activeLi);
2897
- this.close(index2);
2898
- });
2899
- }
2900
- li.classList.add("active");
2901
- this._animateIn(index);
2909
+ const item = this._items[index];
2910
+ if (!item || this._isOpen(item)) return;
2911
+ if (this.options.accordion) {
2912
+ this._items.forEach((other, i) => {
2913
+ if (i !== index && this._isOpen(other)) this.close(i);
2914
+ });
2902
2915
  }
2916
+ this._setOpen(item, true);
2903
2917
  };
2904
2918
  /**
2905
2919
  * Close collapsible section.
2906
- * @param n Nth section to close.
2920
+ * @param index 0-based index of the section to close.
2907
2921
  */
2908
2922
  this.close = (index) => {
2909
- const li = Array.from(this.el.children).filter((c) => c.tagName === "LI")[index];
2910
- if (li && li.classList.contains("active")) {
2911
- if (typeof this.options.onCloseStart === "function") {
2912
- this.options.onCloseStart.call(this, li);
2913
- }
2914
- li.classList.remove("active");
2915
- this._animateOut(index);
2916
- }
2923
+ const item = this._items[index];
2924
+ if (!item || !this._isOpen(item)) return;
2925
+ this._setOpen(item, false);
2926
+ };
2927
+ this._handleToggle = (e) => {
2928
+ const item = e.target;
2929
+ if (!(item instanceof HTMLDetailsElement)) return;
2930
+ if (!this._items.includes(item)) return;
2931
+ this._notify(item, item.open);
2917
2932
  };
2918
2933
  this.el["Expressive_Collapsible"] = this;
2934
+ const accordion = options.accordion !== void 0 ? options.accordion : !el.classList.contains("expandable");
2919
2935
  this.options = {
2920
2936
  ..._Collapsible.defaults,
2921
- ...options
2937
+ ...options,
2938
+ accordion
2922
2939
  };
2923
- this._headers = Array.from(this.el.querySelectorAll("li > .collapsible-header"));
2924
- this._headers.forEach((el2) => el2.tabIndex = 0);
2940
+ this._groupName = el.id ? `collapsible-${el.id}` : `collapsible-${Utils.guid()}`;
2941
+ this._items = this._collectItems();
2942
+ this._wrapDetailsBodies();
2943
+ this._setupItems();
2925
2944
  this._setupEventHandlers();
2926
- const activeBodies = Array.from(
2927
- this.el.querySelectorAll("li.active > .collapsible-body")
2928
- );
2929
- if (this.options.accordion) {
2930
- if (activeBodies.length > 0) {
2931
- this._setExpanded(activeBodies[0]);
2932
- }
2933
- } else {
2934
- activeBodies.forEach((el2) => this._setExpanded(el2));
2935
- }
2936
2945
  }
2937
2946
  static get defaults() {
2938
2947
  return _defaults9;
@@ -2950,48 +2959,99 @@ var Collapsible = class _Collapsible extends Component {
2950
2959
  }
2951
2960
  destroy() {
2952
2961
  this._removeEventHandlers();
2962
+ this._endTimers.forEach((id) => clearTimeout(id));
2963
+ this._endTimers = [];
2964
+ this._unwrapDetailsBodies();
2953
2965
  this.el["Expressive_Collapsible"] = void 0;
2954
2966
  }
2955
- _setupEventHandlers() {
2956
- this.el.addEventListener("click", this._handleCollapsibleClick);
2957
- this._headers.forEach(
2958
- (header) => header.addEventListener("keydown", this._handleCollapsibleKeydown)
2959
- );
2967
+ _collectItems() {
2968
+ return Array.from(this.el.querySelectorAll(":scope > details"));
2960
2969
  }
2961
- _removeEventHandlers() {
2962
- this.el.removeEventListener("click", this._handleCollapsibleClick);
2963
- this._headers.forEach(
2964
- (header) => header.removeEventListener("keydown", this._handleCollapsibleKeydown)
2965
- );
2970
+ _setupItems() {
2971
+ if (this.options.accordion) {
2972
+ this._items.forEach((item) => {
2973
+ if (item instanceof HTMLDetailsElement && !item.getAttribute("name")) {
2974
+ item.setAttribute("name", this._groupName);
2975
+ }
2976
+ });
2977
+ }
2978
+ const opened = this._items.filter((item) => this._isOpen(item));
2979
+ if (this.options.accordion && opened.length > 1) {
2980
+ opened.slice(1).forEach((item) => {
2981
+ if (item instanceof HTMLDetailsElement) item.open = false;
2982
+ });
2983
+ }
2966
2984
  }
2967
- _setExpanded(li) {
2968
- li.style.maxHeight = li.scrollHeight + "px";
2985
+ _isOpen(item) {
2986
+ return item instanceof HTMLDetailsElement && item.open;
2969
2987
  }
2970
- _animateIn(index) {
2971
- const li = this.el.children[index];
2972
- if (!li) return;
2973
- const body = li.querySelector(".collapsible-body");
2974
- const duration = this.options.inDuration;
2975
- body.style.transition = `max-height ${duration}ms ease-out`;
2976
- this._setExpanded(body);
2977
- setTimeout(() => {
2978
- if (typeof this.options.onOpenEnd === "function") {
2979
- this.options.onOpenEnd.call(this, li);
2980
- }
2981
- }, duration);
2988
+ _setOpen(item, open) {
2989
+ if (item instanceof HTMLDetailsElement) item.open = open;
2982
2990
  }
2983
- _animateOut(index) {
2984
- const li = this.el.children[index];
2985
- if (!li) return;
2986
- const body = li.querySelector(".collapsible-body");
2987
- const duration = this.options.outDuration;
2988
- body.style.transition = `max-height ${duration}ms ease-out`;
2989
- body.style.maxHeight = "0";
2990
- setTimeout(() => {
2991
- if (typeof this.options.onCloseEnd === "function") {
2992
- this.options.onCloseEnd.call(this, li);
2991
+ _wrapDetailsBodies() {
2992
+ this._items.forEach((item) => {
2993
+ if (!(item instanceof HTMLDetailsElement)) return;
2994
+ if (item.querySelector(":scope > :not(summary)")) {
2995
+ const already = Array.from(item.children).filter((c) => c.tagName !== "SUMMARY");
2996
+ if (already.length === 1 && already[0].tagName === "DIV") {
2997
+ this._wrappedBodies.push(already[0]);
2998
+ return;
2999
+ }
2993
3000
  }
2994
- }, duration);
3001
+ const body = document.createElement("div");
3002
+ Array.from(item.childNodes).forEach((node) => {
3003
+ if (node instanceof HTMLElement && node.tagName === "SUMMARY") return;
3004
+ body.appendChild(node);
3005
+ });
3006
+ item.appendChild(body);
3007
+ this._wrappedBodies.push(body);
3008
+ });
3009
+ }
3010
+ _unwrapDetailsBodies() {
3011
+ this._wrappedBodies.forEach((body) => {
3012
+ const parent = body.parentNode;
3013
+ if (!parent) return;
3014
+ while (body.firstChild) parent.insertBefore(body.firstChild, body);
3015
+ body.remove();
3016
+ });
3017
+ this._wrappedBodies = [];
3018
+ }
3019
+ _bodyOf(item) {
3020
+ return item.querySelector(":scope > :not(summary)");
3021
+ }
3022
+ _setupEventHandlers() {
3023
+ this.el.addEventListener("toggle", this._handleToggle, true);
3024
+ }
3025
+ _removeEventHandlers() {
3026
+ this.el.removeEventListener("toggle", this._handleToggle, true);
3027
+ }
3028
+ _notify(item, opening) {
3029
+ const start = opening ? this.options.onOpenStart : this.options.onCloseStart;
3030
+ if (typeof start === "function") start.call(this, item);
3031
+ const end = opening ? this.options.onOpenEnd : this.options.onCloseEnd;
3032
+ if (typeof end !== "function") return;
3033
+ const target = this._bodyOf(item) ?? item;
3034
+ let done = false;
3035
+ const finish = () => {
3036
+ if (done) return;
3037
+ done = true;
3038
+ target.removeEventListener("transitionend", onEnd);
3039
+ end.call(this, item);
3040
+ };
3041
+ const onEnd = (ev) => {
3042
+ if (ev.target !== target) return;
3043
+ finish();
3044
+ };
3045
+ target.addEventListener("transitionend", onEnd);
3046
+ this._endTimers.push(window.setTimeout(finish, this._durationMs() + 50));
3047
+ }
3048
+ _durationMs() {
3049
+ const raw = getComputedStyle(this.el).getPropertyValue("--md-comp-collapsible-duration").trim();
3050
+ if (!raw) return 500;
3051
+ if (raw.endsWith("ms")) return parseFloat(raw);
3052
+ if (raw.endsWith("s")) return parseFloat(raw) * 1e3;
3053
+ const n = parseFloat(raw);
3054
+ return Number.isFinite(n) ? n : 500;
2995
3055
  }
2996
3056
  };
2997
3057
 
@@ -3017,6 +3077,12 @@ var FormSelect = class _FormSelect extends Component {
3017
3077
  this._selectOptionElement(virtualOption);
3018
3078
  e.stopPropagation();
3019
3079
  };
3080
+ this._handleOptionFocus = (e) => {
3081
+ const target = e.target;
3082
+ if (!(target instanceof Element)) return;
3083
+ const li = target.closest('li[role="option"]');
3084
+ if (li?.id) this.input.setAttribute("aria-activedescendant", li.id);
3085
+ };
3020
3086
  this._handleInputClick = () => {
3021
3087
  if (this.dropdown && this.dropdown.isOpen) {
3022
3088
  this._setValueToInput();
@@ -3033,6 +3099,8 @@ var FormSelect = class _FormSelect extends Component {
3033
3099
  this.nativeTabIndex = this.el.tabIndex ?? -1;
3034
3100
  this.el.tabIndex = -1;
3035
3101
  this._values = [];
3102
+ this._createdWrapper = false;
3103
+ this._originalLabelFor = null;
3036
3104
  this._setupDropdown();
3037
3105
  this._setupEventHandlers();
3038
3106
  }
@@ -3054,6 +3122,7 @@ var FormSelect = class _FormSelect extends Component {
3054
3122
  this._removeEventHandlers();
3055
3123
  this.dropdown?.destroy();
3056
3124
  this._removeDropdown();
3125
+ this.el.tabIndex = this.nativeTabIndex;
3057
3126
  this.el["Expressive_FormSelect"] = void 0;
3058
3127
  }
3059
3128
  /**
@@ -3061,27 +3130,38 @@ var FormSelect = class _FormSelect extends Component {
3061
3130
  *
3062
3131
  * Assigning `select.value` from script fires no `change` event, so nothing
3063
3132
  * else syncs the visible input text or the `.selected` state on the virtual
3064
- * options. Call this after mutating the underlying select programmatically.
3133
+ * options. Adding or removing `<option>`s also requires this — the menu is
3134
+ * rebuilt from the native list, the field and Dropdown instance stay put.
3065
3135
  */
3066
3136
  refresh() {
3137
+ if (!this.dropdownOptions) return;
3138
+ this._rebuildOptions();
3067
3139
  this._setValueToInput();
3068
3140
  this._setSelectedStates();
3069
3141
  }
3070
3142
  _setupEventHandlers() {
3071
- this.dropdownOptions.querySelectorAll("li:not(.optgroup)").forEach((el) => {
3072
- el.addEventListener("click", this._handleOptionClick);
3073
- el.addEventListener("keydown", this._handleOptionKeydown);
3074
- });
3143
+ this._setupOptionHandlers();
3075
3144
  this.el.addEventListener("change", this._handleSelectChange);
3076
3145
  this.input.addEventListener("click", this._handleInputClick);
3146
+ this.dropdownOptions.addEventListener("focusin", this._handleOptionFocus);
3077
3147
  }
3078
3148
  _removeEventHandlers() {
3149
+ this._removeOptionHandlers();
3150
+ this.el.removeEventListener("change", this._handleSelectChange);
3151
+ this.input?.removeEventListener("click", this._handleInputClick);
3152
+ this.dropdownOptions?.removeEventListener("focusin", this._handleOptionFocus);
3153
+ }
3154
+ _setupOptionHandlers() {
3079
3155
  this.dropdownOptions.querySelectorAll("li:not(.optgroup)").forEach((el) => {
3156
+ el.addEventListener("click", this._handleOptionClick);
3157
+ el.addEventListener("keydown", this._handleOptionKeydown);
3158
+ });
3159
+ }
3160
+ _removeOptionHandlers() {
3161
+ this.dropdownOptions?.querySelectorAll("li:not(.optgroup)").forEach((el) => {
3080
3162
  el.removeEventListener("click", this._handleOptionClick);
3081
3163
  el.removeEventListener("keydown", this._handleOptionKeydown);
3082
3164
  });
3083
- this.el.removeEventListener("change", this._handleSelectChange);
3084
- this.input.removeEventListener("click", this._handleInputClick);
3085
3165
  }
3086
3166
  _arraysEqual(a, b) {
3087
3167
  if (a === b) return true;
@@ -3093,6 +3173,7 @@ var FormSelect = class _FormSelect extends Component {
3093
3173
  _selectOptionElement(virtualOption) {
3094
3174
  if (!virtualOption.classList.contains("disabled") && !virtualOption.classList.contains("optgroup")) {
3095
3175
  const value = this._values.find((value2) => value2.optionEl === virtualOption);
3176
+ if (!value) return;
3096
3177
  const previousSelectedValues = this.getSelectedValues();
3097
3178
  if (this.isMultiple) {
3098
3179
  this._toggleEntryFromArray(value);
@@ -3108,70 +3189,54 @@ var FormSelect = class _FormSelect extends Component {
3108
3189
  new Event("change", { bubbles: true, cancelable: true, composed: true })
3109
3190
  );
3110
3191
  }
3111
- if (!this.isMultiple) this.dropdown.close();
3192
+ if (!this.isMultiple) this.dropdown?.close();
3112
3193
  }
3113
3194
  _setupDropdown() {
3114
3195
  this.labelEl = this.el.labels?.[0] ?? null;
3115
- this.wrapper = document.createElement("div");
3116
- this.wrapper.classList.add("select-wrapper", "input-field");
3117
- if (this.options.classes.length > 0) {
3118
- this.wrapper.classList.add(...this.options.classes.split(" ").filter(Boolean));
3119
- }
3120
- this.el.before(this.wrapper);
3121
- const hiddenDiv = document.createElement("div");
3122
- hiddenDiv.classList.add("hide-select");
3123
- this.wrapper.append(hiddenDiv);
3124
- hiddenDiv.appendChild(this.el);
3196
+ this._setupWrapper();
3197
+ this._hideNativeSelect();
3125
3198
  if (this.el.disabled) this.wrapper.classList.add("disabled");
3126
- this.selectOptions = Array.from(this.el.children).filter((el) => ["OPTION", "OPTGROUP"].includes(el.tagName));
3127
- this.dropdownOptions = document.createElement("ul");
3199
+ this.dropdownOptions = document.createElement("menu");
3128
3200
  this.dropdownOptions.id = `select-options-${Utils.guid()}`;
3129
3201
  this.dropdownOptions.setAttribute("popover", "auto");
3130
- this.dropdownOptions.classList.add("dropdown-content", "select-dropdown");
3131
3202
  this.dropdownOptions.setAttribute("role", "listbox");
3132
3203
  this.dropdownOptions.ariaMultiSelectable = this.isMultiple.toString();
3133
3204
  if (this.isMultiple) this.dropdownOptions.classList.add("multiple-select-dropdown");
3134
- if (this.selectOptions.length > 0) {
3135
- this.selectOptions.forEach((realOption) => {
3136
- if (realOption.tagName === "OPTION") {
3137
- const virtualOption = this._createAndAppendOptionWithIcon(
3138
- realOption,
3139
- this.isMultiple ? "multiple" : void 0
3140
- );
3141
- this._addOptionToValues(realOption, virtualOption);
3142
- } else if (realOption.tagName === "OPTGROUP") {
3143
- const groupId = "opt-group-" + Utils.guid();
3144
- const groupParent = document.createElement("li");
3145
- groupParent.classList.add("optgroup");
3146
- groupParent.tabIndex = -1;
3147
- groupParent.setAttribute("role", "group");
3148
- groupParent.setAttribute("aria-labelledby", groupId);
3149
- const groupLabel = document.createElement("span");
3150
- groupLabel.id = groupId;
3151
- groupLabel.setAttribute("role", "presentation");
3152
- groupLabel.textContent = realOption.getAttribute("label") ?? "";
3153
- groupParent.replaceChildren(groupLabel);
3154
- this.dropdownOptions.append(groupParent);
3155
- const groupChildren = [];
3156
- const selectOptions = Array.from(realOption.children).filter((el) => el.tagName === "OPTION");
3157
- selectOptions.forEach((realOption2) => {
3158
- const virtualOption = this._createAndAppendOptionWithIcon(
3159
- realOption2,
3160
- "optgroup-option"
3161
- );
3162
- const childId = "opt-child-" + Utils.guid();
3163
- virtualOption.id = childId;
3164
- groupChildren.push(childId);
3165
- this._addOptionToValues(realOption2, virtualOption);
3166
- });
3167
- groupParent.setAttribute("aria-owns", groupChildren.join(" "));
3168
- }
3169
- });
3170
- }
3205
+ this._buildOptions();
3171
3206
  this.wrapper.append(this.dropdownOptions);
3207
+ this._buildInput();
3208
+ this._buildCaret();
3209
+ this._initDropdown();
3210
+ this._setSelectedStates();
3211
+ if (this.labelEl) this.input.after(this.labelEl);
3212
+ }
3213
+ _setupWrapper() {
3214
+ const parent = this.el.parentElement;
3215
+ const reuse = parent && parent.matches(".field") && !parent.querySelector(":scope > .hide-select") && !parent.querySelector(":scope > input, :scope > textarea");
3216
+ if (reuse) {
3217
+ this.wrapper = parent;
3218
+ this._createdWrapper = false;
3219
+ } else {
3220
+ this.wrapper = document.createElement("div");
3221
+ this.wrapper.classList.add("field");
3222
+ this._createdWrapper = true;
3223
+ this.el.before(this.wrapper);
3224
+ }
3225
+ if (this.options.classes.length > 0) {
3226
+ this.wrapper.classList.add(...this.options.classes.split(" ").filter(Boolean));
3227
+ }
3228
+ }
3229
+ _hideNativeSelect() {
3230
+ const hiddenDiv = document.createElement("div");
3231
+ hiddenDiv.classList.add("hide-select");
3232
+ this.el.before(hiddenDiv);
3233
+ hiddenDiv.appendChild(this.el);
3234
+ this.wrapper.append(hiddenDiv);
3235
+ }
3236
+ _buildInput() {
3172
3237
  this.input = document.createElement("input");
3173
- this.input.id = "m_select-input-" + Utils.guid();
3174
- this.input.classList.add("select-dropdown", "dropdown-trigger");
3238
+ this.input.id = "select-input-" + Utils.guid();
3239
+ this.input.classList.add("dropdown-trigger");
3175
3240
  this.input.type = "text";
3176
3241
  this.input.readOnly = true;
3177
3242
  this.input.setAttribute("data-target", this.dropdownOptions.id);
@@ -3185,71 +3250,131 @@ var FormSelect = class _FormSelect extends Component {
3185
3250
  if (attr.name.startsWith("aria-")) this.input.setAttribute(attr.name, attr.value);
3186
3251
  }
3187
3252
  this.input.setAttribute("role", "combobox");
3253
+ this.input.setAttribute("aria-haspopup", "listbox");
3188
3254
  this.input.ariaExpanded = "false";
3189
- this.input.setAttribute("aria-owns", this.dropdownOptions.id);
3190
3255
  this.input.setAttribute("aria-controls", this.dropdownOptions.id);
3191
3256
  this.input.placeholder = " ";
3257
+ if (this.labelEl) {
3258
+ this._originalLabelFor = this.labelEl.htmlFor;
3259
+ this.labelEl.htmlFor = this.input.id;
3260
+ }
3192
3261
  this.wrapper.prepend(this.input);
3193
3262
  this._setValueToInput();
3194
- const dropdownIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
3195
- dropdownIcon.classList.add("caret");
3196
- dropdownIcon.setAttribute("height", "24");
3197
- dropdownIcon.setAttribute("width", "24");
3198
- dropdownIcon.setAttribute("viewBox", "0 0 24 24");
3199
- dropdownIcon.ariaHidden = "true";
3200
- dropdownIcon.innerHTML = `<path d="M7 10l5 5 5-5z"/><path d="M0 0h24v24H0z" fill="none"/>`;
3201
- this.wrapper.prepend(dropdownIcon);
3202
- if (!this.el.disabled) {
3203
- const dropdownOptions = { ...this.options.dropdownOptions };
3204
- dropdownOptions.coverTrigger = false;
3205
- const userOnOpenEnd = dropdownOptions.onOpenEnd;
3206
- const userOnCloseEnd = dropdownOptions.onCloseEnd;
3207
- dropdownOptions.onOpenEnd = () => {
3208
- const selectedOption = this.dropdownOptions.querySelector(".selected");
3209
- if (selectedOption) {
3210
- Utils.keyDown = true;
3211
- this.dropdown.focusedIndex = [...selectedOption.parentNode.children].indexOf(
3212
- selectedOption
3213
- );
3214
- this.dropdown._focusFocusedItem();
3215
- Utils.keyDown = false;
3216
- if (this.dropdown.isScrollable) {
3217
- let scrollOffset = selectedOption.getBoundingClientRect().top - this.dropdownOptions.getBoundingClientRect().top;
3218
- scrollOffset -= this.dropdownOptions.clientHeight / 2;
3219
- this.dropdownOptions.scrollTop = scrollOffset;
3220
- }
3263
+ }
3264
+ _buildCaret() {
3265
+ const caret = document.createElement("span");
3266
+ caret.classList.add("caret");
3267
+ caret.setAttribute("aria-hidden", "true");
3268
+ this.wrapper.prepend(caret);
3269
+ }
3270
+ _initDropdown() {
3271
+ if (this.el.disabled) return;
3272
+ const dropdownOptions = { ...this.options.dropdownOptions };
3273
+ dropdownOptions.coverTrigger = false;
3274
+ const userOnOpenEnd = dropdownOptions.onOpenEnd;
3275
+ const userOnCloseEnd = dropdownOptions.onCloseEnd;
3276
+ dropdownOptions.onOpenEnd = () => {
3277
+ const selectedOption = this.dropdownOptions.querySelector(".selected");
3278
+ if (selectedOption) {
3279
+ Utils.keyDown = true;
3280
+ this.dropdown.focusedIndex = [...selectedOption.parentNode.children].indexOf(selectedOption);
3281
+ this.dropdown._focusFocusedItem();
3282
+ Utils.keyDown = false;
3283
+ if (this.dropdown.isScrollable) {
3284
+ let scrollOffset = selectedOption.getBoundingClientRect().top - this.dropdownOptions.getBoundingClientRect().top;
3285
+ scrollOffset -= this.dropdownOptions.clientHeight / 2;
3286
+ this.dropdownOptions.scrollTop = scrollOffset;
3221
3287
  }
3222
- this.input.ariaExpanded = "true";
3223
- if (userOnOpenEnd && typeof userOnOpenEnd === "function")
3224
- userOnOpenEnd.call(this.dropdown, this.el);
3225
- };
3226
- dropdownOptions.onCloseEnd = () => {
3227
- this.input.ariaExpanded = "false";
3228
- if (userOnCloseEnd && typeof userOnCloseEnd === "function")
3229
- userOnCloseEnd.call(this.dropdown, this.el);
3230
- };
3231
- dropdownOptions.closeOnClick = false;
3232
- this.dropdown = Dropdown.init(this.input, dropdownOptions);
3233
- }
3234
- this._setSelectedStates();
3235
- if (this.labelEl) this.input.after(this.labelEl);
3288
+ if (selectedOption.id) this.input.setAttribute("aria-activedescendant", selectedOption.id);
3289
+ }
3290
+ this.input.ariaExpanded = "true";
3291
+ if (userOnOpenEnd && typeof userOnOpenEnd === "function")
3292
+ userOnOpenEnd.call(this.dropdown, this.el);
3293
+ };
3294
+ dropdownOptions.onCloseEnd = () => {
3295
+ this.input.ariaExpanded = "false";
3296
+ this.input.removeAttribute("aria-activedescendant");
3297
+ if (userOnCloseEnd && typeof userOnCloseEnd === "function")
3298
+ userOnCloseEnd.call(this.dropdown, this.el);
3299
+ };
3300
+ dropdownOptions.closeOnClick = false;
3301
+ this.dropdown = Dropdown.init(this.input, dropdownOptions);
3302
+ }
3303
+ _nativeOptions() {
3304
+ return Array.from(this.el.children).filter((el) => ["OPTION", "OPTGROUP"].includes(el.tagName));
3305
+ }
3306
+ _buildOptions() {
3307
+ this.selectOptions = this._nativeOptions();
3308
+ this._values = [];
3309
+ this.selectOptions.forEach((realOption) => {
3310
+ if (realOption.tagName === "OPTION") {
3311
+ const virtualOption = this._createAndAppendOptionWithIcon(
3312
+ realOption,
3313
+ this.isMultiple ? "multiple" : void 0
3314
+ );
3315
+ this._addOptionToValues(realOption, virtualOption);
3316
+ } else if (realOption.tagName === "OPTGROUP") {
3317
+ const groupId = "opt-group-" + Utils.guid();
3318
+ const groupParent = document.createElement("li");
3319
+ groupParent.classList.add("optgroup");
3320
+ groupParent.tabIndex = -1;
3321
+ groupParent.setAttribute("role", "group");
3322
+ groupParent.setAttribute("aria-labelledby", groupId);
3323
+ const groupLabel = document.createElement("span");
3324
+ groupLabel.id = groupId;
3325
+ groupLabel.setAttribute("role", "presentation");
3326
+ groupLabel.textContent = realOption.getAttribute("label") ?? "";
3327
+ groupParent.replaceChildren(groupLabel);
3328
+ this.dropdownOptions.append(groupParent);
3329
+ const groupChildren = [];
3330
+ const selectOptions = Array.from(realOption.children).filter((el) => el.tagName === "OPTION");
3331
+ selectOptions.forEach((child) => {
3332
+ const virtualOption = this._createAndAppendOptionWithIcon(child, "optgroup-option");
3333
+ groupChildren.push(virtualOption.id);
3334
+ this._addOptionToValues(child, virtualOption);
3335
+ });
3336
+ groupParent.setAttribute("aria-owns", groupChildren.join(" "));
3337
+ }
3338
+ });
3339
+ }
3340
+ _rebuildOptions() {
3341
+ this._removeOptionHandlers();
3342
+ this.dropdownOptions.replaceChildren();
3343
+ this._buildOptions();
3344
+ this._setupOptionHandlers();
3345
+ this.dropdown?._makeDropdownFocusable();
3236
3346
  }
3237
3347
  _addOptionToValues(realOption, virtualOption) {
3238
3348
  this._values.push({ el: realOption, optionEl: virtualOption });
3239
3349
  }
3240
3350
  _removeDropdown() {
3241
- this.wrapper.querySelector(".caret").remove();
3242
- this.input.remove();
3243
- this.dropdownOptions.remove();
3244
- this.wrapper.before(this.el);
3245
- this.wrapper.remove();
3351
+ this.wrapper?.querySelector(":scope > .caret")?.remove();
3352
+ this.input?.remove();
3353
+ this.dropdownOptions?.remove();
3354
+ const hide = this.el.parentElement;
3355
+ if (hide?.classList.contains("hide-select")) hide.replaceWith(this.el);
3356
+ if (this.labelEl && this._originalLabelFor !== null) {
3357
+ this.labelEl.htmlFor = this._originalLabelFor;
3358
+ }
3359
+ if (!this.wrapper) return;
3360
+ if (this._createdWrapper) {
3361
+ this.wrapper.replaceWith(this.el);
3362
+ } else {
3363
+ this.wrapper.classList.remove("disabled");
3364
+ if (this.options.classes.length > 0) {
3365
+ this.wrapper.classList.remove(...this.options.classes.split(" ").filter(Boolean));
3366
+ }
3367
+ }
3246
3368
  }
3247
3369
  _createAndAppendOptionWithIcon(realOption, type) {
3248
3370
  const li = document.createElement("li");
3371
+ li.id = "select-option-" + Utils.guid();
3249
3372
  li.setAttribute("role", "option");
3373
+ li.tabIndex = 0;
3250
3374
  if (realOption.disabled) {
3251
3375
  li.classList.add("disabled");
3252
3376
  li.ariaDisabled = "true";
3377
+ li.tabIndex = -1;
3253
3378
  }
3254
3379
  if (type === "optgroup-option") li.classList.add(type);
3255
3380
  const optionText = realOption.textContent;
@@ -3258,6 +3383,7 @@ var FormSelect = class _FormSelect extends Component {
3258
3383
  const label = document.createElement("label");
3259
3384
  const checkbox = document.createElement("input");
3260
3385
  checkbox.type = "checkbox";
3386
+ checkbox.tabIndex = -1;
3261
3387
  const labelText = document.createElement("span");
3262
3388
  labelText.textContent = optionText;
3263
3389
  label.append(checkbox, labelText);
@@ -3321,7 +3447,7 @@ var FormSelect = class _FormSelect extends Component {
3321
3447
  if (texts.length === 0) {
3322
3448
  const firstDisabledOption = this.el.querySelector("option:disabled");
3323
3449
  if (firstDisabledOption && firstDisabledOption.value === "") {
3324
- this.input.value = firstDisabledOption.innerText;
3450
+ this.input.value = firstDisabledOption.textContent;
3325
3451
  return;
3326
3452
  }
3327
3453
  }
@@ -3343,7 +3469,7 @@ var FormSelect = class _FormSelect extends Component {
3343
3469
  _activateOption(ul, li) {
3344
3470
  if (!li) return;
3345
3471
  if (!this.isMultiple)
3346
- ul.querySelectorAll("li.selected").forEach((li2) => li2.classList.remove("selected"));
3472
+ ul.querySelectorAll("li.selected").forEach((row) => row.classList.remove("selected"));
3347
3473
  li.classList.add("selected");
3348
3474
  li.ariaSelected = "true";
3349
3475
  }
@@ -3629,7 +3755,6 @@ var _Datepicker = class _Datepicker extends Component {
3629
3755
  }
3630
3756
  }
3631
3757
  };
3632
- // Set input value to the selected date and close Datepicker
3633
3758
  this._finishSelection = () => {
3634
3759
  this.setInputValues();
3635
3760
  };
@@ -4185,7 +4310,7 @@ var _Datepicker = class _Datepicker extends Component {
4185
4310
  if (opts.yearRangeReverse) arr.reverse();
4186
4311
  const yearHtml = `<select class="datepicker-select orig-select-year" tabindex="-1">${arr.join("")}</select>`;
4187
4312
  const leftArrow = '<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z"/><path d="M0-.5h24v24H0z" fill="none"/></svg>';
4188
- html += `<button class="month-prev${prev ? "" : " is-disabled"} btn" type="button">${leftArrow}</button>`;
4313
+ html += `<button class="month-prev${prev ? "" : " is-disabled"}" type="button">${leftArrow}</button>`;
4189
4314
  html += '<div class="selects-container">';
4190
4315
  if (opts.showMonthAfterYear) {
4191
4316
  html += yearHtml + monthHtml;
@@ -4194,7 +4319,7 @@ var _Datepicker = class _Datepicker extends Component {
4194
4319
  }
4195
4320
  html += "</div>";
4196
4321
  const rightArrow = '<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"/><path d="M0-.25h24v24H0z" fill="none"/></svg>';
4197
- html += `<button class="month-next${next ? "" : " is-disabled"} btn" type="button">${rightArrow}</button>`;
4322
+ html += `<button class="month-next${next ? "" : " is-disabled"}" type="button">${rightArrow}</button>`;
4198
4323
  return html += "</div>";
4199
4324
  }
4200
4325
  /**
@@ -4392,17 +4517,6 @@ var _Datepicker = class _Datepicker extends Component {
4392
4517
  this.el.parentElement.appendChild(dateInput);
4393
4518
  return dateInput;
4394
4519
  }
4395
- // deprecated
4396
- open() {
4397
- console.warn("Datepicker.open() is deprecated. Remove this method and wrap in modal yourself.");
4398
- return this;
4399
- }
4400
- close() {
4401
- console.warn(
4402
- "Datepicker.close() is deprecated. Remove this method and wrap in modal yourself."
4403
- );
4404
- return this;
4405
- }
4406
4520
  };
4407
4521
  _Datepicker._template = `
4408
4522
  <div class="datepicker-container">
@@ -4412,13 +4526,7 @@ _Datepicker._template = `
4412
4526
  </div>
4413
4527
  <div class="datepicker-calendar-container">
4414
4528
  <div class="datepicker-calendar"></div>
4415
- <div class="datepicker-footer">
4416
- <!--<button class="btn-flat datepicker-clear waves-effect" style="visibility: hidden;" type="button"></button>
4417
- <div class="confirmation-btns">
4418
- <button class="btn-flat datepicker-cancel waves-effect" type="button"></button>
4419
- <button class="btn-flat datepicker-done waves-effect" type="button"></button>
4420
- </div>-->
4421
- </div>
4529
+ <div class="datepicker-footer"></div>
4422
4530
  </div>
4423
4531
  </div>`;
4424
4532
  var Datepicker = _Datepicker;
@@ -4707,122 +4815,23 @@ var Lightbox = class _Lightbox extends Component {
4707
4815
  }
4708
4816
  };
4709
4817
 
4710
- // src/ts/components/modal.ts
4711
- var _defaults14 = {
4712
- opacity: 0.5,
4713
- inDuration: 250,
4714
- outDuration: 250,
4715
- onOpenStart: null,
4716
- onOpenEnd: null,
4717
- onCloseStart: null,
4718
- onCloseEnd: null,
4719
- preventScrolling: true,
4720
- dismissible: true,
4721
- startingTop: "4%",
4722
- endingTop: "10%"
4723
- };
4724
- var _Modal_static, createHtml_fn, createHtmlElement_fn;
4725
- var _Modal = class _Modal extends Component {
4726
- constructor(el, options) {
4727
- super(el, options, _Modal);
4728
- this.el["Expressive_Modal"] = this;
4729
- this.options = {
4730
- ..._Modal.defaults,
4731
- ...options
4732
- };
4733
- this.el.tabIndex = 0;
4734
- this._setupEventHandlers();
4735
- }
4736
- static get defaults() {
4737
- return _defaults14;
4738
- }
4739
- static init(els, options = {}) {
4740
- return super.init(els, options, _Modal);
4741
- }
4742
- static getInstance(el) {
4743
- return el["Expressive_Modal"];
4744
- }
4745
- destroy() {
4746
- }
4747
- _setupEventHandlers() {
4748
- }
4749
- _removeEventHandlers() {
4750
- }
4751
- _handleTriggerClick() {
4752
- }
4753
- _handleOverlayClick() {
4754
- }
4755
- _handleModalCloseClick() {
4756
- }
4757
- _handleKeydown() {
4758
- }
4759
- _handleFocus() {
4760
- }
4761
- open() {
4762
- return this;
4763
- }
4764
- close() {
4765
- return this;
4766
- }
4767
- static create(config) {
4768
- const isServer = false;
4769
- if (isServer) return __privateMethod(this, _Modal_static, createHtml_fn).call(this, config);
4770
- return __privateMethod(this, _Modal_static, createHtmlElement_fn).call(this, config);
4771
- }
4772
- };
4773
- _Modal_static = new WeakSet();
4774
- createHtml_fn = function(config) {
4775
- const title = config.header ? `<h2>${typeof config.header === "string" ? config.header : ""}</h2>` : "";
4776
- const actions = config.footer ? `<form method="dialog">${typeof config.footer === "string" ? config.footer : ""}</form>` : "";
4777
- return `<dialog id="${config.id}">
4778
- ${title}
4779
- <div>${typeof config.content === "string" ? config.content : ""}</div>
4780
- ${actions}
4781
- </dialog>`;
4782
- };
4783
- createHtmlElement_fn = function(config) {
4784
- const dialog = document.createElement("dialog");
4785
- dialog.id = config.id;
4786
- return dialog;
4787
- };
4788
- __privateAdd(_Modal, _Modal_static);
4789
- var Modal = _Modal;
4790
-
4791
4818
  // src/ts/components/parallax.ts
4792
- var _defaults15 = {
4793
- responsiveThreshold: 0
4794
- // breakpoint for swipeable
4795
- };
4796
- var _Parallax = class _Parallax extends Component {
4819
+ var _defaults14 = {};
4820
+ var Parallax = class _Parallax extends Component {
4797
4821
  constructor(el, options) {
4798
4822
  super(el, options, _Parallax);
4799
- this._handleImageLoad = () => {
4800
- this._updateParallax();
4801
- };
4802
4823
  this.el["Expressive_Parallax"] = this;
4803
4824
  this.options = {
4804
4825
  ..._Parallax.defaults,
4805
4826
  ...options
4806
4827
  };
4807
- this._enabled = window.innerWidth > this.options.responsiveThreshold;
4808
- this._img = this.el.querySelector("img");
4809
- if (!this._img) {
4810
- console.error("Parallax: no <img> to move inside the .parallax element");
4811
- return;
4828
+ if (!this.el.querySelector("img, video")) {
4829
+ console.error("Parallax: no <img> or <video> to move inside the .parallax element");
4812
4830
  }
4813
- this._updateParallax();
4814
- this._setupEventHandlers();
4815
- this._setupStyles();
4816
- _Parallax._parallaxes.push(this);
4817
4831
  }
4818
4832
  static get defaults() {
4819
- return _defaults15;
4833
+ return _defaults14;
4820
4834
  }
4821
- /**
4822
- * Initializes instances of Parallax.
4823
- * @param els HTML elements.
4824
- * @param options Component options.
4825
- */
4826
4835
  static init(els, options = {}) {
4827
4836
  return super.init(els, options, _Parallax);
4828
4837
  }
@@ -4830,87 +4839,12 @@ var _Parallax = class _Parallax extends Component {
4830
4839
  return el["Expressive_Parallax"];
4831
4840
  }
4832
4841
  destroy() {
4833
- const index = _Parallax._parallaxes.indexOf(this);
4834
- if (index >= 0) _Parallax._parallaxes.splice(index, 1);
4835
- if (this._img) this._img.style.transform = "";
4836
- this._removeEventHandlers();
4837
4842
  this.el["Expressive_Parallax"] = void 0;
4838
4843
  }
4839
- static _handleScroll() {
4840
- for (let i = 0; i < _Parallax._parallaxes.length; i++) {
4841
- const parallaxInstance = _Parallax._parallaxes[i];
4842
- parallaxInstance._updateParallax.call(parallaxInstance);
4843
- }
4844
- }
4845
- static _handleWindowResize() {
4846
- for (let i = 0; i < _Parallax._parallaxes.length; i++) {
4847
- const parallaxInstance = _Parallax._parallaxes[i];
4848
- parallaxInstance._enabled = window.innerWidth > parallaxInstance.options.responsiveThreshold;
4849
- }
4850
- }
4851
- _setupEventHandlers() {
4852
- this._img?.addEventListener("load", this._handleImageLoad);
4853
- if (_Parallax._parallaxes.length === 0) {
4854
- if (!_Parallax._handleWindowResizeThrottled) {
4855
- _Parallax._handleWindowResizeThrottled = Utils.throttle(_Parallax._handleWindowResize, 100);
4856
- }
4857
- window.addEventListener("scroll", _Parallax._requestScrollUpdate, { passive: true });
4858
- window.addEventListener("resize", _Parallax._handleWindowResizeThrottled, { passive: true });
4859
- }
4860
- }
4861
- _removeEventHandlers() {
4862
- this._img?.removeEventListener("load", this._handleImageLoad);
4863
- if (_Parallax._parallaxes.length === 0) {
4864
- window.removeEventListener("scroll", _Parallax._requestScrollUpdate);
4865
- window.removeEventListener("resize", _Parallax._handleWindowResizeThrottled);
4866
- if (_Parallax._scrollFrame !== null) {
4867
- cancelAnimationFrame(_Parallax._scrollFrame);
4868
- _Parallax._scrollFrame = null;
4869
- }
4870
- }
4871
- }
4872
- _setupStyles() {
4873
- if (this._img) this._img.style.opacity = "1";
4874
- }
4875
- _updateParallax() {
4876
- const rect = this.el.getBoundingClientRect();
4877
- const containerHeight = rect.height > 0 ? this.el.parentElement.offsetHeight : 500;
4878
- const imgHeight = this._img.offsetHeight;
4879
- const parallaxDist = imgHeight - containerHeight;
4880
- const top = rect.top + window.scrollY - document.documentElement.clientTop;
4881
- const bottom = top + containerHeight;
4882
- const scrollTop = Utils.getDocumentScrollTop();
4883
- const windowHeight = window.innerHeight;
4884
- const windowBottom = scrollTop + windowHeight;
4885
- const percentScrolled = (windowBottom - top) / (containerHeight + windowHeight);
4886
- const parallax = parallaxDist * percentScrolled;
4887
- if (!this._enabled) {
4888
- this._img.style.transform = "";
4889
- } else if (bottom > scrollTop && top < scrollTop + windowHeight) {
4890
- this._img.style.transform = `translate3D(-50%, ${parallax}px, 0)`;
4891
- }
4892
- }
4893
- };
4894
- _Parallax._parallaxes = [];
4895
- _Parallax._scrollFrame = null;
4896
- /**
4897
- * Coalesce scroll events onto animation frames.
4898
- *
4899
- * The handler reads layout and then writes a transform, so running it more
4900
- * than once per frame is wasted work - and the 5ms throttle it replaces was
4901
- * short enough to let several run per frame anyway.
4902
- */
4903
- _Parallax._requestScrollUpdate = () => {
4904
- if (_Parallax._scrollFrame !== null) return;
4905
- _Parallax._scrollFrame = requestAnimationFrame(() => {
4906
- _Parallax._scrollFrame = null;
4907
- _Parallax._handleScroll();
4908
- });
4909
4844
  };
4910
- var Parallax = _Parallax;
4911
4845
 
4912
4846
  // src/ts/components/pushpin.ts
4913
- var _defaults16 = {
4847
+ var _defaults15 = {
4914
4848
  top: 0,
4915
4849
  bottom: Infinity,
4916
4850
  offset: 0,
@@ -4930,7 +4864,7 @@ var _Pushpin = class _Pushpin extends Component {
4930
4864
  this._updatePosition();
4931
4865
  }
4932
4866
  static get defaults() {
4933
- return _defaults16;
4867
+ return _defaults15;
4934
4868
  }
4935
4869
  /**
4936
4870
  * Initializes instances of Pushpin.
@@ -4999,16 +4933,12 @@ _Pushpin._pushpins = [];
4999
4933
  var Pushpin = _Pushpin;
5000
4934
 
5001
4935
  // src/ts/components/scrollspy.ts
5002
- var _defaults17 = {
5003
- throttle: 100,
4936
+ var _defaultGetActiveElement = (id) => `a[href="#${id}"]`;
4937
+ var _defaults16 = {
5004
4938
  scrollOffset: 200,
5005
- // offset - 200 allows elements near bottom of page to scroll
5006
4939
  activeClass: "active",
5007
- getActiveElement: (id) => {
5008
- return 'a[href="#' + id + '"]';
5009
- },
5010
- keepTopElementActive: false,
5011
- animationDuration: null
4940
+ getActiveElement: _defaultGetActiveElement,
4941
+ keepTopElementActive: false
5012
4942
  };
5013
4943
  var _ScrollSpy = class _ScrollSpy extends Component {
5014
4944
  constructor(el, options) {
@@ -5018,22 +4948,17 @@ var _ScrollSpy = class _ScrollSpy extends Component {
5018
4948
  ..._ScrollSpy.defaults,
5019
4949
  ...options
5020
4950
  };
4951
+ this.el.style.setProperty(
4952
+ "--md-comp-scrollspy-offset",
4953
+ `${this.options.scrollOffset}px`
4954
+ );
5021
4955
  _ScrollSpy._elements.push(this);
5022
- _ScrollSpy._count++;
5023
- _ScrollSpy._increment++;
5024
- this.tickId = -1;
5025
- this.id = _ScrollSpy._increment.toString();
5026
- this._setupEventHandlers();
5027
- _ScrollSpy._handleWindowScroll();
4956
+ _ScrollSpy._ensureObserver(this.options.scrollOffset);
4957
+ _ScrollSpy._observer?.observe(this.el);
5028
4958
  }
5029
4959
  static get defaults() {
5030
- return _defaults17;
4960
+ return _defaults16;
5031
4961
  }
5032
- /**
5033
- * Initializes instances of ScrollSpy.
5034
- * @param els HTML elements.
5035
- * @param options Component options.
5036
- */
5037
4962
  static init(els, options = {}) {
5038
4963
  return super.init(els, options, _ScrollSpy);
5039
4964
  }
@@ -5041,348 +4966,233 @@ var _ScrollSpy = class _ScrollSpy extends Component {
5041
4966
  return el["Expressive_ScrollSpy"];
5042
4967
  }
5043
4968
  destroy() {
5044
- const elementIndex = _ScrollSpy._elements.indexOf(this);
5045
- if (elementIndex >= 0) _ScrollSpy._elements.splice(elementIndex, 1);
5046
- const inViewIndex = _ScrollSpy._elementsInView.indexOf(this);
5047
- if (inViewIndex >= 0) _ScrollSpy._elementsInView.splice(inViewIndex, 1);
5048
- const visibleIndex = _ScrollSpy._visibleElements.indexOf(this.el);
5049
- if (visibleIndex >= 0) _ScrollSpy._visibleElements.splice(visibleIndex, 1);
5050
- _ScrollSpy._count--;
5051
- this._removeEventHandlers();
5052
- const actElem = document.querySelector(this.options.getActiveElement(this.el.id));
5053
- actElem?.classList.remove(this.options.activeClass);
5054
- this.el["Expressive_ScrollSpy"] = void 0;
5055
- }
5056
- _setupEventHandlers() {
5057
- if (_ScrollSpy._count === 1) {
5058
- _ScrollSpy._throttledScroll = Utils.throttle(_ScrollSpy._handleWindowScroll, this.options.throttle);
5059
- _ScrollSpy._throttledResize = Utils.throttle(_ScrollSpy._handleWindowScroll, 200);
5060
- window.addEventListener("scroll", _ScrollSpy._throttledScroll, { passive: true });
5061
- window.addEventListener("resize", _ScrollSpy._throttledResize, { passive: true });
5062
- document.body.addEventListener("click", _ScrollSpy._handleTriggerClick);
5063
- }
5064
- }
5065
- _removeEventHandlers() {
5066
- if (_ScrollSpy._count === 0) {
5067
- window.removeEventListener("scroll", _ScrollSpy._throttledScroll);
5068
- window.removeEventListener("resize", _ScrollSpy._throttledResize);
5069
- document.body.removeEventListener("click", _ScrollSpy._handleTriggerClick);
5070
- _ScrollSpy._throttledScroll = null;
5071
- _ScrollSpy._throttledResize = null;
4969
+ const index = _ScrollSpy._elements.indexOf(this);
4970
+ if (index >= 0) _ScrollSpy._elements.splice(index, 1);
4971
+ _ScrollSpy._ratios.delete(this.el);
4972
+ _ScrollSpy._observer?.unobserve(this.el);
4973
+ const link = _ScrollSpy._linkFor(this.el.id, this.options);
4974
+ if (link && link === _ScrollSpy._active) {
4975
+ _ScrollSpy._clearActive(this.options);
4976
+ } else {
4977
+ link?.classList.remove(this.options.activeClass);
4978
+ link?.removeAttribute("aria-current");
4979
+ }
4980
+ if (_ScrollSpy._elements.length === 0) {
4981
+ _ScrollSpy._observer?.disconnect();
4982
+ _ScrollSpy._observer = null;
4983
+ _ScrollSpy._ratios.clear();
4984
+ _ScrollSpy._active = null;
4985
+ } else {
4986
+ _ScrollSpy._syncActive();
5072
4987
  }
4988
+ this.el["Expressive_ScrollSpy"] = void 0;
5073
4989
  }
5074
- static _findElements(top, right, bottom, left) {
5075
- const hits = [];
5076
- const docElem = document.documentElement;
5077
- const clientTop = docElem.clientTop;
5078
- const clientLeft = docElem.clientLeft;
5079
- const scrollY = window.scrollY;
5080
- const scrollX = window.scrollX;
5081
- for (let i = 0; i < _ScrollSpy._elements.length; i++) {
5082
- const scrollspy = _ScrollSpy._elements[i];
5083
- const currTop = top + scrollspy.options.scrollOffset || 200;
5084
- const box = scrollspy.el.getBoundingClientRect();
5085
- if (box.height > 0) {
5086
- const elTop = box.top + scrollY - clientTop, elLeft = box.left + scrollX - clientLeft, elRight = elLeft + box.width, elBottom = elTop + box.height;
5087
- const isIntersect = !(elLeft > right || elRight < left || elTop > bottom || elBottom < currTop);
5088
- if (isIntersect) {
5089
- hits.push(scrollspy);
5090
- }
5091
- }
5092
- }
5093
- return hits;
4990
+ static _ensureObserver(offset) {
4991
+ if (_ScrollSpy._observer) return;
4992
+ if (typeof IntersectionObserver === "undefined") return;
4993
+ _ScrollSpy._observer = new IntersectionObserver(_ScrollSpy._onIntersect, {
4994
+ rootMargin: `-${offset}px 0px -45% 0px`,
4995
+ threshold: [0, 0.1, 0.25, 0.5, 0.75, 1]
4996
+ });
5094
4997
  }
5095
- _enter() {
5096
- _ScrollSpy._visibleElements = _ScrollSpy._visibleElements.filter(
5097
- (value) => value.getBoundingClientRect().height !== 0
5098
- );
5099
- if (_ScrollSpy._visibleElements[0]) {
5100
- const actElem = document.querySelector(
5101
- this.options.getActiveElement(_ScrollSpy._visibleElements[0].id)
5102
- );
5103
- actElem?.classList.remove(this.options.activeClass);
5104
- if (_ScrollSpy._visibleElements[0]["Expressive_ScrollSpy"] && this.id < _ScrollSpy._visibleElements[0]["Expressive_ScrollSpy"].id) {
5105
- _ScrollSpy._visibleElements.unshift(this.el);
4998
+ static _syncActive() {
4999
+ if (!_ScrollSpy._elements.length) return;
5000
+ const options = _ScrollSpy._elements[0].options;
5001
+ let best = null;
5002
+ let bestRatio = 0;
5003
+ for (const spy of _ScrollSpy._elements) {
5004
+ const ratio = _ScrollSpy._ratios.get(spy.el) ?? 0;
5005
+ if (ratio > bestRatio) {
5006
+ bestRatio = ratio;
5007
+ best = spy;
5008
+ }
5009
+ }
5010
+ if (!best) {
5011
+ if (options.keepTopElementActive) {
5012
+ best = _ScrollSpy._nearestAbove() ?? _ScrollSpy._elements[0];
5106
5013
  } else {
5107
- _ScrollSpy._visibleElements.push(this.el);
5014
+ _ScrollSpy._clearActive(options);
5015
+ return;
5108
5016
  }
5109
- } else {
5110
- _ScrollSpy._visibleElements.push(this.el);
5111
5017
  }
5112
- _ScrollSpy._resetKeptTopActiveElement(this.options.activeClass);
5113
- const selector = this.options.getActiveElement(_ScrollSpy._visibleElements[0].id);
5114
- document.querySelector(selector)?.classList.add(this.options.activeClass);
5018
+ const next = _ScrollSpy._linkFor(best.el.id, best.options);
5019
+ if (next === _ScrollSpy._active) return;
5020
+ _ScrollSpy._clearActive(options);
5021
+ if (!next) return;
5022
+ next.classList.add(best.options.activeClass);
5023
+ next.setAttribute("aria-current", "true");
5024
+ _ScrollSpy._active = next;
5115
5025
  }
5116
- _exit() {
5117
- _ScrollSpy._visibleElements = _ScrollSpy._visibleElements.filter(
5118
- (value) => value.getBoundingClientRect().height !== 0
5119
- );
5120
- if (_ScrollSpy._visibleElements[0]) {
5121
- const actElem = document.querySelector(
5122
- this.options.getActiveElement(_ScrollSpy._visibleElements[0].id)
5123
- );
5124
- actElem?.classList.remove(this.options.activeClass);
5125
- _ScrollSpy._visibleElements = _ScrollSpy._visibleElements.filter((x) => x.id != this.el.id);
5126
- if (_ScrollSpy._visibleElements[0]) {
5127
- const selector = this.options.getActiveElement(_ScrollSpy._visibleElements[0].id);
5128
- document.querySelector(selector)?.classList.add(this.options.activeClass);
5129
- _ScrollSpy._resetKeptTopActiveElement(this.options.activeClass);
5130
- }
5131
- }
5132
- }
5133
- static _resetKeptTopActiveElement(activeClass) {
5134
- if (_ScrollSpy._keptTopActiveElement) {
5135
- _ScrollSpy._keptTopActiveElement.classList.remove(activeClass);
5136
- _ScrollSpy._keptTopActiveElement = null;
5137
- }
5138
- }
5139
- static _getDistanceToViewport(element) {
5140
- const rect = element.getBoundingClientRect();
5141
- const distance = rect.top;
5142
- return distance;
5143
- }
5144
- static _smoothScrollIntoView(element, duration = 300) {
5145
- const targetPosition = element.getBoundingClientRect().top + (window.scrollY || window.pageYOffset);
5146
- const startPosition = window.scrollY || window.pageYOffset;
5147
- const distance = targetPosition - startPosition;
5148
- const startTime = performance.now();
5149
- function scrollStep(currentTime) {
5150
- const elapsed = currentTime - startTime;
5151
- const progress = Math.min(elapsed / duration, 1);
5152
- const scrollY = startPosition + distance * progress;
5153
- if (progress < 1) {
5154
- window.scrollTo(0, scrollY);
5155
- requestAnimationFrame(scrollStep);
5156
- } else {
5157
- window.scrollTo(0, targetPosition);
5026
+ static _nearestAbove() {
5027
+ let best = null;
5028
+ let bestTop = -Infinity;
5029
+ for (const spy of _ScrollSpy._elements) {
5030
+ const top = spy.el.getBoundingClientRect().top;
5031
+ if (top <= 0 && top >= bestTop) {
5032
+ bestTop = top;
5033
+ best = spy;
5158
5034
  }
5159
5035
  }
5160
- requestAnimationFrame(scrollStep);
5036
+ return best;
5161
5037
  }
5162
- };
5163
- _ScrollSpy._keptTopActiveElement = null;
5164
- // The document-level listeners are shared by every instance, so the
5165
- // references they are registered with have to be shared too - registering
5166
- // with one instance's bound handler and unregistering with another's is a
5167
- // silent no-op that leaves the listener attached forever.
5168
- _ScrollSpy._throttledScroll = null;
5169
- _ScrollSpy._throttledResize = null;
5170
- _ScrollSpy._handleTriggerClick = (e) => {
5171
- const trigger = e.target;
5172
- for (let i = _ScrollSpy._elements.length - 1; i >= 0; i--) {
5173
- const scrollspy = _ScrollSpy._elements[i];
5174
- const x = document.querySelector('a[href="#' + scrollspy.el.id + '"]');
5175
- if (trigger === x) {
5176
- e.preventDefault();
5177
- if (scrollspy.el["Expressive_ScrollSpy"].options.animationDuration) {
5178
- _ScrollSpy._smoothScrollIntoView(
5179
- scrollspy.el,
5180
- scrollspy.el["Expressive_ScrollSpy"].options.animationDuration
5181
- );
5182
- } else {
5183
- scrollspy.el.scrollIntoView({ behavior: "smooth" });
5038
+ static _clearActive(options) {
5039
+ if (!_ScrollSpy._active) return;
5040
+ _ScrollSpy._active.classList.remove(options.activeClass);
5041
+ _ScrollSpy._active.removeAttribute("aria-current");
5042
+ _ScrollSpy._active = null;
5043
+ }
5044
+ /**
5045
+ * Default lookup compares href attributes (no interpolated selector).
5046
+ * A custom getActiveElement still returns a selector.
5047
+ */
5048
+ static _linkFor(id, options) {
5049
+ if (!id) return null;
5050
+ if (options.getActiveElement !== _defaultGetActiveElement) {
5051
+ try {
5052
+ return document.querySelector(options.getActiveElement(id));
5053
+ } catch {
5054
+ return null;
5184
5055
  }
5185
- break;
5186
5056
  }
5057
+ const href = `#${id}`;
5058
+ const links = document.querySelectorAll('a[href^="#"]');
5059
+ for (let i = 0; i < links.length; i++) {
5060
+ if (links[i].getAttribute("href") === href) return links[i];
5061
+ }
5062
+ return null;
5187
5063
  }
5188
5064
  };
5189
- _ScrollSpy._handleWindowScroll = () => {
5190
- _ScrollSpy._ticks++;
5191
- const top = Utils.getDocumentScrollTop(), left = Utils.getDocumentScrollLeft(), right = left + window.innerWidth, bottom = top + window.innerHeight;
5192
- const intersections = _ScrollSpy._findElements(top, right, bottom, left);
5193
- for (let i = 0; i < intersections.length; i++) {
5194
- const scrollspy = intersections[i];
5195
- const lastTick = scrollspy.tickId;
5196
- if (lastTick < 0) {
5197
- scrollspy._enter();
5198
- }
5199
- scrollspy.tickId = _ScrollSpy._ticks;
5200
- }
5201
- for (let i = 0; i < _ScrollSpy._elementsInView.length; i++) {
5202
- const scrollspy = _ScrollSpy._elementsInView[i];
5203
- const lastTick = scrollspy.tickId;
5204
- if (lastTick >= 0 && lastTick !== _ScrollSpy._ticks) {
5205
- scrollspy._exit();
5206
- scrollspy.tickId = -1;
5207
- }
5208
- }
5209
- _ScrollSpy._elementsInView = intersections;
5210
- if (_ScrollSpy._elements.length) {
5211
- const options = _ScrollSpy._elements[0].el["Expressive_ScrollSpy"].options;
5212
- if (options.keepTopElementActive && _ScrollSpy._visibleElements.length === 0) {
5213
- _ScrollSpy._resetKeptTopActiveElement(options.activeClass);
5214
- const topElements = _ScrollSpy._elements.map((value) => ({ value, distance: _ScrollSpy._getDistanceToViewport(value.el) })).filter((entry) => entry.distance <= 0).sort((a, b) => a.distance - b.distance).map((entry) => entry.value);
5215
- const nearestTopElement = topElements.length ? topElements[topElements.length - 1] : _ScrollSpy._elements[0];
5216
- const actElem = document.querySelector(options.getActiveElement(nearestTopElement.el.id));
5217
- actElem?.classList.add(options.activeClass);
5218
- _ScrollSpy._keptTopActiveElement = actElem;
5065
+ _ScrollSpy._elements = [];
5066
+ _ScrollSpy._observer = null;
5067
+ _ScrollSpy._ratios = /* @__PURE__ */ new Map();
5068
+ _ScrollSpy._active = null;
5069
+ _ScrollSpy._onIntersect = (entries) => {
5070
+ for (const entry of entries) {
5071
+ if (entry.isIntersecting && entry.intersectionRatio > 0) {
5072
+ _ScrollSpy._ratios.set(entry.target, entry.intersectionRatio);
5073
+ } else {
5074
+ _ScrollSpy._ratios.delete(entry.target);
5219
5075
  }
5220
5076
  }
5077
+ _ScrollSpy._syncActive();
5221
5078
  };
5222
- _ScrollSpy._elements = [];
5223
- _ScrollSpy._elementsInView = [];
5224
- _ScrollSpy._visibleElements = [];
5225
- _ScrollSpy._count = 0;
5226
- _ScrollSpy._increment = 0;
5227
- _ScrollSpy._ticks = 0;
5228
5079
  var ScrollSpy = _ScrollSpy;
5229
5080
 
5230
5081
  // src/ts/components/sidenav.ts
5231
- var _defaults18 = {
5082
+ var _defaults17 = {
5232
5083
  edge: "left",
5233
5084
  draggable: true,
5234
5085
  dragTargetWidth: "10px",
5235
- inDuration: 250,
5236
- outDuration: 200,
5237
5086
  onOpenStart: null,
5238
5087
  onOpenEnd: null,
5239
5088
  onCloseStart: null,
5240
- onCloseEnd: null,
5241
- preventScrolling: true
5089
+ onCloseEnd: null
5242
5090
  };
5091
+ var LARGE_UP = "(width >= 993px)";
5243
5092
  var _Sidenav = class _Sidenav extends Component {
5244
5093
  constructor(el, options) {
5245
5094
  super(el, options, _Sidenav);
5246
- this._handleDragTargetDrag = (e) => {
5247
- if (!this._isDraggable()) return;
5248
- let totalDeltaX = this._calculateDelta(e);
5249
- const dragDirection = totalDeltaX > 0 ? "right" : "left";
5250
- totalDeltaX = Math.min(this._width, Math.abs(totalDeltaX));
5251
- if (this.options.edge === dragDirection) {
5252
- totalDeltaX = 0;
5253
- }
5254
- let transformX = totalDeltaX;
5255
- let transformPrefix = "translateX(-100%)";
5256
- if (this.options.edge === "right") {
5257
- transformPrefix = "translateX(100%)";
5258
- transformX = -transformX;
5259
- }
5260
- this.percentOpen = Math.min(1, totalDeltaX / this._width);
5261
- this.el.style.transform = `${transformPrefix} translateX(${transformX}px)`;
5262
- this._overlay.style.opacity = this.percentOpen.toString();
5263
- };
5264
- this._handleDragTargetRelease = () => {
5265
- if (this.isDragged) {
5266
- if (this.percentOpen > 0.2) {
5267
- this.open();
5268
- } else {
5269
- this._animateOut();
5095
+ /**
5096
+ * Opens the overlay drawer. No-op while the sidenav is docked
5097
+ * (sidenav-fixed at the large breakpoint).
5098
+ */
5099
+ this.open = () => {
5100
+ if (this._isCurrentlyFixed() || this.isOpen || !this._dialog) return;
5101
+ _Sidenav._sidenavs.forEach((other) => {
5102
+ if (other !== this && other.isOpen) other.close();
5103
+ });
5104
+ if (typeof this.options.onOpenStart === "function") {
5105
+ this.options.onOpenStart.call(this, this.el);
5106
+ }
5107
+ this.isOpen = true;
5108
+ this._dialog.classList.remove("dragging");
5109
+ this._clearShift();
5110
+ if (!this._dialog.open) {
5111
+ try {
5112
+ this._dialog.showModal();
5113
+ } catch {
5270
5114
  }
5271
- this.isDragged = false;
5272
- this._verticallyScrolling = false;
5273
5115
  }
5274
- };
5275
- this._handleCloseDrag = (e) => {
5276
- if (!this.isOpen || !this._isDraggable()) return;
5277
- let totalDeltaX = this._calculateDelta(e);
5278
- const dragDirection = totalDeltaX > 0 ? "right" : "left";
5279
- totalDeltaX = Math.min(this._width, Math.abs(totalDeltaX));
5280
- if (this.options.edge !== dragDirection) {
5281
- totalDeltaX = 0;
5116
+ this._syncTriggers();
5117
+ if (typeof this.options.onOpenEnd === "function") {
5118
+ this.options.onOpenEnd.call(this, this.el);
5282
5119
  }
5283
- let transformX = -totalDeltaX;
5284
- if (this.options.edge === "right") {
5285
- transformX = -transformX;
5120
+ };
5121
+ /**
5122
+ * Closes the overlay drawer. No-op while the sidenav is docked.
5123
+ */
5124
+ this.close = () => {
5125
+ if (this._isCurrentlyFixed() || !this.isOpen) return;
5126
+ this._beginClose();
5127
+ if (this._dialog?.open) {
5128
+ this._closing = true;
5129
+ this._dialog.close();
5130
+ } else {
5131
+ this._finishClose();
5286
5132
  }
5287
- this.percentOpen = Math.min(1, 1 - totalDeltaX / this._width);
5288
- this.el.style.transform = `translateX(${transformX}px)`;
5289
- this._overlay.style.opacity = this.percentOpen.toString();
5290
5133
  };
5291
- this._calculateDelta = (e) => {
5292
- if (!this.isDragged) {
5293
- this._startDrag(e);
5134
+ this._onDialogClose = () => {
5135
+ if (this.isOpen) {
5136
+ this._beginClose();
5137
+ this._finishClose();
5138
+ return;
5139
+ }
5140
+ if (this._closing) {
5141
+ this._closing = false;
5142
+ this._finishClose();
5294
5143
  }
5295
- this._dragMoveUpdate(e);
5296
- return this._xPos - this._startingXpos;
5297
5144
  };
5298
- this._handleCloseRelease = () => {
5299
- if (this.isOpen && this.isDragged) {
5300
- if (this.percentOpen > 0.8) {
5301
- this._animateIn();
5302
- } else {
5303
- this.close();
5304
- }
5305
- this.isDragged = false;
5306
- this._verticallyScrolling = false;
5145
+ this._onBreakpointChange = () => {
5146
+ if (this._isCurrentlyFixed() && this.isOpen) {
5147
+ this.close();
5307
5148
  }
5308
5149
  };
5309
- // Handles closing of Sidenav when element with class .sidenav-close
5310
5150
  this._handleCloseTriggerClick = (e) => {
5311
- const closeTrigger = e.target.closest(".sidenav-close");
5151
+ const target = e.target;
5152
+ if (!(target instanceof Element)) return;
5153
+ const closeTrigger = target.closest(".sidenav-close");
5312
5154
  if (closeTrigger && !this._isCurrentlyFixed()) {
5313
5155
  this.close();
5314
5156
  }
5315
5157
  };
5316
- this._handleWindowResize = () => {
5317
- if (this.lastWindowWidth !== window.innerWidth) {
5318
- if (window.innerWidth > 992) {
5319
- this.open();
5320
- } else {
5321
- this.close();
5322
- }
5323
- }
5324
- this.lastWindowWidth = window.innerWidth;
5325
- this.lastWindowHeight = window.innerHeight;
5158
+ this._handleDragTargetDrag = (e) => {
5159
+ if (!this._isDraggable()) return;
5160
+ const delta = this._rawDelta(e);
5161
+ const towardOpen = this.options.edge === "left" ? delta : -delta;
5162
+ const visible = Math.min(this._width, Math.max(0, towardOpen));
5163
+ const percentOpen = this._width ? visible / this._width : 0;
5164
+ this._applyShift(visible - this._width, percentOpen);
5326
5165
  };
5327
- /**
5328
- * Opens Sidenav.
5329
- */
5330
- this.open = () => {
5331
- if (this.isOpen === true) return;
5332
- this.isOpen = true;
5333
- if (typeof this.options.onOpenStart === "function") {
5334
- this.options.onOpenStart.call(this, this.el);
5335
- }
5336
- if (this._isCurrentlyFixed()) {
5337
- this.el.style.transform = "translateX(0)";
5338
- this._enableBodyScrolling();
5339
- this._overlay.style.display = "none";
5340
- } else {
5341
- if (this.options.preventScrolling) this._preventBodyScrolling();
5342
- if (!this.isDragged || this.percentOpen != 1) this._animateIn();
5343
- this._setAriaHidden();
5344
- this._setTabIndex();
5166
+ this._handleDragTargetRelease = () => {
5167
+ if (!this.isDragged) return;
5168
+ this._dialog?.classList.remove("dragging");
5169
+ if (this.percentOpen > 0.2) {
5170
+ this.open();
5171
+ } else if (this._dialog?.open && !this.isOpen) {
5172
+ this._clearShift();
5173
+ this._dialog.close();
5345
5174
  }
5175
+ this.isDragged = false;
5176
+ this._verticallyScrolling = false;
5346
5177
  };
5347
- /**
5348
- * Closes Sidenav.
5349
- */
5350
- this.close = () => {
5351
- if (this.isOpen === false) return;
5352
- this.isOpen = false;
5353
- if (typeof this.options.onCloseStart === "function") {
5354
- this.options.onCloseStart.call(this, this.el);
5355
- }
5356
- if (this._isCurrentlyFixed()) {
5357
- const transformX = this.options.edge === "left" ? "-105%" : "105%";
5358
- this.el.style.transform = `translateX(${transformX})`;
5178
+ this._handleCloseDrag = (e) => {
5179
+ if (!this.isOpen || !this._isDraggable()) return;
5180
+ const delta = this._rawDelta(e);
5181
+ const towardClose = this.options.edge === "left" ? -delta : delta;
5182
+ const hidden = Math.min(this._width, Math.max(0, towardClose));
5183
+ const percentOpen = this._width ? 1 - hidden / this._width : 1;
5184
+ this._applyShift(-hidden, percentOpen);
5185
+ };
5186
+ this._handleCloseRelease = () => {
5187
+ if (!this.isOpen || !this.isDragged) return;
5188
+ this._dialog?.classList.remove("dragging");
5189
+ if (this.percentOpen > 0.8) {
5190
+ this._clearShift();
5359
5191
  } else {
5360
- this._enableBodyScrolling();
5361
- if (!this.isDragged || this.percentOpen != 0) {
5362
- this._animateOut();
5363
- } else {
5364
- this._overlay.style.display = "none";
5365
- }
5366
- this._setAriaHidden();
5367
- this._setTabIndex();
5192
+ this.close();
5368
5193
  }
5369
- };
5370
- this._setAriaHidden = () => {
5371
- this.el.ariaHidden = this.isOpen ? "false" : "true";
5372
- const navWrapper = document.querySelector(".nav-wrapper ul");
5373
- if (navWrapper) navWrapper.ariaHidden = this.isOpen.toString();
5374
- };
5375
- this._setTabIndex = () => {
5376
- const navLinks = document.querySelectorAll(".nav-wrapper ul li a");
5377
- const sideNavLinks = document.querySelectorAll(".sidenav li a");
5378
- if (navLinks)
5379
- navLinks.forEach((navLink) => {
5380
- navLink.tabIndex = this.isOpen ? -1 : 0;
5381
- });
5382
- if (sideNavLinks)
5383
- sideNavLinks.forEach((sideNavLink) => {
5384
- sideNavLink.tabIndex = this.isOpen ? 0 : -1;
5385
- });
5194
+ this.isDragged = false;
5195
+ this._verticallyScrolling = false;
5386
5196
  };
5387
5197
  this.el["Expressive_Sidenav"] = this;
5388
5198
  this.options = {
@@ -5393,17 +5203,26 @@ var _Sidenav = class _Sidenav extends Component {
5393
5203
  this.isOpen = false;
5394
5204
  this.isFixed = this.el.classList.contains("sidenav-fixed");
5395
5205
  this.isDragged = false;
5396
- this.lastWindowWidth = window.innerWidth;
5397
- this.lastWindowHeight = window.innerHeight;
5398
- this._createOverlay();
5206
+ this.dragTarget = null;
5207
+ this._dialog = null;
5208
+ this._createdDialog = false;
5209
+ this._closing = false;
5210
+ this.percentOpen = 0;
5211
+ this._startingXpos = 0;
5212
+ this._xPos = 0;
5213
+ this._width = 0;
5214
+ this._initialScrollTop = 0;
5215
+ this._verticallyScrolling = false;
5216
+ this._mql = window.matchMedia(LARGE_UP);
5217
+ this._setupDialog();
5399
5218
  this._createDragTarget();
5400
- this._setupEventHandlers();
5401
5219
  this._setupClasses();
5402
- this._setupFixed();
5220
+ this._setupEventHandlers();
5221
+ this._syncTriggers();
5403
5222
  _Sidenav._sidenavs.push(this);
5404
5223
  }
5405
5224
  static get defaults() {
5406
- return _defaults18;
5225
+ return _defaults17;
5407
5226
  }
5408
5227
  /**
5409
5228
  * Initializes instances of Sidenav.
@@ -5418,182 +5237,178 @@ var _Sidenav = class _Sidenav extends Component {
5418
5237
  }
5419
5238
  destroy() {
5420
5239
  this._removeEventHandlers();
5421
- this._enableBodyScrolling();
5422
- this._overlay.parentNode.removeChild(this._overlay);
5423
- this.dragTarget.parentNode.removeChild(this.dragTarget);
5240
+ if (this._dialog?.open) {
5241
+ try {
5242
+ this._dialog.close();
5243
+ } catch {
5244
+ }
5245
+ }
5246
+ this._clearShift();
5247
+ this._unwrapDialog();
5248
+ this._removeDragTarget();
5424
5249
  this.el["Expressive_Sidenav"] = void 0;
5425
- this.el.style.transform = "";
5426
5250
  const index = _Sidenav._sidenavs.indexOf(this);
5427
5251
  if (index >= 0) {
5428
5252
  _Sidenav._sidenavs.splice(index, 1);
5429
5253
  }
5430
5254
  }
5431
- _createOverlay() {
5432
- this._overlay = document.createElement("div");
5433
- this._overlay.classList.add("sidenav-overlay");
5434
- this._overlay.addEventListener("click", this.close);
5435
- document.body.appendChild(this._overlay);
5255
+ _beginClose() {
5256
+ this.isOpen = false;
5257
+ this._dialog?.classList.remove("dragging");
5258
+ this._clearShift();
5259
+ if (typeof this.options.onCloseStart === "function") {
5260
+ this.options.onCloseStart.call(this, this.el);
5261
+ }
5262
+ this._syncTriggers();
5263
+ }
5264
+ _finishClose() {
5265
+ if (typeof this.options.onCloseEnd === "function") {
5266
+ this.options.onCloseEnd.call(this, this.el);
5267
+ }
5268
+ }
5269
+ _setupDialog() {
5270
+ if (this.el instanceof HTMLDialogElement) {
5271
+ this._dialog = this.el;
5272
+ this._createdDialog = false;
5273
+ return;
5274
+ }
5275
+ const parent = this.el.parentElement;
5276
+ if (parent instanceof HTMLDialogElement && parent.classList.contains("sidenav-overlay")) {
5277
+ this._dialog = parent;
5278
+ this._createdDialog = false;
5279
+ return;
5280
+ }
5281
+ const dialog = document.createElement("dialog");
5282
+ dialog.classList.add("sidenav-overlay");
5283
+ this.el.replaceWith(dialog);
5284
+ dialog.appendChild(this.el);
5285
+ this._dialog = dialog;
5286
+ this._createdDialog = true;
5287
+ }
5288
+ _unwrapDialog() {
5289
+ if (!this._createdDialog || !this._dialog) return;
5290
+ this._dialog.replaceWith(this.el);
5291
+ this._dialog = null;
5292
+ this._createdDialog = false;
5293
+ }
5294
+ _createDragTarget() {
5295
+ if (!this.options.draggable) return;
5296
+ const dragTarget = document.createElement("div");
5297
+ dragTarget.classList.add("drag-target");
5298
+ dragTarget.style.width = this.options.dragTargetWidth;
5299
+ if (this.isFixed) dragTarget.dataset.fixed = "";
5300
+ document.body.appendChild(dragTarget);
5301
+ this.dragTarget = dragTarget;
5302
+ }
5303
+ _removeDragTarget() {
5304
+ this.dragTarget?.remove();
5305
+ this.dragTarget = null;
5306
+ }
5307
+ _setupClasses() {
5308
+ if (this.options.edge === "right") {
5309
+ this.el.classList.add("right-aligned");
5310
+ this._dialog?.classList.add("right-aligned");
5311
+ this.dragTarget?.classList.add("right-aligned");
5312
+ }
5436
5313
  }
5437
5314
  _setupEventHandlers() {
5438
5315
  if (_Sidenav._sidenavs.length === 0) {
5439
- document.body.addEventListener("click", this._handleTriggerClick);
5316
+ document.body.addEventListener("click", _Sidenav._handleTriggerClick);
5440
5317
  }
5441
5318
  const passive = { passive: true };
5442
- this.dragTarget.addEventListener("touchmove", this._handleDragTargetDrag, passive);
5443
- this.dragTarget.addEventListener("touchend", this._handleDragTargetRelease);
5444
- this._overlay.addEventListener("touchmove", this._handleCloseDrag, passive);
5445
- this._overlay.addEventListener("touchend", this._handleCloseRelease);
5446
- this.el.addEventListener("touchmove", this._handleCloseDrag, passive);
5447
- this.el.addEventListener("touchend", this._handleCloseRelease);
5319
+ this.dragTarget?.addEventListener("touchmove", this._handleDragTargetDrag, passive);
5320
+ this.dragTarget?.addEventListener("touchend", this._handleDragTargetRelease);
5321
+ this._dialog?.addEventListener("touchmove", this._handleCloseDrag, passive);
5322
+ this._dialog?.addEventListener("touchend", this._handleCloseRelease);
5323
+ this._dialog?.addEventListener("close", this._onDialogClose);
5448
5324
  this.el.addEventListener("click", this._handleCloseTriggerClick);
5449
- if (this.isFixed) {
5450
- window.addEventListener("resize", this._handleWindowResize, passive);
5451
- }
5452
- this._setAriaHidden();
5453
- this._setTabIndex();
5325
+ this._mql.addEventListener("change", this._onBreakpointChange);
5454
5326
  }
5455
5327
  _removeEventHandlers() {
5456
5328
  if (_Sidenav._sidenavs.length === 1) {
5457
- document.body.removeEventListener("click", this._handleTriggerClick);
5458
- }
5459
- this.dragTarget.removeEventListener("touchmove", this._handleDragTargetDrag);
5460
- this.dragTarget.removeEventListener("touchend", this._handleDragTargetRelease);
5461
- this._overlay.removeEventListener("touchmove", this._handleCloseDrag);
5462
- this._overlay.removeEventListener("touchend", this._handleCloseRelease);
5463
- this.el.removeEventListener("touchmove", this._handleCloseDrag);
5464
- this.el.removeEventListener("touchend", this._handleCloseRelease);
5465
- this.el.removeEventListener("click", this._handleCloseTriggerClick);
5466
- if (this.isFixed) {
5467
- window.removeEventListener("resize", this._handleWindowResize);
5329
+ document.body.removeEventListener("click", _Sidenav._handleTriggerClick);
5468
5330
  }
5331
+ this.dragTarget?.removeEventListener("touchmove", this._handleDragTargetDrag);
5332
+ this.dragTarget?.removeEventListener("touchend", this._handleDragTargetRelease);
5333
+ this._dialog?.removeEventListener("touchmove", this._handleCloseDrag);
5334
+ this._dialog?.removeEventListener("touchend", this._handleCloseRelease);
5335
+ this._dialog?.removeEventListener("close", this._onDialogClose);
5336
+ this.el.removeEventListener("click", this._handleCloseTriggerClick);
5337
+ this._mql.removeEventListener("change", this._onBreakpointChange);
5469
5338
  }
5470
- _handleTriggerClick(e) {
5471
- const trigger = e.target.closest(".sidenav-trigger");
5472
- if (e.target && trigger) {
5473
- const sidenavId = Utils.getIdFromTrigger(trigger);
5474
- const sidenavInstance = document.getElementById(sidenavId)?.["Expressive_Sidenav"];
5475
- if (sidenavInstance) {
5476
- sidenavInstance.open();
5477
- }
5339
+ static _handleTriggerClick(e) {
5340
+ const target = e.target;
5341
+ if (!(target instanceof Element)) return;
5342
+ const trigger = target.closest(".sidenav-trigger");
5343
+ if (!trigger || !(trigger instanceof HTMLElement)) return;
5344
+ const sidenavId = Utils.getIdFromTrigger(trigger);
5345
+ const sidenavInstance = document.getElementById(sidenavId)?.["Expressive_Sidenav"];
5346
+ if (sidenavInstance) {
5347
+ sidenavInstance.open();
5478
5348
  e.preventDefault();
5479
5349
  }
5480
5350
  }
5481
- // Set variables needed at the beginning of drag and stop any current transition.
5351
+ _isDraggable() {
5352
+ return this.options.draggable && !this._isCurrentlyFixed() && !this._verticallyScrolling;
5353
+ }
5354
+ _isCurrentlyFixed() {
5355
+ return this.isFixed && this._mql.matches;
5356
+ }
5482
5357
  _startDrag(e) {
5483
5358
  const clientX = e.targetTouches[0].clientX;
5484
5359
  this.isDragged = true;
5485
5360
  this._startingXpos = clientX;
5486
- this._xPos = this._startingXpos;
5487
- this._time = Date.now();
5488
- this._width = this.el.getBoundingClientRect().width;
5489
- this._overlay.style.display = "block";
5361
+ this._xPos = clientX;
5362
+ this._width = this._dialog?.getBoundingClientRect().width || this.el.getBoundingClientRect().width;
5490
5363
  this._initialScrollTop = this.isOpen ? this.el.scrollTop : Utils.getDocumentScrollTop();
5491
5364
  this._verticallyScrolling = false;
5365
+ this._dialog?.classList.add("dragging");
5366
+ if (this._dialog && !this._dialog.open) {
5367
+ try {
5368
+ this._dialog.showModal();
5369
+ } catch {
5370
+ }
5371
+ }
5492
5372
  }
5493
- //Set variables needed at each drag move update tick
5494
5373
  _dragMoveUpdate(e) {
5495
5374
  const clientX = e.targetTouches[0].clientX;
5496
5375
  const currentScrollTop = this.isOpen ? this.el.scrollTop : Utils.getDocumentScrollTop();
5497
- this.deltaX = Math.abs(this._xPos - clientX);
5498
5376
  this._xPos = clientX;
5499
- this.velocityX = this.deltaX / (Date.now() - this._time);
5500
- this._time = Date.now();
5501
5377
  if (this._initialScrollTop !== currentScrollTop) {
5502
5378
  this._verticallyScrolling = true;
5503
5379
  }
5504
5380
  }
5505
- _setupClasses() {
5506
- if (this.options.edge === "right") {
5507
- this.el.classList.add("right-aligned");
5508
- this.dragTarget.classList.add("right-aligned");
5509
- }
5510
- }
5511
- _removeClasses() {
5512
- this.el.classList.remove("right-aligned");
5513
- this.dragTarget.classList.remove("right-aligned");
5514
- }
5515
- _setupFixed() {
5516
- if (this._isCurrentlyFixed()) this.open();
5517
- }
5518
- _isDraggable() {
5519
- return this.options.draggable && !this._isCurrentlyFixed() && !this._verticallyScrolling;
5520
- }
5521
- _isCurrentlyFixed() {
5522
- return this.isFixed && window.innerWidth > 992;
5523
- }
5524
- _createDragTarget() {
5525
- const dragTarget = document.createElement("div");
5526
- dragTarget.classList.add("drag-target");
5527
- dragTarget.style.width = this.options.dragTargetWidth;
5528
- document.body.appendChild(dragTarget);
5529
- this.dragTarget = dragTarget;
5530
- }
5531
- _preventBodyScrolling() {
5532
- document.body.style.overflow = "hidden";
5533
- }
5534
- _enableBodyScrolling() {
5535
- document.body.style.overflow = "";
5536
- }
5537
- _animateIn() {
5538
- this._animateSidenavIn();
5539
- this._animateOverlayIn();
5540
- }
5541
- _animateOut() {
5542
- this._animateSidenavOut();
5543
- this._animateOverlayOut();
5544
- }
5545
- _animateSidenavIn() {
5546
- let slideOutPercent = this.options.edge === "left" ? -1 : 1;
5547
- if (this.isDragged) {
5548
- slideOutPercent = this.options.edge === "left" ? slideOutPercent + this.percentOpen : slideOutPercent - this.percentOpen;
5549
- }
5550
- const duration = this.options.inDuration;
5551
- this.el.style.transition = "none";
5552
- this.el.style.transform = "translateX(" + slideOutPercent * 100 + "%)";
5553
- setTimeout(() => {
5554
- this.el.style.transition = `transform ${duration}ms ease`;
5555
- this.el.style.transform = "translateX(0)";
5556
- }, 1);
5557
- setTimeout(() => {
5558
- if (typeof this.options.onOpenEnd === "function") this.options.onOpenEnd.call(this, this.el);
5559
- }, duration);
5560
- }
5561
- _animateSidenavOut() {
5562
- const endPercent = this.options.edge === "left" ? -1 : 1;
5563
- const duration = this.options.outDuration;
5564
- this.el.style.transition = `transform ${duration}ms ease`;
5565
- this.el.style.transform = "translateX(" + endPercent * 100 + "%)";
5566
- setTimeout(() => {
5567
- if (typeof this.options.onCloseEnd === "function")
5568
- this.options.onCloseEnd.call(this, this.el);
5569
- }, duration);
5570
- }
5571
- _animateOverlayIn() {
5572
- let start = 0;
5573
- if (this.isDragged) start = this.percentOpen;
5574
- else this._overlay.style.display = "block";
5575
- const duration = this.options.inDuration;
5576
- this._overlay.style.transition = "none";
5577
- this._overlay.style.opacity = start.toString();
5578
- setTimeout(() => {
5579
- this._overlay.style.transition = `opacity ${duration}ms ease`;
5580
- this._overlay.style.opacity = "1";
5581
- }, 1);
5582
- }
5583
- _animateOverlayOut() {
5584
- const duration = this.options.outDuration;
5585
- this._overlay.style.transition = `opacity ${duration}ms ease`;
5586
- this._overlay.style.opacity = "0";
5587
- setTimeout(() => {
5588
- this._overlay.style.display = "none";
5589
- }, duration);
5381
+ _rawDelta(e) {
5382
+ if (!this.isDragged) this._startDrag(e);
5383
+ this._dragMoveUpdate(e);
5384
+ return this._xPos - this._startingXpos;
5385
+ }
5386
+ _applyShift(shift, percentOpen) {
5387
+ this.percentOpen = percentOpen;
5388
+ const host = this._dialog ?? this.el;
5389
+ host.style.setProperty("--md-comp-nav-drawer-shift", `${shift}px`);
5390
+ host.style.setProperty("--md-comp-nav-drawer-open", String(percentOpen));
5391
+ }
5392
+ _clearShift() {
5393
+ const host = this._dialog ?? this.el;
5394
+ host.style.removeProperty("--md-comp-nav-drawer-shift");
5395
+ host.style.removeProperty("--md-comp-nav-drawer-open");
5396
+ }
5397
+ _syncTriggers() {
5398
+ if (!this.id) return;
5399
+ document.querySelectorAll(".sidenav-trigger").forEach((trigger) => {
5400
+ if (!(trigger instanceof HTMLElement)) return;
5401
+ if (Utils.getIdFromTrigger(trigger) !== this.id) return;
5402
+ trigger.setAttribute("aria-expanded", this.isOpen ? "true" : "false");
5403
+ trigger.setAttribute("aria-controls", this.id);
5404
+ });
5590
5405
  }
5591
5406
  };
5592
5407
  _Sidenav._sidenavs = [];
5593
5408
  var Sidenav = _Sidenav;
5594
5409
 
5595
5410
  // src/ts/components/slider.ts
5596
- var _defaults19 = {
5411
+ var _defaults18 = {
5597
5412
  indicators: true,
5598
5413
  height: 400,
5599
5414
  duration: 500,
@@ -5741,7 +5556,7 @@ var Slider = class _Slider extends Component {
5741
5556
  this.start();
5742
5557
  }
5743
5558
  static get defaults() {
5744
- return _defaults19;
5559
+ return _defaults18;
5745
5560
  }
5746
5561
  /**
5747
5562
  * Initializes instances of Slider.
@@ -5894,7 +5709,7 @@ var Slider = class _Slider extends Component {
5894
5709
  };
5895
5710
 
5896
5711
  // src/ts/components/tabs.ts
5897
- var _defaults20 = {
5712
+ var _defaults19 = {
5898
5713
  duration: 300,
5899
5714
  onShow: null,
5900
5715
  swipeable: false,
@@ -5972,7 +5787,7 @@ var Tabs = class _Tabs extends Component {
5972
5787
  this._setupEventHandlers();
5973
5788
  }
5974
5789
  static get defaults() {
5975
- return _defaults20;
5790
+ return _defaults19;
5976
5791
  }
5977
5792
  /**
5978
5793
  * Initializes instances of Tabs.
@@ -6011,7 +5826,7 @@ var Tabs = class _Tabs extends Component {
6011
5826
  }
6012
5827
  _queryTabLinks() {
6013
5828
  return this.el.querySelectorAll(
6014
- ":scope > a, :scope > .tab > a, :scope > li.tab > a"
5829
+ ":scope > a, :scope > .tab > a"
6015
5830
  );
6016
5831
  }
6017
5832
  _createIndicator() {
@@ -6053,7 +5868,7 @@ var Tabs = class _Tabs extends Component {
6053
5868
  }
6054
5869
  });
6055
5870
  const tabsWrapper = document.createElement("div");
6056
- tabsWrapper.classList.add("tabs-content", "carousel", "carousel-slider");
5871
+ tabsWrapper.classList.add("tabs-content", "carousel", "flat");
6057
5872
  tabsContent[0].parentElement.insertBefore(tabsWrapper, tabsContent[0]);
6058
5873
  tabsContent.forEach((tabContent) => {
6059
5874
  tabsWrapper.appendChild(tabContent);
@@ -6140,67 +5955,103 @@ var Tabs = class _Tabs extends Component {
6140
5955
  };
6141
5956
 
6142
5957
  // src/ts/components/tapTarget.ts
6143
- var _defaults21 = {
5958
+ var _defaults20 = {
6144
5959
  onOpen: null,
6145
5960
  onClose: null
6146
5961
  };
6147
5962
  var _TapTarget = class _TapTarget extends Component {
6148
5963
  constructor(el, options) {
6149
5964
  super(el, options, _TapTarget);
6150
- // Built once per instance; see the note in carousel.ts - the previous form
6151
- // returned a new throttled function on every resize and never invoked it.
6152
- this._handleThrottledResize = Utils.throttle(() => this._handleResize(), 200);
6153
- this._handleKeyboardInteraction = (e) => {
6154
- if (Utils.keys.ENTER.includes(e.key)) {
6155
- this._handleTargetToggle();
6156
- }
6157
- };
6158
- this._handleTargetToggle = () => {
6159
- if (!this.isOpen) this.open();
6160
- else this.close();
6161
- };
6162
- /*_handleOriginClick = () => {
6163
- this.close();
6164
- }*/
6165
- this._handleResize = () => {
6166
- this._calculatePositioning();
6167
- };
6168
- this._handleDocumentClick = (e) => {
6169
- const target = e.target;
6170
- if (!(target instanceof Element)) return;
6171
- if (!this.originEl.contains(target) && !target.closest(".tap-target-wrapper")) {
6172
- this.close();
6173
- }
6174
- };
5965
+ this.wrapper = null;
5966
+ this.waveEl = null;
5967
+ this.contentEl = null;
5968
+ this._createdWrapper = false;
5969
+ this._createdWave = false;
5970
+ this._usePopover = false;
5971
+ this._liftedStatic = false;
5972
+ this._opening = false;
6175
5973
  /**
6176
5974
  * Open Tap Target.
6177
5975
  */
6178
5976
  this.open = () => {
6179
- if (this.isOpen) return;
5977
+ if (this.isOpen || !this.originEl || !this.wrapper) return;
5978
+ _TapTarget._taptargets.forEach((other) => {
5979
+ if (other !== this) other.close();
5980
+ });
5981
+ this._calculatePositioning();
6180
5982
  if (typeof this.options.onOpen === "function") {
6181
5983
  this.options.onOpen.call(this, this.originEl);
6182
5984
  }
6183
5985
  this.isOpen = true;
6184
5986
  this.wrapper.classList.add("open");
6185
- this.wrapper.ariaExpanded = "true";
6186
- document.body.addEventListener("click", this._handleDocumentClick, true);
6187
- document.body.addEventListener("keypress", this._handleDocumentClick, true);
6188
- document.body.addEventListener("touchend", this._handleDocumentClick);
5987
+ this.originEl.setAttribute("aria-expanded", "true");
5988
+ if (!this._usePopover) {
5989
+ this.originEl.classList.add("tap-target-origin");
5990
+ if (getComputedStyle(this.originEl).position === "static") {
5991
+ this.originEl.style.position = "relative";
5992
+ this._liftedStatic = true;
5993
+ }
5994
+ }
5995
+ if (this._usePopover) {
5996
+ this._opening = true;
5997
+ try {
5998
+ this.wrapper.showPopover();
5999
+ } catch {
6000
+ }
6001
+ requestAnimationFrame(() => {
6002
+ this._opening = false;
6003
+ });
6004
+ }
6189
6005
  };
6190
6006
  /**
6191
6007
  * Close Tap Target.
6192
6008
  */
6193
6009
  this.close = () => {
6194
6010
  if (!this.isOpen) return;
6195
- if (typeof this.options.onClose === "function") {
6011
+ if (typeof this.options.onClose === "function" && this.originEl) {
6196
6012
  this.options.onClose.call(this, this.originEl);
6197
6013
  }
6198
6014
  this.isOpen = false;
6199
- this.wrapper.classList.remove("open");
6200
- this.wrapper.ariaExpanded = "false";
6201
- document.body.removeEventListener("click", this._handleDocumentClick, true);
6202
- document.body.removeEventListener("keypress", this._handleDocumentClick, true);
6203
- document.body.removeEventListener("touchend", this._handleDocumentClick);
6015
+ this.wrapper?.classList.remove("open");
6016
+ this.originEl?.setAttribute("aria-expanded", "false");
6017
+ this.originEl?.classList.remove("tap-target-origin");
6018
+ if (this._liftedStatic && this.originEl) {
6019
+ this.originEl.style.position = "";
6020
+ this._liftedStatic = false;
6021
+ }
6022
+ if (this._usePopover && this.wrapper) {
6023
+ try {
6024
+ this.wrapper.hidePopover();
6025
+ } catch {
6026
+ }
6027
+ }
6028
+ };
6029
+ // Built once per instance; a fresh throttle per event never fires.
6030
+ this._handleThrottledResize = Utils.throttle(() => this._handleResize(), 200);
6031
+ this._handleOriginKeydown = (e) => {
6032
+ if (Utils.keys.ENTER.includes(e.key) || e.key === " ") {
6033
+ e.preventDefault();
6034
+ this._handleTargetToggle();
6035
+ }
6036
+ };
6037
+ this._handleTargetToggle = () => {
6038
+ if (!this.isOpen) this.open();
6039
+ else this.close();
6040
+ };
6041
+ this._handleResize = () => {
6042
+ if (this.isOpen) this._calculatePositioning();
6043
+ };
6044
+ this._handlePopoverToggle = (e) => {
6045
+ const state = "newState" in e ? String(e.newState) : "";
6046
+ if (state === "open") return;
6047
+ if (this._opening) {
6048
+ try {
6049
+ this.wrapper?.showPopover();
6050
+ } catch {
6051
+ }
6052
+ return;
6053
+ }
6054
+ if (this.isOpen) this.close();
6204
6055
  };
6205
6056
  this.el["Expressive_TapTarget"] = this;
6206
6057
  this.options = {
@@ -6213,20 +6064,14 @@ var _TapTarget = class _TapTarget extends Component {
6213
6064
  console.error(`TapTarget: no element with id "${el.dataset.target ?? ""}" to attach to`);
6214
6065
  return;
6215
6066
  }
6216
- this.originEl.tabIndex = 0;
6217
6067
  this._setup();
6218
6068
  this._calculatePositioning();
6219
6069
  this._setupEventHandlers();
6220
6070
  _TapTarget._taptargets.push(this);
6221
6071
  }
6222
6072
  static get defaults() {
6223
- return _defaults21;
6073
+ return _defaults20;
6224
6074
  }
6225
- /**
6226
- * Initializes instances of TapTarget.
6227
- * @param els HTML elements.
6228
- * @param options Component options.
6229
- */
6230
6075
  static init(els, options = {}) {
6231
6076
  return super.init(els, options, _TapTarget);
6232
6077
  }
@@ -6236,123 +6081,102 @@ var _TapTarget = class _TapTarget extends Component {
6236
6081
  destroy() {
6237
6082
  this.close();
6238
6083
  this._removeEventHandlers();
6084
+ this._teardownDom();
6239
6085
  this.el["Expressive_TapTarget"] = void 0;
6240
6086
  const index = _TapTarget._taptargets.indexOf(this);
6241
- if (index >= 0) {
6242
- _TapTarget._taptargets.splice(index, 1);
6243
- }
6244
- }
6245
- _setupEventHandlers() {
6246
- if (!this.originEl) return;
6247
- this.originEl.addEventListener("click", this._handleTargetToggle);
6248
- this.originEl.addEventListener("keypress", this._handleKeyboardInteraction, true);
6249
- window.addEventListener("resize", this._handleThrottledResize);
6250
- }
6251
- _removeEventHandlers() {
6252
- if (!this.originEl) return;
6253
- this.originEl.removeEventListener("click", this._handleTargetToggle);
6254
- this.originEl.removeEventListener("keypress", this._handleKeyboardInteraction, true);
6255
- window.removeEventListener("resize", this._handleThrottledResize);
6087
+ if (index >= 0) _TapTarget._taptargets.splice(index, 1);
6256
6088
  }
6257
6089
  _setup() {
6258
- this.wrapper = this.el.parentElement;
6259
- this.waveEl = this.wrapper.querySelector(".tap-target-wave");
6260
- this.el.parentElement.ariaExpanded = "false";
6261
- this.originEl.style.zIndex = "1002";
6262
- this.contentEl = this.el.querySelector(".tap-target-content");
6263
- if (!this.wrapper.classList.contains(".tap-target-wrapper")) {
6090
+ const parent = this.el.parentElement;
6091
+ if (parent?.classList.contains("tap-target-wrapper")) {
6092
+ this.wrapper = parent;
6093
+ } else {
6264
6094
  this.wrapper = document.createElement("div");
6265
6095
  this.wrapper.classList.add("tap-target-wrapper");
6266
6096
  this.el.before(this.wrapper);
6267
6097
  this.wrapper.append(this.el);
6098
+ this._createdWrapper = true;
6268
6099
  }
6100
+ this.contentEl = this.el.querySelector(".tap-target-content");
6269
6101
  if (!this.contentEl) {
6270
6102
  this.contentEl = document.createElement("div");
6271
- this.contentEl.classList.add("tap-target-content");
6103
+ this.contentEl.className = "tap-target-content";
6104
+ while (this.el.firstChild) this.contentEl.append(this.el.firstChild);
6272
6105
  this.el.append(this.contentEl);
6273
6106
  }
6107
+ this.waveEl = this.el.querySelector(".tap-target-wave") ?? this.wrapper.querySelector(".tap-target-wave");
6274
6108
  if (!this.waveEl) {
6275
6109
  this.waveEl = document.createElement("div");
6276
- this.waveEl.classList.add("tap-target-wave");
6277
- this.wrapper.append(this.waveEl);
6110
+ this.waveEl.className = "tap-target-wave";
6111
+ this._createdWave = true;
6112
+ }
6113
+ this.el.prepend(this.waveEl);
6114
+ this.el.setAttribute("role", "dialog");
6115
+ this._labelFromHeading();
6116
+ const origin = this.originEl;
6117
+ origin.setAttribute("aria-expanded", "false");
6118
+ origin.setAttribute("aria-controls", this.el.id || this._ensureId());
6119
+ this._usePopover = typeof this.wrapper.showPopover === "function";
6120
+ if (this._usePopover) {
6121
+ this.wrapper.setAttribute("popover", "auto");
6122
+ }
6123
+ }
6124
+ _ensureId() {
6125
+ if (!this.el.id) this.el.id = `tap-target-${Utils.guid()}`;
6126
+ return this.el.id;
6127
+ }
6128
+ _labelFromHeading() {
6129
+ const heading = this.contentEl?.querySelector("h1, h2, h3, h4, h5, h6");
6130
+ if (!heading) return;
6131
+ if (!heading.id) heading.id = `tap-target-title-${Utils.guid()}`;
6132
+ this.el.setAttribute("aria-labelledby", heading.id);
6133
+ }
6134
+ _teardownDom() {
6135
+ if (this._createdWave) this.waveEl?.remove();
6136
+ if (this._createdWrapper && this.wrapper) {
6137
+ this.wrapper.before(this.el);
6138
+ this.wrapper.remove();
6139
+ }
6140
+ this.wrapper = null;
6141
+ this.waveEl = null;
6142
+ }
6143
+ _setupEventHandlers() {
6144
+ if (!this.originEl) return;
6145
+ this.originEl.addEventListener("click", this._handleTargetToggle);
6146
+ if (!this.originEl.matches('button, a, [tabindex], [role="button"]')) {
6147
+ this.originEl.tabIndex = 0;
6148
+ this.originEl.addEventListener("keydown", this._handleOriginKeydown);
6278
6149
  }
6150
+ this.wrapper?.addEventListener("toggle", this._handlePopoverToggle);
6151
+ window.addEventListener("resize", this._handleThrottledResize, { passive: true });
6279
6152
  }
6280
- _offset(el) {
6281
- const box = el.getBoundingClientRect();
6282
- const docElem = document.documentElement;
6283
- return {
6284
- top: box.top + window.pageYOffset - docElem.clientTop,
6285
- left: box.left + window.pageXOffset - docElem.clientLeft
6286
- };
6153
+ _removeEventHandlers() {
6154
+ if (!this.originEl) return;
6155
+ this.originEl.removeEventListener("click", this._handleTargetToggle);
6156
+ this.originEl.removeEventListener("keydown", this._handleOriginKeydown);
6157
+ this.wrapper?.removeEventListener("toggle", this._handlePopoverToggle);
6158
+ window.removeEventListener("resize", this._handleThrottledResize);
6287
6159
  }
6288
6160
  _calculatePositioning() {
6289
- let isFixed = getComputedStyle(this.originEl).position === "fixed";
6290
- if (!isFixed) {
6291
- let currentElem = this.originEl;
6292
- const parents = [];
6293
- while ((currentElem = currentElem.parentNode) && currentElem !== document)
6294
- parents.push(currentElem);
6295
- for (let i = 0; i < parents.length; i++) {
6296
- isFixed = getComputedStyle(parents[i]).position === "fixed";
6297
- if (isFixed) break;
6298
- }
6299
- }
6300
- const originWidth = this.originEl.offsetWidth;
6301
- const originHeight = this.originEl.offsetHeight;
6302
- const originOffset = this._offset(this.originEl);
6303
- const originTop = isFixed ? originOffset.top - Utils.getDocumentScrollTop() : originOffset.top;
6304
- const originLeft = isFixed ? originOffset.left - Utils.getDocumentScrollLeft() : originOffset.left;
6305
- const windowWidth = window.innerWidth;
6306
- const windowHeight = window.innerHeight;
6307
- const scrollBarWidth = windowWidth - document.documentElement.clientWidth;
6308
- const centerX = windowWidth / 2;
6309
- const centerY = windowHeight / 2;
6310
- const isLeft = originLeft <= centerX;
6311
- const isRight = originLeft > centerX;
6312
- const isTop = originTop <= centerY;
6313
- const isBottom = originTop > centerY;
6314
- const isCenterX = originLeft >= windowWidth * 0.25 && originLeft <= windowWidth * 0.75;
6315
- const tapTargetWidth = this.el.offsetWidth;
6316
- const tapTargetHeight = this.el.offsetHeight;
6317
- const tapTargetTop = originTop + originHeight / 2 - tapTargetHeight / 2;
6318
- const tapTargetLeft = originLeft + originWidth / 2 - tapTargetWidth / 2;
6319
- const tapTargetPosition = isFixed ? "fixed" : "absolute";
6320
- const tapTargetTextWidth = isCenterX ? tapTargetWidth : tapTargetWidth / 2 + originWidth;
6321
- const tapTargetTextHeight = tapTargetHeight / 2;
6322
- const tapTargetTextTop = isTop ? tapTargetHeight / 2 : 0;
6323
- const tapTargetTextBottom = 0;
6324
- const tapTargetTextLeft = isLeft && !isCenterX ? tapTargetWidth / 2 - originWidth : 0;
6325
- const tapTargetTextRight = 0;
6326
- const tapTargetTextPadding = originWidth;
6327
- const tapTargetTextAlign = isBottom ? "bottom" : "top";
6328
- const tapTargetWaveWidth = originWidth > originHeight ? originWidth * 2 : originWidth * 2;
6329
- const tapTargetWaveHeight = tapTargetWaveWidth;
6330
- const tapTargetWaveTop = tapTargetHeight / 2 - tapTargetWaveHeight / 2;
6331
- const tapTargetWaveLeft = tapTargetWidth / 2 - tapTargetWaveWidth / 2;
6332
- this.wrapper.style.top = isTop ? tapTargetTop + "px" : "";
6333
- this.wrapper.style.right = isRight ? windowWidth - tapTargetLeft - tapTargetWidth - scrollBarWidth + "px" : "";
6334
- this.wrapper.style.bottom = isBottom ? windowHeight - tapTargetTop - tapTargetHeight + "px" : "";
6335
- this.wrapper.style.left = isLeft ? tapTargetLeft + "px" : "";
6336
- this.wrapper.style.position = tapTargetPosition;
6337
- this.contentEl.style.width = tapTargetTextWidth + "px";
6338
- this.contentEl.style.height = tapTargetTextHeight + "px";
6339
- this.contentEl.style.top = tapTargetTextTop + "px";
6340
- this.contentEl.style.right = tapTargetTextRight + "px";
6341
- this.contentEl.style.bottom = tapTargetTextBottom + "px";
6342
- this.contentEl.style.left = tapTargetTextLeft + "px";
6343
- this.contentEl.style.padding = tapTargetTextPadding + "px";
6344
- this.contentEl.style.verticalAlign = tapTargetTextAlign;
6345
- this.waveEl.style.top = tapTargetWaveTop + "px";
6346
- this.waveEl.style.left = tapTargetWaveLeft + "px";
6347
- this.waveEl.style.width = tapTargetWaveWidth + "px";
6348
- this.waveEl.style.height = tapTargetWaveHeight + "px";
6161
+ if (!this.originEl || !this.wrapper) return;
6162
+ const rect = this.originEl.getBoundingClientRect();
6163
+ const x = rect.left + rect.width / 2;
6164
+ const y = rect.top + rect.height / 2;
6165
+ const size = Math.max(rect.width, rect.height, 1);
6166
+ this.wrapper.style.setProperty("--md-comp-tap-target-x", `${x}px`);
6167
+ this.wrapper.style.setProperty("--md-comp-tap-target-y", `${y}px`);
6168
+ this.wrapper.style.setProperty("--md-comp-tap-target-origin-size", `${size}px`);
6169
+ const vertical = y <= window.innerHeight / 2 ? "top" : "bottom";
6170
+ const onCenter = x >= window.innerWidth * 0.25 && x <= window.innerWidth * 0.75;
6171
+ const horizontal = onCenter ? "center" : x <= window.innerWidth / 2 ? "left" : "right";
6172
+ this.wrapper.dataset.edge = `${vertical}-${horizontal}`;
6349
6173
  }
6350
6174
  };
6351
6175
  _TapTarget._taptargets = [];
6352
6176
  var TapTarget = _TapTarget;
6353
6177
 
6354
6178
  // src/ts/components/timepicker.ts
6355
- var _defaults22 = {
6179
+ var _defaults21 = {
6356
6180
  dialRadius: 135,
6357
6181
  outerRadius: 105,
6358
6182
  innerRadius: 70,
@@ -6550,7 +6374,7 @@ var _Timepicker = class _Timepicker extends Component {
6550
6374
  }
6551
6375
  }
6552
6376
  static get defaults() {
6553
- return _defaults22;
6377
+ return _defaults21;
6554
6378
  }
6555
6379
  /**
6556
6380
  * Initializes instances of Timepicker.
@@ -6633,7 +6457,7 @@ var _Timepicker = class _Timepicker extends Component {
6633
6457
  }
6634
6458
  /*private _createButton(text: string, visibility: string): HTMLButtonElement {
6635
6459
  const button = document.createElement('button');
6636
- button.classList.add('btn', 'waves-effect', 'text');
6460
+ button.classList.add('waves-effect', 'text');
6637
6461
  button.style.visibility = visibility;
6638
6462
  button.type = 'button';
6639
6463
  button.tabIndex = -1;
@@ -6674,14 +6498,14 @@ var _Timepicker = class _Timepicker extends Component {
6674
6498
  _clockSetup() {
6675
6499
  if (this.options.twelveHour) {
6676
6500
  this._amBtn = document.createElement("div");
6677
- this._amBtn.classList.add("am-btn", "btn");
6501
+ this._amBtn.classList.add("am-btn");
6678
6502
  this._amBtn.innerText = "AM";
6679
6503
  this._amBtn.tabIndex = 0;
6680
6504
  this._amBtn.addEventListener("click", this._handleAmPmClick);
6681
6505
  this._amBtn.addEventListener("keypress", this._handleAmPmKeypress);
6682
6506
  this.spanAmPm.appendChild(this._amBtn);
6683
6507
  this._pmBtn = document.createElement("div");
6684
- this._pmBtn.classList.add("pm-btn", "btn");
6508
+ this._pmBtn.classList.add("pm-btn");
6685
6509
  this._pmBtn.innerText = "PM";
6686
6510
  this._pmBtn.tabIndex = 0;
6687
6511
  this._pmBtn.addEventListener("click", this._handleAmPmClick);
@@ -6888,19 +6712,6 @@ var _Timepicker = class _Timepicker extends Component {
6888
6712
  this.hours = (/* @__PURE__ */ new Date()).getHours();
6889
6713
  this.inputHours.value = (this.hours % (this.options.twelveHour ? 12 : 24)).toString();
6890
6714
  }
6891
- // deprecated
6892
- open() {
6893
- console.warn(
6894
- "Timepicker.close() is deprecated. Remove this method and wrap in modal yourself."
6895
- );
6896
- return this;
6897
- }
6898
- close() {
6899
- console.warn(
6900
- "Timepicker.close() is deprecated. Remove this method and wrap in modal yourself."
6901
- );
6902
- return this;
6903
- }
6904
6715
  };
6905
6716
  _Timepicker._template = `<div class="timepicker-container">
6906
6717
  <div class="timepicker-digital-display">
@@ -6933,7 +6744,7 @@ _Timepicker._template = `<div class="timepicker-container">
6933
6744
  var Timepicker = _Timepicker;
6934
6745
 
6935
6746
  // src/ts/components/toasts.ts
6936
- var _defaults23 = {
6747
+ var _defaults22 = {
6937
6748
  text: "",
6938
6749
  action: "",
6939
6750
  onAction: null,
@@ -6965,7 +6776,7 @@ var _Toast = class _Toast {
6965
6776
  this._setTimer();
6966
6777
  }
6967
6778
  static get defaults() {
6968
- return _defaults23;
6779
+ return _defaults22;
6969
6780
  }
6970
6781
  static getInstance(el) {
6971
6782
  return el["Expressive_Toast"];
@@ -6990,8 +6801,8 @@ var _Toast = class _Toast {
6990
6801
  }
6991
6802
  static _onDragStart(e) {
6992
6803
  const target = e.target;
6993
- if (target && !target.closest("button, a") && target.closest(".toast, .snackbar")) {
6994
- const toastElem = target.closest(".toast, .snackbar");
6804
+ if (target && !target.closest("button, a") && target.closest(".snackbar")) {
6805
+ const toastElem = target.closest(".snackbar");
6995
6806
  const toast = toastElem["Expressive_Toast"];
6996
6807
  if (!toast) return;
6997
6808
  toast.panning = true;
@@ -7058,7 +6869,7 @@ var _Toast = class _Toast {
7058
6869
  const node = toast.content.cloneNode(true);
7059
6870
  toast = node.firstElementChild;
7060
6871
  }
7061
- toast.classList.add("toast", "snackbar");
6872
+ toast.classList.add("snackbar");
7062
6873
  toast.setAttribute("role", "status");
7063
6874
  toast.setAttribute("aria-live", "polite");
7064
6875
  toast.setAttribute("aria-atomic", "true");
@@ -7089,7 +6900,7 @@ var _Toast = class _Toast {
7089
6900
  close.className = "circle";
7090
6901
  close.setAttribute("aria-label", "Dismiss");
7091
6902
  const icon = document.createElement("i");
7092
- icon.className = "material-icons";
6903
+ icon.className = "material-symbols";
7093
6904
  icon.setAttribute("aria-hidden", "true");
7094
6905
  icon.textContent = "close";
7095
6906
  close.appendChild(icon);
@@ -7178,7 +6989,7 @@ _Toast._draggedToast = null;
7178
6989
  var Toast = _Toast;
7179
6990
 
7180
6991
  // src/ts/components/tooltip.ts
7181
- var _defaults24 = {
6992
+ var _defaults23 = {
7182
6993
  exitDelay: 200,
7183
6994
  enterDelay: 0,
7184
6995
  text: "",
@@ -7246,7 +7057,7 @@ var Tooltip = class _Tooltip extends Component {
7246
7057
  this._setupEventHandlers();
7247
7058
  }
7248
7059
  static get defaults() {
7249
- return _defaults24;
7060
+ return _defaults23;
7250
7061
  }
7251
7062
  /**
7252
7063
  * Initializes instances of Tooltip.
@@ -7269,7 +7080,7 @@ var Tooltip = class _Tooltip extends Component {
7269
7080
  }
7270
7081
  _appendTooltipEl() {
7271
7082
  this.tooltipEl = document.createElement("div");
7272
- this.tooltipEl.classList.add("tooltip", "material-tooltip");
7083
+ this.tooltipEl.classList.add("tooltip");
7273
7084
  this.tooltipEl.id = `tooltip-${Utils.guid()}`;
7274
7085
  this.tooltipEl.setAttribute("role", "tooltip");
7275
7086
  this.el.setAttribute("aria-describedby", this.tooltipEl.id);
@@ -7430,7 +7241,6 @@ var AUTO_INIT_COMPONENTS = {
7430
7241
  Datepicker: { component: Datepicker, selector: ".datepicker" },
7431
7242
  Dropdown: { component: Dropdown, selector: ".dropdown-trigger" },
7432
7243
  Lightbox: { component: Lightbox, selector: ".lightboxed" },
7433
- Modal: { component: Modal, selector: ".modal" },
7434
7244
  Parallax: { component: Parallax, selector: ".parallax" },
7435
7245
  Pushpin: { component: Pushpin, selector: ".pushpin" },
7436
7246
  ScrollSpy: { component: ScrollSpy, selector: ".scrollspy" },
@@ -7454,7 +7264,7 @@ function AutoInit(context = document.body, options) {
7454
7264
  }
7455
7265
 
7456
7266
  // src/ts/index.ts
7457
- var version = "0.2.0";
7267
+ var version = "0.4.0";
7458
7268
  if (typeof document !== "undefined") {
7459
7269
  document.addEventListener("keydown", Utils.docHandleKeydown, true);
7460
7270
  document.addEventListener("keyup", Utils.docHandleKeyup, true);
@@ -7482,7 +7292,6 @@ export {
7482
7292
  FormSelect,
7483
7293
  Forms,
7484
7294
  Lightbox,
7485
- Modal,
7486
7295
  Parallax,
7487
7296
  Pushpin,
7488
7297
  Range,