@y14e/roving-tabindex 3.0.0 → 3.0.2

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/roving-tabindex
10
10
 
11
11
  ```ts
12
12
  // npm
13
- import { createRovingTabIndex } from '@y14e/roving-tabindex@3.0.0';
13
+ import { createRovingTabIndex } from '@y14e/roving-tabindex@3.0.2';
14
14
 
15
15
  // CDNs
16
- import { createRovingTabIndex } from 'https://esm.sh/@y14e/roving-tabindex@3.0.0';
16
+ import { createRovingTabIndex } from 'https://esm.sh/@y14e/roving-tabindex@3.0.2';
17
17
  // or
18
- import { createRovingTabIndex } from 'https://cdn.jsdelivr.net/npm/@y14e/roving-tabindex@3.0.0/+esm';
18
+ import { createRovingTabIndex } from 'https://cdn.jsdelivr.net/npm/@y14e/roving-tabindex@3.0.2/+esm';
19
19
  // or
20
- import { createRovingTabIndex } from 'https://esm.unpkg.com/@y14e/roving-tabindex@3.0.0';
20
+ import { createRovingTabIndex } from 'https://esm.unpkg.com/@y14e/roving-tabindex@3.0.2';
21
21
  ```
22
22
 
23
23
  ## 📦 APIs
package/dist/index.cjs CHANGED
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
 
3
3
  // node_modules/@y14e/attributes-utils/dist/index.js
4
- var defaultParser = (value) => value.split(/\s+/);
5
- var defaultSerializer = (tokens) => tokens.join(" ");
4
+ var DEFAULT_PARSER = (value) => value.split(/\s+/);
5
+ var DEFAULT_SERIALIZER = (tokens) => tokens.join(" ");
6
6
  function addTokenToAttribute(element, attribute, token, options = {}) {
7
7
  const {
8
8
  caseInsensitive = false,
9
- parse = defaultParser,
10
- serialize = defaultSerializer
9
+ parse = DEFAULT_PARSER,
10
+ serialize = DEFAULT_SERIALIZER
11
11
  } = options;
12
12
  const value = element.getAttribute(attribute)?.trim();
13
13
  const tokens = value ? parse(value).filter(Boolean) : [];
@@ -17,11 +17,11 @@ function addTokenToAttribute(element, attribute, token, options = {}) {
17
17
  tokens.push(token);
18
18
  element.setAttribute(attribute, serialize(tokens));
19
19
  }
20
- return;
20
+ } else {
21
+ const set = new Set(tokens);
22
+ set.add(token);
23
+ element.setAttribute(attribute, serialize([...set]));
21
24
  }
22
- const set = new Set(tokens);
23
- set.add(token);
24
- element.setAttribute(attribute, serialize([...set]));
25
25
  }
26
26
  var snapshots = /* @__PURE__ */ new WeakMap();
