UI update: new fonts and adjustments

This commit is contained in:
IRBorisov 2023-12-30 19:43:24 +03:00
parent 0a4d48f6ef
commit 7414aa0186
33 changed files with 223 additions and 220 deletions

View File

@ -60,6 +60,7 @@
"filterset", "filterset",
"forceatlas", "forceatlas",
"futr", "futr",
"Geologica",
"Grammeme", "Grammeme",
"Grammemes", "Grammemes",
"GRND", "GRND",

View File

@ -14,16 +14,11 @@
"sourceType": "module", "sourceType": "module",
"project": ["tsconfig.json", "tsconfig.node.json"] "project": ["tsconfig.json", "tsconfig.node.json"]
}, },
"plugins": [ "plugins": ["react-refresh", "simple-import-sort", "eslint-plugin-tsdoc"],
"react-refresh",
"simple-import-sort",
"eslint-plugin-tsdoc"
],
"rules": { "rules": {
"react-refresh/only-export-components": [ "no-console": "off",
"off", "require-jsdoc": "off",
{ "allowConstantExport": true } "react-refresh/only-export-components": ["off", { "allowConstantExport": true }],
],
"simple-import-sort/imports": "warn", "simple-import-sort/imports": "warn",
"tsdoc/syntax": "warn" "tsdoc/syntax": "warn"
} }

View File

@ -7,6 +7,13 @@
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<meta name="description" content="Веб-приложение для работы с концептуальными схемами" /> <meta name="description" content="Веб-приложение для работы с концептуальными схемами" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Geologica:wght@100;200;300;400;500;600;700;800;900&family=Noto+Sans+Math&display=swap"
rel="stylesheet"
/>
<title>Концепт Портал</title> <title>Концепт Портал</title>
<script> <script>

View File

@ -35,7 +35,7 @@ function Button({
{ {
'border rounded': !noBorder, 'border rounded': !noBorder,
'px-1': dense, 'px-1': dense,
'px-3 py-2': !dense, 'px-3 py-1': !dense,
'cursor-progress': loading, 'cursor-progress': loading,
'cursor-pointer': !loading, 'cursor-pointer': !loading,
'outline-none': noOutline, 'outline-none': noOutline,
@ -49,7 +49,7 @@ function Button({
{...restProps} {...restProps}
> >
{icon ? icon : null} {icon ? icon : null}
{text ? <span className='font-semibold'>{text}</span> : null} {text ? <span className='font-medium'>{text}</span> : null}
</button> </button>
); );
} }

View File

@ -5,7 +5,6 @@ import { globalIDs } from '@/utils/constants';
import { CheckboxCheckedIcon } from '../Icons'; import { CheckboxCheckedIcon } from '../Icons';
import { CProps } from '../props'; import { CProps } from '../props';
import Label from './Label';
export interface CheckboxProps extends Omit<CProps.Button, 'value' | 'onClick'> { export interface CheckboxProps extends Omit<CProps.Button, 'value' | 'onClick'> {
label?: string; label?: string;
@ -57,7 +56,9 @@ function Checkbox({ id, disabled, label, title, className, value, setValue, ...r
</div> </div>
) : null} ) : null}
</div> </div>
<Label className={cursor} text={label} htmlFor={id} /> <label className={clsx('text-sm whitespace-nowrap', cursor)} htmlFor={id}>
{label}
</label>
</button> </button>
); );
} }

View File

@ -19,7 +19,7 @@ function ConceptSearch({ value, onChange, noBorder, ...restProps }: ConceptSearc
<TextInput <TextInput
noOutline noOutline
placeholder='Поиск' placeholder='Поиск'
className='pl-10' className='w-full pl-10'
noBorder={noBorder} noBorder={noBorder}
value={value} value={value}
onChange={event => (onChange ? onChange(event.target.value) : undefined)} onChange={event => (onChange ? onChange(event.target.value) : undefined)}

View File

@ -15,7 +15,7 @@ function ConceptTab({ label, title, className, ...otherProps }: ConceptTabProps)
'min-w-[6rem]', 'min-w-[6rem]',
'px-2 py-1 flex justify-center', 'px-2 py-1 flex justify-center',
'clr-tab', 'clr-tab',
'text-sm whitespace-nowrap small-caps font-semibold', 'text-sm whitespace-nowrap font-controls',
'select-none hover:cursor-pointer', 'select-none hover:cursor-pointer',
className className
)} )}

