@y14e/disclosure-css 1.3.5 → 1.3.7

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-css
10
10
 
11
11
  ```ts
12
12
  // npm
13
- import Disclosure from '@y14e/disclosure-css@1.3.5';
13
+ import Disclosure from '@y14e/disclosure-css@1.3.7';
14
14
 
15
15
  // CDNs
16
- import Disclosure from 'https://esm.sh/@y14e/disclosure-css@1.3.5';
16
+ import Disclosure from 'https://esm.sh/@y14e/disclosure-css@1.3.7';
17
17
  // or
18
- import Disclosure from 'https://cdn.jsdelivr.net/npm/@y14e/disclosure-css@1.3.5/+esm';
18
+ import Disclosure from 'https://cdn.jsdelivr.net/npm/@y14e/disclosure-css@1.3.7/+esm';
19
19
  // or
20
- import Disclosure from 'https://esm.unpkg.com/@y14e/disclosure-css@1.3.5';
20
+ import Disclosure from 'https://esm.unpkg.com/@y14e/disclosure-css@1.3.7';
21
21
  ```
22
22
 
23
23
  ## Usage
package/dist/index.cjs CHANGED
@@ -28,28 +28,27 @@ function saveAttributes(elements, attributes) {
28
28
  }
29
29
 
30
30
  // node_modules/@y14e/roving-tabindex/dist/index.js
31
- var defaultParser = (value) => value.split(/\s+/);
32
- var defaultSerializer = (tokens) => tokens.join(" ");
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 = defaultParser,
37
- serialize = defaultSerializer
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) : [];
41
41
  if (caseInsensitive) {
42
42
  const lower = token.toLowerCase();
43
- if (tokens.some((token2) => token2.toLowerCase() === lower)) {
44
- return;
43
+ if (tokens.every((token2) => token2.toLowerCase() !== lower)) {
44
+ tokens.push(token);
45
+ element.setAttribute(attribute, serialize(tokens));
45
46
  }
46
- tokens.push(token);
47
- element.setAttribute(attribute, serialize(tokens));
48
- return;
47
+ } else {
48
+ const set = new Set(tokens);
49
+ set.add(token);
50
+ element.setAttribute(attribute, serialize([...set]));
49
51
  }
50
- const set = new Set(tokens);
51
- set.add(token);
52
- element.setAttribute(attribute, serialize([...set]));
53
52
  }
54
53
  var snapshots2 = /* @__PURE__ */ new WeakMap();
