From 64ebce308265140d3c199c5801cac9fd63036058 Mon Sep 17 00:00:00 2001 From: IRBorisov <8611739+IRBorisov@users.noreply.github.com> Date: Thu, 4 Apr 2024 20:03:41 +0300 Subject: [PATCH] Improve graph UI --- rsconcept/frontend/package-lock.json | 28 +++------ rsconcept/frontend/package.json | 2 +- rsconcept/frontend/src/components/Icons.tsx | 4 +- .../select/ConstituentaMultiPicker.tsx | 45 +++----------- .../components/select/SelectGraphToolbar.tsx | 58 +++++++++++++++++++ .../DlgInlineSynthesis/ConstituentsTab.tsx | 2 +- .../RSFormPage/EditorRSList/EditorRSList.tsx | 2 +- .../EditorTermGraph/GraphToolbar.tsx | 45 +------------- .../src/pages/RSFormPage/RSEditContext.tsx | 4 +- .../ViewConstituents/ConstituentsSearch.tsx | 36 ++++++++++-- rsconcept/frontend/src/utils/labels.ts | 10 ++-- 11 files changed, 120 insertions(+), 116 deletions(-) create mode 100644 rsconcept/frontend/src/components/select/SelectGraphToolbar.tsx diff --git a/rsconcept/frontend/package-lock.json b/rsconcept/frontend/package-lock.json index d469fc0f..cfe07315 100644 --- a/rsconcept/frontend/package-lock.json +++ b/rsconcept/frontend/package-lock.json @@ -28,7 +28,7 @@ "react-tabs": "^6.0.2", "react-toastify": "^9.1.3", "react-tooltip": "^5.26.3", - "reagraph": "^4.15.26" + "reagraph": "^4.15.27" }, "devDependencies": { "@lezer/generator": "^1.7.0", @@ -8301,25 +8301,13 @@ "node": ">=8" } }, - "node_modules/path2d": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/path2d/-/path2d-0.1.1.tgz", - "integrity": "sha512-/+S03c8AGsDYKKBtRDqieTJv2GlkMb0bWjnqOgtF6MkjdUQ9a8ARAtxWf9NgKLGm2+WQr6+/tqJdU8HNGsIDoA==", - "optional": true, - "engines": { - "node": ">=6" - } - }, "node_modules/path2d-polyfill": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/path2d-polyfill/-/path2d-polyfill-2.1.1.tgz", - "integrity": "sha512-4Rka5lN+rY/p0CdD8+E+BFv51lFaFvJOrlOhyQ+zjzyQrzyh3ozmxd1vVGGDdIbUFSBtIZLSnspxTgPT0iJhvA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path2d-polyfill/-/path2d-polyfill-2.0.1.tgz", + "integrity": "sha512-ad/3bsalbbWhmBo0D6FZ4RNMwsLsPpL6gnvhuSaU5Vm7b06Kr5ubSltQQ0T7YKsiJQO+g22zJ4dJKNTXIyOXtA==", "optional": true, - "dependencies": { - "path2d": "0.1.1" - }, "engines": { - "node": ">=18" + "node": ">=8" } }, "node_modules/pdfjs-dist": { @@ -9045,9 +9033,9 @@ } }, "node_modules/reagraph": { - "version": "4.15.26", - "resolved": "https://registry.npmjs.org/reagraph/-/reagraph-4.15.26.tgz", - "integrity": "sha512-s8xYL9frjoQA1BmPS9tOshR8My9qDSRAiU79YfYO+grleBvhsbXMh7Evlj1ACYh8OR6fLNCdsuK1Micz6fZ7zQ==", + "version": "4.15.27", + "resolved": "https://registry.npmjs.org/reagraph/-/reagraph-4.15.27.tgz", + "integrity": "sha512-BoYWSFdbxeLkEL4lAM9Y/ey0L+liY1is/3+dxZ4pvhlVj4f9RIWZh1IqILY+7t2mRYts5RInsgz0ZAV4t4tIJw==", "dependencies": { "@react-spring/three": "9.6.1", "@react-three/fiber": "8.13.5", diff --git a/rsconcept/frontend/package.json b/rsconcept/frontend/package.json index b8337897..b73c6924 100644 --- a/rsconcept/frontend/package.json +++ b/rsconcept/frontend/package.json @@ -32,7 +32,7 @@ "react-tabs": "^6.0.2", "react-toastify": "^9.1.3", "react-tooltip": "^5.26.3", - "reagraph": "^4.15.26" + "reagraph": "^4.15.27" }, "devDependencies": { "@lezer/generator": "^1.7.0", diff --git a/rsconcept/frontend/src/components/Icons.tsx b/rsconcept/frontend/src/components/Icons.tsx index c2e8c97a..2db5ad7e 100644 --- a/rsconcept/frontend/src/components/Icons.tsx +++ b/rsconcept/frontend/src/components/Icons.tsx @@ -27,9 +27,9 @@ export { BiFontFamily as IconText } from 'react-icons/bi'; export { BiFont as IconTextOff } from 'react-icons/bi'; export { RiTreeLine as IconTree } from 'react-icons/ri'; -export { LuMinimize as IconGraphClosure } from 'react-icons/lu'; +export { BiCollapse as IconGraphCollapse } from 'react-icons/bi'; +export { BiExpand as IconGraphExpand } from 'react-icons/bi'; export { LuMaximize as IconGraphMaximize } from 'react-icons/lu'; -export { LuExpand as IconGraphExpand } from 'react-icons/lu'; export { BiGitBranch as IconGraphInputs } from 'react-icons/bi'; export { BiGitMerge as IconGraphOutputs } from 'react-icons/bi'; diff --git a/rsconcept/frontend/src/components/select/ConstituentaMultiPicker.tsx b/rsconcept/frontend/src/components/select/ConstituentaMultiPicker.tsx index 68ba4438..ecc1ff3d 100644 --- a/rsconcept/frontend/src/components/select/ConstituentaMultiPicker.tsx +++ b/rsconcept/frontend/src/components/select/ConstituentaMultiPicker.tsx @@ -1,7 +1,7 @@ 'use client'; import clsx from 'clsx'; -import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; +import { useLayoutEffect, useMemo, useState } from 'react'; import DataTable, { createColumnHelper, RowSelectionState } from '@/components/ui/DataTable'; import { useConceptOptions } from '@/context/OptionsContext'; @@ -9,8 +9,8 @@ import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform'; import { describeConstituenta } from '@/utils/labels'; import ConstituentaBadge from '../info/ConstituentaBadge'; -import Button from '../ui/Button'; import FlexColumn from '../ui/FlexColumn'; +import SelectGraphToolbar from './SelectGraphToolbar'; interface ConstituentaMultiPickerProps { id?: string; @@ -19,7 +19,7 @@ interface ConstituentaMultiPickerProps { rows?: number; selected: ConstituentaID[]; - setSelected: React.Dispatch; + setSelected: React.Dispatch>; } const columnHelper = createColumnHelper(); @@ -55,26 +55,6 @@ function ConstituentaMultiPicker({ id, schema, prefixID, rows, selected, setSele } } - const selectBasis = useCallback(() => { - if (!schema || selected.length === 0) { - return; - } - const addition = schema.graph.expandAllInputs(selected).filter(id => !selected.includes(id)); - if (addition.length > 0) { - setSelected([...selected, ...addition]); - } - }, [schema, selected, setSelected]); - - const selectDependant = useCallback(() => { - if (!schema || selected.length === 0) { - return; - } - const addition = schema.graph.expandAllOutputs(selected).filter(id => !selected.includes(id)); - if (addition.length > 0) { - setSelected([...selected, ...addition]); - } - }, [schema, selected, setSelected]); - const columns = useMemo( () => [ columnHelper.accessor('alias', { @@ -98,20 +78,13 @@ function ConstituentaMultiPicker({ id, schema, prefixID, rows, selected, setSele Выбраны {selected.length} из {schema?.items.length ?? 0} -
-
+ ) : null} >; +} + +function SelectGraphToolbar({ className, graph, setSelected, ...restProps }: SelectGraphToolbarProps) { + return ( +
+ } + onClick={() => setSelected([])} + /> + } + onClick={() => setSelected(prev => [...prev, ...graph.expandAllInputs(prev)])} + /> + } + onClick={() => setSelected(prev => [...prev, ...graph.expandAllOutputs(prev)])} + /> + } + onClick={() => setSelected(prev => graph.maximizePart(prev))} + /> + } + onClick={() => setSelected(prev => [...prev, ...graph.expandInputs(prev)])} + /> + } + onClick={() => setSelected(prev => [...prev, ...graph.expandOutputs(prev)])} + /> +
+ ); +} + +export default SelectGraphToolbar; diff --git a/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/ConstituentsTab.tsx b/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/ConstituentsTab.tsx index 277263ab..b71afb4b 100644 --- a/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/ConstituentsTab.tsx +++ b/rsconcept/frontend/src/dialogs/DlgInlineSynthesis/ConstituentsTab.tsx @@ -11,7 +11,7 @@ interface ConstituentsTabProps { loading?: boolean; error?: ErrorData; selected: ConstituentaID[]; - setSelected: React.Dispatch; + setSelected: React.Dispatch>; } function ConstituentsTab({ schema, error, loading, selected, setSelected }: ConstituentsTabProps) { diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/EditorRSList.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/EditorRSList.tsx index 5449abba..cc276841 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/EditorRSList.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/EditorRSList.tsx @@ -44,7 +44,7 @@ function EditorRSList({ onOpenEdit }: EditorRSListProps) { newSelection.push(cst.id); } }); - controller.setSelection(newSelection); + controller.setSelected(newSelection); } } diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/GraphToolbar.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/GraphToolbar.tsx index 8610bcb5..f285a938 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/GraphToolbar.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/GraphToolbar.tsx @@ -4,18 +4,13 @@ import { IconDestroy, IconFilter, IconFitImage, - IconGraphClosure, - IconGraphExpand, - IconGraphInputs, - IconGraphMaximize, - IconGraphOutputs, IconNewItem, - IconReset, IconRotate3D, IconText, IconTextOff } from '@/components/Icons'; import BadgeHelp from '@/components/man/BadgeHelp'; +import SelectGraphToolbar from '@/components/select/SelectGraphToolbar'; import MiniButton from '@/components/ui/MiniButton'; import Overlay from '@/components/ui/Overlay'; import { HelpTopic } from '@/models/miscellaneous'; @@ -101,43 +96,7 @@ function GraphToolbar({ ) : null} -
- } - onClick={controller.deselectAll} - /> - } - disabled={controller.nothingSelected} - onClick={controller.selectAllInputs} - /> - } - disabled={controller.nothingSelected} - onClick={controller.selectMax} - /> - } - disabled={controller.nothingSelected} - onClick={controller.selectAllOutputs} - /> - } - disabled={controller.nothingSelected} - onClick={controller.selectInputs} - /> - } - disabled={controller.nothingSelected} - onClick={controller.selectOutputs} - /> -
+ ); } diff --git a/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx b/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx index 9db82ea3..f940f364 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/RSEditContext.tsx @@ -56,7 +56,7 @@ interface IRSEditContext { canProduceStructure: boolean; nothingSelected: boolean; - setSelection: (selected: ConstituentaID[]) => void; + setSelected: React.Dispatch>; select: (target: ConstituentaID) => void; selectAllInputs: () => void; selectAllOutputs: () => void; @@ -486,7 +486,7 @@ export const RSEditState = ({ canProduceStructure, nothingSelected, - setSelection: (selected: ConstituentaID[]) => setSelected(selected), + setSelected: setSelected, select: (target: ConstituentaID) => setSelected(prev => [...prev, target]), deselect: (target: ConstituentaID) => setSelected(prev => prev.filter(id => id !== target)), selectAllInputs: () => setSelected(prev => [...prev, ...(model.schema?.graph.expandAllInputs(prev) ?? [])]), diff --git a/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ConstituentsSearch.tsx b/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ConstituentsSearch.tsx index 88c50212..68f29521 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ConstituentsSearch.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ConstituentsSearch.tsx @@ -2,7 +2,15 @@ import { useCallback, useLayoutEffect, useState } from 'react'; -import { IconFilter, IconSettings } from '@/components/Icons'; +import { + IconFilter, + IconGraphCollapse, + IconGraphExpand, + IconGraphInputs, + IconGraphOutputs, + IconSettings, + IconText +} from '@/components/Icons'; import Dropdown from '@/components/ui/Dropdown'; import DropdownButton from '@/components/ui/DropdownButton'; import SearchBar from '@/components/ui/SearchBar'; @@ -24,6 +32,23 @@ interface ConstituentsSearchProps { setFiltered: React.Dispatch>; } +function DependencyIcon(mode: DependencyMode, size: string) { + switch (mode) { + case DependencyMode.ALL: + return ; + case DependencyMode.EXPRESSION: + return ; + case DependencyMode.OUTPUTS: + return ; + case DependencyMode.INPUTS: + return ; + case DependencyMode.EXPAND_OUTPUTS: + return ; + case DependencyMode.EXPAND_INPUTS: + return ; + } +} + function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }: ConstituentsSearchProps) { const [filterMatch, setFilterMatch] = useLocalStorage(storage.cstFilterMatch, CstMatchMode.ALL); const [filterSource, setFilterSource] = useLocalStorage(storage.cstFilterGraph, DependencyMode.ALL); @@ -121,7 +146,7 @@ function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }: title='Настройка фильтрации по графу термов' hideTitle={sourceMenu.isOpen} className='h-full pr-2' - icon={} + icon={DependencyIcon(filterSource, '1.25rem')} text={labelCstSource(filterSource)} onClick={sourceMenu.toggle} /> @@ -132,13 +157,14 @@ function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }: const source = value as DependencyMode; return ( handleSourceChange(source)} > -

