Skip to content

Commit 3facb84

Browse files
Merge pull request #6079 from Hacker0x01/fix/selectsRange-time-input-issues
fix: support time selection with selectsRange for showTimeInput
2 parents 2a3198e + ac15422 commit 3facb84

File tree

3 files changed

+401
-46
lines changed

3 files changed

+401
-46
lines changed

src/calendar.tsx

Lines changed: 63 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,9 @@ type CalendarProps = React.PropsWithChildren<
157157
> &
158158
Omit<TimeProps, "onChange" | "format" | "intervals" | "monthRef"> &
159159
Omit<InputTimeProps, "date" | "timeString" | "onChange"> & {
160+
selectsRange?: boolean;
161+
startDate?: Date | null;
162+
endDate?: Date | null;
160163
className?: string;
161164
container?: React.ElementType;
162165
showYearPicker?: boolean;
@@ -217,7 +220,7 @@ type CalendarProps = React.PropsWithChildren<
217220
| React.KeyboardEvent<HTMLLIElement>
218221
| React.KeyboardEvent<HTMLButtonElement>,
219222
) => void;
220-
onTimeChange?: TimeProps["onChange"] | InputTimeProps["onChange"];
223+
onTimeChange?: (time: Date, modifyDateType?: "start" | "end") => void;
221224
timeFormat?: TimeProps["format"];
222225
timeIntervals?: TimeProps["intervals"];
223226
} & (
@@ -1178,25 +1181,72 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
11781181
};
11791182

11801183
renderInputTimeSection = (): React.ReactElement | undefined => {
1184+
if (!this.props.showTimeInput) {
1185+
return;
1186+
}
1187+
1188+
// Handle selectsRange mode - render two time inputs
1189+
if (this.props.selectsRange) {
1190+
const { startDate, endDate } = this.props;
1191+
1192+
const startTime = startDate ? new Date(startDate) : undefined;
1193+
const startTimeValid =
1194+
startTime && isValid(startTime) && Boolean(startDate);
1195+
const startTimeString = startTimeValid
1196+
? `${addZero(startTime.getHours())}:${addZero(startTime.getMinutes())}`
1197+
: "";
1198+
1199+
const endTime = endDate ? new Date(endDate) : undefined;
1200+
const endTimeValid = endTime && isValid(endTime) && Boolean(endDate);
1201+
const endTimeString = endTimeValid
1202+
? `${addZero(endTime.getHours())}:${addZero(endTime.getMinutes())}`
1203+
: "";
1204+
1205+
return (
1206+
<>
1207+
<InputTime
1208+
{...Calendar.defaultProps}
1209+
{...this.props}
1210+
date={startTime}
1211+
timeString={startTimeString}
1212+
onChange={(time: Date) => {
1213+
this.props.onTimeChange?.(time, "start");
1214+
}}
1215+
timeInputLabel={(this.props.timeInputLabel ?? "Time") + " (Start)"}
1216+
/>
1217+
<InputTime
1218+
{...Calendar.defaultProps}
1219+
{...this.props}
1220+
date={endTime}
1221+
timeString={endTimeString}
1222+
onChange={(time: Date) => {
1223+
this.props.onTimeChange?.(time, "end");
1224+
}}
1225+
timeInputLabel={(this.props.timeInputLabel ?? "Time") + " (End)"}
1226+
/>
1227+
</>
1228+
);
1229+
}
1230+
1231+
// Single date mode (original behavior)
11811232
const time = this.props.selected
11821233
? new Date(this.props.selected)
11831234
: undefined;
11841235
const timeValid = time && isValid(time) && Boolean(this.props.selected);
11851236
const timeString = timeValid
11861237
? `${addZero(time.getHours())}:${addZero(time.getMinutes())}`
11871238
: "";
1188-
if (this.props.showTimeInput) {
1189-
return (
1190-
<InputTime
1191-
{...Calendar.defaultProps}
1192-
{...this.props}
1193-
date={time}
1194-
timeString={timeString}
1195-
onChange={this.props.onTimeChange}
1196-
/>
1197-
);
1198-
}
1199-
return;
1239+
return (
1240+
<InputTime
1241+
{...Calendar.defaultProps}
1242+
{...this.props}
1243+
date={time}
1244+
timeString={timeString}
1245+
onChange={(time: Date) => {
1246+
this.props.onTimeChange?.(time);
1247+
}}
1248+
/>
1249+
);
12001250
};
12011251

