mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 21:10:38 +03:00
UI components refactoring and DOM optimization
This commit is contained in:
parent
75da54cbd3
commit
c1a994bc0c
|
@ -61,8 +61,7 @@ function BackendError({ error }: BackendErrorProps) {
|
||||||
return (
|
return (
|
||||||
<div className='px-3 py-2 min-w-[15rem] text-sm font-semibold select-text text-warning'>
|
<div className='px-3 py-2 min-w-[15rem] text-sm font-semibold select-text text-warning'>
|
||||||
{DescribeError(error)}
|
{DescribeError(error)}
|
||||||
</div>
|
</div>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BackendError;
|
export default BackendError;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
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>, 'className' | 'children' | 'title'| 'type'> {
|
||||||
|
|
|
@ -14,10 +14,9 @@ function ConceptSearch({ value, onChange, dense }: ConceptSearchProps) {
|
||||||
<div className='absolute inset-y-0 flex items-center pl-3 pointer-events-none text-controls'>
|
<div className='absolute inset-y-0 flex items-center pl-3 pointer-events-none text-controls'>
|
||||||
<MagnifyingGlassIcon />
|
<MagnifyingGlassIcon />
|
||||||
</div>
|
</div>
|
||||||
<TextInput
|
<TextInput noOutline
|
||||||
dimensions={`w-full pl-10 ${borderClass} rounded`}
|
|
||||||
placeholder='Поиск'
|
placeholder='Поиск'
|
||||||
noOutline
|
dimensions={`w-full pl-10 ${borderClass}`}
|
||||||
noBorder={dense}
|
noBorder={dense}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={event => (onChange ? onChange(event.target.value) : undefined)}
|
onChange={event => (onChange ? onChange(event.target.value) : undefined)}
|
||||||
|
|
|
@ -10,8 +10,7 @@ function Dropdown({ children, dimensions = 'w-fit', stretchLeft }: DropdownProps
|
||||||
<div className={`absolute ${stretchLeft ? 'right-0' : 'left-0'} mt-3 z-modal-tooltip flex flex-col items-stretch justify-start origin-top-right border rounded-md shadow-lg clr-input ${dimensions}`}>
|
<div className={`absolute ${stretchLeft ? 'right-0' : 'left-0'} mt-3 z-modal-tooltip flex flex-col items-stretch justify-start origin-top-right border rounded-md shadow-lg clr-input ${dimensions}`}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Dropdown;
|
export default Dropdown;
|
||||||
|
|
|
@ -16,8 +16,7 @@ function DropdownButton({ tooltip, onClick, disabled, children }: DropdownButton
|
||||||
className={`px-3 py-1 text-left overflow-ellipsis whitespace-nowrap ${behavior} ${text}`}
|
className={`px-3 py-1 text-left overflow-ellipsis whitespace-nowrap ${behavior} ${text}`}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</button>
|
</button>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DropdownButton;
|
export default DropdownButton;
|
||||||
|
|
|
@ -21,8 +21,7 @@ function DropdownCheckbox({ tooltip, setValue, disabled, ...restProps }: Dropdow
|
||||||
setValue={setValue}
|
setValue={setValue}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DropdownCheckbox;
|
export default DropdownCheckbox;
|
||||||
|
|
|
@ -13,18 +13,16 @@ function EmbedYoutube({ videoID, pxHeight, pxWidth }: EmbedYoutubeProps) {
|
||||||
className='relative'
|
className='relative'
|
||||||
style={{height: 0, paddingBottom: `${pxHeight}px`, paddingLeft: `${pxWidth}px`}}
|
style={{height: 0, paddingBottom: `${pxHeight}px`, paddingLeft: `${pxWidth}px`}}
|
||||||
>
|
>
|
||||||
<iframe
|
<iframe allowFullScreen
|
||||||
|
title='Встроенное видео Youtube'
|
||||||
|
allow='accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture'
|
||||||
className='absolute top-0 left-0 border'
|
className='absolute top-0 left-0 border'
|
||||||
style={{minHeight: `${pxHeight}px`, minWidth: `${pxWidth}px`}}
|
style={{minHeight: `${pxHeight}px`, minWidth: `${pxWidth}px`}}
|
||||||
width={`${pxWidth}px`}
|
width={`${pxWidth}px`}
|
||||||
height={`${pxHeight}px`}
|
height={`${pxHeight}px`}
|
||||||
src={`https://www.youtube.com/embed/${videoID}`}
|
src={`https://www.youtube.com/embed/${videoID}`}
|
||||||
allow='accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture'
|
|
||||||
allowFullScreen
|
|
||||||
title='Встроенное видео Youtube'
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default EmbedYoutube;
|
export default EmbedYoutube;
|
||||||
|
|
|
@ -55,8 +55,7 @@ function FileInput({
|
||||||
<Label
|
<Label
|
||||||
text={fileName}
|
text={fileName}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FileInput;
|
export default FileInput;
|
||||||
|
|
|
@ -1,20 +1,24 @@
|
||||||
interface FormProps {
|
interface FormProps {
|
||||||
title?: string
|
title?: string
|
||||||
|
className?: string
|
||||||
dimensions?: string
|
dimensions?: string
|
||||||
onSubmit: (event: React.FormEvent<HTMLFormElement>) => void
|
onSubmit: (event: React.FormEvent<HTMLFormElement>) => void
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
function Form({ title, onSubmit, dimensions = 'max-w-xs', children }: FormProps) {
|
function Form({
|
||||||
|
title, className, onSubmit,
|
||||||
|
dimensions='max-w-xs',
|
||||||
|
children
|
||||||
|
}: FormProps) {
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
className={`border shadow-md py-2 clr-app px-6 flex flex-col gap-3 ${dimensions}`}
|
className={`border shadow-md py-2 clr-app px-6 flex flex-col gap-3 ${dimensions} ${className}`}
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
>
|
>
|
||||||
{ title ? <h1 className='text-xl whitespace-nowrap'>{title}</h1> : null }
|
{ title ? <h1 className='text-xl whitespace-nowrap'>{title}</h1> : null }
|
||||||
{children}
|
{children}
|
||||||
</form>
|
</form>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Form;
|
export default Form;
|
|
@ -14,8 +14,7 @@ function Label({ text, tooltip, className, ...restProps }: LabelProps) {
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
{text}
|
{text}
|
||||||
</label>
|
</label>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Label;
|
export default Label;
|
||||||
|
|
|
@ -15,13 +15,10 @@ function LabeledText({ id, label, text, tooltip }: LabeledTextProps) {
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</label>
|
</label>
|
||||||
<span
|
<span id={id}>
|
||||||
id={id}
|
|
||||||
>
|
|
||||||
{text}
|
{text}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LabeledText;
|
export default LabeledText;
|
||||||
|
|
|
@ -19,8 +19,7 @@ function MiniButton({
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
{icon}
|
{icon}
|
||||||
</button>
|
</button>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MiniButton;
|
export default MiniButton;
|
||||||
|
|
|
@ -4,6 +4,7 @@ import useEscapeKey from '../../hooks/useEscapeKey';
|
||||||
import { CrossIcon } from '../Icons';
|
import { CrossIcon } from '../Icons';
|
||||||
import Button from './Button';
|
import Button from './Button';
|
||||||
import MiniButton from './MiniButton';
|
import MiniButton from './MiniButton';
|
||||||
|
import Overlay from './Overlay';
|
||||||
|
|
||||||
export interface ModalProps {
|
export interface ModalProps {
|
||||||
title?: string
|
title?: string
|
||||||
|
@ -43,15 +44,13 @@ function Modal({
|
||||||
<div ref={ref}
|
<div ref={ref}
|
||||||
className='fixed bottom-1/2 left-1/2 translate-y-1/2 -translate-x-1/2 px-6 w-fit max-w-[calc(100vw-2rem)] h-fit z-modal clr-app border shadow-md'
|
className='fixed bottom-1/2 left-1/2 translate-y-1/2 -translate-x-1/2 px-6 w-fit max-w-[calc(100vw-2rem)] h-fit z-modal clr-app border shadow-md'
|
||||||
>
|
>
|
||||||
<div className='relative'>
|
<Overlay position='right-[-1rem] top-2' className='text-disabled'>
|
||||||
<div className='absolute right-[-1rem] top-2 text-disabled'>
|
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Закрыть диалоговое окно [ESC]'
|
tooltip='Закрыть диалоговое окно [ESC]'
|
||||||
icon={<CrossIcon size={5}/>}
|
icon={<CrossIcon size={5}/>}
|
||||||
onClick={handleCancel}
|
onClick={handleCancel}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Overlay>
|
||||||
</div>
|
|
||||||
|
|
||||||
{title ? <h1 className='my-2 text-lg select-none'>{title}</h1> : null}
|
{title ? <h1 className='my-2 text-lg select-none'>{title}</h1> : null}
|
||||||
|
|
||||||
|
|
21
rsconcept/frontend/src/components/Common/Overlay.tsx
Normal file
21
rsconcept/frontend/src/components/Common/Overlay.tsx
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
interface OverlayProps {
|
||||||
|
children: React.ReactNode
|
||||||
|
position?: string
|
||||||
|
className?: string
|
||||||
|
layer?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function Overlay({
|
||||||
|
children, className,
|
||||||
|
position='top-0 right-0',
|
||||||
|
layer='z-pop'
|
||||||
|
}: OverlayProps) {
|
||||||
|
return (
|
||||||
|
<div className='relative'>
|
||||||
|
<div className={`absolute ${className} ${position} ${layer}`}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Overlay;
|
|
@ -66,8 +66,7 @@ function SelectMulti<Option, Group extends GroupBase<Option> = GroupBase<Option>
|
||||||
menuPortalTarget={!noPortal ? document.body : null}
|
menuPortalTarget={!noPortal ? document.body : null}
|
||||||
styles={adjustedStyles}
|
styles={adjustedStyles}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SelectMulti;
|
export default SelectMulti;
|
||||||
|
|
|
@ -61,8 +61,7 @@ function SelectSingle<Option, Group extends GroupBase<Option> = GroupBase<Option
|
||||||
menuPortalTarget={!noPortal ? document.body : null}
|
menuPortalTarget={!noPortal ? document.body : null}
|
||||||
styles={adjustedStyles}
|
styles={adjustedStyles}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SelectSingle;
|
export default SelectSingle;
|
||||||
|
|
|
@ -28,8 +28,7 @@ function SelectorButton({
|
||||||
>
|
>
|
||||||
{icon ? icon : null}
|
{icon ? icon : null}
|
||||||
{text ? <div className={'font-semibold whitespace-nowrap pb-1'}>{text}</div> : null}
|
{text ? <div className={'font-semibold whitespace-nowrap pb-1'}>{text}</div> : null}
|
||||||
</button>
|
</button>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SelectorButton;
|
export default SelectorButton;
|
||||||
|
|
|
@ -14,7 +14,7 @@ function SubmitButton({
|
||||||
return (
|
return (
|
||||||
<button type='submit'
|
<button type='submit'
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
className={`px-4 py-2 inline-flex items-center gap-2 align-middle justify-center font-semibold select-none disabled:cursor-not-allowed border rounded clr-btn-primary ${dimensions} ${loading ? ' cursor-progress' : ''}`}
|
className={`px-3 py-2 inline-flex items-center gap-2 align-middle justify-center font-semibold select-none disabled:cursor-not-allowed border clr-btn-primary ${dimensions} ${loading ? ' cursor-progress' : ''}`}
|
||||||
disabled={disabled ?? loading}
|
disabled={disabled ?? loading}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
|
|
|
@ -31,8 +31,7 @@ function TextArea({
|
||||||
required={required}
|
required={required}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TextArea;
|
export default TextArea;
|
||||||
|
|
|
@ -34,8 +34,7 @@ function TextInput({
|
||||||
className={`px-3 py-2 leading-tight truncate hover:text-clip ${colors} ${outlineClass} ${borderClass} ${dense ? 'w-full' : dimensions}`}
|
className={`px-3 py-2 leading-tight truncate hover:text-clip ${colors} ${outlineClass} ${borderClass} ${dense ? 'w-full' : dimensions}`}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TextInput;
|
export default TextInput;
|
||||||
|
|
|
@ -2,15 +2,33 @@ import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
interface TextURLProps {
|
interface TextURLProps {
|
||||||
text: string
|
text: string
|
||||||
href: string
|
tooltip?: string
|
||||||
|
href?: string
|
||||||
|
onClick?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function TextURL({ text, href }: TextURLProps) {
|
function TextURL({ text, href, tooltip, onClick }: TextURLProps) {
|
||||||
|
if (href) {
|
||||||
return (
|
return (
|
||||||
<Link className='hover:underline text-url' to={href}>
|
<Link
|
||||||
|
className='cursor-pointer hover:underline text-url'
|
||||||
|
title={tooltip}
|
||||||
|
to={href}
|
||||||
|
>
|
||||||
{text}
|
{text}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
} else if (onClick) {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className='cursor-pointer hover:underline text-url'
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</span>);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TextURL;
|
export default TextURL;
|
||||||
|
|
|
@ -64,8 +64,7 @@ function Tristate({
|
||||||
text={label}
|
text={label}
|
||||||
htmlFor={id}
|
htmlFor={id}
|
||||||
/> : null}
|
/> : null}
|
||||||
</button>
|
</button>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Tristate;
|
export default Tristate;
|
||||||
|
|
|
@ -11,8 +11,7 @@ function ToasterThemed(props: ToasterThemedProps) {
|
||||||
<ToastContainer
|
<ToastContainer
|
||||||
theme={ darkMode ? 'dark' : 'light'}
|
theme={ darkMode ? 'dark' : 'light'}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ToasterThemed;
|
export default ToasterThemed;
|
||||||
|
|
|
@ -73,7 +73,7 @@ export default function DataTable<TData extends RowData>({
|
||||||
paginationOptions=[10, 20, 30, 40, 50],
|
paginationOptions=[10, 20, 30, 40, 50],
|
||||||
onChangePaginationOption,
|
onChangePaginationOption,
|
||||||
|
|
||||||
...options
|
...restProps
|
||||||
}: DataTableProps<TData>) {
|
}: DataTableProps<TData>) {
|
||||||
const [sorting, setSorting] = useState<SortingState>(initialSorting ? [initialSorting] : []);
|
const [sorting, setSorting] = useState<SortingState>(initialSorting ? [initialSorting] : []);
|
||||||
|
|
||||||
|
@ -97,16 +97,16 @@ export default function DataTable<TData extends RowData>({
|
||||||
onPaginationChange: enablePagination ? setPagination : undefined,
|
onPaginationChange: enablePagination ? setPagination : undefined,
|
||||||
onSortingChange: enableSorting ? setSorting : undefined,
|
onSortingChange: enableSorting ? setSorting : undefined,
|
||||||
enableMultiRowSelection: enableRowSelection,
|
enableMultiRowSelection: enableRowSelection,
|
||||||
...options
|
...restProps
|
||||||
});
|
});
|
||||||
|
|
||||||
const isEmpty = tableImpl.getRowModel().rows.length === 0;
|
const isEmpty = tableImpl.getRowModel().rows.length === 0;
|
||||||
|
|
||||||
function getRowStyles(row: Row<TData>) {
|
function getRowStyles(row: Row<TData>) {
|
||||||
return {...conditionalRowStyles!
|
return ({...conditionalRowStyles!
|
||||||
.filter(item => item.when(row.original))
|
.filter(item => item.when(row.original))
|
||||||
.reduce((prev, item) => ({...prev, ...item.style}), {})
|
.reduce((prev, item) => ({...prev, ...item.style}), {})
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -46,7 +46,7 @@ function PaginationTools<TData>({ table, paginationOptions, onChangePaginationOp
|
||||||
>
|
>
|
||||||
<GotoPrevIcon />
|
<GotoPrevIcon />
|
||||||
</button>
|
</button>
|
||||||
<input type='text'
|
<input
|
||||||
title='Номер страницы. Выделите для ручного ввода'
|
title='Номер страницы. Выделите для ручного ввода'
|
||||||
className='w-6 text-center clr-app'
|
className='w-6 text-center clr-app'
|
||||||
value={table.getState().pagination.pageIndex + 1}
|
value={table.getState().pagination.pageIndex + 1}
|
||||||
|
|
|
@ -17,8 +17,7 @@ function Footer() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</footer >
|
</footer>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Footer;
|
export default Footer;
|
||||||
|
|
|
@ -14,8 +14,7 @@ function ConstituentaTooltip({ data, anchor }: ConstituentaTooltipProps) {
|
||||||
className='max-w-[25rem] min-w-[25rem]'
|
className='max-w-[25rem] min-w-[25rem]'
|
||||||
>
|
>
|
||||||
<InfoConstituenta data={data} />
|
<InfoConstituenta data={data} />
|
||||||
</ConceptTooltip>
|
</ConceptTooltip>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ConstituentaTooltip;
|
export default ConstituentaTooltip;
|
||||||
|
|
38
rsconcept/frontend/src/components/Help/HelpButton.tsx
Normal file
38
rsconcept/frontend/src/components/Help/HelpButton.tsx
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { HelpTopic } from '../../models/miscelanious';
|
||||||
|
import ConceptTooltip from '../Common/ConceptTooltip';
|
||||||
|
import TextURL from '../Common/TextURL';
|
||||||
|
import { HelpIcon } from '../Icons';
|
||||||
|
import InfoTopic from './InfoTopic';
|
||||||
|
|
||||||
|
interface HelpButtonProps {
|
||||||
|
topic: HelpTopic
|
||||||
|
offset?: number
|
||||||
|
dimensions?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function HelpButton({ topic, offset, dimensions }: HelpButtonProps) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
id={`help-${topic}`}
|
||||||
|
className='px-1 py-1'
|
||||||
|
>
|
||||||
|
<HelpIcon color='text-primary' size={5} />
|
||||||
|
</div>
|
||||||
|
<ConceptTooltip clickable
|
||||||
|
anchorSelect={`#help-${topic}`}
|
||||||
|
layer='z-modal-tooltip'
|
||||||
|
className={dimensions}
|
||||||
|
offset={offset}
|
||||||
|
>
|
||||||
|
<div className='relative'>
|
||||||
|
<div className='absolute right-0 text-sm top-[0.4rem]'>
|
||||||
|
<TextURL text='Справка...' href={`/manuals?topic=${topic}`} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<InfoTopic topic={topic} />
|
||||||
|
</ConceptTooltip>
|
||||||
|
</>);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HelpButton;
|
|
@ -2,7 +2,7 @@
|
||||||
function HelpTerminologyControl() {
|
function HelpTerminologyControl() {
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col gap-1'>
|
<div className='flex flex-col gap-1'>
|
||||||
<h1>Терминологизация: Контроль терминологии</h1>
|
<h1>Терминологизация</h1>
|
||||||
<p>Портал позволяет контролировать употребление терминов, привязанных к сущностям в концептуальных схемах.</p>
|
<p>Портал позволяет контролировать употребление терминов, привязанных к сущностям в концептуальных схемах.</p>
|
||||||
<p>Для этого используется механизм текстовых отсылок: <i>использование термина</i> и <i>связывание слов.</i></p>
|
<p>Для этого используется механизм текстовых отсылок: <i>использование термина</i> и <i>связывание слов.</i></p>
|
||||||
<p>При отсылке к термину указывается параметры словоформы так, обеспечивающие корректное согласование слов.</p>
|
<p>При отсылке к термину указывается параметры словоформы так, обеспечивающие корректное согласование слов.</p>
|
||||||
|
|
33
rsconcept/frontend/src/components/Help/InfoTopic.tsx
Normal file
33
rsconcept/frontend/src/components/Help/InfoTopic.tsx
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import { HelpTopic } from '../../models/miscelanious';
|
||||||
|
import HelpAPI from './HelpAPI';
|
||||||
|
import HelpConstituenta from './HelpConstituenta';
|
||||||
|
import HelpExteor from './HelpExteor';
|
||||||
|
import HelpLibrary from './HelpLibrary';
|
||||||
|
import HelpMain from './HelpMain';
|
||||||
|
import HelpRSFormItems from './HelpRSFormItems';
|
||||||
|
import HelpRSFormMeta from './HelpRSFormMeta';
|
||||||
|
import HelpRSLang from './HelpRSLang';
|
||||||
|
import HelpRSTemplates from './HelpRSTemplates';
|
||||||
|
import HelpTermGraph from './HelpTermGraph';
|
||||||
|
import HelpTerminologyControl from './HelpTerminologyControl';
|
||||||
|
|
||||||
|
interface InfoTopicProps {
|
||||||
|
topic: HelpTopic
|
||||||
|
}
|
||||||
|
|
||||||
|
function InfoTopic({ topic }: InfoTopicProps) {
|
||||||
|
if (topic === HelpTopic.MAIN) return <HelpMain />;
|
||||||
|
if (topic === HelpTopic.LIBRARY) return <HelpLibrary />;
|
||||||
|
if (topic === HelpTopic.RSFORM) return <HelpRSFormMeta />;
|
||||||
|
if (topic === HelpTopic.CSTLIST) return <HelpRSFormItems />;
|
||||||
|
if (topic === HelpTopic.CONSTITUENTA) return <HelpConstituenta />;
|
||||||
|
if (topic === HelpTopic.GRAPH_TERM) return <HelpTermGraph />;
|
||||||
|
if (topic === HelpTopic.RSTEMPLATES) return <HelpRSTemplates />;
|
||||||
|
if (topic === HelpTopic.RSLANG) return <HelpRSLang />;
|
||||||
|
if (topic === HelpTopic.TERM_CONTROL) return <HelpTerminologyControl />;
|
||||||
|
if (topic === HelpTopic.EXTEOR) return <HelpExteor />;
|
||||||
|
if (topic === HelpTopic.API) return <HelpAPI />;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InfoTopic;
|
|
@ -14,7 +14,7 @@ export interface IconProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function IconSVG({ viewbox, size = 6, color, props, children }: IconSVGProps) {
|
function IconSVG({ viewbox, size = 6, color, props, children }: IconSVGProps) {
|
||||||
const width = `${size * 1 / 4}rem`
|
const width = `${size * 1 / 4}rem`;
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
width={width}
|
width={width}
|
||||||
|
@ -25,8 +25,7 @@ function IconSVG({ viewbox, size = 6, color, props, children }: IconSVGProps) {
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</svg>
|
</svg>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MagnifyingGlassIcon({ size, ...props }: IconProps) {
|
export function MagnifyingGlassIcon({ size, ...props }: IconProps) {
|
||||||
|
|
|
@ -29,7 +29,6 @@ function Logo() {
|
||||||
className='max-h-[1.6rem] min-w-[2.2rem]'
|
className='max-h-[1.6rem] min-w-[2.2rem]'
|
||||||
|
|
||||||
/> : null}
|
/> : null}
|
||||||
</Link>
|
</Link>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
export default Logo;
|
export default Logo;
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
import { EducationIcon, LibraryIcon, PlusIcon } from '../Icons';
|
import { EducationIcon, LibraryIcon, PlusIcon } from '../Icons';
|
||||||
import Logo from './Logo'
|
import Logo from './Logo'
|
||||||
import NavigationButton from './NavigationButton';
|
import NavigationButton from './NavigationButton';
|
||||||
|
import ToggleNavigationButton from './ToggleNavigationButton';
|
||||||
import UserMenu from './UserMenu';
|
import UserMenu from './UserMenu';
|
||||||
|
|
||||||
function Navigation () {
|
function Navigation () {
|
||||||
|
@ -15,22 +16,7 @@ function Navigation () {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className='sticky top-0 left-0 right-0 select-none clr-app z-navigation h-fit'>
|
<nav className='sticky top-0 left-0 right-0 select-none clr-app z-navigation h-fit'>
|
||||||
{noNavigation ?
|
<ToggleNavigationButton noNavigation={noNavigation} toggleNoNavigation={toggleNoNavigation} />
|
||||||
<button type='button' tabIndex={-1}
|
|
||||||
title='Показать навигацию'
|
|
||||||
className='absolute top-0 right-0 z-navigation px-1 h-[1.6rem] border-b-2 border-l-2 clr-btn-nav rounded-none'
|
|
||||||
onClick={toggleNoNavigation}
|
|
||||||
>
|
|
||||||
{'∨∨∨'}
|
|
||||||
</button> : null}
|
|
||||||
{!noNavigation ?
|
|
||||||
<button type='button' tabIndex={-1}
|
|
||||||
title='Скрыть навигацию'
|
|
||||||
className='absolute top-0 right-0 z-navigation w-[1.2rem] h-[3rem] border-b-2 border-l-2 clr-btn-nav rounded-none'
|
|
||||||
onClick={toggleNoNavigation}
|
|
||||||
>
|
|
||||||
<p>{'>'}</p><p>{'>'}</p>
|
|
||||||
</button> : null}
|
|
||||||
{!noNavigation ?
|
{!noNavigation ?
|
||||||
<div className='flex items-stretch justify-between pl-2 pr-[0.8rem] border-b-2 rounded-none h-[3rem]'>
|
<div className='flex items-stretch justify-between pl-2 pr-[0.8rem] border-b-2 rounded-none h-[3rem]'>
|
||||||
<div className='flex items-center justify-start'>
|
<div className='flex items-center justify-start'>
|
||||||
|
@ -58,8 +44,7 @@ function Navigation () {
|
||||||
<UserMenu />
|
<UserMenu />
|
||||||
</div>
|
</div>
|
||||||
</div> : null}
|
</div> : null}
|
||||||
</nav>
|
</nav>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Navigation;
|
export default Navigation;
|
||||||
|
|
|
@ -15,8 +15,7 @@ function NavigationButton({ id, icon, description, onClick, text }: NavigationBu
|
||||||
>
|
>
|
||||||
{icon ? <span>{icon}</span> : null}
|
{icon ? <span>{icon}</span> : null}
|
||||||
{text ? <span className='font-semibold'>{text}</span> : null}
|
{text ? <span className='font-semibold'>{text}</span> : null}
|
||||||
</button>
|
</button>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NavigationButton;
|
export default NavigationButton;
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
interface ToggleNavigationButtonProps {
|
||||||
|
noNavigation?: boolean
|
||||||
|
toggleNoNavigation: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
function ToggleNavigationButton({ noNavigation, toggleNoNavigation }: ToggleNavigationButtonProps) {
|
||||||
|
if (noNavigation) {
|
||||||
|
return (
|
||||||
|
<button type='button' tabIndex={-1}
|
||||||
|
title='Показать навигацию'
|
||||||
|
className='absolute top-0 right-0 z-navigation px-1 h-[1.6rem] border-b-2 border-l-2 clr-btn-nav rounded-none'
|
||||||
|
onClick={toggleNoNavigation}
|
||||||
|
>
|
||||||
|
{'∨∨∨'}
|
||||||
|
</button>);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<button type='button' tabIndex={-1}
|
||||||
|
title='Скрыть навигацию'
|
||||||
|
className='absolute top-0 right-0 z-navigation w-[1.2rem] h-[3rem] border-b-2 border-l-2 clr-btn-nav rounded-none'
|
||||||
|
onClick={toggleNoNavigation}
|
||||||
|
>
|
||||||
|
<p>{'>'}</p><p>{'>'}</p>
|
||||||
|
</button>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ToggleNavigationButton;
|
|
@ -41,8 +41,7 @@ function UserDropdown({ hideDropdown }: UserDropdownProps) {
|
||||||
<DropdownButton onClick={logoutAndRedirect}>
|
<DropdownButton onClick={logoutAndRedirect}>
|
||||||
<b>Выйти...</b>
|
<b>Выйти...</b>
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
</Dropdown>
|
</Dropdown>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default UserDropdown;
|
export default UserDropdown;
|
||||||
|
|
|
@ -32,8 +32,7 @@ function UserMenu() {
|
||||||
<UserDropdown
|
<UserDropdown
|
||||||
hideDropdown={() => menu.hide()}
|
hideDropdown={() => menu.hide()}
|
||||||
/> : null}
|
/> : null}
|
||||||
</div>
|
</div>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default UserMenu;
|
export default UserMenu;
|
||||||
|
|
|
@ -118,12 +118,11 @@ function RSInput({
|
||||||
}, [thisRef]);
|
}, [thisRef]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`flex flex-col ${dimensions} ${cursor}`}>
|
<div className={`flex flex-col gap-2 ${dimensions} ${cursor}`}>
|
||||||
{label ?
|
{label ?
|
||||||
<Label
|
<Label
|
||||||
text={label}
|
text={label}
|
||||||
htmlFor={id}
|
htmlFor={id}
|
||||||
className='mb-2'
|
|
||||||
/> : null}
|
/> : null}
|
||||||
<CodeMirror id={id}
|
<CodeMirror id={id}
|
||||||
ref={thisRef}
|
ref={thisRef}
|
||||||
|
@ -136,8 +135,7 @@ function RSInput({
|
||||||
onKeyDown={handleInput}
|
onKeyDown={handleInput}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RSInput;
|
export default RSInput;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {LRLanguage} from '@codemirror/language'
|
import {LRLanguage} from '@codemirror/language';
|
||||||
|
|
||||||
import { parser } from './parser';
|
import { parser } from './parser';
|
||||||
import { Function, Global, Predicate } from './parser.terms';
|
import { Function, Global, Predicate } from './parser.terms';
|
||||||
|
|
|
@ -219,7 +219,6 @@ function RefsInput({
|
||||||
onKeyDown={handleInput}
|
onKeyDown={handleInput}
|
||||||
onFocus={handleFocusIn}
|
onFocus={handleFocusIn}
|
||||||
onBlur={handleFocusOut}
|
onBlur={handleFocusOut}
|
||||||
spellCheck
|
|
||||||
// spellCheck= // TODO: figure out while automatic spellcheck doesnt work or implement with extension
|
// spellCheck= // TODO: figure out while automatic spellcheck doesnt work or implement with extension
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {LRLanguage} from '@codemirror/language'
|
import {LRLanguage} from '@codemirror/language';
|
||||||
|
|
||||||
import { parser } from './parser';
|
import { parser } from './parser';
|
||||||
import { RefEntity, RefSyntactic } from './parser.terms';
|
import { RefEntity, RefSyntactic } from './parser.terms';
|
||||||
|
|
|
@ -13,7 +13,8 @@ interface ConstituentaBadgeProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function ConstituentaBadge({ value, prefixID, shortTooltip, theme }: ConstituentaBadgeProps) {
|
function ConstituentaBadge({ value, prefixID, shortTooltip, theme }: ConstituentaBadgeProps) {
|
||||||
return (<div className='w-fit'>
|
return (
|
||||||
|
<div className='w-fit'>
|
||||||
<div
|
<div
|
||||||
id={`${prefixID}${value.alias}`}
|
id={`${prefixID}${value.alias}`}
|
||||||
className='min-w-[3.1rem] max-w-[3.1rem] px-1 text-center rounded-md whitespace-nowrap'
|
className='min-w-[3.1rem] max-w-[3.1rem] px-1 text-center rounded-md whitespace-nowrap'
|
||||||
|
|
|
@ -35,8 +35,8 @@ function ConstituentaPicker({
|
||||||
onSelectValue
|
onSelectValue
|
||||||
} : ConstituentaPickerProps) {
|
} : ConstituentaPickerProps) {
|
||||||
const { colors } = useConceptTheme();
|
const { colors } = useConceptTheme();
|
||||||
const [ filteredData, setFilteredData ] = useState<IConstituenta[]>([]);
|
const [filteredData, setFilteredData] = useState<IConstituenta[]>([]);
|
||||||
const [ filterText, setFilterText ] = useState('');
|
const [filterText, setFilterText] = useState('');
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
() => {
|
() => {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import Overlay from '../Common/Overlay';
|
||||||
|
|
||||||
interface SelectedCounterProps {
|
interface SelectedCounterProps {
|
||||||
total: number
|
total: number
|
||||||
selected: number
|
selected: number
|
||||||
|
@ -13,11 +15,12 @@ function SelectedCounter({
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className='relative w-full z-pop'>
|
<Overlay
|
||||||
<div className={`absolute px-2 select-none whitespace-nowrap small-caps clr-app ${position}`}>
|
position={`px-2 ${position}`}
|
||||||
|
className='select-none whitespace-nowrap small-caps clr-app'
|
||||||
|
>
|
||||||
Выбор {selected} из {total}
|
Выбор {selected} из {total}
|
||||||
</div>
|
</Overlay>);
|
||||||
</div>);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SelectedCounter;
|
export default SelectedCounter;
|
|
@ -118,6 +118,5 @@ export const AuthState = ({ children }: AuthStateProps) => {
|
||||||
value={{ user, login, logout, signup, loading, error, setError, updatePassword }}
|
value={{ user, login, logout, signup, loading, error, setError, updatePassword }}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</AuthContext.Provider>
|
</AuthContext.Provider>);
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -43,14 +43,14 @@ interface LibraryStateProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LibraryState = ({ children }: LibraryStateProps) => {
|
export const LibraryState = ({ children }: LibraryStateProps) => {
|
||||||
const [ items, setItems ] = useState<ILibraryItem[]>([]);
|
|
||||||
const [ templates, setTemplates ] = useState<ILibraryItem[]>([]);
|
|
||||||
const [ loading, setLoading ] = useState(false);
|
|
||||||
const [ processing, setProcessing ] = useState(false);
|
|
||||||
const [ error, setError ] = useState<ErrorInfo>(undefined);
|
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
|
|
||||||
const [ cachedTemplates, setCachedTemplates ] = useState<IRSForm[]>([]);
|
const [items, setItems] = useState<ILibraryItem[]>([]);
|
||||||
|
const [templates, setTemplates] = useState<ILibraryItem[]>([]);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [processing, setProcessing] = useState(false);
|
||||||
|
const [error, setError] = useState<ErrorInfo>(undefined);
|
||||||
|
const [cachedTemplates, setCachedTemplates] = useState<IRSForm[]>([]);
|
||||||
|
|
||||||
const applyFilter = useCallback(
|
const applyFilter = useCallback(
|
||||||
(params: ILibraryFilter) => {
|
(params: ILibraryFilter) => {
|
||||||
|
@ -194,7 +194,6 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
||||||
applyFilter, createItem, cloneItem, destroyItem, retrieveTemplate,
|
applyFilter, createItem, cloneItem, destroyItem, retrieveTemplate,
|
||||||
localUpdateItem, localUpdateTimestamp
|
localUpdateItem, localUpdateTimestamp
|
||||||
}}>
|
}}>
|
||||||
{ children }
|
{children}
|
||||||
</LibraryContext.Provider>
|
</LibraryContext.Provider>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,5 @@ export const NavigationState = ({ children }: NavigationStateProps) => {
|
||||||
navigateTo, navigateHistory
|
navigateTo, navigateHistory
|
||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</NagivationContext.Provider>
|
</NagivationContext.Provider>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
import { createContext, useCallback, useContext, useMemo, useState } from 'react'
|
import { createContext, useCallback, useContext, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { type ErrorInfo } from '../components/BackendError'
|
import { type ErrorInfo } from '../components/BackendError';
|
||||||
import { useRSFormDetails } from '../hooks/useRSFormDetails'
|
import { useRSFormDetails } from '../hooks/useRSFormDetails';
|
||||||
import { ILibraryItem } from '../models/library'
|
import { ILibraryItem } from '../models/library';
|
||||||
import { ILibraryUpdateData } from '../models/library'
|
import { ILibraryUpdateData } from '../models/library';
|
||||||
import {
|
import {
|
||||||
IConstituentaList, IConstituentaMeta, ICstCreateData,
|
IConstituentaList, IConstituentaMeta, ICstCreateData,
|
||||||
ICstMovetoData, ICstRenameData, ICstUpdateData,
|
ICstMovetoData, ICstRenameData, ICstUpdateData,
|
||||||
IRSForm, IRSFormUploadData
|
IRSForm, IRSFormUploadData
|
||||||
} from '../models/rsform'
|
} from '../models/rsform';
|
||||||
import {
|
import {
|
||||||
type DataCallback, deleteUnsubscribe,
|
type DataCallback, deleteUnsubscribe,
|
||||||
getTRSFile,
|
getTRSFile,
|
||||||
patchConstituenta, patchDeleteConstituenta,
|
patchConstituenta, patchDeleteConstituenta,
|
||||||
patchLibraryItem,
|
patchLibraryItem,
|
||||||
patchMoveConstituenta, patchRenameConstituenta,
|
patchMoveConstituenta, patchRenameConstituenta,
|
||||||
patchResetAliases, patchUploadTRS, postClaimLibraryItem, postNewConstituenta, postSubscribe} from '../utils/backendAPI'
|
patchResetAliases, patchUploadTRS, postClaimLibraryItem, postNewConstituenta, postSubscribe} from '../utils/backendAPI';
|
||||||
import { useAuth } from './AuthContext'
|
import { useAuth } from './AuthContext';
|
||||||
import { useLibrary } from './LibraryContext'
|
import { useLibrary } from './LibraryContext';
|
||||||
|
|
||||||
interface IRSFormContext {
|
interface IRSFormContext {
|
||||||
schema?: IRSForm
|
schema?: IRSForm
|
||||||
|
@ -27,14 +27,14 @@ interface IRSFormContext {
|
||||||
processing: boolean
|
processing: boolean
|
||||||
|
|
||||||
isMutable: boolean
|
isMutable: boolean
|
||||||
adminMode: boolean
|
|
||||||
isOwned: boolean
|
isOwned: boolean
|
||||||
isClaimable: boolean
|
isClaimable: boolean
|
||||||
isReadonly: boolean
|
|
||||||
isTracking: boolean
|
isTracking: boolean
|
||||||
|
|
||||||
toggleForceAdmin: () => void
|
adminMode: boolean
|
||||||
toggleReadonly: () => void
|
toggleAdminMode: () => void
|
||||||
|
readerMode: boolean
|
||||||
|
toggleReaderMode: () => void
|
||||||
|
|
||||||
update: (data: ILibraryUpdateData, callback?: DataCallback<ILibraryItem>) => void
|
update: (data: ILibraryUpdateData, callback?: DataCallback<ILibraryItem>) => void
|
||||||
claim: (callback?: DataCallback<ILibraryItem>) => void
|
claim: (callback?: DataCallback<ILibraryItem>) => void
|
||||||
|
@ -56,11 +56,9 @@ const RSFormContext = createContext<IRSFormContext | null>(null)
|
||||||
export const useRSForm = () => {
|
export const useRSForm = () => {
|
||||||
const context = useContext(RSFormContext)
|
const context = useContext(RSFormContext)
|
||||||
if (context === null) {
|
if (context === null) {
|
||||||
throw new Error(
|
throw new Error('useRSForm has to be used within <RSFormState.Provider>');
|
||||||
'useRSForm has to be used within <RSFormState.Provider>'
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
return context
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RSFormStateProps {
|
interface RSFormStateProps {
|
||||||
|
@ -72,11 +70,11 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
||||||
const library = useLibrary();
|
const library = useLibrary();
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const { schema, reload, error, setError, setSchema, loading } = useRSFormDetails({ target: schemaID });
|
const { schema, reload, error, setError, setSchema, loading } = useRSFormDetails({ target: schemaID });
|
||||||
const [ processing, setProcessing ] = useState(false);
|
const [processing, setProcessing] = useState(false);
|
||||||
|
|
||||||
const [ adminMode, setAdminMode ] = useState(false);
|
const [adminMode, setAdminMode] = useState(false);
|
||||||
const [ isReadonly, setIsReadonly ] = useState(false);
|
const [readerMode, setReaderMode] = useState(false);
|
||||||
const [ toggleTracking, setToggleTracking ] = useState(false);
|
const [toggleTracking, setToggleTracking] = useState(false);
|
||||||
|
|
||||||
const isOwned = useMemo(
|
const isOwned = useMemo(
|
||||||
() => {
|
() => {
|
||||||
|
@ -91,10 +89,10 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
||||||
const isMutable = useMemo(
|
const isMutable = useMemo(
|
||||||
() => {
|
() => {
|
||||||
return (
|
return (
|
||||||
!loading && !processing && !isReadonly &&
|
!loading && !processing && !readerMode &&
|
||||||
((isOwned || (adminMode && user?.is_staff)) ?? false)
|
((isOwned || (adminMode && user?.is_staff)) ?? false)
|
||||||
);
|
);
|
||||||
}, [user?.is_staff, isReadonly, adminMode, isOwned, loading, processing]);
|
}, [user?.is_staff, readerMode, adminMode, isOwned, loading, processing]);
|
||||||
|
|
||||||
const isTracking = useMemo(
|
const isTracking = useMemo(
|
||||||
() => {
|
() => {
|
||||||
|
@ -322,14 +320,13 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
||||||
<RSFormContext.Provider value={{
|
<RSFormContext.Provider value={{
|
||||||
schema,
|
schema,
|
||||||
error, loading, processing,
|
error, loading, processing,
|
||||||
adminMode, isReadonly, isOwned, isMutable,
|
adminMode, readerMode, isOwned, isMutable,
|
||||||
isClaimable, isTracking,
|
isClaimable, isTracking,
|
||||||
toggleForceAdmin: () => setAdminMode(prev => !prev),
|
|
||||||
toggleReadonly: () => setIsReadonly(prev => !prev),
|
|
||||||
update, download, upload, claim, resetAliases, subscribe, unsubscribe,
|
update, download, upload, claim, resetAliases, subscribe, unsubscribe,
|
||||||
cstUpdate, cstCreate, cstRename, cstDelete, cstMoveTo
|
cstUpdate, cstCreate, cstRename, cstDelete, cstMoveTo,
|
||||||
|
toggleAdminMode: () => setAdminMode(prev => !prev),
|
||||||
|
toggleReaderMode: () => setReaderMode(prev => !prev)
|
||||||
}}>
|
}}>
|
||||||
{ children }
|
{ children }
|
||||||
</RSFormContext.Provider>
|
</RSFormContext.Provider>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,5 @@ export const ThemeState = ({ children }: ThemeStateProps) => {
|
||||||
viewportHeight, mainHeight
|
viewportHeight, mainHeight
|
||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</ThemeContext.Provider>
|
</ThemeContext.Provider>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ export const UserProfileState = ({ children }: UserProfileStateProps) => {
|
||||||
if (callback) callback(newData);
|
if (callback) callback(newData);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [setUser, users]);
|
}, [setUser, users, user?.id]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
reload();
|
reload();
|
||||||
|
|
|
@ -67,6 +67,5 @@ export const UsersState = ({ children }: UsersStateProps) => {
|
||||||
getUserLabel
|
getUserLabel
|
||||||
}}>
|
}}>
|
||||||
{ children }
|
{ children }
|
||||||
</UsersContext.Provider>
|
</UsersContext.Provider>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,11 @@ import { useLayoutEffect, useState } from 'react';
|
||||||
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
||||||
|
|
||||||
import ConceptTab from '../../components/Common/ConceptTab';
|
import ConceptTab from '../../components/Common/ConceptTab';
|
||||||
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
|
||||||
import Modal, { ModalProps } from '../../components/Common/Modal';
|
import Modal, { ModalProps } from '../../components/Common/Modal';
|
||||||
import HelpRSTemplates from '../../components/Help/HelpRSTemplates';
|
import Overlay from '../../components/Common/Overlay';
|
||||||
import { HelpIcon } from '../../components/Icons';
|
import HelpButton from '../../components/Help/HelpButton';
|
||||||
import usePartialUpdate from '../../hooks/usePartialUpdate';
|
import usePartialUpdate from '../../hooks/usePartialUpdate';
|
||||||
|
import { HelpTopic } from '../../models/miscelanious';
|
||||||
import { CstType, ICstCreateData, IRSForm } from '../../models/rsform';
|
import { CstType, ICstCreateData, IRSForm } from '../../models/rsform';
|
||||||
import { inferTemplatedType, substituteTemplateArgs } from '../../models/rslangAPI';
|
import { inferTemplatedType, substituteTemplateArgs } from '../../models/rslangAPI';
|
||||||
import { createAliasFor, validateCstAlias } from '../../utils/misc';
|
import { createAliasFor, validateCstAlias } from '../../utils/misc';
|
||||||
|
@ -115,15 +115,17 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
submitText='Создать'
|
submitText='Создать'
|
||||||
>
|
>
|
||||||
<div className='max-w-[40rem] min-w-[40rem] min-h-[35rem] px-2 mb-1'>
|
|
||||||
<Tabs defaultFocus forceRenderTabPanel
|
<Tabs defaultFocus forceRenderTabPanel
|
||||||
className='flex flex-col items-center'
|
className='flex flex-col items-center max-w-[40rem] min-w-[40rem] min-h-[35rem] mb-1'
|
||||||
selectedTabClassName='clr-selected'
|
selectedTabClassName='clr-selected'
|
||||||
selectedIndex={activeTab}
|
selectedIndex={activeTab}
|
||||||
onSelect={setActiveTab}
|
onSelect={setActiveTab}
|
||||||
>
|
>
|
||||||
<div className='flex gap-1 pl-6 mb-3'>
|
<Overlay position='top-0 left-[12.3rem]'>
|
||||||
<TabList className='flex border'>
|
<HelpButton topic={HelpTopic.RSTEMPLATES} dimensions='max-w-[35rem]' />
|
||||||
|
</Overlay>
|
||||||
|
|
||||||
|
<TabList className='flex mb-3 border'>
|
||||||
<ConceptTab
|
<ConceptTab
|
||||||
label='Шаблон'
|
label='Шаблон'
|
||||||
tooltip='Выбор шаблона выражения'
|
tooltip='Выбор шаблона выражения'
|
||||||
|
@ -141,18 +143,6 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
|
||||||
/>
|
/>
|
||||||
</TabList>
|
</TabList>
|
||||||
|
|
||||||
<div id='templates-help' className='px-1 py-1'>
|
|
||||||
<HelpIcon color='text-primary' size={5} />
|
|
||||||
</div>
|
|
||||||
<ConceptTooltip
|
|
||||||
anchorSelect='#templates-help'
|
|
||||||
className='max-w-[35rem] z-modal-tooltip'
|
|
||||||
offset={12}
|
|
||||||
>
|
|
||||||
<HelpRSTemplates />
|
|
||||||
</ConceptTooltip>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='w-full'>
|
<div className='w-full'>
|
||||||
<TabPanel style={{ display: activeTab === TabID.TEMPLATE ? '': 'none' }}>
|
<TabPanel style={{ display: activeTab === TabID.TEMPLATE ? '': 'none' }}>
|
||||||
<TemplateTab
|
<TemplateTab
|
||||||
|
@ -177,7 +167,6 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</div>
|
</div>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
|
||||||
</Modal>);
|
</Modal>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ interface TemplateTabProps {
|
||||||
|
|
||||||
function TemplateTab({ state, partialUpdate }: TemplateTabProps) {
|
function TemplateTab({ state, partialUpdate }: TemplateTabProps) {
|
||||||
const { templates, retrieveTemplate } = useLibrary();
|
const { templates, retrieveTemplate } = useLibrary();
|
||||||
const [ selectedSchema, setSelectedSchema ] = useState<IRSForm | undefined>(undefined);
|
const [selectedSchema, setSelectedSchema] = useState<IRSForm | undefined>(undefined);
|
||||||
|
|
||||||
const [filteredData, setFilteredData] = useState<IConstituenta[]>([]);
|
const [filteredData, setFilteredData] = useState<IConstituenta[]>([]);
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ extends Pick<ModalProps, 'hideWindow'> {
|
||||||
function DlgDeleteCst({ hideWindow, selected, onDelete }: DlgDeleteCstProps) {
|
function DlgDeleteCst({ hideWindow, selected, onDelete }: DlgDeleteCstProps) {
|
||||||
const { schema } = useRSForm();
|
const { schema } = useRSForm();
|
||||||
|
|
||||||
const [ expandOut, setExpandOut ] = useState(false);
|
const [expandOut, setExpandOut] = useState(false);
|
||||||
const expansion: number[] = useMemo(() => schema?.graph.expandOutputs(selected) ?? [], [selected, schema?.graph]);
|
const expansion: number[] = useMemo(() => schema?.graph.expandOutputs(selected) ?? [], [selected, schema?.graph]);
|
||||||
|
|
||||||
function handleSubmit() {
|
function handleSubmit() {
|
||||||
|
@ -28,11 +28,10 @@ function DlgDeleteCst({ hideWindow, selected, onDelete }: DlgDeleteCstProps) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal canSubmit
|
||||||
title='Удаление конституент'
|
title='Удаление конституент'
|
||||||
submitText={expandOut ? 'Удалить с зависимыми' : 'Удалить'}
|
submitText={expandOut ? 'Удалить с зависимыми' : 'Удалить'}
|
||||||
hideWindow={hideWindow}
|
hideWindow={hideWindow}
|
||||||
canSubmit={true}
|
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
>
|
>
|
||||||
<div className='max-w-[60vw] min-w-[30rem]'>
|
<div className='max-w-[60vw] min-w-[30rem]'>
|
||||||
|
|
|
@ -2,11 +2,11 @@ import { useState } from 'react';
|
||||||
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
||||||
|
|
||||||
import ConceptTab from '../../components/Common/ConceptTab';
|
import ConceptTab from '../../components/Common/ConceptTab';
|
||||||
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
|
||||||
import Modal from '../../components/Common/Modal';
|
import Modal from '../../components/Common/Modal';
|
||||||
import HelpTerminologyControl from '../../components/Help/HelpTerminologyControl';
|
import Overlay from '../../components/Common/Overlay';
|
||||||
import { HelpIcon } from '../../components/Icons';
|
import HelpButton from '../../components/Help/HelpButton';
|
||||||
import { ReferenceType } from '../../models/language';
|
import { ReferenceType } from '../../models/language';
|
||||||
|
import { HelpTopic } from '../../models/miscelanious';
|
||||||
import { IConstituenta } from '../../models/rsform';
|
import { IConstituenta } from '../../models/rsform';
|
||||||
import { labelReferenceType } from '../../utils/labels';
|
import { labelReferenceType } from '../../utils/labels';
|
||||||
import EntityTab from './EntityTab';
|
import EntityTab from './EntityTab';
|
||||||
|
@ -48,15 +48,17 @@ function DlgEditReference({ hideWindow, items, initial, onSave }: DlgEditReferen
|
||||||
canSubmit={isValid}
|
canSubmit={isValid}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
>
|
>
|
||||||
<div className='min-w-[40rem] max-w-[40rem] flex flex-col gap-3 mb-2 min-h-[34rem]'>
|
|
||||||
<Tabs defaultFocus
|
<Tabs defaultFocus
|
||||||
className='flex flex-col items-center'
|
className='flex flex-col items-center min-w-[40rem] max-w-[40rem] mb-2 min-h-[34rem]'
|
||||||
selectedTabClassName='clr-selected'
|
selectedTabClassName='clr-selected'
|
||||||
selectedIndex={activeTab}
|
selectedIndex={activeTab}
|
||||||
onSelect={setActiveTab}
|
onSelect={setActiveTab}
|
||||||
>
|
>
|
||||||
<div className='flex gap-1 pl-6 mb-3'>
|
<Overlay position='top-0 left-[12.2rem]'>
|
||||||
<TabList className='flex border'>
|
<HelpButton topic={HelpTopic.TERM_CONTROL} dimensions='max-w-[38rem]' offset={14} />
|
||||||
|
</Overlay>
|
||||||
|
|
||||||
|
<TabList className='flex mb-3 border'>
|
||||||
<ConceptTab
|
<ConceptTab
|
||||||
label={labelReferenceType(ReferenceType.ENTITY)}
|
label={labelReferenceType(ReferenceType.ENTITY)}
|
||||||
tooltip='Отсылка на термин в заданной словоформе'
|
tooltip='Отсылка на термин в заданной словоформе'
|
||||||
|
@ -69,18 +71,6 @@ function DlgEditReference({ hideWindow, items, initial, onSave }: DlgEditReferen
|
||||||
/>
|
/>
|
||||||
</TabList>
|
</TabList>
|
||||||
|
|
||||||
<div id='terminology-help' className='px-1 py-1'>
|
|
||||||
<HelpIcon color='text-primary' size={5} />
|
|
||||||
</div>
|
|
||||||
<ConceptTooltip
|
|
||||||
anchorSelect='#terminology-help'
|
|
||||||
className='max-w-[30rem] z-modal-tooltip'
|
|
||||||
offset={10}
|
|
||||||
>
|
|
||||||
<HelpTerminologyControl />
|
|
||||||
</ConceptTooltip>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='w-full'>
|
<div className='w-full'>
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<EntityTab
|
<EntityTab
|
||||||
|
@ -100,7 +90,6 @@ function DlgEditReference({ hideWindow, items, initial, onSave }: DlgEditReferen
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</div>
|
</div>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
|
||||||
</Modal>);
|
</Modal>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
import { useEffect, useLayoutEffect, useMemo, useState } from 'react';
|
import { useEffect, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import ConceptTooltip from '../components/Common/ConceptTooltip';
|
|
||||||
import MiniButton from '../components/Common/MiniButton';
|
import MiniButton from '../components/Common/MiniButton';
|
||||||
import Modal from '../components/Common/Modal';
|
import Modal from '../components/Common/Modal';
|
||||||
|
import Overlay from '../components/Common/Overlay';
|
||||||
import SelectMulti from '../components/Common/SelectMulti';
|
import SelectMulti from '../components/Common/SelectMulti';
|
||||||
import TextArea from '../components/Common/TextArea';
|
import TextArea from '../components/Common/TextArea';
|
||||||
import DataTable, { createColumnHelper } from '../components/DataTable';
|
import DataTable, { createColumnHelper } from '../components/DataTable';
|
||||||
import HelpTerminologyControl from '../components/Help/HelpTerminologyControl';
|
import HelpButton from '../components/Help/HelpButton';
|
||||||
import { ArrowLeftIcon, ArrowRightIcon, CheckIcon, ChevronDoubleDownIcon, CrossIcon, HelpIcon } from '../components/Icons';
|
import { ArrowLeftIcon, ArrowRightIcon, CheckIcon, ChevronDoubleDownIcon, CrossIcon } from '../components/Icons';
|
||||||
import { useConceptTheme } from '../context/ThemeContext';
|
import { useConceptTheme } from '../context/ThemeContext';
|
||||||
import useConceptText from '../hooks/useConceptText';
|
import useConceptText from '../hooks/useConceptText';
|
||||||
import { Grammeme, ITextRequest, IWordForm, IWordFormPlain } from '../models/language';
|
import { Grammeme, ITextRequest, IWordForm, IWordFormPlain } from '../models/language';
|
||||||
import { getCompatibleGrams, parseGrammemes,wordFormEquals } from '../models/languageAPI';
|
import { getCompatibleGrams, parseGrammemes,wordFormEquals } from '../models/languageAPI';
|
||||||
|
import { HelpTopic } from '../models/miscelanious';
|
||||||
import { IConstituenta, TermForm } from '../models/rsform';
|
import { IConstituenta, TermForm } from '../models/rsform';
|
||||||
import { colorfgGrammeme } from '../utils/color';
|
import { colorfgGrammeme } from '../utils/color';
|
||||||
import { labelGrammeme } from '../utils/labels';
|
import { labelGrammeme } from '../utils/labels';
|
||||||
|
@ -204,37 +205,22 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
|
||||||
], [colors]);
|
], [colors]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal canSubmit
|
||||||
title='Редактирование словоформ'
|
title='Редактирование словоформ'
|
||||||
hideWindow={hideWindow}
|
hideWindow={hideWindow}
|
||||||
submitText='Сохранить'
|
submitText='Сохранить'
|
||||||
canSubmit
|
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
>
|
>
|
||||||
<div className='relative w-full'>
|
|
||||||
<div className='absolute top-0 right-0'>
|
|
||||||
<div id='terminology-help' className='px-1 py-1'>
|
|
||||||
<HelpIcon color='text-primary' size={5} />
|
|
||||||
</div>
|
|
||||||
<ConceptTooltip
|
|
||||||
anchorSelect='#terminology-help'
|
|
||||||
className='max-w-[40rem]'
|
|
||||||
layer='z-modal-tooltip'
|
|
||||||
offset={1}
|
|
||||||
>
|
|
||||||
<HelpTerminologyControl />
|
|
||||||
</ConceptTooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='min-w-[40rem] max-w-[40rem]'>
|
<div className='min-w-[40rem] max-w-[40rem]'>
|
||||||
<TextArea id='nominal' label='Начальная форма'
|
<Overlay position='top-[-0.2rem] left-[7.5rem]'>
|
||||||
|
<HelpButton topic={HelpTopic.TERM_CONTROL} dimensions='max-w-[38rem]' offset={3} />
|
||||||
|
</Overlay>
|
||||||
|
|
||||||
|
<TextArea disabled spellCheck
|
||||||
|
label='Начальная форма'
|
||||||
placeholder='Начальная форма'
|
placeholder='Начальная форма'
|
||||||
rows={1}
|
rows={1}
|
||||||
|
|
||||||
value={term}
|
value={term}
|
||||||
disabled={true}
|
|
||||||
spellCheck
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className='mt-3 mb-2 text-sm font-semibold'>
|
<div className='mt-3 mb-2 text-sm font-semibold'>
|
||||||
|
@ -245,8 +231,8 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
|
||||||
<div className='flex items-center'>
|
<div className='flex items-center'>
|
||||||
<TextArea
|
<TextArea
|
||||||
placeholder='Введите текст'
|
placeholder='Введите текст'
|
||||||
rows={2}
|
|
||||||
dimensions='min-w-[18rem] w-full min-h-[4.2rem]'
|
dimensions='min-w-[18rem] w-full min-h-[4.2rem]'
|
||||||
|
rows={2}
|
||||||
value={inputText}
|
value={inputText}
|
||||||
onChange={event => setInputText(event.target.value)}
|
onChange={event => setInputText(event.target.value)}
|
||||||
/>
|
/>
|
||||||
|
@ -289,7 +275,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
|
||||||
onClick={handleAddForm}
|
onClick={handleAddForm}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Генерировать все словоформы'
|
tooltip='Генерировать стандартные словоформы'
|
||||||
icon={<ChevronDoubleDownIcon
|
icon={<ChevronDoubleDownIcon
|
||||||
size={5}
|
size={5}
|
||||||
color={!inputText ? 'text-disabled' : 'text-primary'}
|
color={!inputText ? 'text-disabled' : 'text-primary'}
|
||||||
|
@ -302,7 +288,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
|
||||||
Заданные вручную словоформы [{forms.length}]
|
Заданные вручную словоформы [{forms.length}]
|
||||||
</div>
|
</div>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Сбросить ВСЕ словоформы'
|
tooltip='Сбросить все словоформы'
|
||||||
icon={<CrossIcon size={5} color={forms.length === 0 ? 'text-disabled' : 'text-warning'} />}
|
icon={<CrossIcon size={5} color={forms.length === 0 ? 'text-disabled' : 'text-warning'} />}
|
||||||
disabled={textProcessor.loading || forms.length === 0}
|
disabled={textProcessor.loading || forms.length === 0}
|
||||||
onClick={handleResetAll}
|
onClick={handleResetAll}
|
||||||
|
|
|
@ -48,8 +48,7 @@ function DlgRenameCst({ hideWindow, initial, onRename }: DlgRenameCstProps) {
|
||||||
canSubmit={validated}
|
canSubmit={validated}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
>
|
>
|
||||||
<div className='flex justify-center w-full min-w-[22rem] my-3'>
|
<div className='flex justify-center items-center gap-6 w-full min-w-[22rem] my-3'>
|
||||||
<div className='flex items-center gap-6'>
|
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
placeholder='Выберите тип'
|
placeholder='Выберите тип'
|
||||||
className='min-w-[14rem] self-center'
|
className='min-w-[14rem] self-center'
|
||||||
|
@ -61,14 +60,14 @@ 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>
|
<div>
|
||||||
<TextInput id='alias' label='Имя' dense
|
<TextInput dense
|
||||||
|
label='Имя'
|
||||||
dimensions='w-[7rem]'
|
dimensions='w-[7rem]'
|
||||||
value={cstData.alias}
|
value={cstData.alias}
|
||||||
onChange={event => updateData({alias: event.target.value})}
|
onChange={event => updateData({alias: event.target.value})}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</Modal>);
|
</Modal>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,8 +57,7 @@ function DlgShowAST({ hideWindow, syntaxTree, expression }: DlgShowASTProps) {
|
||||||
hideWindow={hideWindow}
|
hideWindow={hideWindow}
|
||||||
readonly
|
readonly
|
||||||
>
|
>
|
||||||
<div className='flex flex-col items-start gap-2 mt-2'>
|
<div className='w-full my-2 text-lg text-center'>
|
||||||
<div className='w-full text-lg text-center'>
|
|
||||||
{!hoverNode ? expression : null}
|
{!hoverNode ? expression : null}
|
||||||
{hoverNode ?
|
{hoverNode ?
|
||||||
<div>
|
<div>
|
||||||
|
@ -86,7 +85,6 @@ function DlgShowAST({ hideWindow, syntaxTree, expression }: DlgShowASTProps) {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</Modal>);
|
</Modal>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { useCallback, useState } from 'react'
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
import { type ErrorInfo } from '../components/BackendError';
|
import { type ErrorInfo } from '../components/BackendError';
|
||||||
import { CstType, IConstituenta, type IRSForm } from '../models/rsform';
|
import { CstType, IConstituenta, type IRSForm } from '../models/rsform';
|
||||||
import { IExpressionParse, IArgumentInfo } from '../models/rslang';
|
import { IArgumentInfo,IExpressionParse } from '../models/rslang';
|
||||||
import { RSErrorType } from '../models/rslang';
|
import { RSErrorType } from '../models/rslang';
|
||||||
import { DataCallback, postCheckExpression } from '../utils/backendAPI';
|
import { DataCallback, postCheckExpression } from '../utils/backendAPI';
|
||||||
import { getCstExpressionPrefix } from '../utils/misc';
|
import { getCstExpressionPrefix } from '../utils/misc';
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useCallback, useState } from 'react'
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
import { ErrorInfo } from '../components/BackendError';
|
import { ErrorInfo } from '../components/BackendError';
|
||||||
import { ILexemeData, ITextRequest, ITextResult, IWordFormPlain } from '../models/language';
|
import { ILexemeData, ITextRequest, ITextResult, IWordFormPlain } from '../models/language';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { type ErrorInfo } from '../components/BackendError';
|
import { type ErrorInfo } from '../components/BackendError';
|
||||||
import { IRSForm, IRSFormData } from '../models/rsform'
|
import { IRSForm, IRSFormData } from '../models/rsform';
|
||||||
import { loadRSFormData } from '../models/rsformAPI';
|
import { loadRSFormData } from '../models/rsformAPI';
|
||||||
import { getRSFormDetails } from '../utils/backendAPI';
|
import { getRSFormDetails } from '../utils/backendAPI';
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useCallback, useState } from 'react'
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
import { ErrorInfo } from '../components/BackendError';
|
import { ErrorInfo } from '../components/BackendError';
|
||||||
import { IResolutionData } from '../models/language';
|
import { IResolutionData } from '../models/language';
|
||||||
|
|
|
@ -100,42 +100,42 @@ export const Case = [
|
||||||
*
|
*
|
||||||
* Implemented as a list of mututally exclusive {@link Grammeme}s.
|
* Implemented as a list of mututally exclusive {@link Grammeme}s.
|
||||||
*/
|
*/
|
||||||
export const Plurality = [ Grammeme.sing, Grammeme.plur ];
|
export const Plurality = [Grammeme.sing, Grammeme.plur];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents verb perfectivity language concept.
|
* Represents verb perfectivity language concept.
|
||||||
*
|
*
|
||||||
* Implemented as a list of mututally exclusive {@link Grammeme}s.
|
* Implemented as a list of mututally exclusive {@link Grammeme}s.
|
||||||
*/
|
*/
|
||||||
export const Perfectivity = [ Grammeme.perf, Grammeme.impf ];
|
export const Perfectivity = [Grammeme.perf, Grammeme.impf];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents verb transitivity language concept.
|
* Represents verb transitivity language concept.
|
||||||
*
|
*
|
||||||
* Implemented as a list of mututally exclusive {@link Grammeme}s.
|
* Implemented as a list of mututally exclusive {@link Grammeme}s.
|
||||||
*/
|
*/
|
||||||
export const Transitivity = [ Grammeme.tran, Grammeme.intr ];
|
export const Transitivity = [Grammeme.tran, Grammeme.intr];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents verb mood language concept.
|
* Represents verb mood language concept.
|
||||||
*
|
*
|
||||||
* Implemented as a list of mututally exclusive {@link Grammeme}s.
|
* Implemented as a list of mututally exclusive {@link Grammeme}s.
|
||||||
*/
|
*/
|
||||||
export const Mood = [ Grammeme.indc, Grammeme.impr ];
|
export const Mood = [Grammeme.indc, Grammeme.impr];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents verb self-inclusion language concept.
|
* Represents verb self-inclusion language concept.
|
||||||
*
|
*
|
||||||
* Implemented as a list of mututally exclusive {@link Grammeme}s.
|
* Implemented as a list of mututally exclusive {@link Grammeme}s.
|
||||||
*/
|
*/
|
||||||
export const Inclusion = [ Grammeme.incl, Grammeme.excl ];
|
export const Inclusion = [Grammeme.incl, Grammeme.excl];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents verb voice language concept.
|
* Represents verb voice language concept.
|
||||||
*
|
*
|
||||||
* Implemented as a list of mututally exclusive {@link Grammeme}s.
|
* Implemented as a list of mututally exclusive {@link Grammeme}s.
|
||||||
*/
|
*/
|
||||||
export const Voice = [ Grammeme.actv, Grammeme.pssv ];
|
export const Voice = [Grammeme.actv, Grammeme.pssv];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents verb tense language concept.
|
* Represents verb tense language concept.
|
||||||
|
|
|
@ -8,6 +8,7 @@ import Checkbox from '../components/Common/Checkbox';
|
||||||
import Form from '../components/Common/Form';
|
import Form from '../components/Common/Form';
|
||||||
import Label from '../components/Common/Label';
|
import Label from '../components/Common/Label';
|
||||||
import MiniButton from '../components/Common/MiniButton';
|
import MiniButton from '../components/Common/MiniButton';
|
||||||
|
import Overlay from '../components/Common/Overlay';
|
||||||
import SubmitButton from '../components/Common/SubmitButton';
|
import SubmitButton from '../components/Common/SubmitButton';
|
||||||
import TextArea from '../components/Common/TextArea';
|
import TextArea from '../components/Common/TextArea';
|
||||||
import TextInput from '../components/Common/TextInput';
|
import TextInput from '../components/Common/TextInput';
|
||||||
|
@ -78,13 +79,12 @@ function CreateRSFormPage() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RequireAuth>
|
<RequireAuth>
|
||||||
<div className='flex justify-center w-full'>
|
<div className='flex justify-center'>
|
||||||
<Form title='Создание концептуальной схемы'
|
<Form title='Создание концептуальной схемы'
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
dimensions='max-w-lg w-full mt-4'
|
dimensions='max-w-lg w-full mt-4'
|
||||||
>
|
>
|
||||||
<div className='relative w-full'>
|
<Overlay position='top-[-2.4rem] right-[-1rem]'>
|
||||||
<div className='absolute top-[-2.4rem] right-[-1rem] flex'>
|
|
||||||
<input ref={inputRef} type='file'
|
<input ref={inputRef} type='file'
|
||||||
style={{ display: 'none' }}
|
style={{ display: 'none' }}
|
||||||
accept={EXTEOR_TRS_FILE}
|
accept={EXTEOR_TRS_FILE}
|
||||||
|
@ -95,10 +95,9 @@ function CreateRSFormPage() {
|
||||||
icon={<DownloadIcon size={5} color='text-primary'/>}
|
icon={<DownloadIcon size={5} color='text-primary'/>}
|
||||||
onClick={() => inputRef.current?.click()}
|
onClick={() => inputRef.current?.click()}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Overlay>
|
||||||
</div>
|
|
||||||
<div className='flex flex-col gap-3'>
|
|
||||||
{fileName ? <Label text={`Загружен файл: ${fileName}`} /> : null}
|
{fileName ? <Label text={`Загружен файл: ${fileName}`} /> : null}
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
label='Полное название'
|
label='Полное название'
|
||||||
placeholder={file && 'Загрузить из файла'}
|
placeholder={file && 'Загрузить из файла'}
|
||||||
|
@ -137,7 +136,6 @@ function CreateRSFormPage() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{error ? <BackendError error={error} /> : null}
|
{error ? <BackendError error={error} /> : null}
|
||||||
</div>
|
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
</RequireAuth>);
|
</RequireAuth>);
|
||||||
|
|
31
rsconcept/frontend/src/pages/LibraryPage/ItemIcons.tsx
Normal file
31
rsconcept/frontend/src/pages/LibraryPage/ItemIcons.tsx
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { EducationIcon, GroupIcon, SubscribedIcon } from '../../components/Icons';
|
||||||
|
import { ICurrentUser, ILibraryItem } from '../../models/library';
|
||||||
|
import { prefixes } from '../../utils/constants';
|
||||||
|
|
||||||
|
interface ItemIconsProps {
|
||||||
|
user?: ICurrentUser
|
||||||
|
item: ILibraryItem
|
||||||
|
}
|
||||||
|
|
||||||
|
function ItemIcons({ user, item }: ItemIconsProps) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className='inline-flex items-center justify-start gap-1 min-w-[2.75rem]'
|
||||||
|
id={`${prefixes.library_list}${item.id}`}
|
||||||
|
>
|
||||||
|
{(user && user.subscriptions.includes(item.id)) ?
|
||||||
|
<span title='Отслеживаемая'>
|
||||||
|
<SubscribedIcon size={3} />
|
||||||
|
</span> : null}
|
||||||
|
{item.is_common ?
|
||||||
|
<span title='Общедоступная'>
|
||||||
|
<GroupIcon size={3}/>
|
||||||
|
</span> : null}
|
||||||
|
{item.is_canonical ?
|
||||||
|
<span title='Неизменная'>
|
||||||
|
<EducationIcon size={3}/>
|
||||||
|
</span> : null}
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ItemIcons;
|
|
@ -1,7 +1,7 @@
|
||||||
import { useCallback, useLayoutEffect, useState } from 'react';
|
import { useCallback, useLayoutEffect, useState } from 'react';
|
||||||
|
|
||||||
import BackendError from '../../components/BackendError'
|
import BackendError from '../../components/BackendError';
|
||||||
import { ConceptLoader } from '../../components/Common/ConceptLoader'
|
import { ConceptLoader } from '../../components/Common/ConceptLoader';
|
||||||
import { useLibrary } from '../../context/LibraryContext';
|
import { useLibrary } from '../../context/LibraryContext';
|
||||||
import { useConceptTheme } from '../../context/ThemeContext';
|
import { useConceptTheme } from '../../context/ThemeContext';
|
||||||
import useLocalStorage from '../../hooks/useLocalStorage';
|
import useLocalStorage from '../../hooks/useLocalStorage';
|
||||||
|
@ -14,8 +14,8 @@ function LibraryPage() {
|
||||||
const library = useLibrary();
|
const library = useLibrary();
|
||||||
const { setShowScroll } = useConceptTheme();
|
const { setShowScroll } = useConceptTheme();
|
||||||
|
|
||||||
const [ filter, setFilter ] = useState<ILibraryFilter>({});
|
const [filter, setFilter] = useState<ILibraryFilter>({});
|
||||||
const [ items, setItems ] = useState<ILibraryItem[]>([]);
|
const [items, setItems] = useState<ILibraryItem[]>([]);
|
||||||
|
|
||||||
const [query, setQuery] = useState('');
|
const [query, setQuery] = useState('');
|
||||||
const [strategy, setStrategy] = useLocalStorage<LibraryFilterStrategy>('search_strategy', LibraryFilterStrategy.MANUAL);
|
const [strategy, setStrategy] = useLocalStorage<LibraryFilterStrategy>('search_strategy', LibraryFilterStrategy.MANUAL);
|
||||||
|
@ -42,7 +42,7 @@ function LibraryPage() {
|
||||||
{library.loading ? <ConceptLoader/> : null}
|
{library.loading ? <ConceptLoader/> : null}
|
||||||
{library.error ? <BackendError error={library.error}/> : null}
|
{library.error ? <BackendError error={library.error}/> : null}
|
||||||
{(!library.loading && library.items) ?
|
{(!library.loading && library.items) ?
|
||||||
<div className='flex flex-col w-full'>
|
<>
|
||||||
<SearchPanel
|
<SearchPanel
|
||||||
query={query}
|
query={query}
|
||||||
setQuery={setQuery}
|
setQuery={setQuery}
|
||||||
|
@ -56,7 +56,7 @@ function LibraryPage() {
|
||||||
resetQuery={resetQuery}
|
resetQuery={resetQuery}
|
||||||
items={items}
|
items={items}
|
||||||
/>
|
/>
|
||||||
</div> : null}
|
</> : null}
|
||||||
</>);
|
</>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
|
||||||
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
|
||||||
import TextURL from '../../components/Common/TextURL';
|
import TextURL from '../../components/Common/TextURL';
|
||||||
import DataTable, { createColumnHelper } from '../../components/DataTable';
|
import DataTable, { createColumnHelper } from '../../components/DataTable';
|
||||||
import HelpLibrary from '../../components/Help/HelpLibrary';
|
import HelpButton from '../../components/Help/HelpButton';
|
||||||
import { EducationIcon, GroupIcon, HelpIcon,SubscribedIcon } from '../../components/Icons';
|
|
||||||
import { useAuth } from '../../context/AuthContext';
|
import { useAuth } from '../../context/AuthContext';
|
||||||
import { useConceptNavigation } from '../../context/NagivationContext';
|
import { useConceptNavigation } from '../../context/NagivationContext';
|
||||||
import { useUsers } from '../../context/UsersContext';
|
import { useUsers } from '../../context/UsersContext';
|
||||||
import useLocalStorage from '../../hooks/useLocalStorage';
|
import useLocalStorage from '../../hooks/useLocalStorage';
|
||||||
import { ILibraryItem } from '../../models/library';
|
import { ILibraryItem } from '../../models/library';
|
||||||
import { prefixes } from '../../utils/constants';
|
import { HelpTopic } from '../../models/miscelanious';
|
||||||
|
import ItemIcons from './ItemIcons';
|
||||||
|
|
||||||
interface ViewLibraryProps {
|
interface ViewLibraryProps {
|
||||||
items: ILibraryItem[]
|
items: ILibraryItem[]
|
||||||
|
@ -25,10 +24,9 @@ function ViewLibrary({ items, resetQuery: cleanQuery }: ViewLibraryProps) {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const { getUserLabel } = useUsers();
|
const { getUserLabel } = useUsers();
|
||||||
|
const [itemsPerPage, setItemsPerPage] = useLocalStorage<number>('library_per_page', 50);
|
||||||
|
|
||||||
const [ itemsPerPage, setItemsPerPage ] = useLocalStorage<number>('library_per_page', 50);
|
const handleOpenItem = (item: ILibraryItem) => navigateTo(`/rsforms/${item.id}`);
|
||||||
|
|
||||||
const openRSForm = (item: ILibraryItem) => navigateTo(`/rsforms/${item.id}`);
|
|
||||||
|
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() => [
|
() => [
|
||||||
|
@ -38,28 +36,11 @@ function ViewLibrary({ items, resetQuery: cleanQuery }: ViewLibraryProps) {
|
||||||
size: 60,
|
size: 60,
|
||||||
minSize: 60,
|
minSize: 60,
|
||||||
maxSize: 60,
|
maxSize: 60,
|
||||||
cell: props => {
|
cell: props =>
|
||||||
const item = props.row.original;
|
<ItemIcons
|
||||||
return (<>
|
item={props.row.original}
|
||||||
<div
|
user={user}
|
||||||
className='flex items-center justify-start gap-1 min-w-[2.75rem]'
|
/>,
|
||||||
id={`${prefixes.library_list}${item.id}`}
|
|
||||||
>
|
|
||||||
{(user && user.subscriptions.includes(item.id)) ?
|
|
||||||
<p title='Отслеживаемая'>
|
|
||||||
<SubscribedIcon size={3}/>
|
|
||||||
</p> : null}
|
|
||||||
{item.is_common ?
|
|
||||||
<p title='Общедоступная'>
|
|
||||||
<GroupIcon size={3}/>
|
|
||||||
</p> : null}
|
|
||||||
{item.is_canonical ?
|
|
||||||
<p title='Неизменная'>
|
|
||||||
<EducationIcon size={3}/>
|
|
||||||
</p> : null}
|
|
||||||
</div>
|
|
||||||
</>);
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor('alias', {
|
columnHelper.accessor('alias', {
|
||||||
id: 'alias',
|
id: 'alias',
|
||||||
|
@ -95,7 +76,10 @@ function ViewLibrary({ items, resetQuery: cleanQuery }: ViewLibraryProps) {
|
||||||
size: 150,
|
size: 150,
|
||||||
minSize: 150,
|
minSize: 150,
|
||||||
maxSize: 150,
|
maxSize: 150,
|
||||||
cell: props => <div className='text-sm min-w-[8.25rem]'>{new Date(props.cell.getValue()).toLocaleString(intl.locale)}</div>,
|
cell: props =>
|
||||||
|
<div className='text-sm min-w-[8.25rem]'>
|
||||||
|
{new Date(props.cell.getValue()).toLocaleString(intl.locale)}
|
||||||
|
</div>,
|
||||||
enableSorting: true,
|
enableSorting: true,
|
||||||
sortingFn: 'datetime',
|
sortingFn: 'datetime',
|
||||||
sortDescFirst: true
|
sortDescFirst: true
|
||||||
|
@ -104,36 +88,30 @@ function ViewLibrary({ items, resetQuery: cleanQuery }: ViewLibraryProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{items.length !== 0 ?
|
|
||||||
<div className='sticky top-[2.3rem] w-full'>
|
<div className='sticky top-[2.3rem] w-full'>
|
||||||
<div className='absolute top-[-0.125rem] left-0 flex gap-1 ml-3 z-pop'>
|
<div className='absolute top-[0.125rem] left-[0.25rem] flex gap-1 ml-3 z-pop'>
|
||||||
<div id='library-help' className='py-2 '>
|
<HelpButton
|
||||||
<HelpIcon color='text-primary' size={5} />
|
topic={HelpTopic.LIBRARY}
|
||||||
|
dimensions='max-w-[35rem]'
|
||||||
|
offset={0}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<ConceptTooltip anchorSelect='#library-help'>
|
|
||||||
<div className='max-w-[35rem]'>
|
|
||||||
<HelpLibrary />
|
|
||||||
</div>
|
</div>
|
||||||
</ConceptTooltip>
|
|
||||||
</div>
|
|
||||||
</div> : null}
|
|
||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={items}
|
data={items}
|
||||||
|
|
||||||
headPosition='2.3rem'
|
headPosition='2.3rem'
|
||||||
noDataComponent={
|
noDataComponent={
|
||||||
<div className='flex flex-col gap-4 justify-center p-2 text-center min-h-[6rem]'>
|
<div className='p-3 text-center min-h-[6rem]'>
|
||||||
<p>Список схем пуст</p>
|
<p>Список схем пуст</p>
|
||||||
<p className='flex justify-center gap-4'>
|
<p className='flex justify-center gap-6 mt-3'>
|
||||||
<TextURL text='Создать схему' href='/rsform-create'/>
|
<TextURL text='Создать схему' href='/rsform-create'/>
|
||||||
<span className='cursor-pointer hover:underline text-url' onClick={cleanQuery}>
|
<TextURL text='Очистить фильтр' onClick={cleanQuery} />
|
||||||
Очистить фильтр
|
|
||||||
</span>
|
|
||||||
</p>
|
</p>
|
||||||
</div>}
|
</div>}
|
||||||
|
|
||||||
onRowClicked={openRSForm}
|
onRowClicked={handleOpenItem}
|
||||||
|
|
||||||
enableSorting
|
enableSorting
|
||||||
initialSorting={{
|
initialSorting={{
|
||||||
|
|
|
@ -23,8 +23,7 @@ function TopicsList({ activeTopic, onChangeTopic }: TopicsListProps) {
|
||||||
{labelHelpTopic(topic)}
|
{labelHelpTopic(topic)}
|
||||||
</div>);
|
</div>);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TopicsList;
|
export default TopicsList;
|
||||||
|
|
|
@ -1,14 +1,4 @@
|
||||||
import HelpAPI from '../../components/Help/HelpAPI';
|
import InfoTopic from '../../components/Help/InfoTopic';
|
||||||
import HelpConstituenta from '../../components/Help/HelpConstituenta';
|
|
||||||
import HelpExteor from '../../components/Help/HelpExteor';
|
|
||||||
import HelpLibrary from '../../components/Help/HelpLibrary';
|
|
||||||
import HelpMain from '../../components/Help/HelpMain';
|
|
||||||
import HelpRSFormItems from '../../components/Help/HelpRSFormItems';
|
|
||||||
import HelpRSFormMeta from '../../components/Help/HelpRSFormMeta';
|
|
||||||
import HelpRSLang from '../../components/Help/HelpRSLang';
|
|
||||||
import HelpRSTemplates from '../../components/Help/HelpRSTemplates';
|
|
||||||
import HelpTermGraph from '../../components/Help/HelpTermGraph';
|
|
||||||
import HelpTerminologyControl from '../../components/Help/HelpTerminologyControl';
|
|
||||||
import { HelpTopic } from '../../models/miscelanious';
|
import { HelpTopic } from '../../models/miscelanious';
|
||||||
|
|
||||||
interface ViewTopicProps {
|
interface ViewTopicProps {
|
||||||
|
@ -18,17 +8,7 @@ interface ViewTopicProps {
|
||||||
function ViewTopic({ topic }: ViewTopicProps) {
|
function ViewTopic({ topic }: ViewTopicProps) {
|
||||||
return (
|
return (
|
||||||
<div className='w-full px-2 py-2'>
|
<div className='w-full px-2 py-2'>
|
||||||
{topic === HelpTopic.MAIN ? <HelpMain /> : null}
|
<InfoTopic topic={topic}/>
|
||||||
{topic === HelpTopic.LIBRARY ? <HelpLibrary /> : null}
|
|
||||||
{topic === HelpTopic.RSFORM ? <HelpRSFormMeta /> : null}
|
|
||||||
{topic === HelpTopic.CSTLIST ? <HelpRSFormItems /> : null}
|
|
||||||
{topic === HelpTopic.CONSTITUENTA ? <HelpConstituenta /> : null}
|
|
||||||
{topic === HelpTopic.GRAPH_TERM ? <HelpTermGraph /> : null}
|
|
||||||
{topic === HelpTopic.RSTEMPLATES ? <HelpRSTemplates /> : null}
|
|
||||||
{topic === HelpTopic.RSLANG ? <HelpRSLang /> : null}
|
|
||||||
{topic === HelpTopic.TERM_CONTROL ? <HelpTerminologyControl /> : null}
|
|
||||||
{topic === HelpTopic.EXTEOR ? <HelpExteor /> : null}
|
|
||||||
{topic === HelpTopic.API ? <HelpAPI /> : null}
|
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import ConceptTooltip from '../../../components/Common/ConceptTooltip'
|
import MiniButton from '../../../components/Common/MiniButton';
|
||||||
import MiniButton from '../../../components/Common/MiniButton'
|
import Overlay from '../../../components/Common/Overlay';
|
||||||
import HelpConstituenta from '../../../components/Help/HelpConstituenta'
|
import HelpButton from '../../../components/Help/HelpButton';
|
||||||
import { ArrowsRotateIcon, CloneIcon, DiamondIcon, DumpBinIcon, HelpIcon, SaveIcon, SmallPlusIcon } from '../../../components/Icons'
|
import {
|
||||||
|
ArrowsRotateIcon, CloneIcon, DiamondIcon, DumpBinIcon, SaveIcon, SmallPlusIcon
|
||||||
|
} from '../../../components/Icons';
|
||||||
|
import { HelpTopic } from '../../../models/miscelanious';
|
||||||
|
|
||||||
interface ConstituentaToolbarProps {
|
interface ConstituentaToolbarProps {
|
||||||
isMutable: boolean
|
isMutable: boolean
|
||||||
|
@ -25,9 +28,7 @@ function ConstituentaToolbar({
|
||||||
}: ConstituentaToolbarProps) {
|
}: ConstituentaToolbarProps) {
|
||||||
const canSave = useMemo(() => (isModified && isMutable), [isModified, isMutable]);
|
const canSave = useMemo(() => (isModified && isMutable), [isModified, isMutable]);
|
||||||
return (
|
return (
|
||||||
<div className='relative w-full'>
|
<Overlay position='right-1/2 translate-x-1/2 top-1 flex items-start'>
|
||||||
<div className='absolute right-0 flex items-start justify-center w-full top-1'>
|
|
||||||
<div className='flex justify-start select-auto w-fit z-tooltip'>
|
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Сохранить изменения'
|
tooltip='Сохранить изменения'
|
||||||
disabled={!canSave}
|
disabled={!canSave}
|
||||||
|
@ -64,18 +65,8 @@ function ConstituentaToolbar({
|
||||||
onClick={onDelete}
|
onClick={onDelete}
|
||||||
icon={<DumpBinIcon size={5} color={isMutable ? 'text-warning' : ''} />}
|
icon={<DumpBinIcon size={5} color={isMutable ? 'text-warning' : ''} />}
|
||||||
/>
|
/>
|
||||||
<div id='cst-help' className='px-1 py-1'>
|
<HelpButton topic={HelpTopic.CONSTITUENTA} offset={4} />
|
||||||
<HelpIcon color='text-primary' size={5} />
|
</Overlay>);
|
||||||
</div>
|
|
||||||
<ConceptTooltip
|
|
||||||
anchorSelect='#cst-help'
|
|
||||||
offset={4}
|
|
||||||
>
|
|
||||||
<HelpConstituenta />
|
|
||||||
</ConceptTooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ConstituentaToolbar;
|
export default ConstituentaToolbar;
|
|
@ -5,6 +5,7 @@ import { toast } from 'react-toastify';
|
||||||
import Button from '../../../components/Common/Button';
|
import Button from '../../../components/Common/Button';
|
||||||
import { ConceptLoader } from '../../../components/Common/ConceptLoader';
|
import { ConceptLoader } from '../../../components/Common/ConceptLoader';
|
||||||
import MiniButton from '../../../components/Common/MiniButton';
|
import MiniButton from '../../../components/Common/MiniButton';
|
||||||
|
import Overlay from '../../../components/Common/Overlay';
|
||||||
import { ASTNetworkIcon } from '../../../components/Icons';
|
import { ASTNetworkIcon } from '../../../components/Icons';
|
||||||
import RSInput from '../../../components/RSInput';
|
import RSInput from '../../../components/RSInput';
|
||||||
import { RSTextWrapper } from '../../../components/RSInput/textEditing';
|
import { RSTextWrapper } from '../../../components/RSInput/textEditing';
|
||||||
|
@ -118,16 +119,13 @@ function EditorRSExpression({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col items-start w-full'>
|
<div className='flex flex-col items-start w-full'>
|
||||||
<div className='relative w-full'>
|
<Overlay position='top-[-0.2rem] left-[11rem]'>
|
||||||
<div className='absolute top-[-0.2rem] left-[11rem]'>
|
<MiniButton noHover
|
||||||
<MiniButton
|
|
||||||
tooltip='Дерево разбора выражения'
|
tooltip='Дерево разбора выражения'
|
||||||
noHover
|
|
||||||
onClick={handleShowAST}
|
onClick={handleShowAST}
|
||||||
icon={<ASTNetworkIcon size={5} color='text-primary' />}
|
icon={<ASTNetworkIcon size={5} color='text-primary' />}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Overlay>
|
||||||
</div>
|
|
||||||
<RSInput innerref={rsInput}
|
<RSInput innerref={rsInput}
|
||||||
value={value}
|
value={value}
|
||||||
minHeight='3.8rem'
|
minHeight='3.8rem'
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Dispatch, SetStateAction, useLayoutEffect, useMemo, useState } from 're
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import MiniButton from '../../../components/Common/MiniButton';
|
import MiniButton from '../../../components/Common/MiniButton';
|
||||||
|
import Overlay from '../../../components/Common/Overlay';
|
||||||
import SubmitButton from '../../../components/Common/SubmitButton';
|
import SubmitButton from '../../../components/Common/SubmitButton';
|
||||||
import TextArea from '../../../components/Common/TextArea';
|
import TextArea from '../../../components/Common/TextArea';
|
||||||
import { EditIcon, SaveIcon } from '../../../components/Icons';
|
import { EditIcon, SaveIcon } from '../../../components/Icons';
|
||||||
|
@ -101,11 +102,9 @@ function FormConstituenta({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (<>
|
return (<>
|
||||||
{readyForEdit ?
|
<Overlay position='top-0 left-[3rem]' className='flex justify-start select-none' >
|
||||||
<div className='relative'>
|
|
||||||
<div className='absolute top-0 right-[-3rem] w-full flex justify-start select-none'>
|
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip={`Редактировать словоформы термина: ${constituenta!.term_forms.length}`}
|
tooltip={`Редактировать словоформы термина: ${constituenta?.term_forms.length ?? 0}`}
|
||||||
disabled={!readyForEdit}
|
disabled={!readyForEdit}
|
||||||
noHover
|
noHover
|
||||||
onClick={onEditTerm}
|
onClick={onEditTerm}
|
||||||
|
@ -121,8 +120,7 @@ function FormConstituenta({
|
||||||
onClick={handleRename}
|
onClick={handleRename}
|
||||||
icon={<EditIcon size={4} color={readyForEdit ? 'text-primary' : ''} />}
|
icon={<EditIcon size={4} color={readyForEdit ? 'text-primary' : ''} />}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Overlay>
|
||||||
</div> : null}
|
|
||||||
<form id={id}
|
<form id={id}
|
||||||
className='flex flex-col gap-3 mt-1'
|
className='flex flex-col gap-3 mt-1'
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
|
|
|
@ -194,7 +194,7 @@ function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }:
|
||||||
}, [noNavigation, baseHeight]);
|
}, [noNavigation, baseHeight]);
|
||||||
|
|
||||||
return (<>
|
return (<>
|
||||||
<div className='sticky top-0 left-0 right-0 flex items-stretch justify-between gap-1 pl-2 border-b clr-input'>
|
<div className='relative top-0 left-0 right-0 flex items-stretch justify-between gap-1 pl-2 border-b clr-input'>
|
||||||
<div className='absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none text-controls'>
|
<div className='absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none text-controls'>
|
||||||
<MagnifyingGlassIcon />
|
<MagnifyingGlassIcon />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import ConceptTooltip from '../../../components/Common/ConceptTooltip'
|
import MiniButton from '../../../components/Common/MiniButton';
|
||||||
import MiniButton from '../../../components/Common/MiniButton'
|
import Overlay from '../../../components/Common/Overlay';
|
||||||
import HelpRSFormMeta from '../../../components/Help/HelpRSFormMeta'
|
import HelpButton from '../../../components/Help/HelpButton';
|
||||||
import { DownloadIcon, DumpBinIcon, HelpIcon, OwnerIcon, SaveIcon, ShareIcon } from '../../../components/Icons'
|
import { DownloadIcon, DumpBinIcon, OwnerIcon, SaveIcon, ShareIcon } from '../../../components/Icons';
|
||||||
|
import { HelpTopic } from '../../../models/miscelanious';
|
||||||
|
|
||||||
interface RSFormToolbarProps {
|
interface RSFormToolbarProps {
|
||||||
isMutable: boolean
|
isMutable: boolean
|
||||||
|
@ -25,8 +26,7 @@ function RSFormToolbar({
|
||||||
}: RSFormToolbarProps) {
|
}: RSFormToolbarProps) {
|
||||||
const canSave = useMemo(() => (modified && isMutable), [modified, isMutable]);
|
const canSave = useMemo(() => (modified && isMutable), [modified, isMutable]);
|
||||||
return (
|
return (
|
||||||
<div className='relative flex items-start justify-center w-full'>
|
<Overlay position='w-full top-1 flex items-start justify-center'>
|
||||||
<div className='absolute flex mt-1'>
|
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Сохранить изменения'
|
tooltip='Сохранить изменения'
|
||||||
disabled={!canSave}
|
disabled={!canSave}
|
||||||
|
@ -55,14 +55,8 @@ function RSFormToolbar({
|
||||||
onClick={onDestroy}
|
onClick={onDestroy}
|
||||||
icon={<DumpBinIcon size={5} color={isMutable ? 'text-warning' : ''} />}
|
icon={<DumpBinIcon size={5} color={isMutable ? 'text-warning' : ''} />}
|
||||||
/>
|
/>
|
||||||
<div id='rsform-help' className='py-1 ml-1'>
|
<HelpButton topic={HelpTopic.RSFORM} offset={4} />
|
||||||
<HelpIcon color='text-primary' size={5} />
|
</Overlay>);
|
||||||
</div>
|
|
||||||
<ConceptTooltip anchorSelect='#rsform-help' offset={4}>
|
|
||||||
<HelpRSFormMeta />
|
|
||||||
</ConceptTooltip>
|
|
||||||
</div>
|
|
||||||
</div>);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RSFormToolbar;
|
export default RSFormToolbar;
|
|
@ -4,7 +4,7 @@ import { toast } from 'react-toastify';
|
||||||
import { type RowSelectionState } from '../../../components/DataTable';
|
import { type RowSelectionState } from '../../../components/DataTable';
|
||||||
import SelectedCounter from '../../../components/Shared/SelectedCounter';
|
import SelectedCounter from '../../../components/Shared/SelectedCounter';
|
||||||
import { useRSForm } from '../../../context/RSFormContext';
|
import { useRSForm } from '../../../context/RSFormContext';
|
||||||
import { CstType, ICstCreateData, ICstMovetoData } from '../../../models/rsform'
|
import { CstType, ICstCreateData, ICstMovetoData } from '../../../models/rsform';
|
||||||
import RSListToolbar from './RSListToolbar';
|
import RSListToolbar from './RSListToolbar';
|
||||||
import RSTable from './RSTable';
|
import RSTable from './RSTable';
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import ConceptTooltip from '../../../components/Common/ConceptTooltip';
|
|
||||||
import Dropdown from '../../../components/Common/Dropdown';
|
import Dropdown from '../../../components/Common/Dropdown';
|
||||||
import DropdownButton from '../../../components/Common/DropdownButton';
|
import DropdownButton from '../../../components/Common/DropdownButton';
|
||||||
import MiniButton from '../../../components/Common/MiniButton';
|
import MiniButton from '../../../components/Common/MiniButton';
|
||||||
import HelpRSFormItems from '../../../components/Help/HelpRSFormItems';
|
import Overlay from '../../../components/Common/Overlay';
|
||||||
import { ArrowDownIcon, ArrowDropdownIcon, ArrowUpIcon, CloneIcon, DiamondIcon, DumpBinIcon, HelpIcon, SmallPlusIcon, UpdateIcon } from '../../../components/Icons';
|
import HelpButton from '../../../components/Help/HelpButton';
|
||||||
|
import { ArrowDownIcon, ArrowDropdownIcon, ArrowUpIcon, CloneIcon, DiamondIcon, DumpBinIcon, SmallPlusIcon, UpdateIcon } from '../../../components/Icons';
|
||||||
import useDropdown from '../../../hooks/useDropdown';
|
import useDropdown from '../../../hooks/useDropdown';
|
||||||
|
import { HelpTopic } from '../../../models/miscelanious';
|
||||||
import { CstType } from '../../../models/rsform';
|
import { CstType } from '../../../models/rsform';
|
||||||
import { prefixes } from '../../../utils/constants';
|
import { prefixes } from '../../../utils/constants';
|
||||||
import { labelCstType } from '../../../utils/labels';
|
import { labelCstType } from '../../../utils/labels';
|
||||||
|
@ -34,8 +35,7 @@ function RSListToolbar({
|
||||||
const nothingSelected = useMemo(() => selectedCount === 0, [selectedCount]);
|
const nothingSelected = useMemo(() => selectedCount === 0, [selectedCount]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='relative w-full z-pop'>
|
<Overlay position='w-full top-1 flex items-start justify-center'>
|
||||||
<div className='absolute flex items-start justify-center w-full top-1'>
|
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Переместить вверх [Alt + вверх]'
|
tooltip='Переместить вверх [Alt + вверх]'
|
||||||
icon={<ArrowUpIcon size={5}/>}
|
icon={<ArrowUpIcon size={5}/>}
|
||||||
|
@ -103,14 +103,8 @@ function RSListToolbar({
|
||||||
disabled={!isMutable || nothingSelected}
|
disabled={!isMutable || nothingSelected}
|
||||||
onClick={onDelete}
|
onClick={onDelete}
|
||||||
/>
|
/>
|
||||||
<div className='px-1 py-1' id='items-table-help'>
|
<HelpButton topic={HelpTopic.CSTLIST} offset={5} />
|
||||||
<HelpIcon color='text-primary' size={5} />
|
</Overlay>);
|
||||||
</div>
|
|
||||||
<ConceptTooltip anchorSelect='#items-table-help' offset={5}>
|
|
||||||
<HelpRSFormItems />
|
|
||||||
</ConceptTooltip>
|
|
||||||
</div>
|
|
||||||
</div>);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RSListToolbar;
|
export default RSListToolbar;
|
|
@ -1,6 +1,7 @@
|
||||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
import { GraphEdge, GraphNode, LayoutTypes } from 'reagraph';
|
import { GraphEdge, GraphNode, LayoutTypes } from 'reagraph';
|
||||||
|
|
||||||
|
import Overlay from '../../../components/Common/Overlay';
|
||||||
import InfoConstituenta from '../../../components/Shared/InfoConstituenta';
|
import InfoConstituenta from '../../../components/Shared/InfoConstituenta';
|
||||||
import SelectedCounter from '../../../components/Shared/SelectedCounter';
|
import SelectedCounter from '../../../components/Shared/SelectedCounter';
|
||||||
import { useRSForm } from '../../../context/RSFormContext';
|
import { useRSForm } from '../../../context/RSFormContext';
|
||||||
|
@ -225,15 +226,17 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{hoverCst ?
|
{hoverCst ?
|
||||||
<div className='relative'>
|
<Overlay
|
||||||
|
position='top-[1.6rem] left-[2.6rem] px-3 w-[25rem] h-fit min-h-[11rem] overflow-y-auto'
|
||||||
|
layer='z-tooltip'
|
||||||
|
className='border shadow-md clr-app'
|
||||||
|
>
|
||||||
<InfoConstituenta
|
<InfoConstituenta
|
||||||
data={hoverCst}
|
data={hoverCst}
|
||||||
className='absolute top-[1.6rem] left-[2.6rem] z-tooltip w-[25rem] min-h-[11rem] shadow-md overflow-y-auto border h-fit clr-app px-3'
|
|
||||||
/>
|
/>
|
||||||
</div> : null}
|
</Overlay> : null}
|
||||||
|
|
||||||
<div className='relative z-pop'>
|
<Overlay position='top-0 left-0 max-w-[13.5rem] min-w-[13.5rem]' className='flex flex-col gap-3'>
|
||||||
<div className='absolute top-0 left-0 flex flex-col gap-3 max-w-[13.5rem] min-w-[13.5rem]'>
|
|
||||||
<GraphSidebar
|
<GraphSidebar
|
||||||
coloring={coloringScheme}
|
coloring={coloringScheme}
|
||||||
layout={layout}
|
layout={layout}
|
||||||
|
@ -248,8 +251,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
||||||
toggleSelection={toggleDismissed}
|
toggleSelection={toggleDismissed}
|
||||||
onEdit={onOpenEdit}
|
onEdit={onOpenEdit}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Overlay>
|
||||||
</div>
|
|
||||||
|
|
||||||
<TermGraph
|
<TermGraph
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
|
|
|
@ -18,7 +18,7 @@ function GraphSidebar({
|
||||||
layout, setLayout
|
layout, setLayout
|
||||||
} : GraphSidebarProps) {
|
} : GraphSidebarProps) {
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col px-2 pb-2 text-sm select-none mt-9 h-fit'>
|
<div className='flex flex-col px-2 text-sm select-none mt-9 h-fit'>
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
placeholder='Выберите цвет'
|
placeholder='Выберите цвет'
|
||||||
options={SelectorGraphColoring}
|
options={SelectorGraphColoring}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import ConceptTooltip from '../../../components/Common/ConceptTooltip'
|
import MiniButton from '../../../components/Common/MiniButton';
|
||||||
import MiniButton from '../../../components/Common/MiniButton'
|
import Overlay from '../../../components/Common/Overlay';
|
||||||
import HelpTermGraph from '../../../components/Help/HelpTermGraph'
|
import HelpButton from '../../../components/Help/HelpButton';
|
||||||
import { ArrowsFocusIcon, DumpBinIcon, FilterIcon, HelpIcon, LetterAIcon, LetterALinesIcon, PlanetIcon, SmallPlusIcon } from '../../../components/Icons'
|
import { ArrowsFocusIcon, DumpBinIcon, FilterIcon, LetterAIcon, LetterALinesIcon, PlanetIcon, SmallPlusIcon } from '../../../components/Icons';
|
||||||
|
import { HelpTopic } from '../../../models/miscelanious';
|
||||||
|
|
||||||
interface GraphToolbarProps {
|
interface GraphToolbarProps {
|
||||||
isMutable: boolean
|
isMutable: boolean
|
||||||
|
@ -28,8 +29,7 @@ function GraphToolbar({
|
||||||
onCreate, onDelete, onResetViewpoint
|
onCreate, onDelete, onResetViewpoint
|
||||||
} : GraphToolbarProps) {
|
} : GraphToolbarProps) {
|
||||||
return (
|
return (
|
||||||
<div className='relative w-full z-pop'>
|
<Overlay position='w-full top-1 right-0 flex items-start justify-center'>
|
||||||
<div className='absolute right-0 flex items-start justify-center w-full top-1'>
|
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Настройки фильтрации узлов и связей'
|
tooltip='Настройки фильтрации узлов и связей'
|
||||||
icon={<FilterIcon color='text-primary' size={5} />}
|
icon={<FilterIcon color='text-primary' size={5} />}
|
||||||
|
@ -67,16 +67,8 @@ function GraphToolbar({
|
||||||
disabled={!is3D}
|
disabled={!is3D}
|
||||||
onClick={toggleOrbit}
|
onClick={toggleOrbit}
|
||||||
/>
|
/>
|
||||||
<div className='px-1 py-1' id='items-graph-help'>
|
<HelpButton topic={HelpTopic.GRAPH_TERM} dimensions='max-w-[calc(100vw-20rem)]' offset={4} />
|
||||||
<HelpIcon color='text-primary' size={5} />
|
</Overlay>);
|
||||||
</div>
|
|
||||||
<ConceptTooltip anchorSelect='#items-graph-help' offset={4}>
|
|
||||||
<div className='text-sm max-w-[calc(100vw-20rem)]'>
|
|
||||||
<HelpTermGraph />
|
|
||||||
</div>
|
|
||||||
</ConceptTooltip>
|
|
||||||
</div>
|
|
||||||
</div>);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default GraphToolbar;
|
export default GraphToolbar;
|
|
@ -5,7 +5,7 @@ import { CstType, IRSForm } from '../../../models/rsform';
|
||||||
import { Graph } from '../../../utils/Graph';
|
import { Graph } from '../../../utils/Graph';
|
||||||
|
|
||||||
function useGraphFilter(schema: IRSForm | undefined, params: GraphFilterParams, toggleUpdate: boolean) {
|
function useGraphFilter(schema: IRSForm | undefined, params: GraphFilterParams, toggleUpdate: boolean) {
|
||||||
const [ filtered, setFiltered ] = useState<Graph>(new Graph());
|
const [filtered, setFiltered] = useState<Graph>(new Graph());
|
||||||
|
|
||||||
const allowedTypes: CstType[] = useMemo(
|
const allowedTypes: CstType[] = useMemo(
|
||||||
() => {
|
() => {
|
||||||
|
|
|
@ -8,8 +8,7 @@ function RSFormPage() {
|
||||||
return (
|
return (
|
||||||
<RSFormState schemaID={id ?? ''}>
|
<RSFormState schemaID={id ?? ''}>
|
||||||
<RSTabs />
|
<RSTabs />
|
||||||
</RSFormState>
|
</RSFormState>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RSFormPage;
|
export default RSFormPage;
|
||||||
|
|
|
@ -30,8 +30,8 @@ function RSTabsMenu({
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const {
|
const {
|
||||||
isOwned, isMutable, isTracking, isReadonly, isClaimable, adminMode: isForceAdmin,
|
isOwned, isMutable, isTracking, readerMode, isClaimable, adminMode,
|
||||||
toggleForceAdmin, toggleReadonly, processing
|
toggleAdminMode, toggleReaderMode, processing
|
||||||
} = useRSForm();
|
} = useRSForm();
|
||||||
const schemaMenu = useDropdown();
|
const schemaMenu = useDropdown();
|
||||||
const editMenu = useDropdown();
|
const editMenu = useDropdown();
|
||||||
|
@ -147,15 +147,15 @@ function RSTabsMenu({
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
{(isOwned || user?.is_staff) ?
|
{(isOwned || user?.is_staff) ?
|
||||||
<DropdownCheckbox
|
<DropdownCheckbox
|
||||||
value={isReadonly}
|
value={readerMode}
|
||||||
setValue={toggleReadonly}
|
setValue={toggleReaderMode}
|
||||||
label='Я — читатель!'
|
label='Я — читатель!'
|
||||||
tooltip='Режим чтения'
|
tooltip='Режим чтения'
|
||||||
/> : null}
|
/> : null}
|
||||||
{user?.is_staff ?
|
{user?.is_staff ?
|
||||||
<DropdownCheckbox
|
<DropdownCheckbox
|
||||||
value={isForceAdmin}
|
value={adminMode}
|
||||||
setValue={toggleForceAdmin}
|
setValue={toggleAdminMode}
|
||||||
label='Я — администратор!'
|
label='Я — администратор!'
|
||||||
tooltip='Режим редактирования для администраторов'
|
tooltip='Режим редактирования для администраторов'
|
||||||
/> : null}
|
/> : null}
|
||||||
|
|
|
@ -68,9 +68,8 @@ function EditorPassword() {
|
||||||
}, [newPassword, oldPassword, newPasswordRepeat, setError]);
|
}, [newPassword, oldPassword, newPasswordRepeat, setError]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex py-2 border-l-2 max-w-[14rem]'>
|
|
||||||
<form
|
<form
|
||||||
className='flex flex-col justify-between px-6'
|
className='flex flex-col justify-between px-6 border-l-2 max-w-[14rem] py-2'
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
>
|
>
|
||||||
<div className='flex flex-col gap-3'>
|
<div className='flex flex-col gap-3'>
|
||||||
|
@ -104,8 +103,7 @@ function EditorPassword() {
|
||||||
loading={loading}
|
loading={loading}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>);
|
||||||
</div>);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default EditorPassword;
|
export default EditorPassword;
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { useMemo, useState } from 'react';
|
||||||
import BackendError from '../../components/BackendError';
|
import BackendError from '../../components/BackendError';
|
||||||
import { ConceptLoader } from '../../components/Common/ConceptLoader';
|
import { ConceptLoader } from '../../components/Common/ConceptLoader';
|
||||||
import MiniButton from '../../components/Common/MiniButton';
|
import MiniButton from '../../components/Common/MiniButton';
|
||||||
|
import Overlay from '../../components/Common/Overlay';
|
||||||
import { NotSubscribedIcon,SubscribedIcon } from '../../components/Icons';
|
import { NotSubscribedIcon,SubscribedIcon } from '../../components/Icons';
|
||||||
import { useAuth } from '../../context/AuthContext';
|
import { useAuth } from '../../context/AuthContext';
|
||||||
import { useLibrary } from '../../context/LibraryContext';
|
import { useLibrary } from '../../context/LibraryContext';
|
||||||
|
@ -24,14 +25,13 @@ function UserTabs() {
|
||||||
}, [auth, items]);
|
}, [auth, items]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full'>
|
<>
|
||||||
{loading ? <ConceptLoader /> : null}
|
{loading ? <ConceptLoader /> : null}
|
||||||
{error ? <BackendError error={error} /> : null}
|
{error ? <BackendError error={error} /> : null}
|
||||||
{user ?
|
{user ?
|
||||||
<div className='flex justify-center gap-2 py-2'>
|
<div className='flex justify-center gap-2 py-2'>
|
||||||
<div className='flex flex-col gap-2 min-w-max'>
|
<div className='flex flex-col gap-2 min-w-max'>
|
||||||
<div className='relative w-full'>
|
<Overlay position='mt-2 top-0 right-0'>
|
||||||
<div className='absolute top-0 right-0 mt-2'>
|
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Показать/Скрыть список отслеживаний'
|
tooltip='Показать/Скрыть список отслеживаний'
|
||||||
icon={showSubs
|
icon={showSubs
|
||||||
|
@ -40,8 +40,7 @@ function UserTabs() {
|
||||||
}
|
}
|
||||||
onClick={() => setShowSubs(prev => !prev)}
|
onClick={() => setShowSubs(prev => !prev)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Overlay>
|
||||||
</div>
|
|
||||||
<h1>Учетные данные пользователя</h1>
|
<h1>Учетные данные пользователя</h1>
|
||||||
<div className='flex justify-center py-2 max-w-fit'>
|
<div className='flex justify-center py-2 max-w-fit'>
|
||||||
<EditorProfile />
|
<EditorProfile />
|
||||||
|
@ -54,7 +53,7 @@ function UserTabs() {
|
||||||
<ViewSubscriptions items={subscriptions} />
|
<ViewSubscriptions items={subscriptions} />
|
||||||
</div> : null}
|
</div> : null}
|
||||||
</div> : null}
|
</div> : null}
|
||||||
</div>);
|
</>);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default UserTabs;
|
export default UserTabs;
|
|
@ -2,15 +2,15 @@
|
||||||
* Module: CodeMirror customizations.
|
* Module: CodeMirror customizations.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { syntaxTree } from '@codemirror/language'
|
import { syntaxTree } from '@codemirror/language';
|
||||||
import { NodeType, Tree, TreeCursor } from '@lezer/common'
|
import { NodeType, Tree, TreeCursor } from '@lezer/common';
|
||||||
import { ReactCodeMirrorRef, SelectionRange } from '@uiw/react-codemirror'
|
import { ReactCodeMirrorRef, SelectionRange } from '@uiw/react-codemirror';
|
||||||
|
|
||||||
import { IEntityReference, ISyntacticReference } from '../models/language'
|
import { IEntityReference, ISyntacticReference } from '../models/language';
|
||||||
import { parseGrammemes } from '../models/languageAPI'
|
import { parseGrammemes } from '../models/languageAPI';
|
||||||
import { IConstituenta } from '../models/rsform'
|
import { IConstituenta } from '../models/rsform';
|
||||||
import { colorfgGrammeme,IColorTheme } from './color'
|
import { colorfgGrammeme,IColorTheme } from './color';
|
||||||
import { describeConstituentaTerm, labelCstTypification, labelGrammeme } from './labels'
|
import { describeConstituentaTerm, labelCstTypification, labelGrammeme } from './labels';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents syntax tree node data.
|
* Represents syntax tree node data.
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
* Module: Single place for all color definitions in code (see index.css for full defs).
|
* Module: Single place for all color definitions in code (see index.css for full defs).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { GramData, Grammeme, NounGrams, PartOfSpeech, VerbGrams } from '../models/language'
|
import { GramData, Grammeme, NounGrams, PartOfSpeech, VerbGrams } from '../models/language';
|
||||||
import { GraphColoringScheme } from '../models/miscelanious'
|
import { GraphColoringScheme } from '../models/miscelanious';
|
||||||
import { CstClass, ExpressionStatus, IConstituenta } from '../models/rsform'
|
import { CstClass, ExpressionStatus, IConstituenta } from '../models/rsform';
|
||||||
import { ISyntaxTreeNode, TokenID } from '../models/rslang'
|
import { ISyntaxTreeNode, TokenID } from '../models/rslang';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -482,7 +482,7 @@ export function labelSyntaxTree(node: ISyntaxTreeNode): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function labelGrammeme(gram: GramData): string {
|
export function labelGrammeme(gram: GramData): string {
|
||||||
switch (gram) {
|
switch (gram as Grammeme) {
|
||||||
default: return `Неизв: ${gram}`;
|
default: return `Неизв: ${gram}`;
|
||||||
|
|
||||||
case Grammeme.NOUN: return 'ЧР: сущ';
|
case Grammeme.NOUN: return 'ЧР: сущ';
|
||||||
|
|
Loading…
Reference in New Issue
Block a user