@primer/behaviors 0.0.0-20240222105352 → 0.0.0-20240229000302

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.
@@ -37,21 +37,27 @@ function getPositionedParent(element) {
37
37
  }
38
38
  return document.body;
39
39
  }
40
- function isOnTopLayer(element) {
40
+ function isModalDialog(element) {
41
+ return element.matches('dialog:modal');
42
+ }
43
+ function isFullscreen(element) {
44
+ return element.matches(':fullscreen');
45
+ }
46
+ function isPopover(element) {
41
47
  var _a;
42
- if (element.tagName === 'DIALOG') {
43
- return element.matches(':modal');
44
- }
45
48
  try {
46
- if (element.matches(':popover-open') && /native code/.test((_a = document.body.showPopover) === null || _a === void 0 ? void 0 : _a.toString())) {
47
- return true;
48
- }
49
+ return (element.matches(':popover-open') && /native code/.test((_a = document.body.showPopover) === null || _a === void 0 ? void 0 : _a.toString()));
49
50
  }
50
51
  catch (_b) {
52
+ return false;
51
53
  }
52
- return element.matches(':fullscreen');
53
54
  }
54
- function getClippingRect(element) {
55
+ function isOnTopLayer(element) {
56
+ return isModalDialog(element) || isFullscreen(element) || isPopover(element);
57
+ }
58
+ function getClippingParent(element) {
59
+ if (element === document.body)
60
+ return element;
55
61
  let parentNode = element;
56
62
  while (parentNode !== null) {
57
63
  if (!(parentNode instanceof Element)) {
@@ -63,7 +69,10 @@ function getClippingRect(element) {
63
69
  }
64
70
  parentNode = parentNode.parentNode;
65
71
  }
66
- const clippingNode = parentNode === document.body || !(parentNode instanceof HTMLElement) ? document.body : parentNode;
72
+ return parentNode === document.body || !(parentNode instanceof HTMLElement) ? document.body : parentNode;
73
+ }
74
+ function getClippingRect(element) {
75
+ const clippingNode = getClippingParent(element);
67
76
  const elemRect = clippingNode.getBoundingClientRect();
68
77
  const elemStyle = getComputedStyle(clippingNode);
69
78
  const [borderTop, borderLeft, borderRight, borderBottom] = [
@@ -150,7 +159,7 @@ function pureCalculateAnchoredPosition(viewportRect, relativePosition, floatingR
150
159
  }
151
160
  if (alternateOrder && positionAttempt < alternateOrder.length) {
152
161
  if (pos.top + floatingRect.height > viewportRect.height + relativeViewportRect.top) {
153
- pos.top = Math.max(viewportRect.height + relativeViewportRect.top - floatingRect.height, 0);
162
+ pos.top = viewportRect.height + relativeViewportRect.top - floatingRect.height;
154
163
  }
155
164
  }
156
165
  }
@@ -1,4 +1,3 @@
1
- import { IterateFocusableElements } from './utils/iterate-focusable-elements.js';
2
1
  export type Direction = 'previous' | 'next' | 'start' | 'end';
3
2
  export type FocusMovementKeys = 'ArrowLeft' | 'ArrowDown' | 'ArrowUp' | 'ArrowRight' | 'h' | 'j' | 'k' | 'l' | 'a' | 's' | 'w' | 'd' | 'Tab' | 'Home' | 'End' | 'PageUp' | 'PageDown' | 'Backspace';
4
3
  export declare enum FocusKeys {
@@ -17,7 +16,7 @@ export declare enum FocusKeys {
17
16
  WASD = 96,
18
17
  All = 511
19
18
  }
20
- export type FocusZoneSettings = IterateFocusableElements & {
19
+ export interface FocusZoneSettings {
21
20
  focusOutBehavior?: 'stop' | 'wrap';
22
21
  getNextFocusable?: (direction: Direction, from: Element | undefined, event: KeyboardEvent) => HTMLElement | undefined;
23
22
  focusableElementFilter?: (element: HTMLElement) => boolean;
@@ -27,7 +26,7 @@ export type FocusZoneSettings = IterateFocusableElements & {
27
26
  onActiveDescendantChanged?: (newActiveDescendant: HTMLElement | undefined, previousActiveDescendant: HTMLElement | undefined, directlyActivated: boolean) => void;
28
27
  focusInStrategy?: 'first' | 'closest' | 'previous' | ((previousFocusedElement: Element) => HTMLElement | undefined);
29
28
  preventScroll?: boolean;
30
- };
29
+ }
31
30
  export declare const isActiveDescendantAttribute = "data-is-active-descendant";
32
31
  export declare const activeDescendantActivatedDirectly = "activated-directly";
33
32
  export declare const activeDescendantActivatedIndirectly = "activated-indirectly";
@@ -244,26 +244,21 @@ function focusZone(container, settings) {
244
244
  }
245
245
  }
246
246
  }
247
- const iterateFocusableElementsOptions = {
248
- reverse: settings === null || settings === void 0 ? void 0 : settings.reverse,
249
- strict: settings === null || settings === void 0 ? void 0 : settings.strict,
250
- onlyTabbable: settings === null || settings === void 0 ? void 0 : settings.onlyTabbable,
251
- };
252
- beginFocusManagement(...(0, iterate_focusable_elements_js_1.iterateFocusableElements)(container, iterateFocusableElementsOptions));
247
+ beginFocusManagement(...(0, iterate_focusable_elements_js_1.iterateFocusableElements)(container));
253
248
  const initialElement = typeof focusInStrategy === 'function' ? focusInStrategy(document.body) : getFirstFocusableElement();
254
249
  updateFocusedElement(initialElement);
255
250
  const observer = new MutationObserver(mutations => {
256
251
  for (const mutation of mutations) {
257
252
  for (const removedNode of mutation.removedNodes) {
258
253
  if (removedNode instanceof HTMLElement) {
259
- endFocusManagement(...(0, iterate_focusable_elements_js_1.iterateFocusableElements)(removedNode, iterateFocusableElementsOptions));
254
+ endFocusManagement(...(0, iterate_focusable_elements_js_1.iterateFocusableElements)(removedNode));
260
255
  }
261
256
  }
262
257
  }
263
258
  for (const mutation of mutations) {
264
259
  for (const addedNode of mutation.addedNodes) {
265
260
  if (addedNode instanceof HTMLElement) {
266
- beginFocusManagement(...(0, iterate_focusable_elements_js_1.iterateFocusableElements)(addedNode, iterateFocusableElementsOptions));
261
+ beginFocusManagement(...(0, iterate_focusable_elements_js_1.iterateFocusableElements)(addedNode));
267
262
  }
268
263
  }
269
264
  }
@@ -46,21 +46,16 @@ function isFocusable(elem, strict = false) {
46
46
  return false;
47
47
  }
48
48
  if (strict) {
49
- const style = getComputedStyle(elem);
50
49
  const sizeInert = elem.offsetWidth === 0 || elem.offsetHeight === 0;
51
- const visibilityInert = ['hidden', 'collapse'].includes(style.visibility);
52
- const displayInert = style.display === 'none' || !elem.offsetParent;
50
+ const visibilityInert = ['hidden', 'collapse'].includes(getComputedStyle(elem).visibility);
53
51
  const clientRectsInert = elem.getClientRects().length === 0;
54
- if (sizeInert || visibilityInert || clientRectsInert || displayInert) {
52
+ if (sizeInert || visibilityInert || clientRectsInert) {
55
53
  return false;
56
54
  }
57
55
  }
58
56
  if (elem.getAttribute('tabindex') != null) {
59
57
  return true;
60
58
  }
61
- if (elem.getAttribute('contenteditable') === 'true' || elem.getAttribute('contenteditable') === 'plaintext-only') {
62
- return true;
63
- }
64
59
  if (elem instanceof HTMLAnchorElement && elem.getAttribute('href') == null) {
65
60
  return false;
66
61
  }
@@ -33,21 +33,27 @@ function getPositionedParent(element) {
33
33
  }
34
34
  return document.body;
35
35
  }
36
- function isOnTopLayer(element) {
36
+ function isModalDialog(element) {
37
+ return element.matches('dialog:modal');
38
+ }
39
+ function isFullscreen(element) {
40
+ return element.matches(':fullscreen');
41
+ }
42
+ function isPopover(element) {
37
43
  var _a;
38
- if (element.tagName === 'DIALOG') {
39
- return element.matches(':modal');
40
- }
41
44
  try {
42
- if (element.matches(':popover-open') && /native code/.test((_a = document.body.showPopover) === null || _a === void 0 ? void 0 : _a.toString())) {
43
- return true;
44
- }
45
+ return (element.matches(':popover-open') && /native code/.test((_a = document.body.showPopover) === null || _a === void 0 ? void 0 : _a.toString()));
45
46
  }
46
47
  catch (_b) {
48
+ return false;
47
49
  }
48
- return element.matches(':fullscreen');
49
50
  }
50
- function getClippingRect(element) {
51
+ function isOnTopLayer(element) {
52
+ return isModalDialog(element) || isFullscreen(element) || isPopover(element);
53
+ }
54
+ function getClippingParent(element) {
55
+ if (element === document.body)
56
+ return element;
51
57
  let parentNode = element;
52
58
  while (parentNode !== null) {
53
59
  if (!(parentNode instanceof Element)) {
@@ -59,7 +65,10 @@ function getClippingRect(element) {
59
65
  }
60
66
  parentNode = parentNode.parentNode;
61
67
  }
62
- const clippingNode = parentNode === document.body || !(parentNode instanceof HTMLElement) ? document.body : parentNode;
68
+ return parentNode === document.body || !(parentNode instanceof HTMLElement) ? document.body : parentNode;
69
+ }
70
+ function getClippingRect(element) {
71
+ const clippingNode = getClippingParent(element);
63
72
  const elemRect = clippingNode.getBoundingClientRect();
64
73
  const elemStyle = getComputedStyle(clippingNode);
65
74
  const [borderTop, borderLeft, borderRight, borderBottom] = [
@@ -146,7 +155,7 @@ function pureCalculateAnchoredPosition(viewportRect, relativePosition, floatingR
146
155
  }
147
156
  if (alternateOrder && positionAttempt < alternateOrder.length) {
148
157
  if (pos.top + floatingRect.height > viewportRect.height + relativeViewportRect.top) {
149
- pos.top = Math.max(viewportRect.height + relativeViewportRect.top - floatingRect.height, 0);
158
+ pos.top = viewportRect.height + relativeViewportRect.top - floatingRect.height;
150
159
  }
151
160
  }
152
161
  }
@@ -1,4 +1,3 @@
1
- import { IterateFocusableElements } from './utils/iterate-focusable-elements.js';
2
1
  export type Direction = 'previous' | 'next' | 'start' | 'end';
3
2
  export type FocusMovementKeys = 'ArrowLeft' | 'ArrowDown' | 'ArrowUp' | 'ArrowRight' | 'h' | 'j' | 'k' | 'l' | 'a' | 's' | 'w' | 'd' | 'Tab' | 'Home' | 'End' | 'PageUp' | 'PageDown' | 'Backspace';
4
3
  export declare enum FocusKeys {
@@ -17,7 +16,7 @@ export declare enum FocusKeys {
17
16
  WASD = 96,
18
17
  All = 511
19
18
  }
20
- export type FocusZoneSettings = IterateFocusableElements & {
19
+ export interface FocusZoneSettings {
21
20
  focusOutBehavior?: 'stop' | 'wrap';
22
21
  getNextFocusable?: (direction: Direction, from: Element | undefined, event: KeyboardEvent) => HTMLElement | undefined;
23
22
  focusableElementFilter?: (element: HTMLElement) => boolean;
@@ -27,7 +26,7 @@ export type FocusZoneSettings = IterateFocusableElements & {
27
26
  onActiveDescendantChanged?: (newActiveDescendant: HTMLElement | undefined, previousActiveDescendant: HTMLElement | undefined, directlyActivated: boolean) => void;
28
27
  focusInStrategy?: 'first' | 'closest' | 'previous' | ((previousFocusedElement: Element) => HTMLElement | undefined);
29
28
  preventScroll?: boolean;
30
- };
29
+ }
31
30
  export declare const isActiveDescendantAttribute = "data-is-active-descendant";
32
31
  export declare const activeDescendantActivatedDirectly = "activated-directly";
33
32
  export declare const activeDescendantActivatedIndirectly = "activated-indirectly";
@@ -241,26 +241,21 @@ export function focusZone(container, settings) {
241
241
  }
242
242
  }
243
243
  }
244
- const iterateFocusableElementsOptions = {
245
- reverse: settings === null || settings === void 0 ? void 0 : settings.reverse,
246
- strict: settings === null || settings === void 0 ? void 0 : settings.strict,
247
- onlyTabbable: settings === null || settings === void 0 ? void 0 : settings.onlyTabbable,
248
- };
249
- beginFocusManagement(...iterateFocusableElements(container, iterateFocusableElementsOptions));
244
+ beginFocusManagement(...iterateFocusableElements(container));
250
245
  const initialElement = typeof focusInStrategy === 'function' ? focusInStrategy(document.body) : getFirstFocusableElement();
251
246
  updateFocusedElement(initialElement);
252
247
  const observer = new MutationObserver(mutations => {
253
248
  for (const mutation of mutations) {
254
249
  for (const removedNode of mutation.removedNodes) {
255
250
  if (removedNode instanceof HTMLElement) {
256
- endFocusManagement(...iterateFocusableElements(removedNode, iterateFocusableElementsOptions));
251
+ endFocusManagement(...iterateFocusableElements(removedNode));
257
252
  }
258
253
  }
259
254
  }
260
255
  for (const mutation of mutations) {
261
256
  for (const addedNode of mutation.addedNodes) {
262
257
  if (addedNode instanceof HTMLElement) {
263
- beginFocusManagement(...iterateFocusableElements(addedNode, iterateFocusableElementsOptions));
258
+ beginFocusManagement(...iterateFocusableElements(addedNode));
264
259
  }
265
260
  }
266
261
  }
@@ -41,21 +41,16 @@ export function isFocusable(elem, strict = false) {
41
41
  return false;
42
42
  }
43
43
  if (strict) {
44
- const style = getComputedStyle(elem);
45
44
  const sizeInert = elem.offsetWidth === 0 || elem.offsetHeight === 0;
46
- const visibilityInert = ['hidden', 'collapse'].includes(style.visibility);
47
- const displayInert = style.display === 'none' || !elem.offsetParent;
45
+ const visibilityInert = ['hidden', 'collapse'].includes(getComputedStyle(elem).visibility);
48
46
  const clientRectsInert = elem.getClientRects().length === 0;
49
- if (sizeInert || visibilityInert || clientRectsInert || displayInert) {
47
+ if (sizeInert || visibilityInert || clientRectsInert) {
50
48
  return false;
51
49
  }
52
50
  }
53
51
  if (elem.getAttribute('tabindex') != null) {
54
52
  return true;
55
53
  }
56
- if (elem.getAttribute('contenteditable') === 'true' || elem.getAttribute('contenteditable') === 'plaintext-only') {
57
- return true;
58
- }
59
54
  if (elem instanceof HTMLAnchorElement && elem.getAttribute('href') == null) {
60
55
  return false;
61
56
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primer/behaviors",
3
- "version": "0.0.0-20240222105352",
3
+ "version": "0.0.0-20240229000302",
4
4
  "description": "Shared behaviors for JavaScript components",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -66,22 +66,21 @@
66
66
  }
67
67
  ],
68
68
  "devDependencies": {
69
- "@changesets/changelog-github": "^0.5.0",
69
+ "@changesets/changelog-github": "^0.4.2",
70
70
  "@changesets/cli": "^2.18.1",
71
71
  "@github/prettier-config": "^0.0.6",
72
72
  "@size-limit/preset-small-lib": "^8.2.4",
73
73
  "@testing-library/react": "^14.0.0",
74
74
  "@testing-library/user-event": "^14.5.1",
75
- "@types/jest": "^29.5.11",
76
- "@types/node": "^20.10.5",
75
+ "@types/jest": "^27.0.3",
76
+ "@types/node": "^18.18.0",
77
77
  "@types/react": "^18.2.23",
78
- "esbuild": "^0.20.0",
78
+ "esbuild": "^0.19.4",
79
79
  "esbuild-jest": "^0.5.0",
80
80
  "eslint": "^8.50.0",
81
81
  "eslint-plugin-github": "^4.10.0",
82
82
  "eslint-plugin-prettier": "^5.0.0",
83
- "jest": "^29.7.0",
84
- "jest-environment-jsdom": "^29.7.0",
83
+ "jest": "^27.4.3",
85
84
  "prettier": "^3.0.3",
86
85
  "react": "^18.2.0",
87
86
  "react-dom": "^18.2.0",