@y14e/accordion 1.3.1 → 1.3.3

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
@@ -105,7 +105,12 @@ function getFocusables(container = document.body, options = {}) {
105
105
  console.warn("Invalid container element. Fallback: <body> element.");
106
106
  container = document.body;
107
107
  }
108
- let { composed = false, filter, include } = options;
108
+ let {
109
+ composed = false,
110
+ filter,
111
+ include,
112
+ skipVisibilityCheck = false
113
+ } = options;
109
114
  if (typeof composed !== "boolean") {
110
115
  console.warn("Invalid composed option. Fallback: false.");
111
116
  composed = false;
@@ -126,7 +131,7 @@ function getFocusables(container = document.body, options = {}) {
126
131
  if (composed || include) {
127
132
  let traverse2 = function(node) {
128
133
  if (node instanceof Element) {
129
- if (isFocusable(node) || include?.(node)) {
134
+ if (isFocusable(node, { skipVisibilityCheck }) || include?.(node)) {
130
135
  elements[elements.length] = node;
131
136
  }
132
137
  }
@@ -147,7 +152,7 @@ function getFocusables(container = document.body, options = {}) {
147
152
  if (!(candidate instanceof Element)) {
148
153
  continue;
149
154
  }
150
- if (isFocusable(candidate)) {
155
+ if (isFocusable(candidate, { skipVisibilityCheck })) {
151
156
  elements[elements.length] = candidate;
152
157
  }
153
158
  }
@@ -155,11 +160,16 @@ function getFocusables(container = document.body, options = {}) {
155
160
  const unfiltered = normalizeRadioGroup(sortByTabIndex(elements));
156
161
  return filter ? unfiltered.filter(filter) : unfiltered;
157
162
  }
158
- function isFocusable(element) {
163
+ function isFocusable(element, options = {}) {
159
164
  if (!(element instanceof Element)) {
160
165
  console.warn("Invalid element");
161
166
  return false;
162
167
  }
168
+ let { skipVisibilityCheck = false } = options;
169
+ if (typeof skipVisibilityCheck !== "boolean") {
170
+ console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
171
+ skipVisibilityCheck = false;
172
+ }
163
173
  if (element.hasAttribute("hidden") || isInert(element)) {
164
174
  return false;
165
175
  }
@@ -172,7 +182,7 @@ function isFocusable(element) {
172
182
  if (isDisabledDeep(element)) {
173
183
  return false;
174
184
  }
175
- if (!element.checkVisibility({
185
+ if (!skipVisibilityCheck && !element.checkVisibility({
176
186
  contentVisibilityAuto: true,
177
187
  opacityProperty: true,
178
188
  visibilityProperty: true
@@ -457,7 +467,8 @@ var RovingTabIndex = class {
457
467
  ...this.#getFocusables(),
458
468
  ...getFocusables(this.#container, {
459
469
  composed: true,
460
- filter: this.#selectorFilter
470
+ filter: this.#selectorFilter,
471
+ skipVisibilityCheck: true
461
472
  })
462
473
  ]);
463
474
  for (const focusable of this.#focusables) {
@@ -523,7 +534,8 @@ var RovingTabIndex = class {
523
534
  return getFocusables(this.#container, {
524
535
  composed: true,
525
536
  filter: this.#selectorFilter,
526
- include: (element) => this.#focusables.has(element)
537
+ include: (element) => this.#focusables.has(element),
538
+ skipVisibilityCheck: true
527
539
  });
528
540
  }
529
541
  };
@@ -598,12 +610,6 @@ var Accordion = class _Accordion {
598
610
  this.#bindings.set(trigger2, binding);
599
611
  this.#bindings.set(content2, binding);
600
612
  });
601
- this.#cleanupRovingTabIndex = createRovingTabIndex(this.#rootElement, {
602
- direction: "vertical",
603
- navigationOnly: true,
604
- selector: `${trigger}${NOT_NESTED}`,
605
- wrap: true
606
- });
607
613
  this.#initialize();
608
614
  }
609
615
  open(trigger) {
@@ -631,10 +637,10 @@ var Accordion = class _Accordion {
631
637
  return;
632
638
  }
633
639
  this.#isDestroyed = true;
634
- this.#cleanupRovingTabIndex?.();
635
- this.#cleanupRovingTabIndex = null;
636
640
  this.#eventController?.abort();
637
641
  this.#eventController = null;
642
+ this.#cleanupRovingTabIndex?.();
643
+ this.#cleanupRovingTabIndex = null;
638
644
  !force && await this.#waitAnimationsFinish();
639
645
  this.#contentElements.forEach((content) => {
640
646
  force && this.#bindings.get(content)?.animation?.finish();
@@ -657,33 +663,40 @@ var Accordion = class _Accordion {
657
663
  "style",
658
664
  "tabindex"
659
665
  ]);
660
- this.#triggerElements.forEach((trigger, i) => {
666
+ this.#triggerElements.forEach((trigger2, i) => {
661
667
  const id = Math.random().toString(36).slice(-8);
662
- const content = this.#contentElements[i];
663
- if (!content) {
668
+ const content2 = this.#contentElements[i];
669
+ if (!content2) {
664
670
  return;
665
671
  }
666
- saveAttributes([content], ["aria-labelledby", "id", "role"]);
667
- content.id ||= `accordion-content-${id}`;
668
- addTokenToAttribute(trigger, "aria-controls", content.id);
669
- trigger.setAttribute(
672
+ saveAttributes([content2], ["aria-labelledby", "id", "role"]);
673
+ content2.id ||= `accordion-content-${id}`;
674
+ addTokenToAttribute(trigger2, "aria-controls", content2.id);
675
+ trigger2.setAttribute(
670
676
  "aria-expanded",
671
- trigger.ariaExpanded === "true" ? "true" : "false"
677
+ trigger2.ariaExpanded === "true" ? "true" : "false"
672
678
  );
673
- trigger.id ||= `accordion-trigger-${id}`;
674
- if (!isFocusable2(trigger)) {
675
- trigger.setAttribute("aria-disabled", "true");
676
- trigger.setAttribute("tabindex", "-1");
677
- trigger.style.setProperty("pointer-events", "none");
679
+ trigger2.id ||= `accordion-trigger-${id}`;
680
+ if (!isFocusable2(trigger2)) {
681
+ trigger2.setAttribute("aria-disabled", "true");
682
+ trigger2.setAttribute("tabindex", "-1");
683
+ trigger2.style.setProperty("pointer-events", "none");
678
684
  }
679
- trigger.addEventListener("click", this.#onTriggerClick, { signal });
680
- trigger.addEventListener("keydown", this.#onTriggerKeyDown, { signal });
681
- addTokenToAttribute(content, "aria-labelledby", trigger.id);
682
- content.setAttribute("role", "region");
683
- content.addEventListener("beforematch", this.#onContentBeforeMatch, {
685
+ trigger2.addEventListener("click", this.#onTriggerClick, { signal });
686
+ trigger2.addEventListener("keydown", this.#onTriggerKeyDown, { signal });
687
+ addTokenToAttribute(content2, "aria-labelledby", trigger2.id);
688
+ content2.setAttribute("role", "region");
689
+ content2.addEventListener("beforematch", this.#onContentBeforeMatch, {
684
690
  signal
685
691
  });
686
692
  });
693
+ const { trigger, content } = this.#settings.selector;
694
+ this.#cleanupRovingTabIndex = createRovingTabIndex(this.#rootElement, {
695
+ direction: "vertical",
696
+ navigationOnly: true,
697
+ selector: `${trigger}:not(:scope ${content} *)`,
698
+ wrap: true
699
+ });
687
700
  this.#rootElement.setAttribute("data-accordion-initialized", "");
688
701
  }
689
702
  #onTriggerClick = (event) => {
@@ -707,12 +720,7 @@ var Accordion = class _Accordion {
707
720
  return;
708
721
  }
709
722
  event.preventDefault();
710
- switch (key) {
711
- case "Enter":
712
- case " ":
713
- active.click();
714
- return;
715
- }
723
+ active.click();
716
724
  };
717
725
  #onContentBeforeMatch = (event) => {
718
726
  const content = event.currentTarget;
@@ -833,7 +841,7 @@ function waitAnimationFinish(animation) {
833
841
  * Accordion
834
842
  * WAI-ARIA compliant accordion pattern implementation in TypeScript.
835
843
  *
836
- * @version 1.3.1
844
+ * @version 1.3.3
837
845
  * @author Yusuke Kamiyamane
838
846
  * @license MIT
839
847
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -858,7 +866,7 @@ function waitAnimationFinish(animation) {
858
866
  * Lightweight roving tabindex utility with fully focus management.
859
867
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
860
868
  *
861
- * @version 1.3.0
869
+ * @version 1.3.1
862
870
  * @author Yusuke Kamiyamane
863
871
  * @license MIT
864
872
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -883,7 +891,7 @@ function waitAnimationFinish(animation) {
883
891
  * High-precision focus management utility with full composed tree support.
884
892
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
885
893
  *
886
- * @version 4.1.8
894
+ * @version 4.2.0
887
895
  * @author Yusuke Kamiyamane
888
896
  * @license MIT
889
897
  * @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.1
5
+ * @version 1.3.3
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.1
5
+ * @version 1.3.3
6
6
  * @author Yusuke Kamiyamane
7
7
  * @license MIT
8
8
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.js CHANGED
@@ -103,7 +103,12 @@ function getFocusables(container = document.body, options = {}) {
103
103
  console.warn("Invalid container element. Fallback: <body> element.");
104
104
  container = document.body;
105
105
  }
106
- let { composed = false, filter, include } = options;
106
+ let {
107
+ composed = false,
108
+ filter,
109
+ include,
110
+ skipVisibilityCheck = false
111
+ } = options;
107
112
  if (typeof composed !== "boolean") {
108
113
  console.warn("Invalid composed option. Fallback: false.");
109
114
  composed = false;
@@ -124,7 +129,7 @@ function getFocusables(container = document.body, options = {}) {
124
129
  if (composed || include) {
125
130
  let traverse2 = function(node) {
126
131
  if (node instanceof Element) {
127
- if (isFocusable(node) || include?.(node)) {
132
+ if (isFocusable(node, { skipVisibilityCheck }) || include?.(node)) {
128
133
  elements[elements.length] = node;
129
134
  }
130
135
  }
@@ -145,7 +150,7 @@ function getFocusables(container = document.body, options = {}) {
145
150
  if (!(candidate instanceof Element)) {
146
151
  continue;
147
152
  }
148
- if (isFocusable(candidate)) {
153
+ if (isFocusable(candidate, { skipVisibilityCheck })) {
149
154
  elements[elements.length] = candidate;
150
155
  }
151
156
  }
@@ -153,11 +158,16 @@ function getFocusables(container = document.body, options = {}) {
153
158
  const unfiltered = normalizeRadioGroup(sortByTabIndex(elements));
154
159
  return filter ? unfiltered.filter(filter) : unfiltered;
155
160
  }
156
- function isFocusable(element) {
161
+ function isFocusable(element, options = {}) {
157
162
  if (!(element instanceof Element)) {
158
163
  console.warn("Invalid element");
159
164
  return false;
160
165
  }
166
+ let { skipVisibilityCheck = false } = options;
167
+ if (typeof skipVisibilityCheck !== "boolean") {
168
+ console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
169
+ skipVisibilityCheck = false;
170
+ }
161
171
  if (element.hasAttribute("hidden") || isInert(element)) {
162
172
  return false;
163
173
  }
@@ -170,7 +180,7 @@ function isFocusable(element) {
170
180
  if (isDisabledDeep(element)) {
171
181
  return false;
172
182
  }
173
- if (!element.checkVisibility({
183
+ if (!skipVisibilityCheck && !element.checkVisibility({
174
184
  contentVisibilityAuto: true,
175
185
  opacityProperty: true,
176
186
  visibilityProperty: true
@@ -455,7 +465,8 @@ var RovingTabIndex = class {
455
465
  ...this.#getFocusables(),
456
466
  ...getFocusables(this.#container, {
457
467
  composed: true,
458
- filter: this.#selectorFilter
468
+ filter: this.#selectorFilter,
469
+ skipVisibilityCheck: true
459
470
  })
460
471
  ]);
461
472
  for (const focusable of this.#focusables) {
@@ -521,7 +532,8 @@ var RovingTabIndex = class {
521
532
  return getFocusables(this.#container, {
522
533
  composed: true,
523
534
  filter: this.#selectorFilter,
524
- include: (element) => this.#focusables.has(element)
535
+ include: (element) => this.#focusables.has(element),
536
+ skipVisibilityCheck: true
525
537
  });
526
538
  }
527
539
  };
@@ -596,12 +608,6 @@ var Accordion = class _Accordion {
596
608
  this.#bindings.set(trigger2, binding);
597
609
  this.#bindings.set(content2, binding);
598
610
  });
599
- this.#cleanupRovingTabIndex = createRovingTabIndex(this.#rootElement, {
600
- direction: "vertical",
601
- navigationOnly: true,
602
- selector: `${trigger}${NOT_NESTED}`,
603
- wrap: true
604
- });
605
611
  this.#initialize();
606
612
  }
607
613
  open(trigger) {
@@ -629,10 +635,10 @@ var Accordion = class _Accordion {
629
635
  return;
630
636
  }
631
637
  this.#isDestroyed = true;
632
- this.#cleanupRovingTabIndex?.();
633
- this.#cleanupRovingTabIndex = null;
634
638
  this.#eventController?.abort();
635
639
  this.#eventController = null;
640
+ this.#cleanupRovingTabIndex?.();
641
+ this.#cleanupRovingTabIndex = null;
636
642
  !force && await this.#waitAnimationsFinish();
637
643
  this.#contentElements.forEach((content) => {
638
644
  force && this.#bindings.get(content)?.animation?.finish();
@@ -655,33 +661,40 @@ var Accordion = class _Accordion {
655
661
  "style",
656
662
  "tabindex"
657
663
  ]);
658
- this.#triggerElements.forEach((trigger, i) => {
664
+ this.#triggerElements.forEach((trigger2, i) => {
659
665
  const id = Math.random().toString(36).slice(-8);
660
- const content = this.#contentElements[i];
661
- if (!content) {
666
+ const content2 = this.#contentElements[i];
667
+ if (!content2) {
662
668
  return;
663
669
  }
664
- saveAttributes([content], ["aria-labelledby", "id", "role"]);
665
- content.id ||= `accordion-content-${id}`;
666
- addTokenToAttribute(trigger, "aria-controls", content.id);
667
- trigger.setAttribute(
670
+ saveAttributes([content2], ["aria-labelledby", "id", "role"]);
671
+ content2.id ||= `accordion-content-${id}`;
672
+ addTokenToAttribute(trigger2, "aria-controls", content2.id);
673
+ trigger2.setAttribute(
668
674
  "aria-expanded",
669
- trigger.ariaExpanded === "true" ? "true" : "false"
675
+ trigger2.ariaExpanded === "true" ? "true" : "false"
670
676
  );
671
- trigger.id ||= `accordion-trigger-${id}`;
672
- if (!isFocusable2(trigger)) {
673
- trigger.setAttribute("aria-disabled", "true");
674
- trigger.setAttribute("tabindex", "-1");
675
- trigger.style.setProperty("pointer-events", "none");
677
+ trigger2.id ||= `accordion-trigger-${id}`;
678
+ if (!isFocusable2(trigger2)) {
679
+ trigger2.setAttribute("aria-disabled", "true");
680
+ trigger2.setAttribute("tabindex", "-1");
681
+ trigger2.style.setProperty("pointer-events", "none");
676
682
  }
677
- trigger.addEventListener("click", this.#onTriggerClick, { signal });
678
- trigger.addEventListener("keydown", this.#onTriggerKeyDown, { signal });
679
- addTokenToAttribute(content, "aria-labelledby", trigger.id);
680
- content.setAttribute("role", "region");
681
- content.addEventListener("beforematch", this.#onContentBeforeMatch, {
683
+ trigger2.addEventListener("click", this.#onTriggerClick, { signal });
684
+ trigger2.addEventListener("keydown", this.#onTriggerKeyDown, { signal });
685
+ addTokenToAttribute(content2, "aria-labelledby", trigger2.id);
686
+ content2.setAttribute("role", "region");
687
+ content2.addEventListener("beforematch", this.#onContentBeforeMatch, {
682
688
  signal
683
689
  });
684
690
  });
691
+ const { trigger, content } = this.#settings.selector;
692
+ this.#cleanupRovingTabIndex = createRovingTabIndex(this.#rootElement, {
693
+ direction: "vertical",
694
+ navigationOnly: true,
695
+ selector: `${trigger}:not(:scope ${content} *)`,
696
+ wrap: true
697
+ });
685
698
  this.#rootElement.setAttribute("data-accordion-initialized", "");
686
699
  }
687
700
  #onTriggerClick = (event) => {
@@ -705,12 +718,7 @@ var Accordion = class _Accordion {
705
718
  return;
706
719
  }
707
720
  event.preventDefault();
708
- switch (key) {
709
- case "Enter":
710
- case " ":
711
- active.click();
712
- return;
713
- }
721
+ active.click();
714
722
  };
715
723
  #onContentBeforeMatch = (event) => {
716
724
  const content = event.currentTarget;
@@ -831,7 +839,7 @@ function waitAnimationFinish(animation) {
831
839
  * Accordion
832
840
  * WAI-ARIA compliant accordion pattern implementation in TypeScript.
833
841
  *
834
- * @version 1.3.1
842
+ * @version 1.3.3
835
843
  * @author Yusuke Kamiyamane
836
844
  * @license MIT
837
845
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -856,7 +864,7 @@ function waitAnimationFinish(animation) {
856
864
  * Lightweight roving tabindex utility with fully focus management.
857
865
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
858
866
  *
859
- * @version 1.3.0
867
+ * @version 1.3.1
860
868
  * @author Yusuke Kamiyamane
861
869
  * @license MIT
862
870
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -881,7 +889,7 @@ function waitAnimationFinish(animation) {
881
889
  * High-precision focus management utility with full composed tree support.
882
890
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
883
891
  *
884
- * @version 4.1.8
892
+ * @version 4.2.0
885
893
  * @author Yusuke Kamiyamane
886
894
  * @license MIT
887
895
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@y14e/accordion",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "description": "WAI-ARIA compliant accordion pattern implementation in TypeScript",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -44,7 +44,7 @@
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/roving-tabindex": "^1.3.1",
48
48
  "bun-types": "latest",
49
49
  "tsup": "^8.0.0",
50
50
  "typescript": "^5.6.0",