@atlaskit/top-layer 4.4.5 → 5.0.1

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 (137) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +13 -4
  3. package/dist/cjs/dialog/dialog-content.js +2 -0
  4. package/dist/cjs/entry-points/use-popover-id.js +5 -10
  5. package/dist/cjs/internal/anchor-positioning/apply-anchor-positioning.js +86 -0
  6. package/dist/cjs/internal/anchor-positioning/cross-axis-shift-margins.js +34 -0
  7. package/dist/cjs/internal/anchor-positioning/edge-margin.js +21 -0
  8. package/dist/cjs/internal/anchor-positioning/fit-margins.js +77 -0
  9. package/dist/cjs/internal/anchor-positioning/get-existing-anchor-name.js +18 -0
  10. package/dist/cjs/internal/{placement-to-position-area.js → anchor-positioning/placement-to-position-area.js} +11 -11
  11. package/dist/cjs/internal/anchor-positioning/placement-to-try-fallbacks.js +95 -0
  12. package/dist/cjs/internal/anchored-popover-size.js +345 -0
  13. package/dist/cjs/internal/get-aria-for-trigger.js +7 -13
  14. package/dist/cjs/internal/is-native-element-open.js +19 -0
  15. package/dist/cjs/internal/{anchor-positioning-fallback.js → javascript-fallback/anchor-positioning-fallback.js} +12 -33
  16. package/dist/cjs/internal/javascript-fallback/apply-javascript-fallback-positioning.js +297 -0
  17. package/dist/cjs/internal/javascript-fallback/resolve-css-length-to-pixels.js +90 -0
  18. package/dist/cjs/internal/javascript-fallback/warn-fallback-failure.js +19 -0
  19. package/dist/cjs/internal/resolve-placement.js +14 -6
  20. package/dist/cjs/internal/supports-anchor-positioning.js +17 -0
  21. package/dist/cjs/internal/supports-anchor-size.js +17 -0
  22. package/dist/cjs/internal/supports-css-declaration.js +25 -0
  23. package/dist/cjs/internal/surface-reset.js +5 -0
  24. package/dist/cjs/internal/use-anchored-popover-at-point.js +124 -0
  25. package/dist/cjs/internal/use-anchored-popover.js +277 -0
  26. package/dist/cjs/internal/use-animated-visibility.js +11 -23
  27. package/dist/cjs/popover/popover.compiled.css +4 -0
  28. package/dist/cjs/popover/popover.js +63 -61
  29. package/dist/cjs/testing/polyfill.js +6 -7
  30. package/dist/es2019/dialog/dialog-content.js +2 -0
  31. package/dist/es2019/entry-points/use-popover-id.js +6 -10
  32. package/dist/es2019/internal/anchor-positioning/apply-anchor-positioning.js +83 -0
  33. package/dist/es2019/internal/anchor-positioning/cross-axis-shift-margins.js +29 -0
  34. package/dist/es2019/internal/anchor-positioning/edge-margin.js +18 -0
  35. package/dist/es2019/internal/anchor-positioning/fit-margins.js +69 -0
  36. package/dist/es2019/internal/anchor-positioning/get-existing-anchor-name.js +12 -0
  37. package/dist/es2019/internal/{placement-to-position-area.js → anchor-positioning/placement-to-position-area.js} +9 -9
  38. package/dist/es2019/internal/anchor-positioning/placement-to-try-fallbacks.js +94 -0
  39. package/dist/es2019/internal/anchored-popover-size.js +343 -0
  40. package/dist/es2019/internal/get-aria-for-trigger.js +7 -13
  41. package/dist/es2019/internal/is-native-element-open.js +14 -0
  42. package/dist/es2019/internal/{anchor-positioning-fallback.js → javascript-fallback/anchor-positioning-fallback.js} +12 -33
  43. package/dist/es2019/internal/javascript-fallback/apply-javascript-fallback-positioning.js +291 -0
  44. package/dist/es2019/internal/javascript-fallback/resolve-css-length-to-pixels.js +87 -0
  45. package/dist/es2019/internal/javascript-fallback/warn-fallback-failure.js +14 -0
  46. package/dist/es2019/internal/resolve-placement.js +14 -6
  47. package/dist/es2019/internal/supports-anchor-positioning.js +12 -0
  48. package/dist/es2019/internal/supports-anchor-size.js +12 -0
  49. package/dist/es2019/internal/supports-css-declaration.js +20 -0
  50. package/dist/es2019/internal/surface-reset.js +1 -0
  51. package/dist/es2019/internal/use-anchored-popover-at-point.js +116 -0
  52. package/dist/es2019/internal/use-anchored-popover.js +265 -0
  53. package/dist/es2019/internal/use-animated-visibility.js +1 -14
  54. package/dist/es2019/popover/popover.compiled.css +4 -0
  55. package/dist/es2019/popover/popover.js +69 -61
  56. package/dist/es2019/testing/polyfill.js +6 -7
  57. package/dist/esm/dialog/dialog-content.js +2 -0
  58. package/dist/esm/entry-points/use-popover-id.js +6 -10
  59. package/dist/esm/internal/anchor-positioning/apply-anchor-positioning.js +80 -0
  60. package/dist/esm/internal/anchor-positioning/cross-axis-shift-margins.js +28 -0
  61. package/dist/esm/internal/anchor-positioning/edge-margin.js +15 -0
  62. package/dist/esm/internal/anchor-positioning/fit-margins.js +71 -0
  63. package/dist/esm/internal/anchor-positioning/get-existing-anchor-name.js +12 -0
  64. package/dist/esm/internal/{placement-to-position-area.js → anchor-positioning/placement-to-position-area.js} +11 -12
  65. package/dist/esm/internal/anchor-positioning/placement-to-try-fallbacks.js +89 -0
  66. package/dist/esm/internal/anchored-popover-size.js +338 -0
  67. package/dist/esm/internal/get-aria-for-trigger.js +7 -13
  68. package/dist/esm/internal/is-native-element-open.js +13 -0
  69. package/dist/esm/internal/{anchor-positioning-fallback.js → javascript-fallback/anchor-positioning-fallback.js} +12 -33
  70. package/dist/esm/internal/javascript-fallback/apply-javascript-fallback-positioning.js +290 -0
  71. package/dist/esm/internal/javascript-fallback/resolve-css-length-to-pixels.js +85 -0
  72. package/dist/esm/internal/javascript-fallback/warn-fallback-failure.js +13 -0
  73. package/dist/esm/internal/resolve-placement.js +14 -6
  74. package/dist/esm/internal/supports-anchor-positioning.js +12 -0
  75. package/dist/esm/internal/supports-anchor-size.js +12 -0
  76. package/dist/esm/internal/supports-css-declaration.js +19 -0
  77. package/dist/esm/internal/surface-reset.js +1 -0
  78. package/dist/esm/internal/use-anchored-popover-at-point.js +118 -0
  79. package/dist/esm/internal/use-anchored-popover.js +271 -0
  80. package/dist/esm/internal/use-animated-visibility.js +10 -22
  81. package/dist/esm/popover/popover.compiled.css +4 -0
  82. package/dist/esm/popover/popover.js +63 -61
  83. package/dist/esm/testing/polyfill.js +6 -7
  84. package/dist/types/entry-points/placement-map.d.ts +2 -2
  85. package/dist/types/entry-points/use-popover-id.d.ts +3 -7
  86. package/dist/types/internal/anchor-positioning/apply-anchor-positioning.d.ts +26 -0
  87. package/dist/types/internal/anchor-positioning/cross-axis-shift-margins.d.ts +19 -0
  88. package/dist/types/internal/anchor-positioning/edge-margin.d.ts +10 -0
  89. package/dist/types/internal/anchor-positioning/fit-margins.d.ts +30 -0
  90. package/dist/types/internal/anchor-positioning/get-existing-anchor-name.d.ts +8 -0
  91. package/dist/types/internal/{placement-to-position-area.d.ts → anchor-positioning/placement-to-position-area.d.ts} +10 -6
  92. package/dist/types/internal/anchor-positioning/placement-to-try-fallbacks.d.ts +30 -0
  93. package/dist/types/internal/anchored-popover-size.d.ts +126 -0
  94. package/dist/types/internal/get-aria-for-trigger.d.ts +13 -25
  95. package/dist/types/internal/is-native-element-open.d.ts +3 -0
  96. package/dist/types/internal/javascript-fallback/anchor-positioning-fallback.d.ts +36 -0
  97. package/dist/types/internal/javascript-fallback/apply-javascript-fallback-positioning.d.ts +15 -0
  98. package/dist/types/internal/javascript-fallback/resolve-css-length-to-pixels.d.ts +20 -0
  99. package/dist/types/internal/javascript-fallback/warn-fallback-failure.d.ts +7 -0
  100. package/dist/types/internal/resolve-placement.d.ts +18 -2
  101. package/dist/types/internal/supports-anchor-positioning.d.ts +5 -0
  102. package/dist/types/internal/supports-anchor-size.d.ts +5 -0
  103. package/dist/types/internal/supports-css-declaration.d.ts +11 -0
  104. package/dist/types/internal/surface-reset.d.ts +25 -0
  105. package/dist/types/internal/use-anchored-popover-at-point.d.ts +48 -0
  106. package/dist/types/internal/use-anchored-popover.d.ts +143 -0
  107. package/dist/types/popover/index.d.ts +2 -1
  108. package/dist/types/popover/popover.d.ts +1 -1
  109. package/dist/types/popover/types.d.ts +15 -17
  110. package/dist/types/testing/polyfill.d.ts +3 -3
  111. package/package.json +2 -2
  112. package/use-anchored-popover/package.json +10 -0
  113. package/use-anchored-popover-at-point/package.json +10 -0
  114. package/dist/cjs/internal/placement-to-try-fallbacks.js +0 -108
  115. package/dist/cjs/internal/resolve-css-length-to-pixels.js +0 -104
  116. package/dist/cjs/internal/use-anchor-position-at-point.js +0 -129
  117. package/dist/cjs/internal/use-anchor-position.js +0 -501
  118. package/dist/cjs/internal/use-width-from-anchor.js +0 -124
  119. package/dist/es2019/internal/placement-to-try-fallbacks.js +0 -108
  120. package/dist/es2019/internal/resolve-css-length-to-pixels.js +0 -101
  121. package/dist/es2019/internal/use-anchor-position-at-point.js +0 -124
  122. package/dist/es2019/internal/use-anchor-position.js +0 -480
  123. package/dist/es2019/internal/use-width-from-anchor.js +0 -120
  124. package/dist/esm/internal/placement-to-try-fallbacks.js +0 -103
  125. package/dist/esm/internal/resolve-css-length-to-pixels.js +0 -98
  126. package/dist/esm/internal/use-anchor-position-at-point.js +0 -124
  127. package/dist/esm/internal/use-anchor-position.js +0 -494
  128. package/dist/esm/internal/use-width-from-anchor.js +0 -118
  129. package/dist/types/internal/anchor-positioning-fallback.d.ts +0 -51
  130. package/dist/types/internal/placement-to-try-fallbacks.d.ts +0 -13
  131. package/dist/types/internal/resolve-css-length-to-pixels.d.ts +0 -20
  132. package/dist/types/internal/use-anchor-position-at-point.d.ts +0 -67
  133. package/dist/types/internal/use-anchor-position.d.ts +0 -87
  134. package/dist/types/internal/use-width-from-anchor.d.ts +0 -19
  135. package/use-anchor-position/package.json +0 -10
  136. package/use-anchor-position-at-point/package.json +0 -10
  137. package/use-width-from-anchor/package.json +0 -10
