UNPKG

2.87 kBTypeScriptView Raw
1import React, { useMemo } from 'react';
2import dayjs from 'dayjs';
3import utc from 'dayjs/plugin/utc';
4
5import BaseCalendarWrappedInProviders from './Providers';
6import type { DateProperties, WrapperCalendarProps } from '../Entities';
7
8dayjs.extend(utc);
9
10interface SpecificProps {
11 onSelectDates: (dates: string[]) => void;
12 selectedDates: string[];
13}
14
15type Props = SpecificProps & WrapperCalendarProps;
16
17// A thin wrapper to limit the props that can be passed to the BaseCalendar component
18const MultiDateSelectionCalendar: React.FC<Props> = ({
19 onSelectDates,
20 disabledDates,
21 selectedDates,
22 allowYearView = true,
23 ...others
24}) => {
25 if (!selectedDates) {
26 throw new Error(
27 'The `selectedDates` prop is required. Use an empty array if no dates should be selected.'
28 );
29 }
30
31 if (typeof selectedDates !== 'object') {
32 throw new Error(
33 'The `selectedDates` prop should be an array of date strings in YYYY-MM-DD format.'
34 );
35 }
36
37 if (!onSelectDates) {
38 throw new Error('The `onSelectDates` prop is required.');
39 }
40
41 if (typeof onSelectDates !== 'function') {
42 throw new Error(
43 'The `onSelectDates` prop should be function that receives an array of date strings as paramater.'
44 );
45 }
46
47 const selDatesRef = React.useRef<string[]>(selectedDates);
48
49 const dateProperties = useMemo(() => {
50 const disabledDateProperties = disabledDates?.reduce(
51 (disabled: Record<string, DateProperties>, date) => {
52 disabled[date] = { isDisabled: true };
53 return disabled;
54 },
55 {}
56 );
57
58 const selectedDateProperties = selectedDates.reduce(
59 (selected: Record<string, DateProperties>, date) => {
60 selected[date] = { isSelected: true };
61 return selected;
62 },
63 {}
64 );
65
66 // Not possible for a date to be both disabled and selected, so overwriting is OK
67 return {
68 ...disabledDateProperties,
69 ...selectedDateProperties,
70 };
71 }, [selectedDates, disabledDates]);
72
73 const remove = (dateToRemove: string) => {
74 const newSelectedDates = selDatesRef.current.filter((date) => date !== dateToRemove);
75 selDatesRef.current = newSelectedDates;
76 return newSelectedDates;
77 };
78
79 const append = (dateToAppend: string) => {
80 const newSelectedDates = [...selDatesRef.current, dateToAppend].sort();
81 selDatesRef.current = newSelectedDates;
82 return newSelectedDates;
83 };
84
85 const onPressDay = React.useCallback(
86 (date: string) => {
87 if (selDatesRef.current.includes(date)) {
88 onSelectDates(remove(date));
89 } else {
90 onSelectDates(append(date));
91 }
92 },
93 [onSelectDates]
94 );
95
96 return (
97 <BaseCalendarWrappedInProviders
98 onPressDay={onPressDay}
99 allowYearView={allowYearView}
100 dateProperties={dateProperties}
101 {...others}
102 />
103 );
104};
105
106export default MultiDateSelectionCalendar;