@expressivecss/expressive 0.3.0 → 0.5.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 (96) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/README.md +4 -9
  3. package/dist/css/expressive.css +3183 -2150
  4. package/dist/css/expressive.css.map +1 -1
  5. package/dist/css/expressive.min.css +1 -1
  6. package/dist/css/expressive.min.css.map +1 -1
  7. package/dist/js/expressive.cjs +1254 -1567
  8. package/dist/js/expressive.cjs.map +4 -4
  9. package/dist/js/expressive.js +1254 -1567
  10. package/dist/js/expressive.js.map +4 -4
  11. package/dist/js/expressive.min.js +12 -22
  12. package/dist/js/expressive.min.js.map +4 -4
  13. package/dist/js/expressive.mjs +1254 -1568
  14. package/dist/js/expressive.mjs.map +4 -4
  15. package/dist/types/behaviors/bottomSheets.d.ts +18 -0
  16. package/dist/types/behaviors/sideSheets.d.ts +18 -0
  17. package/dist/types/components/autocomplete.d.ts +13 -20
  18. package/dist/types/components/buttons.d.ts +2 -2
  19. package/dist/types/components/cards.d.ts +3 -6
  20. package/dist/types/components/carousel.d.ts +3 -3
  21. package/dist/types/components/characterCounter.d.ts +3 -5
  22. package/dist/types/components/chips.d.ts +2 -2
  23. package/dist/types/components/datepicker.d.ts +2 -8
  24. package/dist/types/components/index.d.ts +3 -6
  25. package/dist/types/components/lightbox.d.ts +2 -2
  26. package/dist/types/components/{dropdown.d.ts → menu.d.ts} +48 -38
  27. package/dist/types/components/navigationRail.d.ts +53 -0
  28. package/dist/types/components/parallax.d.ts +2 -8
  29. package/dist/types/components/range.d.ts +5 -2
  30. package/dist/types/components/registry.d.ts +7 -19
  31. package/dist/types/components/scrollspy.d.ts +26 -50
  32. package/dist/types/components/select.d.ts +28 -14
  33. package/dist/types/components/sidenav.d.ts +40 -55
  34. package/dist/types/components/slider.d.ts +2 -2
  35. package/dist/types/components/{toasts.d.ts → snackbar.d.ts} +28 -25
  36. package/dist/types/components/tabs.d.ts +2 -2
  37. package/dist/types/components/timepicker.d.ts +2 -4
  38. package/dist/types/components/tooltip.d.ts +2 -2
  39. package/dist/types/core/component.d.ts +4 -4
  40. package/dist/types/index.d.ts +3 -1
  41. package/package.json +4 -3
  42. package/src/sass/README.md +13 -15
  43. package/src/sass/abstracts/_breakpoints.scss +14 -19
  44. package/src/sass/abstracts/_mixins.scss +3 -3
  45. package/src/sass/abstracts/_variables.scss +27 -23
  46. package/src/sass/base/_global.scss +1 -1
  47. package/src/sass/base/_grid.scss +64 -133
  48. package/src/sass/base/_index.scss +1 -1
  49. package/src/sass/components/_badges.scss +76 -61
  50. package/src/sass/components/_bottom-sheet.scss +137 -0
  51. package/src/sass/components/_buttons.scss +22 -54
  52. package/src/sass/components/_cards.scss +54 -127
  53. package/src/sass/components/_carousel.scss +4 -6
  54. package/src/sass/components/_chips.scss +6 -5
  55. package/src/sass/components/_datepicker.scss +1 -15
  56. package/src/sass/components/_dialog.scss +268 -0
  57. package/src/sass/components/_index.scss +9 -7
  58. package/src/sass/components/_list.scss +253 -0
  59. package/src/sass/components/_menu.scss +341 -0
  60. package/src/sass/components/_navbar.scss +68 -117
  61. package/src/sass/components/_navigation-bar.scss +165 -0
  62. package/src/sass/components/_navigation-rail.scss +308 -0
  63. package/src/sass/components/_page-footer.scss +138 -15
  64. package/src/sass/components/_parallax.scss +7 -28
  65. package/src/sass/components/_preloader.scss +145 -300
  66. package/src/sass/components/_scrollspy.scss +18 -0
  67. package/src/sass/components/_side-sheet.scss +193 -0
  68. package/src/sass/components/_sidenav.scss +281 -146
  69. package/src/sass/components/{_toast.scss → _snackbar.scss} +43 -43
  70. package/src/sass/components/_table_of_contents.scss +30 -14
  71. package/src/sass/components/_tabs.scss +43 -50
  72. package/src/sass/components/_toolbar.scss +93 -27
  73. package/src/sass/components/_tooltip.scss +5 -10
  74. package/src/sass/components/_transitions.scss +0 -5
  75. package/src/sass/components/forms/_fieldset.scss +1 -1
  76. package/src/sass/components/forms/_index.scss +0 -5
  77. package/src/sass/components/forms/_input-fields.scss +52 -55
  78. package/src/sass/components/forms/_range.scss +423 -98
  79. package/src/sass/components/forms/_select.scss +177 -60
  80. package/src/sass/components/forms/_switches.scss +21 -8
  81. package/src/sass/tokens/_reference.scss +37 -13
  82. package/src/sass/tokens/_theme.scss +5 -13
  83. package/src/sass/utilities/_colors.scss +16 -74
  84. package/src/sass/utilities/_helpers.scss +1 -3
  85. package/src/sass/utilities/_typescale.scss +0 -5
  86. package/src/sass/utilities/_visibility.scss +10 -2
  87. package/dist/types/components/collapsible.d.ts +0 -96
  88. package/dist/types/components/modal.d.ts +0 -45
  89. package/dist/types/components/pushpin.d.ts +0 -51
  90. package/dist/types/components/tapTarget.d.ts +0 -57
  91. package/src/sass/components/_collapsible.scss +0 -181
  92. package/src/sass/components/_collection.scss +0 -198
  93. package/src/sass/components/_dropdown.scss +0 -129
  94. package/src/sass/components/_modal.scss +0 -208
  95. package/src/sass/components/_pushpin.scss +0 -11
  96. package/src/sass/components/_tapTarget.scss +0 -191
@@ -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";
@@ -538,13 +516,13 @@ var Cards = class _Cards extends Component {
538
516
  }
539
517
  };
540
518
 
