@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,19 @@
1
+ import { type TCrossAxisShiftDirection, type TPlacement } from '../resolve-placement';
2
+ import { type TStyleDeclaration } from '../set-style';
3
+ import { type TLogicalMarginProperty } from './fit-margins';
4
+ /**
5
+ * The cross-axis shift, written antisymmetrically on BOTH cross-axis sides rather
6
+ * than as one margin. Do not collapse it to one side: a single margin widens the
7
+ * margin box, so `anchor-center` displaces the border box by only half the value,
8
+ * a `<position-area>` fallback sliding across the cross axis drops it entirely,
9
+ * and the widened box changes where the popover is judged to overflow.
10
+ *
11
+ * A positive value always moves the popover toward the cross-axis END, so
12
+ * `direction: 'forwards'` means the same thing for every `align` value. See
13
+ * `notes/decisions/placement-offset.md`.
14
+ */
15
+ export declare function crossAxisShiftMargins({ placement, crossAxisShiftCssValue, direction, }: {
16
+ placement: TPlacement;
17
+ crossAxisShiftCssValue: string;
18
+ direction: TCrossAxisShiftDirection;
19
+ }): TStyleDeclaration<TLogicalMarginProperty>[];
@@ -0,0 +1,10 @@
1
+ import { type TPlacement } from '../resolve-placement';
2
+ import { type TStyleDeclaration } from '../set-style';
3
+ import { type TLogicalMarginProperty } from './fit-margins';
4
+ /**
5
+ * The margin that creates the gap between the popover and its anchor.
6
+ */
7
+ export declare function edgeMargin({ placement, offset, }: {
8
+ placement: TPlacement;
9
+ offset: string;
10
+ }): TStyleDeclaration<TLogicalMarginProperty>;
@@ -0,0 +1,30 @@
1
+ import { type TPlacement, type TPlacementAxis, type TPlacementEdge } from '../resolve-placement';
2
+ import { type TStyleDeclaration } from '../set-style';
3
+ /**
4
+ * Every logical margin property the fit recipe can write. Declared here and
5
+ * imported by `edge-margin.tsx` and `cross-axis-shift-margins.tsx`: two
6
+ * structurally identical declarations are interchangeable to the compiler, so a
7
+ * divergence would surface only as a silently-ignored property name at
8
+ * `setStyle`.
9
+ */
10
+ export type TLogicalMarginProperty = `margin-${TPlacementAxis}-${TPlacementEdge}`;
11
+ /**
12
+ * The margin half of the fit recipe. The caps reserve the viewport padding by
13
+ * subtracting it, but nothing makes the reservation land on the viewport side, so
14
+ * a capped popover packs flush against the edge it slid or flipped to.
15
+ *
16
+ * The padding buys visual clearance and nothing else: at the cap the margin box
17
+ * is exactly `100%` of the cell, so these margins never make
18
+ * `position-try-fallbacks` fire. Only the unclamped placement-axis floor does
19
+ * that; see `getPlacementAxisFloor`.
20
+ *
21
+ * The cross-axis sides are composed, not overwritten: they already carry the
22
+ * antisymmetric `crossAxisShift` pair.
23
+ */
24
+ export declare function getFitMarginDeclarations({ placement, crossAxisShiftMargins, }: {
25
+ placement: TPlacement;
26
+ /**
27
+ * The antisymmetric cross-axis shift pair, to compose the padding into.
28
+ */
29
+ crossAxisShiftMargins: TStyleDeclaration<TLogicalMarginProperty>[];
30
+ }): TStyleDeclaration<TLogicalMarginProperty>[];
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The `anchor-name` an element already carries, or `null`. Computed as well as
3
+ * inline, so a name set from a STYLESHEET is found: a minted inline name would
4
+ * otherwise override it for every other rule anchored to that element.
5
+ * `anchor-name` is a list and `position-anchor` takes one ident, so the first
6
+ * name is the one to anchor to.
7
+ */
8
+ export declare function getExistingAnchorName(element: HTMLElement): string | null;
@@ -1,6 +1,6 @@
1
- import { type TPlacementOptions } from '../internal/resolve-placement';
1
+ import { type TPlacement } from '../resolve-placement';
2
2
  /**
3
- * Resolves placement options to CSS `position-area` syntax.
3
+ * Resolves a placement to CSS `position-area` syntax.
4
4
  *
5
5
  * The `align` field represents the *visual* alignment:
6
6
  * - `align: 'start'` means "aligned to the trigger's start edge" (left in LTR for block axis)
@@ -11,11 +11,15 @@ import { type TPlacementOptions } from '../internal/resolve-placement';
11
11
  * Visual start-alignment requires `span-{cross-axis}-end` (expand toward end = start-aligned).
12
12
  * This function handles the inversion internally.
13
13
  *
14
+ * Takes the RESOLVED placement, which the hook threads through positioning,
15
+ * sizing and margins alike, so nothing here can disagree with them.
16
+ *
14
17
  * @example
15
- * placementToPositionArea({ placement: { axis: 'block', edge: 'end', align: 'center' } }) // → 'block-end'
16
- * placementToPositionArea({ placement: { axis: 'block', edge: 'end', align: 'start' } }) // → 'block-end span-inline-end'
17
- * placementToPositionArea({ placement: { axis: 'block', edge: 'end', align: 'end' } }) // → 'block-end span-inline-start'
18
+ * // For a `block-end` placement, by `align`:
19
+ * // 'center' → 'block-end'
20
+ * // 'start' → 'block-end span-inline-end'
21
+ * // 'end' → 'block-end span-inline-start'
18
22
  */
