[Feature] Add timepicker to calendar (#5183)

* added timepicker as children to calendar

* new timepicker component

* Add timepicker

* new timePicker component

* timepicker as calendar child

* hover:border and rounded

* adjusted width

* complete rework

* more code comments

* memorization

* preselect hover, transition

* numberOfDaysSelected has minimum of 1

* prefill hours when component mounts

* persist hours when component mount

* accommodate for the new timePicker

* add reset state

* scroll into view

* reuse before, after

* fix LastDayInRange when a time is selected

* do not add hours if before is zero

* use hours instead of days

* useeffect to reset hour. check timerange before scroll

* scroll to last element, not first
This commit is contained in:
Bernt Christian Egeland
2023-06-11 14:48:45 -05:00
committed by GitHub
parent 0f1cf67b91
commit b359ff1b8e
3 changed files with 279 additions and 22 deletions
+20 -11
View File
@@ -5,7 +5,7 @@ import ArrowRightDouble from '../icons/ArrowRightDouble';
const todayTimestamp = new Date().setHours(0, 0, 0, 0).valueOf();
const Calendar = ({ onChange, calendarRef, close, dateRange }) => {
const Calendar = ({ onChange, calendarRef, close, dateRange, children }) => {
const keyRef = useRef([]);
const date = new Date();
@@ -102,8 +102,9 @@ const Calendar = ({ onChange, calendarRef, close, dateRange }) => {
...prev,
selectedDay: todayTimestamp,
monthDetails: getMonthDetails(year, month),
timeRange: { ...dateRange },
}));
}, [year, month, getMonthDetails]);
}, [year, month, getMonthDetails, dateRange]);
useEffect(() => {
// add refs for keyboard navigation
@@ -121,7 +122,7 @@ const Calendar = ({ onChange, calendarRef, close, dateRange }) => {
(day) => {
if (!state.timeRange.after || !state.timeRange.before) return;
return day.timestamp < state.timeRange.before && day.timestamp >= state.timeRange.after;
return day.timestamp < state.timeRange.before && day.timestamp >= new Date(state.timeRange.after).setHours(0);
},
[state.timeRange]
);
@@ -129,14 +130,21 @@ const Calendar = ({ onChange, calendarRef, close, dateRange }) => {
const isFirstDayInRange = useCallback(
(day) => {
if (isCurrentDay(day)) return;
return state.timeRange.after === day.timestamp;
return new Date(state.timeRange.after).setHours(0) === day.timestamp;
},
[state.timeRange.after]
);
const isLastDayInRange = useCallback(
(day) => {
return state.timeRange.before === new Date(day.timestamp).setHours(24, 0, 0, 0);
// if the hour is not above 0, we will use 24 hour.
const beforeHour = new Date(state.timeRange.before).getHours() || 24;
/**
* When user selects a day in the calendar, the before will be 00:00.
* When user selects a time in timepicker, the day.timestamp hour must be changed to match the selected end () hour.
*/
return state.timeRange.before === new Date(day.timestamp).setHours(beforeHour);
},
[state.timeRange.before]
);
@@ -256,12 +264,12 @@ const Calendar = ({ onChange, calendarRef, close, dateRange }) => {
onkeydown={(e) => handleKeydown(e, day, idx)}
ref={(ref) => (keyRef.current[idx] = ref)}
tabIndex={day.month === 0 ? day.date : null}
className={`h-12 w-12 float-left flex flex-shrink justify-center items-center cursor-pointer ${
className={`h-12 w-12 float-left flex flex-shrink justify-center items-center cursor-pointer hover:border hover:rounded-md border-gray-600 ${
day.month !== 0 ? ' opacity-50 bg-gray-700 dark:bg-gray-700 pointer-events-none' : ''
}
${isFirstDayInRange(day) ? ' rounded-l-xl ' : ''}
${isSelectedRange(day) ? ' bg-blue-600 dark:hover:bg-blue-600' : ''}
${isLastDayInRange(day) ? ' rounded-r-xl ' : ''}
${isFirstDayInRange(day) ? ' rounded-l-xl hover:rounded-l-xl' : ''}
${isSelectedRange(day) ? ' bg-blue-600 hover:rounded-none' : ''}
${isLastDayInRange(day) ? ' rounded-r-xl hover:rounded-r-xl' : ''}
${isCurrentDay(day) && !isLastDayInRange(day) ? 'rounded-full bg-gray-100 dark:hover:bg-gray-100 ' : ''}`}
key={idx}
>
@@ -287,8 +295,8 @@ const Calendar = ({ onChange, calendarRef, close, dateRange }) => {
};
return (
<div className="select-none w-96 flex flex-shrink" ref={calendarRef}>
<div className="py-4 px-6">
<div className="select-none w-11/12 flex" ref={calendarRef}>
<div className="px-6">
<div className="flex items-center">
<div className="w-1/6 relative flex justify-around">
<div
@@ -329,6 +337,7 @@ const Calendar = ({ onChange, calendarRef, close, dateRange }) => {
</div>
<div className="mt-3">{renderCalendar()}</div>
</div>
{children}
</div>
);
};