Skip to content

Commit a105cc8

Browse files
authored
fix: dynamic disabledTime should be correct (#642)
* fix: dynamic disabledTime should be correct * fix: 修改 * fix: 修改 * fix: 注释 * fix: useTimeWithDisabled * fix: hook params rename * fix: rename hook
1 parent b28702b commit a105cc8

File tree

3 files changed

+165
-42
lines changed

3 files changed

+165
-42
lines changed

src/hooks/useTimeSelection.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import type { GenerateConfig } from '../generate';
2+
import type { Unit } from '../panels/TimePanel/TimeUnitColumn';
3+
import { setTime as utilSetTime } from '../utils/timeUtil';
4+
5+
export default function useTimeSelection<DateType>({
6+
value,
7+
generateConfig,
8+
disabledMinutes,
9+
disabledSeconds,
10+
minutes,
11+
seconds,
12+
use12Hours,
13+
}: {
14+
value: DateType;
15+
generateConfig: GenerateConfig<DateType>;
16+
disabledMinutes: (hour: number) => number[];
17+
disabledSeconds: (hour: number, minute: number) => number[];
18+
minutes: Unit[];
19+
seconds: Unit[];
20+
use12Hours: boolean;
21+
}) {
22+
const setTime = (
23+
isNewPM: boolean | undefined,
24+
newHour: number,
25+
newMinute: number,
26+
newSecond: number,
27+
) => {
28+
let newDate = value || generateConfig.getNow();
29+
30+
const mergedHour = Math.max(0, newHour);
31+
let mergedMinute = Math.max(0, newMinute);
32+
let mergedSecond = Math.max(0, newSecond);
33+
34+
const newDisabledMinutes = disabledMinutes && disabledMinutes(mergedHour);
35+
if (newDisabledMinutes?.includes(mergedMinute)) {
36+
// find the first available minute in minutes
37+
const availableMinute = minutes.find((i) => !newDisabledMinutes.includes(i.value));
38+
if (availableMinute) {
39+
mergedMinute = availableMinute.value;
40+
} else {
41+
return null;
42+
}
43+
}
44+
const newDisabledSeconds = disabledSeconds && disabledSeconds(mergedHour, mergedMinute);
45+
if (newDisabledSeconds?.includes(mergedSecond)) {
46+
// find the first available second in seconds
47+
const availableSecond = seconds.find((i) => !newDisabledSeconds.includes(i.value));
48+
if (availableSecond) {
49+
mergedSecond = availableSecond.value;
50+
} else {
51+
return null;
52+
}
53+
}
54+
55+
newDate = utilSetTime(
56+
generateConfig,
57+
newDate,
58+
!use12Hours || !isNewPM ? mergedHour : mergedHour + 12,
59+
mergedMinute,
60+
mergedSecond,
61+
);
62+
63+
return newDate;
64+
};
65+
66+
return setTime;
67+
}

src/panels/TimePanel/TimeBody.tsx

Lines changed: 52 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import * as React from 'react';
21
import useMemo from 'rc-util/lib/hooks/useMemo';
2+
import * as React from 'react';
3+
import type { SharedTimeProps } from '.';
34
import type { GenerateConfig } from '../../generate';
5+
import useTimeSelection from '../../hooks/useTimeSelection';
46
import type { CellRender, Locale, OnSelect } from '../../interface';
7+
import { leftPad } from '../../utils/miscUtil';
58
import type { Unit } from './TimeUnitColumn';
69
import TimeUnitColumn from './TimeUnitColumn';
7-
import { leftPad } from '../../utils/miscUtil';
8-
import type { SharedTimeProps } from '.';
9-
import { setTime as utilSetTime } from '../../utils/timeUtil';
1010

1111
function shouldUnitsUpdate(prevUnits: Unit[], nextUnits: Unit[]) {
1212
if (prevUnits.length !== nextUnits.length) return true;
@@ -24,7 +24,7 @@ function generateUnits(
2424
disabledUnits: number[] | undefined,
2525
) {
2626
const units: Unit[] = [];
27-
const integerStep = step >= 1 ? step | 0 : 1
27+
const integerStep = step >= 1 ? step | 0 : 1;
2828
for (let i = start; i <= end; i += integerStep) {
2929
units.push({
3030
label: leftPad(i, 2),
@@ -105,30 +105,6 @@ function TimeBody<DateType>(props: TimeBodyProps<DateType>) {
105105
return [disabledHours, disabledMinutes, disabledSeconds];
106106
}, [disabledHours, disabledMinutes, disabledSeconds, disabledTime, now]);
107107

108-
// Set Time
109-
const setTime = (
110-
isNewPM: boolean | undefined,
111-
newHour: number,
112-
newMinute: number,
113-
newSecond: number,
114-
) => {
115-
let newDate = value || generateConfig.getNow();
116-
117-
const mergedHour = Math.max(0, newHour);
118-
const mergedMinute = Math.max(0, newMinute);
119-
const mergedSecond = Math.max(0, newSecond);
120-
121-
newDate = utilSetTime(
122-
generateConfig,
123-
newDate,
124-
!use12Hours || !isNewPM ? mergedHour : mergedHour + 12,
125-
mergedMinute,
126-
mergedSecond,
127-
);
128-
129-
return newDate;
130-
};
131-
132108
// ========================= Unit =========================
133109
const rawHours = generateUnits(0, 23, hourStep, mergedDisabledHours && mergedDisabledHours());
134110

@@ -185,6 +161,17 @@ function TimeBody<DateType>(props: TimeBodyProps<DateType>) {
185161
mergedDisabledSeconds && mergedDisabledSeconds(originHour, minute),
186162
);
187163

164+
// Set Time
165+
const setTime = useTimeSelection({
166+
value,
167+
generateConfig,
168+
disabledMinutes: mergedDisabledMinutes,
169+
disabledSeconds: mergedDisabledSeconds,
170+
minutes,
171+
seconds,
172+
use12Hours,
173+
});
174+
188175
// ====================== Operations ======================
189176
operationRef.current = {
190177
onUpDown: (diff) => {
@@ -231,19 +218,45 @@ function TimeBody<DateType>(props: TimeBodyProps<DateType>) {
231218
}
232219

233220
// Hour
234-
addColumnNode(showHour, <TimeUnitColumn<DateType> key="hour" type="hour" info={{today: now, locale, cellRender }} />, hour, hours, (num) => {
235-
onSelect(setTime(isPM, num, minute, second), 'mouse');
236-
});
221+
addColumnNode(
222+
showHour,
223+
<TimeUnitColumn<DateType> key="hour" type="hour" info={{ today: now, locale, cellRender }} />,
224+
hour,
225+
hours,
226+
(num) => {
227+
onSelect(setTime(isPM, num, minute, second), 'mouse');
228+
},
229+
);
237230

238231
// Minute
239-
addColumnNode(showMinute, <TimeUnitColumn<DateType> key="minute" type="minute" info={{today: now, locale, cellRender }} />, minute, minutes, (num) => {
240-
onSelect(setTime(isPM, hour, num, second), 'mouse');
241-
});
232+
addColumnNode(
233+
showMinute,
234+
<TimeUnitColumn<DateType>
235+
key="minute"
236+
type="minute"
237+
info={{ today: now, locale, cellRender }}
238+
/>,
239+
minute,
240+
minutes,
241+
(num) => {
242+
onSelect(setTime(isPM, hour, num, second), 'mouse');
243+
},
244+
);
242245

243246
// Second
244-
addColumnNode(showSecond, <TimeUnitColumn<DateType> key="second" type="second" info={{today: now, locale, cellRender }} />, second, seconds, (num) => {
245-
onSelect(setTime(isPM, hour, minute, num), 'mouse');
246-
});
247+
addColumnNode(
248+
showSecond,
249+
<TimeUnitColumn<DateType>
250+
key="second"
251+
type="second"
252+
info={{ today: now, locale, cellRender }}
253+
/>,
254+
second,
255+
seconds,
256+
(num) => {
257+
onSelect(setTime(isPM, hour, minute, num), 'mouse');
258+
},
259+
);
247260

248261
// 12 Hours
249262
let PMIndex = -1;
@@ -253,7 +266,7 @@ function TimeBody<DateType>(props: TimeBodyProps<DateType>) {
253266

254267
addColumnNode(
255268
use12Hours === true,
256-
<TimeUnitColumn key="meridiem" type="meridiem" info={{today: now, locale, cellRender }} />,
269+
<TimeUnitColumn key="meridiem" type="meridiem" info={{ today: now, locale, cellRender }} />,
257270
PMIndex,
258271
[
259272
{ label: 'AM', value: 0, disabled: AMDisabled },

tests/disabledTime.spec.tsx

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,7 @@ describe('Picker.DisabledTime', () => {
7373
// Start
7474
openPicker(container);
7575
expect(
76-
document
77-
.querySelector('.rc-picker-time-panel-column')
78-
.querySelectorAll('li')[11],
76+
document.querySelector('.rc-picker-time-panel-column').querySelectorAll('li')[11],
7977
).toHaveClass('rc-picker-time-panel-cell-disabled');
8078
expect(isSame(disabledTime.mock.calls[0][0], '1989-11-28')).toBeTruthy();
8179
expect(disabledTime.mock.calls[0][1]).toEqual('start');
@@ -93,6 +91,51 @@ describe('Picker.DisabledTime', () => {
9391
closePicker(container, 1);
9492
});
9593

94+
it('dynamic disabledTime should be correct', () => {
95+
render(
96+
<MomentPicker
97+
open
98+
picker="time"
99+
disabledTime={() => ({
100+
disabledHours: () => [0, 1],
101+
disabledMinutes: (selectedHour) => {
102+
if (selectedHour === 2) {
103+
return [0, 1];
104+
} else {
105+
return [];
106+
}
107+
},
108+
disabledSeconds: (_, selectMinute) => {
109+
if (selectMinute === 2) {
110+
return [0, 1];
111+
} else {
112+
return [];
113+
}
114+
},
115+
})}
116+
/>,
117+
);
118+
// click hour 3
119+
fireEvent.click(
120+
document.querySelectorAll('.rc-picker-time-panel-column')[0].querySelectorAll('li')[2],
121+
);
122+
// click minute 0
123+
fireEvent.click(
124+
document.querySelectorAll('.rc-picker-time-panel-column')[1].querySelectorAll('li')[0],
125+
);
126+
// click second 0
127+
fireEvent.click(
128+
document.querySelectorAll('.rc-picker-time-panel-column')[2].querySelectorAll('li')[0],
129+
);
130+
// click hour 2
131+
fireEvent.click(
132+
document.querySelectorAll('.rc-picker-time-panel-column')[0].querySelectorAll('li')[1],
133+
);
134+
expect(document.querySelector('.rc-picker-input input').getAttribute('value')).toEqual(
135+
'02:02:02',
136+
);
137+
});
138+
96139
describe('warning for legacy props', () => {
97140
it('single', () => {
98141
resetWarned();

0 commit comments

Comments
 (0)