@atlaskit/top-layer 4.4.5 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/CHANGELOG.md +41 -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/{anchor-positioning-fallback.js → javascript-fallback/anchor-positioning-fallback.js} +12 -33
  15. package/dist/cjs/internal/javascript-fallback/apply-javascript-fallback-positioning.js +297 -0
  16. package/dist/cjs/internal/javascript-fallback/resolve-css-length-to-pixels.js +90 -0
  17. package/dist/cjs/internal/javascript-fallback/warn-fallback-failure.js +19 -0
  18. package/dist/cjs/internal/resolve-placement.js +14 -6
  19. package/dist/cjs/internal/supports-anchor-positioning.js +17 -0
  20. package/dist/cjs/internal/supports-anchor-size.js +17 -0
  21. package/dist/cjs/internal/supports-css-declaration.js +25 -0
  22. package/dist/cjs/internal/surface-reset.js +5 -0
  23. package/dist/cjs/internal/use-anchored-popover-at-point.js +124 -0
  24. package/dist/cjs/internal/use-anchored-popover.js +277 -0
  25. package/dist/cjs/popover/popover.compiled.css +4 -0
  26. package/dist/cjs/popover/popover.js +11 -4
  27. package/dist/cjs/testing/polyfill.js +6 -7
  28. package/dist/es2019/dialog/dialog-content.js +2 -0
  29. package/dist/es2019/entry-points/use-popover-id.js +6 -10
  30. package/dist/es2019/internal/anchor-positioning/apply-anchor-positioning.js +83 -0
  31. package/dist/es2019/internal/anchor-positioning/cross-axis-shift-margins.js +29 -0
  32. package/dist/es2019/internal/anchor-positioning/edge-margin.js +18 -0
  33. package/dist/es2019/internal/anchor-positioning/fit-margins.js +69 -0
  34. package/dist/es2019/internal/anchor-positioning/get-existing-anchor-name.js +12 -0
  35. package/dist/es2019/internal/{placement-to-position-area.js → anchor-positioning/placement-to-position-area.js} +9 -9
  36. package/dist/es2019/internal/anchor-positioning/placement-to-try-fallbacks.js +94 -0
  37. package/dist/es2019/internal/anchored-popover-size.js +343 -0
  38. package/dist/es2019/internal/get-aria-for-trigger.js +7 -13
  39. package/dist/es2019/internal/{anchor-positioning-fallback.js → javascript-fallback/anchor-positioning-fallback.js} +12 -33
  40. package/dist/es2019/internal/javascript-fallback/apply-javascript-fallback-positioning.js +291 -0
  41. package/dist/es2019/internal/javascript-fallback/resolve-css-length-to-pixels.js +87 -0
  42. package/dist/es2019/internal/javascript-fallback/warn-fallback-failure.js +14 -0
  43. package/dist/es2019/internal/resolve-placement.js +14 -6
  44. package/dist/es2019/internal/supports-anchor-positioning.js +12 -0
  45. package/dist/es2019/internal/supports-anchor-size.js +12 -0
  46. package/dist/es2019/internal/supports-css-declaration.js +20 -0
  47. package/dist/es2019/internal/surface-reset.js +1 -0
  48. package/dist/es2019/internal/use-anchored-popover-at-point.js +116 -0
  49. package/dist/es2019/internal/use-anchored-popover.js +265 -0
  50. package/dist/es2019/popover/popover.compiled.css +4 -0
  51. package/dist/es2019/popover/popover.js +11 -4
  52. package/dist/es2019/testing/polyfill.js +6 -7
  53. package/dist/esm/dialog/dialog-content.js +2 -0
  54. package/dist/esm/entry-points/use-popover-id.js +6 -10
  55. package/dist/esm/internal/anchor-positioning/apply-anchor-positioning.js +80 -0
  56. package/dist/esm/internal/anchor-positioning/cross-axis-shift-margins.js +28 -0
  57. package/dist/esm/internal/anchor-positioning/edge-margin.js +15 -0
  58. package/dist/esm/internal/anchor-positioning/fit-margins.js +71 -0
  59. package/dist/esm/internal/anchor-positioning/get-existing-anchor-name.js +12 -0
  60. package/dist/esm/internal/{placement-to-position-area.js → anchor-positioning/placement-to-position-area.js} +11 -12
  61. package/dist/esm/internal/anchor-positioning/placement-to-try-fallbacks.js +89 -0
  62. package/dist/esm/internal/anchored-popover-size.js +338 -0
  63. package/dist/esm/internal/get-aria-for-trigger.js +7 -13
  64. package/dist/esm/internal/{anchor-positioning-fallback.js → javascript-fallback/anchor-positioning-fallback.js} +12 -33
  65. package/dist/esm/internal/javascript-fallback/apply-javascript-fallback-positioning.js +290 -0
  66. package/dist/esm/internal/javascript-fallback/resolve-css-length-to-pixels.js +85 -0
  67. package/dist/esm/internal/javascript-fallback/warn-fallback-failure.js +13 -0
  68. package/dist/esm/internal/resolve-placement.js +14 -6
  69. package/dist/esm/internal/supports-anchor-positioning.js +12 -0
  70. package/dist/esm/internal/supports-anchor-size.js +12 -0
  71. package/dist/esm/internal/supports-css-declaration.js +19 -0
  72. package/dist/esm/internal/surface-reset.js +1 -0
  73. package/dist/esm/internal/use-anchored-popover-at-point.js +118 -0
  74. package/dist/esm/internal/use-anchored-popover.js +271 -0
  75. package/dist/esm/popover/popover.compiled.css +4 -0
  76. package/dist/esm/popover/popover.js +11 -4
  77. package/dist/esm/testing/polyfill.js +6 -7
  78. package/dist/types/entry-points/placement-map.d.ts +2 -2
  79. package/dist/types/entry-points/use-popover-id.d.ts +3 -7
  80. package/dist/types/internal/anchor-positioning/apply-anchor-positioning.d.ts +26 -0
  81. package/dist/types/internal/anchor-positioning/cross-axis-shift-margins.d.ts +19 -0
  82. package/dist/types/internal/anchor-positioning/edge-margin.d.ts +10 -0
  83. package/dist/types/internal/anchor-positioning/fit-margins.d.ts +30 -0
  84. package/dist/types/internal/anchor-positioning/get-existing-anchor-name.d.ts +8 -0
  85. package/dist/types/internal/{placement-to-position-area.d.ts → anchor-positioning/placement-to-position-area.d.ts} +10 -6
  86. package/dist/types/internal/anchor-positioning/placement-to-try-fallbacks.d.ts +30 -0
  87. package/dist/types/internal/anchored-popover-size.d.ts +126 -0
  88. package/dist/types/internal/get-aria-for-trigger.d.ts +13 -25
  89. package/dist/types/internal/javascript-fallback/anchor-positioning-fallback.d.ts +36 -0
  90. package/dist/types/internal/javascript-fallback/apply-javascript-fallback-positioning.d.ts +15 -0
  91. package/dist/types/internal/javascript-fallback/resolve-css-length-to-pixels.d.ts +20 -0
  92. package/dist/types/internal/javascript-fallback/warn-fallback-failure.d.ts +7 -0
  93. package/dist/types/internal/resolve-placement.d.ts +18 -2
  94. package/dist/types/internal/supports-anchor-positioning.d.ts +5 -0
  95. package/dist/types/internal/supports-anchor-size.d.ts +5 -0
  96. package/dist/types/internal/supports-css-declaration.d.ts +11 -0
  97. package/dist/types/internal/surface-reset.d.ts +25 -0
  98. package/dist/types/internal/use-anchored-popover-at-point.d.ts +48 -0
  99. package/dist/types/internal/use-anchored-popover.d.ts +143 -0
  100. package/dist/types/popover/index.d.ts +2 -1
  101. package/dist/types/popover/popover.d.ts +1 -1
  102. package/dist/types/popover/types.d.ts +15 -17
  103. package/dist/types/testing/polyfill.d.ts +3 -3
  104. package/package.json +2 -2
  105. package/use-anchored-popover/package.json +10 -0
  106. package/use-anchored-popover-at-point/package.json +10 -0
  107. package/dist/cjs/internal/placement-to-try-fallbacks.js +0 -108
  108. package/dist/cjs/internal/resolve-css-length-to-pixels.js +0 -104
  109. package/dist/cjs/internal/use-anchor-position-at-point.js +0 -129
  110. package/dist/cjs/internal/use-anchor-position.js +0 -501
  111. package/dist/cjs/internal/use-width-from-anchor.js +0 -124
  112. package/dist/es2019/internal/placement-to-try-fallbacks.js +0 -108
  113. package/dist/es2019/internal/resolve-css-length-to-pixels.js +0 -101
  114. package/dist/es2019/internal/use-anchor-position-at-point.js +0 -124
  115. package/dist/es2019/internal/use-anchor-position.js +0 -480
  116. package/dist/es2019/internal/use-width-from-anchor.js +0 -120
  117. package/dist/esm/internal/placement-to-try-fallbacks.js +0 -103
  118. package/dist/esm/internal/resolve-css-length-to-pixels.js +0 -98
  119. package/dist/esm/internal/use-anchor-position-at-point.js +0 -124
  120. package/dist/esm/internal/use-anchor-position.js +0 -494
  121. package/dist/esm/internal/use-width-from-anchor.js +0 -118
  122. package/dist/types/internal/anchor-positioning-fallback.d.ts +0 -51
  123. package/dist/types/internal/placement-to-try-fallbacks.d.ts +0 -13
  124. package/dist/types/internal/resolve-css-length-to-pixels.d.ts +0 -20
  125. package/dist/types/internal/use-anchor-position-at-point.d.ts +0 -67
  126. package/dist/types/internal/use-anchor-position.d.ts +0 -87
  127. package/dist/types/internal/use-width-from-anchor.d.ts +0 -19
  128. package/use-anchor-position/package.json +0 -10
  129. package/use-anchor-position-at-point/package.json +0 -10
  130. package/use-width-from-anchor/package.json +0 -10
