@y14e/disclosure-css 1.3.13 → 2.0.1
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 +32 -8
- package/dist/index.bundle.cjs +65 -35
- package/dist/index.bundle.js +65 -35
- package/dist/index.cjs +45 -12
- package/dist/index.d.cts +9 -5
- package/dist/index.d.ts +9 -5
- package/dist/index.js +45 -12
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -13,27 +13,51 @@ npm i @y14e/disclosure-css
|
|
|
13
13
|
import Disclosure from '@y14e/disclosure-css';
|
|
14
14
|
|
|
15
15
|
// CDNs
|
|
16
|
-
import Disclosure from 'https://esm.sh/@y14e/disclosure-css@
|
|
16
|
+
import Disclosure from 'https://esm.sh/@y14e/disclosure-css@2.0.0';
|
|
17
17
|
// or
|
|
18
|
-
import Disclosure from 'https://cdn.jsdelivr.net/npm/@y14e/disclosure-css@
|
|
18
|
+
import Disclosure from 'https://cdn.jsdelivr.net/npm/@y14e/disclosure-css@2.0.0/+esm';
|
|
19
19
|
// or
|
|
20
|
-
import Disclosure from 'https://esm.unpkg.com/@y14e/disclosure-css@
|
|
20
|
+
import Disclosure from 'https://esm.unpkg.com/@y14e/disclosure-css@2.0.0';
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
## Usage
|
|
24
24
|
|
|
25
25
|
```ts
|
|
26
|
-
new Disclosure(root);
|
|
26
|
+
new Disclosure(root, options);
|
|
27
27
|
// => Disclosure
|
|
28
|
+
//
|
|
29
|
+
// root: HTMLElement
|
|
30
|
+
// options (optional): DisclosureOptions
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 🪄 Options
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
interface DisclosureOptions {
|
|
37
|
+
collapsible?: boolean; // default: true
|
|
38
|
+
}
|
|
39
|
+
```
|
|
28
40
|
|
|
41
|
+
### ⚙️ Customize defaults
|
|
42
|
+
|
|
43
|
+
Override the global default settings applied to all disclosure instances.
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import Disclosure from '@y14e/disclosure-css';
|
|
47
|
+
|
|
48
|
+
Disclosure.defaults = {
|
|
49
|
+
collapsible: false,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
new Disclosure(root);
|
|
29
53
|
```
|
|
30
54
|
|
|
31
55
|
## 📦 APIs
|
|
32
56
|
|
|
33
|
-
### `
|
|
57
|
+
### `collapse`
|
|
34
58
|
|
|
35
59
|
```ts
|
|
36
|
-
disclosure.
|
|
60
|
+
disclosure.collapse(details);
|
|
37
61
|
// => void
|
|
38
62
|
//
|
|
39
63
|
// details: HTMLDetailsElement
|
|
@@ -48,10 +72,10 @@ disclosure.destroy();
|
|
|
48
72
|
// => void
|
|
49
73
|
```
|
|
50
74
|
|
|
51
|
-
### `
|
|
75
|
+
### `expand`
|
|
52
76
|
|
|
53
77
|
```ts
|
|
54
|
-
disclosure.
|
|
78
|
+
disclosure.expand(details);
|
|
55
79
|
// => void
|
|
56
80
|
//
|
|
57
81
|
// details: HTMLDetailsElement
|
package/dist/index.bundle.cjs
CHANGED
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
var DEFAULT_PARSER = (value) => value.split(/\s+/);
|
|
5
5
|
var DEFAULT_SERIALIZER = (tokens) => tokens.join(" ");
|
|
6
6
|
function addTokenToAttribute(element, attribute, token, options = {}) {
|
|
7
|
+
const value = element.getAttribute(attribute)?.trim();
|
|
7
8
|
const {
|
|
8
9
|
caseInsensitive = false,
|
|
9
10
|
parse = DEFAULT_PARSER,
|
|
10
11
|
serialize = DEFAULT_SERIALIZER
|
|
11
12
|
} = options;
|
|
12
|
-
const value = element.getAttribute(attribute)?.trim();
|
|
13
13
|
const tokens = value ? parse(value).filter(Boolean) : [];
|
|
14
14
|
if (caseInsensitive) {
|
|
15
15
|
const lower = token.toLowerCase();
|
|
@@ -353,13 +353,7 @@ var RovingTabIndex = class _RovingTabIndex {
|
|
|
353
353
|
console.warn("Invalid wrap option. Fallback: false.");
|
|
354
354
|
wrap = false;
|
|
355
355
|
}
|
|
356
|
-
this.#settings = {
|
|
357
|
-
navigationOnly,
|
|
358
|
-
noMemory,
|
|
359
|
-
noStart,
|
|
360
|
-
typeahead,
|
|
361
|
-
wrap
|
|
362
|
-
};
|
|
356
|
+
this.#settings = { navigationOnly, noMemory, noStart, typeahead, wrap };
|
|
363
357
|
direction && Object.assign(this.#settings, { direction });
|
|
364
358
|
selector && Object.assign(this.#settings, { selector });
|
|
365
359
|
this.#selectorFilter = this.#createSelectorFilter();
|
|
@@ -378,29 +372,32 @@ var RovingTabIndex = class _RovingTabIndex {
|
|
|
378
372
|
}
|
|
379
373
|
#initialize() {
|
|
380
374
|
this.#update(getActiveElement());
|
|
381
|
-
if (!(this.#container instanceof HTMLElement)) {
|
|
382
|
-
return;
|
|
383
|
-
}
|
|
384
375
|
this.#controller = new AbortController();
|
|
385
376
|
const { signal } = this.#controller;
|
|
386
|
-
|
|
387
|
-
this.#settings.noMemory &&
|
|
377
|
+
this.#container.addEventListener("focusin", this.#onFocusIn, { signal });
|
|
378
|
+
this.#settings.noMemory && this.#container.addEventListener("focusout", this.#onFocusOut, {
|
|
379
|
+
signal
|
|
380
|
+
});
|
|
388
381
|
this.#container.addEventListener("keydown", this.#onKeyDown, { signal });
|
|
389
382
|
}
|
|
390
383
|
#onFocusIn = (event) => {
|
|
391
|
-
|
|
392
|
-
if (!(target instanceof Element)) {
|
|
384
|
+
if (!(event instanceof FocusEvent)) {
|
|
393
385
|
return;
|
|
394
386
|
}
|
|
395
|
-
const
|
|
396
|
-
|
|
387
|
+
const { target } = event;
|
|
388
|
+
target instanceof Element && this.#update(target);
|
|
397
389
|
};
|
|
398
390
|
#onFocusOut = (event) => {
|
|
399
|
-
if (!event
|
|
400
|
-
|
|
391
|
+
if (!(event instanceof FocusEvent)) {
|
|
392
|
+
return;
|
|
401
393
|
}
|
|
394
|
+
const target = event.relatedTarget;
|
|
395
|
+
(!(target instanceof Element) || !this.#focusables.has(target)) && this.#update();
|
|
402
396
|
};
|
|
403
397
|
#onKeyDown = (event) => {
|
|
398
|
+
if (!(event instanceof KeyboardEvent)) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
404
401
|
const { key, altKey, ctrlKey, metaKey, shiftKey } = event;
|
|
405
402
|
if (altKey || ctrlKey || metaKey || shiftKey) {
|
|
406
403
|
return;
|
|
@@ -419,7 +416,7 @@ var RovingTabIndex = class _RovingTabIndex {
|
|
|
419
416
|
}
|
|
420
417
|
}
|
|
421
418
|
const active = getActiveElement();
|
|
422
|
-
if (!(active instanceof
|
|
419
|
+
if (!(active instanceof Element)) {
|
|
423
420
|
return;
|
|
424
421
|
}
|
|
425
422
|
const current = this.#getFocusables();
|
|
@@ -534,15 +531,19 @@ var RovingTabIndex = class _RovingTabIndex {
|
|
|
534
531
|
};
|
|
535
532
|
|
|
536
533
|
// src/index.ts
|
|
537
|
-
var Disclosure = class {
|
|
534
|
+
var Disclosure = class _Disclosure {
|
|
535
|
+
static defaults = {};
|
|
538
536
|
#rootElement;
|
|
537
|
+
#defaults = { collapsible: true };
|
|
538
|
+
#settings;
|
|
539
539
|
#detailsElements;
|
|
540
540
|
#summaryElements;
|
|
541
541
|
#contentElements;
|
|
542
542
|
#bindings = /* @__PURE__ */ new WeakMap();
|
|
543
|
+
#controller = null;
|
|
543
544
|
#cleanupRovingTabIndex = null;
|
|
544
545
|
#isDestroyed = false;
|
|
545
|
-
constructor(root) {
|
|
546
|
+
constructor(root, options = {}) {
|
|
546
547
|
if (!(root instanceof HTMLElement)) {
|
|
547
548
|
throw new TypeError("Invalid root element");
|
|
548
549
|
}
|
|
@@ -551,6 +552,8 @@ var Disclosure = class {
|
|
|
551
552
|
return;
|
|
552
553
|
}
|
|
553
554
|
this.#rootElement = root;
|
|
555
|
+
this.#defaults = this.#mergeOptions(this.#defaults, _Disclosure.defaults);
|
|
556
|
+
this.#settings = this.#mergeOptions(this.#defaults, options);
|
|
554
557
|
const NOT_NESTED = ":not(:scope summary + * *)";
|
|
555
558
|
this.#detailsElements = [
|
|
556
559
|
...this.#rootElement.querySelectorAll(
|
|
@@ -592,7 +595,7 @@ var Disclosure = class {
|
|
|
592
595
|
});
|
|
593
596
|
this.#initialize();
|
|
594
597
|
}
|
|
595
|
-
|
|
598
|
+
collapse(details) {
|
|
596
599
|
if (this.#isDestroyed) {
|
|
597
600
|
return;
|
|
598
601
|
}
|
|
@@ -607,6 +610,8 @@ var Disclosure = class {
|
|
|
607
610
|
return;
|
|
608
611
|
}
|
|
609
612
|
this.#isDestroyed = true;
|
|
613
|
+
this.#controller?.abort();
|
|
614
|
+
this.#controller = null;
|
|
610
615
|
this.#cleanupRovingTabIndex?.();
|
|
611
616
|
this.#cleanupRovingTabIndex = null;
|
|
612
617
|
this.#detailsElements.length = 0;
|
|
@@ -615,7 +620,7 @@ var Disclosure = class {
|
|
|
615
620
|
this.#contentElements.length = 0;
|
|
616
621
|
this.#rootElement.removeAttribute("data-disclosure-initialized");
|
|
617
622
|
}
|
|
618
|
-
|
|
623
|
+
expand(details) {
|
|
619
624
|
if (this.#isDestroyed) {
|
|
620
625
|
return;
|
|
621
626
|
}
|
|
@@ -631,10 +636,15 @@ var Disclosure = class {
|
|
|
631
636
|
"style",
|
|
632
637
|
"tabindex"
|
|
633
638
|
]);
|
|
634
|
-
this.#
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
summary
|
|
639
|
+
this.#controller = new AbortController();
|
|
640
|
+
const { signal } = this.#controller;
|
|
641
|
+
this.#summaryElements.forEach((summary) => {
|
|
642
|
+
if (!this.#isFocusable(summary)) {
|
|
643
|
+
summary.setAttribute("aria-disabled", "true");
|
|
644
|
+
summary.setAttribute("tabindex", "-1");
|
|
645
|
+
summary.style.setProperty("pointer-events", "none");
|
|
646
|
+
}
|
|
647
|
+
summary.addEventListener("click", this.#onSummaryClick, { signal });
|
|
638
648
|
});
|
|
639
649
|
this.#cleanupRovingTabIndex = createRovingTabIndex(this.#rootElement, {
|
|
640
650
|
direction: "vertical",
|
|
@@ -644,10 +654,27 @@ var Disclosure = class {
|
|
|
644
654
|
});
|
|
645
655
|
this.#rootElement.setAttribute("data-disclosure-initialized", "");
|
|
646
656
|
}
|
|
647
|
-
#
|
|
648
|
-
|
|
649
|
-
|
|
657
|
+
#onSummaryClick = (event) => {
|
|
658
|
+
event.preventDefault();
|
|
659
|
+
const summary = event.currentTarget;
|
|
660
|
+
if (!(summary instanceof HTMLElement)) {
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
const binding = this.#bindings.get(summary);
|
|
664
|
+
if (!binding) {
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
const { details } = binding;
|
|
668
|
+
this.#toggle(details, !details.open);
|
|
669
|
+
};
|
|
670
|
+
#toggle(details, isExpand) {
|
|
671
|
+
if (details.open === isExpand) {
|
|
672
|
+
return;
|
|
650
673
|
}
|
|
674
|
+
if (!isExpand && !this.#settings.collapsible && this.#detailsElements.filter((details2) => details2.open).length <= 1) {
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
details.open = isExpand;
|
|
651
678
|
}
|
|
652
679
|
#createBinding(details, summary, content) {
|
|
653
680
|
return { details, summary, content };
|
|
@@ -655,13 +682,16 @@ var Disclosure = class {
|
|
|
655
682
|
#isFocusable(element) {
|
|
656
683
|
return element.tabIndex >= 0;
|
|
657
684
|
}
|
|
685
|
+
#mergeOptions(target, source) {
|
|
686
|
+
return { ...target, ...source };
|
|
687
|
+
}
|
|
658
688
|
};
|
|
659
689
|
/**
|
|
660
690
|
* Disclosure (CSS Animation)
|
|
661
691
|
* WAI-ARIA compliant disclosure pattern implementation in TypeScript.
|
|
662
692
|
* Using the <details> and <summary> element.
|
|
663
693
|
*
|
|
664
|
-
* @version
|
|
694
|
+
* @version 2.0.1
|
|
665
695
|
* @author Yusuke Kamiyamane
|
|
666
696
|
* @license MIT
|
|
667
697
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -673,7 +703,7 @@ var Disclosure = class {
|
|
|
673
703
|
(**
|
|
674
704
|
* Attributes Utils
|
|
675
705
|
*
|
|
676
|
-
* @version 1.1.
|
|
706
|
+
* @version 1.1.3
|
|
677
707
|
* @author Yusuke Kamiyamane
|
|
678
708
|
* @license MIT
|
|
679
709
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -686,7 +716,7 @@ power-focusable/dist/index.js:
|
|
|
686
716
|
* High-precision focus management utility with full composed tree support.
|
|
687
717
|
* Handles complex focus rules including tabindex ordering, radio groups, inert.
|
|
688
718
|
*
|
|
689
|
-
* @version 4.3.
|
|
719
|
+
* @version 4.3.8
|
|
690
720
|
* @author Yusuke Kamiyamane
|
|
691
721
|
* @license MIT
|
|
692
722
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -699,7 +729,7 @@ power-focusable/dist/index.js:
|
|
|
699
729
|
* Lightweight roving tabindex utility with fully focus management.
|
|
700
730
|
* Designed for accessible menus, tabs, toolbars, and composite widgets.
|
|
701
731
|
*
|
|
702
|
-
* @version 3.1.
|
|
732
|
+
* @version 3.1.6
|
|
703
733
|
* @author Yusuke Kamiyamane
|
|
704
734
|
* @license MIT
|
|
705
735
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
package/dist/index.bundle.js
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
var DEFAULT_PARSER = (value) => value.split(/\s+/);
|
|
3
3
|
var DEFAULT_SERIALIZER = (tokens) => tokens.join(" ");
|
|
4
4
|
function addTokenToAttribute(element, attribute, token, options = {}) {
|
|
5
|
+
const value = element.getAttribute(attribute)?.trim();
|
|
5
6
|
const {
|
|
6
7
|
caseInsensitive = false,
|
|
7
8
|
parse = DEFAULT_PARSER,
|
|
8
9
|
serialize = DEFAULT_SERIALIZER
|
|
9
10
|
} = options;
|
|
10
|
-
const value = element.getAttribute(attribute)?.trim();
|
|
11
11
|
const tokens = value ? parse(value).filter(Boolean) : [];
|
|
12
12
|
if (caseInsensitive) {
|
|
13
13
|
const lower = token.toLowerCase();
|
|
@@ -351,13 +351,7 @@ var RovingTabIndex = class _RovingTabIndex {
|
|
|
351
351
|
console.warn("Invalid wrap option. Fallback: false.");
|
|
352
352
|
wrap = false;
|
|
353
353
|
}
|
|
354
|
-
this.#settings = {
|
|
355
|
-
navigationOnly,
|
|
356
|
-
noMemory,
|
|
357
|
-
noStart,
|
|
358
|
-
typeahead,
|
|
359
|
-
wrap
|
|
360
|
-
};
|
|
354
|
+
this.#settings = { navigationOnly, noMemory, noStart, typeahead, wrap };
|
|
361
355
|
direction && Object.assign(this.#settings, { direction });
|
|
362
356
|
selector && Object.assign(this.#settings, { selector });
|
|
363
357
|
this.#selectorFilter = this.#createSelectorFilter();
|
|
@@ -376,29 +370,32 @@ var RovingTabIndex = class _RovingTabIndex {
|
|
|
376
370
|
}
|
|
377
371
|
#initialize() {
|
|
378
372
|
this.#update(getActiveElement());
|
|
379
|
-
if (!(this.#container instanceof HTMLElement)) {
|
|
380
|
-
return;
|
|
381
|
-
}
|
|
382
373
|
this.#controller = new AbortController();
|
|
383
374
|
const { signal } = this.#controller;
|
|
384
|
-
|
|
385
|
-
this.#settings.noMemory &&
|
|
375
|
+
this.#container.addEventListener("focusin", this.#onFocusIn, { signal });
|
|
376
|
+
this.#settings.noMemory && this.#container.addEventListener("focusout", this.#onFocusOut, {
|
|
377
|
+
signal
|
|
378
|
+
});
|
|
386
379
|
this.#container.addEventListener("keydown", this.#onKeyDown, { signal });
|
|
387
380
|
}
|
|
388
381
|
#onFocusIn = (event) => {
|
|
389
|
-
|
|
390
|
-
if (!(target instanceof Element)) {
|
|
382
|
+
if (!(event instanceof FocusEvent)) {
|
|
391
383
|
return;
|
|
392
384
|
}
|
|
393
|
-
const
|
|
394
|
-
|
|
385
|
+
const { target } = event;
|
|
386
|
+
target instanceof Element && this.#update(target);
|
|
395
387
|
};
|
|
396
388
|
#onFocusOut = (event) => {
|
|
397
|
-
if (!event
|
|
398
|
-
|
|
389
|
+
if (!(event instanceof FocusEvent)) {
|
|
390
|
+
return;
|
|
399
391
|
}
|
|
392
|
+
const target = event.relatedTarget;
|
|
393
|
+
(!(target instanceof Element) || !this.#focusables.has(target)) && this.#update();
|
|
400
394
|
};
|
|
401
395
|
#onKeyDown = (event) => {
|
|
396
|
+
if (!(event instanceof KeyboardEvent)) {
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
402
399
|
const { key, altKey, ctrlKey, metaKey, shiftKey } = event;
|
|
403
400
|
if (altKey || ctrlKey || metaKey || shiftKey) {
|
|
404
401
|
return;
|
|
@@ -417,7 +414,7 @@ var RovingTabIndex = class _RovingTabIndex {
|
|
|
417
414
|
}
|
|
418
415
|
}
|
|
419
416
|
const active = getActiveElement();
|
|
420
|
-
if (!(active instanceof
|
|
417
|
+
if (!(active instanceof Element)) {
|
|
421
418
|
return;
|
|
422
419
|
}
|
|
423
420
|
const current = this.#getFocusables();
|
|
@@ -532,15 +529,19 @@ var RovingTabIndex = class _RovingTabIndex {
|
|
|
532
529
|
};
|
|
533
530
|
|
|
534
531
|
// src/index.ts
|
|
535
|
-
var Disclosure = class {
|
|
532
|
+
var Disclosure = class _Disclosure {
|
|
533
|
+
static defaults = {};
|
|
536
534
|
#rootElement;
|
|
535
|
+
#defaults = { collapsible: true };
|
|
536
|
+
#settings;
|
|
537
537
|
#detailsElements;
|
|
538
538
|
#summaryElements;
|
|
539
539
|
#contentElements;
|
|
540
540
|
#bindings = /* @__PURE__ */ new WeakMap();
|
|
541
|
+
#controller = null;
|
|
541
542
|
#cleanupRovingTabIndex = null;
|
|
542
543
|
#isDestroyed = false;
|
|
543
|
-
constructor(root) {
|
|
544
|
+
constructor(root, options = {}) {
|
|
544
545
|
if (!(root instanceof HTMLElement)) {
|
|
545
546
|
throw new TypeError("Invalid root element");
|
|
546
547
|
}
|
|
@@ -549,6 +550,8 @@ var Disclosure = class {
|
|
|
549
550
|
return;
|
|
550
551
|
}
|
|
551
552
|
this.#rootElement = root;
|
|
553
|
+
this.#defaults = this.#mergeOptions(this.#defaults, _Disclosure.defaults);
|
|
554
|
+
this.#settings = this.#mergeOptions(this.#defaults, options);
|
|
552
555
|
const NOT_NESTED = ":not(:scope summary + * *)";
|
|
553
556
|
this.#detailsElements = [
|
|
554
557
|
...this.#rootElement.querySelectorAll(
|
|
@@ -590,7 +593,7 @@ var Disclosure = class {
|
|
|
590
593
|
});
|
|
591
594
|
this.#initialize();
|
|
592
595
|
}
|
|
593
|
-
|
|
596
|
+
collapse(details) {
|
|
594
597
|
if (this.#isDestroyed) {
|
|
595
598
|
return;
|
|
596
599
|
}
|
|
@@ -605,6 +608,8 @@ var Disclosure = class {
|
|
|
605
608
|
return;
|
|
606
609
|
}
|
|
607
610
|
this.#isDestroyed = true;
|
|
611
|
+
this.#controller?.abort();
|
|
612
|
+
this.#controller = null;
|
|
608
613
|
this.#cleanupRovingTabIndex?.();
|
|
609
614
|
this.#cleanupRovingTabIndex = null;
|
|
610
615
|
this.#detailsElements.length = 0;
|
|
@@ -613,7 +618,7 @@ var Disclosure = class {
|
|
|
613
618
|
this.#contentElements.length = 0;
|
|
614
619
|
this.#rootElement.removeAttribute("data-disclosure-initialized");
|
|
615
620
|
}
|
|
616
|
-
|
|
621
|
+
expand(details) {
|
|
617
622
|
if (this.#isDestroyed) {
|
|
618
623
|
return;
|
|
619
624
|
}
|
|
@@ -629,10 +634,15 @@ var Disclosure = class {
|
|
|
629
634
|
"style",
|
|
630
635
|
"tabindex"
|
|
631
636
|
]);
|
|
632
|
-
this.#
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
summary
|
|
637
|
+
this.#controller = new AbortController();
|
|
638
|
+
const { signal } = this.#controller;
|
|
639
|
+
this.#summaryElements.forEach((summary) => {
|
|
640
|
+
if (!this.#isFocusable(summary)) {
|
|
641
|
+
summary.setAttribute("aria-disabled", "true");
|
|
642
|
+
summary.setAttribute("tabindex", "-1");
|
|
643
|
+
summary.style.setProperty("pointer-events", "none");
|
|
644
|
+
}
|
|
645
|
+
summary.addEventListener("click", this.#onSummaryClick, { signal });
|
|
636
646
|
});
|
|
637
647
|
this.#cleanupRovingTabIndex = createRovingTabIndex(this.#rootElement, {
|
|
638
648
|
direction: "vertical",
|
|
@@ -642,10 +652,27 @@ var Disclosure = class {
|
|
|
642
652
|
});
|
|
643
653
|
this.#rootElement.setAttribute("data-disclosure-initialized", "");
|
|
644
654
|
}
|
|
645
|
-
#
|
|
646
|
-
|
|
647
|
-
|
|
655
|
+
#onSummaryClick = (event) => {
|
|
656
|
+
event.preventDefault();
|
|
657
|
+
const summary = event.currentTarget;
|
|
658
|
+
if (!(summary instanceof HTMLElement)) {
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
const binding = this.#bindings.get(summary);
|
|
662
|
+
if (!binding) {
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
const { details } = binding;
|
|
666
|
+
this.#toggle(details, !details.open);
|
|
667
|
+
};
|
|
668
|
+
#toggle(details, isExpand) {
|
|
669
|
+
if (details.open === isExpand) {
|
|
670
|
+
return;
|
|
648
671
|
}
|
|
672
|
+
if (!isExpand && !this.#settings.collapsible && this.#detailsElements.filter((details2) => details2.open).length <= 1) {
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
details.open = isExpand;
|
|
649
676
|
}
|
|
650
677
|
#createBinding(details, summary, content) {
|
|
651
678
|
return { details, summary, content };
|
|
@@ -653,13 +680,16 @@ var Disclosure = class {
|
|
|
653
680
|
#isFocusable(element) {
|
|
654
681
|
return element.tabIndex >= 0;
|
|
655
682
|
}
|
|
683
|
+
#mergeOptions(target, source) {
|
|
684
|
+
return { ...target, ...source };
|
|
685
|
+
}
|
|
656
686
|
};
|
|
657
687
|
/**
|
|
658
688
|
* Disclosure (CSS Animation)
|
|
659
689
|
* WAI-ARIA compliant disclosure pattern implementation in TypeScript.
|
|
660
690
|
* Using the <details> and <summary> element.
|
|
661
691
|
*
|
|
662
|
-
* @version
|
|
692
|
+
* @version 2.0.1
|
|
663
693
|
* @author Yusuke Kamiyamane
|
|
664
694
|
* @license MIT
|
|
665
695
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -671,7 +701,7 @@ var Disclosure = class {
|
|
|
671
701
|
(**
|
|
672
702
|
* Attributes Utils
|
|
673
703
|
*
|
|
674
|
-
* @version 1.1.
|
|
704
|
+
* @version 1.1.3
|
|
675
705
|
* @author Yusuke Kamiyamane
|
|
676
706
|
* @license MIT
|
|
677
707
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -684,7 +714,7 @@ power-focusable/dist/index.js:
|
|
|
684
714
|
* High-precision focus management utility with full composed tree support.
|
|
685
715
|
* Handles complex focus rules including tabindex ordering, radio groups, inert.
|
|
686
716
|
*
|
|
687
|
-
* @version 4.3.
|
|
717
|
+
* @version 4.3.8
|
|
688
718
|
* @author Yusuke Kamiyamane
|
|
689
719
|
* @license MIT
|
|
690
720
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -697,7 +727,7 @@ power-focusable/dist/index.js:
|
|
|
697
727
|
* Lightweight roving tabindex utility with fully focus management.
|
|
698
728
|
* Designed for accessible menus, tabs, toolbars, and composite widgets.
|
|
699
729
|
*
|
|
700
|
-
* @version 3.1.
|
|
730
|
+
* @version 3.1.6
|
|
701
731
|
* @author Yusuke Kamiyamane
|
|
702
732
|
* @license MIT
|
|
703
733
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
package/dist/index.cjs
CHANGED
|
@@ -4,15 +4,19 @@ var attributesUtils = require('@y14e/attributes-utils');
|
|
|
4
4
|
var rovingTabindex = require('@y14e/roving-tabindex');
|
|
5
5
|
|
|
6
6
|
// src/index.ts
|
|
7
|
-
var Disclosure = class {
|
|
7
|
+
var Disclosure = class _Disclosure {
|
|
8
|
+
static defaults = {};
|
|
8
9
|
#rootElement;
|
|
10
|
+
#defaults = { collapsible: true };
|
|
11
|
+
#settings;
|
|
9
12
|
#detailsElements;
|
|
10
13
|
#summaryElements;
|
|
11
14
|
#contentElements;
|
|
12
15
|
#bindings = /* @__PURE__ */ new WeakMap();
|
|
16
|
+
#controller = null;
|
|
13
17
|
#cleanupRovingTabIndex = null;
|
|
14
18
|
#isDestroyed = false;
|
|
15
|
-
constructor(root) {
|
|
19
|
+
constructor(root, options = {}) {
|
|
16
20
|
if (!(root instanceof HTMLElement)) {
|
|
17
21
|
throw new TypeError("Invalid root element");
|
|
18
22
|
}
|
|
@@ -21,6 +25,8 @@ var Disclosure = class {
|
|
|
21
25
|
return;
|
|
22
26
|
}
|
|
23
27
|
this.#rootElement = root;
|
|
28
|
+
this.#defaults = this.#mergeOptions(this.#defaults, _Disclosure.defaults);
|
|
29
|
+
this.#settings = this.#mergeOptions(this.#defaults, options);
|
|
24
30
|
const NOT_NESTED = ":not(:scope summary + * *)";
|
|
25
31
|
this.#detailsElements = [
|
|
26
32
|
...this.#rootElement.querySelectorAll(
|
|
@@ -62,7 +68,7 @@ var Disclosure = class {
|
|
|
62
68
|
});
|
|
63
69
|
this.#initialize();
|
|
64
70
|
}
|
|
65
|
-
|
|
71
|
+
collapse(details) {
|
|
66
72
|
if (this.#isDestroyed) {
|
|
67
73
|
return;
|
|
68
74
|
}
|
|
@@ -77,6 +83,8 @@ var Disclosure = class {
|
|
|
77
83
|
return;
|
|
78
84
|
}
|
|
79
85
|
this.#isDestroyed = true;
|
|
86
|
+
this.#controller?.abort();
|
|
87
|
+
this.#controller = null;
|
|
80
88
|
this.#cleanupRovingTabIndex?.();
|
|
81
89
|
this.#cleanupRovingTabIndex = null;
|
|
82
90
|
this.#detailsElements.length = 0;
|
|
@@ -85,7 +93,7 @@ var Disclosure = class {
|
|
|
85
93
|
this.#contentElements.length = 0;
|
|
86
94
|
this.#rootElement.removeAttribute("data-disclosure-initialized");
|
|
87
95
|
}
|
|
88
|
-
|
|
96
|
+
expand(details) {
|
|
89
97
|
if (this.#isDestroyed) {
|
|
90
98
|
return;
|
|
91
99
|
}
|
|
@@ -101,10 +109,15 @@ var Disclosure = class {
|
|
|
101
109
|
"style",
|
|
102
110
|
"tabindex"
|
|
103
111
|
]);
|
|
104
|
-
this.#
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
summary
|
|
112
|
+
this.#controller = new AbortController();
|
|
113
|
+
const { signal } = this.#controller;
|
|
114
|
+
this.#summaryElements.forEach((summary) => {
|
|
115
|
+
if (!this.#isFocusable(summary)) {
|
|
116
|
+
summary.setAttribute("aria-disabled", "true");
|
|
117
|
+
summary.setAttribute("tabindex", "-1");
|
|
118
|
+
summary.style.setProperty("pointer-events", "none");
|
|
119
|
+
}
|
|
120
|
+
summary.addEventListener("click", this.#onSummaryClick, { signal });
|
|
108
121
|
});
|
|
109
122
|
this.#cleanupRovingTabIndex = rovingTabindex.createRovingTabIndex(this.#rootElement, {
|
|
110
123
|
direction: "vertical",
|
|
@@ -114,10 +127,27 @@ var Disclosure = class {
|
|
|
114
127
|
});
|
|
115
128
|
this.#rootElement.setAttribute("data-disclosure-initialized", "");
|
|
116
129
|
}
|
|
117
|
-
#
|
|
118
|
-
|
|
119
|
-
|
|
130
|
+
#onSummaryClick = (event) => {
|
|
131
|
+
event.preventDefault();
|
|
132
|
+
const summary = event.currentTarget;
|
|
133
|
+
if (!(summary instanceof HTMLElement)) {
|
|
134
|
+
return;
|
|
120
135
|
}
|
|
136
|
+
const binding = this.#bindings.get(summary);
|
|
137
|
+
if (!binding) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const { details } = binding;
|
|
141
|
+
this.#toggle(details, !details.open);
|
|
142
|
+
};
|
|
143
|
+
#toggle(details, isExpand) {
|
|
144
|
+
if (details.open === isExpand) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (!isExpand && !this.#settings.collapsible && this.#detailsElements.filter((details2) => details2.open).length <= 1) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
details.open = isExpand;
|
|
121
151
|
}
|
|
122
152
|
#createBinding(details, summary, content) {
|
|
123
153
|
return { details, summary, content };
|
|
@@ -125,13 +155,16 @@ var Disclosure = class {
|
|
|
125
155
|
#isFocusable(element) {
|
|
126
156
|
return element.tabIndex >= 0;
|
|
127
157
|
}
|
|
158
|
+
#mergeOptions(target, source) {
|
|
159
|
+
return { ...target, ...source };
|
|
160
|
+
}
|
|
128
161
|
};
|
|
129
162
|
/**
|
|
130
163
|
* Disclosure (CSS Animation)
|
|
131
164
|
* WAI-ARIA compliant disclosure pattern implementation in TypeScript.
|
|
132
165
|
* Using the <details> and <summary> element.
|
|
133
166
|
*
|
|
134
|
-
* @version
|
|
167
|
+
* @version 2.0.1
|
|
135
168
|
* @author Yusuke Kamiyamane
|
|
136
169
|
* @license MIT
|
|
137
170
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
package/dist/index.d.cts
CHANGED
|
@@ -3,18 +3,22 @@
|
|
|
3
3
|
* WAI-ARIA compliant disclosure pattern implementation in TypeScript.
|
|
4
4
|
* Using the <details> and <summary> element.
|
|
5
5
|
*
|
|
6
|
-
* @version
|
|
6
|
+
* @version 2.0.1
|
|
7
7
|
* @author Yusuke Kamiyamane
|
|
8
8
|
* @license MIT
|
|
9
9
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
10
10
|
* @see {@link https://github.com/y14e/disclosure-css}
|
|
11
11
|
*/
|
|
12
|
+
interface DisclosureOptions {
|
|
13
|
+
readonly collapsible?: boolean;
|
|
14
|
+
}
|
|
12
15
|
declare class Disclosure {
|
|
13
16
|
#private;
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
static defaults: DisclosureOptions;
|
|
18
|
+
constructor(root: HTMLElement, options?: DisclosureOptions);
|
|
19
|
+
collapse(details: HTMLDetailsElement): void;
|
|
16
20
|
destroy(): void;
|
|
17
|
-
|
|
21
|
+
expand(details: HTMLDetailsElement): void;
|
|
18
22
|
}
|
|
19
23
|
|
|
20
|
-
export { Disclosure as default };
|
|
24
|
+
export { type DisclosureOptions, Disclosure as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -3,18 +3,22 @@
|
|
|
3
3
|
* WAI-ARIA compliant disclosure pattern implementation in TypeScript.
|
|
4
4
|
* Using the <details> and <summary> element.
|
|
5
5
|
*
|
|
6
|
-
* @version
|
|
6
|
+
* @version 2.0.1
|
|
7
7
|
* @author Yusuke Kamiyamane
|
|
8
8
|
* @license MIT
|
|
9
9
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
10
10
|
* @see {@link https://github.com/y14e/disclosure-css}
|
|
11
11
|
*/
|
|
12
|
+
interface DisclosureOptions {
|
|
13
|
+
readonly collapsible?: boolean;
|
|
14
|
+
}
|
|
12
15
|
declare class Disclosure {
|
|
13
16
|
#private;
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
static defaults: DisclosureOptions;
|
|
18
|
+
constructor(root: HTMLElement, options?: DisclosureOptions);
|
|
19
|
+
collapse(details: HTMLDetailsElement): void;
|
|
16
20
|
destroy(): void;
|
|
17
|
-
|
|
21
|
+
expand(details: HTMLDetailsElement): void;
|
|
18
22
|
}
|
|
19
23
|
|
|
20
|
-
export { Disclosure as default };
|
|
24
|
+
export { type DisclosureOptions, Disclosure as default };
|
package/dist/index.js
CHANGED
|
@@ -2,15 +2,19 @@ import { restoreAttributes, saveAttributes } from '@y14e/attributes-utils';
|
|
|
2
2
|
import { createRovingTabIndex } from '@y14e/roving-tabindex';
|
|
3
3
|
|
|
4
4
|
// src/index.ts
|
|
5
|
-
var Disclosure = class {
|
|
5
|
+
var Disclosure = class _Disclosure {
|
|
6
|
+
static defaults = {};
|
|
6
7
|
#rootElement;
|
|
8
|
+
#defaults = { collapsible: true };
|
|
9
|
+
#settings;
|
|
7
10
|
#detailsElements;
|
|
8
11
|
#summaryElements;
|
|
9
12
|
#contentElements;
|
|
10
13
|
#bindings = /* @__PURE__ */ new WeakMap();
|
|
14
|
+
#controller = null;
|
|
11
15
|
#cleanupRovingTabIndex = null;
|
|
12
16
|
#isDestroyed = false;
|
|
13
|
-
constructor(root) {
|
|
17
|
+
constructor(root, options = {}) {
|
|
14
18
|
if (!(root instanceof HTMLElement)) {
|
|
15
19
|
throw new TypeError("Invalid root element");
|
|
16
20
|
}
|
|
@@ -19,6 +23,8 @@ var Disclosure = class {
|
|
|
19
23
|
return;
|
|
20
24
|
}
|
|
21
25
|
this.#rootElement = root;
|
|
26
|
+
this.#defaults = this.#mergeOptions(this.#defaults, _Disclosure.defaults);
|
|
27
|
+
this.#settings = this.#mergeOptions(this.#defaults, options);
|
|
22
28
|
const NOT_NESTED = ":not(:scope summary + * *)";
|
|
23
29
|
this.#detailsElements = [
|
|
24
30
|
...this.#rootElement.querySelectorAll(
|
|
@@ -60,7 +66,7 @@ var Disclosure = class {
|
|
|
60
66
|
});
|
|
61
67
|
this.#initialize();
|
|
62
68
|
}
|
|
63
|
-
|
|
69
|
+
collapse(details) {
|
|
64
70
|
if (this.#isDestroyed) {
|
|
65
71
|
return;
|
|
66
72
|
}
|
|
@@ -75,6 +81,8 @@ var Disclosure = class {
|
|
|
75
81
|
return;
|
|
76
82
|
}
|
|
77
83
|
this.#isDestroyed = true;
|
|
84
|
+
this.#controller?.abort();
|
|
85
|
+
this.#controller = null;
|
|
78
86
|
this.#cleanupRovingTabIndex?.();
|
|
79
87
|
this.#cleanupRovingTabIndex = null;
|
|
80
88
|
this.#detailsElements.length = 0;
|
|
@@ -83,7 +91,7 @@ var Disclosure = class {
|
|
|
83
91
|
this.#contentElements.length = 0;
|
|
84
92
|
this.#rootElement.removeAttribute("data-disclosure-initialized");
|
|
85
93
|
}
|
|
86
|
-
|
|
94
|
+
expand(details) {
|
|
87
95
|
if (this.#isDestroyed) {
|
|
88
96
|
return;
|
|
89
97
|
}
|
|
@@ -99,10 +107,15 @@ var Disclosure = class {
|
|
|
99
107
|
"style",
|
|
100
108
|
"tabindex"
|
|
101
109
|
]);
|
|
102
|
-
this.#
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
summary
|
|
110
|
+
this.#controller = new AbortController();
|
|
111
|
+
const { signal } = this.#controller;
|
|
112
|
+
this.#summaryElements.forEach((summary) => {
|
|
113
|
+
if (!this.#isFocusable(summary)) {
|
|
114
|
+
summary.setAttribute("aria-disabled", "true");
|
|
115
|
+
summary.setAttribute("tabindex", "-1");
|
|
116
|
+
summary.style.setProperty("pointer-events", "none");
|
|
117
|
+
}
|
|
118
|
+
summary.addEventListener("click", this.#onSummaryClick, { signal });
|
|
106
119
|
});
|
|
107
120
|
this.#cleanupRovingTabIndex = createRovingTabIndex(this.#rootElement, {
|
|
108
121
|
direction: "vertical",
|
|
@@ -112,10 +125,27 @@ var Disclosure = class {
|
|
|
112
125
|
});
|
|
113
126
|
this.#rootElement.setAttribute("data-disclosure-initialized", "");
|
|
114
127
|
}
|
|
115
|
-
#
|
|
116
|
-
|
|
117
|
-
|
|
128
|
+
#onSummaryClick = (event) => {
|
|
129
|
+
event.preventDefault();
|
|
130
|
+
const summary = event.currentTarget;
|
|
131
|
+
if (!(summary instanceof HTMLElement)) {
|
|
132
|
+
return;
|
|
118
133
|
}
|
|
134
|
+
const binding = this.#bindings.get(summary);
|
|
135
|
+
if (!binding) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const { details } = binding;
|
|
139
|
+
this.#toggle(details, !details.open);
|
|
140
|
+
};
|
|
141
|
+
#toggle(details, isExpand) {
|
|
142
|
+
if (details.open === isExpand) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (!isExpand && !this.#settings.collapsible && this.#detailsElements.filter((details2) => details2.open).length <= 1) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
details.open = isExpand;
|
|
119
149
|
}
|
|
120
150
|
#createBinding(details, summary, content) {
|
|
121
151
|
return { details, summary, content };
|
|
@@ -123,13 +153,16 @@ var Disclosure = class {
|
|
|
123
153
|
#isFocusable(element) {
|
|
124
154
|
return element.tabIndex >= 0;
|
|
125
155
|
}
|
|
156
|
+
#mergeOptions(target, source) {
|
|
157
|
+
return { ...target, ...source };
|
|
158
|
+
}
|
|
126
159
|
};
|
|
127
160
|
/**
|
|
128
161
|
* Disclosure (CSS Animation)
|
|
129
162
|
* WAI-ARIA compliant disclosure pattern implementation in TypeScript.
|
|
130
163
|
* Using the <details> and <summary> element.
|
|
131
164
|
*
|
|
132
|
-
* @version
|
|
165
|
+
* @version 2.0.1
|
|
133
166
|
* @author Yusuke Kamiyamane
|
|
134
167
|
* @license MIT
|
|
135
168
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@y14e/disclosure-css",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "WAI-ARIA compliant disclosure pattern implementation in TypeScript",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -45,13 +45,14 @@
|
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"bun-types": "latest",
|
|
47
47
|
"tsup": "^8.0.0",
|
|
48
|
-
"typescript": "^5.6.0"
|
|
48
|
+
"typescript": "^5.6.0",
|
|
49
|
+
"utility-types": "^3.11.0"
|
|
49
50
|
},
|
|
50
51
|
"engines": {
|
|
51
52
|
"node": ">=18"
|
|
52
53
|
},
|
|
53
54
|
"dependencies": {
|
|
54
|
-
"@y14e/attributes-utils": "^1.1.
|
|
55
|
-
"@y14e/roving-tabindex": "^3.1.
|
|
55
|
+
"@y14e/attributes-utils": "^1.1.3",
|
|
56
|
+
"@y14e/roving-tabindex": "^3.1.6"
|
|
56
57
|
}
|
|
57
58
|
}
|