Skip to content

Commit 51930c1

Browse files
committed
refactor: add useRangeViewDate hook for complex view date logic
1 parent 2881554 commit 51930c1

File tree

4 files changed

+151
-71
lines changed

4 files changed

+151
-71
lines changed

examples/range.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,10 @@ export default () => {
5858
locale={zhCN}
5959
allowClear
6060
ref={rangePickerRef}
61-
defaultValue={[moment('1990-09-03'), moment('1989-11-28')]}
61+
disabled={[false, true]}
62+
defaultValue={[null, moment('1990-09-22')]}
6263
/>
63-
<RangePicker<Moment>
64+
{/* <RangePicker<Moment>
6465
{...sharedProps}
6566
locale={zhCN}
6667
allowClear
@@ -70,10 +71,10 @@ export default () => {
7071
ranges={{
7172
ranges: [moment(), moment().add(10, 'day')],
7273
}}
73-
/>
74+
/> */}
7475
</div>
7576

76-
<div style={{ margin: '0 8px' }}>
77+
{/* <div style={{ margin: '0 8px' }}>
7778
<h3>Focus</h3>
7879
<RangePicker<Moment>
7980
{...sharedProps}
@@ -147,7 +148,7 @@ export default () => {
147148
allowEmpty={[false, true]}
148149
renderExtraFooter={() => <div>extra footer</div>}
149150
/>
150-
</div>
151+
</div> */}
151152
</div>
152153
</div>
153154
);

src/RangePicker.tsx

Lines changed: 16 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,15 @@ import getDataOrAriaProps, {
2626
} from './utils/miscUtil';
2727
import { getDefaultFormat, getInputSize } from './utils/uiUtil';
2828
import PanelContext, { ContextOperationRefProps } from './PanelContext';
29-
import {
30-
isEqual,
31-
getClosingViewDate,
32-
isSameMonth,
33-
isSameYear,
34-
isSameDate,
35-
} from './utils/dateUtil';
29+
import { isEqual, getClosingViewDate, isSameDate } from './utils/dateUtil';
3630
import useValueTexts from './hooks/useValueTexts';
3731
import useTextValueMapping from './hooks/useTextValueMapping';
3832
import { GenerateConfig } from './generate';
3933
import { PickerPanelProps } from '.';
4034
import RangeContext from './RangeContext';
4135
import useRangeDisabled from './hooks/useRangeDisabled';
4236
import getExtraFooter from './utils/getExtraFooter';
37+
import useRangeViewDates from './hooks/useRangeViewDates';
4338

4439
function reorderValues<DateType>(
4540
values: RangeValue<DateType>,
@@ -252,51 +247,12 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
252247
});
253248

254249
// =========================== View Date ===========================
255-
/**
256-
* End view date is use right panel by default.
257-
* But when they in same month (date picker) or year (month picker), will both use left panel.
258-
*/
259-
function getEndViewDate(viewDate: DateType, values: RangeValue<DateType>) {
260-
let compareFunc: (
261-
generateConfig: GenerateConfig<DateType>,
262-
date1: DateType | null,
263-
date2: DateType | null,
264-
) => boolean = isSameMonth;
265-
266-
if (picker === 'month') {
267-
compareFunc = isSameYear;
268-
}
269-
270-
if (compareFunc(generateConfig, getValue(values, 0), getValue(values, 1))) {
271-
return viewDate;
272-
}
273-
return getClosingViewDate(viewDate, picker, generateConfig, -1);
274-
}
275-
276250
// Config view panel
277-
const [viewDates, setViewDates] = useMergedState<
278-
RangeValue<DateType>,
279-
[DateType, DateType]
280-
>({
281-
defaultValue: () =>
282-
defaultPickerValue ||
283-
updateValues(
284-
mergedValue,
285-
(viewDate: DateType) => getEndViewDate(viewDate, mergedValue),
286-
1,
287-
),
288-
defaultStateValue: null,
289-
postState: postViewDates => {
290-
let startViewDate: DateType | null =
291-
getValue(postViewDates, 0) || getValue(mergedValue, 0);
292-
let endViewDate: DateType | null =
293-
getValue(postViewDates, 1) || getValue(mergedValue, 1);
294-
295-
startViewDate = startViewDate || endViewDate || generateConfig.getNow();
296-
endViewDate = endViewDate || startViewDate || generateConfig.getNow();
297-
298-
return [startViewDate, endViewDate];
299-
},
251+
const [getViewDate, setViewDate] = useRangeViewDates({
252+
values: mergedValue,
253+
picker,
254+
defaultDates: defaultPickerValue,
255+
generateConfig,
300256
});
301257

302258
// ========================= Select Values =========================
@@ -418,7 +374,7 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
418374
values = [startValue, null];
419375
endValue = null;
420376

421-
setViewDates(updateValues(viewDates, startValue, 1));
377+
// TODO: setViewDates1(updateValues(viewDates1, startValue, 1));
422378
}
423379

424380
setSelectedValue(values);
@@ -490,6 +446,7 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
490446
if (!preventChangeEvent) {
491447
triggerChange(selectedValue, { source: 'open' });
492448
}
449+
setViewDate(null, index);
493450
}
494451
};
495452

