Skip to content

Commit

Permalink
feat(datepicker): support multiple api
Browse files Browse the repository at this point in the history
  • Loading branch information
HaixingOoO committed Nov 10, 2024
1 parent 291c790 commit 3de0796
Show file tree
Hide file tree
Showing 12 changed files with 174 additions and 37 deletions.
47 changes: 35 additions & 12 deletions src/date-picker/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ 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';
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';
Expand All @@ -35,6 +36,7 @@ const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>((originalProps, r
timePickerProps,
presetsPlacement,
needConfirm,
multiple,
onPick,
} = props;

Expand Down Expand Up @@ -63,7 +65,7 @@ const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>((originalProps, r
mode,
format: props.format,
valueType: props.valueType,
enableTimePicker,
enableTimePicker: multiple ? false : enableTimePicker,
});

const onTriggerNeedConfirm = useLatest(() => {
Expand All @@ -90,6 +92,7 @@ const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>((originalProps, r
}, [popupVisible]);

useEffect(() => {
if (multiple) return;
// 面板展开重置数据
// Date valueType、week mode 、quarter mode nad empty string don't need to be parsed
const dateValue =
Expand All @@ -100,8 +103,8 @@ const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>((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);
Expand All @@ -111,12 +114,14 @@ const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>((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 }));
}
Expand All @@ -133,10 +138,26 @@ const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>((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);
}
}
Expand Down Expand Up @@ -231,14 +252,15 @@ const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>((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,
Expand All @@ -259,10 +281,11 @@ const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>((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={<SinglePanel {...panelProps} />}
multiple
/>
</div>
);
Expand Down
34 changes: 34 additions & 0 deletions src/date-picker/_example/multiple.tsx
Original file line number Diff line number Diff line change
@@ -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<DateValue>(['2000-01-04', '2000-01-03', '2000-01-05']);

const handleChange: DatePickerProps['onChange'] = (value, context) => {
console.log('onChange:', value, context);
setDefaultValue(value);
};

return (
<div
style={{
width: '100%',
height: '100vh',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Space direction="vertical">
<DatePicker
value={defaultValue}
placeholder="可清除、可输入的日期选择器"
onChange={handleChange}
clearable
multiple
/>
</Space>
</div>
);
}
20 changes: 14 additions & 6 deletions src/date-picker/date-picker.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<DateValue> \| 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<number>, minute: Array<number>, second: Array<number>, millisecond: Array<number> }>` | 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<string \| number \| Date>`[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<string \| number \| Date>`[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts) | N
valueDisplay | TNode | - | `MouseEvent<SVGElement>`。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`<br/> | N
onChange | Function | | Typescript:`(value: DateValue, context: { dayjsValue?: Dayjs, trigger?: DatePickerTriggerSource }) => void`<br/>[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/date-picker/type.ts)。<br/>`import { Dayjs } from 'dayjs'`<br/><br/>`type DatePickerTriggerSource = 'confirm' \| 'pick' \| 'enter' \| 'preset' \| 'clear'`<br/> | N
Expand All @@ -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<DateValue> \| 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<Date \| null>, context: { partial: DateRangePickerPartial }) => Partial<{ hour: Array<number>, minute: Array<number>, second: Array<number> }>` | 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<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
Expand Down Expand Up @@ -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<DatePickerProps, 'value' \| 'defaultValue' \| 'disableDate' \| 'enableTimePicker' \| 'firstDayOfWeek' \| 'format' \| 'mode' \| 'presets' \| 'presetsPlacement' \| 'timePickerProps'>` | \- | - | extends `Pick<DatePickerProps, 'value' \| 'defaultValue' \| 'disableDate' \| 'enableTimePicker' \| 'firstDayOfWeek' \| 'format' \| 'mode' \| 'presets' \| 'presetsPlacement' \| 'timePickerProps'>` | N
`Pick<DatePickerProps, 'value' \| 'defaultValue' \| 'disableDate' \| 'disableTime' \| 'enableTimePicker' \| 'firstDayOfWeek' \| 'format' \| 'mode' \| 'presets' \| 'presetsPlacement' \| 'timePickerProps' \| 'needConfirm'>` | \- | - | extends `Pick<DatePickerProps, 'value' \| 'defaultValue' \| 'disableDate' \| 'disableTime' \| 'enableTimePicker' \| 'firstDayOfWeek' \| 'format' \| 'mode' \| 'presets' \| 'presetsPlacement' \| 'timePickerProps' \| 'needConfirm'>` | N
onCellClick | Function | | Typescript:`(context: { date: Date, e: MouseEvent }) => void`<br/> | N
onChange | Function | | Typescript:`(value: DateValue, context: { dayjsValue?: Dayjs, e?: MouseEvent, trigger?: DatePickerTriggerSource }) => void`<br/> | N
onConfirm | Function | | Typescript:`(context: { date: Date, e: MouseEvent }) => void`<br/> | N
Expand Down
Loading

0 comments on commit 3de0796

Please sign in to comment.