55
54
  function restoreAttributes2(elements) {
@@ -116,21 +115,16 @@ function getFocusables(container = document.body, options = {}) {
116
115
  const elements = [];
117
116
  if (composed || include) {
118
117
  let traverse2 = function(node) {
119
- if (node instanceof Element) {
120
- if (isFocusable(node, {
121
- skipNegativeTabIndexCheck,
122
- skipVisibilityCheck
123
- }) || include?.(node)) {
124
- elements[elements.length] = node;
125
- }
118
+ if (!(node instanceof Element)) {
119
+ return;
120
+ }
121
+ if (isFocusable(node, { skipNegativeTabIndexCheck, skipVisibilityCheck }) || include?.(node)) {
122
+ elements[elements.length] = node;
126
123
  }
127
124
  const children = getComposedChildren(node);
128
125
  for (let i = 0, l = children.length; i < l; i++) {
129
126
  const child = children[i];
130
- if (!child) {
131
- continue;
132
- }
133
- traverse2(child);
127
+ child && traverse2(child);
134
128
  }
135
129
  };
136
130
  traverse2(container);
@@ -138,10 +132,7 @@ function getFocusables(container = document.body, options = {}) {
138
132
  const candidates = container.querySelectorAll(FOCUSABLE_SELECTOR);
139
133
  for (let i = 0, l = candidates.length; i < l; i++) {
140
134
  const candidate = candidates[i];
141
- if (!(candidate instanceof Element)) {
142
- continue;
143
- }
144
- if (isFocusable(candidate, {
135
+ if (candidate && isFocusable(candidate, {
145
136
  skipNegativeTabIndexCheck,
146
137
  skipVisibilityCheck
147
138
  })) {
@@ -244,23 +235,19 @@ function normalizeRadioGroup(elements) {
244
235
  for (const group of map.values()) {
245
236
  placeholder.add(group.find((radio) => radio.checked) ?? group[0]);
246
237
  }
247
- return elements.filter((element) => {
248
- if (isUngroupedRadio(element)) {
249
- return placeholder.has(element);
250
- }
251
- return true;
252
- });
238
+ return elements.filter(
239
+ (element) => isUngroupedRadio(element) ? placeholder.has(element) : true
240
+ );
253
241
  }
254
242
  function sortByTabIndex(elements) {
255
243
  const ordered = [];
256
244
  const natural = [];
257
245
  for (let i = 0, l = elements.length; i < l; i++) {
258
246
  const element = elements[i];
259
- if (!element) {
260
- continue;
247
+ if (element) {
248
+ const target = getTabIndex(element) > 0 ? ordered : natural;
249
+ target[target.length] = element;
261
250
  }
262
- const target = getTabIndex(element) > 0 ? ordered : natural;
263
- target[target.length] = element;
264
251
  }
265
252
  ordered.sort((a, b) => getTabIndex(a) - getTabIndex(b));
266
253
  let count = 0;
@@ -323,6 +310,8 @@ function createRovingTabIndex(container, options = {}) {
323
310
  let {
324
311
  direction,
325
312
  navigationOnly = false,
313
+ noMemory = false,
314
+ noStart = false,
326
315
  selector,
327
316
  typeahead = false,
328
317
  wrap = false
@@ -335,6 +324,14 @@ function createRovingTabIndex(container, options = {}) {
335
324
  console.warn("Invalid navigationOnly option. Fallback: false.");
336
325
  navigationOnly = false;
337
326
  }
327
+ if (typeof noMemory !== "boolean") {
328
+ console.warn("Invalid noMemory option. Fallback: false.");
329
+ noMemory = false;
330
+ }
331
+ if (typeof noStart !== "boolean") {
332
+ console.warn("Invalid noStart option. Fallback: false.");
333
+ noStart = false;
334
+ }
338
335
  if (typeof selector !== "undefined" && (typeof selector !== "string" || !selector.trim())) {
339
336
  console.warn(
340
337
  "Invalid selector. Fallback: all focusable elements (undefined)."
@@ -349,13 +346,15 @@ function createRovingTabIndex(container, options = {}) {
349
346
  console.warn("Invalid wrap option. Fallback: false.");
350
347
  wrap = false;
351
348
  }
352
- const settings = { navigationOnly, typeahead, wrap };
353
- if (direction) {
354
- Object.assign(settings, { direction });
355
- }
356
- if (selector) {
357
- Object.assign(settings, { selector });
358
- }
349
+ const settings = {
350
+ navigationOnly,
351
+ noMemory,
352
+ noStart,
353
+ typeahead,
354
+ wrap
355
+ };
356
+ direction && Object.assign(settings, { direction });
357
+ selector && Object.assign(settings, { selector });
359
358
  const roving = new RovingTabIndex(container, settings);
360
359
  return () => roving.destroy();
361
360
  }
@@ -388,12 +387,25 @@ var RovingTabIndex = class {
388
387
  #initialize() {
389
388
  this.#update(document.activeElement);
390
389
  this.#controller = new AbortController();
390
+ const { signal } = this.#controller;
391
+ document.addEventListener("focusin", this.#onFocusIn, {
392
+ capture: true,
393
+ signal
394
+ });
391
395
  document.addEventListener("keydown", this.#onKeyDown, {
392
396
  capture: true,
393
- signal: this.#controller.signal
397
+ signal
394
398
  });
395
399
  this.#container.setAttribute("data-roving-tabindex-initialized", "");
396
400
  }
401
+ #onFocusIn = (event) => {
402
+ const { target } = event;
403
+ if (!(target instanceof Element)) {
404
+ return;
405
+ }
406
+ const isFocusable22 = this.#focusables.has(target);
407
+ this.#options.noMemory && !isFocusable22 ? this.#update(null) : isFocusable22 && this.#update(target);
408
+ };
397
409
  #onKeyDown = (event) => {
398
410
  if (!event.composedPath().includes(this.#container)) {
399
411
  return;
@@ -424,10 +436,8 @@ var RovingTabIndex = class {
424
436
  return;
425
437
  }
426
438
  event.preventDefault();
427
- event.stopPropagation();
428
439
  const currentIndex = current.indexOf(active);
429
- let rawIndex;
430
- let newIndex = currentIndex;
440
+ let newIndex;
431
441
  let target = current;
432
442
  switch (key) {
433
443
  case "End":
@@ -437,15 +447,17 @@ var RovingTabIndex = class {
437
447
  newIndex = 0;
438
448
  break;
439
449
  case "ArrowLeft":
440
- case "ArrowUp":
441
- rawIndex = currentIndex - 1;
450
+ case "ArrowUp": {
451
+ const rawIndex = currentIndex - 1;
442
452
  newIndex = wrap ? rawIndex : Math.max(rawIndex, 0);
443
453
  break;
454
+ }
444
455
  case "ArrowRight":
445
- case "ArrowDown":
446
- rawIndex = currentIndex + 1;
456
+ case "ArrowDown": {
457
+ const rawIndex = currentIndex + 1;
447
458
  newIndex = wrap ? rawIndex % current.length : Math.min(rawIndex, current.length - 1);
448
459
  break;
460
+ }
449
461
  default: {
450
462
  target = this.#focusablesByFirstChar.get(key.toUpperCase()) ?? [];
451
463
  const foundIndex = target.findIndex(
@@ -455,26 +467,21 @@ var RovingTabIndex = class {
455
467
  }
456
468
  }
457
469
  const focusable = target.at(newIndex);
458
- if (!focusable) {
459
- return;
460
- }
461
- this.#update(focusable);
462
- focusElement(focusable);
470
+ focusable && focusElement(focusable);
463
471
  };
464
472
  #update(active) {
465
473
  const current = new Set(this.#getFocusables());
466
474
  for (const focusable of this.#focusables) {
467
- if (current.has(focusable)) {
468
- continue;
475
+ if (!current.has(focusable)) {
476
+ focusable.isConnected && restoreAttributes2([focusable]);
477
+ this.#focusables.delete(focusable);
478
+ this.#focusablesByFirstChar.forEach((focusables) => {
479
+ const index = focusables.indexOf(focusable);
480
+ index >= 0 && focusables.splice(index, 1);
481
+ });
469
482
  }
470
- focusable.isConnected && restoreAttributes2([focusable]);
471
- this.#focusables.delete(focusable);
472
- this.#focusablesByFirstChar.forEach((focusables) => {
473
- const index = focusables.indexOf(focusable);
474
- index >= 0 && focusables.splice(index, 1);
475
- });
476
483
  }
477
- const { navigationOnly } = this.#options;
484
+ const { navigationOnly, noStart, typeahead } = this.#options;
478
485
  for (const focusable of current) {
479
486
  if (this.#focusables.has(focusable)) {
480
487
  continue;
@@ -484,7 +491,7 @@ var RovingTabIndex = class {
484
491
  saveAttributes2([focusable], ["tabindex"]);
485
492
  focusable.setAttribute("tabindex", "-1");
486
493
  }
487
- if (!this.#options.typeahead) {
494
+ if (!typeahead) {
488
495
  continue;
489
496
  }
490
497
  const value = focusable.ariaKeyShortcuts?.trim();
@@ -505,18 +512,17 @@ var RovingTabIndex = class {
505
512
  this.#focusablesByFirstChar.set(key, focusables);
506
513
  });
507
514
  }
508
- if (navigationOnly) {
509
- return;
510
- }
511
- if (active && this.#focusables.has(active)) {
512
- this.#focusables.forEach((focusable) => {
513
- focusable.setAttribute("tabindex", focusable === active ? "0" : "-1");
514
- });
515
- return;
515
+ if (!navigationOnly) {
516
+ if (active && this.#focusables.has(active)) {
517
+ this.#focusables.forEach((focusable) => {
518
+ focusable.setAttribute("tabindex", focusable === active ? "0" : "-1");
519
+ });
520
+ } else {
521
+ [...this.#focusables].forEach((focusable, i) => {
522
+ focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
523
+ });
524
+ }
516
525
  }
517
- [...this.#focusables].forEach((focusable, i) => {
518
- focusable.setAttribute("tabindex", i ? "-1" : "0");
519
- });
520
526
  }
521
527
  #createSelectorFilter() {
522
528
  const { selector } = this.#options;
@@ -526,7 +532,8 @@ var RovingTabIndex = class {
526
532
  return getFocusables(this.#container, {
527
533
  composed: true,
528
534
  filter: this.#selectorFilter,
529
- skipNegativeTabIndexCheck: !this.#options.navigationOnly
535
+ skipNegativeTabIndexCheck: !this.#options.navigationOnly,
536
+ skipVisibilityCheck: true
530
537
  });
531
538
  }
532
539
  };
@@ -669,7 +676,7 @@ function isFocusable2(element) {
669
676
  * WAI-ARIA compliant disclosure pattern implementation in TypeScript.
670
677
  * Using the <details> and <summary> element.
671
678
  *
672
- * @version 1.3.5
679
+ * @version 1.3.7
673
680
  * @author Yusuke Kamiyamane
674
681
  * @license MIT
675
682
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -681,7 +688,7 @@ function isFocusable2(element) {
681
688
  (**
682
689
  * Attributes Utils
683
690
  *
684
- * @version 1.1.0
691
+ * @version 1.1.2
685
692
  * @author Yusuke Kamiyamane
686
693
  * @license MIT
687
694
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -694,7 +701,7 @@ function isFocusable2(element) {
694
701
  * Lightweight roving tabindex utility with fully focus management.
695
702
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
696
703
  *
697
- * @version 2.0.4
704
+ * @version 3.0.2
698
705
  * @author Yusuke Kamiyamane
699
706
  * @license MIT
700
707
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -706,7 +713,7 @@ function isFocusable2(element) {
706
713
  (**
707
714
  * Attributes Utils
708
715
  *
709
- * @version 1.1.0
716
+ * @version 1.1.2
710
717
  * @author Yusuke Kamiyamane
711
718
  * @license MIT
712
719
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -719,7 +726,7 @@ function isFocusable2(element) {
719
726
  * High-precision focus management utility with full composed tree support.
720
727
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
721
728
  *
722
- * @version 4.3.1
729
+ * @version 4.3.3
723
730
  * @author Yusuke Kamiyamane
724
731
  * @license MIT
725
732
  * @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 1.3.5
6
+ * @version 1.3.7
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 1.3.5
6
+ * @version 1.3.7
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.js CHANGED
@@ -26,28 +26,27 @@ function saveAttributes(elements, attributes) {
26
26
  }
27
27
 
28
28
  // node_modules/@y14e/roving-tabindex/dist/index.js
29
- var defaultParser = (value) => value.split(/\s+/);
30
- var defaultSerializer = (tokens) => tokens.join(" ");
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 = defaultParser,
35
- serialize = defaultSerializer
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) : [];
39
39
  if (caseInsensitive) {
40
40
  const lower = token.toLowerCase();
41
- if (tokens.some((token2) => token2.toLowerCase() === lower)) {
42
- return;
41
+ if (tokens.every((token2) => token2.toLowerCase() !== lower)) {
42
+ tokens.push(token);
43
+ element.setAttribute(attribute, serialize(tokens));
43
44
  }
44
- tokens.push(token);
45
- element.setAttribute(attribute, serialize(tokens));
46
- return;
45
+ } else {
46
+ const set = new Set(tokens);
47
+ set.add(token);
48
+ element.setAttribute(attribute, serialize([...set]));
47
49
  }
48
- const set = new Set(tokens);
49
- set.add(token);
50
- element.setAttribute(attribute, serialize([...set]));
51
50
  }
52
51
  var snapshots2 = /* @__PURE__ */ new WeakMap();
53
52
  function restoreAttributes2(elements) {
@@ -114,21 +113,16 @@ function getFocusables(container = document.body, options = {}) {
114
113
  const elements = [];
115
114
  if (composed || include) {
116
115
  let traverse2 = function(node) {
117
- if (node instanceof Element) {
118
- if (isFocusable(node, {
119
- skipNegativeTabIndexCheck,
120
- skipVisibilityCheck
121
- }) || include?.(node)) {
122
- elements[elements.length] = node;
123
- }
116
+ if (!(node instanceof Element)) {
117
+ return;
118
+ }
119
+ if (isFocusable(node, { skipNegativeTabIndexCheck, skipVisibilityCheck }) || include?.(node)) {
120
+ elements[elements.length] = node;
124
121
  }
125
122
  const children = getComposedChildren(node);
126
123
  for (let i = 0, l = children.length; i < l; i++) {
127
124
  const child = children[i];
128
- if (!child) {
129
- continue;
130
- }
131
- traverse2(child);
125
+ child && traverse2(child);
132
126
  }
133
127
  };
134
128
  traverse2(container);
@@ -136,10 +130,7 @@ function getFocusables(container = document.body, options = {}) {
136
130
  const candidates = container.querySelectorAll(FOCUSABLE_SELECTOR);
137
131
  for (let i = 0, l = candidates.length; i < l; i++) {
138
132
  const candidate = candidates[i];
139
- if (!(candidate instanceof Element)) {
140
- continue;
141
- }
142
- if (isFocusable(candidate, {
133
+ if (candidate && isFocusable(candidate, {
143
134
  skipNegativeTabIndexCheck,
144
135
  skipVisibilityCheck
145
136
  })) {
@@ -242,23 +233,19 @@ function normalizeRadioGroup(elements) {
242
233
  for (const group of map.values()) {
243
234
  placeholder.add(group.find((radio) => radio.checked) ?? group[0]);
244
235
  }
245
- return elements.filter((element) => {
246
- if (isUngroupedRadio(element)) {
247
- return placeholder.has(element);
248
- }
249
- return true;
250
- });
236
+ return elements.filter(
237
+ (element) => isUngroupedRadio(element) ? placeholder.has(element) : true
238
+ );
251
239
  }
252
240
  function sortByTabIndex(elements) {
253
241
  const ordered = [];
254
242
  const natural = [];
255
243
  for (let i = 0, l = elements.length; i < l; i++) {
256
244
  const element = elements[i];
257
- if (!element) {
258
- continue;
245
+ if (element) {
246
+ const target = getTabIndex(element) > 0 ? ordered : natural;
247
+ target[target.length] = element;
259
248
  }
260
- const target = getTabIndex(element) > 0 ? ordered : natural;
261
- target[target.length] = element;
262
249
  }
263
250
  ordered.sort((a, b) => getTabIndex(a) - getTabIndex(b));
264
251
  let count = 0;
@@ -321,6 +308,8 @@ function createRovingTabIndex(container, options = {}) {
321
308
  let {
322
309
  direction,
323
310
  navigationOnly = false,
311
+ noMemory = false,
312
+ noStart = false,
324
313
  selector,
325
314
  typeahead = false,
326
315
  wrap = false
@@ -333,6 +322,14 @@ function createRovingTabIndex(container, options = {}) {
333
322
  console.warn("Invalid navigationOnly option. Fallback: false.");
334
323
  navigationOnly = false;
335
324
  }
325
+ if (typeof noMemory !== "boolean") {
326
+ console.warn("Invalid noMemory option. Fallback: false.");
327
+ noMemory = false;
328
+ }
329
+ if (typeof noStart !== "boolean") {
330
+ console.warn("Invalid noStart option. Fallback: false.");
331
+ noStart = false;
332
+ }
336
333
  if (typeof selector !== "undefined" && (typeof selector !== "string" || !selector.trim())) {
337
334
  console.warn(
338
335
  "Invalid selector. Fallback: all focusable elements (undefined)."
@@ -347,13 +344,15 @@ function createRovingTabIndex(container, options = {}) {
347
344
  console.warn("Invalid wrap option. Fallback: false.");
348
345
  wrap = false;
349
346
  }
350
- const settings = { navigationOnly, typeahead, wrap };
351
- if (direction) {
352
- Object.assign(settings, { direction });
353
- }
354
- if (selector) {
355
- Object.assign(settings, { selector });
356
- }
347
+ const settings = {
348
+ navigationOnly,
349
+ noMemory,
350
+ noStart,
351
+ typeahead,
352
+ wrap
353
+ };
354
+ direction && Object.assign(settings, { direction });
355
+ selector && Object.assign(settings, { selector });
357
356
  const roving = new RovingTabIndex(container, settings);
358
357
  return () => roving.destroy();
359
358
  }
@@ -386,12 +385,25 @@ var RovingTabIndex = class {
386
385
  #initialize() {
387
386
  this.#update(document.activeElement);
388
387
  this.#controller = new AbortController();
388
+ const { signal } = this.#controller;
389
+ document.addEventListener("focusin", this.#onFocusIn, {
390
+ capture: true,
391
+ signal
392
+ });
389
393
  document.addEventListener("keydown", this.#onKeyDown, {
390
394
  capture: true,
391
- signal: this.#controller.signal
395
+ signal
392
396
  });
393
397
  this.#container.setAttribute("data-roving-tabindex-initialized", "");
394
398
  }
399
+ #onFocusIn = (event) => {
400
+ const { target } = event;
401
+ if (!(target instanceof Element)) {
402
+ return;
403
+ }
404
+ const isFocusable22 = this.#focusables.has(target);
405
+ this.#options.noMemory && !isFocusable22 ? this.#update(null) : isFocusable22 && this.#update(target);
406
+ };
395
407
  #onKeyDown = (event) => {
396
408
  if (!event.composedPath().includes(this.#container)) {
397
409
  return;
@@ -422,10 +434,8 @@ var RovingTabIndex = class {
422
434
  return;
423
435
  }
424
436
  event.preventDefault();
425
- event.stopPropagation();
426
437
  const currentIndex = current.indexOf(active);
427
- let rawIndex;
428
- let newIndex = currentIndex;
438
+ let newIndex;
429
439
  let target = current;
430
440
  switch (key) {
431
441
  case "End":
@@ -435,15 +445,17 @@ var RovingTabIndex = class {
435
445
  newIndex = 0;
436
446
  break;
437
447
  case "ArrowLeft":
438
- case "ArrowUp":
439
- rawIndex = currentIndex - 1;
448
+ case "ArrowUp": {
449
+ const rawIndex = currentIndex - 1;
440
450
  newIndex = wrap ? rawIndex : Math.max(rawIndex, 0);
441
451
  break;
452
+ }
442
453
  case "ArrowRight":
443
- case "ArrowDown":
444
- rawIndex = currentIndex + 1;
454
+ case "ArrowDown": {
455
+ const rawIndex = currentIndex + 1;
445
456
  newIndex = wrap ? rawIndex % current.length : Math.min(rawIndex, current.length - 1);
446
457
  break;
458
+ }
447
459
  default: {
448
460
  target = this.#focusablesByFirstChar.get(key.toUpperCase()) ?? [];
449
461
  const foundIndex = target.findIndex(
@@ -453,26 +465,21 @@ var RovingTabIndex = class {
453
465
  }
454
466
  }
455
467
  const focusable = target.at(newIndex);
456
- if (!focusable) {
457
- return;
458
- }
459
- this.#update(focusable);
460
- focusElement(focusable);
468
+ focusable && focusElement(focusable);
461
469
  };
462
470
  #update(active) {
463
471
  const current = new Set(this.#getFocusables());
464
472
  for (const focusable of this.#focusables) {
465
- if (current.has(focusable)) {
466
- continue;
473
+ if (!current.has(focusable)) {
474
+ focusable.isConnected && restoreAttributes2([focusable]);
475
+ this.#focusables.delete(focusable);
476
+ this.#focusablesByFirstChar.forEach((focusables) => {
477
+ const index = focusables.indexOf(focusable);
478
+ index >= 0 && focusables.splice(index, 1);
479
+ });
467
480
  }
468
- focusable.isConnected && restoreAttributes2([focusable]);
469
- this.#focusables.delete(focusable);
470
- this.#focusablesByFirstChar.forEach((focusables) => {
471
- const index = focusables.indexOf(focusable);
472
- index >= 0 && focusables.splice(index, 1);
473
- });
474
481
  }
475
- const { navigationOnly } = this.#options;
482
+ const { navigationOnly, noStart, typeahead } = this.#options;
476
483
  for (const focusable of current) {
477
484
  if (this.#focusables.has(focusable)) {
478
485
  continue;
@@ -482,7 +489,7 @@ var RovingTabIndex = class {
482
489
  saveAttributes2([focusable], ["tabindex"]);
483
490
  focusable.setAttribute("tabindex", "-1");
484
491
  }
485
- if (!this.#options.typeahead) {
492
+ if (!typeahead) {
486
493
  continue;
487
494
  }
488
495
  const value = focusable.ariaKeyShortcuts?.trim();
@@ -503,18 +510,17 @@ var RovingTabIndex = class {
503
510
  this.#focusablesByFirstChar.set(key, focusables);
504
511
  });
505
512
  }
506
- if (navigationOnly) {
507
- return;
508
- }
509
- if (active && this.#focusables.has(active)) {
510
- this.#focusables.forEach((focusable) => {
511
- focusable.setAttribute("tabindex", focusable === active ? "0" : "-1");
512
- });
513
- return;
513
+ if (!navigationOnly) {
514
+ if (active && this.#focusables.has(active)) {
515
+ this.#focusables.forEach((focusable) => {
516
+ focusable.setAttribute("tabindex", focusable === active ? "0" : "-1");
517
+ });
518
+ } else {
519
+ [...this.#focusables].forEach((focusable, i) => {
520
+ focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
521
+ });
522
+ }
514
523
  }
515
- [...this.#focusables].forEach((focusable, i) => {
516
- focusable.setAttribute("tabindex", i ? "-1" : "0");
517
- });
518
524
  }
519
525
  #createSelectorFilter() {
520
526
  const { selector } = this.#options;
@@ -524,7 +530,8 @@ var RovingTabIndex = class {
524
530
  return getFocusables(this.#container, {
525
531
  composed: true,
526
532
  filter: this.#selectorFilter,
527
- skipNegativeTabIndexCheck: !this.#options.navigationOnly
533
+ skipNegativeTabIndexCheck: !this.#options.navigationOnly,
534
+ skipVisibilityCheck: true
528
535
  });
529
536
  }
530
537
  };
@@ -667,7 +674,7 @@ function isFocusable2(element) {
667
674
  * WAI-ARIA compliant disclosure pattern implementation in TypeScript.
668
675
  * Using the <details> and <summary> element.
669
676
  *
670
- * @version 1.3.5
677
+ * @version 1.3.7
671
678
  * @author Yusuke Kamiyamane
672
679
  * @license MIT
673
680
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -679,7 +686,7 @@ function isFocusable2(element) {
679
686
  (**
680
687
  * Attributes Utils
681
688
  *
682
- * @version 1.1.0
689
+ * @version 1.1.2
683
690
  * @author Yusuke Kamiyamane
684
691
  * @license MIT
685
692
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -692,7 +699,7 @@ function isFocusable2(element) {
692
699
  * Lightweight roving tabindex utility with fully focus management.
693
700
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
694
701
  *
695
- * @version 2.0.4
702
+ * @version 3.0.2
696
703
  * @author Yusuke Kamiyamane
697
704
  * @license MIT
698
705
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -704,7 +711,7 @@ function isFocusable2(element) {
704
711
  (**
705
712
  * Attributes Utils
706
713
  *
707
- * @version 1.1.0
714
+ * @version 1.1.2
708
715
  * @author Yusuke Kamiyamane
709
716
  * @license MIT
710
717
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -717,7 +724,7 @@ function isFocusable2(element) {
717
724
  * High-precision focus management utility with full composed tree support.
718
725
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
719
726
  *
720
- * @version 4.3.1
727
+ * @version 4.3.3
721
728
  * @author Yusuke Kamiyamane
722
729
  * @license MIT
723
730
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@y14e/disclosure-css",
3
- "version": "1.3.5",
3
+ "version": "1.3.7",
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-css#readme",
45
45
  "devDependencies": {
46
- "@y14e/attributes-utils": "^1.1.0",
47
- "@y14e/roving-tabindex": "^2.0.4",
46
+ "@y14e/attributes-utils": "^1.1.2",
47
+ "@y14e/roving-tabindex": "^3.0.2",
48
48
  "bun-types": "latest",
49
49
  "tsup": "^8.0.0",
50
50
  "typescript": "^5.6.0"