+

+ {DependencyIcon(source, '1.25rem')} {labelCstSource(source)}: {describeCstSource(source)} -

+
); })} diff --git a/rsconcept/frontend/src/utils/labels.ts b/rsconcept/frontend/src/utils/labels.ts index 838190c7..9439488d 100644 --- a/rsconcept/frontend/src/utils/labels.ts +++ b/rsconcept/frontend/src/utils/labels.ts @@ -243,11 +243,11 @@ export function describeCstSource(mode: DependencyMode): string { // prettier-ignore switch (mode) { case DependencyMode.ALL: return 'все конституенты'; - case DependencyMode.EXPRESSION: return 'идентификаторы из выражения'; - case DependencyMode.OUTPUTS: return 'прямые ссылки на текущую'; - case DependencyMode.INPUTS: return 'прямые ссылки из текущей'; - case DependencyMode.EXPAND_OUTPUTS: return 'опосредованные ссылки на текущую'; - case DependencyMode.EXPAND_INPUTS: return 'опосредованные ссылки из текущей'; + case DependencyMode.EXPRESSION: return 'имена из выражения'; + case DependencyMode.OUTPUTS: return 'прямые исходящие'; + case DependencyMode.INPUTS: return 'прямые входящие'; + case DependencyMode.EXPAND_OUTPUTS: return 'цепочка исходящих'; + case DependencyMode.EXPAND_INPUTS: return 'цепочка входящих'; } }