19
23
  export declare function placementToPositionArea({ placement }: {
20
- placement: TPlacementOptions;
24
+ placement: TPlacement;
21
25
  }): string;
@@ -0,0 +1,30 @@
1
+ import { type TPlacement } from '../resolve-placement';
2
+ /**
3
+ * Returns the `position-try-fallbacks` value for a RESOLVED placement. The
4
+ * browser tries the entries in order and uses the first one whose margin box
5
+ * fits, so an entry over a cell already tried can never be chosen; `slideOnEdge`
6
+ * explains which ones those are.
7
+ *
8
+ * Centred: slide along the requested edge to either span, flip to the opposite
9
+ * edge, then slide along that edge to either span.
10
+ *
11
+ * Start / end aligned: slide along the requested edge (re-centred, then the far
12
+ * span), flip to the opposite edge keeping the alignment, flip on the diagonal
13
+ * into the opposite corner, then re-centre on the opposite edge. The diagonal is
14
+ * the clean escape when the popover would land off screen on both axes: a flip
15
+ * keyword carries the gap and shift margins across, where a position-area slide
16
+ * leaves them on the original sides.
17
+ *
18
+ * @example
19
+ * // Centred on the block-end edge (the default placement):
20
+ * placementToTryFallbacks({ placement: resolvePlacement({ placement: {} }) });
21
+ * // => 'block-end span-inline-end, block-end span-inline-start, flip-block,
22
+ * // block-start span-inline-end, block-start span-inline-start'
23
+ *
24
+ * // Start-aligned on the block-end edge:
25
+ * placementToTryFallbacks({ placement: resolvePlacement({ placement: { align: 'start' } }) });
26
+ * // => 'block-end, block-end span-inline-start, flip-block, flip-block flip-inline, block-start'
27
+ */
28
+ export declare function placementToTryFallbacks({ placement }: {
29
+ placement: TPlacement;
30
+ }): string;
@@ -0,0 +1,126 @@
1
+ import { type TPlacement, type TPlacementAxis } from './resolve-placement';
2
+ import { type TStyleDeclaration } from './set-style';
3
+ /**
4
+ * The space kept clear between a popover and the viewport edge. Matches legacy
5
+ * `@atlaskit/popper`'s `preventOverflow` padding of `5`.
6
+ */
7
+ export declare const VIEWPORT_PADDING = "5px";
8
+ /**
9
+ * The upper bound for the default placement-axis floor while fitting. A capped
10
+ * popover never overflows, so it never flips; this floor makes the margin box
11
+ * overflow a too-small cell so `position-try-fallbacks` fires. A flip driver and
12
+ * nothing else, so the JavaScript fallback, which has no cell to overflow, never
13
+ * writes it. See `getPlacementAxisFloor`.
14
+ *
15
+ * 150px is the value the flip was measured with, not a design constant
16
+ * (`notes/plans/should-fit-viewport.md`). Deliberately not a `space.*` token:
17
+ * theming it would move the flip threshold, and so change which side a popover
18
+ * lands on.
19
+ */
20
+ export declare const FALLBACK_MINIMUM_MAIN_AXIS_SIZE = "150px";
21
+ /**
22
+ * Which way the POPOVER's inline axis runs; `'vertical'` covers the `vertical-*`
23
+ * and `sideways-*` writing modes. Measured rather than assumed, because every
24
+ * logical property this module writes resolves against the popover's own writing
25
+ * mode, and so must the viewport unit paired with it.
26
+ */
27
+ export type TPopoverWritingMode = 'horizontal' | 'vertical';
28
+ /**
29
+ * How one axis of an anchored popover is sized. Both axes take these four values
30
+ * under identical rules.
31
+ *
32
+ * - `'content'`: sized by its content. Named after `flex-basis: content`; NOT
33
+ * `'auto'`, whose CSS meaning for a positioned box is shrink-to-fit, the very
34
+ * behaviour rule 4 removes.
35
+ * - `'match-anchor'`: exactly the anchor's size on this axis.
36
+ * - `'min-anchor'`: at least the anchor's size on this axis, free to grow.
37
+ * - `'max-available'`: at most the space between the anchor and the viewport
38
+ * edge, content-sized below that. Capped by the `position-area` cell on the
39
+ * placement axis and by the viewport on the cross axis (a `span-*` cell is
40
+ * narrower than the viewport, so capping to it would wrap content that fits on
41
+ * screen).
42
+ *
43
+ * Four rules apply on top:
44
+ *
45
+ * 1. Fitting one axis fills a `'content'` other axis with `'max-available'` too.
46
+ * An explicit value is never overridden, so caps stay strictly per-axis.
47
+ * 2. Fitting either axis floors the placement axis, because flipping is a
48
+ * whole-popover outcome. See `getPlacementAxisFloor`.
49
+ * 3. The viewport cap is unconditional, so a non-fitting axis still cannot
50
+ * overhang the screen. `'max-available'` and `'content'` are therefore
51
+ * indistinguishable on the cross axis.
52
+ * 4. A non-anchor-relative inline axis gets `inline-size: max-content`. Under
53
+ * `position-area` the containing block is the cell, so a CSS `auto` inline
54
+ * size shrink-to-fits a too-narrow cell and WRAPS into it, never overflowing
55
+ * and so never sliding to the roomier side. The caps still beat it, because a
56
+ * max beats a definite size. Not needed on the block axis, which does not
57
+ * wrap, nor on the JavaScript fallback, whose containing block is the
58
+ * viewport. Consumers opt out with `shouldPreserveInlineSize`.
59
+ *
60
+ * See `notes/decisions/fit-available-space.md`.
61
+ */
62
+ export type TPopoverAxisSize = 'content' | 'match-anchor' | 'min-anchor' | 'max-available';
63
+ /**
64
+ * Narrowed rather than `string`, so a typo cannot reach `setStyle` as a
65
+ * silently-ignored property name.
66
+ */
67
+ export type TPopoverSizeProperty = `${TPlacementAxis}-size` | `min-${TPlacementAxis}-size` | `max-${TPlacementAxis}-size`;
68
+ /**
69
+ * The anchor's size along each axis, as a CSS length. `null` when nothing needs
70
+ * it, so the measurement is skipped.
71
+ *
72
+ * The CSS path uses the `self-` keywords, whose axes are the POPOVER's, so each
73
+ * value matches the axis of the property it is set on (`anchor-size(inline)` and
74
+ * `anchor-size(width)` are both silently wrong in a vertical writing mode). The
75
+ * JavaScript fallback uses a measured pixel length, because `anchor-size()` needs
76
+ * a `position-anchor` that path never sets. See
77
+ * `notes/decisions/width-from-anchor-floors.md`.
78
+ */
79
+ export type TAnchorSizeValues = Record<TPlacementAxis, string | null>;
80
+ /**
81
+ * The whole size recipe for an anchored popover, in one place. Pure, so the truth
82
+ * table is unit-testable without a browser, and every size property on the
83
+ * popover host has exactly one writer.
84
+ *
85
+ * While anything is fitting on the CSS path, the placement-axis floor is never
86
+ * clamped to the CELL cap. Its whole job is to exceed it: overflow detection
87
+ * reads the margin box AFTER the clamp (css-anchor-position-1 §6.5), so
88
+ * `min(floor, cap)` suppresses `position-try-fallbacks` entirely. The cross-axis
89
+ * floor is always clamped, because there it is a size contract rather than a flip
90
+ * driver.
91
+ *
92
+ * See `notes/decisions/fit-available-space.md` for the full recipe and the
93
+ * measured evidence.
94
+ */
95
+ export declare function getAnchoredPopoverSizeDeclarations({ placement, inlineSize, blockSize, isUsingCssAnchorPositioning, anchorSize, writingMode, shouldPreserveInlineSize, }: {
96
+ /**
97
+ * Supplies the axis, the gap and `minSize`.
98
+ */
99
+ placement: TPlacement;
100
+ /**
101
+ * The requested size, before rule 1 fills a `'content'` axis.
102
+ */
103
+ inlineSize: TPopoverAxisSize;
104
+ /**
105
+ * The requested size, before rule 1 fills a `'content'` axis.
106
+ */
107
+ blockSize: TPopoverAxisSize;
108
+ /**
109
+ * `false` on the JavaScript fallback path, which has no position-area cell, so
110
+ * both axes get the viewport cap and no flip floor is written.
111
+ */
112
+ isUsingCssAnchorPositioning: boolean;
113
+ /**
114
+ * Already resolved for the active path. See `TAnchorSizeValues`.
115
+ */
116
+ anchorSize: TAnchorSizeValues;
117
+ /**
118
+ * Picks the physical viewport unit for each logical axis. See `viewportUnit`.
119
+ */
120
+ writingMode: TPopoverWritingMode;
121
+ /**
122
+ * `true` skips rule 4, for an element the consumer owns and may have given a
123
+ * `width` that an inline `max-content` would override.
124
+ */
125
+ shouldPreserveInlineSize?: boolean;
126
+ }): TStyleDeclaration<TPopoverSizeProperty>[];
@@ -8,32 +8,20 @@ import { type TRoleRequiringAccessibleName } from './role-types';
8
8
  * manually for tooltip triggers.
