@y14e/roving-tabindex 1.1.1 → 1.1.3

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
@@ -41,10 +41,10 @@ const cleanup = createRovingTabIndex(container, options);
41
41
 
42
42
  ```ts
43
43
  interface RovingTabIndexOptions {
44
- direction?: 'horizontal' | 'vertical'; // default: both (undefined)
45
- selector?: string;
46
- typeahead?: boolean; // default: false
47
- wrap?: boolean; // default: false
44
+ direction?: 'horizontal' | 'vertical' | undefined; // default: both (undefined)
45
+ selector?: string | undefined;
46
+ typeahead?: boolean; // default: false
47
+ wrap?: boolean; // default: false
48
48
  }
49
49
  ```
50
50
 
package/dist/index.cjs CHANGED
@@ -1,6 +1,13 @@
1
1
  'use strict';
2
2
 
3
3
  // node_modules/@y14e/attributes-utils/dist/index.js
4
+ function addTokenToAttribute(element, attribute, token) {
5
+ const tokens = new Set(
6
+ element.getAttribute(attribute)?.trim().split(/\s+/) ?? []
7
+ );
8
+ tokens.add(token);
9
+ element.setAttribute(attribute, [...tokens].join(" "));
10
+ }
4
11
  var snapshots = /* @__PURE__ */ new WeakMap();
