diff --git a/packages/x-date-pickers/src/DateField/DateField.tsx b/packages/x-date-pickers/src/DateField/DateField.tsx
index 3b265ea6392ee..c16b3e3fadfb3 100644
--- a/packages/x-date-pickers/src/DateField/DateField.tsx
+++ b/packages/x-date-pickers/src/DateField/DateField.tsx
@@ -1,32 +1,15 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import MuiTextField from '@mui/material/TextField';
-import IconButton from '@mui/material/IconButton';
import { useThemeProps } from '@mui/material/styles';
-import ClearIcon from '@mui/icons-material/Clear';
import { useSlotProps } from '@mui/base/utils';
-import { DateFieldProps } from './DateField.types';
+import {
+ DateFieldProps,
+ DateFieldSlotsComponent,
+ DateFieldSlotsComponentsProps,
+} from './DateField.types';
import { useDateField } from './useDateField';
-
-const useClearEndAdornment = ({
- clearable,
- InputProps,
- onClear,
- clearIcon = ,
-}) => {
- return {
- endAdornment: clearable ? (
-
- {InputProps?.endAdornment}
-
- {clearIcon}
-
-
- ) : (
- InputProps?.endAdornment
- ),
- };
-};
+import { useClearEndAdornment } from '../internals/hooks/useClearEndAdornment/useClearEndAdornment';
type DateFieldComponent = ((
props: DateFieldProps & React.RefAttributes,
@@ -71,12 +54,16 @@ const DateField = React.forwardRef(function DateField(
inputRef: externalInputRef,
});
- console.log(fieldProps);
-
- const ProcessedInputProps = useClearEndAdornment({
+ const ProcessedInputProps = useClearEndAdornment<
+ typeof fieldProps.InputProps,
+ DateFieldSlotsComponent,
+ DateFieldSlotsComponentsProps
+ >({
onClear,
clearable,
InputProps: fieldProps.InputProps,
+ slots,
+ slotProps,
});
return (
diff --git a/packages/x-date-pickers/src/DateField/DateField.types.ts b/packages/x-date-pickers/src/DateField/DateField.types.ts
index acf3abdea8a18..a1436c845b942 100644
--- a/packages/x-date-pickers/src/DateField/DateField.types.ts
+++ b/packages/x-date-pickers/src/DateField/DateField.types.ts
@@ -12,6 +12,10 @@ import {
} from '../internals/models/validation';
import { FieldsTextFieldProps } from '../internals/models/fields';
import { SlotsAndSlotProps } from '../internals/utils/slots-migration';
+import {
+ FieldSlotsComponents,
+ FieldSlotsComponentsProps,
+} from '../internals/hooks/useField/useField.types';
export interface UseDateFieldParams {
props: UseDateFieldComponentProps;
@@ -45,7 +49,7 @@ export interface DateFieldProps
export type DateFieldOwnerState = DateFieldProps;
-export interface DateFieldSlotsComponent {
+export interface DateFieldSlotsComponent extends FieldSlotsComponents {
/**
* Form control with an input to render the value.
* Receives the same props as `@mui/material/TextField`.
@@ -54,6 +58,6 @@ export interface DateFieldSlotsComponent {
TextField?: React.ElementType;
}
-export interface DateFieldSlotsComponentsProps {
+export interface DateFieldSlotsComponentsProps extends FieldSlotsComponentsProps {
textField?: SlotComponentProps>;
}
diff --git a/packages/x-date-pickers/src/internals/hooks/useClearEndAdornment/useClearEndAdornment.tsx b/packages/x-date-pickers/src/internals/hooks/useClearEndAdornment/useClearEndAdornment.tsx
new file mode 100644
index 0000000000000..518c0e3659748
--- /dev/null
+++ b/packages/x-date-pickers/src/internals/hooks/useClearEndAdornment/useClearEndAdornment.tsx
@@ -0,0 +1,53 @@
+import * as React from 'react';
+
+import { useSlotProps } from '@mui/base';
+import ClearIcon from '@mui/icons-material/Clear';
+import IconButton from '@mui/material/IconButton';
+import { SlotsAndSlotProps } from '../../utils/slots-migration';
+import { FieldSlotsComponents, FieldSlotsComponentsProps } from '../useField/useField.types';
+
+type UseClearEndAdornmentProps<
+ TInputProps extends { endAdornment?: React.ReactNode } | undefined,
+ TFieldSlotsComponents extends FieldSlotsComponents,
+ TFieldSlotsComponentsProps extends FieldSlotsComponentsProps,
+> = {
+ clearable: boolean;
+ InputProps: TInputProps;
+ onClear: React.MouseEventHandler;
+} & SlotsAndSlotProps;
+
+export const useClearEndAdornment = <
+ TInputProps extends { endAdornment?: React.ReactNode } | undefined,
+ TFieldSlotsComponents extends FieldSlotsComponents,
+ TFieldSlotsComponentsProps extends FieldSlotsComponentsProps,
+>({
+ clearable,
+ InputProps: ForwardedInputProps,
+ onClear,
+ slots,
+ slotProps,
+}: UseClearEndAdornmentProps) => {
+ const EndClearIcon = slots?.clearIcon ?? ClearIcon;
+ const endClearIconProps = useSlotProps({
+ elementType: ClearIcon,
+ externalSlotProps: slotProps?.clearIcon,
+ externalForwardedProps: {},
+ ownerState: {},
+ });
+
+ const InputProps = {
+ ...ForwardedInputProps,
+ endAdornment: clearable ? (
+
+ {ForwardedInputProps?.endAdornment}
+
+
+
+
+ ) : (
+ ForwardedInputProps?.endAdornment
+ ),
+ };
+
+ return InputProps;
+};
diff --git a/packages/x-date-pickers/src/internals/hooks/useField/useField.ts b/packages/x-date-pickers/src/internals/hooks/useField/useField.ts
index 3135012a194f9..8c685094e87bf 100644
--- a/packages/x-date-pickers/src/internals/hooks/useField/useField.ts
+++ b/packages/x-date-pickers/src/internals/hooks/useField/useField.ts
@@ -40,6 +40,7 @@ export const useField = <
setTempAndroidValueStr,
sectionsValueBoundaries,
placeholder,
+ setIsHovered,
} = useFieldState(params);
const {
@@ -56,6 +57,8 @@ export const useField = <
error,
clearable,
onClear,
+ onMouseEnter,
+ onMouseLeave,
...otherForwardedProps
},
fieldValueManager,
@@ -143,13 +146,17 @@ export const useField = <
});
const handleInputBlur = useEventCallback((event: React.FocusEvent, ...args) => {
- const isClearButton = Boolean(
- event.relatedTarget?.className?.split(' ')?.includes('deleteIcon'),
- );
- // if (!isClearButton) {
- onBlur?.(event, ...(args as []));
- setSelectedSections(null);
- // }
+ const { relatedTarget } = event;
+
+ const shouldBlur =
+ !relatedTarget &&
+ relatedTarget !== inputRef.current &&
+ !inputRef.current.contains(relatedTarget);
+
+ if (shouldBlur) {
+ onBlur?.(event, ...(args as []));
+ setSelectedSections(null);
+ }
});
const handleInputPaste = useEventCallback((event: React.ClipboardEvent) => {
@@ -459,6 +466,7 @@ export const useField = <
valueManager.emptyValue,
);
const shouldShowPlaceholder = !inputHasFocus && areAllSectionsEmpty;
+ const isInputHovered = state.isHovered;
React.useImperativeHandle(unstableFieldRef, () => ({
getSections: () => state.sections,
@@ -480,16 +488,25 @@ export const useField = <
setSelectedSections: (activeSectionIndex) => setSelectedSections(activeSectionIndex),
}));
- const handleClearValue = (event, ...args) => {
+ const handleClearValue = useEventCallback((event: React.MouseEvent, ...args) => {
+ // the click event of the endAdornmnet propagates to the input and triggers the `handleInputClick` handler.
event.stopPropagation();
event.preventDefault();
- onClear?.(...(args as []));
+ onClear?.(event, ...(args as []));
clearValue();
setSelectedSections(0);
inputRef?.current?.focus();
- };
+ });
+
+ const handleMouseEnter = useEventCallback((event: React.MouseEvent, ...args) => {
+ onMouseEnter?.(event, ...(args as []));
+ setIsHovered(true);
+ });
- console.log(selectedSectionIndexes);
+ const handleMouseLeave = useEventCallback((event: React.MouseEvent, ...args) => {
+ onMouseLeave?.(event, ...(args as []));
+ setIsHovered(false);
+ });
return {
placeholder,
@@ -508,6 +525,8 @@ export const useField = <
onClear: handleClearValue,
error: inputError,
ref: handleRef,
- clearable: Boolean(clearable && inputHasFocus && !areAllSectionsEmpty),
+ clearable: Boolean(clearable && !areAllSectionsEmpty && (inputHasFocus || isInputHovered)),
+ onMouseEnter: handleMouseEnter,
+ onMouseLeave: handleMouseLeave,
};
};
diff --git a/packages/x-date-pickers/src/internals/hooks/useField/useField.types.ts b/packages/x-date-pickers/src/internals/hooks/useField/useField.types.ts
index e0a2b4143073b..b5ffe29586058 100644
--- a/packages/x-date-pickers/src/internals/hooks/useField/useField.types.ts
+++ b/packages/x-date-pickers/src/internals/hooks/useField/useField.types.ts
@@ -1,4 +1,6 @@
import * as React from 'react';
+import { SlotComponentProps } from '@mui/base/utils';
+import ClearIcon from '@mui/icons-material/Clear';
import {
FieldSectionType,
FieldSection,
@@ -136,8 +138,10 @@ export interface UseFieldForwardedProps {
onFocus?: () => void;
onBlur?: React.FocusEventHandler;
error?: boolean;
- onClear?: () => void;
+ onClear?: React.MouseEventHandler;
clearable?: boolean;
+ onMouseEnter?: React.MouseEventHandler;
+ onMouseLeave?: React.MouseEventHandler;
}
export type UseFieldResponse = Omit<
@@ -319,6 +323,7 @@ export interface UseFieldState {
* The property below allows us to set the first `onChange` value into state waiting for the second one.
*/
tempValueStrAndroid: string | null;
+ isHovered: boolean;
}
export type UseFieldValidationProps<
@@ -361,3 +366,16 @@ export type SectionOrdering = {
*/
endIndex: number;
};
+
+export interface FieldSlotsComponents {
+ /**
+ * Icon to display inside the clear button.
+ * Receives the same props as `@mui/icons-material/Clear`.
+ * @default ClearIcon from '@mui/icons-material/Clear'
+ */
+ ClearIcon?: React.ElementType;
+}
+
+export interface FieldSlotsComponentsProps {
+ clearIcon?: SlotComponentProps;
+}
diff --git a/packages/x-date-pickers/src/internals/hooks/useField/useFieldState.ts b/packages/x-date-pickers/src/internals/hooks/useField/useFieldState.ts
index 14343b895869b..c7b80c04dafda 100644
--- a/packages/x-date-pickers/src/internals/hooks/useField/useFieldState.ts
+++ b/packages/x-date-pickers/src/internals/hooks/useField/useFieldState.ts
@@ -113,6 +113,7 @@ export const useFieldState = <
valueManager.getTodayValue(utils, valueType),
),
tempValueStrAndroid: null,
+ isHovered: false,
};
});
@@ -133,6 +134,13 @@ export const useFieldState = <
}));
};
+ const setIsHovered = (isHovered: boolean) => {
+ setState((prevState) => ({
+ ...prevState,
+ isHovered,
+ }));
+ };
+
const selectedSectionIndexes = React.useMemo(() => {
if (selectedSections == null) {
return null;
@@ -412,5 +420,6 @@ export const useFieldState = <
setTempAndroidValueStr,
sectionsValueBoundaries,
placeholder,
+ setIsHovered,
};
};