Skip to content

Commit

Permalink
refactor(Page): add subTitle, icon detail config and change style
Browse files Browse the repository at this point in the history
  • Loading branch information
Carrotzpc committed May 21, 2024
1 parent 4064836 commit a9b1ae8
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 41 deletions.
5 changes: 1 addition & 4 deletions src/Page/Content/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ export const useStyles = createStyles(
({ css, token }) => {
return {
root: css`
padding: ${token.padding}px;
background-color: ${token.colorBgBase};
border-radius: ${token.borderRadius}px;
box-shadow: ${token.boxShadowTertiary};
padding-top: ${token.paddingXS}px;
`,
};
},
Expand Down
42 changes: 42 additions & 0 deletions src/Page/Header/Icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Avatar } from 'antd';
import React, { useMemo } from 'react';

export interface HeaderIconProps {
/** 样式名 */
className?: string;
/** 图像无法显示时的替代文本 */
alt?: string;
/** 图标的形状,默认为 square */
shape?: 'circle' | 'square';
/** 设置图标的大小 */
size?: number | 'large' | 'small' | 'default';
/** 图标的资源地址或者图片元素 */
src: React.ReactNode;
/** 样式 */
style?: React.CSSProperties;
}

export const getIconSize = (size: HeaderIconProps['size']) => {
if (typeof size === 'number') {
return size;
}
switch (size) {
case 'small': {
return 40;
}
case 'large': {
return 104;
}
default: {
return 64;
}
}
};

export const HeaderIcon: React.FC<HeaderIconProps> = props => {
const { shape = 'square', size, src, ...otherProps } = props;
const sizeNumber = useMemo(() => {
return getIconSize(size);
}, [size]);
return <Avatar shape={shape} size={sizeNumber} src={src} {...otherProps} />;
};
57 changes: 48 additions & 9 deletions src/Page/Header/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { Badge, Flex, Skeleton, Tooltip } from 'antd';
import type { BadgeProps } from 'antd';
import React, { useCallback, useContext } from 'react';
import React, { useCallback, useContext, useMemo } from 'react';

import Divider from '@/Divider';
import Typography from '@/Typography';

import { PageContext } from '../PageContext';
import { HeaderButtonGroup, type HeaderButtonGroupProps } from './ButtonGroup';
import { HeaderIcon, HeaderIconProps, getIconSize } from './Icon';
import { useStyles } from './style';

const { Paragraph } = Typography;