5
12
  function restoreAttributes(elements) {
6
13
  elements.forEach((element) => {
@@ -238,24 +245,31 @@ function createRovingTabIndex(container, options = {}) {
238
245
  if (!(container instanceof Element)) {
239
246
  throw new Error("Invalid container element");
240
247
  }
241
- const { direction, selector, typeahead = false, wrap = false } = options;
248
+ let { direction, selector, typeahead = false, wrap = false } = options;
242
249
  if (typeof direction !== "undefined" && !["horizontal", "vertical"].includes(direction)) {
243
250
  console.warn("Invalid direction. Fallback: both (undefined).");
244
- Object.assign(options, { direction: void 0 });
251
+ direction = void 0;
245
252
  }
246
253
  if (typeof selector !== "undefined" && typeof selector !== "string") {
247
- console.warn("Invalid selector. Fallback: all focusable elements.");
248
- Object.assign(options, { selector: void 0 });
254
+ console.warn(
255
+ "Invalid selector. Fallback: all focusable elements (undefined)."
256
+ );
257
+ selector = void 0;
249
258
  }
250
259
  if (typeof typeahead !== "boolean") {
251
260
  console.warn("Invalid typeahead. Fallback: false.");
252
- Object.assign(options, { typeahead: false });
261
+ typeahead = false;
253
262
  }
254
263
  if (typeof wrap !== "boolean") {
255
264
  console.warn("Invalid wrap. Fallback: false.");
256
- Object.assign(options, { wrap: false });
265
+ wrap = false;
257
266
  }
258
- const roving = new RovingTabIndex(container, options);
267
+ const roving = new RovingTabIndex(container, {
268
+ direction,
269
+ selector,
270
+ typeahead,
271
+ wrap
272
+ });
259
273
  return () => roving.destroy();
260
274
  }
261
275
  var RovingTabIndex = class {
@@ -325,7 +339,7 @@ var RovingTabIndex = class {
325
339
  event.preventDefault();
326
340
  event.stopPropagation();
327
341
  const currentIndex = focusables.indexOf(active);
328
- let rawIndex = currentIndex;
342
+ let rawIndex;
329
343
  let newIndex = currentIndex;
330
344
  let target = focusables;
331
345
  switch (key) {
@@ -396,16 +410,21 @@ var RovingTabIndex = class {
396
410
  if (!this.#options.typeahead) {
397
411
  return;
398
412
  }
399
- const shortcuts = c.ariaKeyShortcuts;
400
- const keys = (shortcuts?.split(/\s+/) ?? [c.textContent?.trim()[0] ?? ""]).filter((key) => /^\S$/i.test(key)).map((key) => key.toLowerCase());
413
+ const shortcuts = c.ariaKeyShortcuts?.trim() ?? "";
414
+ const keys = new Set(
415
+ shortcuts ? shortcuts.split(/\s+/).filter((key) => /^\S$/i.test(key)).map((key) => key.toLowerCase()) : []
416
+ );
417
+ const char = c.textContent?.trim()?.at(0)?.toLowerCase();
418
+ if (char) {
419
+ keys.add(char);
420
+ saveAttributes([c], ["aria-keyshortcuts"]);
421
+ addTokenToAttribute(c, "aria-keyshortcuts", char);
422
+ }
401
423
  keys.forEach((key) => {
402
424
  const focusables = this.#focusablesByFirstChar.get(key) ?? [];
403
425
  focusables.push(c);
404
426
  this.#focusablesByFirstChar.set(key, focusables);
405
427
  });
406
- const first = keys[0];
407
- saveAttributes([c], ["aria-keyshortcuts"]);
408
- !shortcuts && first && c.setAttribute("aria-keyshortcuts", first);
409
428
  });
410
429
  if (active && this.#focusables.has(active)) {
411
430
  this.#focusables.forEach((focusable) => {
@@ -444,7 +463,7 @@ function getActiveElement() {
444
463
  * Lightweight roving tabindex utility with fully focus management.
445
464
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
446
465
  *
447
- * @version 1.1.1
466
+ * @version 1.1.3
448
467
  * @author Yusuke Kamiyamane
449
468
  * @license MIT
450
469
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.d.cts CHANGED
@@ -3,15 +3,15 @@
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.1.1
6
+ * @version 1.1.3
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
10
10
  * @see {@link https://github.com/y14e/roving-tabindex}
11
11
  */
12
12
  interface RovingTabIndexOptions {
13
- readonly direction?: 'horizontal' | 'vertical';
14
- readonly selector?: string;
13
+ readonly direction?: 'horizontal' | 'vertical' | undefined;
14
+ readonly selector?: string | undefined;
15
15
  readonly typeahead?: boolean;
16
16
  readonly wrap?: boolean;
17
17
  }
package/dist/index.d.ts CHANGED
@@ -3,15 +3,15 @@
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.1.1
6
+ * @version 1.1.3
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
10
10
  * @see {@link https://github.com/y14e/roving-tabindex}
11
11
  */
12
12
  interface RovingTabIndexOptions {
13
- readonly direction?: 'horizontal' | 'vertical';
14
- readonly selector?: string;
13
+ readonly direction?: 'horizontal' | 'vertical' | undefined;
14
+ readonly selector?: string | undefined;
15
15
  readonly typeahead?: boolean;
16
16
  readonly wrap?: boolean;
17
17
  }
package/dist/index.js CHANGED
@@ -1,4 +1,11 @@
1
1
  // node_modules/@y14e/attributes-utils/dist/index.js
2
+ function addTokenToAttribute(element, attribute, token) {
3
+ const tokens = new Set(
4
+ element.getAttribute(attribute)?.trim().split(/\s+/) ?? []
5
+ );
6
+ tokens.add(token);
7
+ element.setAttribute(attribute, [...tokens].join(" "));
8
+ }
2
9
  var snapshots = /* @__PURE__ */ new WeakMap();
3
10
  function restoreAttributes(elements) {
4
11
  elements.forEach((element) => {
@@ -236,24 +243,31 @@ function createRovingTabIndex(container, options = {}) {
236
243
  if (!(container instanceof Element)) {
237
244
  throw new Error("Invalid container element");
238
245
  }
239
- const { direction, selector, typeahead = false, wrap = false } = options;
246
+ let { direction, selector, typeahead = false, wrap = false } = options;
240
247
  if (typeof direction !== "undefined" && !["horizontal", "vertical"].includes(direction)) {
241
248
  console.warn("Invalid direction. Fallback: both (undefined).");
242
- Object.assign(options, { direction: void 0 });
249
+ direction = void 0;
243
250
  }
244
251
  if (typeof selector !== "undefined" && typeof selector !== "string") {
245
- console.warn("Invalid selector. Fallback: all focusable elements.");
246
- Object.assign(options, { selector: void 0 });
252
+ console.warn(
253
+ "Invalid selector. Fallback: all focusable elements (undefined)."
254
+ );
255
+ selector = void 0;
247
256
  }
248
257
  if (typeof typeahead !== "boolean") {
249
258
  console.warn("Invalid typeahead. Fallback: false.");
250
- Object.assign(options, { typeahead: false });
259
+ typeahead = false;
251
260
  }
252
261
  if (typeof wrap !== "boolean") {
253
262
  console.warn("Invalid wrap. Fallback: false.");
254
- Object.assign(options, { wrap: false });
263
+ wrap = false;
255
264
  }
256
- const roving = new RovingTabIndex(container, options);
265
+ const roving = new RovingTabIndex(container, {
266
+ direction,
267
+ selector,
268
+ typeahead,
269
+ wrap
270
+ });
257
271
  return () => roving.destroy();
258
272
  }
259
273
  var RovingTabIndex = class {
@@ -323,7 +337,7 @@ var RovingTabIndex = class {
323
337
  event.preventDefault();
324
338
  event.stopPropagation();
325
339
  const currentIndex = focusables.indexOf(active);
326
- let rawIndex = currentIndex;
340
+ let rawIndex;
327
341
  let newIndex = currentIndex;
328
342
  let target = focusables;
329
343
  switch (key) {
@@ -394,16 +408,21 @@ var RovingTabIndex = class {
394
408
  if (!this.#options.typeahead) {
395
409
  return;
396
410
  }
397
- const shortcuts = c.ariaKeyShortcuts;
398
- const keys = (shortcuts?.split(/\s+/) ?? [c.textContent?.trim()[0] ?? ""]).filter((key) => /^\S$/i.test(key)).map((key) => key.toLowerCase());
411
+ const shortcuts = c.ariaKeyShortcuts?.trim() ?? "";
412
+ const keys = new Set(
413
+ shortcuts ? shortcuts.split(/\s+/).filter((key) => /^\S$/i.test(key)).map((key) => key.toLowerCase()) : []
414
+ );
415
+ const char = c.textContent?.trim()?.at(0)?.toLowerCase();
416
+ if (char) {
417
+ keys.add(char);
418
+ saveAttributes([c], ["aria-keyshortcuts"]);
419
+ addTokenToAttribute(c, "aria-keyshortcuts", char);
420
+ }
399
421
  keys.forEach((key) => {
400
422
  const focusables = this.#focusablesByFirstChar.get(key) ?? [];
401
423
  focusables.push(c);
402
424
  this.#focusablesByFirstChar.set(key, focusables);
403
425
  });
404
- const first = keys[0];
405
- saveAttributes([c], ["aria-keyshortcuts"]);
406
- !shortcuts && first && c.setAttribute("aria-keyshortcuts", first);
407
426
  });
408
427
  if (active && this.#focusables.has(active)) {
409
428
  this.#focusables.forEach((focusable) => {
@@ -442,7 +461,7 @@ function getActiveElement() {
442
461
  * Lightweight roving tabindex utility with fully focus management.
443
462
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
444
463
  *
445
- * @version 1.1.1
464
+ * @version 1.1.3
446
465
  * @author Yusuke Kamiyamane
447
466
  * @license MIT
448
467
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@y14e/roving-tabindex",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "Lightweight roving tabindex utility with fully focus management",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",