@y14e/accordion 1.4.6 → 1.4.7

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/accordion
10
10
 
11
11
  ```ts
12
12
  // npm
13
- import Accordion from '@y14e/accordion@1.4.6';
13
+ import Accordion from '@y14e/accordion@1.4.7';
14
14
 
15
15
  // CDNs
16
- import Accordion from 'https://esm.sh/@y14e/accordion@1.4.6';
16
+ import Accordion from 'https://esm.sh/@y14e/accordion@1.4.7';
17
17
  // or
18
- import Accordion from 'https://cdn.jsdelivr.net/npm/@y14e/accordion@1.4.6/+esm';
18
+ import Accordion from 'https://cdn.jsdelivr.net/npm/@y14e/accordion@1.4.7/+esm';
19
19
  // or
20
- import Accordion from 'https://esm.unpkg.com/@y14e/accordion@1.4.6';
20
+ import Accordion from 'https://esm.unpkg.com/@y14e/accordion@1.4.7';
21
21
  ```
22
22
 
23
23
  ## Usage
package/dist/index.cjs CHANGED
@@ -13,11 +13,10 @@ function addTokenToAttribute(element, attribute, token, options = {}) {
13
13
  const tokens = value ? parse(value).filter(Boolean) : [];
14
14
  if (caseInsensitive) {
15
15
  const lower = token.toLowerCase();
16
- if (tokens.some((token2) => token2.toLowerCase() === lower)) {
17
- return;
16
+ if (tokens.every((token2) => token2.toLowerCase() !== lower)) {
17
+ tokens.push(token);
18
+ element.setAttribute(attribute, serialize(tokens));
18
19
  }
19
- tokens.push(token);
20
- element.setAttribute(attribute, serialize(tokens));
21
20
  return;
22
21
  }
23
22
  const set = new Set(tokens);
@@ -119,11 +118,10 @@ function addTokenToAttribute2(element, attribute, token, options = {}) {
119
118
  const tokens = value ? parse(value).filter(Boolean) : [];
120
119
  if (caseInsensitive) {
121
120
  const lower = token.toLowerCase();
122
- if (tokens.some((token2) => token2.toLowerCase() === lower)) {
123
- return;
121
+ if (tokens.every((token2) => token2.toLowerCase() !== lower)) {
122
+ tokens.push(token);
123
+ element.setAttribute(attribute, serialize(tokens));
124
124
  }
125
- tokens.push(token);
126
- element.setAttribute(attribute, serialize(tokens));
127
125
  return;
128
126
  }
129
127
  const set = new Set(tokens);
@@ -402,6 +400,8 @@ function createRovingTabIndex(container, options = {}) {
402
400
  let {
403
401
  direction,
404
402
  navigationOnly = false,
403
+ noMemory = false,
404
+ noStart = false,
405
405
  selector,
406
406
  typeahead = false,
407
407
  wrap = false
@@ -414,6 +414,14 @@ function createRovingTabIndex(container, options = {}) {
414
414
  console.warn("Invalid navigationOnly option. Fallback: false.");
415
415
  navigationOnly = false;
416
416
  }
417
+ if (typeof noMemory !== "boolean") {
418
+ console.warn("Invalid noMemory option. Fallback: false.");
419
+ noMemory = false;
420
+ }
421
+ if (typeof noStart !== "boolean") {
422
+ console.warn("Invalid noStart option. Fallback: false.");
423
+ noStart = false;
424
+ }
417
425
  if (typeof selector !== "undefined" && (typeof selector !== "string" || !selector.trim())) {
418
426
  console.warn(
419
427
  "Invalid selector. Fallback: all focusable elements (undefined)."
@@ -428,13 +436,15 @@ function createRovingTabIndex(container, options = {}) {
428
436
  console.warn("Invalid wrap option. Fallback: false.");
429
437
  wrap = false;
430
438
  }
431
- const settings = { navigationOnly, typeahead, wrap };
432
- if (direction) {
433
- Object.assign(settings, { direction });
434
- }
435
- if (selector) {
436
- Object.assign(settings, { selector });
437
- }
439
+ const settings = {
440
+ navigationOnly,
441
+ noMemory,
442
+ noStart,
443
+ typeahead,
444
+ wrap
445
+ };
446
+ direction && Object.assign(settings, { direction });
447
+ selector && Object.assign(settings, { selector });
438
448
  const roving = new RovingTabIndex(container, settings);
439
449
  return () => roving.destroy();
440
450
  }
@@ -467,12 +477,29 @@ var RovingTabIndex = class {
467
477
  #initialize() {
468
478
  this.#update(document.activeElement);
469
479
  this.#controller = new AbortController();
480
+ const { signal } = this.#controller;
481
+ document.addEventListener("focusin", this.#onFocusIn, {
482
+ capture: true,
483
+ signal
484
+ });
470
485
  document.addEventListener("keydown", this.#onKeyDown, {
471
486
  capture: true,
472
- signal: this.#controller.signal
487
+ signal
473
488
  });
474
489
  this.#container.setAttribute("data-roving-tabindex-initialized", "");
475
490
  }
491
+ #onFocusIn = (event) => {
492
+ const { target } = event;
493
+ if (!(target instanceof Element)) {
494
+ return;
495
+ }
496
+ const isFocusable22 = this.#focusables.has(target);
497
+ if (this.#options.noMemory && !isFocusable22) {
498
+ this.#update(null);
499
+ return;
500
+ }
501
+ isFocusable22 && this.#update(target);
502
+ };
476
503
  #onKeyDown = (event) => {
477
504
  if (!event.composedPath().includes(this.#container)) {
478
505
  return;
@@ -503,7 +530,6 @@ var RovingTabIndex = class {
503
530
  return;
504
531
  }
505
532
  event.preventDefault();
506
- event.stopPropagation();
507
533
  const currentIndex = current.indexOf(active);
508
534
  let rawIndex;
509
535
  let newIndex = currentIndex;
@@ -534,11 +560,7 @@ var RovingTabIndex = class {
534
560
  }
535
561
  }
536
562
  const focusable = target.at(newIndex);
537
- if (!focusable) {
538
- return;
539
- }
540
- this.#update(focusable);
541
- focusElement(focusable);
563
+ focusable && focusElement(focusable);
542
564
  };
543
565
  #update(active) {
544
566
  const current = new Set(this.#getFocusables());
@@ -553,7 +575,7 @@ var RovingTabIndex = class {
553
575
  index >= 0 && focusables.splice(index, 1);
554
576
  });
555
577
  }
556
- const { navigationOnly } = this.#options;
578
+ const { navigationOnly, noStart, typeahead } = this.#options;
557
579
  for (const focusable of current) {
558
580
  if (this.#focusables.has(focusable)) {
559
581
  continue;
@@ -563,7 +585,7 @@ var RovingTabIndex = class {
563
585
  saveAttributes2([focusable], ["tabindex"]);
564
586
  focusable.setAttribute("tabindex", "-1");
565
587
  }
566
- if (!this.#options.typeahead) {
588
+ if (!typeahead) {
567
589
  continue;
568
590
  }
569
591
  const value = focusable.ariaKeyShortcuts?.trim();
@@ -594,7 +616,7 @@ var RovingTabIndex = class {
594
616
  return;
595
617
  }
596
618
  [...this.#focusables].forEach((focusable, i) => {
597
- focusable.setAttribute("tabindex", i ? "-1" : "0");
619
+ focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
598
620
  });
599
621
  }
600
622
  #createSelectorFilter() {
@@ -605,7 +627,8 @@ var RovingTabIndex = class {
605
627
  return getFocusables(this.#container, {
606
628
  composed: true,
607
629
  filter: this.#selectorFilter,
608
- skipNegativeTabIndexCheck: !this.#options.navigationOnly
630
+ skipNegativeTabIndexCheck: !this.#options.navigationOnly,
631
+ skipVisibilityCheck: true
609
632
  });
610
633
  }
611
634
  };
@@ -895,7 +918,7 @@ function waitAnimationFinish(animation) {
895
918
  * Accordion
896
919
  * WAI-ARIA compliant accordion pattern implementation in TypeScript.
897
920
  *
898
- * @version 1.4.6
921
+ * @version 1.4.7
899
922
  * @author Yusuke Kamiyamane
900
923
  * @license MIT
901
924
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -907,7 +930,7 @@ function waitAnimationFinish(animation) {
907
930
  (**
908
931
  * Attributes Utils
909
932
  *
910
- * @version 1.1.0
933
+ * @version 1.1.1
911
934
  * @author Yusuke Kamiyamane
912
935
  * @license MIT
913
936
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -931,7 +954,7 @@ function waitAnimationFinish(animation) {
931
954
  * Lightweight roving tabindex utility with fully focus management.
932
955
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
933
956
  *
934
- * @version 2.0.4
957
+ * @version 3.0.0
935
958
  * @author Yusuke Kamiyamane
936
959
  * @license MIT
937
960
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -943,7 +966,7 @@ function waitAnimationFinish(animation) {
943
966
  (**
944
967
  * Attributes Utils
945
968
  *
946
- * @version 1.1.0
969
+ * @version 1.1.1
947
970
  * @author Yusuke Kamiyamane
948
971
  * @license MIT
949
972
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -956,7 +979,7 @@ function waitAnimationFinish(animation) {
956
979
  * High-precision focus management utility with full composed tree support.
957
980
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
958
981
  *
959
- * @version 4.3.1
982
+ * @version 4.3.2
960
983
  * @author Yusuke Kamiyamane
961
984
  * @license MIT
962
985
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.d.cts CHANGED
@@ -2,7 +2,7 @@
2
2
  * Accordion
3
3
  * WAI-ARIA compliant accordion pattern implementation in TypeScript.
4
4
  *
5
- * @version 1.4.6
5
+ * @version 1.4.7
6
6
  * @author Yusuke Kamiyamane
7
7
  * @license MIT
8
8
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * Accordion
3
3
  * WAI-ARIA compliant accordion pattern implementation in TypeScript.
4
4
  *
5
- * @version 1.4.6
5
+ * @version 1.4.7
6
6
  * @author Yusuke Kamiyamane
7
7
  * @license MIT
8
8
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.js CHANGED
@@ -11,11 +11,10 @@ function addTokenToAttribute(element, attribute, token, options = {}) {
11
11
  const tokens = value ? parse(value).filter(Boolean) : [];
12
12
  if (caseInsensitive) {
13
13
  const lower = token.toLowerCase();
14
- if (tokens.some((token2) => token2.toLowerCase() === lower)) {
15
- return;
14
+ if (tokens.every((token2) => token2.toLowerCase() !== lower)) {
15
+ tokens.push(token);
16
+ element.setAttribute(attribute, serialize(tokens));
16
17
  }
17
- tokens.push(token);
18
- element.setAttribute(attribute, serialize(tokens));
19
18
  return;
20
19
  }
21
20
  const set = new Set(tokens);
@@ -117,11 +116,10 @@ function addTokenToAttribute2(element, attribute, token, options = {}) {
117
116
  const tokens = value ? parse(value).filter(Boolean) : [];
118
117
  if (caseInsensitive) {
119
118
  const lower = token.toLowerCase();
120
- if (tokens.some((token2) => token2.toLowerCase() === lower)) {
121
- return;
119
+ if (tokens.every((token2) => token2.toLowerCase() !== lower)) {
120
+ tokens.push(token);
121
+ element.setAttribute(attribute, serialize(tokens));
122
122
  }
123
- tokens.push(token);
124
- element.setAttribute(attribute, serialize(tokens));
125
123
  return;
126
124
  }
127
125
  const set = new Set(tokens);
@@ -400,6 +398,8 @@ function createRovingTabIndex(container, options = {}) {
400
398
  let {
401
399
  direction,
402
400
  navigationOnly = false,
401
+ noMemory = false,
402
+ noStart = false,
403
403
  selector,
404
404
  typeahead = false,
405
405
  wrap = false
@@ -412,6 +412,14 @@ function createRovingTabIndex(container, options = {}) {
412
412
  console.warn("Invalid navigationOnly option. Fallback: false.");
413
413
  navigationOnly = false;
414
414
  }
415
+ if (typeof noMemory !== "boolean") {
416
+ console.warn("Invalid noMemory option. Fallback: false.");
417
+ noMemory = false;
418
+ }
419
+ if (typeof noStart !== "boolean") {
420
+ console.warn("Invalid noStart option. Fallback: false.");
421
+ noStart = false;
422
+ }
415
423
  if (typeof selector !== "undefined" && (typeof selector !== "string" || !selector.trim())) {
416
424
  console.warn(
417
425
  "Invalid selector. Fallback: all focusable elements (undefined)."
@@ -426,13 +434,15 @@ function createRovingTabIndex(container, options = {}) {
426
434
  console.warn("Invalid wrap option. Fallback: false.");
427
435
  wrap = false;
428
436
  }
429
- const settings = { navigationOnly, typeahead, wrap };
430
- if (direction) {
431
- Object.assign(settings, { direction });
432
- }
433
- if (selector) {
434
- Object.assign(settings, { selector });
435
- }
437
+ const settings = {
438
+ navigationOnly,
439
+ noMemory,
440
+ noStart,
441
+ typeahead,
442
+ wrap
443
+ };
444
+ direction && Object.assign(settings, { direction });
445
+ selector && Object.assign(settings, { selector });
436
446
  const roving = new RovingTabIndex(container, settings);
437
447
  return () => roving.destroy();
438
448
  }
@@ -465,12 +475,29 @@ var RovingTabIndex = class {
465
475
  #initialize() {
466
476
  this.#update(document.activeElement);
467
477
  this.#controller = new AbortController();
478
+ const { signal } = this.#controller;
479
+ document.addEventListener("focusin", this.#onFocusIn, {
480
+ capture: true,
481
+ signal
482
+ });
468
483
  document.addEventListener("keydown", this.#onKeyDown, {
469
484
  capture: true,
470
- signal: this.#controller.signal
485
+ signal
471
486
  });
472
487
  this.#container.setAttribute("data-roving-tabindex-initialized", "");
473
488
  }
489
+ #onFocusIn = (event) => {
490
+ const { target } = event;
491
+ if (!(target instanceof Element)) {
492
+ return;
493
+ }
494
+ const isFocusable22 = this.#focusables.has(target);
495
+ if (this.#options.noMemory && !isFocusable22) {
496
+ this.#update(null);
497
+ return;
498
+ }
499
+ isFocusable22 && this.#update(target);
500
+ };
474
501
  #onKeyDown = (event) => {
475
502
  if (!event.composedPath().includes(this.#container)) {
476
503
  return;
@@ -501,7 +528,6 @@ var RovingTabIndex = class {
501
528
  return;
502
529
  }
503
530
  event.preventDefault();
504
- event.stopPropagation();
505
531
  const currentIndex = current.indexOf(active);
506
532
  let rawIndex;
507
533
  let newIndex = currentIndex;
@@ -532,11 +558,7 @@ var RovingTabIndex = class {
532
558
  }
533
559
  }
534
560
  const focusable = target.at(newIndex);
535
- if (!focusable) {
536
- return;
537
- }
538
- this.#update(focusable);
539
- focusElement(focusable);
561
+ focusable && focusElement(focusable);
540
562
  };
541
563
  #update(active) {
542
564
  const current = new Set(this.#getFocusables());
@@ -551,7 +573,7 @@ var RovingTabIndex = class {
551
573
  index >= 0 && focusables.splice(index, 1);
552
574
  });
553
575
  }
554
- const { navigationOnly } = this.#options;
576
+ const { navigationOnly, noStart, typeahead } = this.#options;
555
577
  for (const focusable of current) {
556
578
  if (this.#focusables.has(focusable)) {
557
579
  continue;
@@ -561,7 +583,7 @@ var RovingTabIndex = class {
561
583
  saveAttributes2([focusable], ["tabindex"]);
562
584
  focusable.setAttribute("tabindex", "-1");
563
585
  }
564
- if (!this.#options.typeahead) {
586
+ if (!typeahead) {
565
587
  continue;
566
588
  }
567
589
  const value = focusable.ariaKeyShortcuts?.trim();
@@ -592,7 +614,7 @@ var RovingTabIndex = class {
592
614
  return;
593
615
  }
594
616
  [...this.#focusables].forEach((focusable, i) => {
595
- focusable.setAttribute("tabindex", i ? "-1" : "0");
617
+ focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
596
618
  });
597
619
  }
598
620
  #createSelectorFilter() {
@@ -603,7 +625,8 @@ var RovingTabIndex = class {
603
625
  return getFocusables(this.#container, {
604
626
  composed: true,
605
627
  filter: this.#selectorFilter,
606
- skipNegativeTabIndexCheck: !this.#options.navigationOnly
628
+ skipNegativeTabIndexCheck: !this.#options.navigationOnly,
629
+ skipVisibilityCheck: true
607
630
  });
608
631
  }
609
632
  };
@@ -893,7 +916,7 @@ function waitAnimationFinish(animation) {
893
916
  * Accordion
894
917
  * WAI-ARIA compliant accordion pattern implementation in TypeScript.
895
918
  *
896
- * @version 1.4.6
919
+ * @version 1.4.7
897
920
  * @author Yusuke Kamiyamane
898
921
  * @license MIT
899
922
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -905,7 +928,7 @@ function waitAnimationFinish(animation) {
905
928
  (**
906
929
  * Attributes Utils
907
930
  *
908
- * @version 1.1.0
931
+ * @version 1.1.1
909
932
  * @author Yusuke Kamiyamane
910
933
  * @license MIT
911
934
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -929,7 +952,7 @@ function waitAnimationFinish(animation) {
929
952
  * Lightweight roving tabindex utility with fully focus management.
930
953
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
931
954
  *
932
- * @version 2.0.4
955
+ * @version 3.0.0
933
956
  * @author Yusuke Kamiyamane
934
957
  * @license MIT
935
958
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -941,7 +964,7 @@ function waitAnimationFinish(animation) {
941
964
  (**
942
965
  * Attributes Utils
943
966
  *
944
- * @version 1.1.0
967
+ * @version 1.1.1
945
968
  * @author Yusuke Kamiyamane
946
969
  * @license MIT
947
970
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -954,7 +977,7 @@ function waitAnimationFinish(animation) {
954
977
  * High-precision focus management utility with full composed tree support.
955
978
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
956
979
  *
957
- * @version 4.3.1
980
+ * @version 4.3.2
958
981
  * @author Yusuke Kamiyamane
959
982
  * @license MIT
960
983
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@y14e/accordion",
3
- "version": "1.4.6",
3
+ "version": "1.4.7",
4
4
  "description": "WAI-ARIA compliant accordion pattern implementation in TypeScript",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -43,9 +43,9 @@
43
43
  },
44
44
  "homepage": "https://github.com/y14e/accordion#readme",
45
45
  "devDependencies": {
46
- "@y14e/attributes-utils": "^1.1.0",
46
+ "@y14e/attributes-utils": "^1.1.1",
47
47
  "@y14e/button": "^1.0.0",
48
- "@y14e/roving-tabindex": "^2.0.4",
48
+ "@y14e/roving-tabindex": "^3.0.0",
49
49
  "bun-types": "latest",
50
50
  "tsup": "^8.0.0",
51
51
  "typescript": "^5.6.0",