@y14e/tabs 1.5.1 → 1.5.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
@@ -10,14 +10,14 @@ npm i @y14e/tabs
10
10
 
11
11
  ```ts
12
12
  // npm
13
- import Tabs from '@y14e/tabs';
13
+ import Tabs from '@y14e/tabs@1.5.3';
14
14
 
15
15
  // CDNs
16
- import Tabs from 'https://esm.sh/@y14e/tabs'
16
+ import Tabs from 'https://esm.sh/@y14e/tabs@1.5.3';
17
17
  // or
18
- import Tabs from 'https://cdn.jsdelivr.net/npm/@y14e/tabs/+esm';
18
+ import Tabs from 'https://cdn.jsdelivr.net/npm/@y14e/tabs@1.5.3/+esm';
19
19
  // or
20
- import Tabs from 'https://unpkg.com/@y14e/tabs/dist/index.js';
20
+ import Tabs from 'https://esm.unpkg.com/@y14e/tabs@1.5.3';
21
21
  ```
22
22
 
23
23
  ## Usage
package/dist/index.cjs CHANGED
@@ -165,6 +165,7 @@ function getFocusables(container = document.body, options = {}) {
165
165
  composed = false,
166
166
  filter,
167
167
  include,
168
+ skipNegativeTabIndexCheck = false,
168
169
  skipVisibilityCheck = false
169
170
  } = options;
170
171
  if (typeof composed !== "boolean") {
@@ -183,6 +184,10 @@ function getFocusables(container = document.body, options = {}) {
183
184
  );
184
185
  include = void 0;
185
186
  }
