Minor UI improvements

This commit is contained in:
IRBorisov 2024-02-20 13:12:06 +03:00
parent 6e1460a827
commit 2d3403bb84
4 changed files with 55 additions and 10 deletions

View File

@ -7,17 +7,19 @@ function HelpConstituenta() {
<div className='leading-tight'> <div className='leading-tight'>
<h1>Редактор конституент</h1> <h1>Редактор конституент</h1>
<p><b>Сохранить изменения</b>: Ctrl + S или клик по кнопке Сохранить</p> <p><b>Сохранить изменения</b>: Ctrl + S или клик по кнопке Сохранить</p>
<p className='mt-1'><b>Формальное определение</b>: обратите внимание на кнопки снизу<br/>Горячие клавиши указаны в подсказках при наведении</p> <p className='mt-1'><b>Формальное определение</b></p>
<p className='mt-1'><b>Поля Термин и Определение</b>: Ctrl + Пробел открывает диалог редактирования отсылок<br/>Перед открытием диалога переместите текстовый курсор на заменяемое слово или ссылку</p> <p>- Ctrl + Пробел после ввода первой буквы дополняет до незанятого имени</p>
<p>- специальные конструкции вводятся с помощью кнопок снизу</p>
<p className='mt-1'><b>Термин и Определение</b>: Ctrl + Пробел открывает диалог редактирования отсылок<br/>Перед открытием диалога переместите текстовый курсор на заменяемое слово или ссылку</p>
<p className='mt-1'><b>Список конституент справа</b>: обратите внимание на настройки фильтрации</p> <p className='mt-1'><b>Список конституент справа</b>: обратите внимание на настройки фильтрации</p>
<p>- первая настройка - атрибуты конституенты</p> <p>- первая настройка - атрибуты конституенты</p>
<p>- вторая настройка - принцип отбора конституент по графу термов</p> <p>- вторая настройка - принцип отбора конституент по графу термов</p>
<p>- текущая конституента выделена цветом строки</p> <p>- текущая конституента выделена цветом</p>
<p>- двойной клик / Alt + клик - выбор редактируемой конституенты</p> <p>- двойной клик / Alt + клик - выбор редактируемой конституенты</p>
<p>- при наведении на имя конституенты отображаются ее атрибуты</p> <p>- при наведении на имя конституенты отображаются ее атрибуты</p>
<p>- столбец "Описание" содержит один из непустых текстовых атрибутов</p> <p>- столбец "Описание" содержит один из непустых текстовых атрибутов</p>
<Divider margins='mt-4' /> <Divider margins='my-2' />
<InfoCstStatus title='Статусы' /> <InfoCstStatus title='Статусы' />
</div>); </div>);

View File

@ -11,6 +11,8 @@ import { forwardRef, useCallback, useMemo, useRef } from 'react';
import Label from '@/components/ui/Label'; import Label from '@/components/ui/Label';
import { useRSForm } from '@/context/RSFormContext'; import { useRSForm } from '@/context/RSFormContext';
import { useConceptTheme } from '@/context/ThemeContext'; import { useConceptTheme } from '@/context/ThemeContext';
import { generateAlias, getCstTypePrefix, guessCstType } from '@/models/rsformAPI';
import { extractGlobals } from '@/models/rslangAPI';
import { ccBracketMatching } from './bracketMatching'; import { ccBracketMatching } from './bracketMatching';
import { RSLanguage } from './rslang'; import { RSLanguage } from './rslang';
@ -106,7 +108,22 @@ const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
return; return;
} }
const text = new RSTextWrapper(thisRef.current as Required<ReactCodeMirrorRef>); const text = new RSTextWrapper(thisRef.current as Required<ReactCodeMirrorRef>);
if (event.altKey) { if (event.ctrlKey && event.code === 'Space') {
const selection = text.getSelection();
if (!selection.empty || !schema) {
return;
}
const hint = text.getText(selection.from - 1, selection.from);
const type = guessCstType(hint);
if (hint === getCstTypePrefix(type)) {
text.setSelection(selection.from - 1, selection.from);
}
const takenAliases = [...extractGlobals(thisRef.current.view?.state.doc.toString() ?? '')];
const newAlias = generateAlias(type, schema, takenAliases);
text.replaceWith(newAlias);
event.preventDefault();
event.stopPropagation();
} else if (event.altKey) {
if (text.processAltKey(event.code, event.shiftKey)) { if (text.processAltKey(event.code, event.shiftKey)) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
@ -124,7 +141,7 @@ const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
event.stopPropagation(); event.stopPropagation();
} }
}, },
[thisRef, onAnalyze] [thisRef, onAnalyze, schema]
); );
return ( return (

View File

@ -244,6 +244,27 @@ export function getCstTypePrefix(type: CstType) {
} }
} }
/**
* Guess {@link CstType} from user input hint.
*/
export function guessCstType(hint: string, defaultType: CstType = CstType.TERM) {
if (hint.length !== 1) {
return defaultType;
}
// prettier-ignore
switch (hint) {
case 'X': return CstType.BASE;
case 'C': return CstType.CONSTANT;
case 'S': return CstType.STRUCTURED;
case 'A': return CstType.AXIOM;
case 'D': return CstType.TERM;
case 'F': return CstType.FUNCTION;
case 'P': return CstType.PREDICATE;
case 'T': return CstType.THEOREM;
}
return defaultType;
}
/** /**
* Validate new alias against {@link CstType} and {@link IRSForm}. * Validate new alias against {@link CstType} and {@link IRSForm}.
*/ */
@ -261,17 +282,22 @@ export function getDefinitionPrefix(cst: IConstituenta): string {
/** /**
* Generate alias for new {@link IConstituenta} of a given {@link CstType} for current {@link IRSForm}. * Generate alias for new {@link IConstituenta} of a given {@link CstType} for current {@link IRSForm}.
*/ */
export function generateAlias(type: CstType, schema: IRSForm): string { export function generateAlias(type: CstType, schema: IRSForm, takenAliases: string[] = []): string {
const prefix = getCstTypePrefix(type); const prefix = getCstTypePrefix(type);
if (!schema.items || schema.items.length <= 0) { if (!schema.items || schema.items.length <= 0) {
return `${prefix}1`; return `${prefix}1`;
} }
const index = schema.items.reduce((prev, cst, index) => { let index = schema.items.reduce((prev, cst, index) => {
if (cst.cst_type !== type) { if (cst.cst_type !== type) {
return prev; return prev;
} }
index = Number(cst.alias.slice(1 - cst.alias.length)) + 1; index = Number(cst.alias.slice(1 - cst.alias.length)) + 1;
return Math.max(prev, index); return Math.max(prev, index);
}, 1); }, 1);
return `${prefix}${index}`; let alias = `${prefix}${index}`;
while (takenAliases.includes(alias)) {
index = index + 1;
alias = `${prefix}${index}`;
}
return alias;
} }

View File

@ -88,7 +88,7 @@ function FormRSForm({ id, disabled, isModified, setIsModified }: FormRSFormProps
return ( return (
<form <form
id={id} id={id}
className={clsx('mt-1 min-w-[22rem] w-full sm:max-w-[30rem]', 'py-1', classnames.flex_col)} className={clsx('mt-1 min-w-[22rem] sm:w-[30rem]', 'py-1', classnames.flex_col)}
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
<TextInput <TextInput