@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,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Positioning AND anchor-relative sizing for top-layer popovers. Anchors an
|
|
3
|
+
* ELEMENT; to anchor a viewport coordinate use `useAnchoredPopoverAtPoint`.
|
|
4
|
+
*
|
|
5
|
+
* Two runtime paths, each a function that applies its styles and returns the
|
|
6
|
+
* cleanup: CSS Anchor Positioning in `./anchor-positioning`, and the JavaScript
|
|
7
|
+
* fallback in `./javascript-fallback`. The path is resolved ONCE, at the top of
|
|
8
|
+
* the hook, and threaded through positioning, sizing and margins.
|
|
9
|
+
*
|
|
10
|
+
* See `notes/architecture/positioning.md` and
|
|
11
|
+
* `notes/decisions/fit-available-space.md`.
|
|
12
|
+
*/
|
|
13
|
+
import { type RefObject } from 'react';
|
|
14
|
+
import { type TPopoverAxisSize } from './anchored-popover-size';
|
|
15
|
+
import { type TPlacementOptions } from './resolve-placement';
|
|
16
|
+
/**
|
|
17
|
+
* Exported so `useAnchoredPopoverAtPoint` can forward the options it does not own.
|
|
18
|
+
*/
|
|
19
|
+
export type TAnchoredPopoverOptions = {
|
|
20
|
+
/**
|
|
21
|
+
* The element to position against. The ref OBJECT must be referentially stable,
|
|
22
|
+
* as the effect depends on it. `.current` may be `null` on the first render.
|
|
23
|
+
*/
|
|
24
|
+
anchorRef: RefObject<HTMLElement | null>;
|
|
25
|
+
/**
|
|
26
|
+
* `false` means "do not position": the hook applies nothing and touches no DOM,
|
|
27
|
+
* so another strategy can own the popover. Defaults to `true`.
|
|
28
|
+
*
|
|
29
|
+
* A disabled hook holds no `setStyle` snapshot, which is what lets a consumer
|
|
30
|
+
* call this AND `useAnchoredPopoverAtPoint` on one popover with COMPLEMENTARY
|
|
31
|
+
* values. Two enabled hooks nest their snapshots, and React unwinds them in
|
|
32
|
+
* commit order rather than write order, so a stale inline value can survive.
|
|
33
|
+
*/
|
|
34
|
+
isEnabled?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Element being positioned. May be unmounted between opens; see `isOpen`.
|
|
37
|
+
*/
|
|
38
|
+
popoverRef: RefObject<HTMLElement | null>;
|
|
39
|
+
/**
|
|
40
|
+
* Where the popover sits relative to the anchor, plus `offset.gap`,
|
|
41
|
+
* `offset.crossAxisShift` and `minSize`. Required, because the sizing rules read
|
|
42
|
+
* the placement axis; individual fields are optional. See `TPlacementOptions`.
|
|
43
|
+
*/
|
|
44
|
+
placement: TPlacementOptions;
|
|
45
|
+
/**
|
|
46
|
+
* Re-runs the effect across open cycles, so styles and listeners follow the live
|
|
47
|
+
* host element through a remount.
|
|
48
|
+
*/
|
|
49
|
+
isOpen: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* How wide the popover is, defaulting to `'content'`: the natural width, so a
|
|
52
|
+
* popover too wide for the space beside its anchor slides to a roomier side
|
|
53
|
+
* rather than wrapping into it. Both axes take the same four values under the
|
|
54
|
+
* same rules - see `TPopoverAxisSize` in `anchored-popover-size.tsx`.
|
|
55
|
+
*/
|
|
56
|
+
inlineSize?: TPopoverAxisSize;
|
|
57
|
+
/**
|
|
58
|
+
* The block-axis twin of `inlineSize`, so `'max-available'` caps the HEIGHT.
|
|
59
|
+
*/
|
|
60
|
+
blockSize?: TPopoverAxisSize;
|
|
61
|
+
/**
|
|
62
|
+
* Leaves `inline-size` to the popover element's own styles, skipping
|
|
63
|
+
* `TPopoverAxisSize` rule 4. For an element the consumer owns and may have given
|
|
64
|
+
* a `width` of its own - `@atlaskit/popper`'s imperative `createPopper` adapter
|
|
65
|
+
* is the case. Everything positioning an unstyled `Popover` host leaves this
|
|
66
|
+
* `false`.
|
|
67
|
+
*/
|
|
68
|
+
shouldPreserveInlineSize?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Forces the JavaScript positioning fallback, so it can be exercised in any
|
|
71
|
+
* environment.
|
|
72
|
+
*
|
|
73
|
+
* @internal Testing only. Also switches sizing to measured pixels, because
|
|
74
|
+
* `anchor-size()` has no anchor to resolve against without `position-anchor`.
|
|
75
|
+
*/
|
|
76
|
+
forceFallbackPositioning?: boolean;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Positions and sizes a popover relative to an anchor. Owns every inline style on
|
|
80
|
+
* the popover host: where it goes, how big it is relative to its anchor, and the
|
|
81
|
+
* margins that keep it clear of the anchor and the viewport edge. It knows nothing
|
|
82
|
+
* about visibility or animation - compose it with `Popover`.
|
|
83
|
+
*
|
|
84
|
+
* On modern browsers the browser handles flipping via CSS Anchor Positioning;
|
|
85
|
+
* otherwise it falls back to measured `position: fixed` coordinates, re-run on
|
|
86
|
+
* scroll and resize.
|
|
87
|
+
*
|
|
88
|
+
* `inlineSize` and `blockSize` both default to `'content'`. Two rules are
|
|
89
|
+
* surprising: fitting one axis also fits an axis left on `'content'`, and fitting
|
|
90
|
+
* either axis floors the placement axis. See `TPopoverAxisSize`.
|
|
91
|
+
*
|
|
92
|
+
* **Several React roots on one page.** The `anchor-name` this hook writes to an
|
|
93
|
+
* anchor that has none is minted from `useId()`, like every other id in the design
|
|
94
|
+
* system. `useId()` is unique within a root; across roots it is unique for
|
|
95
|
+
* client-rendered roots but NOT for roots that hydrate the same markup, where it
|
|
96
|
+
* is derived from tree position. Two anchors carrying one name resolve to the last
|
|
97
|
+
* in tree order, so a popover can attach to another root's trigger. A host that
|
|
98
|
+
* hydrates more than one root must give each a distinct `identifierPrefix` (on
|
|
99
|
+
* `hydrateRoot` and the matching server render). That is already required for the
|
|
100
|
+
* popover's own `id`, which `aria-controls` and `popovertarget` point at, and for
|
|
101
|
+
* every `aria-*` id the design system mints. A bundle with two copies of
|
|
102
|
+
* `react-dom` has the same exposure for client-rendered roots.
|
|
103
|
+
*
|
|
104
|
+
* @example Position a popover below its trigger
|
|
105
|
+
* ```tsx
|
|
106
|
+
* const triggerRef = useRef<HTMLButtonElement>(null);
|
|
107
|
+
* const popoverRef = useRef<HTMLDivElement>(null);
|
|
108
|
+
*
|
|
109
|
+
* useAnchoredPopover({
|
|
110
|
+
* anchorRef: triggerRef,
|
|
111
|
+
* popoverRef,
|
|
112
|
+
* placement: { axis: 'block', edge: 'end' },
|
|
113
|
+
* isOpen,
|
|
114
|
+
* });
|
|
115
|
+
* ```
|
|
116
|
+
*
|
|
117
|
+
* @example Match the trigger's width and keep the popover on screen
|
|
118
|
+
* ```tsx
|
|
119
|
+
* // What `<Popup shouldFitContainer shouldFitViewport>` maps to.
|
|
120
|
+
* useAnchoredPopover({
|
|
121
|
+
* anchorRef: triggerRef,
|
|
122
|
+
* popoverRef,
|
|
123
|
+
* placement: { axis: 'block', edge: 'end' },
|
|
124
|
+
* isOpen,
|
|
125
|
+
* inlineSize: 'match-anchor',
|
|
126
|
+
* blockSize: 'max-available',
|
|
127
|
+
* });
|
|
128
|
+
* ```
|
|
129
|
+
*
|
|
130
|
+
* @example Switch to a cursor position mid-mount
|
|
131
|
+
* ```tsx
|
|
132
|
+
* // Complementary `isEnabled`, so exactly one hook owns the popover.
|
|
133
|
+
* const shared = { popoverRef, placement, isOpen };
|
|
134
|
+
*
|
|
135
|
+
* useAnchoredPopover({ ...shared, anchorRef: triggerRef, isEnabled: !mousePos });
|
|
136
|
+
* useAnchoredPopoverAtPoint({
|
|
137
|
+
* ...shared,
|
|
138
|
+
* isEnabled: Boolean(mousePos),
|
|
139
|
+
* getPoint: () => mousePosRef.current,
|
|
140
|
+
* });
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export declare function useAnchoredPopover({ anchorRef, popoverRef, placement, isOpen, isEnabled, inlineSize, blockSize, shouldPreserveInlineSize, forceFallbackPositioning, }: TAnchoredPopoverOptions): void;
|
|
@@ -9,4 +9,5 @@ export { createPopoverCloseEvent } from './create-close-event';
|
|
|
9
9
|
/**
|
|
10
10
|
* @deprecated Import from the generated per-export subpath instead.
|
|
11
11
|
*/
|
|
12
|
-
export type { TPopoverCloseReason, TPopoverProps
|
|
12
|
+
export type { TPopoverCloseReason, TPopoverProps } from './types';
|
|
13
|
+
export type { TPlacementOptions } from '../internal/resolve-placement';
|
|
@@ -6,7 +6,7 @@ import React from 'react';
|
|
|
6
6
|
import { type TPopoverForwardedProps } from './types';
|
|
7
7
|
/**
|
|
8
8
|
* Unopinionated top-layer primitive. Owns visibility and animation only;
|
|
9
|
-
* compose with `
|
|
9
|
+
* compose with `useAnchoredPopover` for positioning and sizing.
|
|
10
10
|
*
|
|
11
11
|
* ### 🔌 Visibility
|
|
12
12
|
*
|
|
@@ -30,6 +30,18 @@ export type TPopoverForwardedProps = TPopoverBaseProps & {
|
|
|
30
30
|
};
|
|
31
31
|
export type { TPlacementOptions };
|
|
32
32
|
type TPopoverBaseProps = {
|
|
33
|
+
/**
|
|
34
|
+
* Pass ONE element. The host is a flex row while open, so that a size cap can
|
|
35
|
+
* reach it (see `useAnchoredPopover`); two element children become two flex
|
|
36
|
+
* items SIDE BY SIDE at half width each. A fragment does not help - its children
|
|
37
|
+
* are the flex items - which is why this is not expressible as a type. It
|
|
38
|
+
* matters most where a consumer's render prop is handed through unwrapped, as
|
|
39
|
+
* `@atlaskit/popper` and `@atlaskit/spotlight` do.
|
|
40
|
+
*
|
|
41
|
+
* The host zeroes that element's `min-inline-size` and `min-block-size` so the
|
|
42
|
+
* cap can shrink a non-scrolling child, at zero specificity, so a minimum the
|
|
43
|
+
* element sets itself wins. See the host styles in `popover.tsx`.
|
|
44
|
+
*/
|
|
33
45
|
children: ReactNode;
|
|
34
46
|
/**
|
|
35
47
|
* Animation styles applied while the popover is entering.
|
|
@@ -100,7 +112,7 @@ type TPopoverBaseProps = {
|
|
|
100
112
|
* properties (like `--ds-popover-tx`, `--ds-popover-ty`) that control the
|
|
101
113
|
* slide direction. Has no effect without `shouldAnimate`.
|
|
102
114
|
*
|
|
103
|
-
* This does NOT control positioning. Use `
|
|
115
|
+
* This does NOT control positioning. Use `useAnchoredPopover` for that.
|
|
104
116
|
*/
|
|
105
117
|
placement?: TPlacementOptions;
|
|
106
118
|
/**
|
|
@@ -125,8 +137,8 @@ type TPopoverBaseProps = {
|
|
|
125
137
|
* Unopinionated top-layer primitive.
|
|
126
138
|
*
|
|
127
139
|
* `Popover` manages only top-layer visibility and animation. It has no
|
|
128
|
-
* knowledge of positioning. For anchor positioning, compose with the
|
|
129
|
-
* `
|
|
140
|
+
* knowledge of positioning. For anchor positioning and sizing, compose with the
|
|
141
|
+
* `useAnchoredPopover` hook separately.
|
|
130
142
|
*
|
|
131
143
|
* Use `Popover` directly for custom trigger lifecycles (hover, timers,
|
|
132
144
|
* external state) or trigger-less UI (flags, toasts). For the common
|
|
@@ -164,17 +176,3 @@ export type TPopoverProps = TPopoverBaseProps & TAriaRoleRequired & ({
|
|
|
164
176
|
mode: 'manual';
|
|
165
177
|
onClose?: never;
|
|
166
178
|
});
|
|
167
|
-
/**
|
|
168
|
-
* The width mode of the popover relative to its anchor element.
|
|
169
|
-
*
|
|
170
|
-
* - `'none'` (default): popover sizes to its content, ignoring the anchor width.
|
|
171
|
-
* - `'match-anchor'`: popover matches the anchor element's width exactly
|
|
172
|
-
* via CSS `inline-size: anchor-size(self-inline)`. Falls back to a
|
|
173
|
-
* one-off measurement of `anchorRef.current.offsetWidth` when CSS
|
|
174
|
-
* Anchor Positioning is not supported.
|
|
175
|
-
* - `'min-anchor'`: popover is at least as wide as the anchor, but
|
|
176
|
-
* can grow wider if content requires it. Uses
|
|
177
|
-
* `min-inline-size: anchor-size(self-inline)`. Falls back to a one-off
|
|
178
|
-
* measurement of the anchor's width.
|
|
179
|
-
*/
|
|
180
|
-
export type TWidthFromAnchorMode = 'none' | 'match-anchor' | 'min-anchor';
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
* - Popover open state tracked via `data-popover-open` (no `:popover-open` in jsdom).
|
|
13
13
|
* - Auto popovers: maintain an open stack, perform light-dismiss on document mousedown
|
|
14
14
|
* outside the open chain, and dismiss the topmost on Escape.
|
|
15
|
-
* - On open, removes any inline `opacity: 0` from the popover element so
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* - On open, removes any inline `opacity: 0` from the popover element, so the
|
|
16
|
+
* JavaScript positioning fallback's `ResizeObserver` reveal is not needed in
|
|
17
|
+
* jsdom, where its non-zero-layout guard can never pass.
|
|
18
18
|
*
|
|
19
19
|
* Companion `toBeVisible` patch lives at `./to-be-visible`.
|
|
20
20
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/top-layer",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.1",
|
|
4
4
|
"description": "Low-level layering primitives based on popover and dialog",
|
|
5
5
|
"homepage": "https://atlassian.design/components/top-layer",
|
|
6
6
|
"repository": "https://bitbucket.org/atlassian/atlassian-frontend-mirror",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"@atlaskit/ds-lib": "^8.0.0",
|
|
41
41
|
"@atlaskit/layering": "^5.2.0",
|
|
42
42
|
"@atlaskit/react-compiler-gating": "^0.2.0",
|
|
43
|
-
"@atlaskit/tokens": "^
|
|
43
|
+
"@atlaskit/tokens": "^17.0.0",
|
|
44
44
|
"@babel/runtime": "^7.0.0",
|
|
45
45
|
"@compiled/react": "^1.0.2",
|
|
46
46
|
"bind-event-listener": "^3.0.0",
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@atlaskit/top-layer/use-anchored-popover",
|
|
3
|
+
"main": "../dist/cjs/internal/use-anchored-popover.js",
|
|
4
|
+
"module": "../dist/esm/internal/use-anchored-popover.js",
|
|
5
|
+
"module:es2019": "../dist/es2019/internal/use-anchored-popover.js",
|
|
6
|
+
"sideEffects": [
|
|
7
|
+
"*.compiled.css"
|
|
8
|
+
],
|
|
9
|
+
"types": "../dist/types/internal/use-anchored-popover.d.ts"
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@atlaskit/top-layer/use-anchored-popover-at-point",
|
|
3
|
+
"main": "../dist/cjs/internal/use-anchored-popover-at-point.js",
|
|
4
|
+
"module": "../dist/esm/internal/use-anchored-popover-at-point.js",
|
|
5
|
+
"module:es2019": "../dist/es2019/internal/use-anchored-popover-at-point.js",
|
|
6
|
+
"sideEffects": [
|
|
7
|
+
"*.compiled.css"
|
|
8
|
+
],
|
|
9
|
+
"types": "../dist/types/internal/use-anchored-popover-at-point.d.ts"
|
|
10
|
+
}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.placementToTryFallbacks = placementToTryFallbacks;
|
|
7
|
-
var _resolvePlacement = require("./resolve-placement");
|
|
8
|
-
/**
|
|
9
|
-
* A `position-area` primary edge — the placement axis joined to its edge, e.g.
|
|
10
|
-
* `block-end` or `inline-start`. These are the four edges a popover can sit on.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
function slideOnEdge(_ref) {
|
|
14
|
-
var align = _ref.align,
|
|
15
|
-
edgeKey = _ref.edgeKey,
|
|
16
|
-
crossAxis = _ref.crossAxis;
|
|
17
|
-
if (align === 'center') {
|
|
18
|
-
return ["".concat(edgeKey, " span-").concat(crossAxis, "-end"), "".concat(edgeKey, " span-").concat(crossAxis, "-start")];
|
|
19
|
-
}
|
|
20
|
-
// For `align: 'start'` the popover expands toward `end`, so the "near"
|
|
21
|
-
// span is `end` and the "far" span is `start`; `align: 'end'` mirrors
|
|
22
|
-
// this. The bare `edgeKey` (re-centered) sits between them.
|
|
23
|
-
var near = align === 'start' ? 'end' : 'start';
|
|
24
|
-
var far = align === 'start' ? 'start' : 'end';
|
|
25
|
-
return ["".concat(edgeKey, " span-").concat(crossAxis, "-").concat(near), edgeKey, "".concat(edgeKey, " span-").concat(crossAxis, "-").concat(far)];
|
|
26
|
-
}
|
|
27
|
-
function flipDiagonally(_ref2) {
|
|
28
|
-
var axis = _ref2.axis;
|
|
29
|
-
var flipOnMainAxis = axis === 'block' ? 'flip-block' : 'flip-inline';
|
|
30
|
-
var flipToCrossAxis = axis === 'block' ? 'flip-inline' : 'flip-block';
|
|
31
|
-
return "".concat(flipOnMainAxis, " ").concat(flipToCrossAxis);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Returns the `position-try-fallbacks` value for a placement.
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
|
-
* // Centered on the block-end edge (the default placement):
|
|
39
|
-
* placementToTryFallbacks({ placement: { axis: 'block', edge: 'end', align: 'center' } });
|
|
40
|
-
* // => 'block-end span-inline-end, block-end span-inline-start, flip-block,
|
|
41
|
-
* // block-start span-inline-end, block-start span-inline-start'
|
|
42
|
-
*/
|
|
43
|
-
function placementToTryFallbacks(_ref3) {
|
|
44
|
-
var placement = _ref3.placement;
|
|
45
|
-
/**
|
|
46
|
-
* **Build the fallback list**
|
|
47
|
-
*
|
|
48
|
-
* Each entry is a complete alternative position.
|
|
49
|
-
* The browser tries them _in order_ and uses the _first_ one that fits.
|
|
50
|
-
*/
|
|
51
|
-
|
|
52
|
-
var resolved = (0, _resolvePlacement.resolvePlacement)({
|
|
53
|
-
placement: placement
|
|
54
|
-
});
|
|
55
|
-
var axis = resolved.axis,
|
|
56
|
-
edge = resolved.edge,
|
|
57
|
-
align = resolved.align;
|
|
58
|
-
var crossAxis = axis === 'block' ? 'inline' : 'block';
|
|
59
|
-
var flippedEdge = edge === 'start' ? 'end' : 'start';
|
|
60
|
-
var sameEdgeArea = "".concat(axis, "-").concat(edge);
|
|
61
|
-
var oppositeEdgeArea = "".concat(axis, "-").concat(flippedEdge);
|
|
62
|
-
var flipOnMainAxis = axis === 'block' ? 'flip-block' : 'flip-inline';
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* **Fallback algorithm**
|
|
66
|
-
*
|
|
67
|
-
* We have two things we can do to keep something visible:
|
|
68
|
-
*
|
|
69
|
-
* 1. _Slide_ along edges
|
|
70
|
-
* 2. _Flip_ to another edge
|
|
71
|
-
*
|
|
72
|
-
* _Centered popover_
|
|
73
|
-
*
|
|
74
|
-
* Desired: Sits in middle of main edge
|
|
75
|
-
*
|
|
76
|
-
* Fallbacks:
|
|
77
|
-
* 1: _Slide_ along main edge on the cross axis
|
|
78
|
-
* 2: _Flip_ on main axis to opposite edge (centered)
|
|
79
|
-
* 3: _Slide_ along opposite edge on the cross axis
|
|
80
|
-
*
|
|
81
|
-
* _Start or end aligned popover_
|
|
82
|
-
*
|
|
83
|
-
* Desired: Sits at start / end of main edge
|
|
84
|
-
*
|
|
85
|
-
* Fallbacks:
|
|
86
|
-
* 1: _Slide_ along main edge on the cross axis
|
|
87
|
-
* 2: _Flip_ on main axis to opposite edge
|
|
88
|
-
* 3: _Flip_ on the diagonal (flip main axis + flip cross axis) into the
|
|
89
|
-
* opposite corner. For start / end alignment, this is the clean escape
|
|
90
|
-
* when the popover would otherwise land offscreen on both axes (ie in a
|
|
91
|
-
* corner). Rule 4 gets the job done, but a slide drops the gap
|
|
92
|
-
* as named position-area values don't carry margins).
|
|
93
|
-
* The diagonal flip keeps the gap and the alignment.
|
|
94
|
-
* 4: _Slide_ along opposite edge on the cross axis
|
|
95
|
-
*/
|
|
96
|
-
|
|
97
|
-
return [slideOnEdge({
|
|
98
|
-
align: align,
|
|
99
|
-
edgeKey: sameEdgeArea,
|
|
100
|
-
crossAxis: crossAxis
|
|
101
|
-
}), flipOnMainAxis, align !== 'center' ? flipDiagonally({
|
|
102
|
-
axis: axis
|
|
103
|
-
}) : null, slideOnEdge({
|
|
104
|
-
align: align,
|
|
105
|
-
edgeKey: oppositeEdgeArea,
|
|
106
|
-
crossAxis: crossAxis
|
|
107
|
-
})].filter(Boolean).flat().join(', ');
|
|
108
|
-
}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.resolveCssLengthToPixels = resolveCssLengthToPixels;
|
|
7
|
-
var elementCaches = new WeakMap();
|
|
8
|
-
function canCacheResolvedLength(value) {
|
|
9
|
-
// Custom-property values can be retargeted by inline overrides on the
|
|
10
|
-
// consumer's wrapper, so we cannot prove a cached pixel value is still valid.
|
|
11
|
-
return !value.includes('var(');
|
|
12
|
-
}
|
|
13
|
-
function getCachedResolvedLength(_ref) {
|
|
14
|
-
var _cacheForElement$get;
|
|
15
|
-
var element = _ref.element,
|
|
16
|
-
value = _ref.value;
|
|
17
|
-
if (!canCacheResolvedLength(value)) {
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
var cacheForElement = elementCaches.get(element);
|
|
21
|
-
return (_cacheForElement$get = cacheForElement === null || cacheForElement === void 0 ? void 0 : cacheForElement.get(value)) !== null && _cacheForElement$get !== void 0 ? _cacheForElement$get : null;
|
|
22
|
-
}
|
|
23
|
-
function cacheResolvedLength(_ref2) {
|
|
24
|
-
var _elementCaches$get;
|
|
25
|
-
var element = _ref2.element,
|
|
26
|
-
value = _ref2.value,
|
|
27
|
-
result = _ref2.result;
|
|
28
|
-
if (!canCacheResolvedLength(value)) {
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
var cacheForElement = (_elementCaches$get = elementCaches.get(element)) !== null && _elementCaches$get !== void 0 ? _elementCaches$get : new Map();
|
|
32
|
-
cacheForElement.set(value, result);
|
|
33
|
-
elementCaches.set(element, cacheForElement);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Resolves a CSS length value to a pixel number using a hidden DOM probe.
|
|
38
|
-
*
|
|
39
|
-
* Used by the JS positioning fallback: consumer offsets can be numbers,
|
|
40
|
-
* `${n}px` strings, tokenised `var(--ds-space-100, 8px)` strings, `calc()`
|
|
41
|
-
* expressions, viewport units, etc. Parsing arbitrary CSS in JS is fragile
|
|
42
|
-
* (and impossible for `calc(var(...))` without a layout context), so we let
|
|
43
|
-
* the browser do the math.
|
|
44
|
-
*
|
|
45
|
-
* The probe is appended to `container` so it inherits the same containing
|
|
46
|
-
* block, font size, and custom-property scope as the popover.
|
|
47
|
-
*
|
|
48
|
-
* Cost: one synchronous reflow on first cacheable resolution per
|
|
49
|
-
* (container, value) pair, then a `WeakMap` lookup. Resolutions involving
|
|
50
|
-
* `var(` are intentionally not cached.
|
|
51
|
-
*/
|
|
52
|
-
function resolveCssLengthToPixels(_ref3) {
|
|
53
|
-
var value = _ref3.value,
|
|
54
|
-
container = _ref3.container;
|
|
55
|
-
if (typeof value === 'number') {
|
|
56
|
-
return value;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Fast path: unitless zero is universally zero pixels - accept `'0'` and
|
|
60
|
-
// `'-0'` without a probe. (Consumers occasionally pass `'0'` instead of
|
|
61
|
-
// the number `0`.)
|
|
62
|
-
if (value === '0' || value === '-0') {
|
|
63
|
-
return 0;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Fast path: bare `${n}px` strings come from the consumer normalisation
|
|
67
|
-
// in `resolvePlacement`. Avoid a reflow when we can.
|
|
68
|
-
var pxMatch = value.match(/^(-?\d+(?:\.\d+)?)px$/);
|
|
69
|
-
if (pxMatch) {
|
|
70
|
-
return Number(pxMatch[1]);
|
|
71
|
-
}
|
|
72
|
-
var cached = getCachedResolvedLength({
|
|
73
|
-
element: container,
|
|
74
|
-
value: value
|
|
75
|
-
});
|
|
76
|
-
if (cached !== null) {
|
|
77
|
-
return cached;
|
|
78
|
-
}
|
|
79
|
-
var probe = container.ownerDocument.createElement('div');
|
|
80
|
-
probe.style.position = 'absolute';
|
|
81
|
-
probe.style.visibility = 'hidden';
|
|
82
|
-
probe.style.pointerEvents = 'none';
|
|
83
|
-
probe.style.height = '0';
|
|
84
|
-
probe.style.padding = '0';
|
|
85
|
-
probe.style.border = '0';
|
|
86
|
-
// `width` clamps negatives to 0; use `margin-left` instead so signs are
|
|
87
|
-
// preserved. `getComputedStyle` returns the resolved pixel value for
|
|
88
|
-
// margin-* properties even when the source was a token / calc / var.
|
|
89
|
-
probe.style.marginLeft = value;
|
|
90
|
-
// Hide the probe from assistive tech and event targeting; defensive
|
|
91
|
-
// against `getElementsByTagName('div')` walks in consumer code.
|
|
92
|
-
probe.setAttribute('aria-hidden', 'true');
|
|
93
|
-
probe.setAttribute('inert', '');
|
|
94
|
-
container.appendChild(probe);
|
|
95
|
-
var resolved = parseFloat(getComputedStyle(probe).marginLeft);
|
|
96
|
-
container.removeChild(probe);
|
|
97
|
-
var result = Number.isFinite(resolved) ? resolved : 0;
|
|
98
|
-
cacheResolvedLength({
|
|
99
|
-
element: container,
|
|
100
|
-
value: value,
|
|
101
|
-
result: result
|
|
102
|
-
});
|
|
103
|
-
return result;
|
|
104
|
-
}
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.useAnchorPositionAtPoint = useAnchorPositionAtPoint;
|
|
7
|
-
var _react = require("react");
|
|
8
|
-
var _useAnchorPosition = require("./use-anchor-position");
|
|
9
|
-
/**
|
|
10
|
-
* A point in viewport coordinates (CSS pixels) at which the synthetic
|
|
11
|
-
* anchor should be placed.
|
|
12
|
-
*
|
|
13
|
-
* Axis-named (`x`/`y`) rather than edge-named (`left`/`top`) so the
|
|
14
|
-
* shape is unambiguous in RTL writing modes - there is no ambiguity
|
|
15
|
-
* about whether a coordinate is logical (`inline-start`/`block-start`)
|
|
16
|
-
* or physical (`left`/`top`). Viewport coordinates are always
|
|
17
|
-
* physical, so we name them with the physical axis labels.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Styles the synthetic anchor as an invisible, zero-size point.
|
|
22
|
-
* Dynamic `inset-*` overrides are written by the positioning effect.
|
|
23
|
-
*
|
|
24
|
-
* Uses `Object.assign` rather than Compiled CSS (element is outside the React tree)
|
|
25
|
-
* or the internal `setStyle` helper (we own this element entirely - it is created
|
|
26
|
-
* and removed by this hook - so the snapshot/restore semantics `setStyle`
|
|
27
|
-
* provides for caller-owned elements would be dead work here).
|
|
28
|
-
*/
|
|
29
|
-
function applyStaticAnchorStyles(element) {
|
|
30
|
-
Object.assign(element.style, {
|
|
31
|
-
position: 'fixed',
|
|
32
|
-
// `top`/`left` rather than `inset-block-start`/`inset-inline-start`
|
|
33
|
-
// for consistency with the dynamic positioning effect below, which
|
|
34
|
-
// uses physical properties (see comment there for the rationale).
|
|
35
|
-
top: '0',
|
|
36
|
-
left: '0',
|
|
37
|
-
// Zero size - placement decides which side the popover sits on.
|
|
38
|
-
width: '0',
|
|
39
|
-
height: '0',
|
|
40
|
-
// Prevent the overlay from intercepting pointer events.
|
|
41
|
-
pointerEvents: 'none'
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// `useAnchorPosition` requires a real DOM element. When the caller only
|
|
46
|
-
// has a viewport coordinate (e.g. cursor at hover time), this hook
|
|
47
|
-
// manages a hidden `<div>` at that point and delegates the rest.
|
|
48
|
-
//
|
|
49
|
-
// The `<div>` is appended directly to `document.body` to escape any
|
|
50
|
-
// transformed ancestor, which would otherwise break `position: fixed`
|
|
51
|
-
// viewport-relative resolution.
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Positions a popover at a consumer-supplied viewport coordinate using a
|
|
55
|
-
* hidden synthetic anchor.
|
|
56
|
-
*
|
|
57
|
-
* @example
|
|
58
|
-
* ```tsx
|
|
59
|
-
* function PointPositionedPopover({ popoverRef, placement, mousePos }) {
|
|
60
|
-
* useAnchorPositionAtPoint({
|
|
61
|
-
* popoverRef,
|
|
62
|
-
* placement,
|
|
63
|
-
* isEnabled: Boolean(mousePos),
|
|
64
|
-
* getPoint: () => {
|
|
65
|
-
* if (!mousePos) return null;
|
|
66
|
-
* return { x: mousePos.clientX, y: mousePos.clientY };
|
|
67
|
-
* },
|
|
68
|
-
* });
|
|
69
|
-
* return <Popover ref={popoverRef}>...</Popover>;
|
|
70
|
-
* }
|
|
71
|
-
* ```
|
|
72
|
-
*/
|
|
73
|
-
function useAnchorPositionAtPoint(_ref) {
|
|
74
|
-
var popoverRef = _ref.popoverRef,
|
|
75
|
-
placement = _ref.placement,
|
|
76
|
-
getPoint = _ref.getPoint,
|
|
77
|
-
_ref$isEnabled = _ref.isEnabled,
|
|
78
|
-
isEnabled = _ref$isEnabled === void 0 ? true : _ref$isEnabled,
|
|
79
|
-
isOpen = _ref.isOpen;
|
|
80
|
-
var syntheticAnchorRef = (0, _react.useRef)(null);
|
|
81
|
-
|
|
82
|
-
// `getPoint` is kept in a ref so the layout effect can call the
|
|
83
|
-
// latest version without listing it as a dep - that would re-fire
|
|
84
|
-
// on every render and re-latch the point.
|
|
85
|
-
var getPointRef = (0, _react.useRef)(getPoint);
|
|
86
|
-
getPointRef.current = getPoint;
|
|
87
|
-
|
|
88
|
-
// Lazy element creation: the `<div>` is only created when both
|
|
89
|
-
// `isEnabled` is true AND `getPoint()` returns a non-null point.
|
|
90
|
-
// `useLayoutEffect` ensures the element exists before the inner
|
|
91
|
-
// `useAnchorPosition` (also a layout effect) reads the ref.
|
|
92
|
-
(0, _react.useLayoutEffect)(function () {
|
|
93
|
-
if (!isEnabled) {
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
var point = getPointRef.current();
|
|
97
|
-
if (!point) {
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
var anchor = document.createElement('div');
|
|
101
|
-
anchor.setAttribute('aria-hidden', 'true');
|
|
102
|
-
applyStaticAnchorStyles(anchor);
|
|
103
|
-
// `point.x`/`y` are physical viewport coordinates (from
|
|
104
|
-
// `MouseEvent.clientX/Y`), so map directly onto physical
|
|
105
|
-
// `left`/`top`. This is RTL-safe: `position: fixed` + physical
|
|
106
|
-
// insets is direction-independent. Using logical
|
|
107
|
-
// `inset-inline-start` would need `viewportWidth - point.x` in
|
|
108
|
-
// RTL to land at the same physical pixel.
|
|
109
|
-
anchor.style.top = "".concat(point.y, "px");
|
|
110
|
-
anchor.style.left = "".concat(point.x, "px");
|
|
111
|
-
document.body.appendChild(anchor);
|
|
112
|
-
syntheticAnchorRef.current = anchor;
|
|
113
|
-
return function cleanup() {
|
|
114
|
-
anchor.remove();
|
|
115
|
-
syntheticAnchorRef.current = null;
|
|
116
|
-
};
|
|
117
|
-
}, [isEnabled]);
|
|
118
|
-
|
|
119
|
-
// Mirror `isEnabled` so both hooks stay in sync - when disabled,
|
|
120
|
-
// neither this hook nor the inner one touches the popover, leaving
|
|
121
|
-
// any other positioning the consumer has set up free to take effect.
|
|
122
|
-
(0, _useAnchorPosition.useAnchorPosition)({
|
|
123
|
-
anchorRef: syntheticAnchorRef,
|
|
124
|
-
popoverRef: popoverRef,
|
|
125
|
-
placement: placement,
|
|
126
|
-
isEnabled: isEnabled,
|
|
127
|
-
isOpen: isOpen
|
|
128
|
-
});
|
|
129
|
-
}
|