Ant-design 源码分析之数据展示(四)Card
2021SC@SDUSC
一、组件结构
1、ant代码结构
2、组件结构
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 ? (<Tabssize="large"{
...extraProps}className={
`${
prefixCls}-head-tabs`}onChange={
onTabChange}>{
tabList.map(item => (<Tabs.TabPane tab={
item.tab} disabled={
item.disabled} key={
item.key} />))}</Tabs>) : null;//确定headif (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>
);