mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Merge branch 'main' of https://github.com/IRBorisov/ConceptPortal
This commit is contained in:
commit
1f9e761565
5
TODO.txt
5
TODO.txt
|
@ -6,7 +6,6 @@ For more specific TODOs see comments in code
|
||||||
- home page
|
- home page
|
||||||
- manuals
|
- 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 provide initial data for testing
|
||||||
- USe migtation/fixtures to load example common data
|
- USe migtation/fixtures to load example common data
|
||||||
|
|
||||||
|
- create custom Select component
|
||||||
|
- reload react-data-table-component
|
||||||
|
|
||||||
[deployment]
|
[deployment]
|
||||||
- HTTPS
|
|
||||||
- database backup daemon
|
- database backup daemon
|
||||||
- logs collection
|
- logs collection
|
||||||
- status dashboard for servers
|
- status dashboard for servers
|
||||||
|
|
|
@ -22,14 +22,14 @@ function App () {
|
||||||
const scrollWindowSize = useMemo(
|
const scrollWindowSize = useMemo(
|
||||||
() => {
|
() => {
|
||||||
return !noNavigation ?
|
return !noNavigation ?
|
||||||
'max-h-[calc(100vh-4.5rem)]'
|
'calc(100vh - 4.5rem)'
|
||||||
: 'max-h-[100vh]';
|
: '100vh';
|
||||||
}, [noNavigation]);
|
}, [noNavigation]);
|
||||||
const mainSize = useMemo(
|
const mainSize = useMemo(
|
||||||
() => {
|
() => {
|
||||||
return !noNavigation ?
|
return !noNavigation ?
|
||||||
'min-h-[calc(100vh-12rem)]'
|
'calc(100vh - 12rem)'
|
||||||
: 'min-h-[calc(100vh-8rem)] ';
|
: '100vh';
|
||||||
}, [noNavigation]);
|
}, [noNavigation]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -42,8 +42,8 @@ function App () {
|
||||||
pauseOnFocusLoss={false}
|
pauseOnFocusLoss={false}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className={`${scrollWindowSize} overflow-auto`}>
|
<div className='overflow-auto' style={{maxHeight: scrollWindowSize}}>
|
||||||
<main className={`${mainSize} px-2`}>
|
<main className='px-2' style={{minHeight: mainSize}}>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path='/' element={ <HomePage/>} />
|
<Route path='/' element={ <HomePage/>} />
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ function App () {
|
||||||
<Route path='*' element={ <NotFoundPage/>} />
|
<Route path='*' element={ <NotFoundPage/>} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</main>
|
</main>
|
||||||
<Footer />
|
{!noNavigation && <Footer />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -27,7 +27,7 @@ function Button({
|
||||||
disabled={disabled ?? loading}
|
disabled={disabled ?? loading}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
title={tooltip}
|
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}
|
{...props}
|
||||||
>
|
>
|
||||||
{icon && <span>{icon}</span>}
|
{icon && <span>{icon}</span>}
|
||||||
|
|
|
@ -8,7 +8,7 @@ interface SubmitButtonProps {
|
||||||
function SubmitButton({ text = 'ОК', icon, disabled, loading = false }: SubmitButtonProps) {
|
function SubmitButton({ text = 'ОК', icon, disabled, loading = false }: SubmitButtonProps) {
|
||||||
return (
|
return (
|
||||||
<button type='submit'
|
<button type='submit'
|
||||||
className={`px-4 py-2 inline-flex items-center gap-2 align-middle justify-center font-bold disabled:cursor-not-allowed border rounded clr-btn-primary ${loading ? ' cursor-progress' : ''}`}
|
className={`px-4 py-2 inline-flex items-center gap-2 align-middle justify-center font-bold select-none disabled:cursor-not-allowed border rounded clr-btn-primary ${loading ? ' cursor-progress' : ''}`}
|
||||||
disabled={disabled ?? loading}
|
disabled={disabled ?? loading}
|
||||||
>
|
>
|
||||||
{icon && <span>{icon}</span>}
|
{icon && <span>{icon}</span>}
|
||||||
|
|
|
@ -10,7 +10,7 @@ function Footer() {
|
||||||
<div className='px-4 underline whitespace-nowrap'>
|
<div className='px-4 underline whitespace-nowrap'>
|
||||||
<Link to='/manuals' tabIndex={-1}>Справка</Link> <br/>
|
<Link to='/manuals' tabIndex={-1}>Справка</Link> <br/>
|
||||||
<Link to='/library?filter=common' tabIndex={-1}>Библиотека КС</Link> <br/>
|
<Link to='/library?filter=common' tabIndex={-1}>Библиотека КС</Link> <br/>
|
||||||
<a href={urls.gitrepo} className='flex'>
|
<a href={urls.gitrepo} className='flex' tabIndex={-1}>
|
||||||
<GithubIcon />
|
<GithubIcon />
|
||||||
<span className='ml-1'>Репозиторий</span>
|
<span className='ml-1'>Репозиторий</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
22
rsconcept/frontend/src/components/Help/InfoConstituenta.tsx
Normal file
22
rsconcept/frontend/src/components/Help/InfoConstituenta.tsx
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { IConstituenta } from '../../utils/models';
|
||||||
|
import { getCstTypificationLabel } from '../../utils/staticUI';
|
||||||
|
|
||||||
|
interface InfoConstituentaProps
|
||||||
|
extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
|
data: IConstituenta
|
||||||
|
}
|
||||||
|
|
||||||
|
function InfoConstituenta({ data, ...props }: InfoConstituentaProps) {
|
||||||
|
return (
|
||||||
|
<div {...props}>
|
||||||
|
<h1>Конституента {data.alias}</h1>
|
||||||
|
<p><b>Типизация: </b>{getCstTypificationLabel(data)}</p>
|
||||||
|
<p><b>Термин: </b>{data.term.resolved || data.term.raw}</p>
|
||||||
|
{data.definition.formal && <p><b>Выражение: </b>{data.definition.formal}</p>}
|
||||||
|
{data.definition.text.resolved && <p><b>Определение: </b>{data.definition.text.resolved}</p>}
|
||||||
|
{data.convention && <p><b>Конвенция: </b>{data.convention}</p>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InfoConstituenta;
|
29
rsconcept/frontend/src/components/Help/InfoCstClass.tsx
Normal file
29
rsconcept/frontend/src/components/Help/InfoCstClass.tsx
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { prefixes } from '../../utils/constants';
|
||||||
|
import { mapCstClassInfo } from '../../utils/staticUI';
|
||||||
|
|
||||||
|
interface InfoCstClassProps {
|
||||||
|
title?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function InfoCstClass({ title }: InfoCstClassProps) {
|
||||||
|
return (
|
||||||
|
<div className='flex flex-col gap-1'>
|
||||||
|
{ title && <h1>{title}</h1>}
|
||||||
|
{ [... mapCstClassInfo.values()].map(
|
||||||
|
(info, index) => {
|
||||||
|
return (
|
||||||
|
<p key={`${prefixes.cst_status_list}${index}`}>
|
||||||
|
<span className={`px-1 inline-block font-semibold min-w-[6.5rem] text-center border ${info.color}`}>
|
||||||
|
{info.text}
|
||||||
|
</span>
|
||||||
|
<span> - </span>
|
||||||
|
<span>
|
||||||
|
{info.tooltip}
|
||||||
|
</span>
|
||||||
|
</p>);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InfoCstClass;
|
29
rsconcept/frontend/src/components/Help/InfoCstStatus.tsx
Normal file
29
rsconcept/frontend/src/components/Help/InfoCstStatus.tsx
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { prefixes } from '../../utils/constants';
|
||||||
|
import { mapStatusInfo } from '../../utils/staticUI';
|
||||||
|
|
||||||
|
interface InfoCstStatusProps {
|
||||||
|
title?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function InfoCstStatus({ title }: InfoCstStatusProps) {
|
||||||
|
return (
|
||||||
|
<div className='flex flex-col gap-1'>
|
||||||
|
{ title && <h1>{title}</h1>}
|
||||||
|
{ [... mapStatusInfo.values()].map(
|
||||||
|
(info, index) => {
|
||||||
|
return (
|
||||||
|
<p key={`${prefixes.cst_status_list}${index}`}>
|
||||||
|
<span className={`px-1 inline-block font-semibold min-w-[4rem] text-center border ${info.color}`}>
|
||||||
|
{info.text}
|
||||||
|
</span>
|
||||||
|
<span> - </span>
|
||||||
|
<span>
|
||||||
|
{info.tooltip}
|
||||||
|
</span>
|
||||||
|
</p>);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InfoCstStatus;
|
|
@ -1,6 +1,5 @@
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { useAuth } from '../../context/AuthContext';
|
|
||||||
import { useConceptTheme } from '../../context/ThemeContext';
|
import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
import { EducationIcon, LibraryIcon } from '../Icons';
|
import { EducationIcon, LibraryIcon } from '../Icons';
|
||||||
import Logo from './Logo'
|
import Logo from './Logo'
|
||||||
|
@ -10,7 +9,6 @@ import UserMenu from './UserMenu';
|
||||||
import UserTools from './UserTools';
|
import UserTools from './UserTools';
|
||||||
|
|
||||||
function Navigation () {
|
function Navigation () {
|
||||||
const { user } = useAuth();
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { noNavigation, toggleNoNavigation } = useConceptTheme();
|
const { noNavigation, toggleNoNavigation } = useConceptTheme();
|
||||||
|
|
||||||
|
@ -37,12 +35,12 @@ function Navigation () {
|
||||||
</button>}
|
</button>}
|
||||||
{!noNavigation &&
|
{!noNavigation &&
|
||||||
<div className='pr-6 pl-2 py-2.5 h-[4rem] flex items-center justify-between border-b-2 clr-nav rounded-none'>
|
<div className='pr-6 pl-2 py-2.5 h-[4rem] flex items-center justify-between border-b-2 clr-nav rounded-none'>
|
||||||
<div className='flex items-start justify-start '>
|
<div className='flex items-start justify-start select-none'>
|
||||||
<Logo title='КонцептПортал' />
|
<Logo title='КонцептПортал' />
|
||||||
<TopSearch />
|
<TopSearch />
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center'>
|
<div className='flex items-center'>
|
||||||
{user && <UserTools/>}
|
<UserTools/>
|
||||||
<div className='flex items-center pl-2'>
|
<div className='flex items-center pl-2'>
|
||||||
<NavigationButton icon={<LibraryIcon />} description='Общие схемы' onClick={navigateCommon} />
|
<NavigationButton icon={<LibraryIcon />} description='Общие схемы' onClick={navigateCommon} />
|
||||||
<NavigationButton icon={<EducationIcon />} description='Справка' onClick={navigateHelp} />
|
<NavigationButton icon={<EducationIcon />} description='Справка' onClick={navigateHelp} />
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
interface NavigationButtonProps {
|
interface NavigationButtonProps {
|
||||||
|
id?: string
|
||||||
icon: React.ReactNode
|
icon: React.ReactNode
|
||||||
description: string
|
description?: string
|
||||||
colorClass?: string
|
colorClass?: string
|
||||||
onClick: () => void
|
onClick?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultColors = 'text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white'
|
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 (
|
return (
|
||||||
<button title={description}
|
<button id={id}
|
||||||
|
title={description}
|
||||||
type='button'
|
type='button'
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className={'min-w-fit p-2 mr-1 focus:ring-4 rounded-lg focus:ring-gray-300 dark:focus:ring-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 ' + colorClass}
|
className={'min-w-fit p-2 mr-1 focus:ring-4 rounded-lg focus:ring-gray-300 dark:focus:ring-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 ' + colorClass}
|
||||||
|
|
|
@ -2,6 +2,8 @@ import { useNavigate } from 'react-router-dom';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import { useAuth } from '../../context/AuthContext';
|
import { useAuth } from '../../context/AuthContext';
|
||||||
|
import ConceptTooltip from '../Common/ConceptTooltip';
|
||||||
|
import TextURL from '../Common/TextURL';
|
||||||
import { BellIcon, PlusIcon, SquaresIcon } from '../Icons';
|
import { BellIcon, PlusIcon, SquaresIcon } from '../Icons';
|
||||||
import NavigationButton from './NavigationButton';
|
import NavigationButton from './NavigationButton';
|
||||||
|
|
||||||
|
@ -19,13 +21,30 @@ function UserTools() {
|
||||||
return (
|
return (
|
||||||
<div className='flex items-center px-2 border-r-2 border-gray-400 dark:border-gray-300'>
|
<div className='flex items-center px-2 border-r-2 border-gray-400 dark:border-gray-300'>
|
||||||
<span>
|
<span>
|
||||||
<NavigationButton description='Новая схема'
|
{ user &&
|
||||||
|
<NavigationButton
|
||||||
|
description='Новая схема'
|
||||||
icon={<PlusIcon />}
|
icon={<PlusIcon />}
|
||||||
onClick={navigateCreateRSForm}
|
onClick={navigateCreateRSForm}
|
||||||
colorClass='text-blue-500 hover:text-blue-700 dark:text-orange-500 dark:hover:text-orange-300'
|
colorClass='text-blue-500 hover:text-blue-700 dark:text-orange-500 dark:hover:text-orange-300'
|
||||||
/>
|
/>}
|
||||||
|
{ !user &&
|
||||||
|
<NavigationButton id='items-nav-help'
|
||||||
|
description='Невозможно создать новую схему. Войдите в систему'
|
||||||
|
icon={<PlusIcon />}
|
||||||
|
/>}
|
||||||
|
<ConceptTooltip anchorSelect='#items-nav-help' clickable>
|
||||||
|
<div className='flex flex-col cursor-default'>
|
||||||
|
<p>Создание и редактирование концептуальных схем</p>
|
||||||
|
<p>доступно только <b>зарегистрированным пользователям</b></p>
|
||||||
|
<div className='flex flex-col self-center'>
|
||||||
|
<TextURL text='Войти в систему' href='/login'/>
|
||||||
|
<TextURL text='Зарегистрироваться' href='/signup'/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ConceptTooltip>
|
||||||
</span>
|
</span>
|
||||||
<NavigationButton icon={<SquaresIcon />} description='Мои схемы' onClick={navigateMyWork} />
|
{ user && <NavigationButton icon={<SquaresIcon />} description='Мои схемы' onClick={navigateMyWork} /> }
|
||||||
{ user && user.is_staff && <NavigationButton icon={<BellIcon />} description='Уведомления' onClick={handleNotifications} />}
|
{ user && user.is_staff && <NavigationButton icon={<BellIcon />} description='Уведомления' onClick={handleNotifications} />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -76,10 +76,10 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
||||||
const isEditable = useMemo(
|
const isEditable = useMemo(
|
||||||
() => {
|
() => {
|
||||||
return (
|
return (
|
||||||
!loading && !isReadonly &&
|
!loading && !processing && !isReadonly &&
|
||||||
((isOwned || (isForceAdmin && user?.is_staff)) ?? false)
|
((isOwned || (isForceAdmin && user?.is_staff)) ?? false)
|
||||||
)
|
)
|
||||||
}, [user?.is_staff, isReadonly, isForceAdmin, isOwned, loading])
|
}, [user?.is_staff, isReadonly, isForceAdmin, isOwned, loading, processing])
|
||||||
|
|
||||||
const isTracking = useMemo(
|
const isTracking = useMemo(
|
||||||
() => {
|
() => {
|
||||||
|
|
153
rsconcept/frontend/src/pages/RSFormPage/DlgGraphOptions.tsx
Normal file
153
rsconcept/frontend/src/pages/RSFormPage/DlgGraphOptions.tsx
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
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 [ noTerms, setNoTerms ] = 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,
|
||||||
|
noTerms: noTerms,
|
||||||
|
|
||||||
|
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);
|
||||||
|
setNoTerms(initial.noTerms);
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<Modal
|
||||||
|
hideWindow={hideWindow}
|
||||||
|
title='Настройки графа термов'
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
canSubmit
|
||||||
|
submitText='Применить'
|
||||||
|
>
|
||||||
|
<div className='flex gap-2'>
|
||||||
|
<div className='flex flex-col'>
|
||||||
|
<h1>Преобразования</h1>
|
||||||
|
<Checkbox
|
||||||
|
label='Скрыть текст'
|
||||||
|
tooltip='Не отображать термины'
|
||||||
|
value={noTerms}
|
||||||
|
onChange={ event => setNoTerms(event.target.checked) }
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
label='Скрыть несвязанные'
|
||||||
|
tooltip='Неиспользуемые конституенты'
|
||||||
|
value={noHermits}
|
||||||
|
onChange={ event => setNoHermits(event.target.checked) }
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
label='Скрыть шаблоны'
|
||||||
|
tooltip='Терм-функции и предикат-функции с параметризованными аргументами'
|
||||||
|
value={noTemplates}
|
||||||
|
onChange={ event => setNoTemplates(event.target.checked) }
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
label='Транзитивная редукция'
|
||||||
|
tooltip='Удалить связи, образующие транзитивные пути в графе'
|
||||||
|
value={noTransitive}
|
||||||
|
onChange={ event => setNoTransitive(event.target.checked) }
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='flex flex-col'>
|
||||||
|
<h1>Типы конституент</h1>
|
||||||
|
<Checkbox
|
||||||
|
label={getCstTypeLabel(CstType.BASE)}
|
||||||
|
value={allowBase}
|
||||||
|
onChange={ event => setAllowBase(event.target.checked) }
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
label={getCstTypeLabel(CstType.STRUCTURED)}
|
||||||
|
value={allowStruct}
|
||||||
|
onChange={ event => setAllowStruct(event.target.checked) }
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
label={getCstTypeLabel(CstType.TERM)}
|
||||||
|
value={allowTerm}
|
||||||
|
onChange={ event => setAllowTerm(event.target.checked) }
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
label={getCstTypeLabel(CstType.AXIOM)}
|
||||||
|
value={allowAxiom}
|
||||||
|
onChange={ event => setAllowAxiom(event.target.checked) }
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
label={getCstTypeLabel(CstType.FUNCTION)}
|
||||||
|
value={allowFunction}
|
||||||
|
onChange={ event => setAllowFunction(event.target.checked) }
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
label={getCstTypeLabel(CstType.PREDICATE)}
|
||||||
|
value={allowPredicate}
|
||||||
|
onChange={ event => setAllowPredicate(event.target.checked) }
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
label={getCstTypeLabel(CstType.CONSTANT)}
|
||||||
|
value={allowConstant}
|
||||||
|
onChange={ event => setAllowConstant(event.target.checked) }
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
label={getCstTypeLabel(CstType.THEOREM)}
|
||||||
|
value={allowTheorem}
|
||||||
|
onChange={ event => setAllowTheorem(event.target.checked) }
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DlgGraphOptions;
|
|
@ -6,10 +6,11 @@ import Divider from '../../components/Common/Divider';
|
||||||
import MiniButton from '../../components/Common/MiniButton';
|
import MiniButton from '../../components/Common/MiniButton';
|
||||||
import SubmitButton from '../../components/Common/SubmitButton';
|
import SubmitButton from '../../components/Common/SubmitButton';
|
||||||
import TextArea from '../../components/Common/TextArea';
|
import TextArea from '../../components/Common/TextArea';
|
||||||
|
import CstStatusInfo from '../../components/Help/InfoCstStatus';
|
||||||
import { DumpBinIcon, HelpIcon, SaveIcon, SmallPlusIcon } from '../../components/Icons';
|
import { DumpBinIcon, HelpIcon, SaveIcon, SmallPlusIcon } from '../../components/Icons';
|
||||||
import { useRSForm } from '../../context/RSFormContext';
|
import { useRSForm } from '../../context/RSFormContext';
|
||||||
import { type CstType, EditMode, ICstUpdateData, SyntaxTree } from '../../utils/models';
|
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 EditorRSExpression from './EditorRSExpression';
|
||||||
import ViewSideConstituents from './elements/ViewSideConstituents';
|
import ViewSideConstituents from './elements/ViewSideConstituents';
|
||||||
|
|
||||||
|
@ -145,19 +146,19 @@ function EditorConstituenta({ activeID, onShowAST, onCreateCst, onOpenEdit, onDe
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex justify-end'>
|
<div className='flex justify-end'>
|
||||||
<MiniButton
|
|
||||||
tooltip='Создать конституенты после данной'
|
|
||||||
disabled={!isEnabled}
|
|
||||||
onClick={handleCreateCst}
|
|
||||||
icon={<SmallPlusIcon size={5} color={isEnabled ? 'text-green' : ''} />}
|
|
||||||
/>
|
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Удалить редактируемую конституенту'
|
tooltip='Удалить редактируемую конституенту'
|
||||||
disabled={!isEnabled}
|
disabled={!isEnabled}
|
||||||
onClick={handleDelete}
|
onClick={handleDelete}
|
||||||
icon={<DumpBinIcon size={5} color={isEnabled ? 'text-red' : ''} />}
|
icon={<DumpBinIcon size={5} color={isEnabled ? 'text-red' : ''} />}
|
||||||
/>
|
/>
|
||||||
<div id='cst-help' className='flex items-center ml-[0.25rem]'>
|
<MiniButton
|
||||||
|
tooltip='Создать конституенты после данной'
|
||||||
|
disabled={!isEnabled}
|
||||||
|
onClick={handleCreateCst}
|
||||||
|
icon={<SmallPlusIcon size={5} color={isEnabled ? 'text-green' : ''} />}
|
||||||
|
/>
|
||||||
|
<div id='cst-help' className='flex items-center ml-[6px]'>
|
||||||
<HelpIcon color='text-primary' size={5} />
|
<HelpIcon color='text-primary' size={5} />
|
||||||
</div>
|
</div>
|
||||||
<ConceptTooltip anchorSelect='#cst-help'>
|
<ConceptTooltip anchorSelect='#cst-help'>
|
||||||
|
@ -173,18 +174,7 @@ function EditorConstituenta({ activeID, onShowAST, onCreateCst, onOpenEdit, onDe
|
||||||
<p>- при наведении на ID конституенты отображаются ее атрибуты</p>
|
<p>- при наведении на ID конституенты отображаются ее атрибуты</p>
|
||||||
<p>- столбец "Описание" содержит один из непустых текстовых атрибутов</p>
|
<p>- столбец "Описание" содержит один из непустых текстовых атрибутов</p>
|
||||||
<Divider margins='mt-2' />
|
<Divider margins='mt-2' />
|
||||||
<h1>Статусы</h1>
|
<CstStatusInfo title='Статусы' />
|
||||||
{ [... mapStatusInfo.values()].map((info, index) => {
|
|
||||||
return (<p className='py-1' key={`status-info-${index}`}>
|
|
||||||
<span className={`inline-block font-semibold min-w-[4rem] text-center border ${info.color}`}>
|
|
||||||
{info.text}
|
|
||||||
</span>
|
|
||||||
<span> - </span>
|
|
||||||
<span>
|
|
||||||
{info.tooltip}
|
|
||||||
</span>
|
|
||||||
</p>);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</ConceptTooltip>
|
</ConceptTooltip>
|
||||||
</div>
|
</div>
|
||||||
|
@ -241,7 +231,7 @@ function EditorConstituenta({ activeID, onShowAST, onCreateCst, onOpenEdit, onDe
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div className='self-stretch border w-full pb-1'>
|
<div className='self-stretch w-full pb-1 border'>
|
||||||
<ViewSideConstituents
|
<ViewSideConstituents
|
||||||
expression={expression}
|
expression={expression}
|
||||||
baseHeight={UNFOLDED_HEIGHT}
|
baseHeight={UNFOLDED_HEIGHT}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import Button from '../../components/Common/Button';
|
||||||
import ConceptDataTable from '../../components/Common/ConceptDataTable';
|
import ConceptDataTable from '../../components/Common/ConceptDataTable';
|
||||||
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
||||||
import Divider from '../../components/Common/Divider';
|
import Divider from '../../components/Common/Divider';
|
||||||
|
import CstStatusInfo from '../../components/Help/InfoCstStatus';
|
||||||
import { ArrowDownIcon, ArrowsRotateIcon, ArrowUpIcon, DumpBinIcon, HelpIcon, SmallPlusIcon } from '../../components/Icons';
|
import { ArrowDownIcon, ArrowsRotateIcon, ArrowUpIcon, DumpBinIcon, HelpIcon, SmallPlusIcon } from '../../components/Icons';
|
||||||
import { useRSForm } from '../../context/RSFormContext';
|
import { useRSForm } from '../../context/RSFormContext';
|
||||||
import { useConceptTheme } from '../../context/ThemeContext';
|
import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
|
@ -89,7 +90,7 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add new constituenta
|
// Add new constituenta
|
||||||
function handleCreateCst(type?: CstType){
|
function handleCreateCst(type?: CstType) {
|
||||||
if (!schema) {
|
if (!schema) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -156,98 +157,97 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps)
|
||||||
setSelected(selectedRows.map(cst => cst.id));
|
setSelected(selectedRows.map(cst => cst.id));
|
||||||
}, [setSelected]);
|
}, [setSelected]);
|
||||||
|
|
||||||
const columns = useMemo(() =>
|
const columns = useMemo(
|
||||||
[
|
() => [
|
||||||
{
|
{
|
||||||
name: 'ID',
|
name: 'ID',
|
||||||
id: 'id',
|
id: 'id',
|
||||||
selector: (cst: IConstituenta) => cst.id,
|
selector: (cst: IConstituenta) => cst.id,
|
||||||
omit: true
|
omit: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Имя',
|
name: 'Имя',
|
||||||
id: 'alias',
|
id: 'alias',
|
||||||
selector: (cst: IConstituenta) => cst.alias,
|
selector: (cst: IConstituenta) => cst.alias,
|
||||||
cell: (cst: IConstituenta) => {
|
cell: (cst: IConstituenta) => {
|
||||||
const info = mapStatusInfo.get(cst.status)!;
|
const info = mapStatusInfo.get(cst.status)!;
|
||||||
return (<>
|
return (<>
|
||||||
<div
|
<div
|
||||||
id={`${prefixes.cst_list}${cst.alias}`}
|
id={`${prefixes.cst_list}${cst.alias}`}
|
||||||
className={`w-full rounded-md text-center ${info.color}`}
|
className={`w-full rounded-md text-center ${info.color}`}
|
||||||
>
|
>
|
||||||
{cst.alias}
|
{cst.alias}
|
||||||
</div>
|
|
||||||
<ConceptTooltip
|
|
||||||
anchorSelect={`#${prefixes.cst_list}${cst.alias}`}
|
|
||||||
place='right'
|
|
||||||
>
|
|
||||||
<p><b>Статус: </b> {info.tooltip}</p>
|
|
||||||
</ConceptTooltip>
|
|
||||||
</>);
|
|
||||||
},
|
|
||||||
width: '65px',
|
|
||||||
maxWidth: '65px',
|
|
||||||
reorder: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Тип',
|
|
||||||
id: 'type',
|
|
||||||
cell: (cst: IConstituenta) => <div style={{ fontSize: 12 }}>{getCstTypificationLabel(cst)}</div>,
|
|
||||||
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) => (
|
|
||||||
<div style={{ fontSize: 12 }}>
|
|
||||||
{cst.definition?.text.resolved ?? cst.definition?.text.raw ?? ''}
|
|
||||||
</div>
|
</div>
|
||||||
),
|
<ConceptTooltip
|
||||||
minWidth: '200px',
|
anchorSelect={`#${prefixes.cst_list}${cst.alias}`}
|
||||||
grow: 2,
|
place='right'
|
||||||
wrap: true,
|
>
|
||||||
reorder: true
|
<p><b>Статус: </b> {info.tooltip}</p>
|
||||||
|
</ConceptTooltip>
|
||||||
|
</>);
|
||||||
},
|
},
|
||||||
{
|
width: '65px',
|
||||||
name: 'Конвенция / Комментарий',
|
maxWidth: '65px',
|
||||||
id: 'convention',
|
reorder: true,
|
||||||
cell: (cst: IConstituenta) => <div style={{ fontSize: 12 }}>{cst.convention ?? ''}</div>,
|
},
|
||||||
minWidth: '100px',
|
{
|
||||||
wrap: true,
|
name: 'Тип',
|
||||||
reorder: true,
|
id: 'type',
|
||||||
hide: 1800
|
cell: (cst: IConstituenta) => <div style={{ fontSize: 12 }}>{getCstTypificationLabel(cst)}</div>,
|
||||||
}
|
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) => (
|
||||||
|
<div style={{ fontSize: 12 }}>
|
||||||
|
{cst.definition?.text.resolved ?? cst.definition?.text.raw ?? ''}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
minWidth: '200px',
|
||||||
|
grow: 2,
|
||||||
|
wrap: true,
|
||||||
|
reorder: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Конвенция / Комментарий',
|
||||||
|
id: 'convention',
|
||||||
|
cell: (cst: IConstituenta) => <div style={{ fontSize: 12 }}>{cst.convention ?? ''}</div>,
|
||||||
|
minWidth: '100px',
|
||||||
|
wrap: true,
|
||||||
|
reorder: true,
|
||||||
|
hide: 1800
|
||||||
|
}
|
||||||
|
], []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full'>
|
<div className='w-full'>
|
||||||
<div
|
<div
|
||||||
className={'flex justify-start w-full gap-1 px-2 py-1 border-y items-center h-[2.2rem] clr-app' +
|
className={'flex justify-start w-full gap-1 px-2 py-1 border-y items-center h-[2.2rem] select-none clr-app' +
|
||||||
(!noNavigation ? ' sticky z-10 top-[0rem]' : ' sticky z-10 top-[0rem]')}
|
(!noNavigation ? ' sticky z-10 top-[0rem]' : ' sticky z-10 top-[0rem]')}
|
||||||
>
|
>
|
||||||
<div className='mr-3 whitespace-nowrap'>
|
<div className='mr-3 whitespace-nowrap'>
|
||||||
|
@ -314,20 +314,9 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps)
|
||||||
<p><b>Клик на квадрат слева</b> - выделение конституенты</p>
|
<p><b>Клик на квадрат слева</b> - выделение конституенты</p>
|
||||||
<p><b>Alt + вверх/вниз</b> - движение конституент</p>
|
<p><b>Alt + вверх/вниз</b> - движение конституент</p>
|
||||||
<p><b>Delete</b> - удаление конституент</p>
|
<p><b>Delete</b> - удаление конституент</p>
|
||||||
<p><b>Alt + 1-6, Q,W</b> - добавление конституент</p>
|
<p><b>Alt + 1-6,Q,W</b> - добавление конституент</p>
|
||||||
<Divider margins='mt-2' />
|
<Divider margins='mt-2' />
|
||||||
<h1>Статусы</h1>
|
<CstStatusInfo title='Статусы' />
|
||||||
{ [... mapStatusInfo.values()].map(info => {
|
|
||||||
return (<p className='py-1'>
|
|
||||||
<span className={`inline-block font-semibold min-w-[4rem] text-center border ${info.color}`}>
|
|
||||||
{info.text}
|
|
||||||
</span>
|
|
||||||
<span> - </span>
|
|
||||||
<span>
|
|
||||||
{info.tooltip}
|
|
||||||
</span>
|
|
||||||
</p>);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</ConceptTooltip>
|
</ConceptTooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,29 +1,117 @@
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { darkTheme, GraphCanvas, GraphCanvasRef, GraphEdge, GraphNode, LayoutTypes, lightTheme, Sphere, useSelection } from 'reagraph';
|
import { darkTheme, GraphCanvas, GraphCanvasRef, GraphEdge,
|
||||||
|
GraphNode, LayoutTypes, lightTheme, Sphere, useSelection
|
||||||
|
} from 'reagraph';
|
||||||
|
|
||||||
import Button from '../../components/Common/Button';
|
import Button from '../../components/Common/Button';
|
||||||
import Checkbox from '../../components/Common/Checkbox';
|
import Checkbox from '../../components/Common/Checkbox';
|
||||||
import ConceptSelect from '../../components/Common/ConceptSelect';
|
import ConceptSelect from '../../components/Common/ConceptSelect';
|
||||||
import { ArrowsRotateIcon } from '../../components/Icons';
|
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
||||||
|
import Divider from '../../components/Common/Divider';
|
||||||
|
import MiniButton from '../../components/Common/MiniButton';
|
||||||
|
import InfoConstituenta from '../../components/Help/InfoConstituenta';
|
||||||
|
import InfoCstClass from '../../components/Help/InfoCstClass';
|
||||||
|
import CstStatusInfo from '../../components/Help/InfoCstStatus';
|
||||||
|
import { ArrowsRotateIcon, DumpBinIcon, FilterCogIcon, HelpIcon, SmallPlusIcon } from '../../components/Icons';
|
||||||
import { useRSForm } from '../../context/RSFormContext';
|
import { useRSForm } from '../../context/RSFormContext';
|
||||||
import { useConceptTheme } from '../../context/ThemeContext';
|
import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
import useLocalStorage from '../../hooks/useLocalStorage';
|
import useLocalStorage from '../../hooks/useLocalStorage';
|
||||||
import { resources } from '../../utils/constants';
|
import { prefixes, resources } from '../../utils/constants';
|
||||||
import { Graph } from '../../utils/Graph';
|
import { Graph } from '../../utils/Graph';
|
||||||
import { GraphLayoutSelector,mapLayoutLabels } from '../../utils/staticUI';
|
import { CstType, IConstituenta } from '../../utils/models';
|
||||||
|
import { getCstClassColor, getCstStatusColor,
|
||||||
|
GraphColoringSelector, GraphLayoutSelector,
|
||||||
|
mapColoringLabels, mapLayoutLabels
|
||||||
|
} from '../../utils/staticUI';
|
||||||
|
import DlgGraphOptions from './DlgGraphOptions';
|
||||||
|
import ConstituentaTooltip from './elements/ConstituentaTooltip';
|
||||||
|
|
||||||
function EditorTermGraph() {
|
export type ColoringScheme = 'none' | 'status' | 'type';
|
||||||
const { schema } = useRSForm();
|
const TREE_SIZE_MILESTONE = 50;
|
||||||
|
|
||||||
|
function getCstNodeColor(cst: IConstituenta, coloringScheme: ColoringScheme, darkMode: boolean): string {
|
||||||
|
if (coloringScheme === 'type') {
|
||||||
|
return getCstClassColor(cst.cstClass, darkMode);
|
||||||
|
}
|
||||||
|
if (coloringScheme === 'status') {
|
||||||
|
return getCstStatusColor(cst.status, darkMode);
|
||||||
|
}
|
||||||
|
return (darkMode ? '#7a8c9e' :'#7ca0ab');
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GraphEditorParams {
|
||||||
|
noHermits: boolean
|
||||||
|
noTransitive: boolean
|
||||||
|
noTemplates: boolean
|
||||||
|
noTerms: boolean
|
||||||
|
|
||||||
|
allowBase: boolean
|
||||||
|
allowStruct: boolean
|
||||||
|
allowTerm: boolean
|
||||||
|
allowAxiom: boolean
|
||||||
|
allowFunction: boolean
|
||||||
|
allowPredicate: boolean
|
||||||
|
allowConstant: boolean
|
||||||
|
allowTheorem: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
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, onCreateCst, onDeleteCst }: EditorTermGraphProps) {
|
||||||
|
const { schema, isEditable } = useRSForm();
|
||||||
const { darkMode, noNavigation } = useConceptTheme();
|
const { darkMode, noNavigation } = useConceptTheme();
|
||||||
const [ layout, setLayout ] = useLocalStorage<LayoutTypes>('graph_layout', 'treeTd2d');
|
|
||||||
|
|
||||||
const [ filtered, setFiltered ] = useState<Graph>(new Graph());
|
const [ layout, setLayout ] = useLocalStorage<LayoutTypes>('graph_layout', 'treeTd2d');
|
||||||
|
const [ coloringScheme, setColoringScheme ] = useLocalStorage<ColoringScheme>('graph_coloring', 'none');
|
||||||
const [ orbit, setOrbit ] = useState(false);
|
const [ orbit, setOrbit ] = useState(false);
|
||||||
|
|
||||||
const [ noHermits, setNoHermits ] = useLocalStorage('graph_no_hermits', true);
|
const [ noHermits, setNoHermits ] = useLocalStorage('graph_no_hermits', true);
|
||||||
const [ noTransitive, setNoTransitive ] = useLocalStorage('graph_no_transitive', false);
|
const [ noTransitive, setNoTransitive ] = useLocalStorage('graph_no_transitive', false);
|
||||||
const graphRef = useRef<GraphCanvasRef | null>(null);
|
const [ noTemplates, setNoTemplates ] = useLocalStorage('graph_no_templates', false);
|
||||||
|
const [ noTerms, setNoTerms ] = useLocalStorage('graph_no_terms', 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);
|
||||||
|
|
||||||
useEffect(() => {
|
const [ filtered, setFiltered ] = useState<Graph>(new Graph());
|
||||||
|
const [ dismissed, setDismissed ] = useState<number[]>([]);
|
||||||
|
const [ selectedDismissed, setSelectedDismissed ] = useState<number[]>([]);
|
||||||
|
const graphRef = useRef<GraphCanvasRef | null>(null);
|
||||||
|
const [showOptions, setShowOptions] = useState(false);
|
||||||
|
const [toggleUpdate, setToggleUpdate] = useState(false);
|
||||||
|
|
||||||
|
const [hoverID, setHoverID] = useState<string | undefined>(undefined);
|
||||||
|
const hoverCst = useMemo(
|
||||||
|
() => {
|
||||||
|
return schema?.items.find(cst => String(cst.id) == hoverID);
|
||||||
|
}, [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]);
|
||||||
|
|
||||||
|
useLayoutEffect(
|
||||||
|
() => {
|
||||||
if (!schema) {
|
if (!schema) {
|
||||||
setFiltered(new Graph());
|
setFiltered(new Graph());
|
||||||
return;
|
return;
|
||||||
|
@ -35,10 +123,46 @@ function EditorTermGraph() {
|
||||||
if (noTransitive) {
|
if (noTransitive) {
|
||||||
graph.transitiveReduction();
|
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)) {
|
||||||
|
newDismissed.push(cst.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
setFiltered(graph);
|
setFiltered(graph);
|
||||||
}, [schema, noHermits, noTransitive]);
|
setDismissed(newDismissed);
|
||||||
|
setSelectedDismissed([]);
|
||||||
|
setHoverID(undefined);
|
||||||
|
}, [schema, noHermits, noTransitive, noTemplates, allowedTypes, toggleUpdate]);
|
||||||
|
|
||||||
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[] = [];
|
const result: GraphNode[] = [];
|
||||||
if (!schema) {
|
if (!schema) {
|
||||||
return result;
|
return result;
|
||||||
|
@ -48,14 +172,16 @@ function EditorTermGraph() {
|
||||||
if (cst) {
|
if (cst) {
|
||||||
result.push({
|
result.push({
|
||||||
id: String(node.id),
|
id: String(node.id),
|
||||||
label: cst.term.resolved ? `${cst.alias}: ${cst.term.resolved}` : cst.alias
|
fill: getCstNodeColor(cst, coloringScheme, darkMode),
|
||||||
|
label: cst.term.resolved && !noTerms ? `${cst.alias}: ${cst.term.resolved}` : cst.alias
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}, [schema, filtered.nodes]);
|
}, [schema, coloringScheme, filtered.nodes, darkMode, noTerms]);
|
||||||
|
|
||||||
const edges: GraphEdge[] = useMemo(() => {
|
const edges: GraphEdge[] = useMemo(
|
||||||
|
() => {
|
||||||
const result: GraphEdge[] = [];
|
const result: GraphEdge[] = [];
|
||||||
let edgeID = 1;
|
let edgeID = 1;
|
||||||
filtered.nodes.forEach(source => {
|
filtered.nodes.forEach(source => {
|
||||||
|
@ -71,14 +197,10 @@ function EditorTermGraph() {
|
||||||
return result;
|
return result;
|
||||||
}, [filtered.nodes]);
|
}, [filtered.nodes]);
|
||||||
|
|
||||||
const handleCenter = useCallback(() => {
|
|
||||||
graphRef.current?.resetControls();
|
|
||||||
graphRef.current?.centerGraph();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
selections, actives,
|
selections, actives,
|
||||||
onNodeClick,
|
onNodeClick,
|
||||||
|
clearSelections,
|
||||||
onCanvasClick,
|
onCanvasClick,
|
||||||
onNodePointerOver,
|
onNodePointerOver,
|
||||||
onNodePointerOut
|
onNodePointerOut
|
||||||
|
@ -87,52 +209,292 @@ function EditorTermGraph() {
|
||||||
nodes,
|
nodes,
|
||||||
edges,
|
edges,
|
||||||
type: 'multi', // 'single' | 'multi' | 'multiModifier'
|
type: 'multi', // 'single' | 'multi' | 'multiModifier'
|
||||||
pathSelectionType: 'all',
|
pathSelectionType: 'out',
|
||||||
|
pathHoverType: 'all',
|
||||||
focusOnSelect: false
|
focusOnSelect: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const canvasSize = !noNavigation ?
|
const allSelected: string[] = useMemo(
|
||||||
'w-[1240px] h-[736px] 2xl:w-[1880px] 2xl:h-[746px]'
|
() => {
|
||||||
: 'w-[1240px] h-[800px] 2xl:w-[1880px] 2xl:h-[810px]';
|
return [ ... selectedDismissed.map(id => String(id)), ... selections];
|
||||||
|
}, [selectedDismissed, selections]);
|
||||||
|
const nothingSelected = useMemo(() => allSelected.length === 0, [allSelected]);
|
||||||
|
|
||||||
return (<>
|
const handleRecreate = useCallback(
|
||||||
<div className='relative w-full'>
|
() => {
|
||||||
<div className='absolute top-0 left-0 z-20 py-2 w-[12rem] flex flex-col'>
|
graphRef.current?.resetControls();
|
||||||
<div className='flex items-center gap-1 w-[15rem]'>
|
graphRef.current?.centerGraph();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
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 handleCanvasClick = useCallback(
|
||||||
|
(event: MouseEvent) => {
|
||||||
|
setSelectedDismissed([]);
|
||||||
|
if (onCanvasClick) onCanvasClick(event);
|
||||||
|
}, [onCanvasClick]);
|
||||||
|
|
||||||
|
// Implement hotkeys for editing
|
||||||
|
function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
|
||||||
|
console.log(event);
|
||||||
|
if (!isEditable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.key === 'Delete' && allSelected.length > 0) {
|
||||||
|
event.preventDefault();
|
||||||
|
handleDeleteCst();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCreateCst() {
|
||||||
|
if (!schema) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const selectedPosition = allSelected.reduce((prev, cstID) => {
|
||||||
|
const position = schema.items.findIndex(cst => cst.id === Number(cstID));
|
||||||
|
return Math.max(position, prev);
|
||||||
|
}, -1);
|
||||||
|
const insert_where = selectedPosition >= 0 ? schema.items[selectedPosition].id : undefined;
|
||||||
|
onCreateCst(insert_where, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDeleteCst() {
|
||||||
|
if (!schema) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onDeleteCst([... allSelected.map(id => Number(id))], () => {
|
||||||
|
clearSelections();
|
||||||
|
setDismissed([]);
|
||||||
|
setSelectedDismissed([]);
|
||||||
|
setToggleUpdate(prev => !prev);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOptions() {
|
||||||
|
return {
|
||||||
|
noHermits: noHermits,
|
||||||
|
noTemplates: noTemplates,
|
||||||
|
noTransitive: noTransitive,
|
||||||
|
noTerms: noTerms,
|
||||||
|
|
||||||
|
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);
|
||||||
|
setNoTerms(params.noTerms);
|
||||||
|
|
||||||
|
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, setNoTerms]);
|
||||||
|
|
||||||
|
const canvasWidth = useMemo(
|
||||||
|
() => {
|
||||||
|
return 'calc(100vw - 14.6rem)';
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const canvasHeight = useMemo(
|
||||||
|
() => {
|
||||||
|
return !noNavigation ?
|
||||||
|
'calc(100vh - 13rem)'
|
||||||
|
: 'calc(100vh - 2rem)';
|
||||||
|
}, [noNavigation]);
|
||||||
|
|
||||||
|
const dismissedStyle = useCallback(
|
||||||
|
(cstID: number) => {
|
||||||
|
return selectedDismissed.includes(cstID) ? {outlineWidth: '2px', outlineStyle: 'solid'}: {};
|
||||||
|
}, [selectedDismissed]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='flex justify-between w-full outline-none' tabIndex={0} onKeyDown={handleKeyDown}>
|
||||||
|
{showOptions &&
|
||||||
|
<DlgGraphOptions
|
||||||
|
hideWindow={() => setShowOptions(false)}
|
||||||
|
initial={getOptions()}
|
||||||
|
onConfirm={handleChangeOptions}
|
||||||
|
/>}
|
||||||
|
<div className='flex flex-col py-2 border-t border-r max-w-[12.44rem] pr-2 text-sm select-none' style={{height: canvasHeight}}>
|
||||||
|
{hoverCst &&
|
||||||
|
<div className='relative'>
|
||||||
|
<InfoConstituenta
|
||||||
|
data={hoverCst}
|
||||||
|
className='absolute top-0 left-0 z-50 w-[25rem] min-h-[11rem] overflow-y-auto border h-fit clr-app px-3'
|
||||||
|
/>
|
||||||
|
</div>}
|
||||||
|
|
||||||
|
<div className='flex items-center justify-between'>
|
||||||
|
<div className='mr-3 whitespace-nowrap'>
|
||||||
|
Выбраны
|
||||||
|
<span className='ml-1'>
|
||||||
|
<b>{allSelected.length}</b> из {schema?.stats?.count_all ?? 0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<MiniButton
|
||||||
|
tooltip='Удалить выбранные'
|
||||||
|
icon={<DumpBinIcon color={!nothingSelected ? 'text-red' : ''} size={5}/>}
|
||||||
|
disabled={!isEditable || nothingSelected}
|
||||||
|
onClick={handleDeleteCst}
|
||||||
|
/>
|
||||||
|
<MiniButton
|
||||||
|
tooltip='Новая конституента'
|
||||||
|
icon={<SmallPlusIcon color='text-green' size={5}/>}
|
||||||
|
disabled={!isEditable}
|
||||||
|
onClick={handleCreateCst}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='flex items-center w-full gap-1'>
|
||||||
<Button
|
<Button
|
||||||
icon={<ArrowsRotateIcon size={8} />}
|
icon={<FilterCogIcon size={7} />}
|
||||||
dense
|
dense
|
||||||
tooltip='Центрировать изображение'
|
tooltip='Настройки фильтрации узлов и связей'
|
||||||
widthClass='h-full'
|
widthClass='h-full'
|
||||||
onClick={handleCenter}
|
onClick={() => setShowOptions(true)}
|
||||||
/>
|
/>
|
||||||
<ConceptSelect
|
<ConceptSelect
|
||||||
className='min-w-[9.5rem]'
|
className='min-w-[9.3rem]'
|
||||||
options={GraphLayoutSelector}
|
options={GraphColoringSelector}
|
||||||
placeholder='Выберите тип'
|
searchable={false}
|
||||||
values={layout ? [{ value: layout, label: mapLayoutLabels.get(layout) }] : []}
|
placeholder='Выберите цвет'
|
||||||
onChange={data => { setLayout(data.length > 0 ? data[0].value : GraphLayoutSelector[0].value); }}
|
values={coloringScheme ? [{ value: coloringScheme, label: mapColoringLabels.get(coloringScheme) }] : []}
|
||||||
|
onChange={data => { setColoringScheme(data.length > 0 ? data[0].value : GraphColoringSelector[0].value); }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<Checkbox
|
<ConceptSelect
|
||||||
label='Анимация вращения'
|
className='mt-1 w-fit'
|
||||||
value={orbit}
|
options={GraphLayoutSelector}
|
||||||
onChange={ event => setOrbit(event.target.checked) }
|
searchable={false}
|
||||||
|
placeholder='Выберите тип'
|
||||||
|
values={layout ? [{ value: layout, label: mapLayoutLabels.get(layout) }] : []}
|
||||||
|
onChange={data => { setLayout(data.length > 0 ? data[0].value : GraphLayoutSelector[0].value); }}
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label='Удалить несвязанные'
|
label='Скрыть текст'
|
||||||
value={noHermits}
|
value={noTerms}
|
||||||
onChange={ event => setNoHermits(event.target.checked) }
|
onChange={ event => setNoTerms(event.target.checked) }
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label='Транзитивная редукция'
|
label='Транзитивная редукция'
|
||||||
value={noTransitive}
|
value={noTransitive}
|
||||||
onChange={ event => setNoTransitive(event.target.checked) }
|
onChange={ event => setNoTransitive(event.target.checked) }
|
||||||
/>
|
/>
|
||||||
|
<Checkbox
|
||||||
|
disabled={!is3D}
|
||||||
|
label='Анимация вращения'
|
||||||
|
value={orbit}
|
||||||
|
onChange={ event => setOrbit(event.target.checked) }
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Divider margins='mt-3 mb-2' />
|
||||||
|
|
||||||
|
<div className='flex flex-col overflow-y-auto'>
|
||||||
|
<p className='text-center'><b>Скрытые конституенты</b></p>
|
||||||
|
<div className='flex flex-wrap justify-center gap-2 py-2'>
|
||||||
|
{dismissed.map(cstID => {
|
||||||
|
const cst = schema!.items.find(cst => cst.id === cstID)!;
|
||||||
|
const adjustedColoring = coloringScheme === 'none' ? 'status': coloringScheme;
|
||||||
|
return (<>
|
||||||
|
<div
|
||||||
|
key={`${cst.alias}`}
|
||||||
|
id={`${prefixes.cst_list}${cst.alias}`}
|
||||||
|
className='w-fit min-w-[3rem] rounded-md text-center cursor-pointer'
|
||||||
|
style={ { backgroundColor: getCstNodeColor(cst, adjustedColoring, darkMode), ...dismissedStyle(cstID) }}
|
||||||
|
onClick={() => toggleDismissed(cstID)}
|
||||||
|
onDoubleClick={() => onOpenEdit(cstID)}
|
||||||
|
>
|
||||||
|
{cst.alias}
|
||||||
|
</div>
|
||||||
|
<ConstituentaTooltip
|
||||||
|
data={cst}
|
||||||
|
anchor={`#${prefixes.cst_list}${cst.alias}`}
|
||||||
|
/>
|
||||||
|
</>);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className='w-full h-full overflow-auto'>
|
||||||
<div className='flex-wrap w-full h-full overflow-auto'>
|
<div
|
||||||
<div className={`relative border-t border-r ${canvasSize}`}>
|
className='relative border-t border-r'
|
||||||
|
style={{width: canvasWidth, height: canvasHeight, borderBottomWidth: noNavigation ? '1px': ''}}
|
||||||
|
>
|
||||||
|
<div className='relative top-0 right-0 z-10 flex m-2 flex-start'>
|
||||||
|
<div className='px-1 py-1' id='items-graph-help' >
|
||||||
|
<HelpIcon color='text-primary' size={5} />
|
||||||
|
</div>
|
||||||
|
<MiniButton
|
||||||
|
icon={<ArrowsRotateIcon size={5} />}
|
||||||
|
tooltip='Пересоздать граф'
|
||||||
|
onClick={handleRecreate}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<ConceptTooltip anchorSelect='#items-graph-help'>
|
||||||
|
<div className='flex'>
|
||||||
|
<div>
|
||||||
|
<h1>Настройка графа</h1>
|
||||||
|
<p><b>Цвет</b> - выбор правила покраски узлов</p>
|
||||||
|
<p><b>Граф</b> - выбор модели расположения узлов</p>
|
||||||
|
<p><b>Удалить несвязанные</b> - скрыть одинокие вершины</p>
|
||||||
|
<p><b>Транзитивная редукция</b> - скрыть транзитивные пути</p>
|
||||||
|
|
||||||
|
<Divider margins='mt-2' />
|
||||||
|
|
||||||
|
<CstStatusInfo title='Статусы конституент' />
|
||||||
|
</div>
|
||||||
|
<Divider vertical margins='mx-3' />
|
||||||
|
<div>
|
||||||
|
<h1>Горячие клавиши</h1>
|
||||||
|
<p><b>Клик на конституенту</b> - выделение, включая скрытые конституенты</p>
|
||||||
|
<p><b>Довйной клик</b> - редактирование конституенты</p>
|
||||||
|
<p><b>Delete</b> - удалить выбранные</p>
|
||||||
|
|
||||||
|
<Divider margins='mt-2' />
|
||||||
|
|
||||||
|
<InfoCstClass title='Классы конституент' />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ConceptTooltip>
|
||||||
<GraphCanvas
|
<GraphCanvas
|
||||||
draggable
|
draggable
|
||||||
ref={graphRef}
|
ref={graphRef}
|
||||||
|
@ -141,13 +503,15 @@ function EditorTermGraph() {
|
||||||
layoutType={layout}
|
layoutType={layout}
|
||||||
selections={selections}
|
selections={selections}
|
||||||
actives={actives}
|
actives={actives}
|
||||||
onNodeClick={onNodeClick}
|
onNodeClick={handleNodeClick}
|
||||||
onCanvasClick={onCanvasClick}
|
onCanvasClick={handleCanvasClick}
|
||||||
defaultNodeSize={5}
|
onNodePointerOver={handleHoverIn}
|
||||||
onNodePointerOver={onNodePointerOver}
|
onNodePointerOut={handleHoverOut}
|
||||||
onNodePointerOut={onNodePointerOut}
|
cameraMode={ orbit ? 'orbit' : is3D ? 'rotate' : 'pan'}
|
||||||
cameraMode={ orbit ? 'orbit' : layout.includes('3d') ? 'rotate' : 'pan'}
|
layoutOverrides={
|
||||||
layoutOverrides={ layout.includes('tree') ? { nodeLevelRatio: schema && schema?.items.length < 50 ? 3 : 1 } : undefined }
|
layout.includes('tree') ? { nodeLevelRatio: filtered.nodes.size < TREE_SIZE_MILESTONE ? 3 : 1 }
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
labelFontUrl={resources.graph_font}
|
labelFontUrl={resources.graph_font}
|
||||||
theme={darkMode ? darkTheme : lightTheme}
|
theme={darkMode ? darkTheme : lightTheme}
|
||||||
renderNode={({ node, ...rest }) => (
|
renderNode={({ node, ...rest }) => (
|
||||||
|
@ -156,7 +520,7 @@ function EditorTermGraph() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ function RSTabs() {
|
||||||
const { destroySchema } = useLibrary();
|
const { destroySchema } = useLibrary();
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState(RSTabsList.CARD);
|
const [activeTab, setActiveTab] = useState(RSTabsList.CARD);
|
||||||
const [activeID, setActiveID] = useState<number | undefined>(undefined)
|
const [activeID, setActiveID] = useState<number | undefined>(undefined);
|
||||||
|
|
||||||
const [showUpload, setShowUpload] = useState(false);
|
const [showUpload, setShowUpload] = useState(false);
|
||||||
const [showClone, setShowClone] = useState(false);
|
const [showClone, setShowClone] = useState(false);
|
||||||
|
@ -223,7 +223,7 @@ function RSTabs() {
|
||||||
defaultFocus={true}
|
defaultFocus={true}
|
||||||
selectedTabClassName='font-bold'
|
selectedTabClassName='font-bold'
|
||||||
>
|
>
|
||||||
<TabList className='flex items-start w-fit clr-bg-pop'>
|
<TabList className='flex items-start select-none w-fit clr-bg-pop'>
|
||||||
<RSTabsMenu
|
<RSTabsMenu
|
||||||
onDestroy={onDestroySchema}
|
onDestroy={onDestroySchema}
|
||||||
showCloneDialog={() => setShowClone(true)}
|
showCloneDialog={() => setShowClone(true)}
|
||||||
|
@ -264,7 +264,11 @@ function RSTabs() {
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<EditorTermGraph />
|
<EditorTermGraph
|
||||||
|
onOpenEdit={onOpenCst}
|
||||||
|
onCreateCst={promptCreateCst}
|
||||||
|
onDeleteCst={promptDeleteCst}
|
||||||
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</>}
|
</>}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import ConceptTooltip from '../../../components/Common/ConceptTooltip';
|
import ConceptTooltip from '../../../components/Common/ConceptTooltip';
|
||||||
|
import InfoConstituenta from '../../../components/Help/InfoConstituenta';
|
||||||
import { IConstituenta } from '../../../utils/models';
|
import { IConstituenta } from '../../../utils/models';
|
||||||
import { getCstTypificationLabel } from '../../../utils/staticUI';
|
|
||||||
|
|
||||||
interface ConstituentaTooltipProps {
|
interface ConstituentaTooltipProps {
|
||||||
data: IConstituenta
|
data: IConstituenta
|
||||||
|
@ -13,12 +13,7 @@ function ConstituentaTooltip({ data, anchor }: ConstituentaTooltipProps) {
|
||||||
anchorSelect={anchor}
|
anchorSelect={anchor}
|
||||||
className='max-w-[25rem] min-w-[25rem]'
|
className='max-w-[25rem] min-w-[25rem]'
|
||||||
>
|
>
|
||||||
<h1>Конституента {data.alias}</h1>
|
<InfoConstituenta data={data} />
|
||||||
<p><b>Типизация: </b>{getCstTypificationLabel(data)}</p>
|
|
||||||
<p><b>Термин: </b>{data.term.resolved || data.term.raw}</p>
|
|
||||||
{data.definition.formal && <p><b>Выражение: </b>{data.definition.formal}</p>}
|
|
||||||
{data.definition.text.resolved && <p><b>Определение: </b>{data.definition.text.resolved}</p>}
|
|
||||||
{data.convention && <p><b>Конвенция: </b>{data.convention}</p>}
|
|
||||||
</ConceptTooltip>
|
</ConceptTooltip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ function StatusBar({ isModified, constituenta, parseData }: StatusBarProps) {
|
||||||
const data = mapStatusInfo.get(status)!;
|
const data = mapStatusInfo.get(status)!;
|
||||||
return (
|
return (
|
||||||
<div title={data.tooltip}
|
<div title={data.tooltip}
|
||||||
className={`text-sm h-[1.6rem] w-[10rem] font-semibold inline-flex border items-center justify-center align-middle ${data.color}`}>
|
className={`text-sm h-[1.6rem] w-[10rem] font-semibold inline-flex border items-center select-none justify-center align-middle ${data.color}`}>
|
||||||
Статус: [ {data.text} ]
|
Статус: [ {data.text} ]
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -158,7 +158,7 @@ function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }:
|
||||||
onChange={setFilterMatch}
|
onChange={setFilterMatch}
|
||||||
/>
|
/>
|
||||||
<input type='text'
|
<input type='text'
|
||||||
className='w-full px-2 bg-white outline-none hover:text-clip clr-bg-pop clr-border'
|
className='w-full px-2 bg-white outline-none select-none hover:text-clip clr-bg-pop clr-border'
|
||||||
placeholder='наберите текст фильтра'
|
placeholder='наберите текст фильтра'
|
||||||
value={filterText}
|
value={filterText}
|
||||||
onChange={event => setFilterText(event.target.value)}
|
onChange={event => setFilterText(event.target.value)}
|
||||||
|
|
|
@ -45,6 +45,16 @@ describe('Testing Graph editing', () => {
|
||||||
expect(graph.hasEdge(4, 1)).toBeFalsy();
|
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', () => {
|
test('removing isolated nodes', () => {
|
||||||
const graph = new Graph([[9, 1], [9, 2], [2, 1], [4, 3], [5, 9], [7], [8]]);
|
const graph = new Graph([[9, 1], [9, 2], [2, 1], [4, 3], [5, 9], [7], [8]]);
|
||||||
graph.removeIsolated()
|
graph.removeIsolated()
|
||||||
|
@ -53,7 +63,7 @@ describe('Testing Graph editing', () => {
|
||||||
|
|
||||||
test('transitive reduction', () => {
|
test('transitive reduction', () => {
|
||||||
const graph = new Graph([[1, 3], [1, 2], [2, 3]]);
|
const graph = new Graph([[1, 3], [1, 2], [2, 3]]);
|
||||||
graph.transitiveReduction()
|
graph.transitiveReduction();
|
||||||
expect(graph.hasEdge(1, 2)).toBeTruthy();
|
expect(graph.hasEdge(1, 2)).toBeTruthy();
|
||||||
expect(graph.hasEdge(2, 3)).toBeTruthy();
|
expect(graph.hasEdge(2, 3)).toBeTruthy();
|
||||||
expect(graph.hasEdge(1, 3)).toBeFalsy();
|
expect(graph.hasEdge(1, 3)).toBeFalsy();
|
||||||
|
|
|
@ -67,6 +67,10 @@ export class Graph {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasNode(target: number): boolean {
|
||||||
|
return !!this.nodes.get(target);
|
||||||
|
}
|
||||||
|
|
||||||
removeNode(target: number): GraphNode | null {
|
removeNode(target: number): GraphNode | null {
|
||||||
const nodeToRemove = this.nodes.get(target);
|
const nodeToRemove = this.nodes.get(target);
|
||||||
if (!nodeToRemove) {
|
if (!nodeToRemove) {
|
||||||
|
@ -80,6 +84,19 @@ export class Graph {
|
||||||
return nodeToRemove;
|
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[] {
|
removeIsolated(): GraphNode[] {
|
||||||
const result: GraphNode[] = [];
|
const result: GraphNode[] = [];
|
||||||
this.nodes.forEach(node => {
|
this.nodes.forEach(node => {
|
||||||
|
|
|
@ -28,5 +28,6 @@ export const resources = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const prefixes = {
|
export const prefixes = {
|
||||||
cst_list: 'cst-list-'
|
cst_list: 'cst-list-',
|
||||||
|
cst_status_list: 'cst-status-list-'
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,6 +101,13 @@ export enum CstType {
|
||||||
THEOREM = 'theorem'
|
THEOREM = 'theorem'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum CstClass {
|
||||||
|
BASIC = 'basic',
|
||||||
|
DERIVED = 'derived',
|
||||||
|
STATEMENT = 'statement',
|
||||||
|
TEMPLATE = 'template'
|
||||||
|
}
|
||||||
|
|
||||||
export interface IConstituenta {
|
export interface IConstituenta {
|
||||||
id: number
|
id: number
|
||||||
alias: string
|
alias: string
|
||||||
|
@ -118,7 +125,9 @@ export interface IConstituenta {
|
||||||
resolved: string
|
resolved: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cstClass: CstClass
|
||||||
status: ExpressionStatus
|
status: ExpressionStatus
|
||||||
|
isTemplate: boolean
|
||||||
parse: {
|
parse: {
|
||||||
status: ParsingStatus
|
status: ParsingStatus
|
||||||
valueClass: ValueClass
|
valueClass: ValueClass
|
||||||
|
@ -230,12 +239,12 @@ export enum EditMode {
|
||||||
|
|
||||||
// RSExpression status
|
// RSExpression status
|
||||||
export enum ExpressionStatus {
|
export enum ExpressionStatus {
|
||||||
UNDEFINED = 0,
|
UNDEFINED = 'undefined',
|
||||||
UNKNOWN,
|
UNKNOWN = 'unknown',
|
||||||
INCORRECT,
|
INCORRECT = 'incorrect',
|
||||||
INCALCULABLE,
|
INCALCULABLE = 'incalculable',
|
||||||
PROPERTY,
|
PROPERTY = 'property',
|
||||||
VERIFIED
|
VERIFIED = 'verified'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dependency mode for schema analysis
|
// Dependency mode for schema analysis
|
||||||
|
@ -274,7 +283,28 @@ export function inferStatus(parse?: ParsingStatus, value?: ValueClass): Expressi
|
||||||
if (value === ValueClass.PROPERTY) {
|
if (value === ValueClass.PROPERTY) {
|
||||||
return ExpressionStatus.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<string> {
|
export function extractGlobals(expression: string): Set<string> {
|
||||||
|
@ -336,6 +366,8 @@ export function LoadRSFormData(schema: IRSFormData): IRSForm {
|
||||||
}
|
}
|
||||||
result.items.forEach(cst => {
|
result.items.forEach(cst => {
|
||||||
cst.status = inferStatus(cst.parse.status, cst.parse.valueClass);
|
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);
|
result.graph.addNode(cst.id);
|
||||||
const dependencies = extractGlobals(cst.definition.formal);
|
const dependencies = extractGlobals(cst.definition.formal);
|
||||||
dependencies.forEach(value => {
|
dependencies.forEach(value => {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { LayoutTypes } from 'reagraph';
|
import { LayoutTypes } from 'reagraph';
|
||||||
|
|
||||||
|
import { ColoringScheme } from '../pages/RSFormPage/EditorTermGraph';
|
||||||
import { resolveErrorClass,RSErrorClass, RSErrorType, TokenID } from './enums';
|
import { resolveErrorClass,RSErrorClass, RSErrorType, TokenID } from './enums';
|
||||||
import { CstMatchMode, CstType, DependencyMode,ExpressionStatus, IConstituenta,
|
import { CstClass, CstMatchMode, CstType, DependencyMode, ExpressionStatus, IConstituenta,
|
||||||
IFunctionArg,IRSErrorDescription, IRSForm,
|
IFunctionArg,IRSErrorDescription, IRSForm,
|
||||||
ISyntaxTreeNode, ParsingStatus, ValueClass
|
ISyntaxTreeNode, ParsingStatus, ValueClass
|
||||||
} from './models';
|
} from './models';
|
||||||
|
@ -11,7 +12,7 @@ export interface IRSButtonData {
|
||||||
tooltip: string
|
tooltip: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IStatusInfo {
|
export interface IFormatInfo {
|
||||||
text: string
|
text: string
|
||||||
color: string
|
color: string
|
||||||
tooltip: string
|
tooltip: string
|
||||||
|
@ -250,22 +251,6 @@ export const CstTypeSelector = (Object.values(CstType)).map(
|
||||||
return { value: type, label: getCstTypeLabel(type) };
|
return { value: type, label: getCstTypeLabel(type) };
|
||||||
});
|
});
|
||||||
|
|
||||||
export const mapLayoutLabels: Map<string, string> = 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 {
|
export function getCstCompareLabel(mode: CstMatchMode): string {
|
||||||
switch(mode) {
|
switch(mode) {
|
||||||
case CstMatchMode.ALL: return 'везде';
|
case CstMatchMode.ALL: return 'везде';
|
||||||
|
@ -288,22 +273,61 @@ export function getDependencyLabel(mode: DependencyMode): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GraphLayoutSelector: {value: LayoutTypes, label: string}[] = [
|
export const GraphLayoutSelector: {value: LayoutTypes, label: string}[] = [
|
||||||
{ value: 'forceatlas2', label: 'Атлас 2D'},
|
{ value: 'treeTd2d', label: 'Граф: ДеревоВ 2D'},
|
||||||
{ value: 'forceDirected2d', label: 'Силы 2D'},
|
{ value: 'treeTd3d', label: 'Граф: ДеревоВ 3D'},
|
||||||
{ value: 'forceDirected3d', label: 'Силы 3D'},
|
{ value: 'forceatlas2', label: 'Граф: Атлас 2D'},
|
||||||
{ value: 'treeTd2d', label: 'ДеревоВ 2D'},
|
{ value: 'forceDirected2d', label: 'Граф: Силы 2D'},
|
||||||
{ value: 'treeTd3d', label: 'ДеревоВ 3D'},
|
{ value: 'forceDirected3d', label: 'Граф: Силы 3D'},
|
||||||
{ value: 'treeLr2d', label: 'ДеревоГ 2D'},
|
{ value: 'treeLr2d', label: 'Граф: ДеревоГ 2D'},
|
||||||
{ value: 'treeLr3d', label: 'ДеревоГ 3D'},
|
{ value: 'treeLr3d', label: 'Граф: ДеревоГ 3D'},
|
||||||
{ value: 'radialOut2d', label: 'Радиальная 2D'},
|
{ value: 'radialOut2d', label: 'Граф: Радиальная 2D'},
|
||||||
{ value: 'radialOut3d', label: 'Радиальная 3D'},
|
{ value: 'radialOut3d', label: 'Граф: Радиальная 3D'},
|
||||||
// { value: 'circular2d', label: 'circular2d'},
|
// { value: 'circular2d', label: 'circular2d'},
|
||||||
// { value: 'nooverlap', label: 'nooverlap'},
|
// { value: 'nooverlap', label: 'nooverlap'},
|
||||||
// { value: 'hierarchicalTd', label: 'hierarchicalTd'},
|
// { value: 'hierarchicalTd', label: 'hierarchicalTd'},
|
||||||
// { value: 'hierarchicalLr', label: 'hierarchicalLr'}
|
// { value: 'hierarchicalLr', label: 'hierarchicalLr'}
|
||||||
];
|
];
|
||||||
|
|
||||||
export const mapStatusInfo: Map<ExpressionStatus, IStatusInfo> = new Map([
|
export const mapLayoutLabels: Map<string, string> = 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<string, string> = new Map([
|
||||||
|
['none', 'Цвет: моно'],
|
||||||
|
['status', 'Цвет: статус'],
|
||||||
|
['type', 'Цвет: класс'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const GraphColoringSelector: {value: ColoringScheme, label: string}[] = [
|
||||||
|
{ value: 'none', label: 'Цвет: моно'},
|
||||||
|
{ value: 'status', label: 'Цвет: статус'},
|
||||||
|
{ value: 'type', label: 'Цвет: класс'},
|
||||||
|
];
|
||||||
|
|
||||||
|
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<ExpressionStatus, IFormatInfo> = new Map([
|
||||||
[ ExpressionStatus.VERIFIED, {
|
[ ExpressionStatus.VERIFIED, {
|
||||||
text: 'ок',
|
text: 'ок',
|
||||||
color: 'bg-[#aaff80] dark:bg-[#2b8000]',
|
color: 'bg-[#aaff80] dark:bg-[#2b8000]',
|
||||||
|
@ -317,7 +341,7 @@ export const mapStatusInfo: Map<ExpressionStatus, IStatusInfo> = new Map([
|
||||||
[ ExpressionStatus.INCALCULABLE, {
|
[ ExpressionStatus.INCALCULABLE, {
|
||||||
text: 'невыч',
|
text: 'невыч',
|
||||||
color: 'bg-[#ffbb80] dark:bg-[#964600]',
|
color: 'bg-[#ffbb80] dark:bg-[#964600]',
|
||||||
tooltip: 'выражение не вычислимо (экспоненциальная сложность)'
|
tooltip: 'выражение не вычислимо'
|
||||||
}],
|
}],
|
||||||
[ ExpressionStatus.PROPERTY, {
|
[ ExpressionStatus.PROPERTY, {
|
||||||
text: 'св-во',
|
text: 'св-во',
|
||||||
|
@ -336,6 +360,38 @@ export const mapStatusInfo: Map<ExpressionStatus, IStatusInfo> = 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<CstClass, IFormatInfo> = 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 {
|
export function createAliasFor(type: CstType, schema: IRSForm): string {
|
||||||
const prefix = getCstTypePrefix(type);
|
const prefix = getCstTypePrefix(type);
|
||||||
if (!schema.items || schema.items.length <= 0) {
|
if (!schema.items || schema.items.length <= 0) {
|
||||||
|
@ -370,6 +426,8 @@ export function getMockConstituenta(id: number, alias: string, type: CstType, co
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
status: ExpressionStatus.INCORRECT,
|
status: ExpressionStatus.INCORRECT,
|
||||||
|
isTemplate: false,
|
||||||
|
cstClass: CstClass.DERIVED,
|
||||||
parse: {
|
parse: {
|
||||||
status: ParsingStatus.INCORRECT,
|
status: ParsingStatus.INCORRECT,
|
||||||
valueClass: ValueClass.INVALID,
|
valueClass: ValueClass.INVALID,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user