import _extends from "@babel/runtime/helpers/esm/extends"; import * as React from 'react'; import useControlled from '@mui/utils/useControlled'; import { useTheme } from '@mui/material/styles'; import { useUtils, useLocaleText, useLocalizationContext } from '../useUtils'; import { addPositionPropertiesToSections, splitFormatIntoSections, mergeDateIntoReferenceDate, getSectionsBoundaries, validateSections, getDateFromDateSections } from './useField.utils'; import { useValueWithTimezone } from '../useValueWithTimezone'; import { getSectionTypeGranularity } from '../../utils/getDefaultReferenceDate'; export const useFieldState = params => { const utils = useUtils(); const localeText = useLocaleText(); const adapter = useLocalizationContext(); const theme = useTheme(); const isRTL = theme.direction === 'rtl'; const { valueManager, fieldValueManager, valueType, validator, internalProps, internalProps: { value: valueProp, defaultValue, referenceDate: referenceDateProp, onChange, format, formatDensity = 'dense', selectedSections: selectedSectionsProp, onSelectedSectionsChange, shouldRespectLeadingZeros = false, timezone: timezoneProp } } = params; const { timezone, value: valueFromTheOutside, handleValueChange } = useValueWithTimezone({ timezone: timezoneProp, value: valueProp, defaultValue, onChange, valueManager }); const sectionsValueBoundaries = React.useMemo(() => getSectionsBoundaries(utils, timezone), [utils, timezone]); const getSectionsFromValue = React.useCallback((value, fallbackSections = null) => fieldValueManager.getSectionsFromValue(utils, value, fallbackSections, isRTL, date => splitFormatIntoSections(utils, timezone, localeText, format, date, formatDensity, shouldRespectLeadingZeros, isRTL)), [fieldValueManager, format, localeText, isRTL, shouldRespectLeadingZeros, utils, formatDensity, timezone]); const placeholder = React.useMemo(() => fieldValueManager.getValueStrFromSections(getSectionsFromValue(valueManager.emptyValue), isRTL), [fieldValueManager, getSectionsFromValue, valueManager.emptyValue, isRTL]); 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: 'selectedSectionIndexes' }); const setSelectedSections = newSelectedSections => { innerSetSelectedSections(newSelectedSections); onSelectedSectionsChange == null || onSelectedSectionsChange(newSelectedSections); setState(prevState => _extends({}, prevState, { selectedSectionQuery: null })); }; const selectedSectionIndexes = React.useMemo(() => { if (selectedSections == null) { return null; } if (selectedSections === 'all') { return { startIndex: 0, endIndex: state.sections.length - 1, shouldSelectBoundarySelectors: true }; } if (typeof selectedSections === 'number') { return { startIndex: selectedSections, endIndex: selectedSections }; } if (typeof selectedSections === 'string') { const selectedSectionIndex = state.sections.findIndex(section => section.type === selectedSections); return { startIndex: selectedSectionIndex, endIndex: selectedSectionIndex }; } return selectedSections; }, [selectedSections, state.sections]); 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, props: _extends({}, internalProps, { value, timezone }) }) }; handleValueChange(value, context); }; const setSectionValue = (sectionIndex, newSectionValue) => { const newSections = [...state.sections]; newSections[sectionIndex] = _extends({}, newSections[sectionIndex], { value: newSectionValue, modified: true }); return addPositionPropertiesToSections(newSections, isRTL); }; const clearValue = () => { publishValue({ value: valueManager.emptyValue, referenceValue: state.referenceValue, sections: getSectionsFromValue(valueManager.emptyValue) }); }; const clearActiveSection = () => { if (selectedSectionIndexes == null) { return; } const activeSection = state.sections[selectedSectionIndexes.startIndex]; 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(selectedSectionIndexes.startIndex, ''); const newActiveDate = hasNoOtherNonEmptySections ? null : utils.date(new Date('')); const newValues = activeDateManager.getNewValuesFromNewActiveDate(newActiveDate); if ((newActiveDate != null && !utils.isValid(newActiveDate)) !== (activeDateManager.date != null && !utils.isValid(activeDateManager.date))) { publishValue(_extends({}, newValues, { sections: newSections })); } else { setState(prevState => _extends({}, prevState, newValues, { sections: newSections, tempValueStrAndroid: null })); } }; const updateValueFromValueStr = valueStr => { const parseDateStr = (dateStr, referenceDate) => { const date = utils.parse(dateStr, format); if (date == null || !utils.isValid(date)) { return null; } const sections = splitFormatIntoSections(utils, timezone, localeText, format, date, formatDensity, shouldRespectLeadingZeros, isRTL); return mergeDateIntoReferenceDate(utils, timezone, 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 && selectedSectionIndexes && selectedSectionIndexes.startIndex < state.sections.length - 1) { setSelectedSections(selectedSectionIndexes.startIndex + 1); } else if (selectedSectionIndexes && selectedSectionIndexes.startIndex !== selectedSectionIndexes.endIndex) { setSelectedSections(selectedSectionIndexes.startIndex); } /** * 2. Try to build a valid date from the new section value */ const activeDateManager = fieldValueManager.getActiveDateManager(utils, state, activeSection); const newSections = setSectionValue(selectedSectionIndexes.startIndex, newSectionValue); const newActiveDateSections = activeDateManager.getSections(newSections); const newActiveDate = getDateFromDateSections(utils, newActiveDateSections); 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, timezone, 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]); // eslint-disable-line react-hooks/exhaustive-deps React.useEffect(() => { let shouldUpdate = false; 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, selectedSectionIndexes, setSelectedSections, clearValue, clearActiveSection, updateSectionValue, updateValueFromValueStr, setTempAndroidValueStr, sectionsValueBoundaries, placeholder, timezone }; };