@y14e/accordion 1.3.2 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -50,6 +50,62 @@ function saveAttributes(elements, attributes) {
50
50
  });
51
51
  }
52
52
 
53
+ // node_modules/@y14e/button/dist/index.js
54
+ var Button = class {
55
+ #element;
56
+ #controller = null;
57
+ #isDestroyed = false;
58
+ constructor(element) {
59
+ if (!(element instanceof HTMLElement)) {
60
+ throw new TypeError("Invalid element");
61
+ }
62
+ if (element.hasAttribute("data-button-initialized")) {
63
+ console.warn("Already initialized");
64
+ return;
65
+ }
66
+ this.#element = element;
67
+ this.#initialize();
68
+ }
69
+ destroy() {
70
+ if (this.#isDestroyed) {
71
+ return;
72
+ }
73
+ this.#isDestroyed = true;
74
+ this.#controller?.abort();
75
+ this.#controller = null;
76
+ this.#element.removeAttribute("data-button-initialized");
77
+ }
78
+ #initialize() {
79
+ this.#controller = new AbortController();
80
+ this.#element.addEventListener("keydown", this.#onKeyDown, {
81
+ signal: this.#controller.signal
82
+ });
83
+ this.#element.setAttribute("data-button-initialized", "");
84
+ }
85
+ #onKeyDown = (event) => {
86
+ const { key, altKey, ctrlKey, metaKey, shiftKey } = event;
87
+ if (altKey || ctrlKey || metaKey || shiftKey) {
88
+ return;
89
+ }
90
+ if (!["Enter", " "].includes(key)) {
91
+ return;
92
+ }
93
+ const active = getActiveElement();
94
+ if (!(active instanceof HTMLElement)) {
95
+ return;
96
+ }
97
+ event.preventDefault();
98
+ active.click();
99
+ };
100
+ };
101
+ function getActiveElement() {
102
+ let current = document.activeElement;
103
+ while (current?.shadowRoot?.activeElement) {
104
+ current = current.shadowRoot.activeElement;
105
+ }
106
+ return current;
107
+ }
108
+
53
109
  // node_modules/@y14e/roving-tabindex/dist/index.js
54
110
  var defaultParser2 = (value) => value.split(/\s+/);
55
111
  var defaultSerializer2 = (tokens) => tokens.join(" ");
@@ -105,7 +161,12 @@ function getFocusables(container = document.body, options = {}) {
105
161
  console.warn("Invalid container element. Fallback: <body> element.");
106
162
  container = document.body;
107
163
  }
