Minor UI improvements

This commit is contained in:
IRBorisov 2023-11-30 17:47:37 +03:00
parent d9296ada5c
commit dc6321b3bf
11 changed files with 214 additions and 221 deletions

View File

@ -2,19 +2,20 @@ import type { TabProps } from 'react-tabs';
import { Tab } from 'react-tabs';
interface ConceptTabProps
extends Omit<TabProps, 'className' | 'title'> {
extends Omit<TabProps, 'title' | 'children'> {
className?: string
tooltip?: string
label?: string
}
function ConceptTab({ children, tooltip, className, ...otherProps }: ConceptTabProps) {
function ConceptTab({ label, tooltip, className, ...otherProps }: ConceptTabProps) {
return (
<Tab
className={`px-2 py-1 h-full flex justify-center text-sm hover:cursor-pointer clr-tab whitespace-nowrap min-w-[6rem] ${className}`}
title={tooltip}
{...otherProps}
>
{children}
{label}
</Tab>
);
}

View File

@ -8,7 +8,6 @@ import { RefObject, useCallback, useMemo, useRef } from 'react';
import { useRSForm } from '../../context/RSFormContext';
import { useConceptTheme } from '../../context/ThemeContext';
import { TokenID } from '../../models/rslang';
import Label from '../Common/Label';
import { ccBracketMatching } from './bracketMatching';
import { RSLanguage } from './rslang';
@ -105,39 +104,7 @@ function RSInput({
return;
}
const text = new RSTextWrapper(thisRef.current as Required<ReactCodeMirrorRef>);
if (event.shiftKey && !event.altKey) {
if (event.key === '*') {
text.insertToken(TokenID.DECART);
event.preventDefault();
} else if (event.code === 'KeyB') {
text.insertChar('');
event.preventDefault();
} else if (event.code === 'KeyZ') {
text.insertChar('Z');
event.preventDefault();
} else if (event.code === 'KeyR') {
text.insertChar('R');
event.preventDefault();
} else if (event.code === 'KeyF') {
text.insertChar('F');
event.preventDefault();
} else if (event.code === 'KeyP') {
text.insertChar('P');
event.preventDefault();
} else if (event.code === 'KeyX') {
text.insertChar('X');
event.preventDefault();
} else if (event.code === 'KeyS') {
text.insertChar('S');
event.preventDefault();
} else if (event.code === 'KeyD') {
text.insertChar('D');
event.preventDefault();
} else if (event.code === 'KeyC') {
text.insertChar('C');
event.preventDefault();
}
} else if (event.altKey) {
if (event.altKey) {
if (text.processAltKey(event.code, event.shiftKey)) {
event.preventDefault();
}

View File

@ -6,9 +6,24 @@ import { TokenID } from '../../models/rslang';
import { CodeMirrorWrapper } from '../../utils/codemirror';
export function getSymbolSubstitute(keyCode: string, shiftPressed: boolean): string | undefined {
console.log(keyCode);
if (shiftPressed) {
switch (keyCode) {
case 'Backquote': return '∃';
case 'Backslash': return '|';
case 'BracketLeft': return '{';
case 'BracketRight': return '}';
case 'Digit8': return '×';
case 'KeyB': return '';
case 'KeyZ': return 'Z';
case 'KeyR': return 'R';
case 'KeyF': return 'F';
case 'KeyP': return 'P';
case 'KeyX': return 'X';
case 'KeyS': return 'S';
case 'KeyD': return 'D';
case 'KeyC': return 'C';
}
} else {
switch (keyCode) {
@ -37,6 +52,11 @@ export function getSymbolSubstitute(keyCode: string, shiftPressed: boolean): str
case 'KeyV': return 'θ';
case 'KeyB': return 'β';
case 'KeyN': return 'η';
// punctuation
case 'BracketLeft': return '[';
case 'BracketRight': return ']';
case 'Comma': return ',';
}
}
return undefined;

View File

@ -116,26 +116,29 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
submitText='Создать'
>
<div className='max-w-[40rem] min-w-[40rem] min-h-[35rem] px-2 mb-1'>
<Tabs
<Tabs defaultFocus forceRenderTabPanel
className='flex flex-col items-center'
selectedTabClassName='clr-selected'
selectedIndex={activeTab}
onSelect={setActiveTab}
defaultFocus
forceRenderTabPanel
selectedTabClassName='clr-selected'
className='flex flex-col items-center'
>
<div className='flex gap-1 pl-6 mb-3'>
<TabList className='flex items-start font-semibold text-center border select-none clr-controls small-caps'>
<ConceptTab tooltip='Выбор шаблона выражения' className='border-r w-[8rem]'>
Шаблон
</ConceptTab>
<ConceptTab tooltip='Подстановка аргументов шаблона' className='border-r w-[8rem]'>
Аргументы
</ConceptTab>
<ConceptTab tooltip='Редактирование атрибутов конституенты' className='w-[8rem]'>
Конституента
</ConceptTab>
<ConceptTab
label='Шаблон'
tooltip='Выбор шаблона выражения'
className='border-r w-[8rem]'
/>
<ConceptTab
label='Аргументы'
tooltip='Подстановка аргументов шаблона'
className='border-r w-[8rem]'
/>
<ConceptTab
label='Конституента'
tooltip='Редактирование атрибутов конституенты'
className='w-[8rem]'
/>
</TabList>
<div id='templates-help' className='px-1 py-1'>

View File

@ -26,7 +26,8 @@ function ConstituentaToolbar({
const canSave = useMemo(() => (isModified && editorMode), [isModified, editorMode]);
return (
<div className='relative w-full'>
<div className='absolute right-0 flex items-start justify-center w-full select-none top-1'>
<div className='absolute right-0 flex items-start justify-center w-full top-1'>
<div className=' flex justify-start w-fit select-auto z-pop'>
<MiniButton
tooltip='Сохранить изменения'
disabled={!canSave}
@ -70,6 +71,7 @@ function ConstituentaToolbar({
<HelpConstituenta />
</ConceptTooltip>
</div>
</div>
</div>);
}

View File

@ -103,7 +103,7 @@ function FormConstituenta({
return (<>
{readyForEdit ?
<div className='relative'>
<div className='absolute top-0 right-[-3rem] w-full flex justify-start'>
<div className='absolute top-0 right-[-3rem] w-full flex justify-start select-none'>
<MiniButton
tooltip={`Редактировать словоформы термина: ${constituenta!.term_forms.length}`}
disabled={!readyForEdit}
@ -111,7 +111,7 @@ function FormConstituenta({
onClick={onEditTerm}
icon={<EditIcon size={4} color={readyForEdit ? 'text-primary' : ''} />}
/>
<div className='pt-1 pl-6 text-sm font-semibold pointer-events-none w-fit'>
<div className='pt-1 pl-6 text-sm font-semibold w-fit'>
<span>Имя </span>
<span className='ml-1'>{constituenta?.alias ?? ''}</span>
</div>

View File

@ -53,8 +53,8 @@ function EditorRSForm({ onDestroy, onClaim, onShare, isModified, setIsModified,
onDestroy={onDestroy}
/>
<div className='flex w-full'>
<div className='flex-grow max-w-[40rem] min-w-[30rem] px-4 py-2'>
<div className='flex flex-col gap-3 mt-2'>
<div className='flex-grow max-w-[40rem] min-w-[30rem] px-4 pb-2'>
<div className='flex flex-col gap-3'>
<FormRSForm id={globalIDs.library_item_editor}
isModified={isModified}
setIsModified={setIsModified}

View File

@ -11,7 +11,7 @@ function RSFormStats({ stats }: RSFormStatsProps) {
return null;
}
return (
<div className='flex flex-col gap-1 px-4 py-2 mt-7 min-w-[16rem]'>
<div className='flex flex-col gap-1 px-4 mt-7 min-w-[16rem]'>
<LabeledText id='count_all'
label='Всего конституент '
text={stats.count_all}

View File

@ -131,7 +131,7 @@ function EditorRSList({ onOpenEdit, onCreateCst, onDeleteCst, onTemplates }: Edi
// Clone selected
function handleClone() {
if (selected.length !== 1 || !schema) {
if (selected.length < 1 || !schema) {
return;
}
const activeCst = schema.items.find(cst => cst.id === selected[0]);
@ -164,30 +164,30 @@ function EditorRSList({ onOpenEdit, onCreateCst, onDeleteCst, onTemplates }: Edi
if (!event.altKey || event.shiftKey) {
return;
}
if (processAltKey(event.key)) {
if (processAltKey(event.code)) {
event.preventDefault();
return;
}
}
function processAltKey(key: string): boolean {
function processAltKey(code: string): boolean {
if (selected.length > 0) {
switch (key) {
switch (code) {
case 'ArrowUp': handleMoveUp(); return true;
case 'ArrowDown': handleMoveDown(); return true;
case 'KeyV': handleClone(); return true;
}
}
switch (key) {
case '1': handleCreateCst(CstType.BASE); return true;
case '2': handleCreateCst(CstType.STRUCTURED); return true;
case '3': handleCreateCst(CstType.TERM); return true;
case '4': handleCreateCst(CstType.AXIOM); return true;
case 'й':
case 'q': handleCreateCst(CstType.FUNCTION); return true;
case 'ц':
case 'w': handleCreateCst(CstType.PREDICATE); return true;
case '5': handleCreateCst(CstType.CONSTANT); return true;
case '6': handleCreateCst(CstType.THEOREM); return true;
switch (code) {
case 'Backquote': handleCreateCst(); return true;
case 'Digit1': handleCreateCst(CstType.BASE); return true;
case 'Digit2': handleCreateCst(CstType.STRUCTURED); return true;
case 'Digit3': handleCreateCst(CstType.TERM); return true;
case 'Digit4': handleCreateCst(CstType.AXIOM); return true;
case 'KeyQ': handleCreateCst(CstType.FUNCTION); return true;
case 'KeyW': handleCreateCst(CstType.PREDICATE); return true;
case 'Digit5': handleCreateCst(CstType.CONSTANT); return true;
case 'Digit6': handleCreateCst(CstType.THEOREM); return true;
}
return false;
}
@ -295,8 +295,8 @@ function EditorRSList({ onOpenEdit, onCreateCst, onDeleteCst, onTemplates }: Edi
style={{minHeight: mainHeight}}
onKeyDown={handleTableKey}
>
<div className='sticky top-0 flex justify-start w-full gap-1 px-2 py-1 border-b items-center h-[2.2rem] select-none clr-app'>
<div className='mr-3 min-w-[9rem] whitespace-nowrap small-caps'>
<div className='sticky top-0 flex justify-start w-full gap-1 px-2 border-b py-1 items-center select-none clr-app'>
<div className='min-w-[9rem] max-w-[9rem] whitespace-nowrap small-caps'>
Выбор {selected.length} из {schema?.stats?.count_all ?? 0}
</div>
<RSListToolbar
@ -316,7 +316,7 @@ function EditorRSList({ onOpenEdit, onCreateCst, onDeleteCst, onTemplates }: Edi
<DataTable dense noFooter
data={schema?.items ?? []}
columns={columns}
headPosition='2.2rem'
headPosition='2.3rem'
onRowDoubleClicked={handleRowDoubleClicked}
onRowClicked={handleRowClicked}

View File

@ -34,33 +34,27 @@ function RSListToolbar({
const nothingSelected = useMemo(() => selectedCount === 0, [selectedCount]);
return (
<div className='flex items-center justify-center w-full pr-[9rem] mt-1'>
<div className='flex items-center justify-center w-full mr-[9rem]'>
<MiniButton
tooltip='Переместить вверх'
tooltip='Переместить вверх [Alt + вверх]'
icon={<ArrowUpIcon size={5}/>}
disabled={!editorMode || nothingSelected}
onClick={onMoveUp}
/>
<MiniButton
tooltip='Переместить вниз'
tooltip='Переместить вниз [Alt + вниз]'
icon={<ArrowDownIcon size={5}/>}
disabled={!editorMode || nothingSelected}
onClick={onMoveDown}
/>
<MiniButton
tooltip='Удалить выбранные'
icon={<DumpBinIcon color={editorMode && !nothingSelected ? 'text-warning' : ''} size={5}/>}
disabled={!editorMode || nothingSelected}
onClick={onDelete}
/>
<MiniButton
tooltip='Клонировать конституенту'
tooltip='Клонировать конституенту [Alt + V]'
icon={<CloneIcon color={editorMode && selectedCount === 1 ? 'text-success': ''} size={5}/>}
disabled={!editorMode || selectedCount !== 1}
onClick={onClone}
/>
<MiniButton
tooltip='Добавить новую конституенту...'
tooltip='Добавить новую конституенту... [Alt + `]'
icon={<SmallPlusIcon color={editorMode ? 'text-success': ''} size={5}/>}
disabled={!editorMode}
onClick={() => onCreate()}
@ -72,7 +66,7 @@ function RSListToolbar({
disabled={!editorMode}
onClick={insertMenu.toggle}
/>
{ insertMenu.isActive &&
{insertMenu.isActive ?
<Dropdown>
{(Object.values(CstType)).map(
(typeStr) => {
@ -86,7 +80,7 @@ function RSListToolbar({
{`${getCstTypePrefix(type)}1 — ${labelCstType(type)}`}
</DropdownButton>);
})}
</Dropdown>}
</Dropdown> : null}
</div>
<MiniButton
tooltip='Создать конституенту из шаблона'
@ -100,7 +94,12 @@ function RSListToolbar({
disabled={!editorMode}
onClick={onReindex}
/>
<MiniButton
tooltip='Удалить выбранные [Delete]'
icon={<DumpBinIcon color={editorMode && !nothingSelected ? 'text-warning' : ''} size={5}/>}
disabled={!editorMode || nothingSelected}
onClick={onDelete}
/>
<div className='ml-1' id='items-table-help'>
<HelpIcon color='text-primary' size={5} />
</div>

View File

@ -327,11 +327,10 @@ function RSTabs() {
cstUpdate(data, () => toast.success('Изменения сохранены'));
}, [cstUpdate, activeID]);
return (
<div className='w-full'>
return (<>
{loading ? <ConceptLoader /> : null}
{error ? <ProcessError error={error} /> : null}
{(schema && !loading) ? <>
{showUpload ?
<DlgUploadRSForm
hideWindow={() => setShowUpload(false)}
@ -350,7 +349,7 @@ function RSTabs() {
<DlgCreateCst
hideWindow={() => setShowCreateCst(false)}
onCreate={handleCreateCst}
schema={schema}
schema={schema!}
initial={createInitialData}
/> : null}
{showRenameCst ?
@ -373,18 +372,21 @@ function RSTabs() {
/> : null}
{showTemplates ?
<DlgConstituentaTemplate
schema={schema}
schema={schema!}
hideWindow={() => setShowTemplates(false)}
insertAfter={insertCstID}
onCreate={handleCreateCst}
/> : null}
{(schema && !loading) ?
<Tabs
selectedIndex={activeTab}
onSelect={onSelectTab}
defaultFocus
selectedTabClassName='clr-selected'
className='flex flex-col items-center w-full'
className='flex flex-col w-full'
>
<div className='flex justify-center w-[100vw]'>
<TabList className='flex items-start border-b-2 border-x-2 select-none justify-stretch w-fit clr-controls h-[1.9rem] small-caps font-semibold'>
<RSTabsMenu
onDownload={onDownloadSchema}
@ -396,26 +398,26 @@ function RSTabs() {
showUploadDialog={() => setShowUpload(true)}
/>
<ConceptTab
label='Карточка'
className='border-x-2'
tooltip={`Название схемы: ${schema.title ?? ''}`}
>
Карточка
</ConceptTab>
/>
<ConceptTab
label='Содержание'
className='border-r-2'
tooltip={`Всего конституент: ${schema.stats?.count_all ?? 0}\nКоличество ошибок: ${schema.stats?.count_errors ?? 0}`}
>
Содержание
</ConceptTab>
<ConceptTab className='border-r-2'>
Редактор
</ConceptTab>
<ConceptTab className=''>
Граф термов
</ConceptTab>
/>
<ConceptTab
label='Редактор'
className='border-r-2'
/>
<ConceptTab
label='Граф термов'
/>
</TabList>
</div>
<div className='overflow-y-auto min-w-[48rem]' style={{ maxHeight: panelHeight}}>
<div className='overflow-y-auto min-w-[48rem] w-[100vw] flex justify-center' style={{ maxHeight: panelHeight}}>
<TabPanel forceRender style={{ display: activeTab === RSTabID.CARD ? '': 'none' }}>
<EditorRSForm
isModified={isModified}
@ -460,9 +462,8 @@ function RSTabs() {
/>
</TabPanel>
</div>
</Tabs>
</> : null}
</div>);
</Tabs> : null}
</>);
}
export default RSTabs;