@y14e/roving-tabindex 2.0.8 → 2.2.0

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
@@ -10,14 +10,14 @@ npm i @y14e/roving-tabindex
10
10
 
11
11
  ```ts
12
12
  // npm
13
- import { createRovingTabIndex } from '@y14e/roving-tabindex@2.0.8';
13
+ import { createRovingTabIndex } from '@y14e/roving-tabindex@2.2.0';
14
14
 
15
15
  // CDNs
16
- import { createRovingTabIndex } from 'https://esm.sh/@y14e/roving-tabindex@2.0.8';
16
+ import { createRovingTabIndex } from 'https://esm.sh/@y14e/roving-tabindex@2.2.0';
17
17
  // or
18
- import { createRovingTabIndex } from 'https://cdn.jsdelivr.net/npm/@y14e/roving-tabindex@2.0.8/+esm';
18
+ import { createRovingTabIndex } from 'https://cdn.jsdelivr.net/npm/@y14e/roving-tabindex@2.2.0/+esm';
19
19
  // or
20
- import { createRovingTabIndex } from 'https://esm.unpkg.com/@y14e/roving-tabindex@2.0.8';
20
+ import { createRovingTabIndex } from 'https://esm.unpkg.com/@y14e/roving-tabindex@2.2.0';
21
21
  ```
22
22
 
23
23
  ## 📦 APIs
