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

View File

@ -11,6 +11,8 @@ import { forwardRef, useCallback, useMemo, useRef } from 'react';
import Label from '@/components/ui/Label';
import { useRSForm } from '@/context/RSFormContext';
import { useConceptTheme } from '@/context/ThemeContext';
import { generateAlias, getCstTypePrefix, guessCstType } from '@/models/rsformAPI';
import { extractGlobals } from '@/models/rslangAPI';
import { ccBracketMatching } from './bracketMatching';
import { RSLanguage } from './rslang';
@ -106,7 +108,22 @@ const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
return;
}
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)) {
event.preventDefault();
event.stopPropagation();
@ -124,7 +141,7 @@ const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
event.stopPropagation();
}
},
[thisRef, onAnalyze]
[thisRef, onAnalyze, schema]
);
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}.
*/
@ -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}.
*/
export function generateAlias(type: CstType, schema: IRSForm): string {
export function generateAlias(type: CstType, schema: IRSForm, takenAliases: string[] = []): string {
const prefix = getCstTypePrefix(type);
if (!schema.items || schema.items.length <= 0) {
return `${prefix}1`;
}
const index = schema.items.reduce((prev, cst, index) => {
let index = schema.items.reduce((prev, cst, index) => {
if (cst.cst_type !== type) {
return prev;
}
index = Number(cst.alias.slice(1 - cst.alias.length)) + 1;
return Math.max(prev, index);
}, 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 (
<form
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}
>
<TextInput