@y14e/roving-tabindex 1.3.2 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -5
- package/dist/index.cjs +26 -23
- package/dist/index.d.cts +1 -2
- package/dist/index.d.ts +1 -2
- package/dist/index.js +26 -23
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -44,7 +44,6 @@ interface RovingTabIndexOptions {
|
|
|
44
44
|
direction?: 'horizontal' | 'vertical' | undefined; // default: both (undefined)
|
|
45
45
|
navigationOnly?: boolean; // default: false
|
|
46
46
|
selector?: string | undefined;
|
|
47
|
-
skipVisibilityCheck?: boolean; // default: false
|
|
48
47
|
typeahead?: boolean; // default: false
|
|
49
48
|
wrap?: boolean; // default: false
|
|
50
49
|
}
|
|
@@ -54,10 +53,6 @@ interface RovingTabIndexOptions {
|
|
|
54
53
|
|
|
55
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.
|
|
56
55
|
|
|
57
|
-
### `skipVisibilityCheck`
|
|
58
|
-
|
|
59
|
-
If `true`, skips `checkVisibility()` when determining focusability. Useful for initializing.
|
|
60
|
-
|
|
61
56
|
### `typeahead`
|
|
62
57
|
|
|
63
58
|
If `true`, enables character-based focus navigation. Typing a character moves focus to the next matching element.
|
package/dist/index.cjs
CHANGED
|
@@ -61,6 +61,7 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
61
61
|
composed = false,
|
|
62
62
|
filter,
|
|
63
63
|
include,
|
|
64
|
+
skipNegativeTabIndexCheck = false,
|
|
64
65
|
skipVisibilityCheck = false
|
|
65
66
|
} = options;
|
|
66
67
|
if (typeof composed !== "boolean") {
|
|
@@ -79,6 +80,10 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
79
80
|
);
|
|
80
81
|
include = void 0;
|
|
81
82
|
}
|
|
83
|
+
if (typeof skipNegativeTabIndexCheck !== "boolean") {
|
|
84
|
+
console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
|
|
85
|
+
skipNegativeTabIndexCheck = false;
|
|
86
|
+
}
|
|
82
87
|
if (typeof skipVisibilityCheck !== "boolean") {
|
|
83
88
|
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
84
89
|
skipVisibilityCheck = false;
|
|
@@ -87,7 +92,10 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
87
92
|
if (composed || include) {
|
|
88
93
|
let traverse2 = function(node) {
|
|
89
94
|
if (node instanceof Element) {
|
|
90
|
-
if (isFocusable(node, {
|
|
95
|
+
if (isFocusable(node, {
|
|
96
|
+
skipNegativeTabIndexCheck,
|
|
97
|
+
skipVisibilityCheck
|
|
98
|
+
}) || include?.(node)) {
|
|
91
99
|
elements[elements.length] = node;
|
|
92
100
|
}
|
|
93
101
|
}
|
|
@@ -108,7 +116,10 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
108
116
|
if (!(candidate instanceof Element)) {
|
|
109
117
|
continue;
|
|
110
118
|
}
|
|
111
|
-
if (isFocusable(candidate, {
|
|
119
|
+
if (isFocusable(candidate, {
|
|
120
|
+
skipNegativeTabIndexCheck,
|
|
121
|
+
skipVisibilityCheck
|
|
122
|
+
})) {
|
|
112
123
|
elements[elements.length] = candidate;
|
|
113
124
|
}
|
|
114
125
|
}
|
|
@@ -121,7 +132,11 @@ function isFocusable(element, options = {}) {
|
|
|
121
132
|
console.warn("Invalid element");
|
|
122
133
|
return false;
|
|
123
134
|
}
|
|
124
|
-
let { skipVisibilityCheck = false } = options;
|
|
135
|
+
let { skipNegativeTabIndexCheck = false, skipVisibilityCheck = false } = options;
|
|
136
|
+
if (typeof skipNegativeTabIndexCheck !== "boolean") {
|
|
137
|
+
console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
|
|
138
|
+
skipNegativeTabIndexCheck = false;
|
|
139
|
+
}
|
|
125
140
|
if (typeof skipVisibilityCheck !== "boolean") {
|
|
126
141
|
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
127
142
|
skipVisibilityCheck = false;
|
|
@@ -129,10 +144,12 @@ function isFocusable(element, options = {}) {
|
|
|
129
144
|
if (element.hasAttribute("hidden") || isInert(element)) {
|
|
130
145
|
return false;
|
|
131
146
|
}
|
|
132
|
-
if (getTabIndex(element) < 0) {
|
|
147
|
+
if (!skipNegativeTabIndexCheck && getTabIndex(element) < 0) {
|
|
133
148
|
return false;
|
|
134
149
|
}
|
|
135
|
-
if (!element.matches(
|
|
150
|
+
if (!element.matches(
|
|
151
|
+
skipNegativeTabIndexCheck ? FOCUSABLE_SELECTOR.replace(/(,\s*)?\[tabindex="-1"\]/g, "") : FOCUSABLE_SELECTOR
|
|
152
|
+
)) {
|
|
136
153
|
return false;
|
|
137
154
|
}
|
|
138
155
|
if (isDisabledDeep(element)) {
|
|
@@ -284,7 +301,6 @@ function createRovingTabIndex(container, options = {}) {
|
|
|
284
301
|
direction,
|
|
285
302
|
navigationOnly = false,
|
|
286
303
|
selector,
|
|
287
|
-
skipVisibilityCheck = false,
|
|
288
304
|
typeahead = false,
|
|
289
305
|
wrap = false
|
|
290
306
|
} = options;
|
|
@@ -302,10 +318,6 @@ function createRovingTabIndex(container, options = {}) {
|
|
|
302
318
|
);
|
|
303
319
|
selector = void 0;
|
|
304
320
|
}
|
|
305
|
-
if (typeof skipVisibilityCheck !== "boolean") {
|
|
306
|
-
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
307
|
-
skipVisibilityCheck = false;
|
|
308
|
-
}
|
|
309
321
|
if (typeof typeahead !== "boolean") {
|
|
310
322
|
console.warn("Invalid typeahead option. Fallback: false.");
|
|
311
323
|
typeahead = false;
|
|
@@ -318,7 +330,6 @@ function createRovingTabIndex(container, options = {}) {
|
|
|
318
330
|
direction,
|
|
319
331
|
navigationOnly,
|
|
320
332
|
selector,
|
|
321
|
-
skipVisibilityCheck,
|
|
322
333
|
typeahead,
|
|
323
334
|
wrap
|
|
324
335
|
});
|
|
@@ -427,14 +438,7 @@ var RovingTabIndex = class {
|
|
|
427
438
|
focusElement(focusable);
|
|
428
439
|
};
|
|
429
440
|
#update(active) {
|
|
430
|
-
const current =
|
|
431
|
-
...this.#getFocusables(),
|
|
432
|
-
...getFocusables(this.#container, {
|
|
433
|
-
composed: true,
|
|
434
|
-
filter: this.#selectorFilter,
|
|
435
|
-
skipVisibilityCheck: !!this.#options.skipVisibilityCheck
|
|
436
|
-
})
|
|
437
|
-
]);
|
|
441
|
+
const current = new Set(this.#getFocusables());
|
|
438
442
|
for (const focusable of this.#focusables) {
|
|
439
443
|
if (current.has(focusable)) {
|
|
440
444
|
continue;
|
|
@@ -498,8 +502,7 @@ var RovingTabIndex = class {
|
|
|
498
502
|
return getFocusables(this.#container, {
|
|
499
503
|
composed: true,
|
|
500
504
|
filter: this.#selectorFilter,
|
|
501
|
-
|
|
502
|
-
skipVisibilityCheck: !!this.#options.skipVisibilityCheck
|
|
505
|
+
skipNegativeTabIndexCheck: !this.#options.navigationOnly
|
|
503
506
|
});
|
|
504
507
|
}
|
|
505
508
|
};
|
|
@@ -518,7 +521,7 @@ function getActiveElement() {
|
|
|
518
521
|
* Lightweight roving tabindex utility with fully focus management.
|
|
519
522
|
* Designed for accessible menus, tabs, toolbars, and composite widgets.
|
|
520
523
|
*
|
|
521
|
-
* @version
|
|
524
|
+
* @version 2.0.1
|
|
522
525
|
* @author Yusuke Kamiyamane
|
|
523
526
|
* @license MIT
|
|
524
527
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -543,7 +546,7 @@ power-focusable/dist/index.js:
|
|
|
543
546
|
* High-precision focus management utility with full composed tree support.
|
|
544
547
|
* Handles complex focus rules including tabindex ordering, radio groups, inert.
|
|
545
548
|
*
|
|
546
|
-
* @version 4.
|
|
549
|
+
* @version 4.3.0
|
|
547
550
|
* @author Yusuke Kamiyamane
|
|
548
551
|
* @license MIT
|
|
549
552
|
* @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
|
|
6
|
+
* @version 2.0.1
|
|
7
7
|
* @author Yusuke Kamiyamane
|
|
8
8
|
* @license MIT
|
|
9
9
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -13,7 +13,6 @@ interface RovingTabIndexOptions {
|
|
|
13
13
|
readonly direction?: 'horizontal' | 'vertical' | undefined;
|
|
14
14
|
readonly navigationOnly?: boolean;
|
|
15
15
|
readonly selector?: string | undefined;
|
|
16
|
-
readonly skipVisibilityCheck?: boolean;
|
|
17
16
|
readonly typeahead?: boolean;
|
|
18
17
|
readonly wrap?: boolean;
|
|
19
18
|
}
|
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
|
|
6
|
+
* @version 2.0.1
|
|
7
7
|
* @author Yusuke Kamiyamane
|
|
8
8
|
* @license MIT
|
|
9
9
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -13,7 +13,6 @@ interface RovingTabIndexOptions {
|
|
|
13
13
|
readonly direction?: 'horizontal' | 'vertical' | undefined;
|
|
14
14
|
readonly navigationOnly?: boolean;
|
|
15
15
|
readonly selector?: string | undefined;
|
|
16
|
-
readonly skipVisibilityCheck?: boolean;
|
|
17
16
|
readonly typeahead?: boolean;
|
|
18
17
|
readonly wrap?: boolean;
|
|
19
18
|
}
|
package/dist/index.js
CHANGED
|
@@ -59,6 +59,7 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
59
59
|
composed = false,
|
|
60
60
|
filter,
|
|
61
61
|
include,
|
|
62
|
+
skipNegativeTabIndexCheck = false,
|
|
62
63
|
skipVisibilityCheck = false
|
|
63
64
|
} = options;
|
|
64
65
|
if (typeof composed !== "boolean") {
|
|
@@ -77,6 +78,10 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
77
78
|
);
|
|
78
79
|
include = void 0;
|
|
79
80
|
}
|
|
81
|
+
if (typeof skipNegativeTabIndexCheck !== "boolean") {
|
|
82
|
+
console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
|
|
83
|
+
skipNegativeTabIndexCheck = false;
|
|
84
|
+
}
|
|
80
85
|
if (typeof skipVisibilityCheck !== "boolean") {
|
|
81
86
|
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
82
87
|
skipVisibilityCheck = false;
|
|
@@ -85,7 +90,10 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
85
90
|
if (composed || include) {
|
|
86
91
|
let traverse2 = function(node) {
|
|
87
92
|
if (node instanceof Element) {
|
|
88
|
-
if (isFocusable(node, {
|
|
93
|
+
if (isFocusable(node, {
|
|
94
|
+
skipNegativeTabIndexCheck,
|
|
95
|
+
skipVisibilityCheck
|
|
96
|
+
}) || include?.(node)) {
|
|
89
97
|
elements[elements.length] = node;
|
|
90
98
|
}
|
|
91
99
|
}
|
|
@@ -106,7 +114,10 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
106
114
|
if (!(candidate instanceof Element)) {
|
|
107
115
|
continue;
|
|
108
116
|
}
|
|
109
|
-
if (isFocusable(candidate, {
|
|
117
|
+
if (isFocusable(candidate, {
|
|
118
|
+
skipNegativeTabIndexCheck,
|
|
119
|
+
skipVisibilityCheck
|
|
120
|
+
})) {
|
|
110
121
|
elements[elements.length] = candidate;
|
|
111
122
|
}
|
|
112
123
|
}
|
|
@@ -119,7 +130,11 @@ function isFocusable(element, options = {}) {
|
|
|
119
130
|
console.warn("Invalid element");
|
|
120
131
|
return false;
|
|
121
132
|
}
|
|
122
|
-
let { skipVisibilityCheck = false } = options;
|
|
133
|
+
let { skipNegativeTabIndexCheck = false, skipVisibilityCheck = false } = options;
|
|
134
|
+
if (typeof skipNegativeTabIndexCheck !== "boolean") {
|
|
135
|
+
console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
|
|
136
|
+
skipNegativeTabIndexCheck = false;
|
|
137
|
+
}
|
|
123
138
|
if (typeof skipVisibilityCheck !== "boolean") {
|
|
124
139
|
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
125
140
|
skipVisibilityCheck = false;
|
|
@@ -127,10 +142,12 @@ function isFocusable(element, options = {}) {
|
|
|
127
142
|
if (element.hasAttribute("hidden") || isInert(element)) {
|
|
128
143
|
return false;
|
|
129
144
|
}
|
|
130
|
-
if (getTabIndex(element) < 0) {
|
|
145
|
+
if (!skipNegativeTabIndexCheck && getTabIndex(element) < 0) {
|
|
131
146
|
return false;
|
|
132
147
|
}
|
|
133
|
-
if (!element.matches(
|
|
148
|
+
if (!element.matches(
|
|
149
|
+
skipNegativeTabIndexCheck ? FOCUSABLE_SELECTOR.replace(/(,\s*)?\[tabindex="-1"\]/g, "") : FOCUSABLE_SELECTOR
|
|
150
|
+
)) {
|
|
134
151
|
return false;
|
|
135
152
|
}
|
|
136
153
|
if (isDisabledDeep(element)) {
|
|
@@ -282,7 +299,6 @@ function createRovingTabIndex(container, options = {}) {
|
|
|
282
299
|
direction,
|
|
283
300
|
navigationOnly = false,
|
|
284
301
|
selector,
|
|
285
|
-
skipVisibilityCheck = false,
|
|
286
302
|
typeahead = false,
|
|
287
303
|
wrap = false
|
|
288
304
|
} = options;
|
|
@@ -300,10 +316,6 @@ function createRovingTabIndex(container, options = {}) {
|
|
|
300
316
|
);
|
|
301
317
|
selector = void 0;
|
|
302
318
|
}
|
|
303
|
-
if (typeof skipVisibilityCheck !== "boolean") {
|
|
304
|
-
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
305
|
-
skipVisibilityCheck = false;
|
|
306
|
-
}
|
|
307
319
|
if (typeof typeahead !== "boolean") {
|
|
308
320
|
console.warn("Invalid typeahead option. Fallback: false.");
|
|
309
321
|
typeahead = false;
|
|
@@ -316,7 +328,6 @@ function createRovingTabIndex(container, options = {}) {
|
|
|
316
328
|
direction,
|
|
317
329
|
navigationOnly,
|
|
318
330
|
selector,
|
|
319
|
-
skipVisibilityCheck,
|
|
320
331
|
typeahead,
|
|
321
332
|
wrap
|
|
322
333
|
});
|
|
@@ -425,14 +436,7 @@ var RovingTabIndex = class {
|
|
|
425
436
|
focusElement(focusable);
|
|
426
437
|
};
|
|
427
438
|
#update(active) {
|
|
428
|
-
const current =
|
|
429
|
-
...this.#getFocusables(),
|
|
430
|
-
...getFocusables(this.#container, {
|
|
431
|
-
composed: true,
|
|
432
|
-
filter: this.#selectorFilter,
|
|
433
|
-
skipVisibilityCheck: !!this.#options.skipVisibilityCheck
|
|
434
|
-
})
|
|
435
|
-
]);
|
|
439
|
+
const current = new Set(this.#getFocusables());
|
|
436
440
|
for (const focusable of this.#focusables) {
|
|
437
441
|
if (current.has(focusable)) {
|
|
438
442
|
continue;
|
|
@@ -496,8 +500,7 @@ var RovingTabIndex = class {
|
|
|
496
500
|
return getFocusables(this.#container, {
|
|
497
501
|
composed: true,
|
|
498
502
|
filter: this.#selectorFilter,
|
|
499
|
-
|
|
500
|
-
skipVisibilityCheck: !!this.#options.skipVisibilityCheck
|
|
503
|
+
skipNegativeTabIndexCheck: !this.#options.navigationOnly
|
|
501
504
|
});
|
|
502
505
|
}
|
|
503
506
|
};
|
|
@@ -516,7 +519,7 @@ function getActiveElement() {
|
|
|
516
519
|
* Lightweight roving tabindex utility with fully focus management.
|
|
517
520
|
* Designed for accessible menus, tabs, toolbars, and composite widgets.
|
|
518
521
|
*
|
|
519
|
-
* @version
|
|
522
|
+
* @version 2.0.1
|
|
520
523
|
* @author Yusuke Kamiyamane
|
|
521
524
|
* @license MIT
|
|
522
525
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -541,7 +544,7 @@ power-focusable/dist/index.js:
|
|
|
541
544
|
* High-precision focus management utility with full composed tree support.
|
|
542
545
|
* Handles complex focus rules including tabindex ordering, radio groups, inert.
|
|
543
546
|
*
|
|
544
|
-
* @version 4.
|
|
547
|
+
* @version 4.3.0
|
|
545
548
|
* @author Yusuke Kamiyamane
|
|
546
549
|
* @license MIT
|
|
547
550
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@y14e/roving-tabindex",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Lightweight roving tabindex utility with fully focus management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@y14e/attributes-utils": "^1.0.5",
|
|
52
52
|
"bun-types": "latest",
|
|
53
|
-
"power-focusable": "^4.
|
|
53
|
+
"power-focusable": "^4.3.0",
|
|
54
54
|
"tsup": "^8.0.0",
|
|
55
55
|
"typescript": "^5.6.0"
|
|
56
56
|
},
|