feat: Modal support loading (#48848)

* feat: Modal support loading

* feat: Modal support loading

* chore: update skeleton className

* fix: fix

* demo: add LoadingOutlined

* fix: revert icon

* fix: fix

* fix: renderFooter

* fix: renderFooter

* demo: update demo

* demo: update demo

* demo: update demo

* chore: clear

* Update loading.tsx

Signed-off-by: 二货爱吃白萝卜 <smith3816@gmail.com>

* fix: fix

---------

Signed-off-by: 二货爱吃白萝卜 <smith3816@gmail.com>
Co-authored-by: 二货爱吃白萝卜 <smith3816@gmail.com>
This commit is contained in:
lijianan 2024-05-15 11:21:40 +08:00 committed by GitHub
parent 05f587a6ce
commit 7cdeecfe1e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 111 additions and 10 deletions

View File

@ -12,6 +12,7 @@ import zIndexContext from '../_util/zindexContext';
import { ConfigContext } from '../config-provider'; import { ConfigContext } from '../config-provider';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls'; import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import { NoFormStyle } from '../form/context'; import { NoFormStyle } from '../form/context';
import Skeleton from '../skeleton';
import { NoCompactStyle } from '../space/Compact'; import { NoCompactStyle } from '../space/Compact';
import { usePanelRef } from '../watermark/context'; import { usePanelRef } from '../watermark/context';
import type { ModalProps, MousePosition } from './interface'; import type { ModalProps, MousePosition } from './interface';
@ -86,6 +87,8 @@ const Modal: React.FC<ModalProps> = (props) => {
footer, footer,
classNames: modalClassNames, classNames: modalClassNames,
styles: modalStyles, styles: modalStyles,
children,
loading,
...restProps ...restProps
} = props; } = props;
@ -100,9 +103,10 @@ const Modal: React.FC<ModalProps> = (props) => {
[`${prefixCls}-wrap-rtl`]: direction === 'rtl', [`${prefixCls}-wrap-rtl`]: direction === 'rtl',
}); });
const dialogFooter = footer !== null && ( const dialogFooter =
<Footer {...props} onOk={handleOk} onCancel={handleCancel} /> footer !== null && !loading ? (
); <Footer {...props} onOk={handleOk} onCancel={handleCancel} />
) : null;
const [mergedClosable, mergedCloseIcon] = useClosable( const [mergedClosable, mergedCloseIcon] = useClosable(
pickClosable(props), pickClosable(props),
@ -149,12 +153,20 @@ const Modal: React.FC<ModalProps> = (props) => {
...modalClassNames, ...modalClassNames,
wrapper: classNames(wrapClassNameExtended, modalClassNames?.wrapper), wrapper: classNames(wrapClassNameExtended, modalClassNames?.wrapper),
}} }}
styles={{ styles={{ ...modalContext?.styles, ...modalStyles }}
...modalContext?.styles,
...modalStyles,
}}
panelRef={panelRef} panelRef={panelRef}
/> >
{loading ? (
<Skeleton
active
title={false}
paragraph={{ rows: 4 }}
className={`${prefixCls}-body-skeleton`}
/>
) : (
children
)}
</Dialog>
</zIndexContext.Provider> </zIndexContext.Provider>
</NoFormStyle> </NoFormStyle>
</NoCompactStyle>, </NoCompactStyle>,

View File

@ -630,6 +630,19 @@ exports[`renders components/modal/demo/hooks.tsx extend context correctly 1`] =
exports[`renders components/modal/demo/hooks.tsx extend context correctly 2`] = `[]`; exports[`renders components/modal/demo/hooks.tsx extend context correctly 2`] = `[]`;
exports[`renders components/modal/demo/loading.tsx extend context correctly 1`] = `
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Open Modal
</span>
</button>
`;
exports[`renders components/modal/demo/loading.tsx extend context correctly 2`] = `[]`;
exports[`renders components/modal/demo/locale.tsx extend context correctly 1`] = ` exports[`renders components/modal/demo/locale.tsx extend context correctly 1`] = `
<div <div
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small" class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"

View File