9
9
  */
10
10
  type TAriaForTriggerRole = TRoleRequiringAccessibleName | 'listbox' | 'tree' | 'grid';
11
- /**
12
- * Valid values for the `aria-haspopup` attribute.
13
- *
14
- * Derived from the HTML spec - maps popover roles to what the trigger
15
- * announces. `undefined` means the attribute is omitted entirely (used for
16
- * non-popup roles like `tooltip`/`status`/`alert`/`note`/`log` where
17
- * `aria-haspopup` would be misleading).
18
- *
19
- * `true` is intentionally NOT in the union - the runtime only ever produces
20
- * the explicit string forms, so widening the type would invite consumers to
21
- * pass `true` and get an `aria-haspopup="true"` serialisation that the
22
- * runtime no longer emits.
23
- */
24
- type TAriaHasPopupValue = 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid' | undefined;
25
- /**
26
- * Non-undefined subset of `TAriaHasPopupValue`. Every role accepted by
27
- * `getAriaForTrigger` maps to a concrete `aria-haspopup` string, so the
28
- * trigger always receives a defined attribute value.
29
- */
30
- type TAriaHasPopupForTrigger = Exclude<TAriaHasPopupValue, undefined>;
31
- type TGetAriaForTriggerOptions = {
11
+ type TAriaHasPopupByRole = {
12
+ dialog: 'dialog';
13
+ alertdialog: 'dialog';
14
+ menu: 'menu';
15
+ listbox: 'listbox';
16
+ tree: 'tree';
17
+ grid: 'grid';
18
+ };
19
+ type TGetAriaForTriggerOptions<TRole extends TAriaForTriggerRole> = {
32
20
  /**
33
21
  * The `role` that will be set on the `<Popover>` element. Used to derive
34
22
  * the correct `aria-haspopup` value for the trigger.
35
23
  */
36
- role: TAriaForTriggerRole;
24
+ role: TRole;
37
25
  /**
38
26
  * Whether the popover is currently open. Drives `aria-expanded`.
39
27
  */
@@ -44,8 +32,8 @@ type TGetAriaForTriggerOptions = {
44
32
  */
45
33
  popoverId: string;
46
34
  };
47
- type TAriaForTrigger = {
48
- 'aria-haspopup': TAriaHasPopupForTrigger;
35
+ type TAriaForTrigger<TRole extends TAriaForTriggerRole> = {
36
+ 'aria-haspopup': TAriaHasPopupByRole[TRole];
49
37
  'aria-expanded': boolean;
50
38
  /**
51
39
  * `aria-controls` is `undefined` while the popover is closed, because
@@ -102,5 +90,5 @@ type TAriaForTrigger = {
102
90
  * );
103
91
  * ```
104
92
  */
105
- export declare function getAriaForTrigger({ role, isOpen, popoverId, }: TGetAriaForTriggerOptions): TAriaForTrigger;
93
+ export declare function getAriaForTrigger<TRole extends TAriaForTriggerRole>({ role, isOpen, popoverId, }: TGetAriaForTriggerOptions<TRole>): TAriaForTrigger<TRole>;
106
94
  export {};
@@ -0,0 +1,3 @@
1
+ export declare function isNativeElementOpen({ element }: {
2
+ element: HTMLElement;
3
+ }): boolean;
@@ -0,0 +1,36 @@
1
+ import { type TCrossAxisShiftDirection, type TPlacement } from '../resolve-placement';
2
+ /**
3
+ * A viewport-relative position, in pixels. Physical rather than logical
4
+ * because it is written straight to `top` / `left`.
5
+ */
6
+ type TPosition = {
7
+ top: number;
8
+ left: number;
9
+ };
10
+ /**
11
+ * `TCrossAxisShiftOffset` with its CSS length already resolved to pixels. Only
12
+ * this module needs the pixel form, because only this module does arithmetic.
13
+ */
14
+ type TResolvedCrossAxisShift = {
15
+ value: number;
16
+ direction: TCrossAxisShiftDirection;
17
+ };
18
+ /**
19
+ * Computes the popover position from the trigger rect and viewport, flipping to
20
+ * the side with more space when needed.
21
+ *
22
+ * `gap` and `crossAxisShift` arrive already resolved to pixels (see
23
+ * `resolveCssLengthToPixels`), so this stays purely numeric.
24
+ */
25
+ export declare function computeFallbackPosition({ triggerRect, popoverEl, placement, viewport, gap, crossAxisShift, }: {
26
+ triggerRect: DOMRect;
27
+ popoverEl: HTMLElement;
28
+ placement: TPlacement;
29
+ viewport: {
30
+ width: number;
31
+ height: number;
32
+ };
33
+ gap: number;
34
+ crossAxisShift: TResolvedCrossAxisShift;
35
+ }): TPosition;
36
+ export {};
@@ -0,0 +1,15 @@
1
+ import { type TPlacement } from '../resolve-placement';
2
+ import { type TStyleDeclaration } from '../set-style';
3
+ /**
4
+ * Positions `popover` against `anchor` from measurements, and keeps it there
5
+ * across opens, scroll and resize. Returns the cleanup.
6
+ *
7
+ * No fit margins on this path: the gap and the cross-axis shift are coordinate
8
+ * deltas here, so writing them as margins too would double-apply them.
9
+ */
10
+ export declare function applyJavaScriptFallbackPositioning({ anchor, popover, placement, sizeStyles, }: {
11
+ anchor: HTMLElement;
12
+ popover: HTMLElement;
13
+ placement: TPlacement;
14
+ sizeStyles: TStyleDeclaration[];
15
+ }): () => void;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Resolves a CSS length value to a pixel number using a hidden DOM probe.
3
+ *
4
+ * Used by the JS positioning fallback, where a consumer offset can be a number, a
5
+ * `${n}px` string, a token, a `calc()` expression, a viewport unit, and so on.
6
+ * Parsing arbitrary CSS in JS is fragile, and impossible for `calc(var(...))`
7
+ * without a layout context, so the browser does the math instead. The probe is
8
+ * appended to `container` so it inherits the popover's containing block, font
9
+ * size and custom-property scope. Anything unmeasurable resolves to `0`.
10
+ *
11
+ * Costs one synchronous style and layout flush per call that reaches the probe.
12
+ * Deliberately NOT memoised: a token can be retargeted by an override on the
13
+ * consumer's wrapper, a viewport unit changes on resize and an `em` on a font
14
+ * change, so no (container, value) cache can be proven fresh. The caller decides
15
+ * when to re-resolve; see `applyJavaScriptFallbackPositioning`.
16
+ */
17
+ export declare function resolveCssLengthToPixels({ value, container, }: {
18
+ value: number | string;
19
+ container: HTMLElement;
20
+ }): number;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Callers on the JavaScript fallback path degrade rather than throw, so this is
3
+ * the only signal that a popover is not where it should be.
4
+ */
5
+ export declare function warnFallbackFailure({ error }: {
6
+ error: unknown;
7
+ }): void;
@@ -17,13 +17,15 @@ export type TPlacementEdge = 'start' | 'end';
17
17
  export type TPlacementAlign = 'start' | 'center' | 'end';
18
18
  /**
19
19
  * Fully-resolved placement (internal). Use `resolvePlacement` for partial input.
20
- * `offset.gap` and `offset.crossAxisShift.value` are always CSS
21
- * length strings.
20
+ * `offset.gap`, `offset.crossAxisShift.value` and `minSize` are always CSS length
21
+ * strings. `minSize` stays `undefined` when unset, so "not specified" and an
22
+ * explicit `0` remain distinguishable.
22
23
  */
23
24
  export type TPlacement = {
24
25
  axis: TPlacementAxis;
25
26
  edge: TPlacementEdge;
26
27
  align: TPlacementAlign;
28
+ minSize?: string;
27
29
  offset: {
28
30
  gap: string;
29
31
  crossAxisShift: TCrossAxisShiftOffset;
@@ -42,6 +44,17 @@ export type TPlacementOptions = {
42
44
  axis?: TPlacementAxis;
43
45
  edge?: TPlacementEdge;
44
46
  align?: TPlacementAlign;
47
+ /**
48
+ * The minimum size the popover keeps along the PLACEMENT axis. A number is
49
+ * pixels; a string is any CSS length.
50
+ *
51
+ * Once EITHER axis asks for `'max-available'` the placement axis is floored
52
+ * whether this is set or not, because a cap with no floor stops the popover
53
+ * flipping to a roomier side. This always beats that default floor, and
54
+ * composes with an anchor floor as `max(minSize, anchorSize)` - so `0` caps the
55
+ * popover without letting it move, but does not remove an anchor floor.
56
+ */
57
+ minSize?: number | string;
45
58
  offset?: {
46
59
  gap?: number | string;
47
60
  crossAxisShift?: {
@@ -57,6 +70,9 @@ export type TPlacementOptions = {
57
70
  * Defaults: `axis: 'block'`, `edge: 'end'`, `align: 'center'`,
58
71
  * `offset.gap: token('space.100', '8px')`,
59
72
  * `offset.crossAxisShift: { value: '0px', direction: 'forwards' }`.
73
+ *
74
+ * `minSize` has no default here: whether it needs one depends on how the popover
75
+ * is sized, which only `useAnchoredPopover` knows.
60
76
  */
61
77
  export declare function resolvePlacement({ placement }: {
62
78
  placement: TPlacementOptions;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Probes `anchor-name` and NOT `anchor-size()`: some browsers have positioning
3
+ * without sizing, which is what `supportsAnchorSize` is for.
4
+ */
5
+ export declare const supportsAnchorPositioning: () => boolean;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Separate from `supportsAnchorPositioning`: some browsers have anchor
3
+ * positioning without anchor sizing.
4
+ */
5
+ export declare const supportsAnchorSize: () => boolean;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * A lazy, cached `CSS.supports(property, value)` probe. Lazy so it is SSR safe
3
+ * and cannot cause a hydration mismatch: nothing is read until the first call,
4
+ * which happens in an effect. Cached because the answer cannot change within a
5
+ * page. `false` wherever `CSS.supports` is missing (SSR, jsdom), so a caller
6
+ * falls back rather than throws.
7
+ */
8
+ export declare function createCssDeclarationProbe({ property, value, }: {
9
+ property: string;
10
+ value: string;
11
+ }): () => boolean;
@@ -0,0 +1,25 @@
1
+ import type { CompiledStyles } from '@compiled/react';
2
+ /**
3
+ * The host surface reset is declared twice, in `popover/popover.tsx` and
4
+ * `dialog/dialog-content.tsx`, because ADS forbids sharing styles across files
5
+ * (`no-exported-styles` / `no-imported-style-values` — Compiled styles are null
6
+ * at runtime). Each copy asserts itself against this type with
7
+ * `TSurfaceResetCheck`, so the compiler fails if either drifts. Change this
8
+ * first, then both copies.
9
+ */
10
+ export type TSurfaceReset = CompiledStyles<{
11
+ pointerEvents: 'auto';
12
+ whiteSpace: 'normal';
13
+ wordBreak: 'normal';
14
+ overflowWrap: 'normal';
15
+ textAlign: 'start';
16
+ textIndent: '0';
17
+ textTransform: 'none';
18
+ }>;
19
+ type TIsIdentical<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => (T extends B ? 1 : 2) ? true : false;
20
+ /**
21
+ * Use as `true satisfies TSurfaceResetCheck<typeof surfaceResetStyles.root>`.
22
+ * Identity rather than assignability, so extra or widened properties also fail.
23
+ */
24
+ export type TSurfaceResetCheck<TActual> = TIsIdentical<TActual, TSurfaceReset> extends true ? true : 'surfaceResetStyles does not match TSurfaceReset in internal/surface-reset.tsx';
25
+ export {};
@@ -0,0 +1,48 @@
1
+ import { type TAnchoredPopoverOptions } from './use-anchored-popover';
2
+ /**
3
+ * A point in viewport coordinates (CSS pixels). Axis-named rather than
4
+ * edge-named, because viewport coordinates are always physical.
5
+ */
6
+ export type TAnchorPoint = {
7
+ x: number;
8
+ y: number;
9
+ };
10
+ /**
11
+ * Positions and sizes a popover at a viewport coordinate. Takes every option
12
+ * `useAnchoredPopover` does except `anchorRef`, with the same meaning. One caveat
13
+ * is specific to a point: it has no size, so `'match-anchor'` and `'min-anchor'`
14
+ * resolve to zero against it.
15
+ *
16
+ * A consumer that anchors to an element sometimes and to a point at other times
17
+ * calls BOTH hooks with complementary `isEnabled` values, since a hook cannot be
18
+ * called conditionally. Nothing enforces that; see `isEnabled` on
19
+ * `useAnchoredPopover` for why two enabled hooks on one popover is a bug.
20
+ *
21
+ * @example Anchor at the cursor, falling back to the trigger element
22
+ * ```tsx
23
+ * const shared = { popoverRef, placement, isOpen };
24
+ *
25
+ * useAnchoredPopover({ ...shared, anchorRef: triggerRef, isEnabled: !mousePos });
26
+ * useAnchoredPopoverAtPoint({
27
+ * ...shared,
28
+ * isEnabled: Boolean(mousePos),
29
+ * // Read through a ref: `getPoint` is latched, see below.
30
+ * getPoint: () => mousePosRef.current,
31
+ * });
32
+ * ```
33
+ */
34
+ export declare function useAnchoredPopoverAtPoint({ getPoint, isEnabled, ...rest }: {
35
+ /**
36
+ * Returns the viewport coordinate to anchor at, or `null` for "no point yet".
37
+ *
38
+ * Called once per activation and then latched. It is read through a ref, so an
39
+ * inline arrow is fine - but for the same reason it must read any value that can
40
+ * change from a ref rather than closing over it.
41
+ */
42
+ getPoint: () => TAnchorPoint | null;
43
+ /**
44
+ * `false` means "do not position": nothing is created and no style is written.
45
+ * Also the latch key - `false` -> `true` is what re-reads `getPoint`.
46
+ */
47
+ isEnabled?: boolean;
48
+ } & Omit<TAnchoredPopoverOptions, 'anchorRef' | 'isEnabled'>): void;