Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🔥 feat(design): Add Modal.Progress component #97

Merged
merged 4 commits into from
Aug 29, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
feat(design): Add Modal.Progress component
  • Loading branch information
dengfuping committed Aug 21, 2023
commit 1a0d41acd4d0719820492e66bd477be12577714e
Original file line number Diff line number Diff line change
@@ -1,38 +1,18 @@
import { Modal as AntModal } from 'antd';
import type { ModalProps as AntModalProps } from 'antd/es/modal';
import type { ModalProps } from 'antd/es/modal';
import classNames from 'classnames';
import React, { useContext } from 'react';
import ConfigProvider from '../config-provider';
import { modal } from '../static-function';
import useStyle from './style';

export * from 'antd/es/modal';

export type ModalProps = AntModalProps;

const Modal = ({
prefixCls: customizePrefixCls,
className,
rootClassName,
title,
footer,
...restProps
}: ModalProps) => {
const Modal = ({ prefixCls: customizePrefixCls, className, ...restProps }: ModalProps) => {
const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);
const prefixCls = getPrefixCls('modal', customizePrefixCls);
const { wrapSSR } = useStyle(prefixCls);
const modalCls = classNames(className);

return wrapSSR(
<AntModal
prefixCls={customizePrefixCls}
className={modalCls}
rootClassName={classNames(rootClassName)}
title={title}
footer={footer}
{...restProps}
/>
);
return wrapSSR(<AntModal prefixCls={customizePrefixCls} className={modalCls} {...restProps} />);
};

// 替换 Modal 上的静态方法,支持消费 ConfigProvider 配置
Expand Down
49 changes: 49 additions & 0 deletions packages/design/src/modal/Progress.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React, { useContext } from 'react';
import { Progress as AntProgress } from 'antd';
import type { ModalProps } from 'antd/es/modal';
import type { ProgressProps } from 'antd/es/progress';
import classNames from 'classnames';
import ConfigProvider from '../config-provider';
import Modal from './Modal';

export interface ModalProgressProps extends ModalProps {
progress?: ProgressProps;
description?: React.ReactNode;
}

const Progress = ({
prefixCls: customizePrefixCls,
className,
width = 480,
maskClosable = false,
progress,
description,
footer = null,
...restProps
}: ModalProgressProps) => {
const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);
const prefixCls = getPrefixCls('modal', customizePrefixCls);
const progressModalCls = classNames(`${prefixCls}-progress`, className);

return (
<Modal
prefixCls={customizePrefixCls}
className={progressModalCls}
width={width}
maskClosable={maskClosable}
footer={footer}
{...restProps}
>
<>
<AntProgress type="circle" size={200} {...progress} />
<div className={`${prefixCls}-description`}>{description}</div>
</>
</Modal>
);
};

if (process.env.NODE_ENV !== 'production') {
Modal.displayName = 'Modal.Progress';
}