export interface PageHeaderProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
/** 是否有边框,默认没有 */
bordered?: boolean;
Expand All @@ -17,12 +21,14 @@ export interface PageHeaderProps extends Omit<React.HTMLAttributes<HTMLDivElemen
text: BadgeProps['text'];
title?: BadgeProps['title'];
};
/** 图标地址 */
icon?: string;
/** 图标 src 路径或者详细配置 */
icon?: string | HeaderIconProps;
/** 标题 */
title: React.ReactNode;
/** 标题自定义渲染 */
titleRender?: (titleElement: React.ReactNode) => React.ReactNode;
/** 副标题:例如描述等 */
subTitle?: React.ReactNode;
/** 描述列表 */
descriptions?: {
icon: {
Expand All @@ -39,6 +45,8 @@ export interface PageHeaderProps extends Omit<React.HTMLAttributes<HTMLDivElemen
extraContent?: HeaderButtonGroupProps;
/** 右侧扩展区域自定义渲染 */
extraContentRender?: (buttonsElement: React.ReactNode) => React.ReactNode;
/** 控制 header 与 content 的分割线,当 bordered 为 true 时,divider 自动设置为 false */
divider?: boolean;
}

export const PageHeader: React.FC<PageHeaderProps> = props => {
Expand All @@ -47,15 +55,30 @@ export const PageHeader: React.FC<PageHeaderProps> = props => {
icon,
title,
titleRender,
subTitle,
status,
descriptions = [],
descriptionsRender,
extraContent = {},
extraContentRender,
bordered,
divider,
...otherProps
} = props;
const { styles, cx } = useStyles({ bordered });
const { styles, cx } = useStyles({ bordered, divider });

const iconProps = useMemo(() => {
if (!icon) {
return;
}
if (typeof icon === 'string') {
return { src: icon };
}
if (subTitle && icon.size === undefined) {
icon.size = 'large';
}
return icon;
}, [icon, subTitle]);

const renderTitle = useCallback(() => {
const titleElement = <span className={styles.title}>{title}</span>;
Expand Down Expand Up @@ -103,10 +126,15 @@ export const PageHeader: React.FC<PageHeaderProps> = props => {
if (loading) {
return (
<Flex className={cx(styles.root, className)} gap={20}>
<Skeleton.Avatar active shape="square" size={64} />
<Flex className={styles.content} flex="2" justify="space-between" vertical>
<Skeleton.Avatar active shape={iconProps?.shape} size={getIconSize(iconProps?.size)} />
<Flex flex="2" justify="space-between" vertical>
<div className={styles.titleBox}>
<Skeleton.Input active size={25 as any} />
{subTitle && (
<div className={styles.subTitle}>
<Skeleton.Input active size={18 as any} />
</div>
)}
</div>
<Skeleton.Input active size={18 as any} />
</Flex>
Expand All @@ -123,9 +151,20 @@ export const PageHeader: React.FC<PageHeaderProps> = props => {

return (
<Flex className={cx(styles.root, className)} gap={20} {...otherProps}>
{icon && <img alt="icon" className={styles.icon} src={icon} />}
<Flex className={styles.content} flex="2" justify="space-between" vertical>
<div className={styles.titleBox}>{renderTitle()}</div>
{iconProps && (
<Flex>
<HeaderIcon className={styles.icon} {...iconProps} />
</Flex>
)}
<Flex flex="2" justify="space-between" vertical>
<Flex className={styles.titleBox} vertical>
{renderTitle()}
{subTitle && (
<Paragraph className={styles.subTitle} ellipsis={{ rows: 2 }}>
{subTitle}
</Paragraph>
)}
</Flex>
<Flex align="center" className={styles.descriptions} gap={4}>
{status && <Badge size="small" {...status} />}
{renderDescriptions()}
Expand Down
32 changes: 23 additions & 9 deletions src/Page/Header/style.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { createStyles } from 'antd-style';

export const useStyles = createStyles(
({ css, token, prefixCls }, { bordered = false }: { bordered?: boolean }) => {
(
{ css, token, prefixCls },
{ bordered = false, divider = true }: { bordered?: boolean; divider?: boolean }
) => {
return {
root: bordered
? css`
Expand All @@ -11,25 +14,36 @@ export const useStyles = createStyles(
box-shadow: ${token.boxShadowTertiary};
`
: css`
padding: ${token.padding}px 0;
padding-top: ${token.paddingXS}px;
${divider &&
css`
padding-bottom: ${token.paddingLG}px;
border-bottom: 1px solid ${token.colorSplit};
`}
`,
icon: css`
width: 64px;
height: 64px;
background-color: ${token.colorFillTertiary};
border-radius: ${token.borderRadiusLG}px;
`,
content: css`
padding: ${token.paddingXXS}px 0;
&.${prefixCls}-avatar.${prefixCls}-avatar-square {
border-radius: 10px;
}
`,
titleBox: css`
margin-top: ${token.marginXXS}px;
margin-bottom: ${token.marginSM}px;
`,
subTitle: css`
margin-top: ${token.marginXXS}px;
&.${prefixCls}-typography {
margin-bottom: 0 !important;
font-size: ${token.fontSize}px;
color: ${token.colorTextSecondary};
}
`,
title: css`
font-size: ${token.fontSizeHeading5}px;
font-weight: 700;
`,
descriptions: css`
margin-bottom: ${token.marginXXS}px;
font-size: ${token.fontSize}px;
color: ${token.colorTextSecondary};
.${prefixCls}-badge.${prefixCls}-badge-status {
Expand Down
38 changes: 33 additions & 5 deletions src/Page/demos/BoderedHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,53 @@
/**
* compact: true
*/
import { UserOutlined } from '@ant-design/icons';
import { Page } from '@yuntijs/ui';

import { useStyles } from './style';

const { Breadcrumb, Header, Content } = Page;
const { Header } = Page;

const PageBorderedHeaderDemo = () => {
const { styles } = useStyles();
return (
<Page className={styles.root}>
<Breadcrumb items={[{ title: '插件列表', path: '/plugins' }, { title: '插件详情' }]} />
<Header
bordered
descriptions={[
{
icon: {
content: <UserOutlined />,
tooltip: '创建者',
},
text: '张萝卜',
},
]}
extraContent={{
items: [
{
key: 'edit',
label: '编辑',
},
{
key: 'delete',
label: '删除',
danger: true,
},
{
key: 'test',
label: '测试',
},
],
}}
icon="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
status={{
status: 'processing',
text: '运行中',
title: '插件运行正常',
}}
title="我是一个插件"
/>
<Content>
<></>
</Content>
</Page>
);
};
Expand Down
6 changes: 4 additions & 2 deletions src/Page/demos/Loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ const PageLoadingDemo = () => {
<Page className={styles.root} loading>
<Breadcrumb items={[{ title: '插件列表', path: '/plugins' }, { title: '插件详情' }]} />
<Header
bordered
icon="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
icon={{
src: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
}}
subTitle="我是一个插件的描述"
title="我是一个插件"
/>
<Content>
Expand Down
14 changes: 12 additions & 2 deletions src/Page/demos/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
UserOutlined,
} from '@ant-design/icons';
import { Button, Input, Page, Space } from '@yuntijs/ui';
import { useEffect, useState } from 'react';

import { List } from './List';
import { useStyles } from './style';
Expand All @@ -19,9 +20,15 @@ const { Breadcrumb, Header, Content } = Page;

const PageDemo = () => {
const { styles } = useStyles();
const [loading, setLoading] = useState(true);

useEffect(() => {
const timeout = setTimeout(() => setLoading(false), 1500);
return () => clearTimeout(timeout);
}, []);

return (
<Page className={styles.root}>
<Page className={styles.root} loading={loading}>
<Breadcrumb items={[{ title: '插件列表', path: '/plugins' }, { title: '插件详情' }]} />
<Header
descriptions={[
Expand Down Expand Up @@ -58,12 +65,15 @@ const PageDemo = () => {
],
onClick: (key, e) => console.log('key', key, e),
}}
icon={'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png'}
icon={{
src: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
}}
status={{
status: 'processing',
text: '运行中',
title: '插件运行正常',
}}
subTitle="我是一个插件的描述"
title="我是一个插件"
titleRender={title => (
<Space>
Expand Down
13 changes: 3 additions & 10 deletions src/Page/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,8 @@ const { Breadcrumb, Header, Content } = Page;

const IndexPage = () => {
return (
<Page>
<Breadcrumb
Link={Link}
items={[{ title: '插件列表', path: '/plugins' }, { title: '插件详情' }]}
/>
<Page Link={Link}>
<Breadcrumb items={[{ title: '插件列表', path: '/plugins' }, { title: '插件' }] />
<Header
descriptions={[
{
Expand All @@ -47,12 +44,8 @@ const IndexPage = () => {
status: 'processing',
text: '运行中',
}}
subTitle="我是一个插件的描述"
title="我是一个插件"
titleRender={title => (
<Space>
{title} <StarOutlined />
</Space>
)}
/>
<Content>
<Space size={12}>
Expand Down

0 comments on commit a9b1ae8

Please sign in to comment.