@primer/behaviors 0.0.0-20251211200451 → 0.0.0-20251214172224

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,9 +27,11 @@ function getPositionedParent(element) {
27
27
  if (isOnTopLayer(element))
28
28
  return document.body;
29
29
  let parentNode = element.parentNode;
30
- while (parentNode !== null) {
31
- if (parentNode instanceof HTMLElement && getComputedStyle(parentNode).position !== 'static') {
32
- return parentNode;
30
+ while (parentNode !== null && parentNode !== document.body) {
31
+ if (parentNode instanceof HTMLElement) {
32
+ if (getComputedStyle(parentNode).position !== 'static') {
33
+ return parentNode;
34
+ }
33
35
  }
34
36
  parentNode = parentNode.parentNode;
35
37
  }
@@ -51,26 +53,24 @@ function isOnTopLayer(element) {
51
53
  return false;
52
54
  }
53
55
  function getClippingRect(element) {
56
+ let clippingNode = document.body;
54
57
  let parentNode = element;
55
- while (parentNode !== null) {
56
- if (!(parentNode instanceof Element)) {
57
- break;
58
- }
59
- const parentNodeStyle = getComputedStyle(parentNode);
60
- if (parentNodeStyle.overflow !== 'visible') {
61
- break;
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
+ }
62
65
  }
63
66
  parentNode = parentNode.parentNode;
64
67
  }
65
- const clippingNode = parentNode === document.body || !(parentNode instanceof HTMLElement) ? document.body : parentNode;
66
68
  const elemRect = clippingNode.getBoundingClientRect();
67
69
  const elemStyle = getComputedStyle(clippingNode);
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);
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;
74
74
  return {
75
75
  top: elemRect.top + borderTop,
76
76
  left: elemRect.left + borderLeft,
@@ -47,23 +47,25 @@ function focusTrap(container, initialFocus, abortSignal) {
47
47
  const signal = abortSignal !== null && abortSignal !== void 0 ? abortSignal : controller.signal;
48
48
  container.setAttribute('data-focus-trap', 'active');
49
49
  const sentinelStart = document.createElement('span');
50
- sentinelStart.setAttribute('class', 'sentinel');
51
- sentinelStart.setAttribute('tabindex', '0');
50
+ sentinelStart.className = 'sentinel';
51
+ sentinelStart.tabIndex = 0;
52
52
  sentinelStart.setAttribute('aria-hidden', 'true');
53
+ sentinelStart.style.cssText = 'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0';
53
54
  sentinelStart.onfocus = () => {
54
55
  const lastFocusableChild = iterateFocusableElements.getFocusableChild(container, true);
55
56
  lastFocusableChild === null || lastFocusableChild === void 0 ? void 0 : lastFocusableChild.focus();
56
57
  };
57
58
  const sentinelEnd = document.createElement('span');
58
- sentinelEnd.setAttribute('class', 'sentinel');
59
- sentinelEnd.setAttribute('tabindex', '0');
59
+ sentinelEnd.className = 'sentinel';
60
+ sentinelEnd.tabIndex = 0;
60
61
  sentinelEnd.setAttribute('aria-hidden', 'true');
62
+ sentinelEnd.style.cssText = 'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0';
61
63
  sentinelEnd.onfocus = () => {
62
64
  const firstFocusableChild = iterateFocusableElements.getFocusableChild(container);
63
65
  firstFocusableChild === null || firstFocusableChild === void 0 ? void 0 : firstFocusableChild.focus();
64
66
  };
65
- const existingSentinels = Array.from(container.children).filter(e => e.classList.contains('sentinel') && e.tagName === 'SPAN');
66
- if (!existingSentinels.length) {
67
+ const hasExistingSentinels = container.querySelector(':scope > span.sentinel') !== null;
68
+ if (!hasExistingSentinels) {
67
69
  container.prepend(sentinelStart);
68
70
  container.append(sentinelEnd);
69
71
  }
@@ -64,12 +64,19 @@ const KEY_TO_DIRECTION = {
64
64
  PageDown: 'end',
65
65
  Backspace: 'previous',
66
66
  };
67
+ let cachedIsMac;
68
+ function getIsMac() {
69
+ if (cachedIsMac === undefined) {
70
+ cachedIsMac = userAgent.isMacOS();
71
+ }
72
+ return cachedIsMac;
73
+ }
67
74
  function getDirection(keyboardEvent) {
68
75
  const direction = KEY_TO_DIRECTION[keyboardEvent.key];
69
76
  if (keyboardEvent.key === 'Tab' && keyboardEvent.shiftKey) {
70
77
  return 'previous';
71
78
  }
72
- const isMac = userAgent.isMacOS();
79
+ const isMac = getIsMac();
73
80
  if ((isMac && keyboardEvent.metaKey) || (!isMac && keyboardEvent.ctrlKey)) {
74
81
  if (keyboardEvent.key === 'ArrowLeft' || keyboardEvent.key === 'ArrowUp') {
75
82
  return 'start';
@@ -187,8 +194,9 @@ function focusZone(container, settings) {
187
194
  activeDescendantControl === null || activeDescendantControl === void 0 ? void 0 : activeDescendantControl.removeAttribute('aria-activedescendant');
188
195
  container.removeAttribute(hasActiveDescendantAttribute);
189
196
  previouslyActiveElement === null || previouslyActiveElement === void 0 ? void 0 : previouslyActiveElement.removeAttribute(isActiveDescendantAttribute);
190
- for (const item of container.querySelectorAll(`[${isActiveDescendantAttribute}]`)) {
191
- item === null || item === void 0 ? void 0 : item.removeAttribute(isActiveDescendantAttribute);
197
+ const items = container.querySelectorAll(`[${isActiveDescendantAttribute}]`);
198
+ for (let i = 0; i < items.length; i++) {
199
+ items[i].removeAttribute(isActiveDescendantAttribute);
192
200
  }
193
201
  activeDescendantCallback === null || activeDescendantCallback === void 0 ? void 0 : activeDescendantCallback(undefined, previouslyActiveElement, false);
194
202
  }
@@ -261,30 +269,50 @@ function focusZone(container, settings) {
261
269
  if (!preventInitialFocus)
262
270
  updateFocusedElement(initialElement);
263
271
  const observer = new MutationObserver(mutations => {
272
+ const elementsToRemove = [];
273
+ const elementsToAdd = [];
274
+ const attributeRemovals = [];
275
+ const attributeAdditions = [];
264
276
  for (const mutation of mutations) {
265
- for (const removedNode of mutation.removedNodes) {
266
- if (removedNode instanceof HTMLElement) {
267
- endFocusManagement(...iterateFocusableElements.iterateFocusableElements(removedNode));
277
+ if (mutation.type === 'childList') {
278
+ for (const removedNode of mutation.removedNodes) {
279
+ if (removedNode instanceof HTMLElement) {
280
+ elementsToRemove.push(removedNode);
281
+ }
282
+ }
283
+ for (const addedNode of mutation.addedNodes) {
284
+ if (addedNode instanceof HTMLElement) {
285
+ elementsToAdd.push(addedNode);
286
+ }
268
287
  }
269
288
  }
270
- if (mutation.type === 'attributes' && mutation.oldValue === null) {
271
- if (mutation.target instanceof HTMLElement) {
272
- endFocusManagement(mutation.target);
289
+ else if (mutation.type === 'attributes' && mutation.target instanceof HTMLElement) {
290
+ if (mutation.oldValue === null) {
291
+ attributeRemovals.push(mutation.target);
292
+ }
293
+ else {
294
+ attributeAdditions.push(mutation.target);
273
295
  }
274
296
  }
275
297
  }
276
- for (const mutation of mutations) {
277
- for (const addedNode of mutation.addedNodes) {
278
- if (addedNode instanceof HTMLElement) {
279
- beginFocusManagement(...iterateFocusableElements.iterateFocusableElements(addedNode, iterateFocusableElementsOptions));
280
- }
298
+ if (elementsToRemove.length > 0) {
299
+ const toRemove = elementsToRemove.flatMap(node => [...iterateFocusableElements.iterateFocusableElements(node)]);
300
+ if (toRemove.length > 0) {
301
+ endFocusManagement(...toRemove);
281
302
  }
282
- if (mutation.type === 'attributes' && mutation.oldValue !== null) {
283
- if (mutation.target instanceof HTMLElement) {
284
- beginFocusManagement(mutation.target);
285
- }
303
+ }
304
+ if (attributeRemovals.length > 0) {
305
+ endFocusManagement(...attributeRemovals);
306
+ }
307
+ if (elementsToAdd.length > 0) {
308
+ const toAdd = elementsToAdd.flatMap(node => [...iterateFocusableElements.iterateFocusableElements(node, iterateFocusableElementsOptions)]);
309
+ if (toAdd.length > 0) {
310
+ beginFocusManagement(...toAdd);
286
311
  }
287
312
  }
313
+ if (attributeAdditions.length > 0) {
314
+ beginFocusManagement(...attributeAdditions);
315
+ }
288
316
  });
289
317
  observer.observe(container, {
290
318
  subtree: true,
@@ -319,7 +347,7 @@ function focusZone(container, settings) {
319
347
  if (focusableElement) {
320
348
  updateFocusedElement(focusableElement);
321
349
  }
322
- }, { signal, capture: true });
350
+ }, { signal, capture: true, passive: true });
323
351
  }
324
352
  activeDescendantControl.addEventListener('focusin', () => {
325
353
  if (!currentFocusedElement) {
@@ -33,24 +33,31 @@ 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']);
36
37
  function isFocusable(elem, strict = false) {
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) {
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)
43
45
  return false;
44
- }
45
46
  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
+ if (!offsetParent)
53
+ return false;
46
54
  const style = getComputedStyle(elem);
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) {
55
+ if (style.display === 'none')
56
+ return false;
57
+ if (style.visibility === 'hidden' || style.visibility === 'collapse')
58
+ return false;
59
+ if (elem.getClientRects().length === 0)
52
60
  return false;
53
- }
54
61
  }
55
62
  if (elem.getAttribute('tabindex') != null) {
56
63
  return true;
@@ -25,9 +25,11 @@ function getPositionedParent(element) {
25
25
  if (isOnTopLayer(element))
26
26
  return document.body;
27
27
  let parentNode = element.parentNode;
28
- while (parentNode !== null) {
29
- if (parentNode instanceof HTMLElement && getComputedStyle(parentNode).position !== 'static') {
30
- return parentNode;
28
+ while (parentNode !== null && parentNode !== document.body) {
29
+ if (parentNode instanceof HTMLElement) {
30
+ if (getComputedStyle(parentNode).position !== 'static') {
31
+ return parentNode;
32
+ }
31
33
  }
32
34
  parentNode = parentNode.parentNode;
33
35
  }
@@ -49,26 +51,24 @@ function isOnTopLayer(element) {
49
51
  return false;
50
52
  }
51
53
  function getClippingRect(element) {
54
+ let clippingNode = document.body;
52
55
  let parentNode = element;
53
- while (parentNode !== null) {
54
- if (!(parentNode instanceof Element)) {
55
- break;
56
- }
57
- const parentNodeStyle = getComputedStyle(parentNode);
58
- if (parentNodeStyle.overflow !== 'visible') {
59
- break;
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
+ }
60
63
  }
61
64
  parentNode = parentNode.parentNode;
62
65
  }
63
- const clippingNode = parentNode === document.body || !(parentNode instanceof HTMLElement) ? document.body : parentNode;
64
66
  const elemRect = clippingNode.getBoundingClientRect();
65
67
  const elemStyle = getComputedStyle(clippingNode);
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);
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;
72
72
  return {
73
73
  top: elemRect.top + borderTop,
74
74
  left: elemRect.left + borderLeft,
@@ -45,23 +45,25 @@ function focusTrap(container, initialFocus, abortSignal) {
45
45
  const signal = abortSignal !== null && abortSignal !== void 0 ? abortSignal : controller.signal;
46
46
  container.setAttribute('data-focus-trap', 'active');
47
47
  const sentinelStart = document.createElement('span');
48
- sentinelStart.setAttribute('class', 'sentinel');
49
- sentinelStart.setAttribute('tabindex', '0');
48
+ sentinelStart.className = 'sentinel';
49
+ sentinelStart.tabIndex = 0;
50
50
  sentinelStart.setAttribute('aria-hidden', 'true');
51
+ sentinelStart.style.cssText = 'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0';
51
52
  sentinelStart.onfocus = () => {
52
53
  const lastFocusableChild = getFocusableChild(container, true);
53
54
  lastFocusableChild === null || lastFocusableChild === void 0 ? void 0 : lastFocusableChild.focus();
54
55
  };
55
56
  const sentinelEnd = document.createElement('span');
56
- sentinelEnd.setAttribute('class', 'sentinel');
57
- sentinelEnd.setAttribute('tabindex', '0');
57
+ sentinelEnd.className = 'sentinel';
58
+ sentinelEnd.tabIndex = 0;
58
59
  sentinelEnd.setAttribute('aria-hidden', 'true');
60
+ sentinelEnd.style.cssText = 'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0';
59
61
  sentinelEnd.onfocus = () => {
60
62
  const firstFocusableChild = getFocusableChild(container);
61
63
  firstFocusableChild === null || firstFocusableChild === void 0 ? void 0 : firstFocusableChild.focus();
62
64
  };
63
- const existingSentinels = Array.from(container.children).filter(e => e.classList.contains('sentinel') && e.tagName === 'SPAN');
64
- if (!existingSentinels.length) {
65
+ const hasExistingSentinels = container.querySelector(':scope > span.sentinel') !== null;
66
+ if (!hasExistingSentinels) {
65
67
  container.prepend(sentinelStart);
66
68
  container.append(sentinelEnd);
67
69
  }
@@ -62,12 +62,19 @@ const KEY_TO_DIRECTION = {
62
62
  PageDown: 'end',
63
63
  Backspace: 'previous',
64
64
  };
65
+ let cachedIsMac;
66
+ function getIsMac() {
67
+ if (cachedIsMac === undefined) {
68
+ cachedIsMac = isMacOS();
69
+ }
70
+ return cachedIsMac;
71
+ }
65
72
  function getDirection(keyboardEvent) {
66
73
  const direction = KEY_TO_DIRECTION[keyboardEvent.key];
67
74
  if (keyboardEvent.key === 'Tab' && keyboardEvent.shiftKey) {
68
75
  return 'previous';
69
76
  }
70
- const isMac = isMacOS();
77
+ const isMac = getIsMac();
71
78
  if ((isMac && keyboardEvent.metaKey) || (!isMac && keyboardEvent.ctrlKey)) {
72
79
  if (keyboardEvent.key === 'ArrowLeft' || keyboardEvent.key === 'ArrowUp') {
73
80
  return 'start';
@@ -185,8 +192,9 @@ function focusZone(container, settings) {
185
192
  activeDescendantControl === null || activeDescendantControl === void 0 ? void 0 : activeDescendantControl.removeAttribute('aria-activedescendant');
186
193
  container.removeAttribute(hasActiveDescendantAttribute);
187
194
  previouslyActiveElement === null || previouslyActiveElement === void 0 ? void 0 : previouslyActiveElement.removeAttribute(isActiveDescendantAttribute);
188
- for (const item of container.querySelectorAll(`[${isActiveDescendantAttribute}]`)) {
189
- item === null || item === void 0 ? void 0 : item.removeAttribute(isActiveDescendantAttribute);
195
+ const items = container.querySelectorAll(`[${isActiveDescendantAttribute}]`);
196
+ for (let i = 0; i < items.length; i++) {
197
+ items[i].removeAttribute(isActiveDescendantAttribute);
190
198
  }
191
199
  activeDescendantCallback === null || activeDescendantCallback === void 0 ? void 0 : activeDescendantCallback(undefined, previouslyActiveElement, false);
192
200
  }
@@ -259,30 +267,50 @@ function focusZone(container, settings) {
259
267
  if (!preventInitialFocus)
260
268
  updateFocusedElement(initialElement);
261
269
  const observer = new MutationObserver(mutations => {
270
+ const elementsToRemove = [];
271
+ const elementsToAdd = [];
272
+ const attributeRemovals = [];
273
+ const attributeAdditions = [];
262
274
  for (const mutation of mutations) {
263
- for (const removedNode of mutation.removedNodes) {
264
- if (removedNode instanceof HTMLElement) {
265
- endFocusManagement(...iterateFocusableElements(removedNode));
275
+ if (mutation.type === 'childList') {
276
+ for (const removedNode of mutation.removedNodes) {
277
+ if (removedNode instanceof HTMLElement) {
278
+ elementsToRemove.push(removedNode);
279
+ }
280
+ }
281
+ for (const addedNode of mutation.addedNodes) {
282
+ if (addedNode instanceof HTMLElement) {
283
+ elementsToAdd.push(addedNode);
284
+ }
266
285
  }
267
286
  }
268
- if (mutation.type === 'attributes' && mutation.oldValue === null) {
269
- if (mutation.target instanceof HTMLElement) {
270
- endFocusManagement(mutation.target);
287
+ else if (mutation.type === 'attributes' && mutation.target instanceof HTMLElement) {
288
+ if (mutation.oldValue === null) {
289
+ attributeRemovals.push(mutation.target);
290
+ }
291
+ else {
292
+ attributeAdditions.push(mutation.target);
271
293
  }
272
294
  }
273
295
  }
274
- for (const mutation of mutations) {
275
- for (const addedNode of mutation.addedNodes) {
276
- if (addedNode instanceof HTMLElement) {
277
- beginFocusManagement(...iterateFocusableElements(addedNode, iterateFocusableElementsOptions));
278
- }
296
+ if (elementsToRemove.length > 0) {
297
+ const toRemove = elementsToRemove.flatMap(node => [...iterateFocusableElements(node)]);
298
+ if (toRemove.length > 0) {
299
+ endFocusManagement(...toRemove);
279
300
  }
280
- if (mutation.type === 'attributes' && mutation.oldValue !== null) {
281
- if (mutation.target instanceof HTMLElement) {
282
- beginFocusManagement(mutation.target);
283
- }
301
+ }
302
+ if (attributeRemovals.length > 0) {
303
+ endFocusManagement(...attributeRemovals);
304
+ }
305
+ if (elementsToAdd.length > 0) {
306
+ const toAdd = elementsToAdd.flatMap(node => [...iterateFocusableElements(node, iterateFocusableElementsOptions)]);
307
+ if (toAdd.length > 0) {
308
+ beginFocusManagement(...toAdd);
284
309
  }
285
310
  }
311
+ if (attributeAdditions.length > 0) {
312
+ beginFocusManagement(...attributeAdditions);
313
+ }
286
314
  });
287
315
  observer.observe(container, {
288
316
  subtree: true,
@@ -317,7 +345,7 @@ function focusZone(container, settings) {
317
345
  if (focusableElement) {
318
346
  updateFocusedElement(focusableElement);
319
347
  }
320
- }, { signal, capture: true });
348
+ }, { signal, capture: true, passive: true });
321
349
  }
322
350
  activeDescendantControl.addEventListener('focusin', () => {
323
351
  if (!currentFocusedElement) {
@@ -31,24 +31,31 @@ 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']);
34
35
  function isFocusable(elem, strict = false) {
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) {
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)
41
43
  return false;
42
- }
43
44
  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
+ if (!offsetParent)
51
+ return false;
44
52
  const style = getComputedStyle(elem);
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) {
53
+ if (style.display === 'none')
54
+ return false;
55
+ if (style.visibility === 'hidden' || style.visibility === 'collapse')
56
+ return false;
57
+ if (elem.getClientRects().length === 0)
50
58
  return false;
51
- }
52
59
  }
53
60
  if (elem.getAttribute('tabindex') != null) {
54
61
  return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primer/behaviors",
3
- "version": "0.0.0-20251211200451",
3
+ "version": "0.0.0-20251214172224",
4
4
  "description": "Shared behaviors for JavaScript components",
5
5
  "type": "commonjs",
6
6
  "main": "dist/cjs/index.js",