大多都是有依赖于 Rc-Trigger 基础包, 所以也是需要列入需要研究的工具包
几乎所有的 antd 组件均是依赖了底层的 rc-components 组件, DatePicker 依赖的是 Picker, 不得不说底层实现是真的麻烦, 兼容了多种模式
自定义 panelRender 失效
rc-picker
目录
src
├── generate # 主要为了兼容多种日期工具包
│ ├── moment
│ ├── dayjs
│ └── dateFns
├── hooks
│ ├── useCellClassName # 动态设置单元格 classname
│ ├── useHoverValue # 动态获取 hover 单元格的值
│ ├── usePickerInput #
│ ├── useRangeDisabled # 动态处理单元格是否需要禁用
│ ├── useRangeViewDates # 动态获取视图显示日期单元格
│ ├── useTextValueMapping #
│ └── useValueTexts # 动态设置回填日期
├── locale # 国际化
├── panels # 面板
│ ├── DatePanel # 日期
│ ├── DatetimePanel # 日期 + 时间
│ ├── DecadePanel # 日历
│ ├── MonthPanel # 月份
│ ├── QuarterPanel # 季度
│ ├── TimePanel # 时间 基于 ul li 实现
│ ├── WeekPanel # 周
│ ├── YearPanel # 年
│ ├── Header # 通用 Header 包含 上一年 / 上个月 + 下个月 / 下一年操作
│ └── PanelBody # 通用 Body 渲染 基于 table
├── PickerPanel # 纯日期面板(通过 picker 类型渲染不同的 Panel)
├── PickerTrigger # 封装
├── Picker # PickerTrigger + PickerPanel
├── RangePicker # 带 Trigger 的日期范围
└── utils
结合目录不难看出已经包含了日常所用的功能, 个别需求需要将 Range 部分进行扩展, 可以参考 PickerPanel 扣出来 RangePanel(TODO)
流程
工具
提供一些在组件内通用的判断 / 渲染工具 如 isSameDate(是否同年同月同日)
- getViewDate: 获取当前第一个面板展示时间
- getClosingViewDate: 获取第二个面板的展示时间
- isSame
- isSameDate: 是否为同一天
- isSameTime: 是否为同一个时间段
- isSameWeek: 是否为同一周
- isSameMonth: 是否为同一月
- isSameQuarter: 是否为同一个季度
uiUtils
- elementContains: 判断触发事件的节点是否为其中之一, 用于 isClickOutside, document.activeElement
Hooks
- useMergedState: 用于处理 value & defaultValue 以及提供 set 方法
- useCellClassName: 用于处理不同状态下需要添加的 className
- useHoverValue: 在Range模式下 hover 的值范围
- usePickerInput: 处理 Input 不同的状态
- useRangeDisabled: 处理 Range 模式下的日期是否禁选
- useTextValueMapping:
-
日期
为了更好的兼容更多的第三方日期工具方法, 提供了一个类似适配器模式的工具 在 generateConfig 目录下, 支持 dayjs / moment / date-fns 三种(npm 工具包分析工具: npm-unpkg)
- getYear
- getMonth: 0 ~ 11
- getDay: 获取指定日期在当前周的第几天 0 ~ 6, 0 表示周天
- getDate: 获取指定日期在当前月的第几天 1 ~ 31
- getHours: 指定日期的小时数 0 ~ 23(24小时制)
- getMinutes: 指定日期的分钟数 0 ~ 59(60进制)
- getSeconds: 指定日期的秒数 0 ~ 59(60进制)
- getMilliseconds: 指定日期的毫秒数, 指从 1970年 0时0分0秒之后, 0 ~ 999 (1000 ms === 1s)
getTime: 返回事件的格林威治时间数值 等于 Date 对象的 valueOf 方法 ```typescript export type GenerateConfig
= { // 获取类 / 获取周对应天 —> getDay */ getWeekDay: (value: DateType) => number; / 指定日期的秒 / getSecond: (value: DateType) => number; /** 指定日期分钟数 / getMinute: (value: DateType) => number; / 指定日期小时数 */ getHour: (value: DateType) => number; / 指定月份中指定天 / getDate: (value: DateType) => number; /** 指定月份: 0 ~ 11 / getMonth: (value: DateType) => number; / 指定时间的年份 */ getYear: (value: DateType) => number; / 当前时间 / getNow: () => DateType; /** 指定日期 格式化(YYYY-MM-DD)字符串 / getFixedDate: (fixed: string) => DateType; /* 当前月最后一天 / getEndDate: (value: DateType) => DateType; // 设置类 / 设置指定日期的年份 负数等于 subtract */ addYear: (value: DateType, diff: number) => DateType; / 设置指定日期月份的日期 负数同于 subtract / addMonth: (value: DateType, diff: number) => DateType; /** 设置以指定日期为基础的日期 负数同于 subtract / addDate: (value: DateType, diff: number) => DateType; / 设置指定日期的年份 */ setYear: (value: DateType, year: number) => DateType; / 设置指定日期的月份 / setMonth: (value: DateType, month: number) => DateType; /** 设置指定日期的日期 / setDate: (value: DateType, date: number) => DateType; / 设置指定日期的小时 */ setHour: (value: DateType, hour: number) => DateType; / 设置指定日期的分钟 / setMinute: (value: DateType, minute: number) => DateType; /** 设置指定日期的秒数 / setSecond: (value: DateType, second: number) => DateType;
// 对比类 / 判断 date1 是否在 date2日期前 */ isAfter: (date1: DateType, date2: DateType) => boolean; / 校验是否为有效的日期格式 */ isValidate: (date: DateType) => boolean;
locale: { / 指定语言环境中一周的第一天 */ getWeekFirstDay: (locale: string) => number; / 指定语言环境中一周第一天在当前月的日期 / getWeekFirstDate: (locale: string, value: DateType) => DateType; /** 获取一年中的第几周 / getWeek: (locale: string, value: DateType) => number;
/* / format: (locale: string, date: DateType, format: string) => string;
/* Should only return validate date instance / parse: (locale: string, text: string, formats: string[]) => DateType | null;
/ 获取每周的缩写: 天 一 二 三 四 五 六 */ getShortWeekDays?: (locale: string) => string[]; / 获取每年月份的缩写 全部 */ getShortMonths?: (locale: string) => string[]; }; };
更多可参考 [moment 语言环境](https://momentjs.com/docs/#/i18n/changing-locale/) 以 moment 为例
```typescript
// ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月']
const monthsShort = moment().locale('zh_CN').localeData().monthsShort();
// ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
const months = moment().locale('zh_CN').localeData().months();
// ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
const weekdays = moment().locale('zh_CN').localeData().weekdays();
// ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
const weekdaysShort = moment().locale('zh_CN').localeData().weekdaysShort();
// ['日', '一', '二', '三', '四', '五', '六']
const weekdaysMin = moment().locale('zh_CN').localeData().weekdaysMin()
其他相关
- Intl.DateTimeFormat: 根据语言格式化日期和时间的对象的构造器
- Intl: 国际化 API命名空间, 提供精确的字符串对比, 数值格式化 和 日期时间格式化
以上仅仅是关于适配三方日期工具包统一方法, 还包含其他的如 isSameDate isSameTime 等方法, 也正是因为在日期中返回的值范围有些从 0 开始 在渲染时通常为了便于阅读和保持统一会补全两位(leftPad 类似于 padStart)
单元格: 支持自定义渲染 dateRender
依赖
内部依赖了以下工具为基础包
- shallowequal: 基于 lodash 3.x 版本修改
- rc-util: 整个 react-component 基础库几乎都有依赖的工具包
- rc-trigger: 触发 Popup 的外层容器
classnames: 专门用于处理 className
示例
- rc-picker panelRender
统一共用
名称 | 描述 | 备注 |
---|---|---|
Header | 统一头部定义 | |
PanelBody | 动态计算并生成行 和 列 | 当 pickMode=’date’ 时展示星期 |
DatePanel | 仅日期 | |
DateTimePanel | 日期 + 时间 | |
MonthPanel | 月份面板 | |
QuarterPanel | 季度面板 | |
TimePanel | 时间面板 | |
WeekPanel | 星期面板 | |
YearPanel | 年份面板 |
其中每一个 Panel 都包含独立的 Header 和 Body 配置, 不同的 pickMode 展示不同, 有些包含月份操作, 有些不包含, 根据 PickerPanel 结合 pickMode 动态渲染
Trigger
- input
- suffix
- clear
- 支持 RangePanel 纯面板支持
ant-design:
- 支持纯面板模式: 若无法支持 则单独组件支持
hoverChange: hover 时是否修改result 结果展示
参考
- 火山引擎-增长分析
- 抖音前端
- arco-design
- MUI
- zent
- rc-picker:
- rtl: 示例报错 rangepicker
- 15分钟从零到1构建日期筛选器
- 详情查看
- react-dateramge-picker
- react-datepicker
- 参考 rsuitejs
- 自定义日历组件: 替换 moment —> dayjs
- react-range-picker
- activeElement: 返回当前聚焦的节点, 只读属性
- contains: 判读传入节点是否为当前节点的后代节点
- rc-trigger
- rc-select
图图
思路
拆分
- 快捷面板
- hover: 设置面板区域
- click: 点击设置值
- 结果区
- 开始 ~ 结束 时间
- 前置节点: 如果在快捷筛选区内 则展示
- 面板区
- 自定义面板
- 选中 / hover 样式
- 输入区
- 清空: 1. 关闭面板 2. 清空值 3. 触发 blur
- change: 触发后设置面板中的值