Skip to content

Commit

Permalink
feat(ui): add loading component
Browse files Browse the repository at this point in the history
  • Loading branch information
xiejay97 committed Jul 29, 2022
1 parent 8c18cdc commit 2f55334
Show file tree
Hide file tree
Showing 24 changed files with 361 additions and 44 deletions.
2 changes: 1 addition & 1 deletion packages/site/src/app/components/layout/header/Header.scss
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
&:hover,
&:focus {
color: var(--d-color-primary-lighter);
background-color: var(--d-background-color-hover-primary);
background-color: var(--d-background-color-primary-hover);
}

&:active {
Expand Down
31 changes: 12 additions & 19 deletions packages/ui/src/components/_transition/Transition.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,29 +85,22 @@ export function DTransition(props: DTransitionProps): JSX.Element | null {
onEnterRendered?.();
}

setNextState(isEnter ? T_ENTERING : T_LEAVING);
dataRef.current.clearTid = asyncCapture.requestAnimationFrame(() => {
const nextState = isEnter ? T_ENTERING : T_LEAVING;
updateTransitionState(nextState);

const isEntering = nextState === T_ENTERING;
const endState = isEntering ? T_ENTERED : T_LEAVED;
const during = isNumber(dDuring) ? dDuring : isEntering ? dDuring.enter : dDuring.leave;
dataRef.current.clearTid = asyncCapture.setTimeout(() => {
updateTransitionState(endState);
endState === T_ENTERED ? afterEnter?.() : afterLeave?.();
}, during);
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dIn]);

const [nextState, setNextState] = useState<DTransitionState | null>(null);
useEffect(() => {
if (nextState !== null) {
updateTransitionState(nextState);

const isEntering = nextState === T_ENTERING;
const endState = isEntering ? T_ENTERED : T_LEAVED;
const during = isNumber(dDuring) ? dDuring : isEntering ? dDuring.enter : dDuring.leave;
dataRef.current.clearTid = asyncCapture.setTimeout(() => {
updateTransitionState(endState);
endState === T_ENTERED ? afterEnter?.() : afterLeave?.();
}, during);

setNextState(null);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [nextState]);

const shouldRender = (() => {
if (dataRef.current.isFirstEnter && !dMountBeforeFirstEnter) {
return false;
Expand Down
20 changes: 9 additions & 11 deletions packages/ui/src/components/drawer/Drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,20 +112,18 @@ export function DDrawer(props: DDrawerProps): JSX.Element | null {
})();

const containerEl = useElement(
isUndefined(dContainer) || dContainer === false
isUndefined(dContainer)
? () => {
if (isUndefined(dContainer)) {
let el = document.getElementById(`${dPrefix}drawer-root`);
if (!el) {
el = document.createElement('div');
el.id = `${dPrefix}drawer-root`;
document.body.appendChild(el);
}
return el;
} else {
return drawerRef.current?.parentElement ?? null;
let el = document.getElementById(`${dPrefix}drawer-root`);
if (!el) {
el = document.createElement('div');
el.id = `${dPrefix}drawer-root`;
document.body.appendChild(el);
}
return el;
}
: dContainer === false
? null
: dContainer
);

Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/components/drawer/demos/3.Container.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ title:

# en-US

Set the container through `dContainer`.
Set the container via `dContainer`, the container must be positioned (set `position`).

# zh-Hant

通过 `dContainer` 设置容器。
通过 `dContainer` 设置容器, 容器必须已定位(设置 `position`)

```tsx
import { useState } from 'react';
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ export { DImage, DImagePreview } from './image';
export type { DInputProps } from './input';
export { DInput } from './input';

export type { DLoadingProps } from './loading';
export { DLoading } from './loading';

export type { DMenuProps } from './menu';
export { DMenu } from './menu';

Expand Down
106 changes: 106 additions & 0 deletions packages/ui/src/components/loading/Loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import type { DTransitionState } from '../_transition';

import { isNumber, isUndefined } from 'lodash';
import { useEffect, useRef } from 'react';

import { usePrefixConfig, useComponentConfig, useAsync, useForceUpdate } from '../../hooks';
import { registerComponentMate, getClassName, checkNodeExist } from '../../utils';
import { TTANSITION_DURING_BASE } from '../../utils/global';
import { DTransition } from '../_transition';

export interface DLoadingProps extends React.HTMLAttributes<HTMLDivElement> {
dVisible?: boolean;
dText?: React.ReactNode;
dDelay?: number;
dSize?: number;
afterVisibleChange?: (visible: boolean) => void;
}

const { COMPONENT_NAME } = registerComponentMate({ COMPONENT_NAME: 'DLoading' });
export function DLoading(props: DLoadingProps): JSX.Element | null {
const {
children,
dVisible = true,
dText,
dDelay,
dSize = '28px',
afterVisibleChange,

...restProps
} = useComponentConfig(COMPONENT_NAME, props);

//#region Context
const dPrefix = usePrefixConfig();
//#endregion

const asyncCapture = useAsync();
const forceUpdate = useForceUpdate();

const delayVisible = useRef(false);
if (dVisible === false) {
delayVisible.current = false;
}
const visible = isUndefined(dDelay) ? dVisible : delayVisible.current;

const transitionStyles: Partial<Record<DTransitionState, React.CSSProperties>> = {
enter: { opacity: 0 },
entering: {
transition: ['opacity'].map((attr) => `${attr} ${TTANSITION_DURING_BASE}ms linear`).join(', '),
},
leaving: {
opacity: 0,
transition: ['opacity'].map((attr) => `${attr} ${TTANSITION_DURING_BASE}ms linear`).join(', '),
},
leaved: { display: 'none' },
};

useEffect(() => {
if (isNumber(dDelay) && dVisible) {
const [asyncGroup, asyncId] = asyncCapture.createGroup();

asyncGroup.setTimeout(() => {
delayVisible.current = true;
forceUpdate();
}, dDelay);

return () => {
asyncCapture.deleteGroup(asyncId);
};
}
}, [asyncCapture, dDelay, dVisible, forceUpdate]);

return (
<DTransition
dIn={visible}
dDuring={TTANSITION_DURING_BASE}
afterEnter={() => {
afterVisibleChange?.(true);
}}
afterLeave={() => {
afterVisibleChange?.(false);
}}
>
{(state) => (
<div
{...restProps}
className={getClassName(restProps.className, `${dPrefix}loading`)}
style={{
...restProps.style,
...transitionStyles[state],
}}
>
<div className={`${dPrefix}loading__icon`} style={{ fontSize: dSize }}>
{checkNodeExist(children) ? (
children
) : (
<svg className={`${dPrefix}loading__spinner`} width="1em" height="1em" viewBox="0 0 50 50">
<circle cx="25" cy="25" r="20" fill="none" stroke="currentColor" strokeWidth="4" strokeLinecap="round"></circle>
</svg>
)}
</div>
{checkNodeExist(dText) && <div className={`${dPrefix}loading__text`}>{dText}</div>}
</div>
)}
</DTransition>
);
}
6 changes: 6 additions & 0 deletions packages/ui/src/components/loading/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
group: Feedback
title: Loading
---

## API
5 changes: 5 additions & 0 deletions packages/ui/src/components/loading/README.zh-Hant.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
title: 加载中
---

## API
37 changes: 37 additions & 0 deletions packages/ui/src/components/loading/demos/1.Basic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
title:
en-US: Basic
zh-Hant: 基本
---

# en-US

The simplest usage.

# zh-Hant

最简单的用法。

```tsx
import { useState } from 'react';

import { DLoading, DAlert, DSwitch } from '@react-devui/ui';

export default function Demo() {
const [loading, setLoading] = useState(true);

return (
<>
<DSwitch dModel={loading} onModelChange={setLoading}>
Loading
</DSwitch>
<br />
<br />
<div style={{ position: 'relative' }}>
<DLoading dVisible={loading}></DLoading>
<DAlert dTitle="I love DevUI so much!" dDescription="Detailed description and advice about DevUI." dType="info"></DAlert>
</div>
</>
);
}
```
37 changes: 37 additions & 0 deletions packages/ui/src/components/loading/demos/2.Text.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
title:
en-US: Add text
zh-Hant: 添加文字
---

# en-US

Add text via `dText`.

# zh-Hant

通过 `dText` 添加文字。

```tsx
import { useState } from 'react';

import { DLoading, DAlert, DSwitch } from '@react-devui/ui';

export default function Demo() {
const [loading, setLoading] = useState(true);

return (
<>
<DSwitch dModel={loading} onModelChange={setLoading}>
Loading
</DSwitch>
<br />
<br />
<div style={{ position: 'relative' }}>
<DLoading dVisible={loading} dText="Loading..."></DLoading>
<DAlert dTitle="I love DevUI so much!" dDescription="Detailed description and advice about DevUI." dType="info"></DAlert>
</div>
</>
);
}
```
40 changes: 40 additions & 0 deletions packages/ui/src/components/loading/demos/3.Icon.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
title:
en-US: Custom icon
zh-Hant: 自定义图标
---

# en-US

Custom loading icon.

# zh-Hant

自定义加载图标。

```tsx
import { useState } from 'react';

import { DLoading, DAlert, DSwitch } from '@react-devui/ui';
import { LoadingOutlined } from '@react-devui/ui/icons';

export default function Demo() {
const [loading, setLoading] = useState(true);

return (
<>
<DSwitch dModel={loading} onModelChange={setLoading}>
Loading
</DSwitch>
<br />
<br />
<div style={{ position: 'relative' }}>
<DLoading dVisible={loading}>
<LoadingOutlined dSpin />
</DLoading>
<DAlert dTitle="I love DevUI so much!" dDescription="Detailed description and advice about DevUI." dType="info"></DAlert>
</div>
</>
);
}
```
37 changes: 37 additions & 0 deletions packages/ui/src/components/loading/demos/4.Delay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
title:
en-US: Delayed display
zh-Hant: 延迟显示
---

# en-US

Delay display loading to prevent screen flickering.

# zh-Hant

延迟显示加载,防止画面闪烁。

```tsx
import { useState } from 'react';

import { DLoading, DAlert, DSwitch } from '@react-devui/ui';

export default function Demo() {
const [loading, setLoading] = useState(false);

return (
<>
<DSwitch dModel={loading} onModelChange={setLoading}>
Loading
</DSwitch>
<br />
<br />
<div style={{ position: 'relative' }}>
<DLoading dVisible={loading} dDelay={500}></DLoading>
<DAlert dTitle="I love DevUI so much!" dDescription="Detailed description and advice about DevUI." dType="info"></DAlert>
</div>
</>
);
}
```
1 change: 1 addition & 0 deletions packages/ui/src/components/loading/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './Loading';
3 changes: 1 addition & 2 deletions packages/ui/src/components/slider/Slider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import type { DFormControl } from '../form';
import type { DTooltipRef } from '../tooltip';

import { isArray, isNumber, toNumber } from 'lodash';
import { useEffect, useState } from 'react';
import { useRef } from 'react';
import { useEffect, useState, useRef } from 'react';

import { usePrefixConfig, useComponentConfig, useGeneralContext, useAsync, useEventCallback, useDValue } from '../../hooks';
import { registerComponentMate, getClassName } from '../../utils';
Expand Down
Loading

0 comments on commit 2f55334

Please sign in to comment.