Skip to content

Commit

Permalink
feat(ui): support two-way binding
Browse files Browse the repository at this point in the history
  • Loading branch information
xiejay97 committed Nov 29, 2021
1 parent bfbf6e0 commit 246f968
Show file tree
Hide file tree
Showing 23 changed files with 136 additions and 137 deletions.
15 changes: 1 addition & 14 deletions packages/site/src/app/components/route/DemoBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,6 @@ export function AppDemoBox(props: AppDemoBoxProps) {
setCopycode(true);
}, [setCopycode, tsxSource]);

const [copyVisible, setCopyVisible] = useImmer(false);
const handleCopyTrige = useCallback(
(v) => {
setCopyVisible(v);
},
[setCopyVisible]
);

const afterCopyTrige = useCallback(
(v) => {
if (!v) {
Expand Down Expand Up @@ -100,12 +92,7 @@ export function AppDemoBox(props: AppDemoBoxProps) {
<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>
</DIcon>
</DTooltip>
<DTooltip
dVisible={copyVisible}
dTitle={copyCode ? t('Copied!') : t('Copy code')}
onTrigger={handleCopyTrige}
afterVisibleChange={afterCopyTrige}
>
<DTooltip dTitle={copyCode ? t('Copied!') : t('Copy code')} afterVisibleChange={afterCopyTrige}>
<DIcon className="icon-button" dSize={18} onClick={handleCopyClick}>
{copyCode ? (
<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>
Expand Down
13 changes: 3 additions & 10 deletions packages/site/src/app/components/sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useEffect } from 'react';
import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';

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

const [activeId, setActiveId] = useImmer<string | undefined>(undefined);

const handleActiveChange = useCallback(
(id) => {
setActiveId(id);
},
[setActiveId]
);
const [activeId, setActiveId] = useImmer<string | null>(null);

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

return (
<nav className="app-sidebar">
<DMenu dActive={activeId} onActiveChange={handleActiveChange}>
<DMenu dActive={[activeId, setActiveId]}>
{menu.map((group) => (
<DMenuGroup key={group.title} dId={group.title} dTitle={t(`menu-group.${group.title}`)}>
{group.children.map((child) => (
Expand Down
70 changes: 41 additions & 29 deletions packages/ui/src/components/_popup/Popup.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import type { DElementSelector } from '../../hooks/element-ref';
import type { Updater } from '../../hooks/immer';
import type { DPlacement } from '../../utils/position';
import type { DTransitionStateList, DTransitionRef } from '../_transition';

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

import { useDPrefixConfig, useAsync, useRefSelector, useManualOrAutoState, useId, useImmer, useRefCallback } from '../../hooks';
import { useDPrefixConfig, useAsync, useRefSelector, useId, useImmer, useRefCallback } from '../../hooks';
import { getClassName, globalMaxIndexManager, getPopupPlacementStyle } from '../../utils';
import { DTransition } from '../_transition';

Expand All @@ -25,7 +26,7 @@ export interface DPopupRef {
}

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

const [visible, dispatchVisible] = useManualOrAutoState(false, dVisible, onTrigger);
const setVisible = dVisible?.[1];
const [autoVisible, setAutoVisible] = useImmer(false);
const visible = dVisible?.[0] ?? autoVisible;
const changeVisible = useCallback(
(val?: boolean) => {
const _val = isUndefined(val) ? !visible : val;
setVisible?.(_val);
setAutoVisible(_val);
onTrigger?.(_val);
},
[onTrigger, setAutoVisible, setVisible, visible]
);

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

Expand Down Expand Up @@ -249,11 +261,11 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
dataRef.current.clearTid && dataRef.current.clearTid();
dataRef.current.clearTid = asyncCapture.setTimeout(() => {
dataRef.current.clearTid = null;
dispatchVisible({ value: true });
changeVisible(true);
}, dMouseEnterDelay);
}
},
[asyncCapture, dMouseEnterDelay, dTrigger, onMouseEnter, dispatchVisible]
[onMouseEnter, dTrigger, asyncCapture, dMouseEnterDelay, changeVisible]
);

const handleMouseLeave = useCallback(
Expand All @@ -264,11 +276,11 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
dataRef.current.clearTid && dataRef.current.clearTid();
dataRef.current.clearTid = asyncCapture.setTimeout(() => {
dataRef.current.clearTid = null;
dispatchVisible({ value: false });
changeVisible(false);
}, dMouseLeaveDelay);
}
},
[asyncCapture, dMouseLeaveDelay, dTrigger, onMouseLeave, dispatchVisible]
[onMouseLeave, dTrigger, asyncCapture, dMouseLeaveDelay, changeVisible]
);

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

if (dTrigger === 'focus') {
dataRef.current.clearTid && dataRef.current.clearTid();
dispatchVisible({ value: true });
changeVisible(true);
}
},
[dTrigger, onFocus, dispatchVisible]
[onFocus, dTrigger, changeVisible]
);

const handleBlur = useCallback(
(e) => {
onBlur?.(e);

if (dTrigger === 'focus') {
dataRef.current.clearTid = asyncCapture.setTimeout(() => dispatchVisible({ value: false }), 20);
dataRef.current.clearTid = asyncCapture.setTimeout(() => changeVisible(false), 20);
}
},
[asyncCapture, dTrigger, onBlur, dispatchVisible]
[onBlur, dTrigger, asyncCapture, changeVisible]
);

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

if (dTrigger === 'click') {
dataRef.current.clearTid && dataRef.current.clearTid();
dispatchVisible({ value: true });
changeVisible(true);
}
},
[dTrigger, onClick, dispatchVisible]
[onClick, dTrigger, changeVisible]
);

//#region DidUpdate
useEffect(() => {
if (dVisible) {
if (visible) {
if (isUndefined(dZIndex)) {
if (isUndefined(dContainer)) {
const [key, maxZIndex] = globalMaxIndexManager.getMaxIndex();
Expand All @@ -323,7 +335,7 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
setZIndex(dZIndex);
}
}
}, [dVisible, dContainer, dZIndex, setZIndex]);
}, [dContainer, dZIndex, setZIndex, visible]);

useEffect(() => {
if (!isUndefined(dTriggerEl)) {
Expand All @@ -335,7 +347,7 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
dataRef.current.clearTid && dataRef.current.clearTid();
dataRef.current.clearTid = asyncCapture.setTimeout(() => {
dataRef.current.clearTid = null;
flushSync(() => dispatchVisible({ value: true }));
flushSync(() => changeVisible(true));
}, dMouseEnterDelay);
},
});
Expand All @@ -344,7 +356,7 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
dataRef.current.clearTid && dataRef.current.clearTid();
dataRef.current.clearTid = asyncCapture.setTimeout(() => {
dataRef.current.clearTid = null;
flushSync(() => dispatchVisible({ value: false }));
flushSync(() => changeVisible(false));
}, dMouseLeaveDelay);
},
});
Expand All @@ -354,12 +366,12 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
asyncGroup.fromEvent(triggerRef.current, 'focus').subscribe({
next: () => {
dataRef.current.clearTid && dataRef.current.clearTid();
flushSync(() => dispatchVisible({ value: true }));
flushSync(() => changeVisible(true));
},
});
asyncGroup.fromEvent(triggerRef.current, 'blur').subscribe({
next: () => {
dataRef.current.clearTid = asyncCapture.setTimeout(() => flushSync(() => dispatchVisible({ value: false })), 20);
dataRef.current.clearTid = asyncCapture.setTimeout(() => flushSync(() => changeVisible(false)), 20);
},
});
}
Expand All @@ -368,7 +380,7 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
asyncGroup.fromEvent(triggerRef.current, 'click').subscribe({
next: () => {
dataRef.current.clearTid && dataRef.current.clearTid();
flushSync(() => dispatchVisible({ reverse: true }));
flushSync(() => changeVisible());
},
});
}
Expand All @@ -378,7 +390,7 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
asyncCapture.deleteGroup(asyncId);
};
}
}, [asyncCapture, dMouseEnterDelay, dMouseLeaveDelay, dTrigger, dTriggerEl, dispatchVisible, triggerRef]);
}, [asyncCapture, changeVisible, dMouseEnterDelay, dMouseLeaveDelay, dTrigger, dTriggerEl, triggerRef]);