@@ -1,494 +0,0 @@
1
- import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
- import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
3
- import _createClass from "@babel/runtime/helpers/createClass";
4
- /**
5
- * Anchor positioning for top-layer popovers. See
6
- * `notes/architecture/positioning.md` for the full model.
7
- *
8
- * Two runtime paths: CSS Anchor Positioning (modern browsers) and a JS
9
- * fallback that measures the trigger and writes `top`/`left`.
10
- *
11
- * Both paths honour `offset.gap` and `offset.crossAxisShift`. The
12
- * CSS path passes the values through as strings; the JS fallback resolves
13
- * them to pixels via a hidden DOM probe (see `resolveCssLengthToPixels`)
14
- * so tokens, `calc()`, `var()`, etc all work. The JS fallback hides the
15
- * popover with `opacity: 0` until the first measurement completes, so it
16
- * is never painted at the wrong location.
17
- */
18
- import { useId, useLayoutEffect, useMemo } from 'react';
19
- import { bind } from 'bind-event-listener';
20
- import rafSchedule from 'raf-schd';
21
- import once from '@atlaskit/ds-lib/once';
22
- import { computeFallbackPosition } from './anchor-positioning-fallback';
23
- import { combine } from './combine';
24
- import { placementToPositionArea } from './placement-to-position-area';
25
- import { placementToTryFallbacks } from './placement-to-try-fallbacks';
26
- import { resolveCssLengthToPixels } from './resolve-css-length-to-pixels';
27
- import { resolvePlacement } from './resolve-placement';
28
- import { setStyle } from './set-style';
29
-
30
- // `resolvePlacement` is intentionally NOT re-exported here - import it directly
31
- // from `./resolve-placement` so search-and-jump lands at the source of truth.
32
-
33
- /**
34
- * Module-scope no-op `ResizeObserver` used in non-DOM jest environments where
35
- * `ResizeObserver` is `undefined`. Hoisted out of the effect to avoid
36
- * allocating a new class declaration on every run.
37
- */
38
- var NoopResizeObserver = /*#__PURE__*/function () {
39
- function NoopResizeObserver() {
40
- _classCallCheck(this, NoopResizeObserver);
41
- }
42
- return _createClass(NoopResizeObserver, [{
43
- key: "observe",
44
- value: function observe() {}
45
- }, {
46
- key: "unobserve",
47
- value: function unobserve() {}
48
- }, {
49
- key: "disconnect",
50
- value: function disconnect() {}
51
- }]);
52
- }();
53
-
54
- /**
55
- * Returns true when the popover is currently open. Wrapped because the
56
- * `:popover-open` pseudo-class is not implemented in some test environments
57
- * (e.g. jsdom), where `matches` throws on unknown selectors.
58
- */
59
- function isPopoverOpen(popover) {
60
- try {
61
- return popover.matches(':popover-open');
62
- } catch (_unused) {
63
- return false;
64
- }
65
- }
66
-
67
- /**
68
- * Detects whether the browser supports CSS Anchor Positioning.
69
- * Uses `once()` for lazy evaluation: safe for SSR and avoids hydration mismatches.
70
- */
71
- var supportsAnchorPositioning = once(function () {
72
- if (typeof window === 'undefined' || typeof CSS === 'undefined' || typeof CSS.supports !== 'function') {
73
- return false;
74
- }
75
- return CSS.supports('anchor-name', '--a');
76
- });
77
-
78
- /**
79
- * Every logical margin property this module can write: one per axis, per side.
80
- *
81
- * Deriving the union from `TPlacementAxis` and `TPlacementEdge` rather than
82
- * writing the four names out keeps it in step with the placement model, and
83
- * makes `CROSS_AXIS_SHIFT_CUSTOM_PROPERTY` below exhaustive by construction.
84
- */
85
-
86
- /**
87
- * The custom property mirroring each cross-axis shift margin.
88
- *
89
- * These are written for the planned named arrow `@position-try` rules. Nothing
90
- * reads them yet, and the shift no longer depends on them: the antisymmetric
91
- * margins written by `crossAxisShiftMargins` survive a cross-axis slide on
92
- * their own.
93
- *
94
- * Keyed on `TLogicalMarginProperty` and not on `string`, so a margin property
95
- * that has no custom property (or a custom property whose margin no longer
96
- * exists) is a compile error rather than an `undefined` lookup that reaches
97
- * `setStyle` as `{ property: undefined, value }`.
98
- */
99
- var CROSS_AXIS_SHIFT_CUSTOM_PROPERTY = {
100
- 'margin-inline-start': '--ds-cross-axis-shift-margin-start',
101
- 'margin-inline-end': '--ds-cross-axis-shift-margin-end',
102
- 'margin-block-start': '--ds-cross-axis-shift-margin-block-start',
103
- 'margin-block-end': '--ds-cross-axis-shift-margin-block-end'
104
- };
105
- var ZEROED_CROSS_AXIS_SHIFT_CUSTOM_PROPERTIES = Object.values(CROSS_AXIS_SHIFT_CUSTOM_PROPERTY).map(function (property) {
106
- return {
107
- property: property,
108
- value: '0px'
109
- };
110
- });
111
-
112
- /**
113
- * Returns the CSS margin declaration that creates a gap between the
114
- * popover and its trigger on the side facing the anchor.
115
- *
116
- * For example, a `block-end` placement (popover below) gets
117
- * `margin-block-start: 8px` to push it away from the trigger's bottom edge.
118
- */
119
- function edgeMargin(_ref) {
120
- var placement = _ref.placement,
121
- offset = _ref.offset;
122
- var axis = placement.axis,
123
- edge = placement.edge;
124
- // The gap sits on the side FACING the anchor, which is always the side
125
- // opposite the placement edge.
126
- var anchorFacingEdge = edge === 'end' ? 'start' : 'end';
127
- return {
128
- property: "margin-".concat(axis, "-").concat(anchorFacingEdge),
129
- value: offset
130
- };
131
- }
132
-
133
- /**
134
- * **The cross-axis shift margins.**
135
- *
136
- * The shift is written antisymmetrically on BOTH cross-axis sides
137
- * (`start: +value`, `end: -value`) rather than as a single margin on the side
138
- * picked from `align`. Two separate behaviours require it:
139
- *
140
- * 1. **`align: 'center'` is centered with `anchor-center`, which centers the
141
- * popover's MARGIN box on the anchor.** A single-sided margin widens the
142
- * margin box, so it displaces the border box by only half its value. An
143
- * antisymmetric pair leaves the margin box width unchanged (`+value` and
144
- * `-value` sum to zero) while moving its center by the full value.
145
- * 2. **`position-try-fallbacks` can slide the popover onto the opposite
146
- * cross-axis side.** A `<position-area>` fallback keeps the base style's
147
- * margins, and margin on the un-anchored side has no effect, so a
148
- * single-sided margin is silently dropped once the browser slides across the
149
- * cross axis. With both sides written, whichever side ends up anchored moves
150
- * the popover the same physical direction.
151
- *
152
- * Leaving the margin box width unchanged also means the shift does not move
153
- * the point at which `position-try-fallbacks` decides the popover overflows.
154
- *
155
- * A `<try-tactic>` fallback (`flip-block`, `flip-inline`) behaves differently to
156
- * a `<position-area>` one: it SWAPS the start and end margins rather than
157
- * keeping them, which is what preserves the gap through a flip. So a flip across
158
- * the cross axis mirrors the shift along with the rest of the placement, rather
159
- * than preserving its physical direction. Only the diagonal fallback does that,
160
- * and only for `align: 'start' | 'end'`. See `placementToTryFallbacks`.
161
- *
162
- * A positive value always moves the popover toward the cross-axis END, so
163
- * `direction: 'forwards'` means the same thing for every `align` value. See
164
- * `notes/decisions/placement-offset.md`.
165
- */
166
- function crossAxisShiftMargins(_ref2) {
167
- var placement = _ref2.placement,
168
- crossAxisShiftCssValue = _ref2.crossAxisShiftCssValue,
169
- direction = _ref2.direction;
170
- var crossAxis = placement.axis === 'block' ? 'inline' : 'block';
171
- // Wrap any string in calc() with the sign factor. CSS handles the math,
172
- // so opaque values such as design tokens negate correctly.
173
- var negated = "calc(-1 * ".concat(crossAxisShiftCssValue, ")");
174
- var isForwards = direction === 'forwards';
175
- return [{
176
- property: "margin-".concat(crossAxis, "-start"),
177
- value: isForwards ? crossAxisShiftCssValue : negated
178
- }, {
179
- property: "margin-".concat(crossAxis, "-end"),
180
- value: isForwards ? negated : crossAxisShiftCssValue
181
- }];
182
- }
183
-
184
- /**
185
- * Returns a referentially-stable, fully-resolved `TPlacement`. The memo is
186
- * keyed on the resolved primitive fields so a fresh inline `placement` object
187
- * (or undefined fields vs explicit defaults) does not produce a fresh result.
188
- */
189
- function useStablePlacement(placement) {
190
- var resolved = resolvePlacement({
191
- placement: placement
192
- });
193
- var axis = resolved.axis;
194
- var edge = resolved.edge;
195
- var align = resolved.align;
196
- var gap = resolved.offset.gap;
197
- var shiftValue = resolved.offset.crossAxisShift.value;
198
- var shiftDirection = resolved.offset.crossAxisShift.direction;
199
- // Rebuild inside the memo from primitive deps so the factory does not
200
- // close over the outer `resolved` reference and exhaustive-deps can verify.
201
- return useMemo(function () {
202
- return {
203
- axis: axis,
204
- edge: edge,
205
- align: align,
206
- offset: {
207
- gap: gap,
208
- crossAxisShift: {
209
- value: shiftValue,
210
- direction: shiftDirection
211
- }
212
- }
213
- };
214
- }, [axis, edge, align, gap, shiftValue, shiftDirection]);
215
- }
216
-
217
- /**
218
- * Hook that positions an element relative to an anchor element using
219
- * CSS Anchor Positioning (with a JS fallback for older browsers).
220
- *
221
- * This hook is the positioning primitive. It has no knowledge of popovers,
222
- * visibility, or animation. Compose it with `Popover` for anchor-positioned
223
- * top-layer content.
224
- *
225
- * When CSS Anchor Positioning is supported, it sets CSS properties
226
- * (`anchor-name`, `position-anchor`, `position-area`, `position-try-fallbacks`)
227
- * directly on the elements via `el.style.setProperty()`.
228
- *
229
- * When not supported, it falls back to JavaScript-based positioning using
230
- * `position: fixed` with measured coordinates, re-running on scroll (capture)
231
- * and resize events.
232
- */
233
- // eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
234
- export function useAnchorPosition(_ref3) {
235
- var anchorRef = _ref3.anchorRef,
236
- popoverRef = _ref3.popoverRef,
237
- _ref3$placement = _ref3.placement,
238
- placement = _ref3$placement === void 0 ? {} : _ref3$placement,
239
- _ref3$forceFallbackPo = _ref3.forceFallbackPositioning,
240
- forceFallbackPositioning = _ref3$forceFallbackPo === void 0 ? false : _ref3$forceFallbackPo,
241
- _ref3$isEnabled = _ref3.isEnabled,
242
- isEnabled = _ref3$isEnabled === void 0 ? true : _ref3$isEnabled,
243
- isOpen = _ref3.isOpen;
244
- var id = useId();
245
- var stablePlacement = useStablePlacement(placement);
246
- useLayoutEffect(function () {
247
- if (!isEnabled) {
248
- return;
249
- }
250
- var trigger = anchorRef.current;
251
- var popover = popoverRef.current;
252
- if (!trigger || !popover) {
253
- return;
254
- }
255
- var gapCssValue = stablePlacement.offset.gap;
256
- var crossAxisShiftCssValue = stablePlacement.offset.crossAxisShift.value;
257
- var shouldForceFallback = Boolean(forceFallbackPositioning);
258
- if (supportsAnchorPositioning() && !shouldForceFallback) {
259
- // Reuse an existing `anchor-name` if the element already has one.
260
- // Another popover might already be anchored to the old name.
261
- var anchorName = trigger.style.getPropertyValue('anchor-name') || "--anchor-".concat(id.replace(/:/g, ''));
262
- var gap = edgeMargin({
263
- placement: stablePlacement,
264
- offset: gapCssValue
265
- });
266
- // Computed once; also mirrored into the
267
- // `--ds-cross-axis-shift-margin-*` custom properties below.
268
- var crossAxisShift = crossAxisShiftMargins({
269
- placement: stablePlacement,
270
- crossAxisShiftCssValue: crossAxisShiftCssValue,
271
- direction: stablePlacement.offset.crossAxisShift.direction
272
- });
273
- var popoverStyles = [{
274
- property: 'position-anchor',
275
- value: anchorName
276
- }, {
277
- property: 'position-area',
278
- value: placementToPositionArea({
279
- placement: stablePlacement
280
- })
281
- }, {
282
- property: 'position-try-fallbacks',
283
- value: placementToTryFallbacks({
284
- placement: stablePlacement
285
- })
286
- },
287
- // Reset browser default popover positioning that conflicts
288
- // with anchor positioning (UA: `inset: 0; margin: auto;`)
289
- {
290
- property: 'margin',
291
- value: '0'
292
- }, {
293
- property: 'inset',
294
- value: 'auto'
295
- }, gap].concat(_toConsumableArray(crossAxisShift), _toConsumableArray(ZEROED_CROSS_AXIS_SHIFT_CUSTOM_PROPERTIES), _toConsumableArray(crossAxisShift.map(function (_ref4) {
296
- var property = _ref4.property,
297
- value = _ref4.value;
298
- return {
299
- property: CROSS_AXIS_SHIFT_CUSTOM_PROPERTY[property],
300
- value: value
301
- };
302
- })));
303
-
304
- /**
305
- * **We are never cleaning up anchor names**
306
- *
307
- * _Rationale_
308
- *
309
- * - Multiple popovers can share the same anchor element.
310
- * - Sometimes the `useAnchorPosition` hook and `Popover`
311
- * are not in the same component.
312
- * - There can be times when, even with reference counting, we
313
- * could remove the anchor-name while it is still being used
314
- * by something else - especially with async react updates
315
- * which can cause tearing
316
- *
317
- * _More_
318
- *
319
- * See `notes/decisions/anchor-name-lifetime.md`
320
- */
321
- trigger.style.setProperty('anchor-name', anchorName);
322
- return setStyle({
323
- element: popover,
324
- styles: popoverStyles
325
- });
326
- }
327
-
328
- // JS fallback. The popover is already in the top layer via
329
- // popover="auto", so we only reset UA defaults and set top/left
330
- // based on measurements.
331
- var cleanupBaseStyles = setStyle({
332
- element: popover,
333
- styles: [{
334
- property: 'margin',
335
- value: '0'
336
- }, {
337
- property: 'inset',
338
- value: 'auto'
339
- }]
340
- });
341
-
342
- // The popover can be consumer-owned (`@atlaskit/popper`'s `createPopper`),
343
- // so restore prior inline values rather than removing ours.
344
- var undoPosition;
345
- var undoHide;
346
-
347
- // `opacity: 0` not `visibility: hidden`: Firefox skips visibility-hidden
348
- // elements during `<dialog>` initial-focus traversal.
349
- function hideUntilPositioned() {
350
- // Keep the first snapshot, else we capture our own `opacity: 0`.
351
- // (`!popover` is for TS: narrowing does not reach nested functions.)
352
- if (!popover || undoHide) {
353
- return;
354
- }
355
- undoHide = setStyle({
356
- element: popover,
357
- styles: [{
358
- property: 'opacity',
359
- value: '0'
360
- }]
361
- });
362
- }
363
- function reveal() {
364
- var _undoHide;
365
- (_undoHide = undoHide) === null || _undoHide === void 0 || _undoHide();
366
- undoHide = undefined;
367
- }
368
- function restorePosition() {
369
- var _undoPosition;
370
- (_undoPosition = undoPosition) === null || _undoPosition === void 0 || _undoPosition();
371
- undoPosition = undefined;
372
- }
373
- function update() {
374
- if (!trigger || !popover) {
375
- return;
376
- }
377
- var triggerRect = trigger.getBoundingClientRect();
378
- var viewport = {
379
- width: window.innerWidth,
380
- height: window.innerHeight
381
- };
382
-
383
- // Resolve consumer-supplied CSS length strings (tokens, calc, var,
384
- // px, rem, etc) to pixels. The probe is mounted INSIDE the popover
385
- // itself so the resolved length matches the scope a token would
386
- // resolve against from inside the popover content. (The popover
387
- // lives in the top layer, not the trigger's DOM tree, so probing
388
- // at `popover.parentElement` would resolve in a different scope
389
- // than the consumer's token authoring expects.)
390
- var gapPx = resolveCssLengthToPixels({
391
- value: gapCssValue,
392
- container: popover
393
- });
394
- var crossAxisShiftPx = resolveCssLengthToPixels({
395
- value: crossAxisShiftCssValue,
396
- container: popover
397
- });
398
- var _computeFallbackPosit = computeFallbackPosition({
399
- triggerRect: triggerRect,
400
- popoverEl: popover,
401
- placement: stablePlacement,
402
- viewport: viewport,
403
- gap: gapPx,
404
- crossAxisShift: {
405
- value: crossAxisShiftPx,
406
- direction: stablePlacement.offset.crossAxisShift.direction
407
- }
408
- }),
409
- top = _computeFallbackPosit.top,
410
- left = _computeFallbackPosit.left;
411
-
412
- // Restore first so the snapshot stays the consumer's value.
413
- restorePosition();
414
- undoPosition = setStyle({
415
- element: popover,
416
- styles: [{
417
- property: 'top',
418
- value: "".concat(top, "px")
419
- }, {
420
- property: 'left',
421
- value: "".concat(left, "px")
422
- }]
423
- });
424
- reveal();
425
- }
426
-
427
- // Throttle scroll/resize updates to one per animation frame
428
- var scheduledUpdate = rafSchedule(update);
429
-
430
- // Wait for the FIRST valid layout before measuring: the popover
431
- // is `display: none` until `showPopover()`, and some browsers
432
- // fire `toggle` before layout, so RAF-after-toggle is unreliable.
433
- // ResizeObserver fires once the browser has real dimensions.
434
- // Self-disconnects after one valid measurement; ongoing
435
- // scroll/resize is handled by the window listeners below.
436
- // `ResizeObserver` is missing in some non-DOM jest environments
437
- // (e.g. post-office's `node` environment). Fall back to a no-op
438
- // observer there. The scroll/resize listeners below still keep
439
- // the popover positioned in the rare case the consumer also
440
- // polyfilled `showPopover` but not `ResizeObserver`. Real
441
- // browsers always have it. The `NoopResizeObserver` class is
442
- // hoisted to module scope (see top of file) so we do not allocate
443
- // a new constructor on every effect run.
444
- var ResizeObserverImpl = typeof ResizeObserver !== 'undefined' ? ResizeObserver : NoopResizeObserver;
445
- var resizeObserver = new ResizeObserverImpl(function () {
446
- if (popover.offsetWidth > 0 && popover.offsetHeight > 0) {
447
- update();
448
- resizeObserver.disconnect();
449
- }
450
- });
451
-
452
- // If the popover is already open by the time this effect runs (for
453
- // example, the parent `useAnchorPosition` effect runs after the child
454
- // `Popover` effect that called `showPopover()`), the initial `toggle`
455
- // event has already fired and our listener below would miss it. Start
456
- // observing immediately so the first measurement still happens.
457
- if (isPopoverOpen(popover)) {
458
- hideUntilPositioned();
459
- resizeObserver.observe(popover);
460
- }
461
- var undoPositioning = combine(cleanupBaseStyles,
462
- // Each open needs a fresh measurement, so hide and re-observe.
463
- bind(popover, {
464
- type: 'toggle',
465
- listener: function listener(event) {
466
- var toggleEvent = event;
467
- if (toggleEvent.newState === 'open') {
468
- hideUntilPositioned();
469
- resizeObserver.observe(popover);
470
- }
471
- }
472
- }), function () {
473
- return resizeObserver.disconnect();
474
- }, bind(window, {
475
- type: 'scroll',
476
- listener: scheduledUpdate,
477
- options: {
478
- capture: true,
479
- passive: true
480
- }
481
- }), bind(window, {
482
- type: 'resize',
483
- listener: scheduledUpdate,
484
- options: {
485
- passive: true
486
- }
487
- }), function () {
488
- scheduledUpdate.cancel();
489
- restorePosition();
490
- reveal();
491
- });
492
- return undoPositioning;
493
- }, [anchorRef, popoverRef, stablePlacement, forceFallbackPositioning, isEnabled, id, isOpen]);
494
- }
@@ -1,118 +0,0 @@
1
- import { useLayoutEffect } from 'react';
2
- import once from '@atlaskit/ds-lib/once';
3
- import { setStyle } from './set-style';
4
-
5
- /**
6
- * Probes anchor SIZING, separately from `anchor-name` (see
7
- * `supportsAnchorPositioning`): some browsers have positioning but not sizing.
8
- * `once()` for SSR safety.
9
- */
10
- var supportsAnchorSize = once(function () {
11
- if (typeof window === 'undefined' || typeof CSS === 'undefined' || typeof CSS.supports !== 'function') {
12
- return false;
13
- }
14
- return CSS.supports('min-inline-size', 'anchor-size(self-inline)');
15
- });
16
-
17
- /**
18
- * The JS fallback's stand-in for `anchor-size(self-inline)`. `offsetWidth` / `offsetHeight` are
19
- * physical, so the axis is picked from the popover's `writing-mode`. Anything not horizontal is
20
- * treated as vertical, and an unknown or empty value falls back to `offsetWidth`.
21
- */
22
- function getFallbackAnchorInlineSize(_ref) {
23
- var anchor = _ref.anchor,
24
- popover = _ref.popover;
25
- // `String()` because not every test environment implements `writing-mode`.
26
- var writingMode = String(getComputedStyle(popover).writingMode);
27
- var isVertical = writingMode.startsWith('vertical') || writingMode.startsWith('sideways');
28
- return isVertical ? anchor.offsetHeight : anchor.offsetWidth;
29
- }
30
-
31
- /**
32
- * Sets the width of a popover element relative to its anchor element.
33
- *
34
- * - `'match-anchor'` → `inline-size: anchor-size(self-inline)`, exact
35
- * - `'min-anchor'` → `min-inline-size: anchor-size(self-inline)`, floor only
36
- * - `'none'` → `min-inline-size: max-content`, content floor (default)
37
- *
38
- * `self-inline` is the inline axis of the element USING the function (the popover), never the
39
- * anchor, so it matches the axis `inline-size` constrains. See
40
- * `notes/decisions/width-from-anchor-floors.md`.
41
- */
42
- export function useWidthFromAnchor(_ref2) {
43
- var mode = _ref2.mode,
44
- popoverRef = _ref2.popoverRef,
45
- anchorRef = _ref2.anchorRef,
46
- isOpen = _ref2.isOpen;
47
- useLayoutEffect(function () {
48
- var node = popoverRef.current;
49
- if (!node) {
50
- return;
51
- }
52
- if (mode === 'match-anchor') {
53
- // No floor here: it would let the popover grow past the anchor.
54
- if (supportsAnchorSize()) {
55
- return setStyle({
56
- element: node,
57
- styles: [{
58
- property: 'inline-size',
59
- value: 'anchor-size(self-inline)'
60
- }]
61
- });
62
- }
63
- var anchor = anchorRef.current;
64
- if (anchor) {
65
- return setStyle({
66
- element: node,
67
- styles: [{
68
- property: 'inline-size',
69
- value: "".concat(getFallbackAnchorInlineSize({
70
- anchor: anchor,
71
- popover: node
72
- }), "px")
73
- }]
74
- });
75
- }
76
- return;
77
- }
78
- if (mode === 'min-anchor') {
79
- // One floor only. The content floor from mode 'none' deliberately does not also apply
80
- // here, and cannot: it would need this same property. See the decision note.
81
- if (supportsAnchorSize()) {
82
- return setStyle({
83
- element: node,
84
- styles: [{
85
- property: 'min-inline-size',
86
- value: 'anchor-size(self-inline)'
87
- }]
88
- });
89
- }
90
- var _anchor = anchorRef.current;
91
- if (_anchor) {
92
- return setStyle({
93
- element: node,
94
- styles: [{
95
- property: 'min-inline-size',
96
- value: "".concat(getFallbackAnchorInlineSize({
97
- anchor: _anchor,
98
- popover: node
99
- }), "px")
100
- }]
101
- });
102
- }
103
-
104
- // Nothing to measure, so no floor.
105
- return;
106
- }
107
-
108
- // Makes a too-narrow span overflow and drive `position-try-fallbacks` instead of wrapping.
109
- // `@atlaskit/popper` resets this exact property to `0` for its fit-to-viewport mode.
110
- return setStyle({
111
- element: node,
112
- styles: [{
113
- property: 'min-inline-size',
114
- value: 'max-content'
115
- }]
116
- });
117
- }, [mode, popoverRef, anchorRef, isOpen]);
118
- }
@@ -1,51 +0,0 @@
1
- import { type TCrossAxisShiftDirection, type TPlacement } from './resolve-placement';
2
- /**
3
- * JS fallback positioning math. Used only when the browser does not
4
- * support CSS Anchor Positioning (less than 6% of users).
5
- *
6
- * Consumer-supplied `offset.gap` and `offset.crossAxisShift` are
7
- * honored: the caller (`useAnchorPosition`) resolves CSS length strings
8
- * (tokens, calc, var, etc) to pixels via a DOM probe before passing them
9
- * in. The caller also keeps the popover hidden (`opacity: 0`) until the
10
- * first measurement completes, so the popover never paints at the wrong
11
- * position.
12
- */
13
- /**
14
- * A viewport-relative position, in pixels. Physical rather than logical
15
- * because it is written straight to `top` / `left`.
16
- */
17
- type TPosition = {
18
- top: number;
19
- left: number;
20
- };
21
- /**
22
- * The cross-axis shift with its length already resolved to pixels.
23
- *
24
- * Mirrors `TCrossAxisShiftOffset`, whose `value` is an unresolved CSS length
25
- * string. Only this module needs the pixel form, because only this module does
26
- * the arithmetic itself.
27
- */
28
- type TResolvedCrossAxisShift = {
29
- value: number;
30
- direction: TCrossAxisShiftDirection;
31
- };
32
- /**
33
- * Computes popover position from trigger rect and viewport, flipping to
34
- * the side with more space when needed. Re-runs on scroll/resize.
35
- *
36
- * `gap` and `crossAxisShift` are pre-resolved to pixels by the
37
- * caller (see `resolveCssLengthToPixels`), so this function stays purely
38
- * numeric.
39
- */
40
- export declare function computeFallbackPosition({ triggerRect, popoverEl, placement, viewport, gap, crossAxisShift, }: {
41
- triggerRect: DOMRect;
42
- popoverEl: HTMLElement;
43
- placement: TPlacement;
44
- viewport: {
45
- width: number;
46
- height: number;
47
- };
48
- gap: number;
49
- crossAxisShift: TResolvedCrossAxisShift;
50
- }): TPosition;
51
- export {};
@@ -1,13 +0,0 @@
1
- import { type TPlacementOptions } from './resolve-placement';
2
- /**
3
- * Returns the `position-try-fallbacks` value for a placement.
4
- *
5
- * @example
6
- * // Centered on the block-end edge (the default placement):
7
- * placementToTryFallbacks({ placement: { axis: 'block', edge: 'end', align: 'center' } });
8
- * // => 'block-end span-inline-end, block-end span-inline-start, flip-block,
9
- * // block-start span-inline-end, block-start span-inline-start'
10
- */
11
- export declare function placementToTryFallbacks({ placement }: {
12
- placement: TPlacementOptions;
13
- }): string;
@@ -1,20 +0,0 @@
1
- /**
2
- * Resolves a CSS length value to a pixel number using a hidden DOM probe.
3
- *
4
- * Used by the JS positioning fallback: consumer offsets can be numbers,
5
- * `${n}px` strings, tokenised `var(--ds-space-100, 8px)` strings, `calc()`
6
- * expressions, viewport units, etc. Parsing arbitrary CSS in JS is fragile
7
- * (and impossible for `calc(var(...))` without a layout context), so we let
8
- * the browser do the math.
9
- *
10
- * The probe is appended to `container` so it inherits the same containing
11
- * block, font size, and custom-property scope as the popover.
12
- *
13
- * Cost: one synchronous reflow on first cacheable resolution per
14
- * (container, value) pair, then a `WeakMap` lookup. Resolutions involving
15
- * `var(` are intentionally not cached.
16
- */
17
- export declare function resolveCssLengthToPixels({ value, container, }: {
18
- value: number | string;
19
- container: HTMLElement;
20
- }): number;