{
+export interface DlgChangeInputSchemaProps {
oss: IOperationSchema;
target: IOperation;
- onSubmit: (newSchema: LibraryItemID | undefined) => void;
+ onSubmit: (target: OperationID, newSchema: LibraryItemID | undefined) => void;
}
-function DlgChangeInputSchema({ oss, hideWindow, target, onSubmit }: DlgChangeInputSchemaProps) {
+function DlgChangeInputSchema() {
+ const { oss, target, onSubmit } = useDialogsStore(state => state.props as DlgChangeInputSchemaProps);
const [selected, setSelected] = useState
(target.result ?? undefined);
const library = useLibrary();
const sortedItems = sortItemsForOSS(oss, library.items);
@@ -38,9 +40,8 @@ function DlgChangeInputSchema({ oss, hideWindow, target, onSubmit }: DlgChangeIn
overflowVisible
header='Выбор концептуальной схемы'
submitText='Подтвердить выбор'
- hideWindow={hideWindow}
canSubmit={isValid}
- onSubmit={() => onSubmit(selected)}
+ onSubmit={() => onSubmit(target.id, selected)}
className={clsx('w-[35rem]', 'pb-3 px-6 cc-column')}
>
diff --git a/rsconcept/frontend/src/dialogs/DlgChangeLocation.tsx b/rsconcept/frontend/src/dialogs/DlgChangeLocation.tsx
index a887d0de..5f8c20f4 100644
--- a/rsconcept/frontend/src/dialogs/DlgChangeLocation.tsx
+++ b/rsconcept/frontend/src/dialogs/DlgChangeLocation.tsx
@@ -6,20 +6,22 @@ import { useState } from 'react';
import SelectLocationContext from '@/components/select/SelectLocationContext';
import SelectLocationHead from '@/components/select/SelectLocationHead';
import Label from '@/components/ui/Label';
-import Modal, { ModalProps } from '@/components/ui/Modal';
+import Modal from '@/components/ui/Modal';
import TextArea from '@/components/ui/TextArea';
import { useAuth } from '@/context/AuthContext';
import { useLibrary } from '@/context/LibraryContext';
import { LocationHead } from '@/models/library';
import { combineLocation, validateLocation } from '@/models/libraryAPI';
+import { useDialogsStore } from '@/stores/dialogs';
import { limits } from '@/utils/constants';
-interface DlgChangeLocationProps extends Pick
{
+export interface DlgChangeLocationProps {
initial: string;
onChangeLocation: (newLocation: string) => void;
}
-function DlgChangeLocation({ hideWindow, initial, onChangeLocation }: DlgChangeLocationProps) {
+function DlgChangeLocation() {
+ const { initial, onChangeLocation } = useDialogsStore(state => state.props as DlgChangeLocationProps);
const { user } = useAuth();
const [head, setHead] = useState(initial.substring(0, 2) as LocationHead);
const [body, setBody] = useState(initial.substring(3));
@@ -40,7 +42,6 @@ function DlgChangeLocation({ hideWindow, initial, onChangeLocation }: DlgChangeL
header='Изменение расположения'
submitText='Переместить'
submitInvalidTooltip={`Допустимы буквы, цифры, подчерк, пробел и "!". Сегмент пути не может начинаться и заканчиваться пробелом. Общая длина (с корнем) не должна превышать ${limits.location_len}`}
- hideWindow={hideWindow}
canSubmit={isValid}
onSubmit={() => onChangeLocation(location)}
className={clsx('w-[35rem]', 'pb-3 px-6 flex gap-3 h-[9rem]')}
diff --git a/rsconcept/frontend/src/dialogs/DlgCloneLibraryItem.tsx b/rsconcept/frontend/src/dialogs/DlgCloneLibraryItem.tsx
index 8465b8ac..6690c6dd 100644
--- a/rsconcept/frontend/src/dialogs/DlgCloneLibraryItem.tsx
+++ b/rsconcept/frontend/src/dialogs/DlgCloneLibraryItem.tsx
@@ -12,7 +12,7 @@ import SelectLocationHead from '@/components/select/SelectLocationHead';
import Checkbox from '@/components/ui/Checkbox';
import Label from '@/components/ui/Label';
import MiniButton from '@/components/ui/MiniButton';
-import Modal, { ModalProps } from '@/components/ui/Modal';
+import Modal from '@/components/ui/Modal';
import TextArea from '@/components/ui/TextArea';
import TextInput from '@/components/ui/TextInput';
import { useAuth } from '@/context/AuthContext';
@@ -21,18 +21,23 @@ import { useConceptNavigation } from '@/context/NavigationContext';
import { AccessPolicy, ILibraryItem, LocationHead } from '@/models/library';
import { cloneTitle, combineLocation, validateLocation } from '@/models/libraryAPI';
import { ConstituentaID, IRSFormCloneData } from '@/models/rsform';
+import { useDialogsStore } from '@/stores/dialogs';
import { information } from '@/utils/labels';
-interface DlgCloneLibraryItemProps extends Pick {
+export interface DlgCloneLibraryItemProps {
base: ILibraryItem;
initialLocation: string;
selected: ConstituentaID[];
totalCount: number;
}
-function DlgCloneLibraryItem({ hideWindow, base, initialLocation, selected, totalCount }: DlgCloneLibraryItemProps) {
+function DlgCloneLibraryItem() {
+ const { base, initialLocation, selected, totalCount } = useDialogsStore(
+ state => state.props as DlgCloneLibraryItemProps
+ );
const router = useConceptNavigation();
const { user } = useAuth();
+
const [title, setTitle] = useState(cloneTitle(base));
const [alias, setAlias] = useState(base.alias);
const [comment, setComment] = useState(base.comment);
@@ -77,7 +82,6 @@ function DlgCloneLibraryItem({ hideWindow, base, initialLocation, selected, tota
return (
{
+export interface DlgCreateCstProps {
initial?: ICstCreateData;
schema: IRSForm;
onCreate: (data: ICstCreateData) => void;
}
-function DlgCreateCst({ hideWindow, initial, schema, onCreate }: DlgCreateCstProps) {
+function DlgCreateCst() {
+ const { initial, schema, onCreate } = useDialogsStore(state => state.props as DlgCreateCstProps);
const [validated, setValidated] = useState(false);
const [cstData, updateCstData] = usePartialUpdate(
initial || {
@@ -35,7 +37,6 @@ function DlgCreateCst({ hideWindow, initial, schema, onCreate }: DlgCreateCstPro
return (
void;
+export interface DlgCreateOperationProps {
oss: IOperationSchema;
onCreate: (data: IOperationCreateData) => void;
initialInputs: OperationID[];
@@ -27,7 +27,8 @@ export enum TabID {
SYNTHESIS = 1
}
-function DlgCreateOperation({ hideWindow, oss, onCreate, initialInputs }: DlgCreateOperationProps) {
+function DlgCreateOperation() {
+ const { oss, onCreate, initialInputs } = useDialogsStore(state => state.props as DlgCreateOperationProps);
const library = useLibrary();
const [activeTab, setActiveTab] = useState(initialInputs.length > 0 ? TabID.SYNTHESIS : TabID.INPUT);
@@ -98,7 +99,6 @@ function DlgCreateOperation({ hideWindow, oss, onCreate, initialInputs }: DlgCre
{
+export interface DlgCreateVersionProps {
versions: IVersionInfo[];
onCreate: (data: IVersionCreateData) => void;
selected: ConstituentaID[];
totalCount: number;
}
-function DlgCreateVersion({ hideWindow, versions, selected, totalCount, onCreate }: DlgCreateVersionProps) {
+function DlgCreateVersion() {
+ const { versions, selected, totalCount, onCreate } = useDialogsStore(state => state.props as DlgCreateVersionProps);
const [version, setVersion] = useState(versions.length > 0 ? nextVersion(versions[0].version) : '1.0.0');
const [description, setDescription] = useState('');
const [onlySelected, setOnlySelected] = useState(false);
@@ -39,7 +41,6 @@ function DlgCreateVersion({ hideWindow, versions, selected, totalCount, onCreate
return (
{
+export interface DlgCstTemplateProps {
schema: IRSForm;
onCreate: (data: ICstCreateData) => void;
insertAfter?: number;
@@ -30,7 +31,8 @@ export enum TabID {
CONSTITUENTA = 2
}
-function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }: DlgConstituentaTemplateProps) {
+function DlgCstTemplate() {
+ const { schema, onCreate, insertAfter } = useDialogsStore(state => state.props as DlgCstTemplateProps);
const { retrieveTemplate } = useLibrary();
const [activeTab, setActiveTab] = useState(TabID.TEMPLATE);
@@ -128,7 +130,6 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
header='Создание конституенты из шаблона'
submitText='Создать'
className='w-[43rem] h-[35rem] px-6'
- hideWindow={hideWindow}
canSubmit={validated}
beforeSubmit={handlePrompt}
onSubmit={handleSubmit}
@@ -164,4 +165,4 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
);
}
-export default DlgConstituentaTemplate;
+export default DlgCstTemplate;
diff --git a/rsconcept/frontend/src/dialogs/DlgConstituentaTemplate/TabArguments.tsx b/rsconcept/frontend/src/dialogs/DlgCstTemplate/TabArguments.tsx
similarity index 100%
rename from rsconcept/frontend/src/dialogs/DlgConstituentaTemplate/TabArguments.tsx
rename to rsconcept/frontend/src/dialogs/DlgCstTemplate/TabArguments.tsx
diff --git a/rsconcept/frontend/src/dialogs/DlgConstituentaTemplate/TabTemplate.tsx b/rsconcept/frontend/src/dialogs/DlgCstTemplate/TabTemplate.tsx
similarity index 100%
rename from rsconcept/frontend/src/dialogs/DlgConstituentaTemplate/TabTemplate.tsx
rename to rsconcept/frontend/src/dialogs/DlgCstTemplate/TabTemplate.tsx
diff --git a/rsconcept/frontend/src/dialogs/DlgCstTemplate/index.tsx b/rsconcept/frontend/src/dialogs/DlgCstTemplate/index.tsx
new file mode 100644
index 00000000..0d3ba18c
--- /dev/null
+++ b/rsconcept/frontend/src/dialogs/DlgCstTemplate/index.tsx
@@ -0,0 +1 @@
+export { default } from './DlgCstTemplate';
diff --git a/rsconcept/frontend/src/dialogs/DlgDeleteCst/DlgDeleteCst.tsx b/rsconcept/frontend/src/dialogs/DlgDeleteCst/DlgDeleteCst.tsx
index 4dd8e6b4..7447120c 100644
--- a/rsconcept/frontend/src/dialogs/DlgDeleteCst/DlgDeleteCst.tsx
+++ b/rsconcept/frontend/src/dialogs/DlgDeleteCst/DlgDeleteCst.tsx
@@ -4,19 +4,22 @@ import clsx from 'clsx';
import { useState } from 'react';
import Checkbox from '@/components/ui/Checkbox';
-import Modal, { ModalProps } from '@/components/ui/Modal';
+import Modal from '@/components/ui/Modal';
import { ConstituentaID, IRSForm } from '@/models/rsform';
+import { useDialogsStore } from '@/stores/dialogs';
import { prefixes } from '@/utils/constants';
import ListConstituents from './ListConstituents';
-interface DlgDeleteCstProps extends Pick {
+export interface DlgDeleteCstProps {
schema: IRSForm;
selected: ConstituentaID[];
onDelete: (items: ConstituentaID[]) => void;
}
-function DlgDeleteCst({ hideWindow, selected, schema, onDelete }: DlgDeleteCstProps) {
+function DlgDeleteCst() {
+ const { selected, schema, onDelete } = useDialogsStore(state => state.props as DlgDeleteCstProps);
+ const hideDialog = useDialogsStore(state => state.hideDialog);
const [expandOut, setExpandOut] = useState(false);
const expansion: ConstituentaID[] = schema.graph.expandAllOutputs(selected);
const hasInherited = selected.some(
@@ -25,7 +28,7 @@ function DlgDeleteCst({ hideWindow, selected, schema, onDelete }: DlgDeleteCstPr
);
function handleSubmit() {
- hideWindow();
+ hideDialog();
if (expandOut) {
onDelete(selected.concat(expansion));
} else {
@@ -38,7 +41,6 @@ function DlgDeleteCst({ hideWindow, selected, schema, onDelete }: DlgDeleteCstPr
canSubmit
header='Удаление конституент'
submitText={expandOut ? 'Удалить с зависимыми' : 'Удалить'}
- hideWindow={hideWindow}
onSubmit={handleSubmit}
className={clsx('cc-column', 'max-w-[60vw] min-w-[30rem]', 'px-6')}
>
diff --git a/rsconcept/frontend/src/dialogs/DlgDeleteOperation.tsx b/rsconcept/frontend/src/dialogs/DlgDeleteOperation.tsx
index a11d37e4..fa48a1c4 100644
--- a/rsconcept/frontend/src/dialogs/DlgDeleteOperation.tsx
+++ b/rsconcept/frontend/src/dialogs/DlgDeleteOperation.tsx
@@ -4,22 +4,24 @@ import clsx from 'clsx';
import { useState } from 'react';
import Checkbox from '@/components/ui/Checkbox';
-import Modal, { ModalProps } from '@/components/ui/Modal';
+import Modal from '@/components/ui/Modal';
import TextInput from '@/components/ui/TextInput';
import { HelpTopic } from '@/models/miscellaneous';
-import { IOperation } from '@/models/oss';
+import { IOperation, OperationID } from '@/models/oss';
+import { useDialogsStore } from '@/stores/dialogs';
-interface DlgDeleteOperationProps extends Pick {
+export interface DlgDeleteOperationProps {
target: IOperation;
- onSubmit: (keepConstituents: boolean, deleteSchema: boolean) => void;
+ onSubmit: (targetID: OperationID, keepConstituents: boolean, deleteSchema: boolean) => void;
}
-function DlgDeleteOperation({ hideWindow, target, onSubmit }: DlgDeleteOperationProps) {
+function DlgDeleteOperation() {
+ const { target, onSubmit } = useDialogsStore(state => state.props as DlgDeleteOperationProps);
const [keepConstituents, setKeepConstituents] = useState(false);
const [deleteSchema, setDeleteSchema] = useState(false);
function handleSubmit() {
- onSubmit(keepConstituents, deleteSchema);
+ onSubmit(target.id, keepConstituents, deleteSchema);
}
return (
@@ -27,7 +29,6 @@ function DlgDeleteOperation({ hideWindow, target, onSubmit }: DlgDeleteOperation
overflowVisible
header='Удаление операции'
submitText='Подтвердить удаление'
- hideWindow={hideWindow}
canSubmit={true}
onSubmit={handleSubmit}
className={clsx('w-[35rem]', 'pb-3 px-6 cc-column', 'select-none')}
diff --git a/rsconcept/frontend/src/dialogs/DlgEditEditors/DlgEditEditors.tsx b/rsconcept/frontend/src/dialogs/DlgEditEditors/DlgEditEditors.tsx
index 1c74cc6c..02c1fd47 100644
--- a/rsconcept/frontend/src/dialogs/DlgEditEditors/DlgEditEditors.tsx
+++ b/rsconcept/frontend/src/dialogs/DlgEditEditors/DlgEditEditors.tsx
@@ -10,16 +10,17 @@ import MiniButton from '@/components/ui/MiniButton';
import Modal from '@/components/ui/Modal';
import { useUsers } from '@/context/UsersContext';
import { UserID } from '@/models/user';
+import { useDialogsStore } from '@/stores/dialogs';
import TableUsers from './TableUsers';
-interface DlgEditEditorsProps {
+export interface DlgEditEditorsProps {
editors: UserID[];
setEditors: (newValue: UserID[]) => void;
- hideWindow: () => void;
}
-function DlgEditEditors({ hideWindow, editors, setEditors }: DlgEditEditorsProps) {
+function DlgEditEditors() {
+ const { editors, setEditors } = useDialogsStore(state => state.props as DlgEditEditorsProps);
const [selected, setSelected] = useState(editors);
const { users } = useUsers();
const filtered = users.filter(user => !selected.includes(user.id));
@@ -41,7 +42,6 @@ function DlgEditEditors({ hideWindow, editors, setEditors }: DlgEditEditorsProps
canSubmit
header='Список редакторов'
submitText='Сохранить список'
- hideWindow={hideWindow}
className='flex flex-col w-[35rem] px-6 gap-3 pb-6'
onSubmit={handleSubmit}
>
diff --git a/rsconcept/frontend/src/dialogs/DlgEditOperation/DlgEditOperation.tsx b/rsconcept/frontend/src/dialogs/DlgEditOperation/DlgEditOperation.tsx
index 7c019cb5..3356c3d9 100644
--- a/rsconcept/frontend/src/dialogs/DlgEditOperation/DlgEditOperation.tsx
+++ b/rsconcept/frontend/src/dialogs/DlgEditOperation/DlgEditOperation.tsx
@@ -19,13 +19,13 @@ import {
} from '@/models/oss';
import { SubstitutionValidator } from '@/models/ossAPI';
import { ConstituentaID } from '@/models/rsform';
+import { useDialogsStore } from '@/stores/dialogs';
import TabArguments from './TabArguments';
import TabOperation from './TabOperation';
import TabSynthesis from './TabSynthesis';
-interface DlgEditOperationProps {
- hideWindow: () => void;
+export interface DlgEditOperationProps {
oss: IOperationSchema;
target: IOperation;
onSubmit: (data: IOperationUpdateData) => void;
@@ -37,7 +37,8 @@ export enum TabID {
SUBSTITUTION = 2
}
-function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperationProps) {
+function DlgEditOperation() {
+ const { oss, target, onSubmit } = useDialogsStore(state => state.props as DlgEditOperationProps);
const [activeTab, setActiveTab] = useState(TabID.CARD);
const [alias, setAlias] = useState(target.alias);
@@ -142,7 +143,6 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio
void;
+export interface DlgEditReferenceProps {
schema: IRSForm;
initial: IReferenceInputState;
onSave: (newRef: string) => void;
@@ -34,7 +34,8 @@ export enum TabID {
SYNTACTIC = 1
}
-function DlgEditReference({ hideWindow, schema, initial, onSave }: DlgEditReferenceProps) {
+function DlgEditReference() {
+ const { schema, initial, onSave } = useDialogsStore(state => state.props as DlgEditReferenceProps);
const [activeTab, setActiveTab] = useState(initial.type === ReferenceType.ENTITY ? TabID.ENTITY : TabID.SYNTACTIC);
const [reference, setReference] = useState('');
const [isValid, setIsValid] = useState(false);
@@ -43,7 +44,6 @@ function DlgEditReference({ hideWindow, schema, initial, onSave }: DlgEditRefere
onSave(reference)}
className='w-[40rem] px-6 h-[32rem]'
diff --git a/rsconcept/frontend/src/dialogs/DlgEditVersions/DlgEditVersions.tsx b/rsconcept/frontend/src/dialogs/DlgEditVersions/DlgEditVersions.tsx
index 82aa8b1b..1c920962 100644
--- a/rsconcept/frontend/src/dialogs/DlgEditVersions/DlgEditVersions.tsx
+++ b/rsconcept/frontend/src/dialogs/DlgEditVersions/DlgEditVersions.tsx
@@ -7,21 +7,21 @@ import MiniButton from '@/components/ui/MiniButton';
import Modal from '@/components/ui/Modal';
import TextArea from '@/components/ui/TextArea';
import TextInput from '@/components/ui/TextInput';
-import { useRSForm } from '@/context/RSFormContext';
import { IVersionData, IVersionInfo, VersionID } from '@/models/library';
+import { useDialogsStore } from '@/stores/dialogs';
import TableVersions from './TableVersions';
-interface DlgEditVersionsProps {
- hideWindow: () => void;
+export interface DlgEditVersionsProps {
versions: IVersionInfo[];
onDelete: (versionID: VersionID) => void;
onUpdate: (versionID: VersionID, data: IVersionData) => void;
}
-function DlgEditVersions({ hideWindow, versions, onDelete, onUpdate }: DlgEditVersionsProps) {
- const { processing } = useRSForm();
+function DlgEditVersions() {
+ const { versions, onDelete, onUpdate } = useDialogsStore(state => state.props as DlgEditVersionsProps);
const [selected, setSelected] = useState(undefined);
+ const processing = false; // TODO: fix processing hook and versions update
const [version, setVersion] = useState('');
const [description, setDescription] = useState('');
@@ -54,12 +54,7 @@ function DlgEditVersions({ hideWindow, versions, onDelete, onUpdate }: DlgEditVe
}, [selected]);
return (
-
+
void;
+export interface DlgEditWordFormsProps {
target: IConstituenta;
onSave: (data: TermForm[]) => void;
}
-function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps) {
+function DlgEditWordForms() {
+ const { target, onSave } = useDialogsStore(state => state.props as DlgEditWordFormsProps);
const textProcessor = useConceptText();
const [term, setTerm] = useState('');
@@ -123,7 +124,6 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
{
+export interface DlgGraphParamsProps {
initial: GraphFilterParams;
onConfirm: (params: GraphFilterParams) => void;
}
-function DlgGraphParams({ hideWindow, initial, onConfirm }: DlgGraphParamsProps) {
+function DlgGraphParams() {
+ const { initial, onConfirm } = useDialogsStore(state => state.props as DlgGraphParamsProps);
const [params, updateParams] = usePartialUpdate(initial);
return (
onConfirm(params)}
submitText='Применить'
diff --git a/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/DlgInlineSynthesis.tsx b/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/DlgInlineSynthesis.tsx
index d290efe2..b99b0f9b 100644
--- a/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/DlgInlineSynthesis.tsx
+++ b/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/DlgInlineSynthesis.tsx
@@ -4,18 +4,19 @@ import clsx from 'clsx';
import { useEffect, useState } from 'react';
import { TabList, TabPanel, Tabs } from 'react-tabs';
-import Modal, { ModalProps } from '@/components/ui/Modal';
+import Modal from '@/components/ui/Modal';
import TabLabel from '@/components/ui/TabLabel';
import useRSFormDetails from '@/hooks/useRSFormDetails';
import { LibraryItemID } from '@/models/library';
import { ICstSubstitute } from '@/models/oss';
import { ConstituentaID, IInlineSynthesisData, IRSForm } from '@/models/rsform';
+import { useDialogsStore } from '@/stores/dialogs';
import TabConstituents from './TabConstituents';
import TabSchema from './TabSchema';
import TabSubstitutions from './TabSubstitutions';
-interface DlgInlineSynthesisProps extends Pick {
+export interface DlgInlineSynthesisProps {
receiver: IRSForm;
onInlineSynthesis: (data: IInlineSynthesisData) => void;
}
@@ -26,7 +27,8 @@ export enum TabID {
SUBSTITUTIONS = 2
}
-function DlgInlineSynthesis({ hideWindow, receiver, onInlineSynthesis }: DlgInlineSynthesisProps) {
+function DlgInlineSynthesis() {
+ const { receiver, onInlineSynthesis } = useDialogsStore(state => state.props as DlgInlineSynthesisProps);
const [activeTab, setActiveTab] = useState(TabID.SCHEMA);
const [donorID, setDonorID] = useState(undefined);
@@ -60,7 +62,6 @@ function DlgInlineSynthesis({ hideWindow, receiver, onInlineSynthesis }: DlgInli
header='Импорт концептуальной схем'
submitText='Добавить конституенты'
className='w-[40rem] h-[33rem] px-6'
- hideWindow={hideWindow}
canSubmit={validated}
onSubmit={handleSubmit}
>
diff --git a/rsconcept/frontend/src/dialogs/DlgRelocateConstituents.tsx b/rsconcept/frontend/src/dialogs/DlgRelocateConstituents.tsx
index 98e26930..2e599de5 100644
--- a/rsconcept/frontend/src/dialogs/DlgRelocateConstituents.tsx
+++ b/rsconcept/frontend/src/dialogs/DlgRelocateConstituents.tsx
@@ -7,7 +7,7 @@ import { RelocateUpIcon } from '@/components/DomainIcons';
import PickMultiConstituenta from '@/components/select/PickMultiConstituenta';
import SelectLibraryItem from '@/components/select/SelectLibraryItem';
import MiniButton from '@/components/ui/MiniButton';
-import Modal, { ModalProps } from '@/components/ui/Modal';
+import Modal from '@/components/ui/Modal';
import DataLoader from '@/components/wrap/DataLoader';
import { useLibrary } from '@/context/LibraryContext';
import useRSFormDetails from '@/hooks/useRSFormDetails';
@@ -16,15 +16,17 @@ import { HelpTopic } from '@/models/miscellaneous';
import { ICstRelocateData, IOperation, IOperationSchema } from '@/models/oss';
import { getRelocateCandidates } from '@/models/ossAPI';
import { ConstituentaID } from '@/models/rsform';
+import { useDialogsStore } from '@/stores/dialogs';
import { prefixes } from '@/utils/constants';
-interface DlgRelocateConstituentsProps extends Pick {
+export interface DlgRelocateConstituentsProps {
oss: IOperationSchema;
initialTarget?: IOperation;
onSubmit: (data: ICstRelocateData) => void;
}
-function DlgRelocateConstituents({ oss, hideWindow, initialTarget, onSubmit }: DlgRelocateConstituentsProps) {
+function DlgRelocateConstituents() {
+ const { oss, initialTarget, onSubmit } = useDialogsStore(state => state.props as DlgRelocateConstituentsProps);
const library = useLibrary();
const [directionUp, setDirectionUp] = useState(true);
@@ -88,7 +90,6 @@ function DlgRelocateConstituents({ oss, hideWindow, initialTarget, onSubmit }: D
{
+export interface DlgRenameCstProps {
+ schema: IRSForm;
initial: ICstRenameData;
allowChangeType: boolean;
onRename: (data: ICstRenameData) => void;
}
-function DlgRenameCst({ hideWindow, initial, allowChangeType, onRename }: DlgRenameCstProps) {
- const { schema } = useRSForm();
+function DlgRenameCst() {
+ const { schema, initial, allowChangeType, onRename } = useDialogsStore(state => state.props as DlgRenameCstProps);
const [validated, setValidated] = useState(false);
const [cstData, updateData] = usePartialUpdate(initial);
@@ -42,7 +43,6 @@ function DlgRenameCst({ hideWindow, initial, allowChangeType, onRename }: DlgRen
header='Переименование конституенты'
submitText='Переименовать'
submitInvalidTooltip='Введите незанятое имя, соответствующее типу'
- hideWindow={hideWindow}
canSubmit={validated}
onSubmit={() => onRename(cstData)}
className={clsx('w-[30rem]', 'py-6 pr-3 pl-6 flex gap-3 justify-center items-center ')}
diff --git a/rsconcept/frontend/src/dialogs/DlgShowAST/DlgShowAST.tsx b/rsconcept/frontend/src/dialogs/DlgShowAST/DlgShowAST.tsx
index d48e2dc2..69bea28d 100644
--- a/rsconcept/frontend/src/dialogs/DlgShowAST/DlgShowAST.tsx
+++ b/rsconcept/frontend/src/dialogs/DlgShowAST/DlgShowAST.tsx
@@ -3,19 +3,21 @@
import { useState } from 'react';
import { ReactFlowProvider } from 'reactflow';
-import Modal, { ModalProps } from '@/components/ui/Modal';
+import Modal from '@/components/ui/Modal';
import Overlay from '@/components/ui/Overlay';
import { HelpTopic } from '@/models/miscellaneous';
import { SyntaxTree } from '@/models/rslang';
+import { useDialogsStore } from '@/stores/dialogs';
import ASTFlow from './ASTFlow';
-interface DlgShowASTProps extends Pick {
+export interface DlgShowASTProps {
syntaxTree: SyntaxTree;
expression: string;
}
-function DlgShowAST({ hideWindow, syntaxTree, expression }: DlgShowASTProps) {
+function DlgShowAST() {
+ const { syntaxTree, expression } = useDialogsStore(state => state.props as DlgShowASTProps);
const [hoverID, setHoverID] = useState(undefined);
const hoverNode = syntaxTree.find(node => node.uid === hoverID);
@@ -24,7 +26,6 @@ function DlgShowAST({ hideWindow, syntaxTree, expression }: DlgShowASTProps) {
return (
diff --git a/rsconcept/frontend/src/dialogs/DlgShowQR.tsx b/rsconcept/frontend/src/dialogs/DlgShowQR.tsx
index 8a1b16ac..84ff55e8 100644
--- a/rsconcept/frontend/src/dialogs/DlgShowQR.tsx
+++ b/rsconcept/frontend/src/dialogs/DlgShowQR.tsx
@@ -3,19 +3,17 @@
import clsx from 'clsx';
import { QRCodeSVG } from 'qrcode.react';
-import Modal, { ModalProps } from '@/components/ui/Modal';
+import Modal from '@/components/ui/Modal';
+import { useDialogsStore } from '@/stores/dialogs';
-interface DlgShowQRProps extends Pick {
+export interface DlgShowQRProps {
target: string;
}
-function DlgShowQR({ hideWindow, target }: DlgShowQRProps) {
+function DlgShowQR() {
+ const { target } = useDialogsStore(state => state.props as DlgShowQRProps);
return (
-
+
diff --git a/rsconcept/frontend/src/dialogs/DlgShowTypeGraph/DlgShowTypeGraph.tsx b/rsconcept/frontend/src/dialogs/DlgShowTypeGraph/DlgShowTypeGraph.tsx
index d6e9bded..a3cb0b95 100644
--- a/rsconcept/frontend/src/dialogs/DlgShowTypeGraph/DlgShowTypeGraph.tsx
+++ b/rsconcept/frontend/src/dialogs/DlgShowTypeGraph/DlgShowTypeGraph.tsx
@@ -3,19 +3,22 @@
import { toast } from 'react-toastify';
import { ReactFlowProvider } from 'reactflow';
-import Modal, { ModalProps } from '@/components/ui/Modal';
+import Modal from '@/components/ui/Modal';
import { HelpTopic } from '@/models/miscellaneous';
import { ITypeInfo } from '@/models/rslang';
import { TMGraph } from '@/models/TMGraph';
+import { useDialogsStore } from '@/stores/dialogs';
import { errors } from '@/utils/labels';
import MGraphFlow from './MGraphFlow';
-interface DlgShowTypeGraphProps extends Pick {
+export interface DlgShowTypeGraphProps {
items: ITypeInfo[];
}
-function DlgShowTypeGraph({ hideWindow, items }: DlgShowTypeGraphProps) {
+function DlgShowTypeGraph() {
+ const { items } = useDialogsStore(state => state.props as DlgShowTypeGraphProps);
+ const hideDialog = useDialogsStore(state => state.hideDialog);
const graph = (() => {
const result = new TMGraph();
items.forEach(item => result.addConstituenta(item.alias, item.result, item.args));
@@ -24,7 +27,7 @@ function DlgShowTypeGraph({ hideWindow, items }: DlgShowTypeGraphProps) {
if (graph.nodes.length === 0) {
toast.error(errors.typeStructureFailed);
- hideWindow();
+ hideDialog();
return null;
}
@@ -32,7 +35,6 @@ function DlgShowTypeGraph({ hideWindow, items }: DlgShowTypeGraphProps) {
diff --git a/rsconcept/frontend/src/dialogs/DlgSubstituteCst.tsx b/rsconcept/frontend/src/dialogs/DlgSubstituteCst.tsx
index d696a7cb..21a017b4 100644
--- a/rsconcept/frontend/src/dialogs/DlgSubstituteCst.tsx
+++ b/rsconcept/frontend/src/dialogs/DlgSubstituteCst.tsx
@@ -4,18 +4,20 @@ import clsx from 'clsx';
import { useState } from 'react';
import PickSubstitutions from '@/components/select/PickSubstitutions';
-import Modal, { ModalProps } from '@/components/ui/Modal';
+import Modal from '@/components/ui/Modal';
import { HelpTopic } from '@/models/miscellaneous';
import { ICstSubstitute, ICstSubstituteData } from '@/models/oss';
import { IRSForm } from '@/models/rsform';
+import { useDialogsStore } from '@/stores/dialogs';
import { prefixes } from '@/utils/constants';
-interface DlgSubstituteCstProps extends Pick {
+export interface DlgSubstituteCstProps {
schema: IRSForm;
onSubstitute: (data: ICstSubstituteData) => void;
}
-function DlgSubstituteCst({ hideWindow, onSubstitute, schema }: DlgSubstituteCstProps) {
+function DlgSubstituteCst() {
+ const { onSubstitute, schema } = useDialogsStore(state => state.props as DlgSubstituteCstProps);
const [substitutions, setSubstitutions] = useState([]);
const canSubmit = substitutions.length > 0;
@@ -31,7 +33,6 @@ function DlgSubstituteCst({ hideWindow, onSubstitute, schema }: DlgSubstituteCst
header='Отождествление'
submitText='Отождествить'
submitInvalidTooltip='Выберите две различные конституенты'
- hideWindow={hideWindow}
canSubmit={canSubmit}
onSubmit={handleSubmit}
className={clsx('w-[40rem]', 'px-6 pb-3')}
diff --git a/rsconcept/frontend/src/dialogs/DlgUploadRSForm.tsx b/rsconcept/frontend/src/dialogs/DlgUploadRSForm.tsx
index efe48225..ef31a68b 100644
--- a/rsconcept/frontend/src/dialogs/DlgUploadRSForm.tsx
+++ b/rsconcept/frontend/src/dialogs/DlgUploadRSForm.tsx
@@ -6,16 +6,16 @@ import { toast } from 'react-toastify';
import Checkbox from '@/components/ui/Checkbox';
import FileInput from '@/components/ui/FileInput';
import Modal from '@/components/ui/Modal';
-import { useRSForm } from '@/context/RSFormContext';
import { IRSFormUploadData } from '@/models/rsform';
+import { useDialogsStore } from '@/stores/dialogs';
import { EXTEOR_TRS_FILE } from '@/utils/constants';
-interface DlgUploadRSFormProps {
- hideWindow: () => void;
+export interface DlgUploadRSFormProps {
+ upload: (data: IRSFormUploadData, callback: () => void) => void;
}
-function DlgUploadRSForm({ hideWindow }: DlgUploadRSFormProps) {
- const { upload } = useRSForm();
+function DlgUploadRSForm() {
+ const { upload } = useDialogsStore(state => state.props as DlgUploadRSFormProps);
const [loadMetadata, setLoadMetadata] = useState(false);
const [file, setFile] = useState();
@@ -42,7 +42,6 @@ function DlgUploadRSForm({ hideWindow }: DlgUploadRSFormProps) {
return (
state.showChangeLocation);
- const handleRenameLocation = useCallback(
- (newLocation: string) => {
- const data: IRenameLocationData = {
- target: location,
- new_location: newLocation
- };
- library.renameLocation(data, () => {
- setLocation(newLocation);
- toast.success(information.locationRenamed);
- });
- },
- [location, setLocation, library]
- );
+ function handleRenameLocation(newLocation: string) {
+ const data: IRenameLocationData = {
+ target: location,
+ new_location: newLocation
+ };
+ library.renameLocation(data, () => {
+ setLocation(newLocation);
+ toast.success(information.locationRenamed);
+ });
+ }
- const handleDownloadCSV = useCallback(() => {
+ function handleDownloadCSV() {
if (items.length === 0) {
toast.error(information.noDataToExport);
return;
@@ -58,17 +54,10 @@ function LibraryPage() {
} catch (error) {
console.error(error);
}
- }, [items]);
+ }
return (
- {showRenameLocation ? (
- setShowRenameLocation(false)}
- />
- ) : null}
setShowRenameLocation(true)}
+ onRenameLocation={() => showChangeLocation({ initial: location, onChangeLocation: handleRenameLocation })}
/>
diff --git a/rsconcept/frontend/src/pages/OssPage/OssEditContext.tsx b/rsconcept/frontend/src/pages/OssPage/OssEditContext.tsx
index 99b695ab..4289e853 100644
--- a/rsconcept/frontend/src/pages/OssPage/OssEditContext.tsx
+++ b/rsconcept/frontend/src/pages/OssPage/OssEditContext.tsx
@@ -1,6 +1,6 @@
'use client';
-import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
+import { createContext, useCallback, useContext, useEffect, useState } from 'react';
import { toast } from 'react-toastify';
import { urls } from '@/app/urls';
@@ -8,13 +8,6 @@ import { useAuth } from '@/context/AuthContext';
import { useLibrary } from '@/context/LibraryContext';
import { useConceptNavigation } from '@/context/NavigationContext';
import { useOSS } from '@/context/OssContext';
-import DlgChangeInputSchema from '@/dialogs/DlgChangeInputSchema';
-import DlgChangeLocation from '@/dialogs/DlgChangeLocation';
-import DlgCreateOperation from '@/dialogs/DlgCreateOperation';
-import DlgDeleteOperation from '@/dialogs/DlgDeleteOperation';
-import DlgEditEditors from '@/dialogs/DlgEditEditors';
-import DlgEditOperation from '@/dialogs/DlgEditOperation';
-import DlgRelocateConstituents from '@/dialogs/DlgRelocateConstituents';
import { AccessPolicy, ILibraryItemEditor, LibraryItemID } from '@/models/library';
import { Position2D } from '@/models/miscellaneous';
import { calculateInsertPosition } from '@/models/miscellaneousAPI';
@@ -30,6 +23,7 @@ import {
OperationType
} from '@/models/oss';
import { UserID, UserRole } from '@/models/user';
+import { useDialogsStore } from '@/stores/dialogs';
import { usePreferencesStore } from '@/stores/preferences';
import { useRoleStore } from '@/stores/role';
import { PARAMETER } from '@/utils/constants';
@@ -106,24 +100,18 @@ export const OssEditState = ({ selected, setSelected, children }: React.PropsWit
const [showTooltip, setShowTooltip] = useState(true);
- const [showEditEditors, setShowEditEditors] = useState(false);
- const [showEditLocation, setShowEditLocation] = useState(false);
- const [showEditInput, setShowEditInput] = useState(false);
- const [showEditOperation, setShowEditOperation] = useState(false);
- const [showDeleteOperation, setShowDeleteOperation] = useState(false);
- const [showRelocateConstituents, setShowRelocateConstituents] = useState(false);
-
- const [showCreateOperation, setShowCreateOperation] = useState(false);
const [insertPosition, setInsertPosition] = useState({ x: 0, y: 0 });
- const [initialInputs, setInitialInputs] = useState([]);
const [createCallback, setCreateCallback] = useState<((newID: OperationID) => void) | undefined>(undefined);
+ const showEditEditors = useDialogsStore(state => state.showEditEditors);
+ const showEditLocation = useDialogsStore(state => state.showChangeLocation);
+ const showEditInput = useDialogsStore(state => state.showChangeInputSchema);
+ const showEditOperation = useDialogsStore(state => state.showEditOperation);
+ const showDeleteOperation = useDialogsStore(state => state.showDeleteOperation);
+ const showRelocateConstituents = useDialogsStore(state => state.showRelocateConstituents);
+ const showCreateOperation = useDialogsStore(state => state.showCreateOperation);
+
const [positions, setPositions] = useState([]);
- const [targetOperationID, setTargetOperationID] = useState(undefined);
- const targetOperation = useMemo(
- () => (targetOperationID ? model.schema?.operationByID.get(targetOperationID) : undefined),
- [model, targetOperationID]
- );
useEffect(
() =>
@@ -146,14 +134,6 @@ export const OssEditState = ({ selected, setSelected, children }: React.PropsWit
[model]
);
- const promptEditors = useCallback(() => {
- setShowEditEditors(true);
- }, []);
-
- const promptLocation = useCallback(() => {
- setShowEditLocation(true);
- }, []);
-
const share = useCallback(() => {
const currentRef = window.location.href;
const url = currentRef.includes('?') ? currentRef + '&share' : currentRef + '?share';
@@ -177,7 +157,7 @@ export const OssEditState = ({ selected, setSelected, children }: React.PropsWit
[model]
);
- const setEditors = useCallback(
+ const handleSetEditors = useCallback(
(newEditors: UserID[]) => {
model.setEditors(newEditors, () => toast.success(information.changesSaved));
},
@@ -212,17 +192,6 @@ export const OssEditState = ({ selected, setSelected, children }: React.PropsWit
[model]
);
- const promptCreateOperation = useCallback(
- ({ defaultX, defaultY, inputs, positions, callback }: ICreateOperationPrompt) => {
- setInsertPosition({ x: defaultX, y: defaultY });
- setInitialInputs(inputs);
- setPositions(positions);
- setCreateCallback(() => callback);
- setShowCreateOperation(true);
- },
- []
- );
-
const handleCreateOperation = useCallback(
(data: IOperationCreateData) => {
const target = calculateInsertPosition(
@@ -245,12 +214,6 @@ export const OssEditState = ({ selected, setSelected, children }: React.PropsWit
[model, positions, insertPosition, createCallback]
);
- const promptEditOperation = useCallback((target: OperationID, positions: IOperationPosition[]) => {
- setPositions(positions);
- setTargetOperationID(target);
- setShowEditOperation(true);
- }, []);
-
const handleEditOperation = useCallback(
(data: IOperationUpdateData) => {
data.positions = positions;
@@ -276,26 +239,17 @@ export const OssEditState = ({ selected, setSelected, children }: React.PropsWit
[model]
);
- const promptDeleteOperation = useCallback((target: OperationID, positions: IOperationPosition[]) => {
- setPositions(positions);
- setTargetOperationID(target);
- setShowDeleteOperation(true);
- }, []);
-
- const deleteOperation = useCallback(
- (keepConstituents: boolean, deleteSchema: boolean) => {
- if (!targetOperationID) {
- return;
- }
+ const handleDeleteOperation = useCallback(
+ (targetID: OperationID, keepConstituents: boolean, deleteSchema: boolean) => {
const data: IOperationDeleteData = {
- target: targetOperationID,
+ target: targetID,
positions: positions,
keep_constituents: keepConstituents,
delete_schema: deleteSchema
};
model.deleteOperation(data, () => toast.success(information.operationDestroyed));
},
- [model, targetOperationID, positions]
+ [model, positions]
);
const createInput = useCallback(
@@ -316,44 +270,18 @@ export const OssEditState = ({ selected, setSelected, children }: React.PropsWit
[model, library.items, router]
);
- const promptEditInput = useCallback((target: OperationID, positions: IOperationPosition[]) => {
- setPositions(positions);
- setTargetOperationID(target);
- setShowEditInput(true);
- }, []);
-
const setTargetInput = useCallback(
- (newInput: LibraryItemID | undefined) => {
- if (!targetOperationID) {
- return;
- }
+ (target: OperationID, newInput: LibraryItemID | undefined) => {
const data: IOperationSetInputData = {
- target: targetOperationID,
+ target: target,
positions: positions,
input: newInput ?? null
};
model.setInput(data, () => toast.success(information.changesSaved));
},
- [model, targetOperationID, positions]
+ [model, positions]
);
- const executeOperation = useCallback(
- (target: OperationID, positions: IOperationPosition[]) => {
- const data = {
- target: target,
- positions: positions
- };
- model.executeOperation(data, () => toast.success(information.operationExecuted));
- },
- [model]
- );
-
- const promptRelocateConstituents = useCallback((target: OperationID | undefined, positions: IOperationPosition[]) => {
- setPositions(positions);
- setTargetOperationID(target);
- setShowRelocateConstituents(true);
- }, []);
-
const handleRelocateConstituents = useCallback(
(data: ICstRelocateData) => {
if (
@@ -372,6 +300,92 @@ export const OssEditState = ({ selected, setSelected, children }: React.PropsWit
[model, positions]
);
+ const executeOperation = useCallback(
+ (target: OperationID, positions: IOperationPosition[]) => {
+ const data = {
+ target: target,
+ positions: positions
+ };
+ model.executeOperation(data, () => toast.success(information.operationExecuted));
+ },
+ [model]
+ );
+
+ const promptEditors = useCallback(() => {
+ if (!model.schema) {
+ return;
+ }
+ showEditEditors({ editors: model.schema.editors, setEditors: handleSetEditors });
+ }, [model.schema, showEditEditors, handleSetEditors]);
+
+ const promptLocation = useCallback(() => {
+ if (!model.schema) {
+ return;
+ }
+ showEditLocation({ initial: model.schema.location, onChangeLocation: handleSetLocation });
+ }, [model.schema, showEditLocation, handleSetLocation]);
+
+ const promptCreateOperation = useCallback(
+ ({ defaultX, defaultY, inputs, positions, callback }: ICreateOperationPrompt) => {
+ if (!model.schema) {
+ return;
+ }
+ setInsertPosition({ x: defaultX, y: defaultY });
+ setPositions(positions);
+ setCreateCallback(() => callback);
+ showCreateOperation({ oss: model.schema, onCreate: handleCreateOperation, initialInputs: inputs });
+ },
+ [model.schema, showCreateOperation, handleCreateOperation]
+ );
+
+ const promptEditOperation = useCallback(
+ (target: OperationID, positions: IOperationPosition[]) => {
+ const operation = model.schema?.operationByID.get(target);
+ if (!model.schema || !operation) {
+ return;
+ }
+ setPositions(positions);
+ showEditOperation({ oss: model.schema, target: operation, onSubmit: handleEditOperation });
+ },
+ [model.schema, showEditOperation, handleEditOperation]
+ );
+
+ const promptDeleteOperation = useCallback(
+ (target: OperationID, positions: IOperationPosition[]) => {
+ const operation = model.schema?.operationByID.get(target);
+ if (!model.schema || !operation) {
+ return;
+ }
+ setPositions(positions);
+ showDeleteOperation({ target: operation, onSubmit: handleDeleteOperation });
+ },
+ [model.schema, showDeleteOperation, handleDeleteOperation]
+ );
+
+ const promptEditInput = useCallback(
+ (target: OperationID, positions: IOperationPosition[]) => {
+ const operation = model.schema?.operationByID.get(target);
+ if (!model.schema || !operation) {
+ return;
+ }
+ setPositions(positions);
+ showEditInput({ oss: model.schema, target: operation, onSubmit: setTargetInput });
+ },
+ [model.schema, showEditInput, setTargetInput]
+ );
+
+ const promptRelocateConstituents = useCallback(
+ (target: OperationID | undefined, positions: IOperationPosition[]) => {
+ if (!model.schema) {
+ return;
+ }
+ const operation = target ? model.schema?.operationByID.get(target) : undefined;
+ setPositions(positions);
+ showRelocateConstituents({ oss: model.schema, initialTarget: operation, onSubmit: handleRelocateConstituents });
+ },
+ [model.schema, showRelocateConstituents, handleRelocateConstituents]
+ );
+
return (
- {model.schema ? (
- <>
- {showEditEditors ? (
- setShowEditEditors(false)}
- editors={model.schema.editors}
- setEditors={setEditors}
- />
- ) : null}
- {showEditLocation ? (
- setShowEditLocation(false)}
- initial={model.schema.location}
- onChangeLocation={handleSetLocation}
- />
- ) : null}
- {showCreateOperation ? (
- setShowCreateOperation(false)}
- oss={model.schema}
- onCreate={handleCreateOperation}
- initialInputs={initialInputs}
- />
- ) : null}
- {showEditInput ? (
- setShowEditInput(false)}
- oss={model.schema}
- target={targetOperation!}
- onSubmit={setTargetInput}
- />
- ) : null}
- {showEditOperation ? (
- setShowEditOperation(false)}
- oss={model.schema}
- target={targetOperation!}
- onSubmit={handleEditOperation}
- />
- ) : null}
- {showDeleteOperation ? (
- setShowDeleteOperation(false)}
- target={targetOperation!}
- onSubmit={deleteOperation}
- />
- ) : null}
- {showRelocateConstituents ? (
- setShowRelocateConstituents(false)}
- initialTarget={targetOperation}
- oss={model.schema}
- onSubmit={handleRelocateConstituents}
- />
- ) : null}
- >
- ) : null}
-
{children}
);
diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/FormConstituenta.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/FormConstituenta.tsx
index 870f6555..5ef8b9d3 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/FormConstituenta.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/FormConstituenta.tsx
@@ -12,10 +12,10 @@ import Overlay from '@/components/ui/Overlay';
import SubmitButton from '@/components/ui/SubmitButton';
import TextArea from '@/components/ui/TextArea';
import { useRSForm } from '@/context/RSFormContext';
-import DlgShowTypeGraph from '@/dialogs/DlgShowTypeGraph';
import { ConstituentaID, CstType, IConstituenta, ICstUpdateData } from '@/models/rsform';
import { isBaseSet, isBasicConcept, isFunctional } from '@/models/rsformAPI';
import { IExpressionParse, ParsingStatus } from '@/models/rslang';
+import { useDialogsStore } from '@/stores/dialogs';
import { errors, information, labelCstTypification } from '@/utils/labels';
import EditorRSExpression from '../EditorRSExpression';
@@ -56,7 +56,6 @@ function FormConstituenta({
const [expression, setExpression] = useState(state?.definition_formal ?? '');
const [convention, setConvention] = useState(state?.convention ?? '');
const [typification, setTypification] = useState('N/A');
- const [showTypification, setShowTypification] = useState(false);
const [localParse, setLocalParse] = useState(undefined);
const typeInfo = state
? {
@@ -72,6 +71,8 @@ function FormConstituenta({
const isElementary = !!state && isBaseSet(state.cst_type);
const showConvention = !state || !!state.convention || forceComment || isBasic;
+ const showTypification = useDialogsStore(state => state.showShowTypeGraph);
+
useEffect(() => {
if (state) {
setConvention(state.convention);
@@ -142,14 +143,11 @@ function FormConstituenta({
}
event.stopPropagation();
event.preventDefault();
- setShowTypification(true);
+ showTypification({ items: typeInfo ? [typeInfo] : [] });
}
return (
- {showTypification && state ? (
-
setShowTypification(false)} />
- ) : null}
{state ? (
(null);
const showControls = usePreferencesStore(state => state.showExpressionControls);
- const [syntaxTree, setSyntaxTree] = useState([]);
- const [expression, setExpression] = useState('');
- const [showAST, setShowAST] = useState(false);
+ const showAST = useDialogsStore(state => state.showShowAST);
useEffect(() => {
setIsModified(false);
@@ -131,17 +129,13 @@ function EditorRSExpression({
if (event.ctrlKey) {
const tree = rslangParser.parse(value);
const ast = transformAST(tree);
- setSyntaxTree(ast);
- setExpression(value);
- setShowAST(true);
+ showAST({ syntaxTree: ast, expression: value });
} else {
handleCheckExpression(parse => {
if (!parse.astText) {
toast.error(errors.astFailed);
} else {
- setSyntaxTree(parse.ast);
- setExpression(getDefinitionPrefix(activeCst) + value);
- setShowAST(true);
+ showAST({ syntaxTree: parse.ast, expression: getDefinitionPrefix(activeCst) + value });
}
});
}
@@ -149,10 +143,6 @@ function EditorRSExpression({
return (
- {showAST ? (
-
setShowAST(false)} />
- ) : null}
-
state.showGraphParams);
const filter = useTermGraphStore(state => state.filter);
const setFilter = useTermGraphStore(state => state.setFilter);
@@ -295,15 +295,11 @@ function TGFlow({ onOpenEdit }: TGFlowProps) {
return (
<>
- {showParamsDialog ? (
- setShowParamsDialog(false)} initial={filter} onConfirm={setFilter} />
- ) : null}
-
setShowParamsDialog(true)}
+ showParamsDialog={() => showParams({ initial: filter, onConfirm: setFilter })}
onCreate={handleCreateCst}
onDelete={handleDeleteCst}
onFitView={() => setToggleResetView(prev => !prev)}
diff --git a/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx b/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx
index 3fd738a2..530ab7c0 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx
@@ -8,21 +8,6 @@ import { urls } from '@/app/urls';
import { useAuth } from '@/context/AuthContext';
import { useConceptNavigation } from '@/context/NavigationContext';
import { useRSForm } from '@/context/RSFormContext';
-import DlgChangeLocation from '@/dialogs/DlgChangeLocation';
-import DlgCloneLibraryItem from '@/dialogs/DlgCloneLibraryItem';
-import DlgConstituentaTemplate from '@/dialogs/DlgConstituentaTemplate';
-import DlgCreateCst from '@/dialogs/DlgCreateCst';
-import DlgCreateVersion from '@/dialogs/DlgCreateVersion';
-import DlgDeleteCst from '@/dialogs/DlgDeleteCst';
-import DlgEditEditors from '@/dialogs/DlgEditEditors';
-import DlgEditVersions from '@/dialogs/DlgEditVersions';
-import DlgEditWordForms from '@/dialogs/DlgEditWordForms';
-import DlgInlineSynthesis from '@/dialogs/DlgInlineSynthesis';
-import DlgRenameCst from '@/dialogs/DlgRenameCst';
-import DlgShowQR from '@/dialogs/DlgShowQR';
-import DlgShowTypeGraph from '@/dialogs/DlgShowTypeGraph';
-import DlgSubstituteCst from '@/dialogs/DlgSubstituteCst';
-import DlgUploadRSForm from '@/dialogs/DlgUploadRSForm';
import {
AccessPolicy,
ILibraryItemEditor,
@@ -49,6 +34,7 @@ import {
} from '@/models/rsform';
import { generateAlias } from '@/models/rsformAPI';
import { UserID, UserRole } from '@/models/user';
+import { useDialogsStore } from '@/stores/dialogs';
import { usePreferencesStore } from '@/stores/preferences';
import { useRoleStore } from '@/stores/role';
import { EXTEOR_TRS_FILE } from '@/utils/constants';
@@ -161,27 +147,26 @@ export const RSEditState = ({
[model.schema]
);
- const [showUpload, setShowUpload] = useState(false);
- const [showClone, setShowClone] = useState(false);
- const [showDeleteCst, setShowDeleteCst] = useState(false);
- const [showEditEditors, setShowEditEditors] = useState(false);
- const [showEditLocation, setShowEditLocation] = useState(false);
- const [showEditTerm, setShowEditTerm] = useState(false);
- const [showSubstitute, setShowSubstitute] = useState(false);
- const [showCreateVersion, setShowCreateVersion] = useState(false);
- const [showEditVersions, setShowEditVersions] = useState(false);
- const [showInlineSynthesis, setShowInlineSynthesis] = useState(false);
- const [showTypeGraph, setShowTypeGraph] = useState(false);
- const [showQR, setShowQR] = useState(false);
-
- const [createInitialData, setCreateInitialData] = useState();
- const [showCreateCst, setShowCreateCst] = useState(false);
-
const [renameInitialData, setRenameInitialData] = useState();
- const [showRenameCst, setShowRenameCst] = useState(false);
- const [insertCstID, setInsertCstID] = useState(undefined);
- const [showTemplates, setShowTemplates] = useState(false);
+ const showClone = useDialogsStore(state => state.showCloneLibraryItem);
+ const showCreateVersion = useDialogsStore(state => state.showCreateVersion);
+ const showEditVersions = useDialogsStore(state => state.showEditVersions);
+ const showEditEditors = useDialogsStore(state => state.showEditEditors);
+ const showEditLocation = useDialogsStore(state => state.showChangeLocation);
+
+ const showCreateCst = useDialogsStore(state => state.showCreateCst);
+ const showDeleteCst = useDialogsStore(state => state.showDeleteCst);
+
+ const showRenameCst = useDialogsStore(state => state.showRenameCst);
+ const showEditTerm = useDialogsStore(state => state.showEditWordForms);
+
+ const showSubstituteCst = useDialogsStore(state => state.showSubstituteCst);
+ const showCstTemplate = useDialogsStore(state => state.showCstTemplate);
+ const showInlineSynthesis = useDialogsStore(state => state.showInlineSynthesis);
+ const showTypeGraph = useDialogsStore(state => state.showShowTypeGraph);
+ const showUpload = useDialogsStore(state => state.showUploadRSForm);
+ const showQR = useDialogsStore(state => state.showQR);
const typeInfo = useMemo(
() =>
@@ -235,13 +220,6 @@ export const RSEditState = ({
[router]
);
- const createVersion = useCallback(() => {
- if (isModified && !promptUnsaved()) {
- return;
- }
- setShowCreateVersion(true);
- }, [isModified]);
-
const restoreVersion = useCallback(() => {
if (!model.versionID || !window.confirm(prompts.restoreArchive)) {
return;
@@ -252,7 +230,7 @@ export const RSEditState = ({
});
}, [model, viewVersion]);
- function calculateCloneLocation(): string {
+ const calculateCloneLocation = useCallback(() => {
if (!model.schema) {
return LocationHead.USER;
}
@@ -265,7 +243,7 @@ export const RSEditState = ({
return location;
}
return head === LocationHead.USER ? LocationHead.USER : location;
- }
+ }, [model.schema, user]);
const handleCreateCst = useCallback(
(data: ICstCreateData) => {
@@ -397,6 +375,29 @@ export const RSEditState = ({
[model, setSelected]
);
+ const createVersion = useCallback(() => {
+ if (!model.schema || (isModified && !promptUnsaved())) {
+ return;
+ }
+ showCreateVersion({
+ versions: model.schema.versions,
+ onCreate: handleCreateVersion,
+ selected: selected,
+ totalCount: model.schema.items.length
+ });
+ }, [isModified, model.schema, selected, handleCreateVersion, showCreateVersion]);
+
+ const promptEditVersions = useCallback(() => {
+ if (!model.schema) {
+ return;
+ }
+ showEditVersions({
+ versions: model.schema.versions,
+ onDelete: handleDeleteVersion,
+ onUpdate: handleUpdateVersion
+ });
+ }, [model.schema, handleDeleteVersion, handleUpdateVersion, showEditVersions]);
+
const moveUp = useCallback(() => {
if (!model.schema?.items || selected.length === 0) {
return;
@@ -460,11 +461,10 @@ export const RSEditState = ({
if (skipDialog) {
handleCreateCst(data);
} else {
- setCreateInitialData(data);
- setShowCreateCst(true);
+ showCreateCst({ schema: model.schema, onCreate: handleCreateCst, initial: data });
}
},
- [activeCst, handleCreateCst, model.schema]
+ [activeCst, handleCreateCst, model.schema, showCreateCst]
);
const cloneCst = useCallback(() => {
@@ -485,7 +485,7 @@ export const RSEditState = ({
}, [activeCst, handleCreateCst, model.schema]);
const renameCst = useCallback(() => {
- if (!activeCst) {
+ if (!activeCst || !model.schema) {
return;
}
const data: ICstRenameData = {
@@ -494,22 +494,34 @@ export const RSEditState = ({
cst_type: activeCst.cst_type
};
setRenameInitialData(data);
- setShowRenameCst(true);
- }, [activeCst]);
+ showRenameCst({
+ schema: model.schema,
+ initial: data,
+ allowChangeType: !activeCst.is_inherited,
+ onRename: handleRenameCst
+ });
+ }, [activeCst, model.schema, handleRenameCst, showRenameCst]);
const substitute = useCallback(() => {
- if (isModified && !promptUnsaved()) {
+ if (!model.schema || (isModified && !promptUnsaved())) {
return;
}
- setShowSubstitute(true);
- }, [isModified]);
+ showSubstituteCst({ schema: model.schema, onSubstitute: handleSubstituteCst });
+ }, [isModified, model.schema, handleSubstituteCst, showSubstituteCst]);
const inlineSynthesis = useCallback(() => {
- if (isModified && !promptUnsaved()) {
+ if (!model.schema || (isModified && !promptUnsaved())) {
return;
}
- setShowInlineSynthesis(true);
- }, [isModified]);
+ showInlineSynthesis({ receiver: model.schema, onInlineSynthesis: handleInlineSynthesis });
+ }, [isModified, model.schema, handleInlineSynthesis, showInlineSynthesis]);
+
+ const promptDeleteCst = useCallback(() => {
+ if (!model.schema) {
+ return;
+ }
+ showDeleteCst({ schema: model.schema, selected: selected, onDelete: handleDeleteCst });
+ }, [model.schema, selected, handleDeleteCst, showDeleteCst]);
const editTermForms = useCallback(() => {
if (!activeCst) {
@@ -518,8 +530,8 @@ export const RSEditState = ({
if (isModified && !promptUnsaved()) {
return;
}
- setShowEditTerm(true);
- }, [isModified, activeCst]);
+ showEditTerm({ target: activeCst, onSave: handleSaveWordforms });
+ }, [isModified, activeCst, handleSaveWordforms, showEditTerm]);
const reindex = useCallback(() => model.resetAliases(() => toast.success(information.reindexComplete)), [model]);
const reorder = useCallback(() => model.restoreOrder(() => toast.success(information.reorderComplete)), [model]);
@@ -551,28 +563,45 @@ export const RSEditState = ({
});
}, [activeCst, setSelected, model, isModified]);
+ const setEditors = useCallback(
+ (newEditors: UserID[]) => {
+ model.setEditors(newEditors, () => toast.success(information.changesSaved));
+ },
+ [model]
+ );
+
const promptTemplate = useCallback(() => {
- if (isModified && !promptUnsaved()) {
+ if ((isModified && !promptUnsaved()) || !model.schema) {
return;
}
- setInsertCstID(activeCst?.id);
- setShowTemplates(true);
- }, [activeCst, isModified]);
+ showCstTemplate({ schema: model.schema, onCreate: handleCreateCst, insertAfter: activeCst?.id });
+ }, [activeCst, isModified, handleCreateCst, model.schema, showCstTemplate]);
const promptClone = useCallback(() => {
- if (isModified && !promptUnsaved()) {
+ if (!model.schema || (isModified && !promptUnsaved())) {
return;
}
- setShowClone(true);
- }, [isModified]);
+ showClone({
+ base: model.schema,
+ initialLocation: calculateCloneLocation(),
+ selected: selected,
+ totalCount: model.schema.items.length
+ });
+ }, [isModified, model.schema, selected, showClone, calculateCloneLocation]);
const promptEditors = useCallback(() => {
- setShowEditEditors(true);
- }, []);
+ if (!model.schema) {
+ return;
+ }
+ showEditEditors({ editors: model.schema.editors, setEditors: setEditors });
+ }, [model.schema, showEditEditors, setEditors]);
const promptLocation = useCallback(() => {
- setShowEditLocation(true);
- }, []);
+ if (!model.schema) {
+ return;
+ }
+ showEditLocation({ initial: model.schema.location, onChangeLocation: handleSetLocation });
+ }, [model.schema, showEditLocation, handleSetLocation]);
const download = useCallback(() => {
if (isModified && !promptUnsaved()) {
@@ -611,13 +640,6 @@ export const RSEditState = ({
[model]
);
- const setEditors = useCallback(
- (newEditors: UserID[]) => {
- model.setEditors(newEditors, () => toast.success(information.changesSaved));
- },
- [model]
- );
-
function generateQR(): string {
const currentRef = window.location.href;
return currentRef.includes('?') ? currentRef + '&qr' : currentRef + '?qr';
@@ -653,19 +675,19 @@ export const RSEditState = ({
viewPredecessor,
createVersion,
restoreVersion,
- promptEditVersions: () => setShowEditVersions(true),
+ promptEditVersions,
moveUp,
moveDown,
createCst,
cloneCst,
renameCst,
- promptDeleteCst: () => setShowDeleteCst(true),
+ promptDeleteCst,
editTermForms,
promptTemplate,
promptClone,
- promptUpload: () => setShowUpload(true),
+ promptUpload: () => showUpload({ upload: model.upload }),
download,
share,
@@ -675,113 +697,10 @@ export const RSEditState = ({
produceStructure,
substitute,
- showTypeGraph: () => setShowTypeGraph(true),
- showQR: () => setShowQR(true)
+ showTypeGraph: () => showTypeGraph({ items: typeInfo }),
+ showQR: () => showQR({ target: generateQR() })
}}
>
- {model.schema ? (
- <>
- {showQR ? setShowQR(false)} target={generateQR()} /> : null}
- {showUpload ? setShowUpload(false)} /> : null}
- {showClone ? (
- setShowClone(false)}
- selected={selected}
- totalCount={model.schema.items.length}
- />
- ) : null}
- {showCreateCst ? (
- setShowCreateCst(false)}
- onCreate={handleCreateCst}
- schema={model.schema}
- initial={createInitialData}
- />
- ) : null}
- {activeCst && showRenameCst && renameInitialData ? (
- setShowRenameCst(false)}
- onRename={handleRenameCst}
- allowChangeType={!activeCst.is_inherited}
- initial={renameInitialData}
- />
- ) : null}
- {showSubstitute ? (
- setShowSubstitute(false)} // prettier: split lines
- onSubstitute={handleSubstituteCst}
- />
- ) : null}
- {showDeleteCst ? (
- setShowDeleteCst(false)}
- onDelete={handleDeleteCst}
- selected={selected}
- />
- ) : null}
- {showEditTerm && activeCst ? (
- setShowEditTerm(false)}
- onSave={handleSaveWordforms}
- target={activeCst}
- />
- ) : null}
- {showTemplates ? (
- setShowTemplates(false)}
- insertAfter={insertCstID}
- onCreate={handleCreateCst}
- />
- ) : null}
- {showCreateVersion ? (
- setShowCreateVersion(false)}
- onCreate={handleCreateVersion}
- selected={selected}
- totalCount={model.schema.items.length}
- />
- ) : null}
- {showEditVersions ? (
- setShowEditVersions(false)}
- onDelete={handleDeleteVersion}
- onUpdate={handleUpdateVersion}
- />
- ) : null}
- {showEditEditors ? (
- setShowEditEditors(false)}
- editors={model.schema.editors}
- setEditors={setEditors}
- />
- ) : null}
- {showEditLocation ? (
- setShowEditLocation(false)}
- initial={model.schema.location}
- onChangeLocation={handleSetLocation}
- />
- ) : null}
-
- {showInlineSynthesis ? (
- setShowInlineSynthesis(false)}
- onInlineSynthesis={handleInlineSynthesis}
- />
- ) : null}
-
- {showTypeGraph ? setShowTypeGraph(false)} /> : null}
- >
- ) : null}
-
{children}
);
diff --git a/rsconcept/frontend/src/stores/dialogs.ts b/rsconcept/frontend/src/stores/dialogs.ts
new file mode 100644
index 00000000..c45af1db
--- /dev/null
+++ b/rsconcept/frontend/src/stores/dialogs.ts
@@ -0,0 +1,86 @@
+import { create } from 'zustand';
+
+import { DlgChangeInputSchemaProps } from '@/dialogs/DlgChangeInputSchema';
+import { DlgChangeLocationProps } from '@/dialogs/DlgChangeLocation';
+import { DlgCloneLibraryItemProps } from '@/dialogs/DlgCloneLibraryItem';
+import { DlgCreateCstProps } from '@/dialogs/DlgCreateCst/DlgCreateCst';
+import { DlgCreateOperationProps } from '@/dialogs/DlgCreateOperation/DlgCreateOperation';
+import { DlgCreateVersionProps } from '@/dialogs/DlgCreateVersion';
+import { DlgCstTemplateProps } from '@/dialogs/DlgCstTemplate/DlgCstTemplate';
+import { DlgDeleteCstProps } from '@/dialogs/DlgDeleteCst/DlgDeleteCst';
+import { DlgDeleteOperationProps } from '@/dialogs/DlgDeleteOperation';
+import { DlgEditEditorsProps } from '@/dialogs/DlgEditEditors/DlgEditEditors';
+import { DlgEditOperationProps } from '@/dialogs/DlgEditOperation/DlgEditOperation';
+import { DlgEditReferenceProps } from '@/dialogs/DlgEditReference/DlgEditReference';
+import { DlgEditVersionsProps } from '@/dialogs/DlgEditVersions/DlgEditVersions';
+import { DlgEditWordFormsProps } from '@/dialogs/DlgEditWordForms/DlgEditWordForms';
+import { DlgGraphParamsProps } from '@/dialogs/DlgGraphParams';
+import { DlgInlineSynthesisProps } from '@/dialogs/DlgInlineSynthesis/DlgInlineSynthesis';
+import { DlgRelocateConstituentsProps } from '@/dialogs/DlgRelocateConstituents';
+import { DlgRenameCstProps } from '@/dialogs/DlgRenameCst';
+import { DlgShowASTProps } from '@/dialogs/DlgShowAST/DlgShowAST';
+import { DlgShowQRProps } from '@/dialogs/DlgShowQR';
+import { DlgShowTypeGraphProps } from '@/dialogs/DlgShowTypeGraph/DlgShowTypeGraph';
+import { DlgSubstituteCstProps } from '@/dialogs/DlgSubstituteCst';
+import { DlgUploadRSFormProps } from '@/dialogs/DlgUploadRSForm';
+import { DialogType } from '@/models/miscellaneous';
+
+interface DialogsStore {
+ active: DialogType | undefined;
+ props: unknown;
+ hideDialog: () => void;
+
+ showCstTemplate: (props: DlgCstTemplateProps) => void;
+ showCreateCst: (props: DlgCreateCstProps) => void;
+ showCreateOperation: (props: DlgCreateOperationProps) => void;
+ showDeleteCst: (props: DlgDeleteCstProps) => void;
+ showEditEditors: (props: DlgEditEditorsProps) => void;
+ showEditOperation: (props: DlgEditOperationProps) => void;
+ showEditReference: (props: DlgEditReferenceProps) => void;
+ showEditVersions: (props: DlgEditVersionsProps) => void;
+ showEditWordForms: (props: DlgEditWordFormsProps) => void;
+ showInlineSynthesis: (props: DlgInlineSynthesisProps) => void;
+ showShowAST: (props: DlgShowASTProps) => void;
+ showShowTypeGraph: (props: DlgShowTypeGraphProps) => void;
+ showChangeInputSchema: (props: DlgChangeInputSchemaProps) => void;
+ showChangeLocation: (props: DlgChangeLocationProps) => void;
+ showCloneLibraryItem: (props: DlgCloneLibraryItemProps) => void;
+ showCreateVersion: (props: DlgCreateVersionProps) => void;
+ showDeleteOperation: (props: DlgDeleteOperationProps) => void;
+ showGraphParams: (props: DlgGraphParamsProps) => void;
+ showRelocateConstituents: (props: DlgRelocateConstituentsProps) => void;
+ showRenameCst: (props: DlgRenameCstProps) => void;
+ showQR: (props: DlgShowQRProps) => void;
+ showSubstituteCst: (props: DlgSubstituteCstProps) => void;
+ showUploadRSForm: (props: DlgUploadRSFormProps) => void;
+}
+
+export const useDialogsStore = create()(set => ({
+ active: undefined,
+ props: undefined,
+ hideDialog: () => set({ active: undefined, props: undefined }),
+
+ showCstTemplate: props => set({ active: DialogType.CONSTITUENTA_TEMPLATE, props: props }),
+ showCreateCst: props => set({ active: DialogType.CREATE_CONSTITUENTA, props: props }),
+ showCreateOperation: props => set({ active: DialogType.CREATE_OPERATION, props: props }),
+ showDeleteCst: props => set({ active: DialogType.DELETE_CONSTITUENTA, props: props }),
+ showEditEditors: props => set({ active: DialogType.EDIT_EDITORS, props: props }),
+ showEditOperation: props => set({ active: DialogType.EDIT_OPERATION, props: props }),
+ showEditReference: props => set({ active: DialogType.EDIT_REFERENCE, props: props }),
+ showEditVersions: props => set({ active: DialogType.EDIT_VERSIONS, props: props }),
+ showEditWordForms: props => set({ active: DialogType.EDIT_WORD_FORMS, props: props }),
+ showInlineSynthesis: props => set({ active: DialogType.INLINE_SYNTHESIS, props: props }),
+ showShowAST: props => set({ active: DialogType.SHOW_AST, props: props }),
+ showShowTypeGraph: props => set({ active: DialogType.SHOW_TYPE_GRAPH, props: props }),
+ showChangeInputSchema: props => set({ active: DialogType.CHANGE_INPUT_SCHEMA, props: props }),
+ showChangeLocation: props => set({ active: DialogType.CHANGE_LOCATION, props: props }),
+ showCloneLibraryItem: props => set({ active: DialogType.CLONE_LIBRARY_ITEM, props: props }),
+ showCreateVersion: props => set({ active: DialogType.CREATE_VERSION, props: props }),
+ showDeleteOperation: props => set({ active: DialogType.DELETE_OPERATION, props: props }),
+ showGraphParams: props => set({ active: DialogType.GRAPH_PARAMETERS, props: props }),
+ showRelocateConstituents: props => set({ active: DialogType.RELOCATE_CONSTITUENTS, props: props }),
+ showRenameCst: props => set({ active: DialogType.RENAME_CONSTITUENTA, props: props }),
+ showQR: props => set({ active: DialogType.SHOW_QR_CODE, props: props }),
+ showSubstituteCst: props => set({ active: DialogType.SUBSTITUTE_CONSTITUENTS, props: props }),
+ showUploadRSForm: props => set({ active: DialogType.UPLOAD_RSFORM, props: props })
+}));