@y14e/roving-tabindex 3.1.0 → 3.1.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
@@ -10,14 +10,14 @@ npm i @y14e/roving-tabindex
10
10
 
11
11
  ```ts
12
12
  // npm
13
- import { createRovingTabIndex } from '@y14e/roving-tabindex@3.1.0';
13
+ import { createRovingTabIndex } from '@y14e/roving-tabindex@3.1.2';
14
14
 
15
15
  // CDNs
16
- import { createRovingTabIndex } from 'https://esm.sh/@y14e/roving-tabindex@3.1.0';
16
+ import { createRovingTabIndex } from 'https://esm.sh/@y14e/roving-tabindex@3.1.2';
17
17
  // or
18
- import { createRovingTabIndex } from 'https://cdn.jsdelivr.net/npm/@y14e/roving-tabindex@3.1.0/+esm';
18
+ import { createRovingTabIndex } from 'https://cdn.jsdelivr.net/npm/@y14e/roving-tabindex@3.1.2/+esm';
19
19
  // or
20
- import { createRovingTabIndex } from 'https://esm.unpkg.com/@y14e/roving-tabindex@3.1.0';
20
+ import { createRovingTabIndex } from 'https://esm.unpkg.com/@y14e/roving-tabindex@3.1.2';
21
21
  ```
22
22
 
23
23
  ## 📦 APIs
@@ -118,12 +118,6 @@ function getFocusables(container = document.body, options = {}) {
118
118
  const unfiltered = normalizeRadioGroup(sortByTabIndex(elements));
119
119
  return filter ? unfiltered.filter(filter) : unfiltered;
120
120
  }
