@y14e/tabs 1.5.8 → 2.0.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/tabs
10
10
 
11
11
  ```ts
12
12
  // npm
13
- import Tabs from '@y14e/tabs@1.5.8';
13
+ import Tabs from '@y14e/tabs@2.0.0';
14
14
 
15
15
  // CDNs
16
- import Tabs from 'https://esm.sh/@y14e/tabs@1.5.8';
16
+ import Tabs from 'https://esm.sh/@y14e/tabs@2.0.0';
17
17
  // or
18
- import Tabs from 'https://cdn.jsdelivr.net/npm/@y14e/tabs@1.5.8/+esm';
18
+ import Tabs from 'https://cdn.jsdelivr.net/npm/@y14e/tabs@2.0.0/+esm';
19
19
  // or
20
- import Tabs from 'https://esm.unpkg.com/@y14e/tabs@1.5.8';
20
+ import Tabs from 'https://esm.unpkg.com/@y14e/tabs@2.0.0';
21
21
  ```
22
22
 
23
23
  ## Usage
@@ -39,7 +39,7 @@ interface TabsOptions {
39
39
  crossFade?: boolean; // default: true
40
40
  duration?: number; // ms (default: 300)
41
41
  easing?: string; // <easing-function> (default: 'ease')
42
- fade?: boolean; // default: false
42
+ fade?: boolean; // default: true
43
43
  };
44
44
  indicator?: {
45
45
  duration?: number; // ms (default: 300)
package/dist/index.cjs CHANGED
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
 
3
3
  // node_modules/@y14e/attributes-utils/dist/index.js
4
- var defaultParser = (value) => value.split(/\s+/);
5
- var defaultSerializer = (tokens) => tokens.join(" ");
4
+ var DEFAULT_PARSER = (value) => value.split(/\s+/);
5
+ var DEFAULT_SERIALIZER = (tokens) => tokens.join(" ");
6
6
  function addTokenToAttribute(element, attribute, token, options = {}) {
7
7
  const {
8
8
  caseInsensitive = false,
9
- parse = defaultParser,
10
- serialize = defaultSerializer
9
+ parse = DEFAULT_PARSER,
10
+ serialize = DEFAULT_SERIALIZER
11
11
  } = options;
12
12
  const value = element.getAttribute(attribute)?.trim();
13
13
  const tokens = value ? parse(value).filter(Boolean) : [];
@@ -17,11 +17,11 @@ function addTokenToAttribute(element, attribute, token, options = {}) {
17
17
  tokens.push(token);
18
18
  element.setAttribute(attribute, serialize(tokens));
19
19
  }
20
- return;
20
+ } else {
21
+ const set = new Set(tokens);
22
+ set.add(token);
23
+ element.setAttribute(attribute, serialize([...set]));
21
24
  }
22
- const set = new Set(tokens);
23
- set.add(token);
24
- element.setAttribute(attribute, serialize([...set]));
25
25
  }
26
26
  var snapshots = /* @__PURE__ */ new WeakMap();
27
27
  function restoreAttributes(elements) {
@@ -106,13 +106,13 @@ function getActiveElement() {
106
106
  }
107
107
 
108
108
  // node_modules/@y14e/roving-tabindex/dist/index.js
109
- var defaultParser2 = (value) => value.split(/\s+/);
110
- var defaultSerializer2 = (tokens) => tokens.join(" ");
109
+ var DEFAULT_PARSER2 = (value) => value.split(/\s+/);
110
+ var DEFAULT_SERIALIZER2 = (tokens) => tokens.join(" ");
111
111
  function addTokenToAttribute2(element, attribute, token, options = {}) {
112
112
  const {
113
113
  caseInsensitive = false,
114
- parse = defaultParser2,
115
- serialize = defaultSerializer2
114
+ parse = DEFAULT_PARSER2,
115
+ serialize = DEFAULT_SERIALIZER2
116
116
  } = options;
117
117
  const value = element.getAttribute(attribute)?.trim();
118
118
  const tokens = value ? parse(value).filter(Boolean) : [];
@@ -122,11 +122,11 @@ function addTokenToAttribute2(element, attribute, token, options = {}) {
122
122
  tokens.push(token);
123
123
  element.setAttribute(attribute, serialize(tokens));
124
124
  }
125
- return;
125
+ } else {
126
+ const set = new Set(tokens);
127
+ set.add(token);
128
+ element.setAttribute(attribute, serialize([...set]));
126
129
  }
127
- const set = new Set(tokens);
128
- set.add(token);
129
- element.setAttribute(attribute, serialize([...set]));
130
130
  }
