@expressivecss/expressive 0.10.1 → 0.11.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 (66) hide show
  1. package/CHANGELOG.md +77 -1
  2. package/README.md +22 -1
  3. package/dist/css/expressive.css +44 -10
  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-QYCYP443.mjs → chunk-74N6U5JJ.mjs} +12 -6
  11. package/dist/js/chunks/chunk-74N6U5JJ.mjs.map +7 -0
  12. package/dist/js/chunks/{chunk-ZIZVUWRL.mjs → chunk-7GKXUG4B.mjs} +8 -8
  13. package/dist/js/chunks/{chunk-7MM4VJJO.mjs → chunk-AYYYWUJ4.mjs} +7 -5
  14. package/dist/js/chunks/chunk-AYYYWUJ4.mjs.map +7 -0
  15. package/dist/js/chunks/{chunk-ZZBZI2XN.mjs → chunk-CDW6WIRT.mjs} +2 -2
  16. package/dist/js/chunks/chunk-FRB7TOCS.mjs +135 -0
  17. package/dist/js/chunks/chunk-FRB7TOCS.mjs.map +7 -0
  18. package/dist/js/chunks/{chunk-AOGFNMXS.mjs → chunk-I5KR22BY.mjs} +2 -2
  19. package/dist/js/chunks/{chunk-AOGFNMXS.mjs.map → chunk-I5KR22BY.mjs.map} +2 -2
  20. package/dist/js/chunks/{chunk-YFXEJ7GR.mjs → chunk-IMUPRRI6.mjs} +6 -7
  21. package/dist/js/chunks/chunk-IMUPRRI6.mjs.map +7 -0
  22. package/dist/js/chunks/chunk-PMBPAFAS.mjs +7 -0
  23. package/dist/js/chunks/{chunk-ZAOYQ6SY.mjs.map → chunk-PMBPAFAS.mjs.map} +1 -1
  24. package/dist/js/chunks/{chunk-G576EP5O.mjs → chunk-XFJ452AZ.mjs} +74 -11
  25. package/dist/js/chunks/chunk-XFJ452AZ.mjs.map +7 -0
  26. package/dist/js/expressive.cjs +139 -88
  27. package/dist/js/expressive.cjs.map +2 -2
  28. package/dist/js/expressive.js +139 -88
  29. package/dist/js/expressive.js.map +2 -2
  30. package/dist/js/expressive.min.js +10 -10
  31. package/dist/js/expressive.min.js.map +3 -3
  32. package/dist/js/expressive.mjs +9 -9
  33. package/dist/js/internal/components/autocomplete.mjs +1 -1
  34. package/dist/js/internal/components/carousel.mjs +1 -1
  35. package/dist/js/internal/components/chips.mjs +2 -2
  36. package/dist/js/internal/components/datepicker.mjs +2 -2
  37. package/dist/js/internal/components/index.mjs +7 -7
  38. package/dist/js/internal/components/registry.mjs +8 -8
  39. package/dist/js/internal/components/scrollspy.mjs +1 -1
  40. package/dist/js/internal/components/select.mjs +1 -1
  41. package/dist/js/internal/components/tabs.mjs +2 -2
  42. package/dist/js/modular.mjs +9 -9
  43. package/dist/types/components/autocomplete.d.ts +2 -1
  44. package/dist/types/components/datepicker.d.ts +1 -0
  45. package/dist/types/components/scrollspy.d.ts +5 -4
  46. package/dist/types/components/select.d.ts +7 -1
  47. package/dist/types/modular.d.ts +1 -1
  48. package/package.json +1 -1
  49. package/src/sass/abstracts/_mixins.scss +19 -11
  50. package/src/sass/components/_buttons.scss +45 -0
  51. package/src/sass/components/_chips.scss +5 -9
  52. package/src/sass/components/_dialog.scss +0 -7
  53. package/src/sass/components/_navbar.scss +3 -3
  54. package/src/sass/components/forms/_fieldset.scss +20 -1
  55. package/src/sass/components/forms/_file-input.scss +11 -3
  56. package/src/sass/components/forms/_select.scss +3 -2
  57. package/src/sass/utilities/_visibility.scss +5 -0
  58. package/dist/js/chunks/chunk-7MM4VJJO.mjs.map +0 -7
  59. package/dist/js/chunks/chunk-G576EP5O.mjs.map +0 -7
  60. package/dist/js/chunks/chunk-QYCYP443.mjs.map +0 -7
  61. package/dist/js/chunks/chunk-TZZ24ZQS.mjs +0 -154
  62. package/dist/js/chunks/chunk-TZZ24ZQS.mjs.map +0 -7
  63. package/dist/js/chunks/chunk-YFXEJ7GR.mjs.map +0 -7
  64. package/dist/js/chunks/chunk-ZAOYQ6SY.mjs +0 -7
  65. /package/dist/js/chunks/{chunk-ZIZVUWRL.mjs.map → chunk-7GKXUG4B.mjs.map} +0 -0
  66. /package/dist/js/chunks/{chunk-ZZBZI2XN.mjs.map → chunk-CDW6WIRT.mjs.map} +0 -0
