@atlaskit/top-layer 0.16.0 → 0.18.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 (141) hide show
  1. package/AGENTS.md +113 -0
  2. package/CHANGELOG.md +14 -0
  3. package/README.md +4 -4
  4. package/animations/package.json +1 -8
  5. package/dialog/package.json +1 -8
  6. package/dialog-scroll-lock/package.json +1 -8
  7. package/dist/cjs/animations/presets.js +96 -9
  8. package/dist/cjs/dialog/dialog-content.js +12 -30
  9. package/dist/cjs/entry-points/animations.js +12 -0
  10. package/dist/cjs/internal/set-style.js +5 -5
  11. package/dist/cjs/internal/slot.js +1 -1
  12. package/dist/cjs/internal/use-anchor-position-at-point.js +8 -8
  13. package/dist/cjs/internal/use-anchor-position.js +19 -40
  14. package/dist/cjs/internal/use-animated-visibility.js +31 -61
  15. package/dist/cjs/internal/use-initial-focus.js +9 -13
  16. package/dist/cjs/internal/use-width-from-anchor.js +6 -6
  17. package/dist/cjs/popover/popover.js +30 -58
  18. package/dist/cjs/popover-surface/popover-surface.js +4 -3
  19. package/dist/cjs/testing/polyfill.js +9 -9
  20. package/dist/cjs/use-simple-light-dismiss/index.js +3 -3
  21. package/dist/es2019/animations/presets.js +145 -2
  22. package/dist/es2019/dialog/dialog-content.js +12 -30
  23. package/dist/es2019/entry-points/animations.js +1 -1
  24. package/dist/es2019/internal/set-style.js +5 -5
  25. package/dist/es2019/internal/slot.js +1 -1
  26. package/dist/es2019/internal/use-anchor-position-at-point.js +8 -8
  27. package/dist/es2019/internal/use-anchor-position.js +19 -40
  28. package/dist/es2019/internal/use-animated-visibility.js +31 -61
  29. package/dist/es2019/internal/use-initial-focus.js +9 -13
  30. package/dist/es2019/internal/use-width-from-anchor.js +6 -6
  31. package/dist/es2019/popover/popover.js +30 -58
  32. package/dist/es2019/popover-surface/popover-surface.js +1 -1
  33. package/dist/es2019/testing/polyfill.js +7 -11
  34. package/dist/es2019/use-simple-light-dismiss/index.js +3 -3
  35. package/dist/esm/animations/presets.js +93 -9
  36. package/dist/esm/dialog/dialog-content.js +12 -30
  37. package/dist/esm/entry-points/animations.js +1 -1
  38. package/dist/esm/internal/set-style.js +5 -5
  39. package/dist/esm/internal/slot.js +1 -1
  40. package/dist/esm/internal/use-anchor-position-at-point.js +8 -8
  41. package/dist/esm/internal/use-anchor-position.js +19 -40
  42. package/dist/esm/internal/use-animated-visibility.js +31 -61
  43. package/dist/esm/internal/use-initial-focus.js +9 -13
  44. package/dist/esm/internal/use-width-from-anchor.js +6 -6
  45. package/dist/esm/popover/popover.js +30 -58
  46. package/dist/esm/popover-surface/popover-surface.js +1 -1
  47. package/dist/esm/testing/polyfill.js +9 -9
  48. package/dist/esm/use-simple-light-dismiss/index.js +3 -3
  49. package/dist/types/animations/presets.d.ts +16 -0
  50. package/dist/types/entry-points/animations.d.ts +1 -1
  51. package/dist/types/internal/set-style.d.ts +2 -2
  52. package/dist/types/internal/slot.d.ts +2 -2
  53. package/dist/types/internal/use-anchor-position-at-point.d.ts +2 -2
  54. package/dist/types/internal/use-anchor-position.d.ts +0 -4
  55. package/dist/types/internal/use-initial-focus.d.ts +1 -1
  56. package/dist/types/popover/types.d.ts +5 -7
  57. package/dist/types/popover-surface/popover-surface.d.ts +2 -2
  58. package/focus/package.json +1 -8
  59. package/get-aria-for-trigger/package.json +1 -8
  60. package/open-layer-observer/package.json +1 -8
  61. package/package.json +3 -3
  62. package/placement-map/package.json +1 -8
  63. package/popover/package.json +1 -8
  64. package/popover-surface/package.json +1 -8
  65. package/testing/polyfill/package.json +1 -8
  66. package/testing/to-be-visible/package.json +1 -8
  67. package/use-anchor-position/package.json +1 -8
  68. package/use-anchor-position-at-point/package.json +1 -8
  69. package/use-arrow-navigation/package.json +1 -8
  70. package/use-popover-id/package.json +1 -8
  71. package/use-simple-light-dismiss/package.json +1 -8
  72. package/use-width-from-anchor/package.json +1 -8
  73. package/dist/types-ts4.5/animations/presets.d.ts +0 -94
  74. package/dist/types-ts4.5/animations/types.d.ts +0 -38
  75. package/dist/types-ts4.5/dialog/create-close-event.d.ts +0 -23
  76. package/dist/types-ts4.5/dialog/dialog-content.d.ts +0 -24
  77. package/dist/types-ts4.5/dialog/index.d.ts +0 -3
  78. package/dist/types-ts4.5/dialog/types.d.ts +0 -126
  79. package/dist/types-ts4.5/dialog-scroll-lock/index.d.ts +0 -40
  80. package/dist/types-ts4.5/entry-points/animations.d.ts +0 -2
  81. package/dist/types-ts4.5/entry-points/dialog-scroll-lock.d.ts +0 -1
  82. package/dist/types-ts4.5/entry-points/dialog.d.ts +0 -2
  83. package/dist/types-ts4.5/entry-points/focus.d.ts +0 -4
  84. package/dist/types-ts4.5/entry-points/get-aria-for-trigger.d.ts +0 -1
  85. package/dist/types-ts4.5/entry-points/open-layer-observer.d.ts +0 -5
  86. package/dist/types-ts4.5/entry-points/placement-map.d.ts +0 -3
  87. package/dist/types-ts4.5/entry-points/popover-surface.d.ts +0 -2
  88. package/dist/types-ts4.5/entry-points/popover.d.ts +0 -2
  89. package/dist/types-ts4.5/entry-points/use-anchor-position-at-point.d.ts +0 -1
  90. package/dist/types-ts4.5/entry-points/use-anchor-position.d.ts +0 -1
  91. package/dist/types-ts4.5/entry-points/use-arrow-navigation.d.ts +0 -3
  92. package/dist/types-ts4.5/entry-points/use-popover-id.d.ts +0 -11
  93. package/dist/types-ts4.5/entry-points/use-simple-light-dismiss.d.ts +0 -2
  94. package/dist/types-ts4.5/entry-points/use-width-from-anchor.d.ts +0 -1
  95. package/dist/types-ts4.5/focus/get-first-focusable.d.ts +0 -8
  96. package/dist/types-ts4.5/focus/get-focusables.d.ts +0 -13
  97. package/dist/types-ts4.5/focus/get-last-focusable.d.ts +0 -8
  98. package/dist/types-ts4.5/focus/get-next-focusable.d.ts +0 -13
  99. package/dist/types-ts4.5/focus/is-in-same-layer.d.ts +0 -8
  100. package/dist/types-ts4.5/focus/is-nested-layer-focused.d.ts +0 -8
  101. package/dist/types-ts4.5/focus/selectors.d.ts +0 -4
  102. package/dist/types-ts4.5/focus/types.d.ts +0 -5
  103. package/dist/types-ts4.5/index.d.ts +0 -0
  104. package/dist/types-ts4.5/internal/anchor-positioning-fallback.d.ts +0 -26
  105. package/dist/types-ts4.5/internal/combine.d.ts +0 -14
  106. package/dist/types-ts4.5/internal/ensure-preset-styles.d.ts +0 -6
  107. package/dist/types-ts4.5/internal/get-aria-for-trigger.d.ts +0 -93
  108. package/dist/types-ts4.5/internal/placement-to-position-area.d.ts +0 -21
  109. package/dist/types-ts4.5/internal/placement-to-try-fallbacks.d.ts +0 -36
  110. package/dist/types-ts4.5/internal/reduced-motion.d.ts +0 -14
  111. package/dist/types-ts4.5/internal/resolve-css-length-to-pixels.d.ts +0 -20
  112. package/dist/types-ts4.5/internal/resolve-css-length.d.ts +0 -8
  113. package/dist/types-ts4.5/internal/resolve-placement.d.ts +0 -58
  114. package/dist/types-ts4.5/internal/role-to-aria-has-popup.d.ts +0 -13
  115. package/dist/types-ts4.5/internal/role-types.d.ts +0 -71
  116. package/dist/types-ts4.5/internal/set-style.d.ts +0 -11
  117. package/dist/types-ts4.5/internal/should-focus-into-popover.d.ts +0 -8
  118. package/dist/types-ts4.5/internal/slot.d.ts +0 -29
  119. package/dist/types-ts4.5/internal/use-anchor-position-at-point.d.ts +0 -67
  120. package/dist/types-ts4.5/internal/use-anchor-position.d.ts +0 -91
  121. package/dist/types-ts4.5/internal/use-animated-visibility.d.ts +0 -109
  122. package/dist/types-ts4.5/internal/use-animation-preset.d.ts +0 -11
  123. package/dist/types-ts4.5/internal/use-focus-wrap.d.ts +0 -25
  124. package/dist/types-ts4.5/internal/use-initial-focus.d.ts +0 -73
  125. package/dist/types-ts4.5/internal/use-preset-styles.d.ts +0 -15
  126. package/dist/types-ts4.5/internal/use-width-from-anchor.d.ts +0 -21
  127. package/dist/types-ts4.5/placement-map/index.d.ts +0 -51
  128. package/dist/types-ts4.5/placement-map/legacy-placements.d.ts +0 -27
  129. package/dist/types-ts4.5/placement-map/placement-mapping.d.ts +0 -13
  130. package/dist/types-ts4.5/popover/create-close-event.d.ts +0 -25
  131. package/dist/types-ts4.5/popover/index.d.ts +0 -3
  132. package/dist/types-ts4.5/popover/popover.d.ts +0 -17
  133. package/dist/types-ts4.5/popover/types.d.ts +0 -193
  134. package/dist/types-ts4.5/popover-surface/index.d.ts +0 -2
  135. package/dist/types-ts4.5/popover-surface/popover-surface.d.ts +0 -21
  136. package/dist/types-ts4.5/testing/polyfill.d.ts +0 -25
  137. package/dist/types-ts4.5/testing/to-be-visible.d.ts +0 -23
  138. package/dist/types-ts4.5/use-arrow-navigation/index.d.ts +0 -3
  139. package/dist/types-ts4.5/use-arrow-navigation/is-at-current-menu-level.d.ts +0 -48
  140. package/dist/types-ts4.5/use-arrow-navigation/use-arrow-navigation.d.ts +0 -102
  141. package/dist/types-ts4.5/use-simple-light-dismiss/index.d.ts +0 -45