541
- // src/ts/components/dropdown.ts
519
+ // src/ts/components/menu.ts
542
520
  var _defaults2 = {
543
521
  alignment: "left",
544
522
  autoFocus: true,
545
523
  constrainWidth: true,
546
524
  container: null,
547
- coverTrigger: true,
525
+ coverTrigger: false,
548
526
  closeOnClick: true,
549
527
  hover: false,
550
528
  inDuration: 150,
@@ -555,9 +533,9 @@ var _defaults2 = {
555
533
  onCloseEnd: null,
556
534
  onItemClick: null
557
535
  };
558
- var _Dropdown = class _Dropdown extends Component {
536
+ var _Menu = class _Menu extends Component {
559
537
  constructor(el, options) {
560
- super(el, options, _Dropdown);
538
+ super(el, options, _Menu);
561
539
  this._handleClick = (e) => {
562
540
  e.preventDefault();
563
541
  if (this.isOpen) {
@@ -575,21 +553,22 @@ var _Dropdown = class _Dropdown extends Component {
575
553
  this.close();
576
554
  return;
577
555
  }
578
- const leaveToDropdownContent = !!toEl.closest(".dropdown-content, menu");
579
- let leaveToActiveDropdownTrigger = false;
580
- const closestTrigger = toEl.closest(".dropdown-trigger");
581
- if (closestTrigger && !!closestTrigger["Expressive_Dropdown"] && closestTrigger["Expressive_Dropdown"].isOpen) {
582
- leaveToActiveDropdownTrigger = true;
556
+ const leaveToMenuContent = !!(this.menuEl && toEl && this.menuEl.contains(toEl));
557
+ let leaveToActiveMenuTrigger = false;
558
+ const closestTrigger = toEl.closest(".menu-trigger");
559
+ if (closestTrigger && !!closestTrigger["Expressive_Menu"] && closestTrigger["Expressive_Menu"].isOpen) {
560
+ leaveToActiveMenuTrigger = true;
583
561
  }
584
- if (!leaveToActiveDropdownTrigger && !leaveToDropdownContent) {
562
+ if (!leaveToActiveMenuTrigger && !leaveToMenuContent) {
585
563
  this.close();
586
564
  }
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.menuEl?.contains(target) && !this.isTouchMoving) {
591
570
  this.close();
592
- } else if (!target.closest(".dropdown-content, menu")) {
571
+ } else if (!this.menuEl?.contains(target)) {
593
572
  setTimeout(() => {
594
573
  if (this.isOpen) {
595
574
  this.close();
@@ -607,52 +586,100 @@ 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.menuEl?.contains(target)) {
611
590
  this.isTouchMoving = true;
612
591
  }
613
592
  };
614
- this._handleDropdownClick = (e) => {
593
+ this._handleMenuClick = (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.menuEl?.contains(li)) {
611
+ this._alignSubmenu(li);
618
612
  }
619
613
  };
620
- this._handleDropdownKeydown = (e) => {
614
+ this._handleMenuKeydown = (e) => {
621
615
  const arrowUpOrDown = Utils.keys.ARROW_DOWN.includes(e.key) || Utils.keys.ARROW_UP.includes(e.key);
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.menuEl;
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.menuEl.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.menuEl.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.menuEl;
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) {
@@ -686,7 +714,7 @@ var _Dropdown = class _Dropdown extends Component {
686
714
  this.filterQuery = [];
687
715
  };
688
716
  /**
689
- * Open dropdown.
717
+ * Open menu.
690
718
  */
691
719
  this.open = () => {
692
720
  if (this.isOpen) return;
@@ -694,20 +722,21 @@ var _Dropdown = class _Dropdown extends Component {
694
722
  if (typeof this.options.onOpenStart === "function") {
695
723
  this.options.onOpenStart.call(this, this.el);
696
724
  }
697
- this._resetDropdownStyles();
698
- this.dropdownEl.style.display = "block";
699
- this._placeDropdown();
725
+ this._resetMenuStyles();
726
+ this.menuEl.style.display = "block";
727
+ this._placeMenu();
700
728
  this._animateIn();
701
729
  setTimeout(() => this._setupTemporaryEventHandlers(), 0);
702
730
  this.el.ariaExpanded = "true";
703
731
  };
704
732
  /**
705
- * Close dropdown.
733
+ * Close menu.
706
734
  */
707
735
  this.close = () => {
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
  }
@@ -719,21 +748,20 @@ var _Dropdown = class _Dropdown extends Component {
719
748
  this.el.ariaExpanded = "false";
720
749
  };
721
750
  /**
722
- * While dropdown is open, you can recalculate its dimensions if its contents have changed.
751
+ * While menu is open, you can recalculate its dimensions if its contents have changed.
723
752
  */
724
753
  this.recalculateDimensions = () => {
725
754
  if (this.isOpen) {
726
- this._resetDropdownPositioningStyles();
727
- this._placeDropdown();
755
+ this._resetMenuPositioningStyles();
756
+ this._placeMenu();
728
757
  }
729
758
  };
730
- this.el["Expressive_Dropdown"] = this;
731
- _Dropdown._dropdowns.push(this);
759
+ this.el["Expressive_Menu"] = this;
760
+ _Menu._menus.push(this);
732
761
  this.id = Utils.getIdFromTrigger(el);
733
- this.dropdownEl = document.getElementById(this.id);
734
- this.dropdownEl?.classList.add("dropdown-content");
762
+ this.menuEl = document.getElementById(this.id);
735
763
  this.options = {
736
- ..._Dropdown.defaults,
764
+ ..._Menu.defaults,
737
765
  ...options
738
766
  };
739
767
  this.isOpen = false;
@@ -742,51 +770,56 @@ var _Dropdown = class _Dropdown extends Component {
742
770
  this.focusedIndex = -1;
743
771
  this.filterQuery = [];
744
772
  this.el.ariaExpanded = "false";
745
- this._moveDropdownToElement();
746
- this._makeDropdownFocusable();
773
+ this._moveMenuToElement();
774
+ this._makeMenuFocusable();
775
+ this._setupSubmenus();
747
776
  this._setupEventHandlers();
748
777
  }
749
778
  static get defaults() {
750
779
  return _defaults2;
751
780
  }
752
781
  /**
753
- * Initializes instances of Dropdown.
782
+ * Initializes instances of Menu.
754
783
  * @param els HTML elements.
755
784
  * @param options Component options.
756
785
  */
757
786
  static init(els, options = {}) {
758
- return super.init(els, options, _Dropdown);
787
+ return super.init(els, options, _Menu);
759
788
  }
760
789
  static getInstance(el) {
761
- return el["Expressive_Dropdown"];
790
+ return el["Expressive_Menu"];
762
791
  }
763
792
  destroy() {
764
793
  clearTimeout(this.filterTimeout);
765
- this._resetDropdownStyles();
794
+ this._resetMenuStyles();
766
795
  this._removeEventHandlers();
767
796
  this._removeTemporaryEventHandlers();
768
- const index = _Dropdown._dropdowns.indexOf(this);
769
- if (index >= 0) _Dropdown._dropdowns.splice(index, 1);
770
- this.el["Expressive_Dropdown"] = void 0;
797
+ const index = _Menu._menus.indexOf(this);
798
+ if (index >= 0) _Menu._menus.splice(index, 1);
799
+ this.el["Expressive_Menu"] = void 0;
771
800
  }
772
801
  _setupEventHandlers() {
773
802
  this.el.addEventListener("keydown", this._handleTriggerKeydown);
774
- this.dropdownEl?.addEventListener("click", this._handleDropdownClick);
803
+ this.menuEl?.addEventListener("click", this._handleMenuClick);
804
+ if (this.menuEl?.querySelector(":scope > li > menu")) {
805
+ this.menuEl.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);
778
- this.dropdownEl.addEventListener("mouseleave", this._handleMouseLeave);
810
+ this.menuEl.addEventListener("mouseleave", this._handleMouseLeave);
779
811
  } else {
780
812
  this.el.addEventListener("click", this._handleClick);
781
813
  }
782
814
  }
783
815
  _removeEventHandlers() {
784
816
  this.el.removeEventListener("keydown", this._handleTriggerKeydown);
785
- this.dropdownEl?.removeEventListener("click", this._handleDropdownClick);
817
+ this.menuEl?.removeEventListener("click", this._handleMenuClick);
818
+ this.menuEl?.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);
789
- this.dropdownEl?.removeEventListener("mouseleave", this._handleMouseLeave);
822
+ this.menuEl?.removeEventListener("mouseleave", this._handleMouseLeave);
790
823
  } else {
791
824
  this.el.removeEventListener("click", this._handleClick);
792
825
  }
@@ -794,45 +827,104 @@ var _Dropdown = class _Dropdown extends Component {
794
827
  _setupTemporaryEventHandlers() {
795
828
  document.body.addEventListener("click", this._handleDocumentClick);
796
829
  document.body.addEventListener("touchmove", this._handleDocumentTouchmove, { passive: true });
797
- this.dropdownEl?.addEventListener("keydown", this._handleDropdownKeydown);
830
+ this.menuEl?.addEventListener("keydown", this._handleMenuKeydown);
798
831
  window.addEventListener("resize", this._handleWindowResize);
799
832
  }
800
833
  _removeTemporaryEventHandlers() {
801
834
  document.body.removeEventListener("click", this._handleDocumentClick);
802
835
  document.body.removeEventListener("touchmove", this._handleDocumentTouchmove);
803
- this.dropdownEl?.removeEventListener("keydown", this._handleDropdownKeydown);
836
+ this.menuEl?.removeEventListener("keydown", this._handleMenuKeydown);
804
837
  window.removeEventListener("resize", this._handleWindowResize);
805
838
  }
806
- _resetDropdownStyles() {
807
- this.dropdownEl.style.display = "";
808
- this._resetDropdownPositioningStyles();
809
- this.dropdownEl.style.transform = "";
810
- this.dropdownEl.style.opacity = "";
839
+ _isSubmenuTriggerClick(target) {
840
+ if (!this.menuEl) return false;
841
+ const li = target.closest("li");
842
+ if (!li || !this.menuEl.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.menuEl?.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
+ li.classList.remove("submenu-start");
875
+ const rect = menu.getBoundingClientRect();
876
+ if (rect.right > window.innerWidth - 8) li.classList.add("submenu-start");
877
+ }
878
+ _closeSubmenus() {
879
+ this.menuEl?.querySelectorAll("li.open").forEach((li) => {
880
+ li.classList.remove("open", "submenu-start");
881
+ this._syncSubmenuAria(li, false);
882
+ });
883
+ }
884
+ _containingSubmenu(e) {
885
+ const target = e.target;
886
+ if (!(target instanceof Element)) return null;
887
+ const submenu = target.closest("li > menu");
888
+ if (submenu instanceof HTMLElement && this.menuEl?.contains(submenu)) return submenu;
889
+ return null;
811
890
  }
812
- _resetDropdownPositioningStyles() {
813
- this.dropdownEl.style.width = "";
814
- this.dropdownEl.style.height = "";
815
- this.dropdownEl.style.left = "";
816
- this.dropdownEl.style.top = "";
817
- this.dropdownEl.style.transformOrigin = "";
891
+ _focusedRow(e) {
892
+ const target = e.target;
893
+ if (!(target instanceof Element)) return null;
894
+ const li = target.closest("li");
895
+ if (li instanceof HTMLElement && this.menuEl?.contains(li)) return li;
896
+ return null;
897
+ }
898
+ _resetMenuStyles() {
899
+ this.menuEl.style.display = "";
900
+ this._resetMenuPositioningStyles();
901
+ this.menuEl.style.transform = "";
902
+ this.menuEl.style.opacity = "";
903
+ }
904
+ _resetMenuPositioningStyles() {
905
+ this.menuEl.style.width = "";
906
+ this.menuEl.style.height = "";
907
+ this.menuEl.style.left = "";
908
+ this.menuEl.style.top = "";
909
+ this.menuEl.style.transformOrigin = "";
818
910
  }
819
- _moveDropdownToElement(containerEl = null) {
911
+ _moveMenuToElement(containerEl = null) {
820
912
  if (this.options.container) {
821
- this.options.container.append(this.dropdownEl);
913
+ this.options.container.append(this.menuEl);
822
914
  return;
823
915
  }
824
916
  if (containerEl) {
825
- if (!containerEl.contains(this.dropdownEl)) containerEl.append(this.dropdownEl);
917
+ if (!containerEl.contains(this.menuEl)) containerEl.append(this.menuEl);
826
918
  return;
827
919
  }
828
- this.el.after(this.dropdownEl);
920
+ this.el.after(this.menuEl);
829
921
  }
830
- _makeDropdownFocusable() {
831
- if (!this.dropdownEl) return;
832
- this.dropdownEl.popover = "";
833
- this.dropdownEl.tabIndex = 0;
834
- Array.from(this.dropdownEl.children).forEach((el) => {
835
- if (el instanceof HTMLHRElement || el.classList.contains("divider")) {
922
+ _makeMenuFocusable() {
923
+ if (!this.menuEl) return;
924
+ this.menuEl.popover = "";
925
+ this.menuEl.tabIndex = 0;
926
+ this.menuEl.querySelectorAll(":scope li, :scope > hr").forEach((el) => {
927
+ if (el instanceof HTMLHRElement || el.classList.contains("divider") || el.classList.contains("gap") || el.classList.contains("label") || el.classList.contains("optgroup")) {
836
928
  el.setAttribute("tabindex", "-1");
837
929
  return;
838
930
  }
@@ -840,25 +932,25 @@ var _Dropdown = class _Dropdown extends Component {
840
932
  });
841
933
  }
842
934
  _focusFocusedItem() {
843
- if (this.focusedIndex >= 0 && this.focusedIndex < this.dropdownEl.children.length && this.options.autoFocus) {
844
- this.dropdownEl.children[this.focusedIndex].focus({
935
+ if (this.focusedIndex >= 0 && this.focusedIndex < this.menuEl.children.length && this.options.autoFocus) {
936
+ this.menuEl.children[this.focusedIndex].focus({
845
937
  preventScroll: true
846
938
  });
847
- this.dropdownEl.children[this.focusedIndex].scrollIntoView({
939
+ this.menuEl.children[this.focusedIndex].scrollIntoView({
848
940
  behavior: "smooth",
849
941
  block: "nearest",
850
942
  inline: "nearest"
851
943
  });
852
944
  }
853
945
  }
854
- _getDropdownPosition(closestOverflowParent) {
946
+ _getMenuPosition(closestOverflowParent) {
855
947
  const triggerBRect = this.el.getBoundingClientRect();
856
- const dropdownBRect = this.dropdownEl.getBoundingClientRect();
857
- let idealHeight = dropdownBRect.height;
858
- let idealWidth = dropdownBRect.width;
859
- let idealXPos = triggerBRect.left - dropdownBRect.left;
860
- let idealYPos = triggerBRect.top - dropdownBRect.top;
861
- const dropdownBounds = {
948
+ const menuBRect = this.menuEl.getBoundingClientRect();
949
+ let idealHeight = menuBRect.height;
950
+ let idealWidth = menuBRect.width;
951
+ let idealXPos = triggerBRect.left - menuBRect.left;
952
+ let idealYPos = triggerBRect.top - menuBRect.top;
953
+ const menuBounds = {
862
954
  left: idealXPos,
863
955
  top: idealYPos,
864
956
  height: idealHeight,
@@ -867,7 +959,7 @@ var _Dropdown = class _Dropdown extends Component {
867
959
  const alignments = Utils.checkPossibleAlignments(
868
960
  this.el,
869
961
  closestOverflowParent,
870
- dropdownBounds,
962
+ menuBounds,
871
963
  this.options.coverTrigger ? 0 : triggerBRect.height
872
964
  );
873
965
  let verticalAlignment = "top";
@@ -908,10 +1000,10 @@ var _Dropdown = class _Dropdown extends Component {
908
1000
  }
909
1001
  }
910
1002
  if (verticalAlignment === "bottom") {
911
- idealYPos = idealYPos - dropdownBRect.height + (this.options.coverTrigger ? triggerBRect.height : 0);
1003
+ idealYPos = idealYPos - menuBRect.height + (this.options.coverTrigger ? triggerBRect.height : 0);
912
1004
  }
913
1005
  if (horizontalAlignment === "right") {
914
- idealXPos = idealXPos - dropdownBRect.width + triggerBRect.width;
1006
+ idealXPos = idealXPos - menuBRect.width + triggerBRect.width;
915
1007
  }
916
1008
  return {
917
1009
  x: idealXPos,
@@ -924,26 +1016,26 @@ var _Dropdown = class _Dropdown extends Component {
924
1016
  }
925
1017
  _animateIn() {
926
1018
  const duration = this.options.inDuration;
927
- this.dropdownEl.style.transition = "none";
928
- this.dropdownEl.style.opacity = "0";
929
- this.dropdownEl.style.transform = "scale(0.3, 0.3)";
1019
+ this.menuEl.style.transition = "none";
1020
+ this.menuEl.style.opacity = "0";
1021
+ this.menuEl.style.transform = "scale(0.85)";
930
1022
  setTimeout(() => {
931
- this.dropdownEl.style.transition = `opacity ${duration}ms ease, transform ${duration}ms ease`;
932
- this.dropdownEl.style.opacity = "1";
933
- this.dropdownEl.style.transform = "scale(1, 1)";
1023
+ this.menuEl.style.transition = `opacity ${duration}ms ease, transform ${duration}ms ease`;
1024
+ this.menuEl.style.opacity = "1";
1025
+ this.menuEl.style.transform = "scale(1)";
934
1026
  }, 1);
935
1027
  setTimeout(() => {
936
- if (this.options.autoFocus) this.dropdownEl.focus();
1028
+ if (this.options.autoFocus) this.menuEl.focus();
937
1029
  if (typeof this.options.onOpenEnd === "function") this.options.onOpenEnd.call(this, this.el);
938
1030
  }, duration);
939
1031
  }
940
1032
  _animateOut() {
941
1033
  const duration = this.options.outDuration;
942
- this.dropdownEl.style.transition = `opacity ${duration}ms ease, transform ${duration}ms ease`;
943
- this.dropdownEl.style.opacity = "0";
944
- this.dropdownEl.style.transform = "scale(0.3, 0.3)";
1034
+ this.menuEl.style.transition = `opacity ${duration}ms ease, transform ${duration}ms ease`;
1035
+ this.menuEl.style.opacity = "0";
1036
+ this.menuEl.style.transform = "scale(0.85)";
945
1037
  setTimeout(() => {
946
- this._resetDropdownStyles();
1038
+ this._resetMenuStyles();
947
1039
  if (typeof this.options.onCloseEnd === "function")
948
1040
  this.options.onCloseEnd.call(this, this.el);
949
1041
  }, duration);
@@ -958,30 +1050,31 @@ var _Dropdown = class _Dropdown extends Component {
958
1050
  }
959
1051
  return null;
960
1052
  }
961
- _placeDropdown() {
1053
+ _placeMenu() {
962
1054
  let closestOverflowParent = this._getClosestAncestor(
963
- this.dropdownEl,
1055
+ this.menuEl,
964
1056
  (ancestor) => {
965
1057
  return !["HTML", "BODY"].includes(ancestor.tagName) && getComputedStyle(ancestor).overflow !== "visible";
966
1058
  }
967
1059
  );
968
1060
  if (!closestOverflowParent) {
969
- closestOverflowParent = !!this.dropdownEl.offsetParent ? this.dropdownEl.offsetParent : this.dropdownEl.parentNode;
1061
+ closestOverflowParent = !!this.menuEl.offsetParent ? this.menuEl.offsetParent : this.menuEl.parentNode;
970
1062
  }
971
1063
  if (getComputedStyle(closestOverflowParent).position === "static")
972
1064
  closestOverflowParent.style.position = "relative";
973
- const idealWidth = this.options.constrainWidth ? this.el.getBoundingClientRect().width : this.dropdownEl.getBoundingClientRect().width;
974
- this.dropdownEl.style.width = idealWidth + "px";
975
- const positionInfo = this._getDropdownPosition(closestOverflowParent);
976
- this.dropdownEl.style.left = positionInfo.x + "px";
977
- this.dropdownEl.style.top = positionInfo.y + "px";
978
- this.dropdownEl.style.height = positionInfo.height + "px";
979
- this.dropdownEl.style.width = positionInfo.width + "px";
980
- this.dropdownEl.style.transformOrigin = `${positionInfo.horizontalAlignment === "left" ? "0" : "100%"} ${positionInfo.verticalAlignment === "top" ? "0" : "100%"}`;
1065
+ const natural = this.menuEl.getBoundingClientRect().width;
1066
+ const idealWidth = this.options.constrainWidth ? this.el.getBoundingClientRect().width : Math.min(280, Math.max(112, natural));
1067
+ this.menuEl.style.width = idealWidth + "px";
1068
+ const positionInfo = this._getMenuPosition(closestOverflowParent);
1069
+ this.menuEl.style.left = positionInfo.x + "px";
1070
+ this.menuEl.style.top = positionInfo.y + "px";
1071
+ this.menuEl.style.height = positionInfo.height + "px";
1072
+ this.menuEl.style.width = positionInfo.width + "px";
1073
+ this.menuEl.style.transformOrigin = `${positionInfo.horizontalAlignment === "left" ? "0" : "100%"} ${positionInfo.verticalAlignment === "top" ? "0" : "100%"}`;
981
1074
  }
982
1075
  };
983
- _Dropdown._dropdowns = [];
984
- var Dropdown = _Dropdown;
1076
+ _Menu._menus = [];
1077
+ var Menu = _Menu;
985
1078
 
986
1079
  // src/ts/components/autocomplete.ts
987
1080
  var _defaults3 = {
@@ -989,8 +1082,8 @@ var _defaults3 = {
989
1082
  // Autocomplete data set
990
1083
  onAutocomplete: null,
991
1084
  // Callback for when autocompleted
992
- dropdownOptions: {
993
- // Default dropdown options
1085
+ menuOptions: {
1086
+ // Default menu options
994
1087
  autoFocus: false,
995
1088
  closeOnClick: false,
996
1089
  coverTrigger: false
@@ -1006,7 +1099,7 @@ var _defaults3 = {
1006
1099
  )
1007
1100
  );
1008
1101
  },
1009
- maxDropDownHeight: "300px",
1102
+ maxMenuHeight: "300px",
1010
1103
  allowUnsafeHTML: false,
1011
1104
  selected: []
1012
1105
  };
@@ -1091,19 +1184,19 @@ var _Autocomplete = class _Autocomplete extends Component {
1091
1184
  this._resetAutocomplete();
1092
1185
  if (inputText.length >= this.options.minLength) {
1093
1186
  this.isOpen = true;
1094
- this._renderDropdown();
1187
+ this._renderMenu();
1095
1188
  }
1096
- if (!this.dropdown.isOpen) {
1189
+ if (!this.menu.isOpen) {
1097
1190
  setTimeout(() => {
1098
- this.dropdown.open();
1191
+ this.menu.open();
1099
1192
  }, 0);
1100
- } else this.dropdown.recalculateDimensions();
1193
+ } else this.menu.recalculateDimensions();
1101
1194
  };
1102
1195
  /**
1103
1196
  * Hide autocomplete.
1104
1197
  */
1105
1198
  this.close = () => {
1106
- this.dropdown.close();
1199
+ this.menu.close();
1107
1200
  };
1108
1201
  this.el["Expressive_Autocomplete"] = this;
1109
1202
  this.options = {
@@ -1118,7 +1211,7 @@ var _Autocomplete = class _Autocomplete extends Component {
1118
1211
  this.menuItems = this.options.data || [];
1119
1212
  this.$active = null;
1120
1213
  this._mousedown = false;
1121
- this._setupDropdown();
1214
+ this._setupMenu();
1122
1215
  this._setupEventHandlers();
1123
1216
  }
1124
1217
  static get defaults() {
@@ -1149,7 +1242,7 @@ var _Autocomplete = class _Autocomplete extends Component {
1149
1242
  }
1150
1243
  destroy() {
1151
1244
  this._removeEventHandlers();
1152
- this._removeDropdown();
1245
+ this._removeMenu();
1153
1246
  this.el["Expressive_Autocomplete"] = void 0;
1154
1247
  }
1155
1248
  _setupEventHandlers() {
@@ -1178,34 +1271,33 @@ var _Autocomplete = class _Autocomplete extends Component {
1178
1271
  this.container.removeEventListener("touchend", this._handleContainerMouseupAndTouchend);
1179
1272
  }
1180
1273
  }
1181
- _setupDropdown() {
1182
- this.container = document.createElement("ul");
1183
- this.container.style.maxHeight = this.options.maxDropDownHeight;
1274
+ _setupMenu() {
1275
+ this.container = document.createElement("menu");
1276
+ this.container.style.maxHeight = this.options.maxMenuHeight;
1184
1277
  this.container.id = `autocomplete-options-${Utils.guid()}`;
1185
- this.container.classList.add("autocomplete-content", "dropdown-content");
1278
+ this.container.classList.add("autocomplete-content");
1186
1279
  this.container.ariaExpanded = "true";
1187
1280
  this.el.setAttribute("data-target", this.container.id);
1188
1281
  this.menuItems.forEach((menuItem) => {
1189
- const itemElement = this._createDropdownItem(menuItem);
1282
+ const itemElement = this._createMenuItem(menuItem);
1190
1283
  this.container.append(itemElement);
1191
1284
  });
1192
1285
  this.el.parentElement.appendChild(this.container);
1193
- const dropdownOptions = {
1194
- ..._Autocomplete.defaults.dropdownOptions,
1195
- ...this.options.dropdownOptions
1286
+ const menuOptions = {
1287
+ ..._Autocomplete.defaults.menuOptions,
1288
+ ...this.options.menuOptions
1196
1289
  };
1197
- const userOnItemClick = dropdownOptions.onItemClick;
1198
- dropdownOptions.onItemClick = (li) => {
1290
+ const userOnItemClick = menuOptions.onItemClick;
1291
+ menuOptions.onItemClick = (li) => {
1199
1292
  if (!li) return;
1200
1293
  const entryID = li.getAttribute("data-id");
1201
1294
  this.selectOption(entryID);
1202
- if (userOnItemClick && typeof userOnItemClick === "function")
1203
- userOnItemClick.call(this.dropdown, this.el);
1295
+ if (typeof userOnItemClick === "function") userOnItemClick.call(this.menu, li);
1204
1296
  };
1205
- this.dropdown = Dropdown.init(this.el, dropdownOptions);
1297
+ this.menu = Menu.init(this.el, menuOptions);
1206
1298
  const label = this.el.parentElement.querySelector("label");
1207
1299
  if (label) this.el.after(label);
1208
- this.el.removeEventListener("click", this.dropdown._handleClick);
1300
+ this.el.removeEventListener("click", this.menu._handleClick);
1209
1301
  if (!this.options.isMultiSelect && !(this.options.selected.length === 0)) {
1210
1302
  const selectedValue = this.menuItems.filter((value) => value.id === this.selectedValues[0].id);
1211
1303
  this.el.value = selectedValue[0].text;
@@ -1217,7 +1309,7 @@ var _Autocomplete = class _Autocomplete extends Component {
1217
1309
  this.el.parentElement.appendChild(div);
1218
1310
  this._updateSelectedInfo();
1219
1311
  }
1220
- _removeDropdown() {
1312
+ _removeMenu() {
1221
1313
  this.container.ariaExpanded = "false";
1222
1314
  this.container.parentNode.removeChild(this.container);
1223
1315
  }
@@ -1240,7 +1332,7 @@ var _Autocomplete = class _Autocomplete extends Component {
1240
1332
  }
1241
1333
  return [label.slice(0, start), label.slice(start, end + 1), label.slice(end + 1)];
1242
1334
  }
1243
- _createDropdownItem(entry) {
1335
+ _createMenuItem(entry) {
1244
1336
  const item = document.createElement("li");
1245
1337
  item.setAttribute("data-id", entry.id);
1246
1338
  item.setAttribute(
@@ -1307,13 +1399,13 @@ var _Autocomplete = class _Autocomplete extends Component {
1307
1399
  item.style.gridTemplateColumns = getGridConfig();
1308
1400
  return item;
1309
1401
  }
1310
- _renderDropdown() {
1402
+ _renderMenu() {
1311
1403
  this._resetAutocomplete();
1312
1404
  if (this.menuItems.length === 0) {
1313
1405
  this.menuItems = this.selectedValues;
1314
1406
  }
1315
1407
  for (let i = 0; i < this.menuItems.length; i++) {
1316
- const item = this._createDropdownItem(this.menuItems[i]);
1408
+ const item = this._createMenuItem(this.menuItems[i]);
1317
1409
  this.container.append(item);
1318
1410
  }
1319
1411
  }
@@ -1344,7 +1436,7 @@ var _Autocomplete = class _Autocomplete extends Component {
1344
1436
  * Updates the visible or selectable items shown in the menu.
1345
1437
  * @param menuItems Items to be available.
1346
1438
  * @param selected Selected item ids
1347
- * @param open Option to conditionally open dropdown
1439
+ * @param open Option to conditionally open menu
1348
1440
  */
1349
1441
  setMenuItems(menuItems, selected = null, open = true) {
1350
1442
  this.menuItems = menuItems;
@@ -1355,7 +1447,7 @@ var _Autocomplete = class _Autocomplete extends Component {
1355
1447
  );
1356
1448
  }
1357
1449
  if (this.options.isMultiSelect) {
1358
- this._renderDropdown();
1450
+ this._renderMenu();
1359
1451
  } else {
1360
1452
  this._refreshInputText();
1361
1453
  }
@@ -1363,19 +1455,6 @@ var _Autocomplete = class _Autocomplete extends Component {
1363
1455
  this._updateSelectedInfo();
1364
1456
  this._triggerChanged();
1365
1457
  }
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
1458
  /**
1380
1459
  * Select a specific autocomplete option via id-property.
1381
1460
  * @param id The id of a data-entry.
@@ -1390,7 +1469,7 @@ var _Autocomplete = class _Autocomplete extends Component {
1390
1469
  else this.selectedValues = this.selectedValues.filter(
1391
1470
  (selectedEntry) => selectedEntry.id !== entry.id
1392
1471
  );
1393
- this._renderDropdown();
1472
+ this._renderMenu();
1394
1473
  this.el.focus();
1395
1474
  } else {
1396
1475
  this.selectedValues = [entry];
@@ -1401,14 +1480,6 @@ var _Autocomplete = class _Autocomplete extends Component {
1401
1480
  this._updateSelectedInfo();
1402
1481
  this._triggerChanged();
1403
1482
  }
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
1483
  };
1413
1484
  /**
1414
1485
  * The three-dot loading indicator, parsed once and cloned thereafter.
@@ -1488,7 +1559,7 @@ var _Chips = class _Chips extends Component {
1488
1559
  this._renderChips();
1489
1560
  }
1490
1561
  if (this.options.allowUserInput) {
1491
- this.el.classList.add("input-field");
1562
+ this.el.classList.add("field");
1492
1563
  this._setupInput();
1493
1564
  this._setupEventHandlers();
1494
1565
  this.el.append(this._input);
@@ -1716,12 +1787,14 @@ var Range = class _Range extends Component {
1716
1787
  constructor(el, options) {
1717
1788
  super(el, options, _Range);
1718
1789
  this._handleRangeChange = () => {
1790
+ this._clampDual();
1719
1791
  this._sync();
1720
1792
  this.thumb.classList.add("active");
1721
1793
  };
1722
1794
  this._handleRangeMousedownTouchstart = (e) => {
1723
1795
  this._mousedown = true;
1724
1796
  this.el.classList.add("active");
1797
+ this._clampDual();
1725
1798
  this._sync();
1726
1799
  if (e.type !== "input") {
1727
1800
  this.thumb.classList.add("active");
@@ -1729,6 +1802,7 @@ var Range = class _Range extends Component {
1729
1802
  };
1730
1803
  this._handleRangeInputMousemoveTouchmove = () => {
1731
1804
  if (this._mousedown) {
1805
+ this._clampDual();
1732
1806
  this._sync();
1733
1807
  this.thumb.classList.add("active");
1734
1808
  }
@@ -1813,16 +1887,48 @@ var Range = class _Range extends Component {
1813
1887
  _isVertical() {
1814
1888
  return !!this.el.closest(".vertical");
1815
1889
  }
1890
+ _host() {
1891
+ return this.el.closest(".range, .range-field, label");
1892
+ }
1893
+ _clampDual() {
1894
+ const host = this._host();
1895
+ const peers = host?.querySelectorAll('input[type="range"]');
1896
+ if (!peers || peers.length !== 2) return;
1897
+ const start = peers[0];
1898
+ const end = peers[1];
1899
+ if (this.el === start && +start.value > +end.value) start.value = end.value;
1900
+ if (this.el === end && +end.value < +start.value) end.value = start.value;
1901
+ }
1902
+ _fraction(el) {
1903
+ const max = parseFloat(el.max) || 100;
1904
+ const min = parseFloat(el.min) || 0;
1905
+ const val = parseFloat(el.value) || 0;
1906
+ return max === min ? 0 : (val - min) / (max - min);
1907
+ }
1816
1908
  _sync() {
1817
- const max = parseFloat(this.el.getAttribute("max")) || 100;
1818
- const min = parseFloat(this.el.getAttribute("min")) || 0;
1819
- const val = parseFloat(this.el.value) || 0;
1820
- const percent = max === min ? 0 : (val - min) / (max - min);
1909
+ const percent = this._fraction(this.el);
1910
+ const fraction = `${percent * 100}%`;
1821
1911
  const left = this.el.offsetLeft;
1822
1912
  const top = this.el.offsetTop;
1823
1913
  const width = this.el.offsetWidth;
1824
1914
  const height = this.el.offsetHeight;
1825
- this.el.style.setProperty("--md-comp-slider-active-fraction", `${percent * 100}%`);
1915
+ this.el.style.setProperty("--md-comp-slider-active-fraction", fraction);
1916
+ const host = this._host();
1917
+ const peers = host?.querySelectorAll('input[type="range"]');
1918
+ if (host && peers && peers.length === 2) {
1919
+ const nums = Array.from(peers).map((el) => this._fraction(el));
1920
+ host.style.setProperty("--md-comp-slider-start-fraction", `${Math.min(...nums) * 100}%`);
1921
+ host.style.setProperty("--md-comp-slider-end-fraction", `${Math.max(...nums) * 100}%`);
1922
+ }
1923
+ if (host?.classList.contains("stops")) {
1924
+ const max = parseFloat(this.el.max) || 100;
1925
+ const min = parseFloat(this.el.min) || 0;
1926
+ const step = parseFloat(this.el.step);
1927
+ if (step > 0 && Number.isFinite(step)) {
1928
+ const n = Math.round((max - min) / step) + 1;
1929
+ host.style.setProperty("--md-comp-slider-stop-count", String(Math.max(2, n)));
1930
+ }
1931
+ }
1826
1932
  this.value.textContent = this.el.value;
1827
1933
  if (this._isVertical()) {
1828
1934
  this.thumb.style.left = `${left + width / 2}px`;
@@ -1910,6 +2016,170 @@ var _Dialogs = class _Dialogs {
1910
2016
  _Dialogs._down = null;
1911
2017
  var Dialogs = _Dialogs;
1912
2018
 
2019
+ // src/ts/behaviors/bottomSheets.ts
2020
+ var _BottomSheets = class _BottomSheets {
2021
+ static Init() {
2022
+ Utils.onDocumentReady(() => {
2023
+ document.addEventListener("pointerdown", _BottomSheets._onDown, { passive: true });
2024
+ document.addEventListener("pointermove", _BottomSheets._onMove, { passive: true });
2025
+ document.addEventListener("pointerup", _BottomSheets._onUp, { passive: true });
2026
+ document.addEventListener("pointercancel", _BottomSheets._onCancel, { passive: true });
2027
+ });
2028
+ }
2029
+ static _onDown(event) {
2030
+ if (!event.isPrimary) return;
2031
+ if (event.pointerType === "mouse" && event.button !== 0) return;
2032
+ const dialog = _BottomSheets._sheet(event.target);
2033
+ if (!dialog?.open) {
2034
+ _BottomSheets._drag = null;
2035
+ return;
2036
+ }
2037
+ if (!_BottomSheets._inHandle(event, dialog)) {
2038
+ _BottomSheets._drag = null;
2039
+ return;
2040
+ }
2041
+ dialog.style.transition = "none";
2042
+ _BottomSheets._drag = {
2043
+ dialog,
2044
+ pointerId: event.pointerId,
2045
+ startY: event.clientY,
2046
+ lastY: event.clientY,
2047
+ lastT: Date.now(),
2048
+ shift: 0
2049
+ };
2050
+ }
2051
+ static _onMove(event) {
2052
+ const drag = _BottomSheets._drag;
2053
+ if (!drag || drag.pointerId !== event.pointerId) return;
2054
+ const shift = Math.max(0, event.clientY - drag.startY);
2055
+ drag.shift = shift;
2056
+ drag.lastY = event.clientY;
2057
+ drag.lastT = Date.now();
2058
+ drag.dialog.style.setProperty("--md-comp-bottom-sheet-shift", `${shift}px`);
2059
+ }
2060
+ static _onUp(event) {
2061
+ const drag = _BottomSheets._drag;
2062
+ _BottomSheets._drag = null;
2063
+ if (!drag || drag.pointerId !== event.pointerId) return;
2064
+ const dialog = drag.dialog;
2065
+ dialog.style.transition = "";
2066
+ const dt = Math.max(1, Date.now() - drag.lastT);
2067
+ const velocity = (event.clientY - drag.lastY) / dt;
2068
+ const dismiss = drag.shift > 96 || velocity > 0.5;
2069
+ if (dismiss && dialog.open) {
2070
+ dialog.close();
2071
+ }
2072
+ dialog.style.setProperty("--md-comp-bottom-sheet-shift", "0px");
2073
+ }
2074
+ static _onCancel() {
2075
+ const drag = _BottomSheets._drag;
2076
+ _BottomSheets._drag = null;
2077
+ if (!drag) return;
2078
+ drag.dialog.style.transition = "";
2079
+ drag.dialog.style.setProperty("--md-comp-bottom-sheet-shift", "0px");
2080
+ }
2081
+ static _sheet(target) {
2082
+ if (!(target instanceof Element)) return null;
2083
+ const dialog = target.closest("dialog.bottom-sheet, dialog.bottom");
2084
+ return dialog instanceof HTMLDialogElement ? dialog : null;
2085
+ }
2086
+ static _inHandle(event, dialog) {
2087
+ const target = event.target;
2088
+ if (target instanceof Element && target.closest(".handle")) return true;
2089
+ const rect = dialog.getBoundingClientRect();
2090
+ return event.clientY >= rect.top && event.clientY <= rect.top + 48;
2091
+ }
2092
+ };
2093
+ _BottomSheets._drag = null;
2094
+ var BottomSheets = _BottomSheets;
2095
+
2096
+ // src/ts/behaviors/sideSheets.ts
2097
+ var _SideSheets = class _SideSheets {
2098
+ static Init() {
2099
+ Utils.onDocumentReady(() => {
2100
+ document.addEventListener("pointerdown", _SideSheets._onDown, { passive: true });
2101
+ document.addEventListener("pointermove", _SideSheets._onMove, { passive: true });
2102
+ document.addEventListener("pointerup", _SideSheets._onUp, { passive: true });
2103
+ document.addEventListener("pointercancel", _SideSheets._onCancel, { passive: true });
2104
+ });
2105
+ }
2106
+ static _onDown(event) {
2107
+ if (!event.isPrimary) return;
2108
+ if (event.pointerType === "mouse" && event.button !== 0) return;
2109
+ const dialog = _SideSheets._sheet(event.target);
2110
+ if (!dialog?.open) {
2111
+ _SideSheets._drag = null;
2112
+ return;
2113
+ }
2114
+ if (!_SideSheets._inHandle(event, dialog)) {
2115
+ _SideSheets._drag = null;
2116
+ return;
2117
+ }
2118
+ dialog.style.transition = "none";
2119
+ _SideSheets._drag = {
2120
+ dialog,
2121
+ pointerId: event.pointerId,
2122
+ startX: event.clientX,
2123
+ lastX: event.clientX,
2124
+ lastT: Date.now(),
2125
+ shift: 0,
2126
+ startDocked: _SideSheets._startDocked(dialog)
2127
+ };
2128
+ }
2129
+ static _onMove(event) {
2130
+ const drag = _SideSheets._drag;
2131
+ if (!drag || drag.pointerId !== event.pointerId) return;
2132
+ const delta = event.clientX - drag.startX;
2133
+ const shift = drag.startDocked ? Math.min(0, delta) : Math.max(0, delta);
2134
+ drag.shift = shift;
2135
+ drag.lastX = event.clientX;
2136
+ drag.lastT = Date.now();
2137
+ drag.dialog.style.setProperty("--md-comp-side-sheet-shift", `${shift}px`);
2138
+ }
2139
+ static _onUp(event) {
2140
+ const drag = _SideSheets._drag;
2141
+ _SideSheets._drag = null;
2142
+ if (!drag || drag.pointerId !== event.pointerId) return;
2143
+ const dialog = drag.dialog;
2144
+ dialog.style.transition = "";
2145
+ const dt = Math.max(1, Date.now() - drag.lastT);
2146
+ const velocity = (event.clientX - drag.lastX) / dt;
2147
+ const dismiss = drag.startDocked ? drag.shift < -96 || velocity < -0.5 : drag.shift > 96 || velocity > 0.5;
2148
+ if (dismiss && dialog.open) {
2149
+ dialog.close();
2150
+ }
2151
+ dialog.style.setProperty("--md-comp-side-sheet-shift", "0px");
2152
+ }
2153
+ static _onCancel() {
2154
+ const drag = _SideSheets._drag;
2155
+ _SideSheets._drag = null;
2156
+ if (!drag) return;
2157
+ drag.dialog.style.transition = "";
2158
+ drag.dialog.style.setProperty("--md-comp-side-sheet-shift", "0px");
2159
+ }
2160
+ static _sheet(target) {
2161
+ if (!(target instanceof Element)) return null;
2162
+ const dialog = target.closest(
2163
+ "dialog.side-sheet, dialog.right, dialog.right-sheet, dialog.left, dialog.left-sheet"
2164
+ );
2165
+ return dialog instanceof HTMLDialogElement ? dialog : null;
2166
+ }
2167
+ static _startDocked(dialog) {
2168
+ return dialog.classList.contains("left") || dialog.classList.contains("left-sheet") || dialog.classList.contains("start");
2169
+ }
2170
+ static _inHandle(event, dialog) {
2171
+ const target = event.target;
2172
+ if (target instanceof Element && target.closest("header")) return true;
2173
+ const rect = dialog.getBoundingClientRect();
2174
+ if (_SideSheets._startDocked(dialog)) {
2175
+ return event.clientX >= rect.right - 24 && event.clientX <= rect.right;
2176
+ }
2177
+ return event.clientX >= rect.left && event.clientX <= rect.left + 24;
2178
+ }
2179
+ };
2180
+ _SideSheets._drag = null;
2181
+ var SideSheets = _SideSheets;
2182
+
1913
2183
  // src/ts/behaviors/forms.ts
1914
2184
  var Forms = class _Forms {
1915
2185
  /**
@@ -2352,7 +2622,7 @@ var _Carousel = class _Carousel extends Component {
2352
2622
  ..._Carousel.defaults,
2353
2623
  ...options
2354
2624
  };
2355
- this._flat = this.options.fullWidth || this.el.classList.contains("flat") || this.el.classList.contains("carousel-slider");
2625
+ this._flat = this.options.fullWidth || this.el.classList.contains("flat");
2356
2626
  if (this._flat) {
2357
2627
  this.el.classList.add("flat");
2358
2628
  this.options.fullWidth = true;
@@ -2813,236 +3083,10 @@ var CharacterCounter = class _CharacterCounter extends Component {
2813
3083
  }
2814
3084
  };
2815
3085
 
2816
- // src/ts/components/collapsible.ts
2817
- var _defaults9 = {
2818
- accordion: true,
2819
- onOpenStart: null,
2820
- onOpenEnd: null,
2821
- onCloseStart: null,
2822
- onCloseEnd: null,
2823
- inDuration: 300,
2824
- outDuration: 300
2825
- };
2826
- var Collapsible = class _Collapsible extends Component {
2827
- constructor(el, options) {
2828
- super(el, options, _Collapsible);
2829
- this._items = [];
2830
- this._endTimers = [];
2831
- this._wrappedBodies = [];
2832
- /**
2833
- * Open collapsible section.
2834
- * @param index 0-based index of the section to open.
2835
- */
2836
- this.open = (index) => {
2837
- const item = this._items[index];
2838
- if (!item || this._isOpen(item)) return;
2839
- if (this.options.accordion) {
2840
- this._items.forEach((other, i) => {
2841
- if (i !== index && this._isOpen(other)) this.close(i);
2842
- });
2843
- }
2844
- this._setOpen(item, true);
2845
- };
2846
- /**
2847
- * Close collapsible section.
2848
- * @param index 0-based index of the section to close.
2849
- */
2850
- this.close = (index) => {
2851
- const item = this._items[index];
2852
- if (!item || !this._isOpen(item)) return;
2853
- this._setOpen(item, false);
2854
- };
2855
- this._handleClick = (e) => {
2856
- const header = e.target.closest(".collapsible-header");
2857
- if (!header || header.closest(".collapsible") !== this.el) return;
2858
- if (header.closest("details")) return;
2859
- const item = header.closest("li");
2860
- if (!item) return;
2861
- const index = this._items.indexOf(item);
2862
- if (index < 0) return;
2863
- if (this._isOpen(item)) this.close(index);
2864
- else this.open(index);
2865
- };
2866
- this._handleKeydown = (e) => {
2867
- if (!Utils.keys.ENTER.includes(e.key) && e.key !== " ") return;
2868
- const header = e.target.closest(".collapsible-header");
2869
- if (!header || header.closest(".collapsible") !== this.el) return;
2870
- if (header.closest("details")) return;
2871
- if (header.matches("a, button")) return;
2872
- e.preventDefault();
2873
- const item = header.closest("li");
2874
- if (!item) return;
2875
- const index = this._items.indexOf(item);
2876
- if (index < 0) return;
2877
- if (this._isOpen(item)) this.close(index);
2878
- else this.open(index);
2879
- };
2880
- this._handleToggle = (e) => {
2881
- const item = e.target;
2882
- if (!(item instanceof HTMLDetailsElement)) return;
2883
- if (!this._items.includes(item)) return;
2884
- this._notify(item, item.open);
2885
- };
2886
- this.el["Expressive_Collapsible"] = this;
2887
- const accordion = options.accordion !== void 0 ? options.accordion : !el.classList.contains("expandable");
2888
- this.options = {
2889
- ..._Collapsible.defaults,
2890
- ...options,
2891
- accordion
2892
- };
2893
- this._groupName = el.id ? `collapsible-${el.id}` : `collapsible-${Utils.guid()}`;
2894
- this._items = this._collectItems();
2895
- this._wrapDetailsBodies();
2896
- this._setupItems();
2897
- this._setupEventHandlers();
2898
- }
2899
- static get defaults() {
2900
- return _defaults9;
2901
- }
2902
- /**
2903
- * Initializes instances of Collapsible.
2904
- * @param els HTML elements.
2905
- * @param options Component options.
2906
- */
2907
- static init(els, options = {}) {
2908
- return super.init(els, options, _Collapsible);
2909
- }
2910
- static getInstance(el) {
2911
- return el["Expressive_Collapsible"];
2912
- }
2913
- destroy() {
2914
- this._removeEventHandlers();
2915
- this._endTimers.forEach((id) => clearTimeout(id));
2916
- this._endTimers = [];
2917
- this._unwrapDetailsBodies();
2918
- this.el["Expressive_Collapsible"] = void 0;
2919
- }
2920
- _collectItems() {
2921
- const details = Array.from(this.el.querySelectorAll(":scope > details"));
2922
- if (details.length) return details;
2923
- return Array.from(this.el.children).filter((c) => c.tagName === "LI");
2924
- }
2925
- _setupItems() {
2926
- if (this.options.accordion) {
2927
- this._items.forEach((item) => {
2928
- if (item instanceof HTMLDetailsElement && !item.getAttribute("name")) {
2929
- item.setAttribute("name", this._groupName);
2930
- }
2931
- });
2932
- }
2933
- const opened = this._items.filter((item) => this._isOpen(item));
2934
- if (this.options.accordion && opened.length > 1) {
2935
- opened.slice(1).forEach((item) => {
2936
- if (item instanceof HTMLDetailsElement) item.open = false;
2937
- else item.classList.remove("active");
2938
- });
2939
- }
2940
- this._items.forEach((item) => this._syncAria(item));
2941
- }
2942
- _isOpen(item) {
2943
- if (item instanceof HTMLDetailsElement) return item.open;
2944
- return item.classList.contains("active");
2945
- }
2946
- _setOpen(item, open) {
2947
- if (item instanceof HTMLDetailsElement) {
2948
- item.open = open;
2949
- return;
2950
- }
2951
- item.classList.toggle("active", open);
2952
- this._syncAria(item);
2953
- this._notify(item, open);
2954
- }
2955
- _headerOf(item) {
2956
- if (item instanceof HTMLDetailsElement) {
2957
- return item.querySelector(":scope > summary");
2958
- }
2959
- return item.querySelector(":scope > .collapsible-header");
2960
- }
2961
- _wrapDetailsBodies() {
2962
- this._items.forEach((item) => {
2963
- if (!(item instanceof HTMLDetailsElement)) return;
2964
- if (item.querySelector(":scope > .collapsible-body")) return;
2965
- const body = document.createElement("div");
2966
- body.className = "collapsible-body";
2967
- Array.from(item.childNodes).forEach((node) => {
2968
- if (node instanceof HTMLElement && node.tagName === "SUMMARY") return;
2969
- body.appendChild(node);
2970
- });
2971
- item.appendChild(body);
2972
- this._wrappedBodies.push(body);
2973
- });
2974
- }
2975
- _unwrapDetailsBodies() {
2976
- this._wrappedBodies.forEach((body) => {
2977
- const parent = body.parentNode;
2978
- if (!parent) return;
2979
- while (body.firstChild) parent.insertBefore(body.firstChild, body);
2980
- body.remove();
2981
- });
2982
- this._wrappedBodies = [];
2983
- }
2984
- _bodyOf(item) {
2985
- return item.querySelector(":scope > .collapsible-body");
2986
- }
2987
- _syncAria(item) {
2988
- if (item instanceof HTMLDetailsElement) return;
2989
- const header = this._headerOf(item);
2990
- const body = this._bodyOf(item);
2991
- if (!header) return;
2992
- header.setAttribute("aria-expanded", String(this._isOpen(item)));
2993
- if (body) {
2994
- if (!body.id) body.id = `collapsible-body-${Utils.guid()}`;
2995
- header.setAttribute("aria-controls", body.id);
2996
- }
2997
- if (!header.matches("a, button, summary")) {
2998
- header.setAttribute("role", "button");
2999
- if (header.tabIndex < 0) header.tabIndex = 0;
3000
- }
3001
- }
3002
- _setupEventHandlers() {
3003
- this.el.addEventListener("click", this._handleClick);
3004
- this.el.addEventListener("keydown", this._handleKeydown);
3005
- this.el.addEventListener("toggle", this._handleToggle, true);
3006
- }
3007
- _removeEventHandlers() {
3008
- this.el.removeEventListener("click", this._handleClick);
3009
- this.el.removeEventListener("keydown", this._handleKeydown);
3010
- this.el.removeEventListener("toggle", this._handleToggle, true);
3011
- }
3012
- _notify(item, opening) {
3013
- const start = opening ? this.options.onOpenStart : this.options.onCloseStart;
3014
- if (typeof start === "function") start.call(this, item);
3015
- const end = opening ? this.options.onOpenEnd : this.options.onCloseEnd;
3016
- if (typeof end !== "function") return;
3017
- const target = this._bodyOf(item) ?? item;
3018
- let done = false;
3019
- const finish = () => {
3020
- if (done) return;
3021
- done = true;
3022
- target.removeEventListener("transitionend", onEnd);
3023
- end.call(this, item);
3024
- };
3025
- const onEnd = (ev) => {
3026
- if (ev.target !== target) return;
3027
- finish();
3028
- };
3029
- target.addEventListener("transitionend", onEnd);
3030
- this._endTimers.push(window.setTimeout(finish, this._durationMs() + 50));
3031
- }
3032
- _durationMs() {
3033
- const raw = getComputedStyle(this.el).getPropertyValue("--md-comp-collapsible-duration").trim();
3034
- if (!raw) return 500;
3035
- if (raw.endsWith("ms")) return parseFloat(raw);
3036
- if (raw.endsWith("s")) return parseFloat(raw) * 1e3;
3037
- const n = parseFloat(raw);
3038
- return Number.isFinite(n) ? n : 500;
3039
- }
3040
- };
3041
-
3042
3086
  // src/ts/components/select.ts
3043
- var _defaults10 = {
3087
+ var _defaults9 = {
3044
3088
  classes: "",
3045
- dropdownOptions: {}
3089
+ menuOptions: {}
3046
3090
  };
3047
3091
  var FormSelect = class _FormSelect extends Component {
3048
3092
  constructor(el, options) {
@@ -3061,8 +3105,14 @@ var FormSelect = class _FormSelect extends Component {
3061
3105
  this._selectOptionElement(virtualOption);
3062
3106
  e.stopPropagation();
3063
3107
  };
3108
+ this._handleOptionFocus = (e) => {
3109
+ const target = e.target;
3110
+ if (!(target instanceof Element)) return;
3111
+ const li = target.closest('li[role="option"]');
3112
+ if (li?.id) this.input.setAttribute("aria-activedescendant", li.id);
3113
+ };
3064
3114
  this._handleInputClick = () => {
3065
- if (this.dropdown && this.dropdown.isOpen) {
3115
+ if (this.menu && this.menu.isOpen) {
3066
3116
  this._setValueToInput();
3067
3117
  this._setSelectedStates();
3068
3118
  }
@@ -3077,11 +3127,13 @@ var FormSelect = class _FormSelect extends Component {
3077
3127
  this.nativeTabIndex = this.el.tabIndex ?? -1;
3078
3128
  this.el.tabIndex = -1;
3079
3129
  this._values = [];
3080
- this._setupDropdown();
3130
+ this._createdWrapper = false;
3131
+ this._originalLabelFor = null;
3132
+ this._setupMenu();
3081
3133
  this._setupEventHandlers();
3082
3134
  }
3083
3135
  static get defaults() {
3084
- return _defaults10;
3136
+ return _defaults9;
3085
3137
  }
3086
3138
  /**
3087
3139
  * Initializes instances of FormSelect.
@@ -3096,36 +3148,48 @@ var FormSelect = class _FormSelect extends Component {
3096
3148
  }
3097
3149
  destroy() {
3098
3150
  this._removeEventHandlers();
3099
- this.dropdown?.destroy();
3100
- this._removeDropdown();
3151
+ this.menu?.destroy();
3152
+ this._removeMenu();
3153
+ this.el.tabIndex = this.nativeTabIndex;
3101
3154
  this.el["Expressive_FormSelect"] = void 0;
3102
3155
  }
3103
3156
  /**
3104
- * Re-read the native `<select>` and update the generated dropdown to match.
3157
+ * Re-read the native `<select>` and update the generated menu to match.
3105
3158
  *
3106
3159
  * Assigning `select.value` from script fires no `change` event, so nothing
3107
3160
  * else syncs the visible input text or the `.selected` state on the virtual
3108
- * options. Call this after mutating the underlying select programmatically.
3161
+ * options. Adding or removing `<option>`s also requires this — the menu is
3162
+ * rebuilt from the native list, the field and Menu instance stay put.
3109
3163
  */
3110
3164
  refresh() {
3165
+ if (!this.menuEl) return;
3166
+ this._rebuildOptions();
3111
3167
  this._setValueToInput();
3112
3168
  this._setSelectedStates();
3113
3169
  }
3114
3170
  _setupEventHandlers() {
3115
- this.dropdownOptions.querySelectorAll("li:not(.optgroup)").forEach((el) => {
3116
- el.addEventListener("click", this._handleOptionClick);
3117
- el.addEventListener("keydown", this._handleOptionKeydown);
3118
- });
3171
+ this._setupOptionHandlers();
3119
3172
  this.el.addEventListener("change", this._handleSelectChange);
3120
3173
  this.input.addEventListener("click", this._handleInputClick);
3174
+ this.menuEl.addEventListener("focusin", this._handleOptionFocus);
3121
3175
  }
3122
3176
  _removeEventHandlers() {
3123
- this.dropdownOptions.querySelectorAll("li:not(.optgroup)").forEach((el) => {
3177
+ this._removeOptionHandlers();
3178
+ this.el.removeEventListener("change", this._handleSelectChange);
3179
+ this.input?.removeEventListener("click", this._handleInputClick);
3180
+ this.menuEl?.removeEventListener("focusin", this._handleOptionFocus);
3181
+ }
3182
+ _setupOptionHandlers() {
3183
+ this.menuEl.querySelectorAll("li:not(.optgroup)").forEach((el) => {
3184
+ el.addEventListener("click", this._handleOptionClick);
3185
+ el.addEventListener("keydown", this._handleOptionKeydown);
3186
+ });
3187
+ }
3188
+ _removeOptionHandlers() {
3189
+ this.menuEl?.querySelectorAll("li:not(.optgroup)").forEach((el) => {
3124
3190
  el.removeEventListener("click", this._handleOptionClick);
3125
3191
  el.removeEventListener("keydown", this._handleOptionKeydown);
3126
3192
  });
3127
- this.el.removeEventListener("change", this._handleSelectChange);
3128
- this.input.removeEventListener("click", this._handleInputClick);
3129
3193
  }
3130
3194
  _arraysEqual(a, b) {
3131
3195
  if (a === b) return true;
@@ -3137,6 +3201,7 @@ var FormSelect = class _FormSelect extends Component {
3137
3201
  _selectOptionElement(virtualOption) {
3138
3202
  if (!virtualOption.classList.contains("disabled") && !virtualOption.classList.contains("optgroup")) {
3139
3203
  const value = this._values.find((value2) => value2.optionEl === virtualOption);
3204
+ if (!value) return;
3140
3205
  const previousSelectedValues = this.getSelectedValues();
3141
3206
  if (this.isMultiple) {
3142
3207
  this._toggleEntryFromArray(value);
@@ -3152,73 +3217,57 @@ var FormSelect = class _FormSelect extends Component {
3152
3217
  new Event("change", { bubbles: true, cancelable: true, composed: true })
3153
3218
  );
3154
3219
  }
3155
- if (!this.isMultiple) this.dropdown.close();
3220
+ if (!this.isMultiple) this.menu?.close();
3156
3221
  }
3157
- _setupDropdown() {
3222
+ _setupMenu() {
3158
3223
  this.labelEl = this.el.labels?.[0] ?? null;
3159
- this.wrapper = document.createElement("div");
3160
- this.wrapper.classList.add("select-wrapper", "input-field");
3224
+ this._setupWrapper();
3225
+ this._hideNativeSelect();
3226
+ if (this.el.disabled) this.wrapper.classList.add("disabled");
3227
+ this.menuEl = document.createElement("menu");
3228
+ this.menuEl.id = `select-options-${Utils.guid()}`;
3229
+ this.menuEl.setAttribute("popover", "auto");
3230
+ this.menuEl.setAttribute("role", "listbox");
3231
+ this.menuEl.ariaMultiSelectable = this.isMultiple.toString();
3232
+ if (this.isMultiple) this.menuEl.classList.add("multiple-select-menu");
3233
+ this._buildOptions();
3234
+ this.wrapper.append(this.menuEl);
3235
+ this._buildInput();
3236
+ this._buildCaret();
3237
+ this._initMenu();
3238
+ this._setSelectedStates();
3239
+ if (this.labelEl) this.input.after(this.labelEl);
3240
+ }
3241
+ _setupWrapper() {
3242
+ const parent = this.el.parentElement;
3243
+ const reuse = parent && parent.matches(".field") && !parent.querySelector(":scope > .hide-select") && !parent.querySelector(":scope > input, :scope > textarea");
3244
+ if (reuse) {
3245
+ this.wrapper = parent;
3246
+ this._createdWrapper = false;
3247
+ } else {
3248
+ this.wrapper = document.createElement("div");
3249
+ this.wrapper.classList.add("field");
3250
+ this._createdWrapper = true;
3251
+ this.el.before(this.wrapper);
3252
+ }
3161
3253
  if (this.options.classes.length > 0) {
3162
3254
  this.wrapper.classList.add(...this.options.classes.split(" ").filter(Boolean));
3163
3255
  }
3164
- this.el.before(this.wrapper);
3256
+ }
3257
+ _hideNativeSelect() {
3165
3258
  const hiddenDiv = document.createElement("div");
3166
3259
  hiddenDiv.classList.add("hide-select");
3167
- this.wrapper.append(hiddenDiv);
3260
+ this.el.before(hiddenDiv);
3168
3261
  hiddenDiv.appendChild(this.el);
3169
- if (this.el.disabled) this.wrapper.classList.add("disabled");
3170
- this.selectOptions = Array.from(this.el.children).filter((el) => ["OPTION", "OPTGROUP"].includes(el.tagName));
3171
- this.dropdownOptions = document.createElement("ul");
3172
- this.dropdownOptions.id = `select-options-${Utils.guid()}`;
3173
- this.dropdownOptions.setAttribute("popover", "auto");
3174
- this.dropdownOptions.classList.add("dropdown-content", "select-dropdown");
3175
- this.dropdownOptions.setAttribute("role", "listbox");
3176
- this.dropdownOptions.ariaMultiSelectable = this.isMultiple.toString();
3177
- if (this.isMultiple) this.dropdownOptions.classList.add("multiple-select-dropdown");
3178
- if (this.selectOptions.length > 0) {
3179
- this.selectOptions.forEach((realOption) => {
3180
- if (realOption.tagName === "OPTION") {
3181
- const virtualOption = this._createAndAppendOptionWithIcon(
3182
- realOption,
3183
- this.isMultiple ? "multiple" : void 0
3184
- );
3185
- this._addOptionToValues(realOption, virtualOption);
3186
- } else if (realOption.tagName === "OPTGROUP") {
3187
- const groupId = "opt-group-" + Utils.guid();
3188
- const groupParent = document.createElement("li");
3189
- groupParent.classList.add("optgroup");
3190
- groupParent.tabIndex = -1;
3191
- groupParent.setAttribute("role", "group");
3192
- groupParent.setAttribute("aria-labelledby", groupId);
3193
- const groupLabel = document.createElement("span");
3194
- groupLabel.id = groupId;
3195
- groupLabel.setAttribute("role", "presentation");
3196
- groupLabel.textContent = realOption.getAttribute("label") ?? "";
3197
- groupParent.replaceChildren(groupLabel);
3198
- this.dropdownOptions.append(groupParent);
3199
- const groupChildren = [];
3200
- const selectOptions = Array.from(realOption.children).filter((el) => el.tagName === "OPTION");
3201
- selectOptions.forEach((realOption2) => {
3202
- const virtualOption = this._createAndAppendOptionWithIcon(
3203
- realOption2,
3204
- "optgroup-option"
3205
- );
3206
- const childId = "opt-child-" + Utils.guid();
3207
- virtualOption.id = childId;
3208
- groupChildren.push(childId);
3209
- this._addOptionToValues(realOption2, virtualOption);
3210
- });
3211
- groupParent.setAttribute("aria-owns", groupChildren.join(" "));
3212
- }
3213
- });
3214
- }
3215
- this.wrapper.append(this.dropdownOptions);
3262
+ this.wrapper.append(hiddenDiv);
3263
+ }
3264
+ _buildInput() {
3216
3265
  this.input = document.createElement("input");
3217
- this.input.id = "m_select-input-" + Utils.guid();
3218
- this.input.classList.add("select-dropdown", "dropdown-trigger");
3266
+ this.input.id = "select-input-" + Utils.guid();
3267
+ this.input.classList.add("menu-trigger");
3219
3268
  this.input.type = "text";
3220
3269
  this.input.readOnly = true;
3221
- this.input.setAttribute("data-target", this.dropdownOptions.id);
3270
+ this.input.setAttribute("data-target", this.menuEl.id);
3222
3271
  this.input.ariaReadOnly = "true";
3223
3272
  this.input.ariaRequired = this.el.hasAttribute("required").toString();
3224
3273
  if (this.el.disabled) this.input.disabled = true;
@@ -3229,71 +3278,131 @@ var FormSelect = class _FormSelect extends Component {
3229
3278
  if (attr.name.startsWith("aria-")) this.input.setAttribute(attr.name, attr.value);
3230
3279
  }
3231
3280
  this.input.setAttribute("role", "combobox");
3281
+ this.input.setAttribute("aria-haspopup", "listbox");
3232
3282
  this.input.ariaExpanded = "false";
3233
- this.input.setAttribute("aria-owns", this.dropdownOptions.id);
3234
- this.input.setAttribute("aria-controls", this.dropdownOptions.id);
3283
+ this.input.setAttribute("aria-controls", this.menuEl.id);
3235
3284
  this.input.placeholder = " ";
3285
+ if (this.labelEl) {
3286
+ this._originalLabelFor = this.labelEl.htmlFor;
3287
+ this.labelEl.htmlFor = this.input.id;
3288
+ }
3236
3289
  this.wrapper.prepend(this.input);
3237
3290
  this._setValueToInput();
3238
- const dropdownIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
3239
- dropdownIcon.classList.add("caret");
3240
- dropdownIcon.setAttribute("height", "24");
3241
- dropdownIcon.setAttribute("width", "24");
3242
- dropdownIcon.setAttribute("viewBox", "0 0 24 24");
3243
- dropdownIcon.ariaHidden = "true";
3244
- dropdownIcon.innerHTML = `<path d="M7 10l5 5 5-5z"/><path d="M0 0h24v24H0z" fill="none"/>`;
3245
- this.wrapper.prepend(dropdownIcon);
3246
- if (!this.el.disabled) {
3247
- const dropdownOptions = { ...this.options.dropdownOptions };
3248
- dropdownOptions.coverTrigger = false;
3249
- const userOnOpenEnd = dropdownOptions.onOpenEnd;
3250
- const userOnCloseEnd = dropdownOptions.onCloseEnd;
3251
- dropdownOptions.onOpenEnd = () => {
3252
- const selectedOption = this.dropdownOptions.querySelector(".selected");
3253
- if (selectedOption) {
3254
- Utils.keyDown = true;
3255
- this.dropdown.focusedIndex = [...selectedOption.parentNode.children].indexOf(
3256
- selectedOption
3257
- );
3258
- this.dropdown._focusFocusedItem();
3259
- Utils.keyDown = false;
3260
- if (this.dropdown.isScrollable) {
3261
- let scrollOffset = selectedOption.getBoundingClientRect().top - this.dropdownOptions.getBoundingClientRect().top;
3262
- scrollOffset -= this.dropdownOptions.clientHeight / 2;
3263
- this.dropdownOptions.scrollTop = scrollOffset;
3264
- }
3291
+ }
3292
+ _buildCaret() {
3293
+ const caret = document.createElement("span");
3294
+ caret.classList.add("caret");
3295
+ caret.setAttribute("aria-hidden", "true");
3296
+ this.wrapper.prepend(caret);
3297
+ }
3298
+ _initMenu() {
3299
+ if (this.el.disabled) return;
3300
+ const menuOptions = { ...this.options.menuOptions };
3301
+ menuOptions.coverTrigger = false;
3302
+ const userOnOpenEnd = menuOptions.onOpenEnd;
3303
+ const userOnCloseEnd = menuOptions.onCloseEnd;
3304
+ menuOptions.onOpenEnd = () => {
3305
+ const selectedOption = this.menuEl.querySelector(".selected");
3306
+ if (selectedOption) {
3307
+ Utils.keyDown = true;
3308
+ this.menu.focusedIndex = [...selectedOption.parentNode.children].indexOf(selectedOption);
3309
+ this.menu._focusFocusedItem();
3310
+ Utils.keyDown = false;
3311
+ if (this.menu.isScrollable) {
3312
+ let scrollOffset = selectedOption.getBoundingClientRect().top - this.menuEl.getBoundingClientRect().top;
3313
+ scrollOffset -= this.menuEl.clientHeight / 2;
3314
+ this.menuEl.scrollTop = scrollOffset;
3265
3315
  }
3266
- this.input.ariaExpanded = "true";
3267
- if (userOnOpenEnd && typeof userOnOpenEnd === "function")
3268
- userOnOpenEnd.call(this.dropdown, this.el);
3269
- };
3270
- dropdownOptions.onCloseEnd = () => {
3271
- this.input.ariaExpanded = "false";
3272
- if (userOnCloseEnd && typeof userOnCloseEnd === "function")
3273
- userOnCloseEnd.call(this.dropdown, this.el);
3274
- };
3275
- dropdownOptions.closeOnClick = false;
3276
- this.dropdown = Dropdown.init(this.input, dropdownOptions);
3277
- }
3278
- this._setSelectedStates();
3279
- if (this.labelEl) this.input.after(this.labelEl);
3316
+ if (selectedOption.id) this.input.setAttribute("aria-activedescendant", selectedOption.id);
3317
+ }
3318
+ this.input.ariaExpanded = "true";
3319
+ if (userOnOpenEnd && typeof userOnOpenEnd === "function")
3320
+ userOnOpenEnd.call(this.menu, this.el);
3321
+ };
3322
+ menuOptions.onCloseEnd = () => {
3323
+ this.input.ariaExpanded = "false";
3324
+ this.input.removeAttribute("aria-activedescendant");
3325
+ if (userOnCloseEnd && typeof userOnCloseEnd === "function")
3326
+ userOnCloseEnd.call(this.menu, this.el);
3327
+ };
3328
+ menuOptions.closeOnClick = false;
3329
+ this.menu = Menu.init(this.input, menuOptions);
3330
+ }
3331
+ _nativeOptions() {
3332
+ return Array.from(this.el.children).filter((el) => ["OPTION", "OPTGROUP"].includes(el.tagName));
3333
+ }
3334
+ _buildOptions() {
3335
+ this.selectOptions = this._nativeOptions();
3336
+ this._values = [];
3337
+ this.selectOptions.forEach((realOption) => {
3338
+ if (realOption.tagName === "OPTION") {
3339
+ const virtualOption = this._createAndAppendOptionWithIcon(
3340
+ realOption,
3341
+ this.isMultiple ? "multiple" : void 0
3342
+ );
3343
+ this._addOptionToValues(realOption, virtualOption);
3344
+ } else if (realOption.tagName === "OPTGROUP") {
3345
+ const groupId = "opt-group-" + Utils.guid();
3346
+ const groupParent = document.createElement("li");
3347
+ groupParent.classList.add("optgroup");
3348
+ groupParent.tabIndex = -1;
3349
+ groupParent.setAttribute("role", "group");
3350
+ groupParent.setAttribute("aria-labelledby", groupId);
3351
+ const groupLabel = document.createElement("span");
3352
+ groupLabel.id = groupId;
3353
+ groupLabel.setAttribute("role", "presentation");
3354
+ groupLabel.textContent = realOption.getAttribute("label") ?? "";
3355
+ groupParent.replaceChildren(groupLabel);
3356
+ this.menuEl.append(groupParent);
3357
+ const groupChildren = [];
3358
+ const selectOptions = Array.from(realOption.children).filter((el) => el.tagName === "OPTION");
3359
+ selectOptions.forEach((child) => {
3360
+ const virtualOption = this._createAndAppendOptionWithIcon(child, "optgroup-option");
3361
+ groupChildren.push(virtualOption.id);
3362
+ this._addOptionToValues(child, virtualOption);
3363
+ });
3364
+ groupParent.setAttribute("aria-owns", groupChildren.join(" "));
3365
+ }
3366
+ });
3367
+ }
3368
+ _rebuildOptions() {
3369
+ this._removeOptionHandlers();
3370
+ this.menuEl.replaceChildren();
3371
+ this._buildOptions();
3372
+ this._setupOptionHandlers();
3373
+ this.menu?._makeMenuFocusable();
3280
3374
  }
3281
3375
  _addOptionToValues(realOption, virtualOption) {
3282
3376
  this._values.push({ el: realOption, optionEl: virtualOption });
3283
3377
  }
3284
- _removeDropdown() {
3285
- this.wrapper.querySelector(".caret").remove();
3286
- this.input.remove();
3287
- this.dropdownOptions.remove();
3288
- this.wrapper.before(this.el);
3289
- this.wrapper.remove();
3378
+ _removeMenu() {
3379
+ this.wrapper?.querySelector(":scope > .caret")?.remove();
3380
+ this.input?.remove();
3381
+ this.menuEl?.remove();
3382
+ const hide = this.el.parentElement;
3383
+ if (hide?.classList.contains("hide-select")) hide.replaceWith(this.el);
3384
+ if (this.labelEl && this._originalLabelFor !== null) {
3385
+ this.labelEl.htmlFor = this._originalLabelFor;
3386
+ }
3387
+ if (!this.wrapper) return;
3388
+ if (this._createdWrapper) {
3389
+ this.wrapper.replaceWith(this.el);
3390
+ } else {
3391
+ this.wrapper.classList.remove("disabled");
3392
+ if (this.options.classes.length > 0) {
3393
+ this.wrapper.classList.remove(...this.options.classes.split(" ").filter(Boolean));
3394
+ }
3395
+ }
3290
3396
  }
3291
3397
  _createAndAppendOptionWithIcon(realOption, type) {
3292
3398
  const li = document.createElement("li");
3399
+ li.id = "select-option-" + Utils.guid();
3293
3400
  li.setAttribute("role", "option");
3401
+ li.tabIndex = 0;
3294
3402
  if (realOption.disabled) {
3295
3403
  li.classList.add("disabled");
3296
3404
  li.ariaDisabled = "true";
3405
+ li.tabIndex = -1;
3297
3406
  }
3298
3407
  if (type === "optgroup-option") li.classList.add(type);
3299
3408
  const optionText = realOption.textContent;
@@ -3302,6 +3411,7 @@ var FormSelect = class _FormSelect extends Component {
3302
3411
  const label = document.createElement("label");
3303
3412
  const checkbox = document.createElement("input");
3304
3413
  checkbox.type = "checkbox";
3414
+ checkbox.tabIndex = -1;
3305
3415
  const labelText = document.createElement("span");
3306
3416
  labelText.textContent = optionText;
3307
3417
  label.append(checkbox, labelText);
@@ -3319,7 +3429,7 @@ var FormSelect = class _FormSelect extends Component {
3319
3429
  img.ariaHidden = "true";
3320
3430
  li.prepend(img);
3321
3431
  }
3322
- this.dropdownOptions.append(li);
3432
+ this.menuEl.append(li);
3323
3433
  return li;
3324
3434
  }
3325
3435
  _selectValue(value) {
@@ -3365,7 +3475,7 @@ var FormSelect = class _FormSelect extends Component {
3365
3475
  if (texts.length === 0) {
3366
3476
  const firstDisabledOption = this.el.querySelector("option:disabled");
3367
3477
  if (firstDisabledOption && firstDisabledOption.value === "") {
3368
- this.input.value = firstDisabledOption.innerText;
3478
+ this.input.value = firstDisabledOption.textContent;
3369
3479
  return;
3370
3480
  }
3371
3481
  }
@@ -3377,7 +3487,7 @@ var FormSelect = class _FormSelect extends Component {
3377
3487
  const cb = value.optionEl.querySelector('input[type="checkbox"]');
3378
3488
  if (cb) cb.checked = optionIsSelected;
3379
3489
  if (optionIsSelected) {
3380
- this._activateOption(this.dropdownOptions, value.optionEl);
3490
+ this._activateOption(this.menuEl, value.optionEl);
3381
3491
  } else {
3382
3492
  value.optionEl.classList.remove("selected");
3383
3493
  value.optionEl.ariaSelected = "false";
@@ -3387,7 +3497,7 @@ var FormSelect = class _FormSelect extends Component {
3387
3497
  _activateOption(ul, li) {
3388
3498
  if (!li) return;
3389
3499
  if (!this.isMultiple)
3390
- ul.querySelectorAll("li.selected").forEach((li2) => li2.classList.remove("selected"));
3500
+ ul.querySelectorAll("li.selected").forEach((row) => row.classList.remove("selected"));
3391
3501
  li.classList.add("selected");
3392
3502
  li.ariaSelected = "true";
3393
3503
  }
@@ -3397,7 +3507,7 @@ var FormSelect = class _FormSelect extends Component {
3397
3507
  };
3398
3508
 
3399
3509
  // src/ts/plugins/dockedDisplayPlugin.ts
3400
- var _defaults11 = {
3510
+ var _defaults10 = {
3401
3511
  margin: 5,
3402
3512
  transition: 10,
3403
3513
  duration: 250,
@@ -3446,7 +3556,7 @@ var DockedDisplayPlugin = class _DockedDisplayPlugin {
3446
3556
  };
3447
3557
  this.el = el;
3448
3558
  this.options = {
3449
- ..._defaults11,
3559
+ ..._defaults10,
3450
3560
  ...options
3451
3561
  };
3452
3562
  this.container = document.createElement("div");
@@ -3467,7 +3577,7 @@ var DockedDisplayPlugin = class _DockedDisplayPlugin {
3467
3577
  };
3468
3578
 
3469
3579
  // src/ts/components/datepicker.ts
3470
- var _defaults12 = {
3580
+ var _defaults11 = {
3471
3581
  // the default output format for the input field value
3472
3582
  format: "mmm dd, yyyy",
3473
3583
  // Used to create date object from current input string
@@ -3673,7 +3783,6 @@ var _Datepicker = class _Datepicker extends Component {
3673
3783
  }
3674
3784
  }
3675
3785
  };
3676
- // Set input value to the selected date and close Datepicker
3677
3786
  this._finishSelection = () => {
3678
3787
  this.setInputValues();
3679
3788
  };
@@ -3733,7 +3842,7 @@ var _Datepicker = class _Datepicker extends Component {
3733
3842
  }
3734
3843
  }
3735
3844
  static get defaults() {
3736
- return _defaults12;
3845
+ return _defaults11;
3737
3846
  }
3738
3847
  /**
3739
3848
  * Initializes instances of Datepicker.
@@ -4229,7 +4338,7 @@ var _Datepicker = class _Datepicker extends Component {
4229
4338
  if (opts.yearRangeReverse) arr.reverse();
4230
4339
  const yearHtml = `<select class="datepicker-select orig-select-year" tabindex="-1">${arr.join("")}</select>`;
4231
4340
  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>';
4232
- html += `<button class="month-prev${prev ? "" : " is-disabled"} btn" type="button">${leftArrow}</button>`;
4341
+ html += `<button class="month-prev${prev ? "" : " is-disabled"}" type="button">${leftArrow}</button>`;
4233
4342
  html += '<div class="selects-container">';
4234
4343
  if (opts.showMonthAfterYear) {
4235
4344
  html += yearHtml + monthHtml;
@@ -4238,7 +4347,7 @@ var _Datepicker = class _Datepicker extends Component {
4238
4347
  }
4239
4348
  html += "</div>";
4240
4349
  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>';
4241
- html += `<button class="month-next${next ? "" : " is-disabled"} btn" type="button">${rightArrow}</button>`;
4350
+ html += `<button class="month-next${next ? "" : " is-disabled"}" type="button">${rightArrow}</button>`;
4242
4351
  return html += "</div>";
4243
4352
  }
4244
4353
  /**
@@ -4316,11 +4425,11 @@ var _Datepicker = class _Datepicker extends Component {
4316
4425
  const monthSelect = this.calendarEl.querySelector(".orig-select-month");
4317
4426
  FormSelect.init(yearSelect, {
4318
4427
  classes: "select-year",
4319
- dropdownOptions: { container: document.body, constrainWidth: false }
4428
+ menuOptions: { container: document.body, constrainWidth: false }
4320
4429
  });
4321
4430
  FormSelect.init(monthSelect, {
4322
4431
  classes: "select-month",
4323
- dropdownOptions: { container: document.body, constrainWidth: false }
4432
+ menuOptions: { container: document.body, constrainWidth: false }
4324
4433
  });
4325
4434
  yearSelect.addEventListener("change", this._handleYearChange);
4326
4435
  monthSelect.addEventListener("change", this._handleMonthChange);
@@ -4436,17 +4545,6 @@ var _Datepicker = class _Datepicker extends Component {
4436
4545
  this.el.parentElement.appendChild(dateInput);
4437
4546
  return dateInput;
4438
4547
  }
4439
- // deprecated
4440
- open() {
4441
- console.warn("Datepicker.open() is deprecated. Remove this method and wrap in modal yourself.");
4442
- return this;
4443
- }
4444
- close() {
4445
- console.warn(
4446
- "Datepicker.close() is deprecated. Remove this method and wrap in modal yourself."
4447
- );
4448
- return this;
4449
- }
4450
4548
  };
4451
4549
  _Datepicker._template = `
4452
4550
  <div class="datepicker-container">
@@ -4456,19 +4554,13 @@ _Datepicker._template = `
4456
4554
  </div>
4457
4555
  <div class="datepicker-calendar-container">
4458
4556
  <div class="datepicker-calendar"></div>
4459
- <div class="datepicker-footer">
4460
- <!--<button class="btn-flat datepicker-clear waves-effect" style="visibility: hidden;" type="button"></button>
4461
- <div class="confirmation-btns">
4462
- <button class="btn-flat datepicker-cancel waves-effect" type="button"></button>
4463
- <button class="btn-flat datepicker-done waves-effect" type="button"></button>
4464
- </div>-->
4465
- </div>
4557
+ <div class="datepicker-footer"></div>
4466
4558
  </div>
4467
4559
  </div>`;
4468
4560
  var Datepicker = _Datepicker;
4469
4561
 
4470
4562
  // src/ts/components/lightbox.ts
4471
- var _defaults13 = {
4563
+ var _defaults12 = {
4472
4564
  inDuration: 275,
4473
4565
  outDuration: 200,
4474
4566
  onOpenStart: null,
@@ -4587,7 +4679,7 @@ var Lightbox = class _Lightbox extends Component {
4587
4679
  this._setupEventHandlers();
4588
4680
  }
4589
4681
  static get defaults() {
4590
- return _defaults13;
4682
+ return _defaults12;
4591
4683
  }
4592
4684
  /**
4593
4685
  * Initializes instances of Lightbox.
@@ -4751,217 +4843,41 @@ var Lightbox = class _Lightbox extends Component {
4751
4843
  }
4752
4844
  };
4753
4845
 
4754
- // src/ts/components/modal.ts
4755
- var _defaults14 = {
4756
- opacity: 0.5,
4757
- inDuration: 250,
4758
- outDuration: 250,
4759
- onOpenStart: null,
4760
- onOpenEnd: null,
4761
- onCloseStart: null,
4762
- onCloseEnd: null,
4763
- preventScrolling: true,
4764
- dismissible: true,
4765
- startingTop: "4%",
4766
- endingTop: "10%"
4767
- };
4768
- var _Modal_static, createHtml_fn, createHtmlElement_fn;
4769
- var _Modal = class _Modal extends Component {
4846
+ // src/ts/components/parallax.ts
4847
+ var _defaults13 = {};
4848
+ var Parallax = class _Parallax extends Component {
4770
4849
  constructor(el, options) {
4771
- super(el, options, _Modal);
4772
- this.el["Expressive_Modal"] = this;
4850
+ super(el, options, _Parallax);
4851
+ this.el["Expressive_Parallax"] = this;
4773
4852
  this.options = {
4774
- ..._Modal.defaults,
4853
+ ..._Parallax.defaults,
4775
4854
  ...options
4776
4855
  };
4777
- this.el.tabIndex = 0;
4778
- this._setupEventHandlers();
4856
+ if (!this.el.querySelector("img, video")) {
4857
+ console.error("Parallax: no <img> or <video> to move inside the .parallax element");
4858
+ }
4779
4859
  }
4780
4860
  static get defaults() {
4781
- return _defaults14;
4861
+ return _defaults13;
4782
4862
  }
4783
4863
  static init(els, options = {}) {
4784
- return super.init(els, options, _Modal);
4864
+ return super.init(els, options, _Parallax);
4785
4865
  }
4786
4866
  static getInstance(el) {
4787
- return el["Expressive_Modal"];
4867
+ return el["Expressive_Parallax"];
4788
4868
  }
4789
4869
  destroy() {
4790
- }
4791
- _setupEventHandlers() {
4792
- }
4793
- _removeEventHandlers() {
4794
- }
4795
- _handleTriggerClick() {
4796
- }
4797
- _handleOverlayClick() {
4798
- }
4799
- _handleModalCloseClick() {
4800
- }
4801
- _handleKeydown() {
4802
- }
4803
- _handleFocus() {
4804
- }
4805
- open() {
4806
- return this;
4807
- }
4808
- close() {
4809
- return this;
4810
- }
4811
- static create(config) {
4812
- const isServer = false;
4813
- if (isServer) return __privateMethod(this, _Modal_static, createHtml_fn).call(this, config);
4814
- return __privateMethod(this, _Modal_static, createHtmlElement_fn).call(this, config);
4815
- }
4816
- };
4817
- _Modal_static = new WeakSet();
4818
- createHtml_fn = function(config) {
4819
- const title = config.header ? `<h2>${typeof config.header === "string" ? config.header : ""}</h2>` : "";
4820
- const actions = config.footer ? `<form method="dialog">${typeof config.footer === "string" ? config.footer : ""}</form>` : "";
4821
- return `<dialog id="${config.id}">
4822
- ${title}
4823
- <div>${typeof config.content === "string" ? config.content : ""}</div>
4824
- ${actions}
4825
- </dialog>`;
4826
- };
4827
- createHtmlElement_fn = function(config) {
4828
- const dialog = document.createElement("dialog");
4829
- dialog.id = config.id;
4830
- return dialog;
4831
- };
4832
- __privateAdd(_Modal, _Modal_static);
4833
- var Modal = _Modal;
4834
-
4835
- // src/ts/components/parallax.ts
4836
- var _defaults15 = {
4837
- responsiveThreshold: 0
4838
- };
4839
- var Parallax = class _Parallax extends Component {
4840
- constructor(el, options) {
4841
- super(el, options, _Parallax);
4842
- this.el["Expressive_Parallax"] = this;
4843
- this.options = {
4844
- ..._Parallax.defaults,
4845
- ...options
4846
- };
4847
- if (!this.el.querySelector("img, video")) {
4848
- console.error("Parallax: no <img> or <video> to move inside the .parallax element");
4849
- }
4850
- }
4851
- static get defaults() {
4852
- return _defaults15;
4853
- }
4854
- static init(els, options = {}) {
4855
- return super.init(els, options, _Parallax);
4856
- }
4857
- static getInstance(el) {
4858
- return el["Expressive_Parallax"];
4859
- }
4860
- destroy() {
4861
- this.el["Expressive_Parallax"] = void 0;
4862
- }
4863
- };
4864
-
4865
- // src/ts/components/pushpin.ts
4866
- var _defaults16 = {
4867
- top: 0,
4868
- bottom: Infinity,
4869
- offset: 0,
4870
- onPositionChange: null
4871
- };
4872
- var _Pushpin = class _Pushpin extends Component {
4873
- constructor(el, options) {
4874
- super(el, options, _Pushpin);
4875
- this.el["Expressive_Pushpin"] = this;
4876
- this.options = {
4877
- ..._Pushpin.defaults,
4878
- ...options
4879
- };
4880
- this.originalOffset = this.el.offsetTop;
4881
- _Pushpin._pushpins.push(this);
4882
- this._setupEventHandlers();
4883
- this._updatePosition();
4884
- }
4885
- static get defaults() {
4886
- return _defaults16;
4887
- }
4888
- /**
4889
- * Initializes instances of Pushpin.
4890
- * @param els HTML elements.
4891
- * @param options Component options.
4892
- */
4893
- static init(els, options = {}) {
4894
- return super.init(els, options, _Pushpin);
4895
- }
4896
- static getInstance(el) {
4897
- return el["Expressive_Pushpin"];
4898
- }
4899
- destroy() {
4900
- this.el.style.top = null;
4901
- this._removePinClasses();
4902
- const index = _Pushpin._pushpins.indexOf(this);
4903
- _Pushpin._pushpins.splice(index, 1);
4904
- if (_Pushpin._pushpins.length === 0) {
4905
- this._removeEventHandlers();
4906
- }
4907
- this.el["Expressive_Pushpin"] = void 0;
4908
- }
4909
- static _updateElements() {
4910
- for (let i = 0; i < _Pushpin._pushpins.length; i++) {
4911
- _Pushpin._pushpins[i]._updatePosition();
4912
- }
4913
- }
4914
- _setupEventHandlers() {
4915
- document.addEventListener("scroll", _Pushpin._updateElements, { passive: true });
4916
- }
4917
- _removeEventHandlers() {
4918
- document.removeEventListener("scroll", _Pushpin._updateElements);
4919
- }
4920
- _updatePosition() {
4921
- const scrolled = Utils.getDocumentScrollTop() + this.options.offset;
4922
- if (this.options.top <= scrolled && this.options.bottom >= scrolled && !this.el.classList.contains("pinned")) {
4923
- this._removePinClasses();
4924
- this.el.style.top = `${this.options.offset}px`;
4925
- this.el.classList.add("pinned");
4926
- if (typeof this.options.onPositionChange === "function") {
4927
- this.options.onPositionChange.call(this, "pinned");
4928
- }
4929
- }
4930
- if (scrolled < this.options.top && !this.el.classList.contains("pin-top")) {
4931
- this._removePinClasses();
4932
- this.el.style.top = "0";
4933
- this.el.classList.add("pin-top");
4934
- if (typeof this.options.onPositionChange === "function") {
4935
- this.options.onPositionChange.call(this, "pin-top");
4936
- }
4937
- }
4938
- if (scrolled > this.options.bottom && !this.el.classList.contains("pin-bottom")) {
4939
- this._removePinClasses();
4940
- this.el.classList.add("pin-bottom");
4941
- this.el.style.top = `${this.options.bottom - this.originalOffset}px`;
4942
- if (typeof this.options.onPositionChange === "function") {
4943
- this.options.onPositionChange.call(this, "pin-bottom");
4944
- }
4945
- }
4946
- }
4947
- _removePinClasses() {
4948
- this.el.classList.remove("pin-top", "pinned", "pin-bottom");
4870
+ this.el["Expressive_Parallax"] = void 0;
4949
4871
  }
4950
4872
  };
4951
- _Pushpin._pushpins = [];
4952
- var Pushpin = _Pushpin;
4953
4873
 
4954
4874
  // src/ts/components/scrollspy.ts
4955
- var _defaults17 = {
4956
- throttle: 100,
4875
+ var _defaultGetActiveElement = (id) => `a[href="#${id}"]`;
4876
+ var _defaults14 = {
4957
4877
  scrollOffset: 200,
4958
- // offset - 200 allows elements near bottom of page to scroll
4959
4878
  activeClass: "active",
4960
- getActiveElement: (id) => {
4961
- return 'a[href="#' + id + '"]';
4962
- },
4963
- keepTopElementActive: false,
4964
- animationDuration: null
4879
+ getActiveElement: _defaultGetActiveElement,
4880
+ keepTopElementActive: false
4965
4881
  };
4966
4882
  var _ScrollSpy = class _ScrollSpy extends Component {
4967
4883
  constructor(el, options) {
@@ -4971,22 +4887,17 @@ var _ScrollSpy = class _ScrollSpy extends Component {
4971
4887
  ..._ScrollSpy.defaults,
4972
4888
  ...options
4973
4889
  };
4890
+ this.el.style.setProperty(
4891
+ "--md-comp-scrollspy-offset",
4892
+ `${this.options.scrollOffset}px`
4893
+ );
4974
4894
  _ScrollSpy._elements.push(this);
4975
- _ScrollSpy._count++;
4976
- _ScrollSpy._increment++;
4977
- this.tickId = -1;
4978
- this.id = _ScrollSpy._increment.toString();
4979
- this._setupEventHandlers();
4980
- _ScrollSpy._handleWindowScroll();
4895
+ _ScrollSpy._ensureObserver(this.options.scrollOffset);
4896
+ _ScrollSpy._observer?.observe(this.el);
4981
4897
  }
4982
4898
  static get defaults() {
4983
- return _defaults17;
4899
+ return _defaults14;
4984
4900
  }
4985
- /**
4986
- * Initializes instances of ScrollSpy.
4987
- * @param els HTML elements.
4988
- * @param options Component options.
4989
- */
4990
4901
  static init(els, options = {}) {
4991
4902
  return super.init(els, options, _ScrollSpy);
4992
4903
  }
@@ -4994,348 +4905,233 @@ var _ScrollSpy = class _ScrollSpy extends Component {
4994
4905
  return el["Expressive_ScrollSpy"];
4995
4906
  }
4996
4907
  destroy() {
4997
- const elementIndex = _ScrollSpy._elements.indexOf(this);
4998
- if (elementIndex >= 0) _ScrollSpy._elements.splice(elementIndex, 1);
4999
- const inViewIndex = _ScrollSpy._elementsInView.indexOf(this);
5000
- if (inViewIndex >= 0) _ScrollSpy._elementsInView.splice(inViewIndex, 1);
5001
- const visibleIndex = _ScrollSpy._visibleElements.indexOf(this.el);
5002
- if (visibleIndex >= 0) _ScrollSpy._visibleElements.splice(visibleIndex, 1);
5003
- _ScrollSpy._count--;
5004
- this._removeEventHandlers();
5005
- const actElem = document.querySelector(this.options.getActiveElement(this.el.id));
5006
- actElem?.classList.remove(this.options.activeClass);
5007
- this.el["Expressive_ScrollSpy"] = void 0;
5008
- }
5009
- _setupEventHandlers() {
5010
- if (_ScrollSpy._count === 1) {
5011
- _ScrollSpy._throttledScroll = Utils.throttle(_ScrollSpy._handleWindowScroll, this.options.throttle);
5012
- _ScrollSpy._throttledResize = Utils.throttle(_ScrollSpy._handleWindowScroll, 200);
5013
- window.addEventListener("scroll", _ScrollSpy._throttledScroll, { passive: true });
5014
- window.addEventListener("resize", _ScrollSpy._throttledResize, { passive: true });
5015
- document.body.addEventListener("click", _ScrollSpy._handleTriggerClick);
5016
- }
5017
- }
5018
- _removeEventHandlers() {
5019
- if (_ScrollSpy._count === 0) {
5020
- window.removeEventListener("scroll", _ScrollSpy._throttledScroll);
5021
- window.removeEventListener("resize", _ScrollSpy._throttledResize);
5022
- document.body.removeEventListener("click", _ScrollSpy._handleTriggerClick);
5023
- _ScrollSpy._throttledScroll = null;
5024
- _ScrollSpy._throttledResize = null;
4908
+ const index = _ScrollSpy._elements.indexOf(this);
4909
+ if (index >= 0) _ScrollSpy._elements.splice(index, 1);
4910
+ _ScrollSpy._ratios.delete(this.el);
4911
+ _ScrollSpy._observer?.unobserve(this.el);
4912
+ const link = _ScrollSpy._linkFor(this.el.id, this.options);
4913
+ if (link && link === _ScrollSpy._active) {
4914
+ _ScrollSpy._clearActive(this.options);
4915
+ } else {
4916
+ link?.classList.remove(this.options.activeClass);
4917
+ link?.removeAttribute("aria-current");
4918
+ }
4919
+ if (_ScrollSpy._elements.length === 0) {
4920
+ _ScrollSpy._observer?.disconnect();
4921
+ _ScrollSpy._observer = null;
4922
+ _ScrollSpy._ratios.clear();
4923
+ _ScrollSpy._active = null;
4924
+ } else {
4925
+ _ScrollSpy._syncActive();
5025
4926
  }
4927
+ this.el["Expressive_ScrollSpy"] = void 0;
5026
4928
  }
5027
- static _findElements(top, right, bottom, left) {
5028
- const hits = [];
5029
- const docElem = document.documentElement;
5030
- const clientTop = docElem.clientTop;
5031
- const clientLeft = docElem.clientLeft;
5032
- const scrollY = window.scrollY;
5033
- const scrollX = window.scrollX;
5034
- for (let i = 0; i < _ScrollSpy._elements.length; i++) {
5035
- const scrollspy = _ScrollSpy._elements[i];
5036
- const currTop = top + scrollspy.options.scrollOffset || 200;
5037
- const box = scrollspy.el.getBoundingClientRect();
5038
- if (box.height > 0) {
5039
- const elTop = box.top + scrollY - clientTop, elLeft = box.left + scrollX - clientLeft, elRight = elLeft + box.width, elBottom = elTop + box.height;
5040
- const isIntersect = !(elLeft > right || elRight < left || elTop > bottom || elBottom < currTop);
5041
- if (isIntersect) {
5042
- hits.push(scrollspy);
5043
- }
5044
- }
5045
- }
5046
- return hits;
4929
+ static _ensureObserver(offset) {
4930
+ if (_ScrollSpy._observer) return;
4931
+ if (typeof IntersectionObserver === "undefined") return;
4932
+ _ScrollSpy._observer = new IntersectionObserver(_ScrollSpy._onIntersect, {
4933
+ rootMargin: `-${offset}px 0px -45% 0px`,
4934
+ threshold: [0, 0.1, 0.25, 0.5, 0.75, 1]
4935
+ });
5047
4936
  }
5048
- _enter() {
5049
- _ScrollSpy._visibleElements = _ScrollSpy._visibleElements.filter(
5050
- (value) => value.getBoundingClientRect().height !== 0
5051
- );
5052
- if (_ScrollSpy._visibleElements[0]) {
5053
- const actElem = document.querySelector(
5054
- this.options.getActiveElement(_ScrollSpy._visibleElements[0].id)
5055
- );
5056
- actElem?.classList.remove(this.options.activeClass);
5057
- if (_ScrollSpy._visibleElements[0]["Expressive_ScrollSpy"] && this.id < _ScrollSpy._visibleElements[0]["Expressive_ScrollSpy"].id) {
5058
- _ScrollSpy._visibleElements.unshift(this.el);
4937
+ static _syncActive() {
4938
+ if (!_ScrollSpy._elements.length) return;
4939
+ const options = _ScrollSpy._elements[0].options;
4940
+ let best = null;
4941
+ let bestRatio = 0;
4942
+ for (const spy of _ScrollSpy._elements) {
4943
+ const ratio = _ScrollSpy._ratios.get(spy.el) ?? 0;
4944
+ if (ratio > bestRatio) {
4945
+ bestRatio = ratio;
4946
+ best = spy;
4947
+ }
4948
+ }
4949
+ if (!best) {
4950
+ if (options.keepTopElementActive) {
4951
+ best = _ScrollSpy._nearestAbove() ?? _ScrollSpy._elements[0];
5059
4952
  } else {
5060
- _ScrollSpy._visibleElements.push(this.el);
4953
+ _ScrollSpy._clearActive(options);
4954
+ return;
5061
4955
  }
5062
- } else {
5063
- _ScrollSpy._visibleElements.push(this.el);
5064
4956
  }
5065
- _ScrollSpy._resetKeptTopActiveElement(this.options.activeClass);
5066
- const selector = this.options.getActiveElement(_ScrollSpy._visibleElements[0].id);
5067
- document.querySelector(selector)?.classList.add(this.options.activeClass);
4957
+ const next = _ScrollSpy._linkFor(best.el.id, best.options);
4958
+ if (next === _ScrollSpy._active) return;
4959
+ _ScrollSpy._clearActive(options);
4960
+ if (!next) return;
4961
+ next.classList.add(best.options.activeClass);
4962
+ next.setAttribute("aria-current", "true");
4963
+ _ScrollSpy._active = next;
5068
4964
  }
5069
- _exit() {
5070
- _ScrollSpy._visibleElements = _ScrollSpy._visibleElements.filter(
5071
- (value) => value.getBoundingClientRect().height !== 0
5072
- );
5073
- if (_ScrollSpy._visibleElements[0]) {
5074
- const actElem = document.querySelector(
5075
- this.options.getActiveElement(_ScrollSpy._visibleElements[0].id)
5076
- );
5077
- actElem?.classList.remove(this.options.activeClass);
5078
- _ScrollSpy._visibleElements = _ScrollSpy._visibleElements.filter((x) => x.id != this.el.id);
5079
- if (_ScrollSpy._visibleElements[0]) {
5080
- const selector = this.options.getActiveElement(_ScrollSpy._visibleElements[0].id);
5081
- document.querySelector(selector)?.classList.add(this.options.activeClass);
5082
- _ScrollSpy._resetKeptTopActiveElement(this.options.activeClass);
5083
- }
5084
- }
5085
- }
5086
- static _resetKeptTopActiveElement(activeClass) {
5087
- if (_ScrollSpy._keptTopActiveElement) {
5088
- _ScrollSpy._keptTopActiveElement.classList.remove(activeClass);
5089
- _ScrollSpy._keptTopActiveElement = null;
5090
- }
5091
- }
5092
- static _getDistanceToViewport(element) {
5093
- const rect = element.getBoundingClientRect();
5094
- const distance = rect.top;
5095
- return distance;
5096
- }
5097
- static _smoothScrollIntoView(element, duration = 300) {
5098
- const targetPosition = element.getBoundingClientRect().top + (window.scrollY || window.pageYOffset);
5099
- const startPosition = window.scrollY || window.pageYOffset;
5100
- const distance = targetPosition - startPosition;
5101
- const startTime = performance.now();
5102
- function scrollStep(currentTime) {
5103
- const elapsed = currentTime - startTime;
5104
- const progress = Math.min(elapsed / duration, 1);
5105
- const scrollY = startPosition + distance * progress;
5106
- if (progress < 1) {
5107
- window.scrollTo(0, scrollY);
5108
- requestAnimationFrame(scrollStep);
5109
- } else {
5110
- window.scrollTo(0, targetPosition);
4965
+ static _nearestAbove() {
4966
+ let best = null;
4967
+ let bestTop = -Infinity;
4968
+ for (const spy of _ScrollSpy._elements) {
4969
+ const top = spy.el.getBoundingClientRect().top;
4970
+ if (top <= 0 && top >= bestTop) {
4971
+ bestTop = top;
4972
+ best = spy;
5111
4973
  }
5112
4974
  }
5113
- requestAnimationFrame(scrollStep);
4975
+ return best;
5114
4976
  }
5115
- };
5116
- _ScrollSpy._keptTopActiveElement = null;
5117
- // The document-level listeners are shared by every instance, so the
5118
- // references they are registered with have to be shared too - registering
5119
- // with one instance's bound handler and unregistering with another's is a
5120
- // silent no-op that leaves the listener attached forever.
5121
- _ScrollSpy._throttledScroll = null;
5122
- _ScrollSpy._throttledResize = null;
5123
- _ScrollSpy._handleTriggerClick = (e) => {
5124
- const trigger = e.target;
5125
- for (let i = _ScrollSpy._elements.length - 1; i >= 0; i--) {
5126
- const scrollspy = _ScrollSpy._elements[i];
5127
- const x = document.querySelector('a[href="#' + scrollspy.el.id + '"]');
5128
- if (trigger === x) {
5129
- e.preventDefault();
5130
- if (scrollspy.el["Expressive_ScrollSpy"].options.animationDuration) {
5131
- _ScrollSpy._smoothScrollIntoView(
5132
- scrollspy.el,
5133
- scrollspy.el["Expressive_ScrollSpy"].options.animationDuration
5134
- );
5135
- } else {
5136
- scrollspy.el.scrollIntoView({ behavior: "smooth" });
4977
+ static _clearActive(options) {
4978
+ if (!_ScrollSpy._active) return;
4979
+ _ScrollSpy._active.classList.remove(options.activeClass);
4980
+ _ScrollSpy._active.removeAttribute("aria-current");
4981
+ _ScrollSpy._active = null;
4982
+ }
4983
+ /**
4984
+ * Default lookup compares href attributes (no interpolated selector).
4985
+ * A custom getActiveElement still returns a selector.
4986
+ */
4987
+ static _linkFor(id, options) {
4988
+ if (!id) return null;
4989
+ if (options.getActiveElement !== _defaultGetActiveElement) {
4990
+ try {
4991
+ return document.querySelector(options.getActiveElement(id));
4992
+ } catch {
4993
+ return null;
5137
4994
  }
5138
- break;
5139
4995
  }
4996
+ const href = `#${id}`;
4997
+ const links = document.querySelectorAll('a[href^="#"]');
4998
+ for (let i = 0; i < links.length; i++) {
4999
+ if (links[i].getAttribute("href") === href) return links[i];
5000
+ }
5001
+ return null;
5140
5002
  }
5141
5003
  };
5142
- _ScrollSpy._handleWindowScroll = () => {
5143
- _ScrollSpy._ticks++;
5144
- const top = Utils.getDocumentScrollTop(), left = Utils.getDocumentScrollLeft(), right = left + window.innerWidth, bottom = top + window.innerHeight;
5145
- const intersections = _ScrollSpy._findElements(top, right, bottom, left);
5146
- for (let i = 0; i < intersections.length; i++) {
5147
- const scrollspy = intersections[i];
5148
- const lastTick = scrollspy.tickId;
5149
- if (lastTick < 0) {
5150
- scrollspy._enter();
5151
- }
5152
- scrollspy.tickId = _ScrollSpy._ticks;
5153
- }
5154
- for (let i = 0; i < _ScrollSpy._elementsInView.length; i++) {
5155
- const scrollspy = _ScrollSpy._elementsInView[i];
5156
- const lastTick = scrollspy.tickId;
5157
- if (lastTick >= 0 && lastTick !== _ScrollSpy._ticks) {
5158
- scrollspy._exit();
5159
- scrollspy.tickId = -1;
5160
- }
5161
- }
5162
- _ScrollSpy._elementsInView = intersections;
5163
- if (_ScrollSpy._elements.length) {
5164
- const options = _ScrollSpy._elements[0].el["Expressive_ScrollSpy"].options;
5165
- if (options.keepTopElementActive && _ScrollSpy._visibleElements.length === 0) {
5166
- _ScrollSpy._resetKeptTopActiveElement(options.activeClass);
5167
- 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);
5168
- const nearestTopElement = topElements.length ? topElements[topElements.length - 1] : _ScrollSpy._elements[0];
5169
- const actElem = document.querySelector(options.getActiveElement(nearestTopElement.el.id));
5170
- actElem?.classList.add(options.activeClass);
5171
- _ScrollSpy._keptTopActiveElement = actElem;
5004
+ _ScrollSpy._elements = [];
5005
+ _ScrollSpy._observer = null;
5006
+ _ScrollSpy._ratios = /* @__PURE__ */ new Map();
5007
+ _ScrollSpy._active = null;
5008
+ _ScrollSpy._onIntersect = (entries) => {
5009
+ for (const entry of entries) {
5010
+ if (entry.isIntersecting && entry.intersectionRatio > 0) {
5011
+ _ScrollSpy._ratios.set(entry.target, entry.intersectionRatio);
5012
+ } else {
5013
+ _ScrollSpy._ratios.delete(entry.target);
5172
5014
  }
5173
5015
  }
5016
+ _ScrollSpy._syncActive();
5174
5017
  };
5175
- _ScrollSpy._elements = [];
5176
- _ScrollSpy._elementsInView = [];
5177
- _ScrollSpy._visibleElements = [];
5178
- _ScrollSpy._count = 0;
5179
- _ScrollSpy._increment = 0;
5180
- _ScrollSpy._ticks = 0;
5181
5018
  var ScrollSpy = _ScrollSpy;
5182
5019
 
5183
5020
  // src/ts/components/sidenav.ts
5184
- var _defaults18 = {
5021
+ var _defaults15 = {
5185
5022
  edge: "left",
5186
5023
  draggable: true,
5187
5024
  dragTargetWidth: "10px",
5188
- inDuration: 250,
5189
- outDuration: 200,
5190
5025
  onOpenStart: null,
5191
5026
  onOpenEnd: null,
5192
5027
  onCloseStart: null,
5193
- onCloseEnd: null,
5194
- preventScrolling: true
5028
+ onCloseEnd: null
5195
5029
  };
5030
+ var LARGE_UP = "(width >= 993px)";
5196
5031
  var _Sidenav = class _Sidenav extends Component {
5197
5032
  constructor(el, options) {
5198
5033
  super(el, options, _Sidenav);
5199
- this._handleDragTargetDrag = (e) => {
5200
- if (!this._isDraggable()) return;
5201
- let totalDeltaX = this._calculateDelta(e);
5202
- const dragDirection = totalDeltaX > 0 ? "right" : "left";
5203
- totalDeltaX = Math.min(this._width, Math.abs(totalDeltaX));
5204
- if (this.options.edge === dragDirection) {
5205
- totalDeltaX = 0;
5206
- }
5207
- let transformX = totalDeltaX;
5208
- let transformPrefix = "translateX(-100%)";
5209
- if (this.options.edge === "right") {
5210
- transformPrefix = "translateX(100%)";
5211
- transformX = -transformX;
5212
- }
5213
- this.percentOpen = Math.min(1, totalDeltaX / this._width);
5214
- this.el.style.transform = `${transformPrefix} translateX(${transformX}px)`;
5215
- this._overlay.style.opacity = this.percentOpen.toString();
5216
- };
5217
- this._handleDragTargetRelease = () => {
5218
- if (this.isDragged) {
5219
- if (this.percentOpen > 0.2) {
5220
- this.open();
5221
- } else {
5222
- this._animateOut();
5034
+ /**
5035
+ * Opens the overlay drawer. No-op while the sidenav is docked
5036
+ * (sidenav-fixed at the large breakpoint).
5037
+ */
5038
+ this.open = () => {
5039
+ if (this._isCurrentlyFixed() || this.isOpen || !this._dialog) return;
5040
+ _Sidenav._sidenavs.forEach((other) => {
5041
+ if (other !== this && other.isOpen) other.close();
5042
+ });
5043
+ if (typeof this.options.onOpenStart === "function") {
5044
+ this.options.onOpenStart.call(this, this.el);
5045
+ }
5046
+ this.isOpen = true;
5047
+ this._dialog.classList.remove("dragging");
5048
+ this._clearShift();
5049
+ if (!this._dialog.open) {
5050
+ try {
5051
+ this._dialog.showModal();
5052
+ } catch {
5223
5053
  }
5224
- this.isDragged = false;
5225
- this._verticallyScrolling = false;
5226
5054
  }
5227
- };
5228
- this._handleCloseDrag = (e) => {
5229
- if (!this.isOpen || !this._isDraggable()) return;
5230
- let totalDeltaX = this._calculateDelta(e);
5231
- const dragDirection = totalDeltaX > 0 ? "right" : "left";
5232
- totalDeltaX = Math.min(this._width, Math.abs(totalDeltaX));
5233
- if (this.options.edge !== dragDirection) {
5234
- totalDeltaX = 0;
5055
+ this._syncTriggers();
5056
+ if (typeof this.options.onOpenEnd === "function") {
5057
+ this.options.onOpenEnd.call(this, this.el);
5235
5058
  }
5236
- let transformX = -totalDeltaX;
5237
- if (this.options.edge === "right") {
5238
- transformX = -transformX;
5059
+ };
5060
+ /**
5061
+ * Closes the overlay drawer. No-op while the sidenav is docked.
5062
+ */
5063
+ this.close = () => {
5064
+ if (this._isCurrentlyFixed() || !this.isOpen) return;
5065
+ this._beginClose();
5066
+ if (this._dialog?.open) {
5067
+ this._closing = true;
5068
+ this._dialog.close();
5069
+ } else {
5070
+ this._finishClose();
5239
5071
  }
5240
- this.percentOpen = Math.min(1, 1 - totalDeltaX / this._width);
5241
- this.el.style.transform = `translateX(${transformX}px)`;
5242
- this._overlay.style.opacity = this.percentOpen.toString();
5243
5072
  };
5244
- this._calculateDelta = (e) => {
5245
- if (!this.isDragged) {
5246
- this._startDrag(e);
5073
+ this._onDialogClose = () => {
5074
+ if (this.isOpen) {
5075
+ this._beginClose();
5076
+ this._finishClose();
5077
+ return;
5078
+ }
5079
+ if (this._closing) {
5080
+ this._closing = false;
5081
+ this._finishClose();
5247
5082
  }
5248
- this._dragMoveUpdate(e);
5249
- return this._xPos - this._startingXpos;
5250
5083
  };
5251
- this._handleCloseRelease = () => {
5252
- if (this.isOpen && this.isDragged) {
5253
- if (this.percentOpen > 0.8) {
5254
- this._animateIn();
5255
- } else {
5256
- this.close();
5257
- }
5258
- this.isDragged = false;
5259
- this._verticallyScrolling = false;
5084
+ this._onBreakpointChange = () => {
5085
+ if (this._isCurrentlyFixed() && this.isOpen) {
5086
+ this.close();
5260
5087
  }
5261
5088
  };
5262
- // Handles closing of Sidenav when element with class .sidenav-close
5263
5089
  this._handleCloseTriggerClick = (e) => {
5264
- const closeTrigger = e.target.closest(".sidenav-close");
5090
+ const target = e.target;
5091
+ if (!(target instanceof Element)) return;
5092
+ const closeTrigger = target.closest(".sidenav-close");
5265
5093
  if (closeTrigger && !this._isCurrentlyFixed()) {
5266
5094
  this.close();
5267
5095
  }
5268
5096
  };
5269
- this._handleWindowResize = () => {
5270
- if (this.lastWindowWidth !== window.innerWidth) {
5271
- if (window.innerWidth > 992) {
5272
- this.open();
5273
- } else {
5274
- this.close();
5275
- }
5276
- }
5277
- this.lastWindowWidth = window.innerWidth;
5278
- this.lastWindowHeight = window.innerHeight;
5097
+ this._handleDragTargetDrag = (e) => {
5098
+ if (!this._isDraggable()) return;
5099
+ const delta = this._rawDelta(e);
5100
+ const towardOpen = this.options.edge === "left" ? delta : -delta;
5101
+ const visible = Math.min(this._width, Math.max(0, towardOpen));
5102
+ const percentOpen = this._width ? visible / this._width : 0;
5103
+ this._applyShift(visible - this._width, percentOpen);
5279
5104
  };
5280
- /**
5281
- * Opens Sidenav.
5282
- */
5283
- this.open = () => {
5284
- if (this.isOpen === true) return;
5285
- this.isOpen = true;
5286
- if (typeof this.options.onOpenStart === "function") {
5287
- this.options.onOpenStart.call(this, this.el);
5288
- }
5289
- if (this._isCurrentlyFixed()) {
5290
- this.el.style.transform = "translateX(0)";
5291
- this._enableBodyScrolling();
5292
- this._overlay.style.display = "none";
5293
- } else {
5294
- if (this.options.preventScrolling) this._preventBodyScrolling();
5295
- if (!this.isDragged || this.percentOpen != 1) this._animateIn();
5296
- this._setAriaHidden();
5297
- this._setTabIndex();
5105
+ this._handleDragTargetRelease = () => {
5106
+ if (!this.isDragged) return;
5107
+ this._dialog?.classList.remove("dragging");
5108
+ if (this.percentOpen > 0.2) {
5109
+ this.open();
5110
+ } else if (this._dialog?.open && !this.isOpen) {
5111
+ this._clearShift();
5112
+ this._dialog.close();
5298
5113
  }
5114
+ this.isDragged = false;
5115
+ this._verticallyScrolling = false;
5299
5116
  };
5300
- /**
5301
- * Closes Sidenav.
5302
- */
5303
- this.close = () => {
5304
- if (this.isOpen === false) return;
5305
- this.isOpen = false;
5306
- if (typeof this.options.onCloseStart === "function") {
5307
- this.options.onCloseStart.call(this, this.el);
5308
- }
5309
- if (this._isCurrentlyFixed()) {
5310
- const transformX = this.options.edge === "left" ? "-105%" : "105%";
5311
- this.el.style.transform = `translateX(${transformX})`;
5117
+ this._handleCloseDrag = (e) => {
5118
+ if (!this.isOpen || !this._isDraggable()) return;
5119
+ const delta = this._rawDelta(e);
5120
+ const towardClose = this.options.edge === "left" ? -delta : delta;
5121
+ const hidden = Math.min(this._width, Math.max(0, towardClose));
5122
+ const percentOpen = this._width ? 1 - hidden / this._width : 1;
5123
+ this._applyShift(-hidden, percentOpen);
5124
+ };
5125
+ this._handleCloseRelease = () => {
5126
+ if (!this.isOpen || !this.isDragged) return;
5127
+ this._dialog?.classList.remove("dragging");
5128
+ if (this.percentOpen > 0.8) {
5129
+ this._clearShift();
5312
5130
  } else {
5313
- this._enableBodyScrolling();
5314
- if (!this.isDragged || this.percentOpen != 0) {
5315
- this._animateOut();
5316
- } else {
5317
- this._overlay.style.display = "none";
5318
- }
5319
- this._setAriaHidden();
5320
- this._setTabIndex();
5131
+ this.close();
5321
5132
  }
5322
- };
5323
- this._setAriaHidden = () => {
5324
- this.el.ariaHidden = this.isOpen ? "false" : "true";
5325
- const navWrapper = document.querySelector(".nav-wrapper ul");
5326
- if (navWrapper) navWrapper.ariaHidden = this.isOpen.toString();
5327
- };
5328
- this._setTabIndex = () => {
5329
- const navLinks = document.querySelectorAll(".nav-wrapper ul li a");
5330
- const sideNavLinks = document.querySelectorAll(".sidenav li a");
5331
- if (navLinks)
5332
- navLinks.forEach((navLink) => {
5333
- navLink.tabIndex = this.isOpen ? -1 : 0;
5334
- });
5335
- if (sideNavLinks)
5336
- sideNavLinks.forEach((sideNavLink) => {
5337
- sideNavLink.tabIndex = this.isOpen ? 0 : -1;
5338
- });
5133
+ this.isDragged = false;
5134
+ this._verticallyScrolling = false;
5339
5135
  };
5340
5136
  this.el["Expressive_Sidenav"] = this;
5341
5137
  this.options = {
@@ -5346,17 +5142,26 @@ var _Sidenav = class _Sidenav extends Component {
5346
5142
  this.isOpen = false;
5347
5143
  this.isFixed = this.el.classList.contains("sidenav-fixed");
5348
5144
  this.isDragged = false;
5349
- this.lastWindowWidth = window.innerWidth;
5350
- this.lastWindowHeight = window.innerHeight;
5351
- this._createOverlay();
5145
+ this.dragTarget = null;
5146
+ this._dialog = null;
5147
+ this._createdDialog = false;
5148
+ this._closing = false;
5149
+ this.percentOpen = 0;
5150
+ this._startingXpos = 0;
5151
+ this._xPos = 0;
5152
+ this._width = 0;
5153
+ this._initialScrollTop = 0;
5154
+ this._verticallyScrolling = false;
5155
+ this._mql = window.matchMedia(LARGE_UP);
5156
+ this._setupDialog();
5352
5157
  this._createDragTarget();
5353
- this._setupEventHandlers();
5354
5158
  this._setupClasses();
5355
- this._setupFixed();
5159
+ this._setupEventHandlers();
5160
+ this._syncTriggers();
5356
5161
  _Sidenav._sidenavs.push(this);
5357
5162
  }
5358
5163
  static get defaults() {
5359
- return _defaults18;
5164
+ return _defaults15;
5360
5165
  }
5361
5166
  /**
5362
5167
  * Initializes instances of Sidenav.
@@ -5371,182 +5176,284 @@ var _Sidenav = class _Sidenav extends Component {
5371
5176
  }
5372
5177
  destroy() {
5373
5178
  this._removeEventHandlers();
5374
- this._enableBodyScrolling();
5375
- this._overlay.parentNode.removeChild(this._overlay);
5376
- this.dragTarget.parentNode.removeChild(this.dragTarget);
5179
+ if (this._dialog?.open) {
5180
+ try {
5181
+ this._dialog.close();
5182
+ } catch {
5183
+ }
5184
+ }
5185
+ this._clearShift();
5186
+ this._unwrapDialog();
5187
+ this._removeDragTarget();
5377
5188
  this.el["Expressive_Sidenav"] = void 0;
5378
- this.el.style.transform = "";
5379
5189
  const index = _Sidenav._sidenavs.indexOf(this);
5380
5190
  if (index >= 0) {
5381
5191
  _Sidenav._sidenavs.splice(index, 1);
5382
5192
  }
5383
5193
  }
5384
- _createOverlay() {
5385
- this._overlay = document.createElement("div");
5386
- this._overlay.classList.add("sidenav-overlay");
5387
- this._overlay.addEventListener("click", this.close);
5388
- document.body.appendChild(this._overlay);
5194
+ _beginClose() {
5195
+ this.isOpen = false;
5196
+ this._dialog?.classList.remove("dragging");
5197
+ this._clearShift();
5198
+ if (typeof this.options.onCloseStart === "function") {
5199
+ this.options.onCloseStart.call(this, this.el);
5200
+ }
5201
+ this._syncTriggers();
5202
+ }
5203
+ _finishClose() {
5204
+ if (typeof this.options.onCloseEnd === "function") {
5205
+ this.options.onCloseEnd.call(this, this.el);
5206
+ }
5207
+ }
5208
+ _setupDialog() {
5209
+ if (this.el instanceof HTMLDialogElement) {
5210
+ this._dialog = this.el;
5211
+ this._createdDialog = false;
5212
+ return;
5213
+ }
5214
+ const parent = this.el.parentElement;
5215
+ if (parent instanceof HTMLDialogElement && parent.classList.contains("sidenav-overlay")) {
5216
+ this._dialog = parent;
5217
+ this._createdDialog = false;
5218
+ return;
5219
+ }
5220
+ const dialog = document.createElement("dialog");
5221
+ dialog.classList.add("sidenav-overlay");
5222
+ this.el.replaceWith(dialog);
5223
+ dialog.appendChild(this.el);
5224
+ this._dialog = dialog;
5225
+ this._createdDialog = true;
5226
+ }
5227
+ _unwrapDialog() {
5228
+ if (!this._createdDialog || !this._dialog) return;
5229
+ this._dialog.replaceWith(this.el);
5230
+ this._dialog = null;
5231
+ this._createdDialog = false;
5232
+ }
5233
+ _createDragTarget() {
5234
+ if (!this.options.draggable) return;
5235
+ const dragTarget = document.createElement("div");
5236
+ dragTarget.classList.add("drag-target");
5237
+ dragTarget.style.width = this.options.dragTargetWidth;
5238
+ if (this.isFixed) dragTarget.dataset.fixed = "";
5239
+ document.body.appendChild(dragTarget);
5240
+ this.dragTarget = dragTarget;
5241
+ }
5242
+ _removeDragTarget() {
5243
+ this.dragTarget?.remove();
5244
+ this.dragTarget = null;
5245
+ }
5246
+ _setupClasses() {
5247
+ if (this.options.edge === "right") {
5248
+ this.el.classList.add("right-aligned");
5249
+ this._dialog?.classList.add("right-aligned");
5250
+ this.dragTarget?.classList.add("right-aligned");
5251
+ }
5389
5252
  }
5390
5253
  _setupEventHandlers() {
5391
5254
  if (_Sidenav._sidenavs.length === 0) {
5392
- document.body.addEventListener("click", this._handleTriggerClick);
5255
+ document.body.addEventListener("click", _Sidenav._handleTriggerClick);
5393
5256
  }
5394
5257
  const passive = { passive: true };
5395
- this.dragTarget.addEventListener("touchmove", this._handleDragTargetDrag, passive);
5396
- this.dragTarget.addEventListener("touchend", this._handleDragTargetRelease);
5397
- this._overlay.addEventListener("touchmove", this._handleCloseDrag, passive);
5398
- this._overlay.addEventListener("touchend", this._handleCloseRelease);
5399
- this.el.addEventListener("touchmove", this._handleCloseDrag, passive);
5400
- this.el.addEventListener("touchend", this._handleCloseRelease);
5258
+ this.dragTarget?.addEventListener("touchmove", this._handleDragTargetDrag, passive);
5259
+ this.dragTarget?.addEventListener("touchend", this._handleDragTargetRelease);
5260
+ this._dialog?.addEventListener("touchmove", this._handleCloseDrag, passive);
5261
+ this._dialog?.addEventListener("touchend", this._handleCloseRelease);
5262
+ this._dialog?.addEventListener("close", this._onDialogClose);
5401
5263
  this.el.addEventListener("click", this._handleCloseTriggerClick);
5402
- if (this.isFixed) {
5403
- window.addEventListener("resize", this._handleWindowResize, passive);
5404
- }
5405
- this._setAriaHidden();
5406
- this._setTabIndex();
5264
+ this._mql.addEventListener("change", this._onBreakpointChange);
5407
5265
  }
5408
5266
  _removeEventHandlers() {
5409
5267
  if (_Sidenav._sidenavs.length === 1) {
5410
- document.body.removeEventListener("click", this._handleTriggerClick);
5411
- }
5412
- this.dragTarget.removeEventListener("touchmove", this._handleDragTargetDrag);
5413
- this.dragTarget.removeEventListener("touchend", this._handleDragTargetRelease);
5414
- this._overlay.removeEventListener("touchmove", this._handleCloseDrag);
5415
- this._overlay.removeEventListener("touchend", this._handleCloseRelease);
5416
- this.el.removeEventListener("touchmove", this._handleCloseDrag);
5417
- this.el.removeEventListener("touchend", this._handleCloseRelease);
5418
- this.el.removeEventListener("click", this._handleCloseTriggerClick);
5419
- if (this.isFixed) {
5420
- window.removeEventListener("resize", this._handleWindowResize);
5268
+ document.body.removeEventListener("click", _Sidenav._handleTriggerClick);
5421
5269
  }
5270
+ this.dragTarget?.removeEventListener("touchmove", this._handleDragTargetDrag);
5271
+ this.dragTarget?.removeEventListener("touchend", this._handleDragTargetRelease);
5272
+ this._dialog?.removeEventListener("touchmove", this._handleCloseDrag);
5273
+ this._dialog?.removeEventListener("touchend", this._handleCloseRelease);
5274
+ this._dialog?.removeEventListener("close", this._onDialogClose);
5275
+ this.el.removeEventListener("click", this._handleCloseTriggerClick);
5276
+ this._mql.removeEventListener("change", this._onBreakpointChange);
5422
5277
  }
5423
- _handleTriggerClick(e) {
5424
- const trigger = e.target.closest(".sidenav-trigger");
5425
- if (e.target && trigger) {
5426
- const sidenavId = Utils.getIdFromTrigger(trigger);
5427
- const sidenavInstance = document.getElementById(sidenavId)?.["Expressive_Sidenav"];
5428
- if (sidenavInstance) {
5429
- sidenavInstance.open();
5430
- }
5278
+ static _handleTriggerClick(e) {
5279
+ const target = e.target;
5280
+ if (!(target instanceof Element)) return;
5281
+ const trigger = target.closest(".sidenav-trigger");
5282
+ if (!trigger || !(trigger instanceof HTMLElement)) return;
5283
+ const sidenavId = Utils.getIdFromTrigger(trigger);
5284
+ const sidenavInstance = document.getElementById(sidenavId)?.["Expressive_Sidenav"];
5285
+ if (sidenavInstance) {
5286
+ sidenavInstance.open();
5431
5287
  e.preventDefault();
5432
5288
  }
5433
5289
  }
5434
- // Set variables needed at the beginning of drag and stop any current transition.
5290
+ _isDraggable() {
5291
+ return this.options.draggable && !this._isCurrentlyFixed() && !this._verticallyScrolling;
5292
+ }
5293
+ _isCurrentlyFixed() {
5294
+ return this.isFixed && this._mql.matches;
5295
+ }
5435
5296
  _startDrag(e) {
5436
5297
  const clientX = e.targetTouches[0].clientX;
5437
5298
  this.isDragged = true;
5438
5299
  this._startingXpos = clientX;
5439
- this._xPos = this._startingXpos;
5440
- this._time = Date.now();
5441
- this._width = this.el.getBoundingClientRect().width;
5442
- this._overlay.style.display = "block";
5300
+ this._xPos = clientX;
5301
+ this._width = this._dialog?.getBoundingClientRect().width || this.el.getBoundingClientRect().width;
5443
5302
  this._initialScrollTop = this.isOpen ? this.el.scrollTop : Utils.getDocumentScrollTop();
5444
5303
  this._verticallyScrolling = false;
5304
+ this._dialog?.classList.add("dragging");
5305
+ if (this._dialog && !this._dialog.open) {
5306
+ try {
5307
+ this._dialog.showModal();
5308
+ } catch {
5309
+ }
5310
+ }
5445
5311
  }
5446
- //Set variables needed at each drag move update tick
5447
5312
  _dragMoveUpdate(e) {
5448
5313
  const clientX = e.targetTouches[0].clientX;
5449
5314
  const currentScrollTop = this.isOpen ? this.el.scrollTop : Utils.getDocumentScrollTop();
5450
- this.deltaX = Math.abs(this._xPos - clientX);
5451
5315
  this._xPos = clientX;
5452
- this.velocityX = this.deltaX / (Date.now() - this._time);
5453
- this._time = Date.now();
5454
5316
  if (this._initialScrollTop !== currentScrollTop) {
5455
5317
  this._verticallyScrolling = true;
5456
5318
  }
5457
5319
  }
5458
- _setupClasses() {
5459
- if (this.options.edge === "right") {
5460
- this.el.classList.add("right-aligned");
5461
- this.dragTarget.classList.add("right-aligned");
5462
- }
5463
- }
5464
- _removeClasses() {
5465
- this.el.classList.remove("right-aligned");
5466
- this.dragTarget.classList.remove("right-aligned");
5467
- }
5468
- _setupFixed() {
5469
- if (this._isCurrentlyFixed()) this.open();
5470
- }
5471
- _isDraggable() {
5472
- return this.options.draggable && !this._isCurrentlyFixed() && !this._verticallyScrolling;
5320
+ _rawDelta(e) {
5321
+ if (!this.isDragged) this._startDrag(e);
5322
+ this._dragMoveUpdate(e);
5323
+ return this._xPos - this._startingXpos;
5324
+ }
5325
+ _applyShift(shift, percentOpen) {
5326
+ this.percentOpen = percentOpen;
5327
+ const host = this._dialog ?? this.el;
5328
+ host.style.setProperty("--md-comp-nav-drawer-shift", `${shift}px`);
5329
+ host.style.setProperty("--md-comp-nav-drawer-open", String(percentOpen));
5330
+ }
5331
+ _clearShift() {
5332
+ const host = this._dialog ?? this.el;
5333
+ host.style.removeProperty("--md-comp-nav-drawer-shift");
5334
+ host.style.removeProperty("--md-comp-nav-drawer-open");
5335
+ }
5336
+ _syncTriggers() {
5337
+ if (!this.id) return;
5338
+ document.querySelectorAll(".sidenav-trigger").forEach((trigger) => {
5339
+ if (!(trigger instanceof HTMLElement)) return;
5340
+ if (Utils.getIdFromTrigger(trigger) !== this.id) return;
5341
+ trigger.setAttribute("aria-expanded", this.isOpen ? "true" : "false");
5342
+ trigger.setAttribute("aria-controls", this.id);
5343
+ });
5473
5344
  }
5474
- _isCurrentlyFixed() {
5475
- return this.isFixed && window.innerWidth > 992;
5345
+ };
5346
+ _Sidenav._sidenavs = [];
5347
+ var Sidenav = _Sidenav;
5348
+
5349
+ // src/ts/components/navigationRail.ts
5350
+ var _defaults16 = {
5351
+ onOpenStart: null,
5352
+ onOpenEnd: null,
5353
+ onCloseStart: null,
5354
+ onCloseEnd: null
5355
+ };
5356
+ var COMPACT = "(width < 601px)";
5357
+ var NavigationRail = class _NavigationRail extends Component {
5358
+ constructor(el, options) {
5359
+ super(el, options, _NavigationRail);
5360
+ /**
5361
+ * Expands the rail. On compact windows the overlay (scrim) is CSS.
5362
+ */
5363
+ this.expand = () => {
5364
+ if (this.isExpanded) return;
5365
+ if (typeof this.options.onOpenStart === "function") {
5366
+ this.options.onOpenStart.call(this, this.el);
5367
+ }
5368
+ this.isExpanded = true;
5369
+ this.el.classList.add("expanded");
5370
+ this._sync();
5371
+ if (typeof this.options.onOpenEnd === "function") {
5372
+ this.options.onOpenEnd.call(this, this.el);
5373
+ }
5374
+ };
5375
+ /**
5376
+ * Collapses the rail.
5377
+ */
5378
+ this.collapse = () => {
5379
+ if (!this.isExpanded) return;
5380
+ if (typeof this.options.onCloseStart === "function") {
5381
+ this.options.onCloseStart.call(this, this.el);
5382
+ }
5383
+ this.isExpanded = false;
5384
+ this.el.classList.remove("expanded");
5385
+ this._sync();
5386
+ if (typeof this.options.onCloseEnd === "function") {
5387
+ this.options.onCloseEnd.call(this, this.el);
5388
+ }
5389
+ };
5390
+ /**
5391
+ * Toggles expanded / collapsed.
5392
+ */
5393
+ this.toggle = () => {
5394
+ if (this.isExpanded) this.collapse();
5395
+ else this.expand();
5396
+ };
5397
+ this._onClick = (e) => {
5398
+ const target = e.target;
5399
+ if (!(target instanceof Node)) return;
5400
+ if (this._toggle && (target === this._toggle || this._toggle.contains(target))) {
5401
+ this.toggle();
5402
+ e.preventDefault();
5403
+ return;
5404
+ }
5405
+ if (target === this.el && this.isExpanded && this._isModal()) {
5406
+ this.collapse();
5407
+ }
5408
+ };
5409
+ this._onKeyDown = (e) => {
5410
+ if (e.key !== "Escape") return;
5411
+ if (this.isExpanded && this._isModal()) this.collapse();
5412
+ };
5413
+ this.el["Expressive_NavigationRail"] = this;
5414
+ this.options = {
5415
+ ..._NavigationRail.defaults,
5416
+ ...options
5417
+ };
5418
+ this._toggle = this._findToggle();
5419
+ this._mql = window.matchMedia(COMPACT);
5420
+ this.isExpanded = this.el.classList.contains("expanded");
5421
+ this._sync();
5422
+ this.el.addEventListener("click", this._onClick);
5423
+ document.addEventListener("keydown", this._onKeyDown);
5476
5424
  }
5477
- _createDragTarget() {
5478
- const dragTarget = document.createElement("div");
5479
- dragTarget.classList.add("drag-target");
5480
- dragTarget.style.width = this.options.dragTargetWidth;
5481
- document.body.appendChild(dragTarget);
5482
- this.dragTarget = dragTarget;
5425
+ static get defaults() {
5426
+ return _defaults16;
5483
5427
  }
5484
- _preventBodyScrolling() {
5485
- document.body.style.overflow = "hidden";
5428
+ static init(els, options = {}) {
5429
+ return super.init(els, options, _NavigationRail);
5486
5430
  }
5487
- _enableBodyScrolling() {
5488
- document.body.style.overflow = "";
5431
+ static getInstance(el) {
5432
+ return el["Expressive_NavigationRail"];
5489
5433
  }
5490
- _animateIn() {
5491
- this._animateSidenavIn();
5492
- this._animateOverlayIn();
5434
+ destroy() {
5435
+ this.el.removeEventListener("click", this._onClick);
5436
+ document.removeEventListener("keydown", this._onKeyDown);
5437
+ this.el["Expressive_NavigationRail"] = void 0;
5493
5438
  }
5494
- _animateOut() {
5495
- this._animateSidenavOut();
5496
- this._animateOverlayOut();
5439
+ _findToggle() {
5440
+ const first = this.el.querySelector(":scope > button:not(.button)");
5441
+ return first instanceof HTMLButtonElement ? first : null;
5497
5442
  }
5498
- _animateSidenavIn() {
5499
- let slideOutPercent = this.options.edge === "left" ? -1 : 1;
5500
- if (this.isDragged) {
5501
- slideOutPercent = this.options.edge === "left" ? slideOutPercent + this.percentOpen : slideOutPercent - this.percentOpen;
5502
- }
5503
- const duration = this.options.inDuration;
5504
- this.el.style.transition = "none";
5505
- this.el.style.transform = "translateX(" + slideOutPercent * 100 + "%)";
5506
- setTimeout(() => {
5507
- this.el.style.transition = `transform ${duration}ms ease`;
5508
- this.el.style.transform = "translateX(0)";
5509
- }, 1);
5510
- setTimeout(() => {
5511
- if (typeof this.options.onOpenEnd === "function") this.options.onOpenEnd.call(this, this.el);
5512
- }, duration);
5443
+ _isCompact() {
5444
+ return this._mql.matches;
5513
5445
  }
5514
- _animateSidenavOut() {
5515
- const endPercent = this.options.edge === "left" ? -1 : 1;
5516
- const duration = this.options.outDuration;
5517
- this.el.style.transition = `transform ${duration}ms ease`;
5518
- this.el.style.transform = "translateX(" + endPercent * 100 + "%)";
5519
- setTimeout(() => {
5520
- if (typeof this.options.onCloseEnd === "function")
5521
- this.options.onCloseEnd.call(this, this.el);
5522
- }, duration);
5446
+ _isModal() {
5447
+ return this.el.classList.contains("modal") || this._isCompact();
5523
5448
  }
5524
- _animateOverlayIn() {
5525
- let start = 0;
5526
- if (this.isDragged) start = this.percentOpen;
5527
- else this._overlay.style.display = "block";
5528
- const duration = this.options.inDuration;
5529
- this._overlay.style.transition = "none";
5530
- this._overlay.style.opacity = start.toString();
5531
- setTimeout(() => {
5532
- this._overlay.style.transition = `opacity ${duration}ms ease`;
5533
- this._overlay.style.opacity = "1";
5534
- }, 1);
5535
- }
5536
- _animateOverlayOut() {
5537
- const duration = this.options.outDuration;
5538
- this._overlay.style.transition = `opacity ${duration}ms ease`;
5539
- this._overlay.style.opacity = "0";
5540
- setTimeout(() => {
5541
- this._overlay.style.display = "none";
5542
- }, duration);
5449
+ _sync() {
5450
+ this.el.setAttribute("aria-expanded", this.isExpanded ? "true" : "false");
5451
+ this._toggle?.setAttribute("aria-expanded", this.isExpanded ? "true" : "false");
5543
5452
  }
5544
5453
  };
5545
- _Sidenav._sidenavs = [];
5546
- var Sidenav = _Sidenav;
5547
5454
 
5548
5455
  // src/ts/components/slider.ts
5549
- var _defaults19 = {
5456
+ var _defaults17 = {
5550
5457
  indicators: true,
5551
5458
  height: 400,
5552
5459
  duration: 500,
@@ -5694,7 +5601,7 @@ var Slider = class _Slider extends Component {
5694
5601
  this.start();
5695
5602
  }
5696
5603
  static get defaults() {
5697
- return _defaults19;
5604
+ return _defaults17;
5698
5605
  }
5699
5606
  /**
5700
5607
  * Initializes instances of Slider.
@@ -5847,7 +5754,7 @@ var Slider = class _Slider extends Component {
5847
5754
  };
5848
5755
 
5849
5756
  // src/ts/components/tabs.ts
5850
- var _defaults20 = {
5757
+ var _defaults18 = {
5851
5758
  duration: 300,
5852
5759
  onShow: null,
5853
5760
  swipeable: false,
@@ -5925,7 +5832,7 @@ var Tabs = class _Tabs extends Component {
5925
5832
  this._setupEventHandlers();
5926
5833
  }
5927
5834
  static get defaults() {
5928
- return _defaults20;
5835
+ return _defaults18;
5929
5836
  }
5930
5837
  /**
5931
5838
  * Initializes instances of Tabs.
@@ -5964,7 +5871,7 @@ var Tabs = class _Tabs extends Component {
5964
5871
  }
5965
5872
  _queryTabLinks() {
5966
5873
  return this.el.querySelectorAll(
5967
- ":scope > a, :scope > .tab > a, :scope > li.tab > a"
5874
+ ":scope > a, :scope > .tab > a"
5968
5875
  );
5969
5876
  }
5970
5877
  _createIndicator() {
@@ -6006,7 +5913,7 @@ var Tabs = class _Tabs extends Component {
6006
5913
  }
6007
5914
  });
6008
5915
  const tabsWrapper = document.createElement("div");
6009
- tabsWrapper.classList.add("tabs-content", "carousel", "carousel-slider");
5916
+ tabsWrapper.classList.add("tabs-content", "carousel", "flat");
6010
5917
  tabsContent[0].parentElement.insertBefore(tabsWrapper, tabsContent[0]);
6011
5918
  tabsContent.forEach((tabContent) => {
6012
5919
  tabsWrapper.appendChild(tabContent);
@@ -6092,229 +5999,8 @@ var Tabs = class _Tabs extends Component {
6092
5999
  }
6093
6000
  };
6094
6001
 
6095
- // src/ts/components/tapTarget.ts
6096
- var _defaults21 = {
6097
- onOpen: null,
6098
- onClose: null
6099
- };
6100
- var _TapTarget = class _TapTarget extends Component {
6101
- constructor(el, options) {
6102
- super(el, options, _TapTarget);
6103
- this.wrapper = null;
6104
- this.waveEl = null;
6105
- this.contentEl = null;
6106
- this._createdWrapper = false;
6107
- this._createdWave = false;
6108
- this._usePopover = false;
6109
- this._liftedStatic = false;
6110
- this._opening = false;
6111
- /**
6112
- * Open Tap Target.
6113
- */
6114
- this.open = () => {
6115
- if (this.isOpen || !this.originEl || !this.wrapper) return;
6116
- _TapTarget._taptargets.forEach((other) => {
6117
- if (other !== this) other.close();
6118
- });
6119
- this._calculatePositioning();
6120
- if (typeof this.options.onOpen === "function") {
6121
- this.options.onOpen.call(this, this.originEl);
6122
- }
6123
- this.isOpen = true;
6124
- this.wrapper.classList.add("open");
6125
- this.originEl.setAttribute("aria-expanded", "true");
6126
- if (!this._usePopover) {
6127
- this.originEl.classList.add("tap-target-origin");
6128
- if (getComputedStyle(this.originEl).position === "static") {
6129
- this.originEl.style.position = "relative";
6130
- this._liftedStatic = true;
6131
- }
6132
- }
6133
- if (this._usePopover) {
6134
- this._opening = true;
6135
- try {
6136
- this.wrapper.showPopover();
6137
- } catch {
6138
- }
6139
- requestAnimationFrame(() => {
6140
- this._opening = false;
6141
- });
6142
- }
6143
- };
6144
- /**
6145
- * Close Tap Target.
6146
- */
6147
- this.close = () => {
6148
- if (!this.isOpen) return;
6149
- if (typeof this.options.onClose === "function" && this.originEl) {
6150
- this.options.onClose.call(this, this.originEl);
6151
- }
6152
- this.isOpen = false;
6153
- this.wrapper?.classList.remove("open");
6154
- this.originEl?.setAttribute("aria-expanded", "false");
6155
- this.originEl?.classList.remove("tap-target-origin");
6156
- if (this._liftedStatic && this.originEl) {
6157
- this.originEl.style.position = "";
6158
- this._liftedStatic = false;
6159
- }
6160
- if (this._usePopover && this.wrapper) {
6161
- try {
6162
- this.wrapper.hidePopover();
6163
- } catch {
6164
- }
6165
- }
6166
- };
6167
- // Built once per instance; a fresh throttle per event never fires.
6168
- this._handleThrottledResize = Utils.throttle(() => this._handleResize(), 200);
6169
- this._handleOriginKeydown = (e) => {
6170
- if (Utils.keys.ENTER.includes(e.key) || e.key === " ") {
6171
- e.preventDefault();
6172
- this._handleTargetToggle();
6173
- }
6174
- };
6175
- this._handleTargetToggle = () => {
6176
- if (!this.isOpen) this.open();
6177
- else this.close();
6178
- };
6179
- this._handleResize = () => {
6180
- if (this.isOpen) this._calculatePositioning();
6181
- };
6182
- this._handlePopoverToggle = (e) => {
6183
- const state = "newState" in e ? String(e.newState) : "";
6184
- if (state === "open") return;
6185
- if (this._opening) {
6186
- try {
6187
- this.wrapper?.showPopover();
6188
- } catch {
6189
- }
6190
- return;
6191
- }
6192
- if (this.isOpen) this.close();
6193
- };
6194
- this.el["Expressive_TapTarget"] = this;
6195
- this.options = {
6196
- ..._TapTarget.defaults,
6197
- ...options
6198
- };
6199
- this.isOpen = false;
6200
- this.originEl = el.dataset.target ? document.getElementById(el.dataset.target) : null;
6201
- if (!this.originEl) {
6202
- console.error(`TapTarget: no element with id "${el.dataset.target ?? ""}" to attach to`);
6203
- return;
6204
- }
6205
- this._setup();
6206
- this._calculatePositioning();
6207
- this._setupEventHandlers();
6208
- _TapTarget._taptargets.push(this);
6209
- }
6210
- static get defaults() {
6211
- return _defaults21;
6212
- }
6213
- static init(els, options = {}) {
6214
- return super.init(els, options, _TapTarget);
6215
- }
6216
- static getInstance(el) {
6217
- return el["Expressive_TapTarget"];
6218
- }
6219
- destroy() {
6220
- this.close();
6221
- this._removeEventHandlers();
6222
- this._teardownDom();
6223
- this.el["Expressive_TapTarget"] = void 0;
6224
- const index = _TapTarget._taptargets.indexOf(this);
6225
- if (index >= 0) _TapTarget._taptargets.splice(index, 1);
6226
- }
6227
- _setup() {
6228
- const parent = this.el.parentElement;
6229
- if (parent?.classList.contains("tap-target-wrapper")) {
6230
- this.wrapper = parent;
6231
- } else {
6232
- this.wrapper = document.createElement("div");
6233
- this.wrapper.classList.add("tap-target-wrapper");
6234
- this.el.before(this.wrapper);
6235
- this.wrapper.append(this.el);
6236
- this._createdWrapper = true;
6237
- }
6238
- this.contentEl = this.el.querySelector(".tap-target-content");
6239
- if (!this.contentEl) {
6240
- this.contentEl = document.createElement("div");
6241
- this.contentEl.className = "tap-target-content";
6242
- while (this.el.firstChild) this.contentEl.append(this.el.firstChild);
6243
- this.el.append(this.contentEl);
6244
- }
6245
- this.waveEl = this.el.querySelector(".tap-target-wave") ?? this.wrapper.querySelector(".tap-target-wave");
6246
- if (!this.waveEl) {
6247
- this.waveEl = document.createElement("div");
6248
- this.waveEl.className = "tap-target-wave";
6249
- this._createdWave = true;
6250
- }
6251
- this.el.prepend(this.waveEl);
6252
- this.el.setAttribute("role", "dialog");
6253
- this._labelFromHeading();
6254
- const origin = this.originEl;
6255
- origin.setAttribute("aria-expanded", "false");
6256
- origin.setAttribute("aria-controls", this.el.id || this._ensureId());
6257
- this._usePopover = typeof this.wrapper.showPopover === "function";
6258
- if (this._usePopover) {
6259
- this.wrapper.setAttribute("popover", "auto");
6260
- }
6261
- }
6262
- _ensureId() {
6263
- if (!this.el.id) this.el.id = `tap-target-${Utils.guid()}`;
6264
- return this.el.id;
6265
- }
6266
- _labelFromHeading() {
6267
- const heading = this.contentEl?.querySelector("h1, h2, h3, h4, h5, h6");
6268
- if (!heading) return;
6269
- if (!heading.id) heading.id = `tap-target-title-${Utils.guid()}`;
6270
- this.el.setAttribute("aria-labelledby", heading.id);
6271
- }
6272
- _teardownDom() {
6273
- if (this._createdWave) this.waveEl?.remove();
6274
- if (this._createdWrapper && this.wrapper) {
6275
- this.wrapper.before(this.el);
6276
- this.wrapper.remove();
6277
- }
6278
- this.wrapper = null;
6279
- this.waveEl = null;
6280
- }
6281
- _setupEventHandlers() {
6282
- if (!this.originEl) return;
6283
- this.originEl.addEventListener("click", this._handleTargetToggle);
6284
- if (!this.originEl.matches('button, a, [tabindex], .btn, [role="button"]')) {
6285
- this.originEl.tabIndex = 0;
6286
- this.originEl.addEventListener("keydown", this._handleOriginKeydown);
6287
- }
6288
- this.wrapper?.addEventListener("toggle", this._handlePopoverToggle);
6289
- window.addEventListener("resize", this._handleThrottledResize, { passive: true });
6290
- }
6291
- _removeEventHandlers() {
6292
- if (!this.originEl) return;
6293
- this.originEl.removeEventListener("click", this._handleTargetToggle);
6294
- this.originEl.removeEventListener("keydown", this._handleOriginKeydown);
6295
- this.wrapper?.removeEventListener("toggle", this._handlePopoverToggle);
6296
- window.removeEventListener("resize", this._handleThrottledResize);
6297
- }
6298
- _calculatePositioning() {
6299
- if (!this.originEl || !this.wrapper) return;
6300
- const rect = this.originEl.getBoundingClientRect();
6301
- const x = rect.left + rect.width / 2;
6302
- const y = rect.top + rect.height / 2;
6303
- const size = Math.max(rect.width, rect.height, 1);
6304
- this.wrapper.style.setProperty("--md-comp-tap-target-x", `${x}px`);
6305
- this.wrapper.style.setProperty("--md-comp-tap-target-y", `${y}px`);
6306
- this.wrapper.style.setProperty("--md-comp-tap-target-origin-size", `${size}px`);
6307
- const vertical = y <= window.innerHeight / 2 ? "top" : "bottom";
6308
- const onCenter = x >= window.innerWidth * 0.25 && x <= window.innerWidth * 0.75;
6309
- const horizontal = onCenter ? "center" : x <= window.innerWidth / 2 ? "left" : "right";
6310
- this.wrapper.dataset.edge = `${vertical}-${horizontal}`;
6311
- }
6312
- };
6313
- _TapTarget._taptargets = [];
6314
- var TapTarget = _TapTarget;
6315
-
6316
6002
  // src/ts/components/timepicker.ts
6317
- var _defaults22 = {
6003
+ var _defaults19 = {
6318
6004
  dialRadius: 135,
6319
6005
  outerRadius: 105,
6320
6006
  innerRadius: 70,
@@ -6512,7 +6198,7 @@ var _Timepicker = class _Timepicker extends Component {
6512
6198
  }
6513
6199
  }
6514
6200
  static get defaults() {
6515
- return _defaults22;
6201
+ return _defaults19;
6516
6202
  }
6517
6203
  /**
6518
6204
  * Initializes instances of Timepicker.
@@ -6595,7 +6281,7 @@ var _Timepicker = class _Timepicker extends Component {
6595
6281
  }
6596
6282
  /*private _createButton(text: string, visibility: string): HTMLButtonElement {
6597
6283
  const button = document.createElement('button');
6598
- button.classList.add('btn', 'waves-effect', 'text');
6284
+ button.classList.add('waves-effect', 'text');
6599
6285
  button.style.visibility = visibility;
6600
6286
  button.type = 'button';
6601
6287
  button.tabIndex = -1;
@@ -6636,14 +6322,14 @@ var _Timepicker = class _Timepicker extends Component {
6636
6322
  _clockSetup() {
6637
6323
  if (this.options.twelveHour) {
6638
6324
  this._amBtn = document.createElement("div");
6639
- this._amBtn.classList.add("am-btn", "btn");
6325
+ this._amBtn.classList.add("am-btn");
6640
6326
  this._amBtn.innerText = "AM";
6641
6327
  this._amBtn.tabIndex = 0;
6642
6328
  this._amBtn.addEventListener("click", this._handleAmPmClick);
6643
6329
  this._amBtn.addEventListener("keypress", this._handleAmPmKeypress);
6644
6330
  this.spanAmPm.appendChild(this._amBtn);
6645
6331
  this._pmBtn = document.createElement("div");
6646
- this._pmBtn.classList.add("pm-btn", "btn");
6332
+ this._pmBtn.classList.add("pm-btn");
6647
6333
  this._pmBtn.innerText = "PM";
6648
6334
  this._pmBtn.tabIndex = 0;
6649
6335
  this._pmBtn.addEventListener("click", this._handleAmPmClick);
@@ -6850,19 +6536,6 @@ var _Timepicker = class _Timepicker extends Component {
6850
6536
  this.hours = (/* @__PURE__ */ new Date()).getHours();
6851
6537
  this.inputHours.value = (this.hours % (this.options.twelveHour ? 12 : 24)).toString();
6852
6538
  }
6853
- // deprecated
6854
- open() {
6855
- console.warn(
6856
- "Timepicker.close() is deprecated. Remove this method and wrap in modal yourself."
6857
- );
6858
- return this;
6859
- }
6860
- close() {
6861
- console.warn(
6862
- "Timepicker.close() is deprecated. Remove this method and wrap in modal yourself."
6863
- );
6864
- return this;
6865
- }
6866
6539
  };
6867
6540
  _Timepicker._template = `<div class="timepicker-container">
6868
6541
  <div class="timepicker-digital-display">
@@ -6894,8 +6567,8 @@ _Timepicker._template = `<div class="timepicker-container">
6894
6567
  </div>`;
6895
6568
  var Timepicker = _Timepicker;
6896
6569
 
6897
- // src/ts/components/toasts.ts
6898
- var _defaults23 = {
6570
+ // src/ts/components/snackbar.ts
6571
+ var _defaults20 = {
6899
6572
  text: "",
6900
6573
  action: "",
6901
6574
  onAction: null,
@@ -6907,97 +6580,109 @@ var _defaults23 = {
6907
6580
  completeCallback: null,
6908
6581
  activationPercent: 0.8
6909
6582
  };
6910
- var _Toast = class _Toast {
6911
- constructor(options) {
6583
+ var _Snackbar = class _Snackbar {
6584
+ constructor(options = {}) {
6585
+ this._dismissed = false;
6912
6586
  this.options = {
6913
- ..._Toast.defaults,
6587
+ ..._Snackbar.defaults,
6914
6588
  ...options
6915
6589
  };
6590
+ if (options.displayLength === void 0 && this.options.action) {
6591
+ this.options.displayLength = 1e4;
6592
+ }
6916
6593
  this.message = this.options.text;
6917
6594
  this.panning = false;
6918
6595
  this.timeRemaining = this.options.displayLength;
6919
- if (_Toast._toasts.length === 0) {
6920
- _Toast._createContainer();
6596
+ if (_Snackbar._snackbars.length > 0) {
6597
+ for (const snackbar of [..._Snackbar._snackbars]) {
6598
+ snackbar._dismissed = true;
6599
+ clearTimeout(snackbar.counterTimeout);
6600
+ snackbar.el.remove();
6601
+ }
6602
+ _Snackbar._snackbars.length = 0;
6603
+ }
6604
+ if (!_Snackbar._container || !_Snackbar._container.isConnected) {
6605
+ _Snackbar._createContainer();
6921
6606
  }
6922
- _Toast._toasts.push(this);
6923
- const toastElement = this._createToast();
6924
- toastElement["Expressive_Toast"] = this;
6925
- this.el = toastElement;
6607
+ _Snackbar._snackbars.push(this);
6608
+ const snackbarElement = this._createSnackbar();
6609
+ snackbarElement["Expressive_Snackbar"] = this;
6610
+ this.el = snackbarElement;
6926
6611
  this._animateIn();
6927
6612
  this._setTimer();
6928
6613
  }
6929
6614
  static get defaults() {
6930
- return _defaults23;
6615
+ return _defaults20;
6931
6616
  }
6932
6617
  static getInstance(el) {
6933
- return el["Expressive_Toast"];
6618
+ return el["Expressive_Snackbar"];
6934
6619
  }
6935
6620
  static _createContainer() {
6936
6621
  const container = document.createElement("div");
6937
- container.setAttribute("id", "toast-container");
6938
- container.addEventListener("touchstart", _Toast._onDragStart);
6939
- container.addEventListener("touchmove", _Toast._onDragMove);
6940
- container.addEventListener("touchend", _Toast._onDragEnd);
6941
- container.addEventListener("mousedown", _Toast._onDragStart);
6942
- document.addEventListener("mousemove", _Toast._onDragMove);
6943
- document.addEventListener("mouseup", _Toast._onDragEnd);
6622
+ container.setAttribute("id", "snackbar-container");
6623
+ container.addEventListener("touchstart", _Snackbar._onDragStart);
6624
+ container.addEventListener("touchmove", _Snackbar._onDragMove);
6625
+ container.addEventListener("touchend", _Snackbar._onDragEnd);
6626
+ container.addEventListener("mousedown", _Snackbar._onDragStart);
6627
+ document.addEventListener("mousemove", _Snackbar._onDragMove);
6628
+ document.addEventListener("mouseup", _Snackbar._onDragEnd);
6944
6629
  document.body.appendChild(container);
6945
- _Toast._container = container;
6630
+ _Snackbar._container = container;
6946
6631
  }
6947
6632
  static _removeContainer() {
6948
- document.removeEventListener("mousemove", _Toast._onDragMove);
6949
- document.removeEventListener("mouseup", _Toast._onDragEnd);
6950
- _Toast._container.remove();
6951
- _Toast._container = null;
6633
+ document.removeEventListener("mousemove", _Snackbar._onDragMove);
6634
+ document.removeEventListener("mouseup", _Snackbar._onDragEnd);
6635
+ _Snackbar._container.remove();
6636
+ _Snackbar._container = null;
6952
6637
  }
6953
6638
  static _onDragStart(e) {
6954
6639
  const target = e.target;
6955
- if (target && !target.closest("button, a") && target.closest(".toast, .snackbar")) {
6956
- const toastElem = target.closest(".toast, .snackbar");
6957
- const toast = toastElem["Expressive_Toast"];
6958
- if (!toast) return;
6959
- toast.panning = true;
6960
- toast._pauseTimer();
6961
- _Toast._draggedToast = toast;
6962
- toast.el.classList.add("panning");
6963
- toast.el.style.transition = "";
6964
- toast.startingXPos = _Toast._xPos(e);
6965
- toast.time = Date.now();
6966
- toast.xPos = _Toast._xPos(e);
6640
+ if (target && !target.closest("button, a") && target.closest(".snackbar")) {
6641
+ const snackbarElem = target.closest(".snackbar");
6642
+ const snackbar = snackbarElem["Expressive_Snackbar"];
6643
+ if (!snackbar) return;
6644
+ snackbar.panning = true;
6645
+ snackbar._pauseTimer();
6646
+ _Snackbar._draggedSnackbar = snackbar;
6647
+ snackbar.el.classList.add("panning");
6648
+ snackbar.el.style.transition = "";
6649
+ snackbar.startingXPos = _Snackbar._xPos(e);
6650
+ snackbar.time = Date.now();
6651
+ snackbar.xPos = _Snackbar._xPos(e);
6967
6652
  }
6968
6653
  }
6969
6654
  static _onDragMove(e) {
6970
- if (!!_Toast._draggedToast) {
6655
+ if (!!_Snackbar._draggedSnackbar) {
6971
6656
  e.preventDefault();
6972
- const toast = _Toast._draggedToast;
6973
- toast.deltaX = Math.abs(toast.xPos - _Toast._xPos(e));
6974
- toast.xPos = _Toast._xPos(e);
6975
- toast.velocityX = toast.deltaX / (Date.now() - toast.time);
6976
- toast.time = Date.now();
6977
- const totalDeltaX = toast.xPos - toast.startingXPos;
6978
- const activationDistance = toast.el.offsetWidth * toast.options.activationPercent;
6979
- toast.el.style.transform = `translateX(${totalDeltaX}px)`;
6980
- toast.el.style.opacity = (1 - Math.abs(totalDeltaX / activationDistance)).toString();
6657
+ const snackbar = _Snackbar._draggedSnackbar;
6658
+ snackbar.deltaX = Math.abs(snackbar.xPos - _Snackbar._xPos(e));
6659
+ snackbar.xPos = _Snackbar._xPos(e);
6660
+ snackbar.velocityX = snackbar.deltaX / (Date.now() - snackbar.time);
6661
+ snackbar.time = Date.now();
6662
+ const totalDeltaX = snackbar.xPos - snackbar.startingXPos;
6663
+ const activationDistance = snackbar.el.offsetWidth * snackbar.options.activationPercent;
6664
+ snackbar.el.style.transform = `translateX(${totalDeltaX}px)`;
6665
+ snackbar.el.style.opacity = (1 - Math.abs(totalDeltaX / activationDistance)).toString();
6981
6666
  }
6982
6667
  }
6983
6668
  static _onDragEnd() {
6984
- if (!!_Toast._draggedToast) {
6985
- const toast = _Toast._draggedToast;
6986
- toast.panning = false;
6987
- toast.el.classList.remove("panning");
6988
- const totalDeltaX = toast.xPos - toast.startingXPos;
6989
- const activationDistance = toast.el.offsetWidth * toast.options.activationPercent;
6990
- const shouldBeDismissed = Math.abs(totalDeltaX) > activationDistance || toast.velocityX > 1;
6669
+ if (!!_Snackbar._draggedSnackbar) {
6670
+ const snackbar = _Snackbar._draggedSnackbar;
6671
+ snackbar.panning = false;
6672
+ snackbar.el.classList.remove("panning");
6673
+ const totalDeltaX = snackbar.xPos - snackbar.startingXPos;
6674
+ const activationDistance = snackbar.el.offsetWidth * snackbar.options.activationPercent;
6675
+ const shouldBeDismissed = Math.abs(totalDeltaX) > activationDistance || snackbar.velocityX > 1;
6991
6676
  if (shouldBeDismissed) {
6992
- toast.wasSwiped = true;
6993
- toast.dismiss();
6677
+ snackbar.wasSwiped = true;
6678
+ snackbar.dismiss();
6994
6679
  } else {
6995
- toast.el.style.transition = "transform .2s, opacity .2s";
6996
- toast.el.style.transform = "";
6997
- toast.el.style.opacity = "";
6998
- toast._resumeTimer();
6680
+ snackbar.el.style.transition = "transform .2s, opacity .2s";
6681
+ snackbar.el.style.transform = "";
6682
+ snackbar.el.style.opacity = "";
6683
+ snackbar._resumeTimer();
6999
6684
  }
7000
- _Toast._draggedToast = null;
6685
+ _Snackbar._draggedSnackbar = null;
7001
6686
  }
7002
6687
  }
7003
6688
  static _xPos(e) {
@@ -7007,30 +6692,30 @@ var _Toast = class _Toast {
7007
6692
  return e.clientX;
7008
6693
  }
7009
6694
  /**
7010
- * dismiss all toasts.
6695
+ * dismiss all snackbars.
7011
6696
  */
7012
6697
  static dismissAll() {
7013
- for (const toast of [..._Toast._toasts]) {
7014
- toast.dismiss();
6698
+ for (const snackbar of [..._Snackbar._snackbars]) {
6699
+ snackbar.dismiss();
7015
6700
  }
7016
6701
  }
7017
- _createToast() {
7018
- let toast = this.options.toastId ? document.getElementById(this.options.toastId) : document.createElement("div");
7019
- if (toast instanceof HTMLTemplateElement) {
7020
- const node = toast.content.cloneNode(true);
7021
- toast = node.firstElementChild;
6702
+ _createSnackbar() {
6703
+ let snackbar = this.options.snackbarId ? document.getElementById(this.options.snackbarId) : document.createElement("div");
6704
+ if (snackbar instanceof HTMLTemplateElement) {
6705
+ const node = snackbar.content.cloneNode(true);
6706
+ snackbar = node.firstElementChild;
7022
6707
  }
7023
- toast.classList.add("toast", "snackbar");
7024
- toast.setAttribute("role", "status");
7025
- toast.setAttribute("aria-live", "polite");
7026
- toast.setAttribute("aria-atomic", "true");
6708
+ snackbar.classList.add("snackbar");
6709
+ snackbar.setAttribute("role", "status");
6710
+ snackbar.setAttribute("aria-live", "polite");
6711
+ snackbar.setAttribute("aria-atomic", "true");
7027
6712
  if (this.options.classes.length > 0) {
7028
- toast.classList.add(...this.options.classes.split(" ").filter(Boolean));
6713
+ snackbar.classList.add(...this.options.classes.split(" ").filter(Boolean));
7029
6714
  }
7030
6715
  if (this.message) {
7031
6716
  const text = document.createElement("p");
7032
6717
  text.textContent = this.message;
7033
- toast.replaceChildren(text);
6718
+ snackbar.replaceChildren(text);
7034
6719
  }
7035
6720
  if (this.options.action) {
7036
6721
  const action = document.createElement("button");
@@ -7043,7 +6728,7 @@ var _Toast = class _Toast {
7043
6728
  }
7044
6729
  this.dismiss();
7045
6730
  });
7046
- toast.appendChild(action);
6731
+ snackbar.appendChild(action);
7047
6732
  }
7048
6733
  if (this.options.dismissible) {
7049
6734
  const close = document.createElement("button");
@@ -7059,20 +6744,21 @@ var _Toast = class _Toast {
7059
6744
  e.stopPropagation();
7060
6745
  this.dismiss();
7061
6746
  });
7062
- toast.appendChild(close);
6747
+ snackbar.appendChild(close);
7063
6748
  }
7064
- _Toast._container.appendChild(toast);
7065
- return toast;
6749
+ _Snackbar._container.appendChild(snackbar);
6750
+ return snackbar;
7066
6751
  }
7067
6752
  _animateIn() {
7068
6753
  this.el.style.display = "";
7069
6754
  this.el.style.opacity = "0";
6755
+ this.el.style.transform = "translateY(24px)";
7070
6756
  this.el.style.transition = `
7071
- top ${this.options.inDuration}ms ease,
6757
+ transform ${this.options.inDuration}ms ease,
7072
6758
  opacity ${this.options.inDuration}ms ease
7073
6759
  `;
7074
6760
  setTimeout(() => {
7075
- this.el.style.top = "0";
6761
+ this.el.style.transform = "";
7076
6762
  this.el.style.opacity = "1";
7077
6763
  }, 1);
7078
6764
  }
@@ -7080,7 +6766,7 @@ var _Toast = class _Toast {
7080
6766
  * Arm the dismissal timer.
7081
6767
  *
7082
6768
  * One timeout, not a 20ms interval ticking a counter down: the old form woke
7083
- * the page up 50 times a second per toast purely to subtract 20 from a
6769
+ * the page up 50 times a second per snackbar purely to subtract 20 from a
7084
6770
  * number. Dragging pauses the countdown via _pauseTimer/_resumeTimer instead.
7085
6771
  */
7086
6772
  _setTimer() {
@@ -7101,9 +6787,11 @@ var _Toast = class _Toast {
7101
6787
  this._setTimer();
7102
6788
  }
7103
6789
  /**
7104
- * Dismiss toast with animation.
6790
+ * Dismiss snackbar with animation.
7105
6791
  */
7106
6792
  dismiss() {
6793
+ if (this._dismissed) return;
6794
+ this._dismissed = true;
7107
6795
  clearTimeout(this.counterTimeout);
7108
6796
  this.counterTimeout = null;
7109
6797
  const activationDistance = this.el.offsetWidth * this.options.activationPercent;
@@ -7123,24 +6811,24 @@ var _Toast = class _Toast {
7123
6811
  if (typeof this.options.completeCallback === "function") {
7124
6812
  this.options.completeCallback();
7125
6813
  }
7126
- if (this.el.id != this.options.toastId) {
6814
+ if (this.el.id != this.options.snackbarId) {
7127
6815
  this.el.remove();
7128
- const index = _Toast._toasts.indexOf(this);
7129
- if (index >= 0) _Toast._toasts.splice(index, 1);
7130
- if (_Toast._toasts.length === 0 && _Toast._container) {
7131
- _Toast._removeContainer();
6816
+ const index = _Snackbar._snackbars.indexOf(this);
6817
+ if (index >= 0) _Snackbar._snackbars.splice(index, 1);
6818
+ if (_Snackbar._snackbars.length === 0 && _Snackbar._container && _Snackbar._container.childElementCount === 0) {
6819
+ _Snackbar._removeContainer();
7132
6820
  }
7133
6821
  }
7134
6822
  }, this.options.outDuration);
7135
6823
  }
7136
6824
  };
7137
- _Toast._toasts = [];
7138
- _Toast._container = null;
7139
- _Toast._draggedToast = null;
7140
- var Toast = _Toast;
6825
+ _Snackbar._snackbars = [];
6826
+ _Snackbar._container = null;
6827
+ _Snackbar._draggedSnackbar = null;
6828
+ var Snackbar = _Snackbar;
7141
6829
 
7142
6830
  // src/ts/components/tooltip.ts
7143
- var _defaults24 = {
6831
+ var _defaults21 = {
7144
6832
  exitDelay: 200,
7145
6833
  enterDelay: 0,
7146
6834
  text: "",
@@ -7208,7 +6896,7 @@ var Tooltip = class _Tooltip extends Component {
7208
6896
  this._setupEventHandlers();
7209
6897
  }
7210
6898
  static get defaults() {
7211
- return _defaults24;
6899
+ return _defaults21;
7212
6900
  }
7213
6901
  /**
7214
6902
  * Initializes instances of Tooltip.
@@ -7231,7 +6919,7 @@ var Tooltip = class _Tooltip extends Component {
7231
6919
  }
7232
6920
  _appendTooltipEl() {
7233
6921
  this.tooltipEl = document.createElement("div");
7234
- this.tooltipEl.classList.add("tooltip", "material-tooltip");
6922
+ this.tooltipEl.classList.add("tooltip");
7235
6923
  this.tooltipEl.id = `tooltip-${Utils.guid()}`;
7236
6924
  this.tooltipEl.setAttribute("role", "tooltip");
7237
6925
  this.el.setAttribute("aria-describedby", this.tooltipEl.id);
@@ -7388,18 +7076,15 @@ var AUTO_INIT_COMPONENTS = {
7388
7076
  Cards: { component: Cards, selector: CARDS_SELECTOR },
7389
7077
  Carousel: { component: Carousel, selector: ".carousel" },
7390
7078
  Chips: { component: Chips, selector: ".chips" },
7391
- Collapsible: { component: Collapsible, selector: ".collapsible" },
7392
7079
  Datepicker: { component: Datepicker, selector: ".datepicker" },
7393
- Dropdown: { component: Dropdown, selector: ".dropdown-trigger" },
7080
+ Menu: { component: Menu, selector: ".menu-trigger" },
7394
7081
  Lightbox: { component: Lightbox, selector: ".lightboxed" },
7395
- Modal: { component: Modal, selector: ".modal" },
7396
7082
  Parallax: { component: Parallax, selector: ".parallax" },
7397
- Pushpin: { component: Pushpin, selector: ".pushpin" },
7398
7083
  ScrollSpy: { component: ScrollSpy, selector: ".scrollspy" },
7399
7084
  FormSelect: { component: FormSelect, selector: "select" },
7400
7085
  Sidenav: { component: Sidenav, selector: ".sidenav" },
7086
+ NavigationRail: { component: NavigationRail, selector: ".navigation-rail" },
7401
7087
  Tabs: { component: Tabs, selector: ".tabs" },
7402
- TapTarget: { component: TapTarget, selector: ".tap-target" },
7403
7088
  Timepicker: { component: Timepicker, selector: ".timepicker" },
7404
7089
  Tooltip: { component: Tooltip, selector: ".tooltipped" },
7405
7090
  FloatingActionButton: {
@@ -7416,7 +7101,7 @@ function AutoInit(context = document.body, options) {
7416
7101
  }
7417
7102
 
7418
7103
  // src/ts/index.ts
7419
- var version = "0.3.0";
7104
+ var version = "0.5.0";
7420
7105
  if (typeof document !== "undefined") {
7421
7106
  document.addEventListener("keydown", Utils.docHandleKeydown, true);
7422
7107
  document.addEventListener("keyup", Utils.docHandleKeyup, true);
@@ -7429,32 +7114,33 @@ Waves.Init();
7429
7114
  Range.Init();
7430
7115
  Cards.Init();
7431
7116
  Dialogs.Init();
7117
+ BottomSheets.Init();
7118
+ SideSheets.Init();
7432
7119
  export {
7433
7120
  AutoInit,
7434
7121
  Autocomplete,
7122
+ BottomSheets,
7435
7123
  Cards,
7436
7124
  Carousel,
7437
7125
  CharacterCounter,
7438
7126
  Chips,
7439
- Collapsible,
7440
7127
  Datepicker,
7441
7128
  Dialogs,
7442
- Dropdown,
7443
7129
  FloatingActionButton,
7444
7130
  FormSelect,
7445
7131
  Forms,
7446
7132
  Lightbox,
7447
- Modal,
7133
+ Menu,
7134
+ NavigationRail,
7448
7135
  Parallax,
7449
- Pushpin,
7450
7136
  Range,
7451
7137
  ScrollSpy,
7138
+ SideSheets,
7452
7139
  Sidenav,
7453
7140
  Slider,
7141
+ Snackbar,
7454
7142
  Tabs,
7455
- TapTarget,
7456
7143
  Timepicker,
7457
- Toast,
7458
7144
  Tooltip,
7459
7145
  Waves,
7460
7146
  version