export default Progress;
4 changes: 2 additions & 2 deletions packages/design/src/modal/demo/over-height.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ export default () => {
setOpen(true);
}}
>
Open Modal of over height
Open over height Modal
</Button>
<Modal
title="Basic Modal"
title="Over height Modal"
open={open}
onOk={() => {
setOpen(false);
Expand Down
82 changes: 82 additions & 0 deletions packages/design/src/modal/demo/progress-with-loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React, { useState } from 'react';
import { Alert, Button, Modal, Space } from '@oceanbase/design';
import { useTimeout } from 'ahooks';

export default () => {
const [successOpen, setSuccessOpen] = useState(false);
const [failOpen, setFailOpen] = useState(false);
const [done, setDone] = useState(false);

useTimeout(
() => {
setDone(true);
},
(successOpen && !done) || (failOpen && !done) ? 3000 : undefined
);

return (
<>
<Space direction="vertical" size="middle">
<Button
type="primary"
onClick={() => {
setSuccessOpen(true);
}}
>
Open Progress Modal from loading to success
</Button>
<Button
onClick={() => {
setFailOpen(true);
}}
>
Open Progress Modal from loading to success
</Button>
</Space>
<Modal.Progress
title={done ? '🎉 Success to create cluster!' : 'Cluster is creating...'}
open={successOpen}
description={
done
? 'Congratulations! please enjoy your OceanBase journey.'
: 'Cluster is creating, please waiting for a few seconds.'
}
onOk={() => {
setSuccessOpen(false);
setDone(false);
}}
onCancel={() => {
setSuccessOpen(false);
setDone(false);
}}
/>
<Modal.Progress
title={done ? '😭 Fail to create cluster!' : 'Cluster is creating...'}
open={failOpen}
progress={{
status: done ? 'exception' : 'normal',
}}
description={
done ? (
<Alert
type="error"
showIcon={true}
message="Please fix errors or try agin later"
description="This is error message. This is error message. This is error message. This is error message."
/>
) : (
'Cluster is creating, please waiting for a few seconds.'
)
}
onOk={() => {
setFailOpen(false);
setDone(false);
}}
onCancel={() => {
setFailOpen(false);
setDone(false);
}}
/>
</>
);
};
89 changes: 89 additions & 0 deletions packages/design/src/modal/demo/progress.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React, { useState } from 'react';
import { Alert, Button, Modal, Space } from '@oceanbase/design';
import { useInterval } from 'ahooks';

export default () => {
const [successOpen, setSuccessOpen] = useState(false);
const [failOpen, setFailOpen] = useState(false);
const [percent, setPercent] = useState(0);

const success = percent === 100;
const fail = percent === 60;

useInterval(
() => {
setPercent(percent + 20);
},
(successOpen && !success) || (failOpen && !fail) ? 1000 : undefined
);

return (
<>
<Space direction="vertical" size="middle">
<Button
type="primary"
onClick={() => {
setSuccessOpen(true);
}}
>
Open Progress Modal from percent to success
</Button>
<Button
onClick={() => {
setFailOpen(true);
}}
>
Open Progress Modal from percent to success
</Button>
</Space>
<Modal.Progress
title={success ? '🎉 Success to create cluster!' : 'Cluster is creating...'}
open={successOpen}
progress={{
percent,
}}
description={
success
? 'Congratulations! please enjoy your OceanBase journey.'
: 'Cluster is creating, please waiting for a few seconds.'
}
onOk={() => {
setSuccessOpen(false);
setPercent(0);
}}
onCancel={() => {
setSuccessOpen(false);
setPercent(0);
}}
/>
<Modal.Progress
title={fail ? '😭 Fail to create cluster!' : 'Cluster is creating...'}
open={failOpen}
progress={{
percent,
status: fail ? 'exception' : 'normal',
}}
description={
fail ? (
<Alert
type="error"
showIcon={true}
message="Please fix errors or try agin later"
description="This is error message. This is error message. This is error message. This is error message."
/>
) : (
'Cluster is creating, please waiting for a few seconds.'
)
}
onOk={() => {
setFailOpen(false);
setPercent(0);
}}
onCancel={() => {
setFailOpen(false);
setPercent(0);
}}
/>
</>
);
};
26 changes: 23 additions & 3 deletions packages/design/src/modal/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,41 @@ title: Modal 对话框
nav:
title: 基础组件
path: /components
demo:
cols: 2
---

- 🔥 完全兼容 antd [Modal](https://ant.design/components/modal-cn) 的能力和 API,可无缝切换。
- 💄 定制主题和样式,符合 OceanBase Design 设计规范。
- 🆕 新增 `Modal.Progress` 组件,可用于异步任务或耗时较长的场景。
- 🆕 `Modal.method()` 静态方法,支持消费 `ConfigProvider` 全局配置。

## 代码演示

<!-- prettier-ignore -->
<code src="./demo/basic.tsx" title="基本"></code>
<code src="./demo/over-height.tsx" title="高度自适应" description="高度超出自动滚动"></code>
<code src="./demo/progress.tsx" title="进度对话框" description="可用于异步任务或耗时较长的场景"></code>
<code src="./demo/progress-with-loading.tsx" title="不带百分比的进度对话框" description="无法获取具体进度时,会展示 loading 态"></code>
<code src="./demo/static-function.tsx" title="静态方法" description="支持消费 `ConfigProvider` 全局配置"></code>

## API

- 详见 antd Modal 文档: https://ant.design/components/modal-cn
### Modal

- 详见 antd Modal 文档: https://ant.design/components/modal-cn#api

### Modal.Progress

| 参数 | 说明 | 类型 | 默认值 | 版本 |
| :-- | :-- | :-- | :-- | :-- |
| width | 对话框宽度 | string \| number | 480 | - |
| maskClosable | 点击蒙层是否允许关闭 | boolean | false | - |
| title | 标题 | ReactNode | - | - |
| progress | 进度条属性 | [ProgressProps](https://ant-design.antgroup.com/components/progress-cn#api) | `{ type: 'circle', size: 150 }` | - |
| description | 描述 | ReactNode | - | - |
| footer | 底部内容 | ReactNode | null | - |

- 更多 API 详见 antd Modal 文档: https://ant.design/components/modal-cn#api

### Modal.method()

- 详见 antd Modal.method() 文档 https://ant.design/components/modal-cn#modalmethod
14 changes: 14 additions & 0 deletions packages/design/src/modal/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Progress from './Progress';
import OriginModal from './Modal';
export * from 'antd/es/modal';
export type { ModalProgressProps } from './Progress';

export type ModalType = typeof OriginModal & {
Progress: typeof Progress;
};

const Modal = OriginModal as ModalType;

Modal.Progress = Progress;

export default Modal as ModalType;
30 changes: 30 additions & 0 deletions packages/design/src/modal/style/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export type ModalToken = FullToken<'Modal'>;

export const genModalStyle: GenerateStyle<ModalToken> = (token: ModalToken): CSSObject => {
const {
antCls,
componentCls,
marginSM,
marginLG,
Expand All @@ -20,6 +21,7 @@ export const genModalStyle: GenerateStyle<ModalToken> = (token: ModalToken): CSS
const titleHeight = fontSizeHeading5 * lineHeightHeading5;

return {
/* Modal */
[`${componentCls}:not(${componentCls}-confirm)`]: {
[`${componentCls}-header`]: {
marginBottom: marginLG,
Expand All @@ -35,6 +37,34 @@ export const genModalStyle: GenerateStyle<ModalToken> = (token: ModalToken): CSS
marginTop: marginLG,
},
},
/* Modal.Progress */
[`${componentCls}${componentCls}-progress`]: {
[`${componentCls}-content`]: {
padding: `${token.paddingXL}px ${token.paddingLG}px`,
[`${componentCls}-header`]: {
textAlign: 'center',
marginBottom: token.marginXL,
[`${componentCls}-title`]: {
fontSize: token.fontSizeHeading4,
},
},
[`${componentCls}-body`]: {
textAlign: 'center',
[`${componentCls}-description`]: {
marginTop: token.marginXL,
color: token.colorTextTertiary,
},
// should align to left for Alert
[`${antCls}-alert`]: {
textAlign: 'left',
},
},
[`${componentCls}-footer`]: {
textAlign: 'center',
},
},
},
/* Modal.method() */
[`${componentCls}-confirm`]: {
[`${componentCls}-body ${componentCls}-confirm-title +${componentCls}-confirm-content`]: {
marginBlockStart: marginSM,
Expand Down
Loading