@expressivecss/expressive 0.13.1 → 0.14.1

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 (158) hide show
  1. package/CHANGELOG.md +109 -1
  2. package/README.md +2 -1
  3. package/dist/css/expressive.css +234 -72
  4. package/dist/css/expressive.css.map +1 -1
  5. package/dist/css/expressive.min.css +1 -1
  6. package/dist/css/expressive.min.css.map +1 -1
  7. package/dist/js/chunks/{chunk-55IA2XUC.mjs → chunk-3GVFZ4XO.mjs} +3 -3
  8. package/dist/js/chunks/{chunk-3CLXLPYS.mjs → chunk-4HZMB7LI.mjs} +22 -8
  9. package/dist/js/chunks/chunk-4HZMB7LI.mjs.map +7 -0
  10. package/dist/js/chunks/{chunk-DTKR54I6.mjs → chunk-4KMNFIFZ.mjs} +2 -2
  11. package/dist/js/chunks/{chunk-K4CJCRGE.mjs → chunk-4TKTLGEM.mjs} +2 -2
  12. package/dist/js/chunks/{chunk-3VOK6J7U.mjs → chunk-5BKHWHYY.mjs} +13 -6
  13. package/dist/js/chunks/chunk-5BKHWHYY.mjs.map +7 -0
  14. package/dist/js/chunks/{chunk-PLYXLSH3.mjs → chunk-5HQSGDZ5.mjs} +18 -9
  15. package/dist/js/chunks/chunk-5HQSGDZ5.mjs.map +7 -0
  16. package/dist/js/chunks/{chunk-LWBQIX5J.mjs → chunk-A7ALMYUR.mjs} +41 -44
  17. package/dist/js/chunks/chunk-A7ALMYUR.mjs.map +7 -0
  18. package/dist/js/chunks/chunk-BG6ZRB5H.mjs +154 -0
  19. package/dist/js/chunks/chunk-BG6ZRB5H.mjs.map +7 -0
  20. package/dist/js/chunks/{chunk-DLWYKFEI.mjs → chunk-CUQPB25U.mjs} +3 -3
  21. package/dist/js/chunks/{chunk-6WLSDZ4O.mjs → chunk-DAIU5SLG.mjs} +2 -2
  22. package/dist/js/chunks/{chunk-VTLBW7QS.mjs → chunk-FS7XY3HT.mjs} +2 -2
  23. package/dist/js/chunks/{chunk-WJXDBPXQ.mjs → chunk-GIW4K4SE.mjs} +8 -12
  24. package/dist/js/chunks/chunk-GIW4K4SE.mjs.map +7 -0
  25. package/dist/js/chunks/{chunk-LNCCYGPC.mjs → chunk-GUB3TNO2.mjs} +6 -5
  26. package/dist/js/chunks/chunk-GUB3TNO2.mjs.map +7 -0
  27. package/dist/js/chunks/{chunk-XH6BEEMY.mjs → chunk-GZKEFWPM.mjs} +39 -31
  28. package/dist/js/chunks/chunk-GZKEFWPM.mjs.map +7 -0
  29. package/dist/js/chunks/{chunk-QKWPQK5N.mjs → chunk-ILP3PDTL.mjs} +11 -3
  30. package/dist/js/chunks/chunk-ILP3PDTL.mjs.map +7 -0
  31. package/dist/js/chunks/{chunk-U2OTHOTI.mjs → chunk-JIDMQGK6.mjs} +3 -2
  32. package/dist/js/chunks/{chunk-U2OTHOTI.mjs.map → chunk-JIDMQGK6.mjs.map} +2 -2
  33. package/dist/js/chunks/chunk-KBNN3QDH.mjs +7 -0
  34. package/dist/js/chunks/{chunk-4PWAPV6F.mjs.map → chunk-KBNN3QDH.mjs.map} +1 -1
  35. package/dist/js/chunks/{chunk-NNYP33NB.mjs → chunk-KHV6DZXX.mjs} +5 -4
  36. package/dist/js/chunks/chunk-KHV6DZXX.mjs.map +7 -0
  37. package/dist/js/chunks/{chunk-XGHORT7J.mjs → chunk-KY6U46SM.mjs} +3 -3
  38. package/dist/js/chunks/{chunk-CKQP6P76.mjs → chunk-LRZMJZ3T.mjs} +4 -3
  39. package/dist/js/chunks/chunk-LRZMJZ3T.mjs.map +7 -0
  40. package/dist/js/chunks/{chunk-6OMRGML6.mjs → chunk-MNPROCZF.mjs} +4 -4
  41. package/dist/js/chunks/{chunk-6OMRGML6.mjs.map → chunk-MNPROCZF.mjs.map} +2 -2
  42. package/dist/js/chunks/{chunk-QV2JAWIZ.mjs → chunk-MZ6Z24BX.mjs} +3 -2
  43. package/dist/js/chunks/{chunk-QV2JAWIZ.mjs.map → chunk-MZ6Z24BX.mjs.map} +2 -2
  44. package/dist/js/chunks/{chunk-CCWDV5CR.mjs → chunk-NS2UMNZV.mjs} +2 -2
  45. package/dist/js/chunks/{chunk-UBLRRJ7N.mjs → chunk-PM655FXL.mjs} +2 -2
  46. package/dist/js/chunks/{chunk-IFXVDDDH.mjs → chunk-QERLECVU.mjs} +3 -3
  47. package/dist/js/chunks/{chunk-GZWPXXY6.mjs → chunk-QJABDLSD.mjs} +5 -2
  48. package/dist/js/chunks/chunk-QJABDLSD.mjs.map +7 -0
  49. package/dist/js/chunks/{chunk-UNEMYWEW.mjs → chunk-S3XDFGSZ.mjs} +2 -2
  50. package/dist/js/chunks/chunk-TBM3FT7G.mjs +398 -0
  51. package/dist/js/chunks/chunk-TBM3FT7G.mjs.map +7 -0
  52. package/dist/js/chunks/chunk-UEXCIMJZ.mjs +214 -0
  53. package/dist/js/chunks/chunk-UEXCIMJZ.mjs.map +7 -0
  54. package/dist/js/chunks/{chunk-P3G3QHII.mjs → chunk-VGZSAFTX.mjs} +11 -4
  55. package/dist/js/chunks/{chunk-P3G3QHII.mjs.map → chunk-VGZSAFTX.mjs.map} +2 -2
  56. package/dist/js/chunks/{chunk-UAEAQBRC.mjs → chunk-X7OCHZVF.mjs} +2 -2
  57. package/dist/js/chunks/{chunk-EC63CHXU.mjs → chunk-YMSXJON6.mjs} +7 -6
  58. package/dist/js/chunks/chunk-YMSXJON6.mjs.map +7 -0
  59. package/dist/js/chunks/{chunk-QVVEWBOF.mjs → chunk-Z2NRKI7R.mjs} +2 -2
  60. package/dist/js/chunks/{chunk-PIJ2TAR3.mjs → chunk-ZAS25YFD.mjs} +2 -2
  61. package/dist/js/chunks/chunk-ZKZKNSLC.mjs +1 -0
  62. package/dist/js/expressive.cjs +905 -117
  63. package/dist/js/expressive.cjs.map +4 -4
  64. package/dist/js/expressive.js +905 -117
  65. package/dist/js/expressive.js.map +4 -4
  66. package/dist/js/expressive.min.js +10 -10
  67. package/dist/js/expressive.min.js.map +4 -4
  68. package/dist/js/expressive.mjs +57 -45
  69. package/dist/js/expressive.mjs.map +1 -1
  70. package/dist/js/internal/behaviors/bottomSheets.mjs +3 -3
  71. package/dist/js/internal/behaviors/dialogs.mjs +2 -2
  72. package/dist/js/internal/behaviors/forms.mjs +2 -2
  73. package/dist/js/internal/behaviors/sheetDrag.mjs +2 -2
  74. package/dist/js/internal/behaviors/sideSheets.mjs +3 -3
  75. package/dist/js/internal/components/autocomplete.mjs +3 -3
  76. package/dist/js/internal/components/buttons.mjs +2 -2
  77. package/dist/js/internal/components/cards.mjs +2 -2
  78. package/dist/js/internal/components/carousel.mjs +2 -2
  79. package/dist/js/internal/components/chips.mjs +4 -4
  80. package/dist/js/internal/components/commandPalette.mjs +2 -2
  81. package/dist/js/internal/components/confetti.mjs +8 -0
  82. package/dist/js/internal/components/confetti.mjs.map +7 -0
  83. package/dist/js/internal/components/datepicker.mjs +5 -5
  84. package/dist/js/internal/components/expandingCard.mjs +2 -2
  85. package/dist/js/internal/components/heatmapChart.mjs +1 -1
  86. package/dist/js/internal/components/index.mjs +50 -38
  87. package/dist/js/internal/components/lightbox.mjs +2 -2
  88. package/dist/js/internal/components/mapView.mjs +9 -0
  89. package/dist/js/internal/components/mapView.mjs.map +7 -0
  90. package/dist/js/internal/components/menu.mjs +2 -2
  91. package/dist/js/internal/components/messageScroller.mjs +2 -2
  92. package/dist/js/internal/components/particles.mjs +8 -0
  93. package/dist/js/internal/components/particles.mjs.map +7 -0
  94. package/dist/js/internal/components/pieChart.mjs +1 -1
  95. package/dist/js/internal/components/questionnaire.mjs +2 -2
  96. package/dist/js/internal/components/registry.mjs +31 -28
  97. package/dist/js/internal/components/richTextEditor.mjs +2 -2
  98. package/dist/js/internal/components/scrollspy.mjs +1 -1
  99. package/dist/js/internal/components/select.mjs +3 -3
  100. package/dist/js/internal/components/slider.mjs +2 -2
  101. package/dist/js/internal/components/snackbar.mjs +2 -2
  102. package/dist/js/internal/components/tabs.mjs +3 -3
  103. package/dist/js/internal/components/timepicker.mjs +3 -3
  104. package/dist/js/internal/components/tooltip.mjs +2 -2
  105. package/dist/js/internal/core/utils.mjs +1 -1
  106. package/dist/js/internal/plugins/dockedDisplayPlugin.mjs +2 -2
  107. package/dist/js/modular.mjs +57 -45
  108. package/dist/types/behaviors/forms.d.ts +5 -0
  109. package/dist/types/components/carousel.d.ts +2 -0
  110. package/dist/types/components/confetti.d.ts +103 -0
  111. package/dist/types/components/datepicker.d.ts +2 -0
  112. package/dist/types/components/index.d.ts +3 -0
  113. package/dist/types/components/mapView.d.ts +151 -0
  114. package/dist/types/components/particles.d.ts +72 -0
  115. package/dist/types/components/pieChart.d.ts +3 -0
  116. package/dist/types/components/registry.d.ts +8 -0
  117. package/dist/types/components/slider.d.ts +1 -1
  118. package/dist/types/components/snackbar.d.ts +2 -0
  119. package/dist/types/components/tabs.d.ts +1 -1
  120. package/dist/types/modular.d.ts +1 -1
  121. package/dist/types/plugins/dockedDisplayPlugin.d.ts +3 -3
  122. package/package.json +2 -1
  123. package/src/sass/components/_buttons.scss +1 -1
  124. package/src/sass/components/_confetti.scss +14 -0
  125. package/src/sass/components/_index.scss +3 -0
  126. package/src/sass/components/_map.scss +188 -0
  127. package/src/sass/components/_particles.scss +14 -0
  128. package/src/sass/components/_table_of_contents.scss +3 -1
  129. package/src/sass/components/forms/_checkboxes.scss +7 -2
  130. package/dist/js/chunks/chunk-3CLXLPYS.mjs.map +0 -7
  131. package/dist/js/chunks/chunk-3VOK6J7U.mjs.map +0 -7
  132. package/dist/js/chunks/chunk-4PWAPV6F.mjs +0 -7
  133. package/dist/js/chunks/chunk-CKQP6P76.mjs.map +0 -7
  134. package/dist/js/chunks/chunk-EC63CHXU.mjs.map +0 -7
  135. package/dist/js/chunks/chunk-GZWPXXY6.mjs.map +0 -7
  136. package/dist/js/chunks/chunk-LNCCYGPC.mjs.map +0 -7
  137. package/dist/js/chunks/chunk-LWBQIX5J.mjs.map +0 -7
  138. package/dist/js/chunks/chunk-NNYP33NB.mjs.map +0 -7
  139. package/dist/js/chunks/chunk-NQLC3YMW.mjs +0 -1
  140. package/dist/js/chunks/chunk-PLYXLSH3.mjs.map +0 -7
  141. package/dist/js/chunks/chunk-QKWPQK5N.mjs.map +0 -7
  142. package/dist/js/chunks/chunk-WJXDBPXQ.mjs.map +0 -7
  143. package/dist/js/chunks/chunk-XH6BEEMY.mjs.map +0 -7
  144. /package/dist/js/chunks/{chunk-55IA2XUC.mjs.map → chunk-3GVFZ4XO.mjs.map} +0 -0
  145. /package/dist/js/chunks/{chunk-DTKR54I6.mjs.map → chunk-4KMNFIFZ.mjs.map} +0 -0
  146. /package/dist/js/chunks/{chunk-K4CJCRGE.mjs.map → chunk-4TKTLGEM.mjs.map} +0 -0
  147. /package/dist/js/chunks/{chunk-DLWYKFEI.mjs.map → chunk-CUQPB25U.mjs.map} +0 -0
  148. /package/dist/js/chunks/{chunk-6WLSDZ4O.mjs.map → chunk-DAIU5SLG.mjs.map} +0 -0
  149. /package/dist/js/chunks/{chunk-VTLBW7QS.mjs.map → chunk-FS7XY3HT.mjs.map} +0 -0
  150. /package/dist/js/chunks/{chunk-XGHORT7J.mjs.map → chunk-KY6U46SM.mjs.map} +0 -0
  151. /package/dist/js/chunks/{chunk-CCWDV5CR.mjs.map → chunk-NS2UMNZV.mjs.map} +0 -0
  152. /package/dist/js/chunks/{chunk-UBLRRJ7N.mjs.map → chunk-PM655FXL.mjs.map} +0 -0
  153. /package/dist/js/chunks/{chunk-IFXVDDDH.mjs.map → chunk-QERLECVU.mjs.map} +0 -0
  154. /package/dist/js/chunks/{chunk-UNEMYWEW.mjs.map → chunk-S3XDFGSZ.mjs.map} +0 -0
  155. /package/dist/js/chunks/{chunk-UAEAQBRC.mjs.map → chunk-X7OCHZVF.mjs.map} +0 -0
  156. /package/dist/js/chunks/{chunk-QVVEWBOF.mjs.map → chunk-Z2NRKI7R.mjs.map} +0 -0
  157. /package/dist/js/chunks/{chunk-PIJ2TAR3.mjs.map → chunk-ZAS25YFD.mjs.map} +0 -0
  158. /package/dist/js/chunks/{chunk-NQLC3YMW.mjs.map → chunk-ZKZKNSLC.mjs.map} +0 -0
