@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.
- package/CHANGELOG.md +50 -0
- package/README.md +13 -4
- package/dist/cjs/dialog/dialog-content.js +2 -0
- package/dist/cjs/entry-points/use-popover-id.js +5 -10
- package/dist/cjs/internal/anchor-positioning/apply-anchor-positioning.js +86 -0
- package/dist/cjs/internal/anchor-positioning/cross-axis-shift-margins.js +34 -0
- package/dist/cjs/internal/anchor-positioning/edge-margin.js +21 -0
- package/dist/cjs/internal/anchor-positioning/fit-margins.js +77 -0
- package/dist/cjs/internal/anchor-positioning/get-existing-anchor-name.js +18 -0
- package/dist/cjs/internal/{placement-to-position-area.js → anchor-positioning/placement-to-position-area.js} +11 -11
- package/dist/cjs/internal/anchor-positioning/placement-to-try-fallbacks.js +95 -0
- package/dist/cjs/internal/anchored-popover-size.js +345 -0
- package/dist/cjs/internal/get-aria-for-trigger.js +7 -13
- package/dist/cjs/internal/is-native-element-open.js +19 -0
- package/dist/cjs/internal/{anchor-positioning-fallback.js → javascript-fallback/anchor-positioning-fallback.js} +12 -33
- package/dist/cjs/internal/javascript-fallback/apply-javascript-fallback-positioning.js +297 -0
- package/dist/cjs/internal/javascript-fallback/resolve-css-length-to-pixels.js +90 -0
- package/dist/cjs/internal/javascript-fallback/warn-fallback-failure.js +19 -0
- package/dist/cjs/internal/resolve-placement.js +14 -6
- package/dist/cjs/internal/supports-anchor-positioning.js +17 -0
- package/dist/cjs/internal/supports-anchor-size.js +17 -0
- package/dist/cjs/internal/supports-css-declaration.js +25 -0
- package/dist/cjs/internal/surface-reset.js +5 -0
- package/dist/cjs/internal/use-anchored-popover-at-point.js +124 -0
- package/dist/cjs/internal/use-anchored-popover.js +277 -0
- package/dist/cjs/internal/use-animated-visibility.js +11 -23
- package/dist/cjs/popover/popover.compiled.css +4 -0
- package/dist/cjs/popover/popover.js +63 -61
- package/dist/cjs/testing/polyfill.js +6 -7
- package/dist/es2019/dialog/dialog-content.js +2 -0
- package/dist/es2019/entry-points/use-popover-id.js +6 -10
- package/dist/es2019/internal/anchor-positioning/apply-anchor-positioning.js +83 -0
- package/dist/es2019/internal/anchor-positioning/cross-axis-shift-margins.js +29 -0
- package/dist/es2019/internal/anchor-positioning/edge-margin.js +18 -0
- package/dist/es2019/internal/anchor-positioning/fit-margins.js +69 -0
- package/dist/es2019/internal/anchor-positioning/get-existing-anchor-name.js +12 -0
- package/dist/es2019/internal/{placement-to-position-area.js → anchor-positioning/placement-to-position-area.js} +9 -9
- package/dist/es2019/internal/anchor-positioning/placement-to-try-fallbacks.js +94 -0
- package/dist/es2019/internal/anchored-popover-size.js +343 -0
- package/dist/es2019/internal/get-aria-for-trigger.js +7 -13
- package/dist/es2019/internal/is-native-element-open.js +14 -0
- package/dist/es2019/internal/{anchor-positioning-fallback.js → javascript-fallback/anchor-positioning-fallback.js} +12 -33
- package/dist/es2019/internal/javascript-fallback/apply-javascript-fallback-positioning.js +291 -0
- package/dist/es2019/internal/javascript-fallback/resolve-css-length-to-pixels.js +87 -0
- package/dist/es2019/internal/javascript-fallback/warn-fallback-failure.js +14 -0
- package/dist/es2019/internal/resolve-placement.js +14 -6
- package/dist/es2019/internal/supports-anchor-positioning.js +12 -0
- package/dist/es2019/internal/supports-anchor-size.js +12 -0
- package/dist/es2019/internal/supports-css-declaration.js +20 -0
- package/dist/es2019/internal/surface-reset.js +1 -0
- package/dist/es2019/internal/use-anchored-popover-at-point.js +116 -0
- package/dist/es2019/internal/use-anchored-popover.js +265 -0
- package/dist/es2019/internal/use-animated-visibility.js +1 -14
- package/dist/es2019/popover/popover.compiled.css +4 -0
- package/dist/es2019/popover/popover.js +69 -61
- package/dist/es2019/testing/polyfill.js +6 -7
- package/dist/esm/dialog/dialog-content.js +2 -0
- package/dist/esm/entry-points/use-popover-id.js +6 -10
- package/dist/esm/internal/anchor-positioning/apply-anchor-positioning.js +80 -0
- package/dist/esm/internal/anchor-positioning/cross-axis-shift-margins.js +28 -0
- package/dist/esm/internal/anchor-positioning/edge-margin.js +15 -0
- package/dist/esm/internal/anchor-positioning/fit-margins.js +71 -0
- package/dist/esm/internal/anchor-positioning/get-existing-anchor-name.js +12 -0
- package/dist/esm/internal/{placement-to-position-area.js → anchor-positioning/placement-to-position-area.js} +11 -12
- package/dist/esm/internal/anchor-positioning/placement-to-try-fallbacks.js +89 -0
- package/dist/esm/internal/anchored-popover-size.js +338 -0
- package/dist/esm/internal/get-aria-for-trigger.js +7 -13
- package/dist/esm/internal/is-native-element-open.js +13 -0
- package/dist/esm/internal/{anchor-positioning-fallback.js → javascript-fallback/anchor-positioning-fallback.js} +12 -33
- package/dist/esm/internal/javascript-fallback/apply-javascript-fallback-positioning.js +290 -0
- package/dist/esm/internal/javascript-fallback/resolve-css-length-to-pixels.js +85 -0
- package/dist/esm/internal/javascript-fallback/warn-fallback-failure.js +13 -0
- package/dist/esm/internal/resolve-placement.js +14 -6
- package/dist/esm/internal/supports-anchor-positioning.js +12 -0
- package/dist/esm/internal/supports-anchor-size.js +12 -0
- package/dist/esm/internal/supports-css-declaration.js +19 -0
- package/dist/esm/internal/surface-reset.js +1 -0
- package/dist/esm/internal/use-anchored-popover-at-point.js +118 -0
- package/dist/esm/internal/use-anchored-popover.js +271 -0
- package/dist/esm/internal/use-animated-visibility.js +10 -22
- package/dist/esm/popover/popover.compiled.css +4 -0
- package/dist/esm/popover/popover.js +63 -61
- package/dist/esm/testing/polyfill.js +6 -7
- package/dist/types/entry-points/placement-map.d.ts +2 -2
- package/dist/types/entry-points/use-popover-id.d.ts +3 -7
- package/dist/types/internal/anchor-positioning/apply-anchor-positioning.d.ts +26 -0
- package/dist/types/internal/anchor-positioning/cross-axis-shift-margins.d.ts +19 -0
- package/dist/types/internal/anchor-positioning/edge-margin.d.ts +10 -0
- package/dist/types/internal/anchor-positioning/fit-margins.d.ts +30 -0
- package/dist/types/internal/anchor-positioning/get-existing-anchor-name.d.ts +8 -0
- package/dist/types/internal/{placement-to-position-area.d.ts → anchor-positioning/placement-to-position-area.d.ts} +10 -6
- package/dist/types/internal/anchor-positioning/placement-to-try-fallbacks.d.ts +30 -0
- package/dist/types/internal/anchored-popover-size.d.ts +126 -0
- package/dist/types/internal/get-aria-for-trigger.d.ts +13 -25
- package/dist/types/internal/is-native-element-open.d.ts +3 -0
- package/dist/types/internal/javascript-fallback/anchor-positioning-fallback.d.ts +36 -0
- package/dist/types/internal/javascript-fallback/apply-javascript-fallback-positioning.d.ts +15 -0
- package/dist/types/internal/javascript-fallback/resolve-css-length-to-pixels.d.ts +20 -0
- package/dist/types/internal/javascript-fallback/warn-fallback-failure.d.ts +7 -0
- package/dist/types/internal/resolve-placement.d.ts +18 -2
- package/dist/types/internal/supports-anchor-positioning.d.ts +5 -0
- package/dist/types/internal/supports-anchor-size.d.ts +5 -0
- package/dist/types/internal/supports-css-declaration.d.ts +11 -0
- package/dist/types/internal/surface-reset.d.ts +25 -0
- package/dist/types/internal/use-anchored-popover-at-point.d.ts +48 -0
- package/dist/types/internal/use-anchored-popover.d.ts +143 -0
- package/dist/types/popover/index.d.ts +2 -1
- package/dist/types/popover/popover.d.ts +1 -1
- package/dist/types/popover/types.d.ts +15 -17
- package/dist/types/testing/polyfill.d.ts +3 -3
- package/package.json +2 -2
- package/use-anchored-popover/package.json +10 -0
- package/use-anchored-popover-at-point/package.json +10 -0
- package/dist/cjs/internal/placement-to-try-fallbacks.js +0 -108
- package/dist/cjs/internal/resolve-css-length-to-pixels.js +0 -104
- package/dist/cjs/internal/use-anchor-position-at-point.js +0 -129
- package/dist/cjs/internal/use-anchor-position.js +0 -501
- package/dist/cjs/internal/use-width-from-anchor.js +0 -124
- package/dist/es2019/internal/placement-to-try-fallbacks.js +0 -108
- package/dist/es2019/internal/resolve-css-length-to-pixels.js +0 -101
- package/dist/es2019/internal/use-anchor-position-at-point.js +0 -124
- package/dist/es2019/internal/use-anchor-position.js +0 -480
- package/dist/es2019/internal/use-width-from-anchor.js +0 -120
- package/dist/esm/internal/placement-to-try-fallbacks.js +0 -103
- package/dist/esm/internal/resolve-css-length-to-pixels.js +0 -98
- package/dist/esm/internal/use-anchor-position-at-point.js +0 -124
- package/dist/esm/internal/use-anchor-position.js +0 -494
- package/dist/esm/internal/use-width-from-anchor.js +0 -118
- package/dist/types/internal/anchor-positioning-fallback.d.ts +0 -51
- package/dist/types/internal/placement-to-try-fallbacks.d.ts +0 -13
- package/dist/types/internal/resolve-css-length-to-pixels.d.ts +0 -20
- package/dist/types/internal/use-anchor-position-at-point.d.ts +0 -67
- package/dist/types/internal/use-anchor-position.d.ts +0 -87
- package/dist/types/internal/use-width-from-anchor.d.ts +0 -19
- package/use-anchor-position/package.json +0 -10
- package/use-anchor-position-at-point/package.json +0 -10
- 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
|
|
1
|
+
import { type TPlacement } from '../resolve-placement';
|
|
2
2
|
/**
|
|
3
|
-
* Resolves placement
|
|
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
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
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:
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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:
|
|
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':
|
|
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,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;
|
|
@@ -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
|
|
21
|
-
*
|
|
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,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;
|