Ant-design 源码分析之数据展示(三)Calendar

Ant-design 源码分析之数据展示(三)Calendar

2021SC@SDUSC

一、组件结构

1、ant代码结构
在这里插入图片描述
2、rc-ant代码结构
在这里插入图片描述

3、组件结构
在这里插入图片描述

二、antd组件调用关系

1、generateCalendar.tsx
导入相应模块以及相应的ICON图标

import * as React from 'react';
import useMergedState from 'rc-util/lib/hooks/useMergedState';
import classNames from 'classnames';
import padStart from 'lodash/padStart';
import { PickerPanel as RCPickerPanel } from 'rc-picker';
import { Locale } from 'rc-picker/lib/interface';
import { GenerateConfig } from 'rc-picker/lib/generate';
import {
  PickerPanelBaseProps as RCPickerPanelBaseProps,
  PickerPanelDateProps as RCPickerPanelDateProps,
  PickerPanelTimeProps as RCPickerPanelTimeProps,
} from 'rc-picker/lib/PickerPanel';
import LocaleReceiver from '../locale-provider/LocaleReceiver';
import enUS from './locale/en_US';
import { ConfigContext } from '../config-provider';
import CalendarHeader from './Header';

创建了CalendarProps接口。

export interface CalendarProps<DateType> {
  prefixCls?: string;
  className?: string;
  style?: React.CSSProperties;
  locale?: typeof enUS;
  validRange?: [DateType, DateType];
  disabledDate?: (date: DateType) => boolean;
  dateFullCellRender?: (date: DateType) => React.ReactNode;
  dateCellRender?: (date: DateType) => React.ReactNode;
  monthFullCellRender?: (date: DateType) => React.ReactNode;
  monthCellRender?: (date: DateType) => React.ReactNode;
  headerRender?: HeaderRender<DateType>;
  value?: DateType;
  defaultValue?: DateType;
  mode?: CalendarMode;
  fullscreen?: boolean;
  onChange?: (date: DateType) => void;
  onPanelChange?: (date: DateType, mode: CalendarMode) => void;
  onSelect?: (date: DateType) => void;
}

dateCellRender:自定义渲染日期单元格,返回内容会被追加到单元格,类型为function(date: moment): ReactNode。
dateFullCellRender:自定义渲染日期单元格,返回内容覆盖单元格,类型为function(date: moment): ReactNode。
defaultValue:默认展示的日期类型为moment。
disabledDate:不可选择的日期,参数为当前 value,注意使用时不要直接修改,类型为(currentDate: moment) => boolean。
fullscreen:是否全屏显示,类型为boolean。
headerRender:自定义头部内容,类型为function(object:{value: moment, type: string, onChange: f(), onTypeChange: f()})。
locale:国际化配置,类型为object。
mode:初始模式,类型为month | year。
monthCellRender:自定义渲染月单元格,返回内容会被追加到单元格,类型为function(date: moment): ReactNode。
monthFullCellRender:自定义渲染月单元格,返回内容覆盖单元格,类型为function(date: moment): ReactNode。
validRange:设置可以显示的日期,类型为[moment, moment] 。
value 展示日期 moment -
onChange:日期变化回调,类型为function(date: moment)。
onPanelChange:日期面板变化回调,类型为function(date: moment, mode: string)。
onSelect:点击选择日期回调,类型为function(date: moment)。

通过接口实现了Calendar。