121
- function getNextFocusable(container = document.body, options = {}) {
122
- return getRelativeFocusable(container, 1, options);
123
- }
124
- function getPreviousFocusable(container = document.body, options = {}) {
125
- return getRelativeFocusable(container, -1, options);
126
- }
127
121
  function isFocusable(element, options = {}) {
128
122
  if (!(element instanceof Element)) {
129
123
  console.warn("Invalid element");
@@ -161,84 +155,6 @@ function isFocusable(element, options = {}) {
161
155
  }
162
156
  return true;
163
157
  }
164
- function getRelativeFocusable(container, offset, options) {
165
- if (!(container instanceof Element)) {
166
- console.warn("Invalid container element. Fallback: <body> element.");
167
- container = document.body;
168
- }
169
- let {
170
- anchor = getActiveElement(),
171
- composed = false,
172
- filter,
173
- include,
174
- skipNegativeTabIndexCheck = false,
175
- skipVisibilityCheck = false,
176
- wrap = false
177
- } = options;
178
- if (!(anchor instanceof Element)) {
179
- const active = getActiveElement();
180
- if (active instanceof Element) {
181
- console.warn("Invalid anchor element. Fallback: active element.");
182
- anchor = active;
183
- } else {
184
- console.warn("Invalid anchor element");
185
- return null;
186
- }
187
- }
188
- if (!containsComposed(container, anchor)) {
189
- console.warn("Anchor (active) element not within container");
190
- return null;
191
- }
192
- if (typeof composed !== "boolean") {
193
- console.warn("Invalid composed option. Fallback: false.");
194
- composed = false;
195
- }
196
- if (typeof filter !== "undefined" && typeof filter !== "function") {
197
- console.warn(
198
- "Invalid filter function. Fallback: no filter function (undefined)."
199
- );
200
- filter = void 0;
201
- }
202
- if (typeof include !== "undefined" && typeof include !== "function") {
203
- console.warn(
204
- "Invalid include function. Fallback: no include function (undefined)."
205
- );
206
- include = void 0;
207
- }
208
- if (typeof skipNegativeTabIndexCheck !== "boolean") {
209
- console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
210
- skipNegativeTabIndexCheck = false;
211
- }
212
- if (typeof skipVisibilityCheck !== "boolean") {
213
- console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
214
- skipVisibilityCheck = false;
215
- }
216
- if (typeof wrap !== "boolean") {
217
- console.warn("Invalid wrap option. Fallback: false.");
218
- wrap = false;
219
- }
220
- const settings = { composed, skipNegativeTabIndexCheck, skipVisibilityCheck };
221
- if (filter !== void 0) {
222
- Object.assign(settings, { filter });
223
- }
224
- if (include !== void 0) {
225
- Object.assign(settings, { include });
226
- }
227
- const focusables = getFocusables(container, settings);
228
- const { length } = focusables;
229
- if (!length) {
230
- return null;
231
- }
232
- const currentIndex = focusables.indexOf(anchor);
233
- if (currentIndex === -1) {
234
- return null;
235
- }
236
- const offsetIndex = currentIndex + offset;
237
- if ((offsetIndex < 0 || offsetIndex >= length) && !wrap) {
238
- return null;
239
- }
240
- return focusables[(offsetIndex + length) % length] ?? null;
241
- }
242
158
  function isDisabledDeep(element) {
243
159
  let current = element;
244
160
  while (current) {
@@ -319,17 +235,6 @@ function sortByTabIndex(elements) {
319
235
  }
320
236
  return sorted;
321
237
  }
322
- function containsComposed(container, element) {
323
- let current = element;
324
- while (current) {
325
- if (current === container) {
326
- return true;
327
- } else {
328
- current = current instanceof ShadowRoot ? current.mode === "open" ? current.host : null : current.parentNode;
329
- }
330
- }
331
- return false;
332
- }
333
238
  function getComposedChildren(node) {
334
239
  if (node instanceof ShadowRoot) {
335
240
  return getChildren(node);
@@ -478,18 +383,9 @@ var RovingTabIndex = class _RovingTabIndex {
478
383
  }
479
384
  this.#controller = new AbortController();
480
385
  const { signal } = this.#controller;
481
- document.addEventListener("focusin", this.#onFocusIn, {
482
- capture: true,
483
- signal
484
- });
485
- this.#settings.noMemory && document.addEventListener("focusout", this.#onFocusOut, {
486
- capture: true,
487
- signal
488
- });
489
- this.#container.addEventListener("keydown", this.#onKeyDown, {
490
- capture: true,
491
- signal
492
- });
386
+ document.addEventListener("focusin", this.#onFocusIn, { signal });
387
+ this.#settings.noMemory && document.addEventListener("focusout", this.#onFocusOut, { signal });
388
+ this.#container.addEventListener("keydown", this.#onKeyDown, { signal });
493
389
  }
494
390
  #onFocusIn = (event) => {
495
391
  const { target } = event;
@@ -506,14 +402,13 @@ var RovingTabIndex = class _RovingTabIndex {
506
402
  };
507
403
  #onKeyDown = (event) => {
508
404
  const { key, altKey, ctrlKey, metaKey, shiftKey } = event;