@@ -40,6 +40,8 @@ const cleanup = createRovingTabIndex(container, options);
40
40
  interface RovingTabIndexOptions {
41
41
  direction?: 'horizontal' | 'vertical'; // default: both (undefined)
42
42
  navigationOnly?: boolean; // default: false
43
+ noMemory?: boolean; // default: false
44
+ noStart?: boolean; // default: false
43
45
  selector?: string;
44
46
  typeahead?: boolean; // default: false
45
47
  wrap?: boolean; // default: false
@@ -50,6 +52,14 @@ interface RovingTabIndexOptions {
50
52
 
51
53
  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.
52
54
 
55
+ ### `noMemory`
56
+
57
+ If `true`, disables focus memory. Focus always starts from the first item.
58
+
59
+ ### `noStart`
60
+
61
+ If `true`, does not assign `tabindex="0"` to the first item during initialization.
62
+
53
63
  ### `typeahead`
54
64
 
55
65
  If `true`, enables character-based focus navigation. Typing a character moves focus to the next matching element.
package/dist/index.cjs CHANGED
@@ -299,6 +299,8 @@ function createRovingTabIndex(container, options = {}) {
299
299
  let {
300
300
  direction,
301
301
  navigationOnly = false,
302
+ noMemory = false,
303
+ noStart = false,
302
304
  selector,
303
305
  typeahead = false,
304
306
  wrap = false
@@ -311,6 +313,14 @@ function createRovingTabIndex(container, options = {}) {
311
313
  console.warn("Invalid navigationOnly option. Fallback: false.");
312
314
  navigationOnly = false;
313
315
  }
316
+ if (typeof noMemory !== "boolean") {
317
+ console.warn("Invalid noMemory option. Fallback: false.");
318
+ noMemory = false;
319
+ }
320
+ if (typeof noStart !== "boolean") {
321
+ console.warn("Invalid noStart option. Fallback: false.");
322
+ noStart = false;
323
+ }
314
324
  if (typeof selector !== "undefined" && (typeof selector !== "string" || !selector.trim())) {
315
325
  console.warn(
316
326
  "Invalid selector. Fallback: all focusable elements (undefined)."
@@ -325,7 +335,7 @@ function createRovingTabIndex(container, options = {}) {
325
335
  console.warn("Invalid wrap option. Fallback: false.");
326
336
  wrap = false;
327
337
  }
328
- const settings = { navigationOnly, typeahead, wrap };
338
+ const settings = { navigationOnly, noMemory, noStart, typeahead, wrap };
329
339
  direction && Object.assign(settings, { direction });
330
340
  selector && Object.assign(settings, { selector });
331
341
  const roving = new RovingTabIndex(container, settings);
@@ -373,10 +383,15 @@ var RovingTabIndex = class {
373
383
  }
374
384
  #onFocusIn = (event) => {
375
385
  const { target } = event;
376
- if (!(target instanceof Element) || !this.#focusables.has(target)) {
386
+ if (!(target instanceof Element)) {
387
+ return;
388
+ }
389
+ const isFocusable2 = this.#focusables.has(target);
390
+ if (this.#options.noMemory && !isFocusable2) {
391
+ this.#update(null);
377
392
  return;
378
393
  }
379
- this.#update(target);
394
+ isFocusable2 && this.#update(target);
380
395
  };
381
396
  #onKeyDown = (event) => {
382
397
  if (!event.composedPath().includes(this.#container)) {
@@ -454,7 +469,7 @@ var RovingTabIndex = class {
454
469
  index >= 0 && focusables.splice(index, 1);
455
470
  });
456
471
  }
457
- const { navigationOnly } = this.#options;
472
+ const { navigationOnly, noStart, typeahead } = this.#options;
458
473
  for (const focusable of current) {
459
474
  if (this.#focusables.has(focusable)) {
460
475
  continue;
@@ -464,7 +479,7 @@ var RovingTabIndex = class {
464
479
  saveAttributes([focusable], ["tabindex"]);
465
480
  focusable.setAttribute("tabindex", "-1");
466
481
  }
467
- if (!this.#options.typeahead) {
482
+ if (!typeahead) {
468
483
  continue;
469
484
  }
470
485
  const value = focusable.ariaKeyShortcuts?.trim();
@@ -495,7 +510,7 @@ var RovingTabIndex = class {
495
510
  return;
496
511
  }
497
512
  [...this.#focusables].forEach((focusable, i) => {
498
- focusable.setAttribute("tabindex", i ? "-1" : "0");
513
+ focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
499
514
  });
500
515
  }
501
516
  #createSelectorFilter() {
@@ -526,7 +541,7 @@ function getActiveElement() {
526
541
  * Lightweight roving tabindex utility with fully focus management.
527
542
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
528
543
  *
529
- * @version 2.0.8
544
+ * @version 2.2.0
530
545
  * @author Yusuke Kamiyamane
531
546
  * @license MIT
532
547
  * @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 2.0.8
6
+ * @version 2.2.0
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -12,6 +12,8 @@
12
12
  interface RovingTabIndexOptions {
13
13
  readonly direction?: 'horizontal' | 'vertical';
14
14
  readonly navigationOnly?: boolean;
15
+ readonly noMemory?: boolean;
16
+ readonly noStart?: boolean;
15
17
  readonly selector?: string;
16
18
  readonly typeahead?: boolean;
17
19
  readonly wrap?: boolean;
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 2.0.8
6
+ * @version 2.2.0
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -12,6 +12,8 @@
12
12
  interface RovingTabIndexOptions {
13
13
  readonly direction?: 'horizontal' | 'vertical';
14
14
  readonly navigationOnly?: boolean;
15
+ readonly noMemory?: boolean;
16
+ readonly noStart?: boolean;
15
17
  readonly selector?: string;
16
18
  readonly typeahead?: boolean;
17
19
  readonly wrap?: boolean;
package/dist/index.js CHANGED
@@ -297,6 +297,8 @@ function createRovingTabIndex(container, options = {}) {
297
297
  let {
298
298
  direction,
299
299
  navigationOnly = false,
300
+ noMemory = false,
301
+ noStart = false,
300
302
  selector,
301
303
  typeahead = false,
302
304
  wrap = false
@@ -309,6 +311,14 @@ function createRovingTabIndex(container, options = {}) {
309
311
  console.warn("Invalid navigationOnly option. Fallback: false.");
310
312
  navigationOnly = false;
311
313
  }
314
+ if (typeof noMemory !== "boolean") {
315
+ console.warn("Invalid noMemory option. Fallback: false.");
316
+ noMemory = false;
317
+ }
318
+ if (typeof noStart !== "boolean") {
319
+ console.warn("Invalid noStart option. Fallback: false.");
320
+ noStart = false;
321
+ }
312
322
  if (typeof selector !== "undefined" && (typeof selector !== "string" || !selector.trim())) {
313
323
  console.warn(
314
324
  "Invalid selector. Fallback: all focusable elements (undefined)."
@@ -323,7 +333,7 @@ function createRovingTabIndex(container, options = {}) {
323
333
  console.warn("Invalid wrap option. Fallback: false.");
324
334
  wrap = false;
325
335
  }
326
- const settings = { navigationOnly, typeahead, wrap };
336
+ const settings = { navigationOnly, noMemory, noStart, typeahead, wrap };
327
337
  direction && Object.assign(settings, { direction });
328
338
  selector && Object.assign(settings, { selector });
329
339
  const roving = new RovingTabIndex(container, settings);
@@ -371,10 +381,15 @@ var RovingTabIndex = class {
371
381
  }
372
382
  #onFocusIn = (event) => {
373
383
  const { target } = event;
374
- if (!(target instanceof Element) || !this.#focusables.has(target)) {
384
+ if (!(target instanceof Element)) {
385
+ return;
386
+ }
387
+ const isFocusable2 = this.#focusables.has(target);
388
+ if (this.#options.noMemory && !isFocusable2) {
389
+ this.#update(null);
375
390
  return;
376
391
  }
377
- this.#update(target);
392
+ isFocusable2 && this.#update(target);
378
393
  };
379
394
  #onKeyDown = (event) => {
380
395
  if (!event.composedPath().includes(this.#container)) {
@@ -452,7 +467,7 @@ var RovingTabIndex = class {
452
467
  index >= 0 && focusables.splice(index, 1);
453
468
  });
454
469
  }
455
- const { navigationOnly } = this.#options;
470
+ const { navigationOnly, noStart, typeahead } = this.#options;
456
471
  for (const focusable of current) {
457
472
  if (this.#focusables.has(focusable)) {
458
473
  continue;
@@ -462,7 +477,7 @@ var RovingTabIndex = class {
462
477
  saveAttributes([focusable], ["tabindex"]);
463
478
  focusable.setAttribute("tabindex", "-1");
464
479
  }
465
- if (!this.#options.typeahead) {
480
+ if (!typeahead) {
466
481
  continue;
467
482
  }
468
483
  const value = focusable.ariaKeyShortcuts?.trim();
@@ -493,7 +508,7 @@ var RovingTabIndex = class {
493
508
  return;
494
509
  }
495
510
  [...this.#focusables].forEach((focusable, i) => {
496
- focusable.setAttribute("tabindex", i ? "-1" : "0");
511
+ focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
497
512
  });
498
513
  }
499
514
  #createSelectorFilter() {
@@ -524,7 +539,7 @@ function getActiveElement() {
524
539
  * Lightweight roving tabindex utility with fully focus management.
525
540
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
526
541
  *
527
- * @version 2.0.8
542
+ * @version 2.2.0
528
543
  * @author Yusuke Kamiyamane
529
544
  * @license MIT
530
545
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@y14e/roving-tabindex",
3
- "version": "2.0.8",
3
+ "version": "2.2.0",
4
4
  "description": "Lightweight roving tabindex utility with fully focus management",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",