12021252
renderAriaLiveRegion = (): React.ReactElement => {

src/index.tsx

Lines changed: 63 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,7 +1007,7 @@ export class DatePicker extends Component<DatePickerProps, DatePickerState> {
10071007
this.setOpen(!this.state.open);
10081008
};
10091009

1010-
handleTimeChange = (time: Date): void => {
1010+
handleTimeChange = (time: Date, modifyDateType?: "start" | "end"): void => {
10111011
if (this.props.selectsMultiple) {
10121012
return;
10131013
}
@@ -1016,39 +1016,69 @@ export class DatePicker extends Component<DatePickerProps, DatePickerState> {
10161016

10171017
if (selectsRange) {
10181018
// In range mode, apply time to the appropriate date
1019-
// If we have a startDate but no endDate, apply time to startDate
1020-
// If we have both, apply time to endDate
1021-
const hasStartRange = startDate && !endDate;
1022-
1023-
if (hasStartRange) {
1024-
// Apply time to startDate
1025-
const changedStartDate = setTime(startDate, {
1026-
hour: getHours(time),
1027-
minute: getMinutes(time),
1028-
});
1029-
this.setState({
1030-
preSelection: changedStartDate,
1031-
});
1032-
onChange?.([changedStartDate, null], undefined);
1033-
} else if (startDate && endDate) {
1034-
// Apply time to endDate
1035-
const changedEndDate = setTime(endDate, {
1036-
hour: getHours(time),
1037-
minute: getMinutes(time),
1038-
});
1039-
this.setState({
1040-
preSelection: changedEndDate,
1041-
});
1042-
onChange?.([startDate, changedEndDate], undefined);
1019+
// If modifyDateType is specified, use that to determine which date to modify
1020+
// Otherwise, use the legacy behavior:
1021+
// - If we have a startDate but no endDate, apply time to startDate
1022+
// - If we have both, apply time to endDate
1023+
1024+
if (modifyDateType === "start") {
1025+
// Explicitly modify start date
1026+
if (startDate) {
1027+
const changedStartDate = setTime(startDate, {
1028+
hour: getHours(time),
1029+
minute: getMinutes(time),
1030+
});
1031+
this.setState({
1032+
preSelection: changedStartDate,
1033+
});
1034+
onChange?.([changedStartDate, endDate ?? null], undefined);
1035+
}
1036+
} else if (modifyDateType === "end") {
1037+
// Explicitly modify end date
1038+
if (endDate) {
1039+
const changedEndDate = setTime(endDate, {
1040+
hour: getHours(time),
1041+
minute: getMinutes(time),
1042+
});
1043+
this.setState({
1044+
preSelection: changedEndDate,
1045+
});
1046+
onChange?.([startDate ?? null, changedEndDate], undefined);
1047+
}
10431048
} else {
1044-
// No dates selected yet, just update preSelection
1045-
const changedDate = setTime(this.getPreSelection(), {
1046-
hour: getHours(time),
1047-
minute: getMinutes(time),
1048-
});
1049-
this.setState({
1050-
preSelection: changedDate,
1051-
});
1049+
// Legacy behavior for showTimeSelect (single time picker)
1050+
const hasStartRange = startDate && !endDate;
1051+
1052+
if (hasStartRange) {
1053+
// Apply time to startDate
1054+
const changedStartDate = setTime(startDate, {
1055+
hour: getHours(time),
1056+
minute: getMinutes(time),
1057+
});
1058+
this.setState({
1059+
preSelection: changedStartDate,
1060+
});
1061+
onChange?.([changedStartDate, null], undefined);
1062+
} else if (startDate && endDate) {
1063+
// Apply time to endDate
1064+
const changedEndDate = setTime(endDate, {
1065+
hour: getHours(time),
1066+
minute: getMinutes(time),
1067+
});
1068+
this.setState({
1069+
preSelection: changedEndDate,
1070+
});
1071+
onChange?.([startDate, changedEndDate], undefined);
1072+
} else {
1073+
// No dates selected yet, just update preSelection
1074+
const changedDate = setTime(this.getPreSelection(), {
1075+
hour: getHours(time),
1076+
minute: getMinutes(time),
1077+
});
1078+
this.setState({
1079+
preSelection: changedDate,
1080+
});
1081+
}
10521082
}
10531083
} else {
10541084
// Single date mode (original behavior)

0 commit comments

Comments
 (0)