mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Minor UI improvements
This commit is contained in:
parent
d9296ada5c
commit
dc6321b3bf
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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'>
|
||||
|
|
|
@ -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>);
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue
Block a user