const Calendar = (props: CalendarProps<DateType>) => {
    const {
      prefixCls: customizePrefixCls,
      className,
      style,
      dateFullCellRender,
      dateCellRender,
      monthFullCellRender,
      monthCellRender,
      headerRender,
      value,
      defaultValue,
      disabledDate,
      mode,
      validRange,
      fullscreen = true,
      onChange,
      onPanelChange,
      onSelect,
    } = props;

设置样式前缀 prefixCls:

    const { getPrefixCls, direction } = React.useContext(ConfigContext);
    const prefixCls = getPrefixCls('picker', customizePrefixCls);
    const calendarPrefixCls = `${prefixCls}-calendar`;

返回日期

    const today = generateConfig.getNow();

状态

    // 设置Value
    const [mergedValue, setMergedValue] = useMergedState(() => value || generateConfig.getNow(), {
      defaultValue,
      value,
    });

    // 设置Mode
    const [mergedMode, setMergedMode] = useMergedState('month', {
      value: mode,
    });
    const panelMode = React.useMemo<'month' | 'date'>(
      () => (mergedMode === 'year' ? 'month' : 'date'),
      [mergedMode],
    );

    // 设置Disabled Date
    const mergedDisabledDate = React.useCallback(
      (date: DateType) => {
        const notInRange = validRange
          ? generateConfig.isAfter(validRange[0], date) ||
            generateConfig.isAfter(date, validRange[1])
          : false;
        return notInRange || !!disabledDate?.(date);
      },
      [disabledDate, validRange],
    );
	//界面触发器
	const triggerPanelChange = (date: DateType, newMode: CalendarMode) => {
      onPanelChange?.(date, newMode);
    };

    const triggerChange = (date: DateType) => {
      setMergedValue(date);

      if (!isSameDate(date, mergedValue)) {
        // Trigger when month panel switch month
        if (
          (panelMode === 'date' && !isSameMonth(date, mergedValue)) ||
          (panelMode === 'month' && !isSameYear(date, mergedValue))
        ) {
          triggerPanelChange(date, mergedMode);
        }

        onChange?.(date);
      }
    };

    const triggerModeChange = (newMode: CalendarMode) => {
      setMergedMode(newMode);
      triggerPanelChange(mergedValue, newMode);
    };

    const onInternalSelect = (date: DateType) => {
      triggerChange(date);

      onSelect?.(date);
    };
	//获取时间
    const getDefaultLocale = () => {
      const { locale } = props;
      const result = {
        ...enUS,
        ...locale,
      };
      result.lang = {
        ...result.lang,
        ...((locale || {}) as any).lang,
      };
      return result;
    };

    //显示日期
    const dateRender = React.useCallback(
      (date: DateType): React.ReactNode => {
        if (dateFullCellRender) {
          return dateFullCellRender(date);
        }

        return (
          <div
            className={classNames(`${prefixCls}-cell-inner`, `${calendarPrefixCls}-date`, {
              [`${calendarPrefixCls}-date-today`]: isSameDate(today, date),
            })}
          >
            <div className={`${calendarPrefixCls}-date-value`}>
              {padStart(String(generateConfig.getDate(date)), 2, '0')}
            </div>
            <div className={`${calendarPrefixCls}-date-content`}>
              {dateCellRender && dateCellRender(date)}
            </div>
          </div>
        );
      },
      [dateFullCellRender, dateCellRender],
    );

	//显示月份
    const monthRender = React.useCallback(
      (date: DateType, locale: Locale): React.ReactNode => {
        if (monthFullCellRender) {
          return monthFullCellRender(date);
        }

        const months = locale.shortMonths || generateConfig.locale.getShortMonths!(locale.locale);

        return (
          <div
            className={classNames(`${prefixCls}-cell-inner`, `${calendarPrefixCls}-date`, {
              [`${calendarPrefixCls}-date-today`]: isSameMonth(today, date),
            })}
          >
            <div className={`${calendarPrefixCls}-date-value`}>
              {months[generateConfig.getMonth(date)]}
            </div>
            <div className={`${calendarPrefixCls}-date-content`}>
              {monthCellRender && monthCellRender(date)}
            </div>
          </div>
        );
      },
      [monthFullCellRender, monthCellRender],
    );

    return (
      <LocaleReceiver componentName="Calendar" defaultLocale={getDefaultLocale}>
        {(mergedLocale: any) => (
          <div
            className={classNames(
              calendarPrefixCls,
              {
                [`${calendarPrefixCls}-full`]: fullscreen,
                [`${calendarPrefixCls}-mini`]: !fullscreen,
                [`${calendarPrefixCls}-rtl`]: direction === 'rtl',
              },
              className,
            )}
            style={style}
          >
            {headerRender ? (
              headerRender({
                value: mergedValue,
                type: mergedMode,
                onChange: onInternalSelect,
                onTypeChange: triggerModeChange,
              })
            ) : (
              <CalendarHeader
                prefixCls={calendarPrefixCls}
                value={mergedValue}
                generateConfig={generateConfig}
                mode={mergedMode}
                fullscreen={fullscreen}
                locale={mergedLocale.lang}
                validRange={validRange}
                onChange={onInternalSelect}
                onModeChange={triggerModeChange}
              />
            )}

            <RCPickerPanel
              value={mergedValue}
              prefixCls={prefixCls}
              locale={mergedLocale.lang}
              generateConfig={generateConfig}
              dateRender={dateRender}
              monthCellRender={date => monthRender(date, mergedLocale.lang)}
              onSelect={onInternalSelect}
              mode={panelMode}
              picker={panelMode as any}
              disabledDate={mergedDisabledDate}
              hideHeader
            />
          </div>
        )}
      </LocaleReceiver>
    );
  };

