@atlaskit/top-layer 1.6.0 → 1.7.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 (41) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +13 -0
  3. package/compass.yml +10 -1
  4. package/dist/cjs/dialog/dialog-content.compiled.css +0 -10
  5. package/dist/cjs/dialog/dialog-content.js +20 -28
  6. package/dist/cjs/entry-points/animations.js +1 -49
  7. package/dist/cjs/internal/use-animated-visibility.js +14 -17
  8. package/dist/cjs/internal/use-safari-escape-fix.js +62 -0
  9. package/dist/cjs/popover/animations.js +1 -147
  10. package/dist/cjs/popover/popover.compiled.css +1 -10
  11. package/dist/cjs/popover/popover.js +9 -12
  12. package/dist/cjs/testing/polyfill.js +3 -0
  13. package/dist/es2019/dialog/dialog-content.compiled.css +0 -10
  14. package/dist/es2019/dialog/dialog-content.js +20 -28
  15. package/dist/es2019/entry-points/animations.js +0 -2
  16. package/dist/es2019/internal/use-animated-visibility.js +14 -18
  17. package/dist/es2019/internal/use-safari-escape-fix.js +58 -0
  18. package/dist/es2019/popover/animations.js +1 -145
  19. package/dist/es2019/popover/popover.compiled.css +1 -10
  20. package/dist/es2019/popover/popover.js +8 -12
  21. package/dist/es2019/testing/polyfill.js +3 -0
  22. package/dist/esm/dialog/dialog-content.compiled.css +0 -10
  23. package/dist/esm/dialog/dialog-content.js +20 -28
  24. package/dist/esm/entry-points/animations.js +0 -2
  25. package/dist/esm/internal/use-animated-visibility.js +14 -18
  26. package/dist/esm/internal/use-safari-escape-fix.js +57 -0
  27. package/dist/esm/popover/animations.js +1 -144
  28. package/dist/esm/popover/popover.compiled.css +1 -10
  29. package/dist/esm/popover/popover.js +9 -12
  30. package/dist/esm/testing/polyfill.js +3 -0
  31. package/dist/types/dialog/types.d.ts +5 -5
  32. package/dist/types/entry-points/animations.d.ts +0 -2
  33. package/dist/types/internal/use-animated-visibility.d.ts +15 -33
  34. package/dist/types/internal/use-safari-escape-fix.d.ts +12 -0
  35. package/dist/types/popover/animations.d.ts +7 -52
  36. package/dist/types/popover/types.d.ts +6 -3
  37. package/package.json +6 -5
  38. package/dist/cjs/dialog/animations.js +0 -90
  39. package/dist/es2019/dialog/animations.js +0 -80
  40. package/dist/esm/dialog/animations.js +0 -82
  41. package/dist/types/dialog/animations.d.ts +0 -57
@@ -19,6 +19,7 @@ var _setStyle = require("../internal/set-style");
19
19
  var _useAnimatedVisibility = require("../internal/use-animated-visibility");
20
20
  var _useFocusWrap = require("../internal/use-focus-wrap");
21
21
  var _useInitialFocus = require("../internal/use-initial-focus");
