mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Refactor UI classes and improve editing UX
This commit is contained in:
parent
1009a2ec98
commit
6965e83e19
|
@ -33,7 +33,7 @@ function Root() {
|
||||||
<Navigation />
|
<Navigation />
|
||||||
|
|
||||||
<div id={globalIDs.main_scroll}
|
<div id={globalIDs.main_scroll}
|
||||||
className='overflow-x-auto overscroll-none'
|
className='overscroll-none min-w-fit overflow-y-auto'
|
||||||
style={{
|
style={{
|
||||||
maxHeight: viewportHeight,
|
maxHeight: viewportHeight,
|
||||||
overflowY: showScroll ? 'scroll': 'auto'
|
overflowY: showScroll ? 'scroll': 'auto'
|
||||||
|
@ -45,8 +45,7 @@ function Root() {
|
||||||
>
|
>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</main>
|
</main>
|
||||||
|
<Footer />
|
||||||
<Footer />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</NavigationState>);
|
</NavigationState>);
|
||||||
|
|
|
@ -3,7 +3,7 @@ import clsx from 'clsx';
|
||||||
import { IColorsProps, IControlProps } from './commonInterfaces';
|
import { IColorsProps, IControlProps } from './commonInterfaces';
|
||||||
|
|
||||||
interface ButtonProps
|
interface ButtonProps
|
||||||
extends IControlProps, IColorsProps, Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'className' | 'children' | 'title'| 'type'> {
|
extends IControlProps, IColorsProps, Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children' | 'title'| 'type'> {
|
||||||
text?: string
|
text?: string
|
||||||
icon?: React.ReactNode
|
icon?: React.ReactNode
|
||||||
|
|
||||||
|
@ -12,11 +12,10 @@ extends IControlProps, IColorsProps, Omit<React.ButtonHTMLAttributes<HTMLButtonE
|
||||||
}
|
}
|
||||||
|
|
||||||
function Button({
|
function Button({
|
||||||
text, icon, tooltip,
|
text, icon, tooltip, loading,
|
||||||
dense, disabled, noBorder, noOutline,
|
dense, disabled, noBorder, noOutline,
|
||||||
colors = 'clr-btn-default',
|
colors = 'clr-btn-default',
|
||||||
dimensions = 'w-fit h-fit',
|
className,
|
||||||
loading,
|
|
||||||
...restProps
|
...restProps
|
||||||
}: ButtonProps) {
|
}: ButtonProps) {
|
||||||
return (
|
return (
|
||||||
|
@ -36,7 +35,7 @@ function Button({
|
||||||
'clr-outline': !noOutline,
|
'clr-outline': !noOutline,
|
||||||
},
|
},
|
||||||
colors,
|
colors,
|
||||||
dimensions
|
className
|
||||||
)}
|
)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
|
|
|
@ -5,10 +5,9 @@ import { CheckboxCheckedIcon } from '../Icons';
|
||||||
import Label from './Label';
|
import Label from './Label';
|
||||||
|
|
||||||
export interface CheckboxProps
|
export interface CheckboxProps
|
||||||
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'className' | 'children' | 'title' | 'value' | 'onClick' > {
|
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children' | 'title' | 'value' | 'onClick' > {
|
||||||
label?: string
|
label?: string
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
dimensions?: string
|
|
||||||
tooltip?: string
|
tooltip?: string
|
||||||
|
|
||||||
value: boolean
|
value: boolean
|
||||||
|
@ -17,7 +16,7 @@ extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'className' | 'child
|
||||||
|
|
||||||
function Checkbox({
|
function Checkbox({
|
||||||
id, disabled, tooltip, label,
|
id, disabled, tooltip, label,
|
||||||
dimensions = 'w-fit', value, setValue, ...restProps
|
className, value, setValue, ...restProps
|
||||||
}: CheckboxProps) {
|
}: CheckboxProps) {
|
||||||
const cursor = useMemo(
|
const cursor = useMemo(
|
||||||
() => {
|
() => {
|
||||||
|
@ -44,8 +43,8 @@ function Checkbox({
|
||||||
'flex items-center gap-2',
|
'flex items-center gap-2',
|
||||||
'outline-none',
|
'outline-none',
|
||||||
'text-start',
|
'text-start',
|
||||||
dimensions,
|
cursor,
|
||||||
cursor
|
className
|
||||||
)}
|
)}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
|
|
@ -11,7 +11,7 @@ interface ConceptLoaderProps {
|
||||||
export function ConceptLoader({size=10}: ConceptLoaderProps) {
|
export function ConceptLoader({size=10}: ConceptLoaderProps) {
|
||||||
const {colors} = useConceptTheme();
|
const {colors} = useConceptTheme();
|
||||||
return (
|
return (
|
||||||
<div className='flex justify-center w-full h-full'>
|
<div className='flex justify-center'>
|
||||||
<ThreeDots
|
<ThreeDots
|
||||||
color={colors.bgSelected}
|
color={colors.bgSelected}
|
||||||
height={size*10}
|
height={size*10}
|
||||||
|
|
|
@ -21,7 +21,7 @@ function ConceptSearch({ value, onChange, noBorder, dimensions }: ConceptSearchP
|
||||||
</Overlay>
|
</Overlay>
|
||||||
<TextInput noOutline
|
<TextInput noOutline
|
||||||
placeholder='Поиск'
|
placeholder='Поиск'
|
||||||
dimensions='w-full pl-10'
|
className='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)}
|
||||||
|
|
|
@ -21,8 +21,7 @@ function DropdownCheckbox({ tooltip, setValue, disabled, ...restProps }: Dropdow
|
||||||
!!setValue && !disabled && 'clr-hover'
|
!!setValue && !disabled && 'clr-hover'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
dimensions='w-full'
|
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
setValue={setValue}
|
setValue={setValue}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
|
|
|
@ -71,7 +71,6 @@ function Modal({
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'w-full h-fit',
|
|
||||||
'overflow-auto',
|
'overflow-auto',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
|
@ -84,7 +83,6 @@ function Modal({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={clsx(
|
<div className={clsx(
|
||||||
'w-full min-w-fit',
|
|
||||||
'z-modal-controls',
|
'z-modal-controls',
|
||||||
'px-6 py-3 flex gap-12 justify-center'
|
'px-6 py-3 flex gap-12 justify-center'
|
||||||
)}>
|
)}>
|
||||||
|
@ -92,14 +90,14 @@ function Modal({
|
||||||
<Button autoFocus
|
<Button autoFocus
|
||||||
text={submitText}
|
text={submitText}
|
||||||
tooltip={!canSubmit ? submitInvalidTooltip: ''}
|
tooltip={!canSubmit ? submitInvalidTooltip: ''}
|
||||||
dimensions='min-w-[8rem] min-h-[2.6rem]'
|
className='min-w-[8rem] min-h-[2.6rem]'
|
||||||
colors='clr-btn-primary'
|
colors='clr-btn-primary'
|
||||||
disabled={!canSubmit}
|
disabled={!canSubmit}
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
/> : null}
|
/> : null}
|
||||||
<Button
|
<Button
|
||||||
text={readonly ? 'Закрыть' : 'Отмена'}
|
text={readonly ? 'Закрыть' : 'Отмена'}
|
||||||
dimensions='min-w-[8rem] min-h-[2.6rem]'
|
className='min-w-[8rem] min-h-[2.6rem]'
|
||||||
onClick={handleCancel}
|
onClick={handleCancel}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,12 +6,13 @@ extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children' | 'title'
|
||||||
tooltip?: string
|
tooltip?: string
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
icon?: React.ReactNode
|
icon?: React.ReactNode
|
||||||
dimensions?: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function SubmitButton({
|
function SubmitButton({
|
||||||
text = 'ОК', icon, disabled, tooltip, loading, className,
|
text = 'ОК',
|
||||||
dimensions = 'w-fit h-fit', ...restProps
|
icon, disabled, tooltip, loading,
|
||||||
|
className,
|
||||||
|
...restProps
|
||||||
}: SubmitButtonProps) {
|
}: SubmitButtonProps) {
|
||||||
return (
|
return (
|
||||||
<button type='submit'
|
<button type='submit'
|
||||||
|
@ -23,7 +24,6 @@ function SubmitButton({
|
||||||
'clr-btn-primary',
|
'clr-btn-primary',
|
||||||
'select-none disabled:cursor-not-allowed',
|
'select-none disabled:cursor-not-allowed',
|
||||||
loading && 'cursor-progress',
|
loading && 'cursor-progress',
|
||||||
dimensions,
|
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
disabled={disabled ?? loading}
|
disabled={disabled ?? loading}
|
||||||
|
|
|
@ -5,24 +5,24 @@ import { IColorsProps, IEditorProps } from './commonInterfaces';
|
||||||
import Label from './Label';
|
import Label from './Label';
|
||||||
|
|
||||||
export interface TextAreaProps
|
export interface TextAreaProps
|
||||||
extends IEditorProps, IColorsProps, Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'className' | 'title'> {
|
extends IEditorProps, IColorsProps, Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'title'> {
|
||||||
dense?: boolean
|
dense?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
function TextArea({
|
function TextArea({
|
||||||
id, label, required, tooltip, rows,
|
id, label, required, tooltip, rows,
|
||||||
dense, noBorder, noOutline,
|
dense, noBorder, noOutline,
|
||||||
dimensions = 'w-full',
|
className,
|
||||||
colors = 'clr-input',
|
colors = 'clr-input',
|
||||||
...restProps
|
...restProps
|
||||||
}: TextAreaProps) {
|
}: TextAreaProps) {
|
||||||
return (
|
return (
|
||||||
<div className={clsx(
|
<div className={clsx(
|
||||||
{
|
{
|
||||||
'flex items-center gap-3': dense,
|
'flex flex-col gap-2': !dense,
|
||||||
'flex flex-col items-start gap-2': !dense
|
'flex justify-stretch items-center gap-3': dense
|
||||||
},
|
},
|
||||||
dense && dimensions,
|
dense && className,
|
||||||
)}>
|
)}>
|
||||||
<Label text={label} htmlFor={id} />
|
<Label text={label} htmlFor={id} />
|
||||||
<textarea id={id}
|
<textarea id={id}
|
||||||
|
@ -31,12 +31,12 @@ function TextArea({
|
||||||
'px-3 py-2',
|
'px-3 py-2',
|
||||||
'leading-tight',
|
'leading-tight',
|
||||||
{
|
{
|
||||||
'w-full': dense,
|
|
||||||
'border': !noBorder,
|
'border': !noBorder,
|
||||||
|
'flex-grow': dense,
|
||||||
'clr-outline': !noOutline
|
'clr-outline': !noOutline
|
||||||
},
|
},
|
||||||
colors,
|
colors,
|
||||||
!dense && dimensions
|
!dense && className
|
||||||
)}
|
)}
|
||||||
rows={rows}
|
rows={rows}
|
||||||
required={required}
|
required={required}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { IColorsProps, IEditorProps } from './commonInterfaces';
|
||||||
import Label from './Label';
|
import Label from './Label';
|
||||||
|
|
||||||
interface TextInputProps
|
interface TextInputProps
|
||||||
extends IEditorProps, IColorsProps, Omit<React.InputHTMLAttributes<HTMLInputElement>, 'className' | 'title'> {
|
extends IEditorProps, IColorsProps, Omit<React.InputHTMLAttributes<HTMLInputElement>, 'title'> {
|
||||||
dense?: boolean
|
dense?: boolean
|
||||||
allowEnter?: boolean
|
allowEnter?: boolean
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ function preventEnterCapture(event: React.KeyboardEvent<HTMLInputElement>) {
|
||||||
|
|
||||||
function TextInput({
|
function TextInput({
|
||||||
id, label, dense, tooltip, noBorder, noOutline, allowEnter, disabled,
|
id, label, dense, tooltip, noBorder, noOutline, allowEnter, disabled,
|
||||||
dimensions = 'w-full',
|
className,
|
||||||
colors = 'clr-input',
|
colors = 'clr-input',
|
||||||
onKeyDown,
|
onKeyDown,
|
||||||
...restProps
|
...restProps
|
||||||
|
@ -25,10 +25,10 @@ function TextInput({
|
||||||
return (
|
return (
|
||||||
<div className={clsx(
|
<div className={clsx(
|
||||||
{
|
{
|
||||||
'flex flex-col items-start gap-2': !dense,
|
'flex flex-col gap-2': !dense,
|
||||||
'flex items-center gap-3': dense,
|
'flex justify-stretch items-center gap-3': dense,
|
||||||
},
|
},
|
||||||
dense && dimensions
|
dense && className
|
||||||
)}>
|
)}>
|
||||||
<Label text={label} htmlFor={id} />
|
<Label text={label} htmlFor={id} />
|
||||||
<input id={id}
|
<input id={id}
|
||||||
|
@ -38,12 +38,12 @@ function TextInput({
|
||||||
'leading-tight truncate hover:text-clip',
|
'leading-tight truncate hover:text-clip',
|
||||||
{
|
{
|
||||||
'px-3': !noBorder || !disabled,
|
'px-3': !noBorder || !disabled,
|
||||||
'w-full': dense,
|
'flex-grow': dense,
|
||||||
'border': !noBorder,
|
'border': !noBorder,
|
||||||
'clr-outline': !noOutline
|
'clr-outline': !noOutline
|
||||||
},
|
},
|
||||||
colors,
|
colors,
|
||||||
!dense && dimensions
|
!dense && className
|
||||||
)}
|
)}
|
||||||
onKeyDown={!allowEnter && !onKeyDown ? preventEnterCapture : onKeyDown}
|
onKeyDown={!allowEnter && !onKeyDown ? preventEnterCapture : onKeyDown}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
|
|
@ -14,7 +14,7 @@ extends Omit<CheckboxProps, 'value' | 'setValue'> {
|
||||||
|
|
||||||
function Tristate({
|
function Tristate({
|
||||||
id, disabled, tooltip, label,
|
id, disabled, tooltip, label,
|
||||||
dimensions = 'w-fit',
|
className,
|
||||||
value, setValue,
|
value, setValue,
|
||||||
...restProps
|
...restProps
|
||||||
}: TristateProps) {
|
}: TristateProps) {
|
||||||
|
@ -48,8 +48,8 @@ function Tristate({
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'flex items-center gap-2 text-start',
|
'flex items-center gap-2 text-start',
|
||||||
'outline-none',
|
'outline-none',
|
||||||
dimensions,
|
cursor,
|
||||||
cursor
|
className
|
||||||
)}
|
)}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
// =========== Module contains interfaces for common UI elements. ==========
|
// =========== Module contains interfaces for common UI elements. ==========
|
||||||
export interface IControlProps {
|
export interface IControlProps {
|
||||||
tooltip?: string
|
tooltip?: string
|
||||||
dimensions?: string
|
|
||||||
|
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
noBorder?: boolean
|
noBorder?: boolean
|
||||||
noOutline?: boolean
|
noOutline?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IStylingProps {
|
||||||
|
style?: React.CSSProperties
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface IEditorProps extends IControlProps {
|
export interface IEditorProps extends IControlProps {
|
||||||
label?: string
|
label?: string
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import { IStylingProps } from '../Common/commonInterfaces';
|
||||||
import DefaultNoData from './DefaultNoData';
|
import DefaultNoData from './DefaultNoData';
|
||||||
import PaginationTools from './PaginationTools';
|
import PaginationTools from './PaginationTools';
|
||||||
import TableBody from './TableBody';
|
import TableBody from './TableBody';
|
||||||
|
@ -24,13 +25,10 @@ export interface IConditionalStyle<TData> {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DataTableProps<TData extends RowData>
|
export interface DataTableProps<TData extends RowData>
|
||||||
extends Pick<TableOptions<TData>,
|
extends IStylingProps, Pick<TableOptions<TData>,
|
||||||
'data' | 'columns' |
|
'data' | 'columns' |
|
||||||
'onRowSelectionChange' | 'onColumnVisibilityChange'
|
'onRowSelectionChange' | 'onColumnVisibilityChange'
|
||||||
> {
|
> {
|
||||||
style?: React.CSSProperties
|
|
||||||
className?: string
|
|
||||||
|
|
||||||
dense?: boolean
|
dense?: boolean
|
||||||
headPosition?: string
|
headPosition?: string
|
||||||
noHeader?: boolean
|
noHeader?: boolean
|
||||||
|
|
|
@ -23,7 +23,7 @@ function Navigation () {
|
||||||
return (
|
return (
|
||||||
<nav className={clsx(
|
<nav className={clsx(
|
||||||
'z-navigation',
|
'z-navigation',
|
||||||
'sticky top-0 left-0 right-0',
|
'sticky top-0 left-0 right-0',
|
||||||
'clr-app',
|
'clr-app',
|
||||||
'select-none'
|
'select-none'
|
||||||
)}>
|
)}>
|
||||||
|
|
|
@ -55,10 +55,11 @@ extends Pick<ReactCodeMirrorProps,
|
||||||
noTooltip?: boolean
|
noTooltip?: boolean
|
||||||
innerref?: RefObject<ReactCodeMirrorRef> | undefined
|
innerref?: RefObject<ReactCodeMirrorRef> | undefined
|
||||||
onChange?: (newValue: string) => void
|
onChange?: (newValue: string) => void
|
||||||
|
onAnalyze?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function RSInput({
|
function RSInput({
|
||||||
id, label, innerref, onChange,
|
id, label, innerref, onChange, onAnalyze,
|
||||||
disabled, noTooltip,
|
disabled, noTooltip,
|
||||||
dimensions = 'w-full',
|
dimensions = 'w-full',
|
||||||
...restProps
|
...restProps
|
||||||
|
@ -120,8 +121,12 @@ function RSInput({
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
|
} else if (event.ctrlKey && event.code === 'KeyQ' && onAnalyze) {
|
||||||
|
onAnalyze();
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
}, [thisRef]);
|
}, [thisRef, onAnalyze]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={clsx(
|
<div className={clsx(
|
||||||
|
|
|
@ -16,7 +16,8 @@ Omit<SelectMultiProps<IGrammemeOption>, 'value' | 'onChange'> {
|
||||||
|
|
||||||
function SelectGrammeme({
|
function SelectGrammeme({
|
||||||
value, setValue,
|
value, setValue,
|
||||||
dimensions, className, placeholder
|
dimensions, className, placeholder,
|
||||||
|
...restProps
|
||||||
}: SelectGrammemeProps) {
|
}: SelectGrammemeProps) {
|
||||||
const [options, setOptions] = useState<IGrammemeOption[]>([]);
|
const [options, setOptions] = useState<IGrammemeOption[]>([]);
|
||||||
|
|
||||||
|
@ -37,6 +38,7 @@ function SelectGrammeme({
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={newValue => setValue([...newValue].sort(compareGrammemeOptions))}
|
onChange={newValue => setValue([...newValue].sort(compareGrammemeOptions))}
|
||||||
|
{...restProps}
|
||||||
/>);
|
/>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ function DlgCloneLibraryItem({ hideWindow, base }: DlgCloneLibraryItemProps) {
|
||||||
<TextInput
|
<TextInput
|
||||||
label='Сокращение'
|
label='Сокращение'
|
||||||
value={alias}
|
value={alias}
|
||||||
dimensions='max-w-sm'
|
className='max-w-sm'
|
||||||
onChange={event => setAlias(event.target.value)}
|
onChange={event => setAlias(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<TextArea
|
<TextArea
|
||||||
|
|
|
@ -18,46 +18,48 @@ interface ConstituentaTabProps {
|
||||||
function ConstituentaTab({state, partialUpdate}: ConstituentaTabProps) {
|
function ConstituentaTab({state, partialUpdate}: ConstituentaTabProps) {
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col gap-3'>
|
<div className='flex flex-col gap-3'>
|
||||||
<div className='flex justify-center w-full gap-3 pr-2'>
|
<div className='flex self-center gap-3 pr-2'>
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
className='min-w-[14rem] self-center'
|
className='min-w-[14rem]'
|
||||||
options={SelectorCstType}
|
options={SelectorCstType}
|
||||||
placeholder='Выберите тип'
|
placeholder='Выберите тип'
|
||||||
value={{ value: state.cst_type, label: labelCstType(state.cst_type) }}
|
value={{ value: state.cst_type, label: labelCstType(state.cst_type) }}
|
||||||
onChange={data => partialUpdate({ cst_type: data?.value ?? CstType.TERM})}
|
onChange={data => partialUpdate({ cst_type: data?.value ?? CstType.TERM})}
|
||||||
/>
|
/>
|
||||||
<TextInput id='alias' label='Имя'
|
<TextInput dense
|
||||||
dense
|
label='Имя'
|
||||||
dimensions='w-[7rem]'
|
className='w-[7rem]'
|
||||||
value={state.alias}
|
value={state.alias}
|
||||||
onChange={event => partialUpdate({ alias: event.target.value})}
|
onChange={event => partialUpdate({ alias: event.target.value})}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<TextArea id='term' label='Термин'
|
<TextArea spellCheck
|
||||||
|
label='Термин'
|
||||||
placeholder='Схемный или предметный термин, обозначающий данное понятие или утверждение'
|
placeholder='Схемный или предметный термин, обозначающий данное понятие или утверждение'
|
||||||
rows={2}
|
rows={2}
|
||||||
value={state.term_raw}
|
value={state.term_raw}
|
||||||
spellCheck
|
|
||||||
onChange={event => partialUpdate({ term_raw: event.target.value })}
|
onChange={event => partialUpdate({ term_raw: event.target.value })}
|
||||||
/>
|
/>
|
||||||
<RSInput id='expression' label='Формальное определение'
|
<RSInput
|
||||||
|
label='Формальное определение'
|
||||||
placeholder='Родоструктурное выражение, задающее формальное определение'
|
placeholder='Родоструктурное выражение, задающее формальное определение'
|
||||||
height='5.1rem'
|
height='5.1rem'
|
||||||
value={state.definition_formal}
|
value={state.definition_formal}
|
||||||
onChange={value => partialUpdate({definition_formal: value})}
|
onChange={value => partialUpdate({definition_formal: value})}
|
||||||
/>
|
/>
|
||||||
<TextArea id='definition' label='Текстовое определение'
|
<TextArea
|
||||||
|
label='Текстовое определение'
|
||||||
placeholder='Лингвистическая интерпретация формального выражения'
|
placeholder='Лингвистическая интерпретация формального выражения'
|
||||||
rows={2}
|
rows={2}
|
||||||
value={state.definition_raw}
|
value={state.definition_raw}
|
||||||
spellCheck
|
spellCheck
|
||||||
onChange={event => partialUpdate({ definition_raw: event.target.value })}
|
onChange={event => partialUpdate({ definition_raw: event.target.value })}
|
||||||
/>
|
/>
|
||||||
<TextArea id='convention' label='Конвенция / Комментарий'
|
<TextArea spellCheck
|
||||||
|
label='Конвенция / Комментарий'
|
||||||
placeholder='Договоренность об интерпретации или пояснение к схеме'
|
placeholder='Договоренность об интерпретации или пояснение к схеме'
|
||||||
rows={2}
|
rows={2}
|
||||||
value={state.convention}
|
value={state.convention}
|
||||||
spellCheck
|
|
||||||
onChange={event => partialUpdate({ convention: event.target.value })}
|
onChange={event => partialUpdate({ convention: event.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>);
|
</div>);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import clsx from 'clsx';
|
||||||
import { useLayoutEffect, useState } from 'react';
|
import { useLayoutEffect, useState } from 'react';
|
||||||
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
||||||
|
|
||||||
|
@ -118,18 +119,21 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
submitText='Создать'
|
submitText='Создать'
|
||||||
className='max-w-[43rem] min-w-[43rem] min-h-[36rem] px-6'
|
className='max-w-[43rem] min-w-[43rem] min-h-[36rem] px-6'
|
||||||
>
|
|
||||||
<Tabs defaultFocus forceRenderTabPanel
|
|
||||||
selectedTabClassName='clr-selected'
|
|
||||||
selectedIndex={activeTab}
|
|
||||||
onSelect={setActiveTab}
|
|
||||||
>
|
>
|
||||||
<Overlay position='top-0 right-[6rem]'>
|
<Overlay position='top-0 right-[6rem]'>
|
||||||
<HelpButton topic={HelpTopic.RSTEMPLATES} dimensions='max-w-[35rem]' />
|
<HelpButton topic={HelpTopic.RSTEMPLATES} dimensions='max-w-[35rem]' />
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
<Tabs forceRenderTabPanel
|
||||||
<TabList className='flex justify-center mb-3'>
|
selectedTabClassName='clr-selected'
|
||||||
<div className='flex border divide-x rounded-none w-fit'>
|
className='flex flex-col'
|
||||||
|
selectedIndex={activeTab}
|
||||||
|
onSelect={setActiveTab}
|
||||||
|
>
|
||||||
|
<TabList className={clsx(
|
||||||
|
'mb-3 self-center',
|
||||||
|
'flex',
|
||||||
|
'border divide-x rounded-none'
|
||||||
|
)}>
|
||||||
<ConceptTab
|
<ConceptTab
|
||||||
label='Шаблон'
|
label='Шаблон'
|
||||||
tooltip='Выбор шаблона выражения'
|
tooltip='Выбор шаблона выражения'
|
||||||
|
@ -145,33 +149,30 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
|
||||||
tooltip='Редактирование атрибутов конституенты'
|
tooltip='Редактирование атрибутов конституенты'
|
||||||
className='w-[8rem]'
|
className='w-[8rem]'
|
||||||
/>
|
/>
|
||||||
</div>
|
</TabList>
|
||||||
</TabList>
|
|
||||||
|
|
||||||
<div className='w-full'>
|
|
||||||
<TabPanel style={{ display: activeTab === TabID.TEMPLATE ? '': 'none' }}>
|
<TabPanel style={{ display: activeTab === TabID.TEMPLATE ? '': 'none' }}>
|
||||||
<TemplateTab
|
<TemplateTab
|
||||||
state={template}
|
state={template}
|
||||||
partialUpdate={updateTemplate}
|
partialUpdate={updateTemplate}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
<TabPanel style={{ display: activeTab === TabID.ARGUMENTS ? '': 'none' }}>
|
<TabPanel style={{ display: activeTab === TabID.ARGUMENTS ? '': 'none' }}>
|
||||||
<ArgumentsTab
|
<ArgumentsTab
|
||||||
schema={schema}
|
schema={schema}
|
||||||
state={substitutes}
|
state={substitutes}
|
||||||
partialUpdate={updateSubstitutes}
|
partialUpdate={updateSubstitutes}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
<TabPanel style={{ display: activeTab === TabID.CONSTITUENTA ? '': 'none' }}>
|
<TabPanel style={{ display: activeTab === TabID.CONSTITUENTA ? '': 'none' }}>
|
||||||
<ConstituentaTab
|
<ConstituentaTab
|
||||||
state={constituenta}
|
state={constituenta}
|
||||||
partialUpdate={updateConstituenta}
|
partialUpdate={updateConstituenta}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</div>
|
</Tabs>
|
||||||
</Tabs>
|
|
||||||
</Modal>);
|
</Modal>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,10 +89,10 @@ function TemplateTab({ state, partialUpdate }: TemplateTabProps) {
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col gap-3'>
|
<div className='flex flex-col gap-3'>
|
||||||
<div>
|
<div>
|
||||||
<div className='flex justify-between'>
|
<div className='flex justify-stretch'>
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
placeholder='Выберите категорию'
|
placeholder='Выберите категорию'
|
||||||
className='w-full border-none'
|
className='flex-grow border-none'
|
||||||
options={categorySelector}
|
options={categorySelector}
|
||||||
value={state.filterCategory && selectedSchema ? {
|
value={state.filterCategory && selectedSchema ? {
|
||||||
value: state.filterCategory.id,
|
value: state.filterCategory.id,
|
||||||
|
@ -103,7 +103,7 @@ function TemplateTab({ state, partialUpdate }: TemplateTabProps) {
|
||||||
/>
|
/>
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
placeholder='Выберите источник'
|
placeholder='Выберите источник'
|
||||||
className='min-w-[15rem]'
|
className='min-w-[12rem]'
|
||||||
options={templateSelector}
|
options={templateSelector}
|
||||||
value={state.templateID ? { value: state.templateID, label: templates.find(item => item.id == state.templateID)!.title }: null}
|
value={state.templateID ? { value: state.templateID, label: templates.find(item => item.id == state.templateID)!.title }: null}
|
||||||
onChange={data => partialUpdate({templateID: (data ? data.value : undefined)})}
|
onChange={data => partialUpdate({templateID: (data ? data.value : undefined)})}
|
||||||
|
|
|
@ -56,21 +56,21 @@ function DlgCreateCst({ hideWindow, initial, schema, onCreate }: DlgCreateCstPro
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
submitText='Создать'
|
submitText='Создать'
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'h-fit min-w-[35rem]',
|
'w-[35rem]',
|
||||||
'py-2 px-6 flex flex-col gap-3 justify-stretch'
|
'py-2 px-6 flex flex-col gap-3'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className='flex justify-center w-full gap-6'>
|
<div className='flex self-center gap-6'>
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
placeholder='Выберите тип'
|
placeholder='Выберите тип'
|
||||||
className='min-w-[15rem] self-center'
|
className='min-w-[15rem]'
|
||||||
options={SelectorCstType}
|
options={SelectorCstType}
|
||||||
value={{ value: cstData.cst_type, label: labelCstType(cstData.cst_type) }}
|
value={{ value: cstData.cst_type, label: labelCstType(cstData.cst_type) }}
|
||||||
onChange={data => updateCstData({ cst_type: data?.value ?? CstType.BASE})}
|
onChange={data => updateCstData({ cst_type: data?.value ?? CstType.BASE})}
|
||||||
/>
|
/>
|
||||||
<TextInput dense
|
<TextInput dense
|
||||||
label='Имя'
|
label='Имя'
|
||||||
dimensions='w-[7rem]'
|
className='w-[7rem]'
|
||||||
value={cstData.alias}
|
value={cstData.alias}
|
||||||
onChange={event => updateCstData({ alias: event.target.value})}
|
onChange={event => updateCstData({ alias: event.target.value})}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import clsx from 'clsx';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
||||||
|
|
||||||
|
@ -51,18 +52,22 @@ function DlgEditReference({ hideWindow, items, initial, onSave }: DlgEditReferen
|
||||||
canSubmit={isValid}
|
canSubmit={isValid}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
className='min-w-[40rem] max-w-[40rem] px-6 min-h-[34rem]'
|
className='min-w-[40rem] max-w-[40rem] px-6 min-h-[34rem]'
|
||||||
>
|
|
||||||
<Tabs defaultFocus
|
|
||||||
selectedTabClassName='clr-selected'
|
|
||||||
selectedIndex={activeTab}
|
|
||||||
onSelect={setActiveTab}
|
|
||||||
>
|
>
|
||||||
<Overlay position='top-0 right-[4rem]'>
|
<Overlay position='top-0 right-[4rem]'>
|
||||||
<HelpButton topic={HelpTopic.TERM_CONTROL} dimensions='max-w-[35rem]' offset={14} />
|
<HelpButton topic={HelpTopic.TERM_CONTROL} dimensions='max-w-[35rem]' offset={14} />
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
|
||||||
<TabList className='flex justify-center mb-3'>
|
<Tabs
|
||||||
<div className='flex border divide-x rounded-none w-fit'>
|
selectedTabClassName='clr-selected'
|
||||||
|
className='flex flex-col'
|
||||||
|
selectedIndex={activeTab}
|
||||||
|
onSelect={setActiveTab}
|
||||||
|
>
|
||||||
|
<TabList className={clsx(
|
||||||
|
'mb-3 self-center',
|
||||||
|
'flex',
|
||||||
|
'border divide-x rounded-none'
|
||||||
|
)}>
|
||||||
<ConceptTab
|
<ConceptTab
|
||||||
tooltip='Отсылка на термин в заданной словоформе'
|
tooltip='Отсылка на термин в заданной словоформе'
|
||||||
label={labelReferenceType(ReferenceType.ENTITY)}
|
label={labelReferenceType(ReferenceType.ENTITY)}
|
||||||
|
@ -73,26 +78,25 @@ function DlgEditReference({ hideWindow, items, initial, onSave }: DlgEditReferen
|
||||||
label={labelReferenceType(ReferenceType.SYNTACTIC)}
|
label={labelReferenceType(ReferenceType.SYNTACTIC)}
|
||||||
className='w-[12rem]'
|
className='w-[12rem]'
|
||||||
/>
|
/>
|
||||||
</div>
|
</TabList>
|
||||||
</TabList>
|
|
||||||
|
<TabPanel>
|
||||||
<TabPanel>
|
<EntityTab
|
||||||
<EntityTab
|
initial={initial}
|
||||||
initial={initial}
|
items={items}
|
||||||
items={items}
|
setReference={setReference}
|
||||||
setReference={setReference}
|
setIsValid={setIsValid}
|
||||||
setIsValid={setIsValid}
|
/>
|
||||||
/>
|
</TabPanel>
|
||||||
</TabPanel>
|
|
||||||
|
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<SyntacticTab
|
<SyntacticTab
|
||||||
initial={initial}
|
initial={initial}
|
||||||
setReference={setReference}
|
setReference={setReference}
|
||||||
setIsValid={setIsValid}
|
setIsValid={setIsValid}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Modal>);
|
</Modal>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,19 +73,19 @@ function EntityTab({ initial, items, setIsValid, setReference }: EntityTabProps)
|
||||||
rows={8}
|
rows={8}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className='flex gap-6 flex-start'>
|
<div className='flex gap-3'>
|
||||||
<TextInput dense
|
<TextInput dense
|
||||||
label='Отсылаемая конституента'
|
label='Конституента'
|
||||||
placeholder='Имя'
|
placeholder='Имя'
|
||||||
dimensions='max-w-[17rem] min-w-[17rem]'
|
className='max-w-[11rem] min-w-[11rem]'
|
||||||
value={alias}
|
value={alias}
|
||||||
onChange={event => setAlias(event.target.value)}
|
onChange={event => setAlias(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<TextInput disabled dense noBorder
|
<TextInput disabled dense noBorder
|
||||||
label='Термин'
|
label='Термин'
|
||||||
|
className='flex-grow text-sm'
|
||||||
value={term}
|
value={term}
|
||||||
tooltip={term}
|
tooltip={term}
|
||||||
dimensions='w-full text-sm'
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -94,11 +94,10 @@ function EntityTab({ initial, items, setIsValid, setReference }: EntityTabProps)
|
||||||
setSelected={setSelectedGrams}
|
setSelected={setSelectedGrams}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className='flex items-center gap-4 flex-start'>
|
<div className='flex items-center gap-4'>
|
||||||
<Label text='Отсылаемая словоформа'/>
|
<Label text='Словоформа'/>
|
||||||
<SelectGrammeme
|
<SelectGrammeme
|
||||||
placeholder='Выберите граммемы'
|
placeholder='Выберите граммемы'
|
||||||
dimensions='h-full '
|
|
||||||
className='flex-grow'
|
className='flex-grow'
|
||||||
menuPlacement='top'
|
menuPlacement='top'
|
||||||
value={selectedGrams}
|
value={selectedGrams}
|
||||||
|
|
|
@ -20,9 +20,8 @@ function SelectWordForm({ selected, setSelected }: SelectWordFormProps) {
|
||||||
}, [setSelected]);
|
}, [setSelected]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col items-center w-full text-sm'>
|
<div className='text-sm'>
|
||||||
<div className='flex flex-start'>
|
{PremadeWordForms.slice(0, 12).map(
|
||||||
{PremadeWordForms.slice(0, 6).map(
|
|
||||||
(data, index) =>
|
(data, index) =>
|
||||||
<WordformButton key={`${prefixes.wordform_list}${index}`}
|
<WordformButton key={`${prefixes.wordform_list}${index}`}
|
||||||
text={data.text} example={data.example} grams={data.grams}
|
text={data.text} example={data.example} grams={data.grams}
|
||||||
|
@ -30,18 +29,6 @@ function SelectWordForm({ selected, setSelected }: SelectWordFormProps) {
|
||||||
onSelectGrams={handleSelect}
|
onSelectGrams={handleSelect}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='flex flex-start'>
|
|
||||||
{PremadeWordForms.slice(6, 12).map(
|
|
||||||
(data, index) =>
|
|
||||||
<WordformButton key={`${prefixes.wordform_list}${index}`}
|
|
||||||
text={data.text} example={data.example} grams={data.grams}
|
|
||||||
isSelected={data.grams.every(gram => selected.find(item => item.value as Grammeme === gram))}
|
|
||||||
onSelectGrams={handleSelect}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ function SyntacticTab({ initial, setIsValid, setReference }: SyntacticTabProps)
|
||||||
<div className='flex flex-col gap-2'>
|
<div className='flex flex-col gap-2'>
|
||||||
<TextInput type='number' dense
|
<TextInput type='number' dense
|
||||||
label='Смещение'
|
label='Смещение'
|
||||||
dimensions='max-w-[10rem]'
|
className='max-w-[10rem]'
|
||||||
value={offset}
|
value={offset}
|
||||||
onChange={event => setOffset(event.target.valueAsNumber)}
|
onChange={event => setOffset(event.target.valueAsNumber)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -15,7 +15,7 @@ function WordformButton({ text, example, grams, onSelectGrams, isSelected, ...re
|
||||||
<button type='button' tabIndex={-1}
|
<button type='button' tabIndex={-1}
|
||||||
onClick={() => onSelectGrams(grams)}
|
onClick={() => onSelectGrams(grams)}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'min-w-[6rem]',
|
'min-w-[6.15rem]',
|
||||||
'p-1',
|
'p-1',
|
||||||
'border rounded-none',
|
'border rounded-none',
|
||||||
'cursor-pointer',
|
'cursor-pointer',
|
||||||
|
|
|
@ -127,7 +127,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
|
||||||
hideWindow={hideWindow}
|
hideWindow={hideWindow}
|
||||||
submitText='Сохранить'
|
submitText='Сохранить'
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
className='min-w-[40rem] max-w-[40rem] px-6'
|
className='flex flex-col min-w-[40rem] max-w-[40rem] px-6'
|
||||||
>
|
>
|
||||||
<Overlay position='top-[-0.2rem] left-[7.5rem]'>
|
<Overlay position='top-[-0.2rem] left-[7.5rem]'>
|
||||||
<HelpButton topic={HelpTopic.TERM_CONTROL} dimensions='max-w-[38rem]' offset={3} />
|
<HelpButton topic={HelpTopic.TERM_CONTROL} dimensions='max-w-[38rem]' offset={3} />
|
||||||
|
@ -144,44 +144,41 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
|
||||||
<Label text='Параметры словоформы' />
|
<Label text='Параметры словоформы' />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex items-start justify-between w-full'>
|
<div className='flex justify-stretch'>
|
||||||
<div className='flex items-center'>
|
<TextArea
|
||||||
<TextArea
|
placeholder='Введите текст'
|
||||||
placeholder='Введите текст'
|
className='min-w-[20rem] min-h-[5rem] flex-grow'
|
||||||
dimensions='min-w-[20rem] w-full min-h-[5rem]'
|
rows={2}
|
||||||
rows={2}
|
value={inputText}
|
||||||
value={inputText}
|
onChange={event => setInputText(event.target.value)}
|
||||||
onChange={event => setInputText(event.target.value)}
|
/>
|
||||||
|
<div className='flex flex-col self-center gap-1'>
|
||||||
|
<MiniButton noHover
|
||||||
|
tooltip='Определить граммемы'
|
||||||
|
icon={<BiRightArrow
|
||||||
|
size='1.25rem'
|
||||||
|
className={inputText ? 'clr-text-primary' : ''}
|
||||||
|
/>}
|
||||||
|
disabled={textProcessor.loading || !inputText}
|
||||||
|
onClick={handleParse}
|
||||||
|
/>
|
||||||
|
<MiniButton noHover
|
||||||
|
tooltip='Генерировать словоформу'
|
||||||
|
icon={<BiLeftArrow size='1.25rem' className={inputGrams.length !== 0 ? 'clr-text-primary' : ''} />}
|
||||||
|
disabled={textProcessor.loading || inputGrams.length == 0}
|
||||||
|
onClick={handleInflect}
|
||||||
/>
|
/>
|
||||||
<div className='flex flex-col gap-1'>
|
|
||||||
<MiniButton
|
|
||||||
tooltip='Определить граммемы'
|
|
||||||
icon={<BiRightArrow
|
|
||||||
size='1.25rem'
|
|
||||||
className={inputText ? 'clr-text-primary' : ''}
|
|
||||||
/>}
|
|
||||||
disabled={textProcessor.loading || !inputText}
|
|
||||||
onClick={handleParse}
|
|
||||||
/>
|
|
||||||
<MiniButton
|
|
||||||
tooltip='Генерировать словоформу'
|
|
||||||
icon={<BiLeftArrow size='1.25rem' className={inputGrams.length !== 0 ? 'clr-text-primary' : ''} />}
|
|
||||||
disabled={textProcessor.loading || inputGrams.length == 0}
|
|
||||||
onClick={handleInflect}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<SelectGrammeme
|
<SelectGrammeme
|
||||||
placeholder='Выберите граммемы'
|
placeholder='Выберите граммемы'
|
||||||
dimensions='min-w-[15rem] max-w-[15rem] h-full '
|
dimensions='min-w-[15rem] max-w-[15rem]'
|
||||||
className='flex-grow'
|
|
||||||
value={inputGrams}
|
value={inputGrams}
|
||||||
setValue={setInputGrams}
|
setValue={setInputGrams}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Overlay position='top-2 left-0'>
|
<Overlay position='top-2 left-0'>
|
||||||
<MiniButton
|
<MiniButton noHover
|
||||||
tooltip='Внести словоформу'
|
tooltip='Внести словоформу'
|
||||||
icon={<BiCheck
|
icon={<BiCheck
|
||||||
size='1.25rem'
|
size='1.25rem'
|
||||||
|
@ -190,7 +187,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
|
||||||
disabled={textProcessor.loading || !inputText || inputGrams.length == 0}
|
disabled={textProcessor.loading || !inputText || inputGrams.length == 0}
|
||||||
onClick={handleAddForm}
|
onClick={handleAddForm}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton noHover
|
||||||
tooltip='Генерировать стандартные словоформы'
|
tooltip='Генерировать стандартные словоформы'
|
||||||
icon={<BiChevronsDown size='1.25rem' className={inputText ? 'clr-text-primary' : ''}
|
icon={<BiChevronsDown size='1.25rem' className={inputText ? 'clr-text-primary' : ''}
|
||||||
/>}
|
/>}
|
||||||
|
@ -201,7 +198,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
|
||||||
|
|
||||||
<div className={clsx(
|
<div className={clsx(
|
||||||
'mt-3 mb-2',
|
'mt-3 mb-2',
|
||||||
'flex justify-center items-center',
|
'flex self-center items-center',
|
||||||
'text-sm text-center font-semibold'
|
'text-sm text-center font-semibold'
|
||||||
)}>
|
)}>
|
||||||
<span>Заданные вручную словоформы [{forms.length}]</span>
|
<span>Заданные вручную словоформы [{forms.length}]</span>
|
||||||
|
|
|
@ -51,13 +51,13 @@ function DlgRenameCst({ hideWindow, initial, onRename }: DlgRenameCstProps) {
|
||||||
canSubmit={validated}
|
canSubmit={validated}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'w-full min-w-[24rem]',
|
'w-[30rem]',
|
||||||
'py-6 px-6 flex gap-6 justify-center items-center'
|
'py-6 px-6 flex gap-6 justify-center items-center'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
placeholder='Выберите тип'
|
placeholder='Выберите тип'
|
||||||
className='min-w-[14rem] self-center'
|
className='min-w-[16rem] self-center'
|
||||||
options={SelectorCstType}
|
options={SelectorCstType}
|
||||||
value={{
|
value={{
|
||||||
value: cstData.cst_type,
|
value: cstData.cst_type,
|
||||||
|
@ -65,14 +65,12 @@ function DlgRenameCst({ hideWindow, initial, onRename }: DlgRenameCstProps) {
|
||||||
}}
|
}}
|
||||||
onChange={data => updateData({cst_type: data?.value ?? CstType.BASE})}
|
onChange={data => updateData({cst_type: data?.value ?? CstType.BASE})}
|
||||||
/>
|
/>
|
||||||
<div>
|
|
||||||
<TextInput dense
|
<TextInput dense
|
||||||
label='Имя'
|
label='Имя'
|
||||||
dimensions='w-[7rem]'
|
className='w-[7rem]'
|
||||||
value={cstData.alias}
|
value={cstData.alias}
|
||||||
onChange={event => updateData({alias: event.target.value})}
|
onChange={event => updateData({alias: event.target.value})}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</Modal>);
|
</Modal>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ function DlgShowAST({ hideWindow, syntaxTree, expression }: DlgShowASTProps) {
|
||||||
hideWindow={hideWindow}
|
hideWindow={hideWindow}
|
||||||
className='px-6'
|
className='px-6'
|
||||||
>
|
>
|
||||||
<div className='w-full my-2 text-lg text-center'>
|
<div className='my-2 text-lg text-center'>
|
||||||
{!hoverNode ? expression : null}
|
{!hoverNode ? expression : null}
|
||||||
{hoverNode ?
|
{hoverNode ?
|
||||||
<div>
|
<div>
|
||||||
|
@ -68,7 +68,6 @@ function DlgShowAST({ hideWindow, syntaxTree, expression }: DlgShowASTProps) {
|
||||||
<span>{expression.slice(hoverNode.finish)}</span>
|
<span>{expression.slice(hoverNode.finish)}</span>
|
||||||
</div> : null}
|
</div> : null}
|
||||||
</div>
|
</div>
|
||||||
<div className='w-full h-full overflow-auto'>
|
|
||||||
<div
|
<div
|
||||||
className='relative'
|
className='relative'
|
||||||
style={{
|
style={{
|
||||||
|
@ -86,7 +85,6 @@ function DlgShowAST({ hideWindow, syntaxTree, expression }: DlgShowASTProps) {
|
||||||
onNodePointerOut={handleHoverOut}
|
onNodePointerOut={handleHoverOut}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</Modal>);
|
</Modal>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ function DlgUploadRSForm({ hideWindow }: DlgUploadRSFormProps) {
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label='Загружать название и комментарий'
|
label='Загружать название и комментарий'
|
||||||
dimensions='w-fit py-2'
|
className='py-2'
|
||||||
value={loadMetadata}
|
value={loadMetadata}
|
||||||
setValue={value => setLoadMetadata(value)}
|
setValue={value => setLoadMetadata(value)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -11,6 +11,37 @@ import { getCstExpressionPrefix } from '@/utils/misc';
|
||||||
|
|
||||||
const LOGIC_TYPIIFCATION = 'LOGIC';
|
const LOGIC_TYPIIFCATION = 'LOGIC';
|
||||||
|
|
||||||
|
function useCheckExpression({ schema }: { schema?: IRSForm }) {
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [error, setError] = useState<ErrorData>(undefined);
|
||||||
|
const [parseData, setParseData] = useState<IExpressionParse | undefined>(undefined);
|
||||||
|
|
||||||
|
const resetParse = useCallback(() => setParseData(undefined), []);
|
||||||
|
|
||||||
|
function checkExpression(expression: string, activeCst?: IConstituenta, onSuccess?: DataCallback<IExpressionParse>) {
|
||||||
|
setError(undefined);
|
||||||
|
postCheckExpression(String(schema!.id), {
|
||||||
|
data: { expression: expression },
|
||||||
|
showError: true,
|
||||||
|
setLoading,
|
||||||
|
onError: error => setError(error),
|
||||||
|
onSuccess: parse => {
|
||||||
|
if (activeCst) {
|
||||||
|
adjustResults(parse, expression.trim() === getCstExpressionPrefix(activeCst), activeCst.cst_type);
|
||||||
|
}
|
||||||
|
setParseData(parse);
|
||||||
|
if (onSuccess) onSuccess(parse);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { parseData, checkExpression, resetParse, error, setError, loading };
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useCheckExpression;
|
||||||
|
|
||||||
|
// ===== Internals ========
|
||||||
|
|
||||||
function checkTypeConsistency(type: CstType, typification: string, args: IArgumentInfo[]): boolean {
|
function checkTypeConsistency(type: CstType, typification: string, args: IArgumentInfo[]): boolean {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case CstType.BASE:
|
case CstType.BASE:
|
||||||
|
@ -45,6 +76,16 @@ function adjustResults(parse: IExpressionParse, emptyExpression: boolean, cstTyp
|
||||||
position: 0
|
position: 0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (emptyExpression) {
|
||||||
|
parse.parseResult = false;
|
||||||
|
parse.errors.push({
|
||||||
|
errorType: RSErrorType.globalEmptyDerived,
|
||||||
|
isCritical: true,
|
||||||
|
params: [],
|
||||||
|
position: 0
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!checkTypeConsistency(cstType, parse.typification, parse.args)) {
|
if (!checkTypeConsistency(cstType, parse.typification, parse.args)) {
|
||||||
parse.parseResult = false;
|
parse.parseResult = false;
|
||||||
|
@ -55,33 +96,4 @@ function adjustResults(parse: IExpressionParse, emptyExpression: boolean, cstTyp
|
||||||
position: 0
|
position: 0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function useCheckExpression({ schema }: { schema?: IRSForm }) {
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const [error, setError] = useState<ErrorData>(undefined);
|
|
||||||
const [parseData, setParseData] = useState<IExpressionParse | undefined>(undefined);
|
|
||||||
|
|
||||||
const resetParse = useCallback(() => setParseData(undefined), []);
|
|
||||||
|
|
||||||
function checkExpression(expression: string, activeCst?: IConstituenta, onSuccess?: DataCallback<IExpressionParse>) {
|
|
||||||
setError(undefined);
|
|
||||||
postCheckExpression(String(schema!.id), {
|
|
||||||
data: { expression: expression },
|
|
||||||
showError: true,
|
|
||||||
setLoading,
|
|
||||||
onError: error => setError(error),
|
|
||||||
onSuccess: parse => {
|
|
||||||
if (activeCst) {
|
|
||||||
adjustResults(parse, expression === getCstExpressionPrefix(activeCst), activeCst.cst_type);
|
|
||||||
}
|
|
||||||
setParseData(parse);
|
|
||||||
if (onSuccess) onSuccess(parse);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return { parseData, checkExpression, resetParse, error, setError, loading };
|
|
||||||
}
|
|
||||||
|
|
||||||
export default useCheckExpression;
|
|
|
@ -250,6 +250,7 @@ export enum RSErrorType {
|
||||||
// !!!! Добавлены по сравнению с ConceptCore !!!!!
|
// !!!! Добавлены по сравнению с ConceptCore !!!!!
|
||||||
globalNonemptyBase = 34855,
|
globalNonemptyBase = 34855,
|
||||||
globalUnexpectedType = 34856,
|
globalUnexpectedType = 34856,
|
||||||
|
globalEmptyDerived = 34857,
|
||||||
|
|
||||||
|
|
||||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
|
|
@ -110,7 +110,7 @@ function CreateRSFormPage() {
|
||||||
<TextInput required={!file}
|
<TextInput required={!file}
|
||||||
label='Сокращение'
|
label='Сокращение'
|
||||||
placeholder={file && 'Загрузить из файла'}
|
placeholder={file && 'Загрузить из файла'}
|
||||||
dimensions='w-[14rem]'
|
className='w-[14rem]'
|
||||||
pattern={patterns.alias}
|
pattern={patterns.alias}
|
||||||
tooltip={`не более ${limits.alias_len} символов`}
|
tooltip={`не более ${limits.alias_len} символов`}
|
||||||
value={alias}
|
value={alias}
|
||||||
|
@ -131,11 +131,11 @@ function CreateRSFormPage() {
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
text='Создать схему'
|
text='Создать схему'
|
||||||
loading={processing}
|
loading={processing}
|
||||||
dimensions='min-w-[10rem]'
|
className='min-w-[10rem]'
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
text='Отмена'
|
text='Отмена'
|
||||||
dimensions='min-w-[10rem]'
|
className='min-w-[10rem]'
|
||||||
onClick={() => handleCancel()}
|
onClick={() => handleCancel()}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -89,8 +89,7 @@ function LoginPage() {
|
||||||
|
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
text='Войти'
|
text='Войти'
|
||||||
dimensions='w-[12rem] mt-3'
|
className='self-center w-[12rem] mt-3'
|
||||||
className='self-center'
|
|
||||||
loading={loading}
|
loading={loading}
|
||||||
disabled={!username || !password}
|
disabled={!username || !password}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
import { ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
import { ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
||||||
import { useCallback, useLayoutEffect, useRef, useState } from 'react';
|
import { useCallback, useLayoutEffect, useRef, useState } from 'react';
|
||||||
import { PiGraphLight } from "react-icons/pi";
|
import { FaRegKeyboard } from 'react-icons/fa6';
|
||||||
|
import { RiNodeTree } from 'react-icons/ri'
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import MiniButton from '@/components/Common/MiniButton';
|
import MiniButton from '@/components/Common/MiniButton';
|
||||||
|
@ -12,14 +13,16 @@ import { RSTextWrapper } from '@/components/RSInput/textEditing';
|
||||||
import { useRSForm } from '@/context/RSFormContext';
|
import { useRSForm } from '@/context/RSFormContext';
|
||||||
import DlgShowAST from '@/dialogs/DlgShowAST';
|
import DlgShowAST from '@/dialogs/DlgShowAST';
|
||||||
import useCheckExpression from '@/hooks/useCheckExpression';
|
import useCheckExpression from '@/hooks/useCheckExpression';
|
||||||
|
import useLocalStorage from '@/hooks/useLocalStorage';
|
||||||
import { IConstituenta } from '@/models/rsform';
|
import { IConstituenta } from '@/models/rsform';
|
||||||
import { IExpressionParse, IRSErrorDescription, SyntaxTree } from '@/models/rslang';
|
import { IExpressionParse, IRSErrorDescription, SyntaxTree } from '@/models/rslang';
|
||||||
import { TokenID } from '@/models/rslang';
|
import { TokenID } from '@/models/rslang';
|
||||||
import { labelTypification } from '@/utils/labels';
|
import { labelTypification } from '@/utils/labels';
|
||||||
import { getCstExpressionPrefix } from '@/utils/misc';
|
import { getCstExpressionPrefix } from '@/utils/misc';
|
||||||
|
|
||||||
import RSAnalyzer from './RSAnalyzer';
|
import ParsingResult from './ParsingResult';
|
||||||
import RSEditorControls from './RSEditControls';
|
import RSEditorControls from './RSEditControls';
|
||||||
|
import StatusBar from './StatusBar';
|
||||||
|
|
||||||
interface EditorRSExpressionProps {
|
interface EditorRSExpressionProps {
|
||||||
id?: string
|
id?: string
|
||||||
|
@ -46,6 +49,7 @@ function EditorRSExpression({
|
||||||
const [syntaxTree, setSyntaxTree] = useState<SyntaxTree>([]);
|
const [syntaxTree, setSyntaxTree] = useState<SyntaxTree>([]);
|
||||||
const [expression, setExpression] = useState('');
|
const [expression, setExpression] = useState('');
|
||||||
const [showAST, setShowAST] = useState(false);
|
const [showAST, setShowAST] = useState(false);
|
||||||
|
const [showControls, setShowControls] = useLocalStorage('rseditor-show-controls', true);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
setIsModified(false);
|
setIsModified(false);
|
||||||
|
@ -132,35 +136,52 @@ function EditorRSExpression({
|
||||||
/> : null}
|
/> : null}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<Overlay position='top-[-0.375rem] left-[11rem]'>
|
<Overlay position='top-0 right-0 flex'>
|
||||||
|
<MiniButton noHover
|
||||||
|
tooltip='Включение специальной клавиатуры'
|
||||||
|
onClick={() => setShowControls(prev => !prev)}
|
||||||
|
icon={<FaRegKeyboard size='1.25rem' className={showControls ? 'clr-text-primary': ''} />}
|
||||||
|
/>
|
||||||
<MiniButton noHover
|
<MiniButton noHover
|
||||||
tooltip='Дерево разбора выражения'
|
tooltip='Дерево разбора выражения'
|
||||||
onClick={handleShowAST}
|
onClick={handleShowAST}
|
||||||
icon={<PiGraphLight size='1.25rem' className='clr-text-primary' />}
|
icon={<RiNodeTree size='1.25rem' className='clr-text-primary' />}
|
||||||
/>
|
/>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
|
||||||
|
<Overlay position='top-[-0.5rem] right-1/2 translate-x-1/2'>
|
||||||
|
<StatusBar
|
||||||
|
processing={loading}
|
||||||
|
isModified={isModified}
|
||||||
|
constituenta={activeCst}
|
||||||
|
parseData={parseData}
|
||||||
|
onAnalyze={() => handleCheckExpression()}
|
||||||
|
/>
|
||||||
|
</Overlay>
|
||||||
|
|
||||||
<RSInput innerref={rsInput}
|
<RSInput innerref={rsInput}
|
||||||
value={value}
|
value={value}
|
||||||
minHeight='3.8rem'
|
minHeight='3.8rem'
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
onAnalyze={handleCheckExpression}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{showControls ?
|
||||||
<RSEditorControls
|
<RSEditorControls
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onEdit={handleEdit}
|
onEdit={handleEdit}
|
||||||
/>
|
/> : null}
|
||||||
|
|
||||||
<RSAnalyzer
|
{(parseData && parseData.errors.length > 0) ?
|
||||||
parseData={parseData}
|
<div className='flex-grow text-sm border overflow-y-auto max-h-[4.5rem] min-h-[4.5rem]'>
|
||||||
processing={loading}
|
<ParsingResult
|
||||||
isModified={isModified}
|
data={parseData}
|
||||||
activeCst={activeCst}
|
disabled={disabled}
|
||||||
onCheckExpression={handleCheckExpression}
|
onShowError={onShowError}
|
||||||
onShowError={onShowError}
|
/>
|
||||||
/>
|
</div> : null}
|
||||||
</div>
|
</div>
|
||||||
</>);
|
</>);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ function ParsingResult({ data, disabled, onShowError }: ParsingResultProps) {
|
||||||
const warningsCount = data.errors.length - errorCount;
|
const warningsCount = data.errors.length - errorCount;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='px-2 py-1'>
|
<div className='px-2 pt-1 text-sm'>
|
||||||
<p>Ошибок: <b>{errorCount}</b> | Предупреждений: <b>{warningsCount}</b></p>
|
<p>Ошибок: <b>{errorCount}</b> | Предупреждений: <b>{warningsCount}</b></p>
|
||||||
{data.errors.map(
|
{data.errors.map(
|
||||||
(error, index) => {
|
(error, index) => {
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import Button from '@/components/Common/Button';
|
|
||||||
import { ConceptLoader } from '@/components/Common/ConceptLoader';
|
|
||||||
import { IConstituenta } from '@/models/rsform';
|
|
||||||
import { IExpressionParse, IRSErrorDescription } from '@/models/rslang';
|
|
||||||
|
|
||||||
import ParsingResult from './ParsingResult';
|
|
||||||
import StatusBar from './StatusBar';
|
|
||||||
|
|
||||||
interface RSAnalyzerProps {
|
|
||||||
parseData?: IExpressionParse
|
|
||||||
processing?: boolean
|
|
||||||
|
|
||||||
activeCst?: IConstituenta
|
|
||||||
isModified: boolean
|
|
||||||
disabled?: boolean
|
|
||||||
|
|
||||||
onCheckExpression: () => void
|
|
||||||
onShowError: (error: IRSErrorDescription) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
function RSAnalyzer({
|
|
||||||
parseData, processing,
|
|
||||||
activeCst, disabled, isModified,
|
|
||||||
onCheckExpression, onShowError,
|
|
||||||
}: RSAnalyzerProps) {
|
|
||||||
return (
|
|
||||||
<div className='w-full max-h-[4.5rem] min-h-[4.5rem] flex'>
|
|
||||||
<div className='flex flex-col'>
|
|
||||||
<Button noOutline
|
|
||||||
text='Проверить'
|
|
||||||
tooltip='Проверить формальное определение'
|
|
||||||
dimensions='w-[6.75rem] min-h-[3rem] z-pop rounded-none'
|
|
||||||
colors='clr-btn-default'
|
|
||||||
onClick={() => onCheckExpression()}
|
|
||||||
/>
|
|
||||||
<StatusBar
|
|
||||||
isModified={isModified}
|
|
||||||
constituenta={activeCst}
|
|
||||||
parseData={parseData}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='w-full overflow-y-auto text-sm border rounded-none'>
|
|
||||||
{processing ? <ConceptLoader size={6} /> : null}
|
|
||||||
{(!processing && parseData) ?
|
|
||||||
<ParsingResult
|
|
||||||
data={parseData}
|
|
||||||
disabled={disabled}
|
|
||||||
onShowError={onShowError}
|
|
||||||
/> : null}
|
|
||||||
{(!processing && !parseData) ?
|
|
||||||
<input disabled
|
|
||||||
className='w-full px-2 py-1 text-base select-none h-fit clr-app'
|
|
||||||
placeholder='Результаты проверки выражения'
|
|
||||||
/> : null}
|
|
||||||
</div>
|
|
||||||
</div>);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default RSAnalyzer;
|
|
|
@ -85,54 +85,39 @@ interface RSEditorControlsProps {
|
||||||
|
|
||||||
function RSEditorControls({ onEdit, disabled }: RSEditorControlsProps) {
|
function RSEditorControls({ onEdit, disabled }: RSEditorControlsProps) {
|
||||||
return (
|
return (
|
||||||
<div className='flex items-center justify-start w-full text-sm'>
|
<div className='flex-wrap text-sm divide-solid'>
|
||||||
<div className='w-fit'>
|
{MAIN_FIRST_ROW.map(
|
||||||
<div className='flex justify-start'>
|
(token) =>
|
||||||
{MAIN_FIRST_ROW.map(
|
<RSTokenButton key={`${prefixes.rsedit_btn}${token}`}
|
||||||
(token) =>
|
token={token} onInsert={onEdit} disabled={disabled}
|
||||||
<RSTokenButton key={`${prefixes.rsedit_btn}${token}`}
|
/>)}
|
||||||
token={token} onInsert={onEdit} disabled={disabled}
|
{SECONDARY_FIRST_ROW.map(
|
||||||
/>)}
|
({text, tooltip}) =>
|
||||||
</div>
|
<RSLocalButton key={`${prefixes.rsedit_btn}${tooltip}`}
|
||||||
<div className='flex justify-start'>
|
text={text} tooltip={tooltip} onInsert={onEdit} disabled={disabled}
|
||||||
{MAIN_SECOND_ROW.map(
|
/>)}
|
||||||
(token) =>
|
|
||||||
<RSTokenButton key={`${prefixes.rsedit_btn}${token}`}
|
|
||||||
token={token} onInsert={onEdit} disabled={disabled}
|
|
||||||
/>)}
|
|
||||||
</div>
|
|
||||||
<div className='flex justify-start'>
|
|
||||||
{MAIN_THIRD_ROW.map(
|
|
||||||
(token) =>
|
|
||||||
<RSTokenButton key={`${prefixes.rsedit_btn}${token}`}
|
|
||||||
token={token} onInsert={onEdit} disabled={disabled}
|
|
||||||
/>)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='w-fit'>
|
{MAIN_SECOND_ROW.map(
|
||||||
<div className='flex justify-start'>
|
(token) =>
|
||||||
{SECONDARY_FIRST_ROW.map(
|
<RSTokenButton key={`${prefixes.rsedit_btn}${token}`}
|
||||||
({text, tooltip}) =>
|
token={token} onInsert={onEdit} disabled={disabled}
|
||||||
<RSLocalButton key={`${prefixes.rsedit_btn}${tooltip}`}
|
/>)}
|
||||||
text={text} tooltip={tooltip} onInsert={onEdit} disabled={disabled}
|
{SECONDARY_SECOND_ROW.map(
|
||||||
/>)}
|
({text, tooltip}) =>
|
||||||
</div>
|
<RSLocalButton key={`${prefixes.rsedit_btn}${tooltip}`}
|
||||||
<div className='flex justify-start'>
|
text={text} tooltip={tooltip} onInsert={onEdit} disabled={disabled}
|
||||||
{SECONDARY_SECOND_ROW.map(
|
/>)}
|
||||||
({text, tooltip}) =>
|
|
||||||
<RSLocalButton key={`${prefixes.rsedit_btn}${tooltip}`}
|
{MAIN_THIRD_ROW.map(
|
||||||
text={text} tooltip={tooltip} onInsert={onEdit} disabled={disabled}
|
(token) =>
|
||||||
/>)}
|
<RSTokenButton key={`${prefixes.rsedit_btn}${token}`}
|
||||||
</div>
|
token={token} onInsert={onEdit} disabled={disabled}
|
||||||
<div className='flex justify-start'>
|
/>)}
|
||||||
{SECONDARY_THIRD_ROW.map(
|
{SECONDARY_THIRD_ROW.map(
|
||||||
({text, tooltip}) =>
|
({text, tooltip}) =>
|
||||||
<RSLocalButton key={`${prefixes.rsedit_btn}${tooltip}`}
|
<RSLocalButton key={`${prefixes.rsedit_btn}${tooltip}`}
|
||||||
text={text} tooltip={tooltip} onInsert={onEdit} disabled={disabled}
|
text={text} tooltip={tooltip} onInsert={onEdit} disabled={disabled}
|
||||||
/>)}
|
/>)}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ function RSLocalButton({ text, tooltip, disabled, onInsert }: RSLocalButtonProps
|
||||||
<button type='button' tabIndex={-1}
|
<button type='button' tabIndex={-1}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
className='w-[2rem] h-6 cursor-pointer disabled:cursor-default border rounded-none clr-hover clr-btn-clear'
|
className='w-[2rem] h-6 cursor-pointer disabled:cursor-default rounded-none clr-hover clr-btn-clear'
|
||||||
onClick={() => onInsert(TokenID.ID_LOCAL, text)}
|
onClick={() => onInsert(TokenID.ID_LOCAL, text)}
|
||||||
>
|
>
|
||||||
{text}
|
{text}
|
||||||
|
|
|
@ -15,7 +15,7 @@ function RSTokenButton({ token, disabled, onInsert }: RSTokenButtonProps) {
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onClick={() => onInsert(token)}
|
onClick={() => onInsert(token)}
|
||||||
title={describeToken(token)}
|
title={describeToken(token)}
|
||||||
className={`px-1 cursor-pointer disabled:cursor-default border rounded-none h-6 ${width} outline-none clr-hover clr-btn-clear`}
|
className={`px-1 cursor-pointer disabled:cursor-default h-6 ${width} outline-none clr-hover clr-btn-clear`}
|
||||||
>
|
>
|
||||||
{label ? <span className='whitespace-nowrap'>{label}</span> : null}
|
{label ? <span className='whitespace-nowrap'>{label}</span> : null}
|
||||||
</button>);
|
</button>);
|
||||||
|
|
|
@ -2,22 +2,26 @@
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
import { BiBug } from 'react-icons/bi';
|
||||||
|
|
||||||
|
import { ConceptLoader } from '@/components/Common/ConceptLoader';
|
||||||
import { useConceptTheme } from '@/context/ThemeContext';
|
import { useConceptTheme } from '@/context/ThemeContext';
|
||||||
import { ExpressionStatus } from '@/models/rsform';
|
import { ExpressionStatus } from '@/models/rsform';
|
||||||
import { type IConstituenta } from '@/models/rsform';
|
import { type IConstituenta } from '@/models/rsform';
|
||||||
import { inferStatus } from '@/models/rsformAPI';
|
import { inferStatus } from '@/models/rsformAPI';
|
||||||
import { IExpressionParse, ParsingStatus } from '@/models/rslang';
|
import { IExpressionParse, ParsingStatus } from '@/models/rslang';
|
||||||
import { colorbgCstStatus } from '@/utils/color';
|
import { colorbgCstStatus } from '@/utils/color';
|
||||||
import { describeExpressionStatus, labelExpressionStatus } from '@/utils/labels';
|
import { labelExpressionStatus } from '@/utils/labels';
|
||||||
|
|
||||||
interface StatusBarProps {
|
interface StatusBarProps {
|
||||||
|
processing?: boolean
|
||||||
isModified?: boolean
|
isModified?: boolean
|
||||||
parseData?: IExpressionParse
|
parseData?: IExpressionParse
|
||||||
constituenta?: IConstituenta
|
constituenta?: IConstituenta
|
||||||
|
onAnalyze: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function StatusBar({ isModified, constituenta, parseData }: StatusBarProps) {
|
function StatusBar({ isModified, processing, constituenta, parseData, onAnalyze }: StatusBarProps) {
|
||||||
const { colors } = useConceptTheme();
|
const { colors } = useConceptTheme();
|
||||||
const status = useMemo(() => {
|
const status = useMemo(() => {
|
||||||
if (isModified) {
|
if (isModified) {
|
||||||
|
@ -31,16 +35,28 @@ function StatusBar({ isModified, constituenta, parseData }: StatusBarProps) {
|
||||||
}, [isModified, constituenta, parseData]);
|
}, [isModified, constituenta, parseData]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div title={describeExpressionStatus(status)}
|
<div
|
||||||
|
title='Проверить определение [Ctrl + Q]'
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'h-full',
|
'w-[10rem] h-[1.75rem]',
|
||||||
'border rounded-none',
|
'px-3',
|
||||||
'text-sm font-semibold small-caps text-center',
|
'border',
|
||||||
'select-none'
|
'select-none',
|
||||||
|
'cursor-pointer',
|
||||||
|
'duration-500 transition-colors'
|
||||||
)}
|
)}
|
||||||
style={{backgroundColor: colorbgCstStatus(status, colors)}}
|
style={{backgroundColor: processing ? colors.bgDefault : colorbgCstStatus(status, colors)}}
|
||||||
|
onClick={onAnalyze}
|
||||||
>
|
>
|
||||||
{labelExpressionStatus(status)}
|
{processing ?
|
||||||
|
<ConceptLoader size={3} /> :
|
||||||
|
<div className='flex items-center justify-center h-full gap-2'>
|
||||||
|
<BiBug size='1rem' className='translate-y-[0.1rem]' />
|
||||||
|
<span className='font-semibold small-caps'>
|
||||||
|
{labelExpressionStatus(status)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,7 @@ function FormRSForm({
|
||||||
/>
|
/>
|
||||||
<TextInput required
|
<TextInput required
|
||||||
label='Сокращение'
|
label='Сокращение'
|
||||||
dimensions='w-[14rem]'
|
className='w-[14rem]'
|
||||||
pattern={patterns.alias}
|
pattern={patterns.alias}
|
||||||
tooltip={`не более ${limits.alias_len} символов`}
|
tooltip={`не более ${limits.alias_len} символов`}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
@ -107,7 +107,6 @@ function FormRSForm({
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label='Общедоступная схема'
|
label='Общедоступная схема'
|
||||||
tooltip='Общедоступные схемы видны всем пользователям и могут быть изменены'
|
tooltip='Общедоступные схемы видны всем пользователям и могут быть изменены'
|
||||||
dimensions='w-fit'
|
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
value={common}
|
value={common}
|
||||||
setValue={value => setCommon(value)}
|
setValue={value => setCommon(value)}
|
||||||
|
@ -115,7 +114,6 @@ function FormRSForm({
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label='Неизменная схема'
|
label='Неизменная схема'
|
||||||
tooltip='Только администраторы могут присваивать схемам неизменный статус'
|
tooltip='Только администраторы могут присваивать схемам неизменный статус'
|
||||||
dimensions='w-fit'
|
|
||||||
disabled={disabled || !user?.is_staff}
|
disabled={disabled || !user?.is_staff}
|
||||||
value={canonical}
|
value={canonical}
|
||||||
setValue={value => setCanonical(value)}
|
setValue={value => setCanonical(value)}
|
||||||
|
@ -123,8 +121,7 @@ function FormRSForm({
|
||||||
</div>
|
</div>
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
text='Сохранить изменения'
|
text='Сохранить изменения'
|
||||||
className='self-center'
|
className='self-center my-2'
|
||||||
dimensions='my-2 w-fit'
|
|
||||||
loading={processing}
|
loading={processing}
|
||||||
disabled={!isModified || disabled}
|
disabled={!isModified || disabled}
|
||||||
icon={<FiSave size='1.5rem' />}
|
icon={<FiSave size='1.5rem' />}
|
||||||
|
|
|
@ -128,7 +128,7 @@ function RSTable({
|
||||||
<DataTable dense noFooter
|
<DataTable dense noFooter
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'min-h-[20rem]',
|
'min-h-[20rem]',
|
||||||
'overflow-auto',
|
'overflow-y-auto',
|
||||||
'text-sm',
|
'text-sm',
|
||||||
'select-none'
|
'select-none'
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -111,7 +111,7 @@ function TermGraph({
|
||||||
}, [noNavigation]);
|
}, [noNavigation]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full h-full overflow-auto outline-none'>
|
<div className='outline-none'>
|
||||||
<div className='relative' style={{width: canvasWidth, height: canvasHeight}}>
|
<div className='relative' style={{width: canvasWidth, height: canvasHeight}}>
|
||||||
<GraphUI
|
<GraphUI
|
||||||
draggable
|
draggable
|
||||||
|
|
|
@ -68,7 +68,7 @@ function RSTabs() {
|
||||||
cstCreate, cstDelete, cstRename, subscribe, unsubscribe, cstUpdate, resetAliases
|
cstCreate, cstDelete, cstRename, subscribe, unsubscribe, cstUpdate, resetAliases
|
||||||
} = useRSForm();
|
} = useRSForm();
|
||||||
const { destroyItem } = useLibrary();
|
const { destroyItem } = useLibrary();
|
||||||
const { setNoFooter, noNavigation } = useConceptTheme();
|
const { setNoFooter } = useConceptTheme();
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const { mode, setMode } = useAccessMode();
|
const { mode, setMode } = useAccessMode();
|
||||||
|
|
||||||
|
@ -106,13 +106,6 @@ function RSTabs() {
|
||||||
const [insertCstID, setInsertCstID] = useState<number | undefined>(undefined);
|
const [insertCstID, setInsertCstID] = useState<number | undefined>(undefined);
|
||||||
const [showTemplates, setShowTemplates] = useState(false);
|
const [showTemplates, setShowTemplates] = useState(false);
|
||||||
|
|
||||||
const panelHeight = useMemo(
|
|
||||||
() => {
|
|
||||||
return !noNavigation ?
|
|
||||||
'calc(100vh - 4.8rem - 4px)'
|
|
||||||
: 'calc(100vh - 2rem - 4px)';
|
|
||||||
}, [noNavigation]);
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (schema) {
|
if (schema) {
|
||||||
const oldTitle = document.title;
|
const oldTitle = document.title;
|
||||||
|
@ -403,11 +396,11 @@ function RSTabs() {
|
||||||
onSelect={onSelectTab}
|
onSelect={onSelectTab}
|
||||||
defaultFocus
|
defaultFocus
|
||||||
selectedTabClassName='clr-selected'
|
selectedTabClassName='clr-selected'
|
||||||
className='flex flex-col items-center min-w-[45rem]'
|
className='flex flex-col min-w-[45rem]'
|
||||||
>
|
>
|
||||||
<TabList className={clsx(
|
<TabList className={clsx(
|
||||||
'h-[1.9rem]',
|
'h-[1.9rem] mx-auto',
|
||||||
'flex justify-stretch',
|
'flex',
|
||||||
'border-b-2 border-x-2 divide-x-2'
|
'border-b-2 border-x-2 divide-x-2'
|
||||||
)}>
|
)}>
|
||||||
<RSTabsMenu isMutable={isMutable}
|
<RSTabsMenu isMutable={isMutable}
|
||||||
|
@ -435,53 +428,51 @@ function RSTabs() {
|
||||||
<ConceptTab label='Граф термов' />
|
<ConceptTab label='Граф термов' />
|
||||||
</TabList>
|
</TabList>
|
||||||
|
|
||||||
<div className='overflow-y-auto' style={{ maxHeight: panelHeight}}>
|
<TabPanel forceRender style={{ display: activeTab === RSTabID.CARD ? '': 'none' }}>
|
||||||
<TabPanel forceRender style={{ display: activeTab === RSTabID.CARD ? '': 'none' }}>
|
<EditorRSForm
|
||||||
<EditorRSForm
|
isMutable={isMutable}
|
||||||
isMutable={isMutable}
|
isModified={isModified}
|
||||||
isModified={isModified}
|
setIsModified={setIsModified}
|
||||||
setIsModified={setIsModified}
|
onToggleSubscribe={handleToggleSubscribe}
|
||||||
onToggleSubscribe={handleToggleSubscribe}
|
onDownload={onDownloadSchema}
|
||||||
onDownload={onDownloadSchema}
|
onDestroy={onDestroySchema}
|
||||||
onDestroy={onDestroySchema}
|
onClaim={onClaimSchema}
|
||||||
onClaim={onClaimSchema}
|
onShare={onShareSchema}
|
||||||
onShare={onShareSchema}
|
/>
|
||||||
/>
|
</TabPanel>
|
||||||
</TabPanel>
|
|
||||||
|
|
||||||
<TabPanel forceRender style={{ display: activeTab === RSTabID.CST_LIST ? '': 'none' }}>
|
<TabPanel forceRender style={{ display: activeTab === RSTabID.CST_LIST ? '': 'none' }}>
|
||||||
<EditorRSList
|
<EditorRSList
|
||||||
isMutable={isMutable}
|
isMutable={isMutable}
|
||||||
onOpenEdit={onOpenCst}
|
onOpenEdit={onOpenCst}
|
||||||
onCreateCst={promptCreateCst}
|
onCreateCst={promptCreateCst}
|
||||||
onDeleteCst={promptDeleteCst}
|
onDeleteCst={promptDeleteCst}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
<TabPanel forceRender style={{ display: activeTab === RSTabID.CST_EDIT ? '': 'none' }}>
|
<TabPanel forceRender style={{ display: activeTab === RSTabID.CST_EDIT ? '': 'none' }}>
|
||||||
<EditorConstituenta
|
<EditorConstituenta
|
||||||
isMutable={isMutable}
|
isMutable={isMutable}
|
||||||
isModified={isModified}
|
isModified={isModified}
|
||||||
setIsModified={setIsModified}
|
setIsModified={setIsModified}
|
||||||
activeID={activeID}
|
activeID={activeID}
|
||||||
activeCst={activeCst}
|
activeCst={activeCst}
|
||||||
onOpenEdit={onOpenCst}
|
onOpenEdit={onOpenCst}
|
||||||
onCreateCst={promptCreateCst}
|
onCreateCst={promptCreateCst}
|
||||||
onDeleteCst={promptDeleteCst}
|
onDeleteCst={promptDeleteCst}
|
||||||
onRenameCst={promptRenameCst}
|
onRenameCst={promptRenameCst}
|
||||||
onEditTerm={promptShowEditTerm}
|
onEditTerm={promptShowEditTerm}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
<TabPanel style={{ display: activeTab === RSTabID.TERM_GRAPH ? '': 'none' }}>
|
<TabPanel style={{ display: activeTab === RSTabID.TERM_GRAPH ? '': 'none' }}>
|
||||||
<EditorTermGraph
|
<EditorTermGraph
|
||||||
isMutable={isMutable}
|
isMutable={isMutable}
|
||||||
onOpenEdit={onOpenCst}
|
onOpenEdit={onOpenCst}
|
||||||
onCreateCst={promptCreateCst}
|
onCreateCst={promptCreateCst}
|
||||||
onDeleteCst={promptDeleteCst}
|
onDeleteCst={promptDeleteCst}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</div>
|
|
||||||
</Tabs> : null}
|
</Tabs> : null}
|
||||||
</>);
|
</>);
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,12 +95,12 @@ function RSTabsMenu({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex items-stretch h-full w-fit'>
|
<div className='flex'>
|
||||||
<div ref={schemaMenu.ref}>
|
<div ref={schemaMenu.ref}>
|
||||||
<Button noBorder dense tabIndex={-1}
|
<Button noBorder dense tabIndex={-1}
|
||||||
tooltip='Меню'
|
tooltip='Меню'
|
||||||
icon={<BiMenu size='1.25rem' className='clr-text-controls' />}
|
icon={<BiMenu size='1.25rem' className='clr-text-controls' />}
|
||||||
dimensions='h-full w-fit pl-2'
|
className='h-full pl-2'
|
||||||
style={{outlineColor: 'transparent'}}
|
style={{outlineColor: 'transparent'}}
|
||||||
onClick={schemaMenu.toggle}
|
onClick={schemaMenu.toggle}
|
||||||
/>
|
/>
|
||||||
|
@ -148,7 +148,7 @@ function RSTabsMenu({
|
||||||
<div ref={editMenu.ref}>
|
<div ref={editMenu.ref}>
|
||||||
<Button dense noBorder tabIndex={-1}
|
<Button dense noBorder tabIndex={-1}
|
||||||
tooltip={'Редактирование'}
|
tooltip={'Редактирование'}
|
||||||
dimensions='h-full w-fit'
|
className='h-full'
|
||||||
style={{outlineColor: 'transparent'}}
|
style={{outlineColor: 'transparent'}}
|
||||||
icon={<FiEdit size='1.25rem' className={isMutable ? 'clr-text-success' : 'clr-text-warning'}/>}
|
icon={<FiEdit size='1.25rem' className={isMutable ? 'clr-text-success' : 'clr-text-warning'}/>}
|
||||||
onClick={editMenu.toggle}
|
onClick={editMenu.toggle}
|
||||||
|
@ -173,7 +173,7 @@ function RSTabsMenu({
|
||||||
<div ref={accessMenu.ref}>
|
<div ref={accessMenu.ref}>
|
||||||
<Button dense noBorder tabIndex={-1}
|
<Button dense noBorder tabIndex={-1}
|
||||||
tooltip={`режим ${labelAccessMode(mode)}`}
|
tooltip={`режим ${labelAccessMode(mode)}`}
|
||||||
dimensions='h-full w-fit pr-2'
|
className='h-full pr-2'
|
||||||
style={{outlineColor: 'transparent'}}
|
style={{outlineColor: 'transparent'}}
|
||||||
icon={
|
icon={
|
||||||
mode === UserAccessMode.ADMIN ? <BiMeteor size='1.25rem' className='clr-text-primary'/>
|
mode === UserAccessMode.ADMIN ? <BiMeteor size='1.25rem' className='clr-text-primary'/>
|
||||||
|
|
|
@ -93,18 +93,18 @@ function RegisterPage() {
|
||||||
pattern={patterns.login}
|
pattern={patterns.login}
|
||||||
tooltip='Минимум 3 знака. Латинские буквы и цифры. Не может начинаться с цифры'
|
tooltip='Минимум 3 знака. Латинские буквы и цифры. Не может начинаться с цифры'
|
||||||
value={username}
|
value={username}
|
||||||
dimensions='w-[15rem]'
|
className='w-[15rem]'
|
||||||
onChange={event => setUsername(event.target.value)}
|
onChange={event => setUsername(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<TextInput id='password' type='password' required
|
<TextInput id='password' type='password' required
|
||||||
label='Пароль'
|
label='Пароль'
|
||||||
dimensions='w-[15rem]'
|
className='w-[15rem]'
|
||||||
value={password}
|
value={password}
|
||||||
onChange={event => setPassword(event.target.value)}
|
onChange={event => setPassword(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<TextInput id='password2' required type='password'
|
<TextInput id='password2' required type='password'
|
||||||
label='Повторите пароль'
|
label='Повторите пароль'
|
||||||
dimensions='w-[15rem]'
|
className='w-[15rem]'
|
||||||
value={password2}
|
value={password2}
|
||||||
onChange={event => setPassword2(event.target.value)}
|
onChange={event => setPassword2(event.target.value)}
|
||||||
/>
|
/>
|
||||||
|
@ -145,13 +145,13 @@ function RegisterPage() {
|
||||||
<div className='flex justify-around my-3'>
|
<div className='flex justify-around my-3'>
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
text='Регистрировать'
|
text='Регистрировать'
|
||||||
dimensions='min-w-[10rem]'
|
className='min-w-[10rem]'
|
||||||
loading={loading}
|
loading={loading}
|
||||||
disabled={!acceptPrivacy}
|
disabled={!acceptPrivacy}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
text='Назад'
|
text='Назад'
|
||||||
dimensions='min-w-[10rem]'
|
className='min-w-[10rem]'
|
||||||
onClick={() => handleCancel()}
|
onClick={() => handleCancel()}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -53,7 +53,7 @@ function ViewSubscriptions({items}: ViewSubscriptionsProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataTable dense noFooter
|
<DataTable dense noFooter
|
||||||
className='max-h-[23.8rem] overflow-auto text-sm border'
|
className='max-h-[23.8rem] overflow-y-auto text-sm border'
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={items}
|
data={items}
|
||||||
headPosition='0'
|
headPosition='0'
|
||||||
|
|
|
@ -650,6 +650,8 @@ export function describeRSError(error: IRSErrorDescription): string {
|
||||||
return `Непустое выражение базисного/константного множества`;
|
return `Непустое выражение базисного/константного множества`;
|
||||||
case RSErrorType.globalUnexpectedType:
|
case RSErrorType.globalUnexpectedType:
|
||||||
return `Типизация выражения не соответствует типу конституенты`;
|
return `Типизация выражения не соответствует типу конституенты`;
|
||||||
|
case RSErrorType.globalEmptyDerived:
|
||||||
|
return `Пустое выражение для выводимого понятия или утверждения`;
|
||||||
}
|
}
|
||||||
return 'UNKNOWN ERROR';
|
return 'UNKNOWN ERROR';
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user