@primer/behaviors 0.0.0-20251215125128 → 0.0.0-20251215200522

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;
@@ -65,19 +64,12 @@ const KEY_TO_DIRECTION = {
65
64
  PageDown: 'end',
66
65
  Backspace: 'previous',
67
66
  };
68
- let cachedIsMac;
69
- function getIsMac() {
70
- if (cachedIsMac === undefined) {
71
- cachedIsMac = userAgent.isMacOS();
72
- }
73
- return cachedIsMac;
74
- }
75
67
  function getDirection(keyboardEvent) {
76
68
  const direction = KEY_TO_DIRECTION[keyboardEvent.key];
77
69
  if (keyboardEvent.key === 'Tab' && keyboardEvent.shiftKey) {
78
70
  return 'previous';
79
71
  }
80
- const isMac = getIsMac();
72
+ const isMac = userAgent.isMacOS();
81
73
  if ((isMac && keyboardEvent.metaKey) || (!isMac && keyboardEvent.ctrlKey)) {
82
74
  if (keyboardEvent.key === 'ArrowLeft' || keyboardEvent.key === 'ArrowUp') {
83
75
  return 'start';
@@ -90,18 +82,17 @@ function getDirection(keyboardEvent) {
90
82
  }
91
83
  function shouldIgnoreFocusHandling(keyboardEvent, activeElement) {
92
84
  const key = keyboardEvent.key;
93
- const isSingleChar = key.length === 1 || (key.length === 2 && key.charCodeAt(0) >= 0xd800 && key.charCodeAt(0) <= 0xdbff);
85
+ const keyLength = [...key].length;
94
86
  const isEditable = isEditableElement.isEditableElement(activeElement);
95
87
  const isSelect = activeElement instanceof HTMLSelectElement;
96
- if (isEditable && (isSingleChar || key === 'Home' || key === 'End')) {
88
+ if (isEditable && (keyLength === 1 || key === 'Home' || key === 'End')) {
97
89
  return true;
98
90
  }
99
91
  if (isSelect) {
100
- const isMac = getIsMac();
101
- if (key === 'ArrowDown' && isMac && !keyboardEvent.metaKey) {
92
+ if (key === 'ArrowDown' && userAgent.isMacOS() && !keyboardEvent.metaKey) {
102
93
  return true;
103
94
  }
104
- if (key === 'ArrowDown' && !isMac && keyboardEvent.altKey) {
95
+ if (key === 'ArrowDown' && !userAgent.isMacOS() && keyboardEvent.altKey) {
105
96
  return true;
106
97
  }
107
98
  return false;
@@ -139,7 +130,7 @@ const activeDescendantActivatedIndirectly = 'activated-indirectly';
139
130
  const hasActiveDescendantAttribute = 'data-has-active-descendant';
140
131
  function focusZone(container, settings) {
141
132
  var _a, _b, _c, _d, _e, _f;
142
- const focusableElements = new indexedSet.IndexedSet();
133
+ const focusableElements = [];
143
134
  const savedTabIndex = new WeakMap();
144
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;
145
136
  const focusOutBehavior = (_b = settings === null || settings === void 0 ? void 0 : settings.focusOutBehavior) !== null && _b !== void 0 ? _b : 'stop';
@@ -151,7 +142,7 @@ function focusZone(container, settings) {
151
142
  const preventScroll = (_e = settings === null || settings === void 0 ? void 0 : settings.preventScroll) !== null && _e !== void 0 ? _e : false;
152
143
  const preventInitialFocus = focusInStrategy === 'initial' && (settings === null || settings === void 0 ? void 0 : settings.activeDescendantControl);
153
144
  function getFirstFocusableElement() {
154
- return focusableElements.get(0);
145
+ return focusableElements[0];
155
146
  }
156
147
  function isActiveDescendantInputFocused() {
157
148
  return document.activeElement === activeDescendantControl;
@@ -196,20 +187,17 @@ function focusZone(container, settings) {
196
187
  activeDescendantControl === null || activeDescendantControl === void 0 ? void 0 : activeDescendantControl.removeAttribute('aria-activedescendant');
197
188
  container.removeAttribute(hasActiveDescendantAttribute);
198
189
  previouslyActiveElement === null || previouslyActiveElement === void 0 ? void 0 : previouslyActiveElement.removeAttribute(isActiveDescendantAttribute);
199
- const items = container.querySelectorAll(`[${isActiveDescendantAttribute}]`);
200
- for (let i = 0; i < items.length; i++) {
201
- items[i].removeAttribute(isActiveDescendantAttribute);
190
+ for (const item of container.querySelectorAll(`[${isActiveDescendantAttribute}]`)) {
191
+ item === null || item === void 0 ? void 0 : item.removeAttribute(isActiveDescendantAttribute);
202
192
  }
203
193
  activeDescendantCallback === null || activeDescendantCallback === void 0 ? void 0 : activeDescendantCallback(undefined, previouslyActiveElement, false);
204
194
  }
205
195
  function beginFocusManagement(...elements) {
206
- const filteredElements = (settings === null || settings === void 0 ? void 0 : settings.focusableElementFilter)
207
- ? elements.filter(e => settings.focusableElementFilter(e))
208
- : 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; });
209
197
  if (filteredElements.length === 0) {
210
198
  return;
211
199
  }
212
- focusableElements.insertAt(findInsertionIndex(filteredElements), ...filteredElements);
200
+ focusableElements.splice(findInsertionIndex(filteredElements), 0, ...filteredElements);
213
201
  for (const element of filteredElements) {
214
202
  if (!savedTabIndex.has(element)) {
215
203
  savedTabIndex.set(element, element.getAttribute('tabindex'));
@@ -222,13 +210,13 @@ function focusZone(container, settings) {
222
210
  }
223
211
  function findInsertionIndex(elementsToInsert) {
224
212
  const firstElementToInsert = elementsToInsert[0];
225
- if (focusableElements.size === 0)
213
+ if (focusableElements.length === 0)
226
214
  return 0;
227
215
  let iMin = 0;
228
- let iMax = focusableElements.size - 1;
216
+ let iMax = focusableElements.length - 1;
229
217
  while (iMin <= iMax) {
230
218
  const i = Math.floor((iMin + iMax) / 2);
231
- const element = focusableElements.get(i);
219
+ const element = focusableElements[i];
232
220
  if (followsInDocument(firstElementToInsert, element)) {
233
221
  iMax = i - 1;
234
222
  }
@@ -243,7 +231,10 @@ function focusZone(container, settings) {
243
231
  }
244
232
  function endFocusManagement(...elements) {
245
233
  for (const element of elements) {
246
- focusableElements.delete(element);
234
+ const focusableElementIndex = focusableElements.indexOf(element);
235
+ if (focusableElementIndex >= 0) {
236
+ focusableElements.splice(focusableElementIndex, 1);
237
+ }
247
238
  const savedIndex = savedTabIndex.get(element);
248
239
  if (savedIndex !== undefined) {
249
240
  if (savedIndex === null) {
@@ -270,61 +261,29 @@ function focusZone(container, settings) {
270
261
  if (!preventInitialFocus)
271
262
  updateFocusedElement(initialElement);
272
263
  const observer = new MutationObserver(mutations => {
273
- const elementsToRemove = new Set();
274
- const elementsToAdd = new Set();
275
- const attributeRemovals = new Set();
276
- const attributeAdditions = new Set();
277
264
  for (const mutation of mutations) {
278
- if (mutation.type === 'childList') {
279
- for (const removedNode of mutation.removedNodes) {
280
- if (removedNode instanceof HTMLElement) {
281
- elementsToRemove.add(removedNode);
282
- }
283
- }
284
- for (const addedNode of mutation.addedNodes) {
285
- if (addedNode instanceof HTMLElement) {
286
- elementsToAdd.add(addedNode);
287
- }
265
+ for (const removedNode of mutation.removedNodes) {
266
+ if (removedNode instanceof HTMLElement) {
267
+ endFocusManagement(...iterateFocusableElements.iterateFocusableElements(removedNode));
288
268
  }
289
269
  }
290
- else if (mutation.type === 'attributes' && mutation.target instanceof HTMLElement) {
291
- const attributeName = mutation.attributeName;
292
- const hasAttribute = attributeName ? mutation.target.hasAttribute(attributeName) : false;
293
- if (hasAttribute) {
294
- attributeRemovals.add(mutation.target);
295
- }
296
- else {
297
- attributeAdditions.add(mutation.target);
270
+ if (mutation.type === 'attributes' && mutation.oldValue === null) {
271
+ if (mutation.target instanceof HTMLElement) {
272
+ endFocusManagement(mutation.target);
298
273
  }
299
274
  }
300
275
  }
301
- if (elementsToRemove.size > 0) {
302
- const toRemove = [];
303
- for (const node of elementsToRemove) {
304
- for (const el of iterateFocusableElements.iterateFocusableElements(node)) {
305
- 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));
306
280
  }
307
281
  }
308
- if (toRemove.length > 0) {
309
- endFocusManagement(...toRemove);
310
- }
311
- }
312
- if (attributeRemovals.size > 0) {
313
- endFocusManagement(...attributeRemovals);
314
- }
315
- if (elementsToAdd.size > 0) {
316
- const toAdd = [];
317
- for (const node of elementsToAdd) {
318
- for (const el of iterateFocusableElements.iterateFocusableElements(node, iterateFocusableElementsOptions)) {
319
- toAdd.push(el);
282
+ if (mutation.type === 'attributes' && mutation.oldValue !== null) {
283
+ if (mutation.target instanceof HTMLElement) {
284
+ beginFocusManagement(mutation.target);
320
285
  }
321
286
  }
322
- if (toAdd.length > 0) {
323
- beginFocusManagement(...toAdd);
324
- }
325
- }
326
- if (attributeAdditions.size > 0) {
327
- beginFocusManagement(...attributeAdditions);
328
287
  }
329
288
  });
330
289
  observer.observe(container, {
@@ -336,9 +295,7 @@ function focusZone(container, settings) {
336
295
  const controller = new AbortController();
337
296
  const signal = (_f = settings === null || settings === void 0 ? void 0 : settings.abortSignal) !== null && _f !== void 0 ? _f : controller.signal;
338
297
  signal.addEventListener('abort', () => {
339
- observer.disconnect();
340
298
  endFocusManagement(...focusableElements);
341
- focusableElements.clear();
342
299
  });
343
300
  let elementIndexFocusedByClick = undefined;
344
301
  container.addEventListener('mousedown', event => {
@@ -348,7 +305,7 @@ function focusZone(container, settings) {
348
305
  }, { signal });
349
306
  if (activeDescendantControl) {
350
307
  container.addEventListener('focusin', event => {
351
- if (event.target instanceof HTMLElement && focusableElements.has(event.target)) {
308
+ if (event.target instanceof HTMLElement && focusableElements.includes(event.target)) {
352
309
  activeDescendantControl.focus({ preventScroll });
353
310
  updateFocusedElement(event.target);
354
311
  }
@@ -358,10 +315,6 @@ function focusZone(container, settings) {
358
315
  if (!(target instanceof Node)) {
359
316
  return;
360
317
  }
361
- if (target instanceof HTMLElement && focusableElements.has(target)) {
362
- updateFocusedElement(target);
363
- return;
364
- }
365
318
  const focusableElement = focusableElements.find(element => element.contains(target));
366
319
  if (focusableElement) {
367
320
  updateFocusedElement(focusableElement);
@@ -386,9 +339,8 @@ function focusZone(container, settings) {
386
339
  if (event.target instanceof HTMLElement) {
387
340
  if (elementIndexFocusedByClick !== undefined) {
388
341
  if (elementIndexFocusedByClick >= 0) {
389
- const clickedElement = focusableElements.get(elementIndexFocusedByClick);
390
- if (clickedElement && clickedElement !== currentFocusedElement) {
391
- updateFocusedElement(clickedElement);
342
+ if (focusableElements[elementIndexFocusedByClick] !== currentFocusedElement) {
343
+ updateFocusedElement(focusableElements[elementIndexFocusedByClick]);
392
344
  }
393
345
  }
394
346
  elementIndexFocusedByClick = undefined;
@@ -399,8 +351,8 @@ function focusZone(container, settings) {
399
351
  }
400
352
  else if (focusInStrategy === 'closest' || focusInStrategy === 'first') {
401
353
  if (event.relatedTarget instanceof Element && !container.contains(event.relatedTarget)) {
402
- const targetElementIndex = lastKeyboardFocusDirection === 'previous' ? focusableElements.size - 1 : 0;
403
- const targetElement = focusableElements.get(targetElementIndex);
354
+ const targetElementIndex = lastKeyboardFocusDirection === 'previous' ? focusableElements.length - 1 : 0;
355
+ const targetElement = focusableElements[targetElementIndex];
404
356
  targetElement === null || targetElement === void 0 ? void 0 : targetElement.focus({ preventScroll });
405
357
  return;
406
358
  }
@@ -411,7 +363,8 @@ function focusZone(container, settings) {
411
363
  else if (typeof focusInStrategy === 'function') {
412
364
  if (event.relatedTarget instanceof Element && !container.contains(event.relatedTarget)) {
413
365
  const elementToFocus = focusInStrategy(event.relatedTarget);
414
- if (elementToFocus && focusableElements.has(elementToFocus)) {
366
+ const requestedFocusElementIndex = elementToFocus ? focusableElements.indexOf(elementToFocus) : -1;
367
+ if (requestedFocusElementIndex >= 0 && elementToFocus instanceof HTMLElement) {
415
368
  elementToFocus.focus({ preventScroll });
416
369
  return;
417
370
  }
@@ -470,26 +423,26 @@ function focusZone(container, settings) {
470
423
  nextFocusedIndex += 1;
471
424
  }
472
425
  else {
473
- nextFocusedIndex = focusableElements.size - 1;
426
+ nextFocusedIndex = focusableElements.length - 1;
474
427
  }
475
428
  if (nextFocusedIndex < 0) {
476
429
  if (focusOutBehavior === 'wrap' && event.key !== 'Tab') {
477
- nextFocusedIndex = focusableElements.size - 1;
430
+ nextFocusedIndex = focusableElements.length - 1;
478
431
  }
479
432
  else {
480
433
  nextFocusedIndex = 0;
481
434
  }
482
435
  }
483
- if (nextFocusedIndex >= focusableElements.size) {
436
+ if (nextFocusedIndex >= focusableElements.length) {
484
437
  if (focusOutBehavior === 'wrap' && event.key !== 'Tab') {
485
438
  nextFocusedIndex = 0;
486
439
  }
487
440
  else {
488
- nextFocusedIndex = focusableElements.size - 1;
441
+ nextFocusedIndex = focusableElements.length - 1;
489
442
  }
490
443
  }
491
444
  if (lastFocusedIndex !== nextFocusedIndex) {
492
- nextElementToFocus = focusableElements.get(nextFocusedIndex);
445
+ nextElementToFocus = focusableElements[nextFocusedIndex];
493
446
  }
494
447
  }
495
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;
@@ -63,19 +62,12 @@ const KEY_TO_DIRECTION = {
63
62
  PageDown: 'end',
64
63
  Backspace: 'previous',
65
64
  };
66
- let cachedIsMac;
67
- function getIsMac() {
68
- if (cachedIsMac === undefined) {
69
- cachedIsMac = isMacOS();
70
- }
71
- return cachedIsMac;
72
- }
73
65
  function getDirection(keyboardEvent) {
74
66
  const direction = KEY_TO_DIRECTION[keyboardEvent.key];
75
67
  if (keyboardEvent.key === 'Tab' && keyboardEvent.shiftKey) {
76
68
  return 'previous';
77
69
  }
78
- const isMac = getIsMac();
70
+ const isMac = isMacOS();
79
71
  if ((isMac && keyboardEvent.metaKey) || (!isMac && keyboardEvent.ctrlKey)) {
80
72
  if (keyboardEvent.key === 'ArrowLeft' || keyboardEvent.key === 'ArrowUp') {
81
73
  return 'start';
@@ -88,18 +80,17 @@ function getDirection(keyboardEvent) {
88
80
  }
89
81
  function shouldIgnoreFocusHandling(keyboardEvent, activeElement) {
90
82
  const key = keyboardEvent.key;
91
- const isSingleChar = key.length === 1 || (key.length === 2 && key.charCodeAt(0) >= 0xd800 && key.charCodeAt(0) <= 0xdbff);
83
+ const keyLength = [...key].length;
92
84
  const isEditable = isEditableElement(activeElement);
93
85
  const isSelect = activeElement instanceof HTMLSelectElement;
94
- if (isEditable && (isSingleChar || key === 'Home' || key === 'End')) {
86
+ if (isEditable && (keyLength === 1 || key === 'Home' || key === 'End')) {
95
87
  return true;
96
88
  }
97
89
  if (isSelect) {
98
- const isMac = getIsMac();
99
- if (key === 'ArrowDown' && isMac && !keyboardEvent.metaKey) {
90
+ if (key === 'ArrowDown' && isMacOS() && !keyboardEvent.metaKey) {
100
91
  return true;
101
92
  }
102
- if (key === 'ArrowDown' && !isMac && keyboardEvent.altKey) {
93
+ if (key === 'ArrowDown' && !isMacOS() && keyboardEvent.altKey) {
103
94
  return true;
104
95
  }
105
96
  return false;
@@ -137,7 +128,7 @@ const activeDescendantActivatedIndirectly = 'activated-indirectly';
137
128
  const hasActiveDescendantAttribute = 'data-has-active-descendant';
138
129
  function focusZone(container, settings) {
139
130
  var _a, _b, _c, _d, _e, _f;
140
- const focusableElements = new IndexedSet();
131
+ const focusableElements = [];
141
132
  const savedTabIndex = new WeakMap();
142
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;
143
134
  const focusOutBehavior = (_b = settings === null || settings === void 0 ? void 0 : settings.focusOutBehavior) !== null && _b !== void 0 ? _b : 'stop';
@@ -149,7 +140,7 @@ function focusZone(container, settings) {
149
140
  const preventScroll = (_e = settings === null || settings === void 0 ? void 0 : settings.preventScroll) !== null && _e !== void 0 ? _e : false;
150
141
  const preventInitialFocus = focusInStrategy === 'initial' && (settings === null || settings === void 0 ? void 0 : settings.activeDescendantControl);
151
142
  function getFirstFocusableElement() {
152
- return focusableElements.get(0);
143
+ return focusableElements[0];
153
144
  }
154
145
  function isActiveDescendantInputFocused() {
155
146
  return document.activeElement === activeDescendantControl;
@@ -194,20 +185,17 @@ function focusZone(container, settings) {
194
185
  activeDescendantControl === null || activeDescendantControl === void 0 ? void 0 : activeDescendantControl.removeAttribute('aria-activedescendant');
195
186
  container.removeAttribute(hasActiveDescendantAttribute);
196
187
  previouslyActiveElement === null || previouslyActiveElement === void 0 ? void 0 : previouslyActiveElement.removeAttribute(isActiveDescendantAttribute);
197
- const items = container.querySelectorAll(`[${isActiveDescendantAttribute}]`);
198
- for (let i = 0; i < items.length; i++) {
199
- items[i].removeAttribute(isActiveDescendantAttribute);
188
+ for (const item of container.querySelectorAll(`[${isActiveDescendantAttribute}]`)) {
189
+ item === null || item === void 0 ? void 0 : item.removeAttribute(isActiveDescendantAttribute);
200
190
  }
201
191
  activeDescendantCallback === null || activeDescendantCallback === void 0 ? void 0 : activeDescendantCallback(undefined, previouslyActiveElement, false);
202
192
  }
203
193
  function beginFocusManagement(...elements) {
204
- const filteredElements = (settings === null || settings === void 0 ? void 0 : settings.focusableElementFilter)
205
- ? elements.filter(e => settings.focusableElementFilter(e))
206
- : 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; });
207
195
  if (filteredElements.length === 0) {
208
196
  return;
209
197
  }
210
- focusableElements.insertAt(findInsertionIndex(filteredElements), ...filteredElements);
198
+ focusableElements.splice(findInsertionIndex(filteredElements), 0, ...filteredElements);
211
199
  for (const element of filteredElements) {
212
200
  if (!savedTabIndex.has(element)) {
213
201
  savedTabIndex.set(element, element.getAttribute('tabindex'));
@@ -220,13 +208,13 @@ function focusZone(container, settings) {
220
208
  }
221
209
  function findInsertionIndex(elementsToInsert) {
222
210
  const firstElementToInsert = elementsToInsert[0];
223
- if (focusableElements.size === 0)
211
+ if (focusableElements.length === 0)
224
212
  return 0;
225
213
  let iMin = 0;
226
- let iMax = focusableElements.size - 1;
214
+ let iMax = focusableElements.length - 1;
227
215
  while (iMin <= iMax) {
228
216
  const i = Math.floor((iMin + iMax) / 2);
229
- const element = focusableElements.get(i);
217
+ const element = focusableElements[i];
230
218
  if (followsInDocument(firstElementToInsert, element)) {
231
219
  iMax = i - 1;
232
220
  }
@@ -241,7 +229,10 @@ function focusZone(container, settings) {
241
229
  }
242
230
  function endFocusManagement(...elements) {
243
231
  for (const element of elements) {
244
- focusableElements.delete(element);
232
+ const focusableElementIndex = focusableElements.indexOf(element);
233
+ if (focusableElementIndex >= 0) {
234
+ focusableElements.splice(focusableElementIndex, 1);
235
+ }
245
236
  const savedIndex = savedTabIndex.get(element);
246
237
  if (savedIndex !== undefined) {
247
238
  if (savedIndex === null) {
@@ -268,61 +259,29 @@ function focusZone(container, settings) {
268
259
  if (!preventInitialFocus)
269
260
  updateFocusedElement(initialElement);
270
261
  const observer = new MutationObserver(mutations => {
271
- const elementsToRemove = new Set();
272
- const elementsToAdd = new Set();
273
- const attributeRemovals = new Set();
274
- const attributeAdditions = new Set();
275
262
  for (const mutation of mutations) {
276
- if (mutation.type === 'childList') {
277
- for (const removedNode of mutation.removedNodes) {
278
- if (removedNode instanceof HTMLElement) {
279
- elementsToRemove.add(removedNode);
280
- }
281
- }
282
- for (const addedNode of mutation.addedNodes) {
283
- if (addedNode instanceof HTMLElement) {
284
- elementsToAdd.add(addedNode);
285
- }
263
+ for (const removedNode of mutation.removedNodes) {
264
+ if (removedNode instanceof HTMLElement) {
265
+ endFocusManagement(...iterateFocusableElements(removedNode));
286
266
  }
287
267
  }
288
- else if (mutation.type === 'attributes' && mutation.target instanceof HTMLElement) {
289
- const attributeName = mutation.attributeName;
290
- const hasAttribute = attributeName ? mutation.target.hasAttribute(attributeName) : false;
291
- if (hasAttribute) {
292
- attributeRemovals.add(mutation.target);
293
- }
294
- else {
295
- attributeAdditions.add(mutation.target);
268
+ if (mutation.type === 'attributes' && mutation.oldValue === null) {
269
+ if (mutation.target instanceof HTMLElement) {
270
+ endFocusManagement(mutation.target);
296
271
  }
297
272
  }
298
273
  }
299
- if (elementsToRemove.size > 0) {
300
- const toRemove = [];
301
- for (const node of elementsToRemove) {
302
- for (const el of iterateFocusableElements(node)) {
303
- 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));
304
278
  }
305
279
  }
306
- if (toRemove.length > 0) {
307
- endFocusManagement(...toRemove);
308
- }
309
- }
310
- if (attributeRemovals.size > 0) {
311
- endFocusManagement(...attributeRemovals);
312
- }
313
- if (elementsToAdd.size > 0) {
314
- const toAdd = [];
315
- for (const node of elementsToAdd) {
316
- for (const el of iterateFocusableElements(node, iterateFocusableElementsOptions)) {
317
- toAdd.push(el);
280
+ if (mutation.type === 'attributes' && mutation.oldValue !== null) {
281
+ if (mutation.target instanceof HTMLElement) {
282
+ beginFocusManagement(mutation.target);
318
283
  }
319
284
  }
320
- if (toAdd.length > 0) {
321
- beginFocusManagement(...toAdd);
322
- }
323
- }
324
- if (attributeAdditions.size > 0) {
325
- beginFocusManagement(...attributeAdditions);
326
285
  }
327
286
  });
328
287
  observer.observe(container, {
@@ -334,9 +293,7 @@ function focusZone(container, settings) {
334
293
  const controller = new AbortController();
335
294
  const signal = (_f = settings === null || settings === void 0 ? void 0 : settings.abortSignal) !== null && _f !== void 0 ? _f : controller.signal;
336
295
  signal.addEventListener('abort', () => {
337
- observer.disconnect();
338
296
  endFocusManagement(...focusableElements);
339
- focusableElements.clear();
340
297
  });
341
298
  let elementIndexFocusedByClick = undefined;
342
299
  container.addEventListener('mousedown', event => {
@@ -346,7 +303,7 @@ function focusZone(container, settings) {
346
303
  }, { signal });
347
304
  if (activeDescendantControl) {
348
305
  container.addEventListener('focusin', event => {
349
- if (event.target instanceof HTMLElement && focusableElements.has(event.target)) {
306
+ if (event.target instanceof HTMLElement && focusableElements.includes(event.target)) {
350
307
  activeDescendantControl.focus({ preventScroll });
351
308
  updateFocusedElement(event.target);
352
309
  }
@@ -356,10 +313,6 @@ function focusZone(container, settings) {
356
313
  if (!(target instanceof Node)) {
357
314
  return;
358
315
  }
359
- if (target instanceof HTMLElement && focusableElements.has(target)) {
360
- updateFocusedElement(target);
361
- return;
362
- }
363
316
  const focusableElement = focusableElements.find(element => element.contains(target));
364
317
  if (focusableElement) {
365
318
  updateFocusedElement(focusableElement);
@@ -384,9 +337,8 @@ function focusZone(container, settings) {
384
337
  if (event.target instanceof HTMLElement) {
385
338
  if (elementIndexFocusedByClick !== undefined) {
386
339
  if (elementIndexFocusedByClick >= 0) {
387
- const clickedElement = focusableElements.get(elementIndexFocusedByClick);
388
- if (clickedElement && clickedElement !== currentFocusedElement) {
389
- updateFocusedElement(clickedElement);
340
+ if (focusableElements[elementIndexFocusedByClick] !== currentFocusedElement) {
341
+ updateFocusedElement(focusableElements[elementIndexFocusedByClick]);
390
342
  }
391
343
  }
392
344
  elementIndexFocusedByClick = undefined;
@@ -397,8 +349,8 @@ function focusZone(container, settings) {
397
349
  }
398
350
  else if (focusInStrategy === 'closest' || focusInStrategy === 'first') {
399
351
  if (event.relatedTarget instanceof Element && !container.contains(event.relatedTarget)) {
400
- const targetElementIndex = lastKeyboardFocusDirection === 'previous' ? focusableElements.size - 1 : 0;
401
- const targetElement = focusableElements.get(targetElementIndex);
352
+ const targetElementIndex = lastKeyboardFocusDirection === 'previous' ? focusableElements.length - 1 : 0;
353
+ const targetElement = focusableElements[targetElementIndex];
402
354
  targetElement === null || targetElement === void 0 ? void 0 : targetElement.focus({ preventScroll });
403
355
  return;
404
356
  }
@@ -409,7 +361,8 @@ function focusZone(container, settings) {
409
361
  else if (typeof focusInStrategy === 'function') {
410
362
  if (event.relatedTarget instanceof Element && !container.contains(event.relatedTarget)) {
411
363
  const elementToFocus = focusInStrategy(event.relatedTarget);
412
- if (elementToFocus && focusableElements.has(elementToFocus)) {
364
+ const requestedFocusElementIndex = elementToFocus ? focusableElements.indexOf(elementToFocus) : -1;
365
+ if (requestedFocusElementIndex >= 0 && elementToFocus instanceof HTMLElement) {
413
366
  elementToFocus.focus({ preventScroll });
414
367
  return;
415
368
  }
@@ -468,26 +421,26 @@ function focusZone(container, settings) {
468
421
  nextFocusedIndex += 1;
469
422
  }
470
423
  else {
471
- nextFocusedIndex = focusableElements.size - 1;
424
+ nextFocusedIndex = focusableElements.length - 1;
472
425
  }
473
426
  if (nextFocusedIndex < 0) {
474
427
  if (focusOutBehavior === 'wrap' && event.key !== 'Tab') {
475
- nextFocusedIndex = focusableElements.size - 1;
428
+ nextFocusedIndex = focusableElements.length - 1;
476
429
  }
477
430
  else {
478
431
  nextFocusedIndex = 0;
479
432
  }
480
433
  }
481
- if (nextFocusedIndex >= focusableElements.size) {
434
+ if (nextFocusedIndex >= focusableElements.length) {
482
435
  if (focusOutBehavior === 'wrap' && event.key !== 'Tab') {
483
436
  nextFocusedIndex = 0;
484
437
  }
485
438
  else {
486
- nextFocusedIndex = focusableElements.size - 1;
439
+ nextFocusedIndex = focusableElements.length - 1;
487
440
  }
488
441
  }
489
442
  if (lastFocusedIndex !== nextFocusedIndex) {
490
- nextElementToFocus = focusableElements.get(nextFocusedIndex);
443
+ nextElementToFocus = focusableElements[nextFocusedIndex];
491
444
  }
492
445
  }
493
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-20251215125128",
3
+ "version": "0.0.0-20251215200522",
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 };