@y14e/roving-tabindex 3.1.12 → 3.1.13

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
@@ -13,11 +13,11 @@ npm i @y14e/roving-tabindex
13
13
  import { createRovingTabIndex } from '@y14e/roving-tabindex';
14
14
 
15
15
  // CDNs
16
- import { createRovingTabIndex } from 'https://esm.sh/@y14e/roving-tabindex@3.1.12';
16
+ import { createRovingTabIndex } from 'https://esm.sh/@y14e/roving-tabindex@3.1.13';
17
17
  // or
18
- import { createRovingTabIndex } from 'https://cdn.jsdelivr.net/npm/@y14e/roving-tabindex@3.1.12/+esm';
18
+ import { createRovingTabIndex } from 'https://cdn.jsdelivr.net/npm/@y14e/roving-tabindex@3.1.13/+esm';
19
19
  // or
20
- import { createRovingTabIndex } from 'https://esm.unpkg.com/@y14e/roving-tabindex@3.1.12';
20
+ import { createRovingTabIndex } from 'https://esm.unpkg.com/@y14e/roving-tabindex@3.1.13';
21
21
  ```
22
22
 
23
23
  ## 📦 APIs
@@ -324,14 +324,8 @@ function createRovingTabIndex(container, options = {}) {
324
324
  return () => {
325
325
  };
326
326
  }
327
- try {
328
- const roving = new RovingTabIndex(container, options);
329
- return () => roving.destroy();
330
- } catch (error) {
331
- error instanceof Error && console.warn(error.message || error);
332
- return () => {
333
- };
334
- }
327
+ const roving = new RovingTabIndex(container, options);
328
+ return () => roving.destroy();
335
329
  }
336
330
  var RovingTabIndex = class _RovingTabIndex {
337
331
  static #initialized = /* @__PURE__ */ new Set();
@@ -369,9 +363,18 @@ var RovingTabIndex = class _RovingTabIndex {
369
363
  console.warn("Invalid noStart option. Fallback: false.");
370
364
  noStart = false;
371
365
  }
372
- if (selector !== "" && (typeof selector !== "string" || !selector.trim())) {
373
- console.warn("Invalid selector. Fallback: no selector string.");
374
- selector = "";
366
+ if (selector !== "") {
367
+ if (typeof selector !== "string" || !selector.trim()) {
368
+ console.warn("Invalid selector. Fallback: no selector string.");
369
+ selector = "";
370
+ } else {
371
+ try {
372
+ container.querySelector(selector);
373
+ } catch {
374
+ console.warn("Invalid selector. Fallback: no selector string.");
375
+ selector = "";
376
+ }
377
+ }
375
378
  }
376
379
  if (typeof typeahead !== "boolean") {
377
380
  console.warn("Invalid typeahead option. Fallback: false.");
@@ -400,7 +403,10 @@ var RovingTabIndex = class _RovingTabIndex {
400
403
  this.#isDestroyed = true;
401
404
  this.#controller?.abort();
402
405
  this.#controller = null;
403
- restoreAttributes([...this.#focusables]);
406
+ this.#focusables.forEach((focusable) => {
407
+ _RovingTabIndex.#initialized.delete(focusable);
408
+ restoreAttributes([focusable]);
409
+ });
404
410
  this.#focusables.clear();
405
411
  this.#focusablesByFirstChar.clear();
406
412
  }
@@ -453,7 +459,9 @@ var RovingTabIndex = class _RovingTabIndex {
453
459
  if (!(active instanceof Element)) {
454
460
  return;
455
461
  }
456
- const current = this.#getFocusables();
462
+ const current = this.#getFocusables().filter(
463
+ (focusable2) => this.#focusables.has(focusable2)
464
+ );
457
465
  if (!current.includes(active)) {
458
466
  return;
459
467
  }
@@ -481,7 +489,10 @@ var RovingTabIndex = class _RovingTabIndex {
481
489
  break;
482
490
  }
483
491
  default: {
484
- target = this.#focusablesByFirstChar.get(key.toUpperCase()) ?? [];
492
+ const focusables = new Set(
493
+ this.#focusablesByFirstChar.get(key.toUpperCase()) ?? []
494
+ );
495
+ target = current.filter((focusable2) => focusables.has(focusable2));
485
496
  const foundIndex = target.findIndex(
486
497
  (focusable2) => current.indexOf(focusable2) > currentIndex
487
498
  );
@@ -495,12 +506,16 @@ var RovingTabIndex = class _RovingTabIndex {
495
506
  const current = new Set(this.#getFocusables());
496
507
  for (const focusable of this.#focusables) {
497
508
  if (!current.has(focusable)) {
498
- focusable.isConnected && restoreAttributes([focusable]);
509
+ _RovingTabIndex.#initialized.delete(focusable);
510
+ restoreAttributes([focusable]);
499
511
  this.#focusables.delete(focusable);
500
- this.#focusablesByFirstChar.forEach((focusables) => {
512
+ for (const [key, focusables] of this.#focusablesByFirstChar) {
501
513
  const index = focusables.indexOf(focusable);
502
- index >= 0 && focusables.splice(index, 1);
503
- });
514
+ if (index >= 0) {
515
+ focusables.splice(index, 1);
516
+ !focusables.length && this.#focusablesByFirstChar.delete(key);
517
+ }
518
+ }
504
519
  }
505
520
  }
506
521
  const { navigationOnly, noStart, typeahead } = this.#settings;
@@ -509,7 +524,7 @@ var RovingTabIndex = class _RovingTabIndex {
509
524
  continue;
510
525
  }
511
526
  if (_RovingTabIndex.#initialized.has(focusable)) {
512
- throw new TypeError("Already initialized");
527
+ continue;
513
528
  }
514
529
  this.#focusables.add(focusable);
515
530
  _RovingTabIndex.#initialized.add(focusable);
@@ -552,7 +567,7 @@ var RovingTabIndex = class _RovingTabIndex {
552
567
  }
553
568
  #createSelectorFilter() {
554
569
  const { selector } = this.#settings;
555
- return (element) => !selector || [...this.#container.querySelectorAll(selector)].includes(element);
570
+ return (element) => !selector || element.matches(selector);
556
571
  }
557
572
  #getFocusables() {
558
573
  return getFocusables(this.#container, {
@@ -568,7 +583,7 @@ var RovingTabIndex = class _RovingTabIndex {
568
583
  * Lightweight roving tabindex utility with fully focus management.
569
584
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
570
585
  *
571
- * @version 3.1.12
586
+ * @version 3.1.13
572
587
  * @author Yusuke Kamiyamane
573
588
  * @license MIT
574
589
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -322,14 +322,8 @@ function createRovingTabIndex(container, options = {}) {
322
322
  return () => {
323
323
  };
324
324
  }
325
- try {
326
- const roving = new RovingTabIndex(container, options);
327
- return () => roving.destroy();
328
- } catch (error) {
329
- error instanceof Error && console.warn(error.message || error);
330
- return () => {
331
- };
332
- }
325
+ const roving = new RovingTabIndex(container, options);
326
+ return () => roving.destroy();
333
327
  }
334
328
  var RovingTabIndex = class _RovingTabIndex {
335
329
  static #initialized = /* @__PURE__ */ new Set();
@@ -367,9 +361,18 @@ var RovingTabIndex = class _RovingTabIndex {
367
361
  console.warn("Invalid noStart option. Fallback: false.");
368
362
  noStart = false;
369
363
  }
370
- if (selector !== "" && (typeof selector !== "string" || !selector.trim())) {
371
- console.warn("Invalid selector. Fallback: no selector string.");
372
- selector = "";
364
+ if (selector !== "") {
365
+ if (typeof selector !== "string" || !selector.trim()) {
366
+ console.warn("Invalid selector. Fallback: no selector string.");
367
+ selector = "";
368
+ } else {
369
+ try {
370
+ container.querySelector(selector);
371
+ } catch {
372
+ console.warn("Invalid selector. Fallback: no selector string.");
373
+ selector = "";
374
+ }
375
+ }
373
376
  }
374
377
  if (typeof typeahead !== "boolean") {
375
378
  console.warn("Invalid typeahead option. Fallback: false.");
@@ -398,7 +401,10 @@ var RovingTabIndex = class _RovingTabIndex {
398
401
  this.#isDestroyed = true;
399
402
  this.#controller?.abort();
400
403
  this.#controller = null;
401
- restoreAttributes([...this.#focusables]);
404
+ this.#focusables.forEach((focusable) => {
405
+ _RovingTabIndex.#initialized.delete(focusable);
406
+ restoreAttributes([focusable]);
407
+ });
402
408
  this.#focusables.clear();
403
409
  this.#focusablesByFirstChar.clear();
404
410
  }
@@ -451,7 +457,9 @@ var RovingTabIndex = class _RovingTabIndex {
451
457
  if (!(active instanceof Element)) {
452
458
  return;
453
459
  }
454
- const current = this.#getFocusables();
460
+ const current = this.#getFocusables().filter(
461
+ (focusable2) => this.#focusables.has(focusable2)
462
+ );
455
463
  if (!current.includes(active)) {
456
464
  return;
457
465
  }
@@ -479,7 +487,10 @@ var RovingTabIndex = class _RovingTabIndex {
479
487
  break;
480
488
  }
481
489
  default: {
482
- target = this.#focusablesByFirstChar.get(key.toUpperCase()) ?? [];
490
+ const focusables = new Set(
491
+ this.#focusablesByFirstChar.get(key.toUpperCase()) ?? []
492
+ );
493
+ target = current.filter((focusable2) => focusables.has(focusable2));
483
494
  const foundIndex = target.findIndex(
484
495
  (focusable2) => current.indexOf(focusable2) > currentIndex
485
496
  );
@@ -493,12 +504,16 @@ var RovingTabIndex = class _RovingTabIndex {
493
504
  const current = new Set(this.#getFocusables());
494
505
  for (const focusable of this.#focusables) {
495
506
  if (!current.has(focusable)) {
496
- focusable.isConnected && restoreAttributes([focusable]);
507
+ _RovingTabIndex.#initialized.delete(focusable);
508
+ restoreAttributes([focusable]);
497
509
  this.#focusables.delete(focusable);
498
- this.#focusablesByFirstChar.forEach((focusables) => {
510
+ for (const [key, focusables] of this.#focusablesByFirstChar) {
499
511
  const index = focusables.indexOf(focusable);
500
- index >= 0 && focusables.splice(index, 1);
501
- });
512
+ if (index >= 0) {
513
+ focusables.splice(index, 1);
514
+ !focusables.length && this.#focusablesByFirstChar.delete(key);
515
+ }
516
+ }
502
517
  }
503
518
  }
504
519
  const { navigationOnly, noStart, typeahead } = this.#settings;
@@ -507,7 +522,7 @@ var RovingTabIndex = class _RovingTabIndex {
507
522
  continue;
508
523
  }
509
524
  if (_RovingTabIndex.#initialized.has(focusable)) {
510
- throw new TypeError("Already initialized");
525
+ continue;
511
526
  }
512
527
  this.#focusables.add(focusable);
513
528
  _RovingTabIndex.#initialized.add(focusable);
@@ -550,7 +565,7 @@ var RovingTabIndex = class _RovingTabIndex {
550
565
  }
551
566
  #createSelectorFilter() {
552
567
  const { selector } = this.#settings;
553
- return (element) => !selector || [...this.#container.querySelectorAll(selector)].includes(element);
568
+ return (element) => !selector || element.matches(selector);
554
569
  }
555
570
  #getFocusables() {
556
571
  return getFocusables(this.#container, {
@@ -566,7 +581,7 @@ var RovingTabIndex = class _RovingTabIndex {
566
581
  * Lightweight roving tabindex utility with fully focus management.
567
582
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
568
583
  *
569
- * @version 3.1.12
584
+ * @version 3.1.13
570
585
  * @author Yusuke Kamiyamane
571
586
  * @license MIT
572
587
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.cjs CHANGED
@@ -10,14 +10,8 @@ function createRovingTabIndex(container, options = {}) {
10
10
  return () => {
11
11
  };
12
12
  }
13
- try {
14
- const roving = new RovingTabIndex(container, options);
15
- return () => roving.destroy();
16
- } catch (error) {
17
- error instanceof Error && console.warn(error.message || error);
18
- return () => {
19
- };
20
- }
13
+ const roving = new RovingTabIndex(container, options);
14
+ return () => roving.destroy();
21
15
  }
22
16
  var RovingTabIndex = class _RovingTabIndex {
23
17
  static #initialized = /* @__PURE__ */ new Set();
@@ -55,9 +49,18 @@ var RovingTabIndex = class _RovingTabIndex {
55
49
  console.warn("Invalid noStart option. Fallback: false.");
56
50
  noStart = false;
57
51
  }
58
- if (selector !== "" && (typeof selector !== "string" || !selector.trim())) {
59
- console.warn("Invalid selector. Fallback: no selector string.");
60
- selector = "";
52
+ if (selector !== "") {
53
+ if (typeof selector !== "string" || !selector.trim()) {
54
+ console.warn("Invalid selector. Fallback: no selector string.");
55
+ selector = "";
56
+ } else {
57
+ try {
58
+ container.querySelector(selector);
59
+ } catch {
60
+ console.warn("Invalid selector. Fallback: no selector string.");
61
+ selector = "";
62
+ }
63
+ }
61
64
  }
62
65
  if (typeof typeahead !== "boolean") {
63
66
  console.warn("Invalid typeahead option. Fallback: false.");
@@ -86,7 +89,10 @@ var RovingTabIndex = class _RovingTabIndex {
86
89
  this.#isDestroyed = true;
87
90
  this.#controller?.abort();
88
91
  this.#controller = null;
89
- attributesUtils.restoreAttributes([...this.#focusables]);
92
+ this.#focusables.forEach((focusable) => {
93
+ _RovingTabIndex.#initialized.delete(focusable);
94
+ attributesUtils.restoreAttributes([focusable]);
95
+ });
90
96
  this.#focusables.clear();
91
97
  this.#focusablesByFirstChar.clear();
92
98
  }
@@ -139,7 +145,9 @@ var RovingTabIndex = class _RovingTabIndex {
139
145
  if (!(active instanceof Element)) {
140
146
  return;
141
147
  }
142
- const current = this.#getFocusables();
148
+ const current = this.#getFocusables().filter(
149
+ (focusable2) => this.#focusables.has(focusable2)
150
+ );
143
151
  if (!current.includes(active)) {
144
152
  return;
145
153
  }
@@ -167,7 +175,10 @@ var RovingTabIndex = class _RovingTabIndex {
167
175
  break;
168
176
  }
169
177
  default: {
170
- target = this.#focusablesByFirstChar.get(key.toUpperCase()) ?? [];
178
+ const focusables = new Set(
179
+ this.#focusablesByFirstChar.get(key.toUpperCase()) ?? []
180
+ );
181
+ target = current.filter((focusable2) => focusables.has(focusable2));
171
182
  const foundIndex = target.findIndex(
172
183
  (focusable2) => current.indexOf(focusable2) > currentIndex
173
184
  );
@@ -181,12 +192,16 @@ var RovingTabIndex = class _RovingTabIndex {
181
192
  const current = new Set(this.#getFocusables());
182
193
  for (const focusable of this.#focusables) {
183
194
  if (!current.has(focusable)) {
184
- focusable.isConnected && attributesUtils.restoreAttributes([focusable]);
195
+ _RovingTabIndex.#initialized.delete(focusable);
196
+ attributesUtils.restoreAttributes([focusable]);
185
197
  this.#focusables.delete(focusable);
186
- this.#focusablesByFirstChar.forEach((focusables) => {
198
+ for (const [key, focusables] of this.#focusablesByFirstChar) {
187
199
  const index = focusables.indexOf(focusable);
188
- index >= 0 && focusables.splice(index, 1);
189
- });
200
+ if (index >= 0) {
201
+ focusables.splice(index, 1);
202
+ !focusables.length && this.#focusablesByFirstChar.delete(key);
203
+ }
204
+ }
190
205
  }
191
206
  }
192
207
  const { navigationOnly, noStart, typeahead } = this.#settings;
@@ -195,7 +210,7 @@ var RovingTabIndex = class _RovingTabIndex {
195
210
  continue;
196
211
  }
197
212
  if (_RovingTabIndex.#initialized.has(focusable)) {
198
- throw new TypeError("Already initialized");
213
+ continue;
199
214
  }
200
215
  this.#focusables.add(focusable);
201
216
  _RovingTabIndex.#initialized.add(focusable);
@@ -238,7 +253,7 @@ var RovingTabIndex = class _RovingTabIndex {
238
253
  }
239
254
  #createSelectorFilter() {
240
255
  const { selector } = this.#settings;
241
- return (element) => !selector || [...this.#container.querySelectorAll(selector)].includes(element);
256
+ return (element) => !selector || element.matches(selector);
242
257
  }
243
258
  #getFocusables() {
244
259
  return powerFocusable.getFocusables(this.#container, {
@@ -254,7 +269,7 @@ var RovingTabIndex = class _RovingTabIndex {
254
269
  * Lightweight roving tabindex utility with fully focus management.
255
270
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
256
271
  *
257
- * @version 3.1.12
272
+ * @version 3.1.13
258
273
  * @author Yusuke Kamiyamane
259
274
  * @license MIT
260
275
  * @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 3.1.12
6
+ * @version 3.1.13
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
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 3.1.12
6
+ * @version 3.1.13
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.js CHANGED
@@ -8,14 +8,8 @@ function createRovingTabIndex(container, options = {}) {
8
8
  return () => {
9
9
  };
10
10
  }
11
- try {
12
- const roving = new RovingTabIndex(container, options);
13
- return () => roving.destroy();
14
- } catch (error) {
15
- error instanceof Error && console.warn(error.message || error);
16
- return () => {
17
- };
18
- }
11
+ const roving = new RovingTabIndex(container, options);
12
+ return () => roving.destroy();
19
13
  }
20
14
  var RovingTabIndex = class _RovingTabIndex {
21
15
  static #initialized = /* @__PURE__ */ new Set();
@@ -53,9 +47,18 @@ var RovingTabIndex = class _RovingTabIndex {
53
47
  console.warn("Invalid noStart option. Fallback: false.");
54
48
  noStart = false;
55
49
  }
56
- if (selector !== "" && (typeof selector !== "string" || !selector.trim())) {
57
- console.warn("Invalid selector. Fallback: no selector string.");
58
- selector = "";
50
+ if (selector !== "") {
51
+ if (typeof selector !== "string" || !selector.trim()) {
52
+ console.warn("Invalid selector. Fallback: no selector string.");
53
+ selector = "";
54
+ } else {
55
+ try {
56
+ container.querySelector(selector);
57
+ } catch {
58
+ console.warn("Invalid selector. Fallback: no selector string.");
59
+ selector = "";
60
+ }
61
+ }
59
62
  }
60
63
  if (typeof typeahead !== "boolean") {
61
64
  console.warn("Invalid typeahead option. Fallback: false.");
@@ -84,7 +87,10 @@ var RovingTabIndex = class _RovingTabIndex {
84
87
  this.#isDestroyed = true;
85
88
  this.#controller?.abort();
86
89
  this.#controller = null;
87
- restoreAttributes([...this.#focusables]);
90
+ this.#focusables.forEach((focusable) => {
91
+ _RovingTabIndex.#initialized.delete(focusable);
92
+ restoreAttributes([focusable]);
93
+ });
88
94
  this.#focusables.clear();
89
95
  this.#focusablesByFirstChar.clear();
90
96
  }
@@ -137,7 +143,9 @@ var RovingTabIndex = class _RovingTabIndex {
137
143
  if (!(active instanceof Element)) {
138
144
  return;
139
145
  }
140
- const current = this.#getFocusables();
146
+ const current = this.#getFocusables().filter(
147
+ (focusable2) => this.#focusables.has(focusable2)
148
+ );
141
149
  if (!current.includes(active)) {
142
150
  return;
143
151
  }
@@ -165,7 +173,10 @@ var RovingTabIndex = class _RovingTabIndex {
165
173
  break;
166
174
  }
167
175
  default: {
168
- target = this.#focusablesByFirstChar.get(key.toUpperCase()) ?? [];
176
+ const focusables = new Set(
177
+ this.#focusablesByFirstChar.get(key.toUpperCase()) ?? []
178
+ );
179
+ target = current.filter((focusable2) => focusables.has(focusable2));
169
180
  const foundIndex = target.findIndex(
170
181
  (focusable2) => current.indexOf(focusable2) > currentIndex
171
182
  );
@@ -179,12 +190,16 @@ var RovingTabIndex = class _RovingTabIndex {
179
190
  const current = new Set(this.#getFocusables());
180
191
  for (const focusable of this.#focusables) {
181
192
  if (!current.has(focusable)) {
182
- focusable.isConnected && restoreAttributes([focusable]);
193
+ _RovingTabIndex.#initialized.delete(focusable);
194
+ restoreAttributes([focusable]);
183
195
  this.#focusables.delete(focusable);
184
- this.#focusablesByFirstChar.forEach((focusables) => {
196
+ for (const [key, focusables] of this.#focusablesByFirstChar) {
185
197
  const index = focusables.indexOf(focusable);
186
- index >= 0 && focusables.splice(index, 1);
187
- });
198
+ if (index >= 0) {
199
+ focusables.splice(index, 1);
200
+ !focusables.length && this.#focusablesByFirstChar.delete(key);
201
+ }
202
+ }
188
203
  }
189
204
  }
190
205
  const { navigationOnly, noStart, typeahead } = this.#settings;
@@ -193,7 +208,7 @@ var RovingTabIndex = class _RovingTabIndex {
193
208
  continue;
194
209
  }
195
210
  if (_RovingTabIndex.#initialized.has(focusable)) {
196
- throw new TypeError("Already initialized");
211
+ continue;
197
212
  }
198
213
  this.#focusables.add(focusable);
199
214
  _RovingTabIndex.#initialized.add(focusable);
@@ -236,7 +251,7 @@ var RovingTabIndex = class _RovingTabIndex {
236
251
  }
237
252
  #createSelectorFilter() {
238
253
  const { selector } = this.#settings;
239
- return (element) => !selector || [...this.#container.querySelectorAll(selector)].includes(element);
254
+ return (element) => !selector || element.matches(selector);
240
255
  }
241
256
  #getFocusables() {
242
257
  return getFocusables(this.#container, {
@@ -252,7 +267,7 @@ var RovingTabIndex = class _RovingTabIndex {
252
267
  * Lightweight roving tabindex utility with fully focus management.
253
268
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
254
269
  *
255
- * @version 3.1.12
270
+ * @version 3.1.13
256
271
  * @author Yusuke Kamiyamane
257
272
  * @license MIT
258
273
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@y14e/roving-tabindex",
3
- "version": "3.1.12",
3
+ "version": "3.1.13",
4
4
  "description": "Lightweight roving tabindex utility with fully focus management",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",