187
+ if (typeof skipNegativeTabIndexCheck !== "boolean") {
188
+ console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
189
+ skipNegativeTabIndexCheck = false;
190
+ }
186
191
  if (typeof skipVisibilityCheck !== "boolean") {
187
192
  console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
188
193
  skipVisibilityCheck = false;
@@ -191,7 +196,10 @@ function getFocusables(container = document.body, options = {}) {
191
196
  if (composed || include) {
192
197
  let traverse2 = function(node) {
193
198
  if (node instanceof Element) {
194
- if (isFocusable(node, { skipVisibilityCheck }) || include?.(node)) {
199
+ if (isFocusable(node, {
200
+ skipNegativeTabIndexCheck,
201
+ skipVisibilityCheck
202
+ }) || include?.(node)) {
195
203
  elements[elements.length] = node;
196
204
  }
197
205
  }
@@ -212,7 +220,10 @@ function getFocusables(container = document.body, options = {}) {
212
220
  if (!(candidate instanceof Element)) {
213
221
  continue;
214
222
  }
215
- if (isFocusable(candidate, { skipVisibilityCheck })) {
223
+ if (isFocusable(candidate, {
224
+ skipNegativeTabIndexCheck,
225
+ skipVisibilityCheck
226
+ })) {
216
227
  elements[elements.length] = candidate;
217
228
  }
218
229
  }
@@ -225,7 +236,11 @@ function isFocusable(element, options = {}) {
225
236
  console.warn("Invalid element");
226
237
  return false;
227
238
  }
228
- let { skipVisibilityCheck = false } = options;
239
+ let { skipNegativeTabIndexCheck = false, skipVisibilityCheck = false } = options;
240
+ if (typeof skipNegativeTabIndexCheck !== "boolean") {
241
+ console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
242
+ skipNegativeTabIndexCheck = false;
243
+ }
229
244
  if (typeof skipVisibilityCheck !== "boolean") {
230
245
  console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
231
246
  skipVisibilityCheck = false;
@@ -233,10 +248,12 @@ function isFocusable(element, options = {}) {
233
248
  if (element.hasAttribute("hidden") || isInert(element)) {
234
249
  return false;
235
250
  }
236
- if (getTabIndex(element) < 0) {
251
+ if (!skipNegativeTabIndexCheck && getTabIndex(element) < 0) {
237
252
  return false;
238
253
  }
239
- if (!element.matches(FOCUSABLE_SELECTOR)) {
254
+ if (!element.matches(
255
+ skipNegativeTabIndexCheck ? FOCUSABLE_SELECTOR.replace(/(,\s*)?\[tabindex="-1"\]/g, "") : FOCUSABLE_SELECTOR
256
+ )) {
240
257
  return false;
241
258
  }
242
259
  if (isDisabledDeep(element)) {
@@ -386,7 +403,6 @@ function createRovingTabIndex(container, options = {}) {
386
403
  direction,
387
404
  navigationOnly = false,
388
405
  selector,
389
- skipVisibilityCheck = false,
390
406
  typeahead = false,
391
407
  wrap = false
392
408
  } = options;
@@ -404,10 +420,6 @@ function createRovingTabIndex(container, options = {}) {
404
420
  );
405
421
  selector = void 0;
406
422
  }
407
- if (typeof skipVisibilityCheck !== "boolean") {
408
- console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
409
- skipVisibilityCheck = false;
410
- }
411
423
  if (typeof typeahead !== "boolean") {
412
424
  console.warn("Invalid typeahead option. Fallback: false.");
413
425
  typeahead = false;
@@ -416,14 +428,14 @@ function createRovingTabIndex(container, options = {}) {
416
428
  console.warn("Invalid wrap option. Fallback: false.");
417
429
  wrap = false;
418
430
  }
419
- const roving = new RovingTabIndex(container, {
420
- direction,
421
- navigationOnly,
422
- selector,
423
- skipVisibilityCheck,
424
- typeahead,
425
- wrap
426
- });
431
+ const settings = { navigationOnly, typeahead, wrap };
432
+ if (direction !== void 0) {
433
+ Object.assign(settings, { direction });
434
+ }
435
+ if (selector !== void 0) {
436
+ Object.assign(settings, { selector });
437
+ }
438
+ const roving = new RovingTabIndex(container, settings);
427
439
  return () => roving.destroy();
428
440
  }
429
441
  var RovingTabIndex = class {
@@ -465,8 +477,8 @@ var RovingTabIndex = class {
465
477
  if (!event.composedPath().includes(this.#container)) {
466
478
  return;
467
479
  }
468
- const { key, altKey, ctrlKey, metaKey } = event;
469
- if (altKey || ctrlKey || metaKey) {
480
+ const { key, altKey, ctrlKey, metaKey, shiftKey } = event;
481
+ if (altKey || ctrlKey || metaKey || shiftKey) {
470
482
  return;
471
483
  }
472
484
  const { direction, typeahead, wrap } = this.#options;
@@ -529,14 +541,7 @@ var RovingTabIndex = class {
529
541
  focusElement(focusable);
530
542
  };
531
543
  #update(active) {
532
- const current = /* @__PURE__ */ new Set([
533
- ...this.#getFocusables(),
534
- ...getFocusables(this.#container, {
535
- composed: true,
536
- filter: this.#selectorFilter,
537
- skipVisibilityCheck: !!this.#options.skipVisibilityCheck
538
- })
539
- ]);
544
+ const current = new Set(this.#getFocusables());
540
545
  for (const focusable of this.#focusables) {
541
546
  if (current.has(focusable)) {
542
547
  continue;
@@ -600,8 +605,7 @@ var RovingTabIndex = class {
600
605
  return getFocusables(this.#container, {
601
606
  composed: true,
602
607
  filter: this.#selectorFilter,
603
- include: (element) => this.#focusables.has(element),
604
- skipVisibilityCheck: !!this.#options.skipVisibilityCheck
608
+ skipNegativeTabIndexCheck: !this.#options.navigationOnly
605
609
  });
606
610
  }
607
611
  };
@@ -964,15 +968,14 @@ var Tabs = class _Tabs {
964
968
  signal
965
969
  });
966
970
  });
971
+ const options = { selector: this.#settings.selector.tab, wrap: true };
967
972
  this.#listElements.forEach((list) => {
968
- this.#cleanupsRovingTabIndex.push(
969
- createRovingTabIndex(list, {
970
- direction: list.ariaOrientation === "undefined" ? void 0 : this.#settings.vertical ? "vertical" : "horizontal",
971
- selector: this.#settings.selector.tab,
972
- skipVisibilityCheck: true,
973
- wrap: true
974
- })
975
- );
973
+ if (list.ariaOrientation !== "undefined") {
974
+ Object.assign(options, {
975
+ direction: this.#settings.vertical ? "vertical" : "horizontal"
976
+ });
977
+ }
978
+ this.#cleanupsRovingTabIndex.push(createRovingTabIndex(list, options));
976
979
  list.querySelectorAll(this.#settings.selector.tab).forEach((tab) => {
977
980
  tab.setAttribute(
978
981
  "tabindex",
@@ -1140,7 +1143,7 @@ function isFocusable2(element) {
1140
1143
  * Tabs
1141
1144
  * WAI-ARIA compliant tabs pattern implementation in TypeScript.
1142
1145
  *
1143
- * @version 1.5.1
1146
+ * @version 1.5.3
1144
1147
  * @author Yusuke Kamiyamane
1145
1148
  * @license MIT
1146
1149
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -1152,7 +1155,7 @@ function isFocusable2(element) {
1152
1155
  (**
1153
1156
  * Attributes Utils
1154
1157
  *
1155
- * @version 1.0.5
1158
+ * @version 1.1.0
1156
1159
  * @author Yusuke Kamiyamane
1157
1160
  * @license MIT
1158
1161
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -1176,7 +1179,7 @@ function isFocusable2(element) {
1176
1179
  * Lightweight roving tabindex utility with fully focus management.
1177
1180
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
1178
1181
  *
1179
- * @version 1.3.2
1182
+ * @version 2.0.3
1180
1183
  * @author Yusuke Kamiyamane
1181
1184
  * @license MIT
1182
1185
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -1188,7 +1191,7 @@ function isFocusable2(element) {
1188
1191
  (**
1189
1192
  * Attributes Utils
1190
1193
  *
1191
- * @version 1.0.5
1194
+ * @version 1.1.0
1192
1195
  * @author Yusuke Kamiyamane
1193
1196
  * @license MIT
1194
1197
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -1201,7 +1204,7 @@ function isFocusable2(element) {
1201
1204
  * High-precision focus management utility with full composed tree support.
1202
1205
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
1203
1206
  *
1204
- * @version 4.2.1
1207
+ * @version 4.3.1
1205
1208
  * @author Yusuke Kamiyamane
1206
1209
  * @license MIT
1207
1210
  * @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.1
5
+ * @version 1.5.3
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.1
5
+ * @version 1.5.3
6
6
  * @author Yusuke Kamiyamane
7
7
  * @license MIT
8
8
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.js CHANGED
@@ -163,6 +163,7 @@ function getFocusables(container = document.body, options = {}) {
163
163
  composed = false,
164
164
  filter,
165
165
  include,
166
+ skipNegativeTabIndexCheck = false,
166
167
  skipVisibilityCheck = false
167
168
  } = options;
168
169
  if (typeof composed !== "boolean") {
@@ -181,6 +182,10 @@ function getFocusables(container = document.body, options = {}) {
181
182
  );
182
183
  include = void 0;
183
184
  }
185
+ if (typeof skipNegativeTabIndexCheck !== "boolean") {
186
+ console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
187
+ skipNegativeTabIndexCheck = false;
188
+ }
184
189
  if (typeof skipVisibilityCheck !== "boolean") {
185
190
  console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
186
191
  skipVisibilityCheck = false;
@@ -189,7 +194,10 @@ function getFocusables(container = document.body, options = {}) {
189
194
  if (composed || include) {
190
195
  let traverse2 = function(node) {
191
196
  if (node instanceof Element) {
192
- if (isFocusable(node, { skipVisibilityCheck }) || include?.(node)) {
197
+ if (isFocusable(node, {
198
+ skipNegativeTabIndexCheck,
199
+ skipVisibilityCheck
200
+ }) || include?.(node)) {
193
201
  elements[elements.length] = node;
194
202
  }
195
203
  }
@@ -210,7 +218,10 @@ function getFocusables(container = document.body, options = {}) {
210
218
  if (!(candidate instanceof Element)) {
211
219
  continue;
212
220
  }
213
- if (isFocusable(candidate, { skipVisibilityCheck })) {
221
+ if (isFocusable(candidate, {
222
+ skipNegativeTabIndexCheck,
223
+ skipVisibilityCheck
224
+ })) {
214
225
  elements[elements.length] = candidate;
215
226
  }
216
227
  }
@@ -223,7 +234,11 @@ function isFocusable(element, options = {}) {
223
234
  console.warn("Invalid element");
224
235
  return false;
225
236
  }
226
- let { skipVisibilityCheck = false } = options;
237
+ let { skipNegativeTabIndexCheck = false, skipVisibilityCheck = false } = options;
238
+ if (typeof skipNegativeTabIndexCheck !== "boolean") {
239
+ console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
240
+ skipNegativeTabIndexCheck = false;
241
+ }
227
242
  if (typeof skipVisibilityCheck !== "boolean") {
228
243
  console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
229
244
  skipVisibilityCheck = false;
@@ -231,10 +246,12 @@ function isFocusable(element, options = {}) {
231
246
  if (element.hasAttribute("hidden") || isInert(element)) {
232
247
  return false;
233
248
  }
234
- if (getTabIndex(element) < 0) {
249
+ if (!skipNegativeTabIndexCheck && getTabIndex(element) < 0) {
235
250
  return false;
236
251
  }
237
- if (!element.matches(FOCUSABLE_SELECTOR)) {
252
+ if (!element.matches(
253
+ skipNegativeTabIndexCheck ? FOCUSABLE_SELECTOR.replace(/(,\s*)?\[tabindex="-1"\]/g, "") : FOCUSABLE_SELECTOR
254
+ )) {
238
255
  return false;
239
256
  }
240
257
  if (isDisabledDeep(element)) {
@@ -384,7 +401,6 @@ function createRovingTabIndex(container, options = {}) {
384
401
  direction,
385
402
  navigationOnly = false,
386
403
  selector,
387
- skipVisibilityCheck = false,
388
404
  typeahead = false,
389
405
  wrap = false
390
406
  } = options;
@@ -402,10 +418,6 @@ function createRovingTabIndex(container, options = {}) {
402
418
  );
403
419
  selector = void 0;
404
420
  }
405
- if (typeof skipVisibilityCheck !== "boolean") {
406
- console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
407
- skipVisibilityCheck = false;
408
- }
409
421
  if (typeof typeahead !== "boolean") {
410
422
  console.warn("Invalid typeahead option. Fallback: false.");
411
423
  typeahead = false;
@@ -414,14 +426,14 @@ function createRovingTabIndex(container, options = {}) {
414
426
  console.warn("Invalid wrap option. Fallback: false.");
415
427
  wrap = false;
416
428
  }
417
- const roving = new RovingTabIndex(container, {
418
- direction,
419
- navigationOnly,
420
- selector,
421
- skipVisibilityCheck,
422
- typeahead,
423
- wrap
424
- });
429
+ const settings = { navigationOnly, typeahead, wrap };
430
+ if (direction !== void 0) {
431
+ Object.assign(settings, { direction });
432
+ }
433
+ if (selector !== void 0) {
434
+ Object.assign(settings, { selector });
435
+ }
436
+ const roving = new RovingTabIndex(container, settings);
425
437
  return () => roving.destroy();
426
438
  }
427
439
  var RovingTabIndex = class {
@@ -463,8 +475,8 @@ var RovingTabIndex = class {
463
475
  if (!event.composedPath().includes(this.#container)) {
464
476
  return;
465
477
  }
466
- const { key, altKey, ctrlKey, metaKey } = event;
467
- if (altKey || ctrlKey || metaKey) {
478
+ const { key, altKey, ctrlKey, metaKey, shiftKey } = event;
479
+ if (altKey || ctrlKey || metaKey || shiftKey) {
468
480
  return;
469
481
  }
470
482
  const { direction, typeahead, wrap } = this.#options;
@@ -527,14 +539,7 @@ var RovingTabIndex = class {
527
539
  focusElement(focusable);
528
540
  };
529
541
  #update(active) {
530
- const current = /* @__PURE__ */ new Set([
531
- ...this.#getFocusables(),
532
- ...getFocusables(this.#container, {
533
- composed: true,
534
- filter: this.#selectorFilter,
535
- skipVisibilityCheck: !!this.#options.skipVisibilityCheck
536
- })
537
- ]);
542
+ const current = new Set(this.#getFocusables());
538
543
  for (const focusable of this.#focusables) {
539
544
  if (current.has(focusable)) {
540
545
  continue;
@@ -598,8 +603,7 @@ var RovingTabIndex = class {
598
603
  return getFocusables(this.#container, {
599
604
  composed: true,
600
605
  filter: this.#selectorFilter,
601
- include: (element) => this.#focusables.has(element),
602
- skipVisibilityCheck: !!this.#options.skipVisibilityCheck
606
+ skipNegativeTabIndexCheck: !this.#options.navigationOnly
603
607
  });
604
608
  }
605
609
  };
@@ -962,15 +966,14 @@ var Tabs = class _Tabs {
962
966
  signal
963
967
  });
964
968
  });
969
+ const options = { selector: this.#settings.selector.tab, wrap: true };
965
970
  this.#listElements.forEach((list) => {
966
- this.#cleanupsRovingTabIndex.push(
967
- createRovingTabIndex(list, {
968
- direction: list.ariaOrientation === "undefined" ? void 0 : this.#settings.vertical ? "vertical" : "horizontal",
969
- selector: this.#settings.selector.tab,
970
- skipVisibilityCheck: true,
971
- wrap: true
972
- })
973
- );
971
+ if (list.ariaOrientation !== "undefined") {
972
+ Object.assign(options, {
973
+ direction: this.#settings.vertical ? "vertical" : "horizontal"
974
+ });
975
+ }
976
+ this.#cleanupsRovingTabIndex.push(createRovingTabIndex(list, options));
974
977
  list.querySelectorAll(this.#settings.selector.tab).forEach((tab) => {
975
978
  tab.setAttribute(
976
979
  "tabindex",
@@ -1138,7 +1141,7 @@ function isFocusable2(element) {
1138
1141
  * Tabs
1139
1142
  * WAI-ARIA compliant tabs pattern implementation in TypeScript.
1140
1143
  *
1141
- * @version 1.5.1
1144
+ * @version 1.5.3
1142
1145
  * @author Yusuke Kamiyamane
1143
1146
  * @license MIT
1144
1147
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -1150,7 +1153,7 @@ function isFocusable2(element) {
1150
1153
  (**
1151
1154
  * Attributes Utils
1152
1155
  *
1153
- * @version 1.0.5
1156
+ * @version 1.1.0
1154
1157
  * @author Yusuke Kamiyamane
1155
1158
  * @license MIT
1156
1159
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -1174,7 +1177,7 @@ function isFocusable2(element) {
1174
1177
  * Lightweight roving tabindex utility with fully focus management.
1175
1178
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
1176
1179
  *
1177
- * @version 1.3.2
1180
+ * @version 2.0.3
1178
1181
  * @author Yusuke Kamiyamane
1179
1182
  * @license MIT
1180
1183
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -1186,7 +1189,7 @@ function isFocusable2(element) {
1186
1189
  (**
1187
1190
  * Attributes Utils
1188
1191
  *
1189
- * @version 1.0.5
1192
+ * @version 1.1.0
1190
1193
  * @author Yusuke Kamiyamane
1191
1194
  * @license MIT
1192
1195
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -1199,7 +1202,7 @@ function isFocusable2(element) {
1199
1202
  * High-precision focus management utility with full composed tree support.
1200
1203
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
1201
1204
  *
1202
- * @version 4.2.1
1205
+ * @version 4.3.1
1203
1206
  * @author Yusuke Kamiyamane
1204
1207
  * @license MIT
1205
1208
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@y14e/tabs",
3
- "version": "1.5.1",
3
+ "version": "1.5.3",
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.0.5",
45
+ "@y14e/attributes-utils": "^1.1.0",
46
46
  "@y14e/button": "^1.0.0",
47
- "@y14e/roving-tabindex": "^1.3.2",
47
+ "@y14e/roving-tabindex": "^2.0.3",
48
48
  "bun-types": "latest",
49
49
  "tsup": "^8.0.0",
50
50
  "typescript": "^5.6.0",