@atlaskit/top-layer 0.17.0 → 1.0.0

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.
Files changed (132) hide show
  1. package/AGENTS.md +113 -0
  2. package/CHANGELOG.md +33 -0
  3. package/animations/package.json +1 -8
  4. package/dialog/package.json +1 -8
  5. package/dialog-scroll-lock/package.json +1 -8
  6. package/dist/cjs/dialog/dialog-content.js +12 -30
  7. package/dist/cjs/internal/set-style.js +5 -5
  8. package/dist/cjs/internal/slot.js +1 -1
  9. package/dist/cjs/internal/use-anchor-position-at-point.js +8 -8
  10. package/dist/cjs/internal/use-anchor-position.js +19 -40
  11. package/dist/cjs/internal/use-animated-visibility.js +31 -61
  12. package/dist/cjs/internal/use-initial-focus.js +9 -13
  13. package/dist/cjs/internal/use-width-from-anchor.js +6 -6
  14. package/dist/cjs/popover/popover.js +30 -58
  15. package/dist/cjs/popover-surface/popover-surface.js +4 -3
  16. package/dist/cjs/testing/polyfill.js +9 -9
  17. package/dist/cjs/use-simple-light-dismiss/index.js +3 -3
  18. package/dist/es2019/dialog/dialog-content.js +12 -30
  19. package/dist/es2019/internal/set-style.js +5 -5
  20. package/dist/es2019/internal/slot.js +1 -1
  21. package/dist/es2019/internal/use-anchor-position-at-point.js +8 -8
  22. package/dist/es2019/internal/use-anchor-position.js +19 -40
  23. package/dist/es2019/internal/use-animated-visibility.js +31 -61
  24. package/dist/es2019/internal/use-initial-focus.js +9 -13
  25. package/dist/es2019/internal/use-width-from-anchor.js +6 -6
  26. package/dist/es2019/popover/popover.js +30 -58
  27. package/dist/es2019/popover-surface/popover-surface.js +1 -1
  28. package/dist/es2019/testing/polyfill.js +7 -11
  29. package/dist/es2019/use-simple-light-dismiss/index.js +3 -3
  30. package/dist/esm/dialog/dialog-content.js +12 -30
  31. package/dist/esm/internal/set-style.js +5 -5
  32. package/dist/esm/internal/slot.js +1 -1
  33. package/dist/esm/internal/use-anchor-position-at-point.js +8 -8
  34. package/dist/esm/internal/use-anchor-position.js +19 -40
  35. package/dist/esm/internal/use-animated-visibility.js +31 -61
  36. package/dist/esm/internal/use-initial-focus.js +9 -13
  37. package/dist/esm/internal/use-width-from-anchor.js +6 -6
  38. package/dist/esm/popover/popover.js +30 -58
  39. package/dist/esm/popover-surface/popover-surface.js +1 -1
  40. package/dist/esm/testing/polyfill.js +9 -9
  41. package/dist/esm/use-simple-light-dismiss/index.js +3 -3
  42. package/dist/types/internal/set-style.d.ts +2 -2
  43. package/dist/types/internal/slot.d.ts +2 -2
  44. package/dist/types/internal/use-anchor-position-at-point.d.ts +2 -2
  45. package/dist/types/internal/use-anchor-position.d.ts +0 -4
  46. package/dist/types/internal/use-initial-focus.d.ts +1 -1
  47. package/dist/types/popover/types.d.ts +5 -7
  48. package/dist/types/popover-surface/popover-surface.d.ts +2 -2
  49. package/focus/package.json +1 -8
  50. package/get-aria-for-trigger/package.json +1 -8
  51. package/open-layer-observer/package.json +1 -8
  52. package/package.json +11 -19
  53. package/placement-map/package.json +1 -8
  54. package/popover/package.json +1 -8
  55. package/popover-surface/package.json +1 -8
  56. package/testing/polyfill/package.json +1 -8
  57. package/testing/to-be-visible/package.json +1 -8
  58. package/use-anchor-position/package.json +1 -8
  59. package/use-anchor-position-at-point/package.json +1 -8
  60. package/use-arrow-navigation/package.json +1 -8
  61. package/use-popover-id/package.json +1 -8
  62. package/use-simple-light-dismiss/package.json +1 -8
  63. package/use-width-from-anchor/package.json +1 -8
  64. package/dist/types-ts4.5/animations/presets.d.ts +0 -110
  65. package/dist/types-ts4.5/animations/types.d.ts +0 -38
  66. package/dist/types-ts4.5/dialog/create-close-event.d.ts +0 -23
  67. package/dist/types-ts4.5/dialog/dialog-content.d.ts +0 -24
  68. package/dist/types-ts4.5/dialog/index.d.ts +0 -3
  69. package/dist/types-ts4.5/dialog/types.d.ts +0 -126
  70. package/dist/types-ts4.5/dialog-scroll-lock/index.d.ts +0 -40
  71. package/dist/types-ts4.5/entry-points/animations.d.ts +0 -2
  72. package/dist/types-ts4.5/entry-points/dialog-scroll-lock.d.ts +0 -1
  73. package/dist/types-ts4.5/entry-points/dialog.d.ts +0 -2
  74. package/dist/types-ts4.5/entry-points/focus.d.ts +0 -4
  75. package/dist/types-ts4.5/entry-points/get-aria-for-trigger.d.ts +0 -1
  76. package/dist/types-ts4.5/entry-points/open-layer-observer.d.ts +0 -5
  77. package/dist/types-ts4.5/entry-points/placement-map.d.ts +0 -3
  78. package/dist/types-ts4.5/entry-points/popover-surface.d.ts +0 -2
  79. package/dist/types-ts4.5/entry-points/popover.d.ts +0 -2
  80. package/dist/types-ts4.5/entry-points/use-anchor-position-at-point.d.ts +0 -1
  81. package/dist/types-ts4.5/entry-points/use-anchor-position.d.ts +0 -1
  82. package/dist/types-ts4.5/entry-points/use-arrow-navigation.d.ts +0 -3
  83. package/dist/types-ts4.5/entry-points/use-popover-id.d.ts +0 -11
  84. package/dist/types-ts4.5/entry-points/use-simple-light-dismiss.d.ts +0 -2
  85. package/dist/types-ts4.5/entry-points/use-width-from-anchor.d.ts +0 -1
  86. package/dist/types-ts4.5/focus/get-first-focusable.d.ts +0 -8
  87. package/dist/types-ts4.5/focus/get-focusables.d.ts +0 -13
  88. package/dist/types-ts4.5/focus/get-last-focusable.d.ts +0 -8
  89. package/dist/types-ts4.5/focus/get-next-focusable.d.ts +0 -13
  90. package/dist/types-ts4.5/focus/is-in-same-layer.d.ts +0 -8
  91. package/dist/types-ts4.5/focus/is-nested-layer-focused.d.ts +0 -8
  92. package/dist/types-ts4.5/focus/selectors.d.ts +0 -4
  93. package/dist/types-ts4.5/focus/types.d.ts +0 -5
  94. package/dist/types-ts4.5/index.d.ts +0 -0
  95. package/dist/types-ts4.5/internal/anchor-positioning-fallback.d.ts +0 -26
  96. package/dist/types-ts4.5/internal/combine.d.ts +0 -14
  97. package/dist/types-ts4.5/internal/ensure-preset-styles.d.ts +0 -6
  98. package/dist/types-ts4.5/internal/get-aria-for-trigger.d.ts +0 -93
  99. package/dist/types-ts4.5/internal/placement-to-position-area.d.ts +0 -21
  100. package/dist/types-ts4.5/internal/placement-to-try-fallbacks.d.ts +0 -36
  101. package/dist/types-ts4.5/internal/reduced-motion.d.ts +0 -14
  102. package/dist/types-ts4.5/internal/resolve-css-length-to-pixels.d.ts +0 -20
  103. package/dist/types-ts4.5/internal/resolve-css-length.d.ts +0 -8
  104. package/dist/types-ts4.5/internal/resolve-placement.d.ts +0 -58
  105. package/dist/types-ts4.5/internal/role-to-aria-has-popup.d.ts +0 -13
  106. package/dist/types-ts4.5/internal/role-types.d.ts +0 -71
  107. package/dist/types-ts4.5/internal/set-style.d.ts +0 -11
  108. package/dist/types-ts4.5/internal/should-focus-into-popover.d.ts +0 -8
  109. package/dist/types-ts4.5/internal/slot.d.ts +0 -29
  110. package/dist/types-ts4.5/internal/use-anchor-position-at-point.d.ts +0 -67
  111. package/dist/types-ts4.5/internal/use-anchor-position.d.ts +0 -91
  112. package/dist/types-ts4.5/internal/use-animated-visibility.d.ts +0 -109
  113. package/dist/types-ts4.5/internal/use-animation-preset.d.ts +0 -11
  114. package/dist/types-ts4.5/internal/use-focus-wrap.d.ts +0 -25
  115. package/dist/types-ts4.5/internal/use-initial-focus.d.ts +0 -73
  116. package/dist/types-ts4.5/internal/use-preset-styles.d.ts +0 -15
  117. package/dist/types-ts4.5/internal/use-width-from-anchor.d.ts +0 -21
  118. package/dist/types-ts4.5/placement-map/index.d.ts +0 -51
  119. package/dist/types-ts4.5/placement-map/legacy-placements.d.ts +0 -27
  120. package/dist/types-ts4.5/placement-map/placement-mapping.d.ts +0 -13
  121. package/dist/types-ts4.5/popover/create-close-event.d.ts +0 -25
  122. package/dist/types-ts4.5/popover/index.d.ts +0 -3
  123. package/dist/types-ts4.5/popover/popover.d.ts +0 -17
  124. package/dist/types-ts4.5/popover/types.d.ts +0 -193
  125. package/dist/types-ts4.5/popover-surface/index.d.ts +0 -2
  126. package/dist/types-ts4.5/popover-surface/popover-surface.d.ts +0 -21
  127. package/dist/types-ts4.5/testing/polyfill.d.ts +0 -25
  128. package/dist/types-ts4.5/testing/to-be-visible.d.ts +0 -23
  129. package/dist/types-ts4.5/use-arrow-navigation/index.d.ts +0 -3
  130. package/dist/types-ts4.5/use-arrow-navigation/is-at-current-menu-level.d.ts +0 -48
  131. package/dist/types-ts4.5/use-arrow-navigation/use-arrow-navigation.d.ts +0 -102
  132. package/dist/types-ts4.5/use-simple-light-dismiss/index.d.ts +0 -45