131
131
  var snapshots2 = /* @__PURE__ */ new WeakMap();
132
132
  function restoreAttributes2(elements) {
@@ -193,21 +193,16 @@ function getFocusables(container = document.body, options = {}) {
193
193
  const elements = [];
194
194
  if (composed || include) {
195
195
  let traverse2 = function(node) {
196
- if (node instanceof Element) {
197
- if (isFocusable(node, {
198
- skipNegativeTabIndexCheck,
199
- skipVisibilityCheck
200
- }) || include?.(node)) {
201
- elements[elements.length] = node;
202
- }
196
+ if (!(node instanceof Element)) {
197
+ return;
198
+ }
199
+ if (isFocusable(node, { skipNegativeTabIndexCheck, skipVisibilityCheck }) || include?.(node)) {
200
+ elements[elements.length] = node;
203
201
  }
204
202
  const children = getComposedChildren(node);
205
203
  for (let i = 0, l = children.length; i < l; i++) {
206
204
  const child = children[i];
207
- if (!child) {
208
- continue;
209
- }
210
- traverse2(child);
205
+ child && traverse2(child);
211
206
  }
212
207
  };
213
208
  traverse2(container);
@@ -215,10 +210,7 @@ function getFocusables(container = document.body, options = {}) {
215
210
  const candidates = container.querySelectorAll(FOCUSABLE_SELECTOR);
216
211
  for (let i = 0, l = candidates.length; i < l; i++) {
217
212
  const candidate = candidates[i];
218
- if (!(candidate instanceof Element)) {
219
- continue;
220
- }
221
- if (isFocusable(candidate, {
213
+ if (candidate && isFocusable(candidate, {
222
214
  skipNegativeTabIndexCheck,
223
215
  skipVisibilityCheck
224
216
  })) {
@@ -321,23 +313,19 @@ function normalizeRadioGroup(elements) {
321
313
  for (const group of map.values()) {
322
314
  placeholder.add(group.find((radio) => radio.checked) ?? group[0]);
323
315
  }
324
- return elements.filter((element) => {
325
- if (isUngroupedRadio(element)) {
326
- return placeholder.has(element);
327
- }
328
- return true;
329
- });
316
+ return elements.filter(
317
+ (element) => isUngroupedRadio(element) ? placeholder.has(element) : true
318
+ );
330
319
  }
331
320
  function sortByTabIndex(elements) {
332
321
  const ordered = [];
333
322
  const natural = [];
334
323
  for (let i = 0, l = elements.length; i < l; i++) {
335
324
  const element = elements[i];
336
- if (!element) {
337
- continue;
325
+ if (element) {
326
+ const target = getTabIndex(element) > 0 ? ordered : natural;
327
+ target[target.length] = element;
338
328
  }
339
- const target = getTabIndex(element) > 0 ? ordered : natural;
340
- target[target.length] = element;
341
329
  }
342
330
  ordered.sort((a, b) => getTabIndex(a) - getTabIndex(b));
343
331
  let count = 0;
@@ -494,11 +482,7 @@ var RovingTabIndex = class {
494
482
  return;
495
483
  }
496
484
  const isFocusable22 = this.#focusables.has(target);
497
- if (this.#options.noMemory && !isFocusable22) {
498
- this.#update(null);
499
- return;
500
- }
501
- isFocusable22 && this.#update(target);
485
+ this.#options.noMemory && !isFocusable22 ? this.#update(null) : isFocusable22 && this.#update(target);
502
486
  };
503
487
  #onKeyDown = (event) => {
504
488
  if (!event.composedPath().includes(this.#container)) {
@@ -531,8 +515,7 @@ var RovingTabIndex = class {
531
515
  }
532
516
  event.preventDefault();
533
517
  const currentIndex = current.indexOf(active);
534
- let rawIndex;
535
- let newIndex = currentIndex;
518
+ let newIndex;
536
519
  let target = current;
537
520
  switch (key) {
538
521
  case "End":
@@ -542,15 +525,17 @@ var RovingTabIndex = class {
542
525
  newIndex = 0;
543
526
  break;
544
527
  case "ArrowLeft":
545
- case "ArrowUp":
546
- rawIndex = currentIndex - 1;
528
+ case "ArrowUp": {
529
+ const rawIndex = currentIndex - 1;
547
530
  newIndex = wrap ? rawIndex : Math.max(rawIndex, 0);
548
531
  break;
532
+ }
549
533
  case "ArrowRight":
550
- case "ArrowDown":
551
- rawIndex = currentIndex + 1;
534
+ case "ArrowDown": {
535
+ const rawIndex = currentIndex + 1;
552
536
  newIndex = wrap ? rawIndex % current.length : Math.min(rawIndex, current.length - 1);
553
537
  break;
538
+ }
554
539
  default: {
555
540
  target = this.#focusablesByFirstChar.get(key.toUpperCase()) ?? [];
556
541
  const foundIndex = target.findIndex(
@@ -565,15 +550,14 @@ var RovingTabIndex = class {
565
550
  #update(active) {
566
551
  const current = new Set(this.#getFocusables());
567
552
  for (const focusable of this.#focusables) {
568
- if (current.has(focusable)) {
569
- continue;
553
+ if (!current.has(focusable)) {
554
+ focusable.isConnected && restoreAttributes2([focusable]);
555
+ this.#focusables.delete(focusable);
556
+ this.#focusablesByFirstChar.forEach((focusables) => {
557
+ const index = focusables.indexOf(focusable);
558
+ index >= 0 && focusables.splice(index, 1);
559
+ });
570
560
  }
571
- focusable.isConnected && restoreAttributes2([focusable]);
572
- this.#focusables.delete(focusable);
573
- this.#focusablesByFirstChar.forEach((focusables) => {
574
- const index = focusables.indexOf(focusable);
575
- index >= 0 && focusables.splice(index, 1);
576
- });
577
561
  }
578
562
  const { navigationOnly, noStart, typeahead } = this.#options;
579
563
  for (const focusable of current) {
@@ -588,11 +572,11 @@ var RovingTabIndex = class {
588
572
  if (!typeahead) {
589
573
  continue;
590
574
  }
575
+ const char = focusable.textContent?.trim()?.at(0)?.toUpperCase();
591
576
  const value = focusable.ariaKeyShortcuts?.trim();
592
577
  const keys = new Set(
593
578
  value ? value.split(/\s+/).filter((key) => /^\S$/i.test(key)).map((key) => key.toUpperCase()) : []
594
579
  );
595
- const char = focusable.textContent?.trim()?.at(0)?.toUpperCase();
596
580
  if (char) {
597
581
  keys.add(char);
598
582
  saveAttributes2([focusable], ["aria-keyshortcuts"]);
@@ -606,18 +590,17 @@ var RovingTabIndex = class {
606
590
  this.#focusablesByFirstChar.set(key, focusables);
607
591
  });
608
592
  }
609
- if (navigationOnly) {
610
- return;
611
- }
612
- if (active && this.#focusables.has(active)) {
613
- this.#focusables.forEach((focusable) => {
614
- focusable.setAttribute("tabindex", focusable === active ? "0" : "-1");
615
- });
616
- return;
593
+ if (!navigationOnly) {
594
+ if (active && this.#focusables.has(active)) {
595
+ this.#focusables.forEach((focusable) => {
596
+ focusable.setAttribute("tabindex", focusable === active ? "0" : "-1");
597
+ });
598
+ } else {
599
+ [...this.#focusables].forEach((focusable, i) => {
600
+ focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
601
+ });
602
+ }
617
603
  }
618
- [...this.#focusables].forEach((focusable, i) => {
619
- focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
620
- });
621
604
  }
622
605
  #createSelectorFilter() {
623
606
  const { selector } = this.#options;
@@ -653,7 +636,7 @@ var Tabs = class _Tabs {
653
636
  crossFade: true,
654
637
  duration: 300,
655
638
  easing: "ease",
656
- fade: false
639
+ fade: true
657
640
  },
658
641
  indicator: {
659
642
  duration: 300,
@@ -791,7 +774,7 @@ var Tabs = class _Tabs {
791
774
  }
792
775
  this.#panelElements.forEach((p) => {
793
776
  const { style: style2 } = p;
794
- if (crossFade || fade) {
777
+ if (fade) {
795
778
  style2.setProperty("content-visibility", "visible");
796
779
  style2.setProperty("display", "block");
797
780
  style2.setProperty("opacity", p.hidden ? "0" : "1");
@@ -805,10 +788,7 @@ var Tabs = class _Tabs {
805
788
  p.removeAttribute("hidden");
806
789
  } else {
807
790
  const tab2 = this.#tabElements[i];
808
- if (!tab2) {
809
- return;
810
- }
811
- p.setAttribute("hidden", isFocusable2(tab2) ? "until-found" : "");
791
+ tab2 && p.setAttribute("hidden", isFocusable2(tab2) ? "until-found" : "");
812
792
  }
813
793
  });
814
794
  this.#animation?.cancel();
@@ -840,11 +820,10 @@ var Tabs = class _Tabs {
840
820
  this.#animation.addEventListener(
841
821
  "finish",
842
822
  () => {
843
- if (this.#animation !== animation) {
844
- return;
823
+ if (this.#animation === animation) {
824
+ this.#onAnimationFinish();
825
+ cleanup();
845
826
  }
846
- this.#onAnimationFinish();
847
- cleanup();
848
827
  },
849
828
  {
850
829
  once: true,
@@ -861,10 +840,10 @@ var Tabs = class _Tabs {
861
840
  const isSelected = p === panel;
862
841
  const animation2 = p.animate(
863
842
  {
864
- opacity: fade ? isSelected ? [opacity, opacity, "1"] : [opacity, "0", "0"] : isSelected ? [opacity, "1"] : [opacity, "0"]
843
+ opacity: crossFade || !fade ? isSelected ? [opacity, "1"] : [opacity, "0"] : isSelected ? [opacity, opacity, "1"] : [opacity, "0", "0"]
865
844
  },
866
845
  {
867
- duration: isMatch || !(crossFade || fade) ? 0 : this.#settings.animation.content.duration,
846
+ duration: isMatch || !fade ? 0 : this.#settings.animation.content.duration,
868
847
  easing: "ease"
869
848
  }
870
849
  );
@@ -1034,10 +1013,7 @@ var Tabs = class _Tabs {
1034
1013
  return;
1035
1014
  }
1036
1015
  const tab = this.#bindings.get(panel)?.tabs[0];
1037
- if (!tab) {
1038
- return;
1039
- }
1040
- this.activate(tab, true);
1016
+ tab && this.activate(tab, true);
1041
1017
  };
1042
1018
  #isAvoidedTab(tab) {
1043
1019
  const binding = this.#bindings.get(tab);
@@ -1169,7 +1145,7 @@ function isFocusable2(element) {
1169
1145
  * Tabs
1170
1146
  * WAI-ARIA compliant tabs pattern implementation in TypeScript.
1171
1147
  *
1172
- * @version 1.5.8
1148
+ * @version 2.0.0
1173
1149
  * @author Yusuke Kamiyamane
1174
1150
  * @license MIT
1175
1151
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -1181,7 +1157,7 @@ function isFocusable2(element) {
1181
1157
  (**
1182
1158
  * Attributes Utils
1183
1159
  *
1184
- * @version 1.1.1
1160
+ * @version 1.1.2
1185
1161
  * @author Yusuke Kamiyamane
1186
1162
  * @license MIT
1187
1163
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -1192,7 +1168,7 @@ function isFocusable2(element) {
1192
1168
  (**
1193
1169
  * Button
1194
1170
  *
1195
- * @version 1.0.0
1171
+ * @version 1.0.2
1196
1172
  * @author Yusuke Kamiyamane
1197
1173
  * @license MIT
1198
1174
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -1205,7 +1181,7 @@ function isFocusable2(element) {
1205
1181
  * Lightweight roving tabindex utility with fully focus management.
1206
1182
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
1207
1183
  *
1208
- * @version 3.0.0
1184
+ * @version 3.0.5
1209
1185
  * @author Yusuke Kamiyamane
1210
1186
  * @license MIT
1211
1187
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -1217,7 +1193,7 @@ function isFocusable2(element) {
1217
1193
  (**
1218
1194
  * Attributes Utils
1219
1195
  *
1220
- * @version 1.1.1
1196
+ * @version 1.1.2
1221
1197
  * @author Yusuke Kamiyamane
1222
1198
  * @license MIT
1223
1199
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -1230,7 +1206,7 @@ function isFocusable2(element) {
1230
1206
  * High-precision focus management utility with full composed tree support.
1231
1207
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
1232
1208
  *
1233
- * @version 4.3.2
1209
+ * @version 4.3.3
1234
1210
  * @author Yusuke Kamiyamane
1235
1211
  * @license MIT
1236
1212
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.d.cts CHANGED
@@ -2,7 +2,7 @@
2
2
  * Tabs
3
3
  * WAI-ARIA compliant tabs pattern implementation in TypeScript.
4
4
  *
5
- * @version 1.5.8
5
+ * @version 2.0.0
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
  * Tabs
3
3
  * WAI-ARIA compliant tabs pattern implementation in TypeScript.
4
4
  *
5
- * @version 1.5.8
5
+ * @version 2.0.0
6
6
  * @author Yusuke Kamiyamane
7
7
  * @license MIT
8
8
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  // node_modules/@y14e/attributes-utils/dist/index.js
2
- var defaultParser = (value) => value.split(/\s+/);
3
- var defaultSerializer = (tokens) => tokens.join(" ");
2
+ var DEFAULT_PARSER = (value) => value.split(/\s+/);
3
+ var DEFAULT_SERIALIZER = (tokens) => tokens.join(" ");
4
4
  function addTokenToAttribute(element, attribute, token, options = {}) {
5
5
  const {
6
6
  caseInsensitive = false,
7
- parse = defaultParser,
8
- serialize = defaultSerializer
7
+ parse = DEFAULT_PARSER,
8
+ serialize = DEFAULT_SERIALIZER
9
9
  } = options;
10
10
  const value = element.getAttribute(attribute)?.trim();
11
11
  const tokens = value ? parse(value).filter(Boolean) : [];
@@ -15,11 +15,11 @@ function addTokenToAttribute(element, attribute, token, options = {}) {
15
15
  tokens.push(token);
16
16
  element.setAttribute(attribute, serialize(tokens));
17
17
  }
18
- return;
18
+ } else {
19
+ const set = new Set(tokens);
20
+ set.add(token);
21
+ element.setAttribute(attribute, serialize([...set]));
19
22
  }
20
- const set = new Set(tokens);
21
- set.add(token);
22
- element.setAttribute(attribute, serialize([...set]));
23
23
  }
24
24
  var snapshots = /* @__PURE__ */ new WeakMap();
25
25
  function restoreAttributes(elements) {
@@ -104,13 +104,13 @@ function getActiveElement() {
104
104
  }
105
105
 
106
106
  // node_modules/@y14e/roving-tabindex/dist/index.js
107
- var defaultParser2 = (value) => value.split(/\s+/);
108
- var defaultSerializer2 = (tokens) => tokens.join(" ");
107
+ var DEFAULT_PARSER2 = (value) => value.split(/\s+/);
108
+ var DEFAULT_SERIALIZER2 = (tokens) => tokens.join(" ");
109
109
  function addTokenToAttribute2(element, attribute, token, options = {}) {
110
110
  const {
111
111
  caseInsensitive = false,
112
- parse = defaultParser2,
113
- serialize = defaultSerializer2
112
+ parse = DEFAULT_PARSER2,
113
+ serialize = DEFAULT_SERIALIZER2
114
114
  } = options;
115
115
  const value = element.getAttribute(attribute)?.trim();
116
116
  const tokens = value ? parse(value).filter(Boolean) : [];
@@ -120,11 +120,11 @@ function addTokenToAttribute2(element, attribute, token, options = {}) {
120
120
  tokens.push(token);
121
121
  element.setAttribute(attribute, serialize(tokens));
122
122
  }
123
- return;
123
+ } else {
124
+ const set = new Set(tokens);
125
+ set.add(token);
126
+ element.setAttribute(attribute, serialize([...set]));
124
127
  }
125
- const set = new Set(tokens);
126
- set.add(token);
127
- element.setAttribute(attribute, serialize([...set]));
128
128
  }
129
129
  var snapshots2 = /* @__PURE__ */ new WeakMap();
130
130
  function restoreAttributes2(elements) {
@@ -191,21 +191,16 @@ function getFocusables(container = document.body, options = {}) {
191
191
  const elements = [];
192
192
  if (composed || include) {
193
193
  let traverse2 = function(node) {
194
- if (node instanceof Element) {
195
- if (isFocusable(node, {
196
- skipNegativeTabIndexCheck,
197
- skipVisibilityCheck
198
- }) || include?.(node)) {
199
- elements[elements.length] = node;
200
- }
194
+ if (!(node instanceof Element)) {
195
+ return;
196
+ }
197
+ if (isFocusable(node, { skipNegativeTabIndexCheck, skipVisibilityCheck }) || include?.(node)) {
198
+ elements[elements.length] = node;
201
199
  }
202
200
  const children = getComposedChildren(node);
203
201
  for (let i = 0, l = children.length; i < l; i++) {
204
202
  const child = children[i];
205
- if (!child) {
206
- continue;
207
- }
208
- traverse2(child);
203
+ child && traverse2(child);
209
204
  }
210
205
  };
211
206
  traverse2(container);
@@ -213,10 +208,7 @@ function getFocusables(container = document.body, options = {}) {
213
208
  const candidates = container.querySelectorAll(FOCUSABLE_SELECTOR);
214
209
  for (let i = 0, l = candidates.length; i < l; i++) {
215
210
  const candidate = candidates[i];
216
- if (!(candidate instanceof Element)) {
217
- continue;
218
- }
219
- if (isFocusable(candidate, {
211
+ if (candidate && isFocusable(candidate, {
220
212
  skipNegativeTabIndexCheck,
221
213
  skipVisibilityCheck
222
214
  })) {
@@ -319,23 +311,19 @@ function normalizeRadioGroup(elements) {
319
311
  for (const group of map.values()) {
320
312
  placeholder.add(group.find((radio) => radio.checked) ?? group[0]);
321
313
  }
322
- return elements.filter((element) => {
323
- if (isUngroupedRadio(element)) {
324
- return placeholder.has(element);
325
- }
326
- return true;
327
- });
314
+ return elements.filter(
315
+ (element) => isUngroupedRadio(element) ? placeholder.has(element) : true
316
+ );
328
317
  }
329
318
  function sortByTabIndex(elements) {
330
319
  const ordered = [];
331
320
  const natural = [];
332
321
  for (let i = 0, l = elements.length; i < l; i++) {
333
322
  const element = elements[i];
334
- if (!element) {
335
- continue;
323
+ if (element) {
324
+ const target = getTabIndex(element) > 0 ? ordered : natural;
325
+ target[target.length] = element;
336
326
  }
337
- const target = getTabIndex(element) > 0 ? ordered : natural;
338
- target[target.length] = element;
339
327
  }
340
328
  ordered.sort((a, b) => getTabIndex(a) - getTabIndex(b));
341
329
  let count = 0;
@@ -492,11 +480,7 @@ var RovingTabIndex = class {
492
480
  return;
493
481
  }
494
482
  const isFocusable22 = this.#focusables.has(target);
495
- if (this.#options.noMemory && !isFocusable22) {
496
- this.#update(null);
497
- return;
498
- }
499
- isFocusable22 && this.#update(target);
483
+ this.#options.noMemory && !isFocusable22 ? this.#update(null) : isFocusable22 && this.#update(target);
500
484
  };
501
485
  #onKeyDown = (event) => {
502
486
  if (!event.composedPath().includes(this.#container)) {
@@ -529,8 +513,7 @@ var RovingTabIndex = class {
529
513
  }
530
514
  event.preventDefault();
531
515
  const currentIndex = current.indexOf(active);
532
- let rawIndex;
533
- let newIndex = currentIndex;
516
+ let newIndex;
534
517
  let target = current;
535
518
  switch (key) {
536
519
  case "End":
@@ -540,15 +523,17 @@ var RovingTabIndex = class {
540
523
  newIndex = 0;
541
524
  break;
542
525
  case "ArrowLeft":
543
- case "ArrowUp":
544
- rawIndex = currentIndex - 1;
526
+ case "ArrowUp": {
527
+ const rawIndex = currentIndex - 1;
545
528
  newIndex = wrap ? rawIndex : Math.max(rawIndex, 0);
546
529
  break;
530
+ }
547
531
  case "ArrowRight":
548
- case "ArrowDown":
549
- rawIndex = currentIndex + 1;
532
+ case "ArrowDown": {
533
+ const rawIndex = currentIndex + 1;
550
534
  newIndex = wrap ? rawIndex % current.length : Math.min(rawIndex, current.length - 1);
551
535
  break;
536
+ }
552
537
  default: {
553
538
  target = this.#focusablesByFirstChar.get(key.toUpperCase()) ?? [];
554
539
  const foundIndex = target.findIndex(
@@ -563,15 +548,14 @@ var RovingTabIndex = class {
563
548
  #update(active) {
564
549
  const current = new Set(this.#getFocusables());
565
550
  for (const focusable of this.#focusables) {
566
- if (current.has(focusable)) {
567
- continue;
551
+ if (!current.has(focusable)) {
552
+ focusable.isConnected && restoreAttributes2([focusable]);
553
+ this.#focusables.delete(focusable);
554
+ this.#focusablesByFirstChar.forEach((focusables) => {
555
+ const index = focusables.indexOf(focusable);
556
+ index >= 0 && focusables.splice(index, 1);
557
+ });
568
558
  }
569
- focusable.isConnected && restoreAttributes2([focusable]);
570
- this.#focusables.delete(focusable);
571
- this.#focusablesByFirstChar.forEach((focusables) => {
572
- const index = focusables.indexOf(focusable);
573
- index >= 0 && focusables.splice(index, 1);
574
- });
575
559
  }
576
560
  const { navigationOnly, noStart, typeahead } = this.#options;
577
561
  for (const focusable of current) {
@@ -586,11 +570,11 @@ var RovingTabIndex = class {
586
570
  if (!typeahead) {
587
571
  continue;
588
572
  }
573
+ const char = focusable.textContent?.trim()?.at(0)?.toUpperCase();
589
574
  const value = focusable.ariaKeyShortcuts?.trim();
590
575
  const keys = new Set(
591
576
  value ? value.split(/\s+/).filter((key) => /^\S$/i.test(key)).map((key) => key.toUpperCase()) : []
592
577
  );
593
- const char = focusable.textContent?.trim()?.at(0)?.toUpperCase();
594
578
  if (char) {
595
579
  keys.add(char);
596
580
  saveAttributes2([focusable], ["aria-keyshortcuts"]);
@@ -604,18 +588,17 @@ var RovingTabIndex = class {
604
588
  this.#focusablesByFirstChar.set(key, focusables);
605
589
  });
606
590
  }
607
- if (navigationOnly) {
608
- return;
609
- }
610
- if (active && this.#focusables.has(active)) {
611
- this.#focusables.forEach((focusable) => {
612
- focusable.setAttribute("tabindex", focusable === active ? "0" : "-1");
613
- });
614
- return;
591
+ if (!navigationOnly) {
592
+ if (active && this.#focusables.has(active)) {
593
+ this.#focusables.forEach((focusable) => {
594
+ focusable.setAttribute("tabindex", focusable === active ? "0" : "-1");
595
+ });
596
+ } else {
597
+ [...this.#focusables].forEach((focusable, i) => {
598
+ focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
599
+ });
600
+ }
615
601
  }
616
- [...this.#focusables].forEach((focusable, i) => {
617
- focusable.setAttribute("tabindex", i || noStart ? "-1" : "0");
618
- });
619
602
  }
620
603
  #createSelectorFilter() {
621
604
  const { selector } = this.#options;
@@ -651,7 +634,7 @@ var Tabs = class _Tabs {
651
634
  crossFade: true,
652
635
  duration: 300,
653
636
  easing: "ease",
654
- fade: false
637
+ fade: true
655
638
  },
656
639
  indicator: {
657
640
  duration: 300,
@@ -789,7 +772,7 @@ var Tabs = class _Tabs {
789
772
  }
790
773
  this.#panelElements.forEach((p) => {
791
774
  const { style: style2 } = p;
792
- if (crossFade || fade) {
775
+ if (fade) {
793
776
  style2.setProperty("content-visibility", "visible");
794
777
  style2.setProperty("display", "block");
795
778
  style2.setProperty("opacity", p.hidden ? "0" : "1");
@@ -803,10 +786,7 @@ var Tabs = class _Tabs {
803
786
  p.removeAttribute("hidden");
804
787
  } else {
805
788
  const tab2 = this.#tabElements[i];
806
- if (!tab2) {
807
- return;
808
- }
809
- p.setAttribute("hidden", isFocusable2(tab2) ? "until-found" : "");
789
+ tab2 && p.setAttribute("hidden", isFocusable2(tab2) ? "until-found" : "");
810
790
  }
811
791
  });
812
792
  this.#animation?.cancel();
@@ -838,11 +818,10 @@ var Tabs = class _Tabs {
838
818
  this.#animation.addEventListener(
839
819
  "finish",
840
820
  () => {
841
- if (this.#animation !== animation) {
842
- return;
821
+ if (this.#animation === animation) {
822
+ this.#onAnimationFinish();
823
+ cleanup();
843
824
  }
844
- this.#onAnimationFinish();
845
- cleanup();
846
825
  },
847
826
  {
848
827
  once: true,
@@ -859,10 +838,10 @@ var Tabs = class _Tabs {
859
838
  const isSelected = p === panel;
860
839
  const animation2 = p.animate(
861
840
  {
862
- opacity: fade ? isSelected ? [opacity, opacity, "1"] : [opacity, "0", "0"] : isSelected ? [opacity, "1"] : [opacity, "0"]
841
+ opacity: crossFade || !fade ? isSelected ? [opacity, "1"] : [opacity, "0"] : isSelected ? [opacity, opacity, "1"] : [opacity, "0", "0"]
863
842
  },
864
843
  {
865
- duration: isMatch || !(crossFade || fade) ? 0 : this.#settings.animation.content.duration,
844
+ duration: isMatch || !fade ? 0 : this.#settings.animation.content.duration,
866
845
  easing: "ease"
867
846
  }
868
847
  );
@@ -1032,10 +1011,7 @@ var Tabs = class _Tabs {
1032
1011
  return;
1033
1012
  }
1034
1013
  const tab = this.#bindings.get(panel)?.tabs[0];
1035
- if (!tab) {
1036
- return;
1037
- }
1038
- this.activate(tab, true);
1014
+ tab && this.activate(tab, true);
1039
1015
  };
1040
1016
  #isAvoidedTab(tab) {
1041
1017
  const binding = this.#bindings.get(tab);
@@ -1167,7 +1143,7 @@ function isFocusable2(element) {
1167
1143
  * Tabs
1168
1144
  * WAI-ARIA compliant tabs pattern implementation in TypeScript.
1169
1145
  *
1170
- * @version 1.5.8
1146
+ * @version 2.0.0
1171
1147
  * @author Yusuke Kamiyamane
1172
1148
  * @license MIT
1173
1149
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -1179,7 +1155,7 @@ function isFocusable2(element) {
1179
1155
  (**
1180
1156
  * Attributes Utils
1181
1157
  *
1182
- * @version 1.1.1
1158
+ * @version 1.1.2
1183
1159
  * @author Yusuke Kamiyamane
1184
1160
  * @license MIT
1185
1161
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -1190,7 +1166,7 @@ function isFocusable2(element) {
1190
1166
  (**
1191
1167
  * Button
1192
1168
  *
1193
- * @version 1.0.0
1169
+ * @version 1.0.2
1194
1170
  * @author Yusuke Kamiyamane
1195
1171
  * @license MIT
1196
1172
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -1203,7 +1179,7 @@ function isFocusable2(element) {
1203
1179
  * Lightweight roving tabindex utility with fully focus management.
1204
1180
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
1205
1181
  *
1206
- * @version 3.0.0
1182
+ * @version 3.0.5
1207
1183
  * @author Yusuke Kamiyamane
1208
1184
  * @license MIT
1209
1185
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -1215,7 +1191,7 @@ function isFocusable2(element) {
1215
1191
  (**
1216
1192
  * Attributes Utils
1217
1193
  *
1218
- * @version 1.1.1
1194
+ * @version 1.1.2
1219
1195
  * @author Yusuke Kamiyamane
1220
1196
  * @license MIT
1221
1197
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -1228,7 +1204,7 @@ function isFocusable2(element) {
1228
1204
  * High-precision focus management utility with full composed tree support.
1229
1205
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
1230
1206
  *
1231
- * @version 4.3.2
1207
+ * @version 4.3.3
1232
1208
  * @author Yusuke Kamiyamane
1233
1209
  * @license MIT
1234
1210
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@y14e/tabs",
3
- "version": "1.5.8",
3
+ "version": "2.0.0",
4
4
  "description": "WAI-ARIA compliant tabs pattern implementation in TypeScript",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -42,9 +42,9 @@
42
42
  },
43
43
  "homepage": "https://github.com/y14e/tabs#readme",
44
44
  "devDependencies": {
45
- "@y14e/attributes-utils": "^1.1.1",
46
- "@y14e/button": "^1.0.0",
47
- "@y14e/roving-tabindex": "^3.0.0",
45
+ "@y14e/attributes-utils": "^1.1.2",
46
+ "@y14e/button": "^1.0.2",
47
+ "@y14e/roving-tabindex": "^3.0.5",
48
48
  "bun-types": "latest",
49
49
  "tsup": "^8.0.0",
50
50
  "typescript": "^5.6.0",