@@ -538,7 +495,7 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
538495

539496
if (inputDate && !disabledFunc(inputDate)) {
540497
setSelectedValue(updateValues(selectedValue, inputDate, index));
541-
setViewDates(updateValues(viewDates, inputDate, index));
498+
setViewDate(inputDate, index);
542499
}
543500
};
544501

@@ -758,7 +715,7 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
758715
updateValues(selectedValue, date, activePickerIndex),
759716
);
760717

761-
setViewDates(updateValues(viewDates, date, activePickerIndex));
718+
setViewDate(date, activePickerIndex);
762719
}}
763720
onSelect={undefined}
764721
onChange={undefined}
@@ -798,7 +755,7 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
798755
);
799756

800757
if (picker !== 'time' && !showTime) {
801-
const viewDate = viewDates[activePickerIndex];
758+
const viewDate = getViewDate(activePickerIndex);
802759
const nextViewDate = getClosingViewDate(viewDate, picker, generateConfig);
803760
const currentMode = mergedModes[activePickerIndex];
804761

@@ -809,21 +766,16 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
809766
{renderPanel(showDoublePanel ? 'left' : false, {
810767
pickerValue: viewDate,
811768
onPickerValueChange: newViewDate => {
812-
setViewDates(
813-
updateValues(viewDates, newViewDate, activePickerIndex),
814-
);
769+
setViewDate(newViewDate, activePickerIndex);
815770
},
816771
})}
817772
{showDoublePanel &&
818773
renderPanel('right', {
819774
pickerValue: nextViewDate,
820775
onPickerValueChange: newViewDate => {
821-
setViewDates(
822-
updateValues(
823-
viewDates,
824-
getClosingViewDate(newViewDate, picker, generateConfig, -1),
825-
activePickerIndex,
826-
),
776+
setViewDate(
777+
getClosingViewDate(newViewDate, picker, generateConfig, -1),
778+
activePickerIndex,
827779
);
828780
},
829781
})}

src/hooks/useRangeViewDates.ts

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import * as React from 'react';
2+
import { RangeValue, PickerMode } from '../interface';
3+
import { GenerateConfig } from '../generate';
4+
import { getValue, updateValues } from '../utils/miscUtil';
5+
import {
6+
getClosingViewDate,
7+
isSameYear,
8+
isSameMonth,
9+
isSameDate,
10+
} from '../utils/dateUtil';
11+
12+
function isClosingViewDate<DateType>(
13+
start: DateType | null,
14+
end: DateType | null,
15+
picker: PickerMode,
16+
generateConfig: GenerateConfig<DateType>,
17+
) {
18+
// Skip when no compared
19+
if (!start || !end) {
20+
return true;
21+
}
22+
23+
const startPrev = getClosingViewDate(start, picker, generateConfig, -1);
24+
const startNext = getClosingViewDate(start, picker, generateConfig, 1);
25+
26+
function isSameCompare(compareFunc: (source: DateType | null) => boolean) {
27+
return [startPrev, start, startNext].some(date => compareFunc(date));
28+
}
29+
30+
switch (picker) {
31+
case 'year':
32+
return isSameCompare(source => isSameYear(generateConfig, source, end));
33+
34+
case 'month':
35+
return isSameCompare(source => isSameMonth(generateConfig, source, end));
36+
37+
default:
38+
return isSameCompare(source => isSameDate(generateConfig, source, end));
39+
}
40+
}
41+
42+
function getRangeViewDate<DateType>(
43+
values: RangeValue<DateType>,
44+
index: 0 | 1,
45+
picker: PickerMode,
46+
generateConfig: GenerateConfig<DateType>,
47+
): DateType | null {
48+
const startDate = getValue(values, 0);
49+
const endDate = getValue(values, 1);
50+
51+
if (index === 0) {
52+
return startDate;
53+
}
54+
55+
if (endDate) {
56+
if (!isClosingViewDate(startDate, endDate, picker, generateConfig)) {
57+
return getClosingViewDate(endDate, picker, generateConfig, -1);
58+
}
59+
return endDate;
60+
}
61+
62+
return null;
63+
}
64+
65+
export default function useRangeViewDates<DateType>({
66+
values,
67+
picker,
68+
defaultDates,
69+
generateConfig,
70+
}: {
71+
values: RangeValue<DateType>;
72+
picker: PickerMode;
73+
defaultDates: RangeValue<DateType> | undefined;
74+
generateConfig: GenerateConfig<DateType>;
75+
}): [
76+
(activePickerIndex: 0 | 1) => DateType,
77+
(viewDate: DateType | null, index: 0 | 1) => void,
78+
] {
79+
const [defaultViewDates, setDefaultViewDates] = React.useState<
80+
[DateType | null, DateType | null]
81+
>(() => [getValue(defaultDates, 0), getValue(defaultDates, 1)]);
82+
const [viewDates, setInternalViewDates] = React.useState<
83+
RangeValue<DateType>
84+
>(null);
85+
86+
const startDate = getValue(values, 0);
87+
const endDate = getValue(values, 1);
88+
89+
function getViewDate(index: 0 | 1): DateType {
90+
// If set default view date, use it
91+
if (defaultViewDates[index]) {
92+
return defaultViewDates[index]!;
93+
}
94+
95+
return (
96+
getValue(viewDates, index) ||
97+
getRangeViewDate(values, index, picker, generateConfig) ||
98+
startDate ||
99+
endDate ||
100+
generateConfig.getNow()
101+
);
102+
}
103+
104+
function setViewDate(viewDate: DateType | null, index: 0 | 1) {
105+
if (viewDate) {
106+
let newViewDates = updateValues(viewDates, viewDate, index);
107+
// Set view date will clean up default one
108+
setDefaultViewDates(
109+
// Should always be an array
110+
updateValues(defaultViewDates, null, index) || [null, null],
111+
);
112+
113+
// Reset another one when not have value
114+
const anotherIndex = (index + 1) % 2;
115+
if (getValue(values, anotherIndex)) {
116+
newViewDates = updateValues(newViewDates, viewDate, anotherIndex);
117+
}
118+
119+
setInternalViewDates(newViewDates);
120+
} else if (startDate || endDate) {
121+
// Reset all when has values when `viewDate` is `null` which means from open trigger
122+
setInternalViewDates(null);
123+
}
124+
}
125+
126+
return [getViewDate, setViewDate];
127+
}

tests/range.spec.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,13 +237,13 @@ describe('Picker.Range', () => {
237237

238238
// Disabled date
239239
wrapper.openPicker();
240-
cellNode = wrapper.selectCell(25, 1);
240+
cellNode = wrapper.selectCell(25);
241241
expect(cellNode.hasClass('rc-picker-cell-disabled')).toBeTruthy();
242242
expect(onChange).not.toHaveBeenCalled();
243243

244244
// Enabled date
245245
wrapper.openPicker();
246-
cellNode = wrapper.selectCell(7, 1);
246+
cellNode = wrapper.selectCell(7);
247247
expect(cellNode.hasClass('rc-picker-cell-disabled')).toBeFalsy();
248248
expect(onChange).toHaveBeenCalledWith(
249249
[expect.anything(), expect.anything()],

0 commit comments

Comments
 (0)