Skip to content

Commit a5c946d

Browse files
committed
chore: align it
1 parent 12c41b6 commit a5c946d

3 files changed

Lines changed: 73 additions & 21 deletions

File tree

docs/examples/two-buttons.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import Trigger, { UniqueProvider } from '@rc-component/trigger';
22
import React, { useState } from 'react';
33
import '../../assets/index.less';
44

5+
const LEAVE_DELAY = 0.1;
6+
57
const builtinPlacements = {
68
left: {
79
points: ['cr', 'cl'],
@@ -38,7 +40,7 @@ const MovingPopupDemo = () => {
3840
</div>
3941
<div style={{ display: 'flex', gap: 20 }}>
4042
<Trigger
41-
mouseLeaveDelay={0.3}
43+
mouseLeaveDelay={LEAVE_DELAY}
4244
action={['hover']}
4345
popupPlacement="top"
4446
builtinPlacements={builtinPlacements}
@@ -57,7 +59,7 @@ const MovingPopupDemo = () => {
5759
</Trigger>
5860

5961
<Trigger
60-
mouseLeaveDelay={0.3}
62+
mouseLeaveDelay={LEAVE_DELAY}
6163
action={['hover']}
6264
popupPlacement="top"
6365
builtinPlacements={builtinPlacements}

src/UniqueProvider/index.tsx

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,44 +10,54 @@ import useDelay from '../hooks/useDelay';
1010
import useAlign from '../hooks/useAlign';
1111
import Popup from '../Popup';
1212
import { useEvent } from '@rc-component/util';
13+
import useTargetState from './useTargetState';
14+
import { isDOM } from '@rc-component/util/lib/Dom/findDOMNode';
1315

1416
export interface UniqueProviderProps {
1517
children: React.ReactNode;
1618
}
1719

1820
const UniqueProvider = ({ children }: UniqueProviderProps) => {
19-
const [open, setOpen] = React.useState(false);
20-
const [target, setTarget] = React.useState<HTMLElement | null>(null);
21-
const [currentNode, setCurrentNode] = React.useState<React.ReactNode>(null);
22-
const [options, setOptions] = React.useState<UniqueShowOptions | null>(null);
21+
const [trigger, open, options] = useTargetState();
22+
23+
// =========================== Popup ============================
2324
const [popupEle, setPopupEle] = React.useState<HTMLDivElement>(null);
2425

26+
// Used for forwardRef popup. Not use internal
27+
const externalPopupRef = React.useRef<HTMLDivElement>(null);
28+
29+
const setPopupRef = useEvent((node: HTMLDivElement) => {
30+
externalPopupRef.current = node;
31+
32+
if (isDOM(node) && popupEle !== node) {
33+
setPopupEle(node);
34+
}
35+
36+
});
37+
2538
// ========================== Register ==========================
2639
const delayInvoke = useDelay();
2740

2841
const show = (showOptions: UniqueShowOptions) => {
2942
delayInvoke(() => {
30-
setOpen(true);
31-
setCurrentNode(showOptions.popup);
32-
setTarget(showOptions.target);
33-
setOptions(showOptions);
43+
trigger(showOptions);
3444
}, showOptions.delay);
3545
};
3646

3747
const hide = (delay: number) => {
3848
delayInvoke(() => {
39-
setOpen(false);
49+
trigger(false);
4050
// 不要立即清空 target, currentNode, options,等动画结束后再清空
4151
}, delay);
4252
};
4353

4454
// 动画完成后的回调
4555
const onVisibleChanged = useEvent((visible: boolean) => {
46-
if (!visible) {
47-
setTarget(null);
48-
setCurrentNode(null);
49-
setOptions(null);
50-
}
56+
// if (!visible) {
57+
// setTarget(null);
58+
// setCurrentNode(null);
59+
// setOptions(null);
60+
// }
5161
});
5262

5363
// =========================== Align ============================
@@ -67,7 +77,7 @@ const UniqueProvider = ({ children }: UniqueProviderProps) => {
6777
] = useAlign(
6878
open,
6979
popupEle,
70-
target,
80+
options?.target,
7181
options?.popupPlacement,
7282
options?.builtinPlacements || {},
7383
options?.popupAlign,
@@ -83,6 +93,13 @@ const UniqueProvider = ({ children }: UniqueProviderProps) => {
8393
[],
8494
);
8595

96+
// =========================== Motion ===========================
97+
const onPrepare = useEvent(() => {
98+
onAlign();
99+
100+
return Promise.resolve();
101+
});
102+
86103
// ======================== Trigger Context =====================
87104
const subPopupElements = React.useRef<Record<string, HTMLElement>>({});
88105
const parentContext = React.useContext(TriggerContext);
@@ -104,13 +121,13 @@ const UniqueProvider = ({ children }: UniqueProviderProps) => {
104121
{options && (
105122
<TriggerContext.Provider value={triggerContextValue}>
106123
<Popup
107-
ref={setPopupEle}
124+
ref={setPopupRef}
108125
portal={Portal}
109126
prefixCls={options.prefixCls}
110-
popup={currentNode}
127+
popup={options.popup}
111128
className={options.popupClassName}
112129
style={options.popupStyle}
113-
target={target}
130+
target={options.target}
114131
open={open}
115132
keepDom={true}
116133
fresh={true}
@@ -122,7 +139,7 @@ const UniqueProvider = ({ children }: UniqueProviderProps) => {
122139
offsetR={offsetR}
123140
offsetB={offsetB}
124141
onAlign={onAlign}
125-
onPrepare={() => Promise.resolve()}
142+
onPrepare={onPrepare}
126143
arrowPos={{
127144
x: arrowX,
128145
y: arrowY,
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import React from 'react';
2+
import type { TriggerProps } from '..';
3+
import { useEvent } from '@rc-component/util';
4+
import type { UniqueShowOptions } from '../context';
5+
6+
/**
7+
* Control the state of popup bind target:
8+
* 1. When set `target`. Do show the popup.
9+
* 2. When `target` is removed. Do hide the popup.
10+
* 3. When `target` change to another one:
11+
* a. We wait motion finish of previous popup.
12+
* b. Then we set new target and show the popup.
13+
*/
14+
export default function useTargetState(): [
15+
trigger: (options: UniqueShowOptions | false) => void,
16+
open: boolean,
17+
/* Will always cache last which is not null */
18+
cacheOptions: UniqueShowOptions | null,
19+
] {
20+
const [options, setOptions] = React.useState<UniqueShowOptions | null>(null);
21+
const [open, setOpen] = React.useState(false);
22+
23+
const trigger = useEvent((nextOptions: UniqueShowOptions | false) => {
24+
if (nextOptions === false) {
25+
setOpen(false);
26+
} else {
27+
setOpen(true);
28+
setOptions(nextOptions);
29+
}
30+
});
31+
32+
return [trigger, open, options];
33+
}

0 commit comments

Comments
 (0)