mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 04:50:36 +03:00
UI update: new fonts and adjustments
This commit is contained in:
parent
0a4d48f6ef
commit
7414aa0186
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
@ -60,6 +60,7 @@
|
|||
"filterset",
|
||||
"forceatlas",
|
||||
"futr",
|
||||
"Geologica",
|
||||
"Grammeme",
|
||||
"Grammemes",
|
||||
"GRND",
|
||||
|
|
|
@ -1,30 +1,25 @@
|
|||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended-type-checked",
|
||||
"plugin:react-hooks/recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module",
|
||||
"project": ["tsconfig.json", "tsconfig.node.json"]
|
||||
},
|
||||
"plugins": [
|
||||
"react-refresh",
|
||||
"simple-import-sort",
|
||||
"eslint-plugin-tsdoc"
|
||||
],
|
||||
"rules": {
|
||||
"react-refresh/only-export-components": [
|
||||
"off",
|
||||
{ "allowConstantExport": true }
|
||||
],
|
||||
"simple-import-sort/imports": "warn",
|
||||
"tsdoc/syntax": "warn"
|
||||
}
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended-type-checked",
|
||||
"plugin:react-hooks/recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module",
|
||||
"project": ["tsconfig.json", "tsconfig.node.json"]
|
||||
},
|
||||
"plugins": ["react-refresh", "simple-import-sort", "eslint-plugin-tsdoc"],
|
||||
"rules": {
|
||||
"no-console": "off",
|
||||
"require-jsdoc": "off",
|
||||
"react-refresh/only-export-components": ["off", { "allowConstantExport": true }],
|
||||
"simple-import-sort/imports": "warn",
|
||||
"tsdoc/syntax": "warn"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,13 @@
|
|||
<meta name="theme-color" content="#000000" />
|
||||
<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>
|
||||
|
||||
<script>
|
||||
|
|
|
@ -35,7 +35,7 @@ function Button({
|
|||
{
|
||||
'border rounded': !noBorder,
|
||||
'px-1': dense,
|
||||
'px-3 py-2': !dense,
|
||||
'px-3 py-1': !dense,
|
||||
'cursor-progress': loading,
|
||||
'cursor-pointer': !loading,
|
||||
'outline-none': noOutline,
|
||||
|
@ -49,7 +49,7 @@ function Button({
|
|||
{...restProps}
|
||||
>
|
||||
{icon ? icon : null}
|
||||
{text ? <span className='font-semibold'>{text}</span> : null}
|
||||
{text ? <span className='font-medium'>{text}</span> : null}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import { globalIDs } from '@/utils/constants';
|
|||
|
||||
import { CheckboxCheckedIcon } from '../Icons';
|
||||
import { CProps } from '../props';
|
||||
import Label from './Label';
|
||||
|
||||
export interface CheckboxProps extends Omit<CProps.Button, 'value' | 'onClick'> {
|
||||
label?: string;
|
||||
|
@ -57,7 +56,9 @@ function Checkbox({ id, disabled, label, title, className, value, setValue, ...r
|
|||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<Label className={cursor} text={label} htmlFor={id} />
|
||||
<label className={clsx('text-sm whitespace-nowrap', cursor)} htmlFor={id}>
|
||||
{label}
|
||||
</label>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ function ConceptSearch({ value, onChange, noBorder, ...restProps }: ConceptSearc
|
|||
<TextInput
|
||||
noOutline
|
||||
placeholder='Поиск'
|
||||
className='pl-10'
|
||||
className='w-full pl-10'
|
||||
noBorder={noBorder}
|
||||
value={value}
|
||||
onChange={event => (onChange ? onChange(event.target.value) : undefined)}
|
||||
|
|
|
@ -15,7 +15,7 @@ function ConceptTab({ label, title, className, ...otherProps }: ConceptTabProps)
|
|||
'min-w-[6rem]',
|
||||
'px-2 py-1 flex justify-center',
|
||||
'clr-tab',
|
||||
'text-sm whitespace-nowrap small-caps font-semibold',
|
||||
'text-sm whitespace-nowrap font-controls',
|
||||
'select-none hover:cursor-pointer',
|
||||
className
|
||||
)}
|
||||
|
|
|
@ -44,7 +44,7 @@ function FileInput({ label, acceptType, title, className, style, onChange, ...re
|
|||
onChange={handleFileChange}
|
||||
{...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} />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -11,7 +11,7 @@ function Label({ text, className, ...restProps }: LabelProps) {
|
|||
return null;
|
||||
}
|
||||
return (
|
||||
<label className={clsx('text-sm font-semibold whitespace-nowrap', className)} {...restProps}>
|
||||
<label className={clsx('text-sm font-medium whitespace-nowrap', className)} {...restProps}>
|
||||
{text}
|
||||
</label>
|
||||
);
|
||||
|
|
|
@ -8,7 +8,7 @@ interface LabeledValueProps {
|
|||
function LabeledValue({ id, label, text, title }: LabeledValueProps) {
|
||||
return (
|
||||
<div className='flex justify-between gap-3'>
|
||||
<label className='font-semibold' title={title} htmlFor={id}>
|
||||
<label title={title} htmlFor={id}>
|
||||
{label}
|
||||
</label>
|
||||
<span id={id}>{text}</span>
|
||||
|
|
|
@ -28,7 +28,7 @@ function SelectorButton({
|
|||
data-tooltip-content={title}
|
||||
className={clsx(
|
||||
'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',
|
||||
'disabled:cursor-not-allowed cursor-pointer',
|
||||
{
|
||||
|
@ -41,7 +41,7 @@ function SelectorButton({
|
|||
{...restProps}
|
||||
>
|
||||
{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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -13,9 +13,9 @@ function SubmitButton({ text = 'ОК', icon, disabled, loading, className, ...re
|
|||
<button
|
||||
type='submit'
|
||||
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',
|
||||
'font-semibold',
|
||||
'font-medium',
|
||||
'clr-btn-primary',
|
||||
'select-none disabled:cursor-not-allowed',
|
||||
loading && 'cursor-progress',
|
||||
|
|
|
@ -30,7 +30,7 @@ function SwitchButton<ValueType>({
|
|||
className={clsx(
|
||||
'px-2 py-1',
|
||||
'border rounded-none',
|
||||
'font-semibold small-caps',
|
||||
'font-controls',
|
||||
'clr-btn-clear clr-hover',
|
||||
'cursor-pointer',
|
||||
isSelected && 'clr-selected',
|
||||
|
|
|
@ -5,7 +5,6 @@ import { globalIDs } from '@/utils/constants';
|
|||
|
||||
import { CheckboxCheckedIcon, CheckboxNullIcon } from '../Icons';
|
||||
import { CheckboxProps } from './Checkbox';
|
||||
import Label from './Label';
|
||||
|
||||
export interface TristateProps extends Omit<CheckboxProps, 'value' | 'setValue'> {
|
||||
value: boolean | null;
|
||||
|
@ -65,7 +64,9 @@ function Tristate({ id, disabled, label, title, className, value, setValue, ...r
|
|||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<Label className={cursor} text={label} htmlFor={id} />
|
||||
<label className={clsx('text-sm whitespace-nowrap', cursor)} htmlFor={id}>
|
||||
{label}
|
||||
</label>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -30,19 +30,19 @@ function TableHeader<TData>({ table, headPosition, enableRowSelection, enableSor
|
|||
<th
|
||||
key={header.id}
|
||||
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={{
|
||||
textAlign: header.getSize() > 100 ? 'left' : 'center',
|
||||
textAlign: 'center',
|
||||
width: header.getSize(),
|
||||
cursor: enableSorting && header.column.getCanSort() ? 'pointer' : 'auto'
|
||||
}}
|
||||
onClick={enableSorting ? header.column.getToggleSortingHandler() : undefined}
|
||||
>
|
||||
{!header.isPlaceholder ? (
|
||||
<div className='flex gap-1'>
|
||||
<span className='inline-flex gap-1'>
|
||||
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||
{enableSorting && header.column.getCanSort() ? <SortingIcon column={header.column} /> : null}
|
||||
</div>
|
||||
</span>
|
||||
) : null}
|
||||
</th>
|
||||
))}
|
||||
|
|
|
@ -21,7 +21,7 @@ function NavigationButton({ icon, title, onClick, text }: NavigationButtonProps)
|
|||
'mr-1 h-full', //
|
||||
'flex items-center gap-1',
|
||||
'clr-btn-nav',
|
||||
'small-caps whitespace-nowrap',
|
||||
'font-controls whitespace-nowrap',
|
||||
{
|
||||
'px-2': text,
|
||||
'px-4': !text
|
||||
|
@ -29,7 +29,7 @@ function NavigationButton({ icon, title, onClick, text }: NavigationButtonProps)
|
|||
)}
|
||||
>
|
||||
{icon ? <span>{icon}</span> : null}
|
||||
{text ? <span className='font-semibold'>{text}</span> : null}
|
||||
{text ? <span>{text}</span> : null}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -82,9 +82,9 @@ const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
|
|||
{ tag: tags.propertyName, color: colors.fgTeal }, // Radical
|
||||
{ tag: tags.keyword, color: colors.fgBlue }, // keywords
|
||||
{ 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.brace, color: colors.fgPurple, fontWeight: '700' } // braces (curly brackets)
|
||||
{ tag: tags.brace, color: colors.fgPurple, fontWeight: '600' } // braces (curly brackets)
|
||||
]
|
||||
}),
|
||||
[disabled, colors, darkMode]
|
||||
|
@ -131,6 +131,7 @@ const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
|
|||
<div className={clsx('flex flex-col gap-2', className, cursor)} style={style}>
|
||||
<Label text={label} htmlFor={id} />
|
||||
<CodeMirror
|
||||
className='font-math'
|
||||
id={id}
|
||||
ref={thisRef}
|
||||
basicSetup={editorSetup}
|
||||
|
|
|
@ -1,28 +1,25 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import ConceptTooltip from '@/components/Common/ConceptTooltip';
|
||||
import ConstituentaTooltip from '@/components/Help/ConstituentaTooltip';
|
||||
import { IConstituenta } from '@/models/rsform';
|
||||
import { isMockCst } from '@/models/rsformAPI';
|
||||
import { colorFgCstStatus, IColorTheme } from '@/utils/color';
|
||||
import { describeExpressionStatus } from '@/utils/labels';
|
||||
|
||||
interface ConstituentaBadgeProps {
|
||||
prefixID?: string;
|
||||
shortTooltip?: boolean;
|
||||
value: IConstituenta;
|
||||
theme: IColorTheme;
|
||||
}
|
||||
|
||||
function ConstituentaBadge({ value, prefixID, shortTooltip, theme }: ConstituentaBadgeProps) {
|
||||
function ConstituentaBadge({ value, prefixID, theme }: ConstituentaBadgeProps) {
|
||||
return (
|
||||
<div
|
||||
id={`${prefixID}${value.alias}`}
|
||||
className={clsx(
|
||||
'min-w-[3.1rem] max-w-[3.1rem]',
|
||||
'min-w-[3.1rem] max-w-[3.1rem]', //
|
||||
'px-1',
|
||||
'border rounded-md',
|
||||
'text-center font-semibold whitespace-nowrap'
|
||||
'text-center font-medium whitespace-nowrap'
|
||||
)}
|
||||
style={{
|
||||
borderColor: colorFgCstStatus(value.status, theme),
|
||||
|
@ -31,14 +28,7 @@ function ConstituentaBadge({ value, prefixID, shortTooltip, theme }: Constituent
|
|||
}}
|
||||
>
|
||||
{value.alias}
|
||||
{!shortTooltip ? <ConstituentaTooltip anchor={`#${prefixID}${value.alias}`} data={value} /> : null}
|
||||
{shortTooltip ? (
|
||||
<ConceptTooltip anchorSelect={`#${prefixID}${value.alias}`} place='right'>
|
||||
<p>
|
||||
<b>Статус</b>: {describeExpressionStatus(value.status)}
|
||||
</p>
|
||||
</ConceptTooltip>
|
||||
) : null}
|
||||
<ConstituentaTooltip anchor={`#${prefixID}${value.alias}`} data={value} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -16,10 +16,10 @@ function GrammemeBadge({ key, grammeme }: GrammemeBadgeProps) {
|
|||
<div
|
||||
key={key}
|
||||
className={clsx(
|
||||
'min-w-[3rem]',
|
||||
'min-w-[3rem]', //
|
||||
'px-1',
|
||||
'border rounded-md',
|
||||
'text-sm font-semibold text-center whitespace-nowrap'
|
||||
'text-sm font-medium text-center whitespace-nowrap'
|
||||
)}
|
||||
style={{
|
||||
borderColor: colorFgGrammeme(grammeme, colors),
|
||||
|
|
|
@ -20,13 +20,7 @@ function InfoCstClass({ header }: InfoCstClassProps) {
|
|||
return (
|
||||
<p key={`${prefixes.cst_status_list}${index}`}>
|
||||
<span
|
||||
className={clsx(
|
||||
'inline-block',
|
||||
'min-w-[7rem]',
|
||||
'px-1',
|
||||
'border',
|
||||
'text-center text-sm small-caps font-semibold'
|
||||
)}
|
||||
className={clsx('inline-block', 'min-w-[7rem]', 'px-1', 'border', 'text-center text-sm font-controls')}
|
||||
style={{ backgroundColor: colorBgCstClass(cstClass, colors) }}
|
||||
>
|
||||
{labelCstClass(cstClass)}
|
||||
|
|
|
@ -22,11 +22,11 @@ function InfoCstStatus({ title }: InfoCstStatusProps) {
|
|||
<p key={`${prefixes.cst_status_list}${index}`}>
|
||||
<span
|
||||
className={clsx(
|
||||
'inline-block',
|
||||
'inline-block', //
|
||||
'min-w-[7rem]',
|
||||
'px-1',
|
||||
'border',
|
||||
'text-center text-sm small-caps font-semibold'
|
||||
'text-center text-sm font-controls'
|
||||
)}
|
||||
style={{ backgroundColor: colorBgCstStatus(status, colors) }}
|
||||
>
|
||||
|
|
|
@ -3,6 +3,8 @@ import { useIntl } from 'react-intl';
|
|||
import { useUsers } from '@/context/UsersContext';
|
||||
import { ILibraryItemEx } from '@/models/library';
|
||||
|
||||
import LabeledValue from '../Common/LabeledValue';
|
||||
|
||||
interface InfoLibraryItemProps {
|
||||
item?: ILibraryItemEx;
|
||||
}
|
||||
|
@ -12,26 +14,13 @@ function InfoLibraryItem({ item }: InfoLibraryItemProps) {
|
|||
const intl = useIntl();
|
||||
return (
|
||||
<div className='flex flex-col gap-1'>
|
||||
<div className='flex'>
|
||||
<label className='font-semibold'>Владелец:</label>
|
||||
<span className='min-w-[200px] ml-2 overflow-ellipsis overflow-hidden whitespace-nowrap'>
|
||||
{getUserLabel(item?.owner ?? null)}
|
||||
</span>
|
||||
</div>
|
||||
<div className='flex'>
|
||||
<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>
|
||||
<LabeledValue label='Владелец' text={getUserLabel(item?.owner ?? null)} />
|
||||
<LabeledValue label='Отслеживают' text={item?.subscribers.length ?? 0} />
|
||||
<LabeledValue
|
||||
label='Дата обновления'
|
||||
text={item ? new Date(item?.time_update).toLocaleString(intl.locale) : ''}
|
||||
/>
|
||||
<LabeledValue label='Дата создания' text={item ? new Date(item?.time_create).toLocaleString(intl.locale) : ''} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ function SelectedCounter({ total, selected, hideZero, position = 'top-0 left-0'
|
|||
return null;
|
||||
}
|
||||
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}
|
||||
</Overlay>
|
||||
);
|
||||
|
|
|
@ -26,7 +26,7 @@ function WordformButton({ text, example, grams, onSelectGrams, isSelected, ...re
|
|||
)}
|
||||
{...restProps}
|
||||
>
|
||||
<p className='font-semibold'>{text}</p>
|
||||
<p className='font-medium'>{text}</p>
|
||||
<p>{example}</p>
|
||||
</button>
|
||||
);
|
||||
|
|
|
@ -6,49 +6,53 @@
|
|||
|
||||
/* prettier-ignore */
|
||||
: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 */
|
||||
--cl-bg-120: hsl(000, 000%, 100%);
|
||||
--cl-bg-100: hsl(000, 000%, 098%);
|
||||
--cl-bg-80: hsl(000, 000%, 094%);
|
||||
--cl-bg-60: hsl(000, 000%, 091%);
|
||||
--cl-bg-40: hsl(000, 000%, 080%);
|
||||
--cl-bg-120: hsl(000, 000%, 100%);
|
||||
--cl-bg-100: hsl(000, 000%, 098%);
|
||||
--cl-bg-80: hsl(000, 000%, 094%);
|
||||
--cl-bg-60: hsl(000, 000%, 091%);
|
||||
--cl-bg-40: hsl(000, 000%, 080%);
|
||||
|
||||
--cl-fg-60: hsl(000, 000%, 065%);
|
||||
--cl-fg-80: hsl(000, 000%, 047%);
|
||||
--cl-fg-100: hsl(000, 000%, 000%);
|
||||
--cl-fg-60: hsl(000, 000%, 065%);
|
||||
--cl-fg-80: hsl(000, 000%, 047%);
|
||||
--cl-fg-100: hsl(000, 000%, 000%);
|
||||
|
||||
--cl-prim-bg-100: hsl(220, 100%, 060%);
|
||||
--cl-prim-bg-80: hsl(220, 080%, 092%);
|
||||
--cl-prim-bg-60: hsl(190, 080%, 094%);
|
||||
|
||||
--cl-prim-fg-80: hsl(220, 100%, 050%);
|
||||
--cl-prim-fg-100: hsl(000, 000%, 100%);
|
||||
--cl-prim-bg-100: hsl(220, 100%, 060%);
|
||||
--cl-prim-bg-80: hsl(220, 080%, 092%);
|
||||
--cl-prim-bg-60: hsl(190, 080%, 094%);
|
||||
|
||||
--cl-red-bg-100: hsl(000, 100%, 095%);
|
||||
--cl-red-fg-100: hsl(000, 072%, 051%);
|
||||
--cl-green-fg-100: hsl(120, 080%, 37%);
|
||||
--cl-prim-fg-80: hsl(220, 100%, 050%);
|
||||
--cl-prim-fg-100: hsl(000, 000%, 100%);
|
||||
|
||||
--cl-red-bg-100: hsl(000, 100%, 095%);
|
||||
--cl-red-fg-100: hsl(000, 072%, 051%);
|
||||
--cl-green-fg-100: hsl(120, 080%, 37%);
|
||||
|
||||
/* Dark Theme */
|
||||
--cd-bg-120: hsl(000, 000%, 005%);
|
||||
--cd-bg-100: hsl(000, 000%, 009%);
|
||||
--cd-bg-80: hsl(000, 000%, 015%);
|
||||
--cd-bg-60: hsl(000, 000%, 022%);
|
||||
--cd-bg-40: hsl(000, 000%, 035%);
|
||||
|
||||
--cd-fg-60: hsl(000, 000%, 055%);
|
||||
--cd-fg-80: hsl(000, 000%, 080%);
|
||||
--cd-fg-100: hsl(000, 000%, 093%);
|
||||
--cd-bg-120: hsl(000, 000%, 005%);
|
||||
--cd-bg-100: hsl(000, 000%, 009%);
|
||||
--cd-bg-80: hsl(000, 000%, 015%);
|
||||
--cd-bg-60: hsl(000, 000%, 022%);
|
||||
--cd-bg-40: hsl(000, 000%, 035%);
|
||||
|
||||
--cd-prim-bg-100: hsl(267, 050%, 050%);
|
||||
--cd-prim-bg-80: hsl(267, 050%, 032%);
|
||||
--cd-prim-bg-60: hsl(269, 030%, 028%);
|
||||
--cd-fg-60: hsl(000, 000%, 055%);
|
||||
--cd-fg-80: hsl(000, 000%, 080%);
|
||||
--cd-fg-100: hsl(000, 000%, 093%);
|
||||
|
||||
--cd-prim-fg-80: hsl(267, 070%, 070%);
|
||||
--cd-prim-fg-100: hsl(000, 000%, 100%);
|
||||
--cd-prim-bg-100: hsl(267, 050%, 050%);
|
||||
--cd-prim-bg-80: hsl(267, 050%, 032%);
|
||||
--cd-prim-bg-60: hsl(269, 030%, 028%);
|
||||
|
||||
--cd-red-bg-100: hsl(000, 100%, 015%);
|
||||
--cd-red-fg-100: hsl(000, 080%, 055%);
|
||||
--cd-green-fg-100: hsl(120, 080%, 042%);
|
||||
--cd-prim-fg-80: hsl(267, 070%, 070%);
|
||||
--cd-prim-fg-100: hsl(000, 000%, 100%);
|
||||
|
||||
--cd-red-bg-100: hsl(000, 100%, 015%);
|
||||
--cd-red-fg-100: hsl(000, 080%, 055%);
|
||||
--cd-green-fg-100: hsl(120, 080%, 042%);
|
||||
|
||||
/* Import overrides */
|
||||
--toastify-color-dark: var(--cd-bg-60);
|
||||
|
@ -87,6 +91,8 @@
|
|||
}
|
||||
|
||||
:root {
|
||||
font-family: var(--font-main);
|
||||
|
||||
color: var(--cl-fg-100);
|
||||
border-color: var(--cl-bg-40);
|
||||
background-color: var(--cl-bg-100);
|
||||
|
@ -136,9 +142,14 @@
|
|||
}
|
||||
|
||||
@layer utilities {
|
||||
.small-caps {
|
||||
.font-controls {
|
||||
font-family: var(--font-ui);
|
||||
font-weight: 600;
|
||||
font-variant: small-caps;
|
||||
}
|
||||
.font-math {
|
||||
font-family: var(--font-math);
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
|
|
|
@ -44,8 +44,23 @@ function SearchPanel({ total, filtered, query, setQuery, strategy, setFilter }:
|
|||
);
|
||||
|
||||
return (
|
||||
<div 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')}>
|
||||
<div
|
||||
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'>
|
||||
{filtered} из {total}
|
||||
|
|
|
@ -12,14 +12,7 @@ interface TopicsListProps {
|
|||
function TopicsList({ activeTopic, onChangeTopic }: TopicsListProps) {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'sticky top-0 left-0',
|
||||
'min-w-[13rem] self-start',
|
||||
'border-x',
|
||||
'clr-controls',
|
||||
'small-caps',
|
||||
'select-none'
|
||||
)}
|
||||
className={clsx('sticky top-0 left-0', 'min-w-[13rem] self-start', 'border-x', 'clr-controls', '', 'select-none')}
|
||||
>
|
||||
<h1 className='my-1'>Справка</h1>
|
||||
{Object.values(HelpTopic).map((topic, index) => (
|
||||
|
|
|
@ -123,7 +123,7 @@ function FormConstituenta({
|
|||
|
||||
return (
|
||||
<>
|
||||
<Overlay position='top-1 left-[4rem]' className='flex select-none'>
|
||||
<Overlay position='top-1 left-[4.1rem]' className='flex select-none'>
|
||||
<MiniButton
|
||||
title={`Редактировать словоформы термина: ${constituenta?.term_forms.length ?? 0}`}
|
||||
disabled={disabled}
|
||||
|
@ -131,7 +131,7 @@ function FormConstituenta({
|
|||
onClick={onEditTerm}
|
||||
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 className='ml-1'>{constituenta?.alias ?? ''}</span>
|
||||
</div>
|
||||
|
@ -205,7 +205,7 @@ function FormConstituenta({
|
|||
text='Сохранить изменения'
|
||||
className='self-center'
|
||||
disabled={!isModified || disabled}
|
||||
icon={<FiSave size='1.5rem' />}
|
||||
icon={<FiSave size='1.25rem' />}
|
||||
/>
|
||||
</form>
|
||||
</>
|
||||
|
|
|
@ -22,7 +22,8 @@ function RSLocalButton({ text, title, disabled, onInsert }: RSLocalButtonProps)
|
|||
'w-[2rem] h-6',
|
||||
'cursor-pointer disabled:cursor-default',
|
||||
'rounded-none',
|
||||
'clr-hover clr-btn-clear'
|
||||
'clr-hover clr-btn-clear',
|
||||
'font-math'
|
||||
)}
|
||||
onClick={() => onInsert(TokenID.ID_LOCAL, text)}
|
||||
>
|
||||
|
|
|
@ -23,6 +23,7 @@ function RSTokenButton({ token, disabled, onInsert }: RSTokenButtonProps) {
|
|||
'px-1',
|
||||
'outline-none',
|
||||
'clr-hover clr-btn-clear',
|
||||
'font-math',
|
||||
'cursor-pointer disabled:cursor-default',
|
||||
{
|
||||
'w-[4.5rem]': label.length > 3,
|
||||
|
|
|
@ -56,7 +56,7 @@ function StatusBar({ isModified, processing, constituenta, parseData, onAnalyze
|
|||
) : (
|
||||
<>
|
||||
<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>
|
||||
|
|
|
@ -16,44 +16,51 @@ import { IRSFormCreateData } from '@/models/rsform';
|
|||
import { classnames, limits, patterns } from '@/utils/constants';
|
||||
|
||||
interface FormRSFormProps {
|
||||
id?: string
|
||||
disabled: boolean
|
||||
isModified: boolean
|
||||
setIsModified: Dispatch<SetStateAction<boolean>>
|
||||
id?: string;
|
||||
disabled: boolean;
|
||||
isModified: boolean;
|
||||
setIsModified: Dispatch<SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
function FormRSForm({
|
||||
id, disabled, isModified, setIsModified,
|
||||
}: FormRSFormProps) {
|
||||
function FormRSForm({ id, disabled, isModified, setIsModified }: FormRSFormProps) {
|
||||
const { schema, update, processing } = useRSForm();
|
||||
const { user } = useAuth();
|
||||
|
||||
|
||||
const [title, setTitle] = useState('');
|
||||
const [alias, setAlias] = useState('');
|
||||
const [comment, setComment] = useState('');
|
||||
const [common, setCommon] = useState(false);
|
||||
const [canonical, setCanonical] = useState(false);
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
useEffect(() => {
|
||||
if (!schema) {
|
||||
setIsModified(false);
|
||||
return;
|
||||
}
|
||||
setIsModified(
|
||||
schema.title !== title ||
|
||||
schema.alias !== alias ||
|
||||
schema.comment !== comment ||
|
||||
schema.is_common !== common ||
|
||||
schema.is_canonical !== canonical
|
||||
schema.alias !== alias ||
|
||||
schema.comment !== comment ||
|
||||
schema.is_common !== common ||
|
||||
schema.is_canonical !== canonical
|
||||
);
|
||||
return () => setIsModified(false);
|
||||
}, [schema, schema?.title, schema?.alias, schema?.comment,
|
||||
schema?.is_common, schema?.is_canonical,
|
||||
title, alias, comment, common, canonical, setIsModified]);
|
||||
}, [
|
||||
schema,
|
||||
schema?.title,
|
||||
schema?.alias,
|
||||
schema?.comment,
|
||||
schema?.is_common,
|
||||
schema?.is_canonical,
|
||||
title,
|
||||
alias,
|
||||
comment,
|
||||
common,
|
||||
canonical,
|
||||
setIsModified
|
||||
]);
|
||||
|
||||
useLayoutEffect(
|
||||
() => {
|
||||
useLayoutEffect(() => {
|
||||
if (schema) {
|
||||
setTitle(schema.title);
|
||||
setAlias(schema.alias);
|
||||
|
@ -79,59 +86,55 @@ function FormRSForm({
|
|||
};
|
||||
|
||||
return (
|
||||
<form id={id}
|
||||
className={clsx(
|
||||
'mt-1 min-w-[22rem] w-[30rem]',
|
||||
'py-1',
|
||||
classnames.flex_col
|
||||
)}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<TextInput required
|
||||
label='Полное название'
|
||||
value={title}
|
||||
disabled={disabled}
|
||||
onChange={event => setTitle(event.target.value)}
|
||||
/>
|
||||
<TextInput required
|
||||
label='Сокращение'
|
||||
className='w-[14rem]'
|
||||
pattern={patterns.alias}
|
||||
title={`не более ${limits.alias_len} символов`}
|
||||
disabled={disabled}
|
||||
value={alias}
|
||||
onChange={event => setAlias(event.target.value)}
|
||||
/>
|
||||
<TextArea
|
||||
label='Комментарий'
|
||||
value={comment}
|
||||
disabled={disabled}
|
||||
onChange={event => setComment(event.target.value)}
|
||||
/>
|
||||
<div className='flex justify-between whitespace-nowrap'>
|
||||
<Checkbox
|
||||
label='Общедоступная схема'
|
||||
title='Общедоступные схемы видны всем пользователям и могут быть изменены'
|
||||
<form id={id} className={clsx('mt-1 min-w-[22rem] w-[30rem]', 'py-1', classnames.flex_col)} onSubmit={handleSubmit}>
|
||||
<TextInput
|
||||
required
|
||||
label='Полное название'
|
||||
value={title}
|
||||
disabled={disabled}
|
||||
value={common}
|
||||
setValue={value => setCommon(value)}
|
||||
onChange={event => setTitle(event.target.value)}
|
||||
/>
|
||||
<Checkbox
|
||||
label='Неизменная схема'
|
||||
title='Только администраторы могут присваивать схемам неизменный статус'
|
||||
disabled={disabled || !user?.is_staff}
|
||||
value={canonical}
|
||||
setValue={value => setCanonical(value)}
|
||||
<TextInput
|
||||
required
|
||||
label='Сокращение'
|
||||
className='w-[14rem]'
|
||||
pattern={patterns.alias}
|
||||
title={`не более ${limits.alias_len} символов`}
|
||||
disabled={disabled}
|
||||
value={alias}
|
||||
onChange={event => setAlias(event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<SubmitButton
|
||||
text='Сохранить изменения'
|
||||
className='self-center'
|
||||
loading={processing}
|
||||
disabled={!isModified || disabled}
|
||||
icon={<FiSave size='1.5rem' />}
|
||||
/>
|
||||
</form>);
|
||||
<TextArea
|
||||
label='Комментарий'
|
||||
value={comment}
|
||||
disabled={disabled}
|
||||
onChange={event => setComment(event.target.value)}
|
||||
/>
|
||||
<div className='flex justify-between whitespace-nowrap'>
|
||||
<Checkbox
|
||||
label='Общедоступная схема'
|
||||
title='Общедоступные схемы видны всем пользователям и могут быть изменены'
|
||||
disabled={disabled}
|
||||
value={common}
|
||||
setValue={value => setCommon(value)}
|
||||
/>
|
||||
<Checkbox
|
||||
label='Неизменная схема'
|
||||
title='Только администраторы могут присваивать схемам неизменный статус'
|
||||
disabled={disabled || !user?.is_staff}
|
||||
value={canonical}
|
||||
setValue={value => setCanonical(value)}
|
||||
/>
|
||||
</div>
|
||||
<SubmitButton
|
||||
text='Сохранить изменения'
|
||||
className='self-center'
|
||||
loading={processing}
|
||||
disabled={!isModified || disabled}
|
||||
icon={<FiSave size='1.25rem' />}
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export default FormRSForm;
|
||||
export default FormRSForm;
|
||||
|
|
|
@ -204,7 +204,7 @@ function EditorRSList({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: Edito
|
|||
onCreate={handleCreateCst}
|
||||
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' />
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user