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) {
}
}
+ icon={
}
dense
- tooltip='Центрировать изображение'
+ tooltip='Настройки фильтрации узлов и связей'
widthClass='h-full'
- onClick={handleCenter}
+ onClick={() => setShowOptions(true)}
/>
setOrbit(event.target.checked) }
/>
- setNoHermits(event.target.checked) }
- />
{dismissed.map(cstID => {
const cst = schema!.items.find(cst => cst.id === cstID)!;
- const info = mapStatusInfo.get(cst.status)!;
+ const adjustedColoring = coloringScheme === 'none' ? 'status': coloringScheme;
return (<>
toggleDismissed(cstID)}
onDoubleClick={() => onOpenEdit(cstID)}
>
@@ -283,28 +375,31 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
className='relative border-t border-r'
style={{width: canvasWidth, height: canvasHeight, borderBottomWidth: noNavigation ? '1px': ''}}
>
-
-
+
+
+
+
+
}
+ tooltip='Пересоздать граф'
+ onClick={handleRecreate}
+ />
Настройка графа
Цвет - выбор правила покраски узлов
-
Скрытые конституенты окрашены в цвет статуса
Граф - выбор модели расположения узлов
Удалить несвязанные - в графе не отображаются одинокие вершины
Транзитивная редукция - в графе устраняются транзитивные пути
-
Горячие клавиши
-
Двойной клик - редактирование конституенты
-
Delete - удаление конституент
-
Alt + 1-6,Q,W - добавление конституент
+
-
+
{
expect(graph.hasEdge(4, 1)).toBeFalsy();
});
+ test('folding node redirectes edges', () => {
+ const graph = new Graph([[1, 3], [2, 3], [3, 4], [3, 5], [3, 3]]);
+ graph.foldNode(3);
+ expect(graph.hasNode(3)).toBeFalsy();
+ expect(graph.hasEdge(1, 4)).toBeTruthy();
+ expect(graph.hasEdge(1, 5)).toBeTruthy();
+ expect(graph.hasEdge(2, 4)).toBeTruthy();
+ expect(graph.hasEdge(2, 5)).toBeTruthy();
+ });
+
test('removing isolated nodes', () => {
const graph = new Graph([[9, 1], [9, 2], [2, 1], [4, 3], [5, 9], [7], [8]]);
graph.removeIsolated()
@@ -53,7 +63,7 @@ describe('Testing Graph editing', () => {
test('transitive reduction', () => {
const graph = new Graph([[1, 3], [1, 2], [2, 3]]);
- graph.transitiveReduction()
+ graph.transitiveReduction();
expect(graph.hasEdge(1, 2)).toBeTruthy();
expect(graph.hasEdge(2, 3)).toBeTruthy();
expect(graph.hasEdge(1, 3)).toBeFalsy();
diff --git a/rsconcept/frontend/src/utils/Graph.ts b/rsconcept/frontend/src/utils/Graph.ts
index c060c3d9..4e873e6d 100644
--- a/rsconcept/frontend/src/utils/Graph.ts
+++ b/rsconcept/frontend/src/utils/Graph.ts
@@ -67,6 +67,10 @@ export class Graph {
return node;
}
+ hasNode(target: number): boolean {
+ return !!this.nodes.get(target);
+ }
+
removeNode(target: number): GraphNode | null {
const nodeToRemove = this.nodes.get(target);
if (!nodeToRemove) {
@@ -80,6 +84,19 @@ export class Graph {
return nodeToRemove;
}
+ foldNode(target: number): GraphNode | null {
+ const nodeToRemove = this.nodes.get(target);
+ if (!nodeToRemove) {
+ return null;
+ }
+ nodeToRemove.inputs.forEach(input => {
+ nodeToRemove.outputs.forEach(output => {
+ this.addEdge(input, output);
+ })
+ });
+ return this.removeNode(target);
+ }
+
removeIsolated(): GraphNode[] {
const result: GraphNode[] = [];
this.nodes.forEach(node => {
diff --git a/rsconcept/frontend/src/utils/models.ts b/rsconcept/frontend/src/utils/models.ts
index c26f2214..62a7e228 100644
--- a/rsconcept/frontend/src/utils/models.ts
+++ b/rsconcept/frontend/src/utils/models.ts
@@ -101,6 +101,13 @@ export enum CstType {
THEOREM = 'theorem'
}
+export enum CstClass {
+ BASIC = 'basic',
+ DERIVED = 'derived',
+ STATEMENT = 'statement',
+ TEMPLATE = 'template'
+}
+
export interface IConstituenta {
id: number
alias: string
@@ -118,7 +125,9 @@ export interface IConstituenta {
resolved: string
}
}
+ cstClass: CstClass
status: ExpressionStatus
+ isTemplate: boolean
parse: {
status: ParsingStatus
valueClass: ValueClass
@@ -230,12 +239,12 @@ export enum EditMode {
// RSExpression status
export enum ExpressionStatus {
- UNDEFINED = 0,
- UNKNOWN,
- INCORRECT,
- INCALCULABLE,
- PROPERTY,
- VERIFIED
+ UNDEFINED = 'undefined',
+ UNKNOWN = 'unknown',
+ INCORRECT = 'incorrect',
+ INCALCULABLE = 'incalculable',
+ PROPERTY = 'property',
+ VERIFIED = 'verified'
}
// Dependency mode for schema analysis
@@ -274,7 +283,28 @@ export function inferStatus(parse?: ParsingStatus, value?: ValueClass): Expressi
if (value === ValueClass.PROPERTY) {
return ExpressionStatus.PROPERTY;
}
- return ExpressionStatus.VERIFIED
+ return ExpressionStatus.VERIFIED;
+}
+
+export function inferTemplate(expression: string): boolean {
+ const match = expression.match(/R\d+/g);
+ return (match && match?.length > 0) ?? false;
+}
+
+export function inferClass(type: CstType, isTemplate: boolean): CstClass {
+ if (isTemplate) {
+ return CstClass.TEMPLATE;
+ }
+ switch (type) {
+ case CstType.BASE: return CstClass.BASIC;
+ case CstType.CONSTANT: return CstClass.BASIC;
+ case CstType.STRUCTURED: return CstClass.BASIC;
+ case CstType.TERM: return CstClass.DERIVED;
+ case CstType.FUNCTION: return CstClass.DERIVED;
+ case CstType.AXIOM: return CstClass.STATEMENT;
+ case CstType.PREDICATE: return CstClass.DERIVED;
+ case CstType.THEOREM: return CstClass.STATEMENT;
+ }
}
export function extractGlobals(expression: string): Set {
@@ -336,6 +366,8 @@ export function LoadRSFormData(schema: IRSFormData): IRSForm {
}
result.items.forEach(cst => {
cst.status = inferStatus(cst.parse.status, cst.parse.valueClass);
+ cst.isTemplate = inferTemplate(cst.definition.formal);
+ cst.cstClass = inferClass(cst.cstType, cst.isTemplate);
result.graph.addNode(cst.id);
const dependencies = extractGlobals(cst.definition.formal);
dependencies.forEach(value => {
diff --git a/rsconcept/frontend/src/utils/staticUI.ts b/rsconcept/frontend/src/utils/staticUI.ts
index 1045e83e..82ae2a42 100644
--- a/rsconcept/frontend/src/utils/staticUI.ts
+++ b/rsconcept/frontend/src/utils/staticUI.ts
@@ -2,7 +2,7 @@ import { LayoutTypes } from 'reagraph';
import { ColoringScheme } from '../pages/RSFormPage/EditorTermGraph';
import { resolveErrorClass,RSErrorClass, RSErrorType, TokenID } from './enums';
-import { CstMatchMode, CstType, DependencyMode,ExpressionStatus, IConstituenta,
+import { CstClass, CstMatchMode, CstType, DependencyMode, ExpressionStatus, IConstituenta,
IFunctionArg,IRSErrorDescription, IRSForm,
ISyntaxTreeNode, ParsingStatus, ValueClass
} from './models';
@@ -12,7 +12,7 @@ export interface IRSButtonData {
tooltip: string
}
-export interface IStatusInfo {
+export interface IFormatInfo {
text: string
color: string
tooltip: string
@@ -245,10 +245,6 @@ 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;
@@ -311,28 +307,15 @@ export const mapLayoutLabels: Map = new Map([
export const mapColoringLabels: Map = new Map([
['none', 'Цвет: моно'],
['status', 'Цвет: статус'],
- ['type', 'Цвет: тип'],
+ ['type', 'Цвет: класс'],
]);
export const GraphColoringSelector: {value: ColoringScheme, label: string}[] = [
{ value: 'none', label: 'Цвет: моно'},
{ value: 'status', label: 'Цвет: статус'},
- { value: 'type', 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';
@@ -344,7 +327,7 @@ export function getCstStatusColor(status: ExpressionStatus, darkMode: boolean):
}
}
-export const mapStatusInfo: Map = new Map([
+export const mapStatusInfo: Map = new Map([
[ ExpressionStatus.VERIFIED, {
text: 'ок',
color: 'bg-[#aaff80] dark:bg-[#2b8000]',
@@ -377,6 +360,38 @@ export const mapStatusInfo: Map = new Map([
}],
]);
+export function getCstClassColor(cstClass: CstClass, darkMode: boolean): string {
+ switch (cstClass) {
+ case CstClass.TEMPLATE: return darkMode ? '#36899e': '#a5e9fa';
+ case CstClass.BASIC: return darkMode ? '#2b8000': '#aaff80';
+ case CstClass.DERIVED: return darkMode ? '#1e00b3': '#b3bdff';
+ case CstClass.STATEMENT: return darkMode ? '#592b2b': '#ffc9c9';
+ }
+}
+
+export const mapCstClassInfo: Map = new Map([
+ [ CstClass.BASIC, {
+ text: 'базовый',
+ color: 'bg-[#aaff80] dark:bg-[#2b8000]',
+ tooltip: 'неопределяемое понятие, требует конвенции'
+ }],
+ [ CstClass.DERIVED, {
+ text: 'производный',
+ color: 'bg-[#b3bdff] dark:bg-[#1e00b3]',
+ tooltip: 'выводимое понятие, задаваемое определением'
+ }],
+ [ CstClass.STATEMENT, {
+ text: 'утверждение',
+ color: 'bg-[#ffc9c9] dark:bg-[#592b2b]',
+ tooltip: 'неопределяемое понятие, требует конвенции'
+ }],
+ [ CstClass.TEMPLATE, {
+ text: 'шаблон',
+ color: 'bg-[#a5e9fa] dark:bg-[#36899e]',
+ tooltip: 'параметризованный шаблон определения'
+ }],
+]);
+
export function createAliasFor(type: CstType, schema: IRSForm): string {
const prefix = getCstTypePrefix(type);
if (!schema.items || schema.items.length <= 0) {
@@ -411,6 +426,8 @@ export function getMockConstituenta(id: number, alias: string, type: CstType, co
}
},
status: ExpressionStatus.INCORRECT,
+ isTemplate: false,
+ cstClass: CstClass.DERIVED,
parse: {
status: ParsingStatus.INCORRECT,
valueClass: ValueClass.INVALID,
From b55ad31147fe6b55f1069762e128f648c86c5106 Mon Sep 17 00:00:00 2001
From: IRBorisov <8611739+IRBorisov@users.noreply.github.com>
Date: Wed, 16 Aug 2023 00:50:27 +0300
Subject: [PATCH 06/10] Fix graph UI
---
.../src/pages/RSFormPage/DlgGraphOptions.tsx | 9 ++++++
.../src/pages/RSFormPage/EditorTermGraph.tsx | 28 +++++++++++++------
2 files changed, 28 insertions(+), 9 deletions(-)
diff --git a/rsconcept/frontend/src/pages/RSFormPage/DlgGraphOptions.tsx b/rsconcept/frontend/src/pages/RSFormPage/DlgGraphOptions.tsx
index ddb20b23..e946e815 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/DlgGraphOptions.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/DlgGraphOptions.tsx
@@ -16,6 +16,7 @@ function DlgGraphOptions({ hideWindow, initial, onConfirm }:DlgGraphOptionsProps
const [ noHermits, setNoHermits ] = useState(true);
const [ noTransitive, setNoTransitive ] = useState(false);
const [ noTemplates, setNoTemplates ] = useState(true);
+ const [ noTerms, setNoTerms ] = useState(true);
const [ allowBase, setAllowBase ] = useState(true);
const [ allowStruct, setAllowStruct ] = useState(true);
@@ -31,6 +32,7 @@ function DlgGraphOptions({ hideWindow, initial, onConfirm }:DlgGraphOptionsProps
noHermits: noHermits,
noTransitive: noTransitive,
noTemplates: noTemplates,
+ noTerms: noTerms,
allowBase: allowBase,
allowStruct: allowStruct,
@@ -52,6 +54,7 @@ function DlgGraphOptions({ hideWindow, initial, onConfirm }:DlgGraphOptionsProps
setNoHermits(initial.noHermits);
setNoTransitive(initial.noTransitive);
setNoTemplates(initial.noTemplates);
+ setNoTerms(initial.noTerms);
setAllowBase(initial.allowBase);
setAllowStruct(initial.allowStruct);
@@ -74,6 +77,12 @@ function DlgGraphOptions({ hideWindow, initial, onConfirm }:DlgGraphOptionsProps
Преобразования
+
setNoTerms(event.target.checked) }
+ />
{
@@ -205,7 +207,8 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
nodes,
edges,
type: 'multi', // 'single' | 'multi' | 'multiModifier'
- pathSelectionType: 'all',
+ pathSelectionType: 'out',
+ pathHoverType: 'all',
focusOnSelect: false
});
@@ -241,6 +244,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
noHermits: noHermits,
noTemplates: noTemplates,
noTransitive: noTransitive,
+ noTerms: noTerms,
allowBase: allowBase,
allowStruct: allowStruct,
@@ -258,6 +262,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
setNoHermits(params.noHermits);
setNoTransitive(params.noTransitive);
setNoTemplates(params.noTemplates);
+ setNoTerms(params.noTerms);
setAllowBase(params.allowBase);
setAllowStruct(params.allowStruct);
@@ -269,7 +274,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
setAllowTheorem(params.allowTheorem);
}, [setNoHermits, setNoTransitive, setNoTemplates,
setAllowBase, setAllowStruct, setAllowTerm, setAllowAxiom, setAllowFunction,
- setAllowPredicate, setAllowConstant, setAllowTheorem]);
+ setAllowPredicate, setAllowConstant, setAllowTheorem, setNoTerms]);
const canvasWidth = useMemo(
() => {
@@ -331,16 +336,21 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
onChange={data => { setLayout(data.length > 0 ? data[0].value : GraphLayoutSelector[0].value); }}
/>
setOrbit(event.target.checked) }
+ label='Скрыть текст'
+ value={noTerms}
+ onChange={ event => setNoTerms(event.target.checked) }
/>
setNoTransitive(event.target.checked) }
/>
+ setOrbit(event.target.checked) }
+ />
@@ -354,7 +364,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
toggleDismissed(cstID)}
onDoubleClick={() => onOpenEdit(cstID)}
From d50afb290e1c92e97ad176b530e71c155398c65c Mon Sep 17 00:00:00 2001
From: IRBorisov <8611739+IRBorisov@users.noreply.github.com>
Date: Wed, 16 Aug 2023 10:11:22 +0300
Subject: [PATCH 07/10] Refactor help UI
---
...stituentaInfo.tsx => InfoConstituenta.tsx} | 6 ++---
.../{CstClassInfo.tsx => InfoCstClass.tsx} | 6 ++---
.../{CstStatusInfo.tsx => InfoCstStatus.tsx} | 6 ++---
.../src/components/Navigation/Navigation.tsx | 4 +--
.../Navigation/NavigationButton.tsx | 10 +++++---
.../src/components/Navigation/UserTools.tsx | 25 ++++++++++++++++---
.../pages/RSFormPage/EditorConstituenta.tsx | 2 +-
.../src/pages/RSFormPage/EditorItems.tsx | 2 +-
.../src/pages/RSFormPage/EditorTermGraph.tsx | 10 ++++----
.../elements/ConstituentaTooltip.tsx | 4 +--
10 files changed, 47 insertions(+), 28 deletions(-)
rename rsconcept/frontend/src/components/Help/{ConstituentaInfo.tsx => InfoConstituenta.tsx} (85%)
rename rsconcept/frontend/src/components/Help/{CstClassInfo.tsx => InfoCstClass.tsx} (85%)
rename rsconcept/frontend/src/components/Help/{CstStatusInfo.tsx => InfoCstStatus.tsx} (84%)
diff --git a/rsconcept/frontend/src/components/Help/ConstituentaInfo.tsx b/rsconcept/frontend/src/components/Help/InfoConstituenta.tsx
similarity index 85%
rename from rsconcept/frontend/src/components/Help/ConstituentaInfo.tsx
rename to rsconcept/frontend/src/components/Help/InfoConstituenta.tsx
index 07e34d2e..5c80d48c 100644
--- a/rsconcept/frontend/src/components/Help/ConstituentaInfo.tsx
+++ b/rsconcept/frontend/src/components/Help/InfoConstituenta.tsx
@@ -1,12 +1,12 @@
import { IConstituenta } from '../../utils/models';
import { getCstTypificationLabel } from '../../utils/staticUI';
-interface ConstituentaInfoProps
+interface InfoConstituentaProps
extends React.HTMLAttributes
{
data: IConstituenta
}
-function ConstituentaInfo({ data, ...props }: ConstituentaInfoProps) {
+function InfoConstituenta({ data, ...props }: InfoConstituentaProps) {
return (
Конституента {data.alias}
@@ -19,4 +19,4 @@ function ConstituentaInfo({ data, ...props }: ConstituentaInfoProps) {
);
}
-export default ConstituentaInfo;
\ No newline at end of file
+export default InfoConstituenta;
\ No newline at end of file
diff --git a/rsconcept/frontend/src/components/Help/CstClassInfo.tsx b/rsconcept/frontend/src/components/Help/InfoCstClass.tsx
similarity index 85%
rename from rsconcept/frontend/src/components/Help/CstClassInfo.tsx
rename to rsconcept/frontend/src/components/Help/InfoCstClass.tsx
index fdcefd0f..d1d39be3 100644
--- a/rsconcept/frontend/src/components/Help/CstClassInfo.tsx
+++ b/rsconcept/frontend/src/components/Help/InfoCstClass.tsx
@@ -1,11 +1,11 @@
import { prefixes } from '../../utils/constants';
import { mapCstClassInfo } from '../../utils/staticUI';
-interface CstClassInfoProps {
+interface InfoCstClassProps {
title?: string
}
-function CstClassInfo({ title }: CstClassInfoProps) {
+function InfoCstClass({ title }: InfoCstClassProps) {
return (
{ title &&
{title}
}
@@ -26,4 +26,4 @@ function CstClassInfo({ title }: CstClassInfoProps) {
);
}
-export default CstClassInfo;
+export default InfoCstClass;
diff --git a/rsconcept/frontend/src/components/Help/CstStatusInfo.tsx b/rsconcept/frontend/src/components/Help/InfoCstStatus.tsx
similarity index 84%
rename from rsconcept/frontend/src/components/Help/CstStatusInfo.tsx
rename to rsconcept/frontend/src/components/Help/InfoCstStatus.tsx
index 3ae1f379..d2e05005 100644
--- a/rsconcept/frontend/src/components/Help/CstStatusInfo.tsx
+++ b/rsconcept/frontend/src/components/Help/InfoCstStatus.tsx
@@ -1,11 +1,11 @@
import { prefixes } from '../../utils/constants';
import { mapStatusInfo } from '../../utils/staticUI';
-interface CstStatusInfoProps {
+interface InfoCstStatusProps {
title?: string
}
-function CstStatusInfo({ title }: CstStatusInfoProps) {
+function InfoCstStatus({ title }: InfoCstStatusProps) {
return (
{ title &&
{title}
}
@@ -26,4 +26,4 @@ function CstStatusInfo({ title }: CstStatusInfoProps) {
);
}
-export default CstStatusInfo;
+export default InfoCstStatus;
diff --git a/rsconcept/frontend/src/components/Navigation/Navigation.tsx b/rsconcept/frontend/src/components/Navigation/Navigation.tsx
index 695157c8..3ecf0f4f 100644
--- a/rsconcept/frontend/src/components/Navigation/Navigation.tsx
+++ b/rsconcept/frontend/src/components/Navigation/Navigation.tsx
@@ -1,6 +1,5 @@
import { useNavigate } from 'react-router-dom';
-import { useAuth } from '../../context/AuthContext';
import { useConceptTheme } from '../../context/ThemeContext';
import { EducationIcon, LibraryIcon } from '../Icons';
import Logo from './Logo'
@@ -10,7 +9,6 @@ import UserMenu from './UserMenu';
import UserTools from './UserTools';
function Navigation () {
- const { user } = useAuth();
const navigate = useNavigate();
const { noNavigation, toggleNoNavigation } = useConceptTheme();
@@ -42,7 +40,7 @@ function Navigation () {
- {user &&
}
+
} description='Общие схемы' onClick={navigateCommon} />
} description='Справка' onClick={navigateHelp} />
diff --git a/rsconcept/frontend/src/components/Navigation/NavigationButton.tsx b/rsconcept/frontend/src/components/Navigation/NavigationButton.tsx
index f043afbd..26d71a99 100644
--- a/rsconcept/frontend/src/components/Navigation/NavigationButton.tsx
+++ b/rsconcept/frontend/src/components/Navigation/NavigationButton.tsx
@@ -1,15 +1,17 @@
interface NavigationButtonProps {
+ id?: string
icon: React.ReactNode
- description: string
+ description?: string
colorClass?: string
- onClick: () => void
+ onClick?: () => void
}
const defaultColors = 'text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white'
-function NavigationButton({ icon, description, colorClass = defaultColors, onClick }: NavigationButtonProps) {
+function NavigationButton({ id, icon, description, colorClass = defaultColors, onClick }: NavigationButtonProps) {
return (
-
);
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 (