Skip to content

Commit

Permalink
feat(DatePicker): support week and year multiple (#3264)
Browse files Browse the repository at this point in the history
* feat(datepicker): support week multiple

* docs(datepicker): add week and year multiple example

* fix(datepicker): fix PanelContent multiple type

* test(datepicker): fix multiple test

* docs(DatePicker): optimize DatePicker example

* chore: update common

* fix(datepicker): fix DatePicker week test

* test(datepicker): update test snap

* fix(datepicker): fix DatePicker week multiple judgment

* chore: correct demo init value

* chore: fix cross year week

* chore: fix cross year week

* chore: update snapshot

* chore: update snapshot

* chore: optimize

---------

Co-authored-by: Heising <[email protected]>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Uyarn <[email protected]>
  • Loading branch information
4 people authored Dec 19, 2024
1 parent 9fe694d commit 74cb32c
Show file tree
Hide file tree
Showing 10 changed files with 321 additions and 78 deletions.
18 changes: 14 additions & 4 deletions src/date-picker/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import useDefaultProps from '../hooks/useDefaultProps';
import useLatest from '../hooks/useLatest';
import useUpdateEffect from '../hooks/useUpdateEffect';
import type { TagInputRemoveContext } from '../tag-input';
import { useLocaleReceiver } from '../locale/LocalReceiver';

export interface DatePickerProps extends TdDatePickerProps, StyledProps {}

Expand Down Expand Up @@ -64,6 +65,7 @@ const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>((originalProps, r
setCacheValue,
} = useSingle(props);

const [local] = useLocaleReceiver('datePicker');
const { format, timeFormat, valueType } = getDefaultFormat({
mode,
format: props.format,
Expand Down Expand Up @@ -241,20 +243,28 @@ const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>((originalProps, r
}, []);

function processDate(date: Date) {
const isSameDate = (value as DateMultipleValue).some((val) => isSame(dayjs(val).toDate(), date));
let isSameDate: boolean;
const currentValue = (value || []) as DateMultipleValue;
if (mode !== 'week')
isSameDate = currentValue.some((val) =>
isSame(parseToDayjs(val, format).toDate(), date, mode, local.dayjsLocale),
);
else {
isSameDate = currentValue.some((val) => val === dayjs(date).locale(local.dayjsLocale).format(format));
}
let currentDate: DateMultipleValue;

if (!isSameDate) {
currentDate = (value as DateMultipleValue).concat(formatDate(date, { format, targetFormat: valueType }));
currentDate = currentValue.concat(formatDate(date, { format, targetFormat: valueType }));
} else {
currentDate = (value as DateMultipleValue).filter(
currentDate = currentValue.filter(
(val) =>
formatDate(val, { format, targetFormat: valueType }) !==
formatDate(date, { format, targetFormat: valueType }),
);
}

return currentDate.sort((a, b) => dayjs(a).valueOf() - dayjs(b).valueOf());
return currentDate?.sort((a, b) => dayjs(a).valueOf() - dayjs(b).valueOf());
}

const onTagRemoveClick = (ctx: TagInputRemoveContext) => {
Expand Down
44 changes: 36 additions & 8 deletions src/date-picker/_example/multiple.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,52 @@
import React, { useState } from 'react';
import { DatePicker } from 'tdesign-react';
import { DatePicker, Space } from 'tdesign-react';
import type { DatePickerProps, DateMultipleValue } from 'tdesign-react';

export default function YearDatePicker() {
const [defaultValue, setDefaultValue] = useState<DateMultipleValue>(['2000-01-04', '2000-01-03', '2000-01-05']);
const [dateValue, setDateValue] = useState<DateMultipleValue>(['2024-10-01', '2024-10-24']);
const [weekValue, setWeekValue] = useState<DateMultipleValue>(['2024-50周', '2024-51周']);
const [yearValue, setYearValue] = useState<DateMultipleValue>(['2022', '2023', '2024']);

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

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

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

return (
<div>
<Space direction="vertical">
<DatePicker
value={dateValue}
placeholder="可清除、可输入的日期选择器"
onChange={handleDateChange}
clearable
multiple
/>
<DatePicker
value={weekValue}
placeholder="可清除、可输入的日期选择器"
onChange={handleWeekChange}
clearable
multiple
mode="week"
/>
<DatePicker
value={defaultValue}
value={yearValue}
placeholder="可清除、可输入的日期选择器"
onChange={handleChange}
onChange={handleYearChange}
clearable
multiple
mode="year"
/>
</div>
</Space>
);
}
26 changes: 22 additions & 4 deletions src/date-picker/base/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,22 @@ import React, { useMemo } from 'react';
import classNames from 'classnames';
import dayjs from 'dayjs';
import isoWeek from 'dayjs/plugin/isoWeek';

import type { Dayjs } from 'dayjs';
import { useLocaleReceiver } from '../../locale/LocalReceiver';
import useConfig from '../../hooks/useConfig';
import DatePickerCell from './Cell';
import { TdDatePickerProps } from '../type';

import { SinglePanelProps } from '../panel/SinglePanel';
import { PanelContentProps } from '../panel/PanelContent';
import { parseToDayjs } from '../../_common/js/date-picker/format';

import type { DateMultipleValue, DateRangeValue, DateValue, TdDatePickerProps } from '../type';

dayjs.extend(isoWeek);

export interface DatePickerTableProps
extends Pick<TdDatePickerProps, 'mode' | 'firstDayOfWeek' | 'format'>,
extends Pick<TdDatePickerProps, 'mode' | 'firstDayOfWeek' | 'format' | 'multiple'>,
Pick<SinglePanelProps, 'onCellClick' | 'onCellMouseEnter' | 'onCellMouseLeave'>,
Pick<PanelContentProps, 'value'> {
data?: Array<any>;
Expand Down Expand Up @@ -60,7 +64,7 @@ const DatePickerTable = (props: DatePickerTableProps) => {
}, [mode, value, format]);

// 高亮周区间
const weekRowClass = (value, targetDayjs) => {
const weekRowClass = (value: DateValue | DateRangeValue, targetDayjs: Dayjs) => {
if (mode !== 'week' || !value) return {};

if (Array.isArray(value)) {
Expand Down Expand Up @@ -90,6 +94,20 @@ const DatePickerTable = (props: DatePickerTableProps) => {
};
};

// multiple
const multipleWeekRowClass = (value: DateMultipleValue, targetDayjs: Dayjs) => {
if (mode !== 'week' || (Array.isArray(value) && !value.length)) return {};
const isSomeYearWeek = value
.map((v) => parseToDayjs(v, format))
.some((item) => item.isoWeek() === targetDayjs.isoWeek() && item.isoWeekYear() === targetDayjs.isoWeekYear());

return {
[`${classPrefix}-date-picker__table-${mode}-row--active`]: isSomeYearWeek,
};
};

const activeRowCss = props.multiple ? multipleWeekRowClass : weekRowClass;

return (
<div className={`${classPrefix}-date-picker__table`} onMouseLeave={(e) => onCellMouseLeave?.({ e })}>
<table>
Expand All @@ -107,7 +125,7 @@ const DatePickerTable = (props: DatePickerTableProps) => {
<tr
key={i}
className={classNames(`${classPrefix}-date-picker__table-${mode}-row`, {
...weekRowClass(value, row[0].dayjsObj),
...activeRowCss(value, row[0].dayjsObj),
})}
>
{row.map((col: any, j: number) => (
Expand Down
8 changes: 4 additions & 4 deletions src/date-picker/hooks/useSingleValue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ export default function useSingleValue(props: TdDatePickerProps) {
}

const [time, setTime] = useState(() =>
formatTime(props.multiple ? value[0] : value, format, timeFormat, props.defaultTime),
formatTime(props.multiple ? value?.[0] : value, format, timeFormat, props.defaultTime),
);
const [month, setMonth] = useState<number>(() => parseToDayjs(props.multiple ? value[0] : value, format).month());
const [year, setYear] = useState<number>(() => parseToDayjs(props.multiple ? value[0] : value, format).year());
const [cacheValue, setCacheValue] = useState(() => formatDate(props.multiple ? value[0] : value, { format })); // 缓存选中值,panel 点击时更改
const [month, setMonth] = useState<number>(() => parseToDayjs(props.multiple ? value?.[0] : value, format).month());
const [year, setYear] = useState<number>(() => parseToDayjs(props.multiple ? value?.[0] : value, format).year());
const [cacheValue, setCacheValue] = useState(() => formatDate(props.multiple ? value?.[0] : value, { format })); // 缓存选中值,panel 点击时更改

// 输入框响应 value 变化
useEffect(() => {
Expand Down
3 changes: 2 additions & 1 deletion src/date-picker/panel/PanelContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export interface PanelContentProps {
timePickerProps: SinglePanelProps['timePickerProps'];
firstDayOfWeek: SinglePanelProps['firstDayOfWeek'];
time: SinglePanelProps['time'];

multiple?: SinglePanelProps['multiple'];
popupVisible?: boolean;
tableData: any[];
onMonthChange: SinglePanelProps['onMonthChange'] | RangePanelProps['onMonthChange'];
Expand Down Expand Up @@ -104,6 +104,7 @@ export default function PanelContent(props: PanelContentProps) {
time={time}
format={format}
firstDayOfWeek={firstDayOfWeek}
multiple={props.multiple}
onCellClick={(date: Date, { e }) => onCellClick?.(date, { e, partial })}
onCellMouseEnter={(date: Date) => onCellMouseEnter?.(date, { partial })}
onCellMouseLeave={onCellMouseLeave}
Expand Down
2 changes: 1 addition & 1 deletion src/select-input/useMultiple.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ export default function useMultiple(props: SelectInputProps) {

const getTags = () => {
if (!(value instanceof Array)) {
if (['', null, undefined].includes(value as any)) return [];
return isObject(value) ? [value[iKeys.label]] : [value];
}
return value.map((item: SelectInputValue) => (isObject(item) ? item[iKeys.label] : item));
};
const tags = getTags();

const tPlaceholder = !tags || !tags.length ? props.placeholder : '';

const onTagInputChange = (val: TagInputValue, context: SelectInputChangeContext) => {
Expand Down
1 change: 1 addition & 0 deletions src/tag-input/useTagList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export default function useTagList(props: TagInputProps) {

const renderLabel = ({ displayNode, label }: { displayNode: ReactNode; label: ReactNode }) => {
const newList = minCollapsedNum ? tagValue.slice(0, minCollapsedNum) : tagValue;

const list = displayNode
? [<Fragment key="display-node">{displayNode}</Fragment>]
: newList?.map((item, index) => {
Expand Down
Loading

0 comments on commit 74cb32c

Please sign in to comment.