12
12
*/
13
13
14
14
import * as React from 'react' ;
15
- import KeyCode from 'rc-util/lib/KeyCode' ;
16
15
import classNames from 'classnames' ;
17
16
import { AlignType } from 'rc-trigger/lib/interface' ;
18
17
import PickerPanel , {
@@ -25,11 +24,8 @@ import { isEqual } from './utils/dateUtil';
25
24
import getDataOrAriaProps , { toArray } from './utils/miscUtil' ;
26
25
import PanelContext , { ContextOperationRefProps } from './PanelContext' ;
27
26
import { PickerMode } from './interface' ;
28
- import {
29
- getDefaultFormat ,
30
- getInputSize ,
31
- addGlobalMouseDownEvent ,
32
- } from './utils/uiUtil' ;
27
+ import { getDefaultFormat , getInputSize } from './utils/uiUtil' ;
28
+ import usePickerInput from './hooks/usePickerInput' ;
33
29
34
30
export interface PickerRefConfig {
35
31
focus : ( ) => void ;
@@ -47,6 +43,7 @@ export interface PickerSharedProps<DateType> extends React.AriaAttributes {
47
43
autoFocus ?: boolean ;
48
44
disabled ?: boolean ;
49
45
open ?: boolean ;
46
+ defaultOpen ?: boolean ;
50
47
/** Make input readOnly to avoid popup keyboard in mobile */
51
48
inputReadOnly ?: boolean ;
52
49
@@ -134,6 +131,7 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
134
131
value,
135
132
defaultValue,
136
133
open,
134
+ defaultOpen,
137
135
suffixIcon,
138
136
clearIcon,
139
137
disabled,
@@ -192,7 +190,6 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
192
190
)
193
191
: '' ,
194
192
) ;
195
- const [ typing , setTyping ] = React . useState ( false ) ;
196
193
197
194
/** Similar as `setTextValue` but accept `DateType` and convert into string */
198
195
const setDateText = ( date : DateType | null ) => {
@@ -209,7 +206,12 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
209
206
> ( null ) ;
210
207
211
208
// Trigger
212
- const [ innerOpen , setInnerOpen ] = React . useState < boolean > ( false ) ;
209
+ const [ innerOpen , setInnerOpen ] = React . useState < boolean > ( ( ) => {
210
+ if ( defaultOpen !== undefined ) {
211
+ return defaultOpen ;
212
+ }
213
+ return false ;
214
+ } ) ;
213
215
let mergedOpen : boolean ;
214
216
if ( disabled ) {
215
217
mergedOpen = false ;
@@ -230,9 +232,6 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
230
232
}
231
233
} ;
232
234
233
- // Focus
234
- const [ focused , setFocused ] = React . useState ( false ) ;
235
-
236
235
// ============================= Value =============================
237
236
const isSameTextDate = ( text : string , date : DateType | null ) => {
238
237
if ( date === null ) {
@@ -255,11 +254,6 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
255
254
setInternalSelectedValue ( newDate ) ;
256
255
} ;
257
256
258
- const onInputMouseDown : React . MouseEventHandler < HTMLInputElement > = ( ) => {
259
- triggerOpen ( true ) ;
260
- setTyping ( true ) ;
261
- } ;
262
-
263
257
const onInputChange : React . ChangeEventHandler < HTMLInputElement > = e => {
264
258
const text = e . target . value ;
265
259
setTextValue ( text ) ;
@@ -290,95 +284,42 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
290
284
} ;
291
285
292
286
const forwardKeyDown = ( e : React . KeyboardEvent < HTMLElement > ) => {
293
- if (
294
- ! typing &&
295
- mergedOpen &&
296
- operationRef . current &&
297
- operationRef . current . onKeyDown
298
- ) {
287
+ if ( mergedOpen && operationRef . current && operationRef . current . onKeyDown ) {
299
288
// Let popup panel handle keyboard
300
289
return operationRef . current . onKeyDown ( e ) ;
301
290
}
302
291
return false ;
303
292
} ;
304
293
305
- const onInputKeyDown : React . KeyboardEventHandler < HTMLInputElement > = e => {
306
- switch ( e . which ) {
307
- case KeyCode . ENTER : {
308
- if ( ! mergedOpen ) {
309
- triggerOpen ( true ) ;
310
- } else {
311
- triggerChange ( selectedValue ) ;
312
- triggerOpen ( false ) ;
313
- setTyping ( true ) ;
314
- }
315
- return ;
316
- }
317
-
318
- case KeyCode . TAB : {
319
- if ( typing && mergedOpen && ! e . shiftKey ) {
320
- setTyping ( false ) ;
321
- e . preventDefault ( ) ;
322
- } else if ( ! typing && mergedOpen ) {
323
- if ( ! forwardKeyDown ( e ) && e . shiftKey ) {
324
- setTyping ( true ) ;
325
- e . preventDefault ( ) ;
326
- }
327
- }
328
- return ;
329
- }
330
-
331
- case KeyCode . ESC : {
332
- triggerChange ( mergedValue ) ;
333
- setSelectedValue ( mergedValue ) ;
334
- triggerOpen ( false ) ;
335
- setTyping ( true ) ;
336
- return ;
337
- }
338
- }
339
-
340
- if ( ! mergedOpen && ! [ KeyCode . SHIFT ] . includes ( e . which ) ) {
341
- triggerOpen ( true ) ;
342
- } else {
343
- // Let popup panel handle keyboard
344
- forwardKeyDown ( e ) ;
345
- }
346
- } ;
347
-
348
- const onInputFocus : React . FocusEventHandler < HTMLInputElement > = e => {
349
- setTyping ( true ) ;
350
- setFocused ( true ) ;
351
-
352
- if ( onFocus ) {
353
- onFocus ( e ) ;
354
- }
355
- } ;
356
-
357
- /**
358
- * We will prevent blur to handle open event when user click outside,
359
- * since this will repeat trigger `onOpenChange` event.
360
- */
361
- const preventBlurRef = React . useRef < boolean > ( false ) ;
362
-
363
294
const triggerClose = ( ) => {
364
295
triggerOpen ( false ) ;
365
296
setInnerValue ( selectedValue ) ;
366
297
triggerChange ( selectedValue ) ;
367
298
} ;
368
299
369
- const onInputBlur : React . FocusEventHandler < HTMLInputElement > = e => {
370
- if ( preventBlurRef . current ) {
371
- preventBlurRef . current = false ;
372
- return ;
373
- }
374
-
375
- triggerClose ( ) ;
376
- setFocused ( false ) ;
377
-
378
- if ( onBlur ) {
379
- onBlur ( e ) ;
380
- }
381
- } ;
300
+ const [ inputProps , { focused, typing } ] = usePickerInput ( {
301
+ open : mergedOpen ,
302
+ triggerOpen,
303
+ triggerClose,
304
+ forwardKeyDown,
305
+ isClickOutside : target =>
306
+ ! ! (
307
+ panelDivRef . current &&
308
+ ! panelDivRef . current . contains ( target as Node ) &&
309
+ inputDivRef . current &&
310
+ ! inputDivRef . current . contains ( target as Node ) &&
311
+ onOpenChange
312
+ ) ,
313
+ onSubmit : ( ) => {
314
+ triggerChange ( selectedValue ) ;
315
+ } ,
316
+ onCancel : ( ) => {
317
+ triggerChange ( mergedValue ) ;
318
+ setSelectedValue ( mergedValue ) ;
319
+ } ,
320
+ onFocus,
321
+ onBlur,
322
+ } ) ;
382
323
383
324
// ============================= Sync ==============================
384
325
// Close should sync back with text value
@@ -402,28 +343,6 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
402
343
}
403
344
} , [ mergedValue ] ) ;
404
345
405
- // Global click handler
406
- React . useEffect ( ( ) =>
407
- addGlobalMouseDownEvent ( ( { target } : MouseEvent ) => {
408
- if (
409
- mergedOpen &&
410
- panelDivRef . current &&
411
- ! panelDivRef . current . contains ( target as Node ) &&
412
- inputDivRef . current &&
413
- ! inputDivRef . current . contains ( target as Node ) &&
414
- onOpenChange
415
- ) {
416
- preventBlurRef . current = true ;
417
- triggerClose ( ) ;
418
-
419
- // Always set back in case `onBlur` prevented by user
420
- window . setTimeout ( ( ) => {
421
- preventBlurRef . current = false ;
422
- } , 0 ) ;
423
- }
424
- } ) ,
425
- ) ;
426
-
427
346
// ============================ Private ============================
428
347
if ( pickerRef ) {
429
348
pickerRef . current = {
@@ -523,15 +442,11 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
523
442
< input
524
443
disabled = { disabled }
525
444
readOnly = { inputReadOnly || ! typing }
526
- onMouseDown = { onInputMouseDown }
527
- onFocus = { onInputFocus }
528
- onBlur = { onInputBlur }
529
- value = { textValue }
530
445
onChange = { onInputChange }
531
- onKeyDown = { onInputKeyDown }
532
446
autoFocus = { autoFocus }
533
447
placeholder = { placeholder }
534
448
ref = { inputRef }
449
+ { ...inputProps }
535
450
size = { getInputSize ( picker , formatList [ 0 ] ) }
536
451
{ ...getDataOrAriaProps ( props ) }
537
452
/>
0 commit comments