@ -606,6 +606,17 @@ exports[`renders components/modal/demo/hooks.tsx correctly 1`] = `
</div> </div>
`; `;
exports[`renders components/modal/demo/loading.tsx correctly 1`] = `
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Open Modal
</span>
</button>
`;
exports[`renders components/modal/demo/locale.tsx correctly 1`] = ` exports[`renders components/modal/demo/locale.tsx correctly 1`] = `
<div <div
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small" class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"

View File

@ -0,0 +1,7 @@
## zh-CN
设置对话框加载状态。
## en-US
Set the loading status of Modal.

View File

@ -0,0 +1,42 @@
import React from 'react';
import { Button, Modal } from 'antd';
const App: React.FC = () => {
const [open, setOpen] = React.useState<boolean>(false);
const [loading, setLoading] = React.useState<boolean>(true);
const showLoading = () => {
setOpen(true);
setLoading(true);
// Simple loading mock. You should add cleanup logic in real world.
setTimeout(() => {
setLoading(false);
}, 2000);
};
return (
<>
<Button type="primary" onClick={showLoading}>
Open Modal
</Button>
<Modal
title={<p>Loading Modal</p>}
footer={
<Button type="primary" onClick={showLoading}>
Reload
</Button>
}
loading={loading}
open={open}
onCancel={() => setOpen(false)}
>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
</Modal>
</>
);
};
export default App;

View File

@ -21,6 +21,7 @@ Additionally, if you need to show a simple confirmation dialog, you can use [`Ap
<code src="./demo/basic.tsx">Basic</code> <code src="./demo/basic.tsx">Basic</code>
<code src="./demo/async.tsx">Asynchronously close</code> <code src="./demo/async.tsx">Asynchronously close</code>
<code src="./demo/footer.tsx">Customized Footer</code> <code src="./demo/footer.tsx">Customized Footer</code>
<code src="./demo/loading.tsx" version="5.18.0">Loading</code>
<code src="./demo/footer-render.tsx">Customized Footer render function</code> <code src="./demo/footer-render.tsx">Customized Footer render function</code>
<code src="./demo/hooks.tsx">Use hooks to get context</code> <code src="./demo/hooks.tsx">Use hooks to get context</code>
<code src="./demo/locale.tsx">Internationalization</code> <code src="./demo/locale.tsx">Internationalization</code>
@ -67,6 +68,7 @@ Common props ref[Common props](/docs/react/common-props)
| okText | Text of the OK button | ReactNode | `OK` | | | okText | Text of the OK button | ReactNode | `OK` | |
| okType | Button `type` of the OK button | string | `primary` | | | okType | Button `type` of the OK button | string | `primary` | |
| style | Style of floating layer, typically used at least for adjusting the position | CSSProperties | - | | | style | Style of floating layer, typically used at least for adjusting the position | CSSProperties | - | |
| loading | Show the skeleton | boolean | | 5.18.0 |
| title | The modal dialog's title | ReactNode | - | | | title | The modal dialog's title | ReactNode | - | |
| open | Whether the modal dialog is visible or not | boolean | false | | | open | Whether the modal dialog is visible or not | boolean | false | |
| width | Width of the modal dialog | string \| number | 520 | | | width | Width of the modal dialog | string \| number | 520 | |

View File

@ -22,6 +22,7 @@ demo:
<code src="./demo/basic.tsx">基本</code> <code src="./demo/basic.tsx">基本</code>
<code src="./demo/async.tsx">异步关闭</code> <code src="./demo/async.tsx">异步关闭</code>
<code src="./demo/footer.tsx">自定义页脚</code> <code src="./demo/footer.tsx">自定义页脚</code>
<code src="./demo/loading.tsx" version="5.18.0">加载中</code>
<code src="./demo/footer-render.tsx">自定义页脚渲染函数</code> <code src="./demo/footer-render.tsx">自定义页脚渲染函数</code>
<code src="./demo/hooks.tsx">使用 hooks 获得上下文</code> <code src="./demo/hooks.tsx">使用 hooks 获得上下文</code>
<code src="./demo/locale.tsx">国际化</code> <code src="./demo/locale.tsx">国际化</code>
@ -68,6 +69,7 @@ demo:
| okText | 确认按钮文字 | ReactNode | `确定` | | | okText | 确认按钮文字 | ReactNode | `确定` | |
| okType | 确认按钮类型 | string | `primary` | | | okType | 确认按钮类型 | string | `primary` | |
| style | 可用于设置浮层的样式,调整浮层位置等 | CSSProperties | - | | | style | 可用于设置浮层的样式,调整浮层位置等 | CSSProperties | - | |
| loading | 显示骨架屏 | boolean | | 5.18.0 |
| title | 标题 | ReactNode | - | | | title | 标题 | ReactNode | - | |
| open | 对话框是否可见 | boolean | - | | | open | 对话框是否可见 | boolean | - | |
| width | 宽度 | string \| number | 520 | | | width | 宽度 | string \| number | 520 | |

View File

@ -1,4 +1,4 @@
import type { FC } from 'react'; import type React from 'react';
import type { DialogProps } from 'rc-dialog'; import type { DialogProps } from 'rc-dialog';
import type { ClosableType } from '../_util/hooks/useClosable'; import type { ClosableType } from '../_util/hooks/useClosable';
@ -7,7 +7,7 @@ import type { DirectionType } from '../config-provider';
export type ModalFooterRender = ( export type ModalFooterRender = (
originNode: React.ReactNode, originNode: React.ReactNode,
extra: { OkBtn: FC; CancelBtn: FC }, extra: { OkBtn: React.FC; CancelBtn: React.FC },
) => React.ReactNode; ) => React.ReactNode;
interface ModalCommonProps { interface ModalCommonProps {
styles?: Omit<NonNullable<DialogProps['styles']>, 'wrapper'>; styles?: Omit<NonNullable<DialogProps['styles']>, 'wrapper'>;
@ -73,6 +73,10 @@ export interface ModalProps extends ModalCommonProps {
// Legacy // Legacy
/** @deprecated Please use `open` instead. */ /** @deprecated Please use `open` instead. */
visible?: boolean; visible?: boolean;
/**
* @since 5.18.0
*/
loading?: boolean;
} }
type getContainerFunc = () => HTMLElement; type getContainerFunc = () => HTMLElement;

View File

@ -270,6 +270,14 @@ const genModalStyle: GenerateStyle<ModalToken> = (token) => {
lineHeight: token.lineHeight, lineHeight: token.lineHeight,
wordWrap: 'break-word', wordWrap: 'break-word',
padding: token.bodyPadding, padding: token.bodyPadding,
[`${componentCls}-body-skeleton`]: {
width: '100%',
height: '100%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
margin: `${unit(token.margin)} auto`,
},
}, },
[`${componentCls}-footer`]: { [`${componentCls}-footer`]: {