@expressivecss/expressive 0.9.1 → 0.10.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 (188) hide show
  1. package/CHANGELOG.md +157 -1
  2. package/README.md +178 -25
  3. package/dist/css/expressive.css +365 -1222
  4. package/dist/css/expressive.css.map +1 -1
  5. package/dist/css/expressive.min.css +1 -1
  6. package/dist/css/expressive.min.css.map +1 -1
  7. package/dist/fonts/material-symbols-outlined.woff2 +0 -0
  8. package/dist/fonts/material-symbols-rounded.woff2 +0 -0
  9. package/dist/fonts/material-symbols-sharp.woff2 +0 -0
  10. package/dist/js/chunks/chunk-7MM4VJJO.mjs +509 -0
  11. package/dist/js/chunks/chunk-7MM4VJJO.mjs.map +7 -0
  12. package/dist/js/chunks/chunk-7UXHSYIA.mjs +183 -0
  13. package/dist/js/chunks/chunk-7UXHSYIA.mjs.map +7 -0
  14. package/dist/js/chunks/chunk-A3BJA7V4.mjs +28 -0
  15. package/dist/js/chunks/chunk-A3BJA7V4.mjs.map +7 -0
  16. package/dist/js/chunks/chunk-AOGFNMXS.mjs +691 -0
  17. package/dist/js/chunks/chunk-AOGFNMXS.mjs.map +7 -0
  18. package/dist/js/chunks/chunk-D4UZ4HFN.mjs +560 -0
  19. package/dist/js/chunks/chunk-D4UZ4HFN.mjs.map +7 -0
  20. package/dist/js/chunks/chunk-EP3CVKB5.mjs +88 -0
  21. package/dist/js/chunks/chunk-EP3CVKB5.mjs.map +7 -0
  22. package/dist/js/chunks/chunk-G576EP5O.mjs +459 -0
  23. package/dist/js/chunks/chunk-G576EP5O.mjs.map +7 -0
  24. package/dist/js/chunks/chunk-GY2Z5CJ2.mjs +320 -0
  25. package/dist/js/chunks/chunk-GY2Z5CJ2.mjs.map +7 -0
  26. package/dist/js/chunks/chunk-JKNPQQWY.mjs +191 -0
  27. package/dist/js/chunks/chunk-JKNPQQWY.mjs.map +7 -0
  28. package/dist/js/chunks/chunk-JNPIWFJT.mjs +365 -0
  29. package/dist/js/chunks/chunk-JNPIWFJT.mjs.map +7 -0
  30. package/dist/js/chunks/chunk-JODJAGFS.mjs +267 -0
  31. package/dist/js/chunks/chunk-JODJAGFS.mjs.map +7 -0
  32. package/dist/js/chunks/chunk-JR24UB4X.mjs +100 -0
  33. package/dist/js/chunks/chunk-JR24UB4X.mjs.map +7 -0
  34. package/dist/js/chunks/chunk-KR44BSYQ.mjs +55 -0
  35. package/dist/js/chunks/chunk-KR44BSYQ.mjs.map +7 -0
  36. package/dist/js/chunks/chunk-LZVBJ33S.mjs +102 -0
  37. package/dist/js/chunks/chunk-LZVBJ33S.mjs.map +7 -0
  38. package/dist/js/chunks/chunk-MISGHDUU.mjs +326 -0
  39. package/dist/js/chunks/chunk-MISGHDUU.mjs.map +7 -0
  40. package/dist/js/chunks/chunk-N3UHGN3N.mjs +175 -0
  41. package/dist/js/chunks/chunk-N3UHGN3N.mjs.map +7 -0
  42. package/dist/js/chunks/chunk-PB5L3EXF.mjs +7 -0
  43. package/dist/js/chunks/chunk-PB5L3EXF.mjs.map +7 -0
  44. package/dist/js/chunks/chunk-PIIAEYAJ.mjs +85 -0
  45. package/dist/js/chunks/chunk-PIIAEYAJ.mjs.map +7 -0
  46. package/dist/js/chunks/chunk-QSQ24FYT.mjs +59 -0
  47. package/dist/js/chunks/chunk-QSQ24FYT.mjs.map +7 -0
  48. package/dist/js/chunks/chunk-QYCYP443.mjs +1111 -0
  49. package/dist/js/chunks/chunk-QYCYP443.mjs.map +7 -0
  50. package/dist/js/chunks/chunk-SLFNPY5M.mjs +78 -0
  51. package/dist/js/chunks/chunk-SLFNPY5M.mjs.map +7 -0
  52. package/dist/js/chunks/chunk-TK6PX2GC.mjs +663 -0
  53. package/dist/js/chunks/chunk-TK6PX2GC.mjs.map +7 -0
  54. package/dist/js/chunks/chunk-TYM6U5DY.mjs +76 -0
  55. package/dist/js/chunks/chunk-TYM6U5DY.mjs.map +7 -0
  56. package/dist/js/chunks/chunk-TZZ24ZQS.mjs +154 -0
  57. package/dist/js/chunks/chunk-TZZ24ZQS.mjs.map +7 -0
  58. package/dist/js/chunks/chunk-VAS6JQ6P.mjs +94 -0
  59. package/dist/js/chunks/chunk-VAS6JQ6P.mjs.map +7 -0
  60. package/dist/js/chunks/chunk-WWPAPHMA.mjs +1 -0
  61. package/dist/js/chunks/chunk-WWPAPHMA.mjs.map +7 -0
  62. package/dist/js/chunks/chunk-XDB3G6RL.mjs +114 -0
  63. package/dist/js/chunks/chunk-XDB3G6RL.mjs.map +7 -0
  64. package/dist/js/chunks/chunk-XO5WX3TK.mjs +226 -0
  65. package/dist/js/chunks/chunk-XO5WX3TK.mjs.map +7 -0
  66. package/dist/js/chunks/chunk-YFXEJ7GR.mjs +286 -0
  67. package/dist/js/chunks/chunk-YFXEJ7GR.mjs.map +7 -0
  68. package/dist/js/chunks/chunk-ZIZVUWRL.mjs +120 -0
  69. package/dist/js/chunks/chunk-ZIZVUWRL.mjs.map +7 -0
  70. package/dist/js/chunks/chunk-ZZBZI2XN.mjs +338 -0
  71. package/dist/js/chunks/chunk-ZZBZI2XN.mjs.map +7 -0
  72. package/dist/js/expressive.cjs +747 -754
  73. package/dist/js/expressive.cjs.map +4 -4
  74. package/dist/js/expressive.js +747 -754
  75. package/dist/js/expressive.js.map +4 -4
  76. package/dist/js/expressive.min.js +11 -15
  77. package/dist/js/expressive.min.js.map +4 -4
  78. package/dist/js/expressive.mjs +83 -7490
  79. package/dist/js/expressive.mjs.map +4 -4
  80. package/dist/js/internal/behaviors/bottomSheets.mjs +9 -0
  81. package/dist/js/internal/behaviors/bottomSheets.mjs.map +7 -0
  82. package/dist/js/internal/behaviors/dialogs.mjs +8 -0
  83. package/dist/js/internal/behaviors/dialogs.mjs.map +7 -0
  84. package/dist/js/internal/behaviors/forms.mjs +8 -0
  85. package/dist/js/internal/behaviors/forms.mjs.map +7 -0
  86. package/dist/js/internal/behaviors/sheetDrag.mjs +8 -0
  87. package/dist/js/internal/behaviors/sheetDrag.mjs.map +7 -0
  88. package/dist/js/internal/behaviors/sideSheets.mjs +9 -0
  89. package/dist/js/internal/behaviors/sideSheets.mjs.map +7 -0
  90. package/dist/js/internal/components/appBar.mjs +8 -0
  91. package/dist/js/internal/components/appBar.mjs.map +7 -0
  92. package/dist/js/internal/components/autocomplete.mjs +10 -0
  93. package/dist/js/internal/components/autocomplete.mjs.map +7 -0
  94. package/dist/js/internal/components/buttonGroup.mjs +8 -0
  95. package/dist/js/internal/components/buttonGroup.mjs.map +7 -0
  96. package/dist/js/internal/components/buttons.mjs +9 -0
  97. package/dist/js/internal/components/buttons.mjs.map +7 -0
  98. package/dist/js/internal/components/cards.mjs +13 -0
  99. package/dist/js/internal/components/cards.mjs.map +7 -0
  100. package/dist/js/internal/components/carousel.mjs +9 -0
  101. package/dist/js/internal/components/carousel.mjs.map +7 -0
  102. package/dist/js/internal/components/characterCounter.mjs +8 -0
  103. package/dist/js/internal/components/characterCounter.mjs.map +7 -0
  104. package/dist/js/internal/components/chips.mjs +11 -0
  105. package/dist/js/internal/components/chips.mjs.map +7 -0
  106. package/dist/js/internal/components/datepicker.mjs +12 -0
  107. package/dist/js/internal/components/datepicker.mjs.map +7 -0
  108. package/dist/js/internal/components/expandingCard.mjs +11 -0
  109. package/dist/js/internal/components/expandingCard.mjs.map +7 -0
  110. package/dist/js/internal/components/index.mjs +88 -0
  111. package/dist/js/internal/components/index.mjs.map +7 -0
  112. package/dist/js/internal/components/lightbox.mjs +9 -0
  113. package/dist/js/internal/components/lightbox.mjs.map +7 -0
  114. package/dist/js/internal/components/menu.mjs +9 -0
  115. package/dist/js/internal/components/menu.mjs.map +7 -0
  116. package/dist/js/internal/components/navigationRail.mjs +8 -0
  117. package/dist/js/internal/components/navigationRail.mjs.map +7 -0
  118. package/dist/js/internal/components/registry.mjs +33 -0
  119. package/dist/js/internal/components/registry.mjs.map +7 -0
  120. package/dist/js/internal/components/scrollspy.mjs +8 -0
  121. package/dist/js/internal/components/scrollspy.mjs.map +7 -0
  122. package/dist/js/internal/components/select.mjs +10 -0
  123. package/dist/js/internal/components/select.mjs.map +7 -0
  124. package/dist/js/internal/components/slider.mjs +9 -0
  125. package/dist/js/internal/components/slider.mjs.map +7 -0
  126. package/dist/js/internal/components/snackbar.mjs +8 -0
  127. package/dist/js/internal/components/snackbar.mjs.map +7 -0
  128. package/dist/js/internal/components/tabs.mjs +10 -0
  129. package/dist/js/internal/components/tabs.mjs.map +7 -0
  130. package/dist/js/internal/components/timepicker.mjs +10 -0
  131. package/dist/js/internal/components/timepicker.mjs.map +7 -0
  132. package/dist/js/internal/components/tooltip.mjs +9 -0
  133. package/dist/js/internal/components/tooltip.mjs.map +7 -0
  134. package/dist/js/internal/core/component.mjs +7 -0
  135. package/dist/js/internal/core/component.mjs.map +7 -0
  136. package/dist/js/internal/core/utils.mjs +7 -0
  137. package/dist/js/internal/core/utils.mjs.map +7 -0
  138. package/dist/js/internal/plugins/dockedDisplayPlugin.mjs +8 -0
  139. package/dist/js/internal/plugins/dockedDisplayPlugin.mjs.map +7 -0
  140. package/dist/js/modular.mjs +113 -0
  141. package/dist/js/modular.mjs.map +7 -0
  142. package/dist/types/behaviors/sheetDrag.d.ts +1 -1
  143. package/dist/types/components/appBar.d.ts +1 -1
  144. package/dist/types/components/autocomplete.d.ts +9 -0
  145. package/dist/types/components/buttons.d.ts +4 -39
  146. package/dist/types/components/carousel.d.ts +10 -1
  147. package/dist/types/components/chips.d.ts +1 -0
  148. package/dist/types/components/datepicker.d.ts +7 -0
  149. package/dist/types/components/index.d.ts +0 -2
  150. package/dist/types/components/lightbox.d.ts +12 -0
  151. package/dist/types/components/menu.d.ts +3 -0
  152. package/dist/types/components/registry.d.ts +0 -4
  153. package/dist/types/components/select.d.ts +4 -0
  154. package/dist/types/components/slider.d.ts +2 -1
  155. package/dist/types/components/snackbar.d.ts +4 -2
  156. package/dist/types/components/tabs.d.ts +2 -1
  157. package/dist/types/components/timepicker.d.ts +5 -3
  158. package/dist/types/components/tooltip.d.ts +5 -3
  159. package/dist/types/index.d.ts +1 -8
  160. package/dist/types/modular.d.ts +28 -0
  161. package/package.json +16 -4
  162. package/src/sass/README.md +8 -0
  163. package/src/sass/components/_badges.scss +1 -6
  164. package/src/sass/components/_button-groups.scss +5 -1
  165. package/src/sass/components/_buttons.scss +8 -287
  166. package/src/sass/components/_chips.scss +17 -2
  167. package/src/sass/components/_dialog.scss +9 -4
  168. package/src/sass/components/_fab-menu.scss +11 -9
  169. package/src/sass/components/_icon-buttons.scss +0 -5
  170. package/src/sass/components/_index.scss +0 -5
  171. package/src/sass/components/_menu.scss +2 -2
  172. package/src/sass/components/_navigation-rail.scss +1 -1
  173. package/src/sass/components/_panes.scss +50 -78
  174. package/src/sass/components/_progress.scss +73 -13
  175. package/src/sass/components/_scrim.scss +4 -4
  176. package/src/sass/components/_side-sheet.scss +22 -12
  177. package/src/sass/components/_toolbar.scss +1 -4
  178. package/src/sass/components/_tooltip.scss +20 -2
  179. package/src/sass/components/forms/_input-fields.scss +5 -1
  180. package/src/sass/components/forms/_select.scss +1 -0
  181. package/src/sass/components/forms/_slider.scss +81 -25
  182. package/src/sass/custom.scss +38 -0
  183. package/dist/types/components/navigationDrawer.d.ts +0 -110
  184. package/src/sass/components/_banners.scss +0 -298
  185. package/src/sass/components/_bottom-app-bar.scss +0 -180
  186. package/src/sass/components/_floating-sheet.scss +0 -86
  187. package/src/sass/components/_navigation-drawer.scss +0 -476
  188. package/src/sass/components/_segmented-buttons.scss +0 -188
