@y14e/disclosure 1.3.10 → 1.3.12
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 +4 -4
- package/dist/index.cjs +75 -95
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +75 -95
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -10,14 +10,14 @@ npm i @y14e/disclosure
|
|
|
10
10
|
|
|
11
11
|
```ts
|
|
12
12
|
// npm
|
|
13
|
-
import Disclosure from '@y14e/disclosure@1.3.
|
|
13
|
+
import Disclosure from '@y14e/disclosure@1.3.12';
|
|
14
14
|
|
|
15
15
|
// CDNs
|
|
16
|
-
import Disclosure from 'https://esm.sh/@y14e/disclosure@1.3.
|
|
16
|
+
import Disclosure from 'https://esm.sh/@y14e/disclosure@1.3.12';
|
|
17
17
|
// or
|
|
18
|
-
import Disclosure from 'https://cdn.jsdelivr.net/npm/@y14e/disclosure@1.3.
|
|
18
|
+
import Disclosure from 'https://cdn.jsdelivr.net/npm/@y14e/disclosure@1.3.12/+esm';
|
|
19
19
|
// or
|
|
20
|
-
import Disclosure from 'https://esm.unpkg.com/@y14e/disclosure@1.3.
|
|
20
|
+
import Disclosure from 'https://esm.unpkg.com/@y14e/disclosure@1.3.12';
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
## Usage
|
package/dist/index.cjs
CHANGED
|
@@ -28,13 +28,13 @@ function saveAttributes(elements, attributes) {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
// node_modules/@y14e/roving-tabindex/dist/index.js
|
|
31
|
-
var
|
|
32
|
-
var
|
|
31
|
+
var DEFAULT_PARSER = (value) => value.split(/\s+/);
|
|
32
|
+
var DEFAULT_SERIALIZER = (tokens) => tokens.join(" ");
|
|
33
33
|
function addTokenToAttribute(element, attribute, token, options = {}) {
|
|
34
34
|
const {
|
|
35
35
|
caseInsensitive = false,
|
|
36
|
-
parse =
|
|
37
|
-
serialize =
|
|
36
|
+
parse = DEFAULT_PARSER,
|
|
37
|
+
serialize = DEFAULT_SERIALIZER
|
|
38
38
|
} = options;
|
|
39
39
|
const value = element.getAttribute(attribute)?.trim();
|
|
40
40
|
const tokens = value ? parse(value).filter(Boolean) : [];
|
|
@@ -44,11 +44,11 @@ function addTokenToAttribute(element, attribute, token, options = {}) {
|
|
|
44
44
|
tokens.push(token);
|
|
45
45
|
element.setAttribute(attribute, serialize(tokens));
|
|
46
46
|
}
|
|
47
|
-
|
|
47
|
+
} else {
|
|
48
|
+
const set = new Set(tokens);
|
|
49
|
+
set.add(token);
|
|
50
|
+
element.setAttribute(attribute, serialize([...set]));
|
|
48
51
|
}
|
|
49
|
-
const set = new Set(tokens);
|
|
50
|
-
set.add(token);
|
|
51
|
-
element.setAttribute(attribute, serialize([...set]));
|
|
52
52
|
}
|
|
53
53
|
var snapshots2 = /* @__PURE__ */ new WeakMap();
|
|
54
54
|
function restoreAttributes2(elements) {
|
|
@@ -115,21 +115,16 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
115
115
|
const elements = [];
|
|
116
116
|
if (composed || include) {
|
|
117
117
|
let traverse2 = function(node) {
|
|
118
|
-
if (node instanceof Element) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
elements[elements.length] = node;
|
|
124
|
-
}
|
|
118
|
+
if (!(node instanceof Element)) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (isFocusable(node, { skipNegativeTabIndexCheck, skipVisibilityCheck }) || include?.(node)) {
|
|
122
|
+
elements[elements.length] = node;
|
|
125
123
|
}
|
|
126
124
|
const children = getComposedChildren(node);
|
|
127
125
|
for (let i = 0, l = children.length; i < l; i++) {
|
|
128
126
|
const child = children[i];
|
|
129
|
-
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
traverse2(child);
|
|
127
|
+
child && traverse2(child);
|
|
133
128
|
}
|
|
134
129
|
};
|
|
135
130
|
traverse2(container);
|
|
@@ -137,10 +132,7 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
137
132
|
const candidates = container.querySelectorAll(FOCUSABLE_SELECTOR);
|
|
138
133
|
for (let i = 0, l = candidates.length; i < l; i++) {
|
|
139
134
|
const candidate = candidates[i];
|
|
140
|
-
if (
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
if (isFocusable(candidate, {
|
|
135
|
+
if (candidate && isFocusable(candidate, {
|
|
144
136
|
skipNegativeTabIndexCheck,
|
|
145
137
|
skipVisibilityCheck
|
|
146
138
|
})) {
|
|
@@ -243,23 +235,19 @@ function normalizeRadioGroup(elements) {
|
|
|
243
235
|
for (const group of map.values()) {
|
|
244
236
|
placeholder.add(group.find((radio) => radio.checked) ?? group[0]);
|
|
245
237
|
}
|
|
246
|
-
return elements.filter(
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
}
|
|
250
|
-
return true;
|
|
251
|
-
});
|
|
238
|
+
return elements.filter(
|
|
239
|
+
(element) => isUngroupedRadio(element) ? placeholder.has(element) : true
|
|
240
|
+
);
|
|
252
241
|
}
|
|
253
242
|
function sortByTabIndex(elements) {
|
|
254
243
|
const ordered = [];
|
|
255
244
|
const natural = [];
|
|
256
245
|
for (let i = 0, l = elements.length; i < l; i++) {
|
|
257
246
|
const element = elements[i];
|
|
258
|
-
if (
|
|
259
|
-
|
|
247
|
+
if (element) {
|
|
248
|
+
const target = getTabIndex(element) > 0 ? ordered : natural;
|
|
249
|
+
target[target.length] = element;
|
|
260
250
|
}
|
|
261
|
-
const target = getTabIndex(element) > 0 ? ordered : natural;
|
|
262
|
-
target[target.length] = element;
|
|
263
251
|
}
|
|
264
252
|
ordered.sort((a, b) => getTabIndex(a) - getTabIndex(b));
|
|
265
253
|
let count = 0;
|
|
@@ -412,15 +400,10 @@ var RovingTabIndex = class {
|
|
|
412
400
|
}
|
|
413
401
|
#onFocusIn = (event) => {
|
|
414
402
|
const { target } = event;
|
|
415
|
-
if (
|
|
416
|
-
|
|
403
|
+
if (target instanceof Element) {
|
|
404
|
+
const isFocusable22 = this.#focusables.has(target);
|
|
405
|
+
this.#options.noMemory && !isFocusable22 ? this.#update(null) : isFocusable22 && this.#update(target);
|
|
417
406
|
}
|
|
418
|
-
const isFocusable22 = this.#focusables.has(target);
|
|
419
|
-
if (this.#options.noMemory && !isFocusable22) {
|
|
420
|
-
this.#update(null);
|
|
421
|
-
return;
|
|
422
|
-
}
|
|
423
|
-
isFocusable22 && this.#update(target);
|
|
424
407
|
};
|
|
425
408
|
#onKeyDown = (event) => {
|
|
426
409
|
if (!event.composedPath().includes(this.#container)) {
|
|
@@ -453,8 +436,7 @@ var RovingTabIndex = class {
|
|
|
453
436
|
}
|
|
454
437
|
event.preventDefault();
|
|
455
438
|
const currentIndex = current.indexOf(active);
|
|
456
|
-
let
|
|
457
|
-
let newIndex = currentIndex;
|
|
439
|
+
let newIndex;
|
|
458
440
|
let target = current;
|
|
459
441
|
switch (key) {
|
|
460
442
|
case "End":
|
|
@@ -464,15 +446,17 @@ var RovingTabIndex = class {
|
|
|
464
446
|
newIndex = 0;
|
|
465
447
|
break;
|
|
466
448
|
case "ArrowLeft":
|
|
467
|
-
case "ArrowUp":
|
|
468
|
-
rawIndex = currentIndex - 1;
|
|
449
|
+
case "ArrowUp": {
|
|
450
|
+
const rawIndex = currentIndex - 1;
|
|
469
451
|
newIndex = wrap ? rawIndex : Math.max(rawIndex, 0);
|
|
470
452
|
break;
|
|
453
|
+
}
|
|
471
454
|
case "ArrowRight":
|
|
472
|
-
case "ArrowDown":
|
|
473
|
-
rawIndex = currentIndex + 1;
|
|
455
|
+
case "ArrowDown": {
|
|
456
|
+
const rawIndex = currentIndex + 1;
|
|
474
457
|
newIndex = wrap ? rawIndex % current.length : Math.min(rawIndex, current.length - 1);
|
|
475
458
|
break;
|
|
459
|
+
}
|
|
476
460
|
default: {
|
|
477
461
|
target = this.#focusablesByFirstChar.get(key.toUpperCase()) ?? [];
|
|
478
462
|
const foundIndex = target.findIndex(
|
|
@@ -487,15 +471,14 @@ var RovingTabIndex = class {
|
|
|
487
471
|
#update(active) {
|
|
488
472
|
const current = new Set(this.#getFocusables());
|
|
489
473
|
for (const focusable of this.#focusables) {
|
|
490
|
-
if (current.has(focusable)) {
|
|
491
|
-
|
|
474
|
+
if (!current.has(focusable)) {
|
|
475
|
+
focusable.isConnected && restoreAttributes2([focusable]);
|
|
476
|
+
this.#focusables.delete(focusable);
|
|
477
|
+
this.#focusablesByFirstChar.forEach((focusables) => {
|
|
478
|
+
const index = focusables.indexOf(focusable);
|
|
479
|
+
index >= 0 && focusables.splice(index, 1);
|
|
480
|
+
});
|
|
492
481
|
}
|
|
493
|
-
focusable.isConnected && restoreAttributes2([focusable]);
|
|
494
|
-
this.#focusables.delete(focusable);
|
|
495
|
-
this.#focusablesByFirstChar.forEach((focusables) => {
|
|
496
|
-
const index = focusables.indexOf(focusable);
|
|
497
|
-
index >= 0 && focusables.splice(index, 1);
|
|
498
|
-
});
|
|
499
482
|
}
|
|
500
483
|
const { navigationOnly, noStart, typeahead } = this.#options;
|
|
501
484
|
for (const focusable of current) {
|
|
@@ -510,11 +493,11 @@ var RovingTabIndex = class {
|
|
|
510
493
|
if (!typeahead) {
|
|
511
494
|
continue;
|
|
512
495
|
}
|
|
496
|
+
const char = focusable.textContent?.trim()?.at(0)?.toUpperCase();
|
|
513
497
|
const value = focusable.ariaKeyShortcuts?.trim();
|
|
514
498
|
const keys = new Set(
|
|
515
499
|
value ? value.split(/\s+/).filter((key) => /^\S$/i.test(key)).map((key) => key.toUpperCase()) : []
|
|
516
500
|
);
|
|
517
|
-
const char = focusable.textContent?.trim()?.at(0)?.toUpperCase();
|
|
518
501
|
if (char) {
|
|
519
502
|
keys.add(char);
|
|
520
503
|
saveAttributes2([focusable], ["aria-keyshortcuts"]);
|
|
@@ -528,18 +511,17 @@ var RovingTabIndex = class {
|
|
|
528
511
|
this.#focusablesByFirstChar.set(key, focusables);
|
|
529
512
|
});
|
|
530
513
|
}
|
|
531
|
-
if (navigationOnly) {
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
514
|
+
if (!navigationOnly) {
|
|
515
|
+
if (active && this.#focusables.has(active)) {
|
|
516
|
+
this.#focusables.forEach((focusable) => {
|
|
517
|
+
focusable.setAttribute("tabindex", focusable === active ? "0" : "-1");
|
|
518
|
+
});
|
|
519
|
+
} else {
|
|
520
|
+
[...this.#focusables].forEach((focusable, i) => {
|
|
521
|
+
focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
|
|
522
|
+
});
|
|
523
|
+
}
|
|
539
524
|
}
|
|
540
|
-
[...this.#focusables].forEach((focusable, i) => {
|
|
541
|
-
focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
|
|
542
|
-
});
|
|
543
525
|
}
|
|
544
526
|
#createSelectorFilter() {
|
|
545
527
|
const { selector } = this.#options;
|
|
@@ -628,13 +610,12 @@ var Disclosure = class _Disclosure {
|
|
|
628
610
|
this.#detailsElements.forEach((details, i) => {
|
|
629
611
|
const summary = this.#summaryElements[i];
|
|
630
612
|
const content = this.#contentElements[i];
|
|
631
|
-
if (
|
|
632
|
-
|
|
613
|
+
if (summary && content) {
|
|
614
|
+
const binding = createBinding(details, summary, content);
|
|
615
|
+
this.#bindings.set(details, binding);
|
|
616
|
+
this.#bindings.set(summary, binding);
|
|
617
|
+
this.#bindings.set(content, binding);
|
|
633
618
|
}
|
|
634
|
-
const binding = createBinding(details, summary, content);
|
|
635
|
-
this.#bindings.set(details, binding);
|
|
636
|
-
this.#bindings.set(summary, binding);
|
|
637
|
-
this.#bindings.set(content, binding);
|
|
638
619
|
});
|
|
639
620
|
this.#initialize();
|
|
640
621
|
}
|
|
@@ -642,11 +623,11 @@ var Disclosure = class _Disclosure {
|
|
|
642
623
|
if (this.#isDestroyed) {
|
|
643
624
|
return;
|
|
644
625
|
}
|
|
645
|
-
if (
|
|
626
|
+
if (details instanceof HTMLDetailsElement && this.#bindings.has(details)) {
|
|
627
|
+
this.#toggle(details, false);
|
|
628
|
+
} else {
|
|
646
629
|
console.warn("Invalid <details> element");
|
|
647
|
-
return;
|
|
648
630
|
}
|
|
649
|
-
this.#toggle(details, false);
|
|
650
631
|
}
|
|
651
632
|
async destroy(force = false) {
|
|
652
633
|
if (this.#isDestroyed) {
|
|
@@ -683,11 +664,11 @@ var Disclosure = class _Disclosure {
|
|
|
683
664
|
if (this.#isDestroyed) {
|
|
684
665
|
return;
|
|
685
666
|
}
|
|
686
|
-
if (
|
|
667
|
+
if (details instanceof HTMLDetailsElement && this.#bindings.has(details)) {
|
|
668
|
+
this.#toggle(details, true);
|
|
669
|
+
} else {
|
|
687
670
|
console.warn("Invalid <details> element");
|
|
688
|
-
return;
|
|
689
671
|
}
|
|
690
|
-
this.#toggle(details, true);
|
|
691
672
|
}
|
|
692
673
|
#initialize() {
|
|
693
674
|
saveAttributes(this.#summaryElements, [
|
|
@@ -732,11 +713,10 @@ var Disclosure = class _Disclosure {
|
|
|
732
713
|
return;
|
|
733
714
|
}
|
|
734
715
|
const binding = this.#bindings.get(summary);
|
|
735
|
-
if (
|
|
736
|
-
|
|
716
|
+
if (binding) {
|
|
717
|
+
const { details } = binding;
|
|
718
|
+
this.#toggle(details, !details.hasAttribute("data-disclosure-open"));
|
|
737
719
|
}
|
|
738
|
-
const { details } = binding;
|
|
739
|
-
this.#toggle(details, !details.hasAttribute("data-disclosure-open"));
|
|
740
720
|
};
|
|
741
721
|
#toggle(details, isOpen) {
|
|
742
722
|
if (details.hasAttribute("data-disclosure-open") === isOpen) {
|
|
@@ -785,11 +765,10 @@ var Disclosure = class _Disclosure {
|
|
|
785
765
|
animation.addEventListener(
|
|
786
766
|
"finish",
|
|
787
767
|
() => {
|
|
788
|
-
if (binding?.animation
|
|
789
|
-
|
|
768
|
+
if (binding?.animation === animation) {
|
|
769
|
+
this.#onAnimationFinish(content);
|
|
770
|
+
cleanup();
|
|
790
771
|
}
|
|
791
|
-
this.#onAnimationFinish(content);
|
|
792
|
-
cleanup();
|
|
793
772
|
},
|
|
794
773
|
{ once: true, signal }
|
|
795
774
|
);
|
|
@@ -835,17 +814,18 @@ function isFocusable2(element) {
|
|
|
835
814
|
function waitAnimationFinish(animation) {
|
|
836
815
|
if (["idle", "finished"].includes(animation.playState)) {
|
|
837
816
|
return Promise.resolve();
|
|
817
|
+
} else {
|
|
818
|
+
return new Promise(
|
|
819
|
+
(resolve) => animation.addEventListener("finish", () => resolve(), { once: true })
|
|
820
|
+
);
|
|
838
821
|
}
|
|
839
|
-
return new Promise(
|
|
840
|
-
(resolve) => animation.addEventListener("finish", () => resolve(), { once: true })
|
|
841
|
-
);
|
|
842
822
|
}
|
|
843
823
|
/**
|
|
844
824
|
* Disclosure
|
|
845
825
|
* WAI-ARIA compliant disclosure pattern implementation in TypeScript.
|
|
846
826
|
* Using the <details> and <summary> element.
|
|
847
827
|
*
|
|
848
|
-
* @version 1.3.
|
|
828
|
+
* @version 1.3.12
|
|
849
829
|
* @author Yusuke Kamiyamane
|
|
850
830
|
* @license MIT
|
|
851
831
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -857,7 +837,7 @@ function waitAnimationFinish(animation) {
|
|
|
857
837
|
(**
|
|
858
838
|
* Attributes Utils
|
|
859
839
|
*
|
|
860
|
-
* @version 1.1.
|
|
840
|
+
* @version 1.1.2
|
|
861
841
|
* @author Yusuke Kamiyamane
|
|
862
842
|
* @license MIT
|
|
863
843
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -870,7 +850,7 @@ function waitAnimationFinish(animation) {
|
|
|
870
850
|
* Lightweight roving tabindex utility with fully focus management.
|
|
871
851
|
* Designed for accessible menus, tabs, toolbars, and composite widgets.
|
|
872
852
|
*
|
|
873
|
-
* @version 3.0.
|
|
853
|
+
* @version 3.0.4
|
|
874
854
|
* @author Yusuke Kamiyamane
|
|
875
855
|
* @license MIT
|
|
876
856
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -882,7 +862,7 @@ function waitAnimationFinish(animation) {
|
|
|
882
862
|
(**
|
|
883
863
|
* Attributes Utils
|
|
884
864
|
*
|
|
885
|
-
* @version 1.1.
|
|
865
|
+
* @version 1.1.2
|
|
886
866
|
* @author Yusuke Kamiyamane
|
|
887
867
|
* @license MIT
|
|
888
868
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -895,7 +875,7 @@ function waitAnimationFinish(animation) {
|
|
|
895
875
|
* High-precision focus management utility with full composed tree support.
|
|
896
876
|
* Handles complex focus rules including tabindex ordering, radio groups, inert.
|
|
897
877
|
*
|
|
898
|
-
* @version 4.3.
|
|
878
|
+
* @version 4.3.3
|
|
899
879
|
* @author Yusuke Kamiyamane
|
|
900
880
|
* @license MIT
|
|
901
881
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
package/dist/index.d.cts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -26,13 +26,13 @@ function saveAttributes(elements, attributes) {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
// node_modules/@y14e/roving-tabindex/dist/index.js
|
|
29
|
-
var
|
|
30
|
-
var
|
|
29
|
+
var DEFAULT_PARSER = (value) => value.split(/\s+/);
|
|
30
|
+
var DEFAULT_SERIALIZER = (tokens) => tokens.join(" ");
|
|
31
31
|
function addTokenToAttribute(element, attribute, token, options = {}) {
|
|
32
32
|
const {
|
|
33
33
|
caseInsensitive = false,
|
|
34
|
-
parse =
|
|
35
|
-
serialize =
|
|
34
|
+
parse = DEFAULT_PARSER,
|
|
35
|
+
serialize = DEFAULT_SERIALIZER
|
|
36
36
|
} = options;
|
|
37
37
|
const value = element.getAttribute(attribute)?.trim();
|
|
38
38
|
const tokens = value ? parse(value).filter(Boolean) : [];
|
|
@@ -42,11 +42,11 @@ function addTokenToAttribute(element, attribute, token, options = {}) {
|
|
|
42
42
|
tokens.push(token);
|
|
43
43
|
element.setAttribute(attribute, serialize(tokens));
|
|
44
44
|
}
|
|
45
|
-
|
|
45
|
+
} else {
|
|
46
|
+
const set = new Set(tokens);
|
|
47
|
+
set.add(token);
|
|
48
|
+
element.setAttribute(attribute, serialize([...set]));
|
|
46
49
|
}
|
|
47
|
-
const set = new Set(tokens);
|
|
48
|
-
set.add(token);
|
|
49
|
-
element.setAttribute(attribute, serialize([...set]));
|
|
50
50
|
}
|
|
51
51
|
var snapshots2 = /* @__PURE__ */ new WeakMap();
|
|
52
52
|
function restoreAttributes2(elements) {
|
|
@@ -113,21 +113,16 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
113
113
|
const elements = [];
|
|
114
114
|
if (composed || include) {
|
|
115
115
|
let traverse2 = function(node) {
|
|
116
|
-
if (node instanceof Element) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
elements[elements.length] = node;
|
|
122
|
-
}
|
|
116
|
+
if (!(node instanceof Element)) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (isFocusable(node, { skipNegativeTabIndexCheck, skipVisibilityCheck }) || include?.(node)) {
|
|
120
|
+
elements[elements.length] = node;
|
|
123
121
|
}
|
|
124
122
|
const children = getComposedChildren(node);
|
|
125
123
|
for (let i = 0, l = children.length; i < l; i++) {
|
|
126
124
|
const child = children[i];
|
|
127
|
-
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
|
-
traverse2(child);
|
|
125
|
+
child && traverse2(child);
|
|
131
126
|
}
|
|
132
127
|
};
|
|
133
128
|
traverse2(container);
|
|
@@ -135,10 +130,7 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
135
130
|
const candidates = container.querySelectorAll(FOCUSABLE_SELECTOR);
|
|
136
131
|
for (let i = 0, l = candidates.length; i < l; i++) {
|
|
137
132
|
const candidate = candidates[i];
|
|
138
|
-
if (
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
if (isFocusable(candidate, {
|
|
133
|
+
if (candidate && isFocusable(candidate, {
|
|
142
134
|
skipNegativeTabIndexCheck,
|
|
143
135
|
skipVisibilityCheck
|
|
144
136
|
})) {
|
|
@@ -241,23 +233,19 @@ function normalizeRadioGroup(elements) {
|
|
|
241
233
|
for (const group of map.values()) {
|
|
242
234
|
placeholder.add(group.find((radio) => radio.checked) ?? group[0]);
|
|
243
235
|
}
|
|
244
|
-
return elements.filter(
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
return true;
|
|
249
|
-
});
|
|
236
|
+
return elements.filter(
|
|
237
|
+
(element) => isUngroupedRadio(element) ? placeholder.has(element) : true
|
|
238
|
+
);
|
|
250
239
|
}
|
|
251
240
|
function sortByTabIndex(elements) {
|
|
252
241
|
const ordered = [];
|
|
253
242
|
const natural = [];
|
|
254
243
|
for (let i = 0, l = elements.length; i < l; i++) {
|
|
255
244
|
const element = elements[i];
|
|
256
|
-
if (
|
|
257
|
-
|
|
245
|
+
if (element) {
|
|
246
|
+
const target = getTabIndex(element) > 0 ? ordered : natural;
|
|
247
|
+
target[target.length] = element;
|
|
258
248
|
}
|
|
259
|
-
const target = getTabIndex(element) > 0 ? ordered : natural;
|
|
260
|
-
target[target.length] = element;
|
|
261
249
|
}
|
|
262
250
|
ordered.sort((a, b) => getTabIndex(a) - getTabIndex(b));
|
|
263
251
|
let count = 0;
|
|
@@ -410,15 +398,10 @@ var RovingTabIndex = class {
|
|
|
410
398
|
}
|
|
411
399
|
#onFocusIn = (event) => {
|
|
412
400
|
const { target } = event;
|
|
413
|
-
if (
|
|
414
|
-
|
|
401
|
+
if (target instanceof Element) {
|
|
402
|
+
const isFocusable22 = this.#focusables.has(target);
|
|
403
|
+
this.#options.noMemory && !isFocusable22 ? this.#update(null) : isFocusable22 && this.#update(target);
|
|
415
404
|
}
|
|
416
|
-
const isFocusable22 = this.#focusables.has(target);
|
|
417
|
-
if (this.#options.noMemory && !isFocusable22) {
|
|
418
|
-
this.#update(null);
|
|
419
|
-
return;
|
|
420
|
-
}
|
|
421
|
-
isFocusable22 && this.#update(target);
|
|
422
405
|
};
|
|
423
406
|
#onKeyDown = (event) => {
|
|
424
407
|
if (!event.composedPath().includes(this.#container)) {
|
|
@@ -451,8 +434,7 @@ var RovingTabIndex = class {
|
|
|
451
434
|
}
|
|
452
435
|
event.preventDefault();
|
|
453
436
|
const currentIndex = current.indexOf(active);
|
|
454
|
-
let
|
|
455
|
-
let newIndex = currentIndex;
|
|
437
|
+
let newIndex;
|
|
456
438
|
let target = current;
|
|
457
439
|
switch (key) {
|
|
458
440
|
case "End":
|
|
@@ -462,15 +444,17 @@ var RovingTabIndex = class {
|
|
|
462
444
|
newIndex = 0;
|
|
463
445
|
break;
|
|
464
446
|
case "ArrowLeft":
|
|
465
|
-
case "ArrowUp":
|
|
466
|
-
rawIndex = currentIndex - 1;
|
|
447
|
+
case "ArrowUp": {
|
|
448
|
+
const rawIndex = currentIndex - 1;
|
|
467
449
|
newIndex = wrap ? rawIndex : Math.max(rawIndex, 0);
|
|
468
450
|
break;
|
|
451
|
+
}
|
|
469
452
|
case "ArrowRight":
|
|
470
|
-
case "ArrowDown":
|
|
471
|
-
rawIndex = currentIndex + 1;
|
|
453
|
+
case "ArrowDown": {
|
|
454
|
+
const rawIndex = currentIndex + 1;
|
|
472
455
|
newIndex = wrap ? rawIndex % current.length : Math.min(rawIndex, current.length - 1);
|
|
473
456
|
break;
|
|
457
|
+
}
|
|
474
458
|
default: {
|
|
475
459
|
target = this.#focusablesByFirstChar.get(key.toUpperCase()) ?? [];
|
|
476
460
|
const foundIndex = target.findIndex(
|
|
@@ -485,15 +469,14 @@ var RovingTabIndex = class {
|
|
|
485
469
|
#update(active) {
|
|
486
470
|
const current = new Set(this.#getFocusables());
|
|
487
471
|
for (const focusable of this.#focusables) {
|
|
488
|
-
if (current.has(focusable)) {
|
|
489
|
-
|
|
472
|
+
if (!current.has(focusable)) {
|
|
473
|
+
focusable.isConnected && restoreAttributes2([focusable]);
|
|
474
|
+
this.#focusables.delete(focusable);
|
|
475
|
+
this.#focusablesByFirstChar.forEach((focusables) => {
|
|
476
|
+
const index = focusables.indexOf(focusable);
|
|
477
|
+
index >= 0 && focusables.splice(index, 1);
|
|
478
|
+
});
|
|
490
479
|
}
|
|
491
|
-
focusable.isConnected && restoreAttributes2([focusable]);
|
|
492
|
-
this.#focusables.delete(focusable);
|
|
493
|
-
this.#focusablesByFirstChar.forEach((focusables) => {
|
|
494
|
-
const index = focusables.indexOf(focusable);
|
|
495
|
-
index >= 0 && focusables.splice(index, 1);
|
|
496
|
-
});
|
|
497
480
|
}
|
|
498
481
|
const { navigationOnly, noStart, typeahead } = this.#options;
|
|
499
482
|
for (const focusable of current) {
|
|
@@ -508,11 +491,11 @@ var RovingTabIndex = class {
|
|
|
508
491
|
if (!typeahead) {
|
|
509
492
|
continue;
|
|
510
493
|
}
|
|
494
|
+
const char = focusable.textContent?.trim()?.at(0)?.toUpperCase();
|
|
511
495
|
const value = focusable.ariaKeyShortcuts?.trim();
|
|
512
496
|
const keys = new Set(
|
|
513
497
|
value ? value.split(/\s+/).filter((key) => /^\S$/i.test(key)).map((key) => key.toUpperCase()) : []
|
|
514
498
|
);
|
|
515
|
-
const char = focusable.textContent?.trim()?.at(0)?.toUpperCase();
|
|
516
499
|
if (char) {
|
|
517
500
|
keys.add(char);
|
|
518
501
|
saveAttributes2([focusable], ["aria-keyshortcuts"]);
|
|
@@ -526,18 +509,17 @@ var RovingTabIndex = class {
|
|
|
526
509
|
this.#focusablesByFirstChar.set(key, focusables);
|
|
527
510
|
});
|
|
528
511
|
}
|
|
529
|
-
if (navigationOnly) {
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
512
|
+
if (!navigationOnly) {
|
|
513
|
+
if (active && this.#focusables.has(active)) {
|
|
514
|
+
this.#focusables.forEach((focusable) => {
|
|
515
|
+
focusable.setAttribute("tabindex", focusable === active ? "0" : "-1");
|
|
516
|
+
});
|
|
517
|
+
} else {
|
|
518
|
+
[...this.#focusables].forEach((focusable, i) => {
|
|
519
|
+
focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
|
|
520
|
+
});
|
|
521
|
+
}
|
|
537
522
|
}
|
|
538
|
-
[...this.#focusables].forEach((focusable, i) => {
|
|
539
|
-
focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
|
|
540
|
-
});
|
|
541
523
|
}
|
|
542
524
|
#createSelectorFilter() {
|
|
543
525
|
const { selector } = this.#options;
|
|
@@ -626,13 +608,12 @@ var Disclosure = class _Disclosure {
|
|
|
626
608
|
this.#detailsElements.forEach((details, i) => {
|
|
627
609
|
const summary = this.#summaryElements[i];
|
|
628
610
|
const content = this.#contentElements[i];
|
|
629
|
-
if (
|
|
630
|
-
|
|
611
|
+
if (summary && content) {
|
|
612
|
+
const binding = createBinding(details, summary, content);
|
|
613
|
+
this.#bindings.set(details, binding);
|
|
614
|
+
this.#bindings.set(summary, binding);
|
|
615
|
+
this.#bindings.set(content, binding);
|
|
631
616
|
}
|
|
632
|
-
const binding = createBinding(details, summary, content);
|
|
633
|
-
this.#bindings.set(details, binding);
|
|
634
|
-
this.#bindings.set(summary, binding);
|
|
635
|
-
this.#bindings.set(content, binding);
|
|
636
617
|
});
|
|
637
618
|
this.#initialize();
|
|
638
619
|
}
|
|
@@ -640,11 +621,11 @@ var Disclosure = class _Disclosure {
|
|
|
640
621
|
if (this.#isDestroyed) {
|
|
641
622
|
return;
|
|
642
623
|
}
|
|
643
|
-
if (
|
|
624
|
+
if (details instanceof HTMLDetailsElement && this.#bindings.has(details)) {
|
|
625
|
+
this.#toggle(details, false);
|
|
626
|
+
} else {
|
|
644
627
|
console.warn("Invalid <details> element");
|
|
645
|
-
return;
|
|
646
628
|
}
|
|
647
|
-
this.#toggle(details, false);
|
|
648
629
|
}
|
|
649
630
|
async destroy(force = false) {
|
|
650
631
|
if (this.#isDestroyed) {
|
|
@@ -681,11 +662,11 @@ var Disclosure = class _Disclosure {
|
|
|
681
662
|
if (this.#isDestroyed) {
|
|
682
663
|
return;
|
|
683
664
|
}
|
|
684
|
-
if (
|
|
665
|
+
if (details instanceof HTMLDetailsElement && this.#bindings.has(details)) {
|
|
666
|
+
this.#toggle(details, true);
|
|
667
|
+
} else {
|
|
685
668
|
console.warn("Invalid <details> element");
|
|
686
|
-
return;
|
|
687
669
|
}
|
|
688
|
-
this.#toggle(details, true);
|
|
689
670
|
}
|
|
690
671
|
#initialize() {
|
|
691
672
|
saveAttributes(this.#summaryElements, [
|
|
@@ -730,11 +711,10 @@ var Disclosure = class _Disclosure {
|
|
|
730
711
|
return;
|
|
731
712
|
}
|
|
732
713
|
const binding = this.#bindings.get(summary);
|
|
733
|
-
if (
|
|
734
|
-
|
|
714
|
+
if (binding) {
|
|
715
|
+
const { details } = binding;
|
|
716
|
+
this.#toggle(details, !details.hasAttribute("data-disclosure-open"));
|
|
735
717
|
}
|
|
736
|
-
const { details } = binding;
|
|
737
|
-
this.#toggle(details, !details.hasAttribute("data-disclosure-open"));
|
|
738
718
|
};
|
|
739
719
|
#toggle(details, isOpen) {
|
|
740
720
|
if (details.hasAttribute("data-disclosure-open") === isOpen) {
|
|
@@ -783,11 +763,10 @@ var Disclosure = class _Disclosure {
|
|
|
783
763
|
animation.addEventListener(
|
|
784
764
|
"finish",
|
|
785
765
|
() => {
|
|
786
|
-
if (binding?.animation
|
|
787
|
-
|
|
766
|
+
if (binding?.animation === animation) {
|
|
767
|
+
this.#onAnimationFinish(content);
|
|
768
|
+
cleanup();
|
|
788
769
|
}
|
|
789
|
-
this.#onAnimationFinish(content);
|
|
790
|
-
cleanup();
|
|
791
770
|
},
|
|
792
771
|
{ once: true, signal }
|
|
793
772
|
);
|
|
@@ -833,17 +812,18 @@ function isFocusable2(element) {
|
|
|
833
812
|
function waitAnimationFinish(animation) {
|
|
834
813
|
if (["idle", "finished"].includes(animation.playState)) {
|
|
835
814
|
return Promise.resolve();
|
|
815
|
+
} else {
|
|
816
|
+
return new Promise(
|
|
817
|
+
(resolve) => animation.addEventListener("finish", () => resolve(), { once: true })
|
|
818
|
+
);
|
|
836
819
|
}
|
|
837
|
-
return new Promise(
|
|
838
|
-
(resolve) => animation.addEventListener("finish", () => resolve(), { once: true })
|
|
839
|
-
);
|
|
840
820
|
}
|
|
841
821
|
/**
|
|
842
822
|
* Disclosure
|
|
843
823
|
* WAI-ARIA compliant disclosure pattern implementation in TypeScript.
|
|
844
824
|
* Using the <details> and <summary> element.
|
|
845
825
|
*
|
|
846
|
-
* @version 1.3.
|
|
826
|
+
* @version 1.3.12
|
|
847
827
|
* @author Yusuke Kamiyamane
|
|
848
828
|
* @license MIT
|
|
849
829
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -855,7 +835,7 @@ function waitAnimationFinish(animation) {
|
|
|
855
835
|
(**
|
|
856
836
|
* Attributes Utils
|
|
857
837
|
*
|
|
858
|
-
* @version 1.1.
|
|
838
|
+
* @version 1.1.2
|
|
859
839
|
* @author Yusuke Kamiyamane
|
|
860
840
|
* @license MIT
|
|
861
841
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -868,7 +848,7 @@ function waitAnimationFinish(animation) {
|
|
|
868
848
|
* Lightweight roving tabindex utility with fully focus management.
|
|
869
849
|
* Designed for accessible menus, tabs, toolbars, and composite widgets.
|
|
870
850
|
*
|
|
871
|
-
* @version 3.0.
|
|
851
|
+
* @version 3.0.4
|
|
872
852
|
* @author Yusuke Kamiyamane
|
|
873
853
|
* @license MIT
|
|
874
854
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -880,7 +860,7 @@ function waitAnimationFinish(animation) {
|
|
|
880
860
|
(**
|
|
881
861
|
* Attributes Utils
|
|
882
862
|
*
|
|
883
|
-
* @version 1.1.
|
|
863
|
+
* @version 1.1.2
|
|
884
864
|
* @author Yusuke Kamiyamane
|
|
885
865
|
* @license MIT
|
|
886
866
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -893,7 +873,7 @@ function waitAnimationFinish(animation) {
|
|
|
893
873
|
* High-precision focus management utility with full composed tree support.
|
|
894
874
|
* Handles complex focus rules including tabindex ordering, radio groups, inert.
|
|
895
875
|
*
|
|
896
|
-
* @version 4.3.
|
|
876
|
+
* @version 4.3.3
|
|
897
877
|
* @author Yusuke Kamiyamane
|
|
898
878
|
* @license MIT
|
|
899
879
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@y14e/disclosure",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.12",
|
|
4
4
|
"description": "WAI-ARIA compliant disclosure pattern implementation in TypeScript",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
},
|
|
44
44
|
"homepage": "https://github.com/y14e/disclosure#readme",
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@y14e/attributes-utils": "^1.1.
|
|
47
|
-
"@y14e/roving-tabindex": "^3.0.
|
|
46
|
+
"@y14e/attributes-utils": "^1.1.2",
|
|
47
|
+
"@y14e/roving-tabindex": "^3.0.4",
|
|
48
48
|
"bun-types": "latest",
|
|
49
49
|
"tsup": "^8.0.0",
|
|
50
50
|
"typescript": "^5.6.0",
|