diff --git a/app/components/p8p_dialog.js b/app/components/p8p_dialog.js index 8c2cd8e..f3452bc 100644 --- a/app/components/p8p_dialog.js +++ b/app/components/p8p_dialog.js @@ -26,20 +26,36 @@ const P8P_DIALOG_WIDTH = { XL: "xl" }; +//----------------------- +//Вспомогательные функции +//----------------------- + +//Формирование объекта вида {ключ: значение} из текущего состояния элементов ввода формы +const buildFormValues = inputsState => + inputsState.reduce((res, input) => ({ ...res, [input.name]: input.value == undefined ? null : input.value }), {}); + //----------- //Тело модуля //----------- //Диалог -const P8PDialog = ({ title, width, fullWidth, inputs = [], children, onOk, onCancel, onClose }) => { - //Состояние диалога - const [state, setState] = useState({}); +const P8PDialog = ({ title, width, fullWidth, inputs, children, onOk, onCancel, onClose, onInputChange }) => { + //Состояние элементов ввода диалога + const [inputsState, setInputsState] = useState([]); //При изменении элемента ввода - const handleInputChange = (name, value) => setState(pv => ({ ...pv, [name]: value })); + const handleInputChange = (name, value) => { + //Если есть функция пересчета формы - вызовем её + const doNotChangeInputsState = onInputChange ? onInputChange(name, value, inputsState) : false; + //И ориентируясь на то, пересчитала ли она элементы ввода обновим собственное состояние. + //Если функция пересчета вернула "true", значит она пересчитала что-то, тогда новые настройки элементов придут через свойство inputs и будут обработаны в useEffect ниже. + //Следовательно, и нам здесь не надо состояние выставлять, т.к. всё будет перезаписано useEffectом. + if (!doNotChangeInputsState) + setInputsState(pv => pv.reduce((accum, cur) => [...accum, { ...cur, value: cur.name === name ? value : cur.value }], [])); + }; //При нажатии на "ОК" диалога - const handleOk = () => onOk && onOk(state); + const handleOk = () => onOk && onOk(buildFormValues(inputsState)); //При нажатии на "Отмена" диалога const handleCancel = () => onCancel && onCancel(); @@ -47,20 +63,23 @@ const P8PDialog = ({ title, width, fullWidth, inputs = [], children, onOk, onCan //При нажатии на "Закрыть" диалога const handleClose = () => (onClose ? onClose() : onCancel ? onCancel() : null); - //При подключении к старнице + //При изменении полей для ввода useEffect(() => { - setState(inputs.reduce((res, input) => ({ ...res, [input.name]: input.value == undefined ? null : input.value }), {})); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + if (inputs && Array.isArray(inputs) && inputs.length > 0) setInputsState(inputs.map(input => ({ ...input }))); + }, [inputs]); + + //Расчет объектного представления текущих значений формы + const formValues = buildFormValues(inputsState); //Формирование представления return ( {title} - {inputs.map((input, i) => ( - + {inputsState.map((input, i) => ( + ))} + {children} @@ -81,7 +100,8 @@ P8PDialog.propTypes = { children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)]), onOk: PropTypes.func, onCancel: PropTypes.func, - onClose: PropTypes.func + onClose: PropTypes.func, + onInputChange: PropTypes.func }; //---------------- diff --git a/app/components/p8p_input.js b/app/components/p8p_input.js index 2adc568..5f83bb5 100644 --- a/app/components/p8p_input.js +++ b/app/components/p8p_input.js @@ -35,26 +35,24 @@ const P8P_INPUT = { //Поле ввода const P8PInput = ({ name, value, label, onChange, dictionary, list, type, freeSolo = false, disabled = false, formValues, ...other }) => { - //Значение элемента - const [currentValue, setCurrentValue] = useState(value); + //Значение и тип элемента + const [current, setCurrent] = useState({ type: undefined, value: "" }); - //При получении нового значения из вне - useEffect(() => { - setCurrentValue(value); - }, [value]); + //При получении нового значения или типа из вне + useEffect(() => setCurrent({ value, type }), [type, value]); //Выбор значения из словаря const handleDictionaryClick = () => dictionary && dictionary(formValues, res => (res ? res.map(i => handleChangeByName(i.name, i.value)) : null)); //Изменение значения элемента (по событию) const handleChange = e => { - setCurrentValue(e.target.value); + setCurrent(pv => ({ ...pv, value: e.target.value })); if (onChange) onChange(e.target.name, e.target.value); }; //Изменение значения элемента (по имени и значению) const handleChangeByName = (targetName, value) => { - if (targetName === name) setCurrentValue(value); + if (targetName === name) setCurrent(pv => ({ ...pv, value })); if (onChange) onChange(targetName, value); }; @@ -69,7 +67,7 @@ const P8PInput = ({ name, value, label, onChange, dictionary, list, type, freeSo name={name} freeSolo disabled={disabled} - inputValue={currentValue ? currentValue : ""} + inputValue={current.value ? current.value : ""} onChange={(event, newValue) => handleChangeByName(name, newValue)} onInputChange={(event, newInputValue) => handleChangeByName(name, newInputValue)} options={list} @@ -85,7 +83,7 @@ const P8PInput = ({ name, value, label, onChange, dictionary, list, type, freeSo id={name} name={name} label={label} - value={[undefined, null].includes(currentValue) ? "" : currentValue} + value={[undefined, null].includes(current.value) ? "" : current.value} onChange={handleChange} disabled={disabled} displayEmpty @@ -100,13 +98,13 @@ const P8PInput = ({ name, value, label, onChange, dictionary, list, type, freeSo ) ) : ( <> - + {label} @@ -116,7 +114,7 @@ const P8PInput = ({ name, value, label, onChange, dictionary, list, type, freeSo ) : null } - {...(type ? { type } : {})} + {...(current.type ? { type: current.type } : {})} onChange={handleChange} disabled={disabled} />