mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Implement editing capabilities for TermGraph
This commit is contained in:
parent
101631a8be
commit
e1601ab137
|
@ -76,10 +76,10 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
|||
const isEditable = useMemo(
|
||||
() => {
|
||||
return (
|
||||
!loading && !isReadonly &&
|
||||
!loading && !processing && !isReadonly &&
|
||||
((isOwned || (isForceAdmin && user?.is_staff)) ?? false)
|
||||
)
|
||||
}, [user?.is_staff, isReadonly, isForceAdmin, isOwned, loading])
|
||||
}, [user?.is_staff, isReadonly, isForceAdmin, isOwned, loading, processing])
|
||||
|
||||
const isTracking = useMemo(
|
||||
() => {
|
||||
|
|
|
@ -90,7 +90,7 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps)
|
|||
}
|
||||
|
||||
// Add new constituenta
|
||||
function handleCreateCst(type?: CstType){
|
||||
function handleCreateCst(type?: CstType) {
|
||||
if (!schema) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||
import { darkTheme, GraphCanvas, GraphCanvasRef, GraphEdge,
|
||||
GraphNode, LayoutTypes, lightTheme, Sphere, useSelection
|
||||
} from 'reagraph';
|
||||
|
@ -12,7 +12,7 @@ import MiniButton from '../../components/Common/MiniButton';
|
|||
import InfoConstituenta from '../../components/Help/InfoConstituenta';
|
||||
import InfoCstClass from '../../components/Help/InfoCstClass';
|
||||
import CstStatusInfo from '../../components/Help/InfoCstStatus';
|
||||
import { ArrowsRotateIcon, FilterCogIcon, HelpIcon } from '../../components/Icons';
|
||||
import { ArrowsRotateIcon, DumpBinIcon, FilterCogIcon, HelpIcon, SmallPlusIcon } from '../../components/Icons';
|
||||
import { useRSForm } from '../../context/RSFormContext';
|
||||
import { useConceptTheme } from '../../context/ThemeContext';
|
||||
import useLocalStorage from '../../hooks/useLocalStorage';
|
||||
|
@ -57,12 +57,12 @@ export interface GraphEditorParams {
|
|||
|
||||
interface EditorTermGraphProps {
|
||||
onOpenEdit: (cstID: number) => void
|
||||
// onCreateCst: (selectedID: number | undefined, type: CstType | undefined, skipDialog?: boolean) => void
|
||||
// onDeleteCst: (selected: number[], callback: (items: number[]) => void) => void
|
||||
onCreateCst: (selectedID: number | undefined, type: CstType | undefined, skipDialog?: boolean) => void
|
||||
onDeleteCst: (selected: number[], callback: (items: number[]) => void) => void
|
||||
}
|
||||
|
||||
function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
||||
const { schema } = useRSForm();
|
||||
function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGraphProps) {
|
||||
const { schema, isEditable } = useRSForm();
|
||||
const { darkMode, noNavigation } = useConceptTheme();
|
||||
|
||||
const [ layout, setLayout ] = useLocalStorage<LayoutTypes>('graph_layout', 'treeTd2d');
|
||||
|
@ -87,6 +87,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
|||
const [ selectedDismissed, setSelectedDismissed ] = useState<number[]>([]);
|
||||
const graphRef = useRef<GraphCanvasRef | null>(null);
|
||||
const [showOptions, setShowOptions] = useState(false);
|
||||
const [toggleUpdate, setToggleUpdate] = useState(false);
|
||||
|
||||
const [hoverID, setHoverID] = useState<string | undefined>(undefined);
|
||||
const hoverCst = useMemo(
|
||||
|
@ -109,7 +110,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
|||
return result;
|
||||
}, [allowBase, allowStruct, allowTerm, allowAxiom, allowFunction, allowPredicate, allowConstant, allowTheorem]);
|
||||
|
||||
useEffect(
|
||||
useLayoutEffect(
|
||||
() => {
|
||||
if (!schema) {
|
||||
setFiltered(new Graph());
|
||||
|
@ -146,7 +147,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
|||
setDismissed(newDismissed);
|
||||
setSelectedDismissed([]);
|
||||
setHoverID(undefined);
|
||||
}, [schema, noHermits, noTransitive, noTemplates, allowedTypes]);
|
||||
}, [schema, noHermits, noTransitive, noTemplates, allowedTypes, toggleUpdate]);
|
||||
|
||||
function toggleDismissed(cstID: number) {
|
||||
setSelectedDismissed(prev => {
|
||||
|
@ -199,6 +200,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
|||
const {
|
||||
selections, actives,
|
||||
onNodeClick,
|
||||
clearSelections,
|
||||
onCanvasClick,
|
||||
onNodePointerOver,
|
||||
onNodePointerOut
|
||||
|
@ -213,9 +215,10 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
|||
});
|
||||
|
||||
const allSelected: string[] = useMemo(
|
||||
() => {
|
||||
return [ ... selectedDismissed.map(id => String(id)), ... selections];
|
||||
}, [selectedDismissed, selections]);
|
||||
() => {
|
||||
return [ ... selectedDismissed.map(id => String(id)), ... selections];
|
||||
}, [selectedDismissed, selections]);
|
||||
const nothingSelected = useMemo(() => allSelected.length === 0, [allSelected]);
|
||||
|
||||
const handleRecreate = useCallback(
|
||||
() => {
|
||||
|
@ -250,6 +253,43 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
|||
if (onCanvasClick) onCanvasClick(event);
|
||||
}, [onCanvasClick]);
|
||||
|
||||
// Implement hotkeys for editing
|
||||
function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
|
||||
console.log(event);
|
||||
if (!isEditable) {
|
||||
return;
|
||||
}
|
||||
if (event.key === 'Delete' && allSelected.length > 0) {
|
||||
event.preventDefault();
|
||||
handleDeleteCst();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function handleCreateCst() {
|
||||
if (!schema) {
|
||||
return;
|
||||
}
|
||||
const selectedPosition = allSelected.reduce((prev, cstID) => {
|
||||
const position = schema.items.findIndex(cst => cst.id === Number(cstID));
|
||||
return Math.max(position, prev);
|
||||
}, -1);
|
||||
const insert_where = selectedPosition >= 0 ? schema.items[selectedPosition].id : undefined;
|
||||
onCreateCst(insert_where, undefined);
|
||||
}
|
||||
|
||||
function handleDeleteCst() {
|
||||
if (!schema) {
|
||||
return;
|
||||
}
|
||||
onDeleteCst([... allSelected.map(id => Number(id))], () => {
|
||||
clearSelections();
|
||||
setDismissed([]);
|
||||
setSelectedDismissed([]);
|
||||
setToggleUpdate(prev => !prev);
|
||||
});
|
||||
}
|
||||
|
||||
function getOptions() {
|
||||
return {
|
||||
noHermits: noHermits,
|
||||
|
@ -305,7 +345,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
|||
}, [selectedDismissed]);
|
||||
|
||||
return (
|
||||
<div className='flex justify-between w-full'>
|
||||
<div className='flex justify-between w-full' tabIndex={0} onKeyDown={handleKeyDown}>
|
||||
{showOptions &&
|
||||
<DlgGraphOptions
|
||||
hideWindow={() => setShowOptions(false)}
|
||||
|
@ -321,11 +361,27 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
|||
/>
|
||||
</div>}
|
||||
|
||||
<div className='mr-3 whitespace-nowrap'>
|
||||
Выбраны
|
||||
<span className='ml-2'>
|
||||
<b>{allSelected.length}</b> из {schema?.stats?.count_all ?? 0}
|
||||
</span>
|
||||
<div className='flex items-center justify-between'>
|
||||
<div className='mr-3 whitespace-nowrap'>
|
||||
Выбраны
|
||||
<span className='ml-1'>
|
||||
<b>{allSelected.length}</b> из {schema?.stats?.count_all ?? 0}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<MiniButton
|
||||
tooltip='Удалить выбранные'
|
||||
icon={<DumpBinIcon color={!nothingSelected ? 'text-red' : ''} size={5}/>}
|
||||
disabled={!isEditable || nothingSelected}
|
||||
onClick={handleDeleteCst}
|
||||
/>
|
||||
<MiniButton
|
||||
tooltip='Новая конституента'
|
||||
icon={<SmallPlusIcon color='text-green' size={5}/>}
|
||||
disabled={!isEditable}
|
||||
onClick={handleCreateCst}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Divider margins='mt-3 mb-2' />
|
||||
|
@ -401,7 +457,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex-wrap w-full h-full overflow-auto'>
|
||||
<div className='w-full h-full overflow-auto'>
|
||||
<div
|
||||
className='relative border-t border-r'
|
||||
style={{width: canvasWidth, height: canvasHeight, borderBottomWidth: noNavigation ? '1px': ''}}
|
||||
|
|
|
@ -266,6 +266,8 @@ function RSTabs() {
|
|||
<TabPanel>
|
||||
<EditorTermGraph
|
||||
onOpenEdit={onOpenCst}
|
||||
onCreateCst={promptCreateCst}
|
||||
onDeleteCst={promptDeleteCst}
|
||||
/>
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
|
Loading…
Reference in New Issue
Block a user