2021SC@SDUSC
一、组件结构
ant中Card的index.tsx中引入了Meta和Grid。
二、antd组件调用关系
1、index.tsx
导入相应模块以及相应的ICON图标
import * as React from 'react';
import classNames from 'classnames';
import omit from 'rc-util/lib/omit';
import Grid from './Grid';
import Meta from './Meta';
import Tabs, { TabsProps } from '../tabs';
import Row from '../row';
import Col from '../col';
import { ConfigContext } from '../config-provider';
import SizeContext from '../config-provider/SizeContext';
函数,用于触发事件
function getAction(actions: React.ReactNode[]) {
const actionList = actions.map((action, index) => (
// eslint-disable-next-line react/no-array-index-key
<li style={{ width: `${100 / actions.length}%` }} key={`action-${index}`}>
<span>{action}</span>
</li>
));
return actionList;
}
export { CardGridProps } from './Grid';
export { CardMetaProps } from './Meta';
export type CardType = 'inner';
export type CardSize = 'default' | 'small';
卡片菜单栏
export interface CardTabListType {
key: string;
tab: React.ReactNode;
disabled?: boolean;
}
定义CardProps接口
export interface CardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
prefixCls?: string;
title?: React.ReactNode;
extra?: React.ReactNode;
bordered?: boolean;
headStyle?: React.CSSProperties;
bodyStyle?: React.CSSProperties;
style?: React.CSSProperties;
loading?: boolean;
hoverable?: boolean;
children?: React.ReactNode;
id?: string;
className?: string;
size?: CardSize;
type?: CardType;
cover?: React.ReactNode;
actions?: React.ReactNode[];
tabList?: CardTabListType[];
tabBarExtraContent?: React.ReactNode | null;
onTabChange?: (key: string) => void;
activeTabKey?: string;
defaultActiveTabKey?: string;
tabProps?: TabsProps;
}
actions:卡片操作组,位置在卡片底部,类型为Array
activeTabKey:当前激活页签的 key,类型为string
bodyStyle:内容区域自定义样式,类型为CSSProperties
bordered:是否有边框,类型为boolean
cover:卡片封面,类型为ReactNode
defaultActiveTabKey 初始化选中页签的 key,如果没有设置 activeTabKey string 第一个页签
extra 卡片右上角的操作区域 ReactNode -
headStyle 自定义标题区域样式 CSSProperties -
hoverable:鼠标移过时可浮起,类型为boolean
loading:当卡片内容还在加载中时,可以用 loading 展示一个占位,类型为boolean
size:card 的尺寸,类型为default | small
tabBarExtraContent:tab bar 上额外的元素,类型为ReactNode
tabList:页签标题列表类型为Array<{key: string, tab: ReactNode}>
title:卡片标题,类型为ReactNode
type:卡片类型,可设置为 inner 或 不设置,类型为string
onTabChange:页签切换的回调,类型为(key) => void
定义CardInterface接口
export interface CardInterface extends React.FC<CardProps> {
Grid: typeof Grid;
Meta: typeof Meta;
}
实例化接口
const Card: CardInterface = props => {
const { getPrefixCls, direction } = React.useContext(ConfigContext);
const size = React.useContext(SizeContext);
//菜单改变
const onTabChange = (key: string) => {
props.onTabChange?.(key);
};
//是否是栅格状卡片
const isContainGrid = () => {
let containGrid;
React.Children.forEach(props.children, (element: JSX.Element) => {
if (element && element.type && element.type === Grid) {
containGrid = true;
}
});
return containGrid;
};
const {
prefixCls: customizePrefixCls,
className,
extra,
headStyle = {},
bodyStyle = {},
title,
loading,
bordered = true,
size: customizeSize,
type,
cover,
actions,
tabList,
children,
activeTabKey,
defaultActiveTabKey,
tabBarExtraContent,
hoverable,
tabProps = {},
...others
} = props;
确定卡片样式
const prefixCls = getPrefixCls('card', customizePrefixCls);
const loadingBlockStyle =
bodyStyle.padding === 0 || bodyStyle.padding === '0px' ? { padding: 24 } : undefined;
const block = <div className={`${prefixCls}-loading-block`} />;
确定卡片大小
const loadingBlock = (
<div className={`${prefixCls}-loading-content`} style={loadingBlockStyle}>
<Row gutter={8}>
<Col span={22}>{block}</Col>
</Row>
<Row gutter={8}>
<Col span={8}>{block}</Col>
<Col span={15}>{block}</Col>
</Row>
<Row gutter={8}>
<Col span={6}>{block}</Col>
<Col span={18}>{block}</Col>
</Row>
<Row gutter={8}>
<Col span={13}>{block}</Col>
<Col span={9}>{block}</Col>
</Row>
<Row gutter={8}>
<Col span={4}>{block}</Col>
<Col span={3}>{block}</Col>
<Col span={16}>{block}</Col>
</Row>
</div>
);
卡片是否可用
const hasActiveTabKey = activeTabKey !== undefined;
const extraProps = {
...tabProps,
[hasActiveTabKey ? 'activeKey' : 'defaultActiveKey']: hasActiveTabKey
? activeTabKey
: defaultActiveTabKey,
tabBarExtraContent,
};
let head: React.ReactNode;
const tabs =
tabList && tabList.length ? (
<Tabs
size="large"
{...extraProps}
className={`${prefixCls}-head-tabs`}
onChange={onTabChange}
>
{tabList.map(item => (
<Tabs.TabPane tab={item.tab} disabled={item.disabled} key={item.key} />
))}
</Tabs>
) : null;
//确定head
if (title || extra || tabs) {
head = (
<div className={`${prefixCls}-head`} style={headStyle}>
<div className={`${prefixCls}-head-wrapper`}>
{title && <div className={`${prefixCls}-head-title`}>{title}</div>}
{extra && <div className={`${prefixCls}-extra`}>{extra}</div>}
</div>
{tabs}
</div>
);
}
卡片封面设置
const coverDom = cover ? <div className={`${prefixCls}-cover`}>{cover}</div> : null;
const body = (
<div className={`${prefixCls}-body`} style={bodyStyle}>
{loading ? loadingBlock : children}
</div>
);
const actionDom =
actions && actions.length ? (
<ul className={`${prefixCls}-actions`}>{getAction(actions)}</ul>
) : null;
const divProps = omit(others, ['onTabChange']);
const mergedSize = customizeSize || size;
const classString = classNames(
prefixCls,
{
[`${prefixCls}-loading`]: loading,
[`${prefixCls}-bordered`]: bordered,
[`${prefixCls}-hoverable`]: hoverable,
[`${prefixCls}-contain-grid`]: isContainGrid(),
[`${prefixCls}-contain-tabs`]: tabList && tabList.length,
[`${prefixCls}-${mergedSize}`]: mergedSize,
[`${prefixCls}-type-${type}`]: !!type,
[`${prefixCls}-rtl`]: direction === 'rtl',
},
className,
);
return (
<div {...divProps} className={classString}>
{head}
{coverDom}
{body}
{actionDom}
</div>
);
2、Grid.tsx
导入相应模块
import * as React from 'react';
import classNames from 'classnames';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
声明CardGridProps接口
export interface CardGridProps {
prefixCls?: string;
className?: string;
hoverable?: boolean;
style?: React.CSSProperties;
}
className:网格容器类名,类型为string
hoverable:鼠标移过时可浮起,类型为boolean
style:定义网格容器类名的样式,类型为CSSProperties
const Grid: React.FC<CardGridProps> = ({ prefixCls, className, hoverable = true, ...props }) => (
<ConfigConsumer>
{({ getPrefixCls }: ConfigConsumerProps) => {
const prefix = getPrefixCls('card', prefixCls);
const classString = classNames(`${prefix}-grid`, className, {
[`${prefix}-grid-hoverable`]: hoverable,
});
return <div {...props} className={classString} />;
}}
</ConfigConsumer>
);
3、Meta.tsx
导入相应模块
import * as React from 'react';
import classNames from 'classnames';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
定义CardMetaProps接口
export interface CardMetaProps {
prefixCls?: string;
style?: React.CSSProperties;
className?: string;
avatar?: React.ReactNode;
title?: React.ReactNode;
description?: React.ReactNode;
}
avatar:头像/图标,类型为ReactNode
className:容器类名,类型为string
description:描述内容,类型为ReactNode
style:定义容器类名的样式,类型为CSSProperties
title:标题内容,类型为ReactNode
const Meta: React.FC<CardMetaProps> = props => (
<ConfigConsumer>
{({ getPrefixCls }: ConfigConsumerProps) => {
const {
prefixCls: customizePrefixCls,
className,
avatar,
title,
description,
...others
} = props;
const prefixCls = getPrefixCls('card', customizePrefixCls);
const classString = classNames(`${prefixCls}-meta`, className);
const avatarDom = avatar ? <div className={`${prefixCls}-meta-avatar`}>{avatar}</div> : null;
const titleDom = title ? <div className={`${prefixCls}-meta-title`}>{title}</div> : null;
const descriptionDom = description ? (
<div className={`${prefixCls}-meta-description`}>{description}</div>
) : null;
const MetaDetail =
titleDom || descriptionDom ? (
<div className={`${prefixCls}-meta-detail`}>
{titleDom}
{descriptionDom}
</div>
) : null;
return (
<div {...others} className={classString}>
{avatarDom}
{MetaDetail}
</div>
);
}}
</ConfigConsumer>
);