1
1
import { useDatePicker , useDateRangePicker } from "@react-aria/datepicker" ;
2
2
import { useDatePickerState , useDateRangePickerState } from "@react-stately/datepicker" ;
3
3
import { useRef } from "react" ;
4
+ import { match } from "ts-pattern" ;
4
5
import { FieldProps } from "../Field/FieldProps" ;
5
6
import { CalendarDate , DateValue , getLocalTimeZone } from "@internationalized/date" ;
6
- import { Input } from "./Input" ;
7
- import { Calendar } from "./Calendar" ;
8
- import { Box } from "../Box/Box" ;
7
+ import { BaseDateInput } from "./BaseDateInput" ;
9
8
import { Field } from "../Field/Field" ;
10
- import { LocalizedString } from "../util/ConfigurableTypes" ;
11
9
import { RangeValue } from "@react-types/shared" ;
12
- import { Inline } from "../Layout/Inline " ;
13
- import { Button } from ".. " ;
10
+ import { DateProps , SingleDateProps , RangeDateProps } from "./types " ;
11
+ import { dateToCalendarDate } from "./utils " ;
14
12
15
- export type ShortcutProps < Value > = {
16
- label : LocalizedString ;
17
- value : Value ;
18
- } ;
13
+ type SingleDateFieldProps = SingleDateProps & FieldProps < Date | null > & DateProps ;
14
+ type RangeDateFieldProps = RangeDateProps & FieldProps < [ Date , Date ] | null > & DateProps ;
19
15
20
- type StandaloneProps < T > = Pick <
21
- FieldProps < T > ,
22
- "autoFocus" | "disabled" | "name" | "onBlur" | "onChange" | "value"
23
- > ;
24
-
25
- type SingleDateProps = { type ?: "single" ; shortcuts ?: ShortcutProps < Date | null > [ ] } ;
26
- type RangeDateProps = { type : "range" ; shortcuts ?: ShortcutProps < [ Date , Date ] | null > [ ] } ;
27
-
28
- type SingleDateFieldProps = SingleDateProps & FieldProps < Date | null > ;
29
- type SingleDateStandaloneProps = SingleDateProps & StandaloneProps < Date | null > ;
30
-
31
- type RangeDateFieldProps = RangeDateProps & FieldProps < [ Date , Date ] | null > ;
32
- type RangeDateStandaloneProps = RangeDateProps & StandaloneProps < [ Date , Date ] | null > ;
33
-
34
- type DateProps = {
35
- minDate ?: Date ;
36
- maxDate ?: Date ;
37
- shouldDisableDate ?: ( date : Date ) => boolean ;
38
- readOnly ?: boolean ;
39
- } ;
40
-
41
- type PublicDateFieldProps = ( SingleDateFieldProps | RangeDateFieldProps ) & DateProps ;
42
- type PublicDateInputProps = ( SingleDateStandaloneProps | RangeDateStandaloneProps ) & DateProps ;
43
-
44
- type InternalDateProps =
45
- | ( {
46
- isStandalone : true ;
47
- } & PublicDateInputProps )
48
- | ( {
49
- isStandalone ?: false ;
50
- } & PublicDateFieldProps ) ;
51
-
52
- function dateToCalendarDate ( date : Date ) : CalendarDate {
53
- return new CalendarDate ( date . getFullYear ( ) , date . getMonth ( ) + 1 , date . getDate ( ) ) ;
54
- }
55
-
56
- function SingleDate ( {
57
- disabled,
58
- readOnly,
59
- ...props
60
- } : Extract < InternalDateProps , { type ?: "single" } > ) {
16
+ function SingleDateField ( props : SingleDateFieldProps ) {
61
17
const localTimeZone = getLocalTimeZone ( ) ;
18
+ const ref = useRef ( null ) ;
19
+
62
20
const internalProps = {
63
21
...props ,
64
22
value : props . value ? dateToCalendarDate ( props . value ) : props . value ,
65
23
onChange : ( date : CalendarDate | null ) => {
66
24
props . onChange ( date ?. toDate ( localTimeZone ) ?? null ) ;
67
25
} ,
68
- isDisabled : disabled ,
69
- isReadOnly : readOnly ,
70
- validationState : ! props . isStandalone && props . issues ? "invalid" : "valid" ,
26
+ isDisabled : props . disabled ,
27
+ isReadOnly : props . isReadOnly ?? props . readOnly ,
28
+ validationState : props . issues ? "invalid" : "valid" ,
71
29
minValue : props . minDate ? dateToCalendarDate ( props . minDate ) : undefined ,
72
30
maxValue : props . maxDate ? dateToCalendarDate ( props . maxDate ) : undefined ,
73
31
isDateUnavailable : props . shouldDisableDate
74
32
? ( date : DateValue ) => props . shouldDisableDate ! ( date . toDate ( localTimeZone ) )
75
33
: undefined ,
76
34
shouldForceLeadingZeros : true ,
35
+ onBlur : props . onBlur ,
36
+ autoFocus : props . autoFocus ,
77
37
} as const ;
78
- const state = useDatePickerState ( internalProps ) ;
79
- const ref = useRef ( null ) ;
80
- const {
81
- groupProps,
82
- labelProps,
83
- fieldProps,
84
- buttonProps,
85
- descriptionProps,
86
- errorMessageProps,
87
- calendarProps,
88
- } = useDatePicker ( internalProps , state , ref ) ;
89
38
90
- const shortcuts = props . shortcuts && (
91
- < Inline space = { 4 } >
92
- { props . shortcuts . map ( ( shortcut ) => (
93
- < Button
94
- key = { shortcut . label }
95
- kind = "transparent"
96
- hierarchy = "secondary"
97
- size = "small"
98
- label = { shortcut . label }
99
- onPress = { ( ) => {
100
- props . onChange ( shortcut . value ) ;
101
- state . close ( ) ;
102
- } }
103
- />
104
- ) ) }
105
- </ Inline >
106
- ) ;
39
+ const datePickerState = useDatePickerState ( internalProps ) ;
40
+ const datePickerAria = useDatePicker ( internalProps , datePickerState , ref ) ;
107
41
108
- const datePicker = (
109
- < >
110
- < Box { ...groupProps } ref = { ref } >
111
- < Input
112
- type = "single"
113
- fieldProps = { fieldProps }
114
- buttonProps = { buttonProps }
115
- isCalendarOpen = { state . isOpen }
116
- />
117
- </ Box >
118
- { state . isOpen && (
119
- < Calendar
120
- type = "single"
121
- { ...calendarProps }
122
- inputRef = { ref }
123
- onClose = { state . close }
124
- shortcuts = { shortcuts }
125
- />
126
- ) }
127
- </ >
128
- ) ;
129
-
130
- return props . isStandalone ? (
131
- datePicker
132
- ) : (
42
+ return (
133
43
< Field
134
44
{ ...props }
135
- disabled = { disabled }
136
- labelProps = { labelProps }
137
- assistiveTextProps = { descriptionProps }
138
- errorMessageProps = { errorMessageProps }
45
+ disabled = { props . disabled }
46
+ labelProps = { datePickerAria . labelProps }
47
+ assistiveTextProps = { datePickerAria . descriptionProps }
48
+ errorMessageProps = { datePickerAria . errorMessageProps }
139
49
>
140
- { datePicker }
50
+ < BaseDateInput
51
+ type = "single"
52
+ value = { props . value }
53
+ onChange = { props . onChange }
54
+ shortcuts = { props . shortcuts }
55
+ datePickerAria = { datePickerAria }
56
+ datePickerState = { datePickerState }
57
+ inputRef = { ref }
58
+ />
141
59
</ Field >
142
60
) ;
143
61
}
144
62
145
- function RangeDateField ( {
146
- disabled,
147
- readOnly,
148
- ...props
149
- } : Extract < InternalDateProps , { type : "range" } > ) {
63
+ function RangeDateField ( props : RangeDateFieldProps ) {
150
64
const localTimeZone = getLocalTimeZone ( ) ;
65
+ const ref = useRef ( null ) ;
66
+
151
67
const internalProps = {
152
68
...props ,
153
- isDisabled : disabled ,
154
- isReadOnly : readOnly ,
155
- validationState : ! props . isStandalone && props . issues ? "invalid" : "valid" ,
156
- minValue : props . minDate ? dateToCalendarDate ( props . minDate ) : undefined ,
157
- maxValue : props . maxDate ? dateToCalendarDate ( props . maxDate ) : undefined ,
158
- isDateUnavailable : props . shouldDisableDate
159
- ? ( date : DateValue ) => props . shouldDisableDate ! ( date . toDate ( localTimeZone ) )
160
- : undefined ,
161
69
value : props . value
162
70
? {
163
71
start : dateToCalendarDate ( props . value [ 0 ] ) ,
@@ -171,86 +79,47 @@ function RangeDateField({
171
79
props . onChange ( [ range . start . toDate ( localTimeZone ) , range . end . toDate ( localTimeZone ) ] ) ;
172
80
}
173
81
} ,
82
+ isDisabled : props . disabled ,
83
+ isReadOnly : props . isReadOnly ?? props . readOnly ,
84
+ validationState : props . issues ? "invalid" : "valid" ,
85
+ minValue : props . minDate ? dateToCalendarDate ( props . minDate ) : undefined ,
86
+ maxValue : props . maxDate ? dateToCalendarDate ( props . maxDate ) : undefined ,
87
+ isDateUnavailable : props . shouldDisableDate
88
+ ? ( date : DateValue ) => props . shouldDisableDate ! ( date . toDate ( localTimeZone ) )
89
+ : undefined ,
174
90
shouldForceLeadingZeros : true ,
91
+ onBlur : props . onBlur ,
92
+ autoFocus : props . autoFocus ,
175
93
} as const ;
176
- const state = useDateRangePickerState ( internalProps ) ;
177
- const ref = useRef ( null ) ;
178
- const {
179
- groupProps,
180
- labelProps,
181
- buttonProps,
182
- descriptionProps,
183
- errorMessageProps,
184
- calendarProps,
185
- startFieldProps,
186
- endFieldProps,
187
- } = useDateRangePicker ( internalProps , state , ref ) ;
188
-
189
- const shortcuts = props . shortcuts && (
190
- < Inline space = { 4 } >
191
- { props . shortcuts . map ( ( shortcut ) => (
192
- < Button
193
- key = { shortcut . label }
194
- kind = "transparent"
195
- hierarchy = "secondary"
196
- size = "small"
197
- label = { shortcut . label }
198
- onPress = { ( ) => {
199
- props . onChange ( shortcut . value ) ;
200
- state . close ( ) ;
201
- } }
202
- />
203
- ) ) }
204
- </ Inline >
205
- ) ;
206
94
207
- const datePicker = (
208
- < >
209
- < Box { ...groupProps } ref = { ref } >
210
- < Input
211
- type = "range"
212
- fieldProps = { { start : startFieldProps , end : endFieldProps } }
213
- buttonProps = { buttonProps }
214
- isCalendarOpen = { state . isOpen }
215
- />
216
- </ Box >
217
- { state . isOpen && (
218
- < Calendar
219
- type = "range"
220
- { ...calendarProps }
221
- inputRef = { ref }
222
- onClose = { state . close }
223
- shortcuts = { shortcuts }
224
- />
225
- ) }
226
- </ >
227
- ) ;
95
+ const rangeDatePickerState = useDateRangePickerState ( internalProps ) ;
96
+ const rangeDatePickerAria = useDateRangePicker ( internalProps , rangeDatePickerState , ref ) ;
228
97
229
- return props . isStandalone ? (
230
- datePicker
231
- ) : (
98
+ return (
232
99
< Field
233
100
{ ...props }
234
- disabled = { disabled }
235
- labelProps = { labelProps }
236
- assistiveTextProps = { descriptionProps }
237
- errorMessageProps = { errorMessageProps }
101
+ disabled = { props . disabled }
102
+ labelProps = { rangeDatePickerAria . labelProps }
103
+ assistiveTextProps = { rangeDatePickerAria . descriptionProps }
104
+ errorMessageProps = { rangeDatePickerAria . errorMessageProps }
238
105
>
239
- { datePicker }
106
+ < BaseDateInput
107
+ type = "range"
108
+ value = { props . value }
109
+ onChange = { props . onChange }
110
+ shortcuts = { props . shortcuts }
111
+ dateRangePickerAria = { rangeDatePickerAria }
112
+ dateRangePickerState = { rangeDatePickerState }
113
+ inputRef = { ref }
114
+ />
240
115
</ Field >
241
116
) ;
242
117
}
243
118
244
- export function DateField ( props : PublicDateFieldProps ) {
245
- return props . type === "range" ? < RangeDateField { ...props } /> : < SingleDate { ...props } /> ;
119
+ export function DateField ( props : SingleDateFieldProps | RangeDateFieldProps ) {
120
+ // @ts -ignore
121
+ return match ( props )
122
+ . with ( { type : "single" } , { type : undefined } , ( props ) => < SingleDateField { ...props } /> )
123
+ . with ( { type : "range" } , ( props ) => < RangeDateField { ...props } /> )
124
+ . exhaustive ( ) ;
246
125
}
247
-
248
- export function DateInput ( props : PublicDateInputProps ) {
249
- return props . type === "range" ? (
250
- < RangeDateField { ...props } isStandalone />
251
- ) : (
252
- < SingleDate { ...props } isStandalone />
253
- ) ;
254
- }
255
-
256
- export type { PublicDateFieldProps as DateFieldProps , PublicDateInputProps as DateInputProps } ;
0 commit comments