1- import React , { useRef , useEffect } from 'react' ;
2- import { DayPicker , DayButton , getDefaultClassNames , DateRange } from 'react-day-picker' ;
1+ import React , { useRef , useEffect , useMemo } from 'react' ;
2+ import { DateRange , DayPicker , DayButton as RdpDayButton , getDefaultClassNames } from 'react-day-picker' ;
33import { format } from 'date-fns' ;
44import ChevronLeftIcon from '../../../icons/arrows/ChevronLeftIcon' ;
55import ChevronRightIcon from '../../../icons/arrows/ChevronRightIcon' ;
6-
76import * as Styled from './Calendar.styled' ;
87
9- type Props = {
10- className ?: string ;
11- internalClassNames ?: React . ComponentProps < typeof DayPicker > [ 'classNames' ] ;
12- components ?: React . ComponentProps < typeof DayPicker > [ 'components' ] ;
13- selectionType ?: 'single' | 'range' | 'multiple' ;
8+ export type Range = { from ?: Date ; to ?: Date } ;
9+
10+ type BaseProps = Omit < React . ComponentProps < typeof DayPicker > , 'mode' | 'selected' | 'onSelect' > & {
1411 visibleMonths ?: 1 | 2 | 3 ;
1512 captionLayout ?: React . ComponentProps < typeof DayPicker > [ 'captionLayout' ] ;
1613 weekStartsOn ?: React . ComponentProps < typeof DayPicker > [ 'weekStartsOn' ] ;
1714 selected ?: Date | Date [ ] | DateRange ;
1815 onSelect ?: ( date : Date | Date [ ] | DateRange | undefined ) => void ;
1916} & Omit < React . ComponentProps < typeof DayPicker > , 'mode' | 'classNames' | 'selected' | 'onSelect' > ;
2017
21- function Calendar ( {
22- className,
23- internalClassNames,
24- components,
25- selectionType = 'single' ,
26- visibleMonths = 1 ,
27- captionLayout = 'label' ,
28- weekStartsOn = 1 ,
29- selected,
30- onSelect,
31- ...restProps
32- } : Props ) {
18+ export type SingleProps = BaseProps & {
19+ selectionType ?: 'single' ; // defaults to 'single' if omitted
20+ selected ?: Date ;
21+ onSelect ?: ( value ?: Date ) => void ;
22+ } ;
23+
24+ export type MultipleProps = BaseProps & {
25+ selectionType : 'multiple' ;
26+ selected ?: Date [ ] ;
27+ onSelect ?: ( value ?: Date [ ] ) => void ;
28+ } ;
29+
30+ export type RangeProps = BaseProps & {
31+ selectionType : 'range' ;
32+ selected ?: Range ;
33+ onSelect ?: ( value ?: Range ) => void ;
34+ } ;
35+
36+ export type CalendarProps = SingleProps | MultipleProps | RangeProps ;
37+
38+ export function Calendar ( props : SingleProps ) : JSX . Element ;
39+ export function Calendar ( props : MultipleProps ) : JSX . Element ;
40+ export function Calendar ( props : RangeProps ) : JSX . Element ;
41+
42+ export function Calendar ( props : CalendarProps ) : JSX . Element {
43+ const {
44+ className,
45+ classNames,
46+ components,
47+ visibleMonths = 1 ,
48+ captionLayout = 'label' ,
49+ weekStartsOn = 1 ,
50+ selected,
51+ onSelect,
52+ ...rest
53+ } = props ;
54+
55+ const selectionType = props . selectionType ?? 'single' ;
3356 const defaults = getDefaultClassNames ( ) ;
3457
58+ const DayBtn = useMemo (
59+ ( ) => ( p : React . ComponentProps < typeof RdpDayButton > ) =>
60+ < CalendarDayButton selectionType = { selectionType } { ...p } /> ,
61+ [ selectionType ]
62+ ) ;
63+
3564 const common = {
3665 showOutsideDays : false ,
3766 numberOfMonths : visibleMonths ,
3867 weekStartsOn,
3968 captionLayout,
4069 formatters : {
41- formatWeekdayName : ( date , options ?: { locale } ) => format ( date , 'eee' , { locale : options ?. locale } )
42- } ,
43- classNames : {
44- ...defaults ,
45- ...internalClassNames
70+ formatWeekdayName : ( date , options ?: { locale : unknown } ) =>
71+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
72+ format ( date , 'eee' , { locale : ( options as any ) ?. locale } )
4673 } ,
74+ classNames : { ...defaults , ...classNames } ,
4775 components : {
4876 Chevron : ( { orientation, ...p } : { orientation ?: 'left' | 'right' } ) => {
4977 if ( orientation === 'left' ) return < ChevronLeftIcon size = { 24 } { ...p } /> ;
5078 if ( orientation === 'right' ) return < ChevronRightIcon size = { 24 } { ...p } /> ;
5179 return null as unknown as React . ReactElement ;
5280 } ,
53- DayButton : CalendarDayButton ,
54- ...components
55- }
81+ DayButton : DayBtn ,
82+ ...( components ?? { } )
83+ } ,
84+ ...rest
5685 } satisfies Omit < React . ComponentProps < typeof DayPicker > , 'mode' > ;
5786
58- const modeProps = ( ( ) => {
59- switch ( selectionType ) {
60- case 'range' :
61- return { mode : 'range' } as const ;
62- case 'multiple' :
63- return { mode : 'multiple' } as const ;
64- default :
65- return { mode : 'single' } as const ;
66- }
67- } ) ( ) ;
68-
69- const dayPickerProps = {
70- ...common ,
71- ...modeProps ,
72- selected,
73- onSelect,
74- ...restProps
75- } ;
87+ const selectedProp = selected !== undefined ? { selected : selected as unknown } : { } ;
88+ const onSelectProp = onSelect ? { onSelect : onSelect as unknown } : { } ;
89+
90+ const modeProps =
91+ selectionType === 'range'
92+ ? ( { mode : 'range' } as const )
93+ : selectionType === 'multiple'
94+ ? ( { mode : 'multiple' } as const )
95+ : ( { mode : 'single' } as const ) ;
7696
7797 return (
7898 < Styled . Container className = { className } >
79- < DayPicker { ...( dayPickerProps as any ) } />
99+ < DayPicker
100+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
101+ { ...( common as any ) }
102+ { ...modeProps }
103+ { ...selectedProp }
104+ { ...onSelectProp }
105+ />
80106 </ Styled . Container >
81107 ) ;
82108}
83109
84- function CalendarDayButton ( { day, modifiers, ...props } : React . ComponentProps < typeof DayButton > ) {
110+ type SelectionType = 'single' | 'range' | 'multiple' ;
111+
112+ type CalendarDayButtonProps = React . ComponentProps < typeof RdpDayButton > & {
113+ selectionType : SelectionType ;
114+ } ;
115+
116+ function CalendarDayButton ( { day, modifiers, selectionType, ...props } : CalendarDayButtonProps ) {
85117 const ref = useRef < HTMLButtonElement > ( null ) ;
86118 const defaults = getDefaultClassNames ( ) ;
87119
88120 useEffect ( ( ) => {
89- if ( modifiers . focused ) {
90- ref . current ?. focus ( ) ;
91- }
121+ if ( modifiers . focused ) ref . current ?. focus ( ) ;
92122 } , [ modifiers . focused ] ) ;
93123
94124 const dayNumber = day . date . getDate ( ) . toString ( ) . padStart ( 2 , '0' ) ;
125+ const isSelectedPlain =
126+ modifiers . selected && ! modifiers . range_start && ! modifiers . range_end && ! modifiers . range_middle ;
95127
96128 return (
97129 < Styled . DayButton
98130 ref = { ref }
99131 data-day = { day . date . toLocaleDateString ( ) }
100- data-selected-single = {
101- modifiers . selected && ! modifiers . range_start && ! modifiers . range_end && ! modifiers . range_middle
102- }
103- data-selected-multiple = {
104- modifiers . selected && ! modifiers . range_start && ! modifiers . range_end && ! modifiers . range_middle
105- }
106132 data-selected = { modifiers . selected }
133+ data-selected-single = { selectionType === 'single' && isSelectedPlain }
134+ data-selected-multiple = { selectionType === 'multiple' && modifiers . selected }
107135 data-today = { modifiers . today }
108136 data-outside = { modifiers . outside }
109137 data-disabled = { modifiers . disabled }
@@ -118,5 +146,3 @@ function CalendarDayButton({ day, modifiers, ...props }: React.ComponentProps<ty
118146 </ Styled . DayButton >
119147 ) ;
120148}
121-
122- export { Calendar } ;
0 commit comments