@mui/x-date-pickers
Version:
The community edition of the Date and Time Picker components (MUI X).
475 lines (473 loc) • 17.3 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.PickersInputBaseSectionsContainer = exports.PickersInputBaseRoot = exports.PickersInputBase = void 0;
var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var React = _interopRequireWildcard(require("react"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _FormControl = require("@mui/material/FormControl");
var _styles = require("@mui/material/styles");
var _useForkRef = _interopRequireDefault(require("@mui/utils/useForkRef"));
var _utils = require("@mui/utils");
var _composeClasses = _interopRequireDefault(require("@mui/utils/composeClasses"));
var _capitalize = _interopRequireDefault(require("@mui/utils/capitalize"));
var _useSlotProps = _interopRequireDefault(require("@mui/utils/useSlotProps"));
var _visuallyHidden = _interopRequireDefault(require("@mui/utils/visuallyHidden"));
var _pickersInputBaseClasses = require("./pickersInputBaseClasses");
var _PickersSectionList = require("../../PickersSectionList");
var _usePickerTextFieldOwnerState = require("../usePickerTextFieldOwnerState");
var _jsxRuntime = require("react/jsx-runtime");
const _excluded = ["elements", "areAllSectionsEmpty", "defaultValue", "label", "value", "onChange", "id", "autoFocus", "endAdornment", "startAdornment", "renderSuffix", "slots", "slotProps", "contentEditable", "tabIndex", "onInput", "onPaste", "onKeyDown", "fullWidth", "name", "readOnly", "inputProps", "inputRef", "sectionListRef", "onFocus", "onBlur", "classes", "ownerState"];
const round = value => Math.round(value * 1e5) / 1e5;
const PickersInputBaseRoot = exports.PickersInputBaseRoot = (0, _styles.styled)('div', {
name: 'MuiPickersInputBase',
slot: 'Root'
})(({
theme
}) => (0, _extends2.default)({}, theme.typography.body1, {
color: (theme.vars || theme).palette.text.primary,
cursor: 'text',
padding: 0,
display: 'flex',
justifyContent: 'flex-start',
alignItems: 'center',
position: 'relative',
boxSizing: 'border-box',
// Prevent padding issue with fullWidth.
letterSpacing: `${round(0.15 / 16)}em`,
variants: [{
props: {
isInputInFullWidth: true
},
style: {
width: '100%'
}
}]
}));
const PickersInputBaseSectionsContainer = exports.PickersInputBaseSectionsContainer = (0, _styles.styled)(_PickersSectionList.Unstable_PickersSectionListRoot, {
name: 'MuiPickersInputBase',
slot: 'SectionsContainer'
})(({
theme
}) => ({
padding: '4px 0 5px',
fontFamily: theme.typography.fontFamily,
fontSize: 'inherit',
lineHeight: '1.4375em',
// 23px
flexGrow: 1,
outline: 'none',
display: 'flex',
flexWrap: 'nowrap',
overflow: 'hidden',
letterSpacing: 'inherit',
// Baseline behavior
width: '182px',
variants: [{
props: {
fieldDirection: 'rtl'
},
style: {
textAlign: 'right /*! @noflip */'
}
}, {
props: {
inputSize: 'small'
},
style: {
paddingTop: 1
}
}, {
props: {
hasStartAdornment: false,
isFieldFocused: false,
isFieldValueEmpty: true
},
style: {
color: 'currentColor',
opacity: 0
}
}, {
props: {
hasStartAdornment: false,
isFieldFocused: false,
isFieldValueEmpty: true,
inputHasLabel: false
},
style: theme.vars ? {
opacity: theme.vars.opacity.inputPlaceholder
} : {
opacity: theme.palette.mode === 'light' ? 0.42 : 0.5
}
}]
}));
const PickersInputBaseSection = (0, _styles.styled)(_PickersSectionList.Unstable_PickersSectionListSection, {
name: 'MuiPickersInputBase',
slot: 'Section'
})(({
theme
}) => ({
fontFamily: theme.typography.fontFamily,
fontSize: 'inherit',
letterSpacing: 'inherit',
lineHeight: '1.4375em',
// 23px
display: 'inline-block',
whiteSpace: 'nowrap'
}));
const PickersInputBaseSectionContent = (0, _styles.styled)(_PickersSectionList.Unstable_PickersSectionListSectionContent, {
name: 'MuiPickersInputBase',
slot: 'SectionContent',
overridesResolver: (props, styles) => styles.content // FIXME: Inconsistent naming with slot
})(({
theme
}) => ({
fontFamily: theme.typography.fontFamily,
lineHeight: '1.4375em',
// 23px
letterSpacing: 'inherit',
width: 'fit-content',
outline: 'none'
}));
const PickersInputBaseSectionSeparator = (0, _styles.styled)(_PickersSectionList.Unstable_PickersSectionListSectionSeparator, {
name: 'MuiPickersInputBase',
slot: 'Separator'
})(() => ({
whiteSpace: 'pre',
letterSpacing: 'inherit'
}));
const PickersInputBaseInput = (0, _styles.styled)('input', {
name: 'MuiPickersInputBase',
slot: 'Input',
overridesResolver: (props, styles) => styles.hiddenInput // FIXME: Inconsistent naming with slot
})((0, _extends2.default)({}, _visuallyHidden.default));
const PickersInputBaseActiveBar = (0, _styles.styled)('div', {
name: 'MuiPickersInputBase',
slot: 'ActiveBar'
})(({
theme,
ownerState
}) => ({
display: 'none',
position: 'absolute',
height: 2,
bottom: 2,
borderTopLeftRadius: 2,
borderTopRightRadius: 2,
transition: theme.transitions.create(['width', 'left'], {
duration: theme.transitions.duration.shortest
}),
backgroundColor: (theme.vars || theme).palette.primary.main,
'[data-active-range-position="start"] &, [data-active-range-position="end"] &': {
display: 'block'
},
'[data-active-range-position="start"] &': {
left: ownerState.sectionOffsets[0]
},
'[data-active-range-position="end"] &': {
left: ownerState.sectionOffsets[1]
}
}));
const useUtilityClasses = (classes, ownerState) => {
const {
isFieldFocused,
isFieldDisabled,
isFieldReadOnly,
hasFieldError,
inputSize,
isInputInFullWidth,
inputColor,
hasStartAdornment,
hasEndAdornment
} = ownerState;
const slots = {
root: ['root', isFieldFocused && !isFieldDisabled && 'focused', isFieldDisabled && 'disabled', isFieldReadOnly && 'readOnly', hasFieldError && 'error', isInputInFullWidth && 'fullWidth', `color${(0, _capitalize.default)(inputColor)}`, inputSize === 'small' && 'inputSizeSmall', hasStartAdornment && 'adornedStart', hasEndAdornment && 'adornedEnd'],
notchedOutline: ['notchedOutline'],
input: ['input'],
sectionsContainer: ['sectionsContainer'],
sectionContent: ['sectionContent'],
sectionBefore: ['sectionBefore'],
sectionAfter: ['sectionAfter'],
activeBar: ['activeBar']
};
return (0, _composeClasses.default)(slots, _pickersInputBaseClasses.getPickersInputBaseUtilityClass, classes);
};
function resolveSectionElementWidth(sectionElement, rootRef, index, dateRangePosition) {
if (sectionElement.content.id) {
const activeSectionElements = rootRef.current?.querySelectorAll(`[data-sectionindex="${index}"] [data-range-position="${dateRangePosition}"]`);
if (activeSectionElements) {
return Array.from(activeSectionElements).reduce((currentActiveBarWidth, element) => {
return currentActiveBarWidth + element.offsetWidth;
}, 0);
}
}
return 0;
}
function resolveSectionWidthAndOffsets(elements, rootRef) {
let activeBarWidth = 0;
const activeRangePosition = rootRef.current?.getAttribute('data-active-range-position');
if (activeRangePosition === 'end') {
for (let i = elements.length - 1; i >= elements.length / 2; i -= 1) {
activeBarWidth += resolveSectionElementWidth(elements[i], rootRef, i, 'end');
}
} else {
for (let i = 0; i < elements.length / 2; i += 1) {
activeBarWidth += resolveSectionElementWidth(elements[i], rootRef, i, 'start');
}
}
return {
activeBarWidth,
sectionOffsets: [rootRef.current?.querySelector(`[data-sectionindex="0"]`)?.offsetLeft || 0, rootRef.current?.querySelector(`[data-sectionindex="${elements.length / 2}"]`)?.offsetLeft || 0]
};
}
/**
* @ignore - internal component.
*/
const PickersInputBase = exports.PickersInputBase = /*#__PURE__*/React.forwardRef(function PickersInputBase(inProps, ref) {
const props = (0, _styles.useThemeProps)({
props: inProps,
name: 'MuiPickersInputBase'
});
const {
elements,
areAllSectionsEmpty,
value,
onChange,
id,
endAdornment,
startAdornment,
renderSuffix,
slots,
slotProps,
contentEditable,
tabIndex,
onInput,
onPaste,
onKeyDown,
name,
readOnly,
inputProps,
inputRef,
sectionListRef,
onFocus,
onBlur,
classes: classesProp,
ownerState: ownerStateProp
} = props,
other = (0, _objectWithoutPropertiesLoose2.default)(props, _excluded);
const ownerStateContext = (0, _usePickerTextFieldOwnerState.usePickerTextFieldOwnerState)();
const rootRef = React.useRef(null);
const activeBarRef = React.useRef(null);
const sectionOffsetsRef = React.useRef([]);
const handleRootRef = (0, _useForkRef.default)(ref, rootRef);
const handleInputRef = (0, _useForkRef.default)(inputProps?.ref, inputRef);
const muiFormControl = (0, _FormControl.useFormControl)();
if (!muiFormControl) {
throw new Error('MUI X: PickersInputBase should always be used inside a PickersTextField component');
}
const ownerState = ownerStateProp ?? ownerStateContext;
const handleInputFocus = event => {
muiFormControl.onFocus?.(event);
onFocus?.(event);
};
const handleHiddenInputFocus = event => {
handleInputFocus(event);
};
const handleKeyDown = event => {
onKeyDown?.(event);
if (event.key === 'Enter' && !event.defaultMuiPrevented) {
// Do nothing if it's a multi input field
if (rootRef.current?.dataset.multiInput) {
return;
}
const closestForm = rootRef.current?.closest('form');
const submitTrigger = closestForm?.querySelector('[type="submit"]');
if (!closestForm || !submitTrigger) {
// do nothing if there is no form or no submit button (trigger)
return;
}
event.preventDefault();
// native input trigger submit with the `submitter` field set
closestForm.requestSubmit(submitTrigger);
}
};
const handleInputBlur = event => {
muiFormControl.onBlur?.(event);
onBlur?.(event);
};
React.useEffect(() => {
if (muiFormControl) {
muiFormControl.setAdornedStart(Boolean(startAdornment));
}
}, [muiFormControl, startAdornment]);
React.useEffect(() => {
if (!muiFormControl) {
return;
}
if (areAllSectionsEmpty) {
muiFormControl.onEmpty();
} else {
muiFormControl.onFilled();
}
}, [muiFormControl, areAllSectionsEmpty]);
const classes = useUtilityClasses(classesProp, ownerState);
const InputRoot = slots?.root || PickersInputBaseRoot;
const inputRootProps = (0, _useSlotProps.default)({
elementType: InputRoot,
externalSlotProps: slotProps?.root,
externalForwardedProps: other,
additionalProps: {
'aria-invalid': muiFormControl.error,
ref: handleRootRef
},
className: classes.root,
ownerState
});
const InputSectionsContainer = slots?.input || PickersInputBaseSectionsContainer;
const isSingleInputRange = elements.some(element => element.content['data-range-position'] !== undefined);
React.useEffect(() => {
if (!isSingleInputRange || !ownerState.isPickerOpen) {
return;
}
const {
activeBarWidth,
sectionOffsets
} = resolveSectionWidthAndOffsets(elements, rootRef);
sectionOffsetsRef.current = [sectionOffsets[0], sectionOffsets[1]];
if (activeBarRef.current) {
activeBarRef.current.style.width = `${activeBarWidth}px`;
}
}, [elements, isSingleInputRange, ownerState.isPickerOpen]);
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(InputRoot, (0, _extends2.default)({}, inputRootProps, {
children: [startAdornment, /*#__PURE__*/(0, _jsxRuntime.jsx)(_PickersSectionList.Unstable_PickersSectionList, {
sectionListRef: sectionListRef,
elements: elements,
contentEditable: contentEditable,
tabIndex: tabIndex,
className: classes.sectionsContainer,
onFocus: handleInputFocus,
onBlur: handleInputBlur,
onInput: onInput,
onPaste: onPaste,
onKeyDown: handleKeyDown,
slots: {
root: InputSectionsContainer,
section: PickersInputBaseSection,
sectionContent: PickersInputBaseSectionContent,
sectionSeparator: PickersInputBaseSectionSeparator
},
slotProps: {
root: (0, _extends2.default)({}, slotProps?.input, {
ownerState
}),
sectionContent: {
className: _pickersInputBaseClasses.pickersInputBaseClasses.sectionContent
},
sectionSeparator: ({
separatorPosition
}) => ({
className: separatorPosition === 'before' ? _pickersInputBaseClasses.pickersInputBaseClasses.sectionBefore : _pickersInputBaseClasses.pickersInputBaseClasses.sectionAfter
})
}
}), endAdornment, renderSuffix ? renderSuffix((0, _extends2.default)({}, muiFormControl)) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(PickersInputBaseInput, (0, _extends2.default)({
name: name,
className: classes.input,
value: value,
onChange: onChange,
id: id,
"aria-hidden": "true",
tabIndex: -1,
readOnly: readOnly,
required: muiFormControl.required,
disabled: muiFormControl.disabled
// Hidden input element cannot be focused, trigger the root focus instead
// This allows to maintain the ability to do `inputRef.current.focus()` to focus the field
,
onFocus: handleHiddenInputFocus
}, inputProps, {
ref: handleInputRef
})), isSingleInputRange && /*#__PURE__*/(0, _jsxRuntime.jsx)(PickersInputBaseActiveBar, {
className: classes.activeBar,
ref: activeBarRef,
ownerState: {
sectionOffsets: sectionOffsetsRef.current
}
})]
}));
});
process.env.NODE_ENV !== "production" ? PickersInputBase.propTypes = {
// ----------------------------- Warning --------------------------------
// | These PropTypes are generated from the TypeScript type definitions |
// | To update them edit the TypeScript types and run "pnpm proptypes" |
// ----------------------------------------------------------------------
/**
* Is `true` if the current values equals the empty value.
* For a single item value, it means that `value === null`
* For a range value, it means that `value === [null, null]`
*/
areAllSectionsEmpty: _propTypes.default.bool.isRequired,
className: _propTypes.default.string,
component: _propTypes.default.elementType,
/**
* If true, the whole element is editable.
* Useful when all the sections are selected.
*/
contentEditable: _propTypes.default.bool.isRequired,
'data-multi-input': _propTypes.default.string,
/**
* The elements to render.
* Each element contains the prop to edit a section of the value.
*/
elements: _propTypes.default.arrayOf(_propTypes.default.shape({
after: _propTypes.default.object.isRequired,
before: _propTypes.default.object.isRequired,
container: _propTypes.default.object.isRequired,
content: _propTypes.default.object.isRequired
})).isRequired,
endAdornment: _propTypes.default.node,
fullWidth: _propTypes.default.bool,
id: _propTypes.default.string,
inputProps: _propTypes.default.object,
inputRef: _utils.refType,
label: _propTypes.default.node,
margin: _propTypes.default.oneOf(['dense', 'none', 'normal']),
name: _propTypes.default.string,
onChange: _propTypes.default.func.isRequired,
onClick: _propTypes.default.func.isRequired,
onInput: _propTypes.default.func.isRequired,
onKeyDown: _propTypes.default.func.isRequired,
onPaste: _propTypes.default.func.isRequired,
ownerState: _propTypes.default /* @typescript-to-proptypes-ignore */.any,
readOnly: _propTypes.default.bool,
renderSuffix: _propTypes.default.func,
sectionListRef: _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.shape({
current: _propTypes.default.shape({
getRoot: _propTypes.default.func.isRequired,
getSectionContainer: _propTypes.default.func.isRequired,
getSectionContent: _propTypes.default.func.isRequired,
getSectionIndexFromDOMElement: _propTypes.default.func.isRequired
})
})]),
/**
* The props used for each component slot.
* @default {}
*/
slotProps: _propTypes.default.object,
/**
* The components used for each slot inside.
*
* @default {}
*/
slots: _propTypes.default.object,
startAdornment: _propTypes.default.node,
style: _propTypes.default.object,
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx: _propTypes.default.oneOfType([_propTypes.default.arrayOf(_propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.object, _propTypes.default.bool])), _propTypes.default.func, _propTypes.default.object]),
value: _propTypes.default.string.isRequired
} : void 0;