@@ -54,13 +54,13 @@ export function useSimpleLightDismiss({
54
54
  const unbindClickOutside = bind(document, {
55
55
  type: 'click',
56
56
  listener: event => {
57
- const el = popoverRef.current;
58
- if (!el) {
57
+ const element = popoverRef.current;
58
+ if (!element) {
59
59
  return;
60
60
  }
61
61
 
62
62
  // If the click target is inside the popover, do not dismiss.
63
- if (event.target instanceof Node && el.contains(event.target)) {
63
+ if (event.target instanceof Node && element.contains(event.target)) {
64
64
  return;
65
65
  }
66
66
  onCloseRef.current({
@@ -48,8 +48,6 @@ export var Dialog = /*#__PURE__*/forwardRef(function Dialog(_ref, ref) {
48
48
  var dialogId = providedId !== null && providedId !== void 0 ? providedId : generatedId;
49
49
  var ownRef = useRef(null);
50
50
  var combinedRef = mergeRefs([ownRef, ref]);
51
-
52
- // Animation lifecycle
53
51
  var _useAnimatedVisibilit = useAnimatedVisibility({
54
52
  isOpen: isOpen,
55
53
  animate: animate,
@@ -59,31 +57,22 @@ export var Dialog = /*#__PURE__*/forwardRef(function Dialog(_ref, ref) {
59
57
  phase = _useAnimatedVisibilit.phase,
60
58
  preset = _useAnimatedVisibilit.preset;
61
59
 
62
- // Pre-computed phase predicate. `isVisible` means "host element is
63
- // mounted and on screen" (any phase except `closed`), used as a dep
64
- // signal for the backdrop-click rebind effect so it re-attaches to
65
- // the fresh <dialog> element after a close → reopen unmount/remount
66
- // cycle.
60
+ // True while the host element is mounted (any phase except `closed`).
61
+ // Used as a dep so listener-rebind effects re-attach after a remount.
67
62
  var isVisible = phase !== 'closed';
68
63
 
69
- // Focus wrap
70
- // Native <dialog>.showModal() traps focus but wraps through <body> at
71
- // the boundary (A → B → C → body → A). This hook intercepts Tab to
72
- // wrap directly (A → B → C → A), matching the WAI-ARIA APG pattern.
73
- //
74
- // Takes `phase` directly: the listener stays attached while
75
- // `phase !== 'closed'`, i.e. through the animated-exit window.
76
- // Gating on `isOpen` would unbind the listener at the start of an
77
- // exit animation while the dialog is still visible, letting focus
78
- // escape (WCAG 2.4.3 Focus Order regression).
64
+ // Native `<dialog>.showModal()` traps focus but wraps through `<body>` at
65
+ // the boundary (A → B → C → body → A). This hook intercepts Tab to wrap
66
+ // directly (A → B → C → A), matching the WAI-ARIA APG pattern. Passing
67
+ // `phase` keeps the listener attached through the animated-exit window
68
+ // (WCAG 2.4.3 Focus Order regression guard).
79
69
  useFocusWrap({
80
70
  elementRef: ownRef,
81
71
  role: 'dialog',
82
72
  phase: phase
83
73
  });
84
74
 
85
- // Open layer observer registration
86
- // Notifies the open layer observer so app-coordination features
75
+ // Notify the open layer observer so app-coordination features
87
76
  // (open-count subscriptions) work with top-layer dialogs.
88
77
  useNotifyOpenLayerObserver({
89
78
  type: 'modal',
@@ -91,8 +80,6 @@ export var Dialog = /*#__PURE__*/forwardRef(function Dialog(_ref, ref) {
91
80
  // No-op: no current use case for programmatic close via OpenLayerObserver.
92
81
  onClose: noop
93
82
  });
94
-
95
- // Show/hide the dialog in response to isOpen changes.
96
83
  useLayoutEffect(function () {
97
84
  var dialog = ownRef.current;
98
85
  if (!dialog) {
@@ -150,15 +137,10 @@ export var Dialog = /*#__PURE__*/forwardRef(function Dialog(_ref, ref) {
150
137
  // any atomic class, so the override always wins.
151
138
  var escapedId = CSS.escape(dialogId);
152
139
 
153
- // The `<dialog>` host element is only rendered while open or its exit
154
- // animation is playing. After exit completes the phase returns to
155
- // `closed` and we unmount the dialog entirely so it does not leave an
156
- // empty `role="dialog"` element in the accessibility tree. The element
157
- // re-mounts on the next `isOpen=true` commit; `showModal()` is called
158
- // from the existing `useLayoutEffect([isOpen])` on that same commit,
159
- // and listeners (cancel, click) re-bind via the `[onClose, isVisible]`
160
- // effect re-running after remount (React clears the ref on unmount, so
161
- // we need the listener effect to re-run with the fresh element).
140
+ // Unmount the `<dialog>` once exit completes so it does not leave an
141
+ // empty `role="dialog"` element in the accessibility tree. On the next
142
+ // open the element remounts and the `[isOpen]` and `[onClose, isVisible]`
143
+ // effects re-run against the fresh element.
162
144
  if (!isVisible) {
163
145
  return null;
164
146
  }
@@ -3,7 +3,7 @@
3
3
  * restores the prior inline values (so we do not stomp consumer styles).
4
4
  */
5
5
  export function setStyle(_ref) {
6
- var el = _ref.el,
6
+ var element = _ref.element,
7
7
  styles = _ref.styles;
8
8
  // Snapshot the prior inline value (NOT the computed style - we only want
9
9
  // to restore values that the consumer/our previous run inlined). An empty
@@ -12,23 +12,23 @@ export function setStyle(_ref) {
12
12
  var property = _ref2.property;
13
13
  return {
14
14
  property: property,
15
- value: el.style.getPropertyValue(property)
15
+ value: element.style.getPropertyValue(property)
16
16
  };
17
17
  });
18
18
  styles.forEach(function (_ref3) {
19
19
  var property = _ref3.property,
20
20
  value = _ref3.value;
21
- el.style.setProperty(property, value);
21
+ element.style.setProperty(property, value);
22
22
  });
23
23
  return function cleanup() {
24
24
  priorValues.forEach(function (_ref4) {
25
25
  var property = _ref4.property,
26
26
  value = _ref4.value;
27
27
  if (value === '') {
28
- el.style.removeProperty(property);
28
+ element.style.removeProperty(property);
29
29
  return;
30
30
  }
31
- el.style.setProperty(property, value);
31
+ element.style.setProperty(property, value);
32
32
  });
33
33
  };
34
34
  }
@@ -46,7 +46,7 @@ function isSlotChild(child) {
46
46
  export var Slot = /*#__PURE__*/forwardRef(function Slot(slotProps, ref) {
47
47
  var rawChildren = slotProps.children,
48
48
  props = _objectWithoutProperties(slotProps, _excluded);
49
- // `SlotProps` intersects `Record<string, unknown>`, so `children` is typed as `unknown`.
49
+ // `TSlotProps` intersects `Record<string, unknown>`, so `children` is typed as `unknown`.
50
50
  var children = rawChildren;
51
51
  if (!isSlotChild(children)) {
52
52
  return children;
@@ -6,7 +6,7 @@ import { useAnchorPosition } from './use-anchor-position';
6
6
  * anchor should be placed.
7
7
  *
8
8
  * Axis-named (`x`/`y`) rather than edge-named (`left`/`top`) so the
9
- * shape is unambiguous in RTL writing modes — there is no ambiguity
9
+ * shape is unambiguous in RTL writing modes - there is no ambiguity
10
10
  * about whether a coordinate is logical (`inline-start`/`block-start`)
11
11
  * or physical (`left`/`top`). Viewport coordinates are always
12
12
  * physical, so we name them with the physical axis labels.
@@ -17,19 +17,19 @@ import { useAnchorPosition } from './use-anchor-position';
17
17
  * Dynamic `inset-*` overrides are written by the positioning effect.
18
18
  *
19
19
  * Uses `Object.assign` rather than Compiled CSS (element is outside the React tree)
20
- * or the internal `setStyle` helper (we own this element entirely — it is created
21
- * and removed by this hook — so the snapshot/restore semantics `setStyle`
20
+ * or the internal `setStyle` helper (we own this element entirely - it is created
21
+ * and removed by this hook - so the snapshot/restore semantics `setStyle`
22
22
  * provides for caller-owned elements would be dead work here).
23
23
  */
24
- function applyStaticAnchorStyles(el) {
25
- Object.assign(el.style, {
24
+ function applyStaticAnchorStyles(element) {
25
+ Object.assign(element.style, {
26
26
  position: 'fixed',
27
27
  // `top`/`left` rather than `inset-block-start`/`inset-inline-start`
28
28
  // for consistency with the dynamic positioning effect below, which
29
29
  // uses physical properties (see comment there for the rationale).
30
30
  top: '0',
31
31
  left: '0',
32
- // Zero size — placement decides which side the popover sits on.
32
+ // Zero size - placement decides which side the popover sits on.
33
33
  width: '0',
34
34
  height: '0',
35
35
  // Prevent the overlay from intercepting pointer events.
@@ -75,7 +75,7 @@ export function useAnchorPositionAtPoint(_ref) {
75
75
  var syntheticAnchorRef = useRef(null);
76
76
 
77
77
  // `getPoint` is kept in a ref so the layout effect can call the
78
- // latest version without listing it as a dep — that would re-fire
78
+ // latest version without listing it as a dep - that would re-fire
79
79
  // on every render and re-latch the point.
80
80
  var getPointRef = useRef(getPoint);
81
81
  getPointRef.current = getPoint;
@@ -111,7 +111,7 @@ export function useAnchorPositionAtPoint(_ref) {
111
111
  };
112
112
  }, [isEnabled]);
113
113
 
114
- // Mirror `isEnabled` so both hooks stay in sync — when disabled,
114
+ // Mirror `isEnabled` so both hooks stay in sync - when disabled,
115
115
  // neither this hook nor the inner one touches the popover, leaving
116
116
  // any other positioning the consumer has set up free to take effect.
117
117
  useAnchorPosition({
@@ -145,17 +145,11 @@ function crossAxisShiftMargin(_ref2) {
145
145
  }
146
146
 
147
147
  /**
148
- * Returns a referentially-stable, fully-resolved `TPlacement`. `useMemo` is
149
- * keyed on the primitive fields of the input so a fresh inline `placement`
150
- * object on every render does not produce a fresh resolved object. Both
151
- * `getPlacement` and the equality check are skipped when the inputs are
152
- * unchanged.
148
+ * Returns a referentially-stable, fully-resolved `TPlacement`. The memo is
149
+ * keyed on the resolved primitive fields so a fresh inline `placement` object
150
+ * (or undefined fields vs explicit defaults) does not produce a fresh result.
153
151
  */
154
152
  function useStablePlacement(placement) {
155
- // Resolve to the fully-defaulted primitives BEFORE the memo so the deps
156
- // array sees the same key for `{}`, `undefined` fields, and explicit
157
- // defaults. Otherwise `axis: undefined` and `axis: 'block'` would be
158
- // treated as different inputs and re-run the downstream effect.
159
153
  var resolved = getPlacement({
160
154
  placement: placement
161
155
  });
@@ -165,10 +159,8 @@ function useStablePlacement(placement) {
165
159
  var gap = resolved.offset.gap;
166
160
  var shiftValue = resolved.offset.crossAxisShift.value;
167
161
  var shiftDirection = resolved.offset.crossAxisShift.direction;
168
- // Rebuild the placement object inside the memo from the resolved primitive
169
- // deps so the factory does not close over the (potentially fresh) outer
170
- // `resolved` reference, and `react-hooks/exhaustive-deps` can verify the
171
- // deps array.
162
+ // Rebuild inside the memo from primitive deps so the factory does not
163
+ // close over the outer `resolved` reference and exhaustive-deps can verify.
172
164
  return useMemo(function () {
173
165
  return {
174
166
  axis: axis,
@@ -200,10 +192,6 @@ function useStablePlacement(placement) {
200
192
  * When not supported, it falls back to JavaScript-based positioning using
201
193
  * `position: fixed` with measured coordinates, re-running on scroll (capture)
202
194
  * and resize events.
203
- *
204
- * `placement` may be a fresh inline object on every render: a structural
205
- * comparison is used internally so re-runs are skipped when the resolved
206
- * placement shape is unchanged.
207
195
  */
208
196
  // eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
209
197
  export function useAnchorPosition(_ref3) {
@@ -217,8 +205,6 @@ export function useAnchorPosition(_ref3) {
217
205
  isEnabled = _ref3$isEnabled === void 0 ? true : _ref3$isEnabled,
218
206
  isOpen = _ref3.isOpen;
219
207
  var id = useId();
220
- // Stabilize `placement` object so the same object literal
221
- // value returns an object with the same reference
222
208
  var stablePlacement = useStablePlacement(placement);
223
209
  useLayoutEffect(function () {
224
210
  if (!isEnabled) {
@@ -240,9 +226,9 @@ export function useAnchorPosition(_ref3) {
240
226
  placement: stablePlacement,
241
227
  offset: gapCssValue
242
228
  });
243
- // Hoist crossAxisShift to a single computation; reused below to
244
- // populate the active `--ds-cross-axis-shift-margin-*` custom
245
- // property for the named arrow @position-try rules.
229
+ // Computed once; reused below to populate the active
230
+ // `--ds-cross-axis-shift-margin-*` custom property for the
231
+ // named arrow @position-try rules.
246
232
  var crossAxisShift = crossAxisShiftMargin({
247
233
  placement: stablePlacement,
248
234
  crossAxisShiftCssValue: crossAxisShiftCssValue,
@@ -270,22 +256,17 @@ export function useAnchorPosition(_ref3) {
270
256
  }, {
271
257
  property: 'inset',
272
258
  value: 'auto'
273
- },
274
- // Main-axis gap between the popover and its trigger
275
- {
259
+ }, {
276
260
  property: gap.property,
277
261
  value: gap.value
278
- },
279
- // Cross-axis shift
280
- {
262
+ }, {
281
263
  property: crossAxisShift.property,
282
264
  value: crossAxisShift.value
283
265
  }];
284
266
 
285
- // Surface the active cross-axis shift value as a custom property so
286
- // each named arrow @position-try rule can re-apply it after a flip.
287
- // The four properties cover (cross-axis, side); only one is non-zero
288
- // at a time.
267
+ // Expose the active cross-axis shift via custom properties so each
268
+ // named arrow @position-try rule can re-apply it after a flip.
269
+ // One of the four (cross-axis, side) properties is non-zero at a time.
289
270
  popoverStyles.push({
290
271
  property: '--ds-cross-axis-shift-margin-start',
291
272
  value: '0px'
@@ -302,9 +283,8 @@ export function useAnchorPosition(_ref3) {
302
283
  property: '--ds-cross-axis-shift-margin-block-end',
303
284
  value: '0px'
304
285
  });
305
- // `crossAxisShift` IS the active margin - reuse instead of recomputing.
286
+ // `crossAxisShift` IS the active margin; reuse instead of recomputing.
306
287
  var crossAxisShiftActive = crossAxisShift;
307
- // Map margin property to its corresponding custom property name.
308
288
  var customPropertyByMarginProperty = {
309
289
  'margin-inline-start': '--ds-cross-axis-shift-margin-start',
310
290
  'margin-inline-end': '--ds-cross-axis-shift-margin-end',
@@ -338,18 +318,17 @@ export function useAnchorPosition(_ref3) {
338
318
  */
339
319
  trigger.style.setProperty('anchor-name', anchorName);
340
320
  var _undoPositioning = combine(setStyle({
341
- el: popover,
321
+ element: popover,
342
322
  styles: popoverStyles
343
323
  }));
344
324
  return _undoPositioning;
345
325
  }
346
326
 
347
- // JS fallback
348
- // The popover is already in the browser's top layer via popover="auto",
349
- // so it does not need position:fixed. We only reset the UA defaults
350
- // (inset: 0; margin: auto) and set top/left based on measurements.
327
+ // JS fallback. The popover is already in the top layer via
328
+ // popover="auto", so we only reset UA defaults and set top/left
329
+ // based on measurements.
351
330
  var cleanupBaseStyles = setStyle({
352
- el: popover,
331
+ element: popover,
353
332
  styles: [{
354
333
  property: 'margin',
355
334
  value: '0'
@@ -12,14 +12,16 @@ function runOnTransitionEndOrTimeout(_ref) {
12
12
  var element = _ref.element,
13
13
  safetyNetMs = _ref.safetyNetMs,
14
14
  onSettled = _ref.onSettled;
15
- var isSettled = false;
15
+ var state = {
16
+ isSettled: false
17
+ };
16
18
  function teardown() {
17
- isSettled = true;
19
+ state.isSettled = true;
18
20
  clearTimeout(safetyNetId);
19
21
  unbind();
20
22
  }
21
23
  function settle() {
22
- if (isSettled) {
24
+ if (state.isSettled) {
23
25
  return;
24
26
  }
25
27
  teardown();
@@ -103,22 +105,16 @@ export function useAnimatedVisibility(_ref2) {
103
105
  elementRef = _ref2.elementRef,
104
106
  onEnterFinish = _ref2.onEnterFinish,
105
107
  onExitFinish = _ref2.onExitFinish;
106
- // Resolve the animation preset and inject its CSS (idempotent).
107
- // Returns `null` when `animate` is falsy.
108
108
  var preset = usePresetStyles({
109
109
  preset: animate
110
110
  });
111
111
 
112
- // Whether we should play CSS transitions. False when there is no preset
113
- // or the user has enabled the "prefers reduced motion" OS setting.
112
+ // False when there is no preset or `prefers-reduced-motion` is set.
114
113
  var willAnimate = Boolean(animate) && !prefersReducedMotion();
115
114
 
116
- // `phase` is the single source of truth for the visibility lifecycle.
117
- // We promote `closed → entering` (animated) or `closed → open`
118
- // (non-animated) synchronously during render so the host element mounts
119
- // in the same commit as the open intent (React supports a single
120
- // setState during render as a synchronous re-render before paint).
121
- // All other transitions are driven by phase-specific effects below.
115
+ // Promote `closed → entering`/`closed → open` synchronously during
116
+ // render so the host element mounts in the same commit as the open
117
+ // intent. All other transitions are driven by phase-specific effects.
122
118
  var _useState = useState(function () {
123
119
  if (!isOpen) {
124
120
  return 'closed';
@@ -153,10 +149,8 @@ export function useAnimatedVisibility(_ref2) {
153
149
  var onExitFinishRef = useRef(onExitFinish);
154
150
  onExitFinishRef.current = onExitFinish;
155
151
 
156
- // `entering` (animation enabled):
157
- // Wait for the entry transition to settle, then move to `open` and fire
158
- // `onEnterFinish`. A fallback timeout (enterDurationMs + 50ms buffer)
159
- // guarantees the settle path runs even if `transitionend` is swallowed
152
+ // Animated entry: wait for `transitionend` (or a safety-net timeout) and
153
+ // move to `open`. The fallback guarantees settle on swallowed events
160
154
  // (zero-duration, browser quirk, mid-transition unmount).
161
155
  useEffect(function () {
162
156
  var _preset$enterDuration;
@@ -184,12 +178,9 @@ export function useAnimatedVisibility(_ref2) {
184
178
  });
185
179
  }, [phase, preset === null || preset === void 0 ? void 0 : preset.enterDurationMs, elementRef]);
186
180
 
187
- // Non-animated entry callback:
188
- // When animation is disabled, the lifecycle goes `closed → open`
189
- // directly with no `entering` phase. Fire `onEnterFinish` on the
190
- // transition into `open` from the non-animated path. We detect this
191
- // via a previous-phase ref to avoid firing on the animated path's
192
- // `entering → open` transition (which has its own callback above).
181
+ // Non-animated entry callback. The lifecycle goes `closed → open`
182
+ // directly with no `entering` phase; fire `onEnterFinish` on that jump.
183
+ // A previous-phase ref avoids double-firing on the animated path.
193
184
  var prevPhaseForEntryRef = useRef('closed');
194
185
  useEffect(function () {
195
186
  var prevPhase = prevPhaseForEntryRef.current;
@@ -203,12 +194,8 @@ export function useAnimatedVisibility(_ref2) {
203
194
  }
204
195
  }, [phase]);
205
196
 
206
- // `exiting` (animation enabled):
207
- // Wait for the exit transition to settle, then unmount the host element
208
- // (set phase back to `closed`) and fire `onExitFinish`. The fallback
209
- // timeout (exitDurationMs + 50ms buffer) guarantees we always unmount
210
- // even if `transitionend` is never emitted (element removed from the
211
- // top layer, zero-duration transition, interrupted animation).
197
+ // Animated exit: wait for `transitionend` (or a safety-net timeout),
198
+ // fire `onExitFinish`, then move to `closed` to unmount the host.
212
199
  useEffect(function () {
213
200
  var _preset$exitDurationM;
214
201
  if (phase !== 'exiting' || !willAnimate) {
@@ -237,33 +224,20 @@ export function useAnimatedVisibility(_ref2) {
237
224
  });
238
225
  }, [phase, willAnimate, preset === null || preset === void 0 ? void 0 : preset.exitDurationMs, elementRef]);
239
226
 
240
- // `exiting` (animation disabled):
241
- // Wait for the browser's `toggle` (closed) event (for `<div popover>`)
242
- // or `close` event (for `<dialog>`) to fire before unmounting. This
243
- // guarantees:
244
- // - the consumer's toggle/close listener (close-reason capture,
245
- // nested focus restoration, `onClose` notification) runs against
246
- // the still-attached element,
247
- // - the browser's native focus restoration triggered by
248
- // `hidePopover()` / `<dialog>.close()` has fired,
249
- // before React unmounts the node. A naive `setTimeout(0)` race
250
- // occasionally beats the `toggle` task in Chromium, causing focus
251
- // restoration to drop (the toggle event arrives at a detached element).
227
+ // Non-animated exit: wait for the browser's `toggle` (closed) or
228
+ // `close` event before unmounting, so the consumer's listener (close
229
+ // reason, nested focus restoration, `onClose`) and the browser's
230
+ // native focus restoration both run against the still-attached
231
+ // element. A naive `setTimeout(0)` occasionally beats the `toggle`
232
+ // task in Chromium and drops focus restoration.
252
233
  //
253
- // `useLayoutEffect` (not `useEffect`) so the listener is bound in the
254
- // same commit as the consumer's show/hide layout effect that calls
255
- // `hidePopover()` / `dialog.close()`. The browser queues the resulting
256
- // `toggle`/`close` event as a task, which cannot run until the JS
257
- // frame (including all effect flushes) completes, so the listener is
258
- // always attached before the event fires. Promotion to layout timing
259
- // removes any theoretical gap and future-proofs the ordering against
260
- // effect reordering. Safe under SSR: `phase` starts at `'closed'`, so
261
- // the body short-circuits and no DOM access happens on the server.
234
+ // `useLayoutEffect` so the listener binds in the same commit as the
235
+ // show/hide layout effect that calls `hidePopover()` / `dialog.close()`.
236
+ // The toggle/close event is queued as a task and cannot run until the
237
+ // frame completes. SSR-safe: `phase` starts at `'closed'`.
262
238
  //
263
- // `onExitFinish` is NOT called here. It is decoupled from the unmount
264
- // handshake and fired by the dedicated non-animated exit-callback
265
- // effect below, so consumers observe a synchronous "exit finished"
266
- // signal that matches `isOpen` flipping to `false`.
239
+ // `onExitFinish` is fired separately (see effect below) so consumers
240
+ // observe it the moment `isOpen` flips to `false`.
267
241
  useLayoutEffect(function () {
268
242
  if (phase !== 'exiting' || willAnimate) {
269
243
  return;
@@ -290,13 +264,9 @@ export function useAnimatedVisibility(_ref2) {
290
264
  }]);
291
265
  }, [phase, willAnimate, elementRef]);
292
266
 
293
- // Non-animated exit callback:
294
- // When animation is disabled there is no `transitionend` to wait for,
295
- // so fire `onExitFinish` on the transition into `exiting`. The actual
296
- // host-element unmount is gated on the browser `toggle`/`close` event
297
- // by the effect above; `onExitFinish` is observably decoupled from
298
- // that handshake so consumers see the callback fire at the same time
299
- // `isOpen` flipped to `false`.
267
+ // Non-animated exit callback. No `transitionend` to wait for, so fire
268
+ // `onExitFinish` on the transition into `exiting` (the unmount itself
269
+ // is gated by the toggle/close handshake above).
300
270
  var prevPhaseForExitRef = useRef('closed');
301
271
  useEffect(function () {
302
272
  var prevPhase = prevPhaseForExitRef.current;
@@ -128,8 +128,6 @@ function getInitialFocusTarget(_ref2) {
128
128
  });
129
129
  }
130
130
  if (role === 'menu') {
131
- // ARIA Combobox Pattern: if the popup is owned by a focused combobox,
132
- // leave focus on the textbox. Navigation is proxied via the combobox.
133
131
  if (isComboboxControllingPopup({
134
132
  container: container
135
133
  })) {
@@ -146,9 +144,9 @@ function getInitialFocusTarget(_ref2) {
146
144
  });
147
145
  }
148
146
  if (role === 'listbox') {
149
- // ARIA Combobox Pattern: if the popup is owned by a focused
150
- // combobox-like textbox (in-popup or external e.g. react-select),
151
- // leave focus on the textbox. Navigation is proxied via the combobox.
147
+ // ARIA Combobox Pattern: if a combobox-like textbox owns the popup
148
+ // (in-popup or external e.g. react-select), leave focus on the
149
+ // textbox - navigation is proxied through it.
152
150
  if (isComboboxControllingPopup({
153
151
  container: container
154
152
  })) {
@@ -210,7 +208,7 @@ function getInitialFocusTarget(_ref2) {
210
208
  * `<dialog>.close()`. If the consumer reopens before the exit transition
211
209
  * has settled, the phase machine jumps `exiting → entering` without
212
210
  * passing through `closed`, so without this branch the popup would be
213
- * visible again with focus stranded on the trigger — a keyboard usability
211
+ * visible again with focus stranded on the trigger - a keyboard usability
214
212
  * gap for menu/dialog/listbox roles.
215
213
  *
216
214
  * Note: there is a rare programmatic path where a consumer flips `isOpen`
@@ -251,22 +249,20 @@ export function useInitialFocus(_ref3) {
251
249
  var prevPhase = prevPhaseRef.current;
252
250
  prevPhaseRef.current = phase;
253
251
 
254
- // Skip the `phase === 'closed'` transitions (host is going away,
255
- // nothing to focus) and any transition whose previous phase was
256
- // neither `'closed'` (fresh open) nor `'exiting'` (mid-exit
257
- // reopen, focus already on the trigger).
252
+ // Only focus on transitions into a visible phase from `'closed'`
253
+ // (fresh open) or `'exiting'` (mid-exit reopen).
258
254
  if (phase === 'closed') {
259
255
  return;
260
256
  }
261
257
  if (prevPhase !== 'closed' && prevPhase !== 'exiting') {
262
258
  return;
263
259
  }
264
- var el = elementRef.current;
265
- if (!el) {
260
+ var element = elementRef.current;
261
+ if (!element) {
266
262
  return;
267
263
  }
268
264
  (_getInitialFocusTarge = getInitialFocusTarget({
269
- container: el,
265
+ container: element,
270
266
  role: role
271
267
  })) === null || _getInitialFocusTarge === void 0 || _getInitialFocusTarge.focus();
272
268
  }, [elementRef, phase, role]);
@@ -35,7 +35,7 @@ export function useWidthFromAnchor(_ref) {
35
35
  // explicit width constraint, allowing the popover to grow past the anchor's width.
36
36
  if (supportsAnchorSize) {
37
37
  return setStyle({
38
- el: node,
38
+ element: node,
39
39
  styles: [{
40
40
  property: 'width',
41
41
  value: 'anchor-size(width)'
@@ -47,7 +47,7 @@ export function useWidthFromAnchor(_ref) {
47
47
  var anchor = anchorRef.current;
48
48
  if (anchor) {
49
49
  return setStyle({
50
- el: node,
50
+ element: node,
51
51
  styles: [{
52
52
  property: 'width',
53
53
  value: "".concat(anchor.offsetWidth, "px")
@@ -69,7 +69,7 @@ export function useWidthFromAnchor(_ref) {
69
69
  // if its content requires it.
70
70
  if (supportsAnchorSize) {
71
71
  return setStyle({
72
- el: node,
72
+ element: node,
73
73
  styles: [minInlineSizeStyle, {
74
74
  property: 'min-width',
75
75
  value: 'anchor-size(width)'
@@ -79,7 +79,7 @@ export function useWidthFromAnchor(_ref) {
79
79
  var _anchor = anchorRef.current;
80
80
  if (_anchor) {
81
81
  return setStyle({
82
- el: node,
82
+ element: node,
83
83
  styles: [minInlineSizeStyle, {
84
84
  property: 'min-width',
85
85
  value: "".concat(_anchor.offsetWidth, "px")
@@ -89,14 +89,14 @@ export function useWidthFromAnchor(_ref) {
89
89
 
90
90
  // No anchor available in JS fallback - still apply min-inline-size.
91
91
  return setStyle({
92
- el: node,
92
+ element: node,
93
93
  styles: [minInlineSizeStyle]
94
94
  });
95
95
  }
96
96
 
97
97
  // 'none' mode: no explicit width, but still apply `min-inline-size: max-content`.
98
98
  return setStyle({
99
- el: node,
99
+ element: node,
100
100
  styles: [minInlineSizeStyle]
101
101
  });
102
102
  }, [mode, popoverRef, anchorRef, isOpen]);