Skip to content

Commit 9f39fd9

Browse files
date and time selection
1 parent 06efeb7 commit 9f39fd9

File tree

2 files changed

+71
-41
lines changed

2 files changed

+71
-41
lines changed

src/components/DatePicker/DateTimePicker.tsx

Lines changed: 63 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
SetStateAction,
55
useCallback,
66
useEffect,
7+
useMemo,
78
useState,
89
} from "react";
910
import { isSameDate, UseCalendarOptions } from "@h6s/calendar";
@@ -13,7 +14,7 @@ import { Body, CalendarRenderer, DateTimePickerInput, DateTableCell } from "./Co
1314
import { Container } from "../Container/Container";
1415
import { Panel } from "../Panel/Panel";
1516
import { Icon } from "../Icon/Icon";
16-
import { DateRangeListItem, datesAreWithinMaxRange, timeFormatter } from "./utils";
17+
import { DateRangeListItem, datesAreWithinMaxRange, Time, timeFormatter } from "./utils";
1718
import dayjs from "dayjs";
1819

1920
const PredefinedCalendarContainer = styled(Panel)`
@@ -59,6 +60,7 @@ const TimesDropdownItem = styled(StyledDropdownItem)`
5960
theme.click.datePicker.dateOption.color.background.default};
6061
`;
6162

63+
// max height calculation: 210px height (matches calendar) + 15px padding top and bottom
6264
const ScrollableTimesContainer = styled(Container)`
6365
background: ${({ theme }) =>
6466
theme.click.datePicker.dateOption.color.background.default};
@@ -163,12 +165,6 @@ const Calendar = ({
163165
if (startDate && fullDate < startDate) {
164166
return;
165167
}
166-
167-
// Only close the datepicker if the user hasn't clicked the selected start date.
168-
if (startDate && !isSameDate(fullDate, startDate)) {
169-
closeDatepicker();
170-
return;
171-
}
172168
};
173169
return (
174170
<DateRangeTableCell
@@ -233,11 +229,8 @@ const PredefinedTimes = ({
233229
onSelectDateRange(startDate, endDate);
234230
};
235231

236-
const rangeIsSelected =
237-
selectedEndDate &&
238-
selectedEndDate === endDate
239-
selectedStartDate &&
240-
selectedStartDate === startDate;
232+
const rangeIsSelected = selectedEndDate && selectedEndDate === endDate;
233+
selectedStartDate && selectedStartDate === startDate;
241234

242235
return (
243236
<StyledDropdownItem
@@ -270,31 +263,42 @@ const PredefinedTimes = ({
270263
);
271264
};
272265

273-
const TimesList = ({ selectedDate = new Date(), setSelectedDate }) => {
266+
interface TimesListProps {
267+
selectedDate: Date;
268+
setSelectedTime: (selectedTime: Time, event: MouseEvent<HTMLElement>) => void;
269+
}
270+
271+
const TimesList = ({
272+
selectedDate = new Date(),
273+
setSelectedTime,
274+
}: TimesListProps) => {
274275
const midnight = dayjs(selectedDate).startOf("day");
275276

276-
const times = [];
277-
for (let i = 0; i < 48; i++) {
278-
const date = midnight.add(i * 30, "minutes").toDate();
277+
const times = useMemo(() => {
278+
const times = [];
279+
for (let i = 0; i < 48; i++) {
280+
const dayjsDate = midnight.add(i * 30, "minutes");
279281

280-
const handleItemClick = useCallback(() => {
281-
setSelectedDate(date);
282-
}, [date, setSelectedDate]);
282+
const handleItemClick = (event: MouseEvent<HTMLElement>) => {
283+
setSelectedTime({ hour: dayjsDate.hour(), minutes: dayjsDate.minute() }, event);
284+
};
283285

284-
times.push(
285-
<TimesDropdownItem
286-
key={date.toISOString()}
287-
onClick={handleItemClick}
288-
>
289-
<Container
290-
justifyContent="space-between"
291-
orientation="horizontal"
286+
times.push(
287+
<TimesDropdownItem
288+
key={dayjsDate.toISOString()}
289+
onClick={handleItemClick}
292290
>
293-
{timeFormatter.format(date)}
294-
</Container>
295-
</TimesDropdownItem>
296-
);
297-
}
291+
<Container
292+
justifyContent="space-between"
293+
orientation="horizontal"
294+
>
295+
{timeFormatter.format(dayjsDate.toDate())}
296+
</Container>
297+
</TimesDropdownItem>
298+
);
299+
}
300+
return times;
301+
}, [midnight, setSelectedTime, timeFormatter]);
298302

299303
return (
300304
<ScrollableTimesContainer orientation="vertical">{times}</ScrollableTimesContainer>
@@ -313,7 +317,6 @@ export interface DateTimePickerProps {
313317
startDate?: Date;
314318
}
315319

316-
// this has started as a copy/paste of daterangepicker
317320
export const DateTimePicker = ({
318321
endDate,
319322
startDate,
@@ -354,13 +357,13 @@ export const DateTimePicker = ({
354357
setShouldShowCustomRange(false);
355358
}, []);
356359

357-
const handleOpenChange = (isOpen: boolean): void => {
360+
const handleOpenChange = useCallback((isOpen: boolean): void => {
358361
setIsOpen(isOpen);
359362

360363
if (!isOpen) {
361364
setShouldShowCustomRange(false);
362365
}
363-
};
366+
}, [isOpen]);
364367

365368
const handleSelectDate = useCallback(
366369
(selectedDate: Date): void => {
@@ -395,7 +398,6 @@ export const DateTimePicker = ({
395398
// Otherwise, set the end date to the date the user clicked.
396399
setSelectedEndDate(selectedDate);
397400
onSelectDateRange(selectedStartDate, selectedDate);
398-
setShouldShowCustomRange(false);
399401
return;
400402
}
401403

@@ -404,6 +406,23 @@ export const DateTimePicker = ({
404406
[futureStartDatesDisabled, onSelectDateRange, selectedEndDate, selectedStartDate]
405407
);
406408

409+
const handleSelectTime = useCallback((time: Time, event: MouseEvent<HTMLElement>) => {
410+
console.log('handleSelectTime', selectedStartDate, selectedEndDate, time);
411+
if (selectedStartDate && selectedEndDate) {
412+
const endDateWithTime = dayjs(selectedEndDate).hour(time.hour).minute(time.minutes).toDate();
413+
setSelectedEndDate(endDateWithTime)
414+
return;
415+
}
416+
417+
// keep the calendar from closing when selecting anything but the end date's time
418+
event.preventDefault();
419+
420+
if (selectedStartDate) {
421+
const startDateWithTime = dayjs(selectedStartDate).hour(time.hour).minute(time.minutes).toDate();
422+
setSelectedStartDate(startDateWithTime)
423+
}
424+
}, [selectedEndDate, selectedStartDate]);
425+
407426
const shouldShowPredefinedTimes =
408427
predefinedTimesList !== undefined && predefinedTimesList.length > 0;
409428

@@ -458,7 +477,10 @@ export const DateTimePicker = ({
458477
/>
459478
)}
460479
</StyledCalendarRenderer>
461-
<TimesList />
480+
<TimesList
481+
setSelectedDate={handleSelectDate}
482+
setSelectedTime={handleSelectTime}
483+
/>
462484
</Container>
463485
</CalendarRendererContainer>
464486
)}
@@ -479,7 +501,10 @@ export const DateTimePicker = ({
479501
/>
480502
)}
481503
</CalendarRenderer>
482-
<TimesList />
504+
<TimesList
505+
setSelectedDate={handleSelectDate}
506+
setSelectedTime={handleSelectTime}
507+
/>
483508
</>
484509
)}
485510
</Container>

src/components/DatePicker/utils.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ export interface DateRange {
55
endDate: Date;
66
}
77

8+
export interface Time {
9+
hour: number;
10+
minutes: number;
11+
}
12+
813
export interface DateRangeListItem {
914
dateRange: DateRange;
1015
label: string;
@@ -22,13 +27,13 @@ export const selectedDateTimeFormatter = new Intl.DateTimeFormat(locale, {
2227
day: "2-digit",
2328
month: "short",
2429
hour: "2-digit",
25-
minute: "2-digit"
30+
minute: "2-digit",
2631
});
2732

2833
export const timeFormatter = new Intl.DateTimeFormat(locale, {
2934
hour: "2-digit",
30-
minute: "2-digit"
31-
})
35+
minute: "2-digit",
36+
});
3237

3338
export const weekdayFormatter = new Intl.DateTimeFormat(locale, { weekday: "short" });
3439
export const headerDateFormatter = new Intl.DateTimeFormat(locale, {

0 commit comments

Comments
 (0)