P8-Panels/app/panels/panels_editor/panel_props_editor.js

231 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Парус 8 - Панели мониторинга - Редактор панелей
Редактор глобальных свойств панели
*/
//---------------------
//Подключение библиотек
//---------------------
import React, { useState, useContext } from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
import { Chip, Stack, TextField, Button, Icon } from "@mui/material"; //Интерфейсные элементы
import { P8PConfigDialog } from "../../components/editors/p8p_config_dialog"; //Диалог настройки
import { isElementNameCorrect } from "../../components/editors/p8p_editors_common"; //Общие ресурсы редакторов
import { P8PEditorSubHeader } from "../../components/editors/p8p_editor_sub_header"; //Заголовок раздела редактора
import { P8PEditorBox } from "../../components/editors/p8p_editor_box"; //Контейнер редактора
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
import { STYLES as COMMON_STYLES } from "../../components/editors/p8p_editors_common"; //Общие ресурсы редакторов
import { P8PChipList } from "../../components/editors/p8p_chip_list"; //Дополнительные настройки редактора
//---------
//Константы
//---------
//Стили
const STYLES = {
STACK_DEPENENCIES: { gap: "5px", ...APP_STYLES.SCROLL, overflow: "auto", maxHeight: "160px", width: "260px" },
CHIP_DEPENDENCY: { ...COMMON_STYLES.CHIP(true, false), minHeight: "32px" }
};
//Начальное состояние переменной
const VARIABLE_INITIAL = {
dependencies: [],
description: "",
value: ""
};
//Структура переменной
export const VARIABLE_SHAPE = PropTypes.shape({
dependencies: PropTypes.arrayOf(PropTypes.string),
description: PropTypes.string.isRequired,
value: PropTypes.string
});
//------------------------------------
//Вспомогательные функции и компоненты
//------------------------------------
//Редактор переменной
const VariableEditor = ({ variableName = "", variable = null, dependencies = [], onDependencyClick, onOk, onCancel } = {}) => {
//Собственное состояние - имя переменной
const [name, setName] = useState(variableName || "");
//Собственное состояние - параметры переменной
const [state, setState] = useState({ ...VARIABLE_INITIAL, ...variable });
//При закрытии редактора с сохранением
const handleOk = () => onOk(name, { ...state });
//При закрытии редактора с отменой
const handleCancel = () => onCancel();
//При изменении параметра переменной
const handleChange = e => {
//Если это поле наименования, то проверяем корректность
if (e.target.id === "name" && isElementNameCorrect(e.target.value)) setName(e.target.value);
//Устанавливаем значение
else setState(pv => ({ ...pv, [e.target.id]: e.target.value }));
};
//Доступность сохранения настроек элемента
const okDisabled = !name || !state.description ? true : false;
//Формирование представления
return (
<P8PConfigDialog title={`${variable ? "Изменение" : "Добавление"} элемента`} onOk={handleOk} onCancel={handleCancel} okDisabled={okDisabled}>
<Stack direction={"column"} spacing={1}>
<TextField type={"text"} variant={"standard"} value={name} label={"Имя"} id={"name"} onChange={handleChange} required={true} />
<TextField
type={"text"}
variant={"standard"}
value={state.description}
label={"Описание"}
id={"description"}
onChange={handleChange}
required={true}
/>
</Stack>
{dependencies.length !== 0 ? (
<>
<P8PEditorSubHeader title={"Зависимости"} />
<Stack sx={STYLES.STACK_DEPENENCIES} direction={"column"} p={1} useFlexGap={true}>
{dependencies.map((item, i) => (
<Chip
key={i}
label={item}
variant={"outlined"}
onClick={() => onDependencyClick && onDependencyClick(item)}
sx={STYLES.CHIP_DEPENDENCY}
/>
))}
</Stack>
</>
) : null}
</P8PConfigDialog>
);
};
//Контроль свойств - редактор переменной
VariableEditor.propTypes = {
variableName: PropTypes.string,
variable: VARIABLE_SHAPE,
dependencies: PropTypes.array,
onDependencyClick: PropTypes.func.isRequired,
onOk: PropTypes.func.isRequired,
onCancel: PropTypes.func.isRequired
};
//-----------
//Тело модуля
//-----------
//Редактор глобальных свойств панели
const PanelPropsEditor = ({ valueProviders = {}, onSettingsChange, onDependencyClick } = {}) => {
//Собственное состояние - редактор переменных панели
const [variableEditor, setVariableEditor] = useState({ display: false, item: null });
//Подключение к контексту сообщений
const { showMsgErr } = useContext(MessagingСtx);
//При добавлении новой переменной
const handleVariableAdd = () => setVariableEditor({ display: true, item: null });
//При нажатии на переменную
const handleVariableClick = index => setVariableEditor({ display: true, item: variables[index] });
//При отмене сохранения изменений переменной
const handleVariableCancel = () => setVariableEditor({ display: false, index: null });
//При сохранении изменений переменной
const handleVariableSave = (variableName, variable) => {
//Текст ошибки
let msgError = "";
//Если это добавление или изменилось наименование - проверяем на дублирование
if ((!variableEditor.item || variableEditor.item !== variableName) && Object.prototype.hasOwnProperty.call(valueProviders, variableName)) {
msgError = `Дублирование наименования параметра "${variableName}".`;
}
//Если это исправление наименования параметра, но он используется
if (variableEditor.item && variableEditor.item !== variableName && valueProviders[variableEditor.item].dependencies.length !== 0) {
msgError = `Переменная имеет связи с компонентами. Изменение наименования запрещено.`;
}
//Если есть ошибка - выводим
if (msgError) {
showMsgErr(msgError);
} else {
//Копируем параметры
let newValueProviders = { ...valueProviders };
//Удаляем старое значение, если требуется
variableEditor.item && variableEditor.item !== variableName ? delete newValueProviders[variableEditor.item] : null;
//Добавляем новый параметр
newValueProviders = { ...newValueProviders, [variableName]: { ...variable } };
//Обновляем проводники панели
onSettingsChange({ valueProviders: { ...newValueProviders } });
//Закрываем редактирование
setVariableEditor({ display: false, index: null });
}
};
//При удалении переменной
const handleVariableDelete = index => {
const variable = variables[index];
//Если переменная не используется в компонентах - удаляем
if (valueProviders[variable].dependencies.length === 0) {
const newValueProviders = { ...valueProviders };
delete newValueProviders[variable];
onSettingsChange({ valueProviders: { ...newValueProviders } });
} else {
showMsgErr(`Переменная имеет связи с компонентами. Удаление запрещено.`);
}
};
//При нажатии на зависимый компонент
const handleDependencyClick = id => {
//Закрываем редактирование
setVariableEditor({ display: false, index: null });
//Открываем настройку компонента
onDependencyClick(id);
};
//Текущие переменные панели
const variables = Object.keys(valueProviders).reduce((res, key) => [...res, key], []);
//Определяем структуру переменных для отображения
const variableChips = variables.map(item => ({ text: item, title: item }));
//Формирование представления
return (
<P8PEditorBox title={"Параметры панели"}>
{variableEditor.display && (
<VariableEditor
variableName={variableEditor.item}
variable={variableEditor.item !== null ? { ...valueProviders[variableEditor.item] } : null}
dependencies={variableEditor.item ? valueProviders[variableEditor.item].dependencies : []}
onDependencyClick={handleDependencyClick}
onCancel={handleVariableCancel}
onOk={handleVariableSave}
/>
)}
<P8PEditorSubHeader title={"Переменные"} />
<P8PChipList items={variableChips} onClick={handleVariableClick} onDelete={handleVariableDelete} />
<Button startIcon={<Icon>add</Icon>} onClick={handleVariableAdd}>
Добавить переменную
</Button>
</P8PEditorBox>
);
};
//Контроль свойств компонента - редактор глобальных свойств панели
PanelPropsEditor.propTypes = {
valueProviders: PropTypes.object,
onSettingsChange: PropTypes.func.isRequired,
onDependencyClick: PropTypes.func.isRequired
};
//----------------
//Интерфейс модуля
//----------------
export { PanelPropsEditor };