@atlaskit/top-layer 0.10.0 → 0.12.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/CHANGELOG.md +23 -0
- package/README.md +3 -3
- package/dist/cjs/animations/presets.js +8 -1
- package/dist/cjs/internal/use-animated-visibility.js +84 -7
- package/dist/cjs/popover/popover.js +2 -0
- package/dist/cjs/popup/popup-content.js +53 -13
- package/dist/cjs/popup/popup-context.js +17 -0
- package/dist/cjs/popup/popup-trigger-function.js +7 -4
- package/dist/cjs/popup/popup-trigger.js +12 -7
- package/dist/cjs/popup/popup.js +5 -5
- package/dist/es2019/animations/presets.js +19 -2
- package/dist/es2019/internal/use-animated-visibility.js +84 -7
- package/dist/es2019/popover/popover.js +2 -0
- package/dist/es2019/popup/popup-content.js +53 -13
- package/dist/es2019/popup/popup-context.js +18 -0
- package/dist/es2019/popup/popup-trigger-function.js +7 -4
- package/dist/es2019/popup/popup-trigger.js +12 -7
- package/dist/es2019/popup/popup.js +3 -3
- package/dist/esm/animations/presets.js +8 -1
- package/dist/esm/internal/use-animated-visibility.js +84 -7
- package/dist/esm/popover/popover.js +2 -0
- package/dist/esm/popup/popup-content.js +53 -13
- package/dist/esm/popup/popup-context.js +18 -0
- package/dist/esm/popup/popup-trigger-function.js +7 -4
- package/dist/esm/popup/popup-trigger.js +12 -7
- package/dist/esm/popup/popup.js +5 -5
- package/dist/types/animations/types.d.ts +5 -1
- package/dist/types/internal/use-animated-visibility.d.ts +8 -2
- package/dist/types/popover/types.d.ts +8 -0
- package/dist/types/popup/popup-context.d.ts +24 -4
- package/dist/types/popup/types.d.ts +12 -0
- package/dist/types-ts4.5/animations/types.d.ts +5 -1
- package/dist/types-ts4.5/internal/use-animated-visibility.d.ts +8 -2
- package/dist/types-ts4.5/popover/types.d.ts +8 -0
- package/dist/types-ts4.5/popup/popup-context.d.ts +24 -4
- package/dist/types-ts4.5/popup/types.d.ts +12 -0
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ import _extends from "@babel/runtime/helpers/extends";
|
|
|
3
3
|
import { ax, ix } from "@compiled/react/runtime";
|
|
4
4
|
import React, { forwardRef, useCallback, useLayoutEffect, useRef } from 'react';
|
|
5
5
|
import mergeRefs from '@atlaskit/ds-lib/merge-refs';
|
|
6
|
+
import { prefersReducedMotion } from '../internal/reduced-motion';
|
|
6
7
|
import { roleToAriaHasPopup, shouldFocusIntoPopover } from '../internal/role-types';
|
|
7
8
|
import { useAnchorPosition } from '../internal/use-anchor-position';
|
|
8
9
|
import { useWidthFromAnchor } from '../internal/use-width-from-anchor';
|
|
@@ -16,7 +17,7 @@ import { useMaybePopupContext } from './popup-context';
|
|
|
16
17
|
* use `Popover` + `useAnchorPosition` directly.
|
|
17
18
|
*/
|
|
18
19
|
export var PopupContent = /*#__PURE__*/forwardRef(function PopupContent(_ref, ref) {
|
|
19
|
-
var _ref2,
|
|
20
|
+
var _ref2, _ctx$popoverRef;
|
|
20
21
|
var children = _ref.children,
|
|
21
22
|
role = _ref.role,
|
|
22
23
|
label = _ref.label,
|
|
@@ -32,7 +33,9 @@ export var PopupContent = /*#__PURE__*/forwardRef(function PopupContent(_ref, re
|
|
|
32
33
|
onCloseProp = _ref.onClose,
|
|
33
34
|
testIdProp = _ref.testId,
|
|
34
35
|
forceFallbackPositioningProp = _ref.forceFallbackPositioning,
|
|
35
|
-
idProp = _ref.id
|
|
36
|
+
idProp = _ref.id,
|
|
37
|
+
onEnterFinish = _ref.onEnterFinish,
|
|
38
|
+
onExitFinish = _ref.onExitFinish;
|
|
36
39
|
// When inside <Popup>, context provides defaults. Props always win.
|
|
37
40
|
var ctx = useMaybePopupContext();
|
|
38
41
|
var triggerRef = triggerRefProp !== null && triggerRefProp !== void 0 ? triggerRefProp : ctx === null || ctx === void 0 ? void 0 : ctx.triggerRef;
|
|
@@ -41,16 +44,47 @@ export var PopupContent = /*#__PURE__*/forwardRef(function PopupContent(_ref, re
|
|
|
41
44
|
var testId = testIdProp !== null && testIdProp !== void 0 ? testIdProp : ctx === null || ctx === void 0 ? void 0 : ctx.testId;
|
|
42
45
|
var forceFallbackPositioning = forceFallbackPositioningProp !== null && forceFallbackPositioningProp !== void 0 ? forceFallbackPositioningProp : ctx === null || ctx === void 0 ? void 0 : ctx.forceFallbackPositioning;
|
|
43
46
|
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
var isOpen =
|
|
47
|
-
|
|
48
|
-
// Combine internal state sync with consumer's onOpenChange callback.
|
|
49
|
-
// The Popup compound needs setIsOpen to keep aria-expanded on the trigger
|
|
50
|
-
// in sync, and the consumer may also want to respond to open/close events
|
|
51
|
-
// (e.g. for focus management).
|
|
52
|
-
var setIsOpen = ctx === null || ctx === void 0 ? void 0 : ctx.setIsOpen;
|
|
47
|
+
// isOpen: prop wins, otherwise derive from context popupState.
|
|
48
|
+
// 'animating-open' counts as open so <Popover> stays visible during entry animation.
|
|
49
|
+
var isOpen = isOpenProp !== null && isOpenProp !== void 0 ? isOpenProp : ctx !== null ? ctx.popupState === 'open' || ctx.popupState === 'animating-open' : false;
|
|
50
|
+
var setPopupState = ctx === null || ctx === void 0 ? void 0 : ctx.setPopupState;
|
|
53
51
|
var consumerOnOpenChange = ctx === null || ctx === void 0 ? void 0 : ctx.onOpenChange;
|
|
52
|
+
|
|
53
|
+
// ── TPopupState ──
|
|
54
|
+
//
|
|
55
|
+
// popupState is driven by handleOpenChange (below), which fires on every browser
|
|
56
|
+
// toggle event (showPopover / hidePopover / light-dismiss).
|
|
57
|
+
//
|
|
58
|
+
// Exception — initial mount with isOpen=true (prop-controlled usage):
|
|
59
|
+
// handleOpenChange only fires from the Popover's toggle event listener, which
|
|
60
|
+
// is registered in a useEffect. On the first render, useEffect hasn't run yet,
|
|
61
|
+
// so handleOpenChange never fires. We sync popupState here instead.
|
|
62
|
+
//
|
|
63
|
+
// Transitions when animations are active:
|
|
64
|
+
// closed → animating-open → open (enter)
|
|
65
|
+
// open → animating-closed → closed (exit, via onExitFinish)
|
|
66
|
+
//
|
|
67
|
+
// Without animation: closed ↔ open directly.
|
|
68
|
+
var willAnimate = Boolean(animate) && !prefersReducedMotion();
|
|
69
|
+
var hasMountedRef = useRef(false);
|
|
70
|
+
useLayoutEffect(function () {
|
|
71
|
+
if (hasMountedRef.current) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
hasMountedRef.current = true;
|
|
75
|
+
if (isOpen) {
|
|
76
|
+
setPopupState === null || setPopupState === void 0 || setPopupState(willAnimate ? 'animating-open' : 'open');
|
|
77
|
+
}
|
|
78
|
+
}, [isOpen, setPopupState, willAnimate]);
|
|
79
|
+
var handleEnterFinish = useCallback(function () {
|
|
80
|
+
setPopupState === null || setPopupState === void 0 || setPopupState('open');
|
|
81
|
+
onEnterFinish === null || onEnterFinish === void 0 || onEnterFinish();
|
|
82
|
+
}, [setPopupState, onEnterFinish]);
|
|
83
|
+
var handleExitFinish = useCallback(function () {
|
|
84
|
+
setPopupState === null || setPopupState === void 0 || setPopupState('closed');
|
|
85
|
+
onExitFinish === null || onExitFinish === void 0 || onExitFinish();
|
|
86
|
+
}, [setPopupState, onExitFinish]);
|
|
87
|
+
|
|
54
88
|
// Note: onOpenChange composition is handled by handleOpenChange below,
|
|
55
89
|
// which also adds nested-popover focus restoration.
|
|
56
90
|
|
|
@@ -82,7 +116,11 @@ export var PopupContent = /*#__PURE__*/forwardRef(function PopupContent(_ref, re
|
|
|
82
116
|
// See: notes/architecture/focus-restoration.md
|
|
83
117
|
var handleOpenChange = useCallback(function (args) {
|
|
84
118
|
// Sync compound state
|
|
85
|
-
|
|
119
|
+
if (args.isOpen) {
|
|
120
|
+
setPopupState === null || setPopupState === void 0 || setPopupState(willAnimate ? 'animating-open' : 'open');
|
|
121
|
+
} else {
|
|
122
|
+
setPopupState === null || setPopupState === void 0 || setPopupState(willAnimate ? 'animating-closed' : 'closed');
|
|
123
|
+
}
|
|
86
124
|
consumerOnOpenChange === null || consumerOnOpenChange === void 0 || consumerOnOpenChange(args);
|
|
87
125
|
|
|
88
126
|
// ── Nested popover focus restoration fallback ──
|
|
@@ -112,7 +150,7 @@ export var PopupContent = /*#__PURE__*/forwardRef(function PopupContent(_ref, re
|
|
|
112
150
|
});
|
|
113
151
|
}
|
|
114
152
|
}
|
|
115
|
-
}, [
|
|
153
|
+
}, [consumerOnOpenChange, triggerRef, role, setPopupState, willAnimate]);
|
|
116
154
|
|
|
117
155
|
// ── Anchor positioning ──
|
|
118
156
|
// Compose useAnchorPosition with Popover. The hook positions the popover
|
|
@@ -151,6 +189,8 @@ export var PopupContent = /*#__PURE__*/forwardRef(function PopupContent(_ref, re
|
|
|
151
189
|
labelledBy: labelledBy,
|
|
152
190
|
onClose: onClose !== null && onClose !== void 0 ? onClose : undefined,
|
|
153
191
|
onOpenChange: handleOpenChange,
|
|
192
|
+
onEnterFinish: handleEnterFinish,
|
|
193
|
+
onExitFinish: handleExitFinish,
|
|
154
194
|
testId: testId,
|
|
155
195
|
children: children,
|
|
156
196
|
// Standalone consumers can pass an explicit `id` - used by their own
|
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
import { createContext, useContext } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Represents the lifecycle state of the popup.
|
|
5
|
+
*
|
|
6
|
+
* - `'closed'`: fully closed, not visible, no animation playing.
|
|
7
|
+
* - `'animating-open'`: entry animation is playing (popup is opening but not yet settled).
|
|
8
|
+
* - `'open'`: fully open, browser popover is showing, entry animation complete.
|
|
9
|
+
* - `'animating-closed'`: exit animation is playing (popup is closing but still visible).
|
|
10
|
+
*
|
|
11
|
+
* Used to derive `aria-expanded` on the trigger:
|
|
12
|
+
* - `aria-expanded` is `true` in `'animating-open'`, `'open'`, and `'animating-closed'`.
|
|
13
|
+
*
|
|
14
|
+
* This avoids screen readers announcing the popup as closed while it is
|
|
15
|
+
* still visually present on screen during animations, and prevents consumers
|
|
16
|
+
* that conditionally show/hide trigger UI based on `aria-expanded` from hiding
|
|
17
|
+
* the trigger (and thus the popup anchor) mid-animation.
|
|
18
|
+
*/
|
|
19
|
+
|
|
2
20
|
var PopupContext = /*#__PURE__*/createContext(null);
|
|
3
21
|
|
|
4
22
|
/**
|
|
@@ -41,7 +41,7 @@ export function PopupTriggerFunction(_ref) {
|
|
|
41
41
|
triggerRef = _usePopupContext.triggerRef,
|
|
42
42
|
popoverRef = _usePopupContext.popoverRef,
|
|
43
43
|
popoverId = _usePopupContext.popoverId,
|
|
44
|
-
|
|
44
|
+
popupState = _usePopupContext.popupState,
|
|
45
45
|
ariaHasPopup = _usePopupContext.ariaHasPopup;
|
|
46
46
|
var ref = useCallback(function (node) {
|
|
47
47
|
triggerRef.current = node;
|
|
@@ -57,14 +57,17 @@ export function PopupTriggerFunction(_ref) {
|
|
|
57
57
|
}, [popoverRef]);
|
|
58
58
|
var ariaAttributes = useMemo(function () {
|
|
59
59
|
return {
|
|
60
|
-
|
|
60
|
+
// aria-expanded stays true throughout entry and exit animations so screen readers
|
|
61
|
+
// don't announce the popup as closed while it's still visible, and consumers
|
|
62
|
+
// relying on aria-expanded to show/hide trigger UI don't lose the anchor mid-animation.
|
|
63
|
+
'aria-expanded': popupState === 'open' || popupState === 'animating-open' || popupState === 'animating-closed',
|
|
61
64
|
'aria-controls': popoverId,
|
|
62
65
|
'aria-haspopup': ariaHasPopup
|
|
63
66
|
};
|
|
64
|
-
}, [
|
|
67
|
+
}, [popupState, popoverId, ariaHasPopup]);
|
|
65
68
|
return children({
|
|
66
69
|
ref: ref,
|
|
67
|
-
isOpen:
|
|
70
|
+
isOpen: popupState === 'open' || popupState === 'animating-open',
|
|
68
71
|
popoverId: popoverId,
|
|
69
72
|
toggle: toggle,
|
|
70
73
|
ariaAttributes: ariaAttributes
|
|
@@ -18,14 +18,15 @@ export function PopupTrigger(_ref) {
|
|
|
18
18
|
triggerRef = _usePopupContext.triggerRef,
|
|
19
19
|
popoverRef = _usePopupContext.popoverRef,
|
|
20
20
|
popoverId = _usePopupContext.popoverId,
|
|
21
|
-
|
|
21
|
+
popupState = _usePopupContext.popupState,
|
|
22
22
|
ariaHasPopup = _usePopupContext.ariaHasPopup;
|
|
23
23
|
var childOnClick = /*#__PURE__*/isValidElement(children) ? children.props.onClick : undefined;
|
|
24
24
|
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
isOpenRef
|
|
25
|
+
// Use a ref to track the latest isOpen value so the click handler
|
|
26
|
+
// does not need isOpen in its dependency array (which would recreate
|
|
27
|
+
// the handler on every open/close, breaking cloneElement identity).
|
|
28
|
+
var isOpenRef = React.useRef(popupState === 'open' || popupState === 'animating-open');
|
|
29
|
+
isOpenRef.current = popupState === 'open' || popupState === 'animating-open';
|
|
29
30
|
var handleClick = useCallback(function (event) {
|
|
30
31
|
var popoverEl = popoverRef.current;
|
|
31
32
|
if (popoverEl) {
|
|
@@ -45,8 +46,12 @@ export function PopupTrigger(_ref) {
|
|
|
45
46
|
}, [popoverRef, childOnClick]);
|
|
46
47
|
return /*#__PURE__*/React.createElement(Slot, {
|
|
47
48
|
ref: triggerRef,
|
|
48
|
-
onClick: handleClick
|
|
49
|
-
|
|
49
|
+
onClick: handleClick
|
|
50
|
+
// aria-expanded stays true throughout entry and exit animations so screen readers
|
|
51
|
+
// don't announce the popup as closed while it's still visible, and consumers
|
|
52
|
+
// relying on aria-expanded to show/hide trigger UI don't lose the anchor mid-animation.
|
|
53
|
+
,
|
|
54
|
+
"aria-expanded": popupState === 'open' || popupState === 'animating-open' || popupState === 'animating-closed',
|
|
50
55
|
"aria-controls": popoverId,
|
|
51
56
|
"aria-haspopup": ariaHasPopup
|
|
52
57
|
}, children);
|
package/dist/esm/popup/popup.js
CHANGED
|
@@ -35,10 +35,10 @@ export function PopupRoot(props) {
|
|
|
35
35
|
var popoverId = "popover-".concat(sanitizedId);
|
|
36
36
|
var triggerRef = useRef(null);
|
|
37
37
|
var popoverRef = useRef(null);
|
|
38
|
-
var _useState = useState(
|
|
38
|
+
var _useState = useState('closed'),
|
|
39
39
|
_useState2 = _slicedToArray(_useState, 2),
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
popupState = _useState2[0],
|
|
41
|
+
setPopupState = _useState2[1];
|
|
42
42
|
// Initial seed before `Popup.Content`'s layout effect resolves the
|
|
43
43
|
// real role on first paint.
|
|
44
44
|
var _useState3 = useState('dialog'),
|
|
@@ -57,8 +57,8 @@ export function PopupRoot(props) {
|
|
|
57
57
|
onClose: onClose ? handleClose : null,
|
|
58
58
|
triggerRef: triggerRef,
|
|
59
59
|
popoverRef: popoverRef,
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
popupState: popupState,
|
|
61
|
+
setPopupState: setPopupState,
|
|
62
62
|
onOpenChange: onOpenChange,
|
|
63
63
|
mode: mode,
|
|
64
64
|
ariaHasPopup: ariaHasPopup,
|
|
@@ -28,7 +28,11 @@ export type TAnimationPreset = {
|
|
|
28
28
|
placement: TPlacementOptions;
|
|
29
29
|
}) => Record<string, string>;
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
31
|
+
* Entry duration in ms (for `transitionend` fallback timeout on open).
|
|
32
|
+
*/
|
|
33
|
+
enterDurationMs: number;
|
|
34
|
+
/**
|
|
35
|
+
* Exit duration in ms (for `transitionend` fallback timeout on close).
|
|
32
36
|
*/
|
|
33
37
|
exitDurationMs: number;
|
|
34
38
|
};
|
|
@@ -11,10 +11,16 @@ type TUseAnimatedVisibilityArgs = {
|
|
|
11
11
|
*/
|
|
12
12
|
animate: TAnimationPreset | false | undefined;
|
|
13
13
|
/**
|
|
14
|
-
* Ref to the DOM element that plays the exit
|
|
14
|
+
* Ref to the DOM element that plays the entry/exit transitions.
|
|
15
15
|
* Used to listen for `transitionend`.
|
|
16
16
|
*/
|
|
17
17
|
elementRef: RefObject<HTMLElement | null>;
|
|
18
|
+
/**
|
|
19
|
+
* Called after the entry animation completes (or immediately on open
|
|
20
|
+
* when there is no animation or reduced motion is active).
|
|
21
|
+
* This includes initial mount with `isOpen=true`.
|
|
22
|
+
*/
|
|
23
|
+
onEnterFinish?: () => void;
|
|
18
24
|
/**
|
|
19
25
|
* Called after the exit animation completes (or immediately on close
|
|
20
26
|
* when there is no animation or reduced motion is active).
|
|
@@ -70,5 +76,5 @@ type TUseAnimatedVisibilityResult = {
|
|
|
70
76
|
* `isOpen` goes `false` → `showChildren` → `false` synchronously during
|
|
71
77
|
* the same render → `onExitFinish` fires in a follow-up effect.
|
|
72
78
|
*/
|
|
73
|
-
export declare function useAnimatedVisibility({ isOpen, animate, elementRef, onExitFinish, }: TUseAnimatedVisibilityArgs): TUseAnimatedVisibilityResult;
|
|
79
|
+
export declare function useAnimatedVisibility({ isOpen, animate, elementRef, onEnterFinish, onExitFinish, }: TUseAnimatedVisibilityArgs): TUseAnimatedVisibilityResult;
|
|
74
80
|
export {};
|
|
@@ -105,6 +105,14 @@ type TPopoverBaseProps = {
|
|
|
105
105
|
* This does NOT control positioning. Use `useAnchorPosition` for that.
|
|
106
106
|
*/
|
|
107
107
|
placement?: TPlacementOptions;
|
|
108
|
+
/**
|
|
109
|
+
* Called after the entry animation completes (or immediately on open when
|
|
110
|
+
* there is no animation or reduced motion is active).
|
|
111
|
+
*
|
|
112
|
+
* Use this for external lifecycle coordination — e.g. notifying a manager
|
|
113
|
+
* that the show sequence is finished.
|
|
114
|
+
*/
|
|
115
|
+
onEnterFinish?: () => void;
|
|
108
116
|
/**
|
|
109
117
|
* Called after the exit animation completes (or immediately on close when
|
|
110
118
|
* there is no animation or reduced motion is active).
|
|
@@ -2,6 +2,23 @@ import React, { type MutableRefObject, type RefObject } from 'react';
|
|
|
2
2
|
import { type TAriaHasPopupValue } from '../internal/role-types';
|
|
3
3
|
import { type TPopoverCloseReason } from '../popover/types';
|
|
4
4
|
import { type TPlacementOptions } from './types';
|
|
5
|
+
/**
|
|
6
|
+
* Represents the lifecycle state of the popup.
|
|
7
|
+
*
|
|
8
|
+
* - `'closed'`: fully closed, not visible, no animation playing.
|
|
9
|
+
* - `'animating-open'`: entry animation is playing (popup is opening but not yet settled).
|
|
10
|
+
* - `'open'`: fully open, browser popover is showing, entry animation complete.
|
|
11
|
+
* - `'animating-closed'`: exit animation is playing (popup is closing but still visible).
|
|
12
|
+
*
|
|
13
|
+
* Used to derive `aria-expanded` on the trigger:
|
|
14
|
+
* - `aria-expanded` is `true` in `'animating-open'`, `'open'`, and `'animating-closed'`.
|
|
15
|
+
*
|
|
16
|
+
* This avoids screen readers announcing the popup as closed while it is
|
|
17
|
+
* still visually present on screen during animations, and prevents consumers
|
|
18
|
+
* that conditionally show/hide trigger UI based on `aria-expanded` from hiding
|
|
19
|
+
* the trigger (and thus the popup anchor) mid-animation.
|
|
20
|
+
*/
|
|
21
|
+
export type TPopupState = 'closed' | 'animating-open' | 'open' | 'animating-closed';
|
|
5
22
|
export type TPopupContextValue = {
|
|
6
23
|
/**
|
|
7
24
|
* Auto-generated ID for the popover content element (used for aria-controls).
|
|
@@ -27,13 +44,16 @@ export type TPopupContextValue = {
|
|
|
27
44
|
*/
|
|
28
45
|
popoverRef: RefObject<HTMLDivElement | null>;
|
|
29
46
|
/**
|
|
30
|
-
*
|
|
47
|
+
* The full lifecycle state of the popup.
|
|
48
|
+
*
|
|
49
|
+
* See `TPopupState` for the full state machine description.
|
|
31
50
|
*/
|
|
32
|
-
|
|
51
|
+
popupState: TPopupState;
|
|
33
52
|
/**
|
|
34
|
-
*
|
|
53
|
+
* Updates the popup lifecycle state. Called by `PopupContent` as animations
|
|
54
|
+
* begin and complete.
|
|
35
55
|
*/
|
|
36
|
-
|
|
56
|
+
setPopupState: (state: TPopupState) => void;
|
|
37
57
|
/**
|
|
38
58
|
* Consumer's onOpenChange callback, forwarded to the Popover.
|
|
39
59
|
*/
|
|
@@ -258,6 +258,18 @@ type TPopupContentBaseProps = {
|
|
|
258
258
|
* ```
|
|
259
259
|
*/
|
|
260
260
|
isOpen?: boolean;
|
|
261
|
+
/**
|
|
262
|
+
* Called after the entry animation completes (or immediately on open when
|
|
263
|
+
* there is no animation). Use this for lifecycle coordination that must
|
|
264
|
+
* happen after the popup has fully animated in.
|
|
265
|
+
*/
|
|
266
|
+
onEnterFinish?: () => void;
|
|
267
|
+
/**
|
|
268
|
+
* Called after the exit animation completes (or immediately on close when
|
|
269
|
+
* there is no animation). Use this for lifecycle coordination that must
|
|
270
|
+
* happen after the popup has fully animated out.
|
|
271
|
+
*/
|
|
272
|
+
onExitFinish?: () => void;
|
|
261
273
|
};
|
|
262
274
|
/**
|
|
263
275
|
* Popup content props.
|
|
@@ -28,7 +28,11 @@ export type TAnimationPreset = {
|
|
|
28
28
|
placement: TPlacementOptions;
|
|
29
29
|
}) => Record<string, string>;
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
31
|
+
* Entry duration in ms (for `transitionend` fallback timeout on open).
|
|
32
|
+
*/
|
|
33
|
+
enterDurationMs: number;
|
|
34
|
+
/**
|
|
35
|
+
* Exit duration in ms (for `transitionend` fallback timeout on close).
|
|
32
36
|
*/
|
|
33
37
|
exitDurationMs: number;
|
|
34
38
|
};
|
|
@@ -11,10 +11,16 @@ type TUseAnimatedVisibilityArgs = {
|
|
|
11
11
|
*/
|
|
12
12
|
animate: TAnimationPreset | false | undefined;
|
|
13
13
|
/**
|
|
14
|
-
* Ref to the DOM element that plays the exit
|
|
14
|
+
* Ref to the DOM element that plays the entry/exit transitions.
|
|
15
15
|
* Used to listen for `transitionend`.
|
|
16
16
|
*/
|
|
17
17
|
elementRef: RefObject<HTMLElement | null>;
|
|
18
|
+
/**
|
|
19
|
+
* Called after the entry animation completes (or immediately on open
|
|
20
|
+
* when there is no animation or reduced motion is active).
|
|
21
|
+
* This includes initial mount with `isOpen=true`.
|
|
22
|
+
*/
|
|
23
|
+
onEnterFinish?: () => void;
|
|
18
24
|
/**
|
|
19
25
|
* Called after the exit animation completes (or immediately on close
|
|
20
26
|
* when there is no animation or reduced motion is active).
|
|
@@ -70,5 +76,5 @@ type TUseAnimatedVisibilityResult = {
|
|
|
70
76
|
* `isOpen` goes `false` → `showChildren` → `false` synchronously during
|
|
71
77
|
* the same render → `onExitFinish` fires in a follow-up effect.
|
|
72
78
|
*/
|
|
73
|
-
export declare function useAnimatedVisibility({ isOpen, animate, elementRef, onExitFinish, }: TUseAnimatedVisibilityArgs): TUseAnimatedVisibilityResult;
|
|
79
|
+
export declare function useAnimatedVisibility({ isOpen, animate, elementRef, onEnterFinish, onExitFinish, }: TUseAnimatedVisibilityArgs): TUseAnimatedVisibilityResult;
|
|
74
80
|
export {};
|
|
@@ -105,6 +105,14 @@ type TPopoverBaseProps = {
|
|
|
105
105
|
* This does NOT control positioning. Use `useAnchorPosition` for that.
|
|
106
106
|
*/
|
|
107
107
|
placement?: TPlacementOptions;
|
|
108
|
+
/**
|
|
109
|
+
* Called after the entry animation completes (or immediately on open when
|
|
110
|
+
* there is no animation or reduced motion is active).
|
|
111
|
+
*
|
|
112
|
+
* Use this for external lifecycle coordination — e.g. notifying a manager
|
|
113
|
+
* that the show sequence is finished.
|
|
114
|
+
*/
|
|
115
|
+
onEnterFinish?: () => void;
|
|
108
116
|
/**
|
|
109
117
|
* Called after the exit animation completes (or immediately on close when
|
|
110
118
|
* there is no animation or reduced motion is active).
|
|
@@ -2,6 +2,23 @@ import React, { type MutableRefObject, type RefObject } from 'react';
|
|
|
2
2
|
import { type TAriaHasPopupValue } from '../internal/role-types';
|
|
3
3
|
import { type TPopoverCloseReason } from '../popover/types';
|
|
4
4
|
import { type TPlacementOptions } from './types';
|
|
5
|
+
/**
|
|
6
|
+
* Represents the lifecycle state of the popup.
|
|
7
|
+
*
|
|
8
|
+
* - `'closed'`: fully closed, not visible, no animation playing.
|
|
9
|
+
* - `'animating-open'`: entry animation is playing (popup is opening but not yet settled).
|
|
10
|
+
* - `'open'`: fully open, browser popover is showing, entry animation complete.
|
|
11
|
+
* - `'animating-closed'`: exit animation is playing (popup is closing but still visible).
|
|
12
|
+
*
|
|
13
|
+
* Used to derive `aria-expanded` on the trigger:
|
|
14
|
+
* - `aria-expanded` is `true` in `'animating-open'`, `'open'`, and `'animating-closed'`.
|
|
15
|
+
*
|
|
16
|
+
* This avoids screen readers announcing the popup as closed while it is
|
|
17
|
+
* still visually present on screen during animations, and prevents consumers
|
|
18
|
+
* that conditionally show/hide trigger UI based on `aria-expanded` from hiding
|
|
19
|
+
* the trigger (and thus the popup anchor) mid-animation.
|
|
20
|
+
*/
|
|
21
|
+
export type TPopupState = 'closed' | 'animating-open' | 'open' | 'animating-closed';
|
|
5
22
|
export type TPopupContextValue = {
|
|
6
23
|
/**
|
|
7
24
|
* Auto-generated ID for the popover content element (used for aria-controls).
|
|
@@ -27,13 +44,16 @@ export type TPopupContextValue = {
|
|
|
27
44
|
*/
|
|
28
45
|
popoverRef: RefObject<HTMLDivElement | null>;
|
|
29
46
|
/**
|
|
30
|
-
*
|
|
47
|
+
* The full lifecycle state of the popup.
|
|
48
|
+
*
|
|
49
|
+
* See `TPopupState` for the full state machine description.
|
|
31
50
|
*/
|
|
32
|
-
|
|
51
|
+
popupState: TPopupState;
|
|
33
52
|
/**
|
|
34
|
-
*
|
|
53
|
+
* Updates the popup lifecycle state. Called by `PopupContent` as animations
|
|
54
|
+
* begin and complete.
|
|
35
55
|
*/
|
|
36
|
-
|
|
56
|
+
setPopupState: (state: TPopupState) => void;
|
|
37
57
|
/**
|
|
38
58
|
* Consumer's onOpenChange callback, forwarded to the Popover.
|
|
39
59
|
*/
|
|
@@ -258,6 +258,18 @@ type TPopupContentBaseProps = {
|
|
|
258
258
|
* ```
|
|
259
259
|
*/
|
|
260
260
|
isOpen?: boolean;
|
|
261
|
+
/**
|
|
262
|
+
* Called after the entry animation completes (or immediately on open when
|
|
263
|
+
* there is no animation). Use this for lifecycle coordination that must
|
|
264
|
+
* happen after the popup has fully animated in.
|
|
265
|
+
*/
|
|
266
|
+
onEnterFinish?: () => void;
|
|
267
|
+
/**
|
|
268
|
+
* Called after the exit animation completes (or immediately on close when
|
|
269
|
+
* there is no animation). Use this for lifecycle coordination that must
|
|
270
|
+
* happen after the popup has fully animated out.
|
|
271
|
+
*/
|
|
272
|
+
onExitFinish?: () => void;
|
|
261
273
|
};
|
|
262
274
|
/**
|
|
263
275
|
* Popup content props.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/top-layer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
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",
|