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

View File

@ -8,7 +8,6 @@ import { RefObject, useCallback, useMemo, useRef } from 'react';
import { useRSForm } from '../../context/RSFormContext'; import { useRSForm } from '../../context/RSFormContext';
import { useConceptTheme } from '../../context/ThemeContext'; import { useConceptTheme } from '../../context/ThemeContext';
import { TokenID } from '../../models/rslang';
import Label from '../Common/Label'; import Label from '../Common/Label';
import { ccBracketMatching } from './bracketMatching'; import { ccBracketMatching } from './bracketMatching';
import { RSLanguage } from './rslang'; import { RSLanguage } from './rslang';
@ -105,39 +104,7 @@ function RSInput({
return; return;
} }
const text = new RSTextWrapper(thisRef.current as Required<ReactCodeMirrorRef>); const text = new RSTextWrapper(thisRef.current as Required<ReactCodeMirrorRef>);
if (event.shiftKey && !event.altKey) { if (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 (text.processAltKey(event.code, event.shiftKey)) { if (text.processAltKey(event.code, event.shiftKey)) {
event.preventDefault(); event.preventDefault();
} }

View File

@ -6,9 +6,24 @@ import { TokenID } from '../../models/rslang';
import { CodeMirrorWrapper } from '../../utils/codemirror'; import { CodeMirrorWrapper } from '../../utils/codemirror';
export function getSymbolSubstitute(keyCode: string, shiftPressed: boolean): string | undefined { export function getSymbolSubstitute(keyCode: string, shiftPressed: boolean): string | undefined {
console.log(keyCode);
if (shiftPressed) { if (shiftPressed) {
switch (keyCode) { switch (keyCode) {
case 'Backquote': return '∃'; 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 { } else {
switch (keyCode) { switch (keyCode) {
@ -37,6 +52,11 @@ export function getSymbolSubstitute(keyCode: string, shiftPressed: boolean): str
case 'KeyV': return 'θ'; case 'KeyV': return 'θ';
case 'KeyB': return 'β'; case 'KeyB': return 'β';
case 'KeyN': return 'η'; case 'KeyN': return 'η';
// punctuation
case 'BracketLeft': return '[';
case 'BracketRight': return ']';
case 'Comma': return ',';
} }
} }
return undefined; return undefined;

View File

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

View File

@ -26,7 +26,8 @@ function ConstituentaToolbar({
const canSave = useMemo(() => (isModified && editorMode), [isModified, editorMode]); const canSave = useMemo(() => (isModified && editorMode), [isModified, editorMode]);
return ( return (
<div className='relative w-full'> <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 <MiniButton
tooltip='Сохранить изменения' tooltip='Сохранить изменения'
disabled={!canSave} disabled={!canSave}
@ -70,6 +71,7 @@ function ConstituentaToolbar({
<HelpConstituenta /> <HelpConstituenta />
</ConceptTooltip> </ConceptTooltip>
</div> </div>
</div>
</div>); </div>);
} }

View File

@ -103,7 +103,7 @@ function FormConstituenta({
return (<> return (<>
{readyForEdit ? {readyForEdit ?
<div className='relative'> <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 <MiniButton
tooltip={`Редактировать словоформы термина: ${constituenta!.term_forms.length}`} tooltip={`Редактировать словоформы термина: ${constituenta!.term_forms.length}`}
disabled={!readyForEdit} disabled={!readyForEdit}
@ -111,7 +111,7 @@ function FormConstituenta({
onClick={onEditTerm} onClick={onEditTerm}
icon={<EditIcon size={4} color={readyForEdit ? 'text-primary' : ''} />} 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>Имя </span>
<span className='ml-1'>{constituenta?.alias ?? ''}</span> <span className='ml-1'>{constituenta?.alias ?? ''}</span>
</div> </div>

View File

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

View File

@ -11,7 +11,7 @@ function RSFormStats({ stats }: RSFormStatsProps) {
return null; return null;
} }
return ( 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' <LabeledText id='count_all'
label='Всего конституент ' label='Всего конституент '
text={stats.count_all} text={stats.count_all}

View File

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

View File

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

View File

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