Skip to content

Commit 989e207

Browse files
committed
input move into hooks
1 parent 8d4d62d commit 989e207

File tree

5 files changed

+260
-529
lines changed

5 files changed

+260
-529
lines changed

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module.exports = {
1010
'react/sort-comp': 0,
1111
'@typescript-eslint/no-explicit-any': 0,
1212
'default-case': 0,
13+
'no-confusing-arrow': 0,
1314
'jsx-a11y/no-autofocus': 0,
1415
'import/no-extraneous-dependencies': [
1516
'error',

src/Picker.tsx

Lines changed: 35 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
*/
1313

1414
import * as React from 'react';
15-
import KeyCode from 'rc-util/lib/KeyCode';
1615
import classNames from 'classnames';
1716
import { AlignType } from 'rc-trigger/lib/interface';
1817
import PickerPanel, {
@@ -25,11 +24,8 @@ import { isEqual } from './utils/dateUtil';
2524
import getDataOrAriaProps, { toArray } from './utils/miscUtil';
2625
import PanelContext, { ContextOperationRefProps } from './PanelContext';
2726
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';
3329

3430
export interface PickerRefConfig {
3531
focus: () => void;
@@ -47,6 +43,7 @@ export interface PickerSharedProps<DateType> extends React.AriaAttributes {
4743
autoFocus?: boolean;
4844
disabled?: boolean;
4945
open?: boolean;
46+
defaultOpen?: boolean;
5047
/** Make input readOnly to avoid popup keyboard in mobile */
5148
inputReadOnly?: boolean;
5249

@@ -134,6 +131,7 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
134131
value,
135132
defaultValue,
136133
open,
134+
defaultOpen,
137135
suffixIcon,
138136
clearIcon,
139137
disabled,
@@ -192,7 +190,6 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
192190
)
193191
: '',
194192
);
195-
const [typing, setTyping] = React.useState(false);
196193

197194
/** Similar as `setTextValue` but accept `DateType` and convert into string */
198195
const setDateText = (date: DateType | null) => {
@@ -209,7 +206,12 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
209206
>(null);
210207

211208
// 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+
});
213215
let mergedOpen: boolean;
214216
if (disabled) {
215217
mergedOpen = false;
@@ -230,9 +232,6 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
230232
}
231233
};
232234

233-
// Focus
234-
const [focused, setFocused] = React.useState(false);
235-
236235
// ============================= Value =============================
237236
const isSameTextDate = (text: string, date: DateType | null) => {
238237
if (date === null) {
@@ -255,11 +254,6 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
255254
setInternalSelectedValue(newDate);
256255
};
257256

258-
const onInputMouseDown: React.MouseEventHandler<HTMLInputElement> = () => {
259-
triggerOpen(true);
260-
setTyping(true);
261-
};
262-
263257
const onInputChange: React.ChangeEventHandler<HTMLInputElement> = e => {
264258
const text = e.target.value;
265259
setTextValue(text);
@@ -290,95 +284,42 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
290284
};
291285

292286
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) {
299288
// Let popup panel handle keyboard
300289
return operationRef.current.onKeyDown(e);
301290
}
302291
return false;
303292
};
304293

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-
363294
const triggerClose = () => {
364295
triggerOpen(false);
365296
setInnerValue(selectedValue);
366297
triggerChange(selectedValue);
367298
};
368299

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+
});
382323

383324
// ============================= Sync ==============================
384325
// Close should sync back with text value
@@ -402,28 +343,6 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
402343
}
403344
}, [mergedValue]);
404345

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-
427346
// ============================ Private ============================
428347
if (pickerRef) {
429348
pickerRef.current = {
@@ -523,15 +442,11 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
523442
<input
524443
disabled={disabled}
525444
readOnly={inputReadOnly || !typing}
526-
onMouseDown={onInputMouseDown}
527-
onFocus={onInputFocus}
528-
onBlur={onInputBlur}
529-
value={textValue}
530445
onChange={onInputChange}
531-
onKeyDown={onInputKeyDown}
532446
autoFocus={autoFocus}
533447
placeholder={placeholder}
534448
ref={inputRef}
449+
{...inputProps}
535450
size={getInputSize(picker, formatList[0])}
536451
{...getDataOrAriaProps(props)}
537452
/>

0 commit comments

Comments
 (0)