22
+ var _animations = require("./animations");
22
23
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
23
24
  /**
24
25
  * Detects `popover="hint"` support via DOM reflection. SSR-safe, cached.
@@ -32,22 +33,16 @@ var supportsPopoverHint = (0, _once.default)(function () {
32
33
  element.setAttribute('popover', 'hint');
33
34
  return element.popover === 'hint';
34
35
  });
35
- var slideInKeyframes = null;
36
- var slideOutKeyframes = null;
37
36
 
38
37
  // These animation styles use the non-strict Compiled cssMap because they rely
39
38
  // on `@starting-style`, `:popover-open`, `[dir='rtl']`, and `allow-discrete`.
40
39
  // Keep them in this component so the Compiled transform can statically extract
41
40
  // every referenced style.
42
41
  var popoverAnimationStyles = {
43
- root: "_k8m011k7 _1oecdn4k _1s9z1a5r _1a5xt94y _12t71ign _34bx1n1a _bl8aglyw _156yru3m _8rmvglyw _2qm2ru3m",
44
- motion: "_y44vof1f _1o51q7pw _15ns179n _n70peoah",
45
- 'slide-and-fade': "_j7hq1l2h _5sagdn4k _1pgl1lbj _71rervlo _1psg1ign _o8i7indk",
46
- fade: "_j7hq1a5s _5sagdn4k _1pgl1lbj _71re1bym _1psg1ign _o8i7indk",
47
- 'scale-and-fade': "_j7hqneez _5sagdn4k _1pgl1lbj _71re1v6b _1psg1ign _o8i7indk"
42
+ root: "_y44vof1f _k8m011k7 _1oecdn4k _1s9z1a5r _1o51q7pw _5sagdn4k _1pgl1lbj _15ns179n _12t71ign _n70peoah _1psg1ign _o8i7indk _bl8aglyw _156yru3m _8rmvglyw _2qm2ru3m"
48
43
  };
49
44
  var styles = {
50
- root: "_19itglyw _ca0qidpf _u5f3idpf _n3tdidpf _19bvidpf _19pkidpf _2hwxidpf _otyridpf _18u0idpf _1r041wug _1reoewfl _18m9ewfl _bfhk1j28"
45
+ root: "_19itglyw _ca0qidpf _u5f3idpf _n3tdidpf _19bvidpf _19pkidpf _2hwxidpf _otyridpf _18u0idpf _1r041wug _1reoewfl _18m9ewfl _bfhk1j28 _4t3i1wug"
51
46
  };
52
47
  /**
53
48
  * Roles registered with the open layer observer as `popup`, so `closeLayers()`
@@ -95,7 +90,8 @@ var Popover = exports.Popover = /*#__PURE__*/(0, _react.forwardRef)(function Pop
95
90
  onOpenChange = _ref.onOpenChange,
96
91
  onEnterFinish = _ref.onEnterFinish,
97
92
  onExitFinish = _ref.onExitFinish,
98
- animate = _ref.animate,
93
+ _ref$animate = _ref.animate,
94
+ animate = _ref$animate === void 0 ? false : _ref$animate,
99
95
  placement = _ref.placement,
100
96
  testId = _ref.testId,
101
97
  isOpen = _ref.isOpen,
@@ -111,6 +107,7 @@ var Popover = exports.Popover = /*#__PURE__*/(0, _react.forwardRef)(function Pop
111
107
  var popoverId = idProp !== null && idProp !== void 0 ? idProp : "popover-".concat(autoId.replace(/:/g, ''));
112
108
  var _useAnimatedVisibilit = (0, _useAnimatedVisibility.useAnimatedVisibility)({
113
109
  isOpen: isOpen,
110
+ animationKind: 'popover',
114
111
  animate: animate,
115
112
  elementRef: ownRef,
116
113
  onEnterFinish: onEnterFinish,
@@ -278,12 +275,12 @@ var Popover = exports.Popover = /*#__PURE__*/(0, _react.forwardRef)(function Pop
278
275
  // stale custom properties on the element.
279
276
  (0, _react.useLayoutEffect)(function () {
280
277
  var element = ownRef.current;
281
- if (!element || !(preset !== null && preset !== void 0 && preset.getStyles) || !placement) {
278
+ if (!element || !placement) {
282
279
  return;
283
280
  }
284
281
  return (0, _setStyle.setStyle)({
285
282
  element: element,
286
- styles: preset.getStyles({
283
+ styles: (0, _animations.getPopupMotionStyles)({
287
284
  placement: placement
288
285
  })
289
286
  });
@@ -336,6 +333,6 @@ var Popover = exports.Popover = /*#__PURE__*/(0, _react.forwardRef)(function Pop
336
333
  "aria-label": label,
337
334
  "aria-labelledby": labelledBy,
338
335
  "data-testid": testId,
339
- className: (0, _runtime.ax)([styles.root, preset && popoverAnimationStyles.root, preset && popoverAnimationStyles[preset.name], consumerXcss])
336
+ className: (0, _runtime.ax)([styles.root, preset && popoverAnimationStyles.root, consumerXcss])
340
337
  }, children);
341
338
  });
@@ -526,6 +526,9 @@ if (typeof HTMLElement !== 'undefined') {
526
526
  // delegate every other selector to the original implementation.
527
527
  // Spec: https://html.spec.whatwg.org/multipage/semantics-other.html#selector-popover-open
528
528
  var originalMatches = Element.prototype.matches;
529
+ // The DOM lib types `Element.prototype.matches` as a set of type-predicate
530
+ // overloads. This polyfill intentionally replaces it with a plain boolean
531
+ // implementation, so we cast to the original member type.
529
532
  Element.prototype.matches = function patchedMatches(selector) {
530
533
  if (selector === ':popover-open') {
531
534
  return this.hasAttribute('data-popover-open');
@@ -20,23 +20,15 @@
20
20
  ._1o51q7pw{animation-fill-mode:forwards}
21
21
  ._1oeci9qs{transition-duration:var(--ds-duration-medium,.2s)}
22
22
  ._1oil1d08[open]{transition-timing-function:var(--ds-easing-inout-bold,cubic-bezier(.4,0,0,1))}
23
- ._1pgl1lbj{animation-timing-function:var(--ds-easing-in-practical,cubic-bezier(.6,0,.8,.6))}
24
23
  ._1q51ze3t{padding-block-start:var(--ds-space-0,0)}
25
- ._1rjx1d08[open]{animation-timing-function:var(--ds-easing-inout-bold,cubic-bezier(.4,0,0,1))}
26
24
  ._1rsueoah[open]::-ms-backdrop{animation-fill-mode:backwards}
27
25
  ._1rsueoah[open]::backdrop{animation-fill-mode:backwards}
28
26
  ._1s9z1a5r{transition-behavior:allow-discrete}
29
- ._1szp1mzy[open]{animation-duration:var(--ds-duration-long,.25s)}
30
27
  ._2hwx1wug{margin-right:auto}
31
- ._5sagi9qs{animation-duration:var(--ds-duration-medium,.2s)}
32
28
  ._85i5ze3t{padding-block-end:var(--ds-space-0,0)}
33
- ._8ar61bym[open]{animation-name:var(--ds-keyframe-fade-in,FadeIn0to100)}
34
- ._8ar6ftdr[open]{animation-name:k1s4adx,var(--ds-keyframe-fade-in,FadeIn0to100)}
35
29
  ._bfhk1j28{background-color:transparent}
36
30
  ._bozgze3t{padding-inline-start:var(--ds-space-0,0)}
37
31
  ._c71lglyw{max-height:none}
38
- ._j7hq1a5s{animation-name:var(--ds-keyframe-fade-out,FadeOut100to0)}
39
- ._j7hq1x6c{animation-name:k1gxuu8c,var(--ds-keyframe-fade-out,FadeOut100to0)}
40
32
  ._k8m011k7{transition-property:overlay,display}
41
33
  ._njwk11k7::-ms-backdrop{-ms-transition-property:overlay,display;transition-property:overlay,display}
42
34
  ._njwk11k7::backdrop{transition-property:overlay,display}
@@ -45,6 +37,4 @@
45
37
  ._y4tize3t{padding-inline-end:var(--ds-space-0,0)}
46
38
  ._yhpqi9qs::-ms-backdrop{transition-duration:var(--ds-duration-medium,.2s)}
47
39
  ._yhpqi9qs::backdrop{transition-duration:var(--ds-duration-medium,.2s)}
48
- @keyframes k1gxuu8c{0%{transform:none}to{transform:translateY(calc(var(--ds-dialog-ty, 9pt)*-1))}}
49
- @keyframes k1s4adx{0%{transform:translateY(calc(var(--ds-dialog-ty, 9pt)))}to{transform:none}}
50
40
  @media (prefers-reduced-motion:reduce){._bl8aglyw{animation-name:none}._156y147i{transition-duration:var(--ds-duration-instant,0ms)}._10aiglyw[open]{animation-name:none}._aeld147i[open]{transition-duration:var(--ds-duration-instant,0ms)}._l57nglyw::-ms-backdrop{animation-name:none}._l57nglyw::backdrop{animation-name:none}._1sbq147i::-ms-backdrop{transition-duration:var(--ds-duration-instant,0ms)}._1sbq147i::backdrop{transition-duration:var(--ds-duration-instant,0ms)}._19awglyw[open]::-ms-backdrop{animation-name:none}._19awglyw[open]::backdrop{animation-name:none}._1i6z147i[open]::-ms-backdrop{transition-duration:var(--ds-duration-instant,0ms)}._1i6z147i[open]::backdrop{transition-duration:var(--ds-duration-instant,0ms)}}
@@ -6,22 +6,16 @@ import { bind } from 'bind-event-listener';
6
6
  import mergeRefs from '@atlaskit/ds-lib/merge-refs';
7
7
  import noop from '@atlaskit/ds-lib/noop';
8
8
  import { useNotifyOpenLayerObserver } from '@atlaskit/layering/experimental/open-layer-observer';
9
- import { setStyle } from '../internal/set-style';
10
9
  import { useAnimatedVisibility } from '../internal/use-animated-visibility';
11
10
  import { useFocusWrap } from '../internal/use-focus-wrap';
12
- const slideInKeyframes = null;
13
- const slideOutKeyframes = null;
14
-
11
+ import { useSafariEscapeFix } from '../internal/use-safari-escape-fix';
15
12
  // These animation styles use the non-strict Compiled cssMap because they rely
16
13
  // on `@starting-style`, `[open]`, and `allow-discrete`. Keep them in this
17
14
  // component so the Compiled transform can statically extract every referenced
18
15
  // style.
19
16
  const dialogAnimationStyles = {
20
17
  root: "_k8m011k7 _1oeci9qs _1s9z1a5r _1fsl1mzy _1oil1d08",
21
- motion: "_y44vgeqi _1o51q7pw _1iu47kvq _1i0neoah",
22
- 'slide-up-and-fade': "_j7hq1x6c _5sagi9qs _1pgl1lbj _8ar6ftdr _1szp1mzy _1rjx1d08",
23
- fade: "_j7hq1a5s _5sagi9qs _1pgl1lbj _8ar61bym _1szp1mzy _1rjx1d08",
24
- custom: ""
18
+ motion: "_y44vgeqi _1o51q7pw _1iu47kvq _1i0neoah"
25
19
  };
26
20
  const styles = {
27
21
  dialog: "_19itglyw _19pk1wug _2hwx1wug _otyr1wug _18u01wug _1q51ze3t _y4tize3t _85i5ze3t _bozgze3t _p12fglyw _c71lglyw _bfhk1j28 _bl8aglyw _156y147i _10aiglyw _aeld147i"
@@ -55,7 +49,7 @@ export const Dialog = /*#__PURE__*/forwardRef(function Dialog({
55
49
  onClose,
56
50
  onEnterFinish,
57
51
  onExitFinish,
58
- animate,
52
+ animate = false,
59
53
  xcss: consumerXcss,
60
54
  style,
61
55
  testId,
@@ -73,6 +67,7 @@ export const Dialog = /*#__PURE__*/forwardRef(function Dialog({
73
67
  preset
74
68
  } = useAnimatedVisibility({
75
69
  isOpen,
70
+ animationKind: 'dialog',
76
71
  animate,
77
72
  elementRef: ownRef,
78
73
  onEnterFinish,
@@ -122,23 +117,14 @@ export const Dialog = /*#__PURE__*/forwardRef(function Dialog({
122
117
  }
123
118
  }, [isOpen]);
124
119
 
125
- // Apply preset-provided animation custom properties (e.g. the
126
- // `--ds-dialog-ty` slide distance for `dialogSlideUpAndFade`). The
127
- // cleanup restores the previous inline values so a preset change does
128
- // not leave stale custom properties on the element. Dialog presets do
129
- // not vary by placement, so an empty placement is passed.
130
- useLayoutEffect(() => {
131
- const dialog = ownRef.current;
132
- if (!dialog || !(preset !== null && preset !== void 0 && preset.getStyles)) {
133
- return;
134
- }
135
- return setStyle({
136
- element: dialog,
137
- styles: preset.getStyles({
138
- placement: {}
139
- })
140
- });
141
- }, [preset, isVisible]);
120
+ // Safari bug: escape closes open dialog and popovers, rather than just innermost popover
121
+ // See notes/decisions/safari-escape-nested-popover-in-dialog.md
122
+ const {
123
+ shouldIgnoreEscape
124
+ } = useSafariEscapeFix({
125
+ dialogRef: ownRef,
126
+ isVisible
127
+ });
142
128
 
143
129
  // Handle native Escape (cancel event)
144
130
  const handleCancel = useCallback(event => {
@@ -151,10 +137,16 @@ export const Dialog = /*#__PURE__*/forwardRef(function Dialog({
151
137
  return;
152
138
  }
153
139
  event.preventDefault();
140
+ // Spurious Safari `cancel`: the keydown snapshot saw an open child
141
+ // popover, so this Escape belongs to that popover (light-dismissed
142
+ // natively). Keep the dialog open instead of forwarding to `onClose`.
143
+ if (shouldIgnoreEscape()) {
144
+ return;
145
+ }
154
146
  onClose({
155
147
  reason: 'escape'
156
148
  });
157
- }, [onClose]);
149
+ }, [onClose, shouldIgnoreEscape]);
158
150
 
159
151
  // Handle backdrop click
160
152
  // Attached via bind-event-listener rather than a React prop so we avoid
@@ -200,6 +192,6 @@ export const Dialog = /*#__PURE__*/forwardRef(function Dialog({
200
192
  style: style,
201
193
  onCancel: handleCancel,
202
194
  "data-testid": testId,
203
- className: ax([styles.dialog, shouldHideBackdrop ? backdropStyles.hidden : backdropStyles.root, preset && backdropStyles.motion, preset && dialogAnimationStyles.root, preset && dialogAnimationStyles[preset.name], consumerXcss])
195
+ className: ax([styles.dialog, shouldHideBackdrop ? backdropStyles.hidden : backdropStyles.root, preset && backdropStyles.motion, preset && dialogAnimationStyles.root, preset && dialogAnimationStyles.motion, consumerXcss])
204
196
  }, children);
205
197
  });
@@ -1,2 +0,0 @@
1
- export { dialogMotion, dialogSlideUpAndFade, dialogFade } from '../dialog/animations';
2
- export { popupMotion, slideAndFade, fade, scaleAndFade } from '../popover/animations';
@@ -1,18 +1,6 @@
1
1
  import { useEffect, useLayoutEffect, useRef, useState } from 'react';
2
2
  import { bind, bindAll } from 'bind-event-listener';
3
3
  import { prefersReducedMotion } from './reduced-motion';
4
-
5
- /**
6
- * A self-contained animation preset for top-layer elements.
7
- *
8
- * Contains a stable preset name used by the host component to select its
9
- * component-local Compiled style entry, plus optional per-placement inline
10
- * styles.
11
- *
12
- * The actual Compiled styles are kept static and component-local so the
13
- * Compiled transform can extract them at build time.
14
- */
15
-
16
4
  /**
17
5
  * Using the recommended motion timings for safety net.
18
6
  *
@@ -72,6 +60,13 @@ function runOnTransitionEndOrTimeout({
72
60
  return teardown;
73
61
  }
74
62
 
63
+ /**
64
+ * Animation config for entry/exit transitions.
65
+ *
66
+ * Currently no configuration is supported via this prop,
67
+ * and this is just a placeholder.
68
+ */
69
+
75
70
  /**
76
71
  * Visibility lifecycle phase shared between `useAnimatedVisibility`,
77
72
  * `useFocusWrap`, `useInitialFocus`, and the `Popover` / `Dialog`
@@ -135,6 +130,7 @@ function runOnTransitionEndOrTimeout({
135
130
  */
136
131
  export function useAnimatedVisibility({
137
132
  isOpen,
133
+ animationKind,
138
134
  animate,
139
135
  elementRef,
140
136
  onEnterFinish,
@@ -186,7 +182,7 @@ export function useAnimatedVisibility({
186
182
  // move to `open`. The fallback guarantees settle on swallowed events
187
183
  // (zero-duration, browser quirk, mid-transition unmount).
188
184
  useEffect(() => {
189
- if (phase !== 'entering' || !(preset !== null && preset !== void 0 && preset.kind)) {
185
+ if (phase !== 'entering') {
190
186
  return;
191
187
  }
192
188
  const element = elementRef.current;
@@ -195,7 +191,7 @@ export function useAnimatedVisibility({
195
191
  }
196
192
  return runOnTransitionEndOrTimeout({
197
193
  element,
198
- safetyNetMs: safetyNetMsMap[preset.kind].enter + 50,
194
+ safetyNetMs: safetyNetMsMap[animationKind].enter + 50,
199
195
  onSettled: () => {
200
196
  var _onEnterFinishRef$cur;
201
197
  // Fire the consumer callback before transitioning phase so any
@@ -206,7 +202,7 @@ export function useAnimatedVisibility({
206
202
  setPhase(current => current === 'entering' ? 'open' : current);
207
203
  }
208
204
  });
209
- }, [phase, preset === null || preset === void 0 ? void 0 : preset.kind, elementRef]);
205
+ }, [phase, animationKind, elementRef]);
210
206
 
211
207
  // Non-animated entry callback. The lifecycle goes `closed → open`
212
208
  // directly with no `entering` phase; fire `onEnterFinish` on that jump.
@@ -227,7 +223,7 @@ export function useAnimatedVisibility({
227
223
  // Animated exit: wait for `transitionend` (or a safety-net timeout),
228
224
  // fire `onExitFinish`, then move to `closed` to unmount the host.
229
225
  useEffect(() => {
230
- if (phase !== 'exiting' || !willAnimate || !(preset !== null && preset !== void 0 && preset.kind)) {
226
+ if (phase !== 'exiting' || !willAnimate) {
231
227
  return;
232
228
  }
233
229
  const element = elementRef.current;
@@ -236,7 +232,7 @@ export function useAnimatedVisibility({
236
232
  }
237
233
  return runOnTransitionEndOrTimeout({
238
234
  element,
239
- safetyNetMs: safetyNetMsMap[preset.kind].exit + 50,
235
+ safetyNetMs: safetyNetMsMap[animationKind].exit + 50,
240
236
  onSettled: () => {
241
237
  var _onExitFinishRef$curr;
242
238
  // Fire the consumer callback before transitioning phase to
@@ -249,7 +245,7 @@ export function useAnimatedVisibility({
249
245
  setPhase(current => current === 'exiting' ? 'closed' : current);
250
246
  }
251
247
  });
252
- }, [phase, willAnimate, preset === null || preset === void 0 ? void 0 : preset.kind, elementRef]);
248
+ }, [phase, willAnimate, animationKind, elementRef]);
253
249
 
254
250
  // Non-animated exit: wait for the browser's `toggle` (closed) or
255
251
  // `close` event before unmounting, so the consumer's listener (close
@@ -0,0 +1,58 @@
1
+ import { useEffect, useMemo, useRef } from 'react';
2
+ import { bind } from 'bind-event-listener';
3
+ import { isSafari } from '@atlaskit/ds-lib/is-safari';
4
+
5
+ /**
6
+ * Safari-only guard: WebKit fires dialog `cancel` on Escape even when a child
7
+ * `auto`/`hint` popover consumed it, closing the dialog too.
8
+ * See notes/decisions/safari-escape-nested-popover-in-dialog.md
9
+ */
10
+ export function useSafariEscapeFix({
11
+ dialogRef,
12
+ isVisible
13
+ }) {
14
+ const childPopoverOpenAtEscapeRef = useRef(false);
15
+ useEffect(() => {
16
+ if (!isSafari()) {
17
+ return;
18
+ }
19
+ const dialog = dialogRef.current;
20
+ if (!dialog) {
21
+ return;
22
+ }
23
+
24
+ // Bind to `document`, not the dialog: on Safari focus can be on `<body>`
25
+ // (buttons are not focused on click), where a dialog-scoped listener would
26
+ // never fire. The query below stays scoped to this dialog's subtree.
27
+ return bind(document, {
28
+ type: 'keydown',
29
+ listener(event) {
30
+ if (event.key !== 'Escape') {
31
+ return;
32
+ }
33
+ try {
34
+ childPopoverOpenAtEscapeRef.current = Boolean(dialog.querySelector('[popover="auto"]:popover-open, [popover="hint"]:popover-open'));
35
+ } catch {
36
+ // `:popover-open` is unsupported in some environments (e.g. jsdom);
37
+ // treat as "no child popover" so a plain Escape still closes the dialog.
38
+ childPopoverOpenAtEscapeRef.current = false;
39
+ }
40
+ },
41
+ options: {
42
+ capture: true
43
+ }
44
+ });
45
+ }, [dialogRef, isVisible]);
46
+ const api = useMemo(() => {
47
+ return {
48
+ shouldIgnoreEscape() {
49
+ if (childPopoverOpenAtEscapeRef.current) {
50
+ childPopoverOpenAtEscapeRef.current = false;
51
+ return true;
52
+ }
53
+ return false;
54
+ }
55
+ };
56
+ }, []);
57
+ return api;
58
+ }
@@ -1,5 +1,5 @@
1
1
  import { getPlacement } from '../internal/resolve-placement';
2
- function getPopupMotionStyles({
2
+ export function getPopupMotionStyles({
3
3
  placement
4
4
  }) {
5
5
  const {
@@ -43,148 +43,4 @@ function getPopupMotionStyles({
43
43
  property: '--ds-popover-motion-exit',
44
44
  value: "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)"
45
45
  }];
46
- }
47
-
48
- // eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
49
- export function popupMotion() {
50
- return {
51
- kind: 'popover',
52
- name: 'motion',
53
- getStyles: getPopupMotionStyles
54
- };
55
- }
56
- function getSlideAndFadeStyles({
57
- placement,
58
- distance
59
- }) {
60
- const pos = `${distance}px`;
61
- const neg = `-${distance}px`;
62
- const {
63
- axis,
64
- edge
65
- } = getPlacement({
66
- placement
67
- });
68
- if (axis === 'block' && edge === 'start') {
69
- return [{
70
- property: '--ds-popover-tx',
71
- value: '0'
72
- }, {
73
- property: '--ds-popover-ty',
74
- value: pos
75
- }];
76
- }
77
- if (axis === 'block' && edge === 'end') {
78
- return [{
79
- property: '--ds-popover-tx',
80
- value: '0'
81
- }, {
82
- property: '--ds-popover-ty',
83
- value: neg
84
- }];
85
- }
86
- if (axis === 'inline' && edge === 'start') {
87
- return [{
88
- property: '--ds-popover-tx',
89
- value: pos
90
- }, {
91
- property: '--ds-popover-ty',
92
- value: '0'
93
- }];
94
- }
95
- if (axis === 'inline' && edge === 'end') {
96
- return [{
97
- property: '--ds-popover-tx',
98
- value: neg
99
- }, {
100
- property: '--ds-popover-ty',
101
- value: '0'
102
- }];
103
- }
104
- return [{
105
- property: '--ds-popover-tx',
106
- value: '0'
107
- }, {
108
- property: '--ds-popover-ty',
109
- value: neg
110
- }];
111
- }
112
-
113
- /**
114
- * Directional slide + opacity animation.
115
- *
116
- * The popover slides in from the direction opposite its placement edge
117
- * (e.g. a `block-end` popover slides down from above).
118
- *
119
- * @param options.distance - Slide distance in pixels. Defaults to `4`.
120
- *
121
- * @example
122
- * ```tsx
123
- * import { slideAndFade } from '@atlaskit/top-layer/animations';
124
- *
125
- * // Default 4px slide (tooltip)
126
- * <Popover animate={slideAndFade()} />
127
- *
128
- * // Custom 8px slide (popup / dropdown)
129
- * <Popover animate={slideAndFade({ distance: 8 })} />
130
- * ```
131
- */
132
- // eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
133
- export function slideAndFade(options) {
134
- var _options$distance;
135
- const distance = (_options$distance = options === null || options === void 0 ? void 0 : options.distance) !== null && _options$distance !== void 0 ? _options$distance : 4;
136
- return {
137
- kind: 'popover',
138
- name: 'slide-and-fade',
139
- getStyles: ({
140
- placement
141
- }) => getSlideAndFadeStyles({
142
- placement,
143
- distance
144
- })
145
- };
146
- }
147
-
148
- // fade
149
-
150
- /**
151
- * Simple opacity transition, no transform.
152
- *
153
- * Suitable for popup dialogs or content that does not need directional motion.
154
- *
155
- * @example
156
- * ```tsx
157
- * import { fade } from '@atlaskit/top-layer/animations';
158
- *
159
- * <Popover animate={fade()} />
160
- * ```
161
- */
162
- // eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
163
- export function fade() {
164
- return {
165
- kind: 'popover',
166
- name: 'fade'
167
- };
168
- }
169
-
170
- // scaleAndFade
171
-
172
- /**
173
- * Scale from 0.95 + opacity transition.
174
- *
175
- * Suitable for menus, dropdowns, and select options.
176
- *
177
- * @example
178
- * ```tsx
179
- * import { scaleAndFade } from '@atlaskit/top-layer/animations';
180
- *
181
- * <Popover animate={scaleAndFade()} />
182
- * ```
183
- */
184
- // eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
185
- export function scaleAndFade() {
186
- return {
187
- kind: 'popover',
188
- name: 'scale-and-fade'
189
- };
190
46
  }
@@ -7,7 +7,6 @@
7
7
  ._18u0idpf{margin-left:0}
8
8
  ._19bvidpf{padding-left:0}
9
9
  ._19pkidpf{margin-top:0}
10
- ._1a5xt94y{--dir:1px}
11
10
  ._1o51q7pw{animation-fill-mode:forwards}
12
11
  ._1oecdn4k{transition-duration:var(--ds-duration-xshort,.1s)}
13
12
  ._1pgl1lbj{animation-timing-function:var(--ds-easing-in-practical,cubic-bezier(.6,0,.8,.6))}
@@ -15,22 +14,14 @@
15
14
  ._1reoewfl{overflow-x:visible}
16
15
  ._1s9z1a5r{transition-behavior:allow-discrete}
17
16
  ._2hwxidpf{margin-right:0}
17
+ ._4t3i1wug{height:auto}
18
18
  ._5sagdn4k{animation-duration:var(--ds-duration-xshort,.1s)}
19
- ._71re1bym:popover-open{animation-name:var(--ds-keyframe-fade-in,FadeIn0to100)}
20
- ._71re1v6b:popover-open{animation-name:var(--ds-keyframe-fade-in,FadeIn0to100),var(--ds-keyframe-scale-in-small,ScaleIn95to100)}
21
- ._71rervlo:popover-open{animation-name:k1ut9kob,var(--ds-keyframe-fade-in,FadeIn0to100)}
22
19
  ._bfhk1j28{background-color:transparent}
23
20
  ._ca0qidpf{padding-top:0}
24
- ._j7hq1a5s{animation-name:var(--ds-keyframe-fade-out,FadeOut100to0)}
25
- ._j7hq1l2h{animation-name:k1e4c1ew,var(--ds-keyframe-fade-out,FadeOut100to0)}
26
- ._j7hqneez{animation-name:var(--ds-keyframe-fade-out,FadeOut100to0),var(--ds-keyframe-scale-out-small,ScaleOut100to95)}
27
21
  ._k8m011k7{transition-property:overlay,display}
28
22
  ._n3tdidpf{padding-bottom:0}
29
23
  ._n70peoah:popover-open{animation-fill-mode:backwards}
30
24
  ._o8i7indk:popover-open{animation-timing-function:var(--ds-easing-out-practical,cubic-bezier(.4,1,.6,1))}
31
25
  ._otyridpf{margin-bottom:0}
32
26
  ._u5f3idpf{padding-right:0}
33
- [dir=rtl] ._34bx1n1a{--dir:-1px}
34
- @keyframes k1e4c1ew{0%{transform:none}to{transform:translate(calc(var(--dir)*var(--ds-popover-tx, 0)),var(--ds-popover-ty,0))}}
35
- @keyframes k1ut9kob{0%{transform:translate(calc(var(--dir)*var(--ds-popover-tx, 0)),var(--ds-popover-ty,0))}to{transform:none}}
36
27
  @media (prefers-reduced-motion:reduce){._bl8aglyw{animation-name:none}._156yru3m{transition-duration:0s}._8rmvglyw:popover-open{animation-name:none}._2qm2ru3m:popover-open{transition-duration:0s}}
@@ -11,6 +11,7 @@ import { setStyle } from '../internal/set-style';
11
11
  import { useAnimatedVisibility } from '../internal/use-animated-visibility';
12
12
  import { useFocusWrap } from '../internal/use-focus-wrap';
13
13
  import { useInitialFocus } from '../internal/use-initial-focus';
14
+ import { getPopupMotionStyles } from './animations';
14
15
  /**
15
16
  * Detects `popover="hint"` support via DOM reflection. SSR-safe, cached.
16
17
  */
@@ -23,22 +24,16 @@ const supportsPopoverHint = once(() => {
23
24
  element.setAttribute('popover', 'hint');
24
25
  return element.popover === 'hint';
25
26
  });
26
- const slideInKeyframes = null;
27
- const slideOutKeyframes = null;
28
27
 
29
28
  // These animation styles use the non-strict Compiled cssMap because they rely
30
29
  // on `@starting-style`, `:popover-open`, `[dir='rtl']`, and `allow-discrete`.
31
30
  // Keep them in this component so the Compiled transform can statically extract
32
31
  // every referenced style.
33
32
  const popoverAnimationStyles = {
34
- root: "_k8m011k7 _1oecdn4k _1s9z1a5r _1a5xt94y _12t71ign _34bx1n1a _bl8aglyw _156yru3m _8rmvglyw _2qm2ru3m",
35
- motion: "_y44vof1f _1o51q7pw _15ns179n _n70peoah",
36
- 'slide-and-fade': "_j7hq1l2h _5sagdn4k _1pgl1lbj _71rervlo _1psg1ign _o8i7indk",
37
- fade: "_j7hq1a5s _5sagdn4k _1pgl1lbj _71re1bym _1psg1ign _o8i7indk",
38
- 'scale-and-fade': "_j7hqneez _5sagdn4k _1pgl1lbj _71re1v6b _1psg1ign _o8i7indk"
33
+ root: "_y44vof1f _k8m011k7 _1oecdn4k _1s9z1a5r _1o51q7pw _5sagdn4k _1pgl1lbj _15ns179n _12t71ign _n70peoah _1psg1ign _o8i7indk _bl8aglyw _156yru3m _8rmvglyw _2qm2ru3m"
39
34
  };
40
35
  const styles = {
41
- root: "_19itglyw _ca0qidpf _u5f3idpf _n3tdidpf _19bvidpf _19pkidpf _2hwxidpf _otyridpf _18u0idpf _1r041wug _1reoewfl _18m9ewfl _bfhk1j28"
36
+ root: "_19itglyw _ca0qidpf _u5f3idpf _n3tdidpf _19bvidpf _19pkidpf _2hwxidpf _otyridpf _18u0idpf _1r041wug _1reoewfl _18m9ewfl _bfhk1j28 _4t3i1wug"
42
37
  };
43
38
  /**
44
39
  * Roles registered with the open layer observer as `popup`, so `closeLayers()`
@@ -85,7 +80,7 @@ export const Popover = /*#__PURE__*/forwardRef(function Popover({
85
80
  onOpenChange,
86
81
  onEnterFinish,
87
82
  onExitFinish,
88
- animate,
83
+ animate = false,
89
84
  placement,
90
85
  testId,
91
86
  isOpen,
@@ -106,6 +101,7 @@ export const Popover = /*#__PURE__*/forwardRef(function Popover({
106
101
  preset
107
102
  } = useAnimatedVisibility({
108
103
  isOpen,
104
+ animationKind: 'popover',
109
105
  animate,
110
106
  elementRef: ownRef,
111
107
  onEnterFinish,
@@ -271,12 +267,12 @@ export const Popover = /*#__PURE__*/forwardRef(function Popover({
271
267
  // stale custom properties on the element.
272
268
  useLayoutEffect(() => {
273
269
  const element = ownRef.current;
274
- if (!element || !(preset !== null && preset !== void 0 && preset.getStyles) || !placement) {
270
+ if (!element || !placement) {
275
271
  return;
276
272
  }
277
273
  return setStyle({
278
274
  element,
279
- styles: preset.getStyles({
275
+ styles: getPopupMotionStyles({
280
276
  placement
281
277
  })
282
278
  });
@@ -329,6 +325,6 @@ export const Popover = /*#__PURE__*/forwardRef(function Popover({
329
325
  "aria-label": label,
330
326
  "aria-labelledby": labelledBy,
331
327
  "data-testid": testId,
332
- className: ax([styles.root, preset && popoverAnimationStyles.root, preset && popoverAnimationStyles[preset.name], consumerXcss])
328
+ className: ax([styles.root, preset && popoverAnimationStyles.root, consumerXcss])
333
329
  }, children);
334
330
  });
@@ -540,6 +540,9 @@ if (typeof HTMLElement !== 'undefined') {
540
540
  // delegate every other selector to the original implementation.
541
541
  // Spec: https://html.spec.whatwg.org/multipage/semantics-other.html#selector-popover-open
542
542
  const originalMatches = Element.prototype.matches;
543
+ // The DOM lib types `Element.prototype.matches` as a set of type-predicate
544
+ // overloads. This polyfill intentionally replaces it with a plain boolean
545
+ // implementation, so we cast to the original member type.
543
546
  Element.prototype.matches = function patchedMatches(selector) {
544
547
  if (selector === ':popover-open') {
545
548
  return this.hasAttribute('data-popover-open');