@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 +50 -42
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +50 -42
- package/package.json +2 -2
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 {
|
|
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((
|
|
666
|
+
this.#triggerElements.forEach((trigger2, i) => {
|
|
661
667
|
const id = Math.random().toString(36).slice(-8);
|
|
662
|
-
const
|
|
663
|
-
if (!
|
|
668
|
+
const content2 = this.#contentElements[i];
|
|
669
|
+
if (!content2) {
|
|
664
670
|
return;
|
|
665
671
|
}
|
|
666
|
-
saveAttributes([
|
|
667
|
-
|
|
668
|
-
addTokenToAttribute(
|
|
669
|
-
|
|
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
|
-
|
|
677
|
+
trigger2.ariaExpanded === "true" ? "true" : "false"
|
|
672
678
|
);
|
|
673
|
-
|
|
674
|
-
if (!isFocusable2(
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
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
|
-
|
|
680
|
-
|
|
681
|
-
addTokenToAttribute(
|
|
682
|
-
|
|
683
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
package/dist/index.d.ts
CHANGED
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 {
|
|
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((
|
|
664
|
+
this.#triggerElements.forEach((trigger2, i) => {
|
|
659
665
|
const id = Math.random().toString(36).slice(-8);
|
|
660
|
-
const
|
|
661
|
-
if (!
|
|
666
|
+
const content2 = this.#contentElements[i];
|
|
667
|
+
if (!content2) {
|
|
662
668
|
return;
|
|
663
669
|
}
|
|
664
|
-
saveAttributes([
|
|
665
|
-
|
|
666
|
-
addTokenToAttribute(
|
|
667
|
-
|
|
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
|
-
|
|
675
|
+
trigger2.ariaExpanded === "true" ? "true" : "false"
|
|
670
676
|
);
|
|
671
|
-
|
|
672
|
-
if (!isFocusable2(
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
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
|
-
|
|
678
|
-
|
|
679
|
-
addTokenToAttribute(
|
|
680
|
-
|
|
681
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
47
|
+
"@y14e/roving-tabindex": "^1.3.1",
|
|
48
48
|
"bun-types": "latest",
|
|
49
49
|
"tsup": "^8.0.0",
|
|
50
50
|
"typescript": "^5.6.0",
|