Minor UI fixes

This commit is contained in:
IRBorisov 2023-09-30 12:47:27 +03:00
parent 70737410c2
commit 41bb83b784
14 changed files with 132 additions and 94 deletions

View File

@ -17,7 +17,7 @@ function TextInput({
colorClass = 'clr-input', colorClass = 'clr-input',
...props ...props
}: TextInputProps) { }: TextInputProps) {
const borderClass = noBorder ? '': 'border'; const borderClass = noBorder ? '': 'border shadow';
return ( return (
<div className={`flex ${singleRow ? 'items-center gap-4 ' + dimensions : 'flex-col items-start gap-2'}`}> <div className={`flex ${singleRow ? 'items-center gap-4 ' + dimensions : 'flex-col items-start gap-2'}`}>
{label && {label &&
@ -28,7 +28,7 @@ function TextInput({
/>} />}
<input id={id} <input id={id}
title={tooltip} title={tooltip}
className={`px-3 py-2 leading-tight ${borderClass} shadow truncate hover:text-clip clr-outline ${colorClass} ${singleRow ? 'w-full' : dimensions}`} className={`px-3 py-2 leading-tight ${borderClass} truncate hover:text-clip clr-outline ${colorClass} ${singleRow ? 'w-full' : dimensions}`}
required={required} required={required}
{...props} {...props}
/> />

View File

@ -26,6 +26,7 @@ extends Pick<TableOptions<TData>,
'onRowSelectionChange' | 'onColumnVisibilityChange' 'onRowSelectionChange' | 'onColumnVisibilityChange'
> { > {
dense?: boolean dense?: boolean
headPosition?: string
conditionalRowStyles?: IConditionalStyle<TData>[] conditionalRowStyles?: IConditionalStyle<TData>[]
onRowClicked?: (rowData: TData, event: React.MouseEvent<Element, MouseEvent>) => void onRowClicked?: (rowData: TData, event: React.MouseEvent<Element, MouseEvent>) => void
onRowDoubleClicked?: (rowData: TData, event: React.MouseEvent<Element, MouseEvent>) => void onRowDoubleClicked?: (rowData: TData, event: React.MouseEvent<Element, MouseEvent>) => void
@ -46,8 +47,14 @@ extends Pick<TableOptions<TData>,
initialSorting?: ColumnSort initialSorting?: ColumnSort
} }
/**
* UI element: data representation as a table.
*
* @param headPosition - Top position of sticky header (0 if no other sticky elements are present).
* No sticky header if omitted
*/
export default function DataTable<TData extends RowData>({ export default function DataTable<TData extends RowData>({
dense, conditionalRowStyles, dense, headPosition, conditionalRowStyles,
onRowClicked, onRowDoubleClicked, noDataComponent, onRowClicked, onRowDoubleClicked, noDataComponent,
enableRowSelection, enableRowSelection,
@ -106,7 +113,13 @@ export default function DataTable<TData extends RowData>({
{!isEmpty && {!isEmpty &&
<table> <table>
<thead> <thead
className='clr-app shadow-border'
style={{
top: headPosition,
position: 'sticky'
}}
>
{tableImpl.getHeaderGroups().map( {tableImpl.getHeaderGroups().map(
(headerGroup: HeaderGroup<TData>) => ( (headerGroup: HeaderGroup<TData>) => (
<tr key={headerGroup.id}> <tr key={headerGroup.id}>
@ -122,7 +135,7 @@ export default function DataTable<TData extends RowData>({
style={{ style={{
textAlign: header.getSize() > 100 ? 'left': 'center', textAlign: header.getSize() > 100 ? 'left': 'center',
width: header.getSize(), width: header.getSize(),
cursor: enableSorting && header.column.getCanSort() ? 'pointer': 'auto' cursor: enableSorting && header.column.getCanSort() ? 'pointer': 'auto',
}} }}
onClick={enableSorting ? header.column.getToggleSortingHandler() : undefined} onClick={enableSorting ? header.column.getToggleSortingHandler() : undefined}
> >
@ -144,8 +157,7 @@ export default function DataTable<TData extends RowData>({
key={row.id} key={row.id}
className={ className={
row.getIsSelected() ? 'clr-selected clr-hover' : row.getIsSelected() ? 'clr-selected clr-hover' :
index % 2 === 0 ? 'clr-controls clr-hover' : index % 2 === 0 ? 'clr-controls clr-hover' : 'clr-app clr-hover'
'clr-app clr-hover'
} }
style={conditionalRowStyles && getRowStyles(row)} style={conditionalRowStyles && getRowStyles(row)}
> >

View File

@ -7,7 +7,7 @@ function HelpConstituenta() {
<h1>Подсказки</h1> <h1>Подсказки</h1>
<p><b className='text-warning'>Изменения сохраняются ПОСЛЕ нажатия на кнопку снизу или справа вверху</b></p> <p><b className='text-warning'>Изменения сохраняются ПОСЛЕ нажатия на кнопку снизу или справа вверху</b></p>
<p><b>Клик на формальное выражение</b> - обратите внимание на кнопки снизу<br/>Горячие клавиши указаны в подсказках при наведении</p> <p><b>Клик на формальное выражение</b> - обратите внимание на кнопки снизу<br/>Горячие клавиши указаны в подсказках при наведении</p>
<p><b>Поля Термин и Определение</b> - Ctrl+Пробел открывает диалог редактирования отсылок<br/>Перед открытием диалога переместите текстовый курсор на заменяемое слово или ссылку</p> <p><b>Поля Термин и Определение</b> - Ctrl + Пробел открывает диалог редактирования отсылок<br/>Перед открытием диалога переместите текстовый курсор на заменяемое слово или ссылку</p>
<p><b>Список конституент справа</b> - обратите внимание на настройки фильтрации</p> <p><b>Список конституент справа</b> - обратите внимание на настройки фильтрации</p>
<p>- слева от ввода текста настраивается набор атрибутов конституенты</p> <p>- слева от ввода текста настраивается набор атрибутов конституенты</p>
<p>- справа от ввода текста настраивается список конституент, которые фильтруются</p> <p>- справа от ввода текста настраивается список конституент, которые фильтруются</p>

View File

@ -174,7 +174,7 @@ function DlgEditReference({ hideWindow, items, initialRef, initialText, initialT
const cst = props.row.original; const cst = props.row.original;
return (<> return (<>
<div <div
id={`${prefixes.cst_list}${cst.alias}`} id={`${prefixes.cst_wordform_list}${cst.alias}`}
className='min-w-[3.1rem] max-w-[3.1rem] px-1 text-center rounded-md whitespace-nowrap' className='min-w-[3.1rem] max-w-[3.1rem] px-1 text-center rounded-md whitespace-nowrap'
style={{ style={{
borderWidth: '1px', borderWidth: '1px',
@ -185,7 +185,7 @@ function DlgEditReference({ hideWindow, items, initialRef, initialText, initialT
> >
{cst.alias} {cst.alias}
</div> </div>
<ConstituentaTooltip data={cst} anchor={`#${prefixes.cst_list}${cst.alias}`} /> <ConstituentaTooltip data={cst} anchor={`#${prefixes.cst_wordform_list}${cst.alias}`} />
</>); </>);
} }
}), }),
@ -217,7 +217,7 @@ function DlgEditReference({ hideWindow, items, initialRef, initialText, initialT
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
<div className='min-w-[40rem] flex flex-col gap-4 mb-4 mt-2'> <div className='min-w-[40rem] flex flex-col gap-4 mb-4 mt-2'>
<div className='flex self-center flex-start'> <div className='flex items-center self-center flex-start'>
<SelectSingle <SelectSingle
className='z-modal-top min-w-[20rem] w-fit' className='z-modal-top min-w-[20rem] w-fit'
options={SelectorReferenceType} options={SelectorReferenceType}
@ -231,7 +231,7 @@ function DlgEditReference({ hideWindow, items, initialRef, initialText, initialT
</div> </div>
<ConceptTooltip <ConceptTooltip
anchorSelect='#terminology-help' anchorSelect='#terminology-help'
className='max-w-[30rem]' className='max-w-[30rem] z-modal-tooltip'
offset={4} offset={4}
> >
<HelpTerminologyControl /> <HelpTerminologyControl />
@ -269,6 +269,8 @@ function DlgEditReference({ hideWindow, items, initialRef, initialText, initialT
data={filteredData} data={filteredData}
columns={columnsConstituenta} columns={columnsConstituenta}
conditionalRowStyles={conditionalRowStyles} conditionalRowStyles={conditionalRowStyles}
headPosition='0'
dense dense
noDataComponent={ noDataComponent={
@ -289,15 +291,19 @@ function DlgEditReference({ hideWindow, items, initialRef, initialText, initialT
value={alias} value={alias}
onChange={event => setAlias(event.target.value)} onChange={event => setAlias(event.target.value)}
/> />
<TextInput <div className='flex items-center w-full flex-start'>
label='Термин' <div className='self-center text-sm font-semibold'>
singleRow Термин:
disabled </div>
noBorder <TextInput
value={term} singleRow
tooltip={term} disabled
dimensions='w-full' noBorder
/> value={term}
tooltip={term}
dimensions='w-full text-sm'
/>
</div>
</div> </div>
{FormButtons} {FormButtons}
<div className='flex items-center gap-10 flex-start'> <div className='flex items-center gap-10 flex-start'>

View File

@ -122,6 +122,10 @@
.border { .border {
@apply rounded @apply rounded
} }
.shadow-border {
@apply shadow-sm shadow-[var(--cl-bg-40)] dark:shadow-[var(--cd-bg-40)]
}
.clr-modal-backdrop { .clr-modal-backdrop {
opacity: 0.75; opacity: 0.75;

View File

@ -69,7 +69,7 @@ function SearchPanel({ total, filtered, query, setQuery, strategy, setStrategy,
}, [strategy, navigateTo]); }, [strategy, navigateTo]);
return ( return (
<div className='sticky top-0 left-0 right-0 flex items-center justify-start w-full border-b clr-input'> <div className='sticky top-0 left-0 right-0 flex items-center justify-start w-full border-b clr-input max-h-[2.3rem]'>
<div className='px-2 py-1 select-none whitespace-nowrap min-w-[10rem]'> <div className='px-2 py-1 select-none whitespace-nowrap min-w-[10rem]'>
Фильтр Фильтр
<span className='ml-2'> <span className='ml-2'>

View File

@ -96,9 +96,9 @@ function ViewLibrary({ items, resetQuery: cleanQuery }: ViewLibraryProps) {
return ( return (
<div> <div>
{items.length > 0 && {items.length > 0 &&
<div className='relative w-full'> <div className='sticky top-[2.3rem] w-full'>
<div className='absolute top-[-0.125rem] left-0 flex gap-1 ml-3 z-pop'> <div className='absolute top-[-0.125rem] left-0 flex gap-1 ml-3 z-pop'>
<div id='library-help' className='py-2'> <div id='library-help' className='py-2 '>
<HelpIcon color='text-primary' size={5} /> <HelpIcon color='text-primary' size={5} />
</div> </div>
<ConceptTooltip anchorSelect='#library-help'> <ConceptTooltip anchorSelect='#library-help'>
@ -112,6 +112,7 @@ function ViewLibrary({ items, resetQuery: cleanQuery }: ViewLibraryProps) {
columns={columns} columns={columns}
data={items} data={items}
headPosition='2.3rem'
noDataComponent={ noDataComponent={
<div className='flex flex-col gap-4 justify-center p-2 text-center min-h-[6rem]'> <div className='flex flex-col gap-4 justify-center p-2 text-center min-h-[6rem]'>
<p>Список схем пуст</p> <p>Список схем пуст</p>

View File

@ -1,7 +1,6 @@
import { useEffect, useLayoutEffect, useMemo, useState } from 'react'; import { useEffect, useLayoutEffect, useMemo, useState } from 'react';
import ConceptTooltip from '../../components/Common/ConceptTooltip'; import ConceptTooltip from '../../components/Common/ConceptTooltip';
import Divider from '../../components/Common/Divider';
import MiniButton from '../../components/Common/MiniButton'; import MiniButton from '../../components/Common/MiniButton';
import Modal from '../../components/Common/Modal'; import Modal from '../../components/Common/Modal';
import SelectMulti from '../../components/Common/SelectMulti'; import SelectMulti from '../../components/Common/SelectMulti';
@ -107,9 +106,8 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
setInputGrams(SelectorGrammems.filter(gram => form.grams.find(test => test === gram.value))); setInputGrams(SelectorGrammems.filter(gram => form.grams.find(test => test === gram.value)));
} }
function handleResetForm() { function handleResetAll() {
setInputText(''); setForms([]);
setInputGrams([]);
} }
function handleInflect() { function handleInflect() {
@ -199,12 +197,12 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
maxSize: 50, maxSize: 50,
cell: props => cell: props =>
<div> <div>
<MiniButton <MiniButton
tooltip='Удалить словоформу' tooltip='Удалить словоформу'
icon={<CrossIcon size={4} color='text-warning'/>} icon={<CrossIcon size={4} color='text-warning'/>}
noHover noHover
onClick={() => handleDeleteRow(props.row.index)} onClick={() => handleDeleteRow(props.row.index)}
/> />
</div> </div>
}) })
], [colors]); ], [colors]);
@ -232,7 +230,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
</div> </div>
</div> </div>
<div className='min-w-[40rem]'> <div className='min-w-[40rem] max-w-[40rem]'>
<TextArea id='nominal' label='Начальная форма' <TextArea id='nominal' label='Начальная форма'
placeholder='Начальная форма' placeholder='Начальная форма'
rows={1} rows={1}
@ -242,58 +240,38 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
spellCheck spellCheck
/> />
<Divider margins='my-4'/> <div className='text-sm mt-4 mb-2 font-semibold'>
Параметры словоформы
</div>
<div className='flex items-start gap-2 justify-stretch min-h-[6.3rem]'> <div className='flex items-start justify-between w-full'>
<div className='flex flex-col gap-1'> <TextArea
<TextArea placeholder='Введите текст'
placeholder='Введите текст' rows={2}
rows={2} dimensions='min-w-[18rem] w-full min-h-[4.2rem]'
dimensions='min-w-[20rem] min-h-[4.2rem]'
disabled={textProcessor.loading} value={inputText}
value={inputText} onChange={event => setInputText(event.target.value)}
onChange={event => setInputText(event.target.value)} />
<div className='max-w-min'>
<MiniButton
tooltip='Генерировать словоформу'
icon={<ArrowLeftIcon
size={6}
color={inputGrams.length == 0 ? 'text-disabled' : 'text-primary'}
/>}
disabled={textProcessor.loading || inputGrams.length == 0}
onClick={handleInflect}
/>
<MiniButton
tooltip='Определить граммемы'
icon={<ArrowRightIcon
size={6}
color={!inputText ? 'text-disabled' : 'text-primary'}
/>}
disabled={textProcessor.loading || !inputText}
onClick={handleParse}
/> />
<div className='flex items-center justify-between select-none'>
<div className='flex items-center justify-start'>
<MiniButton
tooltip='Добавить словоформу'
icon={<CheckIcon size={6} color={!inputText || inputGrams.length == 0 ? 'text-disabled' : 'text-success'}/>}
disabled={textProcessor.loading || !inputText || inputGrams.length == 0}
onClick={handleAddForm}
/>
<MiniButton
tooltip='Сбросить словоформу'
icon={<CrossIcon size={6} color='text-warning'/>}
disabled={textProcessor.loading}
onClick={handleResetForm}
/>
<MiniButton
tooltip='Генерировать все словоформы'
icon={<ChevronDoubleDownIcon size={6} color='text-primary'/>}
disabled={textProcessor.loading}
onClick={handleGenerateLexeme}
/>
</div>
<div className='text-sm'>
Словоформ: {forms.length}
</div>
<div className='flex items-center justify-start'>
<MiniButton
tooltip='Генерировать словоформу'
icon={<ArrowLeftIcon size={6} color={inputGrams.length == 0 ? 'text-disabled' : 'text-primary'}/>}
disabled={textProcessor.loading || inputGrams.length == 0}
onClick={handleInflect}
/>
<MiniButton
tooltip='Определить граммемы'
icon={<ArrowRightIcon size={6} color={!inputText ? 'text-disabled' : 'text-primary'}/>}
disabled={textProcessor.loading || !inputText}
onClick={handleParse}
/>
</div>
</div>
</div> </div>
<SelectMulti <SelectMulti
className='z-modal-top min-w-[20rem] max-w-[20rem] h-full flex-grow' className='z-modal-top min-w-[20rem] max-w-[20rem] h-full flex-grow'
@ -301,25 +279,58 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
placeholder='Выберите граммемы' placeholder='Выберите граммемы'
value={inputGrams} value={inputGrams}
isDisabled={textProcessor.loading}
onChange={newValue => setInputGrams([...newValue].sort(compareGrammemeOptions))} onChange={newValue => setInputGrams([...newValue].sort(compareGrammemeOptions))}
/> />
</div> </div>
<div className='flex flex-start justify-between'>
<div className='flex items-center justify-start'>
<MiniButton
tooltip='Внести словоформу'
icon={<CheckIcon
size={6}
color={!inputText || inputGrams.length == 0 ? 'text-disabled' : 'text-success'}
/>}
disabled={textProcessor.loading || !inputText || inputGrams.length == 0}
onClick={handleAddForm}
/>
<MiniButton
tooltip='Генерировать все словоформы'
icon={<ChevronDoubleDownIcon
size={6}
color={!inputText ? 'text-disabled' : 'text-primary'}
/>}
disabled={textProcessor.loading || !inputText}
onClick={handleGenerateLexeme}
/>
</div>
<div className='text-sm mt-2 mb-1 font-semibold w-full text-center'>
Заданные вручную словоформы: [{forms.length}]
</div>
<MiniButton
tooltip='Сбросить ВСЕ словоформы'
icon={<CrossIcon
size={6}
color={forms.length === 0 ? 'text-disabled' : 'text-warning'}
/>}
disabled={textProcessor.loading || forms.length === 0}
onClick={handleResetAll}
/>
</div>
<div className='border overflow-y-auto max-h-[17.4rem] min-h-[17.4rem]'> <div className='border overflow-y-auto max-h-[17.4rem] min-h-[17.4rem] mb-2'>
<DataTable <DataTable
data={forms} data={forms}
columns={columns} columns={columns}
headPosition='0'
dense dense
noDataComponent={ noDataComponent={
<span className='flex flex-col justify-center p-2 text-center min-h-[2rem]'> <span className='flex flex-col justify-center p-2 text-center min-h-[2rem]'>
<p>Список пуст</p> <p>Список пуст</p>
<p>Добавьте словоформу</p> <p>Добавьте словоформу</p>
</span> </span>
} }
onRowClicked={handleRowClicked}
onRowDoubleClicked={handleRowClicked}
/> />
</div> </div>
</div> </div>

View File

@ -361,7 +361,8 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps)
<div className='w-full h-full text-sm'> <div className='w-full h-full text-sm'>
<DataTable <DataTable
data={schema?.items ?? []} data={schema?.items ?? []}
columns={columns} columns={columns}
headPosition='2.2rem'
dense dense
onRowDoubleClicked={handleRowDoubleClicked} onRowDoubleClicked={handleRowDoubleClicked}

View File

@ -209,6 +209,7 @@ function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }:
data={filteredData} data={filteredData}
columns={columns} columns={columns}
conditionalRowStyles={conditionalRowStyles} conditionalRowStyles={conditionalRowStyles}
headPosition='0'
dense dense
enableHiding enableHiding

View File

@ -47,12 +47,13 @@ function ViewSubscriptions({items}: ViewSubscriptionsProps) {
], [intl]); ], [intl]);
return ( return (
<div className='h-full overflow-auto text-sm border w-fit'> <div className='max-h-[23.8rem] overflow-auto text-sm border w-fit'>
<DataTable <DataTable
columns={columns} columns={columns}
data={items} data={items}
headPosition='0'
dense dense
enableSorting enableSorting
initialSorting={{ initialSorting={{
id: 'time_update', id: 'time_update',

View File

@ -210,7 +210,7 @@ export function domTooltipSyntacticReference(ref: ISyntacticReference, masterRef
dom.className = `${DIMENSIONS} ${LAYOUT} border shadow-md text-sm select-none cursor-auto`; dom.className = `${DIMENSIONS} ${LAYOUT} border shadow-md text-sm select-none cursor-auto`;
const title = document.createElement('p'); const title = document.createElement('p');
title.innerHTML = '<b>Синтаксическая ссылка</b>'; title.innerHTML = '<b>Связывание слов</b>';
dom.appendChild(title); dom.appendChild(title);
const offset = document.createElement('p'); const offset = document.createElement('p');

View File

@ -34,6 +34,7 @@ export const globalIDs = {
export const prefixes = { export const prefixes = {
cst_list: 'cst-list-', cst_list: 'cst-list-',
cst_wordform_list: 'cst-wordform-list-',
cst_status_list: 'cst-status-list-', cst_status_list: 'cst-status-list-',
topic_list: 'topic-list-', topic_list: 'topic-list-',
library_list: 'library-list-', library_list: 'library-list-',

View File

@ -239,7 +239,7 @@ export function labelCstType(type: CstType): string {
export function labelReferenceType(type: ReferenceType): string { export function labelReferenceType(type: ReferenceType): string {
switch(type) { switch(type) {
case ReferenceType.ENTITY: return 'Использование термина'; case ReferenceType.ENTITY: return 'Использование термина';
case ReferenceType.SYNTACTIC: return 'Синтаксическая зависимость'; case ReferenceType.SYNTACTIC: return 'Связывание слов';
} }
} }