108
- let { composed = false, filter, include } = options;
164
+ let {
165
+ composed = false,
166
+ filter,
167
+ include,
168
+ skipVisibilityCheck = false
169
+ } = options;
109
170
  if (typeof composed !== "boolean") {
110
171
  console.warn("Invalid composed option. Fallback: false.");
111
172
  composed = false;
@@ -126,7 +187,7 @@ function getFocusables(container = document.body, options = {}) {
126
187
  if (composed || include) {
127
188
  let traverse2 = function(node) {
128
189
  if (node instanceof Element) {
129
- if (isFocusable(node) || include?.(node)) {
190
+ if (isFocusable(node, { skipVisibilityCheck }) || include?.(node)) {
130
191
  elements[elements.length] = node;
131
192
  }
132
193
  }
@@ -147,7 +208,7 @@ function getFocusables(container = document.body, options = {}) {
147
208
  if (!(candidate instanceof Element)) {
148
209
  continue;
149
210
  }
150
- if (isFocusable(candidate)) {
211
+ if (isFocusable(candidate, { skipVisibilityCheck })) {
151
212
  elements[elements.length] = candidate;
152
213
  }
153
214
  }
@@ -155,11 +216,16 @@ function getFocusables(container = document.body, options = {}) {
155
216
  const unfiltered = normalizeRadioGroup(sortByTabIndex(elements));
156
217
  return filter ? unfiltered.filter(filter) : unfiltered;
157
218
  }
158
- function isFocusable(element) {
219
+ function isFocusable(element, options = {}) {
159
220
  if (!(element instanceof Element)) {
160
221
  console.warn("Invalid element");
161
222
  return false;
162
223
  }
224
+ let { skipVisibilityCheck = false } = options;
225
+ if (typeof skipVisibilityCheck !== "boolean") {
226
+ console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
227
+ skipVisibilityCheck = false;
228
+ }
163
229
  if (element.hasAttribute("hidden") || isInert(element)) {
164
230
  return false;
165
231
  }
@@ -172,7 +238,7 @@ function isFocusable(element) {
172
238
  if (isDisabledDeep(element)) {
173
239
  return false;
174
240
  }
175
- if (!element.checkVisibility({
241
+ if (!skipVisibilityCheck && !element.checkVisibility({
176
242
  contentVisibilityAuto: true,
177
243
  opacityProperty: true,
178
244
  visibilityProperty: true
@@ -406,7 +472,7 @@ var RovingTabIndex = class {
406
472
  return;
407
473
  }
408
474
  }
409
- const active = getActiveElement();
475
+ const active = getActiveElement2();
410
476
  if (!(active instanceof HTMLElement)) {
411
477
  return;
412
478
  }
@@ -457,7 +523,8 @@ var RovingTabIndex = class {
457
523
  ...this.#getFocusables(),
458
524
  ...getFocusables(this.#container, {
459
525
  composed: true,
460
- filter: this.#selectorFilter
526
+ filter: this.#selectorFilter,
527
+ skipVisibilityCheck: true
461
528
  })
462
529
  ]);
463
530
  for (const focusable of this.#focusables) {
@@ -523,14 +590,15 @@ var RovingTabIndex = class {
523
590
  return getFocusables(this.#container, {
524
591
  composed: true,
525
592
  filter: this.#selectorFilter,
526
- include: (element) => this.#focusables.has(element)
593
+ include: (element) => this.#focusables.has(element),
594
+ skipVisibilityCheck: true
527
595
  });
528
596
  }
529
597
  };
530
598
  function focusElement(element) {
531
599
  "focus" in element && typeof element.focus === "function" && element.focus();
532
600
  }
533
- function getActiveElement() {
601
+ function getActiveElement2() {
534
602
  let current = document.activeElement;
535
603
  while (current?.shadowRoot?.activeElement) {
536
604
  current = current.shadowRoot.activeElement;
@@ -556,6 +624,7 @@ var Accordion = class _Accordion {
556
624
  #eventController = null;
557
625
  #animationController = null;
558
626
  #cleanupRovingTabIndex = null;
627
+ #buttons = [];
559
628
  #isDestroyed = false;
560
629
  constructor(root, options = {}) {
561
630
  if (!(root instanceof HTMLElement)) {
@@ -629,6 +698,10 @@ var Accordion = class _Accordion {
629
698
  this.#eventController = null;
630
699
  this.#cleanupRovingTabIndex?.();
631
700
  this.#cleanupRovingTabIndex = null;
701
+ this.#buttons.forEach((button) => {
702
+ button.destroy();
703
+ });
704
+ this.#buttons.length = 0;
632
705
  !force && await this.#waitAnimationsFinish();
633
706
  this.#contentElements.forEach((content) => {
634
707
  force && this.#bindings.get(content)?.animation?.finish();
@@ -671,12 +744,12 @@ var Accordion = class _Accordion {
671
744
  trigger2.style.setProperty("pointer-events", "none");
672
745
  }
673
746
  trigger2.addEventListener("click", this.#onTriggerClick, { signal });
674
- trigger2.addEventListener("keydown", this.#onTriggerKeyDown, { signal });
675
747
  addTokenToAttribute(content2, "aria-labelledby", trigger2.id);
676
748
  content2.setAttribute("role", "region");
677
749
  content2.addEventListener("beforematch", this.#onContentBeforeMatch, {
678
750
  signal
679
751
  });
752
+ this.#buttons.push(new Button(trigger2));
680
753
  });
681
754
  const { trigger, content } = this.#settings.selector;
682
755
  this.#cleanupRovingTabIndex = createRovingTabIndex(this.#rootElement, {
@@ -695,26 +768,6 @@ var Accordion = class _Accordion {
695
768
  }
696
769
  this.#toggle(trigger, trigger.ariaExpanded === "false");
697
770
  };
698
- #onTriggerKeyDown = (event) => {
699
- const { key, altKey, ctrlKey, metaKey, shiftKey } = event;
700
- if (altKey || ctrlKey || metaKey || shiftKey) {
701
- return;
702
- }
703
- if (!["Enter", " "].includes(key)) {
704
- return;
705
- }
706
- const active = getActiveElement2();
707
- if (!(active instanceof HTMLElement)) {
708
- return;
709
- }
710
- event.preventDefault();
711
- switch (key) {
712
- case "Enter":
713
- case " ":
714
- active.click();
715
- return;
716
- }
717
- };
718
771
  #onContentBeforeMatch = (event) => {
719
772
  const content = event.currentTarget;
720
773
  if (!(content instanceof HTMLElement)) {
@@ -811,13 +864,6 @@ var Accordion = class _Accordion {
811
864
  function createBinding(trigger, content) {
812
865
  return { trigger, content, animation: null };
813
866
  }
814
- function getActiveElement2() {
815
- let current = document.activeElement;
816
- while (current?.shadowRoot?.activeElement) {
817
- current = current.shadowRoot.activeElement;
818
- }
819
- return current;
820
- }
821
867
  function isFocusable2(element) {
822
868
  return !element.hasAttribute("disabled") && element.tabIndex >= 0;
823
869
  }
@@ -834,7 +880,7 @@ function waitAnimationFinish(animation) {
834
880
  * Accordion
835
881
  * WAI-ARIA compliant accordion pattern implementation in TypeScript.
836
882
  *
837
- * @version 1.3.2
883
+ * @version 1.4.0
838
884
  * @author Yusuke Kamiyamane
839
885
  * @license MIT
840
886
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -853,13 +899,24 @@ function waitAnimationFinish(animation) {
853
899
  * @see {@link https://github.com/y14e/attributes-utils}
854
900
  *)
855
901
 
902
+ @y14e/button/dist/index.js:
903
+ (**
904
+ * Button
905
+ *
906
+ * @version 1.0.0
907
+ * @author Yusuke Kamiyamane
908
+ * @license MIT
909
+ * @copyright Copyright (c) Yusuke Kamiyamane
910
+ * @see {@link https://github.com/y14e/button}
911
+ *)
912
+
856
913
  @y14e/roving-tabindex/dist/index.js:
857
914
  (**
858
915
  * Roving Tabindex
859
916
  * Lightweight roving tabindex utility with fully focus management.
860
917
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
861
918
  *
862
- * @version 1.3.0
919
+ * @version 1.3.1
863
920
  * @author Yusuke Kamiyamane
864
921
  * @license MIT
865
922
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -884,7 +941,7 @@ function waitAnimationFinish(animation) {
884
941
  * High-precision focus management utility with full composed tree support.
885
942
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
886
943
  *
887
- * @version 4.1.8
944
+ * @version 4.2.0
888
945
  * @author Yusuke Kamiyamane
889
946
  * @license MIT
890
947
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.d.cts CHANGED
@@ -2,7 +2,7 @@
2
2
  * Accordion
3
3
  * WAI-ARIA compliant accordion pattern implementation in TypeScript.
4
4
  *
5
- * @version 1.3.2
5
+ * @version 1.4.0
6
6
  * @author Yusuke Kamiyamane
7
7
  * @license MIT
8
8
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * Accordion
3
3
  * WAI-ARIA compliant accordion pattern implementation in TypeScript.
4
4
  *
5
- * @version 1.3.2
5
+ * @version 1.4.0
6
6
  * @author Yusuke Kamiyamane
7
7
  * @license MIT
8
8
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.js CHANGED
@@ -48,6 +48,62 @@ function saveAttributes(elements, attributes) {
48
48
  });
49
49
  }
50
50
 
51
+ // node_modules/@y14e/button/dist/index.js
52
+ var Button = class {
53
+ #element;
54
+ #controller = null;
55
+ #isDestroyed = false;
56
+ constructor(element) {
57
+ if (!(element instanceof HTMLElement)) {
58
+ throw new TypeError("Invalid element");
59
+ }
60
+ if (element.hasAttribute("data-button-initialized")) {
61
+ console.warn("Already initialized");
62
+ return;
63
+ }
64
+ this.#element = element;
65
+ this.#initialize();
66
+ }
67
+ destroy() {
68
+ if (this.#isDestroyed) {
69
+ return;
70
+ }
71
+ this.#isDestroyed = true;
72
+ this.#controller?.abort();
73
+ this.#controller = null;
74
+ this.#element.removeAttribute("data-button-initialized");
75
+ }
76
+ #initialize() {
77
+ this.#controller = new AbortController();
78
+ this.#element.addEventListener("keydown", this.#onKeyDown, {
79
+ signal: this.#controller.signal
80
+ });
81
+ this.#element.setAttribute("data-button-initialized", "");
82
+ }
83
+ #onKeyDown = (event) => {
84
+ const { key, altKey, ctrlKey, metaKey, shiftKey } = event;
85
+ if (altKey || ctrlKey || metaKey || shiftKey) {
86
+ return;
87
+ }
88
+ if (!["Enter", " "].includes(key)) {
89
+ return;
90
+ }
91
+ const active = getActiveElement();
92
+ if (!(active instanceof HTMLElement)) {
93
+ return;
94
+ }
95
+ event.preventDefault();
96
+ active.click();
97
+ };
98
+ };
99
+ function getActiveElement() {
100
+ let current = document.activeElement;
101
+ while (current?.shadowRoot?.activeElement) {
102
+ current = current.shadowRoot.activeElement;
103
+ }
104
+ return current;
105
+ }
106
+
51
107
  // node_modules/@y14e/roving-tabindex/dist/index.js
52
108
  var defaultParser2 = (value) => value.split(/\s+/);
53
109
  var defaultSerializer2 = (tokens) => tokens.join(" ");
@@ -103,7 +159,12 @@ function getFocusables(container = document.body, options = {}) {
103
159
  console.warn("Invalid container element. Fallback: <body> element.");
104
160
  container = document.body;
105
161
  }
106
- let { composed = false, filter, include } = options;
162
+ let {
163
+ composed = false,
164
+ filter,
165
+ include,
166
+ skipVisibilityCheck = false
167
+ } = options;
107
168
  if (typeof composed !== "boolean") {
108
169
  console.warn("Invalid composed option. Fallback: false.");
109
170
  composed = false;
@@ -124,7 +185,7 @@ function getFocusables(container = document.body, options = {}) {
124
185
  if (composed || include) {
125
186
  let traverse2 = function(node) {
126
187
  if (node instanceof Element) {
127
- if (isFocusable(node) || include?.(node)) {
188
+ if (isFocusable(node, { skipVisibilityCheck }) || include?.(node)) {
128
189
  elements[elements.length] = node;
129
190
  }
130
191
  }
@@ -145,7 +206,7 @@ function getFocusables(container = document.body, options = {}) {
145
206
  if (!(candidate instanceof Element)) {
146
207
  continue;
147
208
  }
148
- if (isFocusable(candidate)) {
209
+ if (isFocusable(candidate, { skipVisibilityCheck })) {
149
210
  elements[elements.length] = candidate;
150
211
  }
151
212
  }
@@ -153,11 +214,16 @@ function getFocusables(container = document.body, options = {}) {
153
214
  const unfiltered = normalizeRadioGroup(sortByTabIndex(elements));
154
215
  return filter ? unfiltered.filter(filter) : unfiltered;
155
216
  }
156
- function isFocusable(element) {
217
+ function isFocusable(element, options = {}) {
157
218
  if (!(element instanceof Element)) {
158
219
  console.warn("Invalid element");
159
220
  return false;
160
221
  }
222
+ let { skipVisibilityCheck = false } = options;
223
+ if (typeof skipVisibilityCheck !== "boolean") {
224
+ console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
225
+ skipVisibilityCheck = false;
226
+ }
161
227
  if (element.hasAttribute("hidden") || isInert(element)) {
162
228
  return false;
163
229
  }
@@ -170,7 +236,7 @@ function isFocusable(element) {
170
236
  if (isDisabledDeep(element)) {
171
237
  return false;
172
238
  }
173
- if (!element.checkVisibility({
239
+ if (!skipVisibilityCheck && !element.checkVisibility({
174
240
  contentVisibilityAuto: true,
175
241
  opacityProperty: true,
176
242
  visibilityProperty: true
@@ -404,7 +470,7 @@ var RovingTabIndex = class {
404
470
  return;
405
471
  }
406
472
  }
407
- const active = getActiveElement();
473
+ const active = getActiveElement2();
408
474
  if (!(active instanceof HTMLElement)) {
409
475
  return;
410
476
  }
@@ -455,7 +521,8 @@ var RovingTabIndex = class {
455
521
  ...this.#getFocusables(),
456
522
  ...getFocusables(this.#container, {
457
523
  composed: true,
458
- filter: this.#selectorFilter
524
+ filter: this.#selectorFilter,
525
+ skipVisibilityCheck: true
459
526
  })
460
527
  ]);
461
528
  for (const focusable of this.#focusables) {
@@ -521,14 +588,15 @@ var RovingTabIndex = class {
521
588
  return getFocusables(this.#container, {
522
589
  composed: true,
523
590
  filter: this.#selectorFilter,
524
- include: (element) => this.#focusables.has(element)
591
+ include: (element) => this.#focusables.has(element),
592
+ skipVisibilityCheck: true
525
593
  });
526
594
  }
527
595
  };
528
596
  function focusElement(element) {
529
597
  "focus" in element && typeof element.focus === "function" && element.focus();
530
598
  }
531
- function getActiveElement() {
599
+ function getActiveElement2() {
532
600
  let current = document.activeElement;
533
601
  while (current?.shadowRoot?.activeElement) {
534
602
  current = current.shadowRoot.activeElement;
@@ -554,6 +622,7 @@ var Accordion = class _Accordion {
554
622
  #eventController = null;
555
623
  #animationController = null;
556
624
  #cleanupRovingTabIndex = null;
625
+ #buttons = [];
557
626
  #isDestroyed = false;
558
627
  constructor(root, options = {}) {
559
628
  if (!(root instanceof HTMLElement)) {
@@ -627,6 +696,10 @@ var Accordion = class _Accordion {
627
696
  this.#eventController = null;
628
697
  this.#cleanupRovingTabIndex?.();
629
698
  this.#cleanupRovingTabIndex = null;
699
+ this.#buttons.forEach((button) => {
700
+ button.destroy();
701
+ });
702
+ this.#buttons.length = 0;
630
703
  !force && await this.#waitAnimationsFinish();
631
704
  this.#contentElements.forEach((content) => {
632
705
  force && this.#bindings.get(content)?.animation?.finish();
@@ -669,12 +742,12 @@ var Accordion = class _Accordion {
669
742
  trigger2.style.setProperty("pointer-events", "none");
670
743
  }
671
744
  trigger2.addEventListener("click", this.#onTriggerClick, { signal });
672
- trigger2.addEventListener("keydown", this.#onTriggerKeyDown, { signal });
673
745
  addTokenToAttribute(content2, "aria-labelledby", trigger2.id);
674
746
  content2.setAttribute("role", "region");
675
747
  content2.addEventListener("beforematch", this.#onContentBeforeMatch, {
676
748
  signal
677
749
  });
750
+ this.#buttons.push(new Button(trigger2));
678
751
  });
679
752
  const { trigger, content } = this.#settings.selector;
680
753
  this.#cleanupRovingTabIndex = createRovingTabIndex(this.#rootElement, {
@@ -693,26 +766,6 @@ var Accordion = class _Accordion {
693
766
  }
694
767
  this.#toggle(trigger, trigger.ariaExpanded === "false");
695
768
  };
696
- #onTriggerKeyDown = (event) => {
697
- const { key, altKey, ctrlKey, metaKey, shiftKey } = event;
698
- if (altKey || ctrlKey || metaKey || shiftKey) {
699
- return;
700
- }
701
- if (!["Enter", " "].includes(key)) {
702
- return;
703
- }
704
- const active = getActiveElement2();
705
- if (!(active instanceof HTMLElement)) {
706
- return;
707
- }
708
- event.preventDefault();
709
- switch (key) {
710
- case "Enter":
711
- case " ":
712
- active.click();
713
- return;
714
- }
715
- };
716
769
  #onContentBeforeMatch = (event) => {
717
770
  const content = event.currentTarget;
718
771
  if (!(content instanceof HTMLElement)) {
@@ -809,13 +862,6 @@ var Accordion = class _Accordion {
809
862
  function createBinding(trigger, content) {
810
863
  return { trigger, content, animation: null };
811
864
  }
812
- function getActiveElement2() {
813
- let current = document.activeElement;
814
- while (current?.shadowRoot?.activeElement) {
815
- current = current.shadowRoot.activeElement;
816
- }
817
- return current;
818
- }
819
865
  function isFocusable2(element) {
820
866
  return !element.hasAttribute("disabled") && element.tabIndex >= 0;
821
867
  }
@@ -832,7 +878,7 @@ function waitAnimationFinish(animation) {
832
878
  * Accordion
833
879
  * WAI-ARIA compliant accordion pattern implementation in TypeScript.
834
880
  *
835
- * @version 1.3.2
881
+ * @version 1.4.0
836
882
  * @author Yusuke Kamiyamane
837
883
  * @license MIT
838
884
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -851,13 +897,24 @@ function waitAnimationFinish(animation) {
851
897
  * @see {@link https://github.com/y14e/attributes-utils}
852
898
  *)
853
899
 
900
+ @y14e/button/dist/index.js:
901
+ (**
902
+ * Button
903
+ *
904
+ * @version 1.0.0
905
+ * @author Yusuke Kamiyamane
906
+ * @license MIT
907
+ * @copyright Copyright (c) Yusuke Kamiyamane
908
+ * @see {@link https://github.com/y14e/button}
909
+ *)
910
+
854
911
  @y14e/roving-tabindex/dist/index.js:
855
912
  (**
856
913
  * Roving Tabindex
857
914
  * Lightweight roving tabindex utility with fully focus management.
858
915
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
859
916
  *
860
- * @version 1.3.0
917
+ * @version 1.3.1
861
918
  * @author Yusuke Kamiyamane
862
919
  * @license MIT
863
920
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -882,7 +939,7 @@ function waitAnimationFinish(animation) {
882
939
  * High-precision focus management utility with full composed tree support.
883
940
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
884
941
  *
885
- * @version 4.1.8
942
+ * @version 4.2.0
886
943
  * @author Yusuke Kamiyamane
887
944
  * @license MIT
888
945
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@y14e/accordion",
3
- "version": "1.3.2",
3
+ "version": "1.4.0",
4
4
  "description": "WAI-ARIA compliant accordion pattern implementation in TypeScript",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -44,7 +44,8 @@
44
44
  "homepage": "https://github.com/y14e/accordion#readme",
45
45
  "devDependencies": {
46
46
  "@y14e/attributes-utils": "^1.0.5",
47
- "@y14e/roving-tabindex": "^1.3.0",
47
+ "@y14e/button": "^1.0.0",
48
+ "@y14e/roving-tabindex": "^1.3.1",
48
49
  "bun-types": "latest",
49
50
  "tsup": "^8.0.0",
50
51
  "typescript": "^5.6.0",