@@ -1455,12 +1455,12 @@ var _Autocomplete = class _Autocomplete extends Component {
1455
1455
  this._resetAutocomplete();
1456
1456
  }
1457
1457
  };
1458
- this._handleInputKeyup = (e) => {
1459
- if (e.type === "keyup") _Autocomplete._keydown = false;
1458
+ this._handleInputKeyup = () => {
1459
+ _Autocomplete._keydown = false;
1460
+ };
1461
+ this._handleInput = () => {
1460
1462
  this.count = 0;
1461
1463
  const actualValue = this.el.value.toLocaleLowerCase();
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")
1463
- return;
1464
1464
  if (this.oldVal !== actualValue && this.el.matches(":focus-visible")) {
1465
1465
  this.open();
1466
1466
  }
@@ -1640,6 +1640,7 @@ var _Autocomplete = class _Autocomplete extends Component {
1640
1640
  }
1641
1641
  _setupEventHandlers() {
1642
1642
  this.el.addEventListener("blur", this._handleInputBlur);
1643
+ this.el.addEventListener("input", this._handleInput);
1643
1644
  this.el.addEventListener("keyup", this._handleInputKeyup);
1644
1645
  this.el.addEventListener("focus", this._handleInputFocus);
1645
1646
  this.el.addEventListener("keydown", this._handleInputKeydown);
@@ -1650,6 +1651,7 @@ var _Autocomplete = class _Autocomplete extends Component {
1650
1651
  }
1651
1652
  _removeEventHandlers() {
1652
1653
  this.el.removeEventListener("blur", this._handleInputBlur);
1654
+ this.el.removeEventListener("input", this._handleInput);
1653
1655
  this.el.removeEventListener("keyup", this._handleInputKeyup);
1654
1656
  this.el.removeEventListener("focus", this._handleInputFocus);
1655
1657
  this.el.removeEventListener("keydown", this._handleInputKeydown);
@@ -3325,7 +3327,7 @@ var _Carousel = class _Carousel extends Component {
3325
3327
  this._scroller.removeEventListener("scrollend", this._handleScrollEnd);
3326
3328
  };
3327
3329
  this._handleScrollEnd = () => {
3328
- if (!this.images.some((item) => item.getAnimations().some((animation) => animation.playState === "running"))) {
3330
+ if (this._nearestIndex() === this.center && !this.images.some((item) => item.getAnimations().some((animation) => animation.playState === "running"))) {
3329
3331
  this._finishScroll();
3330
3332
  }
3331
3333
  };
@@ -3874,14 +3876,41 @@ var _defaults12 = {
3874
3876
  var FormSelect = class _FormSelect extends Component {
3875
3877
  constructor(el, options) {
3876
3878
  super(el, options, _FormSelect);
3879
+ this._originalLabels = /* @__PURE__ */ new Map();
3880
+ this._validationMessage = null;
3877
3881
  this._handleSelectChange = () => {
3878
3882
  this._setValueToInput();
3879
3883
  this._setSelectedStates();
3884
+ this._syncValidationError();
3885
+ };
3886
+ this._handleInvalid = (e) => {
3887
+ if (e.defaultPrevented) return;
3888
+ if (!this._validationMessage) {
3889
+ this._validationMessage = document.createElement("small");
3890
+ this._validationMessage.id = this.input.id + "-error";
3891
+ this._validationMessage.className = "supporting-text";
3892
+ this._validationMessage.style.color = "var(--md-sys-color-error)";
3893
+ this.wrapper.append(this._validationMessage);
3894
+ this.input.setAttribute(
3895
+ "aria-describedby",
3896
+ [this.input.getAttribute("aria-describedby"), this._validationMessage.id].filter(Boolean).join(" ")
3897
+ );
3898
+ }
3899
+ this.input.ariaInvalid = "true";
3900
+ this._syncValidationError();
3880
3901
  };
3902
+ // Native reporting chooses the control; static checks never focus. Wait until
3903
+ // reporting finishes so Firefox dismisses its native popup when focus moves.
3904
+ this._handleNativeFocus = () => queueMicrotask(() => {
3905
+ if (_FormSelect.getInstance(this.el) === this && this.el.matches(":focus")) this.input.focus();
3906
+ });
3881
3907
  this._handleReset = (e) => {
3882
3908
  clearTimeout(this._resetTimer);
3883
3909
  this._resetTimer = setTimeout(() => {
3884
- if (!e.defaultPrevented) this._handleSelectChange();
3910
+ if (!e.defaultPrevented) {
3911
+ this._handleSelectChange();
3912
+ this._clearValidationError();
3913
+ }
3885
3914
  }, 0);
3886
3915
  };
3887
3916
  // Named rather than inline so that _removeEventHandlers can actually detach
@@ -3918,7 +3947,6 @@ var FormSelect = class _FormSelect extends Component {
3918
3947
  this.el.tabIndex = -1;
3919
3948
  this._values = [];
3920
3949
  this._createdWrapper = false;
3921
- this._originalLabelFor = null;
3922
3950
  this._setupMenu();
3923
3951
  this._setupEventHandlers();
3924
3952
  }
@@ -3938,6 +3966,7 @@ var FormSelect = class _FormSelect extends Component {
3938
3966
  }
3939
3967
  destroy() {
3940
3968
  this._removeEventHandlers();
3969
+ this._clearValidationError();
3941
3970
  this.menu?.destroy();
3942
3971
  this._removeMenu();
3943
3972
  this.el.tabIndex = this.nativeTabIndex;
@@ -3972,6 +4001,8 @@ var FormSelect = class _FormSelect extends Component {
3972
4001
  _setupEventHandlers() {
3973
4002
  this._setupOptionHandlers();
3974
4003
  this.el.addEventListener("change", this._handleSelectChange);
4004
+ this.el.addEventListener("invalid", this._handleInvalid);
4005
+ this.el.addEventListener("focus", this._handleNativeFocus);
3975
4006
  this.input.addEventListener("click", this._handleInputClick);
3976
4007
  this.menuEl.addEventListener("focusin", this._handleOptionFocus);
3977
4008
  this._bindForm();
@@ -3984,6 +4015,8 @@ var FormSelect = class _FormSelect extends Component {
3984
4015
  _removeEventHandlers() {
3985
4016
  this._removeOptionHandlers();
3986
4017
  this.el.removeEventListener("change", this._handleSelectChange);
4018
+ this.el.removeEventListener("invalid", this._handleInvalid);
4019
+ this.el.removeEventListener("focus", this._handleNativeFocus);
3987
4020
  this.input?.removeEventListener("click", this._handleInputClick);
3988
4021
  this.menuEl?.removeEventListener("focusin", this._handleOptionFocus);
3989
4022
  this._form?.removeEventListener("reset", this._handleReset);
@@ -4001,6 +4034,21 @@ var FormSelect = class _FormSelect extends Component {
4001
4034
  el.removeEventListener("keydown", this._handleOptionKeydown);
4002
4035
  });
4003
4036
  }
4037
+ _syncValidationError() {
4038
+ if (!this._validationMessage) return;
4039
+ if (this.el.willValidate && !this.el.validity.valid) {
4040
+ this._validationMessage.textContent = this.el.validationMessage;
4041
+ } else this._clearValidationError();
4042
+ }
4043
+ _clearValidationError() {
4044
+ if (!this._validationMessage) return;
4045
+ const descriptions = this.input.getAttribute("aria-describedby")?.split(/\s+/).filter((id) => id !== this._validationMessage.id).join(" ");
4046
+ if (descriptions) this.input.setAttribute("aria-describedby", descriptions);
4047
+ else this.input.removeAttribute("aria-describedby");
4048
+ this.input.ariaInvalid = this.el.ariaInvalid;
4049
+ this._validationMessage.remove();
4050
+ this._validationMessage = null;
4051
+ }
4004
4052
  _arraysEqual(a, b) {
4005
4053
  if (a === b) return true;
4006
4054
  if (a == null || b == null) return false;
@@ -4032,6 +4080,13 @@ var FormSelect = class _FormSelect extends Component {
4032
4080
  }
4033
4081
  _setupMenu() {
4034
4082
  this.labelEl = this.el.labels?.[0] ?? null;
4083
+ Array.from(this.el.labels ?? []).forEach((label) => {
4084
+ this._originalLabels.set(label, { for: label.getAttribute("for"), id: label.getAttribute("id") });
4085
+ });
4086
+ if (this.labelEl && !this.el.closest("label")) {
4087
+ this._labelPlaceholder = document.createComment("");
4088
+ this.labelEl.before(this._labelPlaceholder);
4089
+ }
4035
4090
  this._setupWrapper();
4036
4091
  this._hideNativeSelect();
4037
4092
  if (this.el.matches(":disabled")) this.wrapper.classList.add("disabled");
@@ -4047,7 +4102,7 @@ var FormSelect = class _FormSelect extends Component {
4047
4102
  this._buildCaret();
4048
4103
  this._initMenu();
4049
4104
  this._setSelectedStates();
4050
- if (this.labelEl) this.input.after(this.labelEl);
4105
+ if (this._labelPlaceholder) this.input.after(this.labelEl);
4051
4106
  }
4052
4107
  _setupWrapper() {
4053
4108
  const parent = this.el.parentElement;
@@ -4068,9 +4123,10 @@ var FormSelect = class _FormSelect extends Component {
4068
4123
  _hideNativeSelect() {
4069
4124
  const hiddenDiv = document.createElement("div");
4070
4125
  hiddenDiv.classList.add("hide-select");
4126
+ hiddenDiv.ariaHidden = "true";
4071
4127
  this.el.before(hiddenDiv);
4072
4128
  hiddenDiv.appendChild(this.el);
4073
- this.wrapper.append(hiddenDiv);
4129
+ if (this._createdWrapper) this.wrapper.append(hiddenDiv);
4074
4130
  }
4075
4131
  _buildInput() {
4076
4132
  this.input = document.createElement("input");
@@ -4093,9 +4149,14 @@ var FormSelect = class _FormSelect extends Component {
4093
4149
  this.input.ariaExpanded = "false";
4094
4150
  this.input.setAttribute("aria-controls", this.menuEl.id);
4095
4151
  this.input.placeholder = " ";
4096
- if (this.labelEl) {
4097
- this._originalLabelFor = this.labelEl.htmlFor;
4098
- this.labelEl.htmlFor = this.input.id;
4152
+ this._originalLabels.forEach((_, label) => {
4153
+ label.htmlFor = this.input.id;
4154
+ });
4155
+ if (this._labelPlaceholder && this._originalLabels.size > 1 && !this.input.getAttribute("aria-label") && !this.input.getAttribute("aria-labelledby")) {
4156
+ this.input.setAttribute("aria-labelledby", Array.from(this._originalLabels.keys(), (label, index) => {
4157
+ if (!label.id) label.id = `${this.input.id}-label-${index}`;
4158
+ return label.id;
4159
+ }).join(" "));
4099
4160
  }
4100
4161
  this.wrapper.prepend(this.input);
4101
4162
  this._setValueToInput();
@@ -4190,9 +4251,13 @@ var FormSelect = class _FormSelect extends Component {
4190
4251
  this.menuEl?.remove();
4191
4252
  const hide = this.el.parentElement;
4192
4253
  if (hide?.classList.contains("hide-select")) hide.replaceWith(this.el);
4193
- if (this.labelEl && this._originalLabelFor !== null) {
4194
- this.labelEl.htmlFor = this._originalLabelFor;
4195
- }
4254
+ this._originalLabels.forEach((original, label) => {
4255
+ for (const name of ["for", "id"]) {
4256
+ if (original[name] === null) label.removeAttribute(name);
4257
+ else label.setAttribute(name, original[name]);
4258
+ }
4259
+ });
4260
+ this._labelPlaceholder?.replaceWith(this.labelEl);
4196
4261
  if (!this.wrapper) return;
4197
4262
  if (this._createdWrapper) {
4198
4263
  this.wrapper.replaceWith(this.el);
@@ -4618,7 +4683,7 @@ var _Datepicker = class _Datepicker extends Component {
4618
4683
  typeof this.options.format === "function" ? this.options.format(new Date(this.el.value)) : this.options.format
4619
4684
  );
4620
4685
  } else {
4621
- date = new Date(Date.parse(e.target.value));
4686
+ date = _Datepicker._parseDate(e.target.value);
4622
4687
  }
4623
4688
  if (_Datepicker._isDate(date)) {
4624
4689
  if (el == this.endDateEl && date < this.date) return this.setInputValue(el, this.endDate);
@@ -4659,7 +4724,7 @@ var _Datepicker = class _Datepicker extends Component {
4659
4724
  this._insertHTMLIntoDOM();
4660
4725
  this._setupEventHandlers();
4661
4726
  if (!this.options.defaultDate) {
4662
- this.options.defaultDate = new Date(Date.parse(this.el.value));
4727
+ this.options.defaultDate = _Datepicker._parseDate(this.el.value);
4663
4728
  }
4664
4729
  const defDate = this.options.defaultDate;
4665
4730
  if (_Datepicker._isDate(defDate)) {
@@ -4712,6 +4777,12 @@ var _Datepicker = class _Datepicker extends Component {
4712
4777
  static _isDate(obj) {
4713
4778
  return /Date/.test(Object.prototype.toString.call(obj)) && !isNaN(obj.getTime());
4714
4779
  }
4780
+ static _parseDate(value) {
4781
+ value = value.trim();
4782
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) return new Date(Date.parse(value));
4783
+ const date = /* @__PURE__ */ new Date(value + "T00:00:00");
4784
+ return date.getDate() === Number(value.slice(-2)) ? date : /* @__PURE__ */ new Date(NaN);
4785
+ }
4715
4786
  static _isWeekend(date) {
4716
4787
  const day = date.getDay();
4717
4788
  return day === 0 || day === 6;
@@ -4821,7 +4892,7 @@ var _Datepicker = class _Datepicker extends Component {
4821
4892
  * Sets date from input field.
4822
4893
  */
4823
4894
  setDateFromInput(el) {
4824
- const date = new Date(Date.parse(el.value));
4895
+ const date = _Datepicker._parseDate(el.value);
4825
4896
  this.setDate(date, false, el == this.endDateEl, true);
4826
4897
  }
4827
4898
  /**
@@ -4850,7 +4921,7 @@ var _Datepicker = class _Datepicker extends Component {
4850
4921
  return this.draw();
4851
4922
  }
4852
4923
  if (typeof date === "string") {
4853
- date = new Date(Date.parse(date));
4924
+ date = _Datepicker._parseDate(date);
4854
4925
  }
4855
4926
  if (!_Datepicker._isDate(date)) {
4856
4927
  return;
@@ -5840,18 +5911,31 @@ var _defaults16 = {
5840
5911
  var _ScrollSpy = class _ScrollSpy extends Component {
5841
5912
  constructor(el, options) {
5842
5913
  super(el, options, _ScrollSpy);
5914
+ this._ratio = 0;
5915
+ this._link = null;
5843
5916
  this.el["Expressive_ScrollSpy"] = this;
5844
5917
  this.options = {
5845
5918
  ..._ScrollSpy.defaults,
5846
5919
  ...options
5847
5920
  };
5921
+ this._offset = this.el.style.getPropertyValue("--md-comp-scrollspy-offset");
5922
+ this._offsetPriority = this.el.style.getPropertyPriority("--md-comp-scrollspy-offset");
5848
5923
  this.el.style.setProperty(
5849
5924
  "--md-comp-scrollspy-offset",
5850
5925
  `${this.options.scrollOffset}px`
5851
5926
  );
5852
5927
  _ScrollSpy._elements.push(this);
5853
- _ScrollSpy._ensureObserver(this.options.scrollOffset);
5854
- _ScrollSpy._observer?.observe(this.el);
5928
+ if (typeof IntersectionObserver !== "undefined") {
5929
+ this._observer = new IntersectionObserver((entries) => {
5930
+ if (_ScrollSpy.getInstance(this.el) !== this) return;
5931
+ for (const entry of entries) this._ratio = entry.isIntersecting ? entry.intersectionRatio : 0;
5932
+ _ScrollSpy._syncActive(this.options.getActiveElement);
5933
+ }, {
5934
+ rootMargin: `-${this.options.scrollOffset}px 0px -45% 0px`,
5935
+ threshold: [0, 0.1, 0.25, 0.5, 0.75, 1]
5936
+ });
5937
+ this._observer.observe(this.el);
5938
+ }
5855
5939
  }
5856
5940
  static get defaults() {
5857
5941
  return _defaults16;
@@ -5864,66 +5948,44 @@ var _ScrollSpy = class _ScrollSpy extends Component {
5864
5948
  }
5865
5949
  destroy() {
5866
5950
  const index = _ScrollSpy._elements.indexOf(this);
5867
- if (index >= 0) _ScrollSpy._elements.splice(index, 1);
5868
- _ScrollSpy._ratios.delete(this.el);
5869
- _ScrollSpy._observer?.unobserve(this.el);
5870
- const link = _ScrollSpy._linkFor(this.el.id, this.options);
5871
- if (link && link === _ScrollSpy._active) {
5872
- _ScrollSpy._clearActive(this.options);
5873
- } else {
5874
- link?.classList.remove(this.options.activeClass);
5875
- link?.removeAttribute("aria-current");
5876
- }
5877
- if (_ScrollSpy._elements.length === 0) {
5878
- _ScrollSpy._observer?.disconnect();
5879
- _ScrollSpy._observer = null;
5880
- _ScrollSpy._ratios.clear();
5881
- _ScrollSpy._active = null;
5882
- } else {
5883
- _ScrollSpy._syncActive();
5884
- }
5951
+ if (index < 0) return;
5952
+ _ScrollSpy._elements.splice(index, 1);
5953
+ this._observer?.disconnect();
5954
+ const resolver = this.options.getActiveElement;
5955
+ if (_ScrollSpy._active.get(resolver) === this) _ScrollSpy._clearActive(resolver);
5956
+ this.el.style.setProperty("--md-comp-scrollspy-offset", this._offset, this._offsetPriority);
5885
5957
  this.el["Expressive_ScrollSpy"] = void 0;
5958
+ _ScrollSpy._syncActive(resolver);
5886
5959
  }
5887
- static _ensureObserver(offset) {
5888
- if (_ScrollSpy._observer) return;
5889
- if (typeof IntersectionObserver === "undefined") return;
5890
- _ScrollSpy._observer = new IntersectionObserver(_ScrollSpy._onIntersect, {
5891
- rootMargin: `-${offset}px 0px -45% 0px`,
5892
- threshold: [0, 0.1, 0.25, 0.5, 0.75, 1]
5893
- });
5894
- }
5895
- static _syncActive() {
5896
- if (!_ScrollSpy._elements.length) return;
5897
- const options = _ScrollSpy._elements[0].options;
5960
+ static _syncActive(resolver) {
5961
+ const elements = _ScrollSpy._elements.filter((spy) => spy.options.getActiveElement === resolver);
5898
5962
  let best = null;
5899
5963
  let bestRatio = 0;
5900
- for (const spy of _ScrollSpy._elements) {
5901
- const ratio = _ScrollSpy._ratios.get(spy.el) ?? 0;
5964
+ for (const spy of elements) {
5965
+ const ratio = spy._ratio;
5902
5966
  if (ratio > bestRatio) {
5903
5967
  bestRatio = ratio;
5904
5968
  best = spy;
5905
5969
  }
5906
5970
  }
5907
5971
  if (!best) {
5908
- if (options.keepTopElementActive) {
5909
- best = _ScrollSpy._nearestAbove() ?? _ScrollSpy._elements[0];
5910
- } else {
5911
- _ScrollSpy._clearActive(options);
5912
- return;
5913
- }
5972
+ const retained = elements.filter((spy) => spy.options.keepTopElementActive);
5973
+ best = _ScrollSpy._nearestAbove(retained) ?? retained[0];
5914
5974
  }
5915
- const next = _ScrollSpy._linkFor(best.el.id, best.options);
5916
- if (next === _ScrollSpy._active) return;
5917
- _ScrollSpy._clearActive(options);
5975
+ const next = best ? _ScrollSpy._linkFor(best.el.id, best.options) : null;
5976
+ const active = _ScrollSpy._active.get(resolver);
5977
+ if (active === best && active?._link === next) return;
5978
+ _ScrollSpy._clearActive(resolver);
5918
5979
  if (!next) return;
5919
5980
  next.classList.add(best.options.activeClass);
5920
5981
  next.setAttribute("aria-current", "true");
5921
- _ScrollSpy._active = next;
5982
+ best._link = next;
5983
+ _ScrollSpy._active.set(resolver, best);
5922
5984
  }
5923
- static _nearestAbove() {
5985
+ static _nearestAbove(elements) {
5924
5986
  let best = null;
5925
5987
  let bestTop = -Infinity;
5926
- for (const spy of _ScrollSpy._elements) {
5988
+ for (const spy of elements) {
5927
5989
  const top = spy.el.getBoundingClientRect().top;
5928
5990
  if (top <= 0 && top >= bestTop) {
5929
5991
  bestTop = top;
@@ -5932,11 +5994,13 @@ var _ScrollSpy = class _ScrollSpy extends Component {
5932
5994
  }
5933
5995
  return best;
5934
5996
  }
5935
- static _clearActive(options) {
5936
- if (!_ScrollSpy._active) return;
5937
- _ScrollSpy._active.classList.remove(options.activeClass);
5938
- _ScrollSpy._active.removeAttribute("aria-current");
5939
- _ScrollSpy._active = null;
5997
+ static _clearActive(resolver) {
5998
+ const active = _ScrollSpy._active.get(resolver);
5999
+ if (!active) return;
6000
+ active._link?.classList.remove(active.options.activeClass);
6001
+ active._link?.removeAttribute("aria-current");
6002
+ active._link = null;
6003
+ _ScrollSpy._active.delete(resolver);
5940
6004
  }
5941
6005
  /**
5942
6006
  * Default lookup compares href attributes (no interpolated selector).
@@ -5960,19 +6024,7 @@ var _ScrollSpy = class _ScrollSpy extends Component {
5960
6024
  }
5961
6025
  };
5962
6026
  _ScrollSpy._elements = [];
5963
- _ScrollSpy._observer = null;
5964
- _ScrollSpy._ratios = /* @__PURE__ */ new Map();
5965
- _ScrollSpy._active = null;
5966
- _ScrollSpy._onIntersect = (entries) => {
5967
- for (const entry of entries) {
5968
- if (entry.isIntersecting && entry.intersectionRatio > 0) {
5969
- _ScrollSpy._ratios.set(entry.target, entry.intersectionRatio);
5970
- } else {
5971
- _ScrollSpy._ratios.delete(entry.target);
5972
- }
5973
- }
5974
- _ScrollSpy._syncActive();
5975
- };
6027
+ _ScrollSpy._active = /* @__PURE__ */ new Map();
5976
6028
  var ScrollSpy = _ScrollSpy;
5977
6029
 
5978
6030
  // src/ts/components/navigationRail.ts
@@ -6115,14 +6167,13 @@ var Tabs = class _Tabs extends Component {
6115
6167
  }
6116
6168
  if (tabLink.hasAttribute("target")) return;
6117
6169
  if (this.options.swipeable) {
6118
- const panel = tabLink.hash && document.querySelector(tabLink.hash);
6170
+ const panel = tabLink.hash && document.getElementById(tabLink.hash.slice(1));
6119
6171
  const index = this._tabsCarousel.images.indexOf(panel);
6120
6172
  if (index >= 0) this._tabsCarousel.set(index);
6121
6173
  e.preventDefault();
6122
6174
  return;
6123
6175
  }
6124
6176
  const _oldContent = this._content;
6125
- if (tabLink.hash) this._content = document.querySelector(tabLink.hash);
6126
6177
  this._tabLinks = this._queryTabLinks();
6127
6178
  const prevIndex = this._index;
6128
6179
  this._setActiveTabLink(tabLink);
@@ -6149,7 +6200,7 @@ var Tabs = class _Tabs extends Component {
6149
6200
  this._tabLinks.forEach((link) => {
6150
6201
  this._originalLinks.set(link, { active: link.classList.contains("active"), current: link.getAttribute("aria-current") });
6151
6202
  if (link.hasAttribute("target")) return;
6152
- const panel = link.hash && document.querySelector(link.hash);
6203
+ const panel = link.hash && document.getElementById(link.hash.slice(1));
6153
6204
  if (panel) this._originalPanels.set(panel, {
6154
6205
  display: panel.style.display,
6155
6206
  priority: panel.style.getPropertyPriority("display"),
@@ -6270,7 +6321,7 @@ var Tabs = class _Tabs extends Component {
6270
6321
  const tabsContent = [];
6271
6322
  this._tabLinks.forEach((a) => {
6272
6323
  if (a.hash && !a.hasAttribute("target")) {
6273
- const currContent = document.querySelector(a.hash);
6324
+ const currContent = document.getElementById(a.hash.slice(1));
6274
6325
  if (!currContent) return;
6275
6326
  currContent.classList.add("carousel-item");
6276
6327
  tabsContent.push(currContent);
@@ -6310,7 +6361,7 @@ var Tabs = class _Tabs extends Component {
6310
6361
  Array.from(this._tabLinks).forEach((a) => {
6311
6362
  if (a === this._activeTabLink || a.hasAttribute("target")) return;
6312
6363
  if (a.hash) {
6313
- const currContent = document.querySelector(a.hash);
6364
+ const currContent = document.getElementById(a.hash.slice(1));
6314
6365
  if (currContent) currContent.style.display = "none";
6315
6366
  }
6316
6367
  });
@@ -7531,7 +7582,7 @@ function AutoInit(context = document.body, options) {
7531
7582
  }
7532
7583
 
7533
7584
  // src/ts/modular.ts
7534
- var version = "0.10.1";
7585
+ var version = "0.11.0";
7535
7586
 
7536
7587
  // src/ts/index.ts
7537
7588
  Forms.Init();