@primer/behaviors 0.0.0-20251215025613 → 0.0.0-20251215031129

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.
@@ -5,6 +5,7 @@ var userAgent = require('./utils/user-agent.js');
5
5
  var iterateFocusableElements = require('./utils/iterate-focusable-elements.js');
6
6
  var uniqueId = require('./utils/unique-id.js');
7
7
  var isEditableElement = require('./utils/is-editable-element.js');
8
+ var indexedSet = require('./utils/indexed-set.js');
8
9
 
9
10
  eventListenerSignal.polyfill();
10
11
  exports.FocusKeys = void 0;
@@ -89,17 +90,18 @@ function getDirection(keyboardEvent) {
89
90
  }
90
91
  function shouldIgnoreFocusHandling(keyboardEvent, activeElement) {
91
92
  const key = keyboardEvent.key;
92
- const keyLength = [...key].length;
93
+ const isSingleChar = key.length === 1 || (key.length === 2 && key.charCodeAt(0) >= 0xd800 && key.charCodeAt(0) <= 0xdbff);
93
94
  const isEditable = isEditableElement.isEditableElement(activeElement);
94
95
  const isSelect = activeElement instanceof HTMLSelectElement;
95
- if (isEditable && (keyLength === 1 || key === 'Home' || key === 'End')) {
96
+ if (isEditable && (isSingleChar || key === 'Home' || key === 'End')) {
96
97
  return true;
97
98
  }
98
99
  if (isSelect) {
99
- if (key === 'ArrowDown' && userAgent.isMacOS() && !keyboardEvent.metaKey) {
100
+ const isMac = getIsMac();
101
+ if (key === 'ArrowDown' && isMac && !keyboardEvent.metaKey) {
100
102
  return true;
101
103
  }
102
- if (key === 'ArrowDown' && !userAgent.isMacOS() && keyboardEvent.altKey) {
104
+ if (key === 'ArrowDown' && !isMac && keyboardEvent.altKey) {
103
105
  return true;
104
106
  }
105
107
  return false;
@@ -137,7 +139,7 @@ const activeDescendantActivatedIndirectly = 'activated-indirectly';
137
139
  const hasActiveDescendantAttribute = 'data-has-active-descendant';
138
140
  function focusZone(container, settings) {
139
141
  var _a, _b, _c, _d, _e, _f;
140
- const focusableElements = [];
142
+ const focusableElements = new indexedSet.IndexedSet();
141
143
  const savedTabIndex = new WeakMap();
142
144
  const bindKeys = (_a = settings === null || settings === void 0 ? void 0 : settings.bindKeys) !== null && _a !== void 0 ? _a : ((settings === null || settings === void 0 ? void 0 : settings.getNextFocusable) ? exports.FocusKeys.ArrowAll : exports.FocusKeys.ArrowVertical) | exports.FocusKeys.HomeAndEnd;
143
145
  const focusOutBehavior = (_b = settings === null || settings === void 0 ? void 0 : settings.focusOutBehavior) !== null && _b !== void 0 ? _b : 'stop';
@@ -149,7 +151,7 @@ function focusZone(container, settings) {
149
151
  const preventScroll = (_e = settings === null || settings === void 0 ? void 0 : settings.preventScroll) !== null && _e !== void 0 ? _e : false;
150
152
  const preventInitialFocus = focusInStrategy === 'initial' && (settings === null || settings === void 0 ? void 0 : settings.activeDescendantControl);
151
153
  function getFirstFocusableElement() {
152
- return focusableElements[0];
154
+ return focusableElements.get(0);
153
155
  }
154
156
  function isActiveDescendantInputFocused() {
155
157
  return document.activeElement === activeDescendantControl;
@@ -205,7 +207,7 @@ function focusZone(container, settings) {
205
207
  if (filteredElements.length === 0) {
206
208
  return;
207
209
  }
208
- focusableElements.splice(findInsertionIndex(filteredElements), 0, ...filteredElements);
210
+ focusableElements.insertAt(findInsertionIndex(filteredElements), ...filteredElements);
209
211
  for (const element of filteredElements) {
210
212
  if (!savedTabIndex.has(element)) {
211
213
  savedTabIndex.set(element, element.getAttribute('tabindex'));
@@ -218,13 +220,13 @@ function focusZone(container, settings) {
218
220
  }
219
221
  function findInsertionIndex(elementsToInsert) {
220
222
  const firstElementToInsert = elementsToInsert[0];
221
- if (focusableElements.length === 0)
223
+ if (focusableElements.size === 0)
222
224
  return 0;
223
225
  let iMin = 0;
224
- let iMax = focusableElements.length - 1;
226
+ let iMax = focusableElements.size - 1;
225
227
  while (iMin <= iMax) {
226
228
  const i = Math.floor((iMin + iMax) / 2);
227
- const element = focusableElements[i];
229
+ const element = focusableElements.get(i);
228
230
  if (followsInDocument(firstElementToInsert, element)) {
229
231
  iMax = i - 1;
230
232
  }
@@ -239,10 +241,7 @@ function focusZone(container, settings) {
239
241
  }
240
242
  function endFocusManagement(...elements) {
241
243
  for (const element of elements) {
242
- const focusableElementIndex = focusableElements.indexOf(element);
243
- if (focusableElementIndex >= 0) {
244
- focusableElements.splice(focusableElementIndex, 1);
245
- }
244
+ focusableElements.delete(element);
246
245
  const savedIndex = savedTabIndex.get(element);
247
246
  if (savedIndex !== undefined) {
248
247
  if (savedIndex === null) {
@@ -298,7 +297,12 @@ function focusZone(container, settings) {
298
297
  }
299
298
  }
300
299
  if (elementsToRemove.size > 0) {
301
- const toRemove = [...elementsToRemove].flatMap(node => [...iterateFocusableElements.iterateFocusableElements(node)]);
300
+ const toRemove = [];
301
+ for (const node of elementsToRemove) {
302
+ for (const el of iterateFocusableElements.iterateFocusableElements(node)) {
303
+ toRemove.push(el);
304
+ }
305
+ }
302
306
  if (toRemove.length > 0) {
303
307
  endFocusManagement(...toRemove);
304
308
  }
@@ -307,9 +311,12 @@ function focusZone(container, settings) {
307
311
  endFocusManagement(...attributeRemovals);
308
312
  }
309
313
  if (elementsToAdd.size > 0) {
310
- const toAdd = [...elementsToAdd].flatMap(node => [
311
- ...iterateFocusableElements.iterateFocusableElements(node, iterateFocusableElementsOptions),
312
- ]);
314
+ const toAdd = [];
315
+ for (const node of elementsToAdd) {
316
+ for (const el of iterateFocusableElements.iterateFocusableElements(node, iterateFocusableElementsOptions)) {
317
+ toAdd.push(el);
318
+ }
319
+ }
313
320
  if (toAdd.length > 0) {
314
321
  beginFocusManagement(...toAdd);
315
322
  }
@@ -338,7 +345,7 @@ function focusZone(container, settings) {
338
345
  }, { signal });
339
346
  if (activeDescendantControl) {
340
347
  container.addEventListener('focusin', event => {
341
- if (event.target instanceof HTMLElement && focusableElements.includes(event.target)) {
348
+ if (event.target instanceof HTMLElement && focusableElements.has(event.target)) {
342
349
  activeDescendantControl.focus({ preventScroll });
343
350
  updateFocusedElement(event.target);
344
351
  }
@@ -348,9 +355,8 @@ function focusZone(container, settings) {
348
355
  if (!(target instanceof Node)) {
349
356
  return;
350
357
  }
351
- const targetIndex = target instanceof HTMLElement ? focusableElements.indexOf(target) : -1;
352
- if (targetIndex >= 0) {
353
- updateFocusedElement(focusableElements[targetIndex]);
358
+ if (target instanceof HTMLElement && focusableElements.has(target)) {
359
+ updateFocusedElement(target);
354
360
  return;
355
361
  }
356
362
  const focusableElement = focusableElements.find(element => element.contains(target));
@@ -377,8 +383,9 @@ function focusZone(container, settings) {
377
383
  if (event.target instanceof HTMLElement) {
378
384
  if (elementIndexFocusedByClick !== undefined) {
379
385
  if (elementIndexFocusedByClick >= 0) {
380
- if (focusableElements[elementIndexFocusedByClick] !== currentFocusedElement) {
381
- updateFocusedElement(focusableElements[elementIndexFocusedByClick]);
386
+ const clickedElement = focusableElements.get(elementIndexFocusedByClick);
387
+ if (clickedElement && clickedElement !== currentFocusedElement) {
388
+ updateFocusedElement(clickedElement);
382
389
  }
383
390
  }
384
391
  elementIndexFocusedByClick = undefined;
@@ -389,8 +396,8 @@ function focusZone(container, settings) {
389
396
  }
390
397
  else if (focusInStrategy === 'closest' || focusInStrategy === 'first') {
391
398
  if (event.relatedTarget instanceof Element && !container.contains(event.relatedTarget)) {
392
- const targetElementIndex = lastKeyboardFocusDirection === 'previous' ? focusableElements.length - 1 : 0;
393
- const targetElement = focusableElements[targetElementIndex];
399
+ const targetElementIndex = lastKeyboardFocusDirection === 'previous' ? focusableElements.size - 1 : 0;
400
+ const targetElement = focusableElements.get(targetElementIndex);
394
401
  targetElement === null || targetElement === void 0 ? void 0 : targetElement.focus({ preventScroll });
395
402
  return;
396
403
  }
@@ -401,8 +408,7 @@ function focusZone(container, settings) {
401
408
  else if (typeof focusInStrategy === 'function') {
402
409
  if (event.relatedTarget instanceof Element && !container.contains(event.relatedTarget)) {
403
410
  const elementToFocus = focusInStrategy(event.relatedTarget);
404
- const requestedFocusElementIndex = elementToFocus ? focusableElements.indexOf(elementToFocus) : -1;
405
- if (requestedFocusElementIndex >= 0 && elementToFocus instanceof HTMLElement) {
411
+ if (elementToFocus && focusableElements.has(elementToFocus)) {
406
412
  elementToFocus.focus({ preventScroll });
407
413
  return;
408
414
  }
@@ -461,26 +467,26 @@ function focusZone(container, settings) {
461
467
  nextFocusedIndex += 1;
462
468
  }
463
469
  else {
464
- nextFocusedIndex = focusableElements.length - 1;
470
+ nextFocusedIndex = focusableElements.size - 1;
465
471
  }
466
472
  if (nextFocusedIndex < 0) {
467
473
  if (focusOutBehavior === 'wrap' && event.key !== 'Tab') {
468
- nextFocusedIndex = focusableElements.length - 1;
474
+ nextFocusedIndex = focusableElements.size - 1;
469
475
  }
470
476
  else {
471
477
  nextFocusedIndex = 0;
472
478
  }
473
479
  }
474
- if (nextFocusedIndex >= focusableElements.length) {
480
+ if (nextFocusedIndex >= focusableElements.size) {
475
481
  if (focusOutBehavior === 'wrap' && event.key !== 'Tab') {
476
482
  nextFocusedIndex = 0;
477
483
  }
478
484
  else {
479
- nextFocusedIndex = focusableElements.length - 1;
485
+ nextFocusedIndex = focusableElements.size - 1;
480
486
  }
481
487
  }
482
488
  if (lastFocusedIndex !== nextFocusedIndex) {
483
- nextElementToFocus = focusableElements[nextFocusedIndex];
489
+ nextElementToFocus = focusableElements.get(nextFocusedIndex);
484
490
  }
485
491
  }
486
492
  if (activeDescendantControl) {
@@ -0,0 +1,12 @@
1
+ export declare class IndexedSet<T> {
2
+ #private;
3
+ insertAt(index: number, ...elements: T[]): void;
4
+ delete(element: T): boolean;
5
+ has(element: T): boolean;
6
+ indexOf(element: T): number;
7
+ get(index: number): T | undefined;
8
+ get size(): number;
9
+ [Symbol.iterator](): Iterator<T>;
10
+ clear(): void;
11
+ find(predicate: (element: T) => boolean): T | undefined;
12
+ }
@@ -0,0 +1,56 @@
1
+ 'use strict';
2
+
3
+ var tslib = require('tslib');
4
+
5
+ var _IndexedSet_items, _IndexedSet_itemSet;
6
+ class IndexedSet {
7
+ constructor() {
8
+ _IndexedSet_items.set(this, []);
9
+ _IndexedSet_itemSet.set(this, new Set());
10
+ }
11
+ insertAt(index, ...elements) {
12
+ const newElements = elements.filter(e => !tslib.__classPrivateFieldGet(this, _IndexedSet_itemSet, "f").has(e));
13
+ if (newElements.length === 0)
14
+ return;
15
+ tslib.__classPrivateFieldGet(this, _IndexedSet_items, "f").splice(index, 0, ...newElements);
16
+ for (const element of newElements) {
17
+ tslib.__classPrivateFieldGet(this, _IndexedSet_itemSet, "f").add(element);
18
+ }
19
+ }
20
+ delete(element) {
21
+ if (!tslib.__classPrivateFieldGet(this, _IndexedSet_itemSet, "f").has(element))
22
+ return false;
23
+ const index = tslib.__classPrivateFieldGet(this, _IndexedSet_items, "f").indexOf(element);
24
+ if (index >= 0) {
25
+ tslib.__classPrivateFieldGet(this, _IndexedSet_items, "f").splice(index, 1);
26
+ }
27
+ tslib.__classPrivateFieldGet(this, _IndexedSet_itemSet, "f").delete(element);
28
+ return true;
29
+ }
30
+ has(element) {
31
+ return tslib.__classPrivateFieldGet(this, _IndexedSet_itemSet, "f").has(element);
32
+ }
33
+ indexOf(element) {
34
+ if (!tslib.__classPrivateFieldGet(this, _IndexedSet_itemSet, "f").has(element))
35
+ return -1;
36
+ return tslib.__classPrivateFieldGet(this, _IndexedSet_items, "f").indexOf(element);
37
+ }
38
+ get(index) {
39
+ return tslib.__classPrivateFieldGet(this, _IndexedSet_items, "f")[index];
40
+ }
41
+ get size() {
42
+ return tslib.__classPrivateFieldGet(this, _IndexedSet_items, "f").length;
43
+ }
44
+ [(_IndexedSet_items = new WeakMap(), _IndexedSet_itemSet = new WeakMap(), Symbol.iterator)]() {
45
+ return tslib.__classPrivateFieldGet(this, _IndexedSet_items, "f")[Symbol.iterator]();
46
+ }
47
+ clear() {
48
+ tslib.__classPrivateFieldSet(this, _IndexedSet_items, [], "f");
49
+ tslib.__classPrivateFieldGet(this, _IndexedSet_itemSet, "f").clear();
50
+ }
51
+ find(predicate) {
52
+ return tslib.__classPrivateFieldGet(this, _IndexedSet_items, "f").find(predicate);
53
+ }
54
+ }
55
+
56
+ exports.IndexedSet = IndexedSet;
@@ -3,6 +3,7 @@ import { isMacOS } from './utils/user-agent.mjs';
3
3
  import { iterateFocusableElements } from './utils/iterate-focusable-elements.mjs';
4
4
  import { uniqueId } from './utils/unique-id.mjs';
5
5
  import { isEditableElement } from './utils/is-editable-element.mjs';
6
+ import { IndexedSet } from './utils/indexed-set.mjs';
6
7
 
7
8
  polyfill();
8
9
  var FocusKeys;
@@ -87,17 +88,18 @@ function getDirection(keyboardEvent) {
87
88
  }
88
89
  function shouldIgnoreFocusHandling(keyboardEvent, activeElement) {
89
90
  const key = keyboardEvent.key;
90
- const keyLength = [...key].length;
91
+ const isSingleChar = key.length === 1 || (key.length === 2 && key.charCodeAt(0) >= 0xd800 && key.charCodeAt(0) <= 0xdbff);
91
92
  const isEditable = isEditableElement(activeElement);
92
93
  const isSelect = activeElement instanceof HTMLSelectElement;
93
- if (isEditable && (keyLength === 1 || key === 'Home' || key === 'End')) {
94
+ if (isEditable && (isSingleChar || key === 'Home' || key === 'End')) {
94
95
  return true;
95
96
  }
96
97
  if (isSelect) {
97
- if (key === 'ArrowDown' && isMacOS() && !keyboardEvent.metaKey) {
98
+ const isMac = getIsMac();
99
+ if (key === 'ArrowDown' && isMac && !keyboardEvent.metaKey) {
98
100
  return true;
99
101
  }
100
- if (key === 'ArrowDown' && !isMacOS() && keyboardEvent.altKey) {
102
+ if (key === 'ArrowDown' && !isMac && keyboardEvent.altKey) {
101
103
  return true;
102
104
  }
103
105
  return false;
@@ -135,7 +137,7 @@ const activeDescendantActivatedIndirectly = 'activated-indirectly';
135
137
  const hasActiveDescendantAttribute = 'data-has-active-descendant';
136
138
  function focusZone(container, settings) {
137
139
  var _a, _b, _c, _d, _e, _f;
138
- const focusableElements = [];
140
+ const focusableElements = new IndexedSet();
139
141
  const savedTabIndex = new WeakMap();
140
142
  const bindKeys = (_a = settings === null || settings === void 0 ? void 0 : settings.bindKeys) !== null && _a !== void 0 ? _a : ((settings === null || settings === void 0 ? void 0 : settings.getNextFocusable) ? FocusKeys.ArrowAll : FocusKeys.ArrowVertical) | FocusKeys.HomeAndEnd;
141
143
  const focusOutBehavior = (_b = settings === null || settings === void 0 ? void 0 : settings.focusOutBehavior) !== null && _b !== void 0 ? _b : 'stop';
@@ -147,7 +149,7 @@ function focusZone(container, settings) {
147
149
  const preventScroll = (_e = settings === null || settings === void 0 ? void 0 : settings.preventScroll) !== null && _e !== void 0 ? _e : false;
148
150
  const preventInitialFocus = focusInStrategy === 'initial' && (settings === null || settings === void 0 ? void 0 : settings.activeDescendantControl);
149
151
  function getFirstFocusableElement() {
150
- return focusableElements[0];
152
+ return focusableElements.get(0);
151
153
  }
152
154
  function isActiveDescendantInputFocused() {
153
155
  return document.activeElement === activeDescendantControl;
@@ -203,7 +205,7 @@ function focusZone(container, settings) {
203
205
  if (filteredElements.length === 0) {
204
206
  return;
205
207
  }
206
- focusableElements.splice(findInsertionIndex(filteredElements), 0, ...filteredElements);
208
+ focusableElements.insertAt(findInsertionIndex(filteredElements), ...filteredElements);
207
209
  for (const element of filteredElements) {
208
210
  if (!savedTabIndex.has(element)) {
209
211
  savedTabIndex.set(element, element.getAttribute('tabindex'));
@@ -216,13 +218,13 @@ function focusZone(container, settings) {
216
218
  }
217
219
  function findInsertionIndex(elementsToInsert) {
218
220
  const firstElementToInsert = elementsToInsert[0];
219
- if (focusableElements.length === 0)
221
+ if (focusableElements.size === 0)
220
222
  return 0;
221
223
  let iMin = 0;
222
- let iMax = focusableElements.length - 1;
224
+ let iMax = focusableElements.size - 1;
223
225
  while (iMin <= iMax) {
224
226
  const i = Math.floor((iMin + iMax) / 2);
225
- const element = focusableElements[i];
227
+ const element = focusableElements.get(i);
226
228
  if (followsInDocument(firstElementToInsert, element)) {
227
229
  iMax = i - 1;
228
230
  }
@@ -237,10 +239,7 @@ function focusZone(container, settings) {
237
239
  }
238
240
  function endFocusManagement(...elements) {
239
241
  for (const element of elements) {
240
- const focusableElementIndex = focusableElements.indexOf(element);
241
- if (focusableElementIndex >= 0) {
242
- focusableElements.splice(focusableElementIndex, 1);
243
- }
242
+ focusableElements.delete(element);
244
243
  const savedIndex = savedTabIndex.get(element);
245
244
  if (savedIndex !== undefined) {
246
245
  if (savedIndex === null) {
@@ -296,7 +295,12 @@ function focusZone(container, settings) {
296
295
  }
297
296
  }
298
297
  if (elementsToRemove.size > 0) {
299
- const toRemove = [...elementsToRemove].flatMap(node => [...iterateFocusableElements(node)]);
298
+ const toRemove = [];
299
+ for (const node of elementsToRemove) {
300
+ for (const el of iterateFocusableElements(node)) {
301
+ toRemove.push(el);
302
+ }
303
+ }
300
304
  if (toRemove.length > 0) {
301
305
  endFocusManagement(...toRemove);
302
306
  }
@@ -305,9 +309,12 @@ function focusZone(container, settings) {
305
309
  endFocusManagement(...attributeRemovals);
306
310
  }
307
311
  if (elementsToAdd.size > 0) {
308
- const toAdd = [...elementsToAdd].flatMap(node => [
309
- ...iterateFocusableElements(node, iterateFocusableElementsOptions),
310
- ]);
312
+ const toAdd = [];
313
+ for (const node of elementsToAdd) {
314
+ for (const el of iterateFocusableElements(node, iterateFocusableElementsOptions)) {
315
+ toAdd.push(el);
316
+ }
317
+ }
311
318
  if (toAdd.length > 0) {
312
319
  beginFocusManagement(...toAdd);
313
320
  }
@@ -336,7 +343,7 @@ function focusZone(container, settings) {
336
343
  }, { signal });
337
344
  if (activeDescendantControl) {
338
345
  container.addEventListener('focusin', event => {
339
- if (event.target instanceof HTMLElement && focusableElements.includes(event.target)) {
346
+ if (event.target instanceof HTMLElement && focusableElements.has(event.target)) {
340
347
  activeDescendantControl.focus({ preventScroll });
341
348
  updateFocusedElement(event.target);
342
349
  }
@@ -346,9 +353,8 @@ function focusZone(container, settings) {
346
353
  if (!(target instanceof Node)) {
347
354
  return;
348
355
  }
349
- const targetIndex = target instanceof HTMLElement ? focusableElements.indexOf(target) : -1;
350
- if (targetIndex >= 0) {
351
- updateFocusedElement(focusableElements[targetIndex]);
356
+ if (target instanceof HTMLElement && focusableElements.has(target)) {
357
+ updateFocusedElement(target);
352
358
  return;
353
359
  }
354
360
  const focusableElement = focusableElements.find(element => element.contains(target));
@@ -375,8 +381,9 @@ function focusZone(container, settings) {
375
381
  if (event.target instanceof HTMLElement) {
376
382
  if (elementIndexFocusedByClick !== undefined) {
377
383
  if (elementIndexFocusedByClick >= 0) {
378
- if (focusableElements[elementIndexFocusedByClick] !== currentFocusedElement) {
379
- updateFocusedElement(focusableElements[elementIndexFocusedByClick]);
384
+ const clickedElement = focusableElements.get(elementIndexFocusedByClick);
385
+ if (clickedElement && clickedElement !== currentFocusedElement) {
386
+ updateFocusedElement(clickedElement);
380
387
  }
381
388
  }
382
389
  elementIndexFocusedByClick = undefined;
@@ -387,8 +394,8 @@ function focusZone(container, settings) {
387
394
  }
388
395
  else if (focusInStrategy === 'closest' || focusInStrategy === 'first') {
389
396
  if (event.relatedTarget instanceof Element && !container.contains(event.relatedTarget)) {
390
- const targetElementIndex = lastKeyboardFocusDirection === 'previous' ? focusableElements.length - 1 : 0;
391
- const targetElement = focusableElements[targetElementIndex];
397
+ const targetElementIndex = lastKeyboardFocusDirection === 'previous' ? focusableElements.size - 1 : 0;
398
+ const targetElement = focusableElements.get(targetElementIndex);
392
399
  targetElement === null || targetElement === void 0 ? void 0 : targetElement.focus({ preventScroll });
393
400
  return;
394
401
  }
@@ -399,8 +406,7 @@ function focusZone(container, settings) {
399
406
  else if (typeof focusInStrategy === 'function') {
400
407
  if (event.relatedTarget instanceof Element && !container.contains(event.relatedTarget)) {
401
408
  const elementToFocus = focusInStrategy(event.relatedTarget);
402
- const requestedFocusElementIndex = elementToFocus ? focusableElements.indexOf(elementToFocus) : -1;
403
- if (requestedFocusElementIndex >= 0 && elementToFocus instanceof HTMLElement) {
409
+ if (elementToFocus && focusableElements.has(elementToFocus)) {
404
410
  elementToFocus.focus({ preventScroll });
405
411
  return;
406
412
  }
@@ -459,26 +465,26 @@ function focusZone(container, settings) {
459
465
  nextFocusedIndex += 1;
460
466
  }
461
467
  else {
462
- nextFocusedIndex = focusableElements.length - 1;
468
+ nextFocusedIndex = focusableElements.size - 1;
463
469
  }
464
470
  if (nextFocusedIndex < 0) {
465
471
  if (focusOutBehavior === 'wrap' && event.key !== 'Tab') {
466
- nextFocusedIndex = focusableElements.length - 1;
472
+ nextFocusedIndex = focusableElements.size - 1;
467
473
  }
468
474
  else {
469
475
  nextFocusedIndex = 0;
470
476
  }
471
477
  }
472
- if (nextFocusedIndex >= focusableElements.length) {
478
+ if (nextFocusedIndex >= focusableElements.size) {
473
479
  if (focusOutBehavior === 'wrap' && event.key !== 'Tab') {
474
480
  nextFocusedIndex = 0;
475
481
  }
476
482
  else {
477
- nextFocusedIndex = focusableElements.length - 1;
483
+ nextFocusedIndex = focusableElements.size - 1;
478
484
  }
479
485
  }
480
486
  if (lastFocusedIndex !== nextFocusedIndex) {
481
- nextElementToFocus = focusableElements[nextFocusedIndex];
487
+ nextElementToFocus = focusableElements.get(nextFocusedIndex);
482
488
  }
483
489
  }
484
490
  if (activeDescendantControl) {
@@ -0,0 +1,12 @@
1
+ export declare class IndexedSet<T> {
2
+ #private;
3
+ insertAt(index: number, ...elements: T[]): void;
4
+ delete(element: T): boolean;
5
+ has(element: T): boolean;
6
+ indexOf(element: T): number;
7
+ get(index: number): T | undefined;
8
+ get size(): number;
9
+ [Symbol.iterator](): Iterator<T>;
10
+ clear(): void;
11
+ find(predicate: (element: T) => boolean): T | undefined;
12
+ }
@@ -0,0 +1,54 @@
1
+ import { __classPrivateFieldGet, __classPrivateFieldSet } from 'tslib';
2
+
3
+ var _IndexedSet_items, _IndexedSet_itemSet;
4
+ class IndexedSet {
5
+ constructor() {
6
+ _IndexedSet_items.set(this, []);
7
+ _IndexedSet_itemSet.set(this, new Set());
8
+ }
9
+ insertAt(index, ...elements) {
10
+ const newElements = elements.filter(e => !__classPrivateFieldGet(this, _IndexedSet_itemSet, "f").has(e));
11
+ if (newElements.length === 0)
12
+ return;
13
+ __classPrivateFieldGet(this, _IndexedSet_items, "f").splice(index, 0, ...newElements);
14
+ for (const element of newElements) {
15
+ __classPrivateFieldGet(this, _IndexedSet_itemSet, "f").add(element);
16
+ }
17
+ }
18
+ delete(element) {
19
+ if (!__classPrivateFieldGet(this, _IndexedSet_itemSet, "f").has(element))
20
+ return false;
21
+ const index = __classPrivateFieldGet(this, _IndexedSet_items, "f").indexOf(element);
22
+ if (index >= 0) {
23
+ __classPrivateFieldGet(this, _IndexedSet_items, "f").splice(index, 1);
24
+ }
25
+ __classPrivateFieldGet(this, _IndexedSet_itemSet, "f").delete(element);
26
+ return true;
27
+ }
28
+ has(element) {
29
+ return __classPrivateFieldGet(this, _IndexedSet_itemSet, "f").has(element);
30
+ }
31
+ indexOf(element) {
32
+ if (!__classPrivateFieldGet(this, _IndexedSet_itemSet, "f").has(element))
33
+ return -1;
34
+ return __classPrivateFieldGet(this, _IndexedSet_items, "f").indexOf(element);
35
+ }
36
+ get(index) {
37
+ return __classPrivateFieldGet(this, _IndexedSet_items, "f")[index];
38
+ }
39
+ get size() {
40
+ return __classPrivateFieldGet(this, _IndexedSet_items, "f").length;
41
+ }
42
+ [(_IndexedSet_items = new WeakMap(), _IndexedSet_itemSet = new WeakMap(), Symbol.iterator)]() {
43
+ return __classPrivateFieldGet(this, _IndexedSet_items, "f")[Symbol.iterator]();
44
+ }
45
+ clear() {
46
+ __classPrivateFieldSet(this, _IndexedSet_items, [], "f");
47
+ __classPrivateFieldGet(this, _IndexedSet_itemSet, "f").clear();
48
+ }
49
+ find(predicate) {
50
+ return __classPrivateFieldGet(this, _IndexedSet_items, "f").find(predicate);
51
+ }
52
+ }
53
+
54
+ export { IndexedSet };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primer/behaviors",
3
- "version": "0.0.0-20251215025613",
3
+ "version": "0.0.0-20251215031129",
4
4
  "description": "Shared behaviors for JavaScript components",
5
5
  "type": "commonjs",
6
6
  "main": "dist/cjs/index.js",