WEBAPP: Поддержка пересчёта диалогов для P8PDialog и P8PInput

This commit is contained in:
Mikhail Chechnev 2025-09-25 14:06:44 +03:00
parent c66216e47b
commit dae416cd83
2 changed files with 43 additions and 25 deletions

View File

@ -26,20 +26,36 @@ const P8P_DIALOG_WIDTH = {
XL: "xl" 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 P8PDialog = ({ title, width, fullWidth, inputs, children, onOk, onCancel, onClose, onInputChange }) => {
//Состояние диалога //Состояние элементов ввода диалога
const [state, setState] = useState({}); 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(); const handleCancel = () => onCancel && onCancel();
@ -47,20 +63,23 @@ const P8PDialog = ({ title, width, fullWidth, inputs = [], children, onOk, onCan
//При нажатии на "Закрыть" диалога //При нажатии на "Закрыть" диалога
const handleClose = () => (onClose ? onClose() : onCancel ? onCancel() : null); const handleClose = () => (onClose ? onClose() : onCancel ? onCancel() : null);
//При подключении к старнице //При изменении полей для ввода
useEffect(() => { useEffect(() => {
setState(inputs.reduce((res, input) => ({ ...res, [input.name]: input.value == undefined ? null : input.value }), {})); if (inputs && Array.isArray(inputs) && inputs.length > 0) setInputsState(inputs.map(input => ({ ...input })));
// eslint-disable-next-line react-hooks/exhaustive-deps }, [inputs]);
}, []);
//Расчет объектного представления текущих значений формы
const formValues = buildFormValues(inputsState);
//Формирование представления //Формирование представления
return ( return (
<Dialog onClose={handleClose} open {...{ ...(width ? { maxWidth: width } : {}), ...(fullWidth === true ? { fullWidth: true } : {}) }}> <Dialog onClose={handleClose} open {...{ ...(width ? { maxWidth: width } : {}), ...(fullWidth === true ? { fullWidth: true } : {}) }}>
<DialogTitle>{title}</DialogTitle> <DialogTitle>{title}</DialogTitle>
<DialogContent> <DialogContent>
{inputs.map((input, i) => ( {inputsState.map((input, i) => (
<P8PInput key={i} {...input} value={state[input.name]} formValues={state} onChange={handleInputChange} /> <P8PInput key={i} {...input} formValues={formValues} onChange={handleInputChange} />
))} ))}
{children} {children}
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
@ -81,7 +100,8 @@ P8PDialog.propTypes = {
children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)]), children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)]),
onOk: PropTypes.func, onOk: PropTypes.func,
onCancel: PropTypes.func, onCancel: PropTypes.func,
onClose: PropTypes.func onClose: PropTypes.func,
onInputChange: PropTypes.func
}; };
//---------------- //----------------

View File

@ -35,26 +35,24 @@ const P8P_INPUT = {
//Поле ввода //Поле ввода
const P8PInput = ({ name, value, label, onChange, dictionary, list, type, freeSolo = false, disabled = false, formValues, ...other }) => { 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(() => { useEffect(() => setCurrent({ value, type }), [type, value]);
setCurrentValue(value);
}, [value]);
//Выбор значения из словаря //Выбор значения из словаря
const handleDictionaryClick = () => dictionary && dictionary(formValues, res => (res ? res.map(i => handleChangeByName(i.name, i.value)) : null)); const handleDictionaryClick = () => dictionary && dictionary(formValues, res => (res ? res.map(i => handleChangeByName(i.name, i.value)) : null));
//Изменение значения элемента (по событию) //Изменение значения элемента (по событию)
const handleChange = e => { const handleChange = e => {
setCurrentValue(e.target.value); setCurrent(pv => ({ ...pv, value: e.target.value }));
if (onChange) onChange(e.target.name, e.target.value); if (onChange) onChange(e.target.name, e.target.value);
}; };
//Изменение значения элемента (по имени и значению) //Изменение значения элемента (по имени и значению)
const handleChangeByName = (targetName, value) => { const handleChangeByName = (targetName, value) => {
if (targetName === name) setCurrentValue(value); if (targetName === name) setCurrent(pv => ({ ...pv, value }));
if (onChange) onChange(targetName, value); if (onChange) onChange(targetName, value);
}; };
@ -69,7 +67,7 @@ const P8PInput = ({ name, value, label, onChange, dictionary, list, type, freeSo
name={name} name={name}
freeSolo freeSolo
disabled={disabled} disabled={disabled}
inputValue={currentValue ? currentValue : ""} inputValue={current.value ? current.value : ""}
onChange={(event, newValue) => handleChangeByName(name, newValue)} onChange={(event, newValue) => handleChangeByName(name, newValue)}
onInputChange={(event, newInputValue) => handleChangeByName(name, newInputValue)} onInputChange={(event, newInputValue) => handleChangeByName(name, newInputValue)}
options={list} options={list}
@ -85,7 +83,7 @@ const P8PInput = ({ name, value, label, onChange, dictionary, list, type, freeSo
id={name} id={name}
name={name} name={name}
label={label} label={label}
value={[undefined, null].includes(currentValue) ? "" : currentValue} value={[undefined, null].includes(current.value) ? "" : current.value}
onChange={handleChange} onChange={handleChange}
disabled={disabled} disabled={disabled}
displayEmpty displayEmpty
@ -100,13 +98,13 @@ const P8PInput = ({ name, value, label, onChange, dictionary, list, type, freeSo
) )
) : ( ) : (
<> <>
<InputLabel {...(type == "date" ? { shrink: true } : {})} htmlFor={name}> <InputLabel {...(current.type == "date" ? { shrink: true } : {})} htmlFor={name}>
{label} {label}
</InputLabel> </InputLabel>
<Input <Input
id={name} id={name}
name={name} name={name}
value={currentValue ? currentValue : ""} value={current.value ? current.value : ""}
endAdornment={ endAdornment={
dictionary ? ( dictionary ? (
<InputAdornment position="end"> <InputAdornment position="end">
@ -116,7 +114,7 @@ const P8PInput = ({ name, value, label, onChange, dictionary, list, type, freeSo
</InputAdornment> </InputAdornment>
) : null ) : null
} }
{...(type ? { type } : {})} {...(current.type ? { type: current.type } : {})}
onChange={handleChange} onChange={handleChange}
disabled={disabled} disabled={disabled}
/> />