@y14e/accordion 1.4.8 → 1.4.10

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/README.md CHANGED
@@ -10,14 +10,14 @@ npm i @y14e/accordion
10
10
 
11
11
  ```ts
12
12
  // npm
13
- import Accordion from '@y14e/accordion@1.4.8';
13
+ import Accordion from '@y14e/accordion@1.4.10';
14
14
 
15
15
  // CDNs
16
- import Accordion from 'https://esm.sh/@y14e/accordion@1.4.8';
16
+ import Accordion from 'https://esm.sh/@y14e/accordion@1.4.10';
17
17
  // or
18
- import Accordion from 'https://cdn.jsdelivr.net/npm/@y14e/accordion@1.4.8/+esm';
18
+ import Accordion from 'https://cdn.jsdelivr.net/npm/@y14e/accordion@1.4.10/+esm';
19
19
  // or
20
- import Accordion from 'https://esm.unpkg.com/@y14e/accordion@1.4.8';
20
+ import Accordion from 'https://esm.unpkg.com/@y14e/accordion@1.4.10';
21
21
  ```
22
22
 
23
23
  ## Usage
package/dist/index.cjs CHANGED
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
 
3
3
  // node_modules/@y14e/attributes-utils/dist/index.js
4
- var defaultParser = (value) => value.split(/\s+/);
5
- var defaultSerializer = (tokens) => tokens.join(" ");
4
+ var DEFAULT_PARSER = (value) => value.split(/\s+/);
5
+ var DEFAULT_SERIALIZER = (tokens) => tokens.join(" ");
6
6
  function addTokenToAttribute(element, attribute, token, options = {}) {
7
7
  const {
8
8
  caseInsensitive = false,
9
- parse = defaultParser,
10
- serialize = defaultSerializer
9
+ parse = DEFAULT_PARSER,
10
+ serialize = DEFAULT_SERIALIZER
11
11
  } = options;
12
12
  const value = element.getAttribute(attribute)?.trim();
13
13
  const tokens = value ? parse(value).filter(Boolean) : [];
@@ -17,11 +17,11 @@ function addTokenToAttribute(element, attribute, token, options = {}) {
17
17
  tokens.push(token);
18
18
  element.setAttribute(attribute, serialize(tokens));
19
19
  }
20
- return;
20
+ } else {
21
+ const set = new Set(tokens);
22
+ set.add(token);
23
+ element.setAttribute(attribute, serialize([...set]));
21
24
  }
22
- const set = new Set(tokens);
23
- set.add(token);
24
- element.setAttribute(attribute, serialize([...set]));
25
25
  }
26
26
  var snapshots = /* @__PURE__ */ new WeakMap();
27
27
  function restoreAttributes(elements) {
@@ -90,11 +90,10 @@ var Button = class {
90
90
  return;
91
91
  }
92
92
  const active = getActiveElement();
93
- if (!(active instanceof HTMLElement)) {
94
- return;
93
+ if (active instanceof HTMLElement) {
94
+ event.preventDefault();
95
+ active.click();
95
96
  }
96
- event.preventDefault();
97
- active.click();
98
97
  };
99
98
  };
100
99
  function getActiveElement() {
@@ -106,13 +105,13 @@ function getActiveElement() {
106
105
  }
107
106
 
108
107
  // node_modules/@y14e/roving-tabindex/dist/index.js
109
- var defaultParser2 = (value) => value.split(/\s+/);
110
- var defaultSerializer2 = (tokens) => tokens.join(" ");
108
+ var DEFAULT_PARSER2 = (value) => value.split(/\s+/);
109
+ var DEFAULT_SERIALIZER2 = (tokens) => tokens.join(" ");
111
110
  function addTokenToAttribute2(element, attribute, token, options = {}) {
112
111
  const {
113
112
  caseInsensitive = false,
114
- parse = defaultParser2,
115
- serialize = defaultSerializer2
113
+ parse = DEFAULT_PARSER2,
114
+ serialize = DEFAULT_SERIALIZER2
116
115
  } = options;
117
116
  const value = element.getAttribute(attribute)?.trim();
118
117
  const tokens = value ? parse(value).filter(Boolean) : [];
@@ -122,11 +121,11 @@ function addTokenToAttribute2(element, attribute, token, options = {}) {
122
121
  tokens.push(token);
123
122
  element.setAttribute(attribute, serialize(tokens));
124
123
  }
125
- return;
124
+ } else {
125
+ const set = new Set(tokens);
126
+ set.add(token);
127
+ element.setAttribute(attribute, serialize([...set]));
126
128
  }
127
- const set = new Set(tokens);
128
- set.add(token);
129
- element.setAttribute(attribute, serialize([...set]));
130
129
  }
131
130
  var snapshots2 = /* @__PURE__ */ new WeakMap();