@@ -0,0 +1,271 @@
1
+ /**
2
+ * Positioning AND anchor-relative sizing for top-layer popovers. Anchors an
3
+ * ELEMENT; to anchor a viewport coordinate use `useAnchoredPopoverAtPoint`.
4
+ *
5
+ * Two runtime paths, each a function that applies its styles and returns the
6
+ * cleanup: CSS Anchor Positioning in `./anchor-positioning`, and the JavaScript
7
+ * fallback in `./javascript-fallback`. The path is resolved ONCE, at the top of
8
+ * the hook, and threaded through positioning, sizing and margins.
9
+ *
10
+ * See `notes/architecture/positioning.md` and
11
+ * `notes/decisions/fit-available-space.md`.
12
+ */
13
+ import { useLayoutEffect, useMemo } from 'react';
14
+ import { useId } from '@atlaskit/ds-lib/use-id';
15
+ import { applyAnchorPositioning } from './anchor-positioning/apply-anchor-positioning';
16
+ import { getAnchoredPopoverSizeDeclarations } from './anchored-popover-size';
17
+ import { applyJavaScriptFallbackPositioning } from './javascript-fallback/apply-javascript-fallback-positioning';
18
+ import { resolvePlacement } from './resolve-placement';
19
+ import { supportsAnchorPositioning } from './supports-anchor-positioning';
20
+ import { supportsAnchorSize } from './supports-anchor-size';
21
+
22
+ /**
23
+ * Which way the popover's inline axis runs. Maps every physical measurement below
24
+ * onto a logical axis: the viewport unit of each cap and the anchor's measured
25
+ * size. A style read, not a layout read, so it is cheap enough to take for every
26
+ * popover.
27
+ */
28
+ function getPopoverWritingMode(_ref) {
29
+ var popover = _ref.popover;
30
+ // `String()` because not every test environment implements `writing-mode`.
31
+ var writingMode = String(getComputedStyle(popover).writingMode);
32
+ var isVertical = writingMode.startsWith('vertical') || writingMode.startsWith('sideways');
33
+ return isVertical ? 'vertical' : 'horizontal';
34
+ }
35
+
36
+ /**
37
+ * Measured stand-in for `anchor-size(self-*)`, used wherever `anchor-size()` is
38
+ * not: the JavaScript fallback, and browsers with anchor positioning but no
39
+ * `anchor-size()`.
40
+ *
41
+ * `offsetWidth` / `offsetHeight` are physical, so the axis is picked from the
42
+ * POPOVER's `writing-mode`. Deliberately not memoized: a cached anchor size is a
43
+ * stale one. Runs once per effect, not per frame.
44
+ */
45
+ function measureAnchorSize(_ref2) {
46
+ var anchor = _ref2.anchor,
47
+ writingMode = _ref2.writingMode;
48
+ if (writingMode === 'vertical') {
49
+ return {
50
+ inline: "".concat(anchor.offsetHeight, "px"),
51
+ block: "".concat(anchor.offsetWidth, "px")
52
+ };
53
+ }
54
+ return {
55
+ inline: "".concat(anchor.offsetWidth, "px"),
56
+ block: "".concat(anchor.offsetHeight, "px")
57
+ };
58
+ }
59
+ var NO_ANCHOR_SIZE = {
60
+ inline: null,
61
+ block: null
62
+ };
63
+ var CSS_ANCHOR_SIZE = {
64
+ inline: 'anchor-size(self-inline)',
65
+ block: 'anchor-size(self-block)'
66
+ };
67
+ function isAnchorRelative(size) {
68
+ return size === 'match-anchor' || size === 'min-anchor';
69
+ }
70
+
71
+ /**
72
+ * The `anchor-name` written to an anchor that does not already carry one, as a CSS
73
+ * `<dashed-ident>`. `useId()` is the same source as every other id in the design
74
+ * system, including the popover's own `id` that `aria-controls` and `popovertarget`
75
+ * point at, so it is unique exactly where those are and collides exactly where
76
+ * they do. See `useAnchoredPopover` for the one case that needs the host's help.
77
+ *
78
+ * `@atlaskit/ds-lib`'s `useId` already swaps React's `:` and `«»` for `_`. The
79
+ * replace covers a host `identifierPrefix`, which may carry characters a
80
+ * `<dashed-ident>` cannot.
81
+ */
82
+ function useNewAnchorName() {
83
+ var id = useId();
84
+ return useMemo(function () {
85
+ return "--anchor-".concat(id.replace(/[^A-Za-z0-9_-]/g, '_'));
86
+ }, [id]);
87
+ }
88
+
89
+ /**
90
+ * A referentially-stable, fully-resolved `TPlacement`, so a fresh inline
91
+ * `placement` object does not re-run the effect.
92
+ */
93
+ function useStablePlacement(placement) {
94
+ var resolved = resolvePlacement({
95
+ placement: placement
96
+ });
97
+ var axis = resolved.axis;
98
+ var edge = resolved.edge;
99
+ var align = resolved.align;
100
+ var minSize = resolved.minSize;
101
+ var gap = resolved.offset.gap;
102
+ var shiftValue = resolved.offset.crossAxisShift.value;
103
+ var shiftDirection = resolved.offset.crossAxisShift.direction;
104
+ // Rebuilt from primitive deps, so the factory does not close over `resolved`
105
+ // and exhaustive-deps can verify it.
106
+ return useMemo(function () {
107
+ return {
108
+ axis: axis,
109
+ edge: edge,
110
+ align: align,
111
+ minSize: minSize,
112
+ offset: {
113
+ gap: gap,
114
+ crossAxisShift: {
115
+ value: shiftValue,
116
+ direction: shiftDirection
117
+ }
118
+ }
119
+ };
120
+ }, [axis, edge, align, minSize, gap, shiftValue, shiftDirection]);
121
+ }
122
+
123
+ /**
124
+ * Exported so `useAnchoredPopoverAtPoint` can forward the options it does not own.
125
+ */
126
+
127
+ /**
128
+ * Positions and sizes a popover relative to an anchor. Owns every inline style on
129
+ * the popover host: where it goes, how big it is relative to its anchor, and the
130
+ * margins that keep it clear of the anchor and the viewport edge. It knows nothing
131
+ * about visibility or animation - compose it with `Popover`.
132
+ *
133
+ * On modern browsers the browser handles flipping via CSS Anchor Positioning;
134
+ * otherwise it falls back to measured `position: fixed` coordinates, re-run on
135
+ * scroll and resize.
136
+ *
137
+ * `inlineSize` and `blockSize` both default to `'content'`. Two rules are
138
+ * surprising: fitting one axis also fits an axis left on `'content'`, and fitting
139
+ * either axis floors the placement axis. See `TPopoverAxisSize`.
140
+ *
141
+ * **Several React roots on one page.** The `anchor-name` this hook writes to an
142
+ * anchor that has none is minted from `useId()`, like every other id in the design
143
+ * system. `useId()` is unique within a root; across roots it is unique for
144
+ * client-rendered roots but NOT for roots that hydrate the same markup, where it
145
+ * is derived from tree position. Two anchors carrying one name resolve to the last
146
+ * in tree order, so a popover can attach to another root's trigger. A host that
147
+ * hydrates more than one root must give each a distinct `identifierPrefix` (on
148
+ * `hydrateRoot` and the matching server render). That is already required for the
149
+ * popover's own `id`, which `aria-controls` and `popovertarget` point at, and for
150
+ * every `aria-*` id the design system mints. A bundle with two copies of
151
+ * `react-dom` has the same exposure for client-rendered roots.
152
+ *
153
+ * @example Position a popover below its trigger
154
+ * ```tsx
155
+ * const triggerRef = useRef<HTMLButtonElement>(null);
156
+ * const popoverRef = useRef<HTMLDivElement>(null);
157
+ *
158
+ * useAnchoredPopover({
159
+ * anchorRef: triggerRef,
160
+ * popoverRef,
161
+ * placement: { axis: 'block', edge: 'end' },
162
+ * isOpen,
163
+ * });
164
+ * ```
165
+ *
166
+ * @example Match the trigger's width and keep the popover on screen
167
+ * ```tsx
168
+ * // What `<Popup shouldFitContainer shouldFitViewport>` maps to.
169
+ * useAnchoredPopover({
170
+ * anchorRef: triggerRef,
171
+ * popoverRef,
172
+ * placement: { axis: 'block', edge: 'end' },
173
+ * isOpen,
174
+ * inlineSize: 'match-anchor',
175
+ * blockSize: 'max-available',
176
+ * });
177
+ * ```
178
+ *
179
+ * @example Switch to a cursor position mid-mount
180
+ * ```tsx
181
+ * // Complementary `isEnabled`, so exactly one hook owns the popover.
182
+ * const shared = { popoverRef, placement, isOpen };
183
+ *
184
+ * useAnchoredPopover({ ...shared, anchorRef: triggerRef, isEnabled: !mousePos });
185
+ * useAnchoredPopoverAtPoint({
186
+ * ...shared,
187
+ * isEnabled: Boolean(mousePos),
188
+ * getPoint: () => mousePosRef.current,
189
+ * });
190
+ * ```
191
+ */
192
+ export function useAnchoredPopover(_ref3) {
193
+ var anchorRef = _ref3.anchorRef,
194
+ popoverRef = _ref3.popoverRef,
195
+ placement = _ref3.placement,
196
+ isOpen = _ref3.isOpen,
197
+ _ref3$isEnabled = _ref3.isEnabled,
198
+ isEnabled = _ref3$isEnabled === void 0 ? true : _ref3$isEnabled,
199
+ _ref3$inlineSize = _ref3.inlineSize,
200
+ inlineSize = _ref3$inlineSize === void 0 ? 'content' : _ref3$inlineSize,
201
+ _ref3$blockSize = _ref3.blockSize,
202
+ blockSize = _ref3$blockSize === void 0 ? 'content' : _ref3$blockSize,
203
+ _ref3$shouldPreserveI = _ref3.shouldPreserveInlineSize,
204
+ shouldPreserveInlineSize = _ref3$shouldPreserveI === void 0 ? false : _ref3$shouldPreserveI,
205
+ _ref3$forceFallbackPo = _ref3.forceFallbackPositioning,
206
+ forceFallbackPositioning = _ref3$forceFallbackPo === void 0 ? false : _ref3$forceFallbackPo;
207
+ // Stable for the life of the hook instance, so it follows a replacement anchor
208
+ // element (the point hook remounts its `<div>`).
209
+ var fallbackAnchorName = useNewAnchorName();
210
+ var stablePlacement = useStablePlacement(placement);
211
+ useLayoutEffect(function () {
212
+ if (!isEnabled) {
213
+ return;
214
+ }
215
+
216
+ // Resolved once, so positioning, sizing and margins cannot disagree about it.
217
+ var isUsingCssAnchorPositioning = supportsAnchorPositioning() && !forceFallbackPositioning;
218
+
219
+ // BOTH probes: a browser can have positioning without sizing, and
220
+ // `anchor-size()` needs the `position-anchor` only the CSS path writes.
221
+ var canUseAnchorSize = isUsingCssAnchorPositioning && supportsAnchorSize();
222
+ var trigger = anchorRef.current;
223
+ var popover = popoverRef.current;
224
+ if (!trigger || !popover) {
225
+ return;
226
+ }
227
+
228
+ // Fitting also reserves padding from the viewport edge as a margin.
229
+ var isFitting = inlineSize === 'max-available' || blockSize === 'max-available';
230
+
231
+ // Always read: the viewport caps are always written, and their unit follows
232
+ // the popover's writing mode.
233
+ var writingMode = getPopoverWritingMode({
234
+ popover: popover
235
+ });
236
+
237
+ // Skipped where possible, so a position-only consumer pays no layout read.
238
+ // Fitting on the CSS path needs it to clamp the default flip floor; see
239
+ // `getDefaultPlacementAxisFloor`.
240
+ var needsAnchorSize = isAnchorRelative(inlineSize) || isAnchorRelative(blockSize) || isFitting && isUsingCssAnchorPositioning;
241
+ var anchorSize = needsAnchorSize ? canUseAnchorSize ? CSS_ANCHOR_SIZE : measureAnchorSize({
242
+ anchor: trigger,
243
+ writingMode: writingMode
244
+ }) : NO_ANCHOR_SIZE;
245
+ var sizeStyles = getAnchoredPopoverSizeDeclarations({
246
+ placement: stablePlacement,
247
+ inlineSize: inlineSize,
248
+ blockSize: blockSize,
249
+ isUsingCssAnchorPositioning: isUsingCssAnchorPositioning,
250
+ anchorSize: anchorSize,
251
+ writingMode: writingMode,
252
+ shouldPreserveInlineSize: shouldPreserveInlineSize
253
+ });
254
+ if (isUsingCssAnchorPositioning) {
255
+ return applyAnchorPositioning({
256
+ anchor: trigger,
257
+ popover: popover,
258
+ placement: stablePlacement,
259
+ sizeStyles: sizeStyles,
260
+ isFitting: isFitting,
261
+ fallbackAnchorName: fallbackAnchorName
262
+ });
263
+ }
264
+ return applyJavaScriptFallbackPositioning({
265
+ anchor: trigger,
266
+ popover: popover,
267
+ placement: stablePlacement,
268
+ sizeStyles: sizeStyles
269
+ });
270
+ }, [isEnabled, anchorRef, popoverRef, stablePlacement, inlineSize, blockSize, shouldPreserveInlineSize, fallbackAnchorName, forceFallbackPositioning, isOpen]);
271
+ }
@@ -3,6 +3,7 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
3
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
4
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
5
5
  import { useCallback, useEffect, useLayoutEffect, useReducer, useRef } from 'react';
