@y14e/roving-tabindex 1.3.0 → 1.3.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 +5 -0
- package/dist/index.cjs +31 -9
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +31 -9
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -44,6 +44,7 @@ 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
|
|
47
48
|
typeahead?: boolean; // default: false
|
|
48
49
|
wrap?: boolean; // default: false
|
|
49
50
|
}
|
|
@@ -53,6 +54,10 @@ interface RovingTabIndexOptions {
|
|
|
53
54
|
|
|
54
55
|
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
56
|
|
|
57
|
+
### `skipVisibilityCheck`
|
|
58
|
+
|
|
59
|
+
If `true`, skips `checkVisibility()` when determining focusability. Useful for initializing.
|
|
60
|
+
|
|
56
61
|
### `typeahead`
|
|
57
62
|
|
|
58
63
|
If `true`, enables character-based focus navigation. Typing a character moves focus to the next matching element.
|
package/dist/index.cjs
CHANGED
|
@@ -57,7 +57,12 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
57
57
|
console.warn("Invalid container element. Fallback: <body> element.");
|
|
58
58
|
container = document.body;
|
|
59
59
|
}
|
|
60
|
-
let {
|
|
60
|
+
let {
|
|
61
|
+
composed = false,
|
|
62
|
+
filter,
|
|
63
|
+
include,
|
|
64
|
+
skipVisibilityCheck = false
|
|
65
|
+
} = options;
|
|
61
66
|
if (typeof composed !== "boolean") {
|
|
62
67
|
console.warn("Invalid composed option. Fallback: false.");
|
|
63
68
|
composed = false;
|
|
@@ -74,11 +79,15 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
74
79
|
);
|
|
75
80
|
include = void 0;
|
|
76
81
|
}
|
|
82
|
+
if (typeof skipVisibilityCheck !== "boolean") {
|
|
83
|
+
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
84
|
+
skipVisibilityCheck = false;
|
|
85
|
+
}
|
|
77
86
|
const elements = [];
|
|
78
87
|
if (composed || include) {
|
|
79
88
|
let traverse2 = function(node) {
|
|
80
89
|
if (node instanceof Element) {
|
|
81
|
-
if (isFocusable(node) || include?.(node)) {
|
|
90
|
+
if (isFocusable(node, { skipVisibilityCheck }) || include?.(node)) {
|
|
82
91
|
elements[elements.length] = node;
|
|
83
92
|
}
|
|
84
93
|
}
|
|
@@ -99,7 +108,7 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
99
108
|
if (!(candidate instanceof Element)) {
|
|
100
109
|
continue;
|
|
101
110
|
}
|
|
102
|
-
if (isFocusable(candidate)) {
|
|
111
|
+
if (isFocusable(candidate, { skipVisibilityCheck })) {
|
|
103
112
|
elements[elements.length] = candidate;
|
|
104
113
|
}
|
|
105
114
|
}
|
|
@@ -107,11 +116,16 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
107
116
|
const unfiltered = normalizeRadioGroup(sortByTabIndex(elements));
|
|
108
117
|
return filter ? unfiltered.filter(filter) : unfiltered;
|
|
109
118
|
}
|
|
110
|
-
function isFocusable(element) {
|
|
119
|
+
function isFocusable(element, options = {}) {
|
|
111
120
|
if (!(element instanceof Element)) {
|
|
112
121
|
console.warn("Invalid element");
|
|
113
122
|
return false;
|
|
114
123
|
}
|
|
124
|
+
let { skipVisibilityCheck = false } = options;
|
|
125
|
+
if (typeof skipVisibilityCheck !== "boolean") {
|
|
126
|
+
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
127
|
+
skipVisibilityCheck = false;
|
|
128
|
+
}
|
|
115
129
|
if (element.hasAttribute("hidden") || isInert(element)) {
|
|
116
130
|
return false;
|
|
117
131
|
}
|
|
@@ -124,7 +138,7 @@ function isFocusable(element) {
|
|
|
124
138
|
if (isDisabledDeep(element)) {
|
|
125
139
|
return false;
|
|
126
140
|
}
|
|
127
|
-
if (!element.checkVisibility({
|
|
141
|
+
if (!skipVisibilityCheck && !element.checkVisibility({
|
|
128
142
|
contentVisibilityAuto: true,
|
|
129
143
|
opacityProperty: true,
|
|
130
144
|
visibilityProperty: true
|
|
@@ -270,6 +284,7 @@ function createRovingTabIndex(container, options = {}) {
|
|
|
270
284
|
direction,
|
|
271
285
|
navigationOnly = false,
|
|
272
286
|
selector,
|
|
287
|
+
skipVisibilityCheck = false,
|
|
273
288
|
typeahead = false,
|
|
274
289
|
wrap = false
|
|
275
290
|
} = options;
|
|
@@ -287,6 +302,10 @@ function createRovingTabIndex(container, options = {}) {
|
|
|
287
302
|
);
|
|
288
303
|
selector = void 0;
|
|
289
304
|
}
|
|
305
|
+
if (typeof skipVisibilityCheck !== "boolean") {
|
|
306
|
+
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
307
|
+
skipVisibilityCheck = false;
|
|
308
|
+
}
|
|
290
309
|
if (typeof typeahead !== "boolean") {
|
|
291
310
|
console.warn("Invalid typeahead option. Fallback: false.");
|
|
292
311
|
typeahead = false;
|
|
@@ -299,6 +318,7 @@ function createRovingTabIndex(container, options = {}) {
|
|
|
299
318
|
direction,
|
|
300
319
|
navigationOnly,
|
|
301
320
|
selector,
|
|
321
|
+
skipVisibilityCheck,
|
|
302
322
|
typeahead,
|
|
303
323
|
wrap
|
|
304
324
|
});
|
|
@@ -411,7 +431,8 @@ var RovingTabIndex = class {
|
|
|
411
431
|
...this.#getFocusables(),
|
|
412
432
|
...getFocusables(this.#container, {
|
|
413
433
|
composed: true,
|
|
414
|
-
filter: this.#selectorFilter
|
|
434
|
+
filter: this.#selectorFilter,
|
|
435
|
+
skipVisibilityCheck: !!this.#options.skipVisibilityCheck
|
|
415
436
|
})
|
|
416
437
|
]);
|
|
417
438
|
for (const focusable of this.#focusables) {
|
|
@@ -477,7 +498,8 @@ var RovingTabIndex = class {
|
|
|
477
498
|
return getFocusables(this.#container, {
|
|
478
499
|
composed: true,
|
|
479
500
|
filter: this.#selectorFilter,
|
|
480
|
-
include: (element) => this.#focusables.has(element)
|
|
501
|
+
include: (element) => this.#focusables.has(element),
|
|
502
|
+
skipVisibilityCheck: !!this.#options.skipVisibilityCheck
|
|
481
503
|
});
|
|
482
504
|
}
|
|
483
505
|
};
|
|
@@ -496,7 +518,7 @@ function getActiveElement() {
|
|
|
496
518
|
* Lightweight roving tabindex utility with fully focus management.
|
|
497
519
|
* Designed for accessible menus, tabs, toolbars, and composite widgets.
|
|
498
520
|
*
|
|
499
|
-
* @version 1.3.
|
|
521
|
+
* @version 1.3.2
|
|
500
522
|
* @author Yusuke Kamiyamane
|
|
501
523
|
* @license MIT
|
|
502
524
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -521,7 +543,7 @@ power-focusable/dist/index.js:
|
|
|
521
543
|
* High-precision focus management utility with full composed tree support.
|
|
522
544
|
* Handles complex focus rules including tabindex ordering, radio groups, inert.
|
|
523
545
|
*
|
|
524
|
-
* @version 4.1
|
|
546
|
+
* @version 4.2.1
|
|
525
547
|
* @author Yusuke Kamiyamane
|
|
526
548
|
* @license MIT
|
|
527
549
|
* @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.3.
|
|
6
|
+
* @version 1.3.2
|
|
7
7
|
* @author Yusuke Kamiyamane
|
|
8
8
|
* @license MIT
|
|
9
9
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -13,6 +13,7 @@ interface RovingTabIndexOptions {
|
|
|
13
13
|
readonly direction?: 'horizontal' | 'vertical' | undefined;
|
|
14
14
|
readonly navigationOnly?: boolean;
|
|
15
15
|
readonly selector?: string | undefined;
|
|
16
|
+
readonly skipVisibilityCheck?: boolean;
|
|
16
17
|
readonly typeahead?: boolean;
|
|
17
18
|
readonly wrap?: boolean;
|
|
18
19
|
}
|
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.3.
|
|
6
|
+
* @version 1.3.2
|
|
7
7
|
* @author Yusuke Kamiyamane
|
|
8
8
|
* @license MIT
|
|
9
9
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -13,6 +13,7 @@ interface RovingTabIndexOptions {
|
|
|
13
13
|
readonly direction?: 'horizontal' | 'vertical' | undefined;
|
|
14
14
|
readonly navigationOnly?: boolean;
|
|
15
15
|
readonly selector?: string | undefined;
|
|
16
|
+
readonly skipVisibilityCheck?: boolean;
|
|
16
17
|
readonly typeahead?: boolean;
|
|
17
18
|
readonly wrap?: boolean;
|
|
18
19
|
}
|
package/dist/index.js
CHANGED
|
@@ -55,7 +55,12 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
55
55
|
console.warn("Invalid container element. Fallback: <body> element.");
|
|
56
56
|
container = document.body;
|
|
57
57
|
}
|
|
58
|
-
let {
|
|
58
|
+
let {
|
|
59
|
+
composed = false,
|
|
60
|
+
filter,
|
|
61
|
+
include,
|
|
62
|
+
skipVisibilityCheck = false
|
|
63
|
+
} = options;
|
|
59
64
|
if (typeof composed !== "boolean") {
|
|
60
65
|
console.warn("Invalid composed option. Fallback: false.");
|
|
61
66
|
composed = false;
|
|
@@ -72,11 +77,15 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
72
77
|
);
|
|
73
78
|
include = void 0;
|
|
74
79
|
}
|
|
80
|
+
if (typeof skipVisibilityCheck !== "boolean") {
|
|
81
|
+
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
82
|
+
skipVisibilityCheck = false;
|
|
83
|
+
}
|
|
75
84
|
const elements = [];
|
|
76
85
|
if (composed || include) {
|
|
77
86
|
let traverse2 = function(node) {
|
|
78
87
|
if (node instanceof Element) {
|
|
79
|
-
if (isFocusable(node) || include?.(node)) {
|
|
88
|
+
if (isFocusable(node, { skipVisibilityCheck }) || include?.(node)) {
|
|
80
89
|
elements[elements.length] = node;
|
|
81
90
|
}
|
|
82
91
|
}
|
|
@@ -97,7 +106,7 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
97
106
|
if (!(candidate instanceof Element)) {
|
|
98
107
|
continue;
|
|
99
108
|
}
|
|
100
|
-
if (isFocusable(candidate)) {
|
|
109
|
+
if (isFocusable(candidate, { skipVisibilityCheck })) {
|
|
101
110
|
elements[elements.length] = candidate;
|
|
102
111
|
}
|
|
103
112
|
}
|
|
@@ -105,11 +114,16 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
105
114
|
const unfiltered = normalizeRadioGroup(sortByTabIndex(elements));
|
|
106
115
|
return filter ? unfiltered.filter(filter) : unfiltered;
|
|
107
116
|
}
|
|
108
|
-
function isFocusable(element) {
|
|
117
|
+
function isFocusable(element, options = {}) {
|
|
109
118
|
if (!(element instanceof Element)) {
|
|
110
119
|
console.warn("Invalid element");
|
|
111
120
|
return false;
|
|
112
121
|
}
|
|
122
|
+
let { skipVisibilityCheck = false } = options;
|
|
123
|
+
if (typeof skipVisibilityCheck !== "boolean") {
|
|
124
|
+
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
125
|
+
skipVisibilityCheck = false;
|
|
126
|
+
}
|
|
113
127
|
if (element.hasAttribute("hidden") || isInert(element)) {
|
|
114
128
|
return false;
|
|
115
129
|
}
|
|
@@ -122,7 +136,7 @@ function isFocusable(element) {
|
|
|
122
136
|
if (isDisabledDeep(element)) {
|
|
123
137
|
return false;
|
|
124
138
|
}
|
|
125
|
-
if (!element.checkVisibility({
|
|
139
|
+
if (!skipVisibilityCheck && !element.checkVisibility({
|
|
126
140
|
contentVisibilityAuto: true,
|
|
127
141
|
opacityProperty: true,
|
|
128
142
|
visibilityProperty: true
|
|
@@ -268,6 +282,7 @@ function createRovingTabIndex(container, options = {}) {
|
|
|
268
282
|
direction,
|
|
269
283
|
navigationOnly = false,
|
|
270
284
|
selector,
|
|
285
|
+
skipVisibilityCheck = false,
|
|
271
286
|
typeahead = false,
|
|
272
287
|
wrap = false
|
|
273
288
|
} = options;
|
|
@@ -285,6 +300,10 @@ function createRovingTabIndex(container, options = {}) {
|
|
|
285
300
|
);
|
|
286
301
|
selector = void 0;
|
|
287
302
|
}
|
|
303
|
+
if (typeof skipVisibilityCheck !== "boolean") {
|
|
304
|
+
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
305
|
+
skipVisibilityCheck = false;
|
|
306
|
+
}
|
|
288
307
|
if (typeof typeahead !== "boolean") {
|
|
289
308
|
console.warn("Invalid typeahead option. Fallback: false.");
|
|
290
309
|
typeahead = false;
|
|
@@ -297,6 +316,7 @@ function createRovingTabIndex(container, options = {}) {
|
|
|
297
316
|
direction,
|
|
298
317
|
navigationOnly,
|
|
299
318
|
selector,
|
|
319
|
+
skipVisibilityCheck,
|
|
300
320
|
typeahead,
|
|
301
321
|
wrap
|
|
302
322
|
});
|
|
@@ -409,7 +429,8 @@ var RovingTabIndex = class {
|
|
|
409
429
|
...this.#getFocusables(),
|
|
410
430
|
...getFocusables(this.#container, {
|
|
411
431
|
composed: true,
|
|
412
|
-
filter: this.#selectorFilter
|
|
432
|
+
filter: this.#selectorFilter,
|
|
433
|
+
skipVisibilityCheck: !!this.#options.skipVisibilityCheck
|
|
413
434
|
})
|
|
414
435
|
]);
|
|
415
436
|
for (const focusable of this.#focusables) {
|
|
@@ -475,7 +496,8 @@ var RovingTabIndex = class {
|
|
|
475
496
|
return getFocusables(this.#container, {
|
|
476
497
|
composed: true,
|
|
477
498
|
filter: this.#selectorFilter,
|
|
478
|
-
include: (element) => this.#focusables.has(element)
|
|
499
|
+
include: (element) => this.#focusables.has(element),
|
|
500
|
+
skipVisibilityCheck: !!this.#options.skipVisibilityCheck
|
|
479
501
|
});
|
|
480
502
|
}
|
|
481
503
|
};
|
|
@@ -494,7 +516,7 @@ function getActiveElement() {
|
|
|
494
516
|
* Lightweight roving tabindex utility with fully focus management.
|
|
495
517
|
* Designed for accessible menus, tabs, toolbars, and composite widgets.
|
|
496
518
|
*
|
|
497
|
-
* @version 1.3.
|
|
519
|
+
* @version 1.3.2
|
|
498
520
|
* @author Yusuke Kamiyamane
|
|
499
521
|
* @license MIT
|
|
500
522
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -519,7 +541,7 @@ power-focusable/dist/index.js:
|
|
|
519
541
|
* High-precision focus management utility with full composed tree support.
|
|
520
542
|
* Handles complex focus rules including tabindex ordering, radio groups, inert.
|
|
521
543
|
*
|
|
522
|
-
* @version 4.1
|
|
544
|
+
* @version 4.2.1
|
|
523
545
|
* @author Yusuke Kamiyamane
|
|
524
546
|
* @license MIT
|
|
525
547
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@y14e/roving-tabindex",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
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.1
|
|
53
|
+
"power-focusable": "^4.2.1",
|
|
54
54
|
"tsup": "^8.0.0",
|
|
55
55
|
"typescript": "^5.6.0"
|
|
56
56
|
},
|