132
131
  function restoreAttributes2(elements) {
@@ -193,21 +192,16 @@ function getFocusables(container = document.body, options = {}) {
193
192
  const elements = [];
194
193
  if (composed || include) {
195
194
  let traverse2 = function(node) {
196
- if (node instanceof Element) {
197
- if (isFocusable(node, {
198
- skipNegativeTabIndexCheck,
199
- skipVisibilityCheck
200
- }) || include?.(node)) {
201
- elements[elements.length] = node;
202
- }
195
+ if (!(node instanceof Element)) {
196
+ return;
197
+ }
198
+ if (isFocusable(node, { skipNegativeTabIndexCheck, skipVisibilityCheck }) || include?.(node)) {
199
+ elements[elements.length] = node;
203
200
  }
204
201
  const children = getComposedChildren(node);
205
202
  for (let i = 0, l = children.length; i < l; i++) {
206
203
  const child = children[i];
207
- if (!child) {
208
- continue;
209
- }
210
- traverse2(child);
204
+ child && traverse2(child);
211
205
  }
212
206
  };
213
207
  traverse2(container);
@@ -215,10 +209,7 @@ function getFocusables(container = document.body, options = {}) {
215
209
  const candidates = container.querySelectorAll(FOCUSABLE_SELECTOR);
216
210
  for (let i = 0, l = candidates.length; i < l; i++) {
217
211
  const candidate = candidates[i];
218
- if (!(candidate instanceof Element)) {
219
- continue;
220
- }
221
- if (isFocusable(candidate, {
212
+ if (candidate && isFocusable(candidate, {
222
213
  skipNegativeTabIndexCheck,
223
214
  skipVisibilityCheck
224
215
  })) {
@@ -321,23 +312,19 @@ function normalizeRadioGroup(elements) {
321
312
  for (const group of map.values()) {
322
313
  placeholder.add(group.find((radio) => radio.checked) ?? group[0]);
323
314
  }
324
- return elements.filter((element) => {
325
- if (isUngroupedRadio(element)) {
326
- return placeholder.has(element);
327
- }
328
- return true;
329
- });
315
+ return elements.filter(
316
+ (element) => isUngroupedRadio(element) ? placeholder.has(element) : true
317
+ );
330
318
  }
331
319
  function sortByTabIndex(elements) {
332
320
  const ordered = [];
333
321
  const natural = [];
334
322
  for (let i = 0, l = elements.length; i < l; i++) {
335
323
  const element = elements[i];
336
- if (!element) {
337
- continue;
324
+ if (element) {
325
+ const target = getTabIndex(element) > 0 ? ordered : natural;
326
+ target[target.length] = element;
338
327
  }
339
- const target = getTabIndex(element) > 0 ? ordered : natural;
340
- target[target.length] = element;
341
328
  }
342
329
  ordered.sort((a, b) => getTabIndex(a) - getTabIndex(b));
343
330
  let count = 0;
@@ -490,15 +477,10 @@ var RovingTabIndex = class {
490
477
  }
491
478
  #onFocusIn = (event) => {
492
479
  const { target } = event;
493
- if (!(target instanceof Element)) {
494
- return;
495
- }
496
- const isFocusable22 = this.#focusables.has(target);
497
- if (this.#options.noMemory && !isFocusable22) {
498
- this.#update(null);
499
- return;
480
+ if (target instanceof Element) {
481
+ const isFocusable22 = this.#focusables.has(target);
482
+ this.#options.noMemory && !isFocusable22 ? this.#update(null) : isFocusable22 && this.#update(target);
500
483
  }
501
- isFocusable22 && this.#update(target);
502
484
  };
503
485
  #onKeyDown = (event) => {
504
486
  if (!event.composedPath().includes(this.#container)) {
@@ -531,8 +513,7 @@ var RovingTabIndex = class {
531
513
  }
532
514
  event.preventDefault();
533
515
  const currentIndex = current.indexOf(active);
534
- let rawIndex;
535
- let newIndex = currentIndex;
516
+ let newIndex;
536
517
  let target = current;
537
518
  switch (key) {
538
519
  case "End":
@@ -542,15 +523,17 @@ var RovingTabIndex = class {
542
523
  newIndex = 0;
543
524
  break;
544
525
  case "ArrowLeft":
545
- case "ArrowUp":
546
- rawIndex = currentIndex - 1;
526
+ case "ArrowUp": {
527
+ const rawIndex = currentIndex - 1;
547
528
  newIndex = wrap ? rawIndex : Math.max(rawIndex, 0);
548
529
  break;
530
+ }
549
531
  case "ArrowRight":
550
- case "ArrowDown":
551
- rawIndex = currentIndex + 1;
532
+ case "ArrowDown": {
533
+ const rawIndex = currentIndex + 1;
552
534
  newIndex = wrap ? rawIndex % current.length : Math.min(rawIndex, current.length - 1);
553
535
  break;
536
+ }
554
537
  default: {
555
538
  target = this.#focusablesByFirstChar.get(key.toUpperCase()) ?? [];
556
539
  const foundIndex = target.findIndex(
@@ -565,15 +548,14 @@ var RovingTabIndex = class {
565
548
  #update(active) {
566
549
  const current = new Set(this.#getFocusables());
567
550
  for (const focusable of this.#focusables) {
568
- if (current.has(focusable)) {
569
- continue;
551
+ if (!current.has(focusable)) {
552
+ focusable.isConnected && restoreAttributes2([focusable]);
553
+ this.#focusables.delete(focusable);
554
+ this.#focusablesByFirstChar.forEach((focusables) => {
555
+ const index = focusables.indexOf(focusable);
556
+ index >= 0 && focusables.splice(index, 1);
557
+ });
570
558
  }
571
- focusable.isConnected && restoreAttributes2([focusable]);
572
- this.#focusables.delete(focusable);
573
- this.#focusablesByFirstChar.forEach((focusables) => {
574
- const index = focusables.indexOf(focusable);
575
- index >= 0 && focusables.splice(index, 1);
576
- });
577
559
  }
578
560
  const { navigationOnly, noStart, typeahead } = this.#options;
579
561
  for (const focusable of current) {
@@ -588,11 +570,11 @@ var RovingTabIndex = class {
588
570
  if (!typeahead) {
589
571
  continue;
590
572
  }
573
+ const char = focusable.textContent?.trim()?.at(0)?.toUpperCase();
591
574
  const value = focusable.ariaKeyShortcuts?.trim();
592
575
  const keys = new Set(
593
576
  value ? value.split(/\s+/).filter((key) => /^\S$/i.test(key)).map((key) => key.toUpperCase()) : []
594
577
  );
595
- const char = focusable.textContent?.trim()?.at(0)?.toUpperCase();
596
578
  if (char) {
597
579
  keys.add(char);
598
580
  saveAttributes2([focusable], ["aria-keyshortcuts"]);
@@ -606,18 +588,17 @@ var RovingTabIndex = class {
606
588
  this.#focusablesByFirstChar.set(key, focusables);
607
589
  });
608
590
  }
609
- if (navigationOnly) {
610
- return;
611
- }
612
- if (active && this.#focusables.has(active)) {
613
- this.#focusables.forEach((focusable) => {
614
- focusable.setAttribute("tabindex", focusable === active ? "0" : "-1");
615
- });
616
- return;
591
+ if (!navigationOnly) {
592
+ if (active && this.#focusables.has(active)) {
593
+ this.#focusables.forEach((focusable) => {
594
+ focusable.setAttribute("tabindex", focusable === active ? "0" : "-1");
595
+ });
596
+ } else {
597
+ [...this.#focusables].forEach((focusable, i) => {
598
+ focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
599
+ });
600
+ }
617
601
  }
618
- [...this.#focusables].forEach((focusable, i) => {
619
- focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
620
- });
621
602
  }
622
603
  #createSelectorFilter() {
623
604
  const { selector } = this.#options;
@@ -697,12 +678,11 @@ var Accordion = class _Accordion {
697
678
  }
698
679
  this.#triggerElements.forEach((trigger2, i) => {
699
680
  const content2 = this.#contentElements[i];
700
- if (!content2) {
701
- return;
681
+ if (content2) {
682
+ const binding = createBinding(trigger2, content2);
683
+ this.#bindings.set(trigger2, binding);
684
+ this.#bindings.set(content2, binding);
702
685
  }
703
- const binding = createBinding(trigger2, content2);
704
- this.#bindings.set(trigger2, binding);
705
- this.#bindings.set(content2, binding);
706
686
  });
707
687
  this.#initialize();
708
688
  }
@@ -710,11 +690,11 @@ var Accordion = class _Accordion {
710
690
  if (this.#isDestroyed) {
711
691
  return;
712
692
  }
713
- if (!(trigger instanceof HTMLElement) || !this.#bindings.has(trigger)) {
693
+ if (trigger instanceof HTMLElement && this.#bindings.has(trigger)) {
694
+ this.#toggle(trigger, false);
695
+ } else {
714
696
  console.warn("Invalid trigger element");
715
- return;
716
697
  }
717
- this.#toggle(trigger, false);
718
698
  }
719
699
  async destroy(force = false) {
720
700
  if (this.#isDestroyed) {
@@ -745,11 +725,11 @@ var Accordion = class _Accordion {
745
725
  if (this.#isDestroyed) {
746
726
  return;
747
727
  }
748
- if (!(trigger instanceof HTMLElement) || !this.#bindings.has(trigger)) {
728
+ if (trigger instanceof HTMLElement && this.#bindings.has(trigger)) {
729
+ this.#toggle(trigger, true);
730
+ } else {
749
731
  console.warn("Invalid trigger element");
750
- return;
751
732
  }
752
- this.#toggle(trigger, true);
753
733
  }
754
734
  #initialize() {
755
735
  saveAttributes(this.#triggerElements, [
@@ -800,21 +780,16 @@ var Accordion = class _Accordion {
800
780
  #onTriggerClick = (event) => {
801
781
  event.preventDefault();
802
782
  const trigger = event.currentTarget;
803
- if (!(trigger instanceof HTMLElement)) {
804
- return;
783
+ if (trigger instanceof HTMLElement) {
784
+ this.#toggle(trigger, trigger.ariaExpanded === "false");
805
785
  }
806
- this.#toggle(trigger, trigger.ariaExpanded === "false");
807
786
  };
808
787
  #onContentBeforeMatch = (event) => {
809
788
  const content = event.currentTarget;
810
- if (!(content instanceof HTMLElement)) {
811
- return;
812
- }
813
- const binding = this.#bindings.get(content);
814
- if (!binding) {
815
- return;
789
+ if (content instanceof HTMLElement) {
790
+ const binding = this.#bindings.get(content);
791
+ binding && binding.trigger.ariaExpanded === "false" && this.#toggle(binding.trigger, true, true);
816
792
  }
817
- binding.trigger.ariaExpanded === "false" && this.#toggle(binding.trigger, true, true);
818
793
  };
819
794
  #toggle(trigger, isOpen, isMatch = false) {
820
795
  if (trigger.ariaExpanded === String(isOpen)) {
@@ -863,11 +838,10 @@ var Accordion = class _Accordion {
863
838
  animation.addEventListener(
864
839
  "finish",
865
840
  () => {
866
- if (binding?.animation !== animation) {
867
- return;
841
+ if (binding?.animation === animation) {
842
+ this.#onAnimationFinish(content);
843
+ cleanup();
868
844
  }
869
- this.#onAnimationFinish(content);
870
- cleanup();
871
845
  },
872
846
  { once: true, signal }
873
847
  );
@@ -880,10 +854,7 @@ var Accordion = class _Accordion {
880
854
  }
881
855
  #onAnimationFinish(content) {
882
856
  const trigger = this.#bindings.get(content)?.trigger;
883
- if (!trigger) {
884
- return;
885
- }
886
- if (trigger.ariaExpanded === "false") {
857
+ if (trigger && trigger.ariaExpanded === "false") {
887
858
  content.setAttribute("hidden", "until-found");
888
859
  }
889
860
  ["block-size", "overflow"].forEach((name) => {
@@ -908,16 +879,17 @@ function isFocusable2(element) {
908
879
  function waitAnimationFinish(animation) {
909
880
  if (["idle", "finished"].includes(animation.playState)) {
910
881
  return Promise.resolve();
882
+ } else {
883
+ return new Promise(
884
+ (resolve) => animation.addEventListener("finish", () => resolve(), { once: true })
885
+ );
911
886
  }
912
- return new Promise(
913
- (resolve) => animation.addEventListener("finish", () => resolve(), { once: true })
914
- );
915
887
  }
916
888
  /**
917
889
  * Accordion
918
890
  * WAI-ARIA compliant accordion pattern implementation in TypeScript.
919
891
  *
920
- * @version 1.4.8
892
+ * @version 1.4.10
921
893
  * @author Yusuke Kamiyamane
922
894
  * @license MIT
923
895
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -929,7 +901,7 @@ function waitAnimationFinish(animation) {
929
901
  (**
930
902
  * Attributes Utils
931
903
  *
932
- * @version 1.1.1
904
+ * @version 1.1.2
933
905
  * @author Yusuke Kamiyamane
934
906
  * @license MIT
935
907
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -940,7 +912,7 @@ function waitAnimationFinish(animation) {
940
912
  (**
941
913
  * Button
942
914
  *
943
- * @version 1.0.0
915
+ * @version 1.0.1
944
916
  * @author Yusuke Kamiyamane
945
917
  * @license MIT
946
918
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -953,7 +925,7 @@ function waitAnimationFinish(animation) {
953
925
  * Lightweight roving tabindex utility with fully focus management.
954
926
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
955
927
  *
956
- * @version 3.0.0
928
+ * @version 3.0.4
957
929
  * @author Yusuke Kamiyamane
958
930
  * @license MIT
959
931
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -965,7 +937,7 @@ function waitAnimationFinish(animation) {
965
937
  (**
966
938
  * Attributes Utils
967
939
  *
968
- * @version 1.1.1
940
+ * @version 1.1.2
969
941
  * @author Yusuke Kamiyamane
970
942
  * @license MIT
971
943
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -978,7 +950,7 @@ function waitAnimationFinish(animation) {
978
950
  * High-precision focus management utility with full composed tree support.
979
951
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
980
952
  *
981
- * @version 4.3.2
953
+ * @version 4.3.3
982
954
  * @author Yusuke Kamiyamane
983
955
  * @license MIT
984
956
  * @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.4.8
5
+ * @version 1.4.10
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.4.8
5
+ * @version 1.4.10
6
6
  * @author Yusuke Kamiyamane
7
7
  * @license MIT
8
8
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  // node_modules/@y14e/attributes-utils/dist/index.js
2
- var defaultParser = (value) => value.split(/\s+/);
3
- var defaultSerializer = (tokens) => tokens.join(" ");
2
+ var DEFAULT_PARSER = (value) => value.split(/\s+/);
3
+ var DEFAULT_SERIALIZER = (tokens) => tokens.join(" ");
4
4
  function addTokenToAttribute(element, attribute, token, options = {}) {
5
5
  const {
6
6
  caseInsensitive = false,
7
- parse = defaultParser,
8
- serialize = defaultSerializer
7
+ parse = DEFAULT_PARSER,
8
+ serialize = DEFAULT_SERIALIZER
9
9
  } = options;
10
10
  const value = element.getAttribute(attribute)?.trim();
11
11
  const tokens = value ? parse(value).filter(Boolean) : [];
@@ -15,11 +15,11 @@ function addTokenToAttribute(element, attribute, token, options = {}) {
15
15
  tokens.push(token);
16
16
  element.setAttribute(attribute, serialize(tokens));
17
17
  }
18
- return;
18
+ } else {
19
+ const set = new Set(tokens);
20
+ set.add(token);
21
+ element.setAttribute(attribute, serialize([...set]));
19
22
  }
20
- const set = new Set(tokens);
21
- set.add(token);
22
- element.setAttribute(attribute, serialize([...set]));
23
23
  }
24
24
  var snapshots = /* @__PURE__ */ new WeakMap();
25
25
  function restoreAttributes(elements) {
@@ -88,11 +88,10 @@ var Button = class {
88
88
  return;
89
89
  }
90
90
  const active = getActiveElement();
91
- if (!(active instanceof HTMLElement)) {
92
- return;
91
+ if (active instanceof HTMLElement) {
92
+ event.preventDefault();
93
+ active.click();
93
94
  }
94
- event.preventDefault();
95
- active.click();
96
95
  };
97
96
  };
98
97
  function getActiveElement() {
@@ -104,13 +103,13 @@ function getActiveElement() {
104
103
  }
105
104
 
106
105
  // node_modules/@y14e/roving-tabindex/dist/index.js
107
- var defaultParser2 = (value) => value.split(/\s+/);
108
- var defaultSerializer2 = (tokens) => tokens.join(" ");
106
+ var DEFAULT_PARSER2 = (value) => value.split(/\s+/);
107
+ var DEFAULT_SERIALIZER2 = (tokens) => tokens.join(" ");
109
108
  function addTokenToAttribute2(element, attribute, token, options = {}) {
110
109
  const {
111
110
  caseInsensitive = false,
112
- parse = defaultParser2,
113
- serialize = defaultSerializer2
111
+ parse = DEFAULT_PARSER2,
112
+ serialize = DEFAULT_SERIALIZER2
114
113
  } = options;
115
114
  const value = element.getAttribute(attribute)?.trim();
116
115
  const tokens = value ? parse(value).filter(Boolean) : [];
@@ -120,11 +119,11 @@ function addTokenToAttribute2(element, attribute, token, options = {}) {
120
119
  tokens.push(token);
121
120
  element.setAttribute(attribute, serialize(tokens));
122
121
  }
123
- return;
122
+ } else {
123
+ const set = new Set(tokens);
124
+ set.add(token);
125
+ element.setAttribute(attribute, serialize([...set]));
124
126
  }
125
- const set = new Set(tokens);
126
- set.add(token);
127
- element.setAttribute(attribute, serialize([...set]));
128
127
  }
129
128
  var snapshots2 = /* @__PURE__ */ new WeakMap();
130
129
  function restoreAttributes2(elements) {
@@ -191,21 +190,16 @@ function getFocusables(container = document.body, options = {}) {
191
190
  const elements = [];
192
191
  if (composed || include) {
193
192
  let traverse2 = function(node) {
194
- if (node instanceof Element) {
195
- if (isFocusable(node, {
196
- skipNegativeTabIndexCheck,
197
- skipVisibilityCheck
198
- }) || include?.(node)) {
199
- elements[elements.length] = node;
200
- }
193
+ if (!(node instanceof Element)) {
194
+ return;
195
+ }
196
+ if (isFocusable(node, { skipNegativeTabIndexCheck, skipVisibilityCheck }) || include?.(node)) {
197
+ elements[elements.length] = node;
201
198
  }
202
199
  const children = getComposedChildren(node);
203
200
  for (let i = 0, l = children.length; i < l; i++) {
204
201
  const child = children[i];
205
- if (!child) {
206
- continue;
207
- }
208
- traverse2(child);
202
+ child && traverse2(child);
209
203
  }
210
204
  };
211
205
  traverse2(container);
@@ -213,10 +207,7 @@ function getFocusables(container = document.body, options = {}) {
213
207
  const candidates = container.querySelectorAll(FOCUSABLE_SELECTOR);
214
208
  for (let i = 0, l = candidates.length; i < l; i++) {
215
209
  const candidate = candidates[i];
216
- if (!(candidate instanceof Element)) {
217
- continue;
218
- }
219
- if (isFocusable(candidate, {
210
+ if (candidate && isFocusable(candidate, {
220
211
  skipNegativeTabIndexCheck,
221
212
  skipVisibilityCheck
222
213
  })) {
@@ -319,23 +310,19 @@ function normalizeRadioGroup(elements) {
319
310
  for (const group of map.values()) {
320
311
  placeholder.add(group.find((radio) => radio.checked) ?? group[0]);
321
312
  }
322
- return elements.filter((element) => {
323
- if (isUngroupedRadio(element)) {
324
- return placeholder.has(element);
325
- }
326
- return true;
327
- });
313
+ return elements.filter(
314
+ (element) => isUngroupedRadio(element) ? placeholder.has(element) : true
315
+ );
328
316
  }
329
317
  function sortByTabIndex(elements) {
330
318
  const ordered = [];
331
319
  const natural = [];
332
320
  for (let i = 0, l = elements.length; i < l; i++) {
333
321
  const element = elements[i];
334
- if (!element) {
335
- continue;
322
+ if (element) {
323
+ const target = getTabIndex(element) > 0 ? ordered : natural;
324
+ target[target.length] = element;
336
325
  }
337
- const target = getTabIndex(element) > 0 ? ordered : natural;
338
- target[target.length] = element;
339
326
  }
340
327
  ordered.sort((a, b) => getTabIndex(a) - getTabIndex(b));
341
328
  let count = 0;
@@ -488,15 +475,10 @@ var RovingTabIndex = class {
488
475
  }
489
476
  #onFocusIn = (event) => {
490
477
  const { target } = event;
491
- if (!(target instanceof Element)) {
492
- return;
493
- }
494
- const isFocusable22 = this.#focusables.has(target);
495
- if (this.#options.noMemory && !isFocusable22) {
496
- this.#update(null);
497
- return;
478
+ if (target instanceof Element) {
479
+ const isFocusable22 = this.#focusables.has(target);
480
+ this.#options.noMemory && !isFocusable22 ? this.#update(null) : isFocusable22 && this.#update(target);
498
481
  }
499
- isFocusable22 && this.#update(target);
500
482
  };
501
483
  #onKeyDown = (event) => {
502
484
  if (!event.composedPath().includes(this.#container)) {
@@ -529,8 +511,7 @@ var RovingTabIndex = class {
529
511
  }
530
512
  event.preventDefault();
531
513
  const currentIndex = current.indexOf(active);
532
- let rawIndex;
533
- let newIndex = currentIndex;
514
+ let newIndex;
534
515
  let target = current;
535
516
  switch (key) {
536
517
  case "End":
@@ -540,15 +521,17 @@ var RovingTabIndex = class {
540
521
  newIndex = 0;
541
522
  break;
542
523
  case "ArrowLeft":
543
- case "ArrowUp":
544
- rawIndex = currentIndex - 1;
524
+ case "ArrowUp": {
525
+ const rawIndex = currentIndex - 1;
545
526
  newIndex = wrap ? rawIndex : Math.max(rawIndex, 0);
546
527
  break;
528
+ }
547
529
  case "ArrowRight":
548
- case "ArrowDown":
549
- rawIndex = currentIndex + 1;
530
+ case "ArrowDown": {
531
+ const rawIndex = currentIndex + 1;
550
532
  newIndex = wrap ? rawIndex % current.length : Math.min(rawIndex, current.length - 1);
551
533
  break;
534
+ }
552
535
  default: {
553
536
  target = this.#focusablesByFirstChar.get(key.toUpperCase()) ?? [];
554
537
  const foundIndex = target.findIndex(
@@ -563,15 +546,14 @@ var RovingTabIndex = class {
563
546
  #update(active) {
564
547
  const current = new Set(this.#getFocusables());
565
548
  for (const focusable of this.#focusables) {
566
- if (current.has(focusable)) {
567
- continue;
549
+ if (!current.has(focusable)) {
550
+ focusable.isConnected && restoreAttributes2([focusable]);
551
+ this.#focusables.delete(focusable);
552
+ this.#focusablesByFirstChar.forEach((focusables) => {
553
+ const index = focusables.indexOf(focusable);
554
+ index >= 0 && focusables.splice(index, 1);
555
+ });
568
556
  }
569
- focusable.isConnected && restoreAttributes2([focusable]);
570
- this.#focusables.delete(focusable);
571
- this.#focusablesByFirstChar.forEach((focusables) => {
572
- const index = focusables.indexOf(focusable);
573
- index >= 0 && focusables.splice(index, 1);
574
- });
575
557
  }
576
558
  const { navigationOnly, noStart, typeahead } = this.#options;
577
559
  for (const focusable of current) {
@@ -586,11 +568,11 @@ var RovingTabIndex = class {
586
568
  if (!typeahead) {
587
569
  continue;
588
570
  }
571
+ const char = focusable.textContent?.trim()?.at(0)?.toUpperCase();
589
572
  const value = focusable.ariaKeyShortcuts?.trim();
590
573
  const keys = new Set(
591
574
  value ? value.split(/\s+/).filter((key) => /^\S$/i.test(key)).map((key) => key.toUpperCase()) : []
592
575
  );
593
- const char = focusable.textContent?.trim()?.at(0)?.toUpperCase();
594
576
  if (char) {
595
577
  keys.add(char);
596
578
  saveAttributes2([focusable], ["aria-keyshortcuts"]);
@@ -604,18 +586,17 @@ var RovingTabIndex = class {
604
586
  this.#focusablesByFirstChar.set(key, focusables);
605
587
  });
606
588
  }
607
- if (navigationOnly) {
608
- return;
609
- }
610
- if (active && this.#focusables.has(active)) {
611
- this.#focusables.forEach((focusable) => {
612
- focusable.setAttribute("tabindex", focusable === active ? "0" : "-1");
613
- });
614
- return;
589
+ if (!navigationOnly) {
590
+ if (active && this.#focusables.has(active)) {
591
+ this.#focusables.forEach((focusable) => {
592
+ focusable.setAttribute("tabindex", focusable === active ? "0" : "-1");
593
+ });
594
+ } else {
595
+ [...this.#focusables].forEach((focusable, i) => {
596
+ focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
597
+ });
598
+ }
615
599
  }
616
- [...this.#focusables].forEach((focusable, i) => {
617
- focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
618
- });
619
600
  }
620
601
  #createSelectorFilter() {
621
602
  const { selector } = this.#options;
@@ -695,12 +676,11 @@ var Accordion = class _Accordion {
695
676
  }
696
677
  this.#triggerElements.forEach((trigger2, i) => {
697
678
  const content2 = this.#contentElements[i];
698
- if (!content2) {
699
- return;
679
+ if (content2) {
680
+ const binding = createBinding(trigger2, content2);
681
+ this.#bindings.set(trigger2, binding);
682
+ this.#bindings.set(content2, binding);
700
683
  }
701
- const binding = createBinding(trigger2, content2);
702
- this.#bindings.set(trigger2, binding);
703
- this.#bindings.set(content2, binding);
704
684
  });
705
685
  this.#initialize();
706
686
  }
@@ -708,11 +688,11 @@ var Accordion = class _Accordion {
708
688
  if (this.#isDestroyed) {
709
689
  return;
710
690
  }
711
- if (!(trigger instanceof HTMLElement) || !this.#bindings.has(trigger)) {
691
+ if (trigger instanceof HTMLElement && this.#bindings.has(trigger)) {
692
+ this.#toggle(trigger, false);
693
+ } else {
712
694
  console.warn("Invalid trigger element");
713
- return;
714
695
  }
715
- this.#toggle(trigger, false);
716
696
  }
717
697
  async destroy(force = false) {
718
698
  if (this.#isDestroyed) {
@@ -743,11 +723,11 @@ var Accordion = class _Accordion {
743
723
  if (this.#isDestroyed) {
744
724
  return;
745
725
  }
746
- if (!(trigger instanceof HTMLElement) || !this.#bindings.has(trigger)) {
726
+ if (trigger instanceof HTMLElement && this.#bindings.has(trigger)) {
727
+ this.#toggle(trigger, true);
728
+ } else {
747
729
  console.warn("Invalid trigger element");
748
- return;
749
730
  }
750
- this.#toggle(trigger, true);
751
731
  }
752
732
  #initialize() {
753
733
  saveAttributes(this.#triggerElements, [
@@ -798,21 +778,16 @@ var Accordion = class _Accordion {
798
778
  #onTriggerClick = (event) => {
799
779
  event.preventDefault();
800
780
  const trigger = event.currentTarget;
801
- if (!(trigger instanceof HTMLElement)) {
802
- return;
781
+ if (trigger instanceof HTMLElement) {
782
+ this.#toggle(trigger, trigger.ariaExpanded === "false");
803
783
  }
804
- this.#toggle(trigger, trigger.ariaExpanded === "false");
805
784
  };
806
785
  #onContentBeforeMatch = (event) => {
807
786
  const content = event.currentTarget;
808
- if (!(content instanceof HTMLElement)) {
809
- return;
810
- }
811
- const binding = this.#bindings.get(content);
812
- if (!binding) {
813
- return;
787
+ if (content instanceof HTMLElement) {
788
+ const binding = this.#bindings.get(content);
789
+ binding && binding.trigger.ariaExpanded === "false" && this.#toggle(binding.trigger, true, true);
814
790
  }
815
- binding.trigger.ariaExpanded === "false" && this.#toggle(binding.trigger, true, true);
816
791
  };
817
792
  #toggle(trigger, isOpen, isMatch = false) {
818
793
  if (trigger.ariaExpanded === String(isOpen)) {
@@ -861,11 +836,10 @@ var Accordion = class _Accordion {
861
836
  animation.addEventListener(
862
837
  "finish",
863
838
  () => {
864
- if (binding?.animation !== animation) {
865
- return;
839
+ if (binding?.animation === animation) {
840
+ this.#onAnimationFinish(content);
841
+ cleanup();
866
842
  }
867
- this.#onAnimationFinish(content);
868
- cleanup();
869
843
  },
870
844
  { once: true, signal }
871
845
  );
@@ -878,10 +852,7 @@ var Accordion = class _Accordion {
878
852
  }
879
853
  #onAnimationFinish(content) {
880
854
  const trigger = this.#bindings.get(content)?.trigger;
881
- if (!trigger) {
882
- return;
883
- }
884
- if (trigger.ariaExpanded === "false") {
855
+ if (trigger && trigger.ariaExpanded === "false") {
885
856
  content.setAttribute("hidden", "until-found");
886
857
  }
887
858
  ["block-size", "overflow"].forEach((name) => {
@@ -906,16 +877,17 @@ function isFocusable2(element) {
906
877
  function waitAnimationFinish(animation) {
907
878
  if (["idle", "finished"].includes(animation.playState)) {
908
879
  return Promise.resolve();
880
+ } else {
881
+ return new Promise(
882
+ (resolve) => animation.addEventListener("finish", () => resolve(), { once: true })
883
+ );
909
884
  }
910
- return new Promise(
911
- (resolve) => animation.addEventListener("finish", () => resolve(), { once: true })
912
- );
913
885
  }
914
886
  /**
915
887
  * Accordion
916
888
  * WAI-ARIA compliant accordion pattern implementation in TypeScript.
917
889
  *
918
- * @version 1.4.8
890
+ * @version 1.4.10
919
891
  * @author Yusuke Kamiyamane
920
892
  * @license MIT
921
893
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -927,7 +899,7 @@ function waitAnimationFinish(animation) {
927
899
  (**
928
900
  * Attributes Utils
929
901
  *
930
- * @version 1.1.1
902
+ * @version 1.1.2
931
903
  * @author Yusuke Kamiyamane
932
904
  * @license MIT
933
905
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -938,7 +910,7 @@ function waitAnimationFinish(animation) {
938
910
  (**
939
911
  * Button
940
912
  *
941
- * @version 1.0.0
913
+ * @version 1.0.1
942
914
  * @author Yusuke Kamiyamane
943
915
  * @license MIT
944
916
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -951,7 +923,7 @@ function waitAnimationFinish(animation) {
951
923
  * Lightweight roving tabindex utility with fully focus management.
952
924
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
953
925
  *
954
- * @version 3.0.0
926
+ * @version 3.0.4
955
927
  * @author Yusuke Kamiyamane
956
928
  * @license MIT
957
929
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -963,7 +935,7 @@ function waitAnimationFinish(animation) {
963
935
  (**
964
936
  * Attributes Utils
965
937
  *
966
- * @version 1.1.1
938
+ * @version 1.1.2
967
939
  * @author Yusuke Kamiyamane
968
940
  * @license MIT
969
941
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -976,7 +948,7 @@ function waitAnimationFinish(animation) {
976
948
  * High-precision focus management utility with full composed tree support.
977
949
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
978
950
  *
979
- * @version 4.3.2
951
+ * @version 4.3.3
980
952
  * @author Yusuke Kamiyamane
981
953
  * @license MIT
982
954
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@y14e/accordion",
3
- "version": "1.4.8",
3
+ "version": "1.4.10",
4
4
  "description": "WAI-ARIA compliant accordion pattern implementation in TypeScript",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -43,9 +43,9 @@
43
43
  },
44
44
  "homepage": "https://github.com/y14e/accordion#readme",
45
45
  "devDependencies": {
46
- "@y14e/attributes-utils": "^1.1.1",
47
- "@y14e/button": "^1.0.0",
48
- "@y14e/roving-tabindex": "^3.0.0",
46
+ "@y14e/attributes-utils": "^1.1.2",
47
+ "@y14e/button": "^1.0.1",
48
+ "@y14e/roving-tabindex": "^3.0.4",
49
49
  "bun-types": "latest",
50
50
  "tsup": "^8.0.0",
51
51
  "typescript": "^5.6.0",