Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 63 additions & 13 deletions src/calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ type CalendarProps = React.PropsWithChildren<
> &
Omit<TimeProps, "onChange" | "format" | "intervals" | "monthRef"> &
Omit<InputTimeProps, "date" | "timeString" | "onChange"> & {
selectsRange?: boolean;
startDate?: Date | null;
endDate?: Date | null;
className?: string;
container?: React.ElementType;
showYearPicker?: boolean;
Expand Down Expand Up @@ -205,7 +208,7 @@ type CalendarProps = React.PropsWithChildren<
| React.KeyboardEvent<HTMLLIElement>
| React.KeyboardEvent<HTMLButtonElement>,
) => void;
onTimeChange?: TimeProps["onChange"] | InputTimeProps["onChange"];
onTimeChange?: (time: Date, modifyDateType?: "start" | "end") => void;
timeFormat?: TimeProps["format"];
timeIntervals?: TimeProps["intervals"];
} & (
Expand Down Expand Up @@ -1107,25 +1110,72 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
};

renderInputTimeSection = (): React.ReactElement | undefined => {
if (!this.props.showTimeInput) {
return;
}

// Handle selectsRange mode - render two time inputs
if (this.props.selectsRange) {
const { startDate, endDate } = this.props;

const startTime = startDate ? new Date(startDate) : undefined;
const startTimeValid =
startTime && isValid(startTime) && Boolean(startDate);
const startTimeString = startTimeValid
? `${addZero(startTime.getHours())}:${addZero(startTime.getMinutes())}`
: "";

const endTime = endDate ? new Date(endDate) : undefined;
const endTimeValid = endTime && isValid(endTime) && Boolean(endDate);
const endTimeString = endTimeValid
? `${addZero(endTime.getHours())}:${addZero(endTime.getMinutes())}`
: "";

return (
<>
<InputTime
{...Calendar.defaultProps}
{...this.props}
date={startTime}
timeString={startTimeString}
onChange={(time: Date) => {
this.props.onTimeChange?.(time, "start");
}}
timeInputLabel={(this.props.timeInputLabel ?? "Time") + " (Start)"}
/>
<InputTime
{...Calendar.defaultProps}
{...this.props}
date={endTime}
timeString={endTimeString}
onChange={(time: Date) => {
this.props.onTimeChange?.(time, "end");
}}
timeInputLabel={(this.props.timeInputLabel ?? "Time") + " (End)"}
/>
</>
);
}

// Single date mode (original behavior)
const time = this.props.selected
? new Date(this.props.selected)
: undefined;
const timeValid = time && isValid(time) && Boolean(this.props.selected);
const timeString = timeValid
? `${addZero(time.getHours())}:${addZero(time.getMinutes())}`
: "";
if (this.props.showTimeInput) {
return (
<InputTime
{...Calendar.defaultProps}
{...this.props}
date={time}
timeString={timeString}
onChange={this.props.onTimeChange}
/>
);
}
return;
return (
<InputTime
{...Calendar.defaultProps}
{...this.props}
date={time}
timeString={timeString}
onChange={(time: Date) => {
this.props.onTimeChange?.(time);
}}
/>
);
};

renderAriaLiveRegion = (): React.ReactElement => {
Expand Down
96 changes: 63 additions & 33 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -963,7 +963,7 @@ export class DatePicker extends Component<DatePickerProps, DatePickerState> {
this.setOpen(!this.state.open);
};

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

if (selectsRange) {
// In range mode, apply time to the appropriate date
// If we have a startDate but no endDate, apply time to startDate
// If we have both, apply time to endDate
const hasStartRange = startDate && !endDate;

if (hasStartRange) {
// Apply time to startDate
const changedStartDate = setTime(startDate, {
hour: getHours(time),
minute: getMinutes(time),
});
this.setState({
preSelection: changedStartDate,
});
onChange?.([changedStartDate, null], undefined);
} else if (startDate && endDate) {
// Apply time to endDate
const changedEndDate = setTime(endDate, {
hour: getHours(time),
minute: getMinutes(time),
});
this.setState({
preSelection: changedEndDate,
});
onChange?.([startDate, changedEndDate], undefined);
// If modifyDateType is specified, use that to determine which date to modify
// Otherwise, use the legacy behavior:
// - If we have a startDate but no endDate, apply time to startDate
// - If we have both, apply time to endDate

if (modifyDateType === "start") {
// Explicitly modify start date
if (startDate) {
const changedStartDate = setTime(startDate, {
hour: getHours(time),
minute: getMinutes(time),
});
this.setState({
preSelection: changedStartDate,
});
onChange?.([changedStartDate, endDate ?? null], undefined);
}
} else if (modifyDateType === "end") {
// Explicitly modify end date
if (endDate) {
const changedEndDate = setTime(endDate, {
hour: getHours(time),
minute: getMinutes(time),
});
this.setState({
preSelection: changedEndDate,
});
onChange?.([startDate ?? null, changedEndDate], undefined);
}
} else {
// No dates selected yet, just update preSelection
const changedDate = setTime(this.getPreSelection(), {
hour: getHours(time),
minute: getMinutes(time),
});
this.setState({
preSelection: changedDate,
});
// Legacy behavior for showTimeSelect (single time picker)
const hasStartRange = startDate && !endDate;

if (hasStartRange) {
// Apply time to startDate
const changedStartDate = setTime(startDate, {
hour: getHours(time),
minute: getMinutes(time),
});
this.setState({
preSelection: changedStartDate,
});
onChange?.([changedStartDate, null], undefined);
} else if (startDate && endDate) {
// Apply time to endDate
const changedEndDate = setTime(endDate, {
hour: getHours(time),
minute: getMinutes(time),
});
this.setState({
preSelection: changedEndDate,
});
onChange?.([startDate, changedEndDate], undefined);
} else {
// No dates selected yet, just update preSelection
const changedDate = setTime(this.getPreSelection(), {
hour: getHours(time),
minute: getMinutes(time),
});
this.setState({
preSelection: changedDate,
});
}
}
} else {
// Single date mode (original behavior)
Expand Down
Loading
Loading