Skip to content

Commit 246f968

Browse files
committed
feat(ui): support two-way binding
1 parent bfbf6e0 commit 246f968

23 files changed

Lines changed: 136 additions & 137 deletions

packages/site/src/app/components/route/DemoBox.tsx

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,6 @@ export function AppDemoBox(props: AppDemoBoxProps) {
3535
setCopycode(true);
3636
}, [setCopycode, tsxSource]);
3737

38-
const [copyVisible, setCopyVisible] = useImmer(false);
39-
const handleCopyTrige = useCallback(
40-
(v) => {
41-
setCopyVisible(v);
42-
},
43-
[setCopyVisible]
44-
);
45-
4638
const afterCopyTrige = useCallback(
4739
(v) => {
4840
if (!v) {
@@ -100,12 +92,7 @@ export function AppDemoBox(props: AppDemoBoxProps) {
10092
<path d="M848 359.3H627.7L825.8 109c4.1-5.3.4-13-6.3-13H436c-2.8 0-5.5 1.5-6.9 4L170 547.5c-3.1 5.3.7 12 6.9 12h174.4l-89.4 357.6c-1.9 7.8 7.5 13.3 13.3 7.7L853.5 373c5.2-4.9 1.7-13.7-5.5-13.7zM378.2 732.5l60.3-241H281.1l189.6-327.4h224.6L487 427.4h211L378.2 732.5z"></path>
10193
</DIcon>
10294
</DTooltip>
103-
<DTooltip
104-
dVisible={copyVisible}
105-
dTitle={copyCode ? t('Copied!') : t('Copy code')}
106-
onTrigger={handleCopyTrige}
107-
afterVisibleChange={afterCopyTrige}
108-
>
95+
<DTooltip dTitle={copyCode ? t('Copied!') : t('Copy code')} afterVisibleChange={afterCopyTrige}>
10996
<DIcon className="icon-button" dSize={18} onClick={handleCopyClick}>
11097
{copyCode ? (
11198
<path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 00-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z"></path>

packages/site/src/app/components/sidebar/Sidebar.tsx

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useEffect } from 'react';
1+
import { useEffect } from 'react';
22
import { useTranslation } from 'react-i18next';
33
import { useNavigate } from 'react-router-dom';
44

@@ -12,14 +12,7 @@ export function AppSidebar() {
1212
const { t, i18n } = useTranslation();
1313
const navigate = useNavigate();
1414

15-
const [activeId, setActiveId] = useImmer<string | undefined>(undefined);
16-
17-
const handleActiveChange = useCallback(
18-
(id) => {
19-
setActiveId(id);
20-
},
21-
[setActiveId]
22-
);
15+
const [activeId, setActiveId] = useImmer<string | null>(null);
2316

2417
useEffect(() => {
2518
if (window.location.href.includes(String.raw`/components/`)) {
@@ -36,7 +29,7 @@ export function AppSidebar() {
3629

3730
return (
3831
<nav className="app-sidebar">
39-
<DMenu dActive={activeId} onActiveChange={handleActiveChange}>
32+
<DMenu dActive={[activeId, setActiveId]}>
4033
{menu.map((group) => (
4134
<DMenuGroup key={group.title} dId={group.title} dTitle={t(`menu-group.${group.title}`)}>
4235
{group.children.map((child) => (

packages/ui/src/components/_popup/Popup.tsx

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import type { DElementSelector } from '../../hooks/element-ref';
2+
import type { Updater } from '../../hooks/immer';
23
import type { DPlacement } from '../../utils/position';
34
import type { DTransitionStateList, DTransitionRef } from '../_transition';
45

56
import { isUndefined } from 'lodash';
67
import React, { useCallback, useEffect, useMemo, useImperativeHandle, useRef } from 'react';
78
import ReactDOM, { flushSync } from 'react-dom';
89

9-
import { useDPrefixConfig, useAsync, useRefSelector, useManualOrAutoState, useId, useImmer, useRefCallback } from '../../hooks';
10+
import { useDPrefixConfig, useAsync, useRefSelector, useId, useImmer, useRefCallback } from '../../hooks';
1011
import { getClassName, globalMaxIndexManager, getPopupPlacementStyle } from '../../utils';
1112
import { DTransition } from '../_transition';
1213

@@ -25,7 +26,7 @@ export interface DPopupRef {
2526
}
2627

2728
export interface DPopupProps extends React.HTMLAttributes<HTMLDivElement> {
28-
dVisible?: boolean;
29+
dVisible?: [boolean, Updater<boolean>?];
2930
dPopupContent: React.ReactNode;
3031
dTriggerRender?: (props: DTriggerRenderProps) => React.ReactNode;
3132
dTriggerEl?: HTMLElement | null;
@@ -91,7 +92,18 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
9192
const [zIndex, setZIndex] = useImmer(1000);
9293
const id = useId();
9394

94-
const [visible, dispatchVisible] = useManualOrAutoState(false, dVisible, onTrigger);
95+
const setVisible = dVisible?.[1];
96+
const [autoVisible, setAutoVisible] = useImmer(false);
97+
const visible = dVisible?.[0] ?? autoVisible;
98+
const changeVisible = useCallback(
99+
(val?: boolean) => {
100+
const _val = isUndefined(val) ? !visible : val;
101+
setVisible?.(_val);
102+
setAutoVisible(_val);
103+
onTrigger?.(_val);
104+
},
105+
[onTrigger, setAutoVisible, setVisible, visible]
106+
);
95107

96108
const [autoPlacement, setAutoPlacement] = useImmer<DPlacement>(dPlacement);
97109

@@ -249,11 +261,11 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
249261
dataRef.current.clearTid && dataRef.current.clearTid();
250262
dataRef.current.clearTid = asyncCapture.setTimeout(() => {
251263
dataRef.current.clearTid = null;
252-
dispatchVisible({ value: true });
264+
changeVisible(true);
253265
}, dMouseEnterDelay);
254266
}
255267
},
256-
[asyncCapture, dMouseEnterDelay, dTrigger, onMouseEnter, dispatchVisible]
268+
[onMouseEnter, dTrigger, asyncCapture, dMouseEnterDelay, changeVisible]
257269
);
258270

259271
const handleMouseLeave = useCallback(
@@ -264,11 +276,11 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
264276
dataRef.current.clearTid && dataRef.current.clearTid();
265277
dataRef.current.clearTid = asyncCapture.setTimeout(() => {
266278
dataRef.current.clearTid = null;
267-
dispatchVisible({ value: false });
279+
changeVisible(false);
268280
}, dMouseLeaveDelay);
269281
}
270282
},
271-
[asyncCapture, dMouseLeaveDelay, dTrigger, onMouseLeave, dispatchVisible]
283+
[onMouseLeave, dTrigger, asyncCapture, dMouseLeaveDelay, changeVisible]
272284
);
273285

274286
const handleFocus = useCallback(
@@ -277,21 +289,21 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
277289

278290
if (dTrigger === 'focus') {
279291
dataRef.current.clearTid && dataRef.current.clearTid();
280-
dispatchVisible({ value: true });
292+
changeVisible(true);
281293
}
282294
},
283-
[dTrigger, onFocus, dispatchVisible]
295+
[onFocus, dTrigger, changeVisible]
284296
);
285297

286298
const handleBlur = useCallback(
287299
(e) => {
288300
onBlur?.(e);
289301

290302
if (dTrigger === 'focus') {
291-
dataRef.current.clearTid = asyncCapture.setTimeout(() => dispatchVisible({ value: false }), 20);
303+
dataRef.current.clearTid = asyncCapture.setTimeout(() => changeVisible(false), 20);
292304
}
293305
},
294-
[asyncCapture, dTrigger, onBlur, dispatchVisible]
306+
[onBlur, dTrigger, asyncCapture, changeVisible]
295307
);
296308

297309
const handleClick = useCallback(
@@ -300,15 +312,15 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
300312

301313
if (dTrigger === 'click') {
302314
dataRef.current.clearTid && dataRef.current.clearTid();
303-
dispatchVisible({ value: true });
315+
changeVisible(true);
304316
}
305317
},
306-
[dTrigger, onClick, dispatchVisible]
318+
[onClick, dTrigger, changeVisible]
307319
);
308320

309321
//#region DidUpdate
310322
useEffect(() => {
311-
if (dVisible) {
323+
if (visible) {
312324
if (isUndefined(dZIndex)) {
313325
if (isUndefined(dContainer)) {
314326
const [key, maxZIndex] = globalMaxIndexManager.getMaxIndex();
@@ -323,7 +335,7 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
323335
setZIndex(dZIndex);
324336
}
325337
}
326-
}, [dVisible, dContainer, dZIndex, setZIndex]);
338+
}, [dContainer, dZIndex, setZIndex, visible]);
327339

328340
useEffect(() => {
329341
if (!isUndefined(dTriggerEl)) {
@@ -335,7 +347,7 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
335347
dataRef.current.clearTid && dataRef.current.clearTid();
336348
dataRef.current.clearTid = asyncCapture.setTimeout(() => {
337349
dataRef.current.clearTid = null;
338-
flushSync(() => dispatchVisible({ value: true }));
350+
flushSync(() => changeVisible(true));
339351
}, dMouseEnterDelay);
340352
},
341353
});
@@ -344,7 +356,7 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
344356
dataRef.current.clearTid && dataRef.current.clearTid();
345357
dataRef.current.clearTid = asyncCapture.setTimeout(() => {
346358
dataRef.current.clearTid = null;
347-
flushSync(() => dispatchVisible({ value: false }));
359+
flushSync(() => changeVisible(false));
348360
}, dMouseLeaveDelay);
349361
},
350362
});
@@ -354,12 +366,12 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
354366
asyncGroup.fromEvent(triggerRef.current, 'focus').subscribe({
355367
next: () => {
356368
dataRef.current.clearTid && dataRef.current.clearTid();
357-
flushSync(() => dispatchVisible({ value: true }));
369+
flushSync(() => changeVisible(true));
358370
},
359371
});
360372
asyncGroup.fromEvent(triggerRef.current, 'blur').subscribe({
361373
next: () => {
362-
dataRef.current.clearTid = asyncCapture.setTimeout(() => flushSync(() => dispatchVisible({ value: false })), 20);
374+
dataRef.current.clearTid = asyncCapture.setTimeout(() => flushSync(() => changeVisible(false)), 20);
363375
},
364376
});
365377
}
@@ -368,7 +380,7 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
368380
asyncGroup.fromEvent(triggerRef.current, 'click').subscribe({
369381
next: () => {
370382
dataRef.current.clearTid && dataRef.current.clearTid();
371-
flushSync(() => dispatchVisible({ reverse: true }));
383+
flushSync(() => changeVisible());
372384
},
373385
});
374386
}
@@ -378,7 +390,7 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
378390
asyncCapture.deleteGroup(asyncId);
379391
};
380392
}
381-
}, [asyncCapture, dMouseEnterDelay, dMouseLeaveDelay, dTrigger, dTriggerEl, dispatchVisible, triggerRef]);
393+
}, [asyncCapture, changeVisible, dMouseEnterDelay, dMouseLeaveDelay, dTrigger, dTriggerEl, triggerRef]);
382394

