@mui/x-date-pickers
Version:
The community edition of the MUI X Date and Time Picker components.
363 lines (358 loc) • 17.3 kB
JavaScript
"use strict";
'use client';
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.PickerFieldUI = PickerFieldUI;
exports.PickerFieldUIContext = void 0;
exports.PickerFieldUIContextProvider = PickerFieldUIContextProvider;
exports.cleanFieldResponse = void 0;
exports.mergeSlotProps = mergeSlotProps;
exports.useFieldTextFieldProps = useFieldTextFieldProps;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
var React = _interopRequireWildcard(require("react"));
var _useEventCallback = _interopRequireDefault(require("@mui/utils/useEventCallback"));
var _useForkRef = _interopRequireDefault(require("@mui/utils/useForkRef"));
var _resolveComponentProps = _interopRequireDefault(require("@mui/utils/resolveComponentProps"));
var _IconButton = _interopRequireDefault(require("@mui/material/IconButton"));
var _InputAdornment = _interopRequireDefault(require("@mui/material/InputAdornment"));
var _useSlotProps5 = _interopRequireDefault(require("@mui/utils/useSlotProps"));
var _warning = require("@mui/x-internals/warning");
var _useFieldOwnerState = require("../hooks/useFieldOwnerState");
var _hooks = require("../../hooks");
var _icons = require("../../icons");
var _useNullablePickerContext = require("../hooks/useNullablePickerContext");
var _PickersTextField = require("../../PickersTextField");
var _jsxRuntime = require("react/jsx-runtime");
const _excluded = ["readOnly", "onClear", "clearable", "clearButtonPosition", "openPickerButtonPosition", "openPickerAriaLabel", "InputProps", "inputProps", "InputLabelProps", "FormHelperTextProps"],
_excluded2 = ["ownerState"],
_excluded3 = ["ownerState"],
_excluded4 = ["ownerState"],
_excluded5 = ["ownerState"],
_excluded6 = ["InputProps", "inputProps", "InputLabelProps", "FormHelperTextProps"];
const cleanFieldResponse = fieldResponse => {
const _ref = fieldResponse,
{
readOnly,
onClear,
clearable,
clearButtonPosition,
openPickerButtonPosition,
openPickerAriaLabel,
// TODO v10: remove
// Explicitly discard legacy props that are no longer supported on `PickersTextField`.
// Without this, any leftover values would silently leak into `...other` and end up spread
// as unknown attributes on the underlying form control.
InputProps: legacyInputProps,
inputProps: legacyHtmlInputProps,
InputLabelProps: legacyInputLabelProps,
FormHelperTextProps: legacyFormHelperTextProps
} = _ref,
other = (0, _objectWithoutPropertiesLoose2.default)(_ref, _excluded);
if (process.env.NODE_ENV !== 'production') {
if (legacyInputProps || legacyHtmlInputProps || legacyInputLabelProps || legacyFormHelperTextProps) {
(0, _warning.warnOnce)(['MUI X: The `InputProps`, `inputProps`, `InputLabelProps` and `FormHelperTextProps` props are no longer supported on Picker / Field components.', 'They have been silently dropped because they would otherwise be forwarded as unknown attributes on the underlying form control.', 'Use the `slotProps` shape instead (`slotProps.input`, `slotProps.htmlInput`, `slotProps.inputLabel`, `slotProps.formHelperText`).', 'See https://mui.com/x/migration/migration-pickers-v8/#textfield-props for migration details.']);
}
}
return {
clearable,
onClear,
clearButtonPosition,
openPickerButtonPosition,
openPickerAriaLabel,
textFieldProps: (0, _extends2.default)({}, other, {
slotProps: (0, _extends2.default)({}, other?.slotProps, {
input: (0, _extends2.default)({}, other?.slotProps?.input, {
readOnly
})
})
})
};
};
exports.cleanFieldResponse = cleanFieldResponse;
const PickerFieldUIContext = exports.PickerFieldUIContext = /*#__PURE__*/React.createContext({
slots: {},
slotProps: {},
inputRef: undefined
});
/**
* Adds the button to open the Picker and the button to clear the value of the field.
* @ignore - internal component.
*/
if (process.env.NODE_ENV !== "production") PickerFieldUIContext.displayName = "PickerFieldUIContext";
function PickerFieldUI(props) {
const {
fieldResponse,
defaultOpenPickerIcon
} = props;
const translations = (0, _hooks.usePickerTranslations)();
const pickerContext = (0, _useNullablePickerContext.useNullablePickerContext)();
const pickerFieldUIContext = React.useContext(PickerFieldUIContext);
const {
textFieldProps,
onClear,
clearable,
openPickerAriaLabel,
clearButtonPosition: clearButtonPositionProp = 'end',
openPickerButtonPosition: openPickerButtonPositionProp = 'end'
} = cleanFieldResponse(fieldResponse);
const ownerState = (0, _useFieldOwnerState.useFieldOwnerState)(textFieldProps);
const handleClickOpeningButton = (0, _useEventCallback.default)(event => {
event.preventDefault();
// Force open instead of toggling to avoid conflicts with field-level open-on-focus logic
pickerContext?.setOpen(true);
});
const triggerStatus = pickerContext ? pickerContext.triggerStatus : 'hidden';
const clearButtonPosition = clearable ? clearButtonPositionProp : null;
const openPickerButtonPosition = triggerStatus !== 'hidden' ? openPickerButtonPositionProp : null;
const TextField = pickerFieldUIContext.slots.textField ?? _PickersTextField.PickersTextField;
const InputAdornment = pickerFieldUIContext.slots.inputAdornment ?? _InputAdornment.default;
const _useSlotProps = (0, _useSlotProps5.default)({
elementType: InputAdornment,
externalSlotProps: pickerFieldUIContext.slotProps.inputAdornment,
additionalProps: {
position: 'start'
},
ownerState: (0, _extends2.default)({}, ownerState, {
position: 'start'
})
}),
startInputAdornmentProps = (0, _objectWithoutPropertiesLoose2.default)(_useSlotProps, _excluded2);
const _useSlotProps2 = (0, _useSlotProps5.default)({
elementType: InputAdornment,
externalSlotProps: pickerFieldUIContext.slotProps.inputAdornment,
additionalProps: {
position: 'end'
},
ownerState: (0, _extends2.default)({}, ownerState, {
position: 'end'
})
}),
endInputAdornmentProps = (0, _objectWithoutPropertiesLoose2.default)(_useSlotProps2, _excluded3);
const OpenPickerButton = pickerFieldUIContext.slots.openPickerButton ?? _IconButton.default;
// We don't want to forward the `ownerState` to the `<IconButton />` component, see mui/material-ui#34056
const _useSlotProps3 = (0, _useSlotProps5.default)({
elementType: OpenPickerButton,
externalSlotProps: pickerFieldUIContext.slotProps.openPickerButton,
additionalProps: {
disabled: triggerStatus === 'disabled',
onClick: handleClickOpeningButton,
'aria-label': openPickerAriaLabel,
// Mark this element so field handlers can ignore its events
'data-mui-picker-open-button': 'true',
edge:
// open button is always rendered at the edge
textFieldProps.variant !== 'standard' ? openPickerButtonPosition : false
},
ownerState
}),
openPickerButtonProps = (0, _objectWithoutPropertiesLoose2.default)(_useSlotProps3, _excluded4);
const OpenPickerIcon = pickerFieldUIContext.slots.openPickerIcon ?? defaultOpenPickerIcon;
const openPickerIconProps = (0, _useSlotProps5.default)({
elementType: OpenPickerIcon,
externalSlotProps: pickerFieldUIContext.slotProps.openPickerIcon,
ownerState
});
const ClearButton = pickerFieldUIContext.slots.clearButton ?? _IconButton.default;
// We don't want to forward the `ownerState` to the `<IconButton />` component, see mui/material-ui#34056
const _useSlotProps4 = (0, _useSlotProps5.default)({
elementType: ClearButton,
externalSlotProps: pickerFieldUIContext.slotProps.clearButton,
className: 'clearButton',
additionalProps: {
title: translations.fieldClearLabel,
tabIndex: -1,
onClick: onClear,
disabled: fieldResponse.disabled || fieldResponse.readOnly,
edge:
// clear button can only be at the edge if it's position differs from the open button
textFieldProps.variant !== 'standard' && clearButtonPosition !== openPickerButtonPosition ? clearButtonPosition : false
},
ownerState
}),
clearButtonProps = (0, _objectWithoutPropertiesLoose2.default)(_useSlotProps4, _excluded5);
const ClearIcon = pickerFieldUIContext.slots.clearIcon ?? _icons.ClearIcon;
const clearIconProps = (0, _useSlotProps5.default)({
elementType: ClearIcon,
externalSlotProps: pickerFieldUIContext.slotProps.clearIcon,
additionalProps: {
fontSize: 'small'
},
ownerState
});
textFieldProps.ref = (0, _useForkRef.default)(textFieldProps.ref, pickerContext?.rootRef);
const externalInputSlotProps = textFieldProps.slotProps?.input;
const additionalInputSlotProps = {};
const forkedInputRef = (0, _useForkRef.default)(externalInputSlotProps?.ref, pickerContext?.triggerRef);
if (pickerContext) {
additionalInputSlotProps.ref = forkedInputRef;
}
if (!externalInputSlotProps?.startAdornment && (clearButtonPosition === 'start' || openPickerButtonPosition === 'start')) {
additionalInputSlotProps.startAdornment = /*#__PURE__*/(0, _jsxRuntime.jsxs)(InputAdornment, (0, _extends2.default)({}, startInputAdornmentProps, {
children: [openPickerButtonPosition === 'start' && /*#__PURE__*/(0, _jsxRuntime.jsx)(OpenPickerButton, (0, _extends2.default)({}, openPickerButtonProps, {
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(OpenPickerIcon, (0, _extends2.default)({}, openPickerIconProps))
})), clearButtonPosition === 'start' && /*#__PURE__*/(0, _jsxRuntime.jsx)(ClearButton, (0, _extends2.default)({}, clearButtonProps, {
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(ClearIcon, (0, _extends2.default)({}, clearIconProps))
}))]
}));
}
if (!externalInputSlotProps?.endAdornment && (clearButtonPosition === 'end' || openPickerButtonPosition === 'end')) {
additionalInputSlotProps.endAdornment = /*#__PURE__*/(0, _jsxRuntime.jsxs)(InputAdornment, (0, _extends2.default)({}, endInputAdornmentProps, {
children: [clearButtonPosition === 'end' && /*#__PURE__*/(0, _jsxRuntime.jsx)(ClearButton, (0, _extends2.default)({}, clearButtonProps, {
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(ClearIcon, (0, _extends2.default)({}, clearIconProps))
})), openPickerButtonPosition === 'end' && /*#__PURE__*/(0, _jsxRuntime.jsx)(OpenPickerButton, (0, _extends2.default)({}, openPickerButtonProps, {
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(OpenPickerIcon, (0, _extends2.default)({}, openPickerIconProps))
}))]
}));
}
// handle the case of showing custom `inputAdornment` for Field components
if (!additionalInputSlotProps.endAdornment && !additionalInputSlotProps.startAdornment && pickerFieldUIContext.slots.inputAdornment) {
additionalInputSlotProps.endAdornment = /*#__PURE__*/(0, _jsxRuntime.jsx)(InputAdornment, (0, _extends2.default)({}, endInputAdornmentProps));
}
if (clearButtonPosition != null) {
textFieldProps.sx = [{
'& .clearButton': {
opacity: 1
},
'@media (pointer: fine)': {
'& .clearButton': {
opacity: 0
},
'&:hover, &:focus-within': {
'.clearButton': {
opacity: 1
}
}
}
}, ...(Array.isArray(textFieldProps.sx) ? textFieldProps.sx : [textFieldProps.sx])];
}
textFieldProps.slotProps = (0, _extends2.default)({}, textFieldProps.slotProps, {
input: (0, _extends2.default)({}, externalInputSlotProps, additionalInputSlotProps)
});
return /*#__PURE__*/(0, _jsxRuntime.jsx)(TextField, (0, _extends2.default)({}, textFieldProps));
}
function mergeSlotProps(slotPropsA, slotPropsB) {
if (!slotPropsA) {
return slotPropsB;
}
if (!slotPropsB) {
return slotPropsA;
}
return ownerState => {
return (0, _extends2.default)({}, (0, _resolveComponentProps.default)(slotPropsB, ownerState), (0, _resolveComponentProps.default)(slotPropsA, ownerState));
};
}
/**
* The `textField` slot props cannot be handled inside `PickerFieldUI` because it would be a breaking change to not pass the enriched props to `useField`.
* TODO v10: Remove the `textField` slot and clean this logic up.
*/
function useFieldTextFieldProps(parameters) {
const {
ref,
externalForwardedProps,
slotProps
} = parameters;
const pickerFieldUIContext = React.useContext(PickerFieldUIContext);
const pickerContext = (0, _useNullablePickerContext.useNullablePickerContext)();
const ownerState = (0, _useFieldOwnerState.useFieldOwnerState)(externalForwardedProps);
// TODO v10: remove
// Strip the legacy `InputProps` / `inputProps` / `InputLabelProps` / `FormHelperTextProps`
// before they reach `PickersTextField`, which would silently ignore them. JS users without
// TypeScript checks would otherwise see their configuration vanish.
const _ref2 = externalForwardedProps ?? {},
{
InputProps: legacyInputProps,
inputProps: legacyHtmlInputProps,
InputLabelProps: legacyInputLabelProps,
FormHelperTextProps: legacyFormHelperTextProps
} = _ref2,
sanitizedExternalForwardedProps = (0, _objectWithoutPropertiesLoose2.default)(_ref2, _excluded6);
if (process.env.NODE_ENV !== 'production') {
if (legacyInputProps || legacyHtmlInputProps || legacyInputLabelProps || legacyFormHelperTextProps) {
(0, _warning.warnOnce)(['MUI X: Field components no longer accept the `InputProps`, `inputProps`, `InputLabelProps` and `FormHelperTextProps` props.', 'They have been dropped to avoid leaking unknown attributes onto the underlying form control.', 'Use the nested `slotProps.textField.slotProps.{input,htmlInput,inputLabel,formHelperText}` shape instead.']);
}
}
const textFieldProps = (0, _useSlotProps5.default)({
elementType: _PickersTextField.PickersTextField,
externalSlotProps: mergeSlotProps(pickerFieldUIContext.slotProps.textField, slotProps?.textField),
externalForwardedProps: sanitizedExternalForwardedProps,
additionalProps: {
ref,
sx: pickerContext?.rootSx,
label: pickerContext?.label,
name: pickerContext?.name,
className: pickerContext?.rootClassName,
inputRef: pickerFieldUIContext.inputRef
},
ownerState
});
// When requested, open the picker when the user focuses/clicks the field to edit
if (pickerContext && pickerContext.keepOpenDuringFieldFocus && pickerContext.triggerStatus === 'enabled' && !pickerContext.open && !pickerContext.readOnly && !pickerContext.disabled) {
const prevOnFocus = textFieldProps.onFocus;
const prevOnMouseDown = textFieldProps.onMouseDown;
const isFromOpenButton = event => {
const nativeEvent = event.nativeEvent ?? event;
const path = nativeEvent?.composedPath?.();
if (Array.isArray(path)) {
for (const el of path) {
if (el && el.getAttribute && el.getAttribute('data-mui-picker-open-button') === 'true') {
return true;
}
}
}
const target = event.target;
if (target && target.closest) {
return Boolean(target.closest('[data-mui-picker-open-button="true"]'));
}
return false;
};
textFieldProps.onFocus = event => {
prevOnFocus?.(event);
// Avoid opening if event was prevented by user code
if (!event.isDefaultPrevented() && !isFromOpenButton(event)) {
pickerContext.setOpen(true);
}
};
textFieldProps.onMouseDown = event => {
prevOnMouseDown?.(event);
if (!event.isDefaultPrevented() && !isFromOpenButton(event)) {
pickerContext.setOpen(true);
}
};
}
return textFieldProps;
}
function PickerFieldUIContextProvider(props) {
const {
slots = {},
slotProps = {},
inputRef,
children
} = props;
const contextValue = React.useMemo(() => ({
inputRef,
slots: {
openPickerButton: slots.openPickerButton,
openPickerIcon: slots.openPickerIcon,
textField: slots.textField,
inputAdornment: slots.inputAdornment,
clearIcon: slots.clearIcon,
clearButton: slots.clearButton
},
slotProps: {
openPickerButton: slotProps.openPickerButton,
openPickerIcon: slotProps.openPickerIcon,
textField: slotProps.textField,
inputAdornment: slotProps.inputAdornment,
clearIcon: slotProps.clearIcon,
clearButton: slotProps.clearButton
}
}), [inputRef, slots.openPickerButton, slots.openPickerIcon, slots.textField, slots.inputAdornment, slots.clearIcon, slots.clearButton, slotProps.openPickerButton, slotProps.openPickerIcon, slotProps.textField, slotProps.inputAdornment, slotProps.clearIcon, slotProps.clearButton]);
return /*#__PURE__*/(0, _jsxRuntime.jsx)(PickerFieldUIContext.Provider, {
value: contextValue,
children: children
});
}