diff --git a/src/date-picker/DatePicker.tsx b/src/date-picker/DatePicker.tsx index 6de9652de3..26caf3fb14 100644 --- a/src/date-picker/DatePicker.tsx +++ b/src/date-picker/DatePicker.tsx @@ -2,6 +2,7 @@ import React, { forwardRef, useEffect, useCallback } from 'react'; import classNames from 'classnames'; import dayjs from 'dayjs'; import isDate from 'lodash/isDate'; +import { omit } from 'lodash'; import useConfig from '../hooks/useConfig'; import { StyledProps } from '../common'; import { TdDatePickerProps, PresetDate } from './type'; @@ -9,7 +10,7 @@ import SelectInput from '../select-input'; import SinglePanel from './panel/SinglePanel'; import useSingle from './hooks/useSingle'; import { parseToDayjs, getDefaultFormat, formatTime, formatDate } from '../_common/js/date-picker/format'; -import { subtractMonth, addMonth, extractTimeObj, covertToDate } from '../_common/js/date-picker/utils'; +import { subtractMonth, addMonth, extractTimeObj, covertToDate, isSame } from '../_common/js/date-picker/utils'; import { datePickerDefaultProps } from './defaultProps'; import useDefaultProps from '../hooks/useDefaultProps'; import useLatest from '../hooks/useLatest'; @@ -35,6 +36,7 @@ const DatePicker = forwardRef((originalProps, r timePickerProps, presetsPlacement, needConfirm, + multiple, onPick, } = props; @@ -63,7 +65,7 @@ const DatePicker = forwardRef((originalProps, r mode, format: props.format, valueType: props.valueType, - enableTimePicker, + enableTimePicker: multiple ? false : enableTimePicker, }); const onTriggerNeedConfirm = useLatest(() => { @@ -90,6 +92,7 @@ const DatePicker = forwardRef((originalProps, r }, [popupVisible]); useEffect(() => { + if (multiple) return; // 面板展开重置数据 // Date valueType、week mode 、quarter mode nad empty string don't need to be parsed const dateValue = @@ -100,8 +103,8 @@ const DatePicker = forwardRef((originalProps, r setInputValue(formatDate(dateValue, { format })); if (popupVisible) { - setYear(parseToDayjs(value, format).year()); - setMonth(parseToDayjs(value, format).month()); + setYear(parseToDayjs(value as string | number | Date, format).year()); + setMonth(parseToDayjs(value as string | number | Date, format).month()); setTime(formatTime(value, format, timeFormat, defaultTime)); } else { setIsHoverCell(false); @@ -111,12 +114,14 @@ const DatePicker = forwardRef((originalProps, r // 日期 hover function onCellMouseEnter(date: Date) { + if (multiple) return; setIsHoverCell(true); setInputValue(formatDate(date, { format })); } // 日期 leave function onCellMouseLeave() { + if (multiple) return; setIsHoverCell(false); setInputValue(formatDate(cacheValue, { format })); } @@ -133,10 +138,26 @@ const DatePicker = forwardRef((originalProps, r if (enableTimePicker) { setCacheValue(formatDate(date, { format })); } else { - onChange(formatDate(date, { format, targetFormat: valueType }), { - dayjsValue: parseToDayjs(date, format), - trigger: 'pick', - }); + if (multiple) { + const isSameDate = (value as (string | number | Date)[]).some((val) => isSame(dayjs(val).toDate(), date)); + console.log('isSameDate', isSameDate); + const newDate = !isSameDate + ? [...(value as (string | number | Date)[]), formatDate(date, { format, targetFormat: valueType })] + : (value as (string | number | Date)[]).filter( + (val) => + formatDate(val, { format, targetFormat: valueType }) !== + formatDate(date, { format, targetFormat: valueType }), + ); + onChange(newDate, { + dayjsValue: parseToDayjs(date, format), + trigger: 'pick', + }); + } else { + onChange(formatDate(date, { format, targetFormat: valueType }), { + dayjsValue: parseToDayjs(date, format), + trigger: 'pick', + }); + } setPopupVisible(false); } } @@ -231,14 +252,15 @@ const DatePicker = forwardRef((originalProps, r mode, format, presets, - time, + time: multiple ? false : time, disableDate, firstDayOfWeek, timePickerProps, - enableTimePicker, + enableTimePicker: multiple ? false : enableTimePicker, presetsPlacement, popupVisible, needConfirm, + multiple, onCellClick, onCellMouseEnter, onCellMouseLeave, @@ -259,10 +281,11 @@ const DatePicker = forwardRef((originalProps, r status={props.status} tips={props.tips} borderless={props.borderless} - popupProps={popupProps} - inputProps={inputProps} + popupProps={{ ...popupProps, trigger: multiple ? 'click' : 'mousedown' }} + inputProps={omit(inputProps, ['ref'])} popupVisible={popupVisible} panel={} + multiple /> ); diff --git a/src/date-picker/_example/multiple.tsx b/src/date-picker/_example/multiple.tsx new file mode 100644 index 0000000000..91dff2d7b1 --- /dev/null +++ b/src/date-picker/_example/multiple.tsx @@ -0,0 +1,34 @@ +import React, { useState } from 'react'; +import { DatePicker, Space } from 'tdesign-react'; +import type { DatePickerProps, DateValue } from 'tdesign-react'; + +export default function YearDatePicker() { + const [defaultValue, setDefaultValue] = useState(['2000-01-04', '2000-01-03', '2000-01-05']); + + const handleChange: DatePickerProps['onChange'] = (value, context) => { + console.log('onChange:', value, context); + setDefaultValue(value); + }; + + return ( +
+ + + +
+ ); +} diff --git a/src/date-picker/date-picker.en-US.md b/src/date-picker/date-picker.en-US.md index c10e86f11e..a0b34c3fde 100644 --- a/src/date-picker/date-picker.en-US.md +++ b/src/date-picker/date-picker.en-US.md @@ -13,25 +13,30 @@ borderless | Boolean | false | \- | N clearable | Boolean | false | \- | N defaultTime | String | '00:00:00' | Time selector default value | N disableDate | Object / Array / Function | - | Typescript:`DisableDate` `type DisableDate = Array \| DisableDateObj \| ((date: DateValue) => boolean)` `interface DisableDateObj { from?: string; to?: string; before?: string; after?: string }`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N -disabled | Boolean | - | make DatePicker to be disabled | N +disableTime | Function | - | disable time config function。Typescript:`(time: Date) => Partial<{ hour: Array, minute: Array, second: Array, millisecond: Array }>` | N +disabled | Boolean | undefined | make DatePicker to be disabled | N enableTimePicker | Boolean | false | \- | N firstDayOfWeek | Number | 7 | options: 1/2/3/4/5/6/7 | N format | String | 'YYYY-MM-DD' | \- | N inputProps | Object | - | Typescript:`InputProps`,[Input API Documents](./input?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N +label | TNode | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N mode | String | date | options: year/quarter/month/week/date | N +multiple | Boolean | false | support multiple date,but not support being use together with range-picker、enableTimePicker and allowInput。Typescript:`boolean` | N needConfirm | Boolean | true | whether a confirmation button needs to be clicked to complete the action in the date-time picker scenario, default is true | N placeholder | String / Array | undefined | Typescript:`string` | N popupProps | Object | - | Typescript:`PopupProps`,[Popup API Documents](./popup?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N prefixIcon | TElement | - | Typescript:`TNode`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N presets | Object | - | Typescript:`PresetDate` `interface PresetDate { [name: string]: DateValue \| (() => DateValue) }`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N presetsPlacement | String | bottom | options: left/top/right/bottom | N +selectInputProps | Object | - | Typescript:`SelectInputProps`,[SelectInput API Documents](./select-input?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N size | String | medium | options: small/medium/large。Typescript:`SizeEnum`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N status | String | default | options: default/success/warning/error | N suffixIcon | TElement | - | Typescript:`TNode`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N timePickerProps | Object | - | Typescript:`TimePickerProps`,[TimePicker API Documents](./time-picker?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N tips | TNode | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N -value | String / Number / Date | '' | Typescript:`DateValue` `type DateValue = string \| number \| Date`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N -defaultValue | String / Number / Date | '' | uncontrolled property。Typescript:`DateValue` `type DateValue = string \| number \| Date`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N +value | String / Number / Array / Date | '' | Typescript:`DateValue` `type DateValue = string \| number \| Date \| Array`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N +defaultValue | String / Number / Array / Date | '' | uncontrolled property。Typescript:`DateValue` `type DateValue = string \| number \| Date \| Array`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N +valueDisplay | TNode | - | `MouseEvent`。Typescript:`string \| TNode<{ value: DateValue; displayValue?: DateValue }>`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N valueType | String | - | Typescript:`DatePickerValueType` `type DatePickerValueType = 'time-stamp' \| 'Date' \| 'YYYY' \| 'YYYY-MM' \| 'YYYY-MM-DD' \| 'YYYY-MM-DD HH' \| 'YYYY-MM-DD HH:mm' \| 'YYYY-MM-DD HH:mm:ss' \| 'YYYY-MM-DD HH:mm:ss:SSS'` `type ValueTypeEnum = DatePickerValueType`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N onBlur | Function | | Typescript:`(context: { value: DateValue; e: FocusEvent }) => void`
| N onChange | Function | | Typescript:`(value: DateValue, context: { dayjsValue?: Dayjs, trigger?: DatePickerTriggerSource }) => void`
[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts)。
`import { Dayjs } from 'dayjs'`

`type DatePickerTriggerSource = 'confirm' \| 'pick' \| 'enter' \| 'preset' \| 'clear'`
| N @@ -49,15 +54,18 @@ className | String | - | className of component | N style | Object | - | CSS(Cascading Style Sheets),Typescript:`React.CSSProperties` | N allowInput | Boolean | false | \- | N borderless | Boolean | false | \- | N -cancelRangeSelectLimit | Boolean | false | The default date selection interaction is determined based on the order of dates clicked and will be restricted. For example, if a user first clicks on the start date input box and chooses a date, for instance, 2020-05-15, the interaction will automatically shift focus to the end date input box, waiting for the user to select the end time. At this point, the user can only select a date later than 2020-05-15 (previous dates will be grayed out and disabled, restricting the user's selection). When this value is set to `true`, this restriction is lifted. | N +cancelRangeSelectLimit | Boolean | false | The default date selection interaction is determined based on the order of dates clicked and will be restricted. For example, if a user first clicks on the start date input box and chooses a date, for instance, 2020-05-15, the interaction will automatically shift focus to the end date input box, waiting for the user to select the end time. At this point, the user can only select a date later than 2020-05-15 (previous dates will be grayed out and disabled, restricting the user's selection). When this value is set to `true`, this restriction is lifted | N clearable | Boolean | false | \- | N defaultTime | Array | ["00:00:00", "23:59:59"] | Time selector default value。Typescript:`string[]` | N disableDate | Object / Array / Function | - | Typescript:`DisableRangeDate` `type DisableRangeDate = Array \| DisableDateObj \| ((context: { date: DateRangeValue; partial: DateRangePickerPartial }) => boolean)` `interface DisableDateObj { from?: string; to?: string; before?: string; after?: string }` `type DateRangePickerPartial = 'start' \| 'end'`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N -disabled | Boolean | - | \- | N +disableTime | Function | - | disable time config function。Typescript:`(times: Array, context: { partial: DateRangePickerPartial }) => Partial<{ hour: Array, minute: Array, second: Array }>` | N +disabled | Boolean | undefined | \- | N enableTimePicker | Boolean | false | \- | N firstDayOfWeek | Number | - | options: 1/2/3/4/5/6/7 | N format | String | - | \- | N +label | TNode | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N mode | String | date | options: year/quarter/month/week/date | N +needConfirm | Boolean | true | whether a confirmation button needs to be clicked to complete the action in the date-time range picker scenario, default is true | N panelPreselection | Boolean | true | \- | N placeholder | String / Array | - | Typescript:`string \| Array` | N popupProps | Object | - | Typescript:`PopupProps`,[Popup API Documents](./popup?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N @@ -90,7 +98,7 @@ name | type | default | description | required className | String | - | className of component | N style | Object | - | CSS(Cascading Style Sheets),Typescript:`React.CSSProperties` | N defaultTime | String | '00:00:00' | Time selector default value | N -`Pick` | \- | - | extends `Pick` | N +`Pick` | \- | - | extends `Pick` | N onCellClick | Function | | Typescript:`(context: { date: Date, e: MouseEvent }) => void`
| N onChange | Function | | Typescript:`(value: DateValue, context: { dayjsValue?: Dayjs, e?: MouseEvent, trigger?: DatePickerTriggerSource }) => void`
| N onConfirm | Function | | Typescript:`(context: { date: Date, e: MouseEvent }) => void`
| N diff --git a/src/date-picker/date-picker.md b/src/date-picker/date-picker.md index 62a01b105b..aee3539543 100644 --- a/src/date-picker/date-picker.md +++ b/src/date-picker/date-picker.md @@ -13,25 +13,30 @@ borderless | Boolean | false | 无边框模式 | N clearable | Boolean | false | 是否显示清除按钮 | N defaultTime | String | '00:00:00' | 时间选择器默认值,当 value/defaultValue 未设置值时有效 | N disableDate | Object / Array / Function | - | 禁用日期,示例:['A', 'B'] 表示日期 A 和日期 B 会被禁用。`{ from: 'A', to: 'B' }` 表示在 A 到 B 之间的日期会被禁用。`{ before: 'A', after: 'B' }` 表示在 A 之前和在 B 之后的日期都会被禁用。其中 A = '2021-01-01',B = '2021-02-01'。值类型为 Function 则表示返回值为 true 的日期会被禁用。TS 类型:`DisableDate` `type DisableDate = Array \| DisableDateObj \| ((date: DateValue) => boolean)` `interface DisableDateObj { from?: string; to?: string; before?: string; after?: string }`。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N -disabled | Boolean | - | 是否禁用组件 | N +disableTime | Function | - | 禁用时间项的配置函数,仅在日期时间选择器中可用。TS 类型:`(time: Date) => Partial<{ hour: Array, minute: Array, second: Array, millisecond: Array }>` | N +disabled | Boolean | undefined | 是否禁用组件 | N enableTimePicker | Boolean | false | 是否显示时间选择 | N firstDayOfWeek | Number | 7 | 第一天从星期几开始。可选项:1/2/3/4/5/6/7 | N format | String | 'YYYY-MM-DD' | 仅用于格式化日期显示的格式,不影响日期值。注意和 `valueType` 的区别,`valueType`会直接决定日期值 `value` 的格式。全局配置默认为:'YYYY-MM-DD',[详细文档](https://day.js.org/docs/en/display/format) | N inputProps | Object | - | 透传给输入框(Input)组件的参数。TS 类型:`InputProps`,[Input API Documents](./input?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N +label | TNode | - | 左侧文本。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N mode | String | date | 选择器模式。可选项:year/quarter/month/week/date | N +multiple | Boolean | false | 支持多选日期,但不支持在range-picker中,或与enableTimePicker、allowInput 一起使用。TS 类型:`boolean` | N needConfirm | Boolean | true | 决定在日期时间选择器的场景下是否需要点击确认按钮才完成选择动作,默认为`true` | N placeholder | String / Array | undefined | 占位符。TS 类型:`string` | N popupProps | Object | - | 透传给 popup 组件的参数。TS 类型:`PopupProps`,[Popup API Documents](./popup?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N prefixIcon | TElement | - | 用于自定义组件前置图标。TS 类型:`TNode`。[通用类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N presets | Object | - | 预设快捷日期选择,示例:`{ '元旦': '2021-01-01', '昨天': dayjs().subtract(1, 'day').format('YYYY-MM-DD'), '特定日期': () => ['2021-02-01'] }`。TS 类型:`PresetDate` `interface PresetDate { [name: string]: DateValue \| (() => DateValue) }`。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N presetsPlacement | String | bottom | 预设面板展示区域(包含确定按钮)。可选项:left/top/right/bottom | N +selectInputProps | Object | - | 透传 SelectInput 筛选器输入框组件的全部属性。TS 类型:`SelectInputProps`,[SelectInput API Documents](./select-input?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N size | String | medium | 输入框尺寸。可选项:small/medium/large。TS 类型:`SizeEnum`。[通用类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N status | String | default | 输入框状态。可选项:default/success/warning/error | N suffixIcon | TElement | - | 用于自定义组件后置图标。TS 类型:`TNode`。[通用类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N timePickerProps | Object | - | 透传 TimePicker 组件属性。TS 类型:`TimePickerProps`,[TimePicker API Documents](./time-picker?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N tips | TNode | - | 输入框下方提示文本,会根据不同的 `status` 呈现不同的样式。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N -value | String / Number / Date | '' | 选中值。TS 类型:`DateValue` `type DateValue = string \| number \| Date`。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N -defaultValue | String / Number / Date | '' | 选中值。非受控属性。TS 类型:`DateValue` `type DateValue = string \| number \| Date`。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N +value | String / Number / Array / Date | '' | 选中值。TS 类型:`DateValue` `type DateValue = string \| number \| Date \| Array`。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N +defaultValue | String / Number / Array / Date | '' | 选中值。非受控属性。TS 类型:`DateValue` `type DateValue = string \| number \| Date \| Array`。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N +valueDisplay | TNode | - | 自定义选中项呈现的内容。TS 类型:`string \| TNode<{ value: DateValue; displayValue?: DateValue }>`。[通用类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N valueType | String | - | 用于格式化日期的值,仅支持部分格式,时间戳、日期等。⚠️ `YYYYMMDD` 这种格式不支持,请勿使用,如果希望支持可以给 `dayjs` 提个 PR。注意和 `format` 的区别,`format` 仅用于处理日期在页面中呈现的格式。`ValueTypeEnum` 即将废弃,请更为使用 `DatePickerValueType`。TS 类型:`DatePickerValueType` `type DatePickerValueType = 'time-stamp' \| 'Date' \| 'YYYY' \| 'YYYY-MM' \| 'YYYY-MM-DD' \| 'YYYY-MM-DD HH' \| 'YYYY-MM-DD HH:mm' \| 'YYYY-MM-DD HH:mm:ss' \| 'YYYY-MM-DD HH:mm:ss:SSS'` `type ValueTypeEnum = DatePickerValueType`。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N onBlur | Function | | TS 类型:`(context: { value: DateValue; e: FocusEvent }) => void`
当输入框失去焦点时触发 | N onChange | Function | | TS 类型:`(value: DateValue, context: { dayjsValue?: Dayjs, trigger?: DatePickerTriggerSource }) => void`
选中值发生变化时触发。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts)。
`import { Dayjs } from 'dayjs'`

`type DatePickerTriggerSource = 'confirm' \| 'pick' \| 'enter' \| 'preset' \| 'clear'`
| N @@ -49,15 +54,18 @@ className | String | - | 类名 | N style | Object | - | 样式,TS 类型:`React.CSSProperties` | N allowInput | Boolean | false | 是否允许输入日期 | N borderless | Boolean | false | 无边框模式 | N -cancelRangeSelectLimit | Boolean | false | 默认的日期选择交互是根据点击前后日期的顺序来决定并且会加以限制。比如:用户先点击开始时间输入框,选择了一个日期例如2020-05-15,紧接着交互会自动将焦点跳到结束日期输入框,等待用户选择结束时间。此时用户只能选择大于2020-05-15的日期(之前的日期会被灰态禁止点击,限制用户的点击)。当该值传递`true`时,则取消该限制。 | N +cancelRangeSelectLimit | Boolean | false | 默认的日期选择交互是根据点击前后日期的顺序来决定并且会加以限制。比如:用户先点击开始时间输入框,选择了一个日期例如2020-05-15,紧接着交互会自动将焦点跳到结束日期输入框,等待用户选择结束时间。此时用户只能选择大于2020-05-15的日期(之前的日期会被灰态禁止点击,限制用户的点击)。当该值传递`true`时,则取消该限制 | N clearable | Boolean | false | 是否显示清除按钮 | N defaultTime | Array | ["00:00:00", "23:59:59"] | 时间选择器默认值,当 value/defaultValue 未设置值时有效。TS 类型:`string[]` | N disableDate | Object / Array / Function | - | 禁用日期,示例:['A', 'B'] 表示日期 A 和日期 B 会被禁用。{ from: 'A', to: 'B' } 表示在 A 到 B 之间的日期会被禁用。{ before: 'A', after: 'B' } 表示在 A 之前和在 B 之后的日期都会被禁用。其中 A = '2021-01-01',B = '2021-02-01'。值类型为 Function 则表示返回值为 true 的日期会被禁用。TS 类型:`DisableRangeDate` `type DisableRangeDate = Array \| DisableDateObj \| ((context: { date: DateRangeValue; partial: DateRangePickerPartial }) => boolean)` `interface DisableDateObj { from?: string; to?: string; before?: string; after?: string }` `type DateRangePickerPartial = 'start' \| 'end'`。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N -disabled | Boolean | - | 是否禁用组件 | N +disableTime | Function | - | 禁用时间项的配置函数,仅在日期区间选择器中开启时间展示时可用。TS 类型:`(times: Array, context: { partial: DateRangePickerPartial }) => Partial<{ hour: Array, minute: Array, second: Array }>` | N +disabled | Boolean | undefined | 是否禁用组件 | N enableTimePicker | Boolean | false | 是否显示时间选择 | N firstDayOfWeek | Number | - | 第一天从星期几开始。可选项:1/2/3/4/5/6/7 | N format | String | - | 用于格式化日期,[详细文档](https://day.js.org/docs/en/display/format) | N +label | TNode | - | 左侧文本。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N mode | String | date | 选择器模式。可选项:year/quarter/month/week/date | N +needConfirm | Boolean | true | 决定在日期时间区间选择器的场景下是否需要点击确认按钮才完成选择动作,默认为 `true` | N panelPreselection | Boolean | true | 在开始日期选中之前,面板是否显示预选状态,即是否高亮预选日期 | N placeholder | String / Array | - | 占位符,值为数组表示可分别为开始日期和结束日期设置占位符。TS 类型:`string \| Array` | N popupProps | Object | - | 透传给 popup 组件的参数。TS 类型:`PopupProps`,[Popup API Documents](./popup?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N @@ -90,7 +98,7 @@ onPresetClick | Function | | TS 类型:`(context: { preset: PresetDate, e: Mo className | String | - | 类名 | N style | Object | - | 样式,TS 类型:`React.CSSProperties` | N defaultTime | String | '00:00:00' | 时间选择器默认值,当 value/defaultValue 未设置值时有效 | N -`Pick` | \- | - | 继承 `Pick` 中的全部属性 | N +`Pick` | \- | - | 继承 `Pick` 中的全部属性 | N onCellClick | Function | | TS 类型:`(context: { date: Date, e: MouseEvent }) => void`
点击日期单元格时触发 | N onChange | Function | | TS 类型:`(value: DateValue, context: { dayjsValue?: Dayjs, e?: MouseEvent, trigger?: DatePickerTriggerSource }) => void`
选中值发生变化时触发。参数 `context.trigger` 表示触发当前事件的来源,不同的模式触发来源也会不同 | N onConfirm | Function | | TS 类型:`(context: { date: Date, e: MouseEvent }) => void`
如果存在“确定”按钮,则点击“确定”按钮时触发 | N diff --git a/src/date-picker/defaultProps.ts b/src/date-picker/defaultProps.ts index bb653c3c90..2e54536a32 100644 --- a/src/date-picker/defaultProps.ts +++ b/src/date-picker/defaultProps.ts @@ -9,9 +9,11 @@ export const datePickerDefaultProps: TdDatePickerProps = { borderless: false, clearable: false, defaultTime: '00:00:00', + disabled: undefined, enableTimePicker: false, format: undefined, mode: 'date', + multiple: false, needConfirm: true, placeholder: undefined, presetsPlacement: 'bottom', @@ -26,8 +28,10 @@ export const dateRangePickerDefaultProps: TdDateRangePickerProps = { cancelRangeSelectLimit: false, clearable: false, defaultTime: ['00:00:00', '23:59:59'], + disabled: undefined, enableTimePicker: false, mode: 'date', + needConfirm: true, panelPreselection: true, presetsPlacement: 'bottom', size: 'medium', diff --git a/src/date-picker/hooks/useSingle.tsx b/src/date-picker/hooks/useSingle.tsx index 505a0d5866..2bd9a232a0 100644 --- a/src/date-picker/hooks/useSingle.tsx +++ b/src/date-picker/hooks/useSingle.tsx @@ -24,7 +24,7 @@ export default function useSingleInput(props: TdDatePickerProps) { mode: props.mode, format: props.format, valueType: props.valueType, - enableTimePicker: props.enableTimePicker, + enableTimePicker: props.multiple ? false : props.enableTimePicker, }); const inputRef = useRef(); diff --git a/src/date-picker/hooks/useSingleValue.tsx b/src/date-picker/hooks/useSingleValue.tsx index 58faaedcaf..c7e40ac2d2 100644 --- a/src/date-picker/hooks/useSingleValue.tsx +++ b/src/date-picker/hooks/useSingleValue.tsx @@ -17,7 +17,7 @@ export default function useSingleValue(props: TdDatePickerProps) { const { format, timeFormat } = getDefaultFormat({ mode: props.mode, format: props.format, - enableTimePicker: props.enableTimePicker, + enableTimePicker: props.multiple ? false : props.enableTimePicker, }); if (props.enableTimePicker) { @@ -25,10 +25,30 @@ export default function useSingleValue(props: TdDatePickerProps) { log.error('DatePicker', `format: ${format} is invalid,time selection must include time formatting HH:mm:ss`); } - const [time, setTime] = useState(formatTime(value, format, timeFormat, props.defaultTime)); - const [month, setMonth] = useState(parseToDayjs(value, format).month()); - const [year, setYear] = useState(parseToDayjs(value, format).year()); - const [cacheValue, setCacheValue] = useState(formatDate(value, { format })); // 缓存选中值,panel 点击时更改 + const [time, setTime] = useState(() => { + if (props.multiple) { + return formatTime(value[0], format, timeFormat, props.defaultTime); + } + return formatTime(value, format, timeFormat, props.defaultTime); + }); + const [month, setMonth] = useState(() => { + if (props.multiple) { + return parseToDayjs(value[0], format).month(); + } + return parseToDayjs(value as string | number | Date, format).month(); + }); + const [year, setYear] = useState(() => { + if (props.multiple) { + return parseToDayjs(value[0], format).year(); + } + return parseToDayjs(value as string | number | Date, format).year(); + }); + const [cacheValue, setCacheValue] = useState(() => { + if (props.multiple) { + return formatDate(value[0], { format }); + } + return formatDate(value, { format }); + }); // 缓存选中值,panel 点击时更改 // 输入框响应 value 变化 useEffect(() => { diff --git a/src/date-picker/hooks/useTableData.tsx b/src/date-picker/hooks/useTableData.tsx index ea2887410d..b218438b28 100644 --- a/src/date-picker/hooks/useTableData.tsx +++ b/src/date-picker/hooks/useTableData.tsx @@ -32,6 +32,8 @@ export default function useTableData(props: TableDataProps) { minDate, maxDate, isRange, + value, + multiple, } = props; // 列表数据 @@ -61,5 +63,5 @@ export default function useTableData(props: TableDataProps) { data = getYears(year, options); } - return flagActive(data, { start, end, hoverStart, hoverEnd, type: mode, isRange }); + return flagActive(data, { start, end, hoverStart, hoverEnd, type: mode, isRange, value, multiple }); } diff --git a/src/date-picker/panel/SinglePanel.tsx b/src/date-picker/panel/SinglePanel.tsx index cbe1ec423e..cd116167bb 100644 --- a/src/date-picker/panel/SinglePanel.tsx +++ b/src/date-picker/panel/SinglePanel.tsx @@ -58,11 +58,13 @@ const SinglePanel = forwardRef((originalProps, const disableDateOptions = useDisableDate({ disableDate: props.disableDate, mode: props.mode, format }); const tableData = useTableData({ + value, year, month, mode, - start: value ? parseToDayjs(value, format).toDate() : undefined, + start: value ? parseToDayjs(props.multiple ? value[0] : value, format).toDate() : undefined, firstDayOfWeek, + multiple: props.multiple, ...disableDateOptions, }); @@ -75,7 +77,7 @@ const SinglePanel = forwardRef((originalProps, firstDayOfWeek, tableData, popupVisible: props.popupVisible, - + multiple: props.multiple, time: props.time, timePickerProps: props.timePickerProps, enableTimePicker: props.enableTimePicker, diff --git a/src/date-picker/type.ts b/src/date-picker/type.ts index 9e2dd90eed..d1e5061777 100644 --- a/src/date-picker/type.ts +++ b/src/date-picker/type.ts @@ -6,6 +6,7 @@ import { InputProps } from '../input'; import { PopupProps } from '../popup'; +import { SelectInputProps } from '../select-input'; import { TimePickerProps } from '../time-picker'; import { Dayjs } from 'dayjs'; import { RangeInputProps } from '../range-input'; @@ -37,6 +38,12 @@ export interface TdDatePickerProps { * 禁用日期,示例:['A', 'B'] 表示日期 A 和日期 B 会被禁用。`{ from: 'A', to: 'B' }` 表示在 A 到 B 之间的日期会被禁用。`{ before: 'A', after: 'B' }` 表示在 A 之前和在 B 之后的日期都会被禁用。其中 A = '2021-01-01',B = '2021-02-01'。值类型为 Function 则表示返回值为 true 的日期会被禁用 */ disableDate?: DisableDate; + /** + * 禁用时间项的配置函数,仅在日期时间选择器中可用 + */ + disableTime?: ( + time: Date, + ) => Partial<{ hour: Array; minute: Array; second: Array; millisecond: Array }>; /** * 是否禁用组件 */ @@ -58,11 +65,20 @@ export interface TdDatePickerProps { * 透传给输入框(Input)组件的参数 */ inputProps?: InputProps; + /** + * 左侧文本 + */ + label?: TNode; /** * 选择器模式 * @default date */ mode?: 'year' | 'quarter' | 'month' | 'week' | 'date'; + /** + * 支持多选日期,但不支持在range-picker中,或与enableTimePicker、allowInput 一起使用 + * @default false + */ + multiple?: boolean; /** * 决定在日期时间选择器的场景下是否需要点击确认按钮才完成选择动作,默认为`true` * @default true @@ -89,6 +105,10 @@ export interface TdDatePickerProps { * @default bottom */ presetsPlacement?: 'left' | 'top' | 'right' | 'bottom'; + /** + * 透传 SelectInput 筛选器输入框组件的全部属性 + */ + selectInputProps?: SelectInputProps; /** * 输入框尺寸 * @default medium @@ -121,6 +141,10 @@ export interface TdDatePickerProps { * @default '' */ defaultValue?: DateValue; + /** + * 自定义选中项呈现的内容 + */ + valueDisplay?: string | TNode<{ value: DateValue; displayValue?: DateValue }>; /** * 用于格式化日期的值,仅支持部分格式,时间戳、日期等。⚠️ `YYYYMMDD` 这种格式不支持,请勿使用,如果希望支持可以给 `dayjs` 提个 PR。注意和 `format` 的区别,`format` 仅用于处理日期在页面中呈现的格式。`ValueTypeEnum` 即将废弃,请更为使用 `DatePickerValueType` * @default '' @@ -164,7 +188,7 @@ export interface TdDateRangePickerProps { */ borderless?: boolean; /** - * 默认的日期选择交互是根据点击前后日期的顺序来决定并且会加以限制。比如:用户先点击开始时间输入框,选择了一个日期例如2020-05-15,紧接着交互会自动将焦点跳到结束日期输入框,等待用户选择结束时间。此时用户只能选择大于2020-05-15的日期(之前的日期会被灰态禁止点击,限制用户的点击)。当该值传递`true`时,则取消该限制。 + * 默认的日期选择交互是根据点击前后日期的顺序来决定并且会加以限制。比如:用户先点击开始时间输入框,选择了一个日期例如2020-05-15,紧接着交互会自动将焦点跳到结束日期输入框,等待用户选择结束时间。此时用户只能选择大于2020-05-15的日期(之前的日期会被灰态禁止点击,限制用户的点击)。当该值传递`true`时,则取消该限制 * @default false */ cancelRangeSelectLimit?: boolean; @@ -182,6 +206,13 @@ export interface TdDateRangePickerProps { * 禁用日期,示例:['A', 'B'] 表示日期 A 和日期 B 会被禁用。{ from: 'A', to: 'B' } 表示在 A 到 B 之间的日期会被禁用。{ before: 'A', after: 'B' } 表示在 A 之前和在 B 之后的日期都会被禁用。其中 A = '2021-01-01',B = '2021-02-01'。值类型为 Function 则表示返回值为 true 的日期会被禁用 */ disableDate?: DisableRangeDate; + /** + * 禁用时间项的配置函数,仅在日期区间选择器中开启时间展示时可用 + */ + disableTime?: ( + times: Array, + context: { partial: DateRangePickerPartial }, + ) => Partial<{ hour: Array; minute: Array; second: Array }>; /** * 是否禁用组件 */ @@ -200,6 +231,10 @@ export interface TdDateRangePickerProps { * @default '' */ format?: string; + /** + * 左侧文本 + */ + label?: TNode; /** * 选择器模式 * @default date @@ -335,6 +370,7 @@ export interface TdDatePickerPanelProps | 'value' | 'defaultValue' | 'disableDate' + | 'disableTime' | 'enableTimePicker' | 'firstDayOfWeek' | 'format' @@ -494,7 +530,7 @@ export interface PresetDate { [name: string]: DateValue | (() => DateValue); } -export type DateValue = string | number | Date; +export type DateValue = string | number | Date | Array; export type DatePickerValueType = | 'time-stamp' diff --git a/src/tag-input/TagInput.tsx b/src/tag-input/TagInput.tsx index 4c4b121903..d259db26ec 100644 --- a/src/tag-input/TagInput.tsx +++ b/src/tag-input/TagInput.tsx @@ -98,7 +98,7 @@ const TagInput = forwardRef((originalProps, ref) => { const onInnerClick = (context: { e: MouseEvent }) => { if (!props.disabled && !props.readonly) { - (tagInputRef.current as any).inputElement?.focus?.(); + (tagInputRef.current as any)?.inputElement?.focus?.(); } onClick?.(context); }; diff --git a/src/tag-input/useTagScroll.ts b/src/tag-input/useTagScroll.ts index 50972810da..298e31a9f6 100644 --- a/src/tag-input/useTagScroll.ts +++ b/src/tag-input/useTagScroll.ts @@ -80,7 +80,7 @@ export default function useTagScroll(props: TdTagInputProps) { }; useEffect(() => { - initScroll(tagInputRef?.current.currentElement); + initScroll(tagInputRef?.current?.currentElement); return clearScroll; // eslint-disable-next-line react-hooks/exhaustive-deps }, []);