View File

@ -44,7 +44,7 @@ function FileInput({ label, acceptType, title, className, style, onChange, ...re
onChange={handleFileChange} onChange={handleFileChange}
{...restProps} {...restProps}
/> />
<Button text={label} icon={<BiUpload size='1.5rem' />} onClick={handleUploadClick} title={title} /> <Button text={label} icon={<BiUpload size='1.25rem' />} onClick={handleUploadClick} title={title} />
<Label text={fileName} /> <Label text={fileName} />
</div> </div>
); );

View File

@ -11,7 +11,7 @@ function Label({ text, className, ...restProps }: LabelProps) {
return null; return null;
} }
return ( return (
<label className={clsx('text-sm font-semibold whitespace-nowrap', className)} {...restProps}> <label className={clsx('text-sm font-medium whitespace-nowrap', className)} {...restProps}>
{text} {text}
</label> </label>
); );

View File

@ -8,7 +8,7 @@ interface LabeledValueProps {
function LabeledValue({ id, label, text, title }: LabeledValueProps) { function LabeledValue({ id, label, text, title }: LabeledValueProps) {
return ( return (
<div className='flex justify-between gap-3'> <div className='flex justify-between gap-3'>
<label className='font-semibold' title={title} htmlFor={id}> <label title={title} htmlFor={id}>
{label} {label}
</label> </label>
<span id={id}>{text}</span> <span id={id}>{text}</span>

View File

@ -28,7 +28,7 @@ function SelectorButton({
data-tooltip-content={title} data-tooltip-content={title}
className={clsx( className={clsx(
'px-1 flex flex-start items-center gap-1', 'px-1 flex flex-start items-center gap-1',
'text-sm small-caps select-none', 'text-sm font-controls select-none',
'text-btn clr-text-controls', 'text-btn clr-text-controls',
'disabled:cursor-not-allowed cursor-pointer', 'disabled:cursor-not-allowed cursor-pointer',
{ {
@ -41,7 +41,7 @@ function SelectorButton({
{...restProps} {...restProps}
> >
{icon ? icon : null} {icon ? icon : null}
{text ? <div className={'font-semibold whitespace-nowrap pb-1'}>{text}</div> : null} {text ? <div className={'whitespace-nowrap pb-1'}>{text}</div> : null}
</button> </button>
); );
} }

View File

@ -13,9 +13,9 @@ function SubmitButton({ text = 'ОК', icon, disabled, loading, className, ...re
<button <button
type='submit' type='submit'
className={clsx( className={clsx(
'px-3 py-2 flex gap-2 items-center justify-center', 'px-3 py-1 flex gap-2 items-center justify-center',
'border', 'border',
'font-semibold', 'font-medium',
'clr-btn-primary', 'clr-btn-primary',
'select-none disabled:cursor-not-allowed', 'select-none disabled:cursor-not-allowed',
loading && 'cursor-progress', loading && 'cursor-progress',

View File

@ -30,7 +30,7 @@ function SwitchButton<ValueType>({
className={clsx( className={clsx(
'px-2 py-1', 'px-2 py-1',
'border rounded-none', 'border rounded-none',
'font-semibold small-caps', 'font-controls',
'clr-btn-clear clr-hover', 'clr-btn-clear clr-hover',
'cursor-pointer', 'cursor-pointer',
isSelected && 'clr-selected', isSelected && 'clr-selected',

View File

@ -5,7 +5,6 @@ import { globalIDs } from '@/utils/constants';
import { CheckboxCheckedIcon, CheckboxNullIcon } from '../Icons'; import { CheckboxCheckedIcon, CheckboxNullIcon } from '../Icons';
import { CheckboxProps } from './Checkbox'; import { CheckboxProps } from './Checkbox';
import Label from './Label';
export interface TristateProps extends Omit<CheckboxProps, 'value' | 'setValue'> { export interface TristateProps extends Omit<CheckboxProps, 'value' | 'setValue'> {
value: boolean | null; value: boolean | null;
@ -65,7 +64,9 @@ function Tristate({ id, disabled, label, title, className, value, setValue, ...r
</div> </div>
) : null} ) : null}
</div> </div>
<Label className={cursor} text={label} htmlFor={id} /> <label className={clsx('text-sm whitespace-nowrap', cursor)} htmlFor={id}>
{label}
</label>
</button> </button>
); );
} }

View File

@ -30,19 +30,19 @@ function TableHeader<TData>({ table, headPosition, enableRowSelection, enableSor
<th <th
key={header.id} key={header.id}
colSpan={header.colSpan} colSpan={header.colSpan}
className='px-2 py-2 text-xs font-semibold select-none whitespace-nowrap' className='px-2 py-2 text-xs font-medium select-none whitespace-nowrap'
style={{ style={{
textAlign: header.getSize() > 100 ? 'left' : 'center', textAlign: '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}
> >
{!header.isPlaceholder ? ( {!header.isPlaceholder ? (
<div className='flex gap-1'> <span className='inline-flex gap-1'>
{flexRender(header.column.columnDef.header, header.getContext())} {flexRender(header.column.columnDef.header, header.getContext())}
{enableSorting && header.column.getCanSort() ? <SortingIcon column={header.column} /> : null} {enableSorting && header.column.getCanSort() ? <SortingIcon column={header.column} /> : null}
</div> </span>
) : null} ) : null}
</th> </th>
))} ))}

View File

@ -21,7 +21,7 @@ function NavigationButton({ icon, title, onClick, text }: NavigationButtonProps)
'mr-1 h-full', // 'mr-1 h-full', //
'flex items-center gap-1', 'flex items-center gap-1',
'clr-btn-nav', 'clr-btn-nav',
'small-caps whitespace-nowrap', 'font-controls whitespace-nowrap',
{ {
'px-2': text, 'px-2': text,
'px-4': !text 'px-4': !text
@ -29,7 +29,7 @@ function NavigationButton({ icon, title, onClick, text }: NavigationButtonProps)
)} )}
> >
{icon ? <span>{icon}</span> : null} {icon ? <span>{icon}</span> : null}
{text ? <span className='font-semibold'>{text}</span> : null} {text ? <span>{text}</span> : null}
</button> </button>
); );
} }

View File

@ -82,9 +82,9 @@ const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
{ tag: tags.propertyName, color: colors.fgTeal }, // Radical { tag: tags.propertyName, color: colors.fgTeal }, // Radical
{ tag: tags.keyword, color: colors.fgBlue }, // keywords { tag: tags.keyword, color: colors.fgBlue }, // keywords
{ tag: tags.literal, color: colors.fgBlue }, // literals { tag: tags.literal, color: colors.fgBlue }, // literals
{ tag: tags.controlKeyword, fontWeight: '500' }, // R | I | D { tag: tags.controlKeyword, fontWeight: '400' }, // R | I | D
{ tag: tags.unit, fontSize: '0.75rem' }, // indices { tag: tags.unit, fontSize: '0.75rem' }, // indices
{ tag: tags.brace, color: colors.fgPurple, fontWeight: '700' } // braces (curly brackets) { tag: tags.brace, color: colors.fgPurple, fontWeight: '600' } // braces (curly brackets)
] ]
}), }),
[disabled, colors, darkMode] [disabled, colors, darkMode]
@ -131,6 +131,7 @@ const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
<div className={clsx('flex flex-col gap-2', className, cursor)} style={style}> <div className={clsx('flex flex-col gap-2', className, cursor)} style={style}>
<Label text={label} htmlFor={id} /> <Label text={label} htmlFor={id} />
<CodeMirror <CodeMirror
className='font-math'
id={id} id={id}
ref={thisRef} ref={thisRef}
basicSetup={editorSetup} basicSetup={editorSetup}

View File

@ -1,28 +1,25 @@
import clsx from 'clsx'; import clsx from 'clsx';
import ConceptTooltip from '@/components/Common/ConceptTooltip';
import ConstituentaTooltip from '@/components/Help/ConstituentaTooltip'; import ConstituentaTooltip from '@/components/Help/ConstituentaTooltip';
import { IConstituenta } from '@/models/rsform'; import { IConstituenta } from '@/models/rsform';
import { isMockCst } from '@/models/rsformAPI'; import { isMockCst } from '@/models/rsformAPI';
import { colorFgCstStatus, IColorTheme } from '@/utils/color'; import { colorFgCstStatus, IColorTheme } from '@/utils/color';
import { describeExpressionStatus } from '@/utils/labels';
interface ConstituentaBadgeProps { interface ConstituentaBadgeProps {
prefixID?: string; prefixID?: string;
shortTooltip?: boolean;
value: IConstituenta; value: IConstituenta;
theme: IColorTheme; theme: IColorTheme;
} }
function ConstituentaBadge({ value, prefixID, shortTooltip, theme }: ConstituentaBadgeProps) { function ConstituentaBadge({ value, prefixID, theme }: ConstituentaBadgeProps) {
return ( return (
<div <div
id={`${prefixID}${value.alias}`} id={`${prefixID}${value.alias}`}
className={clsx( className={clsx(
'min-w-[3.1rem] max-w-[3.1rem]', 'min-w-[3.1rem] max-w-[3.1rem]', //
'px-1', 'px-1',
'border rounded-md', 'border rounded-md',
'text-center font-semibold whitespace-nowrap' 'text-center font-medium whitespace-nowrap'
)} )}
style={{ style={{
borderColor: colorFgCstStatus(value.status, theme), borderColor: colorFgCstStatus(value.status, theme),
@ -31,14 +28,7 @@ function ConstituentaBadge({ value, prefixID, shortTooltip, theme }: Constituent
}} }}
> >
{value.alias} {value.alias}
{!shortTooltip ? <ConstituentaTooltip anchor={`#${prefixID}${value.alias}`} data={value} /> : null} <ConstituentaTooltip anchor={`#${prefixID}${value.alias}`} data={value} />
{shortTooltip ? (
<ConceptTooltip anchorSelect={`#${prefixID}${value.alias}`} place='right'>
<p>
<b>Статус</b>: {describeExpressionStatus(value.status)}
</p>
</ConceptTooltip>
) : null}
</div> </div>
); );
} }

View File

@ -16,10 +16,10 @@ function GrammemeBadge({ key, grammeme }: GrammemeBadgeProps) {
<div <div
key={key} key={key}
className={clsx( className={clsx(
'min-w-[3rem]', 'min-w-[3rem]', //
'px-1', 'px-1',
'border rounded-md', 'border rounded-md',
'text-sm font-semibold text-center whitespace-nowrap' 'text-sm font-medium text-center whitespace-nowrap'
)} )}
style={{ style={{
borderColor: colorFgGrammeme(grammeme, colors), borderColor: colorFgGrammeme(grammeme, colors),

View File

@ -20,13 +20,7 @@ function InfoCstClass({ header }: InfoCstClassProps) {
return ( return (
<p key={`${prefixes.cst_status_list}${index}`}> <p key={`${prefixes.cst_status_list}${index}`}>
<span <span
className={clsx( className={clsx('inline-block', 'min-w-[7rem]', 'px-1', 'border', 'text-center text-sm font-controls')}
'inline-block',
'min-w-[7rem]',
'px-1',
'border',
'text-center text-sm small-caps font-semibold'
)}
style={{ backgroundColor: colorBgCstClass(cstClass, colors) }} style={{ backgroundColor: colorBgCstClass(cstClass, colors) }}
> >
{labelCstClass(cstClass)} {labelCstClass(cstClass)}

View File

@ -22,11 +22,11 @@ function InfoCstStatus({ title }: InfoCstStatusProps) {
<p key={`${prefixes.cst_status_list}${index}`}> <p key={`${prefixes.cst_status_list}${index}`}>
<span <span
className={clsx( className={clsx(
'inline-block', 'inline-block', //
'min-w-[7rem]', 'min-w-[7rem]',
'px-1', 'px-1',
'border', 'border',
'text-center text-sm small-caps font-semibold' 'text-center text-sm font-controls'
)} )}
style={{ backgroundColor: colorBgCstStatus(status, colors) }} style={{ backgroundColor: colorBgCstStatus(status, colors) }}
> >

View File

@ -3,6 +3,8 @@ import { useIntl } from 'react-intl';
import { useUsers } from '@/context/UsersContext'; import { useUsers } from '@/context/UsersContext';
import { ILibraryItemEx } from '@/models/library'; import { ILibraryItemEx } from '@/models/library';
import LabeledValue from '../Common/LabeledValue';
interface InfoLibraryItemProps { interface InfoLibraryItemProps {
item?: ILibraryItemEx; item?: ILibraryItemEx;
} }
@ -12,26 +14,13 @@ function InfoLibraryItem({ item }: InfoLibraryItemProps) {
const intl = useIntl(); const intl = useIntl();
return ( return (
<div className='flex flex-col gap-1'> <div className='flex flex-col gap-1'>
<div className='flex'> <LabeledValue label='Владелец' text={getUserLabel(item?.owner ?? null)} />
<label className='font-semibold'>Владелец:</label> <LabeledValue label='Отслеживают' text={item?.subscribers.length ?? 0} />
<span className='min-w-[200px] ml-2 overflow-ellipsis overflow-hidden whitespace-nowrap'> <LabeledValue
{getUserLabel(item?.owner ?? null)} label='Дата обновления'
</span> text={item ? new Date(item?.time_update).toLocaleString(intl.locale) : ''}
</div> />
<div className='flex'> <LabeledValue label='Дата создания' text={item ? new Date(item?.time_create).toLocaleString(intl.locale) : ''} />
<label className='font-semibold'>Отслеживают:</label>
<span id='subscriber-count' className='ml-2'>
{item?.subscribers.length ?? 0}
</span>
</div>
<div className='flex'>
<label className='font-semibold'>Дата обновления:</label>
<span className='ml-2'>{item && new Date(item?.time_update).toLocaleString(intl.locale)}</span>
</div>
<div className='flex'>
<label className='font-semibold'>Дата создания:</label>
<span className='ml-8'>{item && new Date(item?.time_create).toLocaleString(intl.locale)}</span>
</div>
</div> </div>
); );
} }

View File

@ -12,7 +12,7 @@ function SelectedCounter({ total, selected, hideZero, position = 'top-0 left-0'
return null; return null;
} }
return ( return (
<Overlay position={`px-2 ${position}`} className='select-none whitespace-nowrap small-caps clr-app'> <Overlay position={`px-2 ${position}`} className='select-none whitespace-nowrap clr-app'>
Выбор {selected} из {total} Выбор {selected} из {total}
</Overlay> </Overlay>
); );

View File

@ -26,7 +26,7 @@ function WordformButton({ text, example, grams, onSelectGrams, isSelected, ...re
)} )}
{...restProps} {...restProps}
> >
<p className='font-semibold'>{text}</p> <p className='font-medium'>{text}</p>
<p>{example}</p> <p>{example}</p>
</button> </button>
); );

View File

@ -6,6 +6,10 @@
/* prettier-ignore */ /* prettier-ignore */
:root { :root {
--font-ui: 'Geologica', sans-serif;
--font-main: 'Rubik', sans-serif, 'Noto Sans Math', 'Segoe UI Symbol';
--font-math: 'Noto Sans Math', sans-serif, 'Segoe UI Symbol';
/* Light Theme */ /* Light Theme */
--cl-bg-120: hsl(000, 000%, 100%); --cl-bg-120: hsl(000, 000%, 100%);
--cl-bg-100: hsl(000, 000%, 098%); --cl-bg-100: hsl(000, 000%, 098%);
@ -87,6 +91,8 @@
} }
:root { :root {
font-family: var(--font-main);
color: var(--cl-fg-100); color: var(--cl-fg-100);
border-color: var(--cl-bg-40); border-color: var(--cl-bg-40);
background-color: var(--cl-bg-100); background-color: var(--cl-bg-100);
@ -136,9 +142,14 @@
} }
@layer utilities { @layer utilities {
.small-caps { .font-controls {
font-family: var(--font-ui);
font-weight: 600;
font-variant: small-caps; font-variant: small-caps;
} }
.font-math {
font-family: var(--font-math);
}
} }
@layer components { @layer components {

View File

@ -44,8 +44,23 @@ function SearchPanel({ total, filtered, query, setQuery, strategy, setFilter }:
); );
return ( return (
<div className={clsx('sticky top-0', 'w-full max-h-[2.3rem]', 'pr-40 flex', 'border-b', 'clr-input')}> <div
<div className={clsx('min-w-[10rem]', 'px-2 self-center', 'select-none', 'whitespace-nowrap')}> className={clsx(
'sticky top-0', //
'w-full max-h-[2.3rem]',
'pr-40 flex',
'border-b',
'clr-input'
)}
>
<div
className={clsx(
'min-w-[10rem]', //
'px-2 self-center',
'select-none',
'whitespace-nowrap'
)}
>
Фильтр Фильтр
<span className='ml-2'> <span className='ml-2'>
{filtered} из {total} {filtered} из {total}

View File

@ -12,14 +12,7 @@ interface TopicsListProps {
function TopicsList({ activeTopic, onChangeTopic }: TopicsListProps) { function TopicsList({ activeTopic, onChangeTopic }: TopicsListProps) {
return ( return (
<div <div
className={clsx( className={clsx('sticky top-0 left-0', 'min-w-[13rem] self-start', 'border-x', 'clr-controls', '', 'select-none')}
'sticky top-0 left-0',
'min-w-[13rem] self-start',
'border-x',
'clr-controls',
'small-caps',
'select-none'
)}
> >
<h1 className='my-1'>Справка</h1> <h1 className='my-1'>Справка</h1>
{Object.values(HelpTopic).map((topic, index) => ( {Object.values(HelpTopic).map((topic, index) => (

View File

@ -123,7 +123,7 @@ function FormConstituenta({
return ( return (
<> <>
<Overlay position='top-1 left-[4rem]' className='flex select-none'> <Overlay position='top-1 left-[4.1rem]' className='flex select-none'>
<MiniButton <MiniButton
title={`Редактировать словоформы термина: ${constituenta?.term_forms.length ?? 0}`} title={`Редактировать словоформы термина: ${constituenta?.term_forms.length ?? 0}`}
disabled={disabled} disabled={disabled}
@ -131,7 +131,7 @@ function FormConstituenta({
onClick={onEditTerm} onClick={onEditTerm}
icon={<LiaEdit size='1rem' className={!disabled ? 'clr-text-primary' : ''} />} icon={<LiaEdit size='1rem' className={!disabled ? 'clr-text-primary' : ''} />}
/> />
<div className='pt-1 pl-[1.375rem] text-sm font-semibold whitespace-nowrap'> <div className='pt-1 pl-[1.375rem] text-sm font-medium whitespace-nowrap'>
<span>Имя </span> <span>Имя </span>
<span className='ml-1'>{constituenta?.alias ?? ''}</span> <span className='ml-1'>{constituenta?.alias ?? ''}</span>
</div> </div>
@ -205,7 +205,7 @@ function FormConstituenta({
text='Сохранить изменения' text='Сохранить изменения'
className='self-center' className='self-center'
disabled={!isModified || disabled} disabled={!isModified || disabled}
icon={<FiSave size='1.5rem' />} icon={<FiSave size='1.25rem' />}
/> />
</form> </form>
</> </>

View File

@ -22,7 +22,8 @@ function RSLocalButton({ text, title, disabled, onInsert }: RSLocalButtonProps)
'w-[2rem] h-6', 'w-[2rem] h-6',
'cursor-pointer disabled:cursor-default', 'cursor-pointer disabled:cursor-default',
'rounded-none', 'rounded-none',
'clr-hover clr-btn-clear' 'clr-hover clr-btn-clear',
'font-math'
)} )}
onClick={() => onInsert(TokenID.ID_LOCAL, text)} onClick={() => onInsert(TokenID.ID_LOCAL, text)}
> >

View File

@ -23,6 +23,7 @@ function RSTokenButton({ token, disabled, onInsert }: RSTokenButtonProps) {
'px-1', 'px-1',
'outline-none', 'outline-none',
'clr-hover clr-btn-clear', 'clr-hover clr-btn-clear',
'font-math',
'cursor-pointer disabled:cursor-default', 'cursor-pointer disabled:cursor-default',
{ {
'w-[4.5rem]': label.length > 3, 'w-[4.5rem]': label.length > 3,

View File

@ -56,7 +56,7 @@ function StatusBar({ isModified, processing, constituenta, parseData, onAnalyze
) : ( ) : (
<> <>
<StatusIcon status={status} /> <StatusIcon status={status} />
<span className='pb-[0.125rem] font-semibold small-caps pr-2'>{labelExpressionStatus(status)}</span> <span className='pb-[0.125rem] font-controls pr-2'>{labelExpressionStatus(status)}</span>
</> </>
)} )}
</div> </div>

View File

@ -16,15 +16,13 @@ import { IRSFormCreateData } from '@/models/rsform';
import { classnames, limits, patterns } from '@/utils/constants'; import { classnames, limits, patterns } from '@/utils/constants';
interface FormRSFormProps { interface FormRSFormProps {
id?: string id?: string;
disabled: boolean disabled: boolean;
isModified: boolean isModified: boolean;
setIsModified: Dispatch<SetStateAction<boolean>> setIsModified: Dispatch<SetStateAction<boolean>>;
} }
function FormRSForm({ function FormRSForm({ id, disabled, isModified, setIsModified }: FormRSFormProps) {
id, disabled, isModified, setIsModified,
}: FormRSFormProps) {
const { schema, update, processing } = useRSForm(); const { schema, update, processing } = useRSForm();
const { user } = useAuth(); const { user } = useAuth();
@ -34,8 +32,7 @@ function FormRSForm({
const [common, setCommon] = useState(false); const [common, setCommon] = useState(false);
const [canonical, setCanonical] = useState(false); const [canonical, setCanonical] = useState(false);
useEffect( useEffect(() => {
() => {
if (!schema) { if (!schema) {
setIsModified(false); setIsModified(false);
return; return;
@ -48,12 +45,22 @@ function FormRSForm({
schema.is_canonical !== canonical schema.is_canonical !== canonical
); );
return () => setIsModified(false); return () => setIsModified(false);
}, [schema, schema?.title, schema?.alias, schema?.comment, }, [
schema?.is_common, schema?.is_canonical, schema,
title, alias, comment, common, canonical, setIsModified]); schema?.title,
schema?.alias,
schema?.comment,
schema?.is_common,
schema?.is_canonical,
title,
alias,
comment,
common,
canonical,
setIsModified
]);
useLayoutEffect( useLayoutEffect(() => {
() => {
if (schema) { if (schema) {
setTitle(schema.title); setTitle(schema.title);
setAlias(schema.alias); setAlias(schema.alias);
@ -79,21 +86,16 @@ function FormRSForm({
}; };
return ( return (
<form id={id} <form id={id} className={clsx('mt-1 min-w-[22rem] w-[30rem]', 'py-1', classnames.flex_col)} onSubmit={handleSubmit}>
className={clsx( <TextInput
'mt-1 min-w-[22rem] w-[30rem]', required
'py-1',
classnames.flex_col
)}
onSubmit={handleSubmit}
>
<TextInput required
label='Полное название' label='Полное название'
value={title} value={title}
disabled={disabled} disabled={disabled}
onChange={event => setTitle(event.target.value)} onChange={event => setTitle(event.target.value)}
/> />
<TextInput required <TextInput
required
label='Сокращение' label='Сокращение'
className='w-[14rem]' className='w-[14rem]'
pattern={patterns.alias} pattern={patterns.alias}
@ -129,9 +131,10 @@ function FormRSForm({
className='self-center' className='self-center'
loading={processing} loading={processing}
disabled={!isModified || disabled} disabled={!isModified || disabled}
icon={<FiSave size='1.5rem' />} icon={<FiSave size='1.25rem' />}
/> />
</form>); </form>
);
} }
export default FormRSForm; export default FormRSForm;

View File

@ -204,7 +204,7 @@ function EditorRSList({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: Edito
onCreate={handleCreateCst} onCreate={handleCreateCst}
onDelete={handleDelete} onDelete={handleDelete}
/> />
<SelectedCounter total={schema?.stats?.count_all ?? 0} selected={selected.length} position='left-0 top-1' /> <SelectedCounter total={schema?.stats?.count_all ?? 0} selected={selected.length} position='left-1 top-2' />
<div className='pt-[2.3rem] border-b' /> <div className='pt-[2.3rem] border-b' />