@y14e/disclosure 2.0.14 → 2.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,14 +10,14 @@ npm i @y14e/disclosure
10
10
 
11
11
  ```ts
12
12
  // npm
13
- import { Disclosure } from "@y14e/disclosure";
13
+ import { Disclosure } from '@y14e/disclosure';
14
14
 
15
15
  // CDNs
16
- import { Disclosure } from "https://esm.sh/@y14e/disclosure@2.0.14";
16
+ import { Disclosure } from 'https://esm.sh/@y14e/disclosure@2.0.16';
17
17
  // or
18
- import { Disclosure } from "https://cdn.jsdelivr.net/npm/@y14e/disclosure@2.0.14/+esm";
18
+ import { Disclosure } from 'https://cdn.jsdelivr.net/npm/@y14e/disclosure@2.0.16/+esm';
19
19
  // or
20
- import { Disclosure } from "https://esm.unpkg.com/@y14e/disclosure@2.0.14";
20
+ import { Disclosure } from 'https://esm.unpkg.com/@y14e/disclosure@2.0.16';
21
21
  ```
22
22
 
23
23
  ## Usage
@@ -47,7 +47,7 @@ interface DisclosureOptions {
47
47
  Override the global default settings applied to all disclosure instances.
48
48
 
49
49
  ```ts
50
- import { Disclosure } from "@y14e/disclosure";
50
+ import { Disclosure } from '@y14e/disclosure';
51
51
 
52
52
  Disclosure.defaults = {
53
53
  animation: {
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  // node_modules/@y14e/attribute-utils/dist/index.js
4
- var DEFAULT_PARSER = (value) => value.split(/\s+/);
5
- var DEFAULT_SERIALIZER = (tokens) => tokens.join(" ");
4
+ var DEFAULT_PARSER = (v) => v.split(/\s+/);
5
+ var DEFAULT_SERIALIZER = (t) => t.join(" ");
6
6
  function addAttributeToken(element, name, token, options = {}) {
7
7
  if (!isValid(element, name, token)) {
8
8
  return;
@@ -12,7 +12,7 @@ function addAttributeToken(element, name, token, options = {}) {
12
12
  const tokens = value ? parse(value).filter(Boolean) : [];
13
13
  if (caseInsensitive) {
14
14
  const lower = token.toLowerCase();
15
- if (tokens.every((token2) => token2.toLowerCase() !== lower)) {
15
+ if (tokens.every((t) => t.toLowerCase() !== lower)) {
16
16
  tokens.push(token);
17
17
  element.setAttribute(name, serialize(tokens));
18
18
  }
@@ -37,16 +37,16 @@ function restoreAttributes(element_or_elements) {
37
37
  }
38
38
  function saveAttributes(element_or_elements, name_or_names) {
39
39
  const names = Array.isArray(name_or_names) ? name_or_names : [name_or_names];
40
- (Array.isArray(element_or_elements) ? element_or_elements : [element_or_elements]).forEach((element) => {
40
+ for (const element of Array.isArray(element_or_elements) ? element_or_elements : [element_or_elements]) {
41
41
  let snapshot = snapshots.get(element);
42
42
  if (!snapshot) {
43
43
  snapshot = /* @__PURE__ */ new Map();
44
44
  snapshots.set(element, snapshot);
45
45
  }
46
- names.forEach((name) => {
46
+ for (const name of names) {
47
47
  snapshot.set(name, element.getAttribute(name));
48
- });
49
- });
48
+ }
49
+ }
50
50
  }
51
51
  function isValid(element, name, token) {
52
52
  if (!(element instanceof Element)) {
@@ -91,7 +91,7 @@ function resolveOptions(options) {
91
91
  }
92
92
 
93
93
  // node_modules/power-focusable/dist/index.js
94
- var FOCUSABLE_SELECTOR = `:is(a[href], area[href], button, embed, iframe, input:not([type="hidden" i]), object, select, details > summary:first-of-type, textarea, [contenteditable]:not([contenteditable="false" i]), [controls], [tabindex]):not(:disabled, [hidden], [inert], [tabindex="-1"])`;
94
+ var FOCUSABLE_SELECTOR = ':is(a[href], area[href], button, embed, iframe, input:not([type="hidden" i]), object, select, details > summary:first-of-type, textarea, [contenteditable]:not([contenteditable="false" i]), [controls], [tabindex]):not(:disabled, [hidden], [inert], [tabindex="-1"])';
95
95
  var FOCUSABLE_SELECTOR_WITH_NEGATIVE_TABINDEX = FOCUSABLE_SELECTOR.replace(
96
96
  /(,\s*)?\[tabindex="-1"\]/g,
97
97
  ""
@@ -115,31 +115,21 @@ function getFocusables(container = document.body, options = {}) {
115
115
  skipNegativeTabIndexCheck,
116
116
  skipVisibilityCheck
117
117
  }) || include?.(node)) {
118
- candidates[candidates.length] = node;
119
- }
120
- const children2 = composed ? getComposedChildren(node) : getChildren(node);
121
- for (let i = 0, l = children2.length; i < l; i++) {
122
- const child = children2[i];
123
- child && traverse2(child);
118
+ candidates.push(node);
124
119
  }
120
+ (composed ? getComposedChildren(node) : [...node.children]).map(traverse2);
125
121
  };
126
- const children = composed ? getComposedChildren(container) : getChildren(container);
127
- for (let i = 0, l = children.length; i < l; i++) {
128
- const child = children[i];
129
- child && traverse2(child);
130
- }
122
+ (composed ? getComposedChildren(container) : [...container.children]).map(
123
+ traverse2
124
+ );
131
125
  } else {
132
- const matches = container.querySelectorAll(
126
+ for (const match of container.querySelectorAll(
133
127
  skipNegativeTabIndexCheck ? FOCUSABLE_SELECTOR_WITH_NEGATIVE_TABINDEX : FOCUSABLE_SELECTOR
134
- );
135
- for (let i = 0, l = matches.length; i < l; i++) {
136
- const matched = matches[i];
137
- if (matched && isFocusable(matched, {
128
+ )) {
129
+ match && isFocusable(match, {
138
130
  skipNegativeTabIndexCheck,
139
131
  skipVisibilityCheck
140
- })) {
141
- candidates[candidates.length] = matched;
142
- }
132
+ }) && candidates.push(match);
143
133
  }
144
134
  }
145
135
  return normalizeRadioGroup(
@@ -206,8 +196,7 @@ function isDisabledDeep(element) {
206
196
  }
207
197
  function normalizeRadioGroup(elements) {
208
198
  let map = null;
209
- for (let i = 0, l = elements.length; i < l; i++) {
210
- const element = elements[i];
199
+ for (const element of elements) {
211
200
  if (!(element instanceof HTMLInputElement)) {
212
201
  continue;
213
202
  }
@@ -218,31 +207,31 @@ function normalizeRadioGroup(elements) {
218
207
  map = /* @__PURE__ */ new Map();
219
208
  }
220
209
  const root = element.getRootNode();
221
- let value = map.get(root);
222
- if (!value) {
223
- value = /* @__PURE__ */ new Map();
224
- map.set(root, value);
210
+ let groups = map.get(root);
211
+ if (!groups) {
212
+ groups = /* @__PURE__ */ new Map();
213
+ map.set(root, groups);
225
214
  }
226
- let v = value.get(element.form);
227
- if (!v) {
228
- v = /* @__PURE__ */ new Map();
229
- value.set(element.form, v);
215
+ let group = groups.get(element.form);
216
+ if (!group) {
217
+ group = /* @__PURE__ */ new Map();
218
+ groups.set(element.form, group);
230
219
  }
231
- let vv = v.get(element.name);
232
- if (!vv) {
233
- vv = [];
234
- v.set(element.name, vv);
220
+ let radios = group.get(element.name);
221
+ if (!radios) {
222
+ radios = [];
223
+ group.set(element.name, radios);
235
224
  }
236
- vv[vv.length] = element;
225
+ radios.push(element);
237
226
  }
238
227
  if (!map) {
239
228
  return elements;
240
229
  }
241
230
  const placeholder = /* @__PURE__ */ new Set();
242
- for (const value of map.values()) {
243
- for (const v of value.values()) {
244
- for (const vv of v.values()) {
245
- const radio = vv.find((element) => element.checked) ?? vv[0];
231
+ for (const groups of map.values()) {
232
+ for (const group of groups.values()) {
233
+ for (const radios of group.values()) {
234
+ const radio = radios.find((r) => r.checked) ?? radios[0];
246
235
  radio && placeholder.add(radio);
247
236
  }
248
237
  }
@@ -257,27 +246,14 @@ function normalizeRadioGroup(elements) {
257
246
  function sortByTabIndex(elements) {
258
247
  const ordered = [];
259
248
  const natural = [];
260
- for (let i = 0, l = elements.length; i < l; i++) {
261
- const element = elements[i];
262
- if (element) {
263
- const target = getTabIndex(element) > 0 ? ordered : natural;
264
- target[target.length] = element;
265
- }
266
- }
267
- ordered.sort((a, b) => getTabIndex(a) - getTabIndex(b));
268
- let count = 0;
269
- const sorted = new Array(ordered.length + natural.length);
270
- for (let i = 0, l = ordered.length; i < l; i++) {
271
- sorted[count++] = ordered[i];
272
- }
273
- for (let i = 0, l = natural.length; i < l; i++) {
274
- sorted[count++] = natural[i];
249
+ for (const element of elements) {
250
+ (getTabIndex(element) > 0 ? ordered : natural).push(element);
275
251
  }
276
- return sorted;
252
+ return ordered.sort((a, b) => getTabIndex(a) - getTabIndex(b)).concat(natural);
277
253
  }
278
254
  function getComposedChildren(node) {
279
255
  if (node instanceof ShadowRoot) {
280
- return getChildren(node);
256
+ return [...node.children];
281
257
  }
282
258
  if (!(node instanceof Element)) {
283
259
  return [];
@@ -289,9 +265,9 @@ function getComposedChildren(node) {
289
265
  }
290
266
  }
291
267
  if (node instanceof HTMLElement && node.shadowRoot?.mode === "open") {
292
- return getChildren(node.shadowRoot);
268
+ return [...node.shadowRoot.children];
293
269
  }
294
- return getChildren(node);
270
+ return [...node.children];
295
271
  }
296
272
  function focusElement(element) {
297
273
  "focus" in element && typeof element.focus === "function" && element.focus();
@@ -303,13 +279,6 @@ function getActiveElement() {
303
279
  }
304
280
  return current;
305
281
  }
306
- function getChildren(node) {
307
- const elements = [];
308
- for (let child = node.firstElementChild; child; child = child.nextElementSibling) {
309
- elements[elements.length] = child;
310
- }
311
- return elements;
312
- }
313
282
  function getTabIndex(element) {
314
283
  return "tabIndex" in element ? Number(element.tabIndex) : 0;
315
284
  }
@@ -414,10 +383,10 @@ var RovingTabIndex = class _RovingTabIndex {
414
383
  this.#isDestroyed = true;
415
384
  this.#controller?.abort();
416
385
  this.#controller = null;
417
- this.#focusables.forEach((focusable) => {
386
+ for (const focusable of this.#focusables) {
418
387
  _RovingTabIndex.#initialized.delete(focusable);
419
388
  restoreAttributes(focusable);
420
- });
389
+ }
421
390
  this.#focusables.clear();
422
391
  this.#focusablesByFirstChar.clear();
423
392
  }
@@ -467,7 +436,7 @@ var RovingTabIndex = class _RovingTabIndex {
467
436
  }
468
437
  }
469
438
  const candidates = this.#getFocusables().filter(
470
- (focusable2) => this.#focusables.has(focusable2)
439
+ (f) => this.#focusables.has(f)
471
440
  );
472
441
  const active = getActiveElement();
473
442
  if (!(active instanceof Element)) {
@@ -504,11 +473,9 @@ var RovingTabIndex = class _RovingTabIndex {
504
473
  const focusablesByFirstChar = new Set(
505
474
  this.#focusablesByFirstChar.get(key.toUpperCase()) ?? []
506
475
  );
507
- focusables = candidates.filter(
508
- (candidate) => focusablesByFirstChar.has(candidate)
509
- );
476
+ focusables = candidates.filter((c) => focusablesByFirstChar.has(c));
510
477
  const afterIndex = focusables.findIndex(
511
- (focusable2) => candidates.indexOf(focusable2) > activeIndex
478
+ (f) => candidates.indexOf(f) > activeIndex
512
479
  );
513
480
  newIndex = afterIndex >= 0 ? afterIndex : 0;
514
481
  }
@@ -552,7 +519,7 @@ var RovingTabIndex = class _RovingTabIndex {
552
519
  const char = focusable.textContent?.trim()?.at(0)?.toUpperCase();
553
520
  const value = focusable.ariaKeyShortcuts?.trim();
554
521
  const keys = new Set(
555
- value ? value.split(/\s+/).filter((key) => /^\S$/i.test(key)).map((key) => key.toUpperCase()) : []
522
+ value ? value.split(/\s+/).filter((k) => /^\S$/i.test(k)).map((k) => k.toUpperCase()) : []
556
523
  );
557
524
  if (char) {
558
525
  keys.add(char);
@@ -561,17 +528,17 @@ var RovingTabIndex = class _RovingTabIndex {
561
528
  caseInsensitive: true
562
529
  });
563
530
  }
564
- keys.forEach((key) => {
531
+ for (const key of keys) {
565
532
  const focusables = this.#focusablesByFirstChar.get(key) ?? [];
566
533
  focusables.push(focusable);
567
534
  this.#focusablesByFirstChar.set(key, focusables);
568
- });
535
+ }
569
536
  }
570
537
  if (!navigationOnly) {
571
538
  if (active && this.#focusables.has(active)) {
572
- this.#focusables.forEach((focusable) => {
539
+ for (const focusable of this.#focusables) {
573
540
  focusable.setAttribute("tabindex", focusable === active ? "0" : "-1");
574
- });
541
+ }
575
542
  } else {
576
543
  [...this.#focusables].forEach((focusable, i) => {
577
544
  focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
@@ -601,6 +568,7 @@ var RovingTabIndex = class _RovingTabIndex {
601
568
  typeahead = false,
602
569
  wrap = false
603
570
  } = options;
571
+ direction = direction.toLowerCase();
604
572
  if (!["both", "horizontal", "vertical"].includes(direction)) {
605
573
  console.warn("Invalid direction option. Fallback: 'both'.");
606
574
  direction = "both";
@@ -736,24 +704,24 @@ var Disclosure = class _Disclosure {
736
704
  this.#isDestroyed = true;
737
705
  this.#eventController?.abort();
738
706
  this.#eventController = null;
739
- this.#observers.forEach((observer) => {
707
+ for (const observer of this.#observers) {
740
708
  observer.disconnect();
741
- });
709
+ }
742
710
  this.#observers.length = 0;
743
711
  this.#cleanupRovingTabIndex?.();
744
712
  this.#cleanupRovingTabIndex = null;
745
713
  !force && await this.#waitAnimationsFinish();
746
- this.#contentElements.forEach((content) => {
714
+ for (const content of this.#contentElements) {
747
715
  force && this.#bindings.get(content)?.animation?.finish();
748
716
  this.#onContentAnimationFinish(content);
749
- });
717
+ }
750
718
  this.#animationController?.abort();
751
719
  this.#animationController = null;
752
- this.#detailsElements.forEach((details) => {
753
- ["name", "open"].forEach((name) => {
720
+ for (const details of this.#detailsElements) {
721
+ for (const name of ["name", "open"]) {
754
722
  details.removeAttribute(`data-disclosure-${name}`);
755
- });
756
- });
723
+ }
724
+ }
757
725
  this.#detailsElements.length = 0;
758
726
  restoreAttributes(this.#summaryElements);
759
727
  this.#summaryElements.length = 0;
@@ -833,16 +801,16 @@ var Disclosure = class _Disclosure {
833
801
  if (!details.hasAttribute("data-disclosure-open")) {
834
802
  details.open = false;
835
803
  }
836
- ["block-size", "overflow"].forEach((name2) => {
804
+ for (const name2 of ["block-size", "overflow"]) {
837
805
  content.style.removeProperty(name2);
838
- });
806
+ }
839
807
  }
840
808
  #toggle(details, isExpand, isProgrammatic = false) {
841
809
  if (details.hasAttribute("data-disclosure-open") === isExpand) {
842
810
  return;
843
811
  }
844
812
  if (!isExpand && !isProgrammatic && !this.#settings.collapsible && this.#detailsElements.filter(
845
- (details2) => details2.hasAttribute("data-disclosure-open")
813
+ (d) => d.hasAttribute("data-disclosure-open")
846
814
  ).length <= 1) {
847
815
  return;
848
816
  }
@@ -932,10 +900,10 @@ var Disclosure = class _Disclosure {
932
900
  }
933
901
  async #waitAnimationsFinish() {
934
902
  const promises = [];
935
- this.#contentElements.forEach((content) => {
903
+ for (const content of this.#contentElements) {
936
904
  const animation = this.#bindings.get(content)?.animation;
937
905
  animation && promises.push(waitAnimationFinish(animation));
938
- });
906
+ }
939
907
  await Promise.allSettled(promises);
940
908
  }
941
909
  };
@@ -949,7 +917,7 @@ function waitAnimationFinish(animation) {
949
917
  * WAI-ARIA compliant disclosure pattern implementation in TypeScript.
950
918
  * Using the <details> and <summary> element.
951
919
  *
952
- * @version 2.0.14
920
+ * @version 2.0.16
953
921
  * @author Yusuke Kamiyamane
954
922
  * @license MIT
955
923
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -961,7 +929,7 @@ function waitAnimationFinish(animation) {
961
929
  (**
962
930
  * Attribute Utils
963
931
  *
964
- * @version 2.0.8
932
+ * @version 2.0.12
965
933
  * @author Yusuke Kamiyamane
966
934
  * @license MIT
967
935
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -974,7 +942,7 @@ power-focusable/dist/index.js:
974
942
  * High-precision focus management utility with full composed tree support.
975
943
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
976
944
  *
977
- * @version 4.3.29
945
+ * @version 4.3.33
978
946
  * @author Yusuke Kamiyamane
979
947
  * @license MIT
980
948
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -987,7 +955,7 @@ power-focusable/dist/index.js:
987
955
  * Lightweight roving tabindex utility with fully focus management.
988
956
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
989
957
  *
990
- * @version 3.1.27
958
+ * @version 3.1.31
991
959
  * @author Yusuke Kamiyamane
992
960
  * @license MIT
993
961
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -1,6 +1,6 @@
1
1
  // node_modules/@y14e/attribute-utils/dist/index.js
2
- var DEFAULT_PARSER = (value) => value.split(/\s+/);
3
- var DEFAULT_SERIALIZER = (tokens) => tokens.join(" ");
2
+ var DEFAULT_PARSER = (v) => v.split(/\s+/);
3
+ var DEFAULT_SERIALIZER = (t) => t.join(" ");
4
4
  function addAttributeToken(element, name, token, options = {}) {
5
5
  if (!isValid(element, name, token)) {
6
6
  return;
@@ -10,7 +10,7 @@ function addAttributeToken(element, name, token, options = {}) {
10
10
  const tokens = value ? parse(value).filter(Boolean) : [];
11
11
  if (caseInsensitive) {
12
12
  const lower = token.toLowerCase();
13
- if (tokens.every((token2) => token2.toLowerCase() !== lower)) {
13
+ if (tokens.every((t) => t.toLowerCase() !== lower)) {
14
14
  tokens.push(token);
15
15
  element.setAttribute(name, serialize(tokens));
16
16
  }
@@ -35,16 +35,16 @@ function restoreAttributes(element_or_elements) {
35
35
  }
36
36
  function saveAttributes(element_or_elements, name_or_names) {
37
37
  const names = Array.isArray(name_or_names) ? name_or_names : [name_or_names];
38
- (Array.isArray(element_or_elements) ? element_or_elements : [element_or_elements]).forEach((element) => {
38
+ for (const element of Array.isArray(element_or_elements) ? element_or_elements : [element_or_elements]) {
39
39
  let snapshot = snapshots.get(element);
40
40
  if (!snapshot) {
41
41
  snapshot = /* @__PURE__ */ new Map();
42
42
  snapshots.set(element, snapshot);
43
43
  }
44
- names.forEach((name) => {
44
+ for (const name of names) {
45
45
  snapshot.set(name, element.getAttribute(name));
46
- });
47
- });
46
+ }
47
+ }
48
48
  }
49
49
  function isValid(element, name, token) {
50
50
  if (!(element instanceof Element)) {
@@ -89,7 +89,7 @@ function resolveOptions(options) {
89
89
  }
90
90
 
91
91
  // node_modules/power-focusable/dist/index.js
92
- var FOCUSABLE_SELECTOR = `:is(a[href], area[href], button, embed, iframe, input:not([type="hidden" i]), object, select, details > summary:first-of-type, textarea, [contenteditable]:not([contenteditable="false" i]), [controls], [tabindex]):not(:disabled, [hidden], [inert], [tabindex="-1"])`;
92
+ var FOCUSABLE_SELECTOR = ':is(a[href], area[href], button, embed, iframe, input:not([type="hidden" i]), object, select, details > summary:first-of-type, textarea, [contenteditable]:not([contenteditable="false" i]), [controls], [tabindex]):not(:disabled, [hidden], [inert], [tabindex="-1"])';
93
93
  var FOCUSABLE_SELECTOR_WITH_NEGATIVE_TABINDEX = FOCUSABLE_SELECTOR.replace(
94
94
  /(,\s*)?\[tabindex="-1"\]/g,
95
95
  ""
@@ -113,31 +113,21 @@ function getFocusables(container = document.body, options = {}) {
113
113
  skipNegativeTabIndexCheck,
114
114
  skipVisibilityCheck
115
115
  }) || include?.(node)) {
116
- candidates[candidates.length] = node;
117
- }
118
- const children2 = composed ? getComposedChildren(node) : getChildren(node);
119
- for (let i = 0, l = children2.length; i < l; i++) {
120
- const child = children2[i];
121
- child && traverse2(child);
116
+ candidates.push(node);
122
117
  }
118
+ (composed ? getComposedChildren(node) : [...node.children]).map(traverse2);
123
119
  };
124
- const children = composed ? getComposedChildren(container) : getChildren(container);
125
- for (let i = 0, l = children.length; i < l; i++) {
126
- const child = children[i];
127
- child && traverse2(child);
128
- }
120
+ (composed ? getComposedChildren(container) : [...container.children]).map(
121
+ traverse2
122
+ );
129
123
  } else {
130
- const matches = container.querySelectorAll(
124
+ for (const match of container.querySelectorAll(
131
125
  skipNegativeTabIndexCheck ? FOCUSABLE_SELECTOR_WITH_NEGATIVE_TABINDEX : FOCUSABLE_SELECTOR
132
- );
133
- for (let i = 0, l = matches.length; i < l; i++) {
134
- const matched = matches[i];
135
- if (matched && isFocusable(matched, {
126
+ )) {
127
+ match && isFocusable(match, {
136
128
  skipNegativeTabIndexCheck,
137
129
  skipVisibilityCheck
138
- })) {
139
- candidates[candidates.length] = matched;
140
- }
130
+ }) && candidates.push(match);
141
131
  }
142
132
  }
143
133
  return normalizeRadioGroup(
@@ -204,8 +194,7 @@ function isDisabledDeep(element) {
204
194
  }
205
195
  function normalizeRadioGroup(elements) {
206
196
  let map = null;
207
- for (let i = 0, l = elements.length; i < l; i++) {
208
- const element = elements[i];
197
+ for (const element of elements) {
209
198
  if (!(element instanceof HTMLInputElement)) {
210
199
  continue;
211
200
  }
@@ -216,31 +205,31 @@ function normalizeRadioGroup(elements) {
216
205
  map = /* @__PURE__ */ new Map();
217
206
  }
218
207
  const root = element.getRootNode();
219
- let value = map.get(root);
220
- if (!value) {
221
- value = /* @__PURE__ */ new Map();
222
- map.set(root, value);
208
+ let groups = map.get(root);
209
+ if (!groups) {
210
+ groups = /* @__PURE__ */ new Map();
211
+ map.set(root, groups);
223
212
  }
224
- let v = value.get(element.form);
225
- if (!v) {
226
- v = /* @__PURE__ */ new Map();
227
- value.set(element.form, v);
213
+ let group = groups.get(element.form);
214
+ if (!group) {
215
+ group = /* @__PURE__ */ new Map();
216
+ groups.set(element.form, group);
228
217
  }
229
- let vv = v.get(element.name);
230
- if (!vv) {
231
- vv = [];
232
- v.set(element.name, vv);
218
+ let radios = group.get(element.name);
219
+ if (!radios) {
220
+ radios = [];
221
+ group.set(element.name, radios);
233
222
  }
234
- vv[vv.length] = element;
223
+ radios.push(element);
235
224
  }
236
225
  if (!map) {
237
226
  return elements;
238
227
  }
239
228
  const placeholder = /* @__PURE__ */ new Set();
240
- for (const value of map.values()) {
241
- for (const v of value.values()) {
242
- for (const vv of v.values()) {
243
- const radio = vv.find((element) => element.checked) ?? vv[0];
229
+ for (const groups of map.values()) {
230
+ for (const group of groups.values()) {
231
+ for (const radios of group.values()) {
232
+ const radio = radios.find((r) => r.checked) ?? radios[0];
244
233
  radio && placeholder.add(radio);
245
234
  }
246
235
  }
@@ -255,27 +244,14 @@ function normalizeRadioGroup(elements) {
255
244
  function sortByTabIndex(elements) {
256
245
  const ordered = [];
257
246
  const natural = [];
258
- for (let i = 0, l = elements.length; i < l; i++) {
259
- const element = elements[i];
260
- if (element) {
261
- const target = getTabIndex(element) > 0 ? ordered : natural;
262
- target[target.length] = element;
263
- }
264
- }
265
- ordered.sort((a, b) => getTabIndex(a) - getTabIndex(b));
266
- let count = 0;
267
- const sorted = new Array(ordered.length + natural.length);
268
- for (let i = 0, l = ordered.length; i < l; i++) {
269
- sorted[count++] = ordered[i];
270
- }
271
- for (let i = 0, l = natural.length; i < l; i++) {
272
- sorted[count++] = natural[i];
247
+ for (const element of elements) {
248
+ (getTabIndex(element) > 0 ? ordered : natural).push(element);
273
249
  }
274
- return sorted;
250
+ return ordered.sort((a, b) => getTabIndex(a) - getTabIndex(b)).concat(natural);
275
251
  }
276
252
  function getComposedChildren(node) {
277
253
  if (node instanceof ShadowRoot) {
278
- return getChildren(node);
254
+ return [...node.children];
279
255
  }
280
256
  if (!(node instanceof Element)) {
281
257
  return [];
@@ -287,9 +263,9 @@ function getComposedChildren(node) {
287
263
  }
288
264
  }
289
265
  if (node instanceof HTMLElement && node.shadowRoot?.mode === "open") {
290
- return getChildren(node.shadowRoot);
266
+ return [...node.shadowRoot.children];
291
267
  }
292
- return getChildren(node);
268
+ return [...node.children];
293
269
  }
294
270
  function focusElement(element) {
295
271
  "focus" in element && typeof element.focus === "function" && element.focus();
@@ -301,13 +277,6 @@ function getActiveElement() {
301
277
  }
302
278
  return current;
303
279
  }
304
- function getChildren(node) {
305
- const elements = [];
306
- for (let child = node.firstElementChild; child; child = child.nextElementSibling) {
307
- elements[elements.length] = child;
308
- }
309
- return elements;
310
- }
311
280
  function getTabIndex(element) {
312
281
  return "tabIndex" in element ? Number(element.tabIndex) : 0;
313
282
  }
@@ -412,10 +381,10 @@ var RovingTabIndex = class _RovingTabIndex {
412
381
  this.#isDestroyed = true;
413
382
  this.#controller?.abort();
414
383
  this.#controller = null;
415
- this.#focusables.forEach((focusable) => {
384
+ for (const focusable of this.#focusables) {
416
385
  _RovingTabIndex.#initialized.delete(focusable);
417
386
  restoreAttributes(focusable);
418
- });
387
+ }
419
388
  this.#focusables.clear();
420
389
  this.#focusablesByFirstChar.clear();
421
390
  }
@@ -465,7 +434,7 @@ var RovingTabIndex = class _RovingTabIndex {
465
434
  }
466
435
  }
467
436
  const candidates = this.#getFocusables().filter(
468
- (focusable2) => this.#focusables.has(focusable2)
437
+ (f) => this.#focusables.has(f)
469
438
  );
470
439
  const active = getActiveElement();
471
440
  if (!(active instanceof Element)) {
@@ -502,11 +471,9 @@ var RovingTabIndex = class _RovingTabIndex {
502
471
  const focusablesByFirstChar = new Set(
503
472
  this.#focusablesByFirstChar.get(key.toUpperCase()) ?? []
504
473
  );
505
- focusables = candidates.filter(
506
- (candidate) => focusablesByFirstChar.has(candidate)
507
- );
474
+ focusables = candidates.filter((c) => focusablesByFirstChar.has(c));
508
475
  const afterIndex = focusables.findIndex(
509
- (focusable2) => candidates.indexOf(focusable2) > activeIndex
476
+ (f) => candidates.indexOf(f) > activeIndex
510
477
  );
511
478
  newIndex = afterIndex >= 0 ? afterIndex : 0;
512
479
  }
@@ -550,7 +517,7 @@ var RovingTabIndex = class _RovingTabIndex {
550
517
  const char = focusable.textContent?.trim()?.at(0)?.toUpperCase();
551
518
  const value = focusable.ariaKeyShortcuts?.trim();
552
519
  const keys = new Set(
553
- value ? value.split(/\s+/).filter((key) => /^\S$/i.test(key)).map((key) => key.toUpperCase()) : []
520
+ value ? value.split(/\s+/).filter((k) => /^\S$/i.test(k)).map((k) => k.toUpperCase()) : []
554
521
  );
555
522
  if (char) {
556
523
  keys.add(char);
@@ -559,17 +526,17 @@ var RovingTabIndex = class _RovingTabIndex {
559
526
  caseInsensitive: true
560
527
  });
561
528
  }
562
- keys.forEach((key) => {
529
+ for (const key of keys) {
563
530
  const focusables = this.#focusablesByFirstChar.get(key) ?? [];
564
531
  focusables.push(focusable);
565
532
  this.#focusablesByFirstChar.set(key, focusables);
566
- });
533
+ }
567
534
  }
568
535
  if (!navigationOnly) {
569
536
  if (active && this.#focusables.has(active)) {
570
- this.#focusables.forEach((focusable) => {
537
+ for (const focusable of this.#focusables) {
571
538
  focusable.setAttribute("tabindex", focusable === active ? "0" : "-1");
572
- });
539
+ }
573
540
  } else {
574
541
  [...this.#focusables].forEach((focusable, i) => {
575
542
  focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
@@ -599,6 +566,7 @@ var RovingTabIndex = class _RovingTabIndex {
599
566
  typeahead = false,
600
567
  wrap = false
601
568
  } = options;
569
+ direction = direction.toLowerCase();
602
570
  if (!["both", "horizontal", "vertical"].includes(direction)) {
603
571
  console.warn("Invalid direction option. Fallback: 'both'.");
604
572
  direction = "both";
@@ -734,24 +702,24 @@ var Disclosure = class _Disclosure {
734
702
  this.#isDestroyed = true;
735
703
  this.#eventController?.abort();
736
704
  this.#eventController = null;
737
- this.#observers.forEach((observer) => {
705
+ for (const observer of this.#observers) {
738
706
  observer.disconnect();
739
- });
707
+ }
740
708
  this.#observers.length = 0;
741
709
  this.#cleanupRovingTabIndex?.();
742
710
  this.#cleanupRovingTabIndex = null;
743
711
  !force && await this.#waitAnimationsFinish();
744
- this.#contentElements.forEach((content) => {
712
+ for (const content of this.#contentElements) {
745
713
  force && this.#bindings.get(content)?.animation?.finish();
746
714
  this.#onContentAnimationFinish(content);
747
- });
715
+ }
748
716
  this.#animationController?.abort();
749
717
  this.#animationController = null;
750
- this.#detailsElements.forEach((details) => {
751
- ["name", "open"].forEach((name) => {
718
+ for (const details of this.#detailsElements) {
719
+ for (const name of ["name", "open"]) {
752
720
  details.removeAttribute(`data-disclosure-${name}`);
753
- });
754
- });
721
+ }
722
+ }
755
723
  this.#detailsElements.length = 0;
756
724
  restoreAttributes(this.#summaryElements);
757
725
  this.#summaryElements.length = 0;
@@ -831,16 +799,16 @@ var Disclosure = class _Disclosure {
831
799
  if (!details.hasAttribute("data-disclosure-open")) {
832
800
  details.open = false;
833
801
  }
834
- ["block-size", "overflow"].forEach((name2) => {
802
+ for (const name2 of ["block-size", "overflow"]) {
835
803
  content.style.removeProperty(name2);
836
- });
804
+ }
837
805
  }
838
806
  #toggle(details, isExpand, isProgrammatic = false) {
839
807
  if (details.hasAttribute("data-disclosure-open") === isExpand) {
840
808
  return;
841
809
  }
842
810
  if (!isExpand && !isProgrammatic && !this.#settings.collapsible && this.#detailsElements.filter(
843
- (details2) => details2.hasAttribute("data-disclosure-open")
811
+ (d) => d.hasAttribute("data-disclosure-open")
844
812
  ).length <= 1) {
845
813
  return;
846
814
  }
@@ -930,10 +898,10 @@ var Disclosure = class _Disclosure {
930
898
  }
931
899
  async #waitAnimationsFinish() {
932
900
  const promises = [];
933
- this.#contentElements.forEach((content) => {
901
+ for (const content of this.#contentElements) {
934
902
  const animation = this.#bindings.get(content)?.animation;
935
903
  animation && promises.push(waitAnimationFinish(animation));
936
- });
904
+ }
937
905
  await Promise.allSettled(promises);
938
906
  }
939
907
  };
@@ -947,7 +915,7 @@ function waitAnimationFinish(animation) {
947
915
  * WAI-ARIA compliant disclosure pattern implementation in TypeScript.
948
916
  * Using the <details> and <summary> element.
949
917
  *
950
- * @version 2.0.14
918
+ * @version 2.0.16
951
919
  * @author Yusuke Kamiyamane
952
920
  * @license MIT
953
921
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -959,7 +927,7 @@ function waitAnimationFinish(animation) {
959
927
  (**
960
928
  * Attribute Utils
961
929
  *
962
- * @version 2.0.8
930
+ * @version 2.0.12
963
931
  * @author Yusuke Kamiyamane
964
932
  * @license MIT
965
933
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -972,7 +940,7 @@ power-focusable/dist/index.js:
972
940
  * High-precision focus management utility with full composed tree support.
973
941
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
974
942
  *
975
- * @version 4.3.29
943
+ * @version 4.3.33
976
944
  * @author Yusuke Kamiyamane
977
945
  * @license MIT
978
946
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -985,7 +953,7 @@ power-focusable/dist/index.js:
985
953
  * Lightweight roving tabindex utility with fully focus management.
986
954
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
987
955
  *
988
- * @version 3.1.27
956
+ * @version 3.1.31
989
957
  * @author Yusuke Kamiyamane
990
958
  * @license MIT
991
959
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.cjs CHANGED
@@ -114,24 +114,24 @@ var Disclosure = class _Disclosure {
114
114
  this.#isDestroyed = true;
115
115
  this.#eventController?.abort();
116
116
  this.#eventController = null;
117
- this.#observers.forEach((observer) => {
117
+ for (const observer of this.#observers) {
118
118
  observer.disconnect();
119
- });
119
+ }
120
120
  this.#observers.length = 0;
121
121
  this.#cleanupRovingTabIndex?.();
122
122
  this.#cleanupRovingTabIndex = null;
123
123
  !force && await this.#waitAnimationsFinish();
124
- this.#contentElements.forEach((content) => {
124
+ for (const content of this.#contentElements) {
125
125
  force && this.#bindings.get(content)?.animation?.finish();
126
126
  this.#onContentAnimationFinish(content);
127
- });
127
+ }
128
128
  this.#animationController?.abort();
129
129
  this.#animationController = null;
130
- this.#detailsElements.forEach((details) => {
131
- ["name", "open"].forEach((name) => {
130
+ for (const details of this.#detailsElements) {
131
+ for (const name of ["name", "open"]) {
132
132
  details.removeAttribute(`data-disclosure-${name}`);
133
- });
134
- });
133
+ }
134
+ }
135
135
  this.#detailsElements.length = 0;
136
136
  utils__namespace.restoreAttributes(this.#summaryElements);
137
137
  this.#summaryElements.length = 0;
@@ -211,16 +211,16 @@ var Disclosure = class _Disclosure {
211
211
  if (!details.hasAttribute("data-disclosure-open")) {
212
212
  details.open = false;
213
213
  }
214
- ["block-size", "overflow"].forEach((name2) => {
214
+ for (const name2 of ["block-size", "overflow"]) {
215
215
  content.style.removeProperty(name2);
216
- });
216
+ }
217
217
  }
218
218
  #toggle(details, isExpand, isProgrammatic = false) {
219
219
  if (details.hasAttribute("data-disclosure-open") === isExpand) {
220
220
  return;
221
221
  }
222
222
  if (!isExpand && !isProgrammatic && !this.#settings.collapsible && this.#detailsElements.filter(
223
- (details2) => details2.hasAttribute("data-disclosure-open")
223
+ (d) => d.hasAttribute("data-disclosure-open")
224
224
  ).length <= 1) {
225
225
  return;
226
226
  }
@@ -310,10 +310,10 @@ var Disclosure = class _Disclosure {
310
310
  }
311
311
  async #waitAnimationsFinish() {
312
312
  const promises = [];
313
- this.#contentElements.forEach((content) => {
313
+ for (const content of this.#contentElements) {
314
314
  const animation = this.#bindings.get(content)?.animation;
315
315
  animation && promises.push(waitAnimationFinish(animation));
316
- });
316
+ }
317
317
  await Promise.allSettled(promises);
318
318
  }
319
319
  };
@@ -327,7 +327,7 @@ function waitAnimationFinish(animation) {
327
327
  * WAI-ARIA compliant disclosure pattern implementation in TypeScript.
328
328
  * Using the <details> and <summary> element.
329
329
  *
330
- * @version 2.0.14
330
+ * @version 2.0.16
331
331
  * @author Yusuke Kamiyamane
332
332
  * @license MIT
333
333
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.d.cts CHANGED
@@ -3,7 +3,7 @@
3
3
  * WAI-ARIA compliant disclosure pattern implementation in TypeScript.
4
4
  * Using the <details> and <summary> element.
5
5
  *
6
- * @version 2.0.14
6
+ * @version 2.0.16
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * WAI-ARIA compliant disclosure pattern implementation in TypeScript.
4
4
  * Using the <details> and <summary> element.
5
5
  *
6
- * @version 2.0.14
6
+ * @version 2.0.16
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.js CHANGED
@@ -92,24 +92,24 @@ var Disclosure = class _Disclosure {
92
92
  this.#isDestroyed = true;
93
93
  this.#eventController?.abort();
94
94
  this.#eventController = null;
95
- this.#observers.forEach((observer) => {
95
+ for (const observer of this.#observers) {
96
96
  observer.disconnect();
97
- });
97
+ }
98
98
  this.#observers.length = 0;
99
99
  this.#cleanupRovingTabIndex?.();
100
100
  this.#cleanupRovingTabIndex = null;
101
101
  !force && await this.#waitAnimationsFinish();
102
- this.#contentElements.forEach((content) => {
102
+ for (const content of this.#contentElements) {
103
103
  force && this.#bindings.get(content)?.animation?.finish();
104
104
  this.#onContentAnimationFinish(content);
105
- });
105
+ }
106
106
  this.#animationController?.abort();
107
107
  this.#animationController = null;
108
- this.#detailsElements.forEach((details) => {
109
- ["name", "open"].forEach((name) => {
108
+ for (const details of this.#detailsElements) {
109
+ for (const name of ["name", "open"]) {
110
110
  details.removeAttribute(`data-disclosure-${name}`);
111
- });
112
- });
111
+ }
112
+ }
113
113
  this.#detailsElements.length = 0;
114
114
  utils.restoreAttributes(this.#summaryElements);
115
115
  this.#summaryElements.length = 0;
@@ -189,16 +189,16 @@ var Disclosure = class _Disclosure {
189
189
  if (!details.hasAttribute("data-disclosure-open")) {
190
190
  details.open = false;
191
191
  }
192
- ["block-size", "overflow"].forEach((name2) => {
192
+ for (const name2 of ["block-size", "overflow"]) {
193
193
  content.style.removeProperty(name2);
194
- });
194
+ }
195
195
  }
196
196
  #toggle(details, isExpand, isProgrammatic = false) {
197
197
  if (details.hasAttribute("data-disclosure-open") === isExpand) {
198
198
  return;
199
199
  }
200
200
  if (!isExpand && !isProgrammatic && !this.#settings.collapsible && this.#detailsElements.filter(
201
- (details2) => details2.hasAttribute("data-disclosure-open")
201
+ (d) => d.hasAttribute("data-disclosure-open")
202
202
  ).length <= 1) {
203
203
  return;
204
204
  }
@@ -288,10 +288,10 @@ var Disclosure = class _Disclosure {
288
288
  }
289
289
  async #waitAnimationsFinish() {
290
290
  const promises = [];
291
- this.#contentElements.forEach((content) => {
291
+ for (const content of this.#contentElements) {
292
292
  const animation = this.#bindings.get(content)?.animation;
293
293
  animation && promises.push(waitAnimationFinish(animation));
294
- });
294
+ }
295
295
  await Promise.allSettled(promises);
296
296
  }
297
297
  };
@@ -305,7 +305,7 @@ function waitAnimationFinish(animation) {
305
305
  * WAI-ARIA compliant disclosure pattern implementation in TypeScript.
306
306
  * Using the <details> and <summary> element.
307
307
  *
308
- * @version 2.0.14
308
+ * @version 2.0.16
309
309
  * @author Yusuke Kamiyamane
310
310
  * @license MIT
311
311
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,11 +1,27 @@
1
1
  {
2
2
  "name": "@y14e/disclosure",
3
- "version": "2.0.14",
3
+ "version": "2.0.16",
4
4
  "description": "WAI-ARIA compliant disclosure pattern implementation in TypeScript",
5
+ "keywords": [
6
+ "a11y",
7
+ "accessibility",
8
+ "disclosure",
9
+ "collapsible",
10
+ "component",
11
+ "typescript",
12
+ "utility"
13
+ ],
14
+ "homepage": "https://github.com/y14e/disclosure#readme",
15
+ "bugs": {
16
+ "url": "https://github.com/y14e/disclosure/issues"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/y14e/disclosure.git"
21
+ },
22
+ "license": "MIT",
23
+ "author": "Yusuke Kamiyamane",
5
24
  "type": "module",
6
- "main": "./dist/index.cjs",
7
- "module": "./dist/index.js",
8
- "types": "./dist/index.d.ts",
9
25
  "exports": {
10
26
  ".": {
11
27
  "types": "./dist/index.d.ts",
@@ -13,6 +29,9 @@
13
29
  "require": "./dist/index.cjs"
14
30
  }
15
31
  },
32
+ "main": "./dist/index.cjs",
33
+ "module": "./dist/index.js",
34
+ "types": "./dist/index.d.ts",
16
35
  "files": [
17
36
  "dist",
18
37
  "LICENSE",
@@ -20,28 +39,9 @@
20
39
  ],
21
40
  "scripts": {
22
41
  "build": "tsup",
23
- "prepublishOnly": "npm run build",
24
- "lint": "tsc --noEmit"
25
- },
26
- "keywords": [
27
- "a11y",
28
- "accessibility",
29
- "disclosure",
30
- "collapsible",
31
- "component",
32
- "typescript",
33
- "utility"
34
- ],
35
- "author": "Yusuke Kamiyamane",
36
- "license": "MIT",
37
- "repository": {
38
- "type": "git",
39
- "url": "git+https://github.com/y14e/disclosure.git"
42
+ "lint": "tsc --noEmit",
43
+ "prepublishOnly": "npm run build"
40
44
  },
41
- "bugs": {
42
- "url": "https://github.com/y14e/disclosure/issues"
43
- },
44
- "homepage": "https://github.com/y14e/disclosure#readme",
45
45
  "devDependencies": {
46
46
  "bun-types": "latest",
47
47
  "tsup": "^8.0.0",
@@ -54,7 +54,7 @@
54
54
  "esbuild": true
55
55
  },
56
56
  "dependencies": {
57
- "@y14e/attribute-utils": "^2.0.8",
58
- "@y14e/roving-tabindex": "^3.1.27"
57
+ "@y14e/attribute-utils": "^2.0.12",
58
+ "@y14e/roving-tabindex": "^3.1.31"
59
59
  }
60
60
  }