From 787f974803768dc83a4029f1906e7b47f890b721 Mon Sep 17 00:00:00 2001 From: dengfuping Date: Tue, 6 Feb 2024 15:18:23 +0800 Subject: [PATCH] feat(design): New design for Empty --- .dumirc.ts | 1 + packages/design/src/empty/demo/basic.tsx | 6 + packages/design/src/empty/demo/complete.tsx | 10 ++ packages/design/src/empty/demo/horizontal.tsx | 21 +++ packages/design/src/empty/demo/image.tsx | 14 ++ packages/design/src/empty/demo/simple.tsx | 6 + packages/design/src/empty/demo/steps.tsx | 30 ++++ .../src/empty/demo/with-page-container.tsx | 31 ++++ packages/design/src/empty/empty.tsx | 153 ++++++++++++++++++ packages/design/src/empty/index.md | 38 +++++ packages/design/src/empty/index.ts | 1 - packages/design/src/empty/index.tsx | 87 ++++++++++ packages/design/src/empty/simple.tsx | 26 +++ packages/design/src/empty/style/index.ts | 98 +++++++++++ packages/design/src/index.ts | 3 + 15 files changed, 524 insertions(+), 1 deletion(-) create mode 100644 packages/design/src/empty/demo/basic.tsx create mode 100644 packages/design/src/empty/demo/complete.tsx create mode 100644 packages/design/src/empty/demo/horizontal.tsx create mode 100644 packages/design/src/empty/demo/image.tsx create mode 100644 packages/design/src/empty/demo/simple.tsx create mode 100644 packages/design/src/empty/demo/steps.tsx create mode 100644 packages/design/src/empty/demo/with-page-container.tsx create mode 100644 packages/design/src/empty/empty.tsx create mode 100644 packages/design/src/empty/index.md delete mode 100644 packages/design/src/empty/index.ts create mode 100644 packages/design/src/empty/index.tsx create mode 100644 packages/design/src/empty/simple.tsx create mode 100644 packages/design/src/empty/style/index.ts diff --git a/.dumirc.ts b/.dumirc.ts index d2eb325b2..b6dd14e55 100644 --- a/.dumirc.ts +++ b/.dumirc.ts @@ -144,6 +144,7 @@ export default defineConfig({ children: [ { title: 'Card 卡片', link: '/components/card' }, { title: 'Descriptions 描述列表', link: '/components/descriptions' }, + { title: 'Empty 空状态', link: '/components/empty' }, { title: 'List 列表', link: '/components/list' }, { title: 'Table 表格', link: '/components/table' }, { title: 'Tabs 标签页', link: '/components/tabs' }, diff --git a/packages/design/src/empty/demo/basic.tsx b/packages/design/src/empty/demo/basic.tsx new file mode 100644 index 000000000..d524212e9 --- /dev/null +++ b/packages/design/src/empty/demo/basic.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import { Empty } from '@oceanbase/design'; + +export default () => { + return ; +}; diff --git a/packages/design/src/empty/demo/complete.tsx b/packages/design/src/empty/demo/complete.tsx new file mode 100644 index 000000000..4c61ededc --- /dev/null +++ b/packages/design/src/empty/demo/complete.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { Button, Empty } from '@oceanbase/design'; + +export default () => { + return ( + + + + ); +}; diff --git a/packages/design/src/empty/demo/horizontal.tsx b/packages/design/src/empty/demo/horizontal.tsx new file mode 100644 index 000000000..8ce6ed473 --- /dev/null +++ b/packages/design/src/empty/demo/horizontal.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { Empty, Button } from '@oceanbase/design'; + +export default () => { + return ( + +
• OB 智能诊断是一个数据库问题诊断的控制面板
+
• 将详细的数据库数据图形化的展示
+
• 旨在帮助客户快速评估数据库的运行状态,并对如何处理问题提供建议和指导
+ + } + > + +
+ ); +}; diff --git a/packages/design/src/empty/demo/image.tsx b/packages/design/src/empty/demo/image.tsx new file mode 100644 index 000000000..ff18b4354 --- /dev/null +++ b/packages/design/src/empty/demo/image.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { Button, Empty } from '@oceanbase/design'; + +export default () => { + return ( + + + + ); +}; diff --git a/packages/design/src/empty/demo/simple.tsx b/packages/design/src/empty/demo/simple.tsx new file mode 100644 index 000000000..649375f66 --- /dev/null +++ b/packages/design/src/empty/demo/simple.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import { Empty } from '@oceanbase/design'; + +export default () => { + return ; +}; diff --git a/packages/design/src/empty/demo/steps.tsx b/packages/design/src/empty/demo/steps.tsx new file mode 100644 index 000000000..31e17e014 --- /dev/null +++ b/packages/design/src/empty/demo/steps.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { Empty, Button } from '@oceanbase/design'; + +export default () => { + const description = 'This is a long long long long long long description.'; + const steps = [ + { + title: 'First', + description, + }, + { + title: 'Second', + description, + }, + { + title: 'Third', + description, + }, + { + title: 'Fourth', + description, + }, + ]; + + return ( + + + + ); +}; diff --git a/packages/design/src/empty/demo/with-page-container.tsx b/packages/design/src/empty/demo/with-page-container.tsx new file mode 100644 index 000000000..c7a6929b0 --- /dev/null +++ b/packages/design/src/empty/demo/with-page-container.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { Empty, Button, Card } from '@oceanbase/design'; +import { PageContainer } from '@oceanbase/ui'; + +export default () => { + return ( + + + + + + + + ); +}; diff --git a/packages/design/src/empty/empty.tsx b/packages/design/src/empty/empty.tsx new file mode 100644 index 000000000..812eae4f3 --- /dev/null +++ b/packages/design/src/empty/empty.tsx @@ -0,0 +1,153 @@ +import React from 'react'; + +const Empty: React.FC = props => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); + +if (process.env.NODE_ENV !== 'production') { + Empty.displayName = 'EmptyImage'; +} + +export default Empty; diff --git a/packages/design/src/empty/index.md b/packages/design/src/empty/index.md new file mode 100644 index 000000000..c64b0ecfe --- /dev/null +++ b/packages/design/src/empty/index.md @@ -0,0 +1,38 @@ +--- +title: Empty 空状态 +nav: + title: 基础组件 + path: /components +--- + +- 🔥 完全兼容 antd [Empty](https://ant.design/components/Empty-cn) 的能力和 API,可无缝切换。 +- 💄 定制插图、主题和样式,符合 OceanBase Design 设计规范。 +- 🆕 新增 `title` 属性,用于设置空状态标题。 +- 🆕 新增 `steps` 属性,用于设置步骤引导。 +- 🆕 新增 `layout` 属性,用于设置空状态布局,默认为 `vertical`。 + +## 代码演示 + + + + + + + + + + + + + + + +## API + +| 参数 | 说明 | 类型 | 默认值 | 版本 | +| :-- | :-- | :-- | :-- | :-- | +| title | 标题 | React.ReactNode | horizontal | - | +| steps | 步骤引导 | [StepItem](https://ant-design.antgroup.com/components/steps-cn#stepitem)[] | - | - | +| layout | 布局 | vertical \| horizontal | vertical | - | + +- 更多 API 详见 antd Empty 文档: https://ant.design/components/Empty-cn diff --git a/packages/design/src/empty/index.ts b/packages/design/src/empty/index.ts deleted file mode 100644 index 882502f83..000000000 --- a/packages/design/src/empty/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from 'antd/es/empty'; diff --git a/packages/design/src/empty/index.tsx b/packages/design/src/empty/index.tsx new file mode 100644 index 000000000..f6260a26a --- /dev/null +++ b/packages/design/src/empty/index.tsx @@ -0,0 +1,87 @@ +import React, { useContext } from 'react'; +import { Empty as AntEmpty, Steps } from 'antd'; +import type { EmptyProps as AntEmptyProps } from 'antd/es/empty'; +import type { StepProps } from 'antd/es/steps'; +import classNames from 'classnames'; +import ConfigProvider from '../config-provider'; +import DefaultEmptyImg from './empty'; +import SimpleEmptyImg from './simple'; +import useStyle from './style'; + +export * from 'antd/es/empty'; + +const defaultEmptyImg = ; +const simpleEmptyImg = ; + +export interface EmptyProps extends AntEmptyProps { + title?: React.ReactNode; + steps?: StepProps[]; + layout?: 'horizontal' | 'vertical'; +} + +type CompoundedComponent = React.FC & { + PRESENTED_IMAGE_DEFAULT: React.ReactNode; + PRESENTED_IMAGE_SIMPLE: React.ReactNode; +}; + +const Empty: CompoundedComponent = ({ + image = defaultEmptyImg, + title, + description, + steps, + layout = 'vertical', + children, + prefixCls: customizePrefixCls, + className, + style, + ...restProps +}) => { + const { getPrefixCls } = useContext(ConfigProvider.ConfigContext); + const prefixCls = getPrefixCls('empty', customizePrefixCls); + const { wrapSSR } = useStyle(prefixCls); + const isHorizontal = layout === 'horizontal'; + const emptyCls = classNames( + prefixCls, + { + [`${prefixCls}-horizontal`]: isHorizontal, + }, + className + ); + + return wrapSSR( + + {title &&
{title}
} + {description && ( +
+ {description} +
+ )} + {steps && } + {isHorizontal && children &&
{children}
} + + ) : undefined + } + prefixCls={customizePrefixCls} + className={emptyCls} + {...restProps} + > + {!isHorizontal && children} +
+ ); +}; + +Empty.PRESENTED_IMAGE_DEFAULT = defaultEmptyImg; +Empty.PRESENTED_IMAGE_SIMPLE = simpleEmptyImg; + +if (process.env.NODE_ENV !== 'production') { + Empty.displayName = AntEmpty.displayName; +} + +export default Empty; diff --git a/packages/design/src/empty/simple.tsx b/packages/design/src/empty/simple.tsx new file mode 100644 index 000000000..eae11847a --- /dev/null +++ b/packages/design/src/empty/simple.tsx @@ -0,0 +1,26 @@ +import React from 'react'; + +const Empty: React.FC = () => ( + + + + + + + + + + + + + +); + +if (process.env.NODE_ENV !== 'production') { + Empty.displayName = 'SimpleImage'; +} + +export default Empty; diff --git a/packages/design/src/empty/style/index.ts b/packages/design/src/empty/style/index.ts new file mode 100644 index 000000000..1fa824eb7 --- /dev/null +++ b/packages/design/src/empty/style/index.ts @@ -0,0 +1,98 @@ +import type { CSSObject } from '@ant-design/cssinjs'; +import type { FullToken, GenerateStyle } from 'antd/es/theme/internal'; +import { genComponentStyleHook } from '../../_util/genComponentStyleHook'; + +export type EmptyToken = FullToken<'Badge'>; + +export const genEmptyStyle: GenerateStyle = (token: EmptyToken): CSSObject => { + const { + antCls, + componentCls, + colorTextTertiary, + colorBgLayout, + colorFill, + colorText, + colorTextSecondary, + } = token; + + return { + [`${componentCls}`]: { + [`${componentCls}-image`]: { + height: 'auto', + }, + [`${componentCls}-description`]: { + [`${componentCls}-title`]: { + fontWeight: token.fontWeightStrong, + fontSize: token.fontSizeHeading3, + }, + [`${componentCls}-description-content`]: { + color: colorTextTertiary, + }, + }, + [`${componentCls}-footer`]: { + marginTop: token.marginLG, + }, + [`${antCls}-steps`]: { + marginTop: token.margin, + padding: token.paddingLG, + backgroundColor: colorBgLayout, + borderRadius: token.borderRadiusLG, + [`${antCls}-steps-item-container`]: { + [`${antCls}-steps-item-icon`]: { + height: token.controlHeightSM, + width: token.controlHeightSM, + lineHeight: `${token.controlHeightSM}px`, + backgroundColor: colorFill, + borderColor: colorFill, + [`${antCls}-steps-icon`]: { + color: colorTextSecondary, + fontSize: token.fontSize, + }, + }, + [`${antCls}-steps-item-content`]: { + [`${antCls}-steps-item-title`]: { + color: colorText, + fontSize: token.fontSize, + fontWeight: token.fontWeightStrong, + lineHeight: `${token.controlHeightSM}px`, + '&::after': { + top: token.controlHeightSM / 2, + }, + }, + [`${antCls}-steps-item-description`]: { + color: colorTextTertiary, + fontSize: token.fontSizeSM, + marginTop: token.marginXS, + }, + }, + }, + }, + }, + + [`${componentCls}-horizontal`]: { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + [`${componentCls}-image`]: { + marginBottom: 0, + }, + [`${componentCls}-description`]: { + marginLeft: token.marginXXL, + textAlign: 'left', + [`${componentCls}-title`]: { + marginTop: 0, + }, + [`${componentCls}-description-content`]: { + marginTop: token.margin, + }, + }, + }, + }; +}; + +export default (prefixCls: string) => { + const useStyle = genComponentStyleHook('Empty', token => { + return [genEmptyStyle(token as EmptyToken)]; + }); + return useStyle(prefixCls); +}; diff --git a/packages/design/src/index.ts b/packages/design/src/index.ts index c8ebbe3d1..cc658c56a 100644 --- a/packages/design/src/index.ts +++ b/packages/design/src/index.ts @@ -33,6 +33,9 @@ export type { DescriptionsItemProps, } from './descriptions'; +export { default as Empty } from './empty'; +export type { EmptyProps } from './empty'; + export { default as Form } from './form'; export type { FormProps, FormItemProps } from './form';