@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 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 { composed = false, filter, include } = options;
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.0
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.8
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.0
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.0
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 { composed = false, filter, include } = options;
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.0
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.8
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.0",
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.8",
53
+ "power-focusable": "^4.2.1",
54
54
  "tsup": "^8.0.0",
55
55
  "typescript": "^5.6.0"
56
56
  },