三、rc-antd组件调用关系
导入相应模块
1、Rc-picker
PickerPanel.tsx
导入相应模块

import * as React from 'react';
import classNames from 'classnames';
import KeyCode from 'rc-util/lib/KeyCode';
import warning from 'rc-util/lib/warning';
import useMergedState from 'rc-util/lib/hooks/useMergedState';
import type { SharedTimeProps } from './panels/TimePanel';
import TimePanel from './panels/TimePanel';
import DatetimePanel from './panels/DatetimePanel';
import DatePanel from './panels/DatePanel';
import WeekPanel from './panels/WeekPanel';
import MonthPanel from './panels/MonthPanel';
import QuarterPanel from './panels/QuarterPanel';
import YearPanel from './panels/YearPanel';
import DecadePanel from './panels/DecadePanel';
import type { GenerateConfig } from './generate';
import type {
  Locale,
  PanelMode,
  PanelRefProps,
  PickerMode,
  DisabledTime,
  OnPanelChange,
  Components,
} from './interface';
import { isEqual } from './utils/dateUtil';
import PanelContext from './PanelContext';
import type { DateRender } from './panels/DatePanel/DateBody';
import { PickerModeMap } from './utils/uiUtil';
import type { MonthCellRender } from './panels/MonthPanel/MonthBody';
import RangeContext from './RangeContext';
import getExtraFooter from './utils/getExtraFooter';
import getRanges from './utils/getRanges';
import { getLowerBoundTime, setDateTime, setTime } from './utils/timeUtil';

对象

function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
  const {
    prefixCls = 'rc-picker',
    className,
    style,
    locale,
    generateConfig,
    value,
    defaultValue,
    pickerValue,
    defaultPickerValue,
    disabledDate,
    mode,
    picker = 'date',
    tabIndex = 0,
    showNow,
    showTime,
    showToday,
    renderExtraFooter,
    hideHeader,
    onSelect,
    onChange,
    onPanelChange,
    onMouseDown,
    onPickerValueChange,
    onOk,
    components,
    direction,
    hourStep = 1,
    minuteStep = 1,
    secondStep = 1,
  } = props as MergedPickerPanelProps<DateType>;
//是否需要确认
const needConfirmButton: boolean = (picker === 'date' && !!showTime) || picker === 'time';
//判断是否为0
  const isHourStepValid = 24 % hourStep === 0;
  const isMinuteStepValid = 60 % minuteStep === 0;
  const isSecondStepValid = 60 % secondStep === 0;
  if (process.env.NODE_ENV !== 'production') {
    warning(!value || generateConfig.isValidate(value), 'Invalidate date pass to `value`.');
    warning(!value || generateConfig.isValidate(value), 'Invalidate date pass to `defaultValue`.');
    warning(isHourStepValid, `\`hourStep\` ${hourStep} is invalid. It should be a factor of 24.`);
    warning(
      isMinuteStepValid,
      `\`minuteStep\` ${minuteStep} is invalid. It should be a factor of 60.`,
    );
    warning(
      isSecondStepValid,
      `\`secondStep\` ${secondStep} is invalid. It should be a factor of 60.`,
    );
  }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值