383395
useEffect(() => {
384396
const [asyncGroup, asyncId] = asyncCapture.createGroup();
@@ -387,7 +399,7 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
387399
asyncGroup.fromEvent(window, 'click', { capture: true }).subscribe({
388400
next: () => {
389401
dataRef.current.clearTid = asyncGroup.setTimeout(() => {
390-
flushSync(() => dispatchVisible({ value: false }));
402+
flushSync(() => changeVisible(false));
391403
}, 20);
392404
},
393405
});
@@ -396,7 +408,7 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
396408
return () => {
397409
asyncCapture.deleteGroup(asyncId);
398410
};
399-
}, [asyncCapture, dTrigger, dispatchVisible]);
411+
}, [asyncCapture, changeVisible, dTrigger]);
400412

401413
useEffect(() => {
402414
const [asyncGroup, asyncId] = asyncCapture.createGroup();
@@ -438,35 +450,35 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
438450
dataRef.current.clearTid && dataRef.current.clearTid();
439451
dataRef.current.clearTid = asyncCapture.setTimeout(() => {
440452
dataRef.current.clearTid = null;
441-
dispatchVisible({ value: true });
453+
changeVisible(true);
442454
}, dMouseEnterDelay);
443455
};
444456
_triggerRenderProps.onMouseLeave = () => {
445457
dataRef.current.clearTid && dataRef.current.clearTid();
446458
dataRef.current.clearTid = asyncCapture.setTimeout(() => {
447459
dataRef.current.clearTid = null;
448-
dispatchVisible({ value: false });
460+
changeVisible(false);
449461
}, dMouseLeaveDelay);
450462
};
451463
}
452464
if (dTrigger === 'focus') {
453465
_triggerRenderProps.onFocus = () => {
454466
dataRef.current.clearTid && dataRef.current.clearTid();
455-
dispatchVisible({ value: true });
467+
changeVisible(true);
456468
};
457469
_triggerRenderProps.onBlur = () => {
458-
dataRef.current.clearTid = asyncCapture.setTimeout(() => dispatchVisible({ value: false }), 20);
470+
dataRef.current.clearTid = asyncCapture.setTimeout(() => changeVisible(false), 20);
459471
};
460472
}
461473
if (dTrigger === 'click') {
462474
_triggerRenderProps.onClick = () => {
463475
dataRef.current.clearTid && dataRef.current.clearTid();
464-
dispatchVisible({ reverse: true });
476+
changeVisible();
465477
};
466478
}
467479

468480
return _triggerRenderProps;
469-
}, [asyncCapture, dMouseEnterDelay, dMouseLeaveDelay, dPrefix, dTrigger, dispatchVisible, id]);
481+
}, [asyncCapture, changeVisible, dMouseEnterDelay, dMouseLeaveDelay, dPrefix, dTrigger, id]);
470482

471483
return (
472484
<>

0 commit comments

Comments
 (0)