UNPKG

@mui/x-date-pickers

Version:

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

267 lines (262 loc) 9.86 kB
import _extends from "@babel/runtime/helpers/esm/extends"; import * as React from 'react'; import useControlled from '@mui/utils/useControlled'; import { useRtl } from '@mui/system/RtlProvider'; import { usePickersTranslations } from "../../../hooks/usePickersTranslations.js"; import { useUtils, useLocalizationContext } from "../useUtils.js"; import { mergeDateIntoReferenceDate, getSectionsBoundaries, validateSections, getDateFromDateSections, parseSelectedSections, getLocalizedDigits } from "./useField.utils.js"; import { buildSectionsFromFormat } from "./buildSectionsFromFormat.js"; import { useValueWithTimezone } from "../useValueWithTimezone.js"; import { getSectionTypeGranularity } from "../../utils/getDefaultReferenceDate.js"; export const useFieldState = params => { const utils = useUtils(); const translations = usePickersTranslations(); const adapter = useLocalizationContext(); const isRtl = useRtl(); const { valueManager, fieldValueManager, valueType, validator, internalProps, internalProps: { value: valueProp, defaultValue, referenceDate: referenceDateProp, onChange, format, formatDensity = 'dense', selectedSections: selectedSectionsProp, onSelectedSectionsChange, shouldRespectLeadingZeros = false, timezone: timezoneProp, enableAccessibleFieldDOMStructure = false } } = params; const { timezone, value: valueFromTheOutside, handleValueChange } = useValueWithTimezone({ timezone: timezoneProp, value: valueProp, defaultValue, referenceDate: referenceDateProp, onChange, valueManager }); const localizedDigits = React.useMemo(() => getLocalizedDigits(utils), [utils]); const sectionsValueBoundaries = React.useMemo(() => getSectionsBoundaries(utils, localizedDigits, timezone), [utils, localizedDigits, timezone]); const getSectionsFromValue = React.useCallback((value, fallbackSections = null) => fieldValueManager.getSectionsFromValue(utils, value, fallbackSections, date => buildSectionsFromFormat({ utils, localeText: translations, localizedDigits, format, date, formatDensity, shouldRespectLeadingZeros, enableAccessibleFieldDOMStructure, isRtl })), [fieldValueManager, format, translations, localizedDigits, isRtl, shouldRespectLeadingZeros, utils, formatDensity, enableAccessibleFieldDOMStructure]); const [state, setState] = React.useState(() => { const sections = getSectionsFromValue(valueFromTheOutside); validateSections(sections, valueType); const stateWithoutReferenceDate = { sections, value: valueFromTheOutside, referenceValue: valueManager.emptyValue, tempValueStrAndroid: null }; const granularity = getSectionTypeGranularity(sections); const referenceValue = valueManager.getInitialReferenceValue({ referenceDate: referenceDateProp, value: valueFromTheOutside, utils, props: internalProps, granularity, timezone }); return _extends({}, stateWithoutReferenceDate, { referenceValue }); }); const [selectedSections, innerSetSelectedSections] = useControlled({ controlled: selectedSectionsProp, default: null, name: 'useField', state: 'selectedSections' }); const setSelectedSections = newSelectedSections => { innerSetSelectedSections(newSelectedSections); onSelectedSectionsChange?.(newSelectedSections); }; const parsedSelectedSections = React.useMemo(() => parseSelectedSections(selectedSections, state.sections), [selectedSections, state.sections]); const activeSectionIndex = parsedSelectedSections === 'all' ? 0 : parsedSelectedSections; const publishValue = ({ value, referenceValue, sections }) => { setState(prevState => _extends({}, prevState, { sections, value, referenceValue, tempValueStrAndroid: null })); if (valueManager.areValuesEqual(utils, state.value, value)) { return; } const context = { validationError: validator({ adapter, value, timezone, props: internalProps }) }; handleValueChange(value, context); }; const setSectionValue = (sectionIndex, newSectionValue) => { const newSections = [...state.sections]; newSections[sectionIndex] = _extends({}, newSections[sectionIndex], { value: newSectionValue, modified: true }); return newSections; }; const clearValue = () => { publishValue({ value: valueManager.emptyValue, referenceValue: state.referenceValue, sections: getSectionsFromValue(valueManager.emptyValue) }); }; const clearActiveSection = () => { if (activeSectionIndex == null) { return; } const activeSection = state.sections[activeSectionIndex]; const activeDateManager = fieldValueManager.getActiveDateManager(utils, state, activeSection); const nonEmptySectionCountBefore = activeDateManager.getSections(state.sections).filter(section => section.value !== '').length; const hasNoOtherNonEmptySections = nonEmptySectionCountBefore === (activeSection.value === '' ? 0 : 1); const newSections = setSectionValue(activeSectionIndex, ''); const newActiveDate = hasNoOtherNonEmptySections ? null : utils.getInvalidDate(); const newValues = activeDateManager.getNewValuesFromNewActiveDate(newActiveDate); publishValue(_extends({}, newValues, { sections: newSections })); }; const updateValueFromValueStr = valueStr => { const parseDateStr = (dateStr, referenceDate) => { const date = utils.parse(dateStr, format); if (date == null || !utils.isValid(date)) { return null; } const sections = buildSectionsFromFormat({ utils, localeText: translations, localizedDigits, format, date, formatDensity, shouldRespectLeadingZeros, enableAccessibleFieldDOMStructure, isRtl }); return mergeDateIntoReferenceDate(utils, date, sections, referenceDate, false); }; const newValue = fieldValueManager.parseValueStr(valueStr, state.referenceValue, parseDateStr); const newReferenceValue = fieldValueManager.updateReferenceValue(utils, newValue, state.referenceValue); publishValue({ value: newValue, referenceValue: newReferenceValue, sections: getSectionsFromValue(newValue, state.sections) }); }; const updateSectionValue = ({ activeSection, newSectionValue, shouldGoToNextSection }) => { /** * 1. Decide which section should be focused */ if (shouldGoToNextSection && activeSectionIndex < state.sections.length - 1) { setSelectedSections(activeSectionIndex + 1); } /** * 2. Try to build a valid date from the new section value */ const activeDateManager = fieldValueManager.getActiveDateManager(utils, state, activeSection); const newSections = setSectionValue(activeSectionIndex, newSectionValue); const newActiveDateSections = activeDateManager.getSections(newSections); const newActiveDate = getDateFromDateSections(utils, newActiveDateSections, localizedDigits); let values; let shouldPublish; /** * If the new date is valid, * Then we merge the value of the modified sections into the reference date. * This makes sure that we don't lose some information of the initial date (like the time on a date field). */ if (newActiveDate != null && utils.isValid(newActiveDate)) { const mergedDate = mergeDateIntoReferenceDate(utils, newActiveDate, newActiveDateSections, activeDateManager.referenceDate, true); values = activeDateManager.getNewValuesFromNewActiveDate(mergedDate); shouldPublish = true; } else { values = activeDateManager.getNewValuesFromNewActiveDate(newActiveDate); shouldPublish = (newActiveDate != null && !utils.isValid(newActiveDate)) !== (activeDateManager.date != null && !utils.isValid(activeDateManager.date)); } /** * Publish or update the internal state with the new value and sections. */ if (shouldPublish) { return publishValue(_extends({}, values, { sections: newSections })); } return setState(prevState => _extends({}, prevState, values, { sections: newSections, tempValueStrAndroid: null })); }; const setTempAndroidValueStr = tempValueStrAndroid => setState(prev => _extends({}, prev, { tempValueStrAndroid })); React.useEffect(() => { const sections = getSectionsFromValue(state.value); validateSections(sections, valueType); setState(prevState => _extends({}, prevState, { sections })); }, [format, utils.locale, isRtl]); // eslint-disable-line react-hooks/exhaustive-deps React.useEffect(() => { let shouldUpdate; if (!valueManager.areValuesEqual(utils, state.value, valueFromTheOutside)) { shouldUpdate = true; } else { shouldUpdate = valueManager.getTimezone(utils, state.value) !== valueManager.getTimezone(utils, valueFromTheOutside); } if (shouldUpdate) { setState(prevState => _extends({}, prevState, { value: valueFromTheOutside, referenceValue: fieldValueManager.updateReferenceValue(utils, valueFromTheOutside, prevState.referenceValue), sections: getSectionsFromValue(valueFromTheOutside) })); } }, [valueFromTheOutside]); // eslint-disable-line react-hooks/exhaustive-deps return { state, activeSectionIndex, parsedSelectedSections, setSelectedSections, clearValue, clearActiveSection, updateSectionValue, updateValueFromValueStr, setTempAndroidValueStr, getSectionsFromValue, sectionsValueBoundaries, localizedDigits, timezone }; };