Refactor dialogs

This commit is contained in:
IRBorisov 2023-07-29 15:37:49 +03:00
parent 4f45b9978e
commit 44cc9371e4
3 changed files with 93 additions and 72 deletions

View File

@ -7,25 +7,24 @@ import SubmitButton from '../../components/Common/SubmitButton';
import TextArea from '../../components/Common/TextArea'; import TextArea from '../../components/Common/TextArea';
import { DumpBinIcon, SaveIcon, SmallPlusIcon } from '../../components/Icons'; import { DumpBinIcon, SaveIcon, SmallPlusIcon } from '../../components/Icons';
import { useRSForm } from '../../context/RSFormContext'; import { useRSForm } from '../../context/RSFormContext';
import { type CstType, EditMode, type ICstCreateData, ICstUpdateData, SyntaxTree } from '../../utils/models'; import { type CstType, EditMode, ICstUpdateData, SyntaxTree } from '../../utils/models';
import { createAliasFor, getCstTypeLabel } from '../../utils/staticUI'; import { getCstTypeLabel } from '../../utils/staticUI';
import DlgCreateCst from './DlgCreateCst';
import EditorRSExpression from './EditorRSExpression'; import EditorRSExpression from './EditorRSExpression';
import ViewSideConstituents from './elements/ViewSideConstituents'; import ViewSideConstituents from './elements/ViewSideConstituents';
import { RSTabsList } from './RSTabs'; import { RSTabsList } from './RSTabs';
interface EditorConstituentaProps { interface EditorConstituentaProps {
onShowAST: (ast: SyntaxTree) => void onShowAST: (ast: SyntaxTree) => void
onShowCreateCst: (position: number | undefined, type: CstType | undefined) => void
} }
function EditorConstituenta({onShowAST}: EditorConstituentaProps) { function EditorConstituenta({ onShowAST, onShowCreateCst }: EditorConstituentaProps) {
const navigate = useNavigate(); const navigate = useNavigate();
const { const {
activeCst, activeID, schema, setActiveID, processing, isEditable, activeCst, activeID, schema, setActiveID, processing, isEditable,
cstDelete, cstUpdate, cstCreate cstDelete, cstUpdate
} = useRSForm(); } = useRSForm();
const [showCstModal, setShowCstModal] = useState(false);
const [isModified, setIsModified] = useState(false); const [isModified, setIsModified] = useState(false);
const [editMode, setEditMode] = useState(EditMode.TEXT); const [editMode, setEditMode] = useState(EditMode.TEXT);
@ -107,24 +106,12 @@ function EditorConstituenta({onShowAST}: EditorConstituentaProps) {
}, [activeID, schema, cstDelete, navigate]); }, [activeID, schema, cstDelete, navigate]);
const handleAddNew = useCallback( const handleAddNew = useCallback(
(type?: CstType) => { () => {
if (!activeID || !schema?.items) { if (!activeID || !schema) {
return; return;
} }
if (!type) { onShowCreateCst(activeID, activeCst?.cstType);
setShowCstModal(true); }, [activeID, activeCst?.cstType, schema, onShowCreateCst]);
} else {
const data: ICstCreateData = {
cst_type: type,
alias: createAliasFor(type, schema),
insert_after: activeID
}
cstCreate(data, newCst => {
navigate(`/rsforms/${schema.id}?tab=${RSTabsList.CST_EDIT}&active=${newCst.id}`);
toast.success(`Конституента добавлена: ${newCst.alias}`);
});
}
}, [activeID, schema, cstCreate, navigate]);
const handleRename = useCallback(() => { const handleRename = useCallback(() => {
toast.info('Переименование в разработке'); toast.info('Переименование в разработке');
@ -136,11 +123,6 @@ function EditorConstituenta({onShowAST}: EditorConstituentaProps) {
return ( return (
<div className='flex items-start w-full gap-2'> <div className='flex items-start w-full gap-2'>
{showCstModal && <DlgCreateCst
hideWindow={() => { setShowCstModal(false); }}
onCreate={handleAddNew}
defaultType={activeCst?.cstType as CstType}
/>}
<form onSubmit={handleSubmit} className='flex-grow min-w-[50rem] max-w-min px-4 py-2 border'> <form onSubmit={handleSubmit} className='flex-grow min-w-[50rem] max-w-min px-4 py-2 border'>
<div className='flex items-start justify-between'> <div className='flex items-start justify-between'>
<button type='submit' <button type='submit'
@ -176,7 +158,7 @@ function EditorConstituenta({onShowAST}: EditorConstituentaProps) {
<MiniButton <MiniButton
tooltip='Создать конституенты после данной' tooltip='Создать конституенты после данной'
disabled={!isEnabled} disabled={!isEnabled}
onClick={() => { handleAddNew(); }} onClick={handleAddNew}
icon={<SmallPlusIcon size={5} color={isEnabled ? 'text-green' : ''} />} icon={<SmallPlusIcon size={5} color={isEnabled ? 'text-green' : ''} />}
/> />
<MiniButton <MiniButton

View File

@ -7,24 +7,23 @@ import Divider from '../../components/Common/Divider';
import { ArrowDownIcon, ArrowsRotateIcon, ArrowUpIcon, DumpBinIcon, SmallPlusIcon } from '../../components/Icons'; import { ArrowDownIcon, ArrowsRotateIcon, ArrowUpIcon, DumpBinIcon, SmallPlusIcon } from '../../components/Icons';
import { useRSForm } from '../../context/RSFormContext'; import { useRSForm } from '../../context/RSFormContext';
import { useConceptTheme } from '../../context/ThemeContext'; import { useConceptTheme } from '../../context/ThemeContext';
import { CstType, type IConstituenta, type ICstCreateData, ICstMovetoData,inferStatus, ParsingStatus, ValueClass } from '../../utils/models' import { CstType, type IConstituenta, ICstMovetoData, inferStatus, ParsingStatus, ValueClass } from '../../utils/models'
import { createAliasFor, getCstTypePrefix, getCstTypeShortcut, getStatusInfo, getTypeLabel } from '../../utils/staticUI'; import { getCstTypePrefix, getCstTypeShortcut, getStatusInfo, getTypeLabel } from '../../utils/staticUI';
import DlgCreateCst from './DlgCreateCst';
interface EditorItemsProps { interface EditorItemsProps {
onOpenEdit: (cst: IConstituenta) => void onOpenEdit: (cst: IConstituenta) => void
onShowCreateCst: (position: number | undefined, type: CstType | undefined, skipDialog?: boolean) => void
} }
function EditorItems({ onOpenEdit }: EditorItemsProps) { function EditorItems({ onOpenEdit, onShowCreateCst }: EditorItemsProps) {
const { const {
schema, isEditable, schema, isEditable,
cstCreate, cstDelete, cstMoveTo, resetAliases cstDelete, cstMoveTo, resetAliases
} = useRSForm(); } = useRSForm();
const { noNavigation } = useConceptTheme(); const { noNavigation } = useConceptTheme();
const [selected, setSelected] = useState<number[]>([]); const [selected, setSelected] = useState<number[]>([]);
const nothingSelected = useMemo(() => selected.length === 0, [selected]); const nothingSelected = useMemo(() => selected.length === 0, [selected]);
const [showCstModal, setShowCstModal] = useState(false);
const [toggledClearRows, setToggledClearRows] = useState(false); const [toggledClearRows, setToggledClearRows] = useState(false);
const handleRowClicked = useCallback( const handleRowClicked = useCallback(
@ -112,25 +111,18 @@ function EditorItems({ onOpenEdit }: EditorItemsProps) {
}, [resetAliases]); }, [resetAliases]);
// Add new constituent // Add new constituent
const handleAddNew = useCallback((type?: CstType) => { const handleAddNew = useCallback(
(type?: CstType) => {
if (!schema) { if (!schema) {
return; return;
} }
if (!type) {
setShowCstModal(true);
} else {
const selectedPosition = selected.reduce((prev, cstID) => { const selectedPosition = selected.reduce((prev, cstID) => {
const position = schema.items.findIndex(cst => cst.id === cstID); const position = schema.items.findIndex(cst => cst.id === cstID);
return Math.max(position, prev); return Math.max(position, prev);
}, -1) + 1; }, -1) + 1;
const data: ICstCreateData = { const insert_where = selectedPosition > 0 ? selectedPosition : undefined;
cst_type: type, onShowCreateCst(insert_where, type, type !== undefined);
alias: createAliasFor(type, schema), }, [schema, onShowCreateCst, selected]);
insert_after: selectedPosition > 0 ? selectedPosition : null
}
cstCreate(data, new_cst => toast.success(`Добавлена конституента ${new_cst.alias}`));
}
}, [schema, selected, cstCreate]);
// Implement hotkeys for working with constituents table // Implement hotkeys for working with constituents table
function handleTableKey(event: React.KeyboardEvent<HTMLDivElement>) { function handleTableKey(event: React.KeyboardEvent<HTMLDivElement>) {
@ -283,11 +275,7 @@ function EditorItems({ onOpenEdit }: EditorItemsProps) {
], [] ], []
); );
return (<> return (
{showCstModal && <DlgCreateCst
hideWindow={() => { setShowCstModal(false); }}
onCreate={handleAddNew}
/>}
<div className='w-full'> <div className='w-full'>
<div <div
className={'flex justify-start w-full gap-1 px-2 py-1 border-y items-center h-[2.2rem] clr-app' + className={'flex justify-start w-full gap-1 px-2 py-1 border-y items-center h-[2.2rem] clr-app' +
@ -376,7 +364,7 @@ function EditorItems({ onOpenEdit }: EditorItemsProps) {
/> />
</div> </div>
</div> </div>
</>); );
} }
export default EditorItems; export default EditorItems;

View File

@ -1,13 +1,17 @@
import { useCallback, useEffect, useLayoutEffect, useState } from 'react'; import { useCallback, useEffect, useLayoutEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { TabList, TabPanel, Tabs } from 'react-tabs'; import { TabList, TabPanel, Tabs } from 'react-tabs';
import { toast } from 'react-toastify';
import BackendError from '../../components/BackendError'; import BackendError from '../../components/BackendError';
import ConceptTab from '../../components/Common/ConceptTab'; import ConceptTab from '../../components/Common/ConceptTab';
import { Loader } from '../../components/Common/Loader'; import { Loader } from '../../components/Common/Loader';
import { useRSForm } from '../../context/RSFormContext'; import { useRSForm } from '../../context/RSFormContext';
import useLocalStorage from '../../hooks/useLocalStorage'; import useLocalStorage from '../../hooks/useLocalStorage';
import { type IConstituenta,SyntaxTree } from '../../utils/models'; import { CstType,type IConstituenta, ICstCreateData, SyntaxTree } from '../../utils/models';
import { createAliasFor } from '../../utils/staticUI';
import DlgCloneRSForm from './DlgCloneRSForm'; import DlgCloneRSForm from './DlgCloneRSForm';
import DlgCreateCst from './DlgCreateCst';
import DlgShowAST from './DlgShowAST'; import DlgShowAST from './DlgShowAST';
import DlgUploadRSForm from './DlgUploadRSForm'; import DlgUploadRSForm from './DlgUploadRSForm';
import EditorConstituenta from './EditorConstituenta'; import EditorConstituenta from './EditorConstituenta';
@ -23,28 +27,64 @@ export enum RSTabsList {
} }
function RSTabs() { function RSTabs() {
const { setActiveID, activeID, error, schema, loading } = useRSForm(); const navigate = useNavigate();
const [tabIndex, setTabIndex] = useLocalStorage('rsform_edit_tab', RSTabsList.CARD); const { setActiveID, activeID, error, schema, loading, cstCreate } = useRSForm();
const [activeTab, setActiveTab] = useLocalStorage('rsform_edit_tab', RSTabsList.CARD);
const [init, setInit] = useState(false); const [init, setInit] = useState(false);
const [showUpload, setShowUpload] = useState(false); const [showUpload, setShowUpload] = useState(false);
const [showClone, setShowClone] = useState(false); const [showClone, setShowClone] = useState(false);
const [syntaxTree, setSyntaxTree] = useState<SyntaxTree>([]); const [syntaxTree, setSyntaxTree] = useState<SyntaxTree>([]);
const [showAST, setShowAST] = useState(false); const [showAST, setShowAST] = useState(false);
const [defaultType, setDefaultType] = useState<CstType | undefined>(undefined);
const [insertPosition, setInsertPosition] = useState<number | undefined>(undefined);
const [showCreateCst, setShowCreateCst] = useState(false);
const handleAddNew = useCallback(
(type: CstType) => {
if (!schema?.items) {
return;
}
const data: ICstCreateData = {
cst_type: type,
alias: createAliasFor(type, schema),
insert_after: insertPosition ?? null
}
cstCreate(data, newCst => {
toast.success(`Конституента добавлена: ${newCst.alias}`);
if (activeTab === RSTabsList.CST_EDIT) {
navigate(`/rsforms/${schema.id}?tab=${RSTabsList.CST_EDIT}&active=${newCst.id}`);
}
});
}, [schema, cstCreate, insertPosition, navigate, activeTab]);
const onShowCreateCst = useCallback(
(position: number | undefined, type: CstType | undefined, skipDialog?: boolean) => {
if (skipDialog && type) {
handleAddNew(type);
} else {
setDefaultType(type);
setInsertPosition(position);
setShowCreateCst(true);
}
}, [handleAddNew]);
const onShowAST = useCallback( const onShowAST = useCallback(
(ast: SyntaxTree) => { (ast: SyntaxTree) => {
setSyntaxTree(ast); setSyntaxTree(ast);
setShowAST(true); setShowAST(true);
}, []) }, []);
const onEditCst = (cst: IConstituenta) => { const onEditCst = (cst: IConstituenta) => {
setActiveID(cst.id); setActiveID(cst.id);
setTabIndex(RSTabsList.CST_EDIT) setActiveTab(RSTabsList.CST_EDIT)
}; };
const onSelectTab = (index: number) => { const onSelectTab = (index: number) => {
setTabIndex(index); setActiveTab(index);
}; };
useLayoutEffect(() => { useLayoutEffect(() => {
@ -66,17 +106,17 @@ function RSTabs() {
useEffect(() => { useEffect(() => {
const url = new URL(window.location.href); const url = new URL(window.location.href);
const tabQuery = url.searchParams.get('tab'); const tabQuery = url.searchParams.get('tab');
setTabIndex(Number(tabQuery) || RSTabsList.CARD); setActiveTab(Number(tabQuery) || RSTabsList.CARD);
}, [setTabIndex]); }, [setActiveTab]);
useEffect(() => { useEffect(() => {
if (init) { if (init) {
const url = new URL(window.location.href); const url = new URL(window.location.href);
const currentActive = url.searchParams.get('active'); const currentActive = url.searchParams.get('active');
const currentTab = url.searchParams.get('tab'); const currentTab = url.searchParams.get('tab');
const saveHistory = tabIndex === RSTabsList.CST_EDIT && currentActive !== String(activeID); const saveHistory = activeTab === RSTabsList.CST_EDIT && currentActive !== String(activeID);
if (currentTab !== String(tabIndex)) { if (currentTab !== String(activeTab)) {
url.searchParams.set('tab', String(tabIndex)); url.searchParams.set('tab', String(activeTab));
} }
if (activeID) { if (activeID) {
if (currentActive !== String(activeID)) { if (currentActive !== String(activeID)) {
@ -91,7 +131,7 @@ function RSTabs() {
window.history.replaceState(null, '', url.toString()); window.history.replaceState(null, '', url.toString());
} }
} }
}, [tabIndex, activeID, init]); }, [activeTab, activeID, init]);
return ( return (
<div className='w-full'> <div className='w-full'>
@ -101,9 +141,19 @@ function RSTabs() {
<> <>
{showUpload && <DlgUploadRSForm hideWindow={() => { setShowUpload(false); }}/>} {showUpload && <DlgUploadRSForm hideWindow={() => { setShowUpload(false); }}/>}
{showClone && <DlgCloneRSForm hideWindow={() => { setShowClone(false); }}/>} {showClone && <DlgCloneRSForm hideWindow={() => { setShowClone(false); }}/>}
{showAST && <DlgShowAST syntaxTree={syntaxTree} hideWindow={() => { setShowAST(false); }}/>} {showAST &&
<DlgShowAST
syntaxTree={syntaxTree}
hideWindow={() => { setShowAST(false); }}
/>}
{showCreateCst &&
<DlgCreateCst
hideWindow={() => { setShowCreateCst(false); }}
onCreate={handleAddNew}
defaultType={defaultType}
/>}
<Tabs <Tabs
selectedIndex={tabIndex} selectedIndex={activeTab}
onSelect={onSelectTab} onSelect={onSelectTab}
defaultFocus={true} defaultFocus={true}
selectedTabClassName='font-bold' selectedTabClassName='font-bold'
@ -127,13 +177,14 @@ function RSTabs() {
</TabPanel> </TabPanel>
<TabPanel className='w-full'> <TabPanel className='w-full'>
<EditorItems onOpenEdit={onEditCst} /> <EditorItems onOpenEdit={onEditCst} onShowCreateCst={onShowCreateCst} />
</TabPanel> </TabPanel>
<TabPanel> <TabPanel>
<EditorConstituenta onShowAST={onShowAST} /> <EditorConstituenta onShowAST={onShowAST} onShowCreateCst={onShowCreateCst} />
</TabPanel> </TabPanel>
</Tabs></> </Tabs>
</>
} }
</div>); </div>);
} }