@primer/behaviors 0.0.0-2023818155639 → 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.
@@ -1,5 +1,5 @@
1
- export declare type AnchorAlignment = 'start' | 'center' | 'end';
2
- export declare type AnchorSide = 'inside-top' | 'inside-bottom' | 'inside-left' | 'inside-right' | 'inside-center' | 'outside-top' | 'outside-bottom' | 'outside-left' | 'outside-right';
1
+ export type AnchorAlignment = 'start' | 'center' | 'end';
2
+ export type AnchorSide = 'inside-top' | 'inside-bottom' | 'inside-left' | 'inside-right' | 'inside-center' | 'outside-top' | 'outside-bottom' | 'outside-left' | 'outside-right';
3
3
  export interface PositionSettings {
4
4
  side: AnchorSide;
5
5
  align: AnchorAlignment;
@@ -5,12 +5,12 @@ const alternateOrders = {
5
5
  'outside-top': ['outside-bottom', 'outside-right', 'outside-left', 'outside-bottom'],
6
6
  'outside-bottom': ['outside-top', 'outside-right', 'outside-left', 'outside-bottom'],
7
7
  'outside-left': ['outside-right', 'outside-bottom', 'outside-top', 'outside-bottom'],
8
- 'outside-right': ['outside-left', 'outside-bottom', 'outside-top', 'outside-bottom']
8
+ 'outside-right': ['outside-left', 'outside-bottom', 'outside-top', 'outside-bottom'],
9
9
  };
10
10
  const alternateAlignments = {
11
11
  start: ['end', 'center'],
12
12
  end: ['start', 'center'],
13
- center: ['end', 'start']
13
+ center: ['end', 'start'],
14
14
  };
15
15
  function getAnchoredPosition(floatingElement, anchorElement, settings = {}) {
16
16
  const parentElement = getPositionedParent(floatingElement);
@@ -20,12 +20,14 @@ function getAnchoredPosition(floatingElement, anchorElement, settings = {}) {
20
20
  const [borderTop, borderLeft] = [parentElementStyle.borderTopWidth, parentElementStyle.borderLeftWidth].map(v => parseInt(v, 10) || 0);
21
21
  const relativeRect = {
22
22
  top: parentElementRect.top + borderTop,
23
- left: parentElementRect.left + borderLeft
23
+ left: parentElementRect.left + borderLeft,
24
24
  };
25
25
  return pureCalculateAnchoredPosition(clippingRect, relativeRect, floatingElement.getBoundingClientRect(), anchorElement instanceof Element ? anchorElement.getBoundingClientRect() : anchorElement, getDefaultSettings(settings));
26
26
  }
27
27
  exports.getAnchoredPosition = getAnchoredPosition;
28
28
  function getPositionedParent(element) {
29
+ if (isOnTopLayer(element))
30
+ return document.body;
29
31
  let parentNode = element.parentNode;
30
32
  while (parentNode !== null) {
31
33
  if (parentNode instanceof HTMLElement && getComputedStyle(parentNode).position !== 'static') {
@@ -35,10 +37,30 @@ function getPositionedParent(element) {
35
37
  }
36
38
  return document.body;
37
39
  }
38
- function getClippingRect(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) {
47
+ var _a;
48
+ try {
49
+ return (element.matches(':popover-open') && /native code/.test((_a = document.body.showPopover) === null || _a === void 0 ? void 0 : _a.toString()));
50
+ }
51
+ catch (_b) {
52
+ return false;
53
+ }
54
+ }
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;
39
61
  let parentNode = element;
40
62
  while (parentNode !== null) {
41
- if (parentNode === document.body) {
63
+ if (!(parentNode instanceof Element)) {
42
64
  break;
43
65
  }
44
66
  const parentNodeStyle = getComputedStyle(parentNode);
@@ -47,20 +69,23 @@ function getClippingRect(element) {
47
69
  }
48
70
  parentNode = parentNode.parentNode;
49
71
  }
50
- 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);
51
76
  const elemRect = clippingNode.getBoundingClientRect();
52
77
  const elemStyle = getComputedStyle(clippingNode);
53
78
  const [borderTop, borderLeft, borderRight, borderBottom] = [
54
79
  elemStyle.borderTopWidth,
55
80
  elemStyle.borderLeftWidth,
56
81
  elemStyle.borderRightWidth,
57
- elemStyle.borderBottomWidth
82
+ elemStyle.borderBottomWidth,
58
83
  ].map(v => parseInt(v, 10) || 0);
59
84
  return {
60
85
  top: elemRect.top + borderTop,
61
86
  left: elemRect.left + borderLeft,
62
87
  width: elemRect.width - borderRight - borderLeft,
63
- height: Math.max(elemRect.height - borderTop - borderBottom, clippingNode === document.body ? window.innerHeight : -Infinity)
88
+ height: Math.max(elemRect.height - borderTop - borderBottom, clippingNode === document.body ? window.innerHeight : -Infinity),
64
89
  };
65
90
  }
66
91
  const positionDefaults = {
@@ -68,7 +93,7 @@ const positionDefaults = {
68
93
  align: 'start',
69
94
  anchorOffset: 4,
70
95
  alignmentOffset: 4,
71
- allowOutOfBounds: false
96
+ allowOutOfBounds: false,
72
97
  };
73
98
  function getDefaultSettings(settings = {}) {
74
99
  var _a, _b, _c, _d, _e;
@@ -79,7 +104,7 @@ function getDefaultSettings(settings = {}) {
79
104
  align,
80
105
  anchorOffset: (_c = settings.anchorOffset) !== null && _c !== void 0 ? _c : (side === 'inside-center' ? 0 : positionDefaults.anchorOffset),
81
106
  alignmentOffset: (_d = settings.alignmentOffset) !== null && _d !== void 0 ? _d : (align !== 'center' && side.startsWith('inside') ? positionDefaults.alignmentOffset : 0),
82
- allowOutOfBounds: (_e = settings.allowOutOfBounds) !== null && _e !== void 0 ? _e : positionDefaults.allowOutOfBounds
107
+ allowOutOfBounds: (_e = settings.allowOutOfBounds) !== null && _e !== void 0 ? _e : positionDefaults.allowOutOfBounds,
83
108
  };
84
109
  }
85
110
  function pureCalculateAnchoredPosition(viewportRect, relativePosition, floatingRect, anchorRect, { side, align, allowOutOfBounds, anchorOffset, alignmentOffset }) {
@@ -87,7 +112,7 @@ function pureCalculateAnchoredPosition(viewportRect, relativePosition, floatingR
87
112
  top: viewportRect.top - relativePosition.top,
88
113
  left: viewportRect.left - relativePosition.left,
89
114
  width: viewportRect.width,
90
- height: viewportRect.height
115
+ height: viewportRect.height,
91
116
  };
92
117
  let pos = calculatePosition(floatingRect, anchorRect, side, align, anchorOffset, alignmentOffset);
93
118
  let anchorSide = side;
@@ -1,4 +1,4 @@
1
- declare type Dimensions = {
1
+ type Dimensions = {
2
2
  top: number;
3
3
  left: number;
4
4
  bottom: number;
@@ -6,7 +6,7 @@ declare type Dimensions = {
6
6
  height?: number;
7
7
  width?: number;
8
8
  };
9
- declare type Offset = {
9
+ type Offset = {
10
10
  top: number;
11
11
  left: number;
12
12
  };
@@ -5,7 +5,7 @@ function offset(element) {
5
5
  const rect = element.getBoundingClientRect();
6
6
  return {
7
7
  top: rect.top + window.pageYOffset,
8
- left: rect.left + window.pageXOffset
8
+ left: rect.left + window.pageXOffset,
9
9
  };
10
10
  }
11
11
  exports.offset = offset;
@@ -62,11 +62,11 @@ function overflowOffset(element, targetContainer) {
62
62
  const scroll = container === document.documentElement && document.defaultView
63
63
  ? {
64
64
  top: document.defaultView.pageYOffset,
65
- left: document.defaultView.pageXOffset
65
+ left: document.defaultView.pageXOffset,
66
66
  }
67
67
  : {
68
68
  top: container.scrollTop,
69
- left: container.scrollLeft
69
+ left: container.scrollLeft,
70
70
  };
71
71
  const top = elementOffset.top - scroll.top;
72
72
  const left = elementOffset.left - scroll.left;
@@ -94,7 +94,7 @@ function focusTrap(container, initialFocus, abortSignal) {
94
94
  container,
95
95
  controller: wrappingController,
96
96
  initialFocus,
97
- originalSignal: signal
97
+ originalSignal: signal,
98
98
  };
99
99
  const suspendedTrapIndex = suspendedTrapStack.findIndex(t => t.container === container);
100
100
  if (suspendedTrapIndex >= 0) {
@@ -1,5 +1,5 @@
1
- export declare type Direction = 'previous' | 'next' | 'start' | 'end';
2
- export declare type FocusMovementKeys = 'ArrowLeft' | 'ArrowDown' | 'ArrowUp' | 'ArrowRight' | 'h' | 'j' | 'k' | 'l' | 'a' | 's' | 'w' | 'd' | 'Tab' | 'Home' | 'End' | 'PageUp' | 'PageDown';
1
+ export type Direction = 'previous' | 'next' | 'start' | 'end';
2
+ export type FocusMovementKeys = 'ArrowLeft' | 'ArrowDown' | 'ArrowUp' | 'ArrowRight' | 'h' | 'j' | 'k' | 'l' | 'a' | 's' | 'w' | 'd' | 'Tab' | 'Home' | 'End' | 'PageUp' | 'PageDown' | 'Backspace';
3
3
  export declare enum FocusKeys {
4
4
  ArrowHorizontal = 1,
5
5
  ArrowVertical = 2,
@@ -10,6 +10,7 @@ export declare enum FocusKeys {
10
10
  WS = 32,
11
11
  AD = 64,
12
12
  Tab = 128,
13
+ Backspace = 512,
13
14
  ArrowAll = 3,
14
15
  HJKL = 12,
15
16
  WASD = 96,
@@ -17,11 +17,12 @@ var FocusKeys;
17
17
  FocusKeys[FocusKeys["WS"] = 32] = "WS";
18
18
  FocusKeys[FocusKeys["AD"] = 64] = "AD";
19
19
  FocusKeys[FocusKeys["Tab"] = 128] = "Tab";
20
+ FocusKeys[FocusKeys["Backspace"] = 512] = "Backspace";
20
21
  FocusKeys[FocusKeys["ArrowAll"] = 3] = "ArrowAll";
21
22
  FocusKeys[FocusKeys["HJKL"] = 12] = "HJKL";
22
23
  FocusKeys[FocusKeys["WASD"] = 96] = "WASD";
23
24
  FocusKeys[FocusKeys["All"] = 511] = "All";
24
- })(FocusKeys = exports.FocusKeys || (exports.FocusKeys = {}));
25
+ })(FocusKeys || (exports.FocusKeys = FocusKeys = {}));
25
26
  const KEY_TO_BIT = {
26
27
  ArrowLeft: FocusKeys.ArrowHorizontal,
27
28
  ArrowDown: FocusKeys.ArrowVertical,
@@ -39,7 +40,8 @@ const KEY_TO_BIT = {
39
40
  Home: FocusKeys.HomeAndEnd,
40
41
  End: FocusKeys.HomeAndEnd,
41
42
  PageUp: FocusKeys.PageUpDown,
42
- PageDown: FocusKeys.PageUpDown
43
+ PageDown: FocusKeys.PageUpDown,
44
+ Backspace: FocusKeys.Backspace,
43
45
  };
44
46
  const KEY_TO_DIRECTION = {
45
47
  ArrowLeft: 'previous',
@@ -58,7 +60,8 @@ const KEY_TO_DIRECTION = {
58
60
  Home: 'start',
59
61
  End: 'end',
60
62
  PageUp: 'start',
61
- PageDown: 'end'
63
+ PageDown: 'end',
64
+ Backspace: 'previous',
62
65
  };
63
66
  function getDirection(keyboardEvent) {
64
67
  const direction = KEY_TO_DIRECTION[keyboardEvent.key];
@@ -187,8 +190,7 @@ function focusZone(container, settings) {
187
190
  if (filteredElements.length === 0) {
188
191
  return;
189
192
  }
190
- const insertIndex = focusableElements.findIndex(e => (e.compareDocumentPosition(filteredElements[0]) & Node.DOCUMENT_POSITION_PRECEDING) > 0);
191
- focusableElements.splice(insertIndex === -1 ? focusableElements.length : insertIndex, 0, ...filteredElements);
193
+ focusableElements.splice(findInsertionIndex(filteredElements), 0, ...filteredElements);
192
194
  for (const element of filteredElements) {
193
195
  if (!savedTabIndex.has(element)) {
194
196
  savedTabIndex.set(element, element.getAttribute('tabindex'));
@@ -199,6 +201,27 @@ function focusZone(container, settings) {
199
201
  updateFocusedElement(getFirstFocusableElement());
200
202
  }
201
203
  }
204
+ function findInsertionIndex(elementsToInsert) {
205
+ const firstElementToInsert = elementsToInsert[0];
206
+ if (focusableElements.length === 0)
207
+ return 0;
208
+ let iMin = 0;
209
+ let iMax = focusableElements.length - 1;
210
+ while (iMin <= iMax) {
211
+ const i = Math.floor((iMin + iMax) / 2);
212
+ const element = focusableElements[i];
213
+ if (followsInDocument(firstElementToInsert, element)) {
214
+ iMax = i - 1;
215
+ }
216
+ else {
217
+ iMin = i + 1;
218
+ }
219
+ }
220
+ return iMin;
221
+ }
222
+ function followsInDocument(first, second) {
223
+ return (second.compareDocumentPosition(first) & Node.DOCUMENT_POSITION_PRECEDING) > 0;
224
+ }
202
225
  function endFocusManagement(...elements) {
203
226
  for (const element of elements) {
204
227
  const focusableElementIndex = focusableElements.indexOf(element);
@@ -222,7 +245,8 @@ function focusZone(container, settings) {
222
245
  }
223
246
  }
224
247
  beginFocusManagement(...(0, iterate_focusable_elements_js_1.iterateFocusableElements)(container));
225
- updateFocusedElement(getFirstFocusableElement());
248
+ const initialElement = typeof focusInStrategy === 'function' ? focusInStrategy(document.body) : getFirstFocusableElement();
249
+ updateFocusedElement(initialElement);
226
250
  const observer = new MutationObserver(mutations => {
227
251
  for (const mutation of mutations) {
228
252
  for (const removedNode of mutation.removedNodes) {
@@ -241,7 +265,7 @@ function focusZone(container, settings) {
241
265
  });
242
266
  observer.observe(container, {
243
267
  subtree: true,
244
- childList: true
268
+ childList: true,
245
269
  });
246
270
  const controller = new AbortController();
247
271
  const signal = (_e = settings === null || settings === void 0 ? void 0 : settings.abortSignal) !== null && _e !== void 0 ? _e : controller.signal;
package/dist/cjs/index.js CHANGED
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -8,8 +8,8 @@ try {
8
8
  signal: {
9
9
  get() {
10
10
  signalSupported = true;
11
- }
12
- }
11
+ },
12
+ },
13
13
  });
14
14
  window.addEventListener('test', noop, options);
15
15
  window.removeEventListener('test', noop, options);
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -6,7 +6,7 @@ function* iterateFocusableElements(container, options = {}) {
6
6
  const strict = (_a = options.strict) !== null && _a !== void 0 ? _a : false;
7
7
  const acceptFn = ((_b = options.onlyTabbable) !== null && _b !== void 0 ? _b : false) ? isTabbable : isFocusable;
8
8
  const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {
9
- acceptNode: node => node instanceof HTMLElement && acceptFn(node, strict) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
9
+ acceptNode: node => node instanceof HTMLElement && acceptFn(node, strict) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP,
10
10
  });
11
11
  let nextNode = null;
12
12
  if (!options.reverse && acceptFn(container, strict)) {
@@ -1,5 +1,5 @@
1
- export declare type AnchorAlignment = 'start' | 'center' | 'end';
2
- export declare type AnchorSide = 'inside-top' | 'inside-bottom' | 'inside-left' | 'inside-right' | 'inside-center' | 'outside-top' | 'outside-bottom' | 'outside-left' | 'outside-right';
1
+ export type AnchorAlignment = 'start' | 'center' | 'end';
2
+ export type AnchorSide = 'inside-top' | 'inside-bottom' | 'inside-left' | 'inside-right' | 'inside-center' | 'outside-top' | 'outside-bottom' | 'outside-left' | 'outside-right';
3
3
  export interface PositionSettings {
4
4
  side: AnchorSide;
5
5
  align: AnchorAlignment;
@@ -2,12 +2,12 @@ const alternateOrders = {
2
2
  'outside-top': ['outside-bottom', 'outside-right', 'outside-left', 'outside-bottom'],
3
3
  'outside-bottom': ['outside-top', 'outside-right', 'outside-left', 'outside-bottom'],
4
4
  'outside-left': ['outside-right', 'outside-bottom', 'outside-top', 'outside-bottom'],
5
- 'outside-right': ['outside-left', 'outside-bottom', 'outside-top', 'outside-bottom']
5
+ 'outside-right': ['outside-left', 'outside-bottom', 'outside-top', 'outside-bottom'],
6
6
  };
7
7
  const alternateAlignments = {
8
8
  start: ['end', 'center'],
9
9
  end: ['start', 'center'],
10
- center: ['end', 'start']
10
+ center: ['end', 'start'],
11
11
  };
12
12
  export function getAnchoredPosition(floatingElement, anchorElement, settings = {}) {
13
13
  const parentElement = getPositionedParent(floatingElement);
@@ -17,11 +17,13 @@ export function getAnchoredPosition(floatingElement, anchorElement, settings = {
17
17
  const [borderTop, borderLeft] = [parentElementStyle.borderTopWidth, parentElementStyle.borderLeftWidth].map(v => parseInt(v, 10) || 0);
18
18
  const relativeRect = {
19
19
  top: parentElementRect.top + borderTop,
20
- left: parentElementRect.left + borderLeft
20
+ left: parentElementRect.left + borderLeft,
21
21
  };
22
22
  return pureCalculateAnchoredPosition(clippingRect, relativeRect, floatingElement.getBoundingClientRect(), anchorElement instanceof Element ? anchorElement.getBoundingClientRect() : anchorElement, getDefaultSettings(settings));
23
23
  }
24
24
  function getPositionedParent(element) {
25
+ if (isOnTopLayer(element))
26
+ return document.body;
25
27
  let parentNode = element.parentNode;
26
28
  while (parentNode !== null) {
27
29
  if (parentNode instanceof HTMLElement && getComputedStyle(parentNode).position !== 'static') {
@@ -31,10 +33,30 @@ function getPositionedParent(element) {
31
33
  }
32
34
  return document.body;
33
35
  }
34
- function getClippingRect(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) {
43
+ var _a;
44
+ try {
45
+ return (element.matches(':popover-open') && /native code/.test((_a = document.body.showPopover) === null || _a === void 0 ? void 0 : _a.toString()));
46
+ }
47
+ catch (_b) {
48
+ return false;
49
+ }
50
+ }
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;
35
57
  let parentNode = element;
36
58
  while (parentNode !== null) {
37
- if (parentNode === document.body) {
59
+ if (!(parentNode instanceof Element)) {
38
60
  break;
39
61
  }
40
62
  const parentNodeStyle = getComputedStyle(parentNode);
@@ -43,20 +65,23 @@ function getClippingRect(element) {
43
65
  }
44
66
  parentNode = parentNode.parentNode;
45
67
  }
46
- 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);
47
72
  const elemRect = clippingNode.getBoundingClientRect();
48
73
  const elemStyle = getComputedStyle(clippingNode);
49
74
  const [borderTop, borderLeft, borderRight, borderBottom] = [
50
75
  elemStyle.borderTopWidth,
51
76
  elemStyle.borderLeftWidth,
52
77
  elemStyle.borderRightWidth,
53
- elemStyle.borderBottomWidth
78
+ elemStyle.borderBottomWidth,
54
79
  ].map(v => parseInt(v, 10) || 0);
55
80
  return {
56
81
  top: elemRect.top + borderTop,
57
82
  left: elemRect.left + borderLeft,
58
83
  width: elemRect.width - borderRight - borderLeft,
59
- height: Math.max(elemRect.height - borderTop - borderBottom, clippingNode === document.body ? window.innerHeight : -Infinity)
84
+ height: Math.max(elemRect.height - borderTop - borderBottom, clippingNode === document.body ? window.innerHeight : -Infinity),
60
85
  };
61
86
  }
62
87
  const positionDefaults = {
@@ -64,7 +89,7 @@ const positionDefaults = {
64
89
  align: 'start',
65
90
  anchorOffset: 4,
66
91
  alignmentOffset: 4,
67
- allowOutOfBounds: false
92
+ allowOutOfBounds: false,
68
93
  };
69
94
  function getDefaultSettings(settings = {}) {
70
95
  var _a, _b, _c, _d, _e;
@@ -75,7 +100,7 @@ function getDefaultSettings(settings = {}) {
75
100
  align,
76
101
  anchorOffset: (_c = settings.anchorOffset) !== null && _c !== void 0 ? _c : (side === 'inside-center' ? 0 : positionDefaults.anchorOffset),
77
102
  alignmentOffset: (_d = settings.alignmentOffset) !== null && _d !== void 0 ? _d : (align !== 'center' && side.startsWith('inside') ? positionDefaults.alignmentOffset : 0),
78
- allowOutOfBounds: (_e = settings.allowOutOfBounds) !== null && _e !== void 0 ? _e : positionDefaults.allowOutOfBounds
103
+ allowOutOfBounds: (_e = settings.allowOutOfBounds) !== null && _e !== void 0 ? _e : positionDefaults.allowOutOfBounds,
79
104
  };
80
105
  }
81
106
  function pureCalculateAnchoredPosition(viewportRect, relativePosition, floatingRect, anchorRect, { side, align, allowOutOfBounds, anchorOffset, alignmentOffset }) {
@@ -83,7 +108,7 @@ function pureCalculateAnchoredPosition(viewportRect, relativePosition, floatingR
83
108
  top: viewportRect.top - relativePosition.top,
84
109
  left: viewportRect.left - relativePosition.left,
85
110
  width: viewportRect.width,
86
- height: viewportRect.height
111
+ height: viewportRect.height,
87
112
  };
88
113
  let pos = calculatePosition(floatingRect, anchorRect, side, align, anchorOffset, alignmentOffset);
89
114
  let anchorSide = side;
@@ -1,4 +1,4 @@
1
- declare type Dimensions = {
1
+ type Dimensions = {
2
2
  top: number;
3
3
  left: number;
4
4
  bottom: number;
@@ -6,7 +6,7 @@ declare type Dimensions = {
6
6
  height?: number;
7
7
  width?: number;
8
8
  };
9
- declare type Offset = {
9
+ type Offset = {
10
10
  top: number;
11
11
  left: number;
12
12
  };
@@ -2,7 +2,7 @@ export function offset(element) {
2
2
  const rect = element.getBoundingClientRect();
3
3
  return {
4
4
  top: rect.top + window.pageYOffset,
5
- left: rect.left + window.pageXOffset
5
+ left: rect.left + window.pageXOffset,
6
6
  };
7
7
  }
8
8
  export function overflowParent(targetElement) {
@@ -57,11 +57,11 @@ export function overflowOffset(element, targetContainer) {
57
57
  const scroll = container === document.documentElement && document.defaultView
58
58
  ? {
59
59
  top: document.defaultView.pageYOffset,
60
- left: document.defaultView.pageXOffset
60
+ left: document.defaultView.pageXOffset,
61
61
  }
62
62
  : {
63
63
  top: container.scrollTop,
64
- left: container.scrollLeft
64
+ left: container.scrollLeft,
65
65
  };
66
66
  const top = elementOffset.top - scroll.top;
67
67
  const left = elementOffset.left - scroll.left;
@@ -91,7 +91,7 @@ export function focusTrap(container, initialFocus, abortSignal) {
91
91
  container,
92
92
  controller: wrappingController,
93
93
  initialFocus,
94
- originalSignal: signal
94
+ originalSignal: signal,
95
95
  };
96
96
  const suspendedTrapIndex = suspendedTrapStack.findIndex(t => t.container === container);
97
97
  if (suspendedTrapIndex >= 0) {
@@ -1,5 +1,5 @@
1
- export declare type Direction = 'previous' | 'next' | 'start' | 'end';
2
- export declare type FocusMovementKeys = 'ArrowLeft' | 'ArrowDown' | 'ArrowUp' | 'ArrowRight' | 'h' | 'j' | 'k' | 'l' | 'a' | 's' | 'w' | 'd' | 'Tab' | 'Home' | 'End' | 'PageUp' | 'PageDown';
1
+ export type Direction = 'previous' | 'next' | 'start' | 'end';
2
+ export type FocusMovementKeys = 'ArrowLeft' | 'ArrowDown' | 'ArrowUp' | 'ArrowRight' | 'h' | 'j' | 'k' | 'l' | 'a' | 's' | 'w' | 'd' | 'Tab' | 'Home' | 'End' | 'PageUp' | 'PageDown' | 'Backspace';
3
3
  export declare enum FocusKeys {
4
4
  ArrowHorizontal = 1,
5
5
  ArrowVertical = 2,
@@ -10,6 +10,7 @@ export declare enum FocusKeys {
10
10
  WS = 32,
11
11
  AD = 64,
12
12
  Tab = 128,
13
+ Backspace = 512,
13
14
  ArrowAll = 3,
14
15
  HJKL = 12,
15
16
  WASD = 96,
@@ -14,6 +14,7 @@ export var FocusKeys;
14
14
  FocusKeys[FocusKeys["WS"] = 32] = "WS";
15
15
  FocusKeys[FocusKeys["AD"] = 64] = "AD";
16
16
  FocusKeys[FocusKeys["Tab"] = 128] = "Tab";
17
+ FocusKeys[FocusKeys["Backspace"] = 512] = "Backspace";
17
18
  FocusKeys[FocusKeys["ArrowAll"] = 3] = "ArrowAll";
18
19
  FocusKeys[FocusKeys["HJKL"] = 12] = "HJKL";
19
20
  FocusKeys[FocusKeys["WASD"] = 96] = "WASD";
@@ -36,7 +37,8 @@ const KEY_TO_BIT = {
36
37
  Home: FocusKeys.HomeAndEnd,
37
38
  End: FocusKeys.HomeAndEnd,
38
39
  PageUp: FocusKeys.PageUpDown,
39
- PageDown: FocusKeys.PageUpDown
40
+ PageDown: FocusKeys.PageUpDown,
41
+ Backspace: FocusKeys.Backspace,
40
42
  };
41
43
  const KEY_TO_DIRECTION = {
42
44
  ArrowLeft: 'previous',
@@ -55,7 +57,8 @@ const KEY_TO_DIRECTION = {
55
57
  Home: 'start',
56
58
  End: 'end',
57
59
  PageUp: 'start',
58
- PageDown: 'end'
60
+ PageDown: 'end',
61
+ Backspace: 'previous',
59
62
  };
60
63
  function getDirection(keyboardEvent) {
61
64
  const direction = KEY_TO_DIRECTION[keyboardEvent.key];
@@ -184,8 +187,7 @@ export function focusZone(container, settings) {
184
187
  if (filteredElements.length === 0) {
185
188
  return;
186
189
  }
187
- const insertIndex = focusableElements.findIndex(e => (e.compareDocumentPosition(filteredElements[0]) & Node.DOCUMENT_POSITION_PRECEDING) > 0);
188
- focusableElements.splice(insertIndex === -1 ? focusableElements.length : insertIndex, 0, ...filteredElements);
190
+ focusableElements.splice(findInsertionIndex(filteredElements), 0, ...filteredElements);
189
191
  for (const element of filteredElements) {
190
192
  if (!savedTabIndex.has(element)) {
191
193
  savedTabIndex.set(element, element.getAttribute('tabindex'));
@@ -196,6 +198,27 @@ export function focusZone(container, settings) {
196
198
  updateFocusedElement(getFirstFocusableElement());
197
199
  }
198
200
  }
201
+ function findInsertionIndex(elementsToInsert) {
202
+ const firstElementToInsert = elementsToInsert[0];
203
+ if (focusableElements.length === 0)
204
+ return 0;
205
+ let iMin = 0;
206
+ let iMax = focusableElements.length - 1;
207
+ while (iMin <= iMax) {
208
+ const i = Math.floor((iMin + iMax) / 2);
209
+ const element = focusableElements[i];
210
+ if (followsInDocument(firstElementToInsert, element)) {
211
+ iMax = i - 1;
212
+ }
213
+ else {
214
+ iMin = i + 1;
215
+ }
216
+ }
217
+ return iMin;
218
+ }
219
+ function followsInDocument(first, second) {
220
+ return (second.compareDocumentPosition(first) & Node.DOCUMENT_POSITION_PRECEDING) > 0;
221
+ }
199
222
  function endFocusManagement(...elements) {
200
223
  for (const element of elements) {
201
224
  const focusableElementIndex = focusableElements.indexOf(element);
@@ -219,7 +242,8 @@ export function focusZone(container, settings) {
219
242
  }
220
243
  }
221
244
  beginFocusManagement(...iterateFocusableElements(container));
222
- updateFocusedElement(getFirstFocusableElement());
245
+ const initialElement = typeof focusInStrategy === 'function' ? focusInStrategy(document.body) : getFirstFocusableElement();
246
+ updateFocusedElement(initialElement);
223
247
  const observer = new MutationObserver(mutations => {
224
248
  for (const mutation of mutations) {
225
249
  for (const removedNode of mutation.removedNodes) {
@@ -238,7 +262,7 @@ export function focusZone(container, settings) {
238
262
  });
239
263
  observer.observe(container, {
240
264
  subtree: true,
241
- childList: true
265
+ childList: true,
242
266
  });
243
267
  const controller = new AbortController();
244
268
  const signal = (_e = settings === null || settings === void 0 ? void 0 : settings.abortSignal) !== null && _e !== void 0 ? _e : controller.signal;
@@ -5,8 +5,8 @@ try {
5
5
  signal: {
6
6
  get() {
7
7
  signalSupported = true;
8
- }
9
- }
8
+ },
9
+ },
10
10
  });
11
11
  window.addEventListener('test', noop, options);
12
12
  window.removeEventListener('test', noop, options);
@@ -3,7 +3,7 @@ export function* iterateFocusableElements(container, options = {}) {
3
3
  const strict = (_a = options.strict) !== null && _a !== void 0 ? _a : false;
4
4
  const acceptFn = ((_b = options.onlyTabbable) !== null && _b !== void 0 ? _b : false) ? isTabbable : isFocusable;
5
5
  const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {
6
- acceptNode: node => node instanceof HTMLElement && acceptFn(node, strict) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
6
+ acceptNode: node => node instanceof HTMLElement && acceptFn(node, strict) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP,
7
7
  });
8
8
  let nextNode = null;
9
9
  if (!options.reverse && acceptFn(container, strict)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primer/behaviors",
3
- "version": "0.0.0-2023818155639",
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",
@@ -30,17 +30,17 @@
30
30
  "dist/cjs/focus-trap.js"
31
31
  ],
32
32
  "scripts": {
33
- "lint": "eslint src/",
34
- "test": "npm run jest && npm run lint",
35
- "test:watch": "jest --watch",
36
- "jest": "jest",
37
33
  "clean": "rm -rf dist",
38
- "prebuild": "npm run clean",
39
34
  "build": "npm run build:esm && npm run build:cjs",
40
35
  "build:esm": "tsc",
41
36
  "build:cjs": "tsc --module commonjs --outDir dist/cjs",
37
+ "lint": "eslint src/",
38
+ "test": "jest",
39
+ "test:watch": "jest --watch",
40
+ "prebuild": "npm run clean",
41
+ "release": "npm run build && changeset publish",
42
42
  "size-limit": "npm run build && size-limit",
43
- "release": "npm run build && changeset publish"
43
+ "type-check": "tsc --noEmit"
44
44
  },
45
45
  "repository": {
46
46
  "type": "git",
@@ -68,21 +68,23 @@
68
68
  "devDependencies": {
69
69
  "@changesets/changelog-github": "^0.4.2",
70
70
  "@changesets/cli": "^2.18.1",
71
- "@github/prettier-config": "0.0.4",
72
- "@size-limit/preset-small-lib": "^7.0.3",
73
- "@testing-library/react": "^12.1.2",
74
- "@testing-library/user-event": "^13.5.0",
71
+ "@github/prettier-config": "^0.0.6",
72
+ "@size-limit/preset-small-lib": "^8.2.4",
73
+ "@testing-library/react": "^14.0.0",
74
+ "@testing-library/user-event": "^14.5.1",
75
75
  "@types/jest": "^27.0.3",
76
- "@types/react": "^17.0.37",
77
- "esbuild": "^0.14.1",
76
+ "@types/node": "^18.18.0",
77
+ "@types/react": "^18.2.23",
78
+ "esbuild": "^0.19.4",
78
79
  "esbuild-jest": "^0.5.0",
79
- "eslint": "^8.3.0",
80
- "eslint-plugin-github": "^4.3.5",
80
+ "eslint": "^8.50.0",
81
+ "eslint-plugin-github": "^4.10.0",
82
+ "eslint-plugin-prettier": "^5.0.0",
81
83
  "jest": "^27.4.3",
82
- "prettier": "^2.5.0",
83
- "react": "^17.0.2",
84
- "react-dom": "^17.0.2",
85
- "size-limit": "^7.0.3",
86
- "typescript": "^4.5.2"
84
+ "prettier": "^3.0.3",
85
+ "react": "^18.2.0",
86
+ "react-dom": "^18.2.0",
87
+ "size-limit": "^8.2.4",
88
+ "typescript": "^5.2.2"
87
89
  }
88
90
  }