Skip to content

Commit

Permalink
[pickers] Improve DOM event management on useField (mui#5901)
Browse files Browse the repository at this point in the history
  • Loading branch information
flaviendelangle authored and alexfauquette committed Sep 20, 2022
1 parent 4c72864 commit a3106e1
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 34 deletions.
28 changes: 12 additions & 16 deletions docs/data/date-pickers/date-field/CustomUIDateField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@ import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
import {
unstable_useDateField as useDateField,
UseDateFieldProps,
UseDateFieldComponentProps,
} from '@mui/x-date-pickers/DateField';

interface JoyDateFieldProps
extends Omit<JoyTextFieldProps, 'value' | 'defaultValue' | 'onChange' | 'onError'>,
UseDateFieldProps<Date, Date> {}
type JoyDateFieldProps = UseDateFieldComponentProps<Date, Date, JoyTextFieldProps>;

const JoyDateField = (props: JoyDateFieldProps) => {
const { inputRef, inputProps } = useDateField<Date, Date, JoyDateFieldProps>(
Expand All @@ -31,12 +29,11 @@ const JoyDateField = (props: JoyDateFieldProps) => {
);
};

interface UnstyledDateFieldProps
extends Omit<
InputUnstyledProps,
'value' | 'defaultValue' | 'onChange' | 'onError'
>,
UseDateFieldProps<Date, Date> {}
type UnstyledDateFieldProps = UseDateFieldComponentProps<
Date,
Date,
InputUnstyledProps
>;

const UnstyledDateField = (props: UnstyledDateFieldProps) => {
const { inputRef, inputProps } = useDateField<Date, Date, UnstyledDateFieldProps>(
Expand All @@ -51,12 +48,11 @@ const UnstyledDateField = (props: UnstyledDateFieldProps) => {
);
};

interface BrowserInputDateFieldProps
extends Omit<
React.HTMLAttributes<HTMLInputElement>,
'value' | 'defaultValue' | 'onChange' | 'onError'
>,
UseDateFieldProps<Date, Date> {}
type BrowserInputDateFieldProps = UseDateFieldComponentProps<
Date,
Date,
React.HTMLAttributes<HTMLInputElement>
>;

const BrowserInputDateField = (props: BrowserInputDateFieldProps) => {
const { inputRef, inputProps } = useDateField<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,18 @@ export type UseDateRangeFieldDefaultizedProps<TInputDate, TDate> = DefaultizedPr
'minDate' | 'maxDate' | 'disableFuture' | 'disablePast'
>;

export type DateRangeFieldProps<TInputDate, TDate> = Omit<
TextFieldProps,
export type UseDateRangeFieldComponentProps<TInputDate, TDate, ChildProps extends {}> = Omit<
ChildProps,
'value' | 'defaultValue' | 'onChange' | 'onError'
> &
UseDateRangeFieldProps<TInputDate, TDate>;

export type DateRangeFieldProps<TInputDate, TDate> = UseDateRangeFieldComponentProps<
TInputDate,
TDate,
TextFieldProps
>;

export interface DateRangeFieldSection extends FieldSection {
dateName: 'start' | 'end';
}
14 changes: 11 additions & 3 deletions packages/x-date-pickers/src/DateField/DateField.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ export type UseDateFieldDefaultizedProps<TInputDate, TDate> = DefaultizedProps<
'minDate' | 'maxDate' | 'disableFuture' | 'disablePast'
>;

export interface DateFieldProps<TInputDate, TDate>
extends Omit<TextFieldProps, 'value' | 'defaultValue' | 'onChange' | 'onError'>,
UseDateFieldProps<TInputDate, TDate> {}
export type UseDateFieldComponentProps<TInputDate, TDate, ChildProps extends {}> = Omit<
ChildProps,
'value' | 'defaultValue' | 'onChange' | 'onError'
> &
UseDateFieldProps<TInputDate, TDate>;

export type DateFieldProps<TInputDate, TDate> = UseDateFieldComponentProps<
TInputDate,
TDate,
TextFieldProps
>;
2 changes: 1 addition & 1 deletion packages/x-date-pickers/src/DateField/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { DateField as Unstable_DateField } from './DateField';
export { useDateField as unstable_useDateField } from './useDateField';
export type { UseDateFieldProps } from './DateField.interfaces';
export type { UseDateFieldProps, UseDateFieldComponentProps } from './DateField.interfaces';
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,17 @@ export interface UseFieldProps<TInputValue, TValue, TError> {
* @default false
*/
readOnly?: boolean;
onKeyDown?: React.KeyboardEventHandler;
onClick?: () => void;
onFocus?: () => void;
onBlur?: () => void;
}

export interface UseFieldResponse<TProps> {
inputProps: Omit<TProps, keyof UseFieldProps<any, any, any>> & {
value: string;
onClick: React.MouseEventHandler<HTMLInputElement>;
onKeyDown: React.KeyboardEventHandler<HTMLInputElement>;
onClick: () => void;
onFocus: () => void;
onBlur: () => void;
error: boolean;
Expand Down
44 changes: 33 additions & 11 deletions packages/x-date-pickers/src/internals/hooks/useField/useField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ export const useField = <
defaultValue,
onChange,
onError,
onClick,
onKeyDown,
onFocus,
onBlur,
format = utils.formats.keyboardDate,
readOnly = false,
...otherProps
Expand All @@ -49,6 +53,7 @@ export const useField = <
} = params;

const firstDefaultValue = React.useRef(defaultValue);
const focusTimeoutRef = React.useRef<NodeJS.Timeout | undefined>(undefined);

const valueParsed = React.useMemo(() => {
// TODO: Avoid this type casting, the emptyValues are both valid TDate and TInputDate
Expand Down Expand Up @@ -97,7 +102,9 @@ export const useField = <
}));
};

const handleInputClick = useEventCallback(() => {
const handleInputClick = useEventCallback((...args) => {
onClick?.(...(args as []));

if (state.sections.length === 0) {
return;
}
Expand All @@ -110,7 +117,24 @@ export const useField = <
updateSelectedSections(sectionIndex);
});

const handleInputKeyDown = useEventCallback((event: React.KeyboardEvent<HTMLInputElement>) => {
const handleInputFocus = useEventCallback((...args) => {
onFocus?.(...(args as []));
focusTimeoutRef.current = setTimeout(() => {
if ((inputRef.current?.selectionEnd ?? 0) - (inputRef.current?.selectionStart ?? 0) === 0) {
handleInputClick();
} else {
updateSelectedSections(0, state.sections.length - 1);
}
});
});

const handleInputBlur = useEventCallback((...args) => {
onBlur?.(...(args as []));
updateSelectedSections();
});

const handleInputKeyDown = useEventCallback((event: React.KeyboardEvent) => {
onKeyDown?.(event);
if (!inputRef.current || state.sections.length === 0) {
return;
}
Expand Down Expand Up @@ -316,13 +340,6 @@ export const useField = <
}
});

const handleInputFocus = useEventCallback(() => {
// TODO: Avoid applying focus when focus is caused by a click
updateSelectedSections(0, state.sections.length - 1);
});

const handleInputBlur = useEventCallback(() => updateSelectedSections());

useEnhancedEffect(() => {
if (!inputRef.current || state.selectedSectionIndexes == null) {
return;
Expand Down Expand Up @@ -375,15 +392,20 @@ export const useField = <
[fieldValueManager, validationError],
);

React.useEffect(() => {
return () => window.clearTimeout(focusTimeoutRef.current);
}, []);

return {
inputProps: {
...otherProps,
value: state.valueStr,
onClick: handleInputClick,
onKeyDown: handleInputKeyDown,
onFocus: handleInputFocus,
onBlur: handleInputBlur,

onKeyDown: handleInputKeyDown,
error: inputError,
...otherProps,
},
inputRef,
};
Expand Down

0 comments on commit a3106e1

Please sign in to comment.