44 SetStateAction ,
55 useCallback ,
66 useEffect ,
7+ useMemo ,
78 useState ,
89} from "react" ;
910import { isSameDate , UseCalendarOptions } from "@h6s/calendar" ;
@@ -13,7 +14,7 @@ import { Body, CalendarRenderer, DateTimePickerInput, DateTableCell } from "./Co
1314import { Container } from "../Container/Container" ;
1415import { Panel } from "../Panel/Panel" ;
1516import { Icon } from "../Icon/Icon" ;
16- import { DateRangeListItem , datesAreWithinMaxRange , timeFormatter } from "./utils" ;
17+ import { DateRangeListItem , datesAreWithinMaxRange , Time , timeFormatter } from "./utils" ;
1718import dayjs from "dayjs" ;
1819
1920const 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
6264const 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
317320export 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 >
0 commit comments