当前位置: 代码迷 >> 综合 >> Ant-design 源码分析之数据展示(四)Card
  详细解决方案

Ant-design 源码分析之数据展示(四)Card

热度:26   发布时间:2023-11-19 18:19:27.0

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>
);
  相关解决方案