@primer/behaviors 0.0.0-20251215125247 → 0.0.0-20251217200500

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.
@@ -27,11 +27,9 @@ function getPositionedParent(element) {
27
27
  if (isOnTopLayer(element))
28
28
  return document.body;
29
29
  let parentNode = element.parentNode;
30
- while (parentNode !== null && parentNode !== document.body) {
31
- if (parentNode instanceof HTMLElement) {
32
- if (getComputedStyle(parentNode).position !== 'static') {
33
- return parentNode;
34
- }
30
+ while (parentNode !== null) {
31
+ if (parentNode instanceof HTMLElement && getComputedStyle(parentNode).position !== 'static') {
32
+ return parentNode;
35
33
  }
36
34
  parentNode = parentNode.parentNode;
37
35
  }
@@ -53,24 +51,26 @@ function isOnTopLayer(element) {
53
51
  return false;
54
52
  }
55
53
  function getClippingRect(element) {
56
- let clippingNode = document.body;
57
54
  let parentNode = element;
58
- while (parentNode !== null && parentNode !== document.body) {
59
- if (parentNode instanceof HTMLElement) {
60
- const overflow = getComputedStyle(parentNode).overflow;
61
- if (overflow !== 'visible') {
62
- clippingNode = parentNode;
63
- break;
64
- }
55
+ while (parentNode !== null) {
56
+ if (!(parentNode instanceof Element)) {
57
+ break;
58
+ }
59
+ const parentNodeStyle = getComputedStyle(parentNode);
60
+ if (parentNodeStyle.overflow !== 'visible') {
61
+ break;
65
62
  }
66
63
  parentNode = parentNode.parentNode;
67
64
  }
65
+ const clippingNode = parentNode === document.body || !(parentNode instanceof HTMLElement) ? document.body : parentNode;
68
66
  const elemRect = clippingNode.getBoundingClientRect();
69
67
  const elemStyle = getComputedStyle(clippingNode);
70
- const borderTop = parseInt(elemStyle.borderTopWidth, 10) || 0;
71
- const borderLeft = parseInt(elemStyle.borderLeftWidth, 10) || 0;
72
- const borderRight = parseInt(elemStyle.borderRightWidth, 10) || 0;
73
- const borderBottom = parseInt(elemStyle.borderBottomWidth, 10) || 0;
68
+ const [borderTop, borderLeft, borderRight, borderBottom] = [
69
+ elemStyle.borderTopWidth,
70
+ elemStyle.borderLeftWidth,
71
+ elemStyle.borderRightWidth,
72
+ elemStyle.borderBottomWidth,
73
+ ].map(v => parseInt(v, 10) || 0);
74
74
  return {
75
75
  top: elemRect.top + borderTop,
76
76
  left: elemRect.left + borderLeft,
@@ -6,16 +6,6 @@ var eventListenerSignal = require('./polyfills/event-listener-signal.js');
6
6
  eventListenerSignal.polyfill();
7
7
  const suspendedTrapStack = [];
8
8
  let activeTrap = undefined;
9
- const SR_ONLY_STYLES = 'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0';
10
- function createSentinel({ onFocus }) {
11
- const sentinel = document.createElement('span');
12
- sentinel.setAttribute('class', 'sentinel');
13
- sentinel.setAttribute('tabindex', '0');
14
- sentinel.setAttribute('aria-hidden', 'true');
15
- sentinel.style.cssText = SR_ONLY_STYLES;
16
- sentinel.onfocus = onFocus;
17
- return sentinel;
18
- }
19
9
  function tryReactivate() {
20
10
  const trapToReactivate = suspendedTrapStack.pop();
21
11
  if (trapToReactivate) {
@@ -33,10 +23,9 @@ function observeFocusTrap(container, sentinels) {
33
23
  const observer = new MutationObserver(mutations => {
34
24
  for (const mutation of mutations) {
35
25
  if (mutation.type === 'childList' && mutation.addedNodes.length) {
36
- for (const node of mutation.addedNodes) {
37
- if (node instanceof HTMLElement && node.tagName === 'SPAN' && node.classList.contains('sentinel')) {
38
- return;
39
- }
26
+ const sentinelChildren = Array.from(mutation.addedNodes).filter(e => e instanceof HTMLElement && e.classList.contains('sentinel') && e.tagName === 'SPAN');
27
+ if (sentinelChildren.length) {
28
+ return;
40
29
  }
41
30
  const firstChild = container.firstElementChild;
42
31
  const lastChild = container.lastElementChild;
@@ -57,20 +46,24 @@ function focusTrap(container, initialFocus, abortSignal) {
57
46
  const controller = new AbortController();
58
47
  const signal = abortSignal !== null && abortSignal !== void 0 ? abortSignal : controller.signal;
59
48
  container.setAttribute('data-focus-trap', 'active');
60
- const sentinelStart = createSentinel({
61
- onFocus: () => {
62
- const lastFocusableChild = iterateFocusableElements.getFocusableChild(container, true);
63
- lastFocusableChild === null || lastFocusableChild === void 0 ? void 0 : lastFocusableChild.focus();
64
- },
65
- });
66
- const sentinelEnd = createSentinel({
67
- onFocus: () => {
68
- const firstFocusableChild = iterateFocusableElements.getFocusableChild(container);
69
- firstFocusableChild === null || firstFocusableChild === void 0 ? void 0 : firstFocusableChild.focus();
70
- },
71
- });
72
- const hasExistingSentinels = container.querySelector(':scope > span.sentinel') !== null;
73
- if (!hasExistingSentinels) {
49
+ const sentinelStart = document.createElement('span');
50
+ sentinelStart.setAttribute('class', 'sentinel');
51
+ sentinelStart.setAttribute('tabindex', '0');
52
+ sentinelStart.setAttribute('aria-hidden', 'true');
53
+ sentinelStart.onfocus = () => {
54
+ const lastFocusableChild = iterateFocusableElements.getFocusableChild(container, true);
55
+ lastFocusableChild === null || lastFocusableChild === void 0 ? void 0 : lastFocusableChild.focus();
56
+ };
57
+ const sentinelEnd = document.createElement('span');
58
+ sentinelEnd.setAttribute('class', 'sentinel');
59
+ sentinelEnd.setAttribute('tabindex', '0');
60
+ sentinelEnd.setAttribute('aria-hidden', 'true');
61
+ sentinelEnd.onfocus = () => {
62
+ const firstFocusableChild = iterateFocusableElements.getFocusableChild(container);
63
+ firstFocusableChild === null || firstFocusableChild === void 0 ? void 0 : firstFocusableChild.focus();
64
+ };
65
+ const existingSentinels = Array.from(container.children).filter(e => e.classList.contains('sentinel') && e.tagName === 'SPAN');
66
+ if (!existingSentinels.length) {
74
67
  container.prepend(sentinelStart);
75
68
  container.append(sentinelEnd);
76
69
  }
@@ -5,7 +5,6 @@ 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');
9
8
 
10
9
  eventListenerSignal.polyfill();
11
10
  exports.FocusKeys = void 0;
@@ -83,18 +82,17 @@ function getDirection(keyboardEvent) {
83
82
  }
84
83
  function shouldIgnoreFocusHandling(keyboardEvent, activeElement) {
85
84
  const key = keyboardEvent.key;
86
- const isSingleChar = key.length === 1 || (key.length === 2 && key.charCodeAt(0) >= 0xd800 && key.charCodeAt(0) <= 0xdbff);
85
+ const keyLength = [...key].length;
87
86
  const isEditable = isEditableElement.isEditableElement(activeElement);
88
87
  const isSelect = activeElement instanceof HTMLSelectElement;
89
- if (isEditable && (isSingleChar || key === 'Home' || key === 'End')) {
88
+ if (isEditable && (keyLength === 1 || key === 'Home' || key === 'End')) {
90
89
  return true;
91
90
  }
92
91
  if (isSelect) {
93
- const isMac = userAgent.isMacOS();
94
- if (key === 'ArrowDown' && isMac && !keyboardEvent.metaKey) {
92
+ if (key === 'ArrowDown' && userAgent.isMacOS() && !keyboardEvent.metaKey) {
95
93
  return true;
96
94
  }
97
- if (key === 'ArrowDown' && !isMac && keyboardEvent.altKey) {
95
+ if (key === 'ArrowDown' && !userAgent.isMacOS() && keyboardEvent.altKey) {
98
96
  return true;
99
97
  }
100
98
  return false;
@@ -132,7 +130,7 @@ const activeDescendantActivatedIndirectly = 'activated-indirectly';
132
130
  const hasActiveDescendantAttribute = 'data-has-active-descendant';
133
131
  function focusZone(container, settings) {
134
132
  var _a, _b, _c, _d, _e, _f;
135
- const focusableElements = new indexedSet.IndexedSet();
133
+ const focusableElements = [];
136
134
  const savedTabIndex = new WeakMap();
137
135
  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;
138
136
  const focusOutBehavior = (_b = settings === null || settings === void 0 ? void 0 : settings.focusOutBehavior) !== null && _b !== void 0 ? _b : 'stop';
@@ -144,7 +142,7 @@ function focusZone(container, settings) {
144
142
  const preventScroll = (_e = settings === null || settings === void 0 ? void 0 : settings.preventScroll) !== null && _e !== void 0 ? _e : false;
145
143
  const preventInitialFocus = focusInStrategy === 'initial' && (settings === null || settings === void 0 ? void 0 : settings.activeDescendantControl);
146
144
  function getFirstFocusableElement() {
147
- return focusableElements.get(0);
145
+ return focusableElements[0];
148
146
  }
149
147
  function isActiveDescendantInputFocused() {
150
148
  return document.activeElement === activeDescendantControl;
@@ -189,20 +187,17 @@ function focusZone(container, settings) {
189
187
  activeDescendantControl === null || activeDescendantControl === void 0 ? void 0 : activeDescendantControl.removeAttribute('aria-activedescendant');
190
188
  container.removeAttribute(hasActiveDescendantAttribute);
191
189
  previouslyActiveElement === null || previouslyActiveElement === void 0 ? void 0 : previouslyActiveElement.removeAttribute(isActiveDescendantAttribute);
192
- const items = container.querySelectorAll(`[${isActiveDescendantAttribute}]`);
193
- for (let i = 0; i < items.length; i++) {
194
- items[i].removeAttribute(isActiveDescendantAttribute);
190
+ for (const item of container.querySelectorAll(`[${isActiveDescendantAttribute}]`)) {
191
+ item === null || item === void 0 ? void 0 : item.removeAttribute(isActiveDescendantAttribute);
195
192
  }
196
193
  activeDescendantCallback === null || activeDescendantCallback === void 0 ? void 0 : activeDescendantCallback(undefined, previouslyActiveElement, false);
197
194
  }
198
195
  function beginFocusManagement(...elements) {
199
- const filteredElements = (settings === null || settings === void 0 ? void 0 : settings.focusableElementFilter)
200
- ? elements.filter(e => settings.focusableElementFilter(e))
201
- : elements;
196
+ const filteredElements = elements.filter(e => { var _a, _b; return (_b = (_a = settings === null || settings === void 0 ? void 0 : settings.focusableElementFilter) === null || _a === void 0 ? void 0 : _a.call(settings, e)) !== null && _b !== void 0 ? _b : true; });
202
197
  if (filteredElements.length === 0) {
203
198
  return;
204
199
  }
205
- focusableElements.insertAt(findInsertionIndex(filteredElements), ...filteredElements);
200
+ focusableElements.splice(findInsertionIndex(filteredElements), 0, ...filteredElements);
206
201
  for (const element of filteredElements) {
207
202
  if (!savedTabIndex.has(element)) {
208
203
  savedTabIndex.set(element, element.getAttribute('tabindex'));
@@ -215,13 +210,13 @@ function focusZone(container, settings) {
215
210
  }
216
211
  function findInsertionIndex(elementsToInsert) {
217
212
  const firstElementToInsert = elementsToInsert[0];
218
- if (focusableElements.size === 0)
213
+ if (focusableElements.length === 0)
219
214
  return 0;
220
215
  let iMin = 0;
221
- let iMax = focusableElements.size - 1;
216
+ let iMax = focusableElements.length - 1;
222
217
  while (iMin <= iMax) {
223
218
  const i = Math.floor((iMin + iMax) / 2);
224
- const element = focusableElements.get(i);
219
+ const element = focusableElements[i];
225
220
  if (followsInDocument(firstElementToInsert, element)) {
226
221
  iMax = i - 1;
227
222
  }
@@ -236,7 +231,10 @@ function focusZone(container, settings) {
236
231
  }
237
232
  function endFocusManagement(...elements) {
238
233
  for (const element of elements) {
239
- focusableElements.delete(element);
234
+ const focusableElementIndex = focusableElements.indexOf(element);
235
+ if (focusableElementIndex >= 0) {
236
+ focusableElements.splice(focusableElementIndex, 1);
237
+ }
240
238
  const savedIndex = savedTabIndex.get(element);
241
239
  if (savedIndex !== undefined) {
242
240
  if (savedIndex === null) {
@@ -263,61 +261,29 @@ function focusZone(container, settings) {
263
261
  if (!preventInitialFocus)
264
262
  updateFocusedElement(initialElement);
265
263
  const observer = new MutationObserver(mutations => {
266
- const elementsToRemove = new Set();
267
- const elementsToAdd = new Set();
268
- const attributeRemovals = new Set();
269
- const attributeAdditions = new Set();
270
264
  for (const mutation of mutations) {
271
- if (mutation.type === 'childList') {
272
- for (const removedNode of mutation.removedNodes) {
273
- if (removedNode instanceof HTMLElement) {
274
- elementsToRemove.add(removedNode);
275
- }
276
- }
277
- for (const addedNode of mutation.addedNodes) {
278
- if (addedNode instanceof HTMLElement) {
279
- elementsToAdd.add(addedNode);
280
- }
265
+ for (const removedNode of mutation.removedNodes) {
266
+ if (removedNode instanceof HTMLElement) {
267
+ endFocusManagement(...iterateFocusableElements.iterateFocusableElements(removedNode));
281
268
  }
282
269
  }
283
- else if (mutation.type === 'attributes' && mutation.target instanceof HTMLElement) {
284
- const attributeName = mutation.attributeName;
285
- const hasAttribute = attributeName ? mutation.target.hasAttribute(attributeName) : false;
286
- if (hasAttribute) {
287
- attributeRemovals.add(mutation.target);
288
- }
289
- else {
290
- attributeAdditions.add(mutation.target);
270
+ if (mutation.type === 'attributes' && mutation.oldValue === null) {
271
+ if (mutation.target instanceof HTMLElement) {
272
+ endFocusManagement(mutation.target);
291
273
  }
292
274
  }
293
275
  }
294
- if (elementsToRemove.size > 0) {
295
- const toRemove = [];
296
- for (const node of elementsToRemove) {
297
- for (const el of iterateFocusableElements.iterateFocusableElements(node)) {
298
- toRemove.push(el);
276
+ for (const mutation of mutations) {
277
+ for (const addedNode of mutation.addedNodes) {
278
+ if (addedNode instanceof HTMLElement) {
279
+ beginFocusManagement(...iterateFocusableElements.iterateFocusableElements(addedNode, iterateFocusableElementsOptions));
299
280
  }
300
281
  }
301
- if (toRemove.length > 0) {
302
- endFocusManagement(...toRemove);
303
- }
304
- }
305
- if (attributeRemovals.size > 0) {
306
- endFocusManagement(...attributeRemovals);
307
- }
308
- if (elementsToAdd.size > 0) {
309
- const toAdd = [];
310
- for (const node of elementsToAdd) {
311
- for (const el of iterateFocusableElements.iterateFocusableElements(node, iterateFocusableElementsOptions)) {
312
- toAdd.push(el);
282
+ if (mutation.type === 'attributes' && mutation.oldValue !== null) {
283
+ if (mutation.target instanceof HTMLElement) {
284
+ beginFocusManagement(mutation.target);
313
285
  }
314
286
  }
315
- if (toAdd.length > 0) {
316
- beginFocusManagement(...toAdd);
317
- }
318
- }
319
- if (attributeAdditions.size > 0) {
320
- beginFocusManagement(...attributeAdditions);
321
287
  }
322
288
  });
323
289
  observer.observe(container, {
@@ -329,9 +295,7 @@ function focusZone(container, settings) {
329
295
  const controller = new AbortController();
330
296
  const signal = (_f = settings === null || settings === void 0 ? void 0 : settings.abortSignal) !== null && _f !== void 0 ? _f : controller.signal;
331
297
  signal.addEventListener('abort', () => {
332
- observer.disconnect();
333
298
  endFocusManagement(...focusableElements);
334
- focusableElements.clear();
335
299
  });
336
300
  let elementIndexFocusedByClick = undefined;
337
301
  container.addEventListener('mousedown', event => {
@@ -341,7 +305,7 @@ function focusZone(container, settings) {
341
305
  }, { signal });
342
306
  if (activeDescendantControl) {
343
307
  container.addEventListener('focusin', event => {
344
- if (event.target instanceof HTMLElement && focusableElements.has(event.target)) {
308
+ if (event.target instanceof HTMLElement && focusableElements.includes(event.target)) {
345
309
  activeDescendantControl.focus({ preventScroll });
346
310
  updateFocusedElement(event.target);
347
311
  }
@@ -351,10 +315,6 @@ function focusZone(container, settings) {
351
315
  if (!(target instanceof Node)) {
352
316
  return;
353
317
  }
354
- if (target instanceof HTMLElement && focusableElements.has(target)) {
355
- updateFocusedElement(target);
356
- return;
357
- }
358
318
  const focusableElement = focusableElements.find(element => element.contains(target));
359
319
  if (focusableElement) {
360
320
  updateFocusedElement(focusableElement);
@@ -379,9 +339,8 @@ function focusZone(container, settings) {
379
339
  if (event.target instanceof HTMLElement) {
380
340
  if (elementIndexFocusedByClick !== undefined) {
381
341
  if (elementIndexFocusedByClick >= 0) {
382
- const clickedElement = focusableElements.get(elementIndexFocusedByClick);
383
- if (clickedElement && clickedElement !== currentFocusedElement) {
384
- updateFocusedElement(clickedElement);
342
+ if (focusableElements[elementIndexFocusedByClick] !== currentFocusedElement) {
343
+ updateFocusedElement(focusableElements[elementIndexFocusedByClick]);
385
344
  }
386
345
  }
387
346
  elementIndexFocusedByClick = undefined;
@@ -392,8 +351,8 @@ function focusZone(container, settings) {
392
351
  }
393
352
  else if (focusInStrategy === 'closest' || focusInStrategy === 'first') {
394
353
  if (event.relatedTarget instanceof Element && !container.contains(event.relatedTarget)) {
395
- const targetElementIndex = lastKeyboardFocusDirection === 'previous' ? focusableElements.size - 1 : 0;
396
- const targetElement = focusableElements.get(targetElementIndex);
354
+ const targetElementIndex = lastKeyboardFocusDirection === 'previous' ? focusableElements.length - 1 : 0;
355
+ const targetElement = focusableElements[targetElementIndex];
397
356
  targetElement === null || targetElement === void 0 ? void 0 : targetElement.focus({ preventScroll });
398
357
  return;
399
358
  }
@@ -404,7 +363,8 @@ function focusZone(container, settings) {
404
363
  else if (typeof focusInStrategy === 'function') {
405
364
  if (event.relatedTarget instanceof Element && !container.contains(event.relatedTarget)) {
406
365
  const elementToFocus = focusInStrategy(event.relatedTarget);
407
- if (elementToFocus && focusableElements.has(elementToFocus)) {
366
+ const requestedFocusElementIndex = elementToFocus ? focusableElements.indexOf(elementToFocus) : -1;
367
+ if (requestedFocusElementIndex >= 0 && elementToFocus instanceof HTMLElement) {
408
368
  elementToFocus.focus({ preventScroll });
409
369
  return;
410
370
  }
@@ -463,26 +423,26 @@ function focusZone(container, settings) {
463
423
  nextFocusedIndex += 1;
464
424
  }
465
425
  else {
466
- nextFocusedIndex = focusableElements.size - 1;
426
+ nextFocusedIndex = focusableElements.length - 1;
467
427
  }
468
428
  if (nextFocusedIndex < 0) {
469
429
  if (focusOutBehavior === 'wrap' && event.key !== 'Tab') {
470
- nextFocusedIndex = focusableElements.size - 1;
430
+ nextFocusedIndex = focusableElements.length - 1;
471
431
  }
472
432
  else {
473
433
  nextFocusedIndex = 0;
474
434
  }
475
435
  }
476
- if (nextFocusedIndex >= focusableElements.size) {
436
+ if (nextFocusedIndex >= focusableElements.length) {
477
437
  if (focusOutBehavior === 'wrap' && event.key !== 'Tab') {
478
438
  nextFocusedIndex = 0;
479
439
  }
480
440
  else {
481
- nextFocusedIndex = focusableElements.size - 1;
441
+ nextFocusedIndex = focusableElements.length - 1;
482
442
  }
483
443
  }
484
444
  if (lastFocusedIndex !== nextFocusedIndex) {
485
- nextElementToFocus = focusableElements.get(nextFocusedIndex);
445
+ nextElementToFocus = focusableElements[nextFocusedIndex];
486
446
  }
487
447
  }
488
448
  if (activeDescendantControl) {
@@ -33,32 +33,24 @@ function* iterateFocusableElements(container, options = {}) {
33
33
  function getFocusableChild(container, lastChild = false) {
34
34
  return iterateFocusableElements(container, { reverse: lastChild, strict: true, onlyTabbable: true }).next().value;
35
35
  }
36
- const DISABLEABLE_TAGS = new Set(['BUTTON', 'INPUT', 'SELECT', 'TEXTAREA', 'OPTGROUP', 'OPTION', 'FIELDSET']);
37
36
  function isFocusable(elem, strict = false) {
38
- if (elem.hidden)
39
- return false;
40
- if (elem.classList.contains('sentinel'))
41
- return false;
42
- if (elem instanceof HTMLInputElement && elem.type === 'hidden')
43
- return false;
44
- if (DISABLEABLE_TAGS.has(elem.tagName) && elem.disabled)
37
+ const disabledAttrInert = ['BUTTON', 'INPUT', 'SELECT', 'TEXTAREA', 'OPTGROUP', 'OPTION', 'FIELDSET'].includes(elem.tagName) &&
38
+ elem.disabled;
39
+ const hiddenInert = elem.hidden;
40
+ const hiddenInputInert = elem instanceof HTMLInputElement && elem.type === 'hidden';
41
+ const sentinelInert = elem.classList.contains('sentinel');
42
+ if (disabledAttrInert || hiddenInert || hiddenInputInert || sentinelInert) {
45
43
  return false;
44
+ }
46
45
  if (strict) {
47
- const offsetWidth = elem.offsetWidth;
48
- const offsetHeight = elem.offsetHeight;
49
- const offsetParent = elem.offsetParent;
50
- if (offsetWidth === 0 || offsetHeight === 0)
51
- return false;
52
46
  const style = getComputedStyle(elem);
53
- if (style.display === 'none')
54
- return false;
55
- if (style.visibility === 'hidden' || style.visibility === 'collapse')
56
- return false;
57
- const position = style.position;
58
- if (!offsetParent && position !== 'fixed' && position !== 'sticky')
59
- return false;
60
- if (elem.getClientRects().length === 0)
47
+ const sizeInert = elem.offsetWidth === 0 || elem.offsetHeight === 0;
48
+ const visibilityInert = ['hidden', 'collapse'].includes(style.visibility);
49
+ const displayInert = style.display === 'none' || !elem.offsetParent;
50
+ const clientRectsInert = elem.getClientRects().length === 0;
51
+ if (sizeInert || visibilityInert || clientRectsInert || displayInert) {
61
52
  return false;
53
+ }
62
54
  }
63
55
  if (elem.getAttribute('tabindex') != null) {
64
56
  return true;
@@ -25,11 +25,9 @@ function getPositionedParent(element) {
25
25
  if (isOnTopLayer(element))
26
26
  return document.body;
27
27
  let parentNode = element.parentNode;
28
- while (parentNode !== null && parentNode !== document.body) {
29
- if (parentNode instanceof HTMLElement) {
30
- if (getComputedStyle(parentNode).position !== 'static') {
31
- return parentNode;
32
- }
28
+ while (parentNode !== null) {
29
+ if (parentNode instanceof HTMLElement && getComputedStyle(parentNode).position !== 'static') {
30
+ return parentNode;
33
31
  }
34
32
  parentNode = parentNode.parentNode;
35
33
  }
@@ -51,24 +49,26 @@ function isOnTopLayer(element) {
51
49
  return false;
52
50
  }
53
51
  function getClippingRect(element) {
54
- let clippingNode = document.body;
55
52
  let parentNode = element;
56
- while (parentNode !== null && parentNode !== document.body) {
57
- if (parentNode instanceof HTMLElement) {
58
- const overflow = getComputedStyle(parentNode).overflow;
59
- if (overflow !== 'visible') {
60
- clippingNode = parentNode;
61
- break;
62
- }
53
+ while (parentNode !== null) {
54
+ if (!(parentNode instanceof Element)) {
55
+ break;
56
+ }
57
+ const parentNodeStyle = getComputedStyle(parentNode);
58
+ if (parentNodeStyle.overflow !== 'visible') {
59
+ break;
63
60
  }
64
61
  parentNode = parentNode.parentNode;
65
62
  }
63
+ const clippingNode = parentNode === document.body || !(parentNode instanceof HTMLElement) ? document.body : parentNode;
66
64
  const elemRect = clippingNode.getBoundingClientRect();
67
65
  const elemStyle = getComputedStyle(clippingNode);
68
- const borderTop = parseInt(elemStyle.borderTopWidth, 10) || 0;
69
- const borderLeft = parseInt(elemStyle.borderLeftWidth, 10) || 0;
70
- const borderRight = parseInt(elemStyle.borderRightWidth, 10) || 0;
71
- const borderBottom = parseInt(elemStyle.borderBottomWidth, 10) || 0;
66
+ const [borderTop, borderLeft, borderRight, borderBottom] = [
67
+ elemStyle.borderTopWidth,
68
+ elemStyle.borderLeftWidth,
69
+ elemStyle.borderRightWidth,
70
+ elemStyle.borderBottomWidth,
71
+ ].map(v => parseInt(v, 10) || 0);
72
72
  return {
73
73
  top: elemRect.top + borderTop,
74
74
  left: elemRect.left + borderLeft,
@@ -4,16 +4,6 @@ import { polyfill } from './polyfills/event-listener-signal.mjs';
4
4
  polyfill();
5
5
  const suspendedTrapStack = [];
6
6
  let activeTrap = undefined;
7
- const SR_ONLY_STYLES = 'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0';
8
- function createSentinel({ onFocus }) {
9
- const sentinel = document.createElement('span');
10
- sentinel.setAttribute('class', 'sentinel');
11
- sentinel.setAttribute('tabindex', '0');
12
- sentinel.setAttribute('aria-hidden', 'true');
13
- sentinel.style.cssText = SR_ONLY_STYLES;
14
- sentinel.onfocus = onFocus;
15
- return sentinel;
16
- }
17
7
  function tryReactivate() {
18
8
  const trapToReactivate = suspendedTrapStack.pop();
19
9
  if (trapToReactivate) {
@@ -31,10 +21,9 @@ function observeFocusTrap(container, sentinels) {
31
21
  const observer = new MutationObserver(mutations => {
32
22
  for (const mutation of mutations) {
33
23
  if (mutation.type === 'childList' && mutation.addedNodes.length) {
34
- for (const node of mutation.addedNodes) {
35
- if (node instanceof HTMLElement && node.tagName === 'SPAN' && node.classList.contains('sentinel')) {
36
- return;
37
- }
24
+ const sentinelChildren = Array.from(mutation.addedNodes).filter(e => e instanceof HTMLElement && e.classList.contains('sentinel') && e.tagName === 'SPAN');
25
+ if (sentinelChildren.length) {
26
+ return;
38
27
  }
39
28
  const firstChild = container.firstElementChild;
40
29
  const lastChild = container.lastElementChild;
@@ -55,20 +44,24 @@ function focusTrap(container, initialFocus, abortSignal) {
55
44
  const controller = new AbortController();
56
45
  const signal = abortSignal !== null && abortSignal !== void 0 ? abortSignal : controller.signal;
57
46
  container.setAttribute('data-focus-trap', 'active');
58
- const sentinelStart = createSentinel({
59
- onFocus: () => {
60
- const lastFocusableChild = getFocusableChild(container, true);
61
- lastFocusableChild === null || lastFocusableChild === void 0 ? void 0 : lastFocusableChild.focus();
62
- },
63
- });
64
- const sentinelEnd = createSentinel({
65
- onFocus: () => {
66
- const firstFocusableChild = getFocusableChild(container);
67
- firstFocusableChild === null || firstFocusableChild === void 0 ? void 0 : firstFocusableChild.focus();
68
- },
69
- });
70
- const hasExistingSentinels = container.querySelector(':scope > span.sentinel') !== null;
71
- if (!hasExistingSentinels) {
47
+ const sentinelStart = document.createElement('span');
48
+ sentinelStart.setAttribute('class', 'sentinel');
49
+ sentinelStart.setAttribute('tabindex', '0');
50
+ sentinelStart.setAttribute('aria-hidden', 'true');
51
+ sentinelStart.onfocus = () => {
52
+ const lastFocusableChild = getFocusableChild(container, true);
53
+ lastFocusableChild === null || lastFocusableChild === void 0 ? void 0 : lastFocusableChild.focus();
54
+ };
55
+ const sentinelEnd = document.createElement('span');
56
+ sentinelEnd.setAttribute('class', 'sentinel');
57
+ sentinelEnd.setAttribute('tabindex', '0');
58
+ sentinelEnd.setAttribute('aria-hidden', 'true');
59
+ sentinelEnd.onfocus = () => {
60
+ const firstFocusableChild = getFocusableChild(container);
61
+ firstFocusableChild === null || firstFocusableChild === void 0 ? void 0 : firstFocusableChild.focus();
62
+ };
63
+ const existingSentinels = Array.from(container.children).filter(e => e.classList.contains('sentinel') && e.tagName === 'SPAN');
64
+ if (!existingSentinels.length) {
72
65
  container.prepend(sentinelStart);
73
66
  container.append(sentinelEnd);
74
67
  }
@@ -3,7 +3,6 @@ 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';
7
6
 
8
7
  polyfill();
9
8
  var FocusKeys;
@@ -81,18 +80,17 @@ function getDirection(keyboardEvent) {
81
80
  }
82
81
  function shouldIgnoreFocusHandling(keyboardEvent, activeElement) {
83
82
  const key = keyboardEvent.key;
84
- const isSingleChar = key.length === 1 || (key.length === 2 && key.charCodeAt(0) >= 0xd800 && key.charCodeAt(0) <= 0xdbff);
83
+ const keyLength = [...key].length;
85
84
  const isEditable = isEditableElement(activeElement);
86
85
  const isSelect = activeElement instanceof HTMLSelectElement;
87
- if (isEditable && (isSingleChar || key === 'Home' || key === 'End')) {
86
+ if (isEditable && (keyLength === 1 || key === 'Home' || key === 'End')) {
88
87
  return true;
89
88
  }
90
89
  if (isSelect) {
91
- const isMac = isMacOS();
92
- if (key === 'ArrowDown' && isMac && !keyboardEvent.metaKey) {
90
+ if (key === 'ArrowDown' && isMacOS() && !keyboardEvent.metaKey) {
93
91
  return true;
94
92
  }
95
- if (key === 'ArrowDown' && !isMac && keyboardEvent.altKey) {
93
+ if (key === 'ArrowDown' && !isMacOS() && keyboardEvent.altKey) {
96
94
  return true;
97
95
  }
98
96
  return false;
@@ -130,7 +128,7 @@ const activeDescendantActivatedIndirectly = 'activated-indirectly';
130
128
  const hasActiveDescendantAttribute = 'data-has-active-descendant';
131
129
  function focusZone(container, settings) {
132
130
  var _a, _b, _c, _d, _e, _f;
133
- const focusableElements = new IndexedSet();
131
+ const focusableElements = [];
134
132
  const savedTabIndex = new WeakMap();
135
133
  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;
136
134
  const focusOutBehavior = (_b = settings === null || settings === void 0 ? void 0 : settings.focusOutBehavior) !== null && _b !== void 0 ? _b : 'stop';
@@ -142,7 +140,7 @@ function focusZone(container, settings) {
142
140
  const preventScroll = (_e = settings === null || settings === void 0 ? void 0 : settings.preventScroll) !== null && _e !== void 0 ? _e : false;
143
141
  const preventInitialFocus = focusInStrategy === 'initial' && (settings === null || settings === void 0 ? void 0 : settings.activeDescendantControl);
144
142
  function getFirstFocusableElement() {
145
- return focusableElements.get(0);
143
+ return focusableElements[0];
146
144
  }
147
145
  function isActiveDescendantInputFocused() {
148
146
  return document.activeElement === activeDescendantControl;
@@ -187,20 +185,17 @@ function focusZone(container, settings) {
187
185
  activeDescendantControl === null || activeDescendantControl === void 0 ? void 0 : activeDescendantControl.removeAttribute('aria-activedescendant');
188
186
  container.removeAttribute(hasActiveDescendantAttribute);
189
187
  previouslyActiveElement === null || previouslyActiveElement === void 0 ? void 0 : previouslyActiveElement.removeAttribute(isActiveDescendantAttribute);
190
- const items = container.querySelectorAll(`[${isActiveDescendantAttribute}]`);
191
- for (let i = 0; i < items.length; i++) {
192
- items[i].removeAttribute(isActiveDescendantAttribute);
188
+ for (const item of container.querySelectorAll(`[${isActiveDescendantAttribute}]`)) {
189
+ item === null || item === void 0 ? void 0 : item.removeAttribute(isActiveDescendantAttribute);
193
190
  }
194
191
  activeDescendantCallback === null || activeDescendantCallback === void 0 ? void 0 : activeDescendantCallback(undefined, previouslyActiveElement, false);
195
192
  }
196
193
  function beginFocusManagement(...elements) {
197
- const filteredElements = (settings === null || settings === void 0 ? void 0 : settings.focusableElementFilter)
198
- ? elements.filter(e => settings.focusableElementFilter(e))
199
- : elements;
194
+ const filteredElements = elements.filter(e => { var _a, _b; return (_b = (_a = settings === null || settings === void 0 ? void 0 : settings.focusableElementFilter) === null || _a === void 0 ? void 0 : _a.call(settings, e)) !== null && _b !== void 0 ? _b : true; });
200
195
  if (filteredElements.length === 0) {
201
196
  return;
202
197
  }
203
- focusableElements.insertAt(findInsertionIndex(filteredElements), ...filteredElements);
198
+ focusableElements.splice(findInsertionIndex(filteredElements), 0, ...filteredElements);
204
199
  for (const element of filteredElements) {
205
200
  if (!savedTabIndex.has(element)) {
206
201
  savedTabIndex.set(element, element.getAttribute('tabindex'));
@@ -213,13 +208,13 @@ function focusZone(container, settings) {
213
208
  }
214
209
  function findInsertionIndex(elementsToInsert) {
215
210
  const firstElementToInsert = elementsToInsert[0];
216
- if (focusableElements.size === 0)
211
+ if (focusableElements.length === 0)
217
212
  return 0;
218
213
  let iMin = 0;
219
- let iMax = focusableElements.size - 1;
214
+ let iMax = focusableElements.length - 1;
220
215
  while (iMin <= iMax) {
221
216
  const i = Math.floor((iMin + iMax) / 2);
222
- const element = focusableElements.get(i);
217
+ const element = focusableElements[i];
223
218
  if (followsInDocument(firstElementToInsert, element)) {
224
219
  iMax = i - 1;
225
220
  }
@@ -234,7 +229,10 @@ function focusZone(container, settings) {
234
229
  }
235
230
  function endFocusManagement(...elements) {
236
231
  for (const element of elements) {
237
- focusableElements.delete(element);
232
+ const focusableElementIndex = focusableElements.indexOf(element);
233
+ if (focusableElementIndex >= 0) {
234
+ focusableElements.splice(focusableElementIndex, 1);
235
+ }
238
236
  const savedIndex = savedTabIndex.get(element);
239
237
  if (savedIndex !== undefined) {
240
238
  if (savedIndex === null) {
@@ -261,61 +259,29 @@ function focusZone(container, settings) {
261
259
  if (!preventInitialFocus)
262
260
  updateFocusedElement(initialElement);
263
261
  const observer = new MutationObserver(mutations => {
264
- const elementsToRemove = new Set();
265
- const elementsToAdd = new Set();
266
- const attributeRemovals = new Set();
267
- const attributeAdditions = new Set();
268
262
  for (const mutation of mutations) {
269
- if (mutation.type === 'childList') {
270
- for (const removedNode of mutation.removedNodes) {
271
- if (removedNode instanceof HTMLElement) {
272
- elementsToRemove.add(removedNode);
273
- }
274
- }
275
- for (const addedNode of mutation.addedNodes) {
276
- if (addedNode instanceof HTMLElement) {
277
- elementsToAdd.add(addedNode);
278
- }
263
+ for (const removedNode of mutation.removedNodes) {
264
+ if (removedNode instanceof HTMLElement) {
265
+ endFocusManagement(...iterateFocusableElements(removedNode));
279
266
  }
280
267
  }
281
- else if (mutation.type === 'attributes' && mutation.target instanceof HTMLElement) {
282
- const attributeName = mutation.attributeName;
283
- const hasAttribute = attributeName ? mutation.target.hasAttribute(attributeName) : false;
284
- if (hasAttribute) {
285
- attributeRemovals.add(mutation.target);
286
- }
287
- else {
288
- attributeAdditions.add(mutation.target);
268
+ if (mutation.type === 'attributes' && mutation.oldValue === null) {
269
+ if (mutation.target instanceof HTMLElement) {
270
+ endFocusManagement(mutation.target);
289
271
  }
290
272
  }
291
273
  }
292
- if (elementsToRemove.size > 0) {
293
- const toRemove = [];
294
- for (const node of elementsToRemove) {
295
- for (const el of iterateFocusableElements(node)) {
296
- toRemove.push(el);
274
+ for (const mutation of mutations) {
275
+ for (const addedNode of mutation.addedNodes) {
276
+ if (addedNode instanceof HTMLElement) {
277
+ beginFocusManagement(...iterateFocusableElements(addedNode, iterateFocusableElementsOptions));
297
278
  }
298
279
  }
299
- if (toRemove.length > 0) {
300
- endFocusManagement(...toRemove);
301
- }
302
- }
303
- if (attributeRemovals.size > 0) {
304
- endFocusManagement(...attributeRemovals);
305
- }
306
- if (elementsToAdd.size > 0) {
307
- const toAdd = [];
308
- for (const node of elementsToAdd) {
309
- for (const el of iterateFocusableElements(node, iterateFocusableElementsOptions)) {
310
- toAdd.push(el);
280
+ if (mutation.type === 'attributes' && mutation.oldValue !== null) {
281
+ if (mutation.target instanceof HTMLElement) {
282
+ beginFocusManagement(mutation.target);
311
283
  }
312
284
  }
313
- if (toAdd.length > 0) {
314
- beginFocusManagement(...toAdd);
315
- }
316
- }
317
- if (attributeAdditions.size > 0) {
318
- beginFocusManagement(...attributeAdditions);
319
285
  }
320
286
  });
321
287
  observer.observe(container, {
@@ -327,9 +293,7 @@ function focusZone(container, settings) {
327
293
  const controller = new AbortController();
328
294
  const signal = (_f = settings === null || settings === void 0 ? void 0 : settings.abortSignal) !== null && _f !== void 0 ? _f : controller.signal;
329
295
  signal.addEventListener('abort', () => {
330
- observer.disconnect();
331
296
  endFocusManagement(...focusableElements);
332
- focusableElements.clear();
333
297
  });
334
298
  let elementIndexFocusedByClick = undefined;
335
299
  container.addEventListener('mousedown', event => {
@@ -339,7 +303,7 @@ function focusZone(container, settings) {
339
303
  }, { signal });
340
304
  if (activeDescendantControl) {
341
305
  container.addEventListener('focusin', event => {
342
- if (event.target instanceof HTMLElement && focusableElements.has(event.target)) {
306
+ if (event.target instanceof HTMLElement && focusableElements.includes(event.target)) {
343
307
  activeDescendantControl.focus({ preventScroll });
344
308
  updateFocusedElement(event.target);
345
309
  }
@@ -349,10 +313,6 @@ function focusZone(container, settings) {
349
313
  if (!(target instanceof Node)) {
350
314
  return;
351
315
  }
352
- if (target instanceof HTMLElement && focusableElements.has(target)) {
353
- updateFocusedElement(target);
354
- return;
355
- }
356
316
  const focusableElement = focusableElements.find(element => element.contains(target));
357
317
  if (focusableElement) {
358
318
  updateFocusedElement(focusableElement);
@@ -377,9 +337,8 @@ function focusZone(container, settings) {
377
337
  if (event.target instanceof HTMLElement) {
378
338
  if (elementIndexFocusedByClick !== undefined) {
379
339
  if (elementIndexFocusedByClick >= 0) {
380
- const clickedElement = focusableElements.get(elementIndexFocusedByClick);
381
- if (clickedElement && clickedElement !== currentFocusedElement) {
382
- updateFocusedElement(clickedElement);
340
+ if (focusableElements[elementIndexFocusedByClick] !== currentFocusedElement) {
341
+ updateFocusedElement(focusableElements[elementIndexFocusedByClick]);
383
342
  }
384
343
  }
385
344
  elementIndexFocusedByClick = undefined;
@@ -390,8 +349,8 @@ function focusZone(container, settings) {
390
349
  }
391
350
  else if (focusInStrategy === 'closest' || focusInStrategy === 'first') {
392
351
  if (event.relatedTarget instanceof Element && !container.contains(event.relatedTarget)) {
393
- const targetElementIndex = lastKeyboardFocusDirection === 'previous' ? focusableElements.size - 1 : 0;
394
- const targetElement = focusableElements.get(targetElementIndex);
352
+ const targetElementIndex = lastKeyboardFocusDirection === 'previous' ? focusableElements.length - 1 : 0;
353
+ const targetElement = focusableElements[targetElementIndex];
395
354
  targetElement === null || targetElement === void 0 ? void 0 : targetElement.focus({ preventScroll });
396
355
  return;
397
356
  }
@@ -402,7 +361,8 @@ function focusZone(container, settings) {
402
361
  else if (typeof focusInStrategy === 'function') {
403
362
  if (event.relatedTarget instanceof Element && !container.contains(event.relatedTarget)) {
404
363
  const elementToFocus = focusInStrategy(event.relatedTarget);
405
- if (elementToFocus && focusableElements.has(elementToFocus)) {
364
+ const requestedFocusElementIndex = elementToFocus ? focusableElements.indexOf(elementToFocus) : -1;
365
+ if (requestedFocusElementIndex >= 0 && elementToFocus instanceof HTMLElement) {
406
366
  elementToFocus.focus({ preventScroll });
407
367
  return;
408
368
  }
@@ -461,26 +421,26 @@ function focusZone(container, settings) {
461
421
  nextFocusedIndex += 1;
462
422
  }
463
423
  else {
464
- nextFocusedIndex = focusableElements.size - 1;
424
+ nextFocusedIndex = focusableElements.length - 1;
465
425
  }
466
426
  if (nextFocusedIndex < 0) {
467
427
  if (focusOutBehavior === 'wrap' && event.key !== 'Tab') {
468
- nextFocusedIndex = focusableElements.size - 1;
428
+ nextFocusedIndex = focusableElements.length - 1;
469
429
  }
470
430
  else {
471
431
  nextFocusedIndex = 0;
472
432
  }
473
433
  }
474
- if (nextFocusedIndex >= focusableElements.size) {
434
+ if (nextFocusedIndex >= focusableElements.length) {
475
435
  if (focusOutBehavior === 'wrap' && event.key !== 'Tab') {
476
436
  nextFocusedIndex = 0;
477
437
  }
478
438
  else {
479
- nextFocusedIndex = focusableElements.size - 1;
439
+ nextFocusedIndex = focusableElements.length - 1;
480
440
  }
481
441
  }
482
442
  if (lastFocusedIndex !== nextFocusedIndex) {
483
- nextElementToFocus = focusableElements.get(nextFocusedIndex);
443
+ nextElementToFocus = focusableElements[nextFocusedIndex];
484
444
  }
485
445
  }
486
446
  if (activeDescendantControl) {
@@ -31,32 +31,24 @@ function* iterateFocusableElements(container, options = {}) {
31
31
  function getFocusableChild(container, lastChild = false) {
32
32
  return iterateFocusableElements(container, { reverse: lastChild, strict: true, onlyTabbable: true }).next().value;
33
33
  }
34
- const DISABLEABLE_TAGS = new Set(['BUTTON', 'INPUT', 'SELECT', 'TEXTAREA', 'OPTGROUP', 'OPTION', 'FIELDSET']);
35
34
  function isFocusable(elem, strict = false) {
36
- if (elem.hidden)
37
- return false;
38
- if (elem.classList.contains('sentinel'))
39
- return false;
40
- if (elem instanceof HTMLInputElement && elem.type === 'hidden')
41
- return false;
42
- if (DISABLEABLE_TAGS.has(elem.tagName) && elem.disabled)
35
+ const disabledAttrInert = ['BUTTON', 'INPUT', 'SELECT', 'TEXTAREA', 'OPTGROUP', 'OPTION', 'FIELDSET'].includes(elem.tagName) &&
36
+ elem.disabled;
37
+ const hiddenInert = elem.hidden;
38
+ const hiddenInputInert = elem instanceof HTMLInputElement && elem.type === 'hidden';
39
+ const sentinelInert = elem.classList.contains('sentinel');
40
+ if (disabledAttrInert || hiddenInert || hiddenInputInert || sentinelInert) {
43
41
  return false;
42
+ }
44
43
  if (strict) {
45
- const offsetWidth = elem.offsetWidth;
46
- const offsetHeight = elem.offsetHeight;
47
- const offsetParent = elem.offsetParent;
48
- if (offsetWidth === 0 || offsetHeight === 0)
49
- return false;
50
44
  const style = getComputedStyle(elem);
51
- if (style.display === 'none')
52
- return false;
53
- if (style.visibility === 'hidden' || style.visibility === 'collapse')
54
- return false;
55
- const position = style.position;
56
- if (!offsetParent && position !== 'fixed' && position !== 'sticky')
57
- return false;
58
- if (elem.getClientRects().length === 0)
45
+ const sizeInert = elem.offsetWidth === 0 || elem.offsetHeight === 0;
46
+ const visibilityInert = ['hidden', 'collapse'].includes(style.visibility);
47
+ const displayInert = style.display === 'none' || !elem.offsetParent;
48
+ const clientRectsInert = elem.getClientRects().length === 0;
49
+ if (sizeInert || visibilityInert || clientRectsInert || displayInert) {
59
50
  return false;
51
+ }
60
52
  }
61
53
  if (elem.getAttribute('tabindex') != null) {
62
54
  return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primer/behaviors",
3
- "version": "0.0.0-20251215125247",
3
+ "version": "0.0.0-20251217200500",
4
4
  "description": "Shared behaviors for JavaScript components",
5
5
  "type": "commonjs",
6
6
  "main": "dist/cjs/index.js",
@@ -1,13 +0,0 @@
1
- export declare class IndexedSet<T> {
2
- private _items;
3
- private _itemSet;
4
- insertAt(index: number, ...elements: T[]): void;
5
- delete(element: T): boolean;
6
- has(element: T): boolean;
7
- indexOf(element: T): number;
8
- get(index: number): T | undefined;
9
- get size(): number;
10
- [Symbol.iterator](): Iterator<T>;
11
- clear(): void;
12
- find(predicate: (element: T) => boolean): T | undefined;
13
- }
@@ -1,53 +0,0 @@
1
- 'use strict';
2
-
3
- class IndexedSet {
4
- constructor() {
5
- this._items = [];
6
- this._itemSet = new Set();
7
- }
8
- insertAt(index, ...elements) {
9
- const newElements = elements.filter(e => !this._itemSet.has(e));
10
- if (newElements.length === 0)
11
- return;
12
- this._items.splice(index, 0, ...newElements);
13
- for (const element of newElements) {
14
- this._itemSet.add(element);
15
- }
16
- }
17
- delete(element) {
18
- if (!this._itemSet.has(element))
19
- return false;
20
- const index = this._items.indexOf(element);
21
- if (index >= 0) {
22
- this._items.splice(index, 1);
23
- }
24
- this._itemSet.delete(element);
25
- return true;
26
- }
27
- has(element) {
28
- return this._itemSet.has(element);
29
- }
30
- indexOf(element) {
31
- if (!this._itemSet.has(element))
32
- return -1;
33
- return this._items.indexOf(element);
34
- }
35
- get(index) {
36
- return this._items[index];
37
- }
38
- get size() {
39
- return this._items.length;
40
- }
41
- [Symbol.iterator]() {
42
- return this._items[Symbol.iterator]();
43
- }
44
- clear() {
45
- this._items = [];
46
- this._itemSet.clear();
47
- }
48
- find(predicate) {
49
- return this._items.find(predicate);
50
- }
51
- }
52
-
53
- exports.IndexedSet = IndexedSet;
@@ -1,13 +0,0 @@
1
- export declare class IndexedSet<T> {
2
- private _items;
3
- private _itemSet;
4
- insertAt(index: number, ...elements: T[]): void;
5
- delete(element: T): boolean;
6
- has(element: T): boolean;
7
- indexOf(element: T): number;
8
- get(index: number): T | undefined;
9
- get size(): number;
10
- [Symbol.iterator](): Iterator<T>;
11
- clear(): void;
12
- find(predicate: (element: T) => boolean): T | undefined;
13
- }
@@ -1,51 +0,0 @@
1
- class IndexedSet {
2
- constructor() {
3
- this._items = [];
4
- this._itemSet = new Set();
5
- }
6
- insertAt(index, ...elements) {
7
- const newElements = elements.filter(e => !this._itemSet.has(e));
8
- if (newElements.length === 0)
9
- return;
10
- this._items.splice(index, 0, ...newElements);
11
- for (const element of newElements) {
12
- this._itemSet.add(element);
13
- }
14
- }
15
- delete(element) {
16
- if (!this._itemSet.has(element))
17
- return false;
18
- const index = this._items.indexOf(element);
19
- if (index >= 0) {
20
- this._items.splice(index, 1);
21
- }
22
- this._itemSet.delete(element);
23
- return true;
24
- }
25
- has(element) {
26
- return this._itemSet.has(element);
27
- }
28
- indexOf(element) {
29
- if (!this._itemSet.has(element))
30
- return -1;
31
- return this._items.indexOf(element);
32
- }
33
- get(index) {
34
- return this._items[index];
35
- }
36
- get size() {
37
- return this._items.length;
38
- }
39
- [Symbol.iterator]() {
40
- return this._items[Symbol.iterator]();
41
- }
42
- clear() {
43
- this._items = [];
44
- this._itemSet.clear();
45
- }
46
- find(predicate) {
47
- return this._items.find(predicate);
48
- }
49
- }
50
-
51
- export { IndexedSet };