@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 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, { skipVisibilityCheck }) || include?.(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, { skipVisibilityCheck })) {
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(FOCUSABLE_SELECTOR)) {
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 = /* @__PURE__ */ new Set([
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
- include: (element) => this.#focusables.has(element),
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 1.3.2
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.2.1
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 1.3.2
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 1.3.2
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, { skipVisibilityCheck }) || include?.(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, { skipVisibilityCheck })) {
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(FOCUSABLE_SELECTOR)) {
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 = /* @__PURE__ */ new Set([
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
- include: (element) => this.#focusables.has(element),
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 1.3.2
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.2.1
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": "1.3.2",
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.2.1",
53
+ "power-focusable": "^4.3.0",
54
54
  "tsup": "^8.0.0",
55
55
  "typescript": "^5.6.0"
56
56
  },