@@ -128,8 +128,6 @@ function getInitialFocusTarget({
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
135
133
  })) {
@@ -146,9 +144,9 @@ function getInitialFocusTarget({
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
154
152
  })) {
@@ -210,7 +208,7 @@ function getInitialFocusTarget({
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`
@@ -252,22 +250,20 @@ export function useInitialFocus({
252
250
  const prevPhase = prevPhaseRef.current;
253
251
  prevPhaseRef.current = phase;
254
252
 
255
- // Skip the `phase === 'closed'` transitions (host is going away,
256
- // nothing to focus) and any transition whose previous phase was
257
- // neither `'closed'` (fresh open) nor `'exiting'` (mid-exit
258
- // reopen, focus already on the trigger).
253
+ // Only focus on transitions into a visible phase from `'closed'`
254
+ // (fresh open) or `'exiting'` (mid-exit reopen).
259
255
  if (phase === 'closed') {
260
256
  return;
261
257
  }
262
258
  if (prevPhase !== 'closed' && prevPhase !== 'exiting') {
263
259
  return;
264
260
  }
265
- const el = elementRef.current;
266
- if (!el) {
261
+ const element = elementRef.current;
262
+ if (!element) {
267
263
  return;
268
264
  }
269
265
  (_getInitialFocusTarge = getInitialFocusTarget({
270
- container: el,
266
+ container: element,
271
267
  role
272
268
  })) === null || _getInitialFocusTarge === void 0 ? void 0 : _getInitialFocusTarge.focus();
273
269
  }, [elementRef, phase, role]);
@@ -36,7 +36,7 @@ export function useWidthFromAnchor({
36
36
  // explicit width constraint, allowing the popover to grow past the anchor's width.
37
37
  if (supportsAnchorSize) {
38
38
  return setStyle({
39
- el: node,
39
+ element: node,
40
40
  styles: [{
41
41
  property: 'width',
42
42
  value: 'anchor-size(width)'
@@ -48,7 +48,7 @@ export function useWidthFromAnchor({
48
48
  const anchor = anchorRef.current;
49
49
  if (anchor) {
50
50
  return setStyle({
51
- el: node,
51
+ element: node,
52
52
  styles: [{
53
53
  property: 'width',
54
54
  value: `${anchor.offsetWidth}px`
@@ -70,7 +70,7 @@ export function useWidthFromAnchor({
70
70
  // if its content requires it.
71
71
  if (supportsAnchorSize) {
72
72
  return setStyle({
73
- el: node,
73
+ element: node,
74
74
  styles: [minInlineSizeStyle, {
75
75
  property: 'min-width',
76
76
  value: 'anchor-size(width)'
@@ -80,7 +80,7 @@ export function useWidthFromAnchor({
80
80
  const anchor = anchorRef.current;
81
81
  if (anchor) {
82
82
  return setStyle({
83
- el: node,
83
+ element: node,
84
84
  styles: [minInlineSizeStyle, {
85
85
  property: 'min-width',
86
86
  value: `${anchor.offsetWidth}px`
@@ -90,14 +90,14 @@ export function useWidthFromAnchor({
90
90
 
91
91
  // No anchor available in JS fallback - still apply min-inline-size.
92
92
  return setStyle({
93
- el: node,
93
+ element: node,
94
94
  styles: [minInlineSizeStyle]
95
95
  });
96
96
  }
97
97
 
98
98
  // 'none' mode: no explicit width, but still apply `min-inline-size: max-content`.
99
99
  return setStyle({
100
- el: node,
100
+ element: node,
101
101
  styles: [minInlineSizeStyle]
102
102
  });
103
103
  }, [mode, popoverRef, anchorRef, isOpen]);
@@ -19,9 +19,9 @@ const supportsPopoverHint = once(() => {
19
19
  return false;
20
20
  }
21
21
  // eslint-disable-next-line @atlaskit/platform/no-direct-document-usage -- feature detection
22
- const el = document.createElement('div');
23
- el.setAttribute('popover', 'hint');
24
- return el.popover === 'hint';
22
+ const element = document.createElement('div');
23
+ element.setAttribute('popover', 'hint');
24
+ return element.popover === 'hint';
25
25
  });
26
26
 
27
27
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values -- popover UA reset requires values not in cssMap's type union
@@ -65,9 +65,7 @@ export const Popover = /*#__PURE__*/forwardRef(function Popover({
65
65
  const autoId = useId();
66
66
  const ownRef = useRef(null);
67
67
  const combinedRef = mergeRefs([ownRef, ref].filter(Boolean));
68
- // React 18 `useId()` returns IDs with colons (invalid in CSS selectors
69
- // and popover target attributes). Strip them; remove once React 18 is
70
- // dropped (React 19.2 uses underscores).
68
+ // `useId()` colons are invalid in CSS selectors and popover target attributes.
71
69
  const popoverId = idProp !== null && idProp !== void 0 ? idProp : `popover-${autoId.replace(/:/g, '')}`;
72
70
  const {
73
71
  phase,
@@ -80,21 +78,15 @@ export const Popover = /*#__PURE__*/forwardRef(function Popover({
80
78
  onExitFinish
81
79
  });
82
80
 
83
- // Pre-computed phase predicate. `isVisible` mirrors "host element is
84
- // mounted and on screen" (any phase except `closed`); used as a dep
85
- // signal for listener-re-bind effects so they re-attach to the fresh
86
- // host element after a close → reopen unmount/remount cycle.
81
+ // True while the host element is mounted (any phase except `closed`).
82
+ // Used as a dep so listener-rebind effects re-attach after a remount.
87
83
  const isVisible = phase !== 'closed';
88
84
 
89
85
  // Focus management: initial focus on entry (role-dependent), Tab cycling
90
86
  // for dialog roles. Restoration is native for outermost popovers; nested
91
- // focus-capturing roles are handled below via `beforetoggle` snapshot.
92
- // See: notes/architecture/focus-restoration.md
93
- //
94
- // Both hooks take `phase` directly: `useFocusWrap` keeps its listener
95
- // attached while `phase !== 'closed'` (so focus stays trapped through
96
- // the animated-exit window — WCAG 2.4.3 regression guard);
97
- // `useInitialFocus` moves focus on the transition into `'open'`.
87
+ // focus-capturing roles are handled below via the `beforetoggle` snapshot.
88
+ // Passing `phase` keeps `useFocusWrap` attached through the animated-exit
89
+ // window (WCAG 2.4.3 guard). See notes/architecture/focus-restoration.md.
98
90
  useFocusWrap({
99
91
  elementRef: ownRef,
100
92
  role,
@@ -191,7 +183,6 @@ export const Popover = /*#__PURE__*/forwardRef(function Popover({
191
183
 
192
184
  // Browser dismiss (Escape/click-outside).
193
185
  const reason = closeReasonRef.current;
194
- // reset for next dismiss
195
186
  closeReasonRef.current = 'light-dismiss';
196
187
  // Optional: `manual` mode has no `onClose` in the forwarded type.
197
188
  onClose === null || onClose === void 0 ? void 0 : onClose({
@@ -200,21 +191,12 @@ export const Popover = /*#__PURE__*/forwardRef(function Popover({
200
191
  }
201
192
  }, [onClose, onOpenChange, role]);
202
193
 
203
- // Bind toggle/beforetoggle/keydown listeners via `useLayoutEffect` so
204
- // they are attached BEFORE the show/hide layout effect calls
205
- // `el.showPopover()`. `showPopover()` dispatches `beforetoggle`
206
- // synchronously; binding the listener in a regular `useEffect`
207
- // (which runs after layout effects) misses that first dispatch and
208
- // the `previouslyFocusedElementRef` snapshot never gets taken — which
209
- // breaks nested-popover focus restoration on close.
210
- //
211
- // `isVisible` is in deps so the listeners re-bind to the new host
212
- // element after a host unmount/remount cycle. The cleanup detaches
213
- // the old listeners.
214
- //
215
- // This layout effect MUST appear in the file before the show/hide
216
- // layout effect — React runs layout effects in source order on the
217
- // same commit, and we need listeners attached first.
194
+ // Bind via `useLayoutEffect`, and BEFORE the show/hide layout effect
195
+ // below, so listeners are attached when `showPopover()` synchronously
196
+ // dispatches `beforetoggle`. A regular `useEffect` would miss that
197
+ // first dispatch, dropping the `previouslyFocusedElementRef` snapshot
198
+ // and breaking nested-popover focus restoration on close. React runs
199
+ // layout effects in source order on the same commit.
218
200
  useLayoutEffect(() => {
219
201
  const element = ownRef.current;
220
202
  if (!element) {
@@ -250,20 +232,17 @@ export const Popover = /*#__PURE__*/forwardRef(function Popover({
250
232
  }, [handleToggle, handleBeforeToggle, isVisible]);
251
233
 
252
234
  // Placement-dependent animation CSS vars. Kept separate from show/hide
253
- // so preset reference changes do not re-trigger visibility. `isVisible`
254
- // is in deps so the CSS vars are re-applied to the new host element after
255
- // a remount cycle (otherwise the freshly mounted element would have no
256
- // transform / placement vars set).
235
+ // so preset reference changes do not re-trigger visibility.
257
236
  useLayoutEffect(() => {
258
- const el = ownRef.current;
259
- if (!el || !(preset !== null && preset !== void 0 && preset.getProperties) || !placement) {
237
+ const element = ownRef.current;
238
+ if (!element || !(preset !== null && preset !== void 0 && preset.getProperties) || !placement) {
260
239
  return;
261
240
  }
262
241
  const props = preset.getProperties({
263
242
  placement
264
243
  });
265
244
  Object.entries(props).forEach(([key, value]) => {
266
- el.style.setProperty(key, String(value));
245
+ element.style.setProperty(key, String(value));
267
246
  });
268
247
  }, [preset, placement, isVisible]);
269
248
 
@@ -271,42 +250,35 @@ export const Popover = /*#__PURE__*/forwardRef(function Popover({
271
250
  // already in the target state. Try/catch guards `InvalidStateError` on
272
251
  // disconnected elements (StrictMode/concurrent/unmount edge cases).
273
252
  useLayoutEffect(() => {
274
- const el = ownRef.current;
275
- if (!el) {
253
+ const element = ownRef.current;
254
+ if (!element) {
276
255
  return;
277
256
  }
278
257
  if (isOpen) {
279
258
  programmaticCloseRef.current = false;
280
- // Reset the close-reason snapshot at the start of every open
281
- // cycle. The ref outlives the host element (lives on the
282
- // component, not the DOM), so a stale 'escape' from a prior
283
- // cycle would otherwise leak into the next close — especially
284
- // in the no-animation path where the host unmounts before
285
- // `handleToggle` has a chance to reset the ref itself.
259
+ // Clear stale 'escape' from a prior cycle: the ref outlives
260
+ // the host element, and the no-animation path unmounts before
261
+ // `handleToggle` can reset it.
286
262
  closeReasonRef.current = 'light-dismiss';
287
263
  try {
288
- el.showPopover();
264
+ element.showPopover();
289
265
  } catch {}
290
266
  return () => {
291
267
  programmaticCloseRef.current = true;
292
268
  try {
293
- el.hidePopover();
269
+ element.hidePopover();
294
270
  } catch {}
295
271
  };
296
272
  }
297
273
  programmaticCloseRef.current = true;
298
274
  try {
299
- el.hidePopover();
275
+ element.hidePopover();
300
276
  } catch {}
301
277
  }, [isOpen]);
302
278
 
303
- // The host element is only rendered while the popover is open or its exit
304
- // animation is playing. Once the phase returns to `closed`, we unmount
305
- // the host entirely so it does not leave an empty `role="..."` /
306
- // `popover` element in the accessibility tree. The element re-mounts on
307
- // the next `isOpen=true` commit; refs and event listeners re-bind via
308
- // the existing layout effect and `useEffect`. The popover `id` is stable
309
- // across opens (derived from `useId()` or the consumer-supplied `idProp`).
279
+ // Unmount the host once exit completes so it does not leave an empty
280
+ // `role`/`popover` element in the accessibility tree. The element
281
+ // remounts on the next open; the `popoverId` is stable across opens.
310
282
  if (!isVisible) {
311
283
  return null;
312
284
  }
@@ -1,7 +1,7 @@
1
1
  /* popover-surface.tsx generated by @compiled/babel-plugin v0.39.1 */
2
2
  import "./popover-surface.compiled.css";
3
+ import * as React from 'react';
3
4
  import { ax, ix } from "@compiled/react/runtime";
4
- import React from 'react';
5
5
  const styles = {
6
6
  root: "_2rkofajl _1reo1wug _18m91wug _bfhk1bhr _16qs130s"
7
7
  };
@@ -367,17 +367,13 @@ function hidePopoversInStackNotInChainTo({
367
367
  stack,
368
368
  target
369
369
  }) {
370
- const snapshot = stack.slice();
371
- for (let i = snapshot.length - 1; i >= 0; i -= 1) {
372
- const popover = snapshot[i];
373
- if (isInChainTo({
374
- candidate: popover,
375
- target
376
- })) {
377
- break;
378
- }
379
- popover.hidePopover();
380
- }
370
+ const snapshot = stack.slice().reverse();
371
+ const stopIndex = snapshot.findIndex(popover => isInChainTo({
372
+ candidate: popover,
373
+ target
374
+ }));
375
+ const toHide = stopIndex === -1 ? snapshot : snapshot.slice(0, stopIndex);
376
+ toHide.forEach(popover => popover.hidePopover());
381
377
  }
382
378
 
383
379
  // Two-phase light-dismiss: pointerdown stores the nearest open popover for
@@ -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({
@@ -1,17 +1,71 @@
1
1
  import { getPlacement } from '../internal/resolve-placement';
2
+ // ══════════════════════════════════════════════════════════════════════════════
3
+ // Popover presets
4
+ // ══════════════════════════════════════════════════════════════════════════════
2
5
 
3
- // slideAndFade
6
+ // ── popupMotion ──
4
7
 
5
- function getSlideAndFadeProperties(_ref) {
6
- var placement = _ref.placement,
7
- distance = _ref.distance;
8
- var pos = "".concat(distance, "px");
9
- var neg = "-".concat(distance, "px");
8
+ var POPUP_MOTION_CSS = "\n [data-ds-popover-popup-motion] {\n animation: var(--ds-popover-motion-exit);\n animation-fill-mode: forwards;\n transition:\n overlay 150ms allow-discrete,\n display 150ms allow-discrete;\n }\n\n [data-ds-popover-popup-motion]:popover-open {\n animation: var(--ds-popover-motion-enter);\n animation-fill-mode: backwards;\n }\n\n @media (prefers-reduced-motion: reduce) {\n [data-ds-popover-popup-motion],\n [data-ds-popover-popup-motion]:popover-open {\n animation: none;\n transition-duration: 0s;\n }\n }\n";
9
+ function getPopupMotionProperties(_ref) {
10
+ var placement = _ref.placement;
10
11
  var _getPlacement = getPlacement({
11
12
  placement: placement
12
13
  }),
13
14
  axis = _getPlacement.axis,
14
15
  edge = _getPlacement.edge;
16
+ if (axis === 'block' && edge === 'start') {
17
+ return {
18
+ '--ds-popover-motion-enter': "var(--ds-popup-enter-top, 150ms cubic-bezier(0.4, 1, 0.6, 1) SlideInTop8px, 150ms cubic-bezier(0.4, 1, 0.6, 1) FadeIn0to100)",
19
+ '--ds-popover-motion-exit': "var(--ds-popup-exit-top, 100ms cubic-bezier(0.6, 0, 0.8, 0.6) SlideOutTop8px, 100ms cubic-bezier(0.6, 0, 0.8, 0.6) FadeOut100to0)"
20
+ };
21
+ }
22
+ if (axis === 'inline' && edge === 'start') {
23
+ return {
24
+ '--ds-popover-motion-enter': "var(--ds-popup-enter-left, 150ms cubic-bezier(0.4, 1, 0.6, 1) SlideInLeft8px, 150ms cubic-bezier(0.4, 1, 0.6, 1) FadeIn0to100)",
25
+ '--ds-popover-motion-exit': "var(--ds-popup-exit-left, 100ms cubic-bezier(0.6, 0, 0.8, 0.6) SlideOutLeft8px, 100ms cubic-bezier(0.6, 0, 0.8, 0.6) FadeOut100to0)"
26
+ };
27
+ }
28
+ if (axis === 'inline' && edge === 'end') {
29
+ return {
30
+ '--ds-popover-motion-enter': "var(--ds-popup-enter-right, 150ms cubic-bezier(0.4, 1, 0.6, 1) SlideInRight8px, 150ms cubic-bezier(0.4, 1, 0.6, 1) FadeIn0to100)",
31
+ '--ds-popover-motion-exit': "var(--ds-popup-exit-right, 100ms cubic-bezier(0.6, 0, 0.8, 0.6) SlideOutRight8px, 100ms cubic-bezier(0.6, 0, 0.8, 0.6) FadeOut100to0)"
32
+ };
33
+ }
34
+ // Default: block/end (popover below trigger)
35
+ return {
36
+ '--ds-popover-motion-enter': "var(--ds-popup-enter-bottom, 150ms cubic-bezier(0.4, 1, 0.6, 1) SlideInBottom8px, 150ms cubic-bezier(0.4, 1, 0.6, 1) FadeIn0to100)",
37
+ '--ds-popover-motion-exit': "var(--ds-popup-exit-bottom, 100ms cubic-bezier(0.6, 0, 0.8, 0.6) SlideOutBottom8px, 100ms cubic-bezier(0.6, 0, 0.8, 0.6) FadeOut100to0)"
38
+ };
39
+ }
40
+
41
+ // eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
42
+ export function popupMotion() {
43
+ return {
44
+ name: 'popup-motion',
45
+ css: POPUP_MOTION_CSS,
46
+ enterDurationMs: 150,
47
+ exitDurationMs: 100,
48
+ getProperties: function getProperties(_ref2) {
49
+ var placement = _ref2.placement;
50
+ return getPopupMotionProperties({
51
+ placement: placement
52
+ });
53
+ }
54
+ };
55
+ }
56
+
57
+ // ── slideAndFade ──
58
+
59
+ function getSlideAndFadeProperties(_ref3) {
60
+ var placement = _ref3.placement,
61
+ distance = _ref3.distance;
62
+ var pos = "".concat(distance, "px");
63
+ var neg = "-".concat(distance, "px");
64
+ var _getPlacement2 = getPlacement({
65
+ placement: placement
66
+ }),
67
+ axis = _getPlacement2.axis,
68
+ edge = _getPlacement2.edge;
15
69
  if (axis === 'block' && edge === 'start') {
16
70
  return {
17
71
  '--ds-popover-tx': '0',
@@ -64,14 +118,15 @@ var SLIDE_AND_FADE_CSS = "\n[data-ds-popover-slide-and-fade] {\n opacity: 0;\n
64
118
  * <Popover animate={slideAndFade({ distance: 8 })} />
65
119
  * ```
66
120
  */
121
+ // eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
67
122
  export function slideAndFade(options) {
68
123
  var _options$distance;
69
124
  var distance = (_options$distance = options === null || options === void 0 ? void 0 : options.distance) !== null && _options$distance !== void 0 ? _options$distance : 4;
70
125
  return {
71
126
  name: 'slide-and-fade',
72
127
  css: SLIDE_AND_FADE_CSS,
73
- getProperties: function getProperties(_ref2) {
74
- var placement = _ref2.placement;
128
+ getProperties: function getProperties(_ref4) {
129
+ var placement = _ref4.placement;
75
130
  return getSlideAndFadeProperties({
76
131
  placement: placement,
77
132
  distance: distance
@@ -138,7 +193,36 @@ export function scaleAndFade() {
138
193
  // Dialog presets
139
194
  // ══════════════════════════════════════════════════════════════════════════════
140
195
 
141
- // dialogSlideUpAndFade
196
+ // ── dialogMotion ──
197
+
198
+ var DIALOG_MOTION_CSS = "\n [data-ds-dialog-motion] {\n animation: ".concat("var(--ds-modal-exit, 200ms cubic-bezier(0.6, 0, 0.8, 0.6) ScaleOut100to95)", ";\n animation-fill-mode: forwards;\n transition:\n overlay 200ms allow-discrete,\n display 200ms allow-discrete;\n }\n\n [data-ds-dialog-motion][open] {\n animation: ", "var(--ds-modal-enter, 250ms cubic-bezier(0.4, 0, 0, 1) ScaleIn95to100)", ";\n animation-fill-mode: backwards;\n }\n\n [data-ds-dialog-motion]::backdrop {\n animation: ", "var(--ds-blanket-exit, 200ms cubic-bezier(0.6, 0, 0.8, 0.6) FadeOut100to0)", ";\n animation-fill-mode: forwards;\n background-color: var(--ds-blanket, #050C1F75);\n transition:\n overlay 200ms allow-discrete,\n display 200ms allow-discrete;\n }\n\n [data-ds-dialog-motion][open]::backdrop {\n animation: ", "var(--ds-blanket-enter, 250ms cubic-bezier(0.4, 0, 0, 1) FadeIn0to100)", ";\n animation-fill-mode: backwards;\n }\n\n @media (prefers-reduced-motion: reduce) {\n [data-ds-dialog-motion],\n [data-ds-dialog-motion][open],\n [data-ds-dialog-motion]::backdrop,\n [data-ds-dialog-motion][open]::backdrop {\n animation: none;\n transition-duration: 0s;\n }\n }\n");
199
+
200
+ /**
201
+ * Dialog motion animation.
202
+ *
203
+ * Includes backdrop fade animation.
204
+ *
205
+ * @example
206
+ * ```tsx
207
+ * import { dialogMotion } from '@atlaskit/top-layer/animations';
208
+ *
209
+ * <Dialog animate={dialogMotion()} isOpen={isOpen} onClose={handleClose} label="...">
210
+ * ...
211
+ * </Dialog>
212
+ * ```
213
+ */
214
+ // eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
215
+ export function dialogMotion() {
216
+ // Bare preset name; `Dialog` prefixes it via `data-ds-dialog-{name}`.
217
+ return {
218
+ name: 'motion',
219
+ css: DIALOG_MOTION_CSS,
220
+ enterDurationMs: 250,
221
+ exitDurationMs: 200
222
+ };
223
+ }
224
+
225
+ // ── dialogSlideUpAndFade ──
142
226
 
143
227
  var DIALOG_SLIDE_UP_AND_FADE_CSS = "\n[data-ds-dialog-slide-up-and-fade] {\n opacity: 0;\n transform: translateY(calc(-1 * var(--ds-dialog-ty, 12px)));\n transition:\n opacity 175ms cubic-bezier(0.15, 1, 0.3, 1),\n transform 175ms cubic-bezier(0.15, 1, 0.3, 1),\n overlay 175ms allow-discrete,\n display 175ms allow-discrete;\n}\n\n[data-ds-dialog-slide-up-and-fade][open] {\n opacity: 1;\n transform: none;\n transition-duration: 350ms;\n}\n\n@starting-style {\n [data-ds-dialog-slide-up-and-fade][open] {\n opacity: 0;\n transform: translateY(var(--ds-dialog-ty, 12px));\n }\n}\n\n[data-ds-dialog-slide-up-and-fade]::backdrop {\n background-color: transparent;\n transition:\n background-color 175ms cubic-bezier(0.15, 1, 0.3, 1),\n overlay 175ms allow-discrete,\n display 175ms allow-discrete;\n}\n\n[data-ds-dialog-slide-up-and-fade][open]::backdrop {\n background-color: var(--ds-blanket, #050C1F75);\n transition-duration: 350ms;\n}\n\n@starting-style {\n [data-ds-dialog-slide-up-and-fade][open]::backdrop {\n background-color: transparent;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n [data-ds-dialog-slide-up-and-fade],\n [data-ds-dialog-slide-up-and-fade][open],\n [data-ds-dialog-slide-up-and-fade]::backdrop,\n [data-ds-dialog-slide-up-and-fade][open]::backdrop {\n transition-duration: 0s;\n }\n}\n";
144
228
 
@@ -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
  }
@@ -1 +1 @@
1
- export { slideAndFade, fade, scaleAndFade, dialogSlideUpAndFade, dialogFade } from '../animations/presets';
1
+ export { dialogMotion, popupMotion, slideAndFade, fade, scaleAndFade, dialogSlideUpAndFade, dialogFade } from '../animations/presets';
@@ -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({