@y14e/roving-tabindex 1.2.11 → 1.3.0
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 +6 -1
- package/dist/index.cjs +21 -4
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +21 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Roving Tabindex
|
|
2
2
|
|
|
3
|
-
Lightweight roving tabindex utility with fully focus management. Designed for accessible menus, tabs, toolbars, and composite widgets.
|
|
3
|
+
Lightweight roving `tabindex` utility with fully focus management. Designed for accessible menus, tabs, toolbars, and composite widgets.
|
|
4
4
|
|
|
5
5
|
> [!NOTE]
|
|
6
6
|
> Focus traversal works across shadow DOM boundaries using composed-tree-aware focus detection powered by [Power Focusable](https://github.com/y14e/power-focusable).
|
|
@@ -42,12 +42,17 @@ const cleanup = createRovingTabIndex(container, options);
|
|
|
42
42
|
```ts
|
|
43
43
|
interface RovingTabIndexOptions {
|
|
44
44
|
direction?: 'horizontal' | 'vertical' | undefined; // default: both (undefined)
|
|
45
|
+
navigationOnly?: boolean; // default: false
|
|
45
46
|
selector?: string | undefined;
|
|
46
47
|
typeahead?: boolean; // default: false
|
|
47
48
|
wrap?: boolean; // default: false
|
|
48
49
|
}
|
|
49
50
|
```
|
|
50
51
|
|
|
52
|
+
### `navigationOnly`
|
|
53
|
+
|
|
54
|
+
If `true`, enables keyboard navigation without modifying `tabindex`. Useful for widgets like accordions where all items should remain tabbable while still supporting arrow key navigation.
|
|
55
|
+
|
|
51
56
|
### `typeahead`
|
|
52
57
|
|
|
53
58
|
If `true`, enables character-based focus navigation. Typing a character moves focus to the next matching element.
|
package/dist/index.cjs
CHANGED
|
@@ -266,11 +266,21 @@ function createRovingTabIndex(container, options = {}) {
|
|
|
266
266
|
return () => {
|
|
267
267
|
};
|
|
268
268
|
}
|
|
269
|
-
let {
|
|
269
|
+
let {
|
|
270
|
+
direction,
|
|
271
|
+
navigationOnly = false,
|
|
272
|
+
selector,
|
|
273
|
+
typeahead = false,
|
|
274
|
+
wrap = false
|
|
275
|
+
} = options;
|
|
270
276
|
if (typeof direction !== "undefined" && !["horizontal", "vertical"].includes(direction)) {
|
|
271
277
|
console.warn("Invalid direction option. Fallback: both (undefined).");
|
|
272
278
|
direction = void 0;
|
|
273
279
|
}
|
|
280
|
+
if (typeof navigationOnly !== "boolean") {
|
|
281
|
+
console.warn("Invalid navigationOnly option. Fallback: false.");
|
|
282
|
+
navigationOnly = false;
|
|
283
|
+
}
|
|
274
284
|
if (typeof selector !== "undefined" && typeof selector !== "string") {
|
|
275
285
|
console.warn(
|
|
276
286
|
"Invalid selector. Fallback: all focusable elements (undefined)."
|
|
@@ -287,6 +297,7 @@ function createRovingTabIndex(container, options = {}) {
|
|
|
287
297
|
}
|
|
288
298
|
const roving = new RovingTabIndex(container, {
|
|
289
299
|
direction,
|
|
300
|
+
navigationOnly,
|
|
290
301
|
selector,
|
|
291
302
|
typeahead,
|
|
292
303
|
wrap
|
|
@@ -414,13 +425,16 @@ var RovingTabIndex = class {
|
|
|
414
425
|
index >= 0 && focusables.splice(index, 1);
|
|
415
426
|
});
|
|
416
427
|
}
|
|
428
|
+
const { navigationOnly } = this.#options;
|
|
417
429
|
for (const focusable of current) {
|
|
418
430
|
if (this.#focusables.has(focusable)) {
|
|
419
431
|
continue;
|
|
420
432
|
}
|
|
421
433
|
this.#focusables.add(focusable);
|
|
422
|
-
|
|
423
|
-
|
|
434
|
+
if (!navigationOnly) {
|
|
435
|
+
saveAttributes([focusable], ["tabindex"]);
|
|
436
|
+
focusable.setAttribute("tabindex", "-1");
|
|
437
|
+
}
|
|
424
438
|
if (!this.#options.typeahead) {
|
|
425
439
|
continue;
|
|
426
440
|
}
|
|
@@ -442,6 +456,9 @@ var RovingTabIndex = class {
|
|
|
442
456
|
this.#focusablesByFirstChar.set(key, focusables);
|
|
443
457
|
});
|
|
444
458
|
}
|
|
459
|
+
if (navigationOnly) {
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
445
462
|
if (active && this.#focusables.has(active)) {
|
|
446
463
|
this.#focusables.forEach((focusable) => {
|
|
447
464
|
focusable.setAttribute("tabindex", focusable === active ? "0" : "-1");
|
|
@@ -479,7 +496,7 @@ function getActiveElement() {
|
|
|
479
496
|
* Lightweight roving tabindex utility with fully focus management.
|
|
480
497
|
* Designed for accessible menus, tabs, toolbars, and composite widgets.
|
|
481
498
|
*
|
|
482
|
-
* @version 1.
|
|
499
|
+
* @version 1.3.0
|
|
483
500
|
* @author Yusuke Kamiyamane
|
|
484
501
|
* @license MIT
|
|
485
502
|
* @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 1.
|
|
6
|
+
* @version 1.3.0
|
|
7
7
|
* @author Yusuke Kamiyamane
|
|
8
8
|
* @license MIT
|
|
9
9
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
interface RovingTabIndexOptions {
|
|
13
13
|
readonly direction?: 'horizontal' | 'vertical' | undefined;
|
|
14
|
+
readonly navigationOnly?: boolean;
|
|
14
15
|
readonly selector?: string | undefined;
|
|
15
16
|
readonly typeahead?: boolean;
|
|
16
17
|
readonly wrap?: boolean;
|
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 1.
|
|
6
|
+
* @version 1.3.0
|
|
7
7
|
* @author Yusuke Kamiyamane
|
|
8
8
|
* @license MIT
|
|
9
9
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
interface RovingTabIndexOptions {
|
|
13
13
|
readonly direction?: 'horizontal' | 'vertical' | undefined;
|
|
14
|
+
readonly navigationOnly?: boolean;
|
|
14
15
|
readonly selector?: string | undefined;
|
|
15
16
|
readonly typeahead?: boolean;
|
|
16
17
|
readonly wrap?: boolean;
|
package/dist/index.js
CHANGED
|
@@ -264,11 +264,21 @@ function createRovingTabIndex(container, options = {}) {
|
|
|
264
264
|
return () => {
|
|
265
265
|
};
|
|
266
266
|
}
|
|
267
|
-
let {
|
|
267
|
+
let {
|
|
268
|
+
direction,
|
|
269
|
+
navigationOnly = false,
|
|
270
|
+
selector,
|
|
271
|
+
typeahead = false,
|
|
272
|
+
wrap = false
|
|
273
|
+
} = options;
|
|
268
274
|
if (typeof direction !== "undefined" && !["horizontal", "vertical"].includes(direction)) {
|
|
269
275
|
console.warn("Invalid direction option. Fallback: both (undefined).");
|
|
270
276
|
direction = void 0;
|
|
271
277
|
}
|
|
278
|
+
if (typeof navigationOnly !== "boolean") {
|
|
279
|
+
console.warn("Invalid navigationOnly option. Fallback: false.");
|
|
280
|
+
navigationOnly = false;
|
|
281
|
+
}
|
|
272
282
|
if (typeof selector !== "undefined" && typeof selector !== "string") {
|
|
273
283
|
console.warn(
|
|
274
284
|
"Invalid selector. Fallback: all focusable elements (undefined)."
|
|
@@ -285,6 +295,7 @@ function createRovingTabIndex(container, options = {}) {
|
|
|
285
295
|
}
|
|
286
296
|
const roving = new RovingTabIndex(container, {
|
|
287
297
|
direction,
|
|
298
|
+
navigationOnly,
|
|
288
299
|
selector,
|
|
289
300
|
typeahead,
|
|
290
301
|
wrap
|
|
@@ -412,13 +423,16 @@ var RovingTabIndex = class {
|
|
|
412
423
|
index >= 0 && focusables.splice(index, 1);
|
|
413
424
|
});
|
|
414
425
|
}
|
|
426
|
+
const { navigationOnly } = this.#options;
|
|
415
427
|
for (const focusable of current) {
|
|
416
428
|
if (this.#focusables.has(focusable)) {
|
|
417
429
|
continue;
|
|
418
430
|
}
|
|
419
431
|
this.#focusables.add(focusable);
|
|
420
|
-
|
|
421
|
-
|
|
432
|
+
if (!navigationOnly) {
|
|
433
|
+
saveAttributes([focusable], ["tabindex"]);
|
|
434
|
+
focusable.setAttribute("tabindex", "-1");
|
|
435
|
+
}
|
|
422
436
|
if (!this.#options.typeahead) {
|
|
423
437
|
continue;
|
|
424
438
|
}
|
|
@@ -440,6 +454,9 @@ var RovingTabIndex = class {
|
|
|
440
454
|
this.#focusablesByFirstChar.set(key, focusables);
|
|
441
455
|
});
|
|
442
456
|
}
|
|
457
|
+
if (navigationOnly) {
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
443
460
|
if (active && this.#focusables.has(active)) {
|
|
444
461
|
this.#focusables.forEach((focusable) => {
|
|
445
462
|
focusable.setAttribute("tabindex", focusable === active ? "0" : "-1");
|
|
@@ -477,7 +494,7 @@ function getActiveElement() {
|
|
|
477
494
|
* Lightweight roving tabindex utility with fully focus management.
|
|
478
495
|
* Designed for accessible menus, tabs, toolbars, and composite widgets.
|
|
479
496
|
*
|
|
480
|
-
* @version 1.
|
|
497
|
+
* @version 1.3.0
|
|
481
498
|
* @author Yusuke Kamiyamane
|
|
482
499
|
* @license MIT
|
|
483
500
|
* @copyright Copyright (c) Yusuke Kamiyamane
|