509
- if (shiftKey && key !== "Tab" || altKey || ctrlKey || metaKey) {
405
+ if (altKey || ctrlKey || metaKey || shiftKey) {
510
406
  return;
511
407
  }
512
408
  const { direction, typeahead, wrap } = this.#settings;
513
409
  const isBoth = !direction;
514
410
  const isHorizontal = direction === "horizontal";
515
411
  if (![
516
- "Tab",
517
412
  "End",
518
413
  "Home",
519
414
  ...isBoth ? ["ArrowLeft", "ArrowUp"] : [`Arrow${isHorizontal ? "Left" : "Up"}`],
@@ -531,14 +426,11 @@ var RovingTabIndex = class _RovingTabIndex {
531
426
  if (!current.includes(active)) {
532
427
  return;
533
428
  }
534
- let shouldPrevent = true;
429
+ event.preventDefault();
535
430
  const currentIndex = current.indexOf(active);
536
- let newIndex = 0;
431
+ let newIndex;
537
432
  let target = current;
538
433
  switch (key) {
539
- case "Tab":
540
- shouldPrevent = this.#moveFocus(shiftKey ? "previous" : "next");
541
- break;
542
434
  case "End":
543
435
  newIndex = -1;
544
436
  break;
@@ -560,16 +452,13 @@ var RovingTabIndex = class _RovingTabIndex {
560
452
  default: {
561
453
  target = this.#focusablesByFirstChar.get(key.toUpperCase()) ?? [];
562
454
  const foundIndex = target.findIndex(
563
- (focusable) => current.indexOf(focusable) > currentIndex
455
+ (focusable2) => current.indexOf(focusable2) > currentIndex
564
456
  );
565
457
  newIndex = foundIndex >= 0 ? foundIndex : 0;
566
458
  }
567
459
  }
568
- shouldPrevent && event.preventDefault();
569
- if (key !== "Tab") {
570
- const focusable = target.at(newIndex);
571
- focusable && focusElement(focusable);
572
- }
460
+ const focusable = target.at(newIndex);
461
+ focusable && focusElement(focusable);
573
462
  };
574
463
  #update(active) {
575
464
  const current = new Set(this.#getFocusables());
@@ -642,24 +531,13 @@ var RovingTabIndex = class _RovingTabIndex {
642
531
  skipVisibilityCheck: true
643
532
  });
644
533
  }
645
- #moveFocus(direction) {
646
- const options = {
647
- composed: true
648
- };
649
- const focusable = direction === "previous" ? getPreviousFocusable(document.body, options) : getNextFocusable(document.body, options);
650
- if (focusable) {
651
- focusElement(focusable);
652
- return true;
653
- }
654
- return false;
655
- }
656
534
  };
657
535
  /**
658
536
  * Roving Tabindex
659
537
  * Lightweight roving tabindex utility with fully focus management.
660
538
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
661
539
  *
662
- * @version 3.1.0
540
+ * @version 3.1.2
663
541
  * @author Yusuke Kamiyamane
664
542
  * @license MIT
665
543
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -116,12 +116,6 @@ function getFocusables(container = document.body, options = {}) {
116
116
  const unfiltered = normalizeRadioGroup(sortByTabIndex(elements));
117
117
  return filter ? unfiltered.filter(filter) : unfiltered;
118
118
  }
119
- function getNextFocusable(container = document.body, options = {}) {
120
- return getRelativeFocusable(container, 1, options);
121
- }
122
- function getPreviousFocusable(container = document.body, options = {}) {
123
- return getRelativeFocusable(container, -1, options);
124
- }
125
119
  function isFocusable(element, options = {}) {
126
120
  if (!(element instanceof Element)) {
127
121
  console.warn("Invalid element");
@@ -159,84 +153,6 @@ function isFocusable(element, options = {}) {
159
153
  }
160
154
  return true;
161
155
  }
162
- function getRelativeFocusable(container, offset, options) {
163
- if (!(container instanceof Element)) {
164
- console.warn("Invalid container element. Fallback: <body> element.");
165
- container = document.body;
166
- }
167
- let {
168
- anchor = getActiveElement(),
169
- composed = false,
170
- filter,
171
- include,
172
- skipNegativeTabIndexCheck = false,
173
- skipVisibilityCheck = false,
174
- wrap = false
175
- } = options;
176
- if (!(anchor instanceof Element)) {
177
- const active = getActiveElement();
178
- if (active instanceof Element) {
179
- console.warn("Invalid anchor element. Fallback: active element.");
180
- anchor = active;
181
- } else {
182
- console.warn("Invalid anchor element");
183
- return null;
184
- }
185
- }
186
- if (!containsComposed(container, anchor)) {
187
- console.warn("Anchor (active) element not within container");
188
- return null;
189
- }
190
- if (typeof composed !== "boolean") {
191
- console.warn("Invalid composed option. Fallback: false.");
192
- composed = false;
193
- }
194
- if (typeof filter !== "undefined" && typeof filter !== "function") {
195
- console.warn(
196
- "Invalid filter function. Fallback: no filter function (undefined)."
197
- );
198
- filter = void 0;
199
- }
200
- if (typeof include !== "undefined" && typeof include !== "function") {
201
- console.warn(
202
- "Invalid include function. Fallback: no include function (undefined)."
203
- );
204
- include = void 0;
205
- }
206
- if (typeof skipNegativeTabIndexCheck !== "boolean") {
207
- console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
208
- skipNegativeTabIndexCheck = false;
209
- }
210
- if (typeof skipVisibilityCheck !== "boolean") {
211
- console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
212
- skipVisibilityCheck = false;
213
- }
214
- if (typeof wrap !== "boolean") {
215
- console.warn("Invalid wrap option. Fallback: false.");
216
- wrap = false;
217
- }
218
- const settings = { composed, skipNegativeTabIndexCheck, skipVisibilityCheck };
219
- if (filter !== void 0) {
220
- Object.assign(settings, { filter });
221
- }
222
- if (include !== void 0) {
223
- Object.assign(settings, { include });
224
- }
225
- const focusables = getFocusables(container, settings);
226
- const { length } = focusables;
227
- if (!length) {
228
- return null;
229
- }
230
- const currentIndex = focusables.indexOf(anchor);
231
- if (currentIndex === -1) {
232
- return null;
233
- }
234
- const offsetIndex = currentIndex + offset;
235
- if ((offsetIndex < 0 || offsetIndex >= length) && !wrap) {
236
- return null;
237
- }
238
- return focusables[(offsetIndex + length) % length] ?? null;
239
- }
240
156
  function isDisabledDeep(element) {
241
157
  let current = element;
242
158
  while (current) {
@@ -317,17 +233,6 @@ function sortByTabIndex(elements) {
317
233
  }
318
234
  return sorted;
319
235
  }
320
- function containsComposed(container, element) {
321
- let current = element;
322
- while (current) {
323
- if (current === container) {
324
- return true;
325
- } else {
326
- current = current instanceof ShadowRoot ? current.mode === "open" ? current.host : null : current.parentNode;
327
- }
328
- }
329
- return false;
330
- }
331
236
  function getComposedChildren(node) {
332
237
  if (node instanceof ShadowRoot) {
333
238
  return getChildren(node);
@@ -476,18 +381,9 @@ var RovingTabIndex = class _RovingTabIndex {
476
381
  }
477
382
  this.#controller = new AbortController();
478
383
  const { signal } = this.#controller;
479
- document.addEventListener("focusin", this.#onFocusIn, {
480
- capture: true,
481
- signal
482
- });
483
- this.#settings.noMemory && document.addEventListener("focusout", this.#onFocusOut, {
484
- capture: true,
485
- signal
486
- });
487
- this.#container.addEventListener("keydown", this.#onKeyDown, {
488
- capture: true,
489
- signal
490
- });
384
+ document.addEventListener("focusin", this.#onFocusIn, { signal });
385
+ this.#settings.noMemory && document.addEventListener("focusout", this.#onFocusOut, { signal });
386
+ this.#container.addEventListener("keydown", this.#onKeyDown, { signal });
491
387
  }
492
388
  #onFocusIn = (event) => {
493
389
  const { target } = event;
@@ -504,14 +400,13 @@ var RovingTabIndex = class _RovingTabIndex {
504
400
  };
505
401
  #onKeyDown = (event) => {
506
402
  const { key, altKey, ctrlKey, metaKey, shiftKey } = event;
507
- if (shiftKey && key !== "Tab" || altKey || ctrlKey || metaKey) {
403
+ if (altKey || ctrlKey || metaKey || shiftKey) {
508
404
  return;
509
405
  }
510
406
  const { direction, typeahead, wrap } = this.#settings;
511
407
  const isBoth = !direction;
512
408
  const isHorizontal = direction === "horizontal";
513
409
  if (![
514
- "Tab",
515
410
  "End",
516
411
  "Home",
517
412
  ...isBoth ? ["ArrowLeft", "ArrowUp"] : [`Arrow${isHorizontal ? "Left" : "Up"}`],
@@ -529,14 +424,11 @@ var RovingTabIndex = class _RovingTabIndex {
529
424
  if (!current.includes(active)) {
530
425
  return;
531
426
  }
532
- let shouldPrevent = true;
427
+ event.preventDefault();
533
428
  const currentIndex = current.indexOf(active);
534
- let newIndex = 0;
429
+ let newIndex;
535
430
  let target = current;
536
431
  switch (key) {
537
- case "Tab":
538
- shouldPrevent = this.#moveFocus(shiftKey ? "previous" : "next");
539
- break;
540
432
  case "End":
541
433
  newIndex = -1;
542
434
  break;
@@ -558,16 +450,13 @@ var RovingTabIndex = class _RovingTabIndex {
558
450
  default: {
559
451
  target = this.#focusablesByFirstChar.get(key.toUpperCase()) ?? [];
560
452
  const foundIndex = target.findIndex(
561
- (focusable) => current.indexOf(focusable) > currentIndex
453
+ (focusable2) => current.indexOf(focusable2) > currentIndex
562
454
  );
563
455
  newIndex = foundIndex >= 0 ? foundIndex : 0;
564
456
  }
565
457
  }
566
- shouldPrevent && event.preventDefault();
567
- if (key !== "Tab") {
568
- const focusable = target.at(newIndex);
569
- focusable && focusElement(focusable);
570
- }
458
+ const focusable = target.at(newIndex);
459
+ focusable && focusElement(focusable);
571
460
  };
572
461
  #update(active) {
573
462
  const current = new Set(this.#getFocusables());
@@ -640,24 +529,13 @@ var RovingTabIndex = class _RovingTabIndex {
640
529
  skipVisibilityCheck: true
641
530
  });
642
531
  }
643
- #moveFocus(direction) {
644
- const options = {
645
- composed: true
646
- };
647
- const focusable = direction === "previous" ? getPreviousFocusable(document.body, options) : getNextFocusable(document.body, options);
648
- if (focusable) {
649
- focusElement(focusable);
650
- return true;
651
- }
652
- return false;
653
- }
654
532
  };
655
533
  /**
656
534
  * Roving Tabindex
657
535
  * Lightweight roving tabindex utility with fully focus management.
658
536
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
659
537
  *
660
- * @version 3.1.0
538
+ * @version 3.1.2
661
539
  * @author Yusuke Kamiyamane
662
540
  * @license MIT
663
541
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.cjs CHANGED
@@ -99,18 +99,9 @@ var RovingTabIndex = class _RovingTabIndex {
99
99
  }
100
100
  this.#controller = new AbortController();
101
101
  const { signal } = this.#controller;
102
- document.addEventListener("focusin", this.#onFocusIn, {
103
- capture: true,
104
- signal
105
- });
106
- this.#settings.noMemory && document.addEventListener("focusout", this.#onFocusOut, {
107
- capture: true,
108
- signal
109
- });
110
- this.#container.addEventListener("keydown", this.#onKeyDown, {
111
- capture: true,
112
- signal
113
- });
102
+ document.addEventListener("focusin", this.#onFocusIn, { signal });
103
+ this.#settings.noMemory && document.addEventListener("focusout", this.#onFocusOut, { signal });
104
+ this.#container.addEventListener("keydown", this.#onKeyDown, { signal });
114
105
  }
115
106
  #onFocusIn = (event) => {
116
107
  const { target } = event;
@@ -127,14 +118,13 @@ var RovingTabIndex = class _RovingTabIndex {
127
118
  };
128
119
  #onKeyDown = (event) => {
129
120
  const { key, altKey, ctrlKey, metaKey, shiftKey } = event;
130
- if (shiftKey && key !== "Tab" || altKey || ctrlKey || metaKey) {
121
+ if (altKey || ctrlKey || metaKey || shiftKey) {
131
122
  return;
132
123
  }
133
124
  const { direction, typeahead, wrap } = this.#settings;
134
125
  const isBoth = !direction;
135
126
  const isHorizontal = direction === "horizontal";
136
127
  if (![
137
- "Tab",
138
128
  "End",
139
129
  "Home",
140
130
  ...isBoth ? ["ArrowLeft", "ArrowUp"] : [`Arrow${isHorizontal ? "Left" : "Up"}`],
@@ -152,14 +142,11 @@ var RovingTabIndex = class _RovingTabIndex {
152
142
  if (!current.includes(active)) {
153
143
  return;
154
144
  }
155
- let shouldPrevent = true;
145
+ event.preventDefault();
156
146
  const currentIndex = current.indexOf(active);
157
- let newIndex = 0;
147
+ let newIndex;
158
148
  let target = current;
159
149
  switch (key) {
160
- case "Tab":
161
- shouldPrevent = this.#moveFocus(shiftKey ? "previous" : "next");
162
- break;
163
150
  case "End":
164
151
  newIndex = -1;
165
152
  break;
@@ -181,16 +168,13 @@ var RovingTabIndex = class _RovingTabIndex {
181
168
  default: {
182
169
  target = this.#focusablesByFirstChar.get(key.toUpperCase()) ?? [];
183
170
  const foundIndex = target.findIndex(
184
- (focusable) => current.indexOf(focusable) > currentIndex
171
+ (focusable2) => current.indexOf(focusable2) > currentIndex
185
172
  );
186
173
  newIndex = foundIndex >= 0 ? foundIndex : 0;
187
174
  }
188
175
  }
189
- shouldPrevent && event.preventDefault();
190
- if (key !== "Tab") {
191
- const focusable = target.at(newIndex);
192
- focusable && powerFocusable.focusElement(focusable);
193
- }
176
+ const focusable = target.at(newIndex);
177
+ focusable && powerFocusable.focusElement(focusable);
194
178
  };
195
179
  #update(active) {
196
180
  const current = new Set(this.#getFocusables());
@@ -263,24 +247,13 @@ var RovingTabIndex = class _RovingTabIndex {
263
247
  skipVisibilityCheck: true
264
248
  });
265
249
  }
266
- #moveFocus(direction) {
267
- const options = {
268
- composed: true
269
- };
270
- const focusable = direction === "previous" ? powerFocusable.getPreviousFocusable(document.body, options) : powerFocusable.getNextFocusable(document.body, options);
271
- if (focusable) {
272
- powerFocusable.focusElement(focusable);
273
- return true;
274
- }
275
- return false;
276
- }
277
250
  };
278
251
  /**
279
252
  * Roving Tabindex
280
253
  * Lightweight roving tabindex utility with fully focus management.
281
254
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
282
255
  *
283
- * @version 3.1.0
256
+ * @version 3.1.2
284
257
  * @author Yusuke Kamiyamane
285
258
  * @license MIT
286
259
  * @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.0
6
+ * @version 3.1.2
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.0
6
+ * @version 3.1.2
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { restoreAttributes, saveAttributes, addTokenToAttribute } from '@y14e/attributes-utils';
2
- import { getActiveElement, focusElement, getFocusables, getPreviousFocusable, getNextFocusable } from 'power-focusable';
2
+ import { getActiveElement, focusElement, getFocusables } from 'power-focusable';
3
3
 
4
4
  // src/index.ts
5
5
  function createRovingTabIndex(container, options = {}) {
@@ -97,18 +97,9 @@ var RovingTabIndex = class _RovingTabIndex {
97
97
  }
98
98
  this.#controller = new AbortController();
99
99
  const { signal } = this.#controller;
100
- document.addEventListener("focusin", this.#onFocusIn, {
101
- capture: true,
102
- signal
103
- });
104
- this.#settings.noMemory && document.addEventListener("focusout", this.#onFocusOut, {
105
- capture: true,
106
- signal
107
- });
108
- this.#container.addEventListener("keydown", this.#onKeyDown, {
109
- capture: true,
110
- signal
111
- });
100
+ document.addEventListener("focusin", this.#onFocusIn, { signal });
101
+ this.#settings.noMemory && document.addEventListener("focusout", this.#onFocusOut, { signal });
102
+ this.#container.addEventListener("keydown", this.#onKeyDown, { signal });
112
103
  }
113
104
  #onFocusIn = (event) => {
114
105
  const { target } = event;
@@ -125,14 +116,13 @@ var RovingTabIndex = class _RovingTabIndex {
125
116
  };
126
117
  #onKeyDown = (event) => {
127
118
  const { key, altKey, ctrlKey, metaKey, shiftKey } = event;
128
- if (shiftKey && key !== "Tab" || altKey || ctrlKey || metaKey) {
119
+ if (altKey || ctrlKey || metaKey || shiftKey) {
129
120
  return;
130
121
  }
131
122
  const { direction, typeahead, wrap } = this.#settings;
132
123
  const isBoth = !direction;
133
124
  const isHorizontal = direction === "horizontal";
134
125
  if (![
135
- "Tab",
136
126
  "End",
137
127
  "Home",
138
128
  ...isBoth ? ["ArrowLeft", "ArrowUp"] : [`Arrow${isHorizontal ? "Left" : "Up"}`],
@@ -150,14 +140,11 @@ var RovingTabIndex = class _RovingTabIndex {
150
140
  if (!current.includes(active)) {
151
141
  return;
152
142
  }
153
- let shouldPrevent = true;
143
+ event.preventDefault();
154
144
  const currentIndex = current.indexOf(active);
155
- let newIndex = 0;
145
+ let newIndex;
156
146
  let target = current;
157
147
  switch (key) {
158
- case "Tab":
159
- shouldPrevent = this.#moveFocus(shiftKey ? "previous" : "next");
160
- break;
161
148
  case "End":
162
149
  newIndex = -1;
163
150
  break;
@@ -179,16 +166,13 @@ var RovingTabIndex = class _RovingTabIndex {
179
166
  default: {
180
167
  target = this.#focusablesByFirstChar.get(key.toUpperCase()) ?? [];
181
168
  const foundIndex = target.findIndex(
182
- (focusable) => current.indexOf(focusable) > currentIndex
169
+ (focusable2) => current.indexOf(focusable2) > currentIndex
183
170
  );
184
171
  newIndex = foundIndex >= 0 ? foundIndex : 0;
185
172
  }
186
173
  }
187
- shouldPrevent && event.preventDefault();
188
- if (key !== "Tab") {
189
- const focusable = target.at(newIndex);
190
- focusable && focusElement(focusable);
191
- }
174
+ const focusable = target.at(newIndex);
175
+ focusable && focusElement(focusable);
192
176
  };
193
177
  #update(active) {
194
178
  const current = new Set(this.#getFocusables());
@@ -261,24 +245,13 @@ var RovingTabIndex = class _RovingTabIndex {
261
245
  skipVisibilityCheck: true
262
246
  });
263
247
  }
264
- #moveFocus(direction) {
265
- const options = {
266
- composed: true
267
- };
268
- const focusable = direction === "previous" ? getPreviousFocusable(document.body, options) : getNextFocusable(document.body, options);
269
- if (focusable) {
270
- focusElement(focusable);
271
- return true;
272
- }
273
- return false;
274
- }
275
248
  };
276
249
  /**
277
250
  * Roving Tabindex
278
251
  * Lightweight roving tabindex utility with fully focus management.
279
252
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
280
253
  *
281
- * @version 3.1.0
254
+ * @version 3.1.2
282
255
  * @author Yusuke Kamiyamane
283
256
  * @license MIT
284
257
  * @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.0",
3
+ "version": "3.1.2",
4
4
  "description": "Lightweight roving tabindex utility with fully focus management",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",