@@ -31,6 +31,7 @@ __export(index_exports, {
31
31
  Chips: () => Chips,
32
32
  ColumnChart: () => ColumnChart,
33
33
  CommandPalette: () => CommandPalette,
34
+ Confetti: () => Confetti,
34
35
  Datepicker: () => Datepicker,
35
36
  Dialogs: () => Dialogs,
36
37
  ExpandingCard: () => ExpandingCard,
@@ -40,10 +41,12 @@ __export(index_exports, {
40
41
  HeatmapChart: () => HeatmapChart,
41
42
  Lightbox: () => Lightbox,
42
43
  LineChart: () => LineChart,
44
+ MapView: () => MapView,
43
45
  Menu: () => Menu,
44
46
  MessageScroller: () => MessageScroller,
45
47
  MixedChart: () => MixedChart,
46
48
  NavigationRail: () => NavigationRail,
49
+ Particles: () => Particles,
47
50
  PieChart: () => PieChart,
48
51
  Questionnaire: () => Questionnaire,
49
52
  RadarChart: () => RadarChart,
@@ -239,9 +242,9 @@ var _Utils = class _Utils {
239
242
  static throttle(func, wait) {
240
243
  let timeout = null;
241
244
  let lastArgs = null;
242
- let previous = 0;
245
+ let previous = -Infinity;
243
246
  return (...args) => {
244
- const now = Date.now();
247
+ const now = performance.now();
245
248
  const remaining = wait - (now - previous);
246
249
  if (remaining <= 0) {
247
250
  clearTimeout(timeout);
@@ -252,7 +255,7 @@ var _Utils = class _Utils {
252
255
  } else if (!timeout) {
253
256
  lastArgs = args;
254
257
  timeout = setTimeout(() => {
255
- previous = Date.now();
258
+ previous = performance.now();
256
259
  timeout = null;
257
260
  func(...lastArgs ?? []);
258
261
  lastArgs = null;
@@ -1276,6 +1279,7 @@ var _Menu = class _Menu extends Component {
1276
1279
  item.scrollIntoView({ block: "nearest", inline: "nearest" });
1277
1280
  }
1278
1281
  _resetMenuStyles() {
1282
+ if (!this.menuEl) return;
1279
1283
  this.menuEl.style.display = "";
1280
1284
  this._resetMenuPositioningStyles();
1281
1285
  this.menuEl.style.transform = "";
@@ -1587,8 +1591,9 @@ var _Autocomplete = class _Autocomplete extends Component {
1587
1591
  this._handleInput = () => {
1588
1592
  this.count = 0;
1589
1593
  const actualValue = this.el.value.toLocaleLowerCase();
1590
- if (this.oldVal !== actualValue && this.el.matches(":focus-visible")) {
1591
- this.open();
1594
+ if (this.oldVal !== actualValue) {
1595
+ this._resetCurrentElementPosition();
1596
+ if (this.el.matches(":focus-visible") && (!this.menu.isOpen || actualValue.length < this.options.minLength)) this.open();
1592
1597
  }
1593
1598
  this._inputChangeDetection(actualValue);
1594
1599
  };
@@ -1898,11 +1903,24 @@ var _Autocomplete = class _Autocomplete extends Component {
1898
1903
  item.appendChild(img);
1899
1904
  }
1900
1905
  const inputText = this.el.value.toLocaleLowerCase();
1901
- const parts = this._highlightPartialText(inputText, (entry.text || entry.id).toString());
1906
+ const label = (entry.text || entry.id).toString();
1907
+ const parts = this._highlightPartialText(inputText, label);
1902
1908
  const div = document.createElement("div");
1903
1909
  div.setAttribute("style", "line-height:1.2;font-weight:500;");
1904
1910
  if (this.options.allowUnsafeHTML) {
1905
- div.innerHTML = parts[0] + '<span class="highlight">' + parts[1] + "</span>" + parts[2];
1911
+ div.innerHTML = label;
1912
+ const walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT);
1913
+ for (let node = walker.nextNode(); node; node = walker.nextNode()) {
1914
+ const [before, match] = this._highlightPartialText(inputText, node.data);
1915
+ if (!match) continue;
1916
+ const matched = node.splitText(before.length);
1917
+ matched.splitText(match.length);
1918
+ const highlight = document.createElement("span");
1919
+ highlight.classList.add("highlight");
1920
+ matched.replaceWith(highlight);
1921
+ highlight.append(matched);
1922
+ break;
1923
+ }
1906
1924
  } else {
1907
1925
  div.appendChild(document.createTextNode(parts[0]));
1908
1926
  if (parts[1]) {
@@ -1994,7 +2012,7 @@ var _Autocomplete = class _Autocomplete extends Component {
1994
2012
  );
1995
2013
  }
1996
2014
  if (this.options.isMultiSelect) {
1997
- this._renderMenu();
2015
+ if (!open) this._renderMenu();
1998
2016
  } else {
1999
2017
  this._refreshInputText();
2000
2018
  }
@@ -2392,12 +2410,10 @@ var Slider = class _Slider extends Component {
2392
2410
  this._sync();
2393
2411
  this.thumb.classList.add("active");
2394
2412
  };
2395
- this._handleRangeInputOrMove = (e) => {
2396
- if (e.type === "input" || this._pointerDown) {
2397
- this._clampDual();
2398
- this._sync();
2399
- this.thumb.classList.add("active");
2400
- }
2413
+ this._handleRangeInput = () => {
2414
+ this._clampDual();
2415
+ this._sync();
2416
+ this.thumb.classList.add("active");
2401
2417
  };
2402
2418
  this._handleRangePointerUp = (e) => {
2403
2419
  if (!e.isPrimary) return;
@@ -2461,8 +2477,7 @@ var Slider = class _Slider extends Component {
2461
2477
  _setupEventHandlers() {
2462
2478
  this.el.addEventListener("change", this._handleRangeChange);
2463
2479
  this.el.addEventListener("pointerdown", this._handleRangePointerDown);
2464
- this.el.addEventListener("input", this._handleRangeInputOrMove);
2465
- this.el.addEventListener("pointermove", this._handleRangeInputOrMove, { passive: true });
2480
+ this.el.addEventListener("input", this._handleRangeInput);
2466
2481
  this.el.addEventListener("pointerup", this._handleRangePointerUp);
2467
2482
  this.el.addEventListener("pointercancel", this._handleRangePointerUp);
2468
2483
  this.el.addEventListener("blur", this._handleRangeRelease);
@@ -2471,8 +2486,7 @@ var Slider = class _Slider extends Component {
2471
2486
  _removeEventHandlers() {
2472
2487
  this.el.removeEventListener("change", this._handleRangeChange);
2473
2488
  this.el.removeEventListener("pointerdown", this._handleRangePointerDown);
2474
- this.el.removeEventListener("input", this._handleRangeInputOrMove);
2475
- this.el.removeEventListener("pointermove", this._handleRangeInputOrMove);
2489
+ this.el.removeEventListener("input", this._handleRangeInput);
2476
2490
  this.el.removeEventListener("pointerup", this._handleRangePointerUp);
2477
2491
  this.el.removeEventListener("pointercancel", this._handleRangePointerUp);
2478
2492
  this.el.removeEventListener("blur", this._handleRangeRelease);
@@ -2640,7 +2654,10 @@ var DISMISS_DISTANCE = 96;
2640
2654
  var DISMISS_VELOCITY = 0.5;
2641
2655
  var FLICK_TIMEOUT = 100;
2642
2656
  var DRAG_SLOP = 4;
2657
+ var installed = /* @__PURE__ */ new Set();
2643
2658
  function installSheetDrag(config) {
2659
+ if (installed.has(config.selector)) return;
2660
+ installed.add(config.selector);
2644
2661
  const coord = (event) => config.axis === "x" ? event.clientX : event.clientY;
2645
2662
  let observer;
2646
2663
  let drag = null;
@@ -2801,7 +2818,7 @@ var SideSheets = class {
2801
2818
  };
2802
2819
 
2803
2820
  // src/ts/behaviors/forms.ts
2804
- var Forms = class _Forms {
2821
+ var _Forms = class _Forms {
2805
2822
  /**
2806
2823
  * Checks if the label has validation and apply
2807
2824
  * the correct class and styles
@@ -2843,7 +2860,13 @@ var Forms = class _Forms {
2843
2860
  const contentHeight = textarea.scrollHeight + borders;
2844
2861
  textarea.style.height = Math.max(originalHeight, contentHeight) + "px";
2845
2862
  }
2863
+ /**
2864
+ * Safe to call more than once: the root entry calls it on import, and a page
2865
+ * that also loads the modular entry may call it again.
2866
+ */
2846
2867
  static Init() {
2868
+ if (_Forms._initialized) return;
2869
+ _Forms._initialized = true;
2847
2870
  Utils.onDocumentReady(() => {
2848
2871
  document.addEventListener("change", (e) => {
2849
2872
  const target = e.target;
@@ -2970,6 +2993,8 @@ var Forms = class _Forms {
2970
2993
  });
2971
2994
  }
2972
2995
  };
2996
+ _Forms._initialized = false;
2997
+ var Forms = _Forms;
2973
2998
 
2974
2999
  // src/ts/components/appBar.ts
2975
3000
  var _defaults7 = {};
@@ -3462,6 +3487,7 @@ var _Carousel = class _Carousel extends Component {
3462
3487
  this._dragStartX = e.clientX;
3463
3488
  this._dragStartY = e.clientY;
3464
3489
  this._dragStartScroll = this._vertical ? this._scroller.scrollTop : this._scroller.scrollLeft;
3490
+ this._setDragListeners(true);
3465
3491
  };
3466
3492
  this._handleTrackPointerMove = (e) => {
3467
3493
  if (!this.pressed || e.pointerId !== this._dragPointerId) return;
@@ -3486,6 +3512,7 @@ var _Carousel = class _Carousel extends Component {
3486
3512
  if (!this.pressed || e.pointerId !== this._dragPointerId) return;
3487
3513
  this.pressed = false;
3488
3514
  this._dragPointerId = null;
3515
+ this._setDragListeners(false);
3489
3516
  this.el.classList.remove("dragging");
3490
3517
  try {
3491
3518
  this._scroller.releasePointerCapture(e.pointerId);
@@ -3552,9 +3579,8 @@ var _Carousel = class _Carousel extends Component {
3552
3579
  if (this._trackRaf !== null) return;
3553
3580
  this._trackRaf = requestAnimationFrame(() => {
3554
3581
  this._trackRaf = null;
3582
+ const index = this._ignoreScroll ? this.center : this._nearestIndex();
3555
3583
  this._updateParallax();
3556
- if (this._ignoreScroll) return;
3557
- const index = this._nearestIndex();
3558
3584
  if (index !== this.center) this._syncActive(index, this.dragged);
3559
3585
  });
3560
3586
  };
@@ -3567,6 +3593,7 @@ var _Carousel = class _Carousel extends Component {
3567
3593
  };
3568
3594
  this._finishScroll = () => {
3569
3595
  this._ignoreScroll = false;
3596
+ this._scroller.style.scrollSnapType = "";
3570
3597
  clearTimeout(this._scrollEndTimeout);
3571
3598
  this._scroller.removeEventListener("scrollend", this._handleScrollEnd);
3572
3599
  };
@@ -3746,9 +3773,6 @@ var _Carousel = class _Carousel extends Component {
3746
3773
  });
3747
3774
  this._scroller.addEventListener("pointerdown", this._handleTrackPointerDown);
3748
3775
  this._scroller.addEventListener("wheel", this._handleNativeScroll, { passive: true });
3749
- document.addEventListener("pointermove", this._handleTrackPointerMove);
3750
- document.addEventListener("pointerup", this._handleTrackPointerUp);
3751
- document.addEventListener("pointercancel", this._handleTrackPointerUp);
3752
3776
  this._scroller.addEventListener("click", this._handleTrackClick, true);
3753
3777
  this._scroller.addEventListener("dragstart", this._handleTrackDragStart);
3754
3778
  this._scroller.addEventListener("transitionend", this._handleItemResize);
@@ -3767,9 +3791,7 @@ var _Carousel = class _Carousel extends Component {
3767
3791
  this._scroller.removeEventListener("scroll", this._handleTrackScroll);
3768
3792
  this._scroller.removeEventListener("pointerdown", this._handleTrackPointerDown);
3769
3793
  this._scroller.removeEventListener("wheel", this._handleNativeScroll);
3770
- document.removeEventListener("pointermove", this._handleTrackPointerMove);
3771
- document.removeEventListener("pointerup", this._handleTrackPointerUp);
3772
- document.removeEventListener("pointercancel", this._handleTrackPointerUp);
3794
+ this._setDragListeners(false);
3773
3795
  this._scroller.removeEventListener("click", this._handleTrackClick, true);
3774
3796
  this._scroller.removeEventListener("dragstart", this._handleTrackDragStart);
3775
3797
  this._scroller.removeEventListener("transitionend", this._handleItemResize);
@@ -3836,6 +3858,13 @@ var _Carousel = class _Carousel extends Component {
3836
3858
  this._autoAdvanceTimer = null;
3837
3859
  }
3838
3860
  }
3861
+ /** The document only hears from a carousel while a mouse press is down on its track. */
3862
+ _setDragListeners(on) {
3863
+ const method = on ? "addEventListener" : "removeEventListener";
3864
+ document[method]("pointermove", this._handleTrackPointerMove);
3865
+ document[method]("pointerup", this._handleTrackPointerUp);
3866
+ document[method]("pointercancel", this._handleTrackPointerUp);
3867
+ }
3839
3868
  _paintIndicators(center) {
3840
3869
  if (!this.showIndicators || !this._indicators) return;
3841
3870
  const pos = (center % this.count + this.count) % this.count;
@@ -3862,40 +3891,31 @@ var _Carousel = class _Carousel extends Component {
3862
3891
  }
3863
3892
  }
3864
3893
  _syncLayoutRoles(index) {
3865
- const setSize = (item, size) => {
3866
- if (this._authoredItemSizes.has(item)) return;
3867
- item.setAttribute("data-carousel-size", size);
3868
- this._generatedItemSizes.add(item);
3894
+ const sizes = this.images.map(() => "large");
3895
+ const setSize = (i, size) => {
3896
+ if (this.images[i]) sizes[i] = size;
3869
3897
  };
3870
- this.images.forEach((item) => setSize(item, "large"));
3871
3898
  if (this.el.classList.contains("flat") || this.el.classList.contains("uncontained") || this.el.classList.contains("full-screen") && this._vertical) {
3872
- return;
3873
- }
3874
- if (this.el.classList.contains("hero") || this.el.classList.contains("full-screen-horizontal")) {
3875
- const next = this.images[index + 1];
3876
- if (next) setSize(next, "small");
3877
- if (this.el.classList.contains("center-aligned")) {
3878
- const previous = this.images[index - 1];
3879
- if (previous) setSize(previous, "small");
3880
- }
3881
- return;
3882
- }
3883
- const width = this.el.clientWidth;
3884
- const desiredLargeCount = width >= 1600 ? 4 : width >= 1200 ? 3 : width >= 600 ? 2 : 1;
3885
- const largeCount = Math.min(desiredLargeCount, Math.max(1, this.count - 2));
3886
- this.el.classList.remove("multi-wide", "multi-large", "multi-extra-large");
3887
- if (largeCount === 2) this.el.classList.add("multi-wide");
3888
- else if (largeCount === 3) this.el.classList.add("multi-large");
3889
- else if (largeCount >= 4) this.el.classList.add("multi-extra-large");
3890
- this._generatedWideLayout = this._generatedWideLayout || largeCount > 1;
3891
- for (let offset = 1; offset < largeCount; offset += 1) {
3892
- const item = this.images[index + offset];
3893
- if (item) setSize(item, "large");
3894
- }
3895
- const medium = this.images[index + largeCount];
3896
- const small = this.images[index + largeCount + 1];
3897
- if (medium) setSize(medium, "medium");
3898
- if (small) setSize(small, "small");
3899
+ } else if (this.el.classList.contains("hero") || this.el.classList.contains("full-screen-horizontal")) {
3900
+ setSize(index + 1, "small");
3901
+ if (this.el.classList.contains("center-aligned")) setSize(index - 1, "small");
3902
+ } else {
3903
+ const width = this.el.clientWidth;
3904
+ const desiredLargeCount = width >= 1600 ? 4 : width >= 1200 ? 3 : width >= 600 ? 2 : 1;
3905
+ const largeCount = Math.min(desiredLargeCount, Math.max(1, this.count - 2));
3906
+ this.el.classList.remove("multi-wide", "multi-large", "multi-extra-large");
3907
+ if (largeCount === 2) this.el.classList.add("multi-wide");
3908
+ else if (largeCount === 3) this.el.classList.add("multi-large");
3909
+ else if (largeCount >= 4) this.el.classList.add("multi-extra-large");
3910
+ this._generatedWideLayout = this._generatedWideLayout || largeCount > 1;
3911
+ setSize(index + largeCount, "medium");
3912
+ setSize(index + largeCount + 1, "small");
3913
+ }
3914
+ this.images.forEach((item, i) => {
3915
+ if (this._authoredItemSizes.has(item)) return;
3916
+ item.setAttribute("data-carousel-size", sizes[i]);
3917
+ this._generatedItemSizes.add(item);
3918
+ });
3899
3919
  }
3900
3920
  _syncAdaptiveMode() {
3901
3921
  if (!this.el.classList.contains("full-screen")) return;
@@ -3920,7 +3940,7 @@ var _Carousel = class _Carousel extends Component {
3920
3940
  (focusable ?? item).focus({ preventScroll: true });
3921
3941
  }
3922
3942
  _updateParallax() {
3923
- if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
3943
+ if (this._motion.matches) {
3924
3944
  this.images.forEach(
3925
3945
  (item) => item.style.setProperty("--md-comp-carousel-item-parallax", "0px")
3926
3946
  );
@@ -3929,8 +3949,9 @@ var _Carousel = class _Carousel extends Component {
3929
3949
  const scrollerRect = this._scroller.getBoundingClientRect();
3930
3950
  const viewportCenter = this._vertical ? scrollerRect.top + scrollerRect.height / 2 : scrollerRect.left + scrollerRect.width / 2;
3931
3951
  const viewportSize = Math.max(1, this._vertical ? scrollerRect.height : scrollerRect.width);
3932
- this.images.forEach((item) => {
3933
- const rect = item.getBoundingClientRect();
3952
+ const rects = this.images.map((item) => item.getBoundingClientRect());
3953
+ this.images.forEach((item, i) => {
3954
+ const rect = rects[i];
3934
3955
  const itemCenter = this._vertical ? rect.top + rect.height / 2 : rect.left + rect.width / 2;
3935
3956
  const progress = Math.max(-1, Math.min(1, (itemCenter - viewportCenter) / viewportSize));
3936
3957
  item.style.setProperty("--md-comp-carousel-item-parallax", `${progress * -24}px`);
@@ -3983,6 +4004,7 @@ var _Carousel = class _Carousel extends Component {
3983
4004
  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;
3984
4005
  const top = el.offsetTop - blockPadding;
3985
4006
  this._ignoreScroll = true;
4007
+ this._scroller.style.scrollSnapType = "none";
3986
4008
  this._scroller.scrollTo({
3987
4009
  left: this._vertical ? 0 : left,
3988
4010
  top: this._vertical ? top : 0,
@@ -4112,8 +4134,360 @@ var CharacterCounter = class _CharacterCounter extends Component {
4112
4134
  }
4113
4135
  };
4114
4136
 
4115
- // src/ts/components/commandPalette.ts
4137
+ // src/ts/components/confetti.ts
4116
4138
  var _defaults12 = {
4139
+ particleCount: 50,
4140
+ angle: 90,
4141
+ spread: 45,
4142
+ startVelocity: 45,
4143
+ decay: 0.9,
4144
+ gravity: 1,
4145
+ drift: 0,
4146
+ ticks: 200,
4147
+ origin: { x: 0.5, y: 0.5 },
4148
+ colors: [],
4149
+ shapes: ["square", "circle"],
4150
+ scalar: 1,
4151
+ manualStart: false
4152
+ };
4153
+ var THEME_COLORS = ["primary", "secondary", "tertiary", "primary-container", "secondary-container", "tertiary-container"];
4154
+ var FALLBACK_COLORS = ["#26ccff", "#a25afd", "#ff5e7e", "#88ff5a", "#fcff42", "#ffa62d", "#ff36ff"];
4155
+ var RAD = Math.PI / 180;
4156
+ var scenes = /* @__PURE__ */ new Map();
4157
+ var overlay = null;
4158
+ var Confetti = class _Confetti extends Component {
4159
+ constructor(el, options) {
4160
+ super(el, options, _Confetti);
4161
+ this._onClick = () => {
4162
+ this.fire();
4163
+ };
4164
+ this.el["Expressive_Confetti"] = this;
4165
+ this.options = { ..._Confetti.defaults, ...options };
4166
+ if (!(el instanceof HTMLCanvasElement)) el.addEventListener("click", this._onClick);
4167
+ else if (!this.options.manualStart) this.fire();
4168
+ }
4169
+ static get defaults() {
4170
+ return _defaults12;
4171
+ }
4172
+ static init(els, options = {}) {
4173
+ return super.init(els, options, _Confetti);
4174
+ }
4175
+ static getInstance(el) {
4176
+ return el["Expressive_Confetti"];
4177
+ }
4178
+ /**
4179
+ * Bursts over the whole viewport. Resolves when the last piece is gone.
4180
+ */
4181
+ static fire(options = {}) {
4182
+ return burst(viewportCanvas, document.body, { ..._Confetti.defaults, ...options });
4183
+ }
4184
+ /**
4185
+ * Bursts on this canvas, or from this element's center over the viewport.
4186
+ * Resolves when the last piece is gone.
4187
+ */
4188
+ fire(options = {}) {
4189
+ const merged = { ...this.options, ...options };
4190
+ if (this.el instanceof HTMLCanvasElement) {
4191
+ const canvas = this.el;
4192
+ return burst(() => canvas, canvas.parentElement ?? document.body, merged);
4193
+ }
4194
+ const r = this.el.getBoundingClientRect();
4195
+ merged.origin = { x: (r.left + r.width / 2) / innerWidth, y: (r.top + r.height / 2) / innerHeight };
4196
+ return burst(viewportCanvas, this.el.parentElement ?? document.body, merged);
4197
+ }
4198
+ destroy() {
4199
+ this.el.removeEventListener("click", this._onClick);
4200
+ if (this.el instanceof HTMLCanvasElement) stop(this.el);
4201
+ this.el["Expressive_Confetti"] = void 0;
4202
+ }
4203
+ };
4204
+ function burst(getCanvas, themed, o) {
4205
+ if (typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches) return Promise.resolve();
4206
+ const canvas = getCanvas();
4207
+ let scene = scenes.get(canvas);
4208
+ if (!scene) {
4209
+ const ctx = canvas.getContext("2d");
4210
+ if (!ctx) return Promise.resolve();
4211
+ scene = { ctx, pieces: [], frame: 0, last: 0, done: [], overlay: canvas === overlay };
4212
+ scenes.set(canvas, scene);
4213
+ }
4214
+ const { width, height } = fit(canvas);
4215
+ const colors = o.colors.length ? o.colors : themeColors(themed);
4216
+ const x = (o.origin.x ?? 0.5) * width;
4217
+ const y = (o.origin.y ?? 0.5) * height;
4218
+ for (let i = 0; i < o.particleCount; i++) {
4219
+ scene.pieces.push({
4220
+ x,
4221
+ y,
4222
+ velocity: o.startVelocity * (0.5 + Math.random()),
4223
+ angle: -o.angle * RAD + (0.5 - Math.random()) * o.spread * RAD,
4224
+ wobble: Math.random() * 10,
4225
+ wobbleSpeed: 0.05 + Math.random() * 0.05,
4226
+ tilt: (0.25 + Math.random() / 2) * Math.PI,
4227
+ tick: 0,
4228
+ color: colors[i % colors.length],
4229
+ shape: o.shapes[Math.floor(Math.random() * o.shapes.length)] ?? "square",
4230
+ options: o
4231
+ });
4232
+ }
4233
+ const s = scene;
4234
+ if (!s.frame) s.frame = requestAnimationFrame((now) => {
4235
+ s.last = now;
4236
+ draw(canvas, s, now);
4237
+ });
4238
+ return new Promise((resolve) => s.done.push(resolve));
4239
+ }
4240
+ function draw(canvas, scene, now) {
4241
+ const step = Math.min((now - scene.last) / (1e3 / 60), 4);
4242
+ scene.last = now;
4243
+ const { width, height } = fit(canvas);
4244
+ const { ctx } = scene;
4245
+ const dpr = canvas.width / (width || 1);
4246
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
4247
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
4248
+ scene.pieces = scene.pieces.filter((p) => {
4249
+ const o = p.options;
4250
+ p.x += (Math.cos(p.angle) * p.velocity + o.drift) * step;
4251
+ p.y += (Math.sin(p.angle) * p.velocity + o.gravity * 3) * step;
4252
+ p.velocity *= o.decay ** step;
4253
+ p.wobble += p.wobbleSpeed * step;
4254
+ p.tilt += 0.1 * step;
4255
+ p.tick += step;
4256
+ if (p.tick >= o.ticks) return false;
4257
+ const size = 8 * o.scalar;
4258
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
4259
+ ctx.translate(p.x + 10 * o.scalar * Math.cos(p.wobble), p.y + 10 * o.scalar * Math.sin(p.wobble));
4260
+ ctx.rotate(p.tilt);
4261
+ ctx.scale(1, Math.cos(p.wobble));
4262
+ ctx.globalAlpha = 1 - p.tick / o.ticks;
4263
+ ctx.fillStyle = p.color;
4264
+ ctx.beginPath();
4265
+ if (p.shape === "circle") ctx.arc(0, 0, size / 2, 0, 2 * Math.PI);
4266
+ else if (p.shape === "star") star(ctx, size * 0.75);
4267
+ else ctx.rect(-size / 2, -size / 2, size, size);
4268
+ ctx.fill();
4269
+ return true;
4270
+ });
4271
+ if (scene.pieces.length) scene.frame = requestAnimationFrame((t) => draw(canvas, scene, t));
4272
+ else stop(canvas);
4273
+ }
4274
+ function stop(canvas) {
4275
+ const scene = scenes.get(canvas);
4276
+ if (!scene) return;
4277
+ cancelAnimationFrame(scene.frame);
4278
+ scenes.delete(canvas);
4279
+ scene.ctx.setTransform(1, 0, 0, 1, 0, 0);
4280
+ scene.ctx.clearRect(0, 0, canvas.width, canvas.height);
4281
+ if (scene.overlay) {
4282
+ canvas.remove();
4283
+ overlay = null;
4284
+ }
4285
+ for (const resolve of scene.done) resolve();
4286
+ }
4287
+ function star(ctx, r) {
4288
+ for (let i = 0; i < 10; i++) {
4289
+ const a = i * Math.PI / 5 - Math.PI / 2;
4290
+ const d = i % 2 ? r / 2 : r;
4291
+ ctx.lineTo(Math.cos(a) * d, Math.sin(a) * d);
4292
+ }
4293
+ ctx.closePath();
4294
+ }
4295
+ function fit(canvas) {
4296
+ const width = canvas.clientWidth;
4297
+ const height = canvas.clientHeight;
4298
+ const w = Math.round(width * devicePixelRatio);
4299
+ const h = Math.round(height * devicePixelRatio);
4300
+ if (canvas.width !== w || canvas.height !== h) {
4301
+ canvas.width = w;
4302
+ canvas.height = h;
4303
+ }
4304
+ return { width, height };
4305
+ }
4306
+ function viewportCanvas() {
4307
+ if (overlay?.isConnected) return overlay;
4308
+ overlay = document.createElement("canvas");
4309
+ overlay.className = "confetti-overlay";
4310
+ overlay.setAttribute("aria-hidden", "true");
4311
+ Object.assign(overlay.style, {
4312
+ position: "fixed",
4313
+ inset: "0",
4314
+ width: "100%",
4315
+ height: "100%",
4316
+ margin: "0",
4317
+ padding: "0",
4318
+ border: "0",
4319
+ background: "transparent",
4320
+ pointerEvents: "none",
4321
+ zIndex: "10001"
4322
+ });
4323
+ document.body.append(overlay);
4324
+ if (typeof overlay.showPopover === "function") {
4325
+ overlay.popover = "manual";
4326
+ overlay.showPopover();
4327
+ }
4328
+ return overlay;
4329
+ }
4330
+ function themeColors(themed) {
4331
+ const probe = document.createElement("span");
4332
+ probe.hidden = true;
4333
+ themed.append(probe);
4334
+ const style = getComputedStyle(probe);
4335
+ const colors = THEME_COLORS.filter((role) => style.getPropertyValue(`--md-sys-color-${role}`).trim()).map((role) => {
4336
+ probe.style.color = `var(--md-sys-color-${role})`;
4337
+ return getComputedStyle(probe).color;
4338
+ });
4339
+ probe.remove();
4340
+ return colors.length ? colors : FALLBACK_COLORS;
4341
+ }
4342
+
4343
+ // src/ts/components/particles.ts
4344
+ var _defaults13 = {
4345
+ quantity: 100,
4346
+ staticity: 50,
4347
+ ease: 50,
4348
+ size: 0.4,
4349
+ color: "",
4350
+ vx: 0,
4351
+ vy: 0
4352
+ };
4353
+ var Particles = class _Particles extends Component {
4354
+ constructor(el, options) {
4355
+ super(el, options, _Particles);
4356
+ this._particles = [];
4357
+ this._width = 0;
4358
+ this._height = 0;
4359
+ this._frame = 0;
4360
+ this._last = 0;
4361
+ // Pointer offset from the canvas center, kept from its last move inside.
4362
+ this._mouse = { x: 0, y: 0 };
4363
+ this._still = false;
4364
+ this._resizeObserver = null;
4365
+ this._visibility = null;
4366
+ this._onPointerMove = (e) => {
4367
+ const r = this.el.getBoundingClientRect();
4368
+ const x = e.clientX - r.left - r.width / 2;
4369
+ const y = e.clientY - r.top - r.height / 2;
4370
+ if (Math.abs(x) < r.width / 2 && Math.abs(y) < r.height / 2) this._mouse = { x, y };
4371
+ };
4372
+ this.el["Expressive_Particles"] = this;
4373
+ this.options = { ..._Particles.defaults, ...options };
4374
+ this._ctx = this.el.getContext("2d");
4375
+ if (!this._ctx) return;
4376
+ this._style = getComputedStyle(this.el);
4377
+ this._still = typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
4378
+ this._reset();
4379
+ if (typeof ResizeObserver !== "undefined") {
4380
+ this._resizeObserver = new ResizeObserver(() => {
4381
+ if (this.el.clientWidth !== this._width || this.el.clientHeight !== this._height) this._reset();
4382
+ });
4383
+ this._resizeObserver.observe(this.el);
4384
+ }
4385
+ if (this._still) return;
4386
+ if (typeof IntersectionObserver === "undefined") {
4387
+ this._start();
4388
+ return;
4389
+ }
4390
+ this._visibility = new IntersectionObserver(([entry]) => entry.isIntersecting ? this._start() : this._stop());
4391
+ this._visibility.observe(this.el);
4392
+ }
4393
+ static get defaults() {
4394
+ return _defaults13;
4395
+ }
4396
+ static init(els, options = {}) {
4397
+ return super.init(els, options, _Particles);
4398
+ }
4399
+ static getInstance(el) {
4400
+ return el["Expressive_Particles"];
4401
+ }
4402
+ destroy() {
4403
+ this._stop();
4404
+ this._resizeObserver?.disconnect();
4405
+ this._visibility?.disconnect();
4406
+ if (this._ctx) {
4407
+ this._ctx.setTransform(1, 0, 0, 1, 0, 0);
4408
+ this._ctx.clearRect(0, 0, this.el.width, this.el.height);
4409
+ }
4410
+ this.el["Expressive_Particles"] = void 0;
4411
+ }
4412
+ _start() {
4413
+ if (this._frame) return;
4414
+ window.addEventListener("pointermove", this._onPointerMove, { passive: true });
4415
+ this._frame = requestAnimationFrame((now) => {
4416
+ this._last = now;
4417
+ this._draw(now);
4418
+ });
4419
+ }
4420
+ _stop() {
4421
+ cancelAnimationFrame(this._frame);
4422
+ this._frame = 0;
4423
+ window.removeEventListener("pointermove", this._onPointerMove);
4424
+ }
4425
+ // Matches the pixel buffer to the canvas box and scatters a new set.
4426
+ _reset() {
4427
+ this._width = this.el.clientWidth;
4428
+ this._height = this.el.clientHeight;
4429
+ this.el.width = Math.round(this._width * devicePixelRatio);
4430
+ this.el.height = Math.round(this._height * devicePixelRatio);
4431
+ this._particles = Array.from({ length: this.options.quantity }, () => this._particle());
4432
+ if (this._still) {
4433
+ for (const p of this._particles) p.alpha = p.targetAlpha;
4434
+ this._paint();
4435
+ }
4436
+ }
4437
+ _particle() {
4438
+ return {
4439
+ x: Math.floor(Math.random() * this._width),
4440
+ y: Math.floor(Math.random() * this._height),
4441
+ translateX: 0,
4442
+ translateY: 0,
4443
+ size: Math.floor(Math.random() * 2) + this.options.size,
4444
+ alpha: 0,
4445
+ targetAlpha: Math.round((Math.random() * 0.6 + 0.1) * 10) / 10,
4446
+ dx: (Math.random() - 0.5) * 0.1,
4447
+ dy: (Math.random() - 0.5) * 0.1,
4448
+ magnetism: 0.1 + Math.random() * 4
4449
+ };
4450
+ }
4451
+ _draw(now) {
4452
+ const step = Math.min((now - this._last) / (1e3 / 60), 4);
4453
+ this._last = now;
4454
+ const { staticity, ease, vx, vy } = this.options;
4455
+ const follow = Math.min(step / ease, 1);
4456
+ this._particles = this._particles.map((p) => {
4457
+ p.x += (p.dx + vx) * step;
4458
+ p.y += (p.dy + vy) * step;
4459
+ p.translateX += (this._mouse.x / (staticity / p.magnetism) - p.translateX) * follow;
4460
+ p.translateY += (this._mouse.y / (staticity / p.magnetism) - p.translateY) * follow;
4461
+ const x = p.x + p.translateX;
4462
+ const y = p.y + p.translateY;
4463
+ const edge = Math.min(x - p.size, this._width - x - p.size, y - p.size, this._height - y - p.size) / 20;
4464
+ p.alpha = edge > 1 ? Math.min(p.alpha + 0.02 * step, p.targetAlpha) : p.targetAlpha * edge;
4465
+ const gone = p.x < -p.size || p.x > this._width + p.size || p.y < -p.size || p.y > this._height + p.size;
4466
+ return gone ? this._particle() : p;
4467
+ });
4468
+ this._paint();
4469
+ this._frame = requestAnimationFrame((t) => this._draw(t));
4470
+ }
4471
+ _paint() {
4472
+ const ctx = this._ctx;
4473
+ const dpr = devicePixelRatio;
4474
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
4475
+ ctx.clearRect(0, 0, this.el.width, this.el.height);
4476
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
4477
+ ctx.fillStyle = this.options.color || this._style.color;
4478
+ for (const p of this._particles) {
4479
+ if (p.alpha <= 0) continue;
4480
+ ctx.globalAlpha = p.alpha;
4481
+ ctx.beginPath();
4482
+ ctx.arc(p.x + p.translateX, p.y + p.translateY, p.size, 0, 2 * Math.PI);
4483
+ ctx.fill();
4484
+ }
4485
+ ctx.globalAlpha = 1;
4486
+ }
4487
+ };
4488
+
4489
+ // src/ts/components/commandPalette.ts
4490
+ var _defaults14 = {
4117
4491
  shortcut: "k",
4118
4492
  closeOnRun: true
4119
4493
  };
@@ -4211,7 +4585,7 @@ var CommandPalette = class _CommandPalette extends Component {
4211
4585
  document.addEventListener("keydown", this._handleShortcut);
4212
4586
  }
4213
4587
  static get defaults() {
4214
- return _defaults12;
4588
+ return _defaults14;
4215
4589
  }
4216
4590
  /**
4217
4591
  * Initializes instances of CommandPalette.
@@ -4364,7 +4738,7 @@ var CommandPalette = class _CommandPalette extends Component {
4364
4738
  };
4365
4739
 
4366
4740
  // src/ts/components/questionnaire.ts
4367
- var _defaults13 = {
4741
+ var _defaults15 = {
4368
4742
  progressLabel: "Question {current} of {total}",
4369
4743
  requiredMessage: "Choose an answer to continue."
4370
4744
  };
@@ -4478,7 +4852,7 @@ var Questionnaire = class _Questionnaire extends Component {
4478
4852
  this.el.addEventListener("invalid", this._handleInvalid, true);
4479
4853
  }
4480
4854
  static get defaults() {
4481
- return _defaults13;
4855
+ return _defaults15;
4482
4856
  }
4483
4857
  /**
4484
4858
  * Initializes instances of Questionnaire.
@@ -4623,7 +4997,7 @@ var Questionnaire = class _Questionnaire extends Component {
4623
4997
  };
4624
4998
 
4625
4999
  // src/ts/components/lineChart.ts
4626
- var _defaults14 = {
5000
+ var _defaults16 = {
4627
5001
  min: null,
4628
5002
  max: null
4629
5003
  };
@@ -4751,7 +5125,7 @@ var LineChart = class extends Component {
4751
5125
  return "Expressive_LineChart";
4752
5126
  }
4753
5127
  static get defaults() {
4754
- return _defaults14;
5128
+ return _defaults16;
4755
5129
  }
4756
5130
  static init(els, options = {}) {
4757
5131
  return super.init(els, options, this);
@@ -5154,18 +5528,25 @@ var PieChart = class extends LineChart {
5154
5528
  }
5155
5529
  /** The row's share of the whole as a percentage, or '' for none. */
5156
5530
  _share(index) {
5531
+ var _a;
5157
5532
  const [start, end] = this._turns[index];
5158
5533
  if (end <= start) return "";
5159
5534
  const share = end - start;
5160
- const options = { style: "percent", maximumFractionDigits: share < 0.1 ? 1 : 0 };
5535
+ const digits = share < 0.1 ? 1 : 0;
5536
+ (_a = this._percents ?? (this._percents = []))[digits] ?? (_a[digits] = this._percent(digits));
5537
+ return this._percents[digits].format(share);
5538
+ }
5539
+ _percent(digits) {
5540
+ const options = { style: "percent", maximumFractionDigits: digits };
5161
5541
  try {
5162
- return share.toLocaleString(this.el.closest("[lang]")?.lang || void 0, options);
5542
+ return new Intl.NumberFormat(this.el.closest("[lang]")?.lang || void 0, options);
5163
5543
  } catch {
5164
- return share.toLocaleString(void 0, options);
5544
+ return new Intl.NumberFormat(void 0, options);
5165
5545
  }
5166
5546
  }
5167
5547
  /** The legend: every row with its color, its cell and its share. */
5168
5548
  _guides() {
5549
+ this._percents = [];
5169
5550
  const legend = document.createElement("ul");
5170
5551
  legend.className = "pie-chart-legend";
5171
5552
  legend.setAttribute("aria-hidden", "true");
@@ -5231,14 +5612,14 @@ var PieChart = class extends LineChart {
5231
5612
  svgNode("circle", { class: "pie-chart-hole", r: String(RADIUS), fill: "black" })
5232
5613
  );
5233
5614
  const slices = svgNode("g", { mask: `url(#${id})` });
5234
- const arc = `A${RADIUS},${RADIUS} 0`;
5615
+ const arc2 = `A${RADIUS},${RADIUS} 0`;
5235
5616
  this._slices = this._turns.map(([start, end], i) => {
5236
5617
  if (end <= start) return null;
5237
5618
  const slice2 = svgNode("path", {
5238
5619
  class: "pie-chart-slice",
5239
5620
  "data-series": this._colors[i],
5240
5621
  // A whole circle is two half arcs; one arc cannot end where it starts.
5241
- d: end - start >= 1 ? `M0,${-RADIUS}${arc} 1 1 0,${RADIUS}${arc} 1 1 0,${-RADIUS}Z` : `M0,0L${at(start)}${arc} ${end - start > 0.5 ? 1 : 0} 1 ${at(end)}Z`
5622
+ d: end - start >= 1 ? `M0,${-RADIUS}${arc2} 1 1 0,${RADIUS}${arc2} 1 1 0,${-RADIUS}Z` : `M0,0L${at(start)}${arc2} ${end - start > 0.5 ? 1 : 0} 1 ${at(end)}Z`
5242
5623
  });
5243
5624
  slices.append(slice2);
5244
5625
  return slice2;
@@ -5281,9 +5662,10 @@ var HeatmapChart = class extends LineChart {
5281
5662
  /** Highlights one cell, numbered row by row. Pass -1 to hide the tooltip. */
5282
5663
  show(index) {
5283
5664
  if (!this._tooltip || index === this.activeIndex) return;
5665
+ const cell = this._cells[index];
5666
+ const [left, top, width, plotWidth] = cell ? [cell.offsetLeft, cell.offsetTop, cell.offsetWidth, this._plot.clientWidth] : [];
5284
5667
  this._cells[this.activeIndex]?.classList.remove("active");
5285
5668
  this.activeIndex = index;
5286
- const cell = this._cells[index];
5287
5669
  this._tooltip.hidden = !cell;
5288
5670
  if (!cell) return;
5289
5671
  cell.classList.add("active");
@@ -5297,10 +5679,10 @@ var HeatmapChart = class extends LineChart {
5297
5679
  else name.style.setProperty("--level", String(this._levels[index]));
5298
5680
  text.textContent = series.text[row];
5299
5681
  this._tooltip.replaceChildren(title, name, text);
5300
- const end = cell.offsetLeft + cell.offsetWidth / 2 > this._plot.clientWidth / 2;
5682
+ const end = left + width / 2 > plotWidth / 2;
5301
5683
  this._tooltip.classList.toggle("end", end);
5302
- this._tooltip.style.left = `${cell.offsetLeft + (end ? 0 : cell.offsetWidth)}px`;
5303
- this._tooltip.style.top = `${cell.offsetTop}px`;
5684
+ this._tooltip.style.left = `${left + (end ? 0 : width)}px`;
5685
+ this._tooltip.style.top = `${top}px`;
5304
5686
  }
5305
5687
  /** Arrows move the way they point, so left and right swap in RTL. */
5306
5688
  _keyTarget(key) {
@@ -5681,7 +6063,7 @@ var MixedChart = class extends ColumnChart {
5681
6063
  };
5682
6064
 
5683
6065
  // src/ts/components/select.ts
5684
- var _defaults15 = {
6066
+ var _defaults17 = {
5685
6067
  classes: "",
5686
6068
  menuOptions: {}
5687
6069
  };
@@ -5763,7 +6145,7 @@ var FormSelect = class _FormSelect extends Component {
5763
6145
  this._setupEventHandlers();
5764
6146
  }
5765
6147
  static get defaults() {
5766
- return _defaults15;
6148
+ return _defaults17;
5767
6149
  }
5768
6150
  /**
5769
6151
  * Initializes instances of FormSelect.
@@ -6188,7 +6570,7 @@ var FormSelect = class _FormSelect extends Component {
6188
6570
  };
6189
6571
 
6190
6572
  // src/ts/plugins/dockedDisplayPlugin.ts
6191
- var _defaults16 = {
6573
+ var _defaults18 = {
6192
6574
  margin: 5,
6193
6575
  transition: 10,
6194
6576
  duration: 250,
@@ -6202,9 +6584,9 @@ var DockedDisplayPlugin = class _DockedDisplayPlugin {
6202
6584
  }
6203
6585
  };
6204
6586
  /**
6205
- * Tears the plugin down. The document listener holds a reference to this
6206
- * instance - and through it the whole picker subtree - so the owning
6207
- * component must call this from its own destroy().
6587
+ * Tears the plugin down. While shown, the document listener holds a
6588
+ * reference to this instance - and through it the whole picker subtree - so
6589
+ * the owning component must call this from its own destroy().
6208
6590
  */
6209
6591
  this.destroy = () => {
6210
6592
  document.removeEventListener("click", this._handleDocumentClick);
@@ -6213,6 +6595,7 @@ var DockedDisplayPlugin = class _DockedDisplayPlugin {
6213
6595
  this.show = () => {
6214
6596
  if (this.visible) return;
6215
6597
  this.visible = true;
6598
+ document.addEventListener("click", this._handleDocumentClick);
6216
6599
  const coordinates = Utils._setAbsolutePosition(this.el, this.container, "bottom", this.options.margin, this.options.transition, this.options.align);
6217
6600
  this.container.style.visibility = "visible";
6218
6601
  this.container.style.transition = `
@@ -6226,6 +6609,7 @@ var DockedDisplayPlugin = class _DockedDisplayPlugin {
6226
6609
  this.hide = () => {
6227
6610
  if (!this.visible) return;
6228
6611
  this.visible = false;
6612
+ document.removeEventListener("click", this._handleDocumentClick);
6229
6613
  this.container.removeAttribute("style");
6230
6614
  this.container.style.transition = `
6231
6615
  transform ${this.options.duration}ms ease-out,
@@ -6237,14 +6621,13 @@ var DockedDisplayPlugin = class _DockedDisplayPlugin {
6237
6621
  };
6238
6622
  this.el = el;
6239
6623
  this.options = {
6240
- ..._defaults16,
6624
+ ..._defaults18,
6241
6625
  ...options
6242
6626
  };
6243
6627
  this.container = document.createElement("div");
6244
6628
  this.container.classList.add("display-docked");
6245
6629
  this.container.append(container);
6246
6630
  el.parentElement.append(this.container);
6247
- document.addEventListener("click", this._handleDocumentClick);
6248
6631
  }
6249
6632
  /**
6250
6633
  * Initializes instance of DockedDisplayPlugin
@@ -6258,7 +6641,7 @@ var DockedDisplayPlugin = class _DockedDisplayPlugin {
6258
6641
  };
6259
6642
 
6260
6643
  // src/ts/components/datepicker.ts
6261
- var _defaults17 = {
6644
+ var _defaults19 = {
6262
6645
  // the default output format for the input field value
6263
6646
  format: "mmm dd, yyyy",
6264
6647
  // Used to create date object from current input string
@@ -6362,6 +6745,8 @@ var _Datepicker = class _Datepicker extends Component {
6362
6745
  constructor(el, options) {
6363
6746
  super(el, options, _Datepicker);
6364
6747
  this.multiple = false;
6748
+ /** Rows renderRow has already checked, so renderBody does not parse them again. */
6749
+ this._checkedRows = /* @__PURE__ */ new Set();
6365
6750
  /** Nesting depth of {@link _batchDraws}; > 0 means draws are being collected. */
6366
6751
  this._drawDepth = 0;
6367
6752
  /** Whether a draw was requested while batching. */
@@ -6428,7 +6813,9 @@ var _Datepicker = class _Datepicker extends Component {
6428
6813
  if (maxDate && date > maxDate) date.setTime(maxDate.getTime());
6429
6814
  if (date.getFullYear() < minYear || date.getFullYear() > maxYear || date.getFullYear() === minYear && date.getMonth() < (minMonth ?? 0) || date.getFullYear() === maxYear && date.getMonth() > (maxMonth ?? 11)) return;
6430
6815
  this._focusedDate = date;
6431
- this.gotoDate(date);
6816
+ const { year, month } = this.calendars[0];
6817
+ if (date.getFullYear() === year && date.getMonth() === month) this._syncCalendarFocus(true);
6818
+ else this.gotoDate(date);
6432
6819
  };
6433
6820
  this._handleCalendarClick = (e) => {
6434
6821
  const target = e.target;
@@ -6543,7 +6930,7 @@ var _Datepicker = class _Datepicker extends Component {
6543
6930
  }
6544
6931
  }
6545
6932
  static get defaults() {
6546
- return _defaults17;
6933
+ return _defaults19;
6547
6934
  }
6548
6935
  /**
6549
6936
  * Initializes instances of Datepicker.
@@ -6985,7 +7372,9 @@ var _Datepicker = class _Datepicker extends Component {
6985
7372
  }
6986
7373
  /** Assemble calendar cells; reject unsupported elements and attributes. */
6987
7374
  renderRow(days, isRTL, isRowSelected) {
6988
- return '<tr class="datepicker-row' + (isRowSelected ? " is-selected" : "") + '">' + _Datepicker._calendarMarkup((isRTL ? days.reverse() : days).join(""), "tr") + "</tr>";
7375
+ const row = '<tr class="datepicker-row' + (isRowSelected ? " is-selected" : "") + '">' + _Datepicker._calendarMarkup((isRTL ? days.reverse() : days).join(""), "tr") + "</tr>";
7376
+ this._checkedRows.add(row);
7377
+ return row;
6989
7378
  }
6990
7379
  /** Assemble calendar rows; reject unsupported elements and attributes. */
6991
7380
  renderTable(opts, data, randId) {
@@ -7003,7 +7392,10 @@ var _Datepicker = class _Datepicker extends Component {
7003
7392
  }
7004
7393
  /** Wrap calendar rows after validating and encoding their contents. */
7005
7394
  renderBody(rows) {
7006
- return "<tbody>" + _Datepicker._calendarMarkup(rows.join(""), "tbody") + "</tbody>";
7395
+ const html = rows.join("");
7396
+ const checked = rows.every((row) => this._checkedRows.has(row));
7397
+ this._checkedRows.clear();
7398
+ return "<tbody>" + (checked ? html : _Datepicker._calendarMarkup(html, "tbody")) + "</tbody>";
7007
7399
  }
7008
7400
  static _calendarMarkup(html, parent) {
7009
7401
  const template = document.createElement("template");
@@ -7359,7 +7751,7 @@ _Datepicker._template = `
7359
7751
  var Datepicker = _Datepicker;
7360
7752
 
7361
7753
  // src/ts/components/messageScroller.ts
7362
- var _defaults18 = {
7754
+ var _defaults20 = {
7363
7755
  autoScroll: true,
7364
7756
  defaultScrollPosition: "end",
7365
7757
  peek: 64
@@ -7482,7 +7874,7 @@ var MessageScroller = class _MessageScroller extends Component {
7482
7874
  this.button?.addEventListener("click", this._handleButtonClick);
7483
7875
  }
7484
7876
  static get defaults() {
7485
- return _defaults18;
7877
+ return _defaults20;
7486
7878
  }
7487
7879
  /**
7488
7880
  * Initializes instances of MessageScroller.
@@ -7640,7 +8032,7 @@ var MessageScroller = class _MessageScroller extends Component {
7640
8032
  };
7641
8033
 
7642
8034
  // src/ts/components/richTextEditor.ts
7643
- var _defaults19 = {
8035
+ var _defaults21 = {
7644
8036
  Editor: null,
7645
8037
  extensions: []
7646
8038
  };
@@ -7720,7 +8112,7 @@ var RichTextEditor = class _RichTextEditor extends Component {
7720
8112
  el.addEventListener("click", this._handleClick);
7721
8113
  }
7722
8114
  static get defaults() {
7723
- return _defaults19;
8115
+ return _defaults21;
7724
8116
  }
7725
8117
  /**
7726
8118
  * Initializes instances of RichTextEditor.
@@ -7752,8 +8144,395 @@ var RichTextEditor = class _RichTextEditor extends Component {
7752
8144
  }
7753
8145
  };
7754
8146
 
8147
+ // src/ts/components/mapView.ts
8148
+ var OPENFREEMAP = "https://tiles.openfreemap.org/styles/";
8149
+ var _defaults22 = {
8150
+ maplibregl: null,
8151
+ style: { light: `${OPENFREEMAP}positron`, dark: `${OPENFREEMAP}dark` },
8152
+ center: [0, 20],
8153
+ zoom: 1,
8154
+ mapOptions: {}
8155
+ };
8156
+ var lngLat = (el) => el.dataset.lngLat.split(",").map(Number);
8157
+ function arc([x0, y0], [x2, y2], steps = 32) {
8158
+ const bow = x2 >= x0 ? 0.25 : -0.25;
8159
+ const x1 = (x0 + x2) / 2 - (y2 - y0) * bow;
8160
+ const y1 = (y0 + y2) / 2 + (x2 - x0) * bow;
8161
+ return Array.from({ length: steps + 1 }, (_, i) => {
8162
+ const t = i / steps, u = 1 - t;
8163
+ return [u * u * x0 + 2 * u * t * x1 + t * t * x2, u * u * y0 + 2 * u * t * y1 + t * t * y2];
8164
+ });
8165
+ }
8166
+ var MapView = class _MapView extends Component {
8167
+ constructor(el, options) {
8168
+ super(el, options, _MapView);
8169
+ /** The MapLibre map. */
8170
+ this.map = null;
8171
+ /** The MapLibre markers made from `.map-marker` children, in order. */
8172
+ this.markers = [];
8173
+ this._children = [];
8174
+ this._popups = [];
8175
+ this._markerPopups = /* @__PURE__ */ new Map();
8176
+ this._overlays = /* @__PURE__ */ new Map();
8177
+ this._styleReady = false;
8178
+ this._dark = false;
8179
+ this._location = null;
8180
+ this._media = null;
8181
+ this._observer = null;
8182
+ // Looked up once: `move` fires every frame of a pan or zoom.
8183
+ this._zoomIn = [];
8184
+ this._zoomOut = [];
8185
+ this._handleStyleLoad = () => {
8186
+ this._styleReady = true;
8187
+ for (const overlay2 of this._overlays.values()) overlay2.draw();
8188
+ };
8189
+ this._handleScheme = () => {
8190
+ const dark = this._isDark();
8191
+ if (dark === this._dark) return;
8192
+ this._dark = dark;
8193
+ this._styleReady = false;
8194
+ this.map.setStyle(this._style(), { diff: false });
8195
+ };
8196
+ this._sync = () => {
8197
+ const zoom = this.map.getZoom();
8198
+ for (const button of this._zoomIn) button.disabled = zoom >= this.map.getMaxZoom();
8199
+ for (const button of this._zoomOut) button.disabled = zoom <= this.map.getMinZoom();
8200
+ this.el.style.setProperty("--md-comp-map-bearing", `${-this.map.getBearing()}deg`);
8201
+ };
8202
+ this._handleFullscreen = () => {
8203
+ for (const button of this._buttons("fullscreen")) {
8204
+ button.setAttribute("aria-pressed", String(document.fullscreenElement === this.el));
8205
+ }
8206
+ };
8207
+ this._handleClick = (e) => {
8208
+ const target = e.target;
8209
+ const button = target.closest(".map-controls [data-action]");
8210
+ const action = button?.dataset.action;
8211
+ if (action === "zoom-in") this.map.zoomIn();
8212
+ else if (action === "zoom-out") this.map.zoomOut();
8213
+ else if (action === "compass") this.map.resetNorthPitch();
8214
+ else if (action === "locate") this._locate(button);
8215
+ else if (action === "fullscreen") {
8216
+ if (document.fullscreenElement === this.el) document.exitFullscreen();
8217
+ else this.el.requestFullscreen();
8218
+ } else {
8219
+ const marker = target.closest(".map-marker");
8220
+ if (this._markerPopups.has(marker)) this._toggle(marker);
8221
+ }
8222
+ };
8223
+ // A click on the map closes marker popups, but not the one being toggled.
8224
+ this._handleMapClick = (e) => {
8225
+ const target = e.originalEvent.target;
8226
+ for (const [marker, popup] of this._markerPopups) {
8227
+ if (!marker.contains(target) && !popup.getElement()?.contains(target)) popup.remove();
8228
+ }
8229
+ };
8230
+ this._handleKeydown = (e) => {
8231
+ if (e.key !== "Escape") return;
8232
+ for (const popup of this._popups) {
8233
+ if (popup.getElement()?.contains(e.target)) {
8234
+ e.preventDefault();
8235
+ popup.remove();
8236
+ }
8237
+ }
8238
+ };
8239
+ this.el["Expressive_MapView"] = this;
8240
+ this.options = {
8241
+ ..._MapView.defaults,
8242
+ ...options
8243
+ };
8244
+ const lib = this.options.maplibregl;
8245
+ if (!lib) {
8246
+ console.error("MapView needs the maplibre-gl module in options.maplibregl.");
8247
+ return;
8248
+ }
8249
+ this._children = [...el.children];
8250
+ this._zoomIn = [...this._buttons("zoom-in")];
8251
+ this._zoomOut = [...this._buttons("zoom-out")];
8252
+ this._dark = this._isDark();
8253
+ this.map = new lib.Map({
8254
+ ...this.options.mapOptions,
8255
+ container: el,
8256
+ style: this._style(),
8257
+ center: this.options.center,
8258
+ zoom: this.options.zoom
8259
+ });
8260
+ this.map.on("style.load", this._handleStyleLoad);
8261
+ this.map.on("move", this._sync);
8262
+ this.map.on("click", this._handleMapClick);
8263
+ const children = this._children.filter((child) => child instanceof HTMLElement);
8264
+ const claimed = /* @__PURE__ */ new Set();
8265
+ for (const marker of children.filter((child) => child.matches(".map-marker[data-lng-lat]"))) {
8266
+ const target = marker.hasAttribute("aria-controls") ? Utils.getElementById(marker, marker.getAttribute("aria-controls")) : null;
8267
+ const popup = target?.parentElement === el && target.matches(".map-popup") ? target : null;
8268
+ if (popup) claimed.add(popup);
8269
+ this._addMarker(marker, popup);
8270
+ }
8271
+ for (const popup of children.filter((child) => child.matches(".map-popup[data-lng-lat]") && !claimed.has(child))) {
8272
+ this._popup(popup, 0).setLngLat(lngLat(popup)).addTo(this.map);
8273
+ }
8274
+ if (this._themed()) {
8275
+ this._media = matchMedia("(prefers-color-scheme: dark)");
8276
+ this._media.addEventListener("change", this._handleScheme);
8277
+ this._observer = new MutationObserver(this._handleScheme);
8278
+ this._observer.observe(document.documentElement, { attributes: true, attributeFilter: ["theme"], subtree: true });
8279
+ }
8280
+ for (const button of this._buttons("fullscreen")) button.hidden = !document.fullscreenEnabled;
8281
+ document.addEventListener("fullscreenchange", this._handleFullscreen);
8282
+ el.addEventListener("click", this._handleClick);
8283
+ el.addEventListener("keydown", this._handleKeydown);
8284
+ this._sync();
8285
+ }
8286
+ static get defaults() {
8287
+ return _defaults22;
8288
+ }
8289
+ /**
8290
+ * Initializes instances of MapView.
8291
+ * @param els HTML elements.
8292
+ * @param options Component options.
8293
+ */
8294
+ static init(els, options = {}) {
8295
+ return super.init(els, options, _MapView);
8296
+ }
8297
+ static getInstance(el) {
8298
+ return el["Expressive_MapView"];
8299
+ }
8300
+ destroy() {
8301
+ this.el["Expressive_MapView"] = void 0;
8302
+ if (!this.map) return;
8303
+ this.el.removeEventListener("click", this._handleClick);
8304
+ this.el.removeEventListener("keydown", this._handleKeydown);
8305
+ document.removeEventListener("fullscreenchange", this._handleFullscreen);
8306
+ this._media?.removeEventListener("change", this._handleScheme);
8307
+ this._observer?.disconnect();
8308
+ for (const overlay2 of this._overlays.values()) overlay2.off?.();
8309
+ for (const popup of this._popups) popup.remove();
8310
+ for (const marker of this.markers) {
8311
+ marker.remove();
8312
+ const element = marker.getElement();
8313
+ element.classList.remove(...[...element.classList].filter((name) => name.startsWith("maplibregl-")));
8314
+ element.style.removeProperty("transform");
8315
+ element.style.removeProperty("opacity");
8316
+ if (element.hasAttribute("aria-controls")) element.removeAttribute("aria-expanded");
8317
+ }
8318
+ this._location?.remove();
8319
+ this._location = null;
8320
+ this.map.remove();
8321
+ this.map = null;
8322
+ this.markers = [];
8323
+ this._popups = [];
8324
+ this._markerPopups.clear();
8325
+ this._overlays.clear();
8326
+ this.el.style.removeProperty("--md-comp-map-bearing");
8327
+ for (const button of this.el.querySelectorAll(".map-controls [data-action]")) {
8328
+ button.disabled = false;
8329
+ button.hidden = false;
8330
+ button.removeAttribute("aria-pressed");
8331
+ }
8332
+ this.el.append(...this._children);
8333
+ }
8334
+ /** Draws a line through `[longitude, latitude]` points. Replaces an overlay with the same id. */
8335
+ addRoute(id, coordinates, options = {}) {
8336
+ this._line(id, { type: "LineString", coordinates }, "route", { width: 4, ...options });
8337
+ }
8338
+ /** Draws a curved line for each `{ from, to }`. Replaces an overlay with the same id. */
8339
+ addArcs(id, arcs, options = {}) {
8340
+ this._line(id, { type: "MultiLineString", coordinates: arcs.map(({ from, to }) => arc(from, to)) }, "arc", { width: 2, ...options });
8341
+ }
8342
+ /**
8343
+ * Draws GeoJSON points, or the URL of a GeoJSON file, as clusters that
8344
+ * show their count and zoom in when clicked. The layers are
8345
+ * `${id}-clusters`, `${id}-count` and `${id}-points`.
8346
+ */
8347
+ addClusters(id, data, options = {}) {
8348
+ const clusters = `${id}-clusters`, points = `${id}-points`;
8349
+ const zoomIn = async (e) => {
8350
+ const [feature] = e.features;
8351
+ const zoom = await this.map.getSource(id).getClusterExpansionZoom(feature.properties.cluster_id);
8352
+ this.map.easeTo({ center: feature.geometry.coordinates, zoom });
8353
+ };
8354
+ const pointer = () => {
8355
+ this.map.getCanvas().style.cursor = "pointer";
8356
+ };
8357
+ const reset = () => {
8358
+ this.map.getCanvas().style.cursor = "";
8359
+ };
8360
+ this._draw(id, {
8361
+ draw: () => {
8362
+ const color = options.color ?? this._color("var(--md-comp-map-cluster-color)");
8363
+ const surface = this._color("var(--md-comp-map-marker-ring-color)");
8364
+ this.map.addSource(id, {
8365
+ type: "geojson",
8366
+ data,
8367
+ cluster: true,
8368
+ clusterRadius: options.radius ?? 50,
8369
+ clusterMaxZoom: options.maxZoom ?? 14
8370
+ });
8371
+ this.map.addLayer({
8372
+ id: clusters,
8373
+ type: "circle",
8374
+ source: id,
8375
+ filter: ["has", "point_count"],
8376
+ paint: {
8377
+ "circle-color": color,
8378
+ "circle-radius": ["step", ["get", "point_count"], 16, 10, 20, 100, 26],
8379
+ "circle-stroke-width": 6,
8380
+ "circle-stroke-color": color,
8381
+ "circle-stroke-opacity": 0.3
8382
+ }
8383
+ });
8384
+ const style = this.map.getStyle();
8385
+ const font = style.glyphs && style.layers.find((layer) => layer.layout?.["text-font"])?.layout["text-font"];
8386
+ if (font) {
8387
+ this.map.addLayer({
8388
+ id: `${id}-count`,
8389
+ type: "symbol",
8390
+ source: id,
8391
+ filter: ["has", "point_count"],
8392
+ layout: { "text-field": ["get", "point_count_abbreviated"], "text-font": font, "text-size": 12 },
8393
+ paint: { "text-color": this._color("var(--md-comp-map-cluster-label-color)") }
8394
+ });
8395
+ }
8396
+ this.map.addLayer({
8397
+ id: points,
8398
+ type: "circle",
8399
+ source: id,
8400
+ filter: ["!", ["has", "point_count"]],
8401
+ paint: { "circle-color": color, "circle-radius": 6, "circle-stroke-width": 2, "circle-stroke-color": surface }
8402
+ });
8403
+ },
8404
+ off: () => {
8405
+ this.map.off("click", clusters, zoomIn);
8406
+ for (const layer of [clusters, points]) {
8407
+ this.map.off("mouseenter", layer, pointer);
8408
+ this.map.off("mouseleave", layer, reset);
8409
+ }
8410
+ }
8411
+ });
8412
+ this.map.on("click", clusters, zoomIn);
8413
+ for (const layer of [clusters, points]) {
8414
+ this.map.on("mouseenter", layer, pointer);
8415
+ this.map.on("mouseleave", layer, reset);
8416
+ }
8417
+ }
8418
+ /** Removes a route, arcs or clusters added with this id. */
8419
+ removeOverlay(id) {
8420
+ const overlay2 = this._overlays.get(id);
8421
+ if (!overlay2) return;
8422
+ this._overlays.delete(id);
8423
+ overlay2.off?.();
8424
+ if (!this._styleReady || !this.map.getSource(id)) return;
8425
+ for (const layer of this.map.getStyle().layers) if (layer.source === id) this.map.removeLayer(layer.id);
8426
+ this.map.removeSource(id);
8427
+ }
8428
+ _line(id, geometry, token, options) {
8429
+ this._draw(id, {
8430
+ draw: () => {
8431
+ this.map.addSource(id, { type: "geojson", data: { type: "Feature", properties: {}, geometry } });
8432
+ this.map.addLayer({
8433
+ id,
8434
+ type: "line",
8435
+ source: id,
8436
+ layout: { "line-cap": "round", "line-join": "round" },
8437
+ paint: {
8438
+ "line-color": options.color ?? this._color(`var(--md-comp-map-${token}-color)`),
8439
+ "line-width": options.width,
8440
+ "line-opacity": options.opacity ?? 1,
8441
+ ...options.dashArray && { "line-dasharray": options.dashArray }
8442
+ }
8443
+ });
8444
+ }
8445
+ });
8446
+ }
8447
+ // Sources and layers go with the style, so every overlay is drawn again
8448
+ // when a theme change swaps it.
8449
+ _draw(id, overlay2) {
8450
+ this.removeOverlay(id);
8451
+ this._overlays.set(id, overlay2);
8452
+ if (this._styleReady) overlay2.draw();
8453
+ }
8454
+ _addMarker(element, content) {
8455
+ const marker = new this.options.maplibregl.Marker({
8456
+ element,
8457
+ draggable: element.hasAttribute("data-draggable")
8458
+ }).setLngLat(lngLat(element)).addTo(this.map);
8459
+ marker.on("dragend", () => {
8460
+ const { lng, lat } = marker.getLngLat();
8461
+ element.dataset.lngLat = `${lng},${lat}`;
8462
+ element.dispatchEvent(new Event("change", { bubbles: true }));
8463
+ });
8464
+ this.markers.push(marker);
8465
+ if (!content) return;
8466
+ element.setAttribute("aria-expanded", "false");
8467
+ const popup = this._popup(content, 16);
8468
+ popup.on("open", () => element.setAttribute("aria-expanded", "true"));
8469
+ popup.on("close", () => {
8470
+ element.setAttribute("aria-expanded", "false");
8471
+ if (!document.activeElement || document.activeElement === document.body) element.focus();
8472
+ });
8473
+ this._markerPopups.set(element, popup);
8474
+ }
8475
+ _popup(content, offset) {
8476
+ const popup = new this.options.maplibregl.Popup({ closeOnClick: false, maxWidth: "none", offset }).setDOMContent(content);
8477
+ this._popups.push(popup);
8478
+ return popup;
8479
+ }
8480
+ _toggle(marker) {
8481
+ const popup = this._markerPopups.get(marker);
8482
+ if (popup.isOpen()) popup.remove();
8483
+ else popup.setLngLat(this.markers.find((m) => m.getElement() === marker).getLngLat()).addTo(this.map);
8484
+ }
8485
+ _buttons(action) {
8486
+ return this.el.querySelectorAll(`.map-controls [data-action="${action}"]`);
8487
+ }
8488
+ _locate(button) {
8489
+ navigator.geolocation?.getCurrentPosition(
8490
+ ({ coords }) => {
8491
+ if (!this.map) return;
8492
+ const center = [coords.longitude, coords.latitude];
8493
+ if (!this._location) {
8494
+ const dot = document.createElement("span");
8495
+ dot.className = "map-marker location";
8496
+ dot.setAttribute("aria-hidden", "true");
8497
+ this._location = new this.options.maplibregl.Marker({ element: dot });
8498
+ }
8499
+ this._location.setLngLat(center).addTo(this.map);
8500
+ this.map.flyTo({ center, zoom: Math.max(this.map.getZoom(), 14) });
8501
+ },
8502
+ (error) => {
8503
+ if (this.map && error.code === error.PERMISSION_DENIED) button.disabled = true;
8504
+ }
8505
+ );
8506
+ }
8507
+ // Resolves a CSS color where the map is, so light-dark() and scoped
8508
+ // themes apply, then paints it on a canvas to read back the sRGB value.
8509
+ // MapLibre does not parse the oklch() the theme tokens compute to.
8510
+ _color(value) {
8511
+ const probe = document.createElement("span");
8512
+ probe.hidden = true;
8513
+ probe.style.color = value;
8514
+ this.el.append(probe);
8515
+ const context = document.createElement("canvas").getContext("2d", { willReadFrequently: true });
8516
+ context.fillStyle = getComputedStyle(probe).color;
8517
+ probe.remove();
8518
+ context.fillRect(0, 0, 1, 1);
8519
+ const [r, g, b, a] = context.getImageData(0, 0, 1, 1).data;
8520
+ return `rgba(${r}, ${g}, ${b}, ${Math.round(a / 255 * 100) / 100})`;
8521
+ }
8522
+ _isDark() {
8523
+ return this._color("light-dark(black, white)") === "rgba(255, 255, 255, 1)";
8524
+ }
8525
+ _themed() {
8526
+ const style = this.options.style;
8527
+ return typeof style === "object" && "light" in style && "dark" in style;
8528
+ }
8529
+ _style() {
8530
+ return this._themed() ? this.options.style[this._dark ? "dark" : "light"] : this.options.style;
8531
+ }
8532
+ };
8533
+
7755
8534
  // src/ts/components/lightbox.ts
7756
- var _defaults20 = {
8535
+ var _defaults23 = {
7757
8536
  inDuration: 275,
7758
8537
  outDuration: 200,
7759
8538
  onOpenStart: null,
@@ -7902,7 +8681,7 @@ var _Lightbox = class _Lightbox extends Component {
7902
8681
  this._setupEventHandlers();
7903
8682
  }
7904
8683
  static get defaults() {
7905
- return _defaults20;
8684
+ return _defaults23;
7906
8685
  }
7907
8686
  /**
7908
8687
  * Initializes instances of Lightbox.
@@ -8108,7 +8887,7 @@ var Lightbox = _Lightbox;
8108
8887
 
8109
8888
  // src/ts/components/scrollspy.ts
8110
8889
  var _defaultGetActiveElement = (id) => `a[href="#${id}"]`;
8111
- var _defaults21 = {
8890
+ var _defaults24 = {
8112
8891
  scrollOffset: 200,
8113
8892
  activeClass: "active",
8114
8893
  getActiveElement: _defaultGetActiveElement,
@@ -8144,7 +8923,7 @@ var _ScrollSpy = class _ScrollSpy extends Component {
8144
8923
  }
8145
8924
  }
8146
8925
  static get defaults() {
8147
- return _defaults21;
8926
+ return _defaults24;
8148
8927
  }
8149
8928
  static init(els, options = {}) {
8150
8929
  return super.init(els, options, _ScrollSpy);
@@ -8178,8 +8957,9 @@ var _ScrollSpy = class _ScrollSpy extends Component {
8178
8957
  const retained = elements.filter((spy) => spy.options.keepTopElementActive);
8179
8958
  best = _ScrollSpy._nearestAbove(retained) ?? retained[0];
8180
8959
  }
8181
- const next = best ? _ScrollSpy._linkFor(best.el.id, best.options) : null;
8182
8960
  const active = _ScrollSpy._active.get(resolver);
8961
+ if (best && active === best && best._link?.isConnected) return;
8962
+ const next = best ? _ScrollSpy._linkFor(best.el.id, best.options) : null;
8183
8963
  if (active === best && active?._link === next) return;
8184
8964
  _ScrollSpy._clearActive(resolver);
8185
8965
  if (!next) return;
@@ -8234,7 +9014,7 @@ _ScrollSpy._active = /* @__PURE__ */ new Map();
8234
9014
  var ScrollSpy = _ScrollSpy;
8235
9015
 
8236
9016
  // src/ts/components/navigationRail.ts
8237
- var _defaults22 = {
9017
+ var _defaults25 = {
8238
9018
  onOpenStart: null,
8239
9019
  onOpenEnd: null,
8240
9020
  onCloseStart: null,
@@ -8321,7 +9101,7 @@ var NavigationRail = class _NavigationRail extends Component {
8321
9101
  document.addEventListener("keydown", this._onKeyDown);
8322
9102
  }
8323
9103
  static get defaults() {
8324
- return _defaults22;
9104
+ return _defaults25;
8325
9105
  }
8326
9106
  static init(els, options = {}) {
8327
9107
  return super.init(els, options, _NavigationRail);
@@ -8351,7 +9131,7 @@ var NavigationRail = class _NavigationRail extends Component {
8351
9131
  };
8352
9132
 
8353
9133
  // src/ts/components/tabs.ts
8354
- var _defaults23 = {
9134
+ var _defaults26 = {
8355
9135
  duration: 300,
8356
9136
  onShow: null,
8357
9137
  swipeable: false,
@@ -8364,13 +9144,17 @@ var Tabs = class _Tabs extends Component {
8364
9144
  super(el, options, _Tabs);
8365
9145
  this._originalPanels = /* @__PURE__ */ new Map();
8366
9146
  this._originalLinks = /* @__PURE__ */ new Map();
8367
- this._handleWindowResize = () => {
9147
+ // Throttled like Carousel's: every bar re-measures on resize, and the left
9148
+ // and right positions are both read before either is written.
9149
+ this._handleWindowResize = Utils.throttle(() => {
8368
9150
  this._setTabsAndTabWidth();
8369
9151
  if (this._tabWidth !== 0 && this._tabsWidth !== 0) {
8370
- this._indicator.style.left = this._calcLeftPos(this._activeTabLink) + "px";
8371
- this._indicator.style.right = this._calcRightPos(this._activeTabLink) + "px";
9152
+ const left = this._calcLeftPos(this._activeTabLink);
9153
+ const right = this._calcRightPos(this._activeTabLink);
9154
+ this._indicator.style.left = left + "px";
9155
+ this._indicator.style.right = right + "px";
8372
9156
  }
8373
- };
9157
+ }, 200);
8374
9158
  this._handleTabClick = (e) => {
8375
9159
  const target = e.target;
8376
9160
  if (!target) return;
@@ -8438,7 +9222,7 @@ var Tabs = class _Tabs extends Component {
8438
9222
  this._setupEventHandlers();
8439
9223
  }
8440
9224
  static get defaults() {
8441
- return _defaults23;
9225
+ return _defaults26;
8442
9226
  }
8443
9227
  /**
8444
9228
  * Initializes instances of Tabs.
@@ -8625,7 +9409,7 @@ var Tabs = class _Tabs extends Component {
8625
9409
  };
8626
9410
 
8627
9411
  // src/ts/components/timepicker.ts
8628
- var _defaults24 = {
9412
+ var _defaults27 = {
8629
9413
  dialRadius: 135,
8630
9414
  outerRadius: 105,
8631
9415
  innerRadius: 70,
@@ -8821,7 +9605,7 @@ var _Timepicker = class _Timepicker extends Component {
8821
9605
  }
8822
9606
  }
8823
9607
  static get defaults() {
8824
- return _defaults24;
9608
+ return _defaults27;
8825
9609
  }
8826
9610
  /**
8827
9611
  * Initializes instances of Timepicker.
@@ -9183,7 +9967,7 @@ _Timepicker._template = `<div class="timepicker-container">
9183
9967
  var Timepicker = _Timepicker;
9184
9968
 
9185
9969
  // src/ts/components/snackbar.ts
9186
- var _defaults25 = {
9970
+ var _defaults28 = {
9187
9971
  text: "",
9188
9972
  action: "",
9189
9973
  onAction: null,
@@ -9233,7 +10017,7 @@ var _Snackbar = class _Snackbar {
9233
10017
  this._setTimer();
9234
10018
  }
9235
10019
  static get defaults() {
9236
- return _defaults25;
10020
+ return _defaults28;
9237
10021
  }
9238
10022
  static getInstance(el) {
9239
10023
  return el["Expressive_Snackbar"];
@@ -9273,6 +10057,7 @@ var _Snackbar = class _Snackbar {
9273
10057
  snackbar.velocityX = 0;
9274
10058
  snackbar.time = Date.now();
9275
10059
  snackbar.xPos = e.clientX;
10060
+ snackbar._activationDistance = snackbar.el.offsetWidth * snackbar.options.activationPercent;
9276
10061
  }
9277
10062
  }
9278
10063
  static _onDragMove(e) {
@@ -9285,7 +10070,7 @@ var _Snackbar = class _Snackbar {
9285
10070
  snackbar.velocityX = snackbar.deltaX / (Date.now() - snackbar.time);
9286
10071
  snackbar.time = Date.now();
9287
10072
  const totalDeltaX = snackbar.xPos - snackbar.startingXPos;
9288
- const activationDistance = snackbar.el.offsetWidth * snackbar.options.activationPercent;
10073
+ const activationDistance = snackbar._activationDistance;
9289
10074
  snackbar.el.style.transform = `translateX(${totalDeltaX}px)`;
9290
10075
  snackbar.el.style.opacity = (1 - Math.abs(totalDeltaX / activationDistance)).toString();
9291
10076
  }
@@ -9298,7 +10083,7 @@ var _Snackbar = class _Snackbar {
9298
10083
  snackbar.panning = false;
9299
10084
  snackbar.el.classList.remove("panning");
9300
10085
  const totalDeltaX = snackbar.xPos - snackbar.startingXPos;
9301
- const activationDistance = snackbar.el.offsetWidth * snackbar.options.activationPercent;
10086
+ const activationDistance = snackbar._activationDistance;
9302
10087
  const velocity = Date.now() - snackbar.time < 100 ? snackbar.velocityX : 0;
9303
10088
  const shouldBeDismissed = Math.abs(totalDeltaX) > activationDistance || velocity > 1;
9304
10089
  if (shouldBeDismissed) {
@@ -9530,7 +10315,7 @@ _Snackbar._draggedSnackbar = null;
9530
10315
  var Snackbar = _Snackbar;
9531
10316
 
9532
10317
  // src/ts/components/tooltip.ts
9533
- var _defaults26 = {
10318
+ var _defaults29 = {
9534
10319
  exitDelay: 200,
9535
10320
  enterDelay: 0,
9536
10321
  text: "",
@@ -9552,6 +10337,7 @@ var Tooltip = class _Tooltip extends Component {
9552
10337
  clearTimeout(this._exitDelayTimeout);
9553
10338
  clearTimeout(this._animationTimeout);
9554
10339
  this.isOpen = true;
10340
+ this.el.ownerDocument.addEventListener("keydown", this._handleKeydown, true);
9555
10341
  this.options = { ...this.options, ...this._getAttributeOptions() };
9556
10342
  this._updateTooltipContent();
9557
10343
  this._setEnterDelayTimeout(isManual);
@@ -9564,6 +10350,7 @@ var Tooltip = class _Tooltip extends Component {
9564
10350
  this.isHovered = false;
9565
10351
  this.isFocused = false;
9566
10352
  this.isOpen = false;
10353
+ this.el.ownerDocument.removeEventListener("keydown", this._handleKeydown, true);
9567
10354
  clearTimeout(this._enterDelayTimeout);
9568
10355
  this._setExitDelayTimeout();
9569
10356
  };
@@ -9612,7 +10399,7 @@ var Tooltip = class _Tooltip extends Component {
9612
10399
  this._setupEventHandlers();
9613
10400
  }
9614
10401
  static get defaults() {
9615
- return _defaults26;
10402
+ return _defaults29;
9616
10403
  }
9617
10404
  /**
9618
10405
  * Initializes instances of Tooltip.
@@ -9693,7 +10480,6 @@ var Tooltip = class _Tooltip extends Component {
9693
10480
  el.addEventListener("focus", this._handleFocus, true);
9694
10481
  el.addEventListener("blur", this._handleBlur, true);
9695
10482
  }
9696
- this.el.ownerDocument.addEventListener("keydown", this._handleKeydown, true);
9697
10483
  }
9698
10484
  _removeEventHandlers() {
9699
10485
  for (const el of [this.el, this.tooltipEl]) {
@@ -9831,6 +10617,8 @@ var AUTO_INIT_COMPONENTS = {
9831
10617
  },
9832
10618
  Carousel: { component: Carousel, selector: ".carousel" },
9833
10619
  Chips: { component: Chips, selector: ".chips" },
10620
+ Confetti: { component: Confetti, selector: ".confetti" },
10621
+ Particles: { component: Particles, selector: "canvas.particles" },
9834
10622
  ColumnChart: { component: ColumnChart, selector: ".column-chart" },
9835
10623
  BarChart: { component: BarChart, selector: ".bar-chart" },
9836
10624
  PieChart: { component: PieChart, selector: ".pie-chart" },
@@ -9889,7 +10677,7 @@ function AutoInit(context = document.body, options) {
9889
10677
  }
9890
10678
 
9891
10679
  // src/ts/modular.ts
9892
- var version = "0.13.1";
10680
+ var version = "0.14.1";
9893
10681
 
9894
10682
  // src/ts/index.ts
9895
10683
  Forms.Init();