6
+ import { isNativeElementOpen } from './is-native-element-open';
6
7
  import { prefersReducedMotion } from './reduced-motion';
7
8
  function runCancellableSettlement(_ref) {
8
9
  var schedule = _ref.schedule,
@@ -38,19 +39,6 @@ function runOnAnimationsSettled(_ref2) {
38
39
  }
39
40
  });
40
41
  }
41
- function isNativeElementOpen(_ref3) {
42
- var element = _ref3.element;
43
- if (element instanceof HTMLDialogElement) {
44
- return element.open;
45
- }
46
-
47
- // Some DOM implementations throw for unsupported pseudo-classes.
48
- try {
49
- return element.matches(':popover-open');
50
- } catch (_unused) {
51
- return false;
52
- }
53
- }
54
42
 
55
43
  /**
56
44
  * Visibility lifecycle phase shared between `useAnimatedVisibility`,
@@ -79,9 +67,9 @@ function isNativeElementOpen(_ref3) {
79
67
  * ```
80
68
  */
81
69
 
82
- function getInitialVisibilityState(_ref4) {
83
- var isOpen = _ref4.isOpen,
84
- willAnimate = _ref4.willAnimate;
70
+ function getInitialVisibilityState(_ref3) {
71
+ var isOpen = _ref3.isOpen,
72
+ willAnimate = _ref3.willAnimate;
85
73
  if (!isOpen) {
86
74
  return {
87
75
  phase: 'closed',
@@ -177,12 +165,12 @@ function getNextVisibilityState(state, action) {
177
165
  * phase: open ──────────────── exiting ─── (exit settle) ─── closed
178
166
  * ```
179
167
  */
180
- export function useAnimatedVisibility(_ref5) {
181
- var isOpen = _ref5.isOpen,
182
- shouldAnimate = _ref5.shouldAnimate,
183
- elementRef = _ref5.elementRef,
184
- onEnterFinish = _ref5.onEnterFinish,
185
- onExitFinish = _ref5.onExitFinish;
168
+ export function useAnimatedVisibility(_ref4) {
169
+ var isOpen = _ref4.isOpen,
170
+ shouldAnimate = _ref4.shouldAnimate,
171
+ elementRef = _ref4.elementRef,
172
+ onEnterFinish = _ref4.onEnterFinish,
173
+ onExitFinish = _ref4.onExitFinish;
186
174
  var willAnimate = shouldAnimate && !prefersReducedMotion();
187
175
  var _useReducer = useReducer(getNextVisibilityState, {
188
176
  isOpen: isOpen,
@@ -22,6 +22,10 @@
22
22
  ._n3tdidpf{padding-bottom:0}
23
23
  ._o5724jg8{white-space:normal}
24
24
  ._otyridpf{margin-bottom:0}
25
+ ._p6861txw:popover-open{display:flex}
25
26
  ._u5f3idpf{padding-right:0}
26
27
  ._y3gnv2br{text-align:start}
28
+ :where(._1yp3idpf)>*{min-inline-size:0}
29
+ :where(._51hckb7n)>*{flex-grow:1}
30
+ :where(._f5r5idpf)>*{min-block-size:0}
27
31
  @media (prefers-reduced-motion:reduce){._bl8aglyw{animation-name:none}._156y147i{transition-duration:var(--ds-duration-instant,0ms)}}
@@ -7,8 +7,8 @@ import { bind } from 'bind-event-listener';
7
7
  import mergeRefs from '@atlaskit/ds-lib/merge-refs';
8
8
  import once from '@atlaskit/ds-lib/once';
9
9
  import { useNotifyOpenLayerObserver } from '@atlaskit/layering/use-notify-open-layer-observer';
10
+ import { isNativeElementOpen } from '../internal/is-native-element-open';
10
11
  import { resolvePlacement } from '../internal/resolve-placement';
11
- import { shouldFocusIntoPopover } from '../internal/should-focus-into-popover';
12
12
  import { useAnimatedVisibility } from '../internal/use-animated-visibility';
13
13
  import { useFocusWrap } from '../internal/use-focus-wrap';
14
14
  import { useInitialFocus } from '../internal/use-initial-focus';
@@ -33,15 +33,22 @@ var supportsPopoverHint = once(function () {
33
33
  // avoided this by rendering at `<body>`). Excludes `color`/`font` (theming) and
34
34
  // `direction`/`unicode-bidi` (RTL must inherit).
35
35
  //
36
+ // Not `all: initial`: the target is what `<body>` gave the portal path, not the
37
+ // spec initial values (UA serif, black, `color-scheme: normal`), and as an author
38
+ // declaration it would also override the UA `[popover]` rules (`position: fixed`,
39
+ // `display: none` when closed).
40
+ //
36
41
  // KEEP IN SYNC with the identical `surfaceResetStyles` in `dialog/dialog-content.tsx`.
37
42
  // ADS forbids sharing styles across files (`no-exported-styles` /
38
43
  // `no-imported-style-values` — Compiled styles are null at runtime), so the reset is
39
- // co-located and duplicated deliberately.
44
+ // co-located and duplicated deliberately. The `satisfies` check below fails the
45
+ // build if either copy drifts.
40
46
  var surfaceResetStyles = {
41
47
  root: "_lcxv1wug _o5724jg8 _1nmz4jg8 _1i4q4jg8 _y3gnv2br _azhwidpf _1p1dglyw"
42
48
  };
49
+ true;
43
50
  var styles = {
44
- root: "_19itglyw _ca0qidpf _u5f3idpf _n3tdidpf _19bvidpf _19pkidpf _2hwxidpf _otyridpf _18u0idpf _1r041wug _1reoewfl _18m9ewfl _bfhk1j28 _4t3i1wug",
51
+ root: "_19itglyw _ca0qidpf _u5f3idpf _n3tdidpf _19bvidpf _19pkidpf _2hwxidpf _otyridpf _18u0idpf _1r041wug _1reoewfl _18m9ewfl _bfhk1j28 _4t3i1wug _p6861txw _51hckb7n _1yp3idpf _f5r5idpf",
45
52
  motion: "_k8m011k7 _1oecdn4k _1s9z1a5r _1o511hrf _bl8aglyw _156y147i",
46
53
  enter: "_y44v179n",
47
54
  exit: "_y44vof1f"
@@ -79,7 +86,7 @@ var POPUP_ROLES = new Set(['menu', 'listbox', 'dialog', 'alertdialog', 'tree', '
79
86
 
80
87
  /**
81
88
  * Unopinionated top-layer primitive. Owns visibility and animation only;
82
- * compose with `useAnchorPosition` / `useWidthFromAnchor` for positioning.
89
+ * compose with `useAnchoredPopover` for positioning and sizing.
83
90
  *
84
91
  * ### 🔌 Visibility
85
92
  *
@@ -131,14 +138,12 @@ export var Popover = /*#__PURE__*/forwardRef(function Popover(_ref, ref) {
131
138
  // `useId()` colons are invalid in CSS selectors and popover target attributes.
132
139
  var popoverId = idProp !== null && idProp !== void 0 ? idProp : "popover-".concat(autoId.replace(/:/g, ''));
133
140
 
134
- // Keep semantic callbacks current without rebinding native lifecycle
141
+ // Keep the semantic callback current without rebinding native lifecycle
135
142
  // listeners during the same commit that calls showPopover()/hidePopover().
136
143
  var onCloseRef = useRef(onClose);
137
- var roleRef = useRef(role);
138
144
  useLayoutEffect(function () {
139
145
  onCloseRef.current = onClose;
140
- roleRef.current = role;
141
- }, [onClose, role]);
146
+ }, [onClose]);
142
147
 
143
148
  // Register with open layer observer so `closeLayers()` and open-count
144
149
  // subscriptions work. Only popup-like roles register as `popup`; passive
@@ -172,65 +177,60 @@ export var Popover = /*#__PURE__*/forwardRef(function Popover(_ref, ref) {
172
177
  return popupMotionStyles[axis][edge];
173
178
  }, [placement, shouldAnimate]);
174
179
 
175
- // Prevents toggle handler from calling onClose for our own hidePopover() calls.
176
- var programmaticCloseRef = useRef(false);
177
-
178
- // Set in capture-phase keydown so the toggle handler knows close reason.
180
+ // Tracks the close reason, including our own hidePopover() calls.
179
181
  var closeReasonRef = useRef('light-dismiss');
180
182
 
181
183
  // Snapshot of pre-open focus, used to restore focus for nested popovers
182
- // with focus-capturing roles (browser only restores the outermost).
183
- var previouslyFocusedElementRef = useRef(null);
184
+ // because the browser only restores the outermost.
185
+ var focusRestorationTargetRef = useRef(null);
184
186
  var handleBeforeToggle = useCallback(function (event) {
185
- if (event.newState !== 'open') {
187
+ var popover = event.currentTarget;
188
+ if (!(popover instanceof HTMLElement)) {
186
189
  return;
187
190
  }
188
- // eslint-disable-next-line @atlaskit/platform/no-direct-document-usage -- need active element snapshot
189
- var active = document.activeElement;
190
- previouslyFocusedElementRef.current = active instanceof HTMLElement ? active : null;
191
- }, []);
192
- var restorePreviouslyFocusedElement = useCallback(function () {
193
- var previouslyFocused = previouslyFocusedElementRef.current;
194
- previouslyFocusedElementRef.current = null;
195
- if (previouslyFocused !== null && previouslyFocused.isConnected && shouldFocusIntoPopover({
196
- role: roleRef.current
197
- })) {
198
- previouslyFocused.focus({
199
- preventScroll: true
200
- });
191
+ var ownerDocument = popover.ownerDocument;
192
+ var activeElement = ownerDocument.activeElement;
193
+ if (event.newState === 'open') {
194
+ focusRestorationTargetRef.current = activeElement instanceof HTMLElement ? activeElement : null;
195
+ return;
196
+ }
197
+
198
+ // Check before closing so focus already on body is not mistaken for focus lost during close.
199
+ if (closeReasonRef.current === 'light-dismiss' || !popover.contains(activeElement)) {
200
+ focusRestorationTargetRef.current = null;
201
201
  }
202
202
  }, []);
203
- var handleToggleClosed = useCallback(function (_event) {
204
- var _onCloseRef$current2;
205
- // Nested-popover focus restoration fallback (browser only
206
- // restores outermost; Firefox skips nested entirely).
207
- // Restore only when the role moved focus in on open AND the
208
- // close was Escape/programmatic. For light dismiss, the
209
- // click target keeps focus per HTML spec
210
- // (`focusPreviousElement=false`).
211
- var reason = closeReasonRef.current;
212
- var isProgrammatic = programmaticCloseRef.current;
213
- if (reason === 'escape' || isProgrammatic) {
214
- restorePreviouslyFocusedElement();
215
- } else {
216
- previouslyFocusedElementRef.current = null;
203
+ var handleToggleClosed = useCallback(function (event) {
204
+ var popover = event.currentTarget;
205
+ if (!(popover instanceof HTMLElement)) {
206
+ return;
217
207
  }
218
-
208
+ var focusRestorationTarget = focusRestorationTargetRef.current;
209
+ focusRestorationTargetRef.current = null;
210
+ var reason = closeReasonRef.current;
219
211
  // Reset reason so a stale 'escape' from a race with Escape keydown
220
212
  // does not corrupt the next browser-dismiss cycle.
221
213
  closeReasonRef.current = 'light-dismiss';
222
-
223
- // Programmatic close: consumer already knows.
224
- if (isProgrammatic) {
225
- return;
214
+ if (focusRestorationTarget) {
215
+ var ownerDocument = popover.ownerDocument;
216
+ // Native restoration has finished. Preserve focus moved outside the popover.
217
+ var activeElementAfterClose = ownerDocument.activeElement;
218
+ if (activeElementAfterClose === ownerDocument.body || popover.contains(activeElementAfterClose)) {
219
+ // Match native popover restoration, which does not scroll the viewport.
220
+ focusRestorationTarget.focus({
221
+ preventScroll: true
222
+ });
223
+ }
226
224
  }
227
225
 
228
- // Browser dismiss (Escape/click-outside).
229
- // Optional: `manual` mode has no `onClose` in the forwarded type.
230
- (_onCloseRef$current2 = onCloseRef.current) === null || _onCloseRef$current2 === void 0 || _onCloseRef$current2.call(onCloseRef, {
231
- reason: reason
232
- });
233
- }, [restorePreviouslyFocusedElement]);
226
+ // Programmatic closes are already known to the consumer.
227
+ if (reason !== 'programmatic') {
228
+ var _onCloseRef$current2;
229
+ (_onCloseRef$current2 = onCloseRef.current) === null || _onCloseRef$current2 === void 0 || _onCloseRef$current2.call(onCloseRef, {
230
+ reason: reason
231
+ });
232
+ }
233
+ }, []);
234
234
  var _useAnimatedVisibilit = useAnimatedVisibility({
235
235
  isOpen: isOpen,
236
236
  shouldAnimate: shouldAnimate,
@@ -245,9 +245,9 @@ export var Popover = /*#__PURE__*/forwardRef(function Popover(_ref, ref) {
245
245
 
246
246
  // Focus management: initial focus on entry (role-dependent), Tab cycling
247
247
  // for dialog roles. Restoration is native for outermost popovers; nested
248
- // focus-capturing roles are handled above via the `beforetoggle` snapshot.
248
+ // popovers are handled above via the `beforetoggle` snapshot.
249
249
  // Passing `phase` keeps `useFocusWrap` attached through the animated-exit
250
- // window (WCAG 2.4.3 guard). See notes/architecture/focus-restoration.md.
250
+ // window (WCAG 2.4.3 guard). See notes/architecture/focus.md.
251
251
  useFocusWrap({
252
252
  elementRef: ownRef,
253
253
  role: role,
@@ -262,7 +262,7 @@ export var Popover = /*#__PURE__*/forwardRef(function Popover(_ref, ref) {
262
262
  // Bind via `useLayoutEffect`, and BEFORE the show/hide layout effect
263
263
  // below, so listeners are attached when `showPopover()` synchronously
264
264
  // dispatches `beforetoggle`. A regular `useEffect` would miss that
265
- // first dispatch, dropping the `previouslyFocusedElementRef` snapshot
265
+ // first dispatch, dropping the `focusRestorationTargetRef` snapshot
266
266
  // and breaking nested-popover focus restoration on close. React runs
267
267
  // layout effects in source order on the same commit.
268
268
  useLayoutEffect(function () {
@@ -275,7 +275,10 @@ export var Popover = /*#__PURE__*/forwardRef(function Popover(_ref, ref) {
275
275
  var unbindEscape = bind(element, {
276
276
  type: 'keydown',
277
277
  listener: function listener(event) {
278
- if (event.key === 'Escape') {
278
+ // Ignore Escape events dispatched after native close.
279
+ if (event.key === 'Escape' && isNativeElementOpen({
280
+ element: element
281
+ })) {
279
282
  closeReasonRef.current = 'escape';
280
283
  }
281
284
  },
@@ -292,7 +295,7 @@ export var Popover = /*#__PURE__*/forwardRef(function Popover(_ref, ref) {
292
295
  onToggle(event);
293
296
  }
294
297
  });
295
- // Snapshot `document.activeElement` before `useInitialFocus` moves it,
298
+ // Snapshot the active element before `useInitialFocus` moves it,
296
299
  // then advance the shared visibility lifecycle.
297
300
  var unbindBeforeToggle = bind(element, {
298
301
  type: 'beforetoggle',
@@ -317,7 +320,6 @@ export var Popover = /*#__PURE__*/forwardRef(function Popover(_ref, ref) {
317
320
  return;
318
321
  }
319
322
  if (isOpen) {
320
- programmaticCloseRef.current = false;
321
323
  // Clear stale 'escape' from a prior cycle: the ref outlives
322
324
  // the host element, and interrupted close lifecycles may not reach
323
325
  // `handleToggleClosed` to reset it.
@@ -326,13 +328,13 @@ export var Popover = /*#__PURE__*/forwardRef(function Popover(_ref, ref) {
326
328
  element.showPopover();
327
329
  } catch (_unused) {}
328
330
  return function () {
329
- programmaticCloseRef.current = true;
331
+ closeReasonRef.current = 'programmatic';
330
332
  try {
331
333
  element.hidePopover();
332
334
  } catch (_unused2) {}
333
335
  };
334
336
  }
335
- programmaticCloseRef.current = true;
337
+ closeReasonRef.current = 'programmatic';
336
338
  try {
337
339
  element.hidePopover();
338
340
  } catch (_unused3) {}
@@ -13,9 +13,9 @@
13
13
  * - Popover open state tracked via `data-popover-open` (no `:popover-open` in jsdom).
14
14
  * - Auto popovers: maintain an open stack, perform light-dismiss on document mousedown
15
15
  * outside the open chain, and dismiss the topmost on Escape.
16
- * - On open, removes any inline `opacity: 0` from the popover element so callers that
17
- * rely on a `ResizeObserver` reveal callback (e.g. `use-anchor-position`) become
18
- * visible in jsdom, where layout-driven RO callbacks never fire.
16
+ * - On open, removes any inline `opacity: 0` from the popover element, so the
17
+ * JavaScript positioning fallback's `ResizeObserver` reveal is not needed in
18
+ * jsdom, where its non-zero-layout guard can never pass.
19
19
  *
20
20
  * Companion `toBeVisible` patch lives at `./to-be-visible`.
21
21
  *
@@ -247,10 +247,9 @@ function stackForMode(_ref9) {
247
247
  return null;
248
248
  }
249
249
 
250
- // Polyfill-only. Real browsers reveal the popover once `ResizeObserver`
251
- // callbacks fire after the first layout pass. jsdom has no layout, so we clear
252
- // any inline `opacity: 0` (set by callers like `use-anchor-position`) once
253
- // the open toggle has fired.
250
+ // Polyfill-only. Real browsers reveal the popover on the first non-zero layout
251
+ // their `ResizeObserver` reports; jsdom always reports 0, so that guard never
252
+ // passes and the inline `opacity: 0` is cleared on the open toggle instead.
254
253
  function clearOpacityOnOpenToggle(_ref0) {
255
254
  var element = _ref0.element;
256
255
  var unbind = bind(element, {
@@ -5,8 +5,8 @@ export { fromLegacyPlacement } from '../placement-map';
5
5
  /**
6
6
  * @deprecated Import from the generated per-export subpath instead.
7
7
  */
8
- export { LEGACY_PLACEMENTS } from '../placement-map/legacy-placements';
8
+ export { LEGACY_PLACEMENTS, type TLegacyPlacement } from '../placement-map/legacy-placements';
9
9
  /**
10
10
  * @deprecated Import from the generated per-export subpath instead.
11
11
  */
12
- export type { TLegacyPlacement, TPlacementOptions } from '../placement-map';
12
+ export type { TPlacementOptions } from '../internal/resolve-placement';
@@ -1,11 +1,7 @@
1
1
  /**
2
- * Returns a stable HTML `id` used to wire a trigger element to its popover
3
- * via ARIA (`aria-controls`) and the Popover API (`popovertarget`).
2
+ * A stable `id` for wiring a trigger to its popover: the popover's `id`, and the
3
+ * trigger's matching `aria-controls` / `popovertarget`.
4
4
  *
5
- * The returned value is intended for the `id` attribute on the popover and
6
- * for the matching `aria-controls` / `popovertarget` references on the
7
- * trigger. It is NOT a CSS `anchor-name` value: CSS anchor positioning is
8
- * handled internally by `useAnchorPosition`, which mints and applies its
9
- * own `anchor-name` on the trigger element.
5
+ * NOT a CSS `anchor-name` - `useAnchoredPopover` mints and writes its own.
10
6
  */
11
7
  export declare function usePopoverId(): string;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The CSS Anchor Positioning path: `position-anchor`, `position-area`,
3
+ * `position-try-fallbacks` and the gap / shift margins, all declared on the
4
+ * popover so the browser owns placement and flipping. The twin of
5
+ * `../javascript-fallback/apply-javascript-fallback-positioning`.
6
+ */
7
+ import { type TPlacement } from '../resolve-placement';
8
+ import { type TStyleDeclaration } from '../set-style';
9
+ /**
10
+ * Returns the cleanup.
11
+ */
12
+ export declare function applyAnchorPositioning({ anchor, popover, placement, sizeStyles, isFitting, fallbackAnchorName, }: {
13
+ anchor: HTMLElement;
14
+ popover: HTMLElement;
15
+ placement: TPlacement;
16
+ sizeStyles: TStyleDeclaration[];
17
+ /**
18
+ * Whether either axis is `'max-available'`, which also reserves padding from
19
+ * the viewport edge as a margin.
20
+ */
21
+ isFitting: boolean;
22
+ /**
23
+ * Written to the anchor when it does not already carry one.
24
+ */
25
+ fallbackAnchorName: string;
26
+ }): () => void;