27
27
  function restoreAttributes(elements) {
@@ -90,21 +90,16 @@ function getFocusables(container = document.body, options = {}) {
90
90
  const elements = [];
91
91
  if (composed || include) {
92
92
  let traverse2 = function(node) {
93
- if (node instanceof Element) {
94
- if (isFocusable(node, {
95
- skipNegativeTabIndexCheck,
96
- skipVisibilityCheck
97
- }) || include?.(node)) {
98
- elements[elements.length] = node;
99
- }
93
+ if (!(node instanceof Element)) {
94
+ return;
95
+ }
96
+ if (isFocusable(node, { skipNegativeTabIndexCheck, skipVisibilityCheck }) || include?.(node)) {
97
+ elements[elements.length] = node;
100
98
  }
101
99
  const children = getComposedChildren(node);
102
100
  for (let i = 0, l = children.length; i < l; i++) {
103
101
  const child = children[i];
104
- if (!child) {
105
- continue;
106
- }
107
- traverse2(child);
102
+ child && traverse2(child);
108
103
  }
109
104
  };
110
105
  traverse2(container);
@@ -112,10 +107,7 @@ function getFocusables(container = document.body, options = {}) {
112
107
  const candidates = container.querySelectorAll(FOCUSABLE_SELECTOR);
113
108
  for (let i = 0, l = candidates.length; i < l; i++) {
114
109
  const candidate = candidates[i];
115
- if (!(candidate instanceof Element)) {
116
- continue;
117
- }
118
- if (isFocusable(candidate, {
110
+ if (candidate && isFocusable(candidate, {
119
111
  skipNegativeTabIndexCheck,
120
112
  skipVisibilityCheck
121
113
  })) {
@@ -218,23 +210,19 @@ function normalizeRadioGroup(elements) {
218
210
  for (const group of map.values()) {
219
211
  placeholder.add(group.find((radio) => radio.checked) ?? group[0]);
220
212
  }
221
- return elements.filter((element) => {
222
- if (isUngroupedRadio(element)) {
223
- return placeholder.has(element);
224
- }
225
- return true;
226
- });
213
+ return elements.filter(
214
+ (element) => isUngroupedRadio(element) ? placeholder.has(element) : true
215
+ );
227
216
  }
228
217
  function sortByTabIndex(elements) {
229
218
  const ordered = [];
230
219
  const natural = [];
231
220
  for (let i = 0, l = elements.length; i < l; i++) {
232
221
  const element = elements[i];
233
- if (!element) {
234
- continue;
222
+ if (element) {
223
+ const target = getTabIndex(element) > 0 ? ordered : natural;
224
+ target[target.length] = element;
235
225
  }
236
- const target = getTabIndex(element) > 0 ? ordered : natural;
237
- target[target.length] = element;
238
226
  }
239
227
  ordered.sort((a, b) => getTabIndex(a) - getTabIndex(b));
240
228
  let count = 0;
@@ -393,11 +381,7 @@ var RovingTabIndex = class {
393
381
  return;
394
382
  }
395
383
  const isFocusable2 = this.#focusables.has(target);
396
- if (this.#options.noMemory && !isFocusable2) {
397
- this.#update(null);
398
- return;
399
- }
400
- isFocusable2 && this.#update(target);
384
+ this.#options.noMemory && !isFocusable2 ? this.#update(null) : isFocusable2 && this.#update(target);
401
385
  };
402
386
  #onKeyDown = (event) => {
403
387
  if (!event.composedPath().includes(this.#container)) {
@@ -430,8 +414,7 @@ var RovingTabIndex = class {
430
414
  }
431
415
  event.preventDefault();
432
416
  const currentIndex = current.indexOf(active);
433
- let rawIndex;
434
- let newIndex = currentIndex;
417
+ let newIndex;
435
418
  let target = current;
436
419
  switch (key) {
437
420
  case "End":
@@ -441,15 +424,17 @@ var RovingTabIndex = class {
441
424
  newIndex = 0;
442
425
  break;
443
426
  case "ArrowLeft":
444
- case "ArrowUp":
445
- rawIndex = currentIndex - 1;
427
+ case "ArrowUp": {
428
+ const rawIndex = currentIndex - 1;
446
429
  newIndex = wrap ? rawIndex : Math.max(rawIndex, 0);
447
430
  break;
431
+ }
448
432
  case "ArrowRight":
449
- case "ArrowDown":
450
- rawIndex = currentIndex + 1;
433
+ case "ArrowDown": {
434
+ const rawIndex = currentIndex + 1;
451
435
  newIndex = wrap ? rawIndex % current.length : Math.min(rawIndex, current.length - 1);
452
436
  break;
437
+ }
453
438
  default: {
454
439
  target = this.#focusablesByFirstChar.get(key.toUpperCase()) ?? [];
455
440
  const foundIndex = target.findIndex(
@@ -464,15 +449,14 @@ var RovingTabIndex = class {
464
449
  #update(active) {
465
450
  const current = new Set(this.#getFocusables());
466
451
  for (const focusable of this.#focusables) {
467
- if (current.has(focusable)) {
468
- continue;
452
+ if (!current.has(focusable)) {
453
+ focusable.isConnected && restoreAttributes([focusable]);
454
+ this.#focusables.delete(focusable);
455
+ this.#focusablesByFirstChar.forEach((focusables) => {
456
+ const index = focusables.indexOf(focusable);
457
+ index >= 0 && focusables.splice(index, 1);
458
+ });
469
459
  }
470
- focusable.isConnected && restoreAttributes([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
460
  }
477
461
  const { navigationOnly, noStart, typeahead } = this.#options;
478
462
  for (const focusable of current) {
@@ -505,18 +489,17 @@ var RovingTabIndex = class {
505
489
  this.#focusablesByFirstChar.set(key, focusables);
506
490
  });
507
491
  }
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;
492
+ if (!navigationOnly) {
493
+ if (active && this.#focusables.has(active)) {
494
+ this.#focusables.forEach((focusable) => {
495
+ focusable.setAttribute("tabindex", focusable === active ? "0" : "-1");
496
+ });
497
+ } else {
498
+ [...this.#focusables].forEach((focusable, i) => {
499
+ focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
500
+ });
501
+ }
516
502
  }
517
- [...this.#focusables].forEach((focusable, i) => {
518
- focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
519
- });
520
503
  }
521
504
  #createSelectorFilter() {
522
505
  const { selector } = this.#options;
@@ -546,7 +529,7 @@ function getActiveElement() {
546
529
  * Lightweight roving tabindex utility with fully focus management.
547
530
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
548
531
  *
549
- * @version 3.0.0
532
+ * @version 3.0.2
550
533
  * @author Yusuke Kamiyamane
551
534
  * @license MIT
552
535
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -558,7 +541,7 @@ function getActiveElement() {
558
541
  (**
559
542
  * Attributes Utils
560
543
  *
561
- * @version 1.1.1
544
+ * @version 1.1.2
562
545
  * @author Yusuke Kamiyamane
563
546
  * @license MIT
564
547
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -571,7 +554,7 @@ power-focusable/dist/index.js:
571
554
  * High-precision focus management utility with full composed tree support.
572
555
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
573
556
  *
574
- * @version 4.3.2
557
+ * @version 4.3.3
575
558
  * @author Yusuke Kamiyamane
576
559
  * @license MIT
577
560
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.d.cts CHANGED
@@ -3,7 +3,7 @@
3
3
  * Lightweight roving tabindex utility with fully focus management.
4
4
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
5
5
  *
6
- * @version 3.0.0
6
+ * @version 3.0.2
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
  * Lightweight roving tabindex utility with fully focus management.
4
4
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
5
5
  *
6
- * @version 3.0.0
6
+ * @version 3.0.2
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  // node_modules/@y14e/attributes-utils/dist/index.js
2
- var defaultParser = (value) => value.split(/\s+/);
3
- var defaultSerializer = (tokens) => tokens.join(" ");
2
+ var DEFAULT_PARSER = (value) => value.split(/\s+/);
3
+ var DEFAULT_SERIALIZER = (tokens) => tokens.join(" ");
4
4
  function addTokenToAttribute(element, attribute, token, options = {}) {
5
5
  const {
6
6
  caseInsensitive = false,
7
- parse = defaultParser,
8
- serialize = defaultSerializer
7
+ parse = DEFAULT_PARSER,
8
+ serialize = DEFAULT_SERIALIZER
9
9
  } = options;
10
10
  const value = element.getAttribute(attribute)?.trim();
11
11
  const tokens = value ? parse(value).filter(Boolean) : [];
@@ -15,11 +15,11 @@ function addTokenToAttribute(element, attribute, token, options = {}) {
15
15
  tokens.push(token);
16
16
  element.setAttribute(attribute, serialize(tokens));
17
17
  }
18
- return;
18
+ } else {
19
+ const set = new Set(tokens);
20
+ set.add(token);
21
+ element.setAttribute(attribute, serialize([...set]));
19
22
  }
20
- const set = new Set(tokens);
21
- set.add(token);
22
- element.setAttribute(attribute, serialize([...set]));
23
23
  }
24
24
  var snapshots = /* @__PURE__ */ new WeakMap();
25
25
  function restoreAttributes(elements) {
@@ -88,21 +88,16 @@ function getFocusables(container = document.body, options = {}) {
88
88
  const elements = [];
89
89
  if (composed || include) {
90
90
  let traverse2 = function(node) {
91
- if (node instanceof Element) {
92
- if (isFocusable(node, {
93
- skipNegativeTabIndexCheck,
94
- skipVisibilityCheck
95
- }) || include?.(node)) {
96
- elements[elements.length] = node;
97
- }
91
+ if (!(node instanceof Element)) {
92
+ return;
93
+ }
94
+ if (isFocusable(node, { skipNegativeTabIndexCheck, skipVisibilityCheck }) || include?.(node)) {
95
+ elements[elements.length] = node;
98
96
  }
99
97
  const children = getComposedChildren(node);
100
98
  for (let i = 0, l = children.length; i < l; i++) {
101
99
  const child = children[i];
102
- if (!child) {
103
- continue;
104
- }
105
- traverse2(child);
100
+ child && traverse2(child);
106
101
  }
107
102
  };
108
103
  traverse2(container);
@@ -110,10 +105,7 @@ function getFocusables(container = document.body, options = {}) {
110
105
  const candidates = container.querySelectorAll(FOCUSABLE_SELECTOR);
111
106
  for (let i = 0, l = candidates.length; i < l; i++) {
112
107
  const candidate = candidates[i];
113
- if (!(candidate instanceof Element)) {
114
- continue;
115
- }
116
- if (isFocusable(candidate, {
108
+ if (candidate && isFocusable(candidate, {
117
109
  skipNegativeTabIndexCheck,
118
110
  skipVisibilityCheck
119
111
  })) {
@@ -216,23 +208,19 @@ function normalizeRadioGroup(elements) {
216
208
  for (const group of map.values()) {
217
209
  placeholder.add(group.find((radio) => radio.checked) ?? group[0]);
218
210
  }
219
- return elements.filter((element) => {
220
- if (isUngroupedRadio(element)) {
221
- return placeholder.has(element);
222
- }
223
- return true;
224
- });
211
+ return elements.filter(
212
+ (element) => isUngroupedRadio(element) ? placeholder.has(element) : true
213
+ );
225
214
  }
226
215
  function sortByTabIndex(elements) {
227
216
  const ordered = [];
228
217
  const natural = [];
229
218
  for (let i = 0, l = elements.length; i < l; i++) {
230
219
  const element = elements[i];
231
- if (!element) {
232
- continue;
220
+ if (element) {
221
+ const target = getTabIndex(element) > 0 ? ordered : natural;
222
+ target[target.length] = element;
233
223
  }
234
- const target = getTabIndex(element) > 0 ? ordered : natural;
235
- target[target.length] = element;
236
224
  }
237
225
  ordered.sort((a, b) => getTabIndex(a) - getTabIndex(b));
238
226
  let count = 0;
@@ -391,11 +379,7 @@ var RovingTabIndex = class {
391
379
  return;
392
380
  }
393
381
  const isFocusable2 = this.#focusables.has(target);
394
- if (this.#options.noMemory && !isFocusable2) {
395
- this.#update(null);
396
- return;
397
- }
398
- isFocusable2 && this.#update(target);
382
+ this.#options.noMemory && !isFocusable2 ? this.#update(null) : isFocusable2 && this.#update(target);
399
383
  };
400
384
  #onKeyDown = (event) => {
401
385
  if (!event.composedPath().includes(this.#container)) {
@@ -428,8 +412,7 @@ var RovingTabIndex = class {
428
412
  }
429
413
  event.preventDefault();
430
414
  const currentIndex = current.indexOf(active);
431
- let rawIndex;
432
- let newIndex = currentIndex;
415
+ let newIndex;
433
416
  let target = current;
434
417
  switch (key) {
435
418
  case "End":
@@ -439,15 +422,17 @@ var RovingTabIndex = class {
439
422
  newIndex = 0;
440
423
  break;
441
424
  case "ArrowLeft":
442
- case "ArrowUp":
443
- rawIndex = currentIndex - 1;
425
+ case "ArrowUp": {
426
+ const rawIndex = currentIndex - 1;
444
427
  newIndex = wrap ? rawIndex : Math.max(rawIndex, 0);
445
428
  break;
429
+ }
446
430
  case "ArrowRight":
447
- case "ArrowDown":
448
- rawIndex = currentIndex + 1;
431
+ case "ArrowDown": {
432
+ const rawIndex = currentIndex + 1;
449
433
  newIndex = wrap ? rawIndex % current.length : Math.min(rawIndex, current.length - 1);
450
434
  break;
435
+ }
451
436
  default: {
452
437
  target = this.#focusablesByFirstChar.get(key.toUpperCase()) ?? [];
453
438
  const foundIndex = target.findIndex(
@@ -462,15 +447,14 @@ var RovingTabIndex = class {
462
447
  #update(active) {
463
448
  const current = new Set(this.#getFocusables());
464
449
  for (const focusable of this.#focusables) {
465
- if (current.has(focusable)) {
466
- continue;
450
+ if (!current.has(focusable)) {
451
+ focusable.isConnected && restoreAttributes([focusable]);
452
+ this.#focusables.delete(focusable);
453
+ this.#focusablesByFirstChar.forEach((focusables) => {
454
+ const index = focusables.indexOf(focusable);
455
+ index >= 0 && focusables.splice(index, 1);
456
+ });
467
457
  }
468
- focusable.isConnected && restoreAttributes([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
458
  }
475
459
  const { navigationOnly, noStart, typeahead } = this.#options;
476
460
  for (const focusable of current) {
@@ -503,18 +487,17 @@ var RovingTabIndex = class {
503
487
  this.#focusablesByFirstChar.set(key, focusables);
504
488
  });
505
489
  }
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;
490
+ if (!navigationOnly) {
491
+ if (active && this.#focusables.has(active)) {
492
+ this.#focusables.forEach((focusable) => {
493
+ focusable.setAttribute("tabindex", focusable === active ? "0" : "-1");
494
+ });
495
+ } else {
496
+ [...this.#focusables].forEach((focusable, i) => {
497
+ focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
498
+ });
499
+ }
514
500
  }
515
- [...this.#focusables].forEach((focusable, i) => {
516
- focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
517
- });
518
501
  }
519
502
  #createSelectorFilter() {
520
503
  const { selector } = this.#options;
@@ -544,7 +527,7 @@ function getActiveElement() {
544
527
  * Lightweight roving tabindex utility with fully focus management.
545
528
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
546
529
  *
547
- * @version 3.0.0
530
+ * @version 3.0.2
548
531
  * @author Yusuke Kamiyamane
549
532
  * @license MIT
550
533
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -556,7 +539,7 @@ function getActiveElement() {
556
539
  (**
557
540
  * Attributes Utils
558
541
  *
559
- * @version 1.1.1
542
+ * @version 1.1.2
560
543
  * @author Yusuke Kamiyamane
561
544
  * @license MIT
562
545
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -569,7 +552,7 @@ power-focusable/dist/index.js:
569
552
  * High-precision focus management utility with full composed tree support.
570
553
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
571
554
  *
572
- * @version 4.3.2
555
+ * @version 4.3.3
573
556
  * @author Yusuke Kamiyamane
574
557
  * @license MIT
575
558
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@y14e/roving-tabindex",
3
- "version": "3.0.0",
3
+ "version": "3.0.2",
4
4
  "description": "Lightweight roving tabindex utility with fully focus management",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -48,9 +48,9 @@
48
48
  },
49
49
  "homepage": "https://github.com/y14e/roving-tabindex#readme",
50
50
  "devDependencies": {
51
- "@y14e/attributes-utils": "^1.1.1",
51
+ "@y14e/attributes-utils": "^1.1.2",
52
52
  "bun-types": "latest",
53
- "power-focusable": "^4.3.2",
53
+ "power-focusable": "^4.3.3",
54
54
  "tsup": "^8.0.0",
55
55
  "typescript": "^5.6.0"
56
56
  },