@@ -6,10 +6,12 @@ import React, {
6
6
useLayoutEffect ,
7
7
useImperativeHandle ,
8
8
useMemo ,
9
+ type MutableRefObject ,
10
+ type KeyboardEvent ,
9
11
} from 'react' ;
10
12
import { useSelector } from 'react-redux' ;
11
13
12
- import * as d from 'date-fns' ;
14
+ import { parse , parseISO , format , subDays , addDays , isValid } from 'date-fns' ;
13
15
import Pikaday from 'pikaday' ;
14
16
15
17
import 'pikaday/css/pikaday.css' ;
@@ -23,9 +25,9 @@ import {
23
25
} from 'loot-core/src/shared/months' ;
24
26
import { stringToInteger } from 'loot-core/src/shared/util' ;
25
27
26
- import { theme } from '../../style' ;
27
- import Input from '../common/Input' ;
28
- import View from '../common/View' ;
28
+ import { type CSSProperties , theme } from '../../style' ;
29
+ import Input , { type InputProps } from '../common/Input' ;
30
+ import View , { type ViewProps } from '../common/View' ;
29
31
import { Tooltip } from '../tooltips' ;
30
32
31
33
import DateSelectLeft from './DateSelect.left.png' ;
@@ -76,7 +78,18 @@ let pickerStyles = {
76
78
} ,
77
79
} ;
78
80
79
- let DatePicker = forwardRef (
81
+ type DatePickerProps = {
82
+ value : string ;
83
+ firstDayOfWeekIdx : string ;
84
+ dateFormat : string ;
85
+ onUpdate ?: ( selectedDate : Date ) => void ;
86
+ onSelect : ( selectedDate : Date | null ) => void ;
87
+ } ;
88
+
89
+ type DatePickerForwardedRef = {
90
+ handleInputKeyDown : ( e : KeyboardEvent < HTMLInputElement > ) => void ;
91
+ } ;
92
+ let DatePicker = forwardRef < DatePickerForwardedRef , DatePickerProps > (
80
93
( { value, firstDayOfWeekIdx, dateFormat, onUpdate, onSelect } , ref ) => {
81
94
let picker = useRef ( null ) ;
82
95
let mountPoint = useRef ( null ) ;
@@ -89,22 +102,23 @@ let DatePicker = forwardRef(
89
102
switch ( e . key ) {
90
103
case 'ArrowLeft' :
91
104
e . preventDefault ( ) ;
92
- newDate = d . subDays ( picker . current . getDate ( ) , 1 ) ;
105
+ newDate = subDays ( picker . current . getDate ( ) , 1 ) ;
93
106
break ;
94
107
case 'ArrowUp' :
95
108
e . preventDefault ( ) ;
96
- newDate = d . subDays ( picker . current . getDate ( ) , 7 ) ;
109
+ newDate = subDays ( picker . current . getDate ( ) , 7 ) ;
97
110
break ;
98
111
case 'ArrowRight' :
99
112
e . preventDefault ( ) ;
100
- newDate = d . addDays ( picker . current . getDate ( ) , 1 ) ;
113
+ newDate = addDays ( picker . current . getDate ( ) , 1 ) ;
101
114
break ;
102
115
case 'ArrowDown' :
103
116
e . preventDefault ( ) ;
104
- newDate = d . addDays ( picker . current . getDate ( ) , 7 ) ;
117
+ newDate = addDays ( picker . current . getDate ( ) , 7 ) ;
105
118
break ;
106
119
default :
107
120
}
121
+
108
122
if ( newDate ) {
109
123
picker . current . setDate ( newDate , true ) ;
110
124
onUpdate ?.( newDate ) ;
@@ -120,14 +134,14 @@ let DatePicker = forwardRef(
120
134
keyboardInput : false ,
121
135
firstDay : stringToInteger ( firstDayOfWeekIdx ) ,
122
136
defaultDate : value
123
- ? d . parse ( value , dateFormat , currentDate ( ) )
137
+ ? parse ( value , dateFormat , currentDate ( ) )
124
138
: currentDate ( ) ,
125
139
setDefaultDate : true ,
126
140
toString ( date ) {
127
- return d . format ( date , dateFormat ) ;
141
+ return format ( date , dateFormat ) ;
128
142
} ,
129
143
parse ( dateString ) {
130
- return d . parse ( dateString , dateFormat , new Date ( ) ) ;
144
+ return parse ( dateString , dateFormat , new Date ( ) ) ;
131
145
} ,
132
146
onSelect,
133
147
} ) ;
@@ -141,7 +155,7 @@ let DatePicker = forwardRef(
141
155
142
156
useEffect ( ( ) => {
143
157
if ( picker . current . getDate ( ) !== value ) {
144
- picker . current . setDate ( d . parse ( value , dateFormat , new Date ( ) ) , true ) ;
158
+ picker . current . setDate ( parse ( value , dateFormat , new Date ( ) ) , true ) ;
145
159
}
146
160
} , [ value , dateFormat ] ) ;
147
161
@@ -153,6 +167,23 @@ function defaultShouldSaveFromKey(e) {
153
167
return e . key === 'Enter' ;
154
168
}
155
169
170
+ type DateSelectProps = {
171
+ containerProps ?: ViewProps ;
172
+ inputProps ?: InputProps ;
173
+ tooltipStyle ?: CSSProperties ;
174
+ value : string ;
175
+ isOpen ?: boolean ;
176
+ embedded ?: boolean ;
177
+ dateFormat : string ;
178
+ focused ?: boolean ;
179
+ openOnFocus ?: boolean ;
180
+ inputRef ?: MutableRefObject < HTMLInputElement > ;
181
+ shouldSaveFromKey ?: ( e : KeyboardEvent < HTMLInputElement > ) => boolean ;
182
+ tableBehavior ?: boolean ;
183
+ onUpdate ?: ( selectedDate : string ) => void ;
184
+ onSelect : ( selectedDate : string ) => void ;
185
+ } ;
186
+
156
187
export default function DateSelect ( {
157
188
containerProps,
158
189
inputProps,
@@ -168,12 +199,12 @@ export default function DateSelect({
168
199
tableBehavior,
169
200
onUpdate,
170
201
onSelect,
171
- } ) {
202
+ } : DateSelectProps ) {
172
203
let parsedDefaultValue = useMemo ( ( ) => {
173
204
if ( defaultValue ) {
174
- let date = d . parseISO ( defaultValue ) ;
175
- if ( d . isValid ( date ) ) {
176
- return d . format ( date , dateFormat ) ;
205
+ let date = parseISO ( defaultValue ) ;
206
+ if ( isValid ( date ) ) {
207
+ return format ( date , dateFormat ) ;
177
208
}
178
209
}
179
210
return '' ;
@@ -216,29 +247,29 @@ export default function DateSelect({
216
247
// Support only entering the month and day (4/5). This is complex
217
248
// because of the various date formats - we need to derive
218
249
// the right day/month format from it
219
- let test = d . parse ( value , getDayMonthFormat ( dateFormat ) , new Date ( ) ) ;
220
- if ( d . isValid ( test ) ) {
221
- onUpdate ?. ( d . format ( test , 'yyyy-MM-dd' ) ) ;
222
- setSelectedValue ( d . format ( test , dateFormat ) ) ;
250
+ let test = parse ( value , getDayMonthFormat ( dateFormat ) , new Date ( ) ) ;
251
+ if ( isValid ( test ) ) {
252
+ onUpdate ?.( format ( test , 'yyyy-MM-dd' ) ) ;
253
+ setSelectedValue ( format ( test , dateFormat ) ) ;
223
254
}
224
255
} else if ( getShortYearRegex ( dateFormat ) . test ( value ) ) {
225
256
// Support entering the year as only two digits (4/5/19)
226
- let test = d . parse ( value , getShortYearFormat ( dateFormat ) , new Date ( ) ) ;
227
- if ( d . isValid ( test ) ) {
228
- onUpdate ?. ( d . format ( test , 'yyyy-MM-dd' ) ) ;
229
- setSelectedValue ( d . format ( test , dateFormat ) ) ;
257
+ let test = parse ( value , getShortYearFormat ( dateFormat ) , new Date ( ) ) ;
258
+ if ( isValid ( test ) ) {
259
+ onUpdate ?.( format ( test , 'yyyy-MM-dd' ) ) ;
260
+ setSelectedValue ( format ( test , dateFormat ) ) ;
230
261
}
231
262
} else {
232
- let test = d . parse ( value , dateFormat , new Date ( ) ) ;
233
- if ( d . isValid ( test ) ) {
234
- let date = d . format ( test , 'yyyy-MM-dd' ) ;
263
+ let test = parse ( value , dateFormat , new Date ( ) ) ;
264
+ if ( isValid ( test ) ) {
265
+ let date = format ( test , 'yyyy-MM-dd' ) ;
235
266
onUpdate ?.( date ) ;
236
267
setSelectedValue ( value ) ;
237
268
}
238
269
}
239
270
} , [ value ] ) ;
240
271
241
- function onKeyDown ( e ) {
272
+ function onKeyDown ( e : KeyboardEvent < HTMLInputElement > ) {
242
273
if (
243
274
[ 'ArrowLeft' , 'ArrowRight' , 'ArrowUp' , 'ArrowDown' ] . includes ( e . key ) &&
244
275
! e . shiftKey &&
@@ -267,8 +298,8 @@ export default function DateSelect({
267
298
setValue ( selectedValue ) ;
268
299
setOpen ( false ) ;
269
300
270
- let date = d . parse ( selectedValue , dateFormat , new Date ( ) ) ;
271
- onSelect ( d . format ( date , 'yyyy-MM-dd' ) ) ;
301
+ let date = parse ( selectedValue , dateFormat , new Date ( ) ) ;
302
+ onSelect ( format ( date , 'yyyy-MM-dd' ) ) ;
272
303
273
304
if ( open && e . key === 'Enter' ) {
274
305
// This stops the event from propagating up
@@ -341,9 +372,9 @@ export default function DateSelect({
341
372
} else {
342
373
setValue ( selectedValue || '' ) ;
343
374
344
- let date = d . parse ( selectedValue , dateFormat , new Date ( ) ) ;
345
- if ( date instanceof Date && ! isNaN ( date ) ) {
346
- onSelect ( d . format ( date , 'yyyy-MM-dd' ) ) ;
375
+ let date = parse ( selectedValue , dateFormat , new Date ( ) ) ;
376
+ if ( date instanceof Date && ! isNaN ( date . valueOf ( ) ) ) {
377
+ onSelect ( format ( date , 'yyyy-MM-dd' ) ) ;
347
378
}
348
379
}
349
380
}
@@ -357,12 +388,12 @@ export default function DateSelect({
357
388
firstDayOfWeekIdx = { firstDayOfWeekIdx }
358
389
dateFormat = { dateFormat }
359
390
onUpdate = { date => {
360
- setSelectedValue ( d . format ( date , dateFormat ) ) ;
361
- onUpdate ?. ( d . format ( date , 'yyyy-MM-dd' ) ) ;
391
+ setSelectedValue ( format ( date , dateFormat ) ) ;
392
+ onUpdate ?.( format ( date , 'yyyy-MM-dd' ) ) ;
362
393
} }
363
394
onSelect = { date => {
364
- setValue ( d . format ( date , dateFormat ) ) ;
365
- onSelect ( d . format ( date , 'yyyy-MM-dd' ) ) ;
395
+ setValue ( format ( date , dateFormat ) ) ;
396
+ onSelect ( format ( date , 'yyyy-MM-dd' ) ) ;
366
397
setOpen ( false ) ;
367
398
} }
368
399
/> ,
0 commit comments