Skip to content

Commit a0319b9

Browse files
committed
chore: motion pending
1 parent a5c946d commit a0319b9

2 files changed

Lines changed: 43 additions & 5 deletions

File tree

src/UniqueProvider/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export interface UniqueProviderProps {
1818
}
1919

2020
const UniqueProvider = ({ children }: UniqueProviderProps) => {
21-
const [trigger, open, options] = useTargetState();
21+
const [trigger, open, options, onTargetVisibleChanged] = useTargetState();
2222

2323
// =========================== Popup ============================
2424
const [popupEle, setPopupEle] = React.useState<HTMLDivElement>(null);
@@ -53,6 +53,8 @@ const UniqueProvider = ({ children }: UniqueProviderProps) => {
5353

5454
// 动画完成后的回调
5555
const onVisibleChanged = useEvent((visible: boolean) => {
56+
// 调用 useTargetState 的回调来处理动画状态
57+
onTargetVisibleChanged(visible);
5658
// if (!visible) {
5759
// setTarget(null);
5860
// setCurrentNode(null);
Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import React from 'react';
2-
import type { TriggerProps } from '..';
32
import { useEvent } from '@rc-component/util';
43
import type { UniqueShowOptions } from '../context';
54

@@ -10,24 +9,61 @@ import type { UniqueShowOptions } from '../context';
109
* 3. When `target` change to another one:
1110
* a. We wait motion finish of previous popup.
1211
* b. Then we set new target and show the popup.
12+
* 4. During appear/enter animation, cache new options and apply after animation completes.
1313
*/
1414
export default function useTargetState(): [
1515
trigger: (options: UniqueShowOptions | false) => void,
1616
open: boolean,
1717
/* Will always cache last which is not null */
1818
cacheOptions: UniqueShowOptions | null,
19+
onVisibleChanged: (visible: boolean) => void,
1920
] {
2021
const [options, setOptions] = React.useState<UniqueShowOptions | null>(null);
2122
const [open, setOpen] = React.useState(false);
23+
const [isAnimating, setIsAnimating] = React.useState(false);
24+
const pendingOptionsRef = React.useRef<UniqueShowOptions | null>(null);
2225

2326
const trigger = useEvent((nextOptions: UniqueShowOptions | false) => {
2427
if (nextOptions === false) {
28+
// 隐藏时清除待处理的选项
29+
pendingOptionsRef.current = null;
2530
setOpen(false);
2631
} else {
27-
setOpen(true);
28-
setOptions(nextOptions);
32+
if (isAnimating && open) {
33+
// 如果正在动画中(appear 或 enter),缓存新的 options
34+
pendingOptionsRef.current = nextOptions;
35+
} else {
36+
// 没有动画或者是首次显示,直接应用
37+
setOpen(true);
38+
setOptions(nextOptions);
39+
pendingOptionsRef.current = null;
40+
}
2941
}
3042
});
3143

32-
return [trigger, open, options];
44+
const onVisibleChanged = useEvent((visible: boolean) => {
45+
if (visible) {
46+
// 动画进入完成,检查是否有待处理的选项
47+
setIsAnimating(false);
48+
if (pendingOptionsRef.current) {
49+
const pendingOptions = pendingOptionsRef.current;
50+
pendingOptionsRef.current = null;
51+
// 应用待处理的选项
52+
setOptions(pendingOptions);
53+
}
54+
} else {
55+
// 动画离开完成
56+
setIsAnimating(false);
57+
pendingOptionsRef.current = null;
58+
}
59+
});
60+
61+
// 当开始显示时标记为动画中
62+
React.useEffect(() => {
63+
if (open && options) {
64+
setIsAnimating(true);
65+
}
66+
}, [open, options]);
67+
68+
return [trigger, open, options, onVisibleChanged];
3369
}

0 commit comments

Comments
 (0)