@@ -37,12 +37,10 @@ var Expressive = (() => {
37
37
  Forms: () => Forms,
38
38
  Lightbox: () => Lightbox,
39
39
  Menu: () => Menu,
40
- NavigationDrawer: () => NavigationDrawer,
41
40
  NavigationRail: () => NavigationRail,
42
41
  Range: () => Slider,
43
42
  ScrollSpy: () => ScrollSpy,
44
43
  SideSheets: () => SideSheets,
45
- Sidenav: () => NavigationDrawer,
46
44
  Slider: () => Slider,
47
45
  Snackbar: () => Snackbar,
48
46
  Tabs: () => Tabs,
@@ -867,34 +865,45 @@ var Expressive = (() => {
867
865
  }
868
866
  };
869
867
  this._handleMenuKeydown = (e) => {
868
+ if (!this.isOpen || e.ctrlKey || e.metaKey || e.altKey || e.isComposing) return;
869
+ const list = this._containingSubmenu(e) ?? this.menuEl;
870
+ const rtl = getComputedStyle(list).direction === "rtl";
871
+ const openKey = rtl ? Utils.keys.ARROW_LEFT : Utils.keys.ARROW_RIGHT;
872
+ const backKey = rtl ? Utils.keys.ARROW_RIGHT : Utils.keys.ARROW_LEFT;
873
+ if ([openKey, backKey, Utils.keys.ARROW_UP, Utils.keys.ARROW_DOWN, Utils.keys.ESC, Utils.keys.TAB].includes(e.key)) {
874
+ clearTimeout(this.filterTimeout);
875
+ this._resetFilterQuery();
876
+ }
870
877
  const arrowUpOrDown = e.key === Utils.keys.ARROW_DOWN || e.key === Utils.keys.ARROW_UP;
871
878
  if (e.key === Utils.keys.TAB) {
872
879
  e.preventDefault();
873
880
  this.close();
874
- } else if (e.key === Utils.keys.ARROW_RIGHT && this.isOpen) {
881
+ } else if (e.key === openKey) {
875
882
  const li = this._focusedRow(e);
876
- if (li?.querySelector(":scope > menu")) {
883
+ if (li?.querySelector(":scope > menu") && !li.matches('.disabled, [aria-disabled="true"]')) {
877
884
  e.preventDefault();
878
885
  li.classList.add("open");
879
886
  this._alignSubmenu(li);
880
887
  this._syncSubmenuAria(li, true);
881
- const first = li.querySelector(":scope > menu > li");
882
- first?.focus();
888
+ const first = Array.from(li.querySelector(":scope > menu").children).find(
889
+ (child) => child instanceof HTMLElement && child.tabIndex !== -1
890
+ );
891
+ if (first) this._focusRow(first);
883
892
  }
884
- } else if (e.key === Utils.keys.ARROW_LEFT && this.isOpen) {
893
+ } else if (e.key === backKey) {
885
894
  const submenu = this._containingSubmenu(e);
886
895
  if (submenu) {
887
896
  e.preventDefault();
888
897
  const parentLi = submenu.parentElement;
889
898
  parentLi.classList.remove("open", "submenu-start");
890
899
  this._syncSubmenuAria(parentLi, false);
891
- parentLi.focus();
900
+ this._focusRow(parentLi);
892
901
  }
893
902
  } else if (arrowUpOrDown && this.isOpen) {
894
903
  e.preventDefault();
895
904
  const direction = e.key === Utils.keys.ARROW_DOWN ? 1 : -1;
896
- const list = this._containingSubmenu(e) ?? this.menuEl;
897
- let newFocusedIndex = this.focusedIndex;
905
+ const row = this._focusedRow(e);
906
+ let newFocusedIndex = row ? Array.from(list.children).indexOf(row) : this.focusedIndex;
898
907
  let hasFoundNewIndex = false;
899
908
  do {
900
909
  newFocusedIndex = newFocusedIndex + direction;
@@ -905,10 +914,9 @@ var Expressive = (() => {
905
914
  } while (newFocusedIndex < list.children.length && newFocusedIndex >= 0);
906
915
  if (hasFoundNewIndex) {
907
916
  if (this.focusedIndex >= 0) list.children[this.focusedIndex]?.classList.remove("active");
908
- this.focusedIndex = newFocusedIndex;
909
- const item = list.children[this.focusedIndex];
917
+ const item = list.children[newFocusedIndex];
910
918
  item.classList.add("active");
911
- item.focus({ preventScroll: true });
919
+ this._focusRow(item);
912
920
  }
913
921
  } else if ((e.key === Utils.keys.ENTER || e.key === SPACE) && this.isOpen) {
914
922
  const li = this._focusedRow(e);
@@ -919,7 +927,7 @@ var Expressive = (() => {
919
927
  return;
920
928
  }
921
929
  const focusedElement = li ?? this.menuEl.children[this.focusedIndex];
922
- const activatableElement = focusedElement?.querySelector("a, button");
930
+ const activatableElement = focusedElement?.querySelector(":scope > a, :scope > button");
923
931
  if (!!activatableElement) {
924
932
  activatableElement.click();
925
933
  } else if (focusedElement instanceof HTMLElement) {
@@ -927,39 +935,28 @@ var Expressive = (() => {
927
935
  }
928
936
  } else if (e.key === Utils.keys.ESC && this.isOpen) {
929
937
  e.preventDefault();
930
- const openSub = this.menuEl.querySelector("li.open");
938
+ const openSub = this._containingSubmenu(e)?.parentElement ?? Array.from(this.menuEl.querySelectorAll("li.open")).reverse()[0];
931
939
  if (openSub) {
932
940
  openSub.classList.remove("open", "submenu-start");
933
941
  this._syncSubmenuAria(openSub, false);
934
- openSub.focus();
942
+ this._focusRow(openSub);
935
943
  } else {
936
944
  this.close();
937
945
  }
938
946
  }
939
947
  const keyText = e.key.toLowerCase();
940
- const isLetter = /[a-zA-Z0-9-_]/.test(keyText);
941
- const specialKeys = [
942
- Utils.keys.ARROW_DOWN,
943
- Utils.keys.ARROW_UP,
944
- Utils.keys.ENTER,
945
- Utils.keys.ESC,
946
- Utils.keys.TAB,
947
- SPACE
948
- ];
949
- if (isLetter && !specialKeys.includes(e.key)) {
948
+ if (e.key.length === 1 && e.key !== SPACE && !e.ctrlKey && !e.metaKey && !e.altKey && !e.isComposing) {
950
949
  this.filterQuery.push(keyText);
951
950
  const string = this.filterQuery.join("");
952
- const list = this._containingSubmenu(e) ?? this.menuEl;
953
951
  const newOptionEl = Array.from(list.querySelectorAll(":scope > li")).find(
954
- (el) => el.textContent.trim().toLowerCase().indexOf(string) === 0
952
+ (el) => el.tabIndex !== -1 && !el.matches('.disabled, [aria-disabled="true"]') && Array.from(el.childNodes).filter((node) => !(node instanceof Element && node.matches("menu"))).map((node) => node.textContent).join("").trim().toLowerCase().startsWith(string)
955
953
  );
956
954
  if (newOptionEl) {
957
- this.focusedIndex = [...newOptionEl.parentNode.children].indexOf(newOptionEl);
958
- this._focusFocusedItem();
955
+ this._focusRow(newOptionEl);
959
956
  }
957
+ clearTimeout(this.filterTimeout);
958
+ this.filterTimeout = setTimeout(this._resetFilterQuery, 1e3);
960
959
  }
961
- clearTimeout(this.filterTimeout);
962
- this.filterTimeout = setTimeout(this._resetFilterQuery, 1e3);
963
960
  };
964
961
  this._handleWindowResize = () => {
965
962
  if (this.el.offsetParent) {
@@ -982,6 +979,7 @@ var Expressive = (() => {
982
979
  if (transition !== this._transition) return;
983
980
  this._resetMenuStyles();
984
981
  this.menuEl.style.display = "block";
982
+ this._restoreInert();
985
983
  this._placeMenu();
986
984
  this._schedule(() => this._setupTemporaryEventHandlers(), 0);
987
985
  this._animateIn();
@@ -993,7 +991,12 @@ var Expressive = (() => {
993
991
  this.close = () => {
994
992
  if (!this.isOpen) return;
995
993
  const transition = this._clearPendingTimers();
994
+ const root = this.menuEl.getRootNode();
995
+ const focused = root.activeElement;
996
996
  this.isOpen = false;
997
+ this.menuEl.setAttribute("inert", "");
998
+ clearTimeout(this.filterTimeout);
999
+ this._resetFilterQuery();
997
1000
  this.focusedIndex = -1;
998
1001
  this._closeSubmenus();
999
1002
  if (typeof this.options.onCloseStart === "function") {
@@ -1003,7 +1006,7 @@ var Expressive = (() => {
1003
1006
  this._animateOut();
1004
1007
  this._removeTemporaryEventHandlers();
1005
1008
  this.el.ariaExpanded = "false";
1006
- if (this.options.autoFocus) {
1009
+ if (this.options.autoFocus && (root.activeElement === focused || !root.activeElement || root.activeElement === document.body)) {
1007
1010
  this.el.focus();
1008
1011
  }
1009
1012
  };
@@ -1020,6 +1023,7 @@ var Expressive = (() => {
1020
1023
  _Menu._menus.push(this);
1021
1024
  this.id = Utils.getIdFromTrigger(el);
1022
1025
  this.menuEl = Utils.getElementById(el, this.id);
1026
+ this._originalInert = this.menuEl?.getAttribute("inert") ?? null;
1023
1027
  this.options = {
1024
1028
  ..._Menu.defaults,
1025
1029
  ...options
@@ -1059,6 +1063,7 @@ var Expressive = (() => {
1059
1063
  this.el.ariaExpanded = "false";
1060
1064
  clearTimeout(this.filterTimeout);
1061
1065
  this._resetMenuStyles();
1066
+ this._restoreInert();
1062
1067
  this._removeEventHandlers();
1063
1068
  this._removeTemporaryEventHandlers();
1064
1069
  const index = _Menu._menus.indexOf(this);
@@ -1169,7 +1174,7 @@ var Expressive = (() => {
1169
1174
  if (!menu) return;
1170
1175
  li.classList.remove("submenu-start");
1171
1176
  const rect = menu.getBoundingClientRect();
1172
- if (rect.right > window.innerWidth - 8) li.classList.add("submenu-start");
1177
+ if (rect.left < 8 || rect.right > window.innerWidth - 8) li.classList.add("submenu-start");
1173
1178
  }
1174
1179
  _closeSubmenus() {
1175
1180
  this.menuEl?.querySelectorAll("li.open").forEach((li) => {
@@ -1191,6 +1196,15 @@ var Expressive = (() => {
1191
1196
  if (li instanceof HTMLElement && this.menuEl?.contains(li)) return li;
1192
1197
  return null;
1193
1198
  }
1199
+ _restoreInert() {
1200
+ if (this._originalInert === null) this.menuEl?.removeAttribute("inert");
1201
+ else this.menuEl?.setAttribute("inert", this._originalInert);
1202
+ }
1203
+ _focusRow(item) {
1204
+ this.focusedIndex = Array.from(item.parentElement.children).indexOf(item);
1205
+ item.focus({ preventScroll: true });
1206
+ item.scrollIntoView({ block: "nearest", inline: "nearest" });
1207
+ }
1194
1208
  _resetMenuStyles() {
1195
1209
  this.menuEl.style.display = "";
1196
1210
  this._resetMenuPositioningStyles();
@@ -1399,6 +1413,12 @@ var Expressive = (() => {
1399
1413
 
1400
1414
  // src/ts/components/autocomplete.ts
1401
1415
  var _defaults4 = {
1416
+ i18n: {
1417
+ loading: "Loading results.",
1418
+ noResults: "No results.",
1419
+ results: (count) => `${count} ${count === 1 ? "result" : "results"} available.`,
1420
+ selected: (count) => `${count} ${count === 1 ? "item" : "items"} selected.`
1421
+ },
1402
1422
  data: [],
1403
1423
  // Autocomplete data set
1404
1424
  onAutocomplete: null,
@@ -1414,11 +1434,13 @@ var Expressive = (() => {
1414
1434
  isMultiSelect: false,
1415
1435
  onSearch: (text, autocomplete) => {
1416
1436
  const normSearch = text.toLocaleLowerCase();
1437
+ const data = autocomplete.options.data;
1417
1438
  autocomplete.setMenuItems(
1418
- autocomplete.options.data.filter(
1439
+ data.filter(
1419
1440
  (option) => option.id.toString().toLocaleLowerCase().includes(normSearch) || option.text?.toLocaleLowerCase().includes(normSearch)
1420
1441
  )
1421
1442
  );
1443
+ autocomplete.options.data = data;
1422
1444
  },
1423
1445
  maxMenuHeight: "300px",
1424
1446
  allowUnsafeHTML: false,
@@ -1437,7 +1459,7 @@ var Expressive = (() => {
1437
1459
  if (e.type === "keyup") _Autocomplete._keydown = false;
1438
1460
  this.count = 0;
1439
1461
  const actualValue = this.el.value.toLocaleLowerCase();
1440
- if (e.key === Utils.keys.ENTER || e.key === Utils.keys.ARROW_UP || e.key === Utils.keys.ARROW_DOWN)
1462
+ if (e.key === Utils.keys.ENTER || e.key === Utils.keys.ARROW_UP || e.key === Utils.keys.ARROW_DOWN || e.key === Utils.keys.ESC || e.key === "Tab")
1441
1463
  return;
1442
1464
  if (this.oldVal !== actualValue && this.el.matches(":focus-visible")) {
1443
1465
  this.open();
@@ -1450,6 +1472,9 @@ var Expressive = (() => {
1450
1472
  this._inputChangeDetection(actualValue);
1451
1473
  };
1452
1474
  this._inputChangeDetection = (value) => {
1475
+ if (!this.options.isMultiSelect && this.selectedValues.length && this.el.value !== (this.selectedValues[0].text || String(this.selectedValues[0].id))) {
1476
+ this.selectedValues = [];
1477
+ }
1453
1478
  if (this.oldVal !== value) {
1454
1479
  this._setStatusLoading();
1455
1480
  this.options.onSearch(this.el.value, this);
@@ -1462,6 +1487,11 @@ var Expressive = (() => {
1462
1487
  };
1463
1488
  this._handleInputKeydown = (e) => {
1464
1489
  _Autocomplete._keydown = true;
1490
+ if (e.key === Utils.keys.ESC || e.key === "Tab") {
1491
+ this.close();
1492
+ this._resetCurrentElementPosition();
1493
+ return;
1494
+ }
1465
1495
  const numItems = this.container.querySelectorAll("li").length;
1466
1496
  if (e.key === Utils.keys.ENTER && this.activeIndex >= 0) {
1467
1497
  const liElement = this.container.querySelectorAll("li")[this.activeIndex];
@@ -1511,6 +1541,7 @@ var Expressive = (() => {
1511
1541
  if (inputText.length >= this.options.minLength) {
1512
1542
  this.isOpen = true;
1513
1543
  this._renderMenu();
1544
+ this._announce(this.menuItems.length ? this.options.i18n.results(this.menuItems.length) : this.options.i18n.noResults);
1514
1545
  }
1515
1546
  if (!this.menu.isOpen) {
1516
1547
  if (this._openTimer !== void 0) clearTimeout(this._openTimer);
@@ -1524,18 +1555,25 @@ var Expressive = (() => {
1524
1555
  * Hide autocomplete.
1525
1556
  */
1526
1557
  this.close = () => {
1558
+ if (this._announcer) this._announcer.textContent = "";
1559
+ if (this._openTimer !== void 0) {
1560
+ clearTimeout(this._openTimer);
1561
+ this._openTimer = void 0;
1562
+ }
1563
+ this.isOpen = false;
1527
1564
  this.menu.close();
1528
1565
  };
1529
1566
  this.el["Expressive_Autocomplete"] = this;
1530
1567
  this.options = {
1531
1568
  ..._Autocomplete.defaults,
1532
- ...options
1569
+ ...options,
1570
+ i18n: { ..._Autocomplete.defaults.i18n, ...options?.i18n }
1533
1571
  };
1534
1572
  this.isOpen = false;
1535
1573
  this.count = 0;
1536
1574
  this.activeIndex = -1;
1537
1575
  this.oldVal = "";
1538
- this.selectedValues = this.selectedValues || this.options.selected.map((value) => ({ id: value })) || [];
1576
+ this.selectedValues = this.options.selected.map((id) => this.options.data.find((entry) => entry.id === id) ?? { id });
1539
1577
  this.menuItems = this.options.data || [];
1540
1578
  this.$active = null;
1541
1579
  this._pointerDown = false;
@@ -1594,7 +1632,10 @@ var Expressive = (() => {
1594
1632
  this._openTimer = void 0;
1595
1633
  }
1596
1634
  this._removeEventHandlers();
1635
+ this.menu.destroy();
1597
1636
  this._removeMenu();
1637
+ this._statusInfo.remove();
1638
+ this._announcer.remove();
1598
1639
  this.el["Expressive_Autocomplete"] = void 0;
1599
1640
  }
1600
1641
  _setupEventHandlers() {
@@ -1645,6 +1686,7 @@ var Expressive = (() => {
1645
1686
  if (typeof userOnItemClick === "function") userOnItemClick.call(this.menu, li);
1646
1687
  };
1647
1688
  this.menu = Menu.init(this.el, menuOptions);
1689
+ this.el.setAttribute("aria-haspopup", "listbox");
1648
1690
  const label = this.el.parentElement.querySelector("label");
1649
1691
  if (label) this.el.after(label);
1650
1692
  this.el.removeEventListener("click", this.menu._handleClick);
@@ -1655,8 +1697,16 @@ var Expressive = (() => {
1655
1697
  if (this.el.value) this.selectOption(this.el.value);
1656
1698
  const div = document.createElement("div");
1657
1699
  div.classList.add("status-info");
1700
+ div.setAttribute("aria-hidden", "true");
1658
1701
  div.setAttribute("style", "position: absolute;right:0;top:0;");
1659
1702
  this.el.parentElement.appendChild(div);
1703
+ this._statusInfo = div;
1704
+ this._announcer = document.createElement("div");
1705
+ this._announcer.classList.add("autocomplete-status");
1706
+ this._announcer.setAttribute("role", "status");
1707
+ this._announcer.setAttribute("aria-live", "polite");
1708
+ this._announcer.setAttribute("aria-atomic", "true");
1709
+ this.el.parentElement.appendChild(this._announcer);
1660
1710
  this._updateSelectedInfo();
1661
1711
  }
1662
1712
  _removeMenu() {
@@ -1693,13 +1743,14 @@ var Expressive = (() => {
1693
1743
  "style",
1694
1744
  "display:grid; grid-auto-flow: column; user-select: none; align-items: center;"
1695
1745
  );
1696
- item.tabIndex = 0;
1746
+ item.tabIndex = -1;
1697
1747
  if (this.options.isMultiSelect) {
1698
1748
  const selection = document.createElement("div");
1699
1749
  selection.classList.add("item-selection");
1700
1750
  selection.style.textAlign = "center";
1701
1751
  const checkbox = document.createElement("input");
1702
1752
  checkbox.type = "checkbox";
1753
+ checkbox.tabIndex = -1;
1703
1754
  checkbox.checked = isSelected;
1704
1755
  const spacer = document.createElement("span");
1705
1756
  spacer.style.paddingLeft = "21px";
@@ -1764,11 +1815,17 @@ var Expressive = (() => {
1764
1815
  }
1765
1816
  }
1766
1817
  _setStatusLoading() {
1767
- const statusElement = this.el.parentElement.querySelector(".status-info");
1818
+ const statusElement = this._statusInfo;
1768
1819
  if (statusElement) statusElement.replaceChildren(_Autocomplete._loadingIndicator());
1820
+ this._announce(this.options.i18n.loading);
1821
+ }
1822
+ _announce(message) {
1823
+ if (!this._announcer) return;
1824
+ const text = this.el.matches(":focus") && this.el.value.length >= this.options.minLength ? message : "";
1825
+ if (this._announcer.textContent !== text) this._announcer.textContent = text;
1769
1826
  }
1770
1827
  _updateSelectedInfo() {
1771
- const statusElement = this.el.parentElement.querySelector(".status-info");
1828
+ const statusElement = this._statusInfo;
1772
1829
  if (statusElement) {
1773
1830
  if (this.options.isMultiSelect)
1774
1831
  statusElement.textContent = this.selectedValues.length.toString();
@@ -1793,6 +1850,8 @@ var Expressive = (() => {
1793
1850
  * @param open Option to conditionally open menu
1794
1851
  */
1795
1852
  setMenuItems(menuItems, selected = null, open = true) {
1853
+ if (this.el["Expressive_Autocomplete"] !== this) return;
1854
+ const resultCount = menuItems.length;
1796
1855
  this.menuItems = menuItems;
1797
1856
  this.options.data = menuItems;
1798
1857
  if (selected) {
@@ -1807,6 +1866,7 @@ var Expressive = (() => {
1807
1866
  }
1808
1867
  if (open) this.open();
1809
1868
  this._updateSelectedInfo();
1869
+ this._announce(open ? resultCount ? this.options.i18n.results(resultCount) : this.options.i18n.noResults : "");
1810
1870
  this._triggerChanged();
1811
1871
  }
1812
1872
  /**
@@ -1832,6 +1892,7 @@ var Expressive = (() => {
1832
1892
  this.close();
1833
1893
  }
1834
1894
  this._updateSelectedInfo();
1895
+ if (this.options.isMultiSelect) this._announce(this.options.i18n.selected(this.selectedValues.length));
1835
1896
  this._triggerChanged();
1836
1897
  }
1837
1898
  };
@@ -1870,8 +1931,7 @@ var Expressive = (() => {
1870
1931
  const index = this._chips.indexOf(_chip);
1871
1932
  if (index < 0) return;
1872
1933
  if (clickedClose) {
1873
- this.deleteChip(index);
1874
- this._input.focus();
1934
+ this._deleteChip(index, "input");
1875
1935
  } else {
1876
1936
  this.selectChip(index);
1877
1937
  }
@@ -1963,8 +2023,8 @@ var Expressive = (() => {
1963
2023
  }
1964
2024
  _setupEventHandlers() {
1965
2025
  this.el.addEventListener("click", this._handleChipClick);
1966
- document.addEventListener("keydown", _Chips._handleChipsKeydown);
1967
- document.addEventListener("keyup", _Chips._handleChipsKeyup);
2026
+ this.el.addEventListener("keydown", _Chips._handleChipsKeydown);
2027
+ this.el.addEventListener("keyup", _Chips._handleChipsKeyup);
1968
2028
  this.el.addEventListener("blur", _Chips._handleChipsBlur, true);
1969
2029
  this._input.addEventListener("focus", this._handleInputFocus);
1970
2030
  this._input.addEventListener("blur", this._handleInputBlur);
@@ -1972,8 +2032,8 @@ var Expressive = (() => {
1972
2032
  }
1973
2033
  _removeEventHandlers() {
1974
2034
  this.el.removeEventListener("click", this._handleChipClick);
1975
- document.removeEventListener("keydown", _Chips._handleChipsKeydown);
1976
- document.removeEventListener("keyup", _Chips._handleChipsKeyup);
2035
+ this.el.removeEventListener("keydown", _Chips._handleChipsKeydown);
2036
+ this.el.removeEventListener("keyup", _Chips._handleChipsKeyup);
1977
2037
  this.el.removeEventListener("blur", _Chips._handleChipsBlur, true);
1978
2038
  this._input.removeEventListener("focus", this._handleInputFocus);
1979
2039
  this._input.removeEventListener("blur", this._handleInputBlur);
@@ -1988,16 +2048,10 @@ var Expressive = (() => {
1988
2048
  const currChips = chips["Expressive_Chips"];
1989
2049
  if (!currChips) return;
1990
2050
  if (e.key === Utils.keys.BACKSPACE || e.key === Utils.keys.DELETE) {
2051
+ const index = currChips._chips.indexOf(e.target.closest(".chip"));
2052
+ if (index < 0) return;
1991
2053
  e.preventDefault();
1992
- let selectIndex = currChips.chipsData.length;
1993
- if (currChips._selectedChip) {
1994
- const index = currChips._chips.indexOf(currChips._selectedChip);
1995
- currChips.deleteChip(index);
1996
- currChips._setSelected(null);
1997
- selectIndex = Math.max(index - 1, 0);
1998
- }
1999
- if (currChips.chipsData.length) currChips.selectChip(selectIndex);
2000
- else currChips._input.focus();
2054
+ currChips._deleteChip(index, "previous");
2001
2055
  } else if (e.key === Utils.keys.ARROW_LEFT) {
2002
2056
  if (currChips._selectedChip) {
2003
2057
  const selectIndex = currChips._chips.indexOf(currChips._selectedChip) - 1;
@@ -2118,6 +2172,10 @@ var Expressive = (() => {
2118
2172
  * @param chipIndex Index of chip
2119
2173
  */
2120
2174
  deleteChip(chipIndex) {
2175
+ const root = this.el.getRootNode();
2176
+ this._deleteChip(chipIndex, this._chips[chipIndex]?.contains(root.activeElement) ? "input" : null);
2177
+ }
2178
+ _deleteChip(chipIndex, focus) {
2121
2179
  const chip = this._chips[chipIndex];
2122
2180
  if (!chip) return;
2123
2181
  if (this._selectedChip === chip) this._setSelected(null);
@@ -2125,9 +2183,21 @@ var Expressive = (() => {
2125
2183
  this._chips.splice(chipIndex, 1);
2126
2184
  this.chipsData.splice(chipIndex, 1);
2127
2185
  this._setPlaceholder();
2186
+ const focusPath = [];
2187
+ let root = this.el.getRootNode();
2188
+ while (root) {
2189
+ focusPath.push({ root, element: root.activeElement });
2190
+ root = "host" in root ? root.host.getRootNode() : null;
2191
+ }
2128
2192
  if (typeof this.options.onChipDelete === "function") {
2129
2193
  this.options.onChipDelete(this.el, chip);
2130
2194
  }
2195
+ if (!focus || focusPath.some(({ root: root2, element }) => root2.activeElement !== element) || this.el["Expressive_Chips"] !== this) return;
2196
+ if (focus === "previous" && this._chips.length) this.selectChip(Math.max(chipIndex - 1, 0));
2197
+ else {
2198
+ this._setSelected(null);
2199
+ this._input?.focus();
2200
+ }
2131
2201
  }
2132
2202
  /**
2133
2203
  * Select nth chip.
@@ -2165,6 +2235,7 @@ var Expressive = (() => {
2165
2235
  var Slider = class _Slider extends Component {
2166
2236
  constructor(el, options) {
2167
2237
  super(el, options, _Slider);
2238
+ this._resizeObserver = null;
2168
2239
  this._handleRangeChange = () => {
2169
2240
  this._clampDual();
2170
2241
  this._sync();
@@ -2179,8 +2250,8 @@ var Expressive = (() => {
2179
2250
  this._sync();
2180
2251
  this.thumb.classList.add("active");
2181
2252
  };
2182
- this._handleRangeInputOrMove = () => {
2183
- if (this._pointerDown) {
2253
+ this._handleRangeInputOrMove = (e) => {
2254
+ if (e.type === "input" || this._pointerDown) {
2184
2255
  this._clampDual();
2185
2256
  this._sync();
2186
2257
  this.thumb.classList.add("active");
@@ -2205,6 +2276,10 @@ var Expressive = (() => {
2205
2276
  this._setupThumb();
2206
2277
  this._sync();
2207
2278
  this._setupEventHandlers();
2279
+ if (typeof ResizeObserver !== "undefined") {
2280
+ this._resizeObserver = new ResizeObserver(() => this._sync());
2281
+ this._resizeObserver.observe(this.el);
2282
+ }
2208
2283
  }
2209
2284
  static get defaults() {
2210
2285
  return _defaults6;
@@ -2221,6 +2296,7 @@ var Expressive = (() => {
2221
2296
  return el["Expressive_Slider"];
2222
2297
  }
2223
2298
  destroy() {
2299
+ this._resizeObserver?.disconnect();
2224
2300
  this._removeEventHandlers();
2225
2301
  this._removeThumb();
2226
2302
  this.el["Expressive_Slider"] = void 0;
@@ -2275,7 +2351,8 @@ var Expressive = (() => {
2275
2351
  if (this.el === end && +end.value < +start.value) end.value = start.value;
2276
2352
  }
2277
2353
  _fraction(el) {
2278
- const max = parseFloat(el.max) || 100;
2354
+ const parsedMax = parseFloat(el.max);
2355
+ const max = Number.isNaN(parsedMax) ? 100 : parsedMax;
2279
2356
  const min = parseFloat(el.min) || 0;
2280
2357
  const val = parseFloat(el.value) || 0;
2281
2358
  return max === min ? 0 : (val - min) / (max - min);
@@ -2287,16 +2364,26 @@ var Expressive = (() => {
2287
2364
  const top = this.el.offsetTop;
2288
2365
  const width = this.el.offsetWidth;
2289
2366
  const height = this.el.offsetHeight;
2367
+ const style = getComputedStyle(this.el);
2368
+ const rtl = style.direction === "rtl";
2369
+ const vertical = this._isVertical();
2370
+ const handle = parseFloat(style.getPropertyValue("--md-comp-slider-handle-width")) || 4;
2371
+ const length = vertical ? height : width;
2372
+ const position = handle / 2 + percent * (length - handle);
2290
2373
  this.el.style.setProperty("--md-comp-slider-active-fraction", fraction);
2374
+ this.el.style.setProperty("--md-comp-slider-active-position", `${position}px`);
2291
2375
  const host = this._host();
2292
2376
  const peers = host?.querySelectorAll('input[type="range"]');
2293
2377
  if (host && peers && peers.length === 2) {
2294
2378
  const nums = Array.from(peers).map((el) => this._fraction(el));
2295
2379
  host.style.setProperty("--md-comp-slider-start-fraction", `${Math.min(...nums) * 100}%`);
2296
2380
  host.style.setProperty("--md-comp-slider-end-fraction", `${Math.max(...nums) * 100}%`);
2381
+ host.style.setProperty("--md-comp-slider-start-position", `${handle / 2 + Math.min(...nums) * (length - handle)}px`);
2382
+ host.style.setProperty("--md-comp-slider-end-position", `${handle / 2 + Math.max(...nums) * (length - handle)}px`);
2297
2383
  }
2298
2384
  if (host?.classList.contains("stops")) {
2299
- const max = parseFloat(this.el.max) || 100;
2385
+ const parsedMax = parseFloat(this.el.max);
2386
+ const max = Number.isNaN(parsedMax) ? 100 : parsedMax;
2300
2387
  const min = parseFloat(this.el.min) || 0;
2301
2388
  const step = parseFloat(this.el.step);
2302
2389
  if (step > 0 && Number.isFinite(step)) {
@@ -2305,11 +2392,11 @@ var Expressive = (() => {
2305
2392
  }
2306
2393
  }
2307
2394
  this.value.textContent = this.el.value;
2308
- if (this._isVertical()) {
2395
+ if (vertical) {
2309
2396
  this.thumb.style.left = `${left + width / 2}px`;
2310
- this.thumb.style.top = `${top + (1 - percent) * height}px`;
2397
+ this.thumb.style.top = `${top + height - position}px`;
2311
2398
  } else {
2312
- this.thumb.style.left = `${left + percent * width}px`;
2399
+ this.thumb.style.left = `${left + (rtl ? width - position : position)}px`;
2313
2400
  this.thumb.style.top = `${top}px`;
2314
2401
  }
2315
2402
  }
@@ -2397,6 +2484,7 @@ var Expressive = (() => {
2397
2484
  var DRAG_SLOP = 4;
2398
2485
  function installSheetDrag(config) {
2399
2486
  const coord = (event) => config.axis === "x" ? event.clientX : event.clientY;
2487
+ let observer;
2400
2488
  let drag = null;
2401
2489
  const sheetFor = (target) => {
2402
2490
  if (!(target instanceof Element)) return null;
@@ -2408,9 +2496,10 @@ var Expressive = (() => {
2408
2496
  dialog.style.setProperty(config.property, "0px");
2409
2497
  };
2410
2498
  const onDown = (event) => {
2411
- config.onPress?.();
2412
2499
  if (!event.isPrimary) return;
2413
2500
  if (event.pointerType === "mouse" && event.button !== 0) return;
2501
+ onCancel();
2502
+ config.onPress?.();
2414
2503
  const dialog = sheetFor(event.target);
2415
2504
  if (!dialog?.open) {
2416
2505
  drag = null;
@@ -2430,9 +2519,14 @@ var Expressive = (() => {
2430
2519
  lastT: Date.now(),
2431
2520
  shift: 0
2432
2521
  };
2522
+ observer.observe(document, { childList: true, subtree: true, attributes: true, attributeFilter: ["open"], attributeOldValue: true });
2433
2523
  };
2434
2524
  const onMove = (event) => {
2435
2525
  if (!drag || drag.pointerId !== event.pointerId) return;
2526
+ if (!drag.dialog.open || !drag.dialog.isConnected) {
2527
+ onCancel();
2528
+ return;
2529
+ }
2436
2530
  const delta = coord(event) - drag.start;
2437
2531
  drag.shift = drag.direction === 1 ? Math.max(0, delta) : Math.min(0, delta);
2438
2532
  drag.last = coord(event);
@@ -2442,7 +2536,12 @@ var Expressive = (() => {
2442
2536
  const onUp = (event) => {
2443
2537
  const held = drag;
2444
2538
  if (!held || held.pointerId !== event.pointerId) return;
2539
+ if (!held.dialog.open || !held.dialog.isConnected) {
2540
+ onCancel();
2541
+ return;
2542
+ }
2445
2543
  drag = null;
2544
+ observer.disconnect();
2446
2545
  held.dialog.style.transition = "";
2447
2546
  const dt = Math.max(1, Date.now() - held.lastT);
2448
2547
  const velocity = (coord(event) - held.last) / dt;
@@ -2451,13 +2550,21 @@ var Expressive = (() => {
2451
2550
  if (dismiss && held.dialog.open) held.dialog.close();
2452
2551
  held.dialog.style.setProperty(config.property, "0px");
2453
2552
  };
2454
- const onCancel = () => {
2553
+ const onCancel = (event) => {
2554
+ if (event && drag?.pointerId !== event.pointerId) return;
2555
+ observer.disconnect();
2455
2556
  config.onRelease?.(false);
2456
2557
  const held = drag;
2457
2558
  drag = null;
2458
2559
  if (held) reset(held.dialog);
2459
2560
  };
2460
2561
  Utils.onDocumentReady(() => {
2562
+ observer = new MutationObserver((records) => {
2563
+ const dialog = drag?.dialog;
2564
+ if (dialog && (!dialog.open || !dialog.isConnected || records.some(
2565
+ (record) => record.type === "attributes" && record.target === dialog && record.oldValue === null || [...record.removedNodes].some((node) => node.contains(dialog))
2566
+ ))) onCancel();
2567
+ });
2461
2568
  document.addEventListener("pointerdown", onDown, { passive: true });
2462
2569
  document.addEventListener("pointermove", onMove, { passive: true });
2463
2570
  document.addEventListener("pointerup", onUp, { passive: true });
@@ -2511,20 +2618,19 @@ var Expressive = (() => {
2511
2618
 
2512
2619
  // src/ts/behaviors/sideSheets.ts
2513
2620
  var startDocked = (dialog) => dialog.classList.contains("left") || dialog.classList.contains("left-sheet") || dialog.classList.contains("start");
2621
+ var leftDocked = (dialog) => startDocked(dialog) !== (getComputedStyle(dialog).direction === "rtl");
2514
2622
  var SideSheets = class {
2515
2623
  static Init() {
2516
2624
  installSheetDrag({
2517
2625
  selector: "dialog.side-sheet, dialog.right, dialog.right-sheet, dialog.left, dialog.left-sheet",
2518
2626
  axis: "x",
2519
2627
  property: "--md-comp-side-sheet-shift",
2520
- // A start-docked sheet leaves toward the start edge, so its dismissal
2521
- // direction is the negative one.
2522
- direction: (dialog) => startDocked(dialog) ? -1 : 1,
2628
+ direction: (dialog) => leftDocked(dialog) ? -1 : 1,
2523
2629
  inHandle: (event, dialog) => {
2524
2630
  const target = event.target;
2525
2631
  if (target instanceof Element && target.closest("header")) return true;
2526
2632
  const rect = dialog.getBoundingClientRect();
2527
- return startDocked(dialog) ? event.clientX >= rect.right - 24 && event.clientX <= rect.right : event.clientX >= rect.left && event.clientX <= rect.left + 24;
2633
+ return leftDocked(dialog) ? event.clientX >= rect.right - 24 && event.clientX <= rect.right : event.clientX >= rect.left && event.clientX <= rect.left + 24;
2528
2634
  }
2529
2635
  });
2530
2636
  }
@@ -2631,22 +2737,19 @@ var Expressive = (() => {
2631
2737
  this._sentinel = null;
2632
2738
  this._input = null;
2633
2739
  this._view = null;
2634
- this._suppressSearchOpen = false;
2740
+ this._searchOpen = false;
2635
2741
  this._onIntersect = (entries) => {
2636
2742
  const entry = entries[0];
2637
2743
  if (!entry) return;
2638
2744
  this.el.classList.toggle("collapsed", !entry.isIntersecting);
2639
2745
  };
2640
2746
  this._onSearchViewClose = () => {
2641
- this._suppressSearchOpen = true;
2747
+ this._searchOpen = this._view.open;
2642
2748
  };
2643
2749
  this._onSearchFocus = () => {
2644
- if (!this._view) return;
2645
- if (this._suppressSearchOpen) {
2646
- this._suppressSearchOpen = false;
2647
- return;
2648
- }
2750
+ if (!this._view || this._searchOpen) return;
2649
2751
  if (this._view instanceof HTMLDialogElement) {
2752
+ this._searchOpen = true;
2650
2753
  if (!this._view.open) this._view.showModal();
2651
2754
  return;
2652
2755
  }
@@ -2673,6 +2776,7 @@ var Expressive = (() => {
2673
2776
  this._sentinel?.remove();
2674
2777
  this._sentinel = null;
2675
2778
  this._input?.removeEventListener("focus", this._onSearchFocus);
2779
+ this._input?.removeEventListener("click", this._onSearchFocus);
2676
2780
  this._view?.removeEventListener("close", this._onSearchViewClose);
2677
2781
  this.el.classList.remove("collapsed");
2678
2782
  this.el["Expressive_AppBar"] = void 0;
@@ -2697,6 +2801,7 @@ var Expressive = (() => {
2697
2801
  if (!this._view) this._view = this.el.querySelector(".search-view");
2698
2802
  if (!this._view) return;
2699
2803
  this._input.addEventListener("focus", this._onSearchFocus);
2804
+ this._input.addEventListener("click", this._onSearchFocus);
2700
2805
  if (this._view instanceof HTMLDialogElement) {
2701
2806
  this._view.addEventListener("close", this._onSearchViewClose);
2702
2807
  }
@@ -2704,30 +2809,24 @@ var Expressive = (() => {
2704
2809
  };
2705
2810
 
2706
2811
  // src/ts/components/buttons.ts
2707
- var _defaults8 = {
2708
- direction: "top",
2709
- hoverEnabled: true,
2710
- toolbarEnabled: false
2711
- };
2812
+ var _defaults8 = {};
2712
2813
  var FloatingActionButton = class _FloatingActionButton extends Component {
2713
2814
  constructor(el, options) {
2714
2815
  super(el, options, _FloatingActionButton);
2715
- /**
2716
- * Open FAB.
2717
- */
2718
2816
  this.open = () => {
2719
2817
  if (this.isOpen) return;
2720
2818
  this.isOpen = true;
2819
+ if (this._list) this._list.inert = false;
2721
2820
  this.el.classList.add("active");
2722
2821
  this._anchor?.setAttribute("aria-expanded", "true");
2723
2822
  document.addEventListener("click", this._handleDocumentClick);
2724
2823
  document.addEventListener("keydown", this._handleDocumentKeydown);
2725
2824
  };
2726
- /**
2727
- * Close FAB.
2728
- */
2729
2825
  this.close = () => {
2730
2826
  if (!this.isOpen) return;
2827
+ const focused = this.el.getRootNode().activeElement;
2828
+ if (focused && this._list?.contains(focused)) this._anchor?.focus();
2829
+ if (this._list) this._list.inert = true;
2731
2830
  this.isOpen = false;
2732
2831
  this.el.classList.remove("active");
2733
2832
  this._anchor?.setAttribute("aria-expanded", "false");
@@ -2735,8 +2834,12 @@ var Expressive = (() => {
2735
2834
  document.removeEventListener("keydown", this._handleDocumentKeydown);
2736
2835
  };
2737
2836
  this._handleFABClick = (e) => {
2738
- e.stopPropagation();
2739
- this._toggle();
2837
+ const target = e.target;
2838
+ if (!(target instanceof Element)) return;
2839
+ const control = target.closest("a, button");
2840
+ if (!control || control.matches(':disabled, [aria-disabled="true"]')) return;
2841
+ if (control === this._anchor) this._toggle();
2842
+ else if (this._list?.contains(control)) this.close();
2740
2843
  };
2741
2844
  this._handleAnchorKeydown = (e) => {
2742
2845
  if (e.key === Utils.keys.ENTER || e.key === " ") {
@@ -2745,36 +2848,26 @@ var Expressive = (() => {
2745
2848
  }
2746
2849
  };
2747
2850
  this._handleDocumentKeydown = (e) => {
2748
- if (e.key === Utils.keys.ESC) this.close();
2851
+ if (e.key === Utils.keys.ESC && !e.defaultPrevented) this.close();
2749
2852
  };
2750
2853
  this._handleDocumentClick = (e) => {
2751
- const target = e.target;
2752
- if (!(target instanceof Node) || this.el.contains(target)) return;
2854
+ if (e.composedPath().includes(this.el)) return;
2753
2855
  this.close();
2754
2856
  };
2755
2857
  this.el["Expressive_FloatingActionButton"] = this;
2756
- const fromMarkup = this._directionFromClass();
2757
- const toolbar = el.classList.contains("toolbar") || options.toolbarEnabled === true;
2758
- const clickToToggle = el.classList.contains("click-to-toggle") || options.hoverEnabled === false;
2759
- this.options = {
2760
- ..._FloatingActionButton.defaults,
2761
- ...options,
2762
- direction: options.direction ?? fromMarkup ?? _FloatingActionButton.defaults.direction,
2763
- hoverEnabled: !clickToToggle,
2764
- toolbarEnabled: toolbar
2765
- };
2858
+ this.options = { ..._FloatingActionButton.defaults, ...options };
2766
2859
  this.isOpen = false;
2767
2860
  this._anchor = this.el.querySelector(":scope > a, :scope > button");
2768
- this.el.classList.add(`direction-${this.options.direction}`);
2769
- if (clickToToggle) this.el.classList.add("click-to-toggle");
2770
- if (toolbar) this.el.classList.add("toolbar");
2771
- if (this._anchor) {
2772
- this._anchor.setAttribute("aria-expanded", "false");
2773
- if (this._anchor instanceof HTMLAnchorElement && !this._anchor.hasAttribute("href") && this._anchor.tabIndex < 0) {
2774
- this._anchor.tabIndex = 0;
2775
- }
2861
+ this._list = this.el.querySelector(":scope > ul, :scope > menu");
2862
+ this._listWasInert = this._list?.inert ?? false;
2863
+ this.el.classList.remove("active");
2864
+ if (this._list) this._list.inert = true;
2865
+ this._anchor?.setAttribute("aria-expanded", "false");
2866
+ if (this._anchor instanceof HTMLAnchorElement && !this._anchor.hasAttribute("href")) {
2867
+ if (this._anchor.tabIndex < 0) this._anchor.tabIndex = 0;
2868
+ this._anchor.addEventListener("keydown", this._handleAnchorKeydown);
2776
2869
  }
2777
- this._setupEventHandlers();
2870
+ this.el.addEventListener("click", this._handleFABClick);
2778
2871
  }
2779
2872
  static get defaults() {
2780
2873
  return _defaults8;
@@ -2787,26 +2880,10 @@ var Expressive = (() => {
2787
2880
  }
2788
2881
  destroy() {
2789
2882
  this.close();
2790
- this._removeEventHandlers();
2791
- this.el["Expressive_FloatingActionButton"] = void 0;
2792
- }
2793
- _directionFromClass() {
2794
- const match = [...this.el.classList].find((c) => c.startsWith("direction-"));
2795
- const value = match?.slice("direction-".length);
2796
- if (value === "top" || value === "right" || value === "bottom" || value === "left") {
2797
- return value;
2798
- }
2799
- return null;
2800
- }
2801
- _setupEventHandlers() {
2802
- this.el.addEventListener("click", this._handleFABClick);
2803
- if (this._anchor instanceof HTMLAnchorElement && !this._anchor.hasAttribute("href")) {
2804
- this._anchor.addEventListener("keydown", this._handleAnchorKeydown);
2805
- }
2806
- }
2807
- _removeEventHandlers() {
2883
+ if (this._list) this._list.inert = this._listWasInert;
2808
2884
  this.el.removeEventListener("click", this._handleFABClick);
2809
2885
  this._anchor?.removeEventListener("keydown", this._handleAnchorKeydown);
2886
+ this.el["Expressive_FloatingActionButton"] = void 0;
2810
2887
  }
2811
2888
  _toggle() {
2812
2889
  if (this.isOpen) this.close();
@@ -3056,10 +3133,12 @@ var Expressive = (() => {
3056
3133
  this._ownIndicators = false;
3057
3134
  this._trackEl = null;
3058
3135
  this._ignoreScroll = false;
3136
+ this._alignAfterResize = false;
3059
3137
  this._trackRaf = null;
3060
3138
  this._started = false;
3061
3139
  this._vertical = false;
3062
3140
  this._resizeObserver = null;
3141
+ this._resizeTimeout = null;
3063
3142
  this._usesWindowResize = false;
3064
3143
  this._dragPointerId = null;
3065
3144
  this._dragStartX = 0;
@@ -3078,19 +3157,32 @@ var Expressive = (() => {
3078
3157
  this._autoPaused = false;
3079
3158
  this._hovered = false;
3080
3159
  this._focused = false;
3160
+ this._motion = window.matchMedia("(prefers-reduced-motion: reduce)");
3081
3161
  this._generatedItemTabIndexes = /* @__PURE__ */ new Set();
3082
3162
  this._generatedItemLabels = /* @__PURE__ */ new Set();
3083
3163
  this._generatedItemDescriptions = /* @__PURE__ */ new Set();
3084
3164
  this._generatedItemSizes = /* @__PURE__ */ new Set();
3085
3165
  this._authoredItemSizes = /* @__PURE__ */ new Set();
3086
3166
  this._itemA11yOriginal = /* @__PURE__ */ new Map();
3087
- this._handleThrottledResize = Utils.throttle(() => this._handleResize(), 200);
3167
+ this._handleThrottledResize = () => {
3168
+ if (!this._started || this._resizeTimeout !== null) return;
3169
+ this._handleResize();
3170
+ this._resizeTimeout = setTimeout(() => {
3171
+ this._resizeTimeout = null;
3172
+ this._handleResize();
3173
+ }, 200);
3174
+ };
3088
3175
  this._handleAutoAdvanceState = (e) => {
3089
3176
  if (e.type === "mouseenter") this._hovered = true;
3090
3177
  else if (e.type === "mouseleave") this._hovered = false;
3091
3178
  else if (e.type === "focusin") this._focused = true;
3092
- else if (e.type === "focusout") this._focused = false;
3179
+ else if (e.type === "focusout") this._focused = this.el.contains(e.relatedTarget);
3180
+ this._syncAutoAdvance();
3181
+ };
3182
+ this._handleMotionChange = () => {
3093
3183
  this._syncAutoAdvance();
3184
+ if (this._motion.matches) this._scrollToIndex(this.center, false);
3185
+ this._updateParallax();
3094
3186
  };
3095
3187
  this._tick = () => {
3096
3188
  this._autoAdvanceTimer = null;
@@ -3110,7 +3202,12 @@ var Expressive = (() => {
3110
3202
  }
3111
3203
  this.set(0);
3112
3204
  };
3205
+ this._handleNativeScroll = () => {
3206
+ this._alignAfterResize = false;
3207
+ this._finishScroll();
3208
+ };
3113
3209
  this._handleTrackPointerDown = (e) => {
3210
+ this._handleNativeScroll();
3114
3211
  if (e.pointerType !== "mouse" || e.button !== 0) return;
3115
3212
  this.pressed = true;
3116
3213
  this.dragged = false;
@@ -3165,6 +3262,11 @@ var Expressive = (() => {
3165
3262
  this._handleTrackDragStart = (e) => {
3166
3263
  if (e.target.closest("img, picture, video")) e.preventDefault();
3167
3264
  };
3265
+ this._handleItemResize = (e) => {
3266
+ if (this._alignAfterResize && !this.pressed && e.propertyName === "flex-basis" && this.images.includes(e.target)) {
3267
+ this._scrollToIndex(this.center, false);
3268
+ }
3269
+ };
3168
3270
  this._handleIndicatorClick = (e) => {
3169
3271
  e.stopPropagation();
3170
3272
  const indicator = e.target.closest(".indicator-item");
@@ -3175,9 +3277,10 @@ var Expressive = (() => {
3175
3277
  };
3176
3278
  this._handleKeydown = (e) => {
3177
3279
  const target = e.target;
3178
- if (target.closest('input, textarea, select, [contenteditable="true"]')) return;
3179
- const forward = this._vertical ? e.key === "ArrowDown" : e.key === Utils.keys.ARROW_RIGHT;
3180
- const backward = this._vertical ? e.key === "ArrowUp" : e.key === Utils.keys.ARROW_LEFT;
3280
+ if (target.isContentEditable || target.closest("input, textarea, select")) return;
3281
+ const rtl = getComputedStyle(this._scroller).direction === "rtl";
3282
+ const forward = this._vertical ? e.key === "ArrowDown" : e.key === (rtl ? Utils.keys.ARROW_LEFT : Utils.keys.ARROW_RIGHT);
3283
+ const backward = this._vertical ? e.key === "ArrowUp" : e.key === (rtl ? Utils.keys.ARROW_RIGHT : Utils.keys.ARROW_LEFT);
3181
3284
  if (e.key === "Home") {
3182
3285
  e.preventDefault();
3183
3286
  this.set(0);
@@ -3210,12 +3313,22 @@ var Expressive = (() => {
3210
3313
  });
3211
3314
  };
3212
3315
  this._handleResize = () => {
3213
- if (!this.images.length) return;
3316
+ if (!this._started || !this.images.length) return;
3214
3317
  this._syncAdaptiveMode();
3215
3318
  this._syncLayoutRoles(this.center);
3216
3319
  this._scrollToIndex(this.center, false);
3217
3320
  this._updateParallax();
3218
3321
  };
3322
+ this._finishScroll = () => {
3323
+ this._ignoreScroll = false;
3324
+ clearTimeout(this._scrollEndTimeout);
3325
+ this._scroller.removeEventListener("scrollend", this._handleScrollEnd);
3326
+ };
3327
+ this._handleScrollEnd = () => {
3328
+ if (!this.images.some((item) => item.getAnimations().some((animation) => animation.playState === "running"))) {
3329
+ this._finishScroll();
3330
+ }
3331
+ };
3219
3332
  this.el["Expressive_Carousel"] = this;
3220
3333
  this.options = {
3221
3334
  ..._Carousel.defaults,
@@ -3229,8 +3342,6 @@ var Expressive = (() => {
3229
3342
  this.options.fullWidth = true;
3230
3343
  }
3231
3344
  this._vertical = this.el.classList.contains("full-screen");
3232
- const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
3233
- if (reducedMotion) this.options.duration = 1;
3234
3345
  this.pressed = false;
3235
3346
  this.dragged = false;
3236
3347
  this.images = this._collectItems();
@@ -3248,7 +3359,7 @@ var Expressive = (() => {
3248
3359
  this.hasMultipleSlides = this.images.length > 1;
3249
3360
  this.showIndicators = this.options.indicators && this.hasMultipleSlides;
3250
3361
  this.count = this.images.length;
3251
- this._autoAdvances = this.options.interval > 0 && this.hasMultipleSlides && !reducedMotion;
3362
+ this._autoAdvances = this.options.interval > 0 && this.hasMultipleSlides;
3252
3363
  this.images.forEach((item) => item.classList.add("carousel-item"));
3253
3364
  if (this.options.height !== null) {
3254
3365
  this._generatedFixedHeight = !this.el.classList.contains("fixed-height");
@@ -3359,6 +3470,7 @@ var Expressive = (() => {
3359
3470
  }
3360
3471
  _setupEventHandlers() {
3361
3472
  this.el.addEventListener("keydown", this._handleKeydown);
3473
+ this._motion.addEventListener("change", this._handleMotionChange);
3362
3474
  if (this._autoAdvances) {
3363
3475
  this.el.addEventListener("mouseenter", this._handleAutoAdvanceState);
3364
3476
  this.el.addEventListener("mouseleave", this._handleAutoAdvanceState);
@@ -3387,14 +3499,17 @@ var Expressive = (() => {
3387
3499
  passive: true
3388
3500
  });
3389
3501
  this._scroller.addEventListener("pointerdown", this._handleTrackPointerDown);
3502
+ this._scroller.addEventListener("wheel", this._handleNativeScroll, { passive: true });
3390
3503
  this._scroller.addEventListener("pointermove", this._handleTrackPointerMove);
3391
3504
  this._scroller.addEventListener("pointerup", this._handleTrackPointerUp);
3392
3505
  this._scroller.addEventListener("pointercancel", this._handleTrackPointerUp);
3393
3506
  this._scroller.addEventListener("click", this._handleTrackClick, true);
3394
3507
  this._scroller.addEventListener("dragstart", this._handleTrackDragStart);
3508
+ this._scroller.addEventListener("transitionend", this._handleItemResize);
3395
3509
  }
3396
3510
  _removeEventHandlers() {
3397
3511
  this.el.removeEventListener("keydown", this._handleKeydown);
3512
+ this._motion.removeEventListener("change", this._handleMotionChange);
3398
3513
  this.el.removeEventListener("mouseenter", this._handleAutoAdvanceState);
3399
3514
  this.el.removeEventListener("mouseleave", this._handleAutoAdvanceState);
3400
3515
  this.el.removeEventListener("focusin", this._handleAutoAdvanceState);
@@ -3405,11 +3520,13 @@ var Expressive = (() => {
3405
3520
  }
3406
3521
  this._scroller.removeEventListener("scroll", this._handleTrackScroll);
3407
3522
  this._scroller.removeEventListener("pointerdown", this._handleTrackPointerDown);
3523
+ this._scroller.removeEventListener("wheel", this._handleNativeScroll);
3408
3524
  this._scroller.removeEventListener("pointermove", this._handleTrackPointerMove);
3409
3525
  this._scroller.removeEventListener("pointerup", this._handleTrackPointerUp);
3410
3526
  this._scroller.removeEventListener("pointercancel", this._handleTrackPointerUp);
3411
3527
  this._scroller.removeEventListener("click", this._handleTrackClick, true);
3412
3528
  this._scroller.removeEventListener("dragstart", this._handleTrackDragStart);
3529
+ this._scroller.removeEventListener("transitionend", this._handleItemResize);
3413
3530
  this._resizeObserver?.disconnect();
3414
3531
  this._resizeObserver = null;
3415
3532
  if (this._usesWindowResize) {
@@ -3421,12 +3538,15 @@ var Expressive = (() => {
3421
3538
  }
3422
3539
  }
3423
3540
  _teardown() {
3541
+ this._started = false;
3424
3542
  this._removeEventHandlers();
3543
+ this._finishScroll();
3425
3544
  this._autoAdvances = false;
3426
3545
  this._syncAutoAdvance();
3427
3546
  if (this._trackRaf !== null) cancelAnimationFrame(this._trackRaf);
3428
3547
  window.clearTimeout(this.scrollingTimeout);
3429
3548
  window.clearTimeout(this._suppressClickTimeout);
3549
+ clearTimeout(this._resizeTimeout);
3430
3550
  if (this._ownIndicators) this._indicators?.remove();
3431
3551
  this._unwrapTrack();
3432
3552
  this.images.forEach((el) => {
@@ -3459,7 +3579,7 @@ var Expressive = (() => {
3459
3579
  }
3460
3580
  /** Start or stop the timer to match the current pause state. */
3461
3581
  _syncAutoAdvance() {
3462
- const run = this._autoAdvances && !this._autoPaused && !this._hovered && !this._focused && !document.hidden;
3582
+ const run = this._autoAdvances && !this._autoPaused && !this._hovered && !this._focused && !this._motion.matches && !document.hidden;
3463
3583
  if (run && this._autoAdvanceTimer === null) {
3464
3584
  this._autoAdvanceTimer = setTimeout(
3465
3585
  this._tick,
@@ -3585,16 +3705,21 @@ var Expressive = (() => {
3585
3705
  }
3586
3706
  _nearestIndex() {
3587
3707
  const centered = this.el.classList.contains("center-aligned");
3708
+ const style = getComputedStyle(this._scroller);
3709
+ const rtl = !this._vertical && style.direction === "rtl";
3588
3710
  const position = this._vertical ? this._scroller.scrollTop : this._scroller.scrollLeft;
3589
- const viewportCenter = position + (this._vertical ? this._scroller.clientHeight : this._scroller.clientWidth) / 2;
3590
- let best = 0;
3711
+ const viewport = this._vertical ? this._scroller.clientHeight : this._scroller.clientWidth;
3712
+ const extent = (this._vertical ? this._scroller.scrollHeight : this._scroller.scrollWidth) - viewport;
3713
+ const padding = parseFloat(this._vertical ? style.paddingBlockStart : style.paddingInlineStart) || 0;
3714
+ let best = this.center;
3591
3715
  let bestDist = Infinity;
3592
3716
  this.images.forEach((el, i) => {
3593
3717
  const start = this._vertical ? el.offsetTop : el.offsetLeft;
3594
3718
  const size = this._vertical ? el.offsetHeight : el.offsetWidth;
3595
- const target = centered ? start + size / 2 : start;
3596
- const dist = Math.abs(target - (centered ? viewportCenter : position));
3597
- if (dist < bestDist) {
3719
+ const target = centered ? start - (viewport - size) / 2 : start + (rtl ? size - viewport + padding : -padding);
3720
+ const bounded = Math.max(rtl ? -extent : 0, Math.min(rtl ? 0 : extent, target));
3721
+ const dist = Math.abs(bounded - position);
3722
+ if (dist < bestDist || dist === bestDist && i === this.center) {
3598
3723
  bestDist = dist;
3599
3724
  best = i;
3600
3725
  }
@@ -3604,31 +3729,24 @@ var Expressive = (() => {
3604
3729
  _scrollToIndex(n, smooth = true) {
3605
3730
  const el = this.images[n];
3606
3731
  if (!el) return;
3732
+ this._finishScroll();
3607
3733
  const centered = this.el.classList.contains("center-aligned");
3608
3734
  const style = getComputedStyle(this._scroller);
3609
3735
  const inlinePadding = Number.parseFloat(style.paddingInlineStart) || 0;
3610
3736
  const blockPadding = Number.parseFloat(style.paddingBlockStart) || 0;
3611
- const left = centered ? el.offsetLeft - (this._scroller.clientWidth - el.offsetWidth) / 2 : el.offsetLeft - inlinePadding;
3737
+ const left = centered ? el.offsetLeft - (this._scroller.clientWidth - el.offsetWidth) / 2 : style.direction === "rtl" ? el.offsetLeft + el.offsetWidth - this._scroller.clientWidth + inlinePadding : el.offsetLeft - inlinePadding;
3612
3738
  const top = el.offsetTop - blockPadding;
3613
- const current = this._vertical ? this._scroller.scrollTop : this._scroller.scrollLeft;
3614
- const target = this._vertical ? top : left;
3615
- if (Math.abs(current - target) < 1) {
3616
- this._syncActive(n, false);
3617
- return;
3618
- }
3619
3739
  this._ignoreScroll = true;
3620
3740
  this._scroller.scrollTo({
3621
3741
  left: this._vertical ? 0 : left,
3622
3742
  top: this._vertical ? top : 0,
3623
- behavior: smooth ? "smooth" : "auto"
3743
+ behavior: smooth && !this._motion.matches ? "smooth" : "instant"
3624
3744
  });
3625
- const done = () => {
3626
- this._ignoreScroll = false;
3627
- };
3628
- this._scroller.addEventListener("scrollend", done, { once: true });
3629
- window.setTimeout(done, this.options.duration + 350);
3745
+ this._scroller.addEventListener("scrollend", this._handleScrollEnd);
3746
+ this._scrollEndTimeout = setTimeout(this._finishScroll, this.options.duration + 350);
3630
3747
  }
3631
3748
  _cycleTo(n, callback = null) {
3749
+ this._alignAfterResize = true;
3632
3750
  if (typeof callback === "function") this.oneTimeCallback = callback;
3633
3751
  this._syncActive(n, false);
3634
3752
  this._scrollToIndex(n);
@@ -3758,6 +3876,13 @@ var Expressive = (() => {
3758
3876
  super(el, options, _FormSelect);
3759
3877
  this._handleSelectChange = () => {
3760
3878
  this._setValueToInput();
3879
+ this._setSelectedStates();
3880
+ };
3881
+ this._handleReset = (e) => {
3882
+ clearTimeout(this._resetTimer);
3883
+ this._resetTimer = setTimeout(() => {
3884
+ if (!e.defaultPrevented) this._handleSelectChange();
3885
+ }, 0);
3761
3886
  };
3762
3887
  // Named rather than inline so that _removeEventHandlers can actually detach
3763
3888
  // it - an anonymous listener per option was unremovable.
@@ -3828,21 +3953,41 @@ var Expressive = (() => {
3828
3953
  */
3829
3954
  refresh() {
3830
3955
  if (!this.menuEl) return;
3956
+ this._bindForm();
3957
+ const root = this.menuEl.getRootNode();
3958
+ const focused = this._values.find((value) => value.optionEl.contains(root.activeElement))?.el;
3959
+ this.input.disabled = this.el.matches(":disabled");
3960
+ this.wrapper.classList.toggle("disabled", this.input.disabled);
3961
+ if (this.input.disabled) this.menu?.close();
3962
+ else if (!this.menu) this._initMenu();
3963
+ this.input.removeAttribute("aria-activedescendant");
3831
3964
  this._rebuildOptions();
3832
- this._setValueToInput();
3833
- this._setSelectedStates();
3965
+ this._handleSelectChange();
3966
+ if (this.menu) this.menu.focusedIndex = -1;
3967
+ if (this.menu?.isOpen) {
3968
+ this.menu.recalculateDimensions();
3969
+ if (focused) (this._values.find((value) => value.el === focused)?.optionEl ?? this.input).focus();
3970
+ }
3834
3971
  }
3835
3972
  _setupEventHandlers() {
3836
3973
  this._setupOptionHandlers();
3837
3974
  this.el.addEventListener("change", this._handleSelectChange);
3838
3975
  this.input.addEventListener("click", this._handleInputClick);
3839
3976
  this.menuEl.addEventListener("focusin", this._handleOptionFocus);
3977
+ this._bindForm();
3978
+ }
3979
+ _bindForm() {
3980
+ this._form?.removeEventListener("reset", this._handleReset);
3981
+ this._form = this.el.form;
3982
+ this._form?.addEventListener("reset", this._handleReset);
3840
3983
  }
3841
3984
  _removeEventHandlers() {
3842
3985
  this._removeOptionHandlers();
3843
3986
  this.el.removeEventListener("change", this._handleSelectChange);
3844
3987
  this.input?.removeEventListener("click", this._handleInputClick);
3845
3988
  this.menuEl?.removeEventListener("focusin", this._handleOptionFocus);
3989
+ this._form?.removeEventListener("reset", this._handleReset);
3990
+ clearTimeout(this._resetTimer);
3846
3991
  }
3847
3992
  _setupOptionHandlers() {
3848
3993
  this.menuEl.querySelectorAll("li:not(.optgroup)").forEach((el) => {
@@ -3864,9 +4009,10 @@ var Expressive = (() => {
3864
4009
  return true;
3865
4010
  }
3866
4011
  _selectOptionElement(virtualOption) {
4012
+ if (this.el.matches(":disabled")) return;
3867
4013
  if (!virtualOption.classList.contains("disabled") && !virtualOption.classList.contains("optgroup")) {
3868
4014
  const value = this._values.find((value2) => value2.optionEl === virtualOption);
3869
- if (!value) return;
4015
+ if (!value || value.el.matches(":disabled")) return;
3870
4016
  const previousSelectedValues = this.getSelectedValues();
3871
4017
  if (this.isMultiple) {
3872
4018
  this._toggleEntryFromArray(value);
@@ -3874,7 +4020,7 @@ var Expressive = (() => {
3874
4020
  this._deselectAll();
3875
4021
  this._selectValue(value);
3876
4022
  }
3877
- this._setValueToInput();
4023
+ this._handleSelectChange();
3878
4024
  const actualSelectedValues = this.getSelectedValues();
3879
4025
  const selectionHasChanged = !this._arraysEqual(previousSelectedValues, actualSelectedValues);
3880
4026
  if (selectionHasChanged)
@@ -3888,7 +4034,7 @@ var Expressive = (() => {
3888
4034
  this.labelEl = this.el.labels?.[0] ?? null;
3889
4035
  this._setupWrapper();
3890
4036
  this._hideNativeSelect();
3891
- if (this.el.disabled) this.wrapper.classList.add("disabled");
4037
+ if (this.el.matches(":disabled")) this.wrapper.classList.add("disabled");
3892
4038
  this.menuEl = document.createElement("menu");
3893
4039
  this.menuEl.id = `select-options-${Utils.guid()}`;
3894
4040
  this.menuEl.setAttribute("popover", "auto");
@@ -3935,7 +4081,7 @@ var Expressive = (() => {
3935
4081
  this.input.setAttribute("data-target", this.menuEl.id);
3936
4082
  this.input.ariaReadOnly = "true";
3937
4083
  this.input.ariaRequired = this.el.hasAttribute("required").toString();
3938
- if (this.el.disabled) this.input.disabled = true;
4084
+ this.input.disabled = this.el.matches(":disabled");
3939
4085
  this.input.setAttribute("tabindex", this.nativeTabIndex.toString());
3940
4086
  const attrs = this.el.attributes;
3941
4087
  for (let i = 0; i < attrs.length; ++i) {
@@ -3961,7 +4107,7 @@ var Expressive = (() => {
3961
4107
  this.wrapper.prepend(caret);
3962
4108
  }
3963
4109
  _initMenu() {
3964
- if (this.el.disabled) return;
4110
+ if (this.el.matches(":disabled")) return;
3965
4111
  const menuOptions = { ...this.options.menuOptions };
3966
4112
  menuOptions.coverTrigger = false;
3967
4113
  const userOnOpenEnd = menuOptions.onOpenEnd;
@@ -4062,7 +4208,8 @@ var Expressive = (() => {
4062
4208
  li.id = "select-option-" + Utils.guid();
4063
4209
  li.setAttribute("role", "option");
4064
4210
  li.tabIndex = 0;
4065
- if (realOption.disabled) {
4211
+ const disabled = this.el.matches(":disabled") || realOption.matches(":disabled");
4212
+ if (disabled) {
4066
4213
  li.classList.add("disabled");
4067
4214
  li.ariaDisabled = "true";
4068
4215
  li.tabIndex = -1;
@@ -4070,7 +4217,7 @@ var Expressive = (() => {
4070
4217
  if (type === "optgroup-option") li.classList.add(type);
4071
4218
  const optionText = realOption.textContent;
4072
4219
  const span = document.createElement("span");
4073
- if (this.isMultiple && !realOption.disabled) {
4220
+ if (this.isMultiple && !disabled) {
4074
4221
  const label = document.createElement("label");
4075
4222
  const checkbox = document.createElement("input");
4076
4223
  checkbox.type = "checkbox";
@@ -4137,22 +4284,7 @@ var Expressive = (() => {
4137
4284
  );
4138
4285
  }
4139
4286
  _setValueToInput() {
4140
- const selectedRealOptions = this._getSelectedOptions();
4141
- const selectedOptionPairs = this._values.filter(
4142
- (value) => selectedRealOptions.indexOf(value.el) >= 0
4143
- );
4144
- const notDisabledOptionPairs = selectedOptionPairs.filter((op) => !op.el.disabled);
4145
- const texts = notDisabledOptionPairs.map(
4146
- (value) => value.optionEl.querySelector("span").textContent.trim()
4147
- );
4148
- if (texts.length === 0) {
4149
- const firstDisabledOption = this.el.querySelector("option:disabled");
4150
- if (firstDisabledOption && firstDisabledOption.value === "") {
4151
- this.input.value = firstDisabledOption.textContent;
4152
- return;
4153
- }
4154
- }
4155
- this.input.value = texts.join(", ");
4287
+ this.input.value = this._getSelectedOptions().map((option) => option.textContent.trim()).join(", ");
4156
4288
  }
4157
4289
  _setSelectedStates() {
4158
4290
  this._values.forEach((value) => {
@@ -4189,7 +4321,7 @@ var Expressive = (() => {
4189
4321
  var DockedDisplayPlugin = class _DockedDisplayPlugin {
4190
4322
  constructor(el, container, options) {
4191
4323
  this._handleDocumentClick = (e) => {
4192
- if (this.visible && !(this.el === e.target) && !e.target.closest(".display-docked")) {
4324
+ if (this.visible && !e.composedPath().includes(this.el) && !e.composedPath().includes(this.container)) {
4193
4325
  this.hide();
4194
4326
  }
4195
4327
  };
@@ -4362,6 +4494,7 @@ var Expressive = (() => {
4362
4494
  this._controlsKey = null;
4363
4495
  /** id linking the controls element to the table's aria-labelledby. */
4364
4496
  this._randId = null;
4497
+ this._focusedDate = null;
4365
4498
  this._handleInputClick = (e) => {
4366
4499
  if (e.type == "date") {
4367
4500
  e.preventDefault();
@@ -4379,17 +4512,55 @@ var Expressive = (() => {
4379
4512
  e.preventDefault();
4380
4513
  this._batchDraws(() => {
4381
4514
  this.setDateFromInput(e.target);
4515
+ this._focusedDate = e.target === this.endDateEl ? this.endDate : this.date;
4382
4516
  this.draw();
4383
4517
  });
4384
4518
  if (this.displayPlugin) this.displayPlugin.show();
4519
+ this._syncCalendarFocus(true);
4385
4520
  if (this.options.onInputInteraction) this.options.onInputInteraction.call(this);
4386
4521
  }
4387
4522
  };
4523
+ this._handleCalendarFocus = (event) => {
4524
+ const button = event.target;
4525
+ if (!button.matches(".datepicker-day-button")) return;
4526
+ this._focusedDate = this._dayDate(button);
4527
+ this._syncCalendarFocus();
4528
+ };
4529
+ this._handleCalendarKeydown = (event) => {
4530
+ const button = event.target;
4531
+ if (!button.matches(".datepicker-day-button") || event.altKey || event.ctrlKey || event.metaKey) return;
4532
+ const date = this._dayDate(button);
4533
+ const weekday = (date.getDay() - this.options.firstDay + 7) % 7;
4534
+ const offsets = {
4535
+ ArrowLeft: this.options.isRTL ? 1 : -1,
4536
+ ArrowRight: this.options.isRTL ? -1 : 1,
4537
+ ArrowUp: -7,
4538
+ ArrowDown: 7,
4539
+ Home: -weekday,
4540
+ End: 6 - weekday
4541
+ };
4542
+ if (Object.hasOwn(offsets, event.key)) date.setDate(date.getDate() + offsets[event.key]);
4543
+ else if (event.key === "PageUp" || event.key === "PageDown") {
4544
+ const day = date.getDate();
4545
+ date.setDate(1);
4546
+ date.setMonth(date.getMonth() + (event.key === "PageUp" ? -1 : 1) * (event.shiftKey ? 12 : 1));
4547
+ const last = new Date(date.getTime());
4548
+ last.setMonth(last.getMonth() + 1, 0);
4549
+ date.setDate(Math.min(day, last.getDate()));
4550
+ } else return;
4551
+ event.preventDefault();
4552
+ const { minDate, maxDate, minYear, maxYear, minMonth, maxMonth } = this.options;
4553
+ if (minDate && date < minDate) date.setTime(minDate.getTime());
4554
+ if (maxDate && date > maxDate) date.setTime(maxDate.getTime());
4555
+ if (date.getFullYear() < minYear || date.getFullYear() > maxYear || date.getFullYear() === minYear && date.getMonth() < (minMonth ?? 0) || date.getFullYear() === maxYear && date.getMonth() > (maxMonth ?? 11)) return;
4556
+ this._focusedDate = date;
4557
+ this.gotoDate(date);
4558
+ };
4388
4559
  this._handleCalendarClick = (e) => {
4389
4560
  const target = e.target;
4390
4561
  if (target.classList.contains("is-disabled")) return;
4391
4562
  this._batchDraws(() => {
4392
- if (target.classList.contains("datepicker-day-button") && !target.classList.contains("is-empty") && !target.parentElement.classList.contains("is-disabled")) {
4563
+ if (target.classList.contains("datepicker-day-button") && !target.classList.contains("is-empty") && !target.parentElement.classList.contains("is-disabled") && !target.parentElement.classList.contains("is-selection-disabled")) {
4393
4564
  const selectedDate = new Date(
4394
4565
  e.target.getAttribute("data-year"),
4395
4566
  e.target.getAttribute("data-month"),
@@ -4427,6 +4598,7 @@ var Expressive = (() => {
4427
4598
  this._clearDates = () => {
4428
4599
  this.date = null;
4429
4600
  this.endDate = null;
4601
+ this.dates?.splice(0);
4430
4602
  this.draw();
4431
4603
  };
4432
4604
  this._handleMonthChange = (e) => {
@@ -4449,6 +4621,7 @@ var Expressive = (() => {
4449
4621
  date = new Date(Date.parse(e.target.value));
4450
4622
  }
4451
4623
  if (_Datepicker._isDate(date)) {
4624
+ if (el == this.endDateEl && date < this.date) return this.setInputValue(el, this.endDate);
4452
4625
  this.setDate(date, false, el == this.endDateEl, true);
4453
4626
  if (e.type == "date") {
4454
4627
  this.setDataDate(e, date);
@@ -4477,6 +4650,11 @@ var Expressive = (() => {
4477
4650
  if (this.options.minDate) this.options.minDate.setHours(0, 0, 0, 0);
4478
4651
  if (this.options.maxDate) this.options.maxDate.setHours(0, 0, 0, 0);
4479
4652
  this.id = Utils.guid();
4653
+ if (this.options.isMultipleSelection) {
4654
+ this.multiple = true;
4655
+ this.dates = [];
4656
+ this.dateEls = [el];
4657
+ }
4480
4658
  this._setupVariables();
4481
4659
  this._insertHTMLIntoDOM();
4482
4660
  this._setupEventHandlers();
@@ -4504,12 +4682,6 @@ var Expressive = (() => {
4504
4682
  }
4505
4683
  }
4506
4684
  }
4507
- if (this.options.isMultipleSelection) {
4508
- this.multiple = true;
4509
- this.dates = [];
4510
- this.dateEls = [];
4511
- this.dateEls.push(el);
4512
- }
4513
4685
  if (this.options.displayPlugin) {
4514
4686
  if (this.options.displayPlugin === "docked") this.displayPlugin = DockedDisplayPlugin.init(this.el, this.containerEl, this.options.displayPluginOptions);
4515
4687
  }
@@ -4570,6 +4742,8 @@ var Expressive = (() => {
4570
4742
  }
4571
4743
  destroy() {
4572
4744
  this._removeEventHandlers();
4745
+ this.dateEls?.slice(1).forEach((input) => this._removeDateInput(input));
4746
+ if (!this.options.dateRangeEndEl && this.endDateEl) this._removeDateInput(this.endDateEl);
4573
4747
  this.displayPlugin?.destroy();
4574
4748
  this.containerEl.remove();
4575
4749
  this.destroySelects();
@@ -4662,9 +4836,9 @@ var Expressive = (() => {
4662
4836
  if (!selectedDate) {
4663
4837
  return;
4664
4838
  }
4839
+ _Datepicker._setToStartOfDay(selectedDate);
4665
4840
  if (!this.options.isMultipleSelection) this.setSingleDate(selectedDate, isEndDate);
4666
4841
  else if (!fromUserInput) this.setMultiDate(selectedDate);
4667
- _Datepicker._setToStartOfDay(selectedDate);
4668
4842
  this.gotoDate(selectedDate);
4669
4843
  if (!preventOnSelect && typeof this.options.onSelect === "function") {
4670
4844
  this.options.onSelect.call(this, selectedDate);
@@ -4736,27 +4910,20 @@ var Expressive = (() => {
4736
4910
  this.setMultipleSelectionInputValues();
4737
4911
  }
4738
4912
  setMultipleSelectionInputValues() {
4739
- const dateElsArr = Array.from(this.dateEls).filter((el, index) => {
4740
- if (index > this.dates.length - 1) return el;
4741
- });
4742
- dateElsArr.forEach((el) => {
4743
- el.remove();
4744
- });
4745
- this.dates.forEach((date, index) => {
4746
- if (Array.from(this.dateEls)[index]) {
4747
- this.setInputValue(this.dateEls[index], date);
4748
- return;
4749
- }
4750
- const dateEl = this.createDateInput();
4751
- this.setInputValue(dateEl, date);
4752
- this.dateEls.push(dateEl);
4753
- });
4913
+ const count = Math.max(1, this.dates.length);
4914
+ for (const input of this.dateEls.splice(count)) this._removeDateInput(input);
4915
+ while (this.dateEls.length < count) this.dateEls.push(this.createDateInput());
4916
+ this.dateEls.forEach((input, index) => this.setInputValue(input, this.dates[index] ?? null));
4754
4917
  }
4755
4918
  /**
4756
4919
  * Sets given date as the input value on the given element.
4757
4920
  */
4758
4921
  setInputValue(el, date) {
4759
- if (el.type == "date") {
4922
+ if (!el) return;
4923
+ if (!_Datepicker._isDate(date)) {
4924
+ el.value = "";
4925
+ if (el.type === "date") el.setAttribute("data-date", "");
4926
+ } else if (el.type == "date") {
4760
4927
  this.setDataDate(el, date);
4761
4928
  el.value = this.formatDate(date, "yyyy-mm-dd");
4762
4929
  } else {
@@ -5094,6 +5261,9 @@ var Expressive = (() => {
5094
5261
  this._drawDirty = true;
5095
5262
  return;
5096
5263
  }
5264
+ const active = this.calendarEl.getRootNode().activeElement;
5265
+ const restoreDayFocus = active?.matches(".datepicker-day-button") && this.calendarEl.contains(active);
5266
+ const restoreArrow = this.calendarEl.contains(active) && active?.matches(".month-prev, .month-next") ? active.classList.contains("month-prev") ? ".month-prev" : ".month-next" : null;
5097
5267
  const opts = this.options, minYear = opts.minYear, maxYear = opts.maxYear, minMonth = opts.minMonth, maxMonth = opts.maxMonth;
5098
5268
  if (this._y <= minYear) {
5099
5269
  this._y = minYear;
@@ -5123,6 +5293,8 @@ var Expressive = (() => {
5123
5293
  this._rebuildControls(year, month);
5124
5294
  }
5125
5295
  this._renderCalendarTable(year, month);
5296
+ this._syncCalendarFocus(!!restoreDayFocus);
5297
+ if (restoreArrow) this.calendarEl.querySelector(restoreArrow)?.focus();
5126
5298
  if (typeof this.options.onDraw === "function") {
5127
5299
  this.options.onDraw.call(this);
5128
5300
  }
@@ -5169,7 +5341,12 @@ var Expressive = (() => {
5169
5341
  this.el.addEventListener("click", this._handleInputClick);
5170
5342
  this.el.addEventListener("keydown", this._handleInputKeydown);
5171
5343
  this.el.addEventListener("change", this._handleInputChange);
5344
+ this.endDateEl?.addEventListener("click", this._handleInputClick);
5345
+ this.endDateEl?.addEventListener("keydown", this._handleInputKeydown);
5346
+ this.endDateEl?.addEventListener("change", this._handleInputChange);
5172
5347
  this.calendarEl.addEventListener("click", this._handleCalendarClick);
5348
+ this.calendarEl.addEventListener("keydown", this._handleCalendarKeydown);
5349
+ this.calendarEl.addEventListener("focusin", this._handleCalendarFocus);
5173
5350
  }
5174
5351
  _setupVariables() {
5175
5352
  const template = document.createElement("template");
@@ -5215,14 +5392,43 @@ var Expressive = (() => {
5215
5392
  };
5216
5393
  }
5217
5394
  _removeEventHandlers() {
5218
- this.el.removeEventListener("click", this._handleInputClick);
5219
- this.el.removeEventListener("keydown", this._handleInputKeydown);
5220
- this.el.removeEventListener("change", this._handleInputChange);
5395
+ for (const input of this.dateEls ?? [this.el]) this._removeInputEventHandlers(input);
5221
5396
  this.calendarEl.removeEventListener("click", this._handleCalendarClick);
5397
+ this.calendarEl.removeEventListener("keydown", this._handleCalendarKeydown);
5398
+ this.calendarEl.removeEventListener("focusin", this._handleCalendarFocus);
5222
5399
  if (this.options.isDateRange && this.endDateEl) {
5223
- this.endDateEl.removeEventListener("click", this._handleInputClick);
5224
- this.endDateEl.removeEventListener("keypress", this._handleInputKeydown);
5225
- this.endDateEl.removeEventListener("change", this._handleInputChange);
5400
+ this._removeInputEventHandlers(this.endDateEl);
5401
+ }
5402
+ }
5403
+ _removeInputEventHandlers(input) {
5404
+ input.removeEventListener("click", this._handleInputClick);
5405
+ input.removeEventListener("keydown", this._handleInputKeydown);
5406
+ input.removeEventListener("change", this._handleInputChange);
5407
+ }
5408
+ _removeDateInput(input) {
5409
+ const focused = input.getRootNode().activeElement === input;
5410
+ this._removeInputEventHandlers(input);
5411
+ input.remove();
5412
+ if (focused && document.activeElement === document.body) this.el.focus();
5413
+ }
5414
+ _dayDate(button) {
5415
+ const date = /* @__PURE__ */ new Date(0);
5416
+ date.setFullYear(Number(button.dataset.year), Number(button.dataset.month), Number(button.dataset.day));
5417
+ _Datepicker._setToStartOfDay(date);
5418
+ return date;
5419
+ }
5420
+ _syncCalendarFocus(restore = false) {
5421
+ const buttons = [...this.calendarEl.querySelectorAll(".datepicker-day-button")];
5422
+ const current = buttons.filter((button) => !button.parentElement.classList.contains("is-selection-disabled"));
5423
+ const preferred = this._focusedDate || this.date || /* @__PURE__ */ new Date();
5424
+ const target = current.find((button) => _Datepicker._compareDates(this._dayDate(button), preferred)) || current.find((button) => button.parentElement.classList.contains("is-selected")) || current.find((button) => !button.parentElement.classList.contains("is-disabled")) || current[0];
5425
+ for (const button of buttons) {
5426
+ button.tabIndex = button === target ? 0 : -1;
5427
+ button.setAttribute("aria-disabled", String(button.parentElement.matches(".is-disabled, .is-selection-disabled")));
5428
+ }
5429
+ if (target) {
5430
+ this._focusedDate = this._dayDate(target);
5431
+ if (restore) target.focus();
5226
5432
  }
5227
5433
  }
5228
5434
  // change view to a specific month (zero-index, e.g. 0: January)
@@ -5250,7 +5456,7 @@ var Expressive = (() => {
5250
5456
  const dateInput = this.el.cloneNode(true);
5251
5457
  dateInput.removeAttribute("id");
5252
5458
  dateInput.addEventListener("click", this._handleInputClick);
5253
- dateInput.addEventListener("keypress", this._handleInputKeydown);
5459
+ dateInput.addEventListener("keydown", this._handleInputKeydown);
5254
5460
  dateInput.addEventListener("change", this._handleInputChange);
5255
5461
  this.el.parentElement.appendChild(dateInput);
5256
5462
  return dateInput;
@@ -5278,19 +5484,39 @@ var Expressive = (() => {
5278
5484
  onCloseStart: null,
5279
5485
  onCloseEnd: null
5280
5486
  };
5281
- var Lightbox = class _Lightbox extends Component {
5487
+ var _Lightbox = class _Lightbox extends Component {
5282
5488
  constructor(el, options) {
5283
5489
  super(el, options, _Lightbox);
5490
+ /** Ancestors forced to `overflow: visible`, with the inline value to put back. */
5491
+ this._changedAncestorList = [];
5492
+ this._timers = /* @__PURE__ */ new Map();
5493
+ this._motion = window.matchMedia("(prefers-reduced-motion: reduce)");
5494
+ this._isOpen = false;
5495
+ this._destroyed = false;
5496
+ this._revision = 0;
5497
+ this._handleMotionChange = () => {
5498
+ if (!this._motion.matches) return;
5499
+ const callbacks = [...this._timers.values()];
5500
+ const revision = this._cancel();
5501
+ for (const callback of callbacks) {
5502
+ if (revision !== this._revision) break;
5503
+ callback();
5504
+ }
5505
+ if (this.overlayActive) {
5506
+ for (const el of [this.el, this._overlay, this._photoCaption]) if (el) el.style.transition = "none";
5507
+ }
5508
+ };
5284
5509
  this._handleLightboxClick = () => {
5285
5510
  this._handleLightboxToggle();
5286
5511
  };
5287
5512
  this._handleLightboxKeypress = (e) => {
5288
- if (e.key === Utils.keys.ENTER) {
5513
+ if (this._trigger === this.el && (e.key === Utils.keys.ENTER || e.key === " ")) {
5514
+ e.preventDefault();
5289
5515
  this._handleLightboxToggle();
5290
5516
  }
5291
5517
  };
5292
5518
  this._handleLightboxToggle = () => {
5293
- if (this.doneAnimating === false || this.overlayActive && this.doneAnimating) this.close();
5519
+ if (this._isOpen) this.close();
5294
5520
  else this.open();
5295
5521
  };
5296
5522
  this._handleWindowScroll = () => {
@@ -5300,12 +5526,16 @@ var Expressive = (() => {
5300
5526
  if (this.overlayActive) this.close();
5301
5527
  };
5302
5528
  this._handleWindowEscape = (e) => {
5303
- if (e.key === Utils.keys.ESC && this.doneAnimating && this.overlayActive) this.close();
5529
+ if (e.key === Utils.keys.ESC) this.close();
5304
5530
  };
5305
5531
  /**
5306
5532
  * Open lightbox.
5307
5533
  */
5308
5534
  this.open = () => {
5535
+ if (this._destroyed || this._isOpen) return;
5536
+ const revision = this._cancel();
5537
+ if (this.overlayActive) this._reset();
5538
+ this._isOpen = true;
5309
5539
  this._updateVars();
5310
5540
  const originRect = this.el.getBoundingClientRect();
5311
5541
  const placeholderRect = this.placeholder.getBoundingClientRect();
@@ -5316,6 +5546,7 @@ var Expressive = (() => {
5316
5546
  this.overlayActive = true;
5317
5547
  if (typeof this.options.onOpenStart === "function")
5318
5548
  this.options.onOpenStart.call(this, this.el);
5549
+ if (revision !== this._revision) return;
5319
5550
  this.placeholder.style.width = placeholderRect.width + "px";
5320
5551
  this.placeholder.style.height = placeholderRect.height + "px";
5321
5552
  this.placeholder.style.position = "relative";
@@ -5350,25 +5581,28 @@ var Expressive = (() => {
5350
5581
  this.newWidth = this.windowHeight * 0.9 * ratio;
5351
5582
  this.newHeight = this.windowHeight * 0.9;
5352
5583
  }
5353
- this._animateImageIn();
5354
5584
  window.addEventListener("scroll", this._handleWindowScroll, { passive: true });
5355
5585
  window.addEventListener("resize", this._handleWindowResize, { passive: true });
5356
5586
  window.addEventListener("keyup", this._handleWindowEscape);
5587
+ this._animateImageIn();
5357
5588
  };
5358
5589
  /**
5359
5590
  * Close lightbox.
5360
5591
  */
5361
5592
  this.close = () => {
5362
- this._updateVars();
5593
+ if (this._destroyed || !this._isOpen) return;
5594
+ const revision = this._cancel();
5595
+ this._isOpen = false;
5363
5596
  this.doneAnimating = false;
5364
5597
  if (typeof this.options.onCloseStart === "function")
5365
5598
  this.options.onCloseStart.call(this, this.el);
5599
+ if (revision !== this._revision) return;
5366
5600
  window.removeEventListener("scroll", this._handleWindowScroll);
5367
5601
  window.removeEventListener("resize", this._handleWindowResize);
5368
5602
  window.removeEventListener("keyup", this._handleWindowEscape);
5369
- this._removeOverlay();
5603
+ if (this._overlay) this._removeOverlay();
5604
+ if (this._photoCaption) this._removeCaption();
5370
5605
  this._animateImageOut();
5371
- if (this.caption !== "") this._removeCaption();
5372
5606
  };
5373
5607
  this.el["Expressive_Lightbox"] = this;
5374
5608
  this.options = {
@@ -5383,7 +5617,9 @@ var Expressive = (() => {
5383
5617
  this.originalHeight = 0;
5384
5618
  this.originInlineStyles = this.el.getAttribute("style");
5385
5619
  this.caption = this.el.getAttribute("data-caption") || "";
5386
- this.el.tabIndex = 0;
5620
+ this._tabindex = this.el.getAttribute("tabindex");
5621
+ this._trigger = this.el.closest("button") ?? this.el;
5622
+ this.el.tabIndex = this._trigger === this.el ? 0 : -1;
5387
5623
  this.el.before(this.placeholder);
5388
5624
  this.placeholder.append(this.el);
5389
5625
  this._setupEventHandlers();
@@ -5403,26 +5639,81 @@ var Expressive = (() => {
5403
5639
  return el["Expressive_Lightbox"];
5404
5640
  }
5405
5641
  destroy() {
5642
+ if (this._destroyed) return;
5643
+ this._destroyed = true;
5644
+ const root = this.el.getRootNode();
5645
+ const focused = root.activeElement === this.el;
5406
5646
  this._removeEventHandlers();
5647
+ this._cancel();
5648
+ this._reset();
5649
+ this.placeholder.replaceWith(this.el);
5650
+ let active = document.activeElement;
5651
+ while (active?.shadowRoot?.activeElement) active = active.shadowRoot.activeElement;
5652
+ if (focused && (active === document.body || active === root.host)) this._trigger.focus();
5653
+ if (this._tabindex === null) this.el.removeAttribute("tabindex");
5654
+ else this.el.setAttribute("tabindex", this._tabindex);
5407
5655
  this.el["Expressive_Lightbox"] = void 0;
5408
- this.placeholder.remove();
5409
- this.el.removeAttribute("style");
5656
+ }
5657
+ _reset() {
5658
+ window.removeEventListener("scroll", this._handleWindowScroll);
5659
+ window.removeEventListener("resize", this._handleWindowResize);
5660
+ window.removeEventListener("keyup", this._handleWindowEscape);
5661
+ this._overlay?.remove();
5662
+ this._photoCaption?.remove();
5663
+ for (const ancestor of this._changedAncestorList) {
5664
+ const saved = _Lightbox._overflow.get(ancestor);
5665
+ if (--saved.count === 0) {
5666
+ ancestor.style.setProperty("overflow", saved.value, saved.priority);
5667
+ _Lightbox._overflow.delete(ancestor);
5668
+ }
5669
+ }
5670
+ this._changedAncestorList = [];
5671
+ if (this.attrWidth !== null && this.attrWidth !== void 0) this.el.setAttribute("width", this.attrWidth);
5672
+ if (this.attrHeight !== null && this.attrHeight !== void 0) this.el.setAttribute("height", this.attrHeight);
5673
+ this.el.classList.remove("active");
5674
+ this.placeholder.removeAttribute("style");
5675
+ if (this.originInlineStyles === null) this.el.removeAttribute("style");
5676
+ else this.el.setAttribute("style", this.originInlineStyles);
5677
+ this.overlayActive = false;
5678
+ this._isOpen = false;
5679
+ this.doneAnimating = true;
5680
+ }
5681
+ _cancel() {
5682
+ for (const timer of this._timers.keys()) clearTimeout(timer);
5683
+ this._timers.clear();
5684
+ return ++this._revision;
5685
+ }
5686
+ _schedule(callback, delay) {
5687
+ if (this._motion.matches) {
5688
+ callback();
5689
+ return;
5690
+ }
5691
+ const timer = setTimeout(() => {
5692
+ this._timers.delete(timer);
5693
+ callback();
5694
+ }, delay);
5695
+ this._timers.set(timer, callback);
5410
5696
  }
5411
5697
  _setupEventHandlers() {
5412
- this.el.addEventListener("click", this._handleLightboxClick);
5413
- this.el.addEventListener("keypress", this._handleLightboxKeypress);
5698
+ this._motion.addEventListener("change", this._handleMotionChange);
5699
+ this._trigger.addEventListener("click", this._handleLightboxClick);
5700
+ this.el.addEventListener("keydown", this._handleLightboxKeypress);
5414
5701
  }
5415
5702
  _removeEventHandlers() {
5416
- this.el.removeEventListener("click", this._handleLightboxClick);
5417
- this.el.removeEventListener("keypress", this._handleLightboxKeypress);
5703
+ this._motion.removeEventListener("change", this._handleMotionChange);
5704
+ this._trigger.removeEventListener("click", this._handleLightboxClick);
5705
+ this.el.removeEventListener("keydown", this._handleLightboxKeypress);
5418
5706
  }
5419
5707
  _makeAncestorsOverflowVisible() {
5420
5708
  this._changedAncestorList = [];
5421
5709
  let ancestor = this.placeholder.parentNode;
5422
5710
  while (ancestor !== null && ancestor !== void 0 && ancestor !== document) {
5423
5711
  const curr = ancestor;
5424
- if (curr.style && curr.style.overflow !== "visible") {
5425
- this._changedAncestorList.push([curr, curr.style.overflow]);
5712
+ if (curr.style && (curr.style.overflow !== "visible" || _Lightbox._overflow.has(curr))) {
5713
+ const saved = _Lightbox._overflow.get(curr) ?? { count: 0, value: curr.style.overflow, priority: curr.style.getPropertyPriority("overflow") };
5714
+ saved.count++;
5715
+ _Lightbox._overflow.set(curr, saved);
5716
+ this._changedAncestorList.push(curr);
5426
5717
  curr.style.overflow = "visible";
5427
5718
  }
5428
5719
  ancestor = ancestor.parentNode ?? ancestor.host;
@@ -5445,11 +5736,11 @@ var Expressive = (() => {
5445
5736
  _animateImageIn() {
5446
5737
  this.el.style.maxHeight = this.newHeight.toString() + "px";
5447
5738
  this.el.style.maxWidth = this.newWidth.toString() + "px";
5448
- const duration = this.options.inDuration;
5739
+ const duration = this._motion.matches ? 0 : this.options.inDuration;
5449
5740
  this.el.style.transition = "none";
5450
5741
  this.el.style.height = this.originalHeight + "px";
5451
5742
  this.el.style.width = this.originalWidth + "px";
5452
- setTimeout(() => {
5743
+ this._schedule(() => {
5453
5744
  const placeholderOffset = this._offset(this.placeholder);
5454
5745
  this.el.style.transition = `height ${duration}ms ease,
5455
5746
  width ${duration}ms ease,
@@ -5461,13 +5752,13 @@ var Expressive = (() => {
5461
5752
  this.el.style.left = Utils.getDocumentScrollLeft() + this.windowWidth / 2 - placeholderOffset.left - this.newWidth / 2 + "px";
5462
5753
  this.el.style.top = Utils.getDocumentScrollTop() + this.windowHeight / 2 - placeholderOffset.top - this.newHeight / 2 + "px";
5463
5754
  }, 1);
5464
- setTimeout(() => {
5755
+ this._schedule(() => {
5465
5756
  this.doneAnimating = true;
5466
5757
  if (typeof this.options.onOpenEnd === "function") this.options.onOpenEnd.call(this, this.el);
5467
5758
  }, duration);
5468
5759
  }
5469
5760
  _animateImageOut() {
5470
- const duration = this.options.outDuration;
5761
+ const duration = this._motion.matches ? 0 : this.options.outDuration;
5471
5762
  this.el.style.transition = `height ${duration}ms ease,
5472
5763
  width ${duration}ms ease,
5473
5764
  left ${duration}ms ease,
@@ -5477,19 +5768,8 @@ var Expressive = (() => {
5477
5768
  this.el.style.width = this.originalWidth + "px";
5478
5769
  this.el.style.left = "0";
5479
5770
  this.el.style.top = "0";
5480
- setTimeout(() => {
5481
- this.placeholder.style.height = "";
5482
- this.placeholder.style.width = "";
5483
- this.placeholder.style.position = "";
5484
- this.placeholder.style.top = "";
5485
- this.placeholder.style.left = "";
5486
- if (this.attrWidth) this.el.setAttribute("width", this.attrWidth.toString());
5487
- if (this.attrHeight) this.el.setAttribute("height", this.attrHeight.toString());
5488
- this.el.removeAttribute("style");
5489
- if (this.originInlineStyles) this.el.setAttribute("style", this.originInlineStyles);
5490
- this.el.classList.remove("active");
5491
- this.doneAnimating = true;
5492
- this._changedAncestorList.forEach(([ancestor, overflow]) => ancestor.style.overflow = overflow);
5771
+ this._schedule(() => {
5772
+ this._reset();
5493
5773
  if (typeof this.options.onCloseEnd === "function")
5494
5774
  this.options.onCloseEnd.call(this, this.el);
5495
5775
  }, duration);
@@ -5503,19 +5783,16 @@ var Expressive = (() => {
5503
5783
  this._photoCaption.style.display = "inline";
5504
5784
  this._photoCaption.style.transition = "none";
5505
5785
  this._photoCaption.style.opacity = "0";
5506
- const duration = this.options.inDuration;
5507
- setTimeout(() => {
5786
+ const duration = this._motion.matches ? 0 : this.options.inDuration;
5787
+ this._schedule(() => {
5508
5788
  this._photoCaption.style.transition = `opacity ${duration}ms ease`;
5509
5789
  this._photoCaption.style.opacity = "1";
5510
5790
  }, 1);
5511
5791
  }
5512
5792
  _removeCaption() {
5513
- const duration = this.options.outDuration;
5793
+ const duration = this._motion.matches ? 0 : this.options.outDuration;
5514
5794
  this._photoCaption.style.transition = `opacity ${duration}ms ease`;
5515
5795
  this._photoCaption.style.opacity = "0";
5516
- setTimeout(() => {
5517
- this._photoCaption.remove();
5518
- }, duration);
5519
5796
  }
5520
5797
  // Overlay
5521
5798
  _addOverlay() {
@@ -5523,8 +5800,9 @@ var Expressive = (() => {
5523
5800
  this._overlay.id = "lightbox-overlay";
5524
5801
  this._overlay.addEventListener(
5525
5802
  "click",
5526
- () => {
5527
- if (this.doneAnimating) this.close();
5803
+ (event) => {
5804
+ event.stopPropagation();
5805
+ this.close();
5528
5806
  },
5529
5807
  { once: true }
5530
5808
  );
@@ -5536,22 +5814,20 @@ var Expressive = (() => {
5536
5814
  this._overlay.style.top = -1 * overlayOffset.top + "px";
5537
5815
  this._overlay.style.transition = "none";
5538
5816
  this._overlay.style.opacity = "0";
5539
- const duration = this.options.inDuration;
5540
- setTimeout(() => {
5817
+ const duration = this._motion.matches ? 0 : this.options.inDuration;
5818
+ this._schedule(() => {
5541
5819
  this._overlay.style.transition = `opacity ${duration}ms ease`;
5542
5820
  this._overlay.style.opacity = "1";
5543
5821
  }, 1);
5544
5822
  }
5545
5823
  _removeOverlay() {
5546
- const duration = this.options.outDuration;
5824
+ const duration = this._motion.matches ? 0 : this.options.outDuration;
5547
5825
  this._overlay.style.transition = `opacity ${duration}ms ease`;
5548
5826
  this._overlay.style.opacity = "0";
5549
- setTimeout(() => {
5550
- this.overlayActive = false;
5551
- this._overlay.remove();
5552
- }, duration);
5553
5827
  }
5554
5828
  };
5829
+ _Lightbox._overflow = /* @__PURE__ */ new WeakMap();
5830
+ var Lightbox = _Lightbox;
5555
5831
 
5556
5832
  // src/ts/components/scrollspy.ts
5557
5833
  var _defaultGetActiveElement = (id) => `a[href="#${id}"]`;
@@ -5699,342 +5975,8 @@ var Expressive = (() => {
5699
5975
  };
5700
5976
  var ScrollSpy = _ScrollSpy;
5701
5977
 
5702
- // src/ts/components/navigationDrawer.ts
5703
- var _defaults17 = {
5704
- edge: "left",
5705
- draggable: true,
5706
- dragTargetWidth: "10px",
5707
- onOpenStart: null,
5708
- onOpenEnd: null,
5709
- onCloseStart: null,
5710
- onCloseEnd: null
5711
- };
5712
- var EXPANDED_UP = "(width >= 840px)";
5713
- var _NavigationDrawer = class _NavigationDrawer extends Component {
5714
- constructor(el, options) {
5715
- super(el, options, _NavigationDrawer);
5716
- /**
5717
- * Opens the overlay drawer. No-op while the sidenav is docked
5718
- * (navigation-drawer-fixed at the Expanded breakpoint).
5719
- */
5720
- this.open = () => {
5721
- if (this._isCurrentlyFixed() || this.isOpen || !this._dialog) return;
5722
- _NavigationDrawer._sidenavs.forEach((other) => {
5723
- if (other !== this && other.isOpen) other.close();
5724
- });
5725
- if (typeof this.options.onOpenStart === "function") {
5726
- this.options.onOpenStart.call(this, this.el);
5727
- }
5728
- this.isOpen = true;
5729
- this._dialog.classList.remove("dragging");
5730
- this._clearShift();
5731
- if (!this._dialog.open) {
5732
- try {
5733
- this._dialog.showModal();
5734
- } catch {
5735
- }
5736
- }
5737
- this._syncTriggers();
5738
- if (typeof this.options.onOpenEnd === "function") {
5739
- this.options.onOpenEnd.call(this, this.el);
5740
- }
5741
- };
5742
- /**
5743
- * Closes the overlay drawer. No-op while the sidenav is docked.
5744
- */
5745
- this.close = () => {
5746
- if (this._isCurrentlyFixed() || !this.isOpen) return;
5747
- this._beginClose();
5748
- if (this._dialog?.open) {
5749
- this._closing = true;
5750
- this._dialog.close();
5751
- } else {
5752
- this._finishClose();
5753
- }
5754
- };
5755
- this._onDialogClose = () => {
5756
- if (this.isOpen) {
5757
- this._beginClose();
5758
- this._finishClose();
5759
- return;
5760
- }
5761
- if (this._closing) {
5762
- this._closing = false;
5763
- this._finishClose();
5764
- }
5765
- };
5766
- this._onBreakpointChange = () => {
5767
- if (this._isCurrentlyFixed() && this.isOpen) {
5768
- this.close();
5769
- }
5770
- };
5771
- this._handleCloseTriggerClick = (e) => {
5772
- const target = e.target;
5773
- if (!(target instanceof Element)) return;
5774
- const closeTrigger = target.closest(".sidenav-close, .navigation-drawer-close");
5775
- if (closeTrigger && !this._isCurrentlyFixed()) {
5776
- this.close();
5777
- }
5778
- };
5779
- this._handleDragTargetDrag = (e) => {
5780
- if (!this._isDraggable()) return;
5781
- const delta = this._rawDelta(e);
5782
- const towardOpen = this.options.edge === "left" ? delta : -delta;
5783
- const visible = Math.min(this._width, Math.max(0, towardOpen));
5784
- const percentOpen = this._width ? visible / this._width : 0;
5785
- this._applyShift(visible - this._width, percentOpen);
5786
- };
5787
- this._handleDragTargetRelease = () => {
5788
- if (!this.isDragged) return;
5789
- this._dialog?.classList.remove("dragging");
5790
- if (this.percentOpen > 0.2) {
5791
- this.open();
5792
- } else if (this._dialog?.open && !this.isOpen) {
5793
- this._clearShift();
5794
- this._dialog.close();
5795
- }
5796
- this.isDragged = false;
5797
- this._verticallyScrolling = false;
5798
- };
5799
- this._handleCloseDrag = (e) => {
5800
- if (!this.isOpen || !this._isDraggable()) return;
5801
- const delta = this._rawDelta(e);
5802
- const towardClose = this.options.edge === "left" ? -delta : delta;
5803
- const hidden = Math.min(this._width, Math.max(0, towardClose));
5804
- const percentOpen = this._width ? 1 - hidden / this._width : 1;
5805
- this._applyShift(-hidden, percentOpen);
5806
- };
5807
- this._handleCloseRelease = () => {
5808
- if (!this.isOpen || !this.isDragged) return;
5809
- this._dialog?.classList.remove("dragging");
5810
- if (this.percentOpen > 0.8) {
5811
- this._clearShift();
5812
- } else {
5813
- this.close();
5814
- }
5815
- this.isDragged = false;
5816
- this._verticallyScrolling = false;
5817
- };
5818
- this.el["Expressive_NavigationDrawer"] = this;
5819
- this.options = {
5820
- ..._NavigationDrawer.defaults,
5821
- ...options
5822
- };
5823
- this.id = this.el.id;
5824
- this.isOpen = false;
5825
- this.isFixed = this.el.classList.contains("navigation-drawer-fixed") || this.el.classList.contains("sidenav-fixed");
5826
- this.isDragged = false;
5827
- this.dragTarget = null;
5828
- this._dialog = null;
5829
- this._createdDialog = false;
5830
- this._closing = false;
5831
- this.percentOpen = 0;
5832
- this._startingXpos = 0;
5833
- this._xPos = 0;
5834
- this._width = 0;
5835
- this._initialScrollTop = 0;
5836
- this._verticallyScrolling = false;
5837
- this._mql = window.matchMedia(EXPANDED_UP);
5838
- this._setupDialog();
5839
- this._createDragTarget();
5840
- this._setupClasses();
5841
- this._setupEventHandlers();
5842
- this._syncTriggers();
5843
- _NavigationDrawer._sidenavs.push(this);
5844
- }
5845
- static get defaults() {
5846
- return _defaults17;
5847
- }
5848
- /**
5849
- * Initializes instances of NavigationDrawer.
5850
- * @param els HTML elements.
5851
- * @param options Component options.
5852
- */
5853
- static init(els, options = {}) {
5854
- return super.init(els, options, _NavigationDrawer);
5855
- }
5856
- static getInstance(el) {
5857
- return el["Expressive_NavigationDrawer"];
5858
- }
5859
- destroy() {
5860
- this._removeEventHandlers();
5861
- if (this._dialog?.open) {
5862
- try {
5863
- this._dialog.close();
5864
- } catch {
5865
- }
5866
- }
5867
- this._clearShift();
5868
- this._unwrapDialog();
5869
- this._removeDragTarget();
5870
- this.el["Expressive_NavigationDrawer"] = void 0;
5871
- const index = _NavigationDrawer._sidenavs.indexOf(this);
5872
- if (index >= 0) {
5873
- _NavigationDrawer._sidenavs.splice(index, 1);
5874
- }
5875
- }
5876
- _beginClose() {
5877
- this.isOpen = false;
5878
- this._dialog?.classList.remove("dragging");
5879
- this._clearShift();
5880
- if (typeof this.options.onCloseStart === "function") {
5881
- this.options.onCloseStart.call(this, this.el);
5882
- }
5883
- this._syncTriggers();
5884
- }
5885
- _finishClose() {
5886
- if (typeof this.options.onCloseEnd === "function") {
5887
- this.options.onCloseEnd.call(this, this.el);
5888
- }
5889
- }
5890
- _setupDialog() {
5891
- if (this.el instanceof HTMLDialogElement) {
5892
- this._dialog = this.el;
5893
- this._createdDialog = false;
5894
- return;
5895
- }
5896
- const parent = this.el.parentElement;
5897
- if (parent instanceof HTMLDialogElement && parent.classList.contains("sidenav-overlay")) {
5898
- this._dialog = parent;
5899
- this._createdDialog = false;
5900
- return;
5901
- }
5902
- const dialog = document.createElement("dialog");
5903
- dialog.classList.add("sidenav-overlay");
5904
- const named = this.el.closest("[aria-label], [aria-labelledby]") ?? this.el;
5905
- const label = named.getAttribute("aria-label");
5906
- const labelledBy = named.getAttribute("aria-labelledby");
5907
- if (label) dialog.setAttribute("aria-label", label);
5908
- else if (labelledBy) dialog.setAttribute("aria-labelledby", labelledBy);
5909
- this.el.replaceWith(dialog);
5910
- dialog.appendChild(this.el);
5911
- this._dialog = dialog;
5912
- this._createdDialog = true;
5913
- }
5914
- _unwrapDialog() {
5915
- if (!this._createdDialog || !this._dialog) return;
5916
- this._dialog.replaceWith(this.el);
5917
- this._dialog = null;
5918
- this._createdDialog = false;
5919
- }
5920
- _createDragTarget() {
5921
- if (!this.options.draggable) return;
5922
- const dragTarget = document.createElement("div");
5923
- dragTarget.classList.add("drag-target");
5924
- dragTarget.style.width = this.options.dragTargetWidth;
5925
- if (this.isFixed) dragTarget.dataset.fixed = "";
5926
- Utils.portalRoot(this.el).appendChild(dragTarget);
5927
- this.dragTarget = dragTarget;
5928
- }
5929
- _removeDragTarget() {
5930
- this.dragTarget?.remove();
5931
- this.dragTarget = null;
5932
- }
5933
- _setupClasses() {
5934
- if (this.options.edge === "right") {
5935
- this.el.classList.add("right-aligned");
5936
- this._dialog?.classList.add("right-aligned");
5937
- this.dragTarget?.classList.add("right-aligned");
5938
- }
5939
- }
5940
- _setupEventHandlers() {
5941
- if (_NavigationDrawer._sidenavs.length === 0) {
5942
- document.body.addEventListener("click", _NavigationDrawer._handleTriggerClick);
5943
- }
5944
- const passive = { passive: true };
5945
- this.dragTarget?.addEventListener("touchmove", this._handleDragTargetDrag, passive);
5946
- this.dragTarget?.addEventListener("touchend", this._handleDragTargetRelease);
5947
- this._dialog?.addEventListener("touchmove", this._handleCloseDrag, passive);
5948
- this._dialog?.addEventListener("touchend", this._handleCloseRelease);
5949
- this._dialog?.addEventListener("close", this._onDialogClose);
5950
- this.el.addEventListener("click", this._handleCloseTriggerClick);
5951
- this._mql.addEventListener("change", this._onBreakpointChange);
5952
- }
5953
- _removeEventHandlers() {
5954
- if (_NavigationDrawer._sidenavs.length === 1) {
5955
- document.body.removeEventListener("click", _NavigationDrawer._handleTriggerClick);
5956
- }
5957
- this.dragTarget?.removeEventListener("touchmove", this._handleDragTargetDrag);
5958
- this.dragTarget?.removeEventListener("touchend", this._handleDragTargetRelease);
5959
- this._dialog?.removeEventListener("touchmove", this._handleCloseDrag);
5960
- this._dialog?.removeEventListener("touchend", this._handleCloseRelease);
5961
- this._dialog?.removeEventListener("close", this._onDialogClose);
5962
- this.el.removeEventListener("click", this._handleCloseTriggerClick);
5963
- this._mql.removeEventListener("change", this._onBreakpointChange);
5964
- }
5965
- static _handleTriggerClick(e) {
5966
- const target = e.target;
5967
- if (!(target instanceof Element)) return;
5968
- const trigger = target.closest(".sidenav-trigger, .navigation-drawer-trigger");
5969
- if (!trigger || !(trigger instanceof HTMLElement)) return;
5970
- const sidenavId = Utils.getIdFromTrigger(trigger);
5971
- const sidenavInstance = Utils.getElementById(trigger, sidenavId)?.["Expressive_NavigationDrawer"];
5972
- if (sidenavInstance) {
5973
- sidenavInstance.open();
5974
- e.preventDefault();
5975
- }
5976
- }
5977
- _isDraggable() {
5978
- return this.options.draggable && !this._isCurrentlyFixed() && !this._verticallyScrolling;
5979
- }
5980
- _isCurrentlyFixed() {
5981
- return this.isFixed && this._mql.matches;
5982
- }
5983
- _startDrag(e) {
5984
- const clientX = e.targetTouches[0].clientX;
5985
- this.isDragged = true;
5986
- this._startingXpos = clientX;
5987
- this._xPos = clientX;
5988
- this._width = this._dialog?.getBoundingClientRect().width || this.el.getBoundingClientRect().width;
5989
- this._initialScrollTop = this.isOpen ? this.el.scrollTop : Utils.getDocumentScrollTop();
5990
- this._verticallyScrolling = false;
5991
- this._dialog?.classList.add("dragging");
5992
- if (this._dialog && !this._dialog.open) {
5993
- try {
5994
- this._dialog.showModal();
5995
- } catch {
5996
- }
5997
- }
5998
- }
5999
- _dragMoveUpdate(e) {
6000
- const clientX = e.targetTouches[0].clientX;
6001
- const currentScrollTop = this.isOpen ? this.el.scrollTop : Utils.getDocumentScrollTop();
6002
- this._xPos = clientX;
6003
- if (this._initialScrollTop !== currentScrollTop) {
6004
- this._verticallyScrolling = true;
6005
- }
6006
- }
6007
- _rawDelta(e) {
6008
- if (!this.isDragged) this._startDrag(e);
6009
- this._dragMoveUpdate(e);
6010
- return this._xPos - this._startingXpos;
6011
- }
6012
- _applyShift(shift, percentOpen) {
6013
- this.percentOpen = percentOpen;
6014
- const host = this._dialog ?? this.el;
6015
- host.style.setProperty("--md-comp-nav-drawer-shift", `${shift}px`);
6016
- host.style.setProperty("--md-comp-nav-drawer-open", String(percentOpen));
6017
- }
6018
- _clearShift() {
6019
- const host = this._dialog ?? this.el;
6020
- host.style.removeProperty("--md-comp-nav-drawer-shift");
6021
- host.style.removeProperty("--md-comp-nav-drawer-open");
6022
- }
6023
- _syncTriggers() {
6024
- if (!this.id) return;
6025
- document.querySelectorAll(".sidenav-trigger, .navigation-drawer-trigger").forEach((trigger) => {
6026
- if (!(trigger instanceof HTMLElement)) return;
6027
- if (Utils.getIdFromTrigger(trigger) !== this.id) return;
6028
- trigger.setAttribute("aria-expanded", this.isOpen ? "true" : "false");
6029
- trigger.setAttribute("aria-controls", this.id);
6030
- });
6031
- }
6032
- };
6033
- _NavigationDrawer._sidenavs = [];
6034
- var NavigationDrawer = _NavigationDrawer;
6035
-
6036
5978
  // src/ts/components/navigationRail.ts
6037
- var _defaults18 = {
5979
+ var _defaults17 = {
6038
5980
  onOpenStart: null,
6039
5981
  onOpenEnd: null,
6040
5982
  onCloseStart: null,
@@ -6110,7 +6052,7 @@ var Expressive = (() => {
6110
6052
  document.addEventListener("keydown", this._onKeyDown);
6111
6053
  }
6112
6054
  static get defaults() {
6113
- return _defaults18;
6055
+ return _defaults17;
6114
6056
  }
6115
6057
  static init(els, options = {}) {
6116
6058
  return super.init(els, options, _NavigationRail);
@@ -6140,7 +6082,7 @@ var Expressive = (() => {
6140
6082
  };
6141
6083
 
6142
6084
  // src/ts/components/tabs.ts
6143
- var _defaults19 = {
6085
+ var _defaults18 = {
6144
6086
  duration: 300,
6145
6087
  onShow: null,
6146
6088
  swipeable: false,
@@ -6149,7 +6091,10 @@ var Expressive = (() => {
6149
6091
  };
6150
6092
  var Tabs = class _Tabs extends Component {
6151
6093
  constructor(el, options) {
6094
+ var _a;
6152
6095
  super(el, options, _Tabs);
6096
+ this._originalPanels = /* @__PURE__ */ new Map();
6097
+ this._originalLinks = /* @__PURE__ */ new Map();
6153
6098
  this._handleWindowResize = () => {
6154
6099
  this._setTabsAndTabWidth();
6155
6100
  if (this._tabWidth !== 0 && this._tabsWidth !== 0) {
@@ -6169,29 +6114,26 @@ var Expressive = (() => {
6169
6114
  return;
6170
6115
  }
6171
6116
  if (tabLink.hasAttribute("target")) return;
6117
+ if (this.options.swipeable) {
6118
+ const panel = tabLink.hash && document.querySelector(tabLink.hash);
6119
+ const index = this._tabsCarousel.images.indexOf(panel);
6120
+ if (index >= 0) this._tabsCarousel.set(index);
6121
+ e.preventDefault();
6122
+ return;
6123
+ }
6172
6124
  const _oldContent = this._content;
6173
6125
  if (tabLink.hash) this._content = document.querySelector(tabLink.hash);
6174
6126
  this._tabLinks = this._queryTabLinks();
6175
- this._setActiveTabLink(tabLink);
6176
6127
  const prevIndex = this._index;
6177
- this._index = Math.max(Array.from(this._tabLinks).indexOf(tabLink), 0);
6178
- if (this.options.swipeable) {
6179
- if (this._tabsCarousel) {
6180
- this._tabsCarousel.set(this._index, () => {
6181
- if (typeof this.options.onShow === "function")
6182
- this.options.onShow.call(this, this._content);
6183
- });
6184
- }
6185
- } else {
6186
- if (this._content) {
6187
- this._content.style.display = "block";
6188
- this._content.classList.add("active");
6189
- if (typeof this.options.onShow === "function")
6190
- this.options.onShow.call(this, this._content);
6191
- if (_oldContent && _oldContent !== this._content) {
6192
- _oldContent.style.display = "none";
6193
- _oldContent.classList.remove("active");
6194
- }
6128
+ this._setActiveTabLink(tabLink);
6129
+ if (this._content) {
6130
+ this._content.style.display = "block";
6131
+ this._content.classList.add("active");
6132
+ if (typeof this.options.onShow === "function")
6133
+ this.options.onShow.call(this, this._content);
6134
+ if (_oldContent && _oldContent !== this._content) {
6135
+ _oldContent.style.display = "none";
6136
+ _oldContent.classList.remove("active");
6195
6137
  }
6196
6138
  }
6197
6139
  this._setTabsAndTabWidth();
@@ -6204,6 +6146,18 @@ var Expressive = (() => {
6204
6146
  ...options
6205
6147
  };
6206
6148
  this._tabLinks = this._queryTabLinks();
6149
+ this._tabLinks.forEach((link) => {
6150
+ this._originalLinks.set(link, { active: link.classList.contains("active"), current: link.getAttribute("aria-current") });
6151
+ if (link.hasAttribute("target")) return;
6152
+ const panel = link.hash && document.querySelector(link.hash);
6153
+ if (panel) this._originalPanels.set(panel, {
6154
+ display: panel.style.display,
6155
+ priority: panel.style.getPropertyPriority("display"),
6156
+ active: panel.classList.contains("active"),
6157
+ carousel: panel.classList.contains("carousel-item")
6158
+ });
6159
+ });
6160
+ (_a = this.options).swipeable && (_a.swipeable = window.innerWidth <= this.options.responsiveThreshold);
6207
6161
  this._index = 0;
6208
6162
  this._setupActiveTabLink();
6209
6163
  if (this.options.swipeable) {
@@ -6216,7 +6170,7 @@ var Expressive = (() => {
6216
6170
  this._setupEventHandlers();
6217
6171
  }
6218
6172
  static get defaults() {
6219
- return _defaults19;
6173
+ return _defaults18;
6220
6174
  }
6221
6175
  /**
6222
6176
  * Initializes instances of Tabs.
@@ -6231,12 +6185,21 @@ var Expressive = (() => {
6231
6185
  }
6232
6186
  destroy() {
6233
6187
  this._removeEventHandlers();
6234
- this._indicator.parentNode.removeChild(this._indicator);
6188
+ this._indicator.remove();
6235
6189
  if (this.options.swipeable) {
6236
6190
  this._teardownSwipeableTabs();
6237
- } else {
6238
- this._teardownNormalTabs();
6239
6191
  }
6192
+ this._originalPanels.forEach((original, panel) => {
6193
+ panel.style.setProperty("display", original.display, original.priority);
6194
+ panel.classList.toggle("active", original.active);
6195
+ panel.classList.toggle("carousel-item", original.carousel);
6196
+ if (!panel.className) panel.removeAttribute("class");
6197
+ });
6198
+ this._originalLinks.forEach((original, link) => {
6199
+ link.classList.toggle("active", original.active);
6200
+ if (original.current === null) link.removeAttribute("aria-current");
6201
+ else link.setAttribute("aria-current", original.current);
6202
+ });
6240
6203
  this.el["Expressive_Tabs"] = void 0;
6241
6204
  }
6242
6205
  /**
@@ -6286,6 +6249,8 @@ var Expressive = (() => {
6286
6249
  this._activeTabLink = tabLink;
6287
6250
  tabLink.classList.add("active");
6288
6251
  tabLink.setAttribute("aria-current", "page");
6252
+ this._index = Array.from(this._tabLinks).indexOf(tabLink);
6253
+ this._content = tabLink.hasAttribute("target") ? null : document.getElementById(tabLink.hash.slice(1));
6289
6254
  }
6290
6255
  _setupActiveTabLink() {
6291
6256
  this._activeTabLink = Array.from(this._tabLinks).find(
@@ -6299,18 +6264,14 @@ var Expressive = (() => {
6299
6264
  this._activeTabLink = activeTabLink;
6300
6265
  }
6301
6266
  this._setActiveTabLink(this._activeTabLink);
6302
- this._index = Math.max(Array.from(this._tabLinks).indexOf(this._activeTabLink), 0);
6303
- if (this._activeTabLink && this._activeTabLink.hash) {
6304
- this._content = document.querySelector(this._activeTabLink.hash);
6305
- if (this._content) this._content.classList.add("active");
6306
- }
6267
+ this._originalPanels.forEach((_, panel) => panel.classList.toggle("active", panel === this._content));
6307
6268
  }
6308
6269
  _setupSwipeableTabs() {
6309
- if (window.innerWidth > this.options.responsiveThreshold) this.options.swipeable = false;
6310
6270
  const tabsContent = [];
6311
6271
  this._tabLinks.forEach((a) => {
6312
- if (a.hash) {
6272
+ if (a.hash && !a.hasAttribute("target")) {
6313
6273
  const currContent = document.querySelector(a.hash);
6274
+ if (!currContent) return;
6314
6275
  currContent.classList.add("carousel-item");
6315
6276
  tabsContent.push(currContent);
6316
6277
  }
@@ -6319,48 +6280,41 @@ var Expressive = (() => {
6319
6280
  tabsWrapper.classList.add("tabs-content", "carousel", "flat");
6320
6281
  tabsContent[0].parentElement.insertBefore(tabsWrapper, tabsContent[0]);
6321
6282
  tabsContent.forEach((tabContent) => {
6283
+ const marker = document.createComment("");
6284
+ tabContent.before(marker);
6285
+ this._originalPanels.get(tabContent).marker = marker;
6322
6286
  tabsWrapper.appendChild(tabContent);
6323
6287
  tabContent.style.display = "";
6324
6288
  });
6325
6289
  this._tabsCarousel = Carousel.init(tabsWrapper, {
6326
6290
  fullWidth: true,
6327
6291
  noWrap: true,
6292
+ duration: this.options.duration,
6328
6293
  onCycleTo: (item) => {
6329
6294
  const prevIndex = this._index;
6330
- this._index = Array.from(item.parentNode.children).indexOf(item);
6331
- this._activeTabLink.classList.remove("active");
6332
- this._activeTabLink = Array.from(this._tabLinks)[this._index];
6333
- this._activeTabLink.classList.add("active");
6295
+ this._setActiveTabLink(Array.from(this._tabLinks).find((a) => !a.hasAttribute("target") && a.hash === "#" + item.id));
6296
+ this._setTabsAndTabWidth();
6334
6297
  this._animateIndicator(prevIndex);
6335
6298
  if (typeof this.options.onShow === "function")
6336
6299
  this.options.onShow.call(this, this._content);
6337
6300
  }
6338
6301
  });
6339
- this._tabsCarousel.set(this._index);
6340
6302
  }
6341
6303
  _teardownSwipeableTabs() {
6342
6304
  const tabsWrapper = this._tabsCarousel.el;
6343
6305
  this._tabsCarousel.destroy();
6344
- tabsWrapper.append(tabsWrapper.parentElement);
6306
+ this._originalPanels.forEach((original, panel) => original.marker?.replaceWith(panel));
6345
6307
  tabsWrapper.remove();
6346
6308
  }
6347
6309
  _setupNormalTabs() {
6348
6310
  Array.from(this._tabLinks).forEach((a) => {
6349
- if (a === this._activeTabLink) return;
6311
+ if (a === this._activeTabLink || a.hasAttribute("target")) return;
6350
6312
  if (a.hash) {
6351
6313
  const currContent = document.querySelector(a.hash);
6352
6314
  if (currContent) currContent.style.display = "none";
6353
6315
  }
6354
6316
  });
6355
6317
  }
6356
- _teardownNormalTabs() {
6357
- this._tabLinks.forEach((a) => {
6358
- if (a.hash) {
6359
- const currContent = document.querySelector(a.hash);
6360
- if (currContent) currContent.style.display = "";
6361
- }
6362
- });
6363
- }
6364
6318
  _setTabsAndTabWidth() {
6365
6319
  this._tabsWidth = this.el.getBoundingClientRect().width;
6366
6320
  this._tabWidth = Math.max(this._tabsWidth, this.el.scrollWidth) / this._tabLinks.length;
@@ -6403,7 +6357,7 @@ var Expressive = (() => {
6403
6357
  };
6404
6358
 
6405
6359
  // src/ts/components/timepicker.ts
6406
- var _defaults20 = {
6360
+ var _defaults19 = {
6407
6361
  dialRadius: 135,
6408
6362
  outerRadius: 105,
6409
6363
  innerRadius: 70,
@@ -6420,7 +6374,9 @@ var Expressive = (() => {
6420
6374
  i18n: {
6421
6375
  cancel: "Cancel",
6422
6376
  clear: "Clear",
6423
- done: "Ok"
6377
+ done: "Ok",
6378
+ hours: "Hours",
6379
+ minutes: "Minutes"
6424
6380
  },
6425
6381
  twelveHour: true,
6426
6382
  // change to 12 hour AM/PM clock from 24 hour
@@ -6443,17 +6399,17 @@ var Expressive = (() => {
6443
6399
  this._clockBuilt = false;
6444
6400
  this._handleInputClick = () => {
6445
6401
  this._ensureClockBuilt();
6402
+ if (this.displayPlugin) this.displayPlugin.show();
6446
6403
  this.inputHours.focus();
6447
6404
  if (typeof this.options.onInputInteraction === "function") this.options.onInputInteraction.call(this);
6448
- if (this.displayPlugin) this.displayPlugin.show();
6449
6405
  };
6450
6406
  this._handleInputKeydown = (e) => {
6451
6407
  if (e.key === Utils.keys.ENTER) {
6452
6408
  e.preventDefault();
6453
6409
  this._ensureClockBuilt();
6410
+ if (this.displayPlugin) this.displayPlugin.show();
6454
6411
  this.inputHours.focus();
6455
6412
  if (typeof this.options.onInputInteraction === "function") this.options.onInputInteraction.call(this);
6456
- if (this.displayPlugin) this.displayPlugin.show();
6457
6413
  }
6458
6414
  };
6459
6415
  this._handleClockClickStart = (e) => {
@@ -6523,11 +6479,6 @@ var Expressive = (() => {
6523
6479
  this._handleAmPmClick = (e) => {
6524
6480
  this._handleAmPmInteraction(e.target);
6525
6481
  };
6526
- this._handleAmPmKeypress = (e) => {
6527
- if (e.key === Utils.keys.ENTER) {
6528
- this._handleAmPmInteraction(e.target);
6529
- }
6530
- };
6531
6482
  this._handleAmPmInteraction = (e) => {
6532
6483
  this.amOrPm = e.classList.contains("am-btn") ? "AM" : "PM";
6533
6484
  this._updateAmPmView();
@@ -6550,26 +6501,16 @@ var Expressive = (() => {
6550
6501
  hideView.style.visibility = "hidden";
6551
6502
  }, this.options.duration);
6552
6503
  };
6553
- this._inputFromTextField = () => {
6554
- const isHours = this.currentView === "hours";
6555
- if (isHours && this.inputHours.value !== "") {
6556
- const value = parseInt(this.inputHours.value);
6557
- if (value > 0 && value < (this.options.twelveHour ? 13 : 24)) {
6558
- this.hours = value;
6559
- } else {
6560
- this.setHoursDefault();
6561
- }
6562
- this.drawClockFromTimeInput(this.hours, isHours);
6563
- } else if (!isHours && this.inputMinutes.value !== "") {
6564
- const value = parseInt(this.inputMinutes.value);
6565
- if (value >= 0 && value < 60) {
6566
- this.minutes = value;
6567
- } else {
6568
- this.minutes = (/* @__PURE__ */ new Date()).getMinutes();
6569
- this.inputMinutes.value = this.minutes.toString();
6570
- }
6571
- this.drawClockFromTimeInput(this.minutes, isHours);
6572
- }
6504
+ this._inputFromTextField = (event) => {
6505
+ const input = event.target;
6506
+ const isHours = input === this.inputHours;
6507
+ if (!isHours && input !== this.inputMinutes) return;
6508
+ const value = Number(input.value);
6509
+ const min = isHours && this.options.twelveHour ? 1 : 0;
6510
+ const max = isHours ? this.options.twelveHour ? 12 : 23 : 59;
6511
+ if (!input.value.trim() || !Number.isInteger(value) || value < min || value > max) return;
6512
+ this[isHours ? "hours" : "minutes"] = value;
6513
+ this.drawClockFromTimeInput(value, isHours);
6573
6514
  };
6574
6515
  this.done = (clearValue = null) => {
6575
6516
  const last = this.el.value;
@@ -6598,7 +6539,8 @@ var Expressive = (() => {
6598
6539
  this.el["Expressive_Timepicker"] = this;
6599
6540
  this.options = {
6600
6541
  ..._Timepicker.defaults,
6601
- ...options
6542
+ ...options,
6543
+ i18n: { ..._Timepicker.defaults.i18n, ...options?.i18n }
6602
6544
  };
6603
6545
  this.id = Utils.guid();
6604
6546
  this._insertHTMLIntoDOM();
@@ -6610,7 +6552,7 @@ var Expressive = (() => {
6610
6552
  }
6611
6553
  }
6612
6554
  static get defaults() {
6613
- return _defaults20;
6555
+ return _defaults19;
6614
6556
  }
6615
6557
  /**
6616
6558
  * Initializes instances of Timepicker.
@@ -6640,13 +6582,14 @@ var Expressive = (() => {
6640
6582
  this.el.addEventListener("click", this._handleInputClick);
6641
6583
  this.el.addEventListener("keydown", this._handleInputKeydown);
6642
6584
  this.plate.addEventListener("pointerdown", this._handleClockClickStart);
6643
- this.digitalClock.addEventListener("keyup", this._inputFromTextField);
6585
+ this.digitalClock.addEventListener("input", this._inputFromTextField);
6644
6586
  this.inputHours.addEventListener("focus", () => this.showView("hours"));
6645
6587
  this.inputHours.addEventListener("focusout", () => this.formatHours());
6646
6588
  this.inputMinutes.addEventListener("focus", () => this.showView("minutes"));
6647
6589
  this.inputMinutes.addEventListener("focusout", () => this.formatMinutes());
6648
6590
  }
6649
6591
  _removeEventHandlers() {
6592
+ this.digitalClock.removeEventListener("input", this._inputFromTextField);
6650
6593
  this.el.removeEventListener("click", this._handleInputClick);
6651
6594
  this.el.removeEventListener("keydown", this._handleInputKeydown);
6652
6595
  document.removeEventListener("pointermove", this._handleDocumentClickMove);
@@ -6676,6 +6619,8 @@ var Expressive = (() => {
6676
6619
  this.minutesView = this.containerEl.querySelector(".timepicker-minutes");
6677
6620
  this.inputHours = this.containerEl.querySelector(".timepicker-input-hours");
6678
6621
  this.inputMinutes = this.containerEl.querySelector(".timepicker-input-minutes");
6622
+ this.inputHours.setAttribute("aria-label", this.options.i18n.hours);
6623
+ this.inputMinutes.setAttribute("aria-label", this.options.i18n.minutes);
6679
6624
  this.spanAmPm = this.containerEl.querySelector(".timepicker-span-am-pm");
6680
6625
  this.footer = this.containerEl.querySelector(".timepicker-footer");
6681
6626
  this.amOrPm = "PM";
@@ -6713,19 +6658,17 @@ var Expressive = (() => {
6713
6658
  }
6714
6659
  _clockSetup() {
6715
6660
  if (this.options.twelveHour) {
6716
- this._amBtn = document.createElement("div");
6661
+ this._amBtn = document.createElement("button");
6662
+ this._amBtn.type = "button";
6717
6663
  this._amBtn.classList.add("am-btn");
6718
6664
  this._amBtn.innerText = "AM";
6719
- this._amBtn.tabIndex = 0;
6720
6665
  this._amBtn.addEventListener("click", this._handleAmPmClick);
6721
- this._amBtn.addEventListener("keypress", this._handleAmPmKeypress);
6722
6666
  this.spanAmPm.appendChild(this._amBtn);
6723
- this._pmBtn = document.createElement("div");
6667
+ this._pmBtn = document.createElement("button");
6668
+ this._pmBtn.type = "button";
6724
6669
  this._pmBtn.classList.add("pm-btn");
6725
6670
  this._pmBtn.innerText = "PM";
6726
- this._pmBtn.tabIndex = 0;
6727
6671
  this._pmBtn.addEventListener("click", this._handleAmPmClick);
6728
- this._pmBtn.addEventListener("keypress", this._handleAmPmKeypress);
6729
6672
  this.spanAmPm.appendChild(this._pmBtn);
6730
6673
  }
6731
6674
  this._buildHoursView();
@@ -6801,6 +6744,10 @@ var Expressive = (() => {
6801
6744
  }
6802
6745
  _updateAmPmView() {
6803
6746
  if (!this._amBtn || !this._pmBtn) return;
6747
+ this._amBtn.setAttribute("aria-pressed", String(this.amOrPm === "AM"));
6748
+ this._pmBtn.setAttribute("aria-pressed", String(this.amOrPm === "PM"));
6749
+ this._amBtn.classList.toggle("outlined", this.amOrPm !== "AM");
6750
+ this._pmBtn.classList.toggle("outlined", this.amOrPm !== "PM");
6804
6751
  if (this.options.twelveHour) {
6805
6752
  if (this.amOrPm === "PM") {
6806
6753
  this._amBtn.classList.remove("filled");
@@ -6812,24 +6759,19 @@ var Expressive = (() => {
6812
6759
  }
6813
6760
  }
6814
6761
  _updateTimeFromInput() {
6815
- let value = ((this.el.value || this.options.defaultTime || "") + "").split(":");
6816
- if (this.options.twelveHour && !(typeof value[1] === "undefined")) {
6817
- if (value[1].toUpperCase().indexOf("AM") > 0) {
6818
- this.amOrPm = "AM";
6819
- } else {
6820
- this.amOrPm = "PM";
6821
- }
6822
- value[1] = value[1].replace("AM", "").replace("PM", "");
6823
- }
6824
- if (value[0] === "now") {
6762
+ let value = ((this.el.value || this.options.defaultTime || "") + "").trim().toUpperCase().split(":");
6763
+ const period = value[1]?.match(/AM|PM/)?.[0];
6764
+ value[1] = value[1]?.replace(/AM|PM/, "");
6765
+ if (value[0] === "NOW") {
6825
6766
  const now = new Date(+/* @__PURE__ */ new Date() + this.options.fromNow);
6826
6767
  value = [now.getHours().toString(), now.getMinutes().toString()];
6827
- if (this.options.twelveHour) {
6828
- this.amOrPm = parseInt(value[0]) >= 12 && parseInt(value[0]) < 24 ? "PM" : "AM";
6829
- }
6830
6768
  }
6831
6769
  this.hours = +value[0] || 0;
6832
6770
  this.minutes = +value[1] || 0;
6771
+ if (this.options.twelveHour) {
6772
+ this.amOrPm = period === "AM" || period === "PM" ? period : this.hours >= 12 ? "PM" : "AM";
6773
+ this.hours = this.hours % 12 || 12;
6774
+ }
6833
6775
  this.inputHours.value = _Timepicker._addLeadingZero(this.hours);
6834
6776
  this.inputMinutes.value = _Timepicker._addLeadingZero(this.minutes);
6835
6777
  this._updateAmPmView();
@@ -6918,15 +6860,16 @@ var Expressive = (() => {
6918
6860
  }
6919
6861
  formatHours() {
6920
6862
  if (this.inputHours.value == "") this.setHoursDefault();
6921
- this.inputHours.value = _Timepicker._addLeadingZero(Number(this.inputHours.value));
6863
+ this.inputHours.value = _Timepicker._addLeadingZero(this.hours);
6922
6864
  }
6923
6865
  formatMinutes() {
6924
6866
  if (this.inputMinutes.value == "") this.minutes = (/* @__PURE__ */ new Date()).getMinutes();
6925
- this.inputMinutes.value = _Timepicker._addLeadingZero(Number(this.inputMinutes.value));
6867
+ this.inputMinutes.value = _Timepicker._addLeadingZero(this.minutes);
6926
6868
  }
6927
6869
  setHoursDefault() {
6928
6870
  this.hours = (/* @__PURE__ */ new Date()).getHours();
6929
- this.inputHours.value = (this.hours % (this.options.twelveHour ? 12 : 24)).toString();
6871
+ if (this.options.twelveHour) this.hours = this.hours % 12 || 12;
6872
+ this.inputHours.value = this.hours.toString();
6930
6873
  }
6931
6874
  };
6932
6875
  _Timepicker._template = `<div class="timepicker-container">
@@ -6960,7 +6903,7 @@ var Expressive = (() => {
6960
6903
  var Timepicker = _Timepicker;
6961
6904
 
6962
6905
  // src/ts/components/snackbar.ts
6963
- var _defaults21 = {
6906
+ var _defaults20 = {
6964
6907
  text: "",
6965
6908
  action: "",
6966
6909
  onAction: null,
@@ -6976,24 +6919,28 @@ var Expressive = (() => {
6976
6919
  var _Snackbar = class _Snackbar {
6977
6920
  constructor(options = {}) {
6978
6921
  this._dismissed = false;
6922
+ this._returnFocus = null;
6979
6923
  this.options = {
6980
6924
  ..._Snackbar.defaults,
6981
6925
  ...options
6982
6926
  };
6983
6927
  if (options.displayLength === void 0 && this.options.action) {
6984
- this.options.displayLength = 1e4;
6928
+ this.options.displayLength = Infinity;
6985
6929
  }
6930
+ if (options.dismissible === void 0 && this.options.action) this.options.dismissible = true;
6986
6931
  this.message = this.options.text;
6987
6932
  this.panning = false;
6988
6933
  this.timeRemaining = this.options.displayLength;
6989
- if (_Snackbar._snackbars.length > 0) {
6990
- for (const snackbar of [..._Snackbar._snackbars]) {
6991
- snackbar._dismissed = true;
6992
- clearTimeout(snackbar.counterTimeout);
6993
- snackbar.el.remove();
6994
- }
6995
- _Snackbar._snackbars.length = 0;
6996
- }
6934
+ while (_Snackbar._snackbars.length > 0) {
6935
+ const snackbar = _Snackbar._snackbars.shift();
6936
+ snackbar._dismissed = true;
6937
+ clearTimeout(snackbar.counterTimeout);
6938
+ snackbar._restoreFocus();
6939
+ snackbar.el.remove();
6940
+ }
6941
+ let focused = document.activeElement;
6942
+ while (focused?.shadowRoot?.activeElement) focused = focused.shadowRoot.activeElement;
6943
+ this._returnFocus = focused instanceof HTMLElement ? focused : null;
6997
6944
  const root = Utils.portalRoot(this.options.root ?? document.body);
6998
6945
  if (!_Snackbar._container || !_Snackbar._container.isConnected || _Snackbar._container.parentNode !== root) {
6999
6946
  if (_Snackbar._container) _Snackbar._removeContainer();
@@ -7007,7 +6954,7 @@ var Expressive = (() => {
7007
6954
  this._setTimer();
7008
6955
  }
7009
6956
  static get defaults() {
7010
- return _defaults21;
6957
+ return _defaults20;
7011
6958
  }
7012
6959
  static getInstance(el) {
7013
6960
  return el["Expressive_Snackbar"];
@@ -7121,6 +7068,22 @@ var Expressive = (() => {
7121
7068
  snackbar.setAttribute("role", "status");
7122
7069
  snackbar.setAttribute("aria-live", "polite");
7123
7070
  snackbar.setAttribute("aria-atomic", "true");
7071
+ snackbar.inert = false;
7072
+ snackbar.addEventListener("focusin", (e) => {
7073
+ if (e.relatedTarget instanceof HTMLElement && !snackbar.contains(e.relatedTarget)) {
7074
+ this._returnFocus = e.relatedTarget;
7075
+ }
7076
+ this._pauseTimer();
7077
+ });
7078
+ snackbar.addEventListener("keydown", (e) => {
7079
+ if (e.key !== "Escape" || e.defaultPrevented || this._dismissed) return;
7080
+ e.preventDefault();
7081
+ e.stopPropagation();
7082
+ this.dismiss();
7083
+ });
7084
+ snackbar.addEventListener("focusout", () => setTimeout(() => this._resumeTimer(), 0));
7085
+ snackbar.addEventListener("pointerenter", () => this._pauseTimer());
7086
+ snackbar.addEventListener("pointerleave", () => this._resumeTimer());
7124
7087
  if (this.options.classes.length > 0) {
7125
7088
  snackbar.classList.add(...this.options.classes.split(" ").filter(Boolean));
7126
7089
  }
@@ -7135,10 +7098,12 @@ var Expressive = (() => {
7135
7098
  action.textContent = this.options.action;
7136
7099
  action.addEventListener("click", (e) => {
7137
7100
  e.stopPropagation();
7138
- if (typeof this.options.onAction === "function") {
7139
- this.options.onAction();
7101
+ if (this._dismissed) return;
7102
+ try {
7103
+ if (typeof this.options.onAction === "function") this.options.onAction();
7104
+ } finally {
7105
+ this.dismiss();
7140
7106
  }
7141
- this.dismiss();
7142
7107
  });
7143
7108
  snackbar.appendChild(action);
7144
7109
  }
@@ -7195,9 +7160,16 @@ var Expressive = (() => {
7195
7160
  }
7196
7161
  /** Restart a countdown stopped by {@link _pauseTimer}. */
7197
7162
  _resumeTimer() {
7198
- if (this.counterTimeout != null) return;
7163
+ if (this._dismissed || this.panning || this.el.matches(":hover, :focus-within") || this.counterTimeout != null) return;
7199
7164
  this._setTimer();
7200
7165
  }
7166
+ _restoreFocus() {
7167
+ const focused = this.el.getRootNode().activeElement;
7168
+ if (focused && this.el.contains(focused) && this._returnFocus?.isConnected) {
7169
+ this._returnFocus.focus({ preventScroll: true });
7170
+ }
7171
+ this.el.inert = true;
7172
+ }
7201
7173
  /**
7202
7174
  * Dismiss snackbar with animation.
7203
7175
  */
@@ -7206,6 +7178,7 @@ var Expressive = (() => {
7206
7178
  this._dismissed = true;
7207
7179
  clearTimeout(this.counterTimeout);
7208
7180
  this.counterTimeout = null;
7181
+ this._restoreFocus();
7209
7182
  const activationDistance = this.el.offsetWidth * this.options.activationPercent;
7210
7183
  if (this.wasSwiped) {
7211
7184
  this.el.style.transition = "transform .05s, opacity .05s";
@@ -7242,7 +7215,7 @@ var Expressive = (() => {
7242
7215
  var Snackbar = _Snackbar;
7243
7216
 
7244
7217
  // src/ts/components/tooltip.ts
7245
- var _defaults22 = {
7218
+ var _defaults21 = {
7246
7219
  exitDelay: 200,
7247
7220
  enterDelay: 0,
7248
7221
  text: "",
@@ -7262,6 +7235,8 @@ var Expressive = (() => {
7262
7235
  this.open = (isManual) => {
7263
7236
  if (this.isOpen) return;
7264
7237
  isManual = isManual === void 0 ? true : void 0;
7238
+ clearTimeout(this._exitDelayTimeout);
7239
+ clearTimeout(this._animationTimeout);
7265
7240
  this.isOpen = true;
7266
7241
  this.options = { ...this.options, ...this._getAttributeOptions() };
7267
7242
  this._updateTooltipContent();
@@ -7275,29 +7250,40 @@ var Expressive = (() => {
7275
7250
  this.isHovered = false;
7276
7251
  this.isFocused = false;
7277
7252
  this.isOpen = false;
7253
+ clearTimeout(this._enterDelayTimeout);
7278
7254
  this._setExitDelayTimeout();
7279
7255
  };
7280
7256
  this._handleMouseEnter = () => {
7281
7257
  this.isHovered = true;
7282
- this.isFocused = false;
7283
7258
  this.open(false);
7284
7259
  };
7285
- this._handleMouseLeave = () => {
7260
+ this._handleMouseLeave = (event) => {
7261
+ const next = event.relatedTarget;
7262
+ if (next && (this.el.contains(next) || this.tooltipEl.contains(next))) return;
7286
7263
  this.isHovered = false;
7287
- this.isFocused = false;
7264
+ if (!this.isFocused) this.close();
7265
+ };
7266
+ this._handleKeydown = (event) => {
7267
+ if (event.key !== "Escape" || !this.isOpen) return;
7268
+ event.preventDefault();
7269
+ event.stopPropagation();
7288
7270
  this.close();
7271
+ clearTimeout(this._exitDelayTimeout);
7272
+ this._animateOut();
7289
7273
  };
7290
7274
  // Focus opens the tooltip only when it arrived by keyboard: a pointer press
7291
7275
  // on the trigger also focuses it, and _handleMouseEnter already covers that.
7292
- this._handleFocus = () => {
7293
- if (this.el.matches(":focus-visible")) {
7276
+ this._handleFocus = (event) => {
7277
+ if (event.target.matches(":focus-visible")) {
7294
7278
  this.isFocused = true;
7295
7279
  this.open(false);
7296
7280
  }
7297
7281
  };
7298
- this._handleBlur = () => {
7282
+ this._handleBlur = (event) => {
7283
+ const next = event.relatedTarget;
7284
+ if (next && (this.el.contains(next) || this.tooltipEl.contains(next))) return;
7299
7285
  this.isFocused = false;
7300
- this.close();
7286
+ if (!this.isHovered) this.close();
7301
7287
  };
7302
7288
  this.el["Expressive_Tooltip"] = this;
7303
7289
  this.options = {
@@ -7312,7 +7298,7 @@ var Expressive = (() => {
7312
7298
  this._setupEventHandlers();
7313
7299
  }
7314
7300
  static get defaults() {
7315
- return _defaults22;
7301
+ return _defaults21;
7316
7302
  }
7317
7303
  /**
7318
7304
  * Initializes instances of Tooltip.
@@ -7363,16 +7349,22 @@ var Expressive = (() => {
7363
7349
  this._setTooltipContent(this.tooltipEl.querySelector(".tooltip-content"));
7364
7350
  }
7365
7351
  _setupEventHandlers() {
7366
- this.el.addEventListener("mouseenter", this._handleMouseEnter);
7367
- this.el.addEventListener("mouseleave", this._handleMouseLeave);
7368
- this.el.addEventListener("focus", this._handleFocus, true);
7369
- this.el.addEventListener("blur", this._handleBlur, true);
7352
+ for (const el of [this.el, this.tooltipEl]) {
7353
+ el.addEventListener("mouseenter", this._handleMouseEnter);
7354
+ el.addEventListener("mouseleave", this._handleMouseLeave);
7355
+ el.addEventListener("focus", this._handleFocus, true);
7356
+ el.addEventListener("blur", this._handleBlur, true);
7357
+ }
7358
+ this.el.ownerDocument.addEventListener("keydown", this._handleKeydown, true);
7370
7359
  }
7371
7360
  _removeEventHandlers() {
7372
- this.el.removeEventListener("mouseenter", this._handleMouseEnter);
7373
- this.el.removeEventListener("mouseleave", this._handleMouseLeave);
7374
- this.el.removeEventListener("focus", this._handleFocus, true);
7375
- this.el.removeEventListener("blur", this._handleBlur, true);
7361
+ for (const el of [this.el, this.tooltipEl]) {
7362
+ el.removeEventListener("mouseenter", this._handleMouseEnter);
7363
+ el.removeEventListener("mouseleave", this._handleMouseLeave);
7364
+ el.removeEventListener("focus", this._handleFocus, true);
7365
+ el.removeEventListener("blur", this._handleBlur, true);
7366
+ }
7367
+ this.el.ownerDocument.removeEventListener("keydown", this._handleKeydown, true);
7376
7368
  }
7377
7369
  _setExitDelayTimeout() {
7378
7370
  clearTimeout(this._exitDelayTimeout);
@@ -7419,6 +7411,7 @@ var Expressive = (() => {
7419
7411
  tooltipWidth,
7420
7412
  tooltipHeight
7421
7413
  );
7414
+ tooltip.style.setProperty("--md-comp-tooltip-anchor-gap", `${Math.max(0, margin) + Math.abs(this.options.transitionMovement)}px`);
7422
7415
  tooltip.style.top = newCoordinates.y + "px";
7423
7416
  tooltip.style.left = newCoordinates.x + "px";
7424
7417
  }
@@ -7434,27 +7427,29 @@ var Expressive = (() => {
7434
7427
  );
7435
7428
  }
7436
7429
  _animateIn() {
7437
- this._positionTooltip();
7438
- this.tooltipEl.style.visibility = "visible";
7439
- const duration = this.options.inDuration;
7440
- this.tooltipEl.style.transition = `
7441
- transform ${duration}ms ease-out,
7442
- opacity ${duration}ms ease-out`;
7443
- clearTimeout(this._animationTimeout);
7444
- this._animationTimeout = setTimeout(() => {
7445
- this.tooltipEl.style.transform = `translateX(${this.xMovement}px) translateY(${this.yMovement}px)`;
7446
- this.tooltipEl.style.opacity = (this.options.opacity || 1).toString();
7447
- }, 1);
7430
+ this._animate(true);
7448
7431
  }
7449
7432
  _animateOut() {
7450
- const duration = this.options.outDuration;
7451
- this.tooltipEl.style.transition = `
7452
- transform ${duration}ms ease-out,
7453
- opacity ${duration}ms ease-out`;
7433
+ this._animate(false);
7434
+ }
7435
+ _animate(show) {
7436
+ const style = this.tooltipEl.style;
7437
+ const reduce = this.el.ownerDocument.defaultView.matchMedia("(prefers-reduced-motion: reduce)").matches;
7438
+ const duration = reduce ? 0 : show ? this.options.inDuration : this.options.outDuration;
7439
+ if (show) {
7440
+ this._positionTooltip();
7441
+ style.visibility = "visible";
7442
+ style.overflow = "visible";
7443
+ }
7444
+ style.transition = `transform ${duration}ms ease-out, opacity ${duration}ms ease-out`;
7454
7445
  clearTimeout(this._animationTimeout);
7455
7446
  this._animationTimeout = setTimeout(() => {
7456
- this.tooltipEl.style.transform = `translateX(0px) translateY(0px)`;
7457
- this.tooltipEl.style.opacity = "0";
7447
+ style.transform = show && !reduce ? `translateX(${this.xMovement}px) translateY(${this.yMovement}px)` : "none";
7448
+ style.opacity = show ? (this.options.opacity || 1).toString() : "0";
7449
+ if (!show) this._animationTimeout = setTimeout(() => {
7450
+ style.visibility = "hidden";
7451
+ style.overflow = "";
7452
+ }, duration);
7458
7453
  }, 1);
7459
7454
  }
7460
7455
  _getAttributeOptions() {
@@ -7506,10 +7501,6 @@ var Expressive = (() => {
7506
7501
  Lightbox: { component: Lightbox, selector: ".lightboxed" },
7507
7502
  ScrollSpy: { component: ScrollSpy, selector: ".scrollspy" },
7508
7503
  FormSelect: { component: FormSelect, selector: "select" },
7509
- NavigationDrawer: {
7510
- component: NavigationDrawer,
7511
- selector: ".sidenav, .navigation-drawer"
7512
- },
7513
7504
  NavigationRail: {
7514
7505
  component: NavigationRail,
7515
7506
  selector: ".navigation-rail"
@@ -7522,7 +7513,7 @@ var Expressive = (() => {
7522
7513
  Tooltip: { component: Tooltip, selector: ".tooltipped" },
7523
7514
  FloatingActionButton: {
7524
7515
  component: FloatingActionButton,
7525
- selector: ".fab, .fixed-action-btn, .fab-menu"
7516
+ selector: ".fab-menu"
7526
7517
  }
7527
7518
  };
7528
7519
  function AutoInit(context = document.body, options) {
@@ -7539,8 +7530,10 @@ var Expressive = (() => {
7539
7530
  }
7540
7531
  }
7541
7532
 
7533
+ // src/ts/modular.ts
7534
+ var version = "0.10.0";
7535
+
7542
7536
  // src/ts/index.ts
7543
- var version = "0.9.1";
7544
7537
  Forms.Init();
7545
7538
  Chips.Init();
7546
7539
  Slider.Init();