@atlaskit/top-layer 0.17.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +113 -0
- package/CHANGELOG.md +33 -0
- package/animations/package.json +1 -8
- package/dialog/package.json +1 -8
- package/dialog-scroll-lock/package.json +1 -8
- package/dist/cjs/dialog/dialog-content.js +12 -30
- package/dist/cjs/internal/set-style.js +5 -5
- package/dist/cjs/internal/slot.js +1 -1
- package/dist/cjs/internal/use-anchor-position-at-point.js +8 -8
- package/dist/cjs/internal/use-anchor-position.js +19 -40
- package/dist/cjs/internal/use-animated-visibility.js +31 -61
- package/dist/cjs/internal/use-initial-focus.js +9 -13
- package/dist/cjs/internal/use-width-from-anchor.js +6 -6
- package/dist/cjs/popover/popover.js +30 -58
- package/dist/cjs/popover-surface/popover-surface.js +4 -3
- package/dist/cjs/testing/polyfill.js +9 -9
- package/dist/cjs/use-simple-light-dismiss/index.js +3 -3
- package/dist/es2019/dialog/dialog-content.js +12 -30
- package/dist/es2019/internal/set-style.js +5 -5
- package/dist/es2019/internal/slot.js +1 -1
- package/dist/es2019/internal/use-anchor-position-at-point.js +8 -8
- package/dist/es2019/internal/use-anchor-position.js +19 -40
- package/dist/es2019/internal/use-animated-visibility.js +31 -61
- package/dist/es2019/internal/use-initial-focus.js +9 -13
- package/dist/es2019/internal/use-width-from-anchor.js +6 -6
- package/dist/es2019/popover/popover.js +30 -58
- package/dist/es2019/popover-surface/popover-surface.js +1 -1
- package/dist/es2019/testing/polyfill.js +7 -11
- package/dist/es2019/use-simple-light-dismiss/index.js +3 -3
- package/dist/esm/dialog/dialog-content.js +12 -30
- package/dist/esm/internal/set-style.js +5 -5
- package/dist/esm/internal/slot.js +1 -1
- package/dist/esm/internal/use-anchor-position-at-point.js +8 -8
- package/dist/esm/internal/use-anchor-position.js +19 -40
- package/dist/esm/internal/use-animated-visibility.js +31 -61
- package/dist/esm/internal/use-initial-focus.js +9 -13
- package/dist/esm/internal/use-width-from-anchor.js +6 -6
- package/dist/esm/popover/popover.js +30 -58
- package/dist/esm/popover-surface/popover-surface.js +1 -1
- package/dist/esm/testing/polyfill.js +9 -9
- package/dist/esm/use-simple-light-dismiss/index.js +3 -3
- package/dist/types/internal/set-style.d.ts +2 -2
- package/dist/types/internal/slot.d.ts +2 -2
- package/dist/types/internal/use-anchor-position-at-point.d.ts +2 -2
- package/dist/types/internal/use-anchor-position.d.ts +0 -4
- package/dist/types/internal/use-initial-focus.d.ts +1 -1
- package/dist/types/popover/types.d.ts +5 -7
- package/dist/types/popover-surface/popover-surface.d.ts +2 -2
- package/focus/package.json +1 -8
- package/get-aria-for-trigger/package.json +1 -8
- package/open-layer-observer/package.json +1 -8
- package/package.json +11 -19
- package/placement-map/package.json +1 -8
- package/popover/package.json +1 -8
- package/popover-surface/package.json +1 -8
- package/testing/polyfill/package.json +1 -8
- package/testing/to-be-visible/package.json +1 -8
- package/use-anchor-position/package.json +1 -8
- package/use-anchor-position-at-point/package.json +1 -8
- package/use-arrow-navigation/package.json +1 -8
- package/use-popover-id/package.json +1 -8
- package/use-simple-light-dismiss/package.json +1 -8
- package/use-width-from-anchor/package.json +1 -8
- package/dist/types-ts4.5/animations/presets.d.ts +0 -110
- package/dist/types-ts4.5/animations/types.d.ts +0 -38
- package/dist/types-ts4.5/dialog/create-close-event.d.ts +0 -23
- package/dist/types-ts4.5/dialog/dialog-content.d.ts +0 -24
- package/dist/types-ts4.5/dialog/index.d.ts +0 -3
- package/dist/types-ts4.5/dialog/types.d.ts +0 -126
- package/dist/types-ts4.5/dialog-scroll-lock/index.d.ts +0 -40
- package/dist/types-ts4.5/entry-points/animations.d.ts +0 -2
- package/dist/types-ts4.5/entry-points/dialog-scroll-lock.d.ts +0 -1
- package/dist/types-ts4.5/entry-points/dialog.d.ts +0 -2
- package/dist/types-ts4.5/entry-points/focus.d.ts +0 -4
- package/dist/types-ts4.5/entry-points/get-aria-for-trigger.d.ts +0 -1
- package/dist/types-ts4.5/entry-points/open-layer-observer.d.ts +0 -5
- package/dist/types-ts4.5/entry-points/placement-map.d.ts +0 -3
- package/dist/types-ts4.5/entry-points/popover-surface.d.ts +0 -2
- package/dist/types-ts4.5/entry-points/popover.d.ts +0 -2
- package/dist/types-ts4.5/entry-points/use-anchor-position-at-point.d.ts +0 -1
- package/dist/types-ts4.5/entry-points/use-anchor-position.d.ts +0 -1
- package/dist/types-ts4.5/entry-points/use-arrow-navigation.d.ts +0 -3
- package/dist/types-ts4.5/entry-points/use-popover-id.d.ts +0 -11
- package/dist/types-ts4.5/entry-points/use-simple-light-dismiss.d.ts +0 -2
- package/dist/types-ts4.5/entry-points/use-width-from-anchor.d.ts +0 -1
- package/dist/types-ts4.5/focus/get-first-focusable.d.ts +0 -8
- package/dist/types-ts4.5/focus/get-focusables.d.ts +0 -13
- package/dist/types-ts4.5/focus/get-last-focusable.d.ts +0 -8
- package/dist/types-ts4.5/focus/get-next-focusable.d.ts +0 -13
- package/dist/types-ts4.5/focus/is-in-same-layer.d.ts +0 -8
- package/dist/types-ts4.5/focus/is-nested-layer-focused.d.ts +0 -8
- package/dist/types-ts4.5/focus/selectors.d.ts +0 -4
- package/dist/types-ts4.5/focus/types.d.ts +0 -5
- package/dist/types-ts4.5/index.d.ts +0 -0
- package/dist/types-ts4.5/internal/anchor-positioning-fallback.d.ts +0 -26
- package/dist/types-ts4.5/internal/combine.d.ts +0 -14
- package/dist/types-ts4.5/internal/ensure-preset-styles.d.ts +0 -6
- package/dist/types-ts4.5/internal/get-aria-for-trigger.d.ts +0 -93
- package/dist/types-ts4.5/internal/placement-to-position-area.d.ts +0 -21
- package/dist/types-ts4.5/internal/placement-to-try-fallbacks.d.ts +0 -36
- package/dist/types-ts4.5/internal/reduced-motion.d.ts +0 -14
- package/dist/types-ts4.5/internal/resolve-css-length-to-pixels.d.ts +0 -20
- package/dist/types-ts4.5/internal/resolve-css-length.d.ts +0 -8
- package/dist/types-ts4.5/internal/resolve-placement.d.ts +0 -58
- package/dist/types-ts4.5/internal/role-to-aria-has-popup.d.ts +0 -13
- package/dist/types-ts4.5/internal/role-types.d.ts +0 -71
- package/dist/types-ts4.5/internal/set-style.d.ts +0 -11
- package/dist/types-ts4.5/internal/should-focus-into-popover.d.ts +0 -8
- package/dist/types-ts4.5/internal/slot.d.ts +0 -29
- package/dist/types-ts4.5/internal/use-anchor-position-at-point.d.ts +0 -67
- package/dist/types-ts4.5/internal/use-anchor-position.d.ts +0 -91
- package/dist/types-ts4.5/internal/use-animated-visibility.d.ts +0 -109
- package/dist/types-ts4.5/internal/use-animation-preset.d.ts +0 -11
- package/dist/types-ts4.5/internal/use-focus-wrap.d.ts +0 -25
- package/dist/types-ts4.5/internal/use-initial-focus.d.ts +0 -73
- package/dist/types-ts4.5/internal/use-preset-styles.d.ts +0 -15
- package/dist/types-ts4.5/internal/use-width-from-anchor.d.ts +0 -21
- package/dist/types-ts4.5/placement-map/index.d.ts +0 -51
- package/dist/types-ts4.5/placement-map/legacy-placements.d.ts +0 -27
- package/dist/types-ts4.5/placement-map/placement-mapping.d.ts +0 -13
- package/dist/types-ts4.5/popover/create-close-event.d.ts +0 -25
- package/dist/types-ts4.5/popover/index.d.ts +0 -3
- package/dist/types-ts4.5/popover/popover.d.ts +0 -17
- package/dist/types-ts4.5/popover/types.d.ts +0 -193
- package/dist/types-ts4.5/popover-surface/index.d.ts +0 -2
- package/dist/types-ts4.5/popover-surface/popover-surface.d.ts +0 -21
- package/dist/types-ts4.5/testing/polyfill.d.ts +0 -25
- package/dist/types-ts4.5/testing/to-be-visible.d.ts +0 -23
- package/dist/types-ts4.5/use-arrow-navigation/index.d.ts +0 -3
- package/dist/types-ts4.5/use-arrow-navigation/is-at-current-menu-level.d.ts +0 -48
- package/dist/types-ts4.5/use-arrow-navigation/use-arrow-navigation.d.ts +0 -102
- package/dist/types-ts4.5/use-simple-light-dismiss/index.d.ts +0 -45
|
@@ -54,13 +54,13 @@ export function useSimpleLightDismiss({
|
|
|
54
54
|
const unbindClickOutside = bind(document, {
|
|
55
55
|
type: 'click',
|
|
56
56
|
listener: event => {
|
|
57
|
-
const
|
|
58
|
-
if (!
|
|
57
|
+
const element = popoverRef.current;
|
|
58
|
+
if (!element) {
|
|
59
59
|
return;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
// If the click target is inside the popover, do not dismiss.
|
|
63
|
-
if (event.target instanceof Node &&
|
|
63
|
+
if (event.target instanceof Node && element.contains(event.target)) {
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
66
66
|
onCloseRef.current({
|
|
@@ -48,8 +48,6 @@ export var Dialog = /*#__PURE__*/forwardRef(function Dialog(_ref, ref) {
|
|
|
48
48
|
var dialogId = providedId !== null && providedId !== void 0 ? providedId : generatedId;
|
|
49
49
|
var ownRef = useRef(null);
|
|
50
50
|
var combinedRef = mergeRefs([ownRef, ref]);
|
|
51
|
-
|
|
52
|
-
// Animation lifecycle
|
|
53
51
|
var _useAnimatedVisibilit = useAnimatedVisibility({
|
|
54
52
|
isOpen: isOpen,
|
|
55
53
|
animate: animate,
|
|
@@ -59,31 +57,22 @@ export var Dialog = /*#__PURE__*/forwardRef(function Dialog(_ref, ref) {
|
|
|
59
57
|
phase = _useAnimatedVisibilit.phase,
|
|
60
58
|
preset = _useAnimatedVisibilit.preset;
|
|
61
59
|
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
// signal for the backdrop-click rebind effect so it re-attaches to
|
|
65
|
-
// the fresh <dialog> element after a close → reopen unmount/remount
|
|
66
|
-
// cycle.
|
|
60
|
+
// True while the host element is mounted (any phase except `closed`).
|
|
61
|
+
// Used as a dep so listener-rebind effects re-attach after a remount.
|
|
67
62
|
var isVisible = phase !== 'closed';
|
|
68
63
|
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
// Takes `phase` directly: the listener stays attached while
|
|
75
|
-
// `phase !== 'closed'`, i.e. through the animated-exit window.
|
|
76
|
-
// Gating on `isOpen` would unbind the listener at the start of an
|
|
77
|
-
// exit animation while the dialog is still visible, letting focus
|
|
78
|
-
// escape (WCAG 2.4.3 Focus Order regression).
|
|
64
|
+
// Native `<dialog>.showModal()` traps focus but wraps through `<body>` at
|
|
65
|
+
// the boundary (A → B → C → body → A). This hook intercepts Tab to wrap
|
|
66
|
+
// directly (A → B → C → A), matching the WAI-ARIA APG pattern. Passing
|
|
67
|
+
// `phase` keeps the listener attached through the animated-exit window
|
|
68
|
+
// (WCAG 2.4.3 Focus Order regression guard).
|
|
79
69
|
useFocusWrap({
|
|
80
70
|
elementRef: ownRef,
|
|
81
71
|
role: 'dialog',
|
|
82
72
|
phase: phase
|
|
83
73
|
});
|
|
84
74
|
|
|
85
|
-
//
|
|
86
|
-
// Notifies the open layer observer so app-coordination features
|
|
75
|
+
// Notify the open layer observer so app-coordination features
|
|
87
76
|
// (open-count subscriptions) work with top-layer dialogs.
|
|
88
77
|
useNotifyOpenLayerObserver({
|
|
89
78
|
type: 'modal',
|
|
@@ -91,8 +80,6 @@ export var Dialog = /*#__PURE__*/forwardRef(function Dialog(_ref, ref) {
|
|
|
91
80
|
// No-op: no current use case for programmatic close via OpenLayerObserver.
|
|
92
81
|
onClose: noop
|
|
93
82
|
});
|
|
94
|
-
|
|
95
|
-
// Show/hide the dialog in response to isOpen changes.
|
|
96
83
|
useLayoutEffect(function () {
|
|
97
84
|
var dialog = ownRef.current;
|
|
98
85
|
if (!dialog) {
|
|
@@ -150,15 +137,10 @@ export var Dialog = /*#__PURE__*/forwardRef(function Dialog(_ref, ref) {
|
|
|
150
137
|
// any atomic class, so the override always wins.
|
|
151
138
|
var escapedId = CSS.escape(dialogId);
|
|
152
139
|
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
// re-mounts on the next `isOpen=true` commit; `showModal()` is called
|
|
158
|
-
// from the existing `useLayoutEffect([isOpen])` on that same commit,
|
|
159
|
-
// and listeners (cancel, click) re-bind via the `[onClose, isVisible]`
|
|
160
|
-
// effect re-running after remount (React clears the ref on unmount, so
|
|
161
|
-
// we need the listener effect to re-run with the fresh element).
|
|
140
|
+
// Unmount the `<dialog>` once exit completes so it does not leave an
|
|
141
|
+
// empty `role="dialog"` element in the accessibility tree. On the next
|
|
142
|
+
// open the element remounts and the `[isOpen]` and `[onClose, isVisible]`
|
|
143
|
+
// effects re-run against the fresh element.
|
|
162
144
|
if (!isVisible) {
|
|
163
145
|
return null;
|
|
164
146
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* restores the prior inline values (so we do not stomp consumer styles).
|
|
4
4
|
*/
|
|
5
5
|
export function setStyle(_ref) {
|
|
6
|
-
var
|
|
6
|
+
var element = _ref.element,
|
|
7
7
|
styles = _ref.styles;
|
|
8
8
|
// Snapshot the prior inline value (NOT the computed style - we only want
|
|
9
9
|
// to restore values that the consumer/our previous run inlined). An empty
|
|
@@ -12,23 +12,23 @@ export function setStyle(_ref) {
|
|
|
12
12
|
var property = _ref2.property;
|
|
13
13
|
return {
|
|
14
14
|
property: property,
|
|
15
|
-
value:
|
|
15
|
+
value: element.style.getPropertyValue(property)
|
|
16
16
|
};
|
|
17
17
|
});
|
|
18
18
|
styles.forEach(function (_ref3) {
|
|
19
19
|
var property = _ref3.property,
|
|
20
20
|
value = _ref3.value;
|
|
21
|
-
|
|
21
|
+
element.style.setProperty(property, value);
|
|
22
22
|
});
|
|
23
23
|
return function cleanup() {
|
|
24
24
|
priorValues.forEach(function (_ref4) {
|
|
25
25
|
var property = _ref4.property,
|
|
26
26
|
value = _ref4.value;
|
|
27
27
|
if (value === '') {
|
|
28
|
-
|
|
28
|
+
element.style.removeProperty(property);
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
31
|
-
|
|
31
|
+
element.style.setProperty(property, value);
|
|
32
32
|
});
|
|
33
33
|
};
|
|
34
34
|
}
|
|
@@ -46,7 +46,7 @@ function isSlotChild(child) {
|
|
|
46
46
|
export var Slot = /*#__PURE__*/forwardRef(function Slot(slotProps, ref) {
|
|
47
47
|
var rawChildren = slotProps.children,
|
|
48
48
|
props = _objectWithoutProperties(slotProps, _excluded);
|
|
49
|
-
// `
|
|
49
|
+
// `TSlotProps` intersects `Record<string, unknown>`, so `children` is typed as `unknown`.
|
|
50
50
|
var children = rawChildren;
|
|
51
51
|
if (!isSlotChild(children)) {
|
|
52
52
|
return children;
|
|
@@ -6,7 +6,7 @@ import { useAnchorPosition } from './use-anchor-position';
|
|
|
6
6
|
* anchor should be placed.
|
|
7
7
|
*
|
|
8
8
|
* Axis-named (`x`/`y`) rather than edge-named (`left`/`top`) so the
|
|
9
|
-
* shape is unambiguous in RTL writing modes
|
|
9
|
+
* shape is unambiguous in RTL writing modes - there is no ambiguity
|
|
10
10
|
* about whether a coordinate is logical (`inline-start`/`block-start`)
|
|
11
11
|
* or physical (`left`/`top`). Viewport coordinates are always
|
|
12
12
|
* physical, so we name them with the physical axis labels.
|
|
@@ -17,19 +17,19 @@ import { useAnchorPosition } from './use-anchor-position';
|
|
|
17
17
|
* Dynamic `inset-*` overrides are written by the positioning effect.
|
|
18
18
|
*
|
|
19
19
|
* Uses `Object.assign` rather than Compiled CSS (element is outside the React tree)
|
|
20
|
-
* or the internal `setStyle` helper (we own this element entirely
|
|
21
|
-
* and removed by this hook
|
|
20
|
+
* or the internal `setStyle` helper (we own this element entirely - it is created
|
|
21
|
+
* and removed by this hook - so the snapshot/restore semantics `setStyle`
|
|
22
22
|
* provides for caller-owned elements would be dead work here).
|
|
23
23
|
*/
|
|
24
|
-
function applyStaticAnchorStyles(
|
|
25
|
-
Object.assign(
|
|
24
|
+
function applyStaticAnchorStyles(element) {
|
|
25
|
+
Object.assign(element.style, {
|
|
26
26
|
position: 'fixed',
|
|
27
27
|
// `top`/`left` rather than `inset-block-start`/`inset-inline-start`
|
|
28
28
|
// for consistency with the dynamic positioning effect below, which
|
|
29
29
|
// uses physical properties (see comment there for the rationale).
|
|
30
30
|
top: '0',
|
|
31
31
|
left: '0',
|
|
32
|
-
// Zero size
|
|
32
|
+
// Zero size - placement decides which side the popover sits on.
|
|
33
33
|
width: '0',
|
|
34
34
|
height: '0',
|
|
35
35
|
// Prevent the overlay from intercepting pointer events.
|
|
@@ -75,7 +75,7 @@ export function useAnchorPositionAtPoint(_ref) {
|
|
|
75
75
|
var syntheticAnchorRef = useRef(null);
|
|
76
76
|
|
|
77
77
|
// `getPoint` is kept in a ref so the layout effect can call the
|
|
78
|
-
// latest version without listing it as a dep
|
|
78
|
+
// latest version without listing it as a dep - that would re-fire
|
|
79
79
|
// on every render and re-latch the point.
|
|
80
80
|
var getPointRef = useRef(getPoint);
|
|
81
81
|
getPointRef.current = getPoint;
|
|
@@ -111,7 +111,7 @@ export function useAnchorPositionAtPoint(_ref) {
|
|
|
111
111
|
};
|
|
112
112
|
}, [isEnabled]);
|
|
113
113
|
|
|
114
|
-
// Mirror `isEnabled` so both hooks stay in sync
|
|
114
|
+
// Mirror `isEnabled` so both hooks stay in sync - when disabled,
|
|
115
115
|
// neither this hook nor the inner one touches the popover, leaving
|
|
116
116
|
// any other positioning the consumer has set up free to take effect.
|
|
117
117
|
useAnchorPosition({
|
|
@@ -145,17 +145,11 @@ function crossAxisShiftMargin(_ref2) {
|
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
/**
|
|
148
|
-
* Returns a referentially-stable, fully-resolved `TPlacement`.
|
|
149
|
-
* keyed on the primitive fields
|
|
150
|
-
*
|
|
151
|
-
* `getPlacement` and the equality check are skipped when the inputs are
|
|
152
|
-
* unchanged.
|
|
148
|
+
* Returns a referentially-stable, fully-resolved `TPlacement`. The memo is
|
|
149
|
+
* keyed on the resolved primitive fields so a fresh inline `placement` object
|
|
150
|
+
* (or undefined fields vs explicit defaults) does not produce a fresh result.
|
|
153
151
|
*/
|
|
154
152
|
function useStablePlacement(placement) {
|
|
155
|
-
// Resolve to the fully-defaulted primitives BEFORE the memo so the deps
|
|
156
|
-
// array sees the same key for `{}`, `undefined` fields, and explicit
|
|
157
|
-
// defaults. Otherwise `axis: undefined` and `axis: 'block'` would be
|
|
158
|
-
// treated as different inputs and re-run the downstream effect.
|
|
159
153
|
var resolved = getPlacement({
|
|
160
154
|
placement: placement
|
|
161
155
|
});
|
|
@@ -165,10 +159,8 @@ function useStablePlacement(placement) {
|
|
|
165
159
|
var gap = resolved.offset.gap;
|
|
166
160
|
var shiftValue = resolved.offset.crossAxisShift.value;
|
|
167
161
|
var shiftDirection = resolved.offset.crossAxisShift.direction;
|
|
168
|
-
// Rebuild
|
|
169
|
-
//
|
|
170
|
-
// `resolved` reference, and `react-hooks/exhaustive-deps` can verify the
|
|
171
|
-
// deps array.
|
|
162
|
+
// Rebuild inside the memo from primitive deps so the factory does not
|
|
163
|
+
// close over the outer `resolved` reference and exhaustive-deps can verify.
|
|
172
164
|
return useMemo(function () {
|
|
173
165
|
return {
|
|
174
166
|
axis: axis,
|
|
@@ -200,10 +192,6 @@ function useStablePlacement(placement) {
|
|
|
200
192
|
* When not supported, it falls back to JavaScript-based positioning using
|
|
201
193
|
* `position: fixed` with measured coordinates, re-running on scroll (capture)
|
|
202
194
|
* and resize events.
|
|
203
|
-
*
|
|
204
|
-
* `placement` may be a fresh inline object on every render: a structural
|
|
205
|
-
* comparison is used internally so re-runs are skipped when the resolved
|
|
206
|
-
* placement shape is unchanged.
|
|
207
195
|
*/
|
|
208
196
|
// eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
|
|
209
197
|
export function useAnchorPosition(_ref3) {
|
|
@@ -217,8 +205,6 @@ export function useAnchorPosition(_ref3) {
|
|
|
217
205
|
isEnabled = _ref3$isEnabled === void 0 ? true : _ref3$isEnabled,
|
|
218
206
|
isOpen = _ref3.isOpen;
|
|
219
207
|
var id = useId();
|
|
220
|
-
// Stabilize `placement` object so the same object literal
|
|
221
|
-
// value returns an object with the same reference
|
|
222
208
|
var stablePlacement = useStablePlacement(placement);
|
|
223
209
|
useLayoutEffect(function () {
|
|
224
210
|
if (!isEnabled) {
|
|
@@ -240,9 +226,9 @@ export function useAnchorPosition(_ref3) {
|
|
|
240
226
|
placement: stablePlacement,
|
|
241
227
|
offset: gapCssValue
|
|
242
228
|
});
|
|
243
|
-
//
|
|
244
|
-
//
|
|
245
|
-
//
|
|
229
|
+
// Computed once; reused below to populate the active
|
|
230
|
+
// `--ds-cross-axis-shift-margin-*` custom property for the
|
|
231
|
+
// named arrow @position-try rules.
|
|
246
232
|
var crossAxisShift = crossAxisShiftMargin({
|
|
247
233
|
placement: stablePlacement,
|
|
248
234
|
crossAxisShiftCssValue: crossAxisShiftCssValue,
|
|
@@ -270,22 +256,17 @@ export function useAnchorPosition(_ref3) {
|
|
|
270
256
|
}, {
|
|
271
257
|
property: 'inset',
|
|
272
258
|
value: 'auto'
|
|
273
|
-
},
|
|
274
|
-
// Main-axis gap between the popover and its trigger
|
|
275
|
-
{
|
|
259
|
+
}, {
|
|
276
260
|
property: gap.property,
|
|
277
261
|
value: gap.value
|
|
278
|
-
},
|
|
279
|
-
// Cross-axis shift
|
|
280
|
-
{
|
|
262
|
+
}, {
|
|
281
263
|
property: crossAxisShift.property,
|
|
282
264
|
value: crossAxisShift.value
|
|
283
265
|
}];
|
|
284
266
|
|
|
285
|
-
//
|
|
286
|
-
//
|
|
287
|
-
//
|
|
288
|
-
// at a time.
|
|
267
|
+
// Expose the active cross-axis shift via custom properties so each
|
|
268
|
+
// named arrow @position-try rule can re-apply it after a flip.
|
|
269
|
+
// One of the four (cross-axis, side) properties is non-zero at a time.
|
|
289
270
|
popoverStyles.push({
|
|
290
271
|
property: '--ds-cross-axis-shift-margin-start',
|
|
291
272
|
value: '0px'
|
|
@@ -302,9 +283,8 @@ export function useAnchorPosition(_ref3) {
|
|
|
302
283
|
property: '--ds-cross-axis-shift-margin-block-end',
|
|
303
284
|
value: '0px'
|
|
304
285
|
});
|
|
305
|
-
// `crossAxisShift` IS the active margin
|
|
286
|
+
// `crossAxisShift` IS the active margin; reuse instead of recomputing.
|
|
306
287
|
var crossAxisShiftActive = crossAxisShift;
|
|
307
|
-
// Map margin property to its corresponding custom property name.
|
|
308
288
|
var customPropertyByMarginProperty = {
|
|
309
289
|
'margin-inline-start': '--ds-cross-axis-shift-margin-start',
|
|
310
290
|
'margin-inline-end': '--ds-cross-axis-shift-margin-end',
|
|
@@ -338,18 +318,17 @@ export function useAnchorPosition(_ref3) {
|
|
|
338
318
|
*/
|
|
339
319
|
trigger.style.setProperty('anchor-name', anchorName);
|
|
340
320
|
var _undoPositioning = combine(setStyle({
|
|
341
|
-
|
|
321
|
+
element: popover,
|
|
342
322
|
styles: popoverStyles
|
|
343
323
|
}));
|
|
344
324
|
return _undoPositioning;
|
|
345
325
|
}
|
|
346
326
|
|
|
347
|
-
// JS fallback
|
|
348
|
-
//
|
|
349
|
-
//
|
|
350
|
-
// (inset: 0; margin: auto) and set top/left based on measurements.
|
|
327
|
+
// JS fallback. The popover is already in the top layer via
|
|
328
|
+
// popover="auto", so we only reset UA defaults and set top/left
|
|
329
|
+
// based on measurements.
|
|
351
330
|
var cleanupBaseStyles = setStyle({
|
|
352
|
-
|
|
331
|
+
element: popover,
|
|
353
332
|
styles: [{
|
|
354
333
|
property: 'margin',
|
|
355
334
|
value: '0'
|
|
@@ -12,14 +12,16 @@ function runOnTransitionEndOrTimeout(_ref) {
|
|
|
12
12
|
var element = _ref.element,
|
|
13
13
|
safetyNetMs = _ref.safetyNetMs,
|
|
14
14
|
onSettled = _ref.onSettled;
|
|
15
|
-
var
|
|
15
|
+
var state = {
|
|
16
|
+
isSettled: false
|
|
17
|
+
};
|
|
16
18
|
function teardown() {
|
|
17
|
-
isSettled = true;
|
|
19
|
+
state.isSettled = true;
|
|
18
20
|
clearTimeout(safetyNetId);
|
|
19
21
|
unbind();
|
|
20
22
|
}
|
|
21
23
|
function settle() {
|
|
22
|
-
if (isSettled) {
|
|
24
|
+
if (state.isSettled) {
|
|
23
25
|
return;
|
|
24
26
|
}
|
|
25
27
|
teardown();
|
|
@@ -103,22 +105,16 @@ export function useAnimatedVisibility(_ref2) {
|
|
|
103
105
|
elementRef = _ref2.elementRef,
|
|
104
106
|
onEnterFinish = _ref2.onEnterFinish,
|
|
105
107
|
onExitFinish = _ref2.onExitFinish;
|
|
106
|
-
// Resolve the animation preset and inject its CSS (idempotent).
|
|
107
|
-
// Returns `null` when `animate` is falsy.
|
|
108
108
|
var preset = usePresetStyles({
|
|
109
109
|
preset: animate
|
|
110
110
|
});
|
|
111
111
|
|
|
112
|
-
//
|
|
113
|
-
// or the user has enabled the "prefers reduced motion" OS setting.
|
|
112
|
+
// False when there is no preset or `prefers-reduced-motion` is set.
|
|
114
113
|
var willAnimate = Boolean(animate) && !prefersReducedMotion();
|
|
115
114
|
|
|
116
|
-
// `
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
// in the same commit as the open intent (React supports a single
|
|
120
|
-
// setState during render as a synchronous re-render before paint).
|
|
121
|
-
// All other transitions are driven by phase-specific effects below.
|
|
115
|
+
// Promote `closed → entering`/`closed → open` synchronously during
|
|
116
|
+
// render so the host element mounts in the same commit as the open
|
|
117
|
+
// intent. All other transitions are driven by phase-specific effects.
|
|
122
118
|
var _useState = useState(function () {
|
|
123
119
|
if (!isOpen) {
|
|
124
120
|
return 'closed';
|
|
@@ -153,10 +149,8 @@ export function useAnimatedVisibility(_ref2) {
|
|
|
153
149
|
var onExitFinishRef = useRef(onExitFinish);
|
|
154
150
|
onExitFinishRef.current = onExitFinish;
|
|
155
151
|
|
|
156
|
-
// `
|
|
157
|
-
//
|
|
158
|
-
// `onEnterFinish`. A fallback timeout (enterDurationMs + 50ms buffer)
|
|
159
|
-
// guarantees the settle path runs even if `transitionend` is swallowed
|
|
152
|
+
// Animated entry: wait for `transitionend` (or a safety-net timeout) and
|
|
153
|
+
// move to `open`. The fallback guarantees settle on swallowed events
|
|
160
154
|
// (zero-duration, browser quirk, mid-transition unmount).
|
|
161
155
|
useEffect(function () {
|
|
162
156
|
var _preset$enterDuration;
|
|
@@ -184,12 +178,9 @@ export function useAnimatedVisibility(_ref2) {
|
|
|
184
178
|
});
|
|
185
179
|
}, [phase, preset === null || preset === void 0 ? void 0 : preset.enterDurationMs, elementRef]);
|
|
186
180
|
|
|
187
|
-
// Non-animated entry callback
|
|
188
|
-
//
|
|
189
|
-
//
|
|
190
|
-
// transition into `open` from the non-animated path. We detect this
|
|
191
|
-
// via a previous-phase ref to avoid firing on the animated path's
|
|
192
|
-
// `entering → open` transition (which has its own callback above).
|
|
181
|
+
// Non-animated entry callback. The lifecycle goes `closed → open`
|
|
182
|
+
// directly with no `entering` phase; fire `onEnterFinish` on that jump.
|
|
183
|
+
// A previous-phase ref avoids double-firing on the animated path.
|
|
193
184
|
var prevPhaseForEntryRef = useRef('closed');
|
|
194
185
|
useEffect(function () {
|
|
195
186
|
var prevPhase = prevPhaseForEntryRef.current;
|
|
@@ -203,12 +194,8 @@ export function useAnimatedVisibility(_ref2) {
|
|
|
203
194
|
}
|
|
204
195
|
}, [phase]);
|
|
205
196
|
|
|
206
|
-
// `
|
|
207
|
-
//
|
|
208
|
-
// (set phase back to `closed`) and fire `onExitFinish`. The fallback
|
|
209
|
-
// timeout (exitDurationMs + 50ms buffer) guarantees we always unmount
|
|
210
|
-
// even if `transitionend` is never emitted (element removed from the
|
|
211
|
-
// top layer, zero-duration transition, interrupted animation).
|
|
197
|
+
// Animated exit: wait for `transitionend` (or a safety-net timeout),
|
|
198
|
+
// fire `onExitFinish`, then move to `closed` to unmount the host.
|
|
212
199
|
useEffect(function () {
|
|
213
200
|
var _preset$exitDurationM;
|
|
214
201
|
if (phase !== 'exiting' || !willAnimate) {
|
|
@@ -237,33 +224,20 @@ export function useAnimatedVisibility(_ref2) {
|
|
|
237
224
|
});
|
|
238
225
|
}, [phase, willAnimate, preset === null || preset === void 0 ? void 0 : preset.exitDurationMs, elementRef]);
|
|
239
226
|
|
|
240
|
-
// `
|
|
241
|
-
//
|
|
242
|
-
//
|
|
243
|
-
//
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
// the still-attached element,
|
|
247
|
-
// - the browser's native focus restoration triggered by
|
|
248
|
-
// `hidePopover()` / `<dialog>.close()` has fired,
|
|
249
|
-
// before React unmounts the node. A naive `setTimeout(0)` race
|
|
250
|
-
// occasionally beats the `toggle` task in Chromium, causing focus
|
|
251
|
-
// restoration to drop (the toggle event arrives at a detached element).
|
|
227
|
+
// Non-animated exit: wait for the browser's `toggle` (closed) or
|
|
228
|
+
// `close` event before unmounting, so the consumer's listener (close
|
|
229
|
+
// reason, nested focus restoration, `onClose`) and the browser's
|
|
230
|
+
// native focus restoration both run against the still-attached
|
|
231
|
+
// element. A naive `setTimeout(0)` occasionally beats the `toggle`
|
|
232
|
+
// task in Chromium and drops focus restoration.
|
|
252
233
|
//
|
|
253
|
-
// `useLayoutEffect`
|
|
254
|
-
//
|
|
255
|
-
//
|
|
256
|
-
//
|
|
257
|
-
// frame (including all effect flushes) completes, so the listener is
|
|
258
|
-
// always attached before the event fires. Promotion to layout timing
|
|
259
|
-
// removes any theoretical gap and future-proofs the ordering against
|
|
260
|
-
// effect reordering. Safe under SSR: `phase` starts at `'closed'`, so
|
|
261
|
-
// the body short-circuits and no DOM access happens on the server.
|
|
234
|
+
// `useLayoutEffect` so the listener binds in the same commit as the
|
|
235
|
+
// show/hide layout effect that calls `hidePopover()` / `dialog.close()`.
|
|
236
|
+
// The toggle/close event is queued as a task and cannot run until the
|
|
237
|
+
// frame completes. SSR-safe: `phase` starts at `'closed'`.
|
|
262
238
|
//
|
|
263
|
-
// `onExitFinish` is
|
|
264
|
-
//
|
|
265
|
-
// effect below, so consumers observe a synchronous "exit finished"
|
|
266
|
-
// signal that matches `isOpen` flipping to `false`.
|
|
239
|
+
// `onExitFinish` is fired separately (see effect below) so consumers
|
|
240
|
+
// observe it the moment `isOpen` flips to `false`.
|
|
267
241
|
useLayoutEffect(function () {
|
|
268
242
|
if (phase !== 'exiting' || willAnimate) {
|
|
269
243
|
return;
|
|
@@ -290,13 +264,9 @@ export function useAnimatedVisibility(_ref2) {
|
|
|
290
264
|
}]);
|
|
291
265
|
}, [phase, willAnimate, elementRef]);
|
|
292
266
|
|
|
293
|
-
// Non-animated exit callback
|
|
294
|
-
//
|
|
295
|
-
//
|
|
296
|
-
// host-element unmount is gated on the browser `toggle`/`close` event
|
|
297
|
-
// by the effect above; `onExitFinish` is observably decoupled from
|
|
298
|
-
// that handshake so consumers see the callback fire at the same time
|
|
299
|
-
// `isOpen` flipped to `false`.
|
|
267
|
+
// Non-animated exit callback. No `transitionend` to wait for, so fire
|
|
268
|
+
// `onExitFinish` on the transition into `exiting` (the unmount itself
|
|
269
|
+
// is gated by the toggle/close handshake above).
|
|
300
270
|
var prevPhaseForExitRef = useRef('closed');
|
|
301
271
|
useEffect(function () {
|
|
302
272
|
var prevPhase = prevPhaseForExitRef.current;
|
|
@@ -128,8 +128,6 @@ function getInitialFocusTarget(_ref2) {
|
|
|
128
128
|
});
|
|
129
129
|
}
|
|
130
130
|
if (role === 'menu') {
|
|
131
|
-
// ARIA Combobox Pattern: if the popup is owned by a focused combobox,
|
|
132
|
-
// leave focus on the textbox. Navigation is proxied via the combobox.
|
|
133
131
|
if (isComboboxControllingPopup({
|
|
134
132
|
container: container
|
|
135
133
|
})) {
|
|
@@ -146,9 +144,9 @@ function getInitialFocusTarget(_ref2) {
|
|
|
146
144
|
});
|
|
147
145
|
}
|
|
148
146
|
if (role === 'listbox') {
|
|
149
|
-
// ARIA Combobox Pattern: if
|
|
150
|
-
//
|
|
151
|
-
//
|
|
147
|
+
// ARIA Combobox Pattern: if a combobox-like textbox owns the popup
|
|
148
|
+
// (in-popup or external e.g. react-select), leave focus on the
|
|
149
|
+
// textbox - navigation is proxied through it.
|
|
152
150
|
if (isComboboxControllingPopup({
|
|
153
151
|
container: container
|
|
154
152
|
})) {
|
|
@@ -210,7 +208,7 @@ function getInitialFocusTarget(_ref2) {
|
|
|
210
208
|
* `<dialog>.close()`. If the consumer reopens before the exit transition
|
|
211
209
|
* has settled, the phase machine jumps `exiting → entering` without
|
|
212
210
|
* passing through `closed`, so without this branch the popup would be
|
|
213
|
-
* visible again with focus stranded on the trigger
|
|
211
|
+
* visible again with focus stranded on the trigger - a keyboard usability
|
|
214
212
|
* gap for menu/dialog/listbox roles.
|
|
215
213
|
*
|
|
216
214
|
* Note: there is a rare programmatic path where a consumer flips `isOpen`
|
|
@@ -251,22 +249,20 @@ export function useInitialFocus(_ref3) {
|
|
|
251
249
|
var prevPhase = prevPhaseRef.current;
|
|
252
250
|
prevPhaseRef.current = phase;
|
|
253
251
|
|
|
254
|
-
//
|
|
255
|
-
//
|
|
256
|
-
// neither `'closed'` (fresh open) nor `'exiting'` (mid-exit
|
|
257
|
-
// reopen, focus already on the trigger).
|
|
252
|
+
// Only focus on transitions into a visible phase from `'closed'`
|
|
253
|
+
// (fresh open) or `'exiting'` (mid-exit reopen).
|
|
258
254
|
if (phase === 'closed') {
|
|
259
255
|
return;
|
|
260
256
|
}
|
|
261
257
|
if (prevPhase !== 'closed' && prevPhase !== 'exiting') {
|
|
262
258
|
return;
|
|
263
259
|
}
|
|
264
|
-
var
|
|
265
|
-
if (!
|
|
260
|
+
var element = elementRef.current;
|
|
261
|
+
if (!element) {
|
|
266
262
|
return;
|
|
267
263
|
}
|
|
268
264
|
(_getInitialFocusTarge = getInitialFocusTarget({
|
|
269
|
-
container:
|
|
265
|
+
container: element,
|
|
270
266
|
role: role
|
|
271
267
|
})) === null || _getInitialFocusTarge === void 0 || _getInitialFocusTarge.focus();
|
|
272
268
|
}, [elementRef, phase, role]);
|
|
@@ -35,7 +35,7 @@ export function useWidthFromAnchor(_ref) {
|
|
|
35
35
|
// explicit width constraint, allowing the popover to grow past the anchor's width.
|
|
36
36
|
if (supportsAnchorSize) {
|
|
37
37
|
return setStyle({
|
|
38
|
-
|
|
38
|
+
element: node,
|
|
39
39
|
styles: [{
|
|
40
40
|
property: 'width',
|
|
41
41
|
value: 'anchor-size(width)'
|
|
@@ -47,7 +47,7 @@ export function useWidthFromAnchor(_ref) {
|
|
|
47
47
|
var anchor = anchorRef.current;
|
|
48
48
|
if (anchor) {
|
|
49
49
|
return setStyle({
|
|
50
|
-
|
|
50
|
+
element: node,
|
|
51
51
|
styles: [{
|
|
52
52
|
property: 'width',
|
|
53
53
|
value: "".concat(anchor.offsetWidth, "px")
|
|
@@ -69,7 +69,7 @@ export function useWidthFromAnchor(_ref) {
|
|
|
69
69
|
// if its content requires it.
|
|
70
70
|
if (supportsAnchorSize) {
|
|
71
71
|
return setStyle({
|
|
72
|
-
|
|
72
|
+
element: node,
|
|
73
73
|
styles: [minInlineSizeStyle, {
|
|
74
74
|
property: 'min-width',
|
|
75
75
|
value: 'anchor-size(width)'
|
|
@@ -79,7 +79,7 @@ export function useWidthFromAnchor(_ref) {
|
|
|
79
79
|
var _anchor = anchorRef.current;
|
|
80
80
|
if (_anchor) {
|
|
81
81
|
return setStyle({
|
|
82
|
-
|
|
82
|
+
element: node,
|
|
83
83
|
styles: [minInlineSizeStyle, {
|
|
84
84
|
property: 'min-width',
|
|
85
85
|
value: "".concat(_anchor.offsetWidth, "px")
|
|
@@ -89,14 +89,14 @@ export function useWidthFromAnchor(_ref) {
|
|
|
89
89
|
|
|
90
90
|
// No anchor available in JS fallback - still apply min-inline-size.
|
|
91
91
|
return setStyle({
|
|
92
|
-
|
|
92
|
+
element: node,
|
|
93
93
|
styles: [minInlineSizeStyle]
|
|
94
94
|
});
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
// 'none' mode: no explicit width, but still apply `min-inline-size: max-content`.
|
|
98
98
|
return setStyle({
|
|
99
|
-
|
|
99
|
+
element: node,
|
|
100
100
|
styles: [minInlineSizeStyle]
|
|
101
101
|
});
|
|
102
102
|
}, [mode, popoverRef, anchorRef, isOpen]);
|