useEffect(() => {
const [asyncGroup, asyncId] = asyncCapture.createGroup();
Expand All @@ -387,7 +399,7 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
asyncGroup.fromEvent(window, 'click', { capture: true }).subscribe({
next: () => {
dataRef.current.clearTid = asyncGroup.setTimeout(() => {
flushSync(() => dispatchVisible({ value: false }));
flushSync(() => changeVisible(false));
}, 20);
},
});
Expand All @@ -396,7 +408,7 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
return () => {
asyncCapture.deleteGroup(asyncId);
};
}, [asyncCapture, dTrigger, dispatchVisible]);
}, [asyncCapture, changeVisible, dTrigger]);

useEffect(() => {
const [asyncGroup, asyncId] = asyncCapture.createGroup();
Expand Down Expand Up @@ -438,35 +450,35 @@ export const DPopup = React.forwardRef<DPopupRef, DPopupProps>((props, ref) => {
dataRef.current.clearTid && dataRef.current.clearTid();
dataRef.current.clearTid = asyncCapture.setTimeout(() => {
dataRef.current.clearTid = null;
dispatchVisible({ value: true });
changeVisible(true);
}, dMouseEnterDelay);
};
_triggerRenderProps.onMouseLeave = () => {
dataRef.current.clearTid && dataRef.current.clearTid();
dataRef.current.clearTid = asyncCapture.setTimeout(() => {
dataRef.current.clearTid = null;
dispatchVisible({ value: false });
changeVisible(false);
}, dMouseLeaveDelay);
};
}
if (dTrigger === 'focus') {
_triggerRenderProps.onFocus = () => {
dataRef.current.clearTid && dataRef.current.clearTid();
dispatchVisible({ value: true });
changeVisible(true);
};
_triggerRenderProps.onBlur = () => {
dataRef.current.clearTid = asyncCapture.setTimeout(() => dispatchVisible({ value: false }), 20);
dataRef.current.clearTid = asyncCapture.setTimeout(() => changeVisible(false), 20);
};
}
if (dTrigger === 'click') {
_triggerRenderProps.onClick = () => {
dataRef.current.clearTid && dataRef.current.clearTid();
dispatchVisible({ reverse: true });
changeVisible();
};
}

return _triggerRenderProps;
}, [asyncCapture, dMouseEnterDelay, dMouseLeaveDelay, dPrefix, dTrigger, dispatchVisible, id]);
}, [asyncCapture, changeVisible, dMouseEnterDelay, dMouseLeaveDelay, dPrefix, dTrigger, id]);

return (
<>
Expand Down

0 comments on commit 246f968

Please sign in to comment.