UNPKG

@mui/x-date-pickers

Version:

The community edition of the Date and Time Picker components (MUI X).

350 lines (343 loc) 12.7 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.usePickerValue = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var React = _interopRequireWildcard(require("react")); var _useEventCallback = _interopRequireDefault(require("@mui/utils/useEventCallback")); var _useOpenState = require("../useOpenState"); var _useUtils = require("../useUtils"); var _useValidation = require("../useValidation"); var _useValueWithTimezone = require("../useValueWithTimezone"); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } /** * Decide if the new value should be published * The published value will be passed to `onChange` if defined. */ const shouldPublishValue = params => { const { action, hasChanged, dateState, isControlled } = params; const isCurrentValueTheDefaultValue = !isControlled && !dateState.hasBeenModifiedSinceMount; // The field is responsible for only calling `onChange` when needed. if (action.name === 'setValueFromField') { return true; } if (action.name === 'setValueFromAction') { // If the component is not controlled, and the value has not been modified since the mount, // Then we want to publish the default value whenever the user pressed the "Accept", "Today" or "Clear" button. if (isCurrentValueTheDefaultValue && ['accept', 'today', 'clear'].includes(action.pickerAction)) { return true; } return hasChanged(dateState.lastPublishedValue); } if (action.name === 'setValueFromView' && action.selectionState !== 'shallow') { // On the first view, // If the value is not controlled, then clicking on any value (including the one equal to `defaultValue`) should call `onChange` if (isCurrentValueTheDefaultValue) { return true; } return hasChanged(dateState.lastPublishedValue); } if (action.name === 'setValueFromShortcut') { // On the first view, // If the value is not controlled, then clicking on any value (including the one equal to `defaultValue`) should call `onChange` if (isCurrentValueTheDefaultValue) { return true; } return hasChanged(dateState.lastPublishedValue); } return false; }; /** * Decide if the new value should be committed. * The committed value will be passed to `onAccept` if defined. * It will also be used as a reset target when calling the `cancel` picker action (when clicking on the "Cancel" button). */ const shouldCommitValue = params => { const { action, hasChanged, dateState, isControlled, closeOnSelect } = params; const isCurrentValueTheDefaultValue = !isControlled && !dateState.hasBeenModifiedSinceMount; if (action.name === 'setValueFromAction') { // If the component is not controlled, and the value has not been modified since the mount, // Then we want to commit the default value whenever the user pressed the "Accept", "Today" or "Clear" button. if (isCurrentValueTheDefaultValue && ['accept', 'today', 'clear'].includes(action.pickerAction)) { return true; } return hasChanged(dateState.lastCommittedValue); } if (action.name === 'setValueFromView' && action.selectionState === 'finish' && closeOnSelect) { // On picker where the 1st view is also the last view, // If the value is not controlled, then clicking on any value (including the one equal to `defaultValue`) should call `onAccept` if (isCurrentValueTheDefaultValue) { return true; } return hasChanged(dateState.lastCommittedValue); } if (action.name === 'setValueFromShortcut') { return action.changeImportance === 'accept' && hasChanged(dateState.lastCommittedValue); } return false; }; /** * Decide if the picker should be closed after the value is updated. */ const shouldClosePicker = params => { const { action, closeOnSelect } = params; if (action.name === 'setValueFromAction') { return true; } if (action.name === 'setValueFromView') { return action.selectionState === 'finish' && closeOnSelect; } if (action.name === 'setValueFromShortcut') { return action.changeImportance === 'accept'; } return false; }; /** * Manage the value lifecycle of all the pickers. */ const usePickerValue = ({ props, valueManager, valueType, wrapperVariant, validator }) => { const { onAccept, onChange, value: inValue, defaultValue: inDefaultValue, closeOnSelect = wrapperVariant === 'desktop', timezone: timezoneProp } = props; const { current: defaultValue } = React.useRef(inDefaultValue); const { current: isControlled } = React.useRef(inValue !== undefined); /* eslint-disable react-hooks/rules-of-hooks, react-hooks/exhaustive-deps */ if (process.env.NODE_ENV !== 'production') { React.useEffect(() => { if (isControlled !== (inValue !== undefined)) { console.error([`MUI X: A component is changing the ${isControlled ? '' : 'un'}controlled value of a picker to be ${isControlled ? 'un' : ''}controlled.`, 'Elements should not switch from uncontrolled to controlled (or vice versa).', `Decide between using a controlled or uncontrolled value` + 'for the lifetime of the component.', "The nature of the state is determined during the first render. It's considered controlled if the value is not `undefined`.", 'More info: https://fb.me/react-controlled-components'].join('\n')); } }, [inValue]); React.useEffect(() => { if (!isControlled && defaultValue !== inDefaultValue) { console.error([`MUI X: A component is changing the defaultValue of an uncontrolled picker after being initialized. ` + `To suppress this warning opt to use a controlled value.`].join('\n')); } }, [JSON.stringify(defaultValue)]); } /* eslint-enable react-hooks/rules-of-hooks, react-hooks/exhaustive-deps */ const utils = (0, _useUtils.useUtils)(); const adapter = (0, _useUtils.useLocalizationContext)(); const { isOpen, setIsOpen } = (0, _useOpenState.useOpenState)(props); const [dateState, setDateState] = React.useState(() => { let initialValue; if (inValue !== undefined) { initialValue = inValue; } else if (defaultValue !== undefined) { initialValue = defaultValue; } else { initialValue = valueManager.emptyValue; } return { draft: initialValue, lastPublishedValue: initialValue, lastCommittedValue: initialValue, lastControlledValue: inValue, hasBeenModifiedSinceMount: false }; }); const { timezone, handleValueChange } = (0, _useValueWithTimezone.useValueWithTimezone)({ timezone: timezoneProp, value: inValue, defaultValue, onChange, valueManager }); (0, _useValidation.useValidation)((0, _extends2.default)({}, props, { value: dateState.draft, timezone }), validator, valueManager.isSameError, valueManager.defaultErrorState); const updateDate = (0, _useEventCallback.default)(action => { const updaterParams = { action, dateState, hasChanged: comparison => !valueManager.areValuesEqual(utils, action.value, comparison), isControlled, closeOnSelect }; const shouldPublish = shouldPublishValue(updaterParams); const shouldCommit = shouldCommitValue(updaterParams); const shouldClose = shouldClosePicker(updaterParams); setDateState(prev => (0, _extends2.default)({}, prev, { draft: action.value, lastPublishedValue: shouldPublish ? action.value : prev.lastPublishedValue, lastCommittedValue: shouldCommit ? action.value : prev.lastCommittedValue, hasBeenModifiedSinceMount: true })); if (shouldPublish) { const validationError = action.name === 'setValueFromField' ? action.context.validationError : validator({ adapter, value: action.value, props: (0, _extends2.default)({}, props, { value: action.value, timezone }) }); const context = { validationError }; if (action.name === 'setValueFromShortcut') { context.shortcut = action.shortcut; } handleValueChange(action.value, context); } if (shouldCommit && onAccept) { onAccept(action.value); } if (shouldClose) { setIsOpen(false); } }); if (inValue !== undefined && (dateState.lastControlledValue === undefined || !valueManager.areValuesEqual(utils, dateState.lastControlledValue, inValue))) { const isUpdateComingFromPicker = valueManager.areValuesEqual(utils, dateState.draft, inValue); setDateState(prev => (0, _extends2.default)({}, prev, { lastControlledValue: inValue }, isUpdateComingFromPicker ? {} : { lastCommittedValue: inValue, lastPublishedValue: inValue, draft: inValue, hasBeenModifiedSinceMount: true })); } const handleClear = (0, _useEventCallback.default)(() => { updateDate({ value: valueManager.emptyValue, name: 'setValueFromAction', pickerAction: 'clear' }); }); const handleAccept = (0, _useEventCallback.default)(() => { updateDate({ value: dateState.lastPublishedValue, name: 'setValueFromAction', pickerAction: 'accept' }); }); const handleDismiss = (0, _useEventCallback.default)(() => { updateDate({ value: dateState.lastPublishedValue, name: 'setValueFromAction', pickerAction: 'dismiss' }); }); const handleCancel = (0, _useEventCallback.default)(() => { updateDate({ value: dateState.lastCommittedValue, name: 'setValueFromAction', pickerAction: 'cancel' }); }); const handleSetToday = (0, _useEventCallback.default)(() => { updateDate({ value: valueManager.getTodayValue(utils, timezone, valueType), name: 'setValueFromAction', pickerAction: 'today' }); }); const handleOpen = (0, _useEventCallback.default)(event => { event.preventDefault(); setIsOpen(true); }); const handleClose = (0, _useEventCallback.default)(event => { event?.preventDefault(); setIsOpen(false); }); const handleChange = (0, _useEventCallback.default)((newValue, selectionState = 'partial') => updateDate({ name: 'setValueFromView', value: newValue, selectionState })); const handleSelectShortcut = (0, _useEventCallback.default)((newValue, changeImportance, shortcut) => updateDate({ name: 'setValueFromShortcut', value: newValue, changeImportance, shortcut })); const handleChangeFromField = (0, _useEventCallback.default)((newValue, context) => updateDate({ name: 'setValueFromField', value: newValue, context })); const actions = { onClear: handleClear, onAccept: handleAccept, onDismiss: handleDismiss, onCancel: handleCancel, onSetToday: handleSetToday, onOpen: handleOpen, onClose: handleClose }; const fieldResponse = { value: dateState.draft, onChange: handleChangeFromField }; const viewValue = React.useMemo(() => valueManager.cleanValue(utils, dateState.draft), [utils, valueManager, dateState.draft]); const viewResponse = { value: viewValue, onChange: handleChange, onClose: handleClose, open: isOpen }; const isValid = testedValue => { const error = validator({ adapter, value: testedValue, props: (0, _extends2.default)({}, props, { value: testedValue, timezone }) }); return !valueManager.hasError(error); }; const layoutResponse = (0, _extends2.default)({}, actions, { value: viewValue, onChange: handleChange, onSelectShortcut: handleSelectShortcut, isValid }); return { open: isOpen, fieldProps: fieldResponse, viewProps: viewResponse, layoutProps: layoutResponse, actions }; }; exports.usePickerValue = usePickerValue;