From 3a3ca5109ec7b9ddbfe05ff15f712e7c883a6825 Mon Sep 17 00:00:00 2001 From: IRBorisov <8611739+IRBorisov@users.noreply.github.com> Date: Tue, 15 Aug 2023 21:22:21 +0300 Subject: [PATCH 01/10] Improve GraphUI --- .../pages/RSFormPage/EditorConstituenta.tsx | 2 +- .../src/pages/RSFormPage/EditorItems.tsx | 2 +- .../src/pages/RSFormPage/EditorTermGraph.tsx | 267 +++++++++++++++--- .../frontend/src/pages/RSFormPage/RSTabs.tsx | 6 +- rsconcept/frontend/src/utils/staticUI.ts | 91 ++++-- 5 files changed, 300 insertions(+), 68 deletions(-) diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta.tsx index de5bbe0b..4f8a428d 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta.tsx @@ -241,7 +241,7 @@ function EditorConstituenta({ activeID, onShowAST, onCreateCst, onOpenEdit, onDe /> -
+
Клик на квадрат слева - выделение конституенты

Alt + вверх/вниз - движение конституент

Delete - удаление конституент

-

Alt + 1-6, Q,W - добавление конституент

+

Alt + 1-6,Q,W - добавление конституент

Статусы

{ [... mapStatusInfo.values()].map(info => { diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx index 13645b8d..0346dc0e 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx @@ -1,29 +1,70 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { darkTheme, GraphCanvas, GraphCanvasRef, GraphEdge, GraphNode, LayoutTypes, lightTheme, Sphere, useSelection } from 'reagraph'; +import { darkTheme, GraphCanvas, GraphCanvasRef, GraphEdge, + GraphNode, LayoutTypes, lightTheme, Sphere, useSelection +} from 'reagraph'; import Button from '../../components/Common/Button'; import Checkbox from '../../components/Common/Checkbox'; import ConceptSelect from '../../components/Common/ConceptSelect'; -import { ArrowsRotateIcon } from '../../components/Icons'; +import ConceptTooltip from '../../components/Common/ConceptTooltip'; +import Divider from '../../components/Common/Divider'; +import { ArrowsRotateIcon, HelpIcon } from '../../components/Icons'; import { useRSForm } from '../../context/RSFormContext'; import { useConceptTheme } from '../../context/ThemeContext'; import useLocalStorage from '../../hooks/useLocalStorage'; -import { resources } from '../../utils/constants'; +import { prefixes, resources } from '../../utils/constants'; import { Graph } from '../../utils/Graph'; -import { GraphLayoutSelector,mapLayoutLabels } from '../../utils/staticUI'; +import { IConstituenta } from '../../utils/models'; +import { getCstStatusColor, getCstTypeColor, getCstTypificationLabel, + GraphColoringSelector, GraphLayoutSelector, + mapColoringLabels, mapLayoutLabels, mapStatusInfo +} from '../../utils/staticUI'; +import ConstituentaTooltip from './elements/ConstituentaTooltip'; -function EditorTermGraph() { +export type ColoringScheme = 'none' | 'status' | 'type'; +const TREE_SIZE_MILESTONE = 50; + +function getCstNodeColor(cst: IConstituenta, coloringScheme: ColoringScheme, darkMode: boolean): string { + if (coloringScheme === 'type') { + return getCstTypeColor(cst.cstType, darkMode); + } + if (coloringScheme === 'status') { + return getCstStatusColor(cst.status, darkMode); + } + return ''; +} + +interface EditorTermGraphProps { + onOpenEdit: (cstID: number) => 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(); const { darkMode, noNavigation } = useConceptTheme(); + const [ layout, setLayout ] = useLocalStorage('graph_layout', 'treeTd2d'); - - const [ filtered, setFiltered ] = useState(new Graph()); + const [ coloringScheme, setColoringScheme ] = useLocalStorage('graph_coloring', 'none'); const [ orbit, setOrbit ] = useState(false); const [ noHermits, setNoHermits ] = useLocalStorage('graph_no_hermits', true); const [ noTransitive, setNoTransitive ] = useLocalStorage('graph_no_transitive', false); + + const [ filtered, setFiltered ] = useState(new Graph()); + const [ dismissed, setDismissed ] = useState([]); + const [ selectedDismissed, setSelectedDismissed ] = useState([]); const graphRef = useRef(null); + + const [hoverID, setHoverID] = useState(undefined); + const hoverCst = useMemo( + () => { + return schema?.items.find(cst => String(cst.id) == hoverID); + }, [schema?.items, hoverID]); - useEffect(() => { + const is3D = useMemo(() => layout.includes('3d'), [layout]); + + useEffect( + () => { if (!schema) { setFiltered(new Graph()); return; @@ -35,10 +76,32 @@ function EditorTermGraph() { if (noTransitive) { graph.transitiveReduction(); } + const newDismissed: number[] = []; + schema.items.forEach(cst => { + if (!graph.nodes.has(cst.id)) { + newDismissed.push(cst.id); + } + }); setFiltered(graph); + setDismissed(newDismissed); + setSelectedDismissed([]); + setHoverID(undefined); }, [schema, noHermits, noTransitive]); - const nodes: GraphNode[] = useMemo(() => { + function toggleDismissed(cstID: number) { + setSelectedDismissed(prev => { + const index = prev.findIndex(id => cstID == id); + if (index !== -1) { + prev.splice(index, 1); + } else { + prev.push(cstID); + } + return [... prev]; + }); + } + + const nodes: GraphNode[] = useMemo( + () => { const result: GraphNode[] = []; if (!schema) { return result; @@ -48,14 +111,16 @@ function EditorTermGraph() { if (cst) { result.push({ id: String(node.id), + fill: getCstNodeColor(cst, coloringScheme, darkMode), label: cst.term.resolved ? `${cst.alias}: ${cst.term.resolved}` : cst.alias }); } }); return result; - }, [schema, filtered.nodes]); + }, [schema, coloringScheme, filtered.nodes, darkMode]); - const edges: GraphEdge[] = useMemo(() => { + const edges: GraphEdge[] = useMemo( + () => { const result: GraphEdge[] = []; let edgeID = 1; filtered.nodes.forEach(source => { @@ -70,12 +135,7 @@ function EditorTermGraph() { }); return result; }, [filtered.nodes]); - - const handleCenter = useCallback(() => { - graphRef.current?.resetControls(); - graphRef.current?.centerGraph(); - }, []); - + const { selections, actives, onNodeClick, @@ -91,30 +151,92 @@ function EditorTermGraph() { focusOnSelect: false }); - const canvasSize = !noNavigation ? - 'w-[1240px] h-[736px] 2xl:w-[1880px] 2xl:h-[746px]' - : 'w-[1240px] h-[800px] 2xl:w-[1880px] 2xl:h-[810px]'; + const handleCenter = useCallback( + () => { + graphRef.current?.resetControls(); + graphRef.current?.centerGraph(); + }, []); - return (<> -
-
-
+ const handleHoverIn = useCallback( + (node: GraphNode) => { + setHoverID(node.id); + if (onNodePointerOver) onNodePointerOver(node); + }, [onNodePointerOver]); + + const handleHoverOut = useCallback( + (node: GraphNode) => { + setHoverID(undefined); + if (onNodePointerOut) onNodePointerOut(node); + }, [onNodePointerOut]); + + const handleNodeClick = useCallback( + (node: GraphNode) => { + if (selections.includes(node.id)) { + onOpenEdit(Number(node.id)); + return; + } + if (onNodeClick) onNodeClick(node); + }, [onNodeClick, selections, onOpenEdit]); + + const canvasWidth = useMemo( + () => { + return 'calc(100vw - 14.6rem)'; + }, []); + + const canvasHeight = useMemo( + () => { + return !noNavigation ? + 'calc(100vh - 13rem)' + : 'calc(100vh - 8.5rem)'; + }, [noNavigation]); + + const dismissedStyle = useCallback( + (cstID: number) => { + return selectedDismissed.includes(cstID) ? {outlineWidth: '2px', outlineStyle: 'solid'}: {}; + }, [selectedDismissed]); + + return ( +
+
+ {hoverCst && +
+
+

Конституента {hoverCst.alias}

+

Типизация: {getCstTypificationLabel(hoverCst)}

+

Термин: {hoverCst.term.resolved || hoverCst.term.raw}

+ {hoverCst.definition.formal &&

Выражение: {hoverCst.definition.formal}

} + {hoverCst.definition.text.resolved &&

Определение: {hoverCst.definition.text.resolved}

} + {hoverCst.convention &&

Конвенция: {hoverCst.convention}

} +
+
} +
+ { setLayout(data.length > 0 ? data[0].value : GraphLayoutSelector[0].value); }} + /> setOrbit(event.target.checked) } @@ -129,10 +251,75 @@ function EditorTermGraph() { value={noTransitive} onChange={ event => setNoTransitive(event.target.checked) } /> -
+ + + +
+

Скрытые конституенты

+
+ {dismissed.map(cstID => { + const cst = schema!.items.find(cst => cst.id === cstID)!; + const info = mapStatusInfo.get(cst.status)!; + return (<> +
toggleDismissed(cstID)} + onDoubleClick={() => onOpenEdit(cstID)} + > + {cst.alias} +
+ + ); + })} +
+
-
+
+
+ +
+ +
+

Настройка графа

+

Цвет - выбор правила покраски узлов

+

Скрытые конституенты окрашены в цвет статуса

+

Граф - выбор модели расположения узлов

+

Удалить несвязанные - в графе не отображаются одинокие вершины

+

Транзитивная редукция - в графе устраняются транзитивные пути

+ + + +

Горячие клавиши

+

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

+

Delete - удаление конституент

+

Alt + 1-6,Q,W - добавление конституент

+ + + +

Статусы

+ { [... mapStatusInfo.values()].map(info => { + return (

+ + {info.text} + + - + + {info.tooltip} + +

); + })} +
+
( @@ -156,7 +345,7 @@ function EditorTermGraph() { />
- ); +
); } diff --git a/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx b/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx index 87e673de..58ddc7c6 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/RSTabs.tsx @@ -40,7 +40,7 @@ function RSTabs() { const { destroySchema } = useLibrary(); const [activeTab, setActiveTab] = useState(RSTabsList.CARD); - const [activeID, setActiveID] = useState(undefined) + const [activeID, setActiveID] = useState(undefined); const [showUpload, setShowUpload] = useState(false); const [showClone, setShowClone] = useState(false); @@ -264,7 +264,9 @@ function RSTabs() { - + } diff --git a/rsconcept/frontend/src/utils/staticUI.ts b/rsconcept/frontend/src/utils/staticUI.ts index ba884892..1045e83e 100644 --- a/rsconcept/frontend/src/utils/staticUI.ts +++ b/rsconcept/frontend/src/utils/staticUI.ts @@ -1,5 +1,6 @@ import { LayoutTypes } from 'reagraph'; +import { ColoringScheme } from '../pages/RSFormPage/EditorTermGraph'; import { resolveErrorClass,RSErrorClass, RSErrorType, TokenID } from './enums'; import { CstMatchMode, CstType, DependencyMode,ExpressionStatus, IConstituenta, IFunctionArg,IRSErrorDescription, IRSForm, @@ -244,28 +245,16 @@ export function getCstTypeShortcut(type: CstType) { } } +export const mapCstTypeColors: Map = new Map([ + [CstType.BASE, 'Атлас 2D'], +]); + export const CstTypeSelector = (Object.values(CstType)).map( (typeStr) => { const type = typeStr as CstType; return { value: type, label: getCstTypeLabel(type) }; }); -export const mapLayoutLabels: Map = new Map([ - ['forceatlas2', 'Атлас 2D'], - ['forceDirected2d', 'Силы 2D'], - ['forceDirected3d', 'Силы 3D'], - ['treeTd2d', 'ДеревоВерт 2D'], - ['treeTd3d', 'ДеревоВерт 3D'], - ['treeLr2d', 'ДеревоГор 2D'], - ['treeLr3d', 'ДеревоГор 3D'], - ['radialOut2d', 'Радиальная 2D'], - ['radialOut3d', 'Радиальная 3D'], - ['circular2d', 'Круговая'], - ['hierarchicalTd', 'ИерархияВерт'], - ['hierarchicalLr', 'ИерархияГор'], - ['nooverlap', 'Без перекрытия'] -]); - export function getCstCompareLabel(mode: CstMatchMode): string { switch(mode) { case CstMatchMode.ALL: return 'везде'; @@ -288,21 +277,73 @@ export function getDependencyLabel(mode: DependencyMode): string { } export const GraphLayoutSelector: {value: LayoutTypes, label: string}[] = [ - { value: 'forceatlas2', label: 'Атлас 2D'}, - { value: 'forceDirected2d', label: 'Силы 2D'}, - { value: 'forceDirected3d', label: 'Силы 3D'}, - { value: 'treeTd2d', label: 'ДеревоВ 2D'}, - { value: 'treeTd3d', label: 'ДеревоВ 3D'}, - { value: 'treeLr2d', label: 'ДеревоГ 2D'}, - { value: 'treeLr3d', label: 'ДеревоГ 3D'}, - { value: 'radialOut2d', label: 'Радиальная 2D'}, - { value: 'radialOut3d', label: 'Радиальная 3D'}, + { value: 'treeTd2d', label: 'Граф: ДеревоВ 2D'}, + { value: 'treeTd3d', label: 'Граф: ДеревоВ 3D'}, + { value: 'forceatlas2', label: 'Граф: Атлас 2D'}, + { value: 'forceDirected2d', label: 'Граф: Силы 2D'}, + { value: 'forceDirected3d', label: 'Граф: Силы 3D'}, + { value: 'treeLr2d', label: 'Граф: ДеревоГ 2D'}, + { value: 'treeLr3d', label: 'Граф: ДеревоГ 3D'}, + { value: 'radialOut2d', label: 'Граф: Радиальная 2D'}, + { value: 'radialOut3d', label: 'Граф: Радиальная 3D'}, // { value: 'circular2d', label: 'circular2d'}, // { value: 'nooverlap', label: 'nooverlap'}, // { value: 'hierarchicalTd', label: 'hierarchicalTd'}, // { value: 'hierarchicalLr', label: 'hierarchicalLr'} ]; +export const mapLayoutLabels: Map = new Map([ + ['forceatlas2', 'Граф: Атлас 2D'], + ['forceDirected2d', 'Граф: Силы 2D'], + ['forceDirected3d', 'Граф: Силы 3D'], + ['treeTd2d', 'Граф: ДеревоВерт 2D'], + ['treeTd3d', 'Граф: ДеревоВерт 3D'], + ['treeLr2d', 'Граф: ДеревоГор 2D'], + ['treeLr3d', 'Граф: ДеревоГор 3D'], + ['radialOut2d', 'Граф: Радиальная 2D'], + ['radialOut3d', 'Граф: Радиальная 3D'], + ['circular2d', 'Граф: Круговая'], + ['hierarchicalTd', 'Граф: ИерархияВерт'], + ['hierarchicalLr', 'Граф: ИерархияГор'], + ['nooverlap', 'Граф: Без перекрытия'] +]); + +export const mapColoringLabels: Map = new Map([ + ['none', 'Цвет: моно'], + ['status', 'Цвет: статус'], + ['type', 'Цвет: тип'], +]); + +export const GraphColoringSelector: {value: ColoringScheme, label: string}[] = [ + { value: 'none', label: 'Цвет: моно'}, + { value: 'status', label: 'Цвет: статус'}, + { value: 'type', label: 'Цвет: тип'}, +]; + +export function getCstTypeColor(type: CstType, darkMode: boolean): string { + switch (type) { + case CstType.BASE: return darkMode ? '#2b8000': '#aaff80'; + case CstType.CONSTANT: return darkMode ? '#2b8000': '#aaff80'; + case CstType.STRUCTURED: return darkMode ? '#2b8000': '#aaff80'; + case CstType.TERM: return darkMode ? '#1e00b3': '#b3bdff'; + case CstType.FUNCTION: return darkMode ? '#1e00b3': '#b3bdff'; + case CstType.AXIOM: return darkMode ? '#592b2b': '#ffc9c9'; + case CstType.PREDICATE: return darkMode ? '#1e00b3': '#b3bdff'; + case CstType.THEOREM: return darkMode ? '#592b2b': '#ffc9c9'; + } +} + +export function getCstStatusColor(status: ExpressionStatus, darkMode: boolean): string { + switch (status) { + case ExpressionStatus.VERIFIED: return darkMode ? '#2b8000': '#aaff80'; + case ExpressionStatus.INCORRECT: return darkMode ? '#592b2b': '#ffc9c9'; + case ExpressionStatus.INCALCULABLE: return darkMode ? '#964600': '#ffbb80'; + case ExpressionStatus.PROPERTY: return darkMode ? '#36899e': '#a5e9fa'; + case ExpressionStatus.UNKNOWN: return darkMode ? '#1e00b3': '#b3bdff'; + case ExpressionStatus.UNDEFINED: return darkMode ? '#1e00b3': '#b3bdff'; + } +} + export const mapStatusInfo: Map = new Map([ [ ExpressionStatus.VERIFIED, { text: 'ок', From 3ba12ca175d23a07808033e61a9b085b5434ada1 Mon Sep 17 00:00:00 2001 From: IRBorisov <8611739+IRBorisov@users.noreply.github.com> Date: Tue, 15 Aug 2023 21:22:24 +0300 Subject: [PATCH 02/10] Update TODO.txt --- TODO.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/TODO.txt b/TODO.txt index 640618f5..fe89ffeb 100644 --- a/TODO.txt +++ b/TODO.txt @@ -6,7 +6,6 @@ For more specific TODOs see comments in code - home page - manuals - текстовый модуль для разрешения отсылок -- компонент для форматирования в редакторе текста (формальное выражения + отсылки в тексте) - блок нотификаций пользователей - блок синтеза - блок организации библиотеки моделей @@ -17,8 +16,10 @@ For more specific TODOs see comments in code - Use migtation/fixtures to provide initial data for testing - USe migtation/fixtures to load example common data +- create custom Select component +- reload react-data-table-component + [deployment] -- HTTPS - database backup daemon - logs collection - status dashboard for servers From 2deeee756cf54f44f06a122f4b50362efa6aa063 Mon Sep 17 00:00:00 2001 From: IRBorisov <8611739+IRBorisov@users.noreply.github.com> Date: Tue, 15 Aug 2023 21:43:15 +0300 Subject: [PATCH 03/10] Refactor common help elements --- .../src/components/Help/ConstituentaInfo.tsx | 22 ++++++++++++++ .../src/components/Help/CstStatusInfo.tsx | 29 +++++++++++++++++++ .../pages/RSFormPage/EditorConstituenta.tsx | 16 ++-------- .../src/pages/RSFormPage/EditorItems.tsx | 14 ++------- .../src/pages/RSFormPage/EditorTermGraph.tsx | 29 +++++-------------- .../elements/ConstituentaTooltip.tsx | 9 ++---- rsconcept/frontend/src/utils/constants.ts | 3 +- 7 files changed, 68 insertions(+), 54 deletions(-) create mode 100644 rsconcept/frontend/src/components/Help/ConstituentaInfo.tsx create mode 100644 rsconcept/frontend/src/components/Help/CstStatusInfo.tsx diff --git a/rsconcept/frontend/src/components/Help/ConstituentaInfo.tsx b/rsconcept/frontend/src/components/Help/ConstituentaInfo.tsx new file mode 100644 index 00000000..07e34d2e --- /dev/null +++ b/rsconcept/frontend/src/components/Help/ConstituentaInfo.tsx @@ -0,0 +1,22 @@ +import { IConstituenta } from '../../utils/models'; +import { getCstTypificationLabel } from '../../utils/staticUI'; + +interface ConstituentaInfoProps +extends React.HTMLAttributes { + data: IConstituenta +} + +function ConstituentaInfo({ data, ...props }: ConstituentaInfoProps) { + return ( +
+

Конституента {data.alias}

+

Типизация: {getCstTypificationLabel(data)}

+

Термин: {data.term.resolved || data.term.raw}

+ {data.definition.formal &&

Выражение: {data.definition.formal}

} + {data.definition.text.resolved &&

Определение: {data.definition.text.resolved}

} + {data.convention &&

Конвенция: {data.convention}

} +
+ ); +} + +export default ConstituentaInfo; \ No newline at end of file diff --git a/rsconcept/frontend/src/components/Help/CstStatusInfo.tsx b/rsconcept/frontend/src/components/Help/CstStatusInfo.tsx new file mode 100644 index 00000000..e1a0c78d --- /dev/null +++ b/rsconcept/frontend/src/components/Help/CstStatusInfo.tsx @@ -0,0 +1,29 @@ +import { prefixes } from '../../utils/constants'; +import { mapStatusInfo } from '../../utils/staticUI'; + +interface CstStatusInfoProps { + title?: string +} + +function CstStatusInfo({ title }: CstStatusInfoProps) { + return ( + <> + { title &&

{title}

} + { [... mapStatusInfo.values()].map( + (info, index) => { + return ( +

+ + {info.text} + + - + + {info.tooltip} + +

); + })} + + ); +} + +export default CstStatusInfo; diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta.tsx index 4f8a428d..459ac9d4 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta.tsx @@ -6,10 +6,11 @@ import Divider from '../../components/Common/Divider'; import MiniButton from '../../components/Common/MiniButton'; import SubmitButton from '../../components/Common/SubmitButton'; import TextArea from '../../components/Common/TextArea'; +import CstStatusInfo from '../../components/Help/CstStatusInfo'; import { DumpBinIcon, HelpIcon, SaveIcon, SmallPlusIcon } from '../../components/Icons'; import { useRSForm } from '../../context/RSFormContext'; import { type CstType, EditMode, ICstUpdateData, SyntaxTree } from '../../utils/models'; -import { getCstTypeLabel, getCstTypificationLabel, mapStatusInfo } from '../../utils/staticUI'; +import { getCstTypeLabel, getCstTypificationLabel } from '../../utils/staticUI'; import EditorRSExpression from './EditorRSExpression'; import ViewSideConstituents from './elements/ViewSideConstituents'; @@ -173,18 +174,7 @@ function EditorConstituenta({ activeID, onShowAST, onCreateCst, onOpenEdit, onDe

- при наведении на ID конституенты отображаются ее атрибуты

- столбец "Описание" содержит один из непустых текстовых атрибутов

-

Статусы

- { [... mapStatusInfo.values()].map((info, index) => { - return (

- - {info.text} - - - - - {info.tooltip} - -

); - })} +
diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorItems.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorItems.tsx index 9d0b0d00..5c6afa2e 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorItems.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorItems.tsx @@ -5,6 +5,7 @@ import Button from '../../components/Common/Button'; import ConceptDataTable from '../../components/Common/ConceptDataTable'; import ConceptTooltip from '../../components/Common/ConceptTooltip'; import Divider from '../../components/Common/Divider'; +import CstStatusInfo from '../../components/Help/CstStatusInfo'; import { ArrowDownIcon, ArrowsRotateIcon, ArrowUpIcon, DumpBinIcon, HelpIcon, SmallPlusIcon } from '../../components/Icons'; import { useRSForm } from '../../context/RSFormContext'; import { useConceptTheme } from '../../context/ThemeContext'; @@ -316,18 +317,7 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps)

Delete - удаление конституент

Alt + 1-6,Q,W - добавление конституент

-

Статусы

- { [... mapStatusInfo.values()].map(info => { - return (

- - {info.text} - - - - - {info.tooltip} - -

); - })} +
diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx index 0346dc0e..97f5ce13 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx @@ -8,6 +8,8 @@ import Checkbox from '../../components/Common/Checkbox'; import ConceptSelect from '../../components/Common/ConceptSelect'; import ConceptTooltip from '../../components/Common/ConceptTooltip'; import Divider from '../../components/Common/Divider'; +import ConstituentaInfo from '../../components/Help/ConstituentaInfo'; +import CstStatusInfo from '../../components/Help/CstStatusInfo'; import { ArrowsRotateIcon, HelpIcon } from '../../components/Icons'; import { useRSForm } from '../../context/RSFormContext'; import { useConceptTheme } from '../../context/ThemeContext'; @@ -15,7 +17,7 @@ import useLocalStorage from '../../hooks/useLocalStorage'; import { prefixes, resources } from '../../utils/constants'; import { Graph } from '../../utils/Graph'; import { IConstituenta } from '../../utils/models'; -import { getCstStatusColor, getCstTypeColor, getCstTypificationLabel, +import { getCstStatusColor, getCstTypeColor, GraphColoringSelector, GraphLayoutSelector, mapColoringLabels, mapLayoutLabels, mapStatusInfo } from '../../utils/staticUI'; @@ -200,14 +202,10 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
{hoverCst &&
-
-

Конституента {hoverCst.alias}

-

Типизация: {getCstTypificationLabel(hoverCst)}

-

Термин: {hoverCst.term.resolved || hoverCst.term.raw}

- {hoverCst.definition.formal &&

Выражение: {hoverCst.definition.formal}

} - {hoverCst.definition.text.resolved &&

Определение: {hoverCst.definition.text.resolved}

} - {hoverCst.convention &&

Конвенция: {hoverCst.convention}

} -
+
}
-

Конституента {data.alias}

-

Типизация: {getCstTypificationLabel(data)}

-

Термин: {data.term.resolved || data.term.raw}

- {data.definition.formal &&

Выражение: {data.definition.formal}

} - {data.definition.text.resolved &&

Определение: {data.definition.text.resolved}

} - {data.convention &&

Конвенция: {data.convention}

} + ); } diff --git a/rsconcept/frontend/src/utils/constants.ts b/rsconcept/frontend/src/utils/constants.ts index e14e1276..b620bcf5 100644 --- a/rsconcept/frontend/src/utils/constants.ts +++ b/rsconcept/frontend/src/utils/constants.ts @@ -28,5 +28,6 @@ export const resources = { } export const prefixes = { - cst_list: 'cst-list-' + cst_list: 'cst-list-', + cst_status_list: 'cst-status-list-' } From 162191040c093fd78e16ffd223a0c83b0dd06951 Mon Sep 17 00:00:00 2001 From: IRBorisov <8611739+IRBorisov@users.noreply.github.com> Date: Tue, 15 Aug 2023 21:55:43 +0300 Subject: [PATCH 04/10] Hide footer when navigation is hidden --- rsconcept/frontend/src/App.tsx | 14 +++++++------- .../src/pages/RSFormPage/EditorTermGraph.tsx | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/rsconcept/frontend/src/App.tsx b/rsconcept/frontend/src/App.tsx index 6769c85d..a0abb79f 100644 --- a/rsconcept/frontend/src/App.tsx +++ b/rsconcept/frontend/src/App.tsx @@ -22,14 +22,14 @@ function App () { const scrollWindowSize = useMemo( () => { return !noNavigation ? - 'max-h-[calc(100vh-4.5rem)]' - : 'max-h-[100vh]'; + 'calc(100vh - 4.5rem)' + : '100vh'; }, [noNavigation]); const mainSize = useMemo( () => { return !noNavigation ? - 'min-h-[calc(100vh-12rem)]' - : 'min-h-[calc(100vh-8rem)] '; + 'calc(100vh - 12rem)' + : '100vh'; }, [noNavigation]); return ( @@ -42,8 +42,8 @@ function App () { pauseOnFocusLoss={false} /> -
-
+
+
} /> @@ -60,7 +60,7 @@ function App () { } />
-
+ {!noNavigation &&
}
); diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx index 97f5ce13..52e0bb91 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx @@ -189,7 +189,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) { () => { return !noNavigation ? 'calc(100vh - 13rem)' - : 'calc(100vh - 8.5rem)'; + : 'calc(100vh - 2rem)'; }, [noNavigation]); const dismissedStyle = useCallback( @@ -281,7 +281,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
From ecea364d69822390db807e1a3c72066412df7fc7 Mon Sep 17 00:00:00 2001 From: IRBorisov <8611739+IRBorisov@users.noreply.github.com> Date: Wed, 16 Aug 2023 00:39:16 +0300 Subject: [PATCH 05/10] Implement GraphEditor params --- .../src/components/Help/CstClassInfo.tsx | 29 +++ .../src/components/Help/CstStatusInfo.tsx | 8 +- .../src/pages/RSFormPage/DlgGraphOptions.tsx | 144 +++++++++++++++ .../src/pages/RSFormPage/EditorItems.tsx | 169 +++++++++--------- .../src/pages/RSFormPage/EditorTermGraph.tsx | 151 +++++++++++++--- rsconcept/frontend/src/utils/Graph.test.ts | 12 +- rsconcept/frontend/src/utils/Graph.ts | 17 ++ rsconcept/frontend/src/utils/models.ts | 46 ++++- rsconcept/frontend/src/utils/staticUI.ts | 61 ++++--- 9 files changed, 490 insertions(+), 147 deletions(-) create mode 100644 rsconcept/frontend/src/components/Help/CstClassInfo.tsx create mode 100644 rsconcept/frontend/src/pages/RSFormPage/DlgGraphOptions.tsx diff --git a/rsconcept/frontend/src/components/Help/CstClassInfo.tsx b/rsconcept/frontend/src/components/Help/CstClassInfo.tsx new file mode 100644 index 00000000..fdcefd0f --- /dev/null +++ b/rsconcept/frontend/src/components/Help/CstClassInfo.tsx @@ -0,0 +1,29 @@ +import { prefixes } from '../../utils/constants'; +import { mapCstClassInfo } from '../../utils/staticUI'; + +interface CstClassInfoProps { + title?: string +} + +function CstClassInfo({ title }: CstClassInfoProps) { + return ( +
+ { title &&

{title}

} + { [... mapCstClassInfo.values()].map( + (info, index) => { + return ( +

+ + {info.text} + + - + + {info.tooltip} + +

); + })} +
+ ); +} + +export default CstClassInfo; diff --git a/rsconcept/frontend/src/components/Help/CstStatusInfo.tsx b/rsconcept/frontend/src/components/Help/CstStatusInfo.tsx index e1a0c78d..3ae1f379 100644 --- a/rsconcept/frontend/src/components/Help/CstStatusInfo.tsx +++ b/rsconcept/frontend/src/components/Help/CstStatusInfo.tsx @@ -7,13 +7,13 @@ interface CstStatusInfoProps { function CstStatusInfo({ title }: CstStatusInfoProps) { return ( - <> +
{ title &&

{title}

} { [... mapStatusInfo.values()].map( (info, index) => { return ( -

- +

+ {info.text} - @@ -22,7 +22,7 @@ function CstStatusInfo({ title }: CstStatusInfoProps) {

); })} - +
); } diff --git a/rsconcept/frontend/src/pages/RSFormPage/DlgGraphOptions.tsx b/rsconcept/frontend/src/pages/RSFormPage/DlgGraphOptions.tsx new file mode 100644 index 00000000..ddb20b23 --- /dev/null +++ b/rsconcept/frontend/src/pages/RSFormPage/DlgGraphOptions.tsx @@ -0,0 +1,144 @@ +import { useLayoutEffect, useState } from 'react'; + +import Checkbox from '../../components/Common/Checkbox'; +import Modal from '../../components/Common/Modal'; +import { CstType } from '../../utils/models'; +import { getCstTypeLabel } from '../../utils/staticUI'; +import { GraphEditorParams } from './EditorTermGraph'; + +interface DlgGraphOptionsProps { + hideWindow: () => void + initial: GraphEditorParams + onConfirm: (params: GraphEditorParams) => void +} + +function DlgGraphOptions({ hideWindow, initial, onConfirm }:DlgGraphOptionsProps) { + const [ noHermits, setNoHermits ] = useState(true); + const [ noTransitive, setNoTransitive ] = useState(false); + const [ noTemplates, setNoTemplates ] = useState(true); + + const [ allowBase, setAllowBase ] = useState(true); + const [ allowStruct, setAllowStruct ] = useState(true); + const [ allowTerm, setAllowTerm ] = useState(true); + const [ allowAxiom, setAllowAxiom ] = useState(true); + const [ allowFunction, setAllowFunction ] = useState(true); + const [ allowPredicate, setAllowPredicate ] = useState(true); + const [ allowConstant, setAllowConstant ] = useState(true); + const [ allowTheorem, setAllowTheorem ] = useState(true); + + function getParams() { + return { + noHermits: noHermits, + noTransitive: noTransitive, + noTemplates: noTemplates, + + allowBase: allowBase, + allowStruct: allowStruct, + allowTerm: allowTerm, + allowAxiom: allowAxiom, + allowFunction: allowFunction, + allowPredicate: allowPredicate, + allowConstant: allowConstant, + allowTheorem: allowTheorem + } + } + + const handleSubmit = () => { + hideWindow(); + onConfirm(getParams()); + }; + + useLayoutEffect(() => { + setNoHermits(initial.noHermits); + setNoTransitive(initial.noTransitive); + setNoTemplates(initial.noTemplates); + + setAllowBase(initial.allowBase); + setAllowStruct(initial.allowStruct); + setAllowTerm(initial.allowTerm); + setAllowAxiom(initial.allowAxiom); + setAllowFunction(initial.allowFunction); + setAllowPredicate(initial.allowPredicate); + setAllowConstant(initial.allowConstant); + setAllowTheorem(initial.allowTheorem); + }, [initial]); + + return ( + +
+
+

Преобразования

+ setNoHermits(event.target.checked) } + /> + setNoTemplates(event.target.checked) } + /> + setNoTransitive(event.target.checked) } + /> +
+
+

Типы конституент

+ setAllowBase(event.target.checked) } + /> + setAllowStruct(event.target.checked) } + /> + setAllowTerm(event.target.checked) } + /> + setAllowAxiom(event.target.checked) } + /> + setAllowFunction(event.target.checked) } + /> + setAllowPredicate(event.target.checked) } + /> + setAllowConstant(event.target.checked) } + /> + setAllowTheorem(event.target.checked) } + /> +
+
+
+ ); +} + +export default DlgGraphOptions; diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorItems.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorItems.tsx index 5c6afa2e..9fff700d 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorItems.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorItems.tsx @@ -157,93 +157,92 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps) setSelected(selectedRows.map(cst => cst.id)); }, [setSelected]); - const columns = useMemo(() => - [ - { - name: 'ID', - id: 'id', - selector: (cst: IConstituenta) => cst.id, - omit: true - }, - { - name: 'Имя', - id: 'alias', - selector: (cst: IConstituenta) => cst.alias, - cell: (cst: IConstituenta) => { - const info = mapStatusInfo.get(cst.status)!; - return (<> -
- {cst.alias} -
- -

Статус: {info.tooltip}

-
- ); - }, - width: '65px', - maxWidth: '65px', - reorder: true, - }, - { - name: 'Тип', - id: 'type', - cell: (cst: IConstituenta) =>
{getCstTypificationLabel(cst)}
, - width: '175px', - maxWidth: '175px', - wrap: true, - reorder: true, - hide: 1600 - }, - { - name: 'Термин', - id: 'term', - selector: (cst: IConstituenta) => cst.term?.resolved ?? cst.term?.raw ?? '', - width: '350px', - minWidth: '150px', - maxWidth: '350px', - wrap: true, - reorder: true - }, - { - name: 'Формальное определение', - id: 'expression', - selector: (cst: IConstituenta) => cst.definition?.formal ?? '', - minWidth: '300px', - maxWidth: '500px', - grow: 2, - wrap: true, - reorder: true - }, - { - name: 'Текстовое определение', - id: 'definition', - cell: (cst: IConstituenta) => ( -
- {cst.definition?.text.resolved ?? cst.definition?.text.raw ?? ''} + const columns = useMemo( + () => [ + { + name: 'ID', + id: 'id', + selector: (cst: IConstituenta) => cst.id, + omit: true + }, + { + name: 'Имя', + id: 'alias', + selector: (cst: IConstituenta) => cst.alias, + cell: (cst: IConstituenta) => { + const info = mapStatusInfo.get(cst.status)!; + return (<> +
+ {cst.alias}
- ), - minWidth: '200px', - grow: 2, - wrap: true, - reorder: true + +

Статус: {info.tooltip}

+
+ ); }, - { - name: 'Конвенция / Комментарий', - id: 'convention', - cell: (cst: IConstituenta) =>
{cst.convention ?? ''}
, - minWidth: '100px', - wrap: true, - reorder: true, - hide: 1800 - } - ], [] - ); + width: '65px', + maxWidth: '65px', + reorder: true, + }, + { + name: 'Тип', + id: 'type', + cell: (cst: IConstituenta) =>
{getCstTypificationLabel(cst)}
, + width: '175px', + maxWidth: '175px', + wrap: true, + reorder: true, + hide: 1600 + }, + { + name: 'Термин', + id: 'term', + selector: (cst: IConstituenta) => cst.term?.resolved ?? cst.term?.raw ?? '', + width: '350px', + minWidth: '150px', + maxWidth: '350px', + wrap: true, + reorder: true + }, + { + name: 'Формальное определение', + id: 'expression', + selector: (cst: IConstituenta) => cst.definition?.formal ?? '', + minWidth: '300px', + maxWidth: '500px', + grow: 2, + wrap: true, + reorder: true + }, + { + name: 'Текстовое определение', + id: 'definition', + cell: (cst: IConstituenta) => ( +
+ {cst.definition?.text.resolved ?? cst.definition?.text.raw ?? ''} +
+ ), + minWidth: '200px', + grow: 2, + wrap: true, + reorder: true + }, + { + name: 'Конвенция / Комментарий', + id: 'convention', + cell: (cst: IConstituenta) =>
{cst.convention ?? ''}
, + minWidth: '100px', + wrap: true, + reorder: true, + hide: 1800 + } + ], []); return (
diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx index 52e0bb91..d4bb4b33 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx @@ -8,19 +8,22 @@ import Checkbox from '../../components/Common/Checkbox'; import ConceptSelect from '../../components/Common/ConceptSelect'; import ConceptTooltip from '../../components/Common/ConceptTooltip'; import Divider from '../../components/Common/Divider'; +import MiniButton from '../../components/Common/MiniButton'; import ConstituentaInfo from '../../components/Help/ConstituentaInfo'; +import CstClassInfo from '../../components/Help/CstClassInfo'; import CstStatusInfo from '../../components/Help/CstStatusInfo'; -import { ArrowsRotateIcon, HelpIcon } from '../../components/Icons'; +import { ArrowsRotateIcon, FilterCogIcon, HelpIcon } from '../../components/Icons'; import { useRSForm } from '../../context/RSFormContext'; import { useConceptTheme } from '../../context/ThemeContext'; import useLocalStorage from '../../hooks/useLocalStorage'; import { prefixes, resources } from '../../utils/constants'; import { Graph } from '../../utils/Graph'; -import { IConstituenta } from '../../utils/models'; -import { getCstStatusColor, getCstTypeColor, +import { CstType, IConstituenta } from '../../utils/models'; +import { getCstClassColor, getCstStatusColor, GraphColoringSelector, GraphLayoutSelector, - mapColoringLabels, mapLayoutLabels, mapStatusInfo + mapColoringLabels, mapLayoutLabels } from '../../utils/staticUI'; +import DlgGraphOptions from './DlgGraphOptions'; import ConstituentaTooltip from './elements/ConstituentaTooltip'; export type ColoringScheme = 'none' | 'status' | 'type'; @@ -28,12 +31,27 @@ const TREE_SIZE_MILESTONE = 50; function getCstNodeColor(cst: IConstituenta, coloringScheme: ColoringScheme, darkMode: boolean): string { if (coloringScheme === 'type') { - return getCstTypeColor(cst.cstType, darkMode); + return getCstClassColor(cst.cstClass, darkMode); } if (coloringScheme === 'status') { return getCstStatusColor(cst.status, darkMode); } - return ''; + return (darkMode ? '#7a8c9e' :'#7ca0ab'); +} + +export interface GraphEditorParams { + noHermits: boolean + noTransitive: boolean + noTemplates: boolean + + allowBase: boolean + allowStruct: boolean + allowTerm: boolean + allowAxiom: boolean + allowFunction: boolean + allowPredicate: boolean + allowConstant: boolean + allowTheorem: boolean } interface EditorTermGraphProps { @@ -49,13 +67,24 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) { const [ layout, setLayout ] = useLocalStorage('graph_layout', 'treeTd2d'); const [ coloringScheme, setColoringScheme ] = useLocalStorage('graph_coloring', 'none'); const [ orbit, setOrbit ] = useState(false); + const [ noHermits, setNoHermits ] = useLocalStorage('graph_no_hermits', true); const [ noTransitive, setNoTransitive ] = useLocalStorage('graph_no_transitive', false); + const [ noTemplates, setNoTemplates ] = useLocalStorage('graph_no_templates', false); + const [ allowBase, setAllowBase ] = useLocalStorage('graph_allow_base', true); + const [ allowStruct, setAllowStruct ] = useLocalStorage('graph_allow_struct', true); + const [ allowTerm, setAllowTerm ] = useLocalStorage('graph_allow_term', true); + const [ allowAxiom, setAllowAxiom ] = useLocalStorage('graph_allow_axiom', true); + const [ allowFunction, setAllowFunction ] = useLocalStorage('function', true); + const [ allowPredicate, setAllowPredicate ] = useLocalStorage('graph_allow_predicate', true); + const [ allowConstant, setAllowConstant ] = useLocalStorage('graph_allow_constant', true); + const [ allowTheorem, setAllowTheorem ] = useLocalStorage('graph_allow_theorem', true); const [ filtered, setFiltered ] = useState(new Graph()); const [ dismissed, setDismissed ] = useState([]); const [ selectedDismissed, setSelectedDismissed ] = useState([]); const graphRef = useRef(null); + const [showOptions, setShowOptions] = useState(false); const [hoverID, setHoverID] = useState(undefined); const hoverCst = useMemo( @@ -64,6 +93,19 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) { }, [schema?.items, hoverID]); const is3D = useMemo(() => layout.includes('3d'), [layout]); + const allowedTypes: CstType[] = useMemo( + () => { + const result: CstType[] = []; + if (allowBase) result.push(CstType.BASE); + if (allowStruct) result.push(CstType.STRUCTURED); + if (allowTerm) result.push(CstType.TERM); + if (allowAxiom) result.push(CstType.AXIOM); + if (allowFunction) result.push(CstType.FUNCTION); + if (allowPredicate) result.push(CstType.PREDICATE); + if (allowConstant) result.push(CstType.CONSTANT); + if (allowTheorem) result.push(CstType.THEOREM); + return result; + }, [allowBase, allowStruct, allowTerm, allowAxiom, allowFunction, allowPredicate, allowConstant, allowTheorem]); useEffect( () => { @@ -78,6 +120,20 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) { if (noTransitive) { graph.transitiveReduction(); } + if (noTemplates) { + schema.items.forEach(cst => { + if (cst.isTemplate) { + graph.foldNode(cst.id); + } + }); + } + if (allowedTypes.length < Object.values(CstType).length) { + schema.items.forEach(cst => { + if (!allowedTypes.includes(cst.cstType)) { + graph.foldNode(cst.id); + } + }); + } const newDismissed: number[] = []; schema.items.forEach(cst => { if (!graph.nodes.has(cst.id)) { @@ -88,7 +144,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) { setDismissed(newDismissed); setSelectedDismissed([]); setHoverID(undefined); - }, [schema, noHermits, noTransitive]); + }, [schema, noHermits, noTransitive, noTemplates, allowedTypes]); function toggleDismissed(cstID: number) { setSelectedDismissed(prev => { @@ -153,7 +209,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) { focusOnSelect: false }); - const handleCenter = useCallback( + const handleRecreate = useCallback( () => { graphRef.current?.resetControls(); graphRef.current?.centerGraph(); @@ -180,6 +236,41 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) { if (onNodeClick) onNodeClick(node); }, [onNodeClick, selections, onOpenEdit]); + function getOptions() { + return { + noHermits: noHermits, + noTemplates: noTemplates, + noTransitive: noTransitive, + + allowBase: allowBase, + allowStruct: allowStruct, + allowTerm: allowTerm, + allowAxiom: allowAxiom, + allowFunction: allowFunction, + allowPredicate: allowPredicate, + allowConstant: allowConstant, + allowTheorem: allowTheorem + } + } + + const handleChangeOptions = useCallback( + (params: GraphEditorParams) => { + setNoHermits(params.noHermits); + setNoTransitive(params.noTransitive); + setNoTemplates(params.noTemplates); + + setAllowBase(params.allowBase); + setAllowStruct(params.allowStruct); + setAllowTerm(params.allowTerm); + setAllowAxiom(params.allowAxiom); + setAllowFunction(params.allowFunction); + setAllowPredicate(params.allowPredicate); + setAllowConstant(params.allowConstant); + setAllowTheorem(params.allowTheorem); + }, [setNoHermits, setNoTransitive, setNoTemplates, + setAllowBase, setAllowStruct, setAllowTerm, setAllowAxiom, setAllowFunction, + setAllowPredicate, setAllowConstant, setAllowTheorem]); + const canvasWidth = useMemo( () => { return 'calc(100vw - 14.6rem)'; @@ -199,6 +290,12 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) { return (
+ {showOptions && + setShowOptions(false)} + initial={getOptions()} + onConfirm={handleChangeOptions} + />}
{hoverCst &&
@@ -209,11 +306,11 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
}
); diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta.tsx index 459ac9d4..bbe72f9e 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta.tsx @@ -6,7 +6,7 @@ import Divider from '../../components/Common/Divider'; import MiniButton from '../../components/Common/MiniButton'; import SubmitButton from '../../components/Common/SubmitButton'; import TextArea from '../../components/Common/TextArea'; -import CstStatusInfo from '../../components/Help/CstStatusInfo'; +import CstStatusInfo from '../../components/Help/InfoCstStatus'; import { DumpBinIcon, HelpIcon, SaveIcon, SmallPlusIcon } from '../../components/Icons'; import { useRSForm } from '../../context/RSFormContext'; import { type CstType, EditMode, ICstUpdateData, SyntaxTree } from '../../utils/models'; diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorItems.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorItems.tsx index 9fff700d..3713dd73 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorItems.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorItems.tsx @@ -5,7 +5,7 @@ import Button from '../../components/Common/Button'; import ConceptDataTable from '../../components/Common/ConceptDataTable'; import ConceptTooltip from '../../components/Common/ConceptTooltip'; import Divider from '../../components/Common/Divider'; -import CstStatusInfo from '../../components/Help/CstStatusInfo'; +import CstStatusInfo from '../../components/Help/InfoCstStatus'; import { ArrowDownIcon, ArrowsRotateIcon, ArrowUpIcon, DumpBinIcon, HelpIcon, SmallPlusIcon } from '../../components/Icons'; import { useRSForm } from '../../context/RSFormContext'; import { useConceptTheme } from '../../context/ThemeContext'; diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx index b395ca0c..5c34b2b4 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx @@ -9,9 +9,9 @@ import ConceptSelect from '../../components/Common/ConceptSelect'; import ConceptTooltip from '../../components/Common/ConceptTooltip'; import Divider from '../../components/Common/Divider'; import MiniButton from '../../components/Common/MiniButton'; -import ConstituentaInfo from '../../components/Help/ConstituentaInfo'; -import CstClassInfo from '../../components/Help/CstClassInfo'; -import CstStatusInfo from '../../components/Help/CstStatusInfo'; +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 { useRSForm } from '../../context/RSFormContext'; import { useConceptTheme } from '../../context/ThemeContext'; @@ -304,7 +304,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
{hoverCst &&
- @@ -405,7 +405,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) { - + diff --git a/rsconcept/frontend/src/pages/RSFormPage/elements/ConstituentaTooltip.tsx b/rsconcept/frontend/src/pages/RSFormPage/elements/ConstituentaTooltip.tsx index 45fd6966..0fc442f9 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/elements/ConstituentaTooltip.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/elements/ConstituentaTooltip.tsx @@ -1,5 +1,5 @@ import ConceptTooltip from '../../../components/Common/ConceptTooltip'; -import ConstituentaInfo from '../../../components/Help/ConstituentaInfo'; +import InfoConstituenta from '../../../components/Help/InfoConstituenta'; import { IConstituenta } from '../../../utils/models'; interface ConstituentaTooltipProps { @@ -13,7 +13,7 @@ function ConstituentaTooltip({ data, anchor }: ConstituentaTooltipProps) { anchorSelect={anchor} className='max-w-[25rem] min-w-[25rem]' > - + ); } From 101631a8be2cdb0b0e351b6dd4a8668b82f490c8 Mon Sep 17 00:00:00 2001 From: IRBorisov <8611739+IRBorisov@users.noreply.github.com> Date: Wed, 16 Aug 2023 10:46:22 +0300 Subject: [PATCH 08/10] Prevent text selections on control elements --- .../frontend/src/components/Common/Button.tsx | 2 +- .../src/components/Common/SubmitButton.tsx | 2 +- rsconcept/frontend/src/components/Footer.tsx | 2 +- .../src/components/Navigation/Navigation.tsx | 2 +- .../src/pages/RSFormPage/EditorItems.tsx | 2 +- .../src/pages/RSFormPage/EditorTermGraph.tsx | 25 +++++++++++++++++-- .../frontend/src/pages/RSFormPage/RSTabs.tsx | 2 +- .../pages/RSFormPage/elements/StatusBar.tsx | 2 +- .../elements/ViewSideConstituents.tsx | 2 +- 9 files changed, 31 insertions(+), 10 deletions(-) diff --git a/rsconcept/frontend/src/components/Common/Button.tsx b/rsconcept/frontend/src/components/Common/Button.tsx index 0e2f9524..c4458d47 100644 --- a/rsconcept/frontend/src/components/Common/Button.tsx +++ b/rsconcept/frontend/src/components/Common/Button.tsx @@ -27,7 +27,7 @@ function Button({ disabled={disabled ?? loading} onClick={onClick} title={tooltip} - className={`inline-flex items-center gap-2 align-middle justify-center ${padding} ${borderClass} ${colorClass} ${widthClass} ${cursor}`} + className={`inline-flex items-center gap-2 align-middle justify-center select-none ${padding} ${borderClass} ${colorClass} ${widthClass} ${cursor}`} {...props} > {icon && {icon}} diff --git a/rsconcept/frontend/src/components/Common/SubmitButton.tsx b/rsconcept/frontend/src/components/Common/SubmitButton.tsx index 84ef8293..04b6b66f 100644 --- a/rsconcept/frontend/src/components/Common/SubmitButton.tsx +++ b/rsconcept/frontend/src/components/Common/SubmitButton.tsx @@ -8,7 +8,7 @@ interface SubmitButtonProps { function SubmitButton({ text = 'ОК', icon, disabled, loading = false }: SubmitButtonProps) { return ( } {!noNavigation &&
-
+
diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorItems.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorItems.tsx index 3713dd73..19fc76ce 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorItems.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorItems.tsx @@ -247,7 +247,7 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps) return (
diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx index 5c34b2b4..dc64f2a7 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx @@ -212,6 +212,11 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) { focusOnSelect: false }); + const allSelected: string[] = useMemo( + () => { + return [ ... selectedDismissed.map(id => String(id)), ... selections]; + }, [selectedDismissed, selections]); + const handleRecreate = useCallback( () => { graphRef.current?.resetControls(); @@ -239,6 +244,12 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) { if (onNodeClick) onNodeClick(node); }, [onNodeClick, selections, onOpenEdit]); + const handleCanvasClick = useCallback( + (event: MouseEvent) => { + setSelectedDismissed([]); + if (onCanvasClick) onCanvasClick(event); + }, [onCanvasClick]); + function getOptions() { return { noHermits: noHermits, @@ -301,7 +312,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) { initial={getOptions()} onConfirm={handleChangeOptions} />} -
+
{hoverCst &&
} + +
+ Выбраны + + {allSelected.length} из {schema?.stats?.count_all ?? 0} + +
+ + +
-
+
From ba4e8219c5ae0b37878f197daf813b4ad0952f5f Mon Sep 17 00:00:00 2001 From: IRBorisov <8611739+IRBorisov@users.noreply.github.com> Date: Wed, 16 Aug 2023 13:48:46 +0300 Subject: [PATCH 10/10] Minor UI fixes --- .../pages/RSFormPage/EditorConstituenta.tsx | 14 +++--- .../src/pages/RSFormPage/EditorTermGraph.tsx | 44 +++++++++++-------- rsconcept/frontend/src/utils/staticUI.ts | 2 +- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta.tsx index bbe72f9e..b71658d5 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta.tsx @@ -146,19 +146,19 @@ function EditorConstituenta({ activeID, onShowAST, onCreateCst, onOpenEdit, onDe
- } - /> } /> -
+ } + /> +
diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx index ca893204..781dd72d 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph.tsx @@ -345,14 +345,14 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra }, [selectedDismissed]); return ( -
+
{showOptions && setShowOptions(false)} initial={getOptions()} onConfirm={handleChangeOptions} />} -
+
{hoverCst &&
- - -