diff --git a/.vscode/settings.json b/.vscode/settings.json index e4009981..238c3b3b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -133,6 +133,7 @@ "компаратив", "конституент", "Конституента", + "конституентами", "конституенту", "конституенты", "неинтерпретируемый", diff --git a/rsconcept/frontend/src/components/man/HelpTermGraph.tsx b/rsconcept/frontend/src/components/man/HelpTermGraph.tsx index bc66b064..34fc42f8 100644 --- a/rsconcept/frontend/src/components/man/HelpTermGraph.tsx +++ b/rsconcept/frontend/src/components/man/HelpTermGraph.tsx @@ -23,7 +23,7 @@ function HelpTermGraph() {

Клавиши

Клик на конституенту - выделение

-

Клик на выделенную - редактирование

+

Двойной клик - редактирование

Delete - удалить выбранные


diff --git a/rsconcept/frontend/src/components/ui/SelectorButton.tsx b/rsconcept/frontend/src/components/ui/SelectorButton.tsx index b5757b25..2d765ff6 100644 --- a/rsconcept/frontend/src/components/ui/SelectorButton.tsx +++ b/rsconcept/frontend/src/components/ui/SelectorButton.tsx @@ -45,7 +45,7 @@ function SelectorButton({ {...restProps} > {icon ? icon : null} - {text ?
{text}
: null} + {text ?
{text}
: null} ); } diff --git a/rsconcept/frontend/src/dialogs/DlgShowAST.tsx b/rsconcept/frontend/src/dialogs/DlgShowAST.tsx index c8113f3d..7721568e 100644 --- a/rsconcept/frontend/src/dialogs/DlgShowAST.tsx +++ b/rsconcept/frontend/src/dialogs/DlgShowAST.tsx @@ -69,6 +69,7 @@ function DlgShowAST({ hideWindow, syntaxTree, expression }: DlgShowASTProps) { }} > >; onOpenEdit: (cstID: ConstituentaID) => void; } -function EditorRSList({ selected, setSelected, onOpenEdit }: EditorRSListProps) { +function EditorRSList({ onOpenEdit }: EditorRSListProps) { const { calculateHeight } = useConceptOptions(); const [rowSelection, setRowSelection] = useState({}); const controller = useRSEdit(); useLayoutEffect(() => { - if (!controller.schema || selected.length === 0) { + if (!controller.schema || controller.selected.length === 0) { setRowSelection({}); } else { const newRowSelection: RowSelectionState = {}; controller.schema.items.forEach((cst, index) => { - newRowSelection[String(index)] = selected.includes(cst.id); + newRowSelection[String(index)] = controller.selected.includes(cst.id); }); setRowSelection(newRowSelection); } - }, [selected, controller.schema]); + }, [controller.selected, controller.schema]); function handleRowSelection(updater: React.SetStateAction) { if (!controller.schema) { - setSelected([]); + controller.deselectAll(); } else { const newRowSelection = typeof updater === 'function' ? updater(rowSelection) : updater; const newSelection: ConstituentaID[] = []; @@ -46,7 +44,7 @@ function EditorRSList({ selected, setSelected, onOpenEdit }: EditorRSListProps) newSelection.push(cst.id); } }); - setSelected(newSelection); + controller.setSelection(newSelection); } } @@ -54,7 +52,7 @@ function EditorRSList({ selected, setSelected, onOpenEdit }: EditorRSListProps) if (!controller.isContentEditable || controller.isProcessing) { return; } - if (event.key === 'Delete' && selected.length > 0) { + if (event.key === 'Delete' && controller.selected.length > 0) { event.preventDefault(); controller.deleteCst(); return; @@ -69,7 +67,7 @@ function EditorRSList({ selected, setSelected, onOpenEdit }: EditorRSListProps) } function processAltKey(code: string): boolean { - if (selected.length > 0) { + if (controller.selected.length > 0) { // prettier-ignore switch (code) { case 'ArrowUp': controller.moveUp(); return true; @@ -100,12 +98,12 @@ function EditorRSList({ selected, setSelected, onOpenEdit }: EditorRSListProps) {controller.isContentEditable ? ( ) : null} - {controller.isContentEditable ? : null} + {controller.isContentEditable ? : null}
selectedCount === 0, [selectedCount]); return ( } - disabled={controller.isProcessing || nothingSelected} + disabled={controller.isProcessing || controller.nothingSelected} onClick={controller.moveUp} /> } - disabled={controller.isProcessing || nothingSelected} + disabled={controller.isProcessing || controller.nothingSelected} onClick={controller.moveDown} /> } - disabled={controller.isProcessing || selectedCount !== 1} + disabled={controller.isProcessing || controller.selected.length !== 1} onClick={controller.cloneCst} /> } - disabled={controller.isProcessing || nothingSelected} + disabled={controller.isProcessing || controller.nothingSelected} onClick={controller.deleteCst} /> diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/EditorTermGraph.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/EditorTermGraph.tsx index dc56dd9e..1720a4f5 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/EditorTermGraph.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/EditorTermGraph.tsx @@ -17,6 +17,7 @@ import { colorBgGraphNode } from '@/styling/color'; import { storage, TIMEOUT_GRAPH_REFRESH } from '@/utils/constants'; import { useRSEdit } from '../RSEditContext'; +import GraphSelectors from './GraphSelectors'; import GraphSidebar from './GraphSidebar'; import GraphToolbar from './GraphToolbar'; import TermGraph from './TermGraph'; @@ -24,12 +25,10 @@ import useGraphFilter from './useGraphFilter'; import ViewHidden from './ViewHidden'; interface EditorTermGraphProps { - selected: ConstituentaID[]; - setSelected: React.Dispatch>; onOpenEdit: (cstID: ConstituentaID) => void; } -function EditorTermGraph({ selected, setSelected, onOpenEdit }: EditorTermGraphProps) { +function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) { const controller = useRSEdit(); const { colors } = useConceptOptions(); @@ -53,8 +52,6 @@ function EditorTermGraph({ selected, setSelected, onOpenEdit }: EditorTermGraphP const [hidden, setHidden] = useState([]); - const nothingSelected = useMemo(() => selected.length === 0, [selected]); - const [layout, setLayout] = useLocalStorage(storage.rsgraphLayout, 'treeTd2d'); const [coloringScheme, setColoringScheme] = useLocalStorage( storage.rsgraphColoringScheme, @@ -118,33 +115,18 @@ function EditorTermGraph({ selected, setSelected, onOpenEdit }: EditorTermGraphP return result; }, [filtered.nodes]); - const handleGraphSelection = useCallback( - (newID: ConstituentaID) => { - setSelected(prev => [...prev, newID]); - }, - [setSelected] - ); - - function toggleDismissed(cstID: ConstituentaID) { - setSelected(prev => { - if (prev.includes(cstID)) { - return [...prev.filter(id => id !== cstID)]; - } else { - return [...prev, cstID]; - } - }); - } - function handleCreateCst() { if (!controller.schema) { return; } - const definition = selected.map(id => controller.schema!.items.find(cst => cst.id === id)!.alias).join(' '); - controller.createCst(selected.length === 0 ? CstType.BASE : CstType.TERM, false, definition); + const definition = controller.selected + .map(id => controller.schema!.items.find(cst => cst.id === id)!.alias) + .join(' '); + controller.createCst(controller.nothingSelected ? CstType.BASE : CstType.TERM, false, definition); } function handleDeleteCst() { - if (!controller.schema || selected.length === 0) { + if (!controller.schema || controller.nothingSelected) { return; } controller.deleteCst(); @@ -178,6 +160,37 @@ function EditorTermGraph({ selected, setSelected, onOpenEdit }: EditorTermGraphP } } + const graph = useMemo( + () => ( + + ), + [ + edges, + nodes, + controller.selected, + layout, + is3D, + orbit, + setHoverID, + onOpenEdit, + toggleResetView, + controller.select, + controller.deselect + ] + ); + return (
@@ -193,12 +206,11 @@ function EditorTermGraph({ selected, setSelected, onOpenEdit }: EditorTermGraphP ) : null} - - - + +
+ + +
+
- setSelected([])} - toggleResetView={toggleResetView} - /> + {graph}
); } diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/GraphSelectors.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/GraphSelectors.tsx new file mode 100644 index 00000000..15e45f18 --- /dev/null +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/GraphSelectors.tsx @@ -0,0 +1,37 @@ +import { LayoutTypes } from 'reagraph'; + +import SelectSingle from '@/components/ui/SelectSingle'; +import { GraphColoringScheme } from '@/models/miscellaneous'; +import { mapLabelColoring, mapLabelLayout } from '@/utils/labels'; +import { SelectorGraphColoring, SelectorGraphLayout } from '@/utils/selectors'; + +interface GraphSelectorsProps { + coloring: GraphColoringScheme; + layout: LayoutTypes; + + setLayout: (newValue: LayoutTypes) => void; + setColoring: (newValue: GraphColoringScheme) => void; +} + +function GraphSelectors({ coloring, setColoring, layout, setLayout }: GraphSelectorsProps) { + return ( +
+ setColoring(data?.value ?? SelectorGraphColoring[0].value)} + /> + setLayout(data?.value ?? SelectorGraphLayout[0].value)} + /> +
+ ); +} + +export default GraphSelectors; diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/GraphSidebar.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/GraphSidebar.tsx index c10d9bc6..59d35bd1 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/GraphSidebar.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/GraphSidebar.tsx @@ -1,34 +1,44 @@ -import { LayoutTypes } from 'reagraph'; +import { BiGitBranch, BiGitMerge, BiReset } from 'react-icons/bi'; +import { LuExpand, LuMaximize, LuMinimize } from 'react-icons/lu'; -import SelectSingle from '@/components/ui/SelectSingle'; -import { GraphColoringScheme } from '@/models/miscellaneous'; -import { mapLabelColoring, mapLabelLayout } from '@/utils/labels'; -import { SelectorGraphColoring, SelectorGraphLayout } from '@/utils/selectors'; +import MiniButton from '@/components/ui/MiniButton'; -interface GraphSidebarProps { - coloring: GraphColoringScheme; - layout: LayoutTypes; +import { useRSEdit } from '../RSEditContext'; - setLayout: (newValue: LayoutTypes) => void; - setColoring: (newValue: GraphColoringScheme) => void; -} +function GraphSidebar() { + const controller = useRSEdit(); -function GraphSidebar({ coloring, setColoring, layout, setLayout }: GraphSidebarProps) { return ( -
- setColoring(data?.value ?? SelectorGraphColoring[0].value)} +
+ } + onClick={controller.deselectAll} /> - setLayout(data?.value ?? SelectorGraphLayout[0].value)} + } + disabled={controller.nothingSelected} + /> + } + disabled={controller.nothingSelected} + /> + } + disabled={controller.nothingSelected} + /> + } + disabled={controller.nothingSelected} + /> + } + disabled={controller.nothingSelected} />
); diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/GraphToolbar.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/GraphToolbar.tsx index 6daf73c3..dc99ea08 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/GraphToolbar.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/GraphToolbar.tsx @@ -1,6 +1,7 @@ 'use client'; -import { BiCollapse, BiFilterAlt, BiFont, BiFontFamily, BiPlanet, BiPlusCircle, BiTrash } from 'react-icons/bi'; +import { BiFilterAlt, BiFont, BiFontFamily, BiPlanet, BiPlusCircle, BiTrash } from 'react-icons/bi'; +import { LuImage } from 'react-icons/lu'; import BadgeHelp from '@/components/man/BadgeHelp'; import MiniButton from '@/components/ui/MiniButton'; @@ -10,7 +11,6 @@ import { HelpTopic } from '@/models/miscellaneous'; import { useRSEdit } from '../RSEditContext'; interface GraphToolbarProps { - nothingSelected: boolean; is3D: boolean; orbit: boolean; @@ -26,7 +26,6 @@ interface GraphToolbarProps { } function GraphToolbar({ - nothingSelected, is3D, noText, toggleNoText, @@ -40,7 +39,7 @@ function GraphToolbar({ const controller = useRSEdit(); return ( - + } @@ -58,7 +57,7 @@ function GraphToolbar({ onClick={toggleNoText} /> } + icon={} title='Восстановить камеру' onClick={onResetViewpoint} /> @@ -80,7 +79,7 @@ function GraphToolbar({ } - disabled={nothingSelected || controller.isProcessing} + disabled={controller.nothingSelected || controller.isProcessing} onClick={onDelete} /> ) : null} diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TermGraph.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TermGraph.tsx index d6f07e4d..769fa59f 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TermGraph.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TermGraph.tsx @@ -20,7 +20,7 @@ interface TermGraphProps { setHoverID: (newID: ConstituentaID | undefined) => void; onEdit: (cstID: ConstituentaID) => void; onSelect: (newID: ConstituentaID) => void; - onDeselectAll: () => void; + onDeselect: (newID: ConstituentaID) => void; toggleResetView: boolean; } @@ -38,54 +38,45 @@ function TermGraph({ setHoverID, onEdit, onSelect, - onDeselectAll + onDeselect }: TermGraphProps) { const { calculateHeight, darkMode } = useConceptOptions(); const graphRef = useRef(null); - const { selections, actives, setSelections, onCanvasClick, onNodePointerOver, onNodePointerOut } = useSelection({ + const { selections, setSelections } = useSelection({ ref: graphRef, nodes, edges, - type: 'multi', // 'single' | 'multi' | 'multiModifier' - pathSelectionType: 'out', - pathHoverType: 'all', - focusOnSelect: false + type: 'multi' }); const handleHoverIn = useCallback( (node: GraphNode) => { setHoverID(Number(node.id)); - if (onNodePointerOver) onNodePointerOver(node); }, - [onNodePointerOver, setHoverID] + [setHoverID] ); - const handleHoverOut = useCallback( - (node: GraphNode) => { - setHoverID(undefined); - if (onNodePointerOut) onNodePointerOut(node); - }, - [onNodePointerOut, setHoverID] - ); + const handleHoverOut = useCallback(() => { + setHoverID(undefined); + }, [setHoverID]); const handleNodeClick = useCallback( (node: GraphNode) => { if (selections.includes(node.id)) { - onEdit(Number(node.id)); + onDeselect(Number(node.id)); } else { onSelect(Number(node.id)); } }, - [onSelect, selections, onEdit] + [onSelect, selections, onDeselect] ); - const handleCanvasClick = useCallback( - (event: MouseEvent) => { - onDeselectAll(); - if (onCanvasClick) onCanvasClick(event); + const handleNodeDoubleClick = useCallback( + (node: GraphNode) => { + onEdit(Number(node.id)); }, - [onCanvasClick, onDeselectAll] + [onEdit] ); useLayoutEffect(() => { @@ -108,15 +99,15 @@ function TermGraph({
void; + select: (target: ConstituentaID) => void; + deselect: (target: ConstituentaID) => void; + toggleSelect: (target: ConstituentaID) => void; + deselectAll: () => void; viewVersion: (version?: number) => void; + createVersion: () => void; + editVersions: () => void; moveUp: () => void; moveDown: () => void; @@ -70,13 +81,11 @@ interface IRSEditContext { share: () => void; toggleSubscribe: () => void; download: () => void; + reindex: () => void; produceStructure: () => void; inlineSynthesis: () => void; substitute: () => void; - - createVersion: () => void; - editVersions: () => void; } const RSEditContext = createContext(null); @@ -119,6 +128,7 @@ export const RSEditState = ({ ); }, [user?.is_staff, mode, model.isOwned]); const isContentEditable = useMemo(() => isMutable && !model.isArchive, [isMutable, model.isArchive]); + const nothingSelected = useMemo(() => selected.length === 0, [selected]); const [showUpload, setShowUpload] = useState(false); const [showClone, setShowClone] = useState(false); @@ -464,12 +474,23 @@ export const RSEditState = ({ setSelected(selected), + select: (target: ConstituentaID) => setSelected(prev => [...prev, target]), + deselect: (target: ConstituentaID) => setSelected(prev => prev.filter(id => id !== target)), + toggleSelect: (target: ConstituentaID) => + setSelected(prev => (prev.includes(target) ? prev.filter(id => id !== target) : [...prev, target])), + deselectAll: () => setSelected([]), viewVersion, + createVersion: () => setShowCreateVersion(true), + editVersions: () => setShowEditVersions(true), moveUp, moveDown, @@ -486,13 +507,11 @@ export const RSEditState = ({ claim, share, toggleSubscribe, + reindex, inlineSynthesis: () => setShowInlineSynthesis(true), produceStructure, - substitute, - - createVersion: () => setShowCreateVersion(true), - editVersions: () => setShowEditVersions(true) + substitute }} > {model.schema ? ( diff --git a/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx b/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx index 9aa1b44e..88ac3c15 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx @@ -204,7 +204,7 @@ function RSTabs() { - + @@ -217,7 +217,7 @@ function RSTabs() { - + diff --git a/rsconcept/frontend/src/styling/color.ts b/rsconcept/frontend/src/styling/color.ts index 416541c1..e0ab4ffe 100644 --- a/rsconcept/frontend/src/styling/color.ts +++ b/rsconcept/frontend/src/styling/color.ts @@ -179,7 +179,7 @@ export const graphLightT = { activeFill: '#1DE9AC', opacity: 1, selectedOpacity: 1, - inactiveOpacity: 0.2, + inactiveOpacity: 0.5, label: { color: '#2A6475', stroke: '#fff', @@ -231,7 +231,7 @@ export const graphDarkT = { activeFill: '#1DE9AC', opacity: 1, selectedOpacity: 1, - inactiveOpacity: 0.2, + inactiveOpacity: 0.5, label: { stroke: '#1E2026', color: '#ACBAC7',