mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 21:10:38 +03:00
Refactoring: replace shortcurctuit with conditionals
This commit is contained in:
parent
747176c673
commit
de9c470276
|
@ -20,32 +20,33 @@ import { globalIDs } from './utils/constants';
|
||||||
function Root() {
|
function Root() {
|
||||||
const { noNavigation, noFooter, viewportHeight, mainHeight, showScroll } = useConceptTheme();
|
const { noNavigation, noFooter, viewportHeight, mainHeight, showScroll } = useConceptTheme();
|
||||||
return (
|
return (
|
||||||
<NavigationState>
|
<NavigationState>
|
||||||
<div className='w-screen antialiased clr-app min-w-[30rem] overflow-hidden'>
|
<div className='w-screen antialiased clr-app min-w-[30rem] overflow-hidden'>
|
||||||
|
|
||||||
<ConceptToaster
|
<ConceptToaster
|
||||||
className='mt-[4rem] text-sm'
|
className='mt-[4rem] text-sm'
|
||||||
autoClose={3000}
|
autoClose={3000}
|
||||||
draggable={false}
|
draggable={false}
|
||||||
pauseOnFocusLoss={false}
|
pauseOnFocusLoss={false}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Navigation />
|
<Navigation />
|
||||||
<div id={globalIDs.main_scroll}
|
|
||||||
className='w-full overflow-x-auto overscroll-none'
|
<div id={globalIDs.main_scroll}
|
||||||
style={{
|
className='w-full overflow-x-auto overscroll-none'
|
||||||
maxHeight: viewportHeight,
|
style={{
|
||||||
overflowY: showScroll ? 'scroll': 'auto'
|
maxHeight: viewportHeight,
|
||||||
}}
|
overflowY: showScroll ? 'scroll': 'auto'
|
||||||
>
|
}}
|
||||||
<main className='w-full h-full min-w-fit' style={{minHeight: mainHeight}}>
|
>
|
||||||
<Outlet />
|
<main className='w-full h-full min-w-fit' style={{minHeight: mainHeight}}>
|
||||||
</main>
|
<Outlet />
|
||||||
{!noNavigation && !noFooter && <Footer />}
|
</main>
|
||||||
</div>
|
|
||||||
|
{(!noNavigation && !noFooter) ? <Footer /> : null}
|
||||||
</div>
|
</div>
|
||||||
</NavigationState>
|
</div>
|
||||||
);
|
</NavigationState>);
|
||||||
}
|
}
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
|
|
|
@ -50,8 +50,8 @@ function DescribeError(error: ErrorInfo) {
|
||||||
<p>{error.message}</p>
|
<p>{error.message}</p>
|
||||||
{error.response.data && (<>
|
{error.response.data && (<>
|
||||||
<p className='mt-2 underline'>Описание</p>
|
<p className='mt-2 underline'>Описание</p>
|
||||||
{ isHtml && <div dangerouslySetInnerHTML={{ __html: error.response.data as TrustedHTML }} /> }
|
{isHtml ? <div dangerouslySetInnerHTML={{ __html: error.response.data as TrustedHTML }} /> : null}
|
||||||
{ !isHtml && <PrettyJson data={error.response.data as object} />}
|
{!isHtml ? <PrettyJson data={error.response.data as object} /> : null}
|
||||||
</>)}
|
</>)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -28,8 +28,8 @@ function Button({
|
||||||
className={`inline-flex items-center gap-2 align-middle justify-center select-none ${padding} ${colors} ${outlineClass} ${borderClass} ${dimensions} ${cursor}`}
|
className={`inline-flex items-center gap-2 align-middle justify-center select-none ${padding} ${colors} ${outlineClass} ${borderClass} ${dimensions} ${cursor}`}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{icon && icon}
|
{icon ? icon : null}
|
||||||
{text && <span className='font-semibold'>{text}</span>}
|
{text ? <span className='font-semibold'>{text}</span> : null}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import Label from './Label';
|
||||||
|
|
||||||
export interface CheckboxProps
|
export interface CheckboxProps
|
||||||
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'className' | 'children' | 'title' | 'value' | 'onClick' > {
|
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'className' | 'children' | 'title' | 'value' | 'onClick' > {
|
||||||
id?: string
|
|
||||||
label?: string
|
label?: string
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
dimensions?: string
|
dimensions?: string
|
||||||
|
@ -43,8 +42,7 @@ function Checkbox({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button type='button' id={id}
|
||||||
id={id}
|
|
||||||
className={`flex items-center clr-outline focus:outline-dotted focus:outline-1 ${dimensions}`}
|
className={`flex items-center clr-outline focus:outline-dotted focus:outline-1 ${dimensions}`}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
@ -52,14 +50,17 @@ function Checkbox({
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<div className={`max-w-[1rem] min-w-[1rem] h-4 mt-0.5 border rounded-sm ${bgColor} ${cursor}`} >
|
<div className={`max-w-[1rem] min-w-[1rem] h-4 mt-0.5 border rounded-sm ${bgColor} ${cursor}`} >
|
||||||
{ value && <div className='mt-[1px] ml-[1px]'><CheckboxCheckedIcon /></div>}
|
{value ?
|
||||||
|
<div className='mt-[1px] ml-[1px]'>
|
||||||
|
<CheckboxCheckedIcon />
|
||||||
|
</div> : null}
|
||||||
</div>
|
</div>
|
||||||
{ label &&
|
{label ?
|
||||||
<Label
|
<Label
|
||||||
className={`${cursor} px-2 text-start`}
|
className={`${cursor} px-2 text-start`}
|
||||||
text={label}
|
text={label}
|
||||||
htmlFor={id}
|
htmlFor={id}
|
||||||
/>}
|
/> : null}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ function ConceptSearch({ value, onChange, dense }: ConceptSearchProps) {
|
||||||
noOutline
|
noOutline
|
||||||
noBorder={dense}
|
noBorder={dense}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={event => onChange && onChange(event.target.value)}
|
onChange={event => (onChange ? onChange(event.target.value) : undefined)}
|
||||||
/>
|
/>
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,10 @@ interface DropdownButtonProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function DropdownButton({ tooltip, onClick, disabled, children }: DropdownButtonProps) {
|
function DropdownButton({ tooltip, onClick, disabled, children }: DropdownButtonProps) {
|
||||||
const behavior = (onClick ? 'cursor-pointer disabled:cursor-not-allowed clr-hover' : 'cursor-default');
|
const behavior = onClick ? 'cursor-pointer disabled:cursor-not-allowed clr-hover' : 'cursor-default';
|
||||||
const text = disabled ? 'text-controls' : '';
|
const text = disabled ? 'text-controls' : '';
|
||||||
return (
|
return (
|
||||||
<button
|
<button type='button'
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
|
|
|
@ -9,7 +9,7 @@ interface DropdownCheckboxProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function DropdownCheckbox({ tooltip, setValue, disabled, ...props }: DropdownCheckboxProps) {
|
function DropdownCheckbox({ tooltip, setValue, disabled, ...props }: DropdownCheckboxProps) {
|
||||||
const behavior = (setValue && !disabled ? 'clr-hover' : '');
|
const behavior = (setValue && !disabled) ? 'clr-hover' : '';
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
|
|
|
@ -11,7 +11,7 @@ function Form({ title, onSubmit, dimensions = 'max-w-xs', children }: FormProps)
|
||||||
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}`}
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
>
|
>
|
||||||
{ title && <h1 className='text-xl whitespace-nowrap'>{title}</h1> }
|
{ title ? <h1 className='text-xl whitespace-nowrap'>{title}</h1> : null }
|
||||||
{children}
|
{children}
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|
|
@ -38,25 +38,23 @@ function Modal({
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className='fixed top-0 left-0 w-full h-full z-navigation clr-modal-backdrop' />
|
<div className='fixed top-0 left-0 w-full h-full z-navigation clr-modal-backdrop' />
|
||||||
<div
|
<div ref={ref}
|
||||||
ref={ref}
|
className='fixed bottom-1/2 left-1/2 translate-y-1/2 -translate-x-1/2 px-4 py-3 flex flex-col justify-start w-fit max-w-[calc(100vw-2rem)] overflow-x-auto 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 py-3 flex flex-col justify-start w-fit max-w-[calc(100vw-2rem)] overflow-x-auto h-fit z-modal clr-app border shadow-md'
|
|
||||||
>
|
>
|
||||||
{ title && <h1 className='pb-3 text-xl select-none'>{title}</h1> }
|
{title ? <h1 className='pb-3 text-xl select-none'>{title}</h1> : null}
|
||||||
<div className='max-h-[calc(100vh-8rem)] overflow-auto'>
|
<div className='max-h-[calc(100vh-8rem)] overflow-auto px-2'>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
<div className='flex justify-center w-full gap-4 pt-3 mt-2 border-t-2 z-modal-controls'>
|
<div className='flex justify-center w-full gap-4 pt-3 mt-2 border-t-2 z-modal-controls'>
|
||||||
{!readonly &&
|
{!readonly ?
|
||||||
<Button
|
<Button autoFocus
|
||||||
text={submitText}
|
text={submitText}
|
||||||
tooltip={!canSubmit ? submitInvalidTooltip: ''}
|
tooltip={!canSubmit ? submitInvalidTooltip: ''}
|
||||||
dimensions='min-w-[8rem] min-h-[2.6rem] w-fit h-fit'
|
dimensions='min-w-[8rem] min-h-[2.6rem] w-fit h-fit'
|
||||||
colors='clr-btn-primary'
|
colors='clr-btn-primary'
|
||||||
disabled={!canSubmit}
|
disabled={!canSubmit}
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
autoFocus
|
/> : null}
|
||||||
/>}
|
|
||||||
<Button
|
<Button
|
||||||
text={readonly ? 'Закрыть' : 'Отмена'}
|
text={readonly ? 'Закрыть' : 'Отмена'}
|
||||||
dimensions='min-w-[8rem] min-h-[2.6rem] w-fit h-fit'
|
dimensions='min-w-[8rem] min-h-[2.6rem] w-fit h-fit'
|
||||||
|
|
|
@ -18,15 +18,16 @@ function SelectorButton({
|
||||||
...props
|
...props
|
||||||
}: SelectorButtonProps) {
|
}: SelectorButtonProps) {
|
||||||
const cursor = 'disabled:cursor-not-allowed cursor-pointer';
|
const cursor = 'disabled:cursor-not-allowed cursor-pointer';
|
||||||
const position = `px-1 flex flex-start items-center gap-1 ${dimensions}`
|
const position = `px-1 flex flex-start items-center gap-1 ${dimensions}`;
|
||||||
|
const design = (transparent ? 'clr-hover' : `border ${colors}`) + ' text-btn text-controls';
|
||||||
return (
|
return (
|
||||||
<button type='button'
|
<button type='button'
|
||||||
className={`text-sm small-caps ${!transparent && 'border'} ${cursor} ${position} text-btn text-controls select-none ${transparent ? 'clr-hover' : colors}`}
|
className={`text-sm small-caps select-none ${cursor} ${position} ${design}`}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{icon && icon}
|
{icon ? icon : null}
|
||||||
{text && <div className={'font-semibold whitespace-nowrap pb-1'}>{text}</div>}
|
{text ? <div className={'font-semibold whitespace-nowrap pb-1'}>{text}</div> : null}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,8 @@ function SubmitButton({
|
||||||
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-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' : ''}`}
|
||||||
disabled={disabled ?? loading}
|
disabled={disabled ?? loading}
|
||||||
>
|
>
|
||||||
{icon && <span>{icon}</span>}
|
{icon ? <span>{icon}</span> : null}
|
||||||
{text && <span>{text}</span>}
|
{text ? <span>{text}</span> : null}
|
||||||
</button>);
|
</button>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ function SwitchButton<ValueType>({
|
||||||
className={`px-2 py-1 border font-semibold small-caps rounded-none cursor-pointer clr-btn-clear clr-hover ${dimensions} ${isSelected ? 'clr-selected': ''}`}
|
className={`px-2 py-1 border font-semibold small-caps rounded-none cursor-pointer clr-btn-clear clr-hover ${dimensions} ${isSelected ? 'clr-selected': ''}`}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{icon && icon}
|
{icon ? icon : null}
|
||||||
{label}
|
{label}
|
||||||
</button>);
|
</button>);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,15 +23,14 @@ function TextInput({
|
||||||
const outlineClass = noOutline ? '' : 'clr-outline';
|
const outlineClass = noOutline ? '' : 'clr-outline';
|
||||||
return (
|
return (
|
||||||
<div className={`flex ${dense ? 'items-center gap-4 ' + dimensions : 'flex-col items-start gap-2'}`}>
|
<div className={`flex ${dense ? 'items-center gap-4 ' + dimensions : 'flex-col items-start gap-2'}`}>
|
||||||
{label &&
|
{label ?
|
||||||
<Label
|
<Label
|
||||||
text={label}
|
text={label}
|
||||||
htmlFor={id}
|
htmlFor={id}
|
||||||
/>}
|
/> : null}
|
||||||
<input id={id}
|
<input id={id}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
|
onKeyDown={!allowEnter && !onKeyDown ? preventEnterCapture : onKeyDown}
|
||||||
onKeyDown={!allowEnter && !onKeyDown ? preventEnterCapture: onKeyDown}
|
|
||||||
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}`}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -45,8 +45,7 @@ function Tristate({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button type='button' id={id}
|
||||||
id={id}
|
|
||||||
className={`flex items-center clr-outline focus:outline-dotted focus:outline-1 ${dimensions}`}
|
className={`flex items-center clr-outline focus:outline-dotted focus:outline-1 ${dimensions}`}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
@ -54,15 +53,15 @@ function Tristate({
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<div className={`w-4 h-4 shrink-0 mt-0.5 border rounded-sm ${bgColor} ${cursor}`} >
|
<div className={`w-4 h-4 shrink-0 mt-0.5 border rounded-sm ${bgColor} ${cursor}`} >
|
||||||
{ value && <div className='mt-[1px] ml-[1px]'><CheckboxCheckedIcon /></div>}
|
{value ? <div className='mt-[1px] ml-[1px]'><CheckboxCheckedIcon /></div> : null}
|
||||||
{ value == null && <div className='mt-[1px] ml-[1px]'><CheckboxNullIcon /></div>}
|
{value == null ? <div className='mt-[1px] ml-[1px]'><CheckboxNullIcon /></div> : null}
|
||||||
</div>
|
</div>
|
||||||
{ label &&
|
{label ?
|
||||||
<Label
|
<Label
|
||||||
className={`${cursor} px-2 text-start`}
|
className={`${cursor} px-2 text-start`}
|
||||||
text={label}
|
text={label}
|
||||||
htmlFor={id}
|
htmlFor={id}
|
||||||
/>}
|
/> : null}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,7 +113,7 @@ export default function DataTable<TData extends RowData>({
|
||||||
<div className='w-full'>
|
<div className='w-full'>
|
||||||
<div className='flex flex-col items-stretch'>
|
<div className='flex flex-col items-stretch'>
|
||||||
<table>
|
<table>
|
||||||
{ !noHeader &&
|
{!noHeader ?
|
||||||
<thead
|
<thead
|
||||||
className={`clr-app shadow-border`}
|
className={`clr-app shadow-border`}
|
||||||
style={{
|
style={{
|
||||||
|
@ -124,10 +124,10 @@ export default function DataTable<TData extends RowData>({
|
||||||
{tableImpl.getHeaderGroups().map(
|
{tableImpl.getHeaderGroups().map(
|
||||||
(headerGroup: HeaderGroup<TData>) => (
|
(headerGroup: HeaderGroup<TData>) => (
|
||||||
<tr key={headerGroup.id}>
|
<tr key={headerGroup.id}>
|
||||||
{enableRowSelection &&
|
{enableRowSelection ?
|
||||||
<th className='pl-3 pr-1'>
|
<th className='pl-3 pr-1'>
|
||||||
<SelectAll table={tableImpl} />
|
<SelectAll table={tableImpl} />
|
||||||
</th>}
|
</th> : null}
|
||||||
{headerGroup.headers.map(
|
{headerGroup.headers.map(
|
||||||
(header: Header<TData, unknown>) => (
|
(header: Header<TData, unknown>) => (
|
||||||
<th key={header.id}
|
<th key={header.id}
|
||||||
|
@ -140,16 +140,16 @@ export default function DataTable<TData extends RowData>({
|
||||||
}}
|
}}
|
||||||
onClick={enableSorting ? header.column.getToggleSortingHandler() : undefined}
|
onClick={enableSorting ? header.column.getToggleSortingHandler() : undefined}
|
||||||
>
|
>
|
||||||
{header.isPlaceholder ? null : (
|
{!header.isPlaceholder ? (
|
||||||
<div className='flex gap-1'>
|
<div className='flex gap-1'>
|
||||||
{flexRender(header.column.columnDef.header, header.getContext())}
|
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||||
{enableSorting && header.column.getCanSort() && <SortingIcon column={header.column} />}
|
{(enableSorting && header.column.getCanSort()) ? <SortingIcon column={header.column} /> : null}
|
||||||
</div>)}
|
</div>) : null}
|
||||||
</th>
|
</th>
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</thead>}
|
</thead> : null}
|
||||||
|
|
||||||
<tbody>
|
<tbody>
|
||||||
{tableImpl.getRowModel().rows.map(
|
{tableImpl.getRowModel().rows.map(
|
||||||
|
@ -162,10 +162,10 @@ export default function DataTable<TData extends RowData>({
|
||||||
}
|
}
|
||||||
style={conditionalRowStyles && getRowStyles(row)}
|
style={conditionalRowStyles && getRowStyles(row)}
|
||||||
>
|
>
|
||||||
{enableRowSelection &&
|
{enableRowSelection ?
|
||||||
<td key={`select-${row.id}`} className='pl-3 pr-1 border-y'>
|
<td key={`select-${row.id}`} className='pl-3 pr-1 border-y'>
|
||||||
<SelectRow row={row} />
|
<SelectRow row={row} />
|
||||||
</td>}
|
</td> : null}
|
||||||
{row.getVisibleCells().map(
|
{row.getVisibleCells().map(
|
||||||
(cell: Cell<TData, unknown>) => (
|
(cell: Cell<TData, unknown>) => (
|
||||||
<td
|
<td
|
||||||
|
@ -176,8 +176,8 @@ export default function DataTable<TData extends RowData>({
|
||||||
paddingBottom: dense ? '0.25rem': '0.5rem',
|
paddingBottom: dense ? '0.25rem': '0.5rem',
|
||||||
paddingTop: dense ? '0.25rem': '0.5rem'
|
paddingTop: dense ? '0.25rem': '0.5rem'
|
||||||
}}
|
}}
|
||||||
onClick={event => onRowClicked && onRowClicked(row.original, event)}
|
onClick={event => onRowClicked ? onRowClicked(row.original, event) : undefined}
|
||||||
onDoubleClick={event => onRowDoubleClicked && onRowDoubleClicked(row.original, event)}
|
onDoubleClick={event => onRowDoubleClicked ? onRowDoubleClicked(row.original, event) : undefined}
|
||||||
>
|
>
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
</td>
|
</td>
|
||||||
|
@ -186,7 +186,7 @@ export default function DataTable<TData extends RowData>({
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
||||||
{!noFooter &&
|
{!noFooter ?
|
||||||
<tfoot>
|
<tfoot>
|
||||||
{tableImpl.getFooterGroups().map(
|
{tableImpl.getFooterGroups().map(
|
||||||
(footerGroup: HeaderGroup<TData>) => (
|
(footerGroup: HeaderGroup<TData>) => (
|
||||||
|
@ -194,23 +194,21 @@ export default function DataTable<TData extends RowData>({
|
||||||
{footerGroup.headers.map(
|
{footerGroup.headers.map(
|
||||||
(header: Header<TData, unknown>) => (
|
(header: Header<TData, unknown>) => (
|
||||||
<th key={header.id}>
|
<th key={header.id}>
|
||||||
{header.isPlaceholder ? null
|
{!header.isPlaceholder ? flexRender(header.column.columnDef.footer, header.getContext()) : null}
|
||||||
: flexRender(header.column.columnDef.footer, header.getContext())
|
|
||||||
}
|
|
||||||
</th>
|
</th>
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tfoot>}
|
</tfoot> : null}
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
{enablePagination && !isEmpty &&
|
{(enablePagination && !isEmpty) ?
|
||||||
<PaginationTools
|
<PaginationTools
|
||||||
table={tableImpl}
|
table={tableImpl}
|
||||||
paginationOptions={paginationOptions}
|
paginationOptions={paginationOptions}
|
||||||
onChangePaginationOption={onChangePaginationOption}
|
onChangePaginationOption={onChangePaginationOption}
|
||||||
/>}
|
/> : null}
|
||||||
</div>
|
</div>
|
||||||
{isEmpty && (noDataComponent ?? <DefaultNoData />)}
|
{isEmpty ? (noDataComponent ?? <DefaultNoData />) : null}
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Table } from '@tanstack/react-table';
|
import { Table } from '@tanstack/react-table';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
|
import { prefixes } from '../../utils/constants';
|
||||||
import { GotoFirstIcon, GotoLastIcon, GotoNextIcon, GotoPrevIcon } from '../Icons';
|
import { GotoFirstIcon, GotoLastIcon, GotoNextIcon, GotoPrevIcon } from '../Icons';
|
||||||
|
|
||||||
interface PaginationToolsProps<TData> {
|
interface PaginationToolsProps<TData> {
|
||||||
|
@ -20,7 +21,7 @@ function PaginationTools<TData>({ table, paginationOptions, onChangePaginationOp
|
||||||
}, [onChangePaginationOption, table]);
|
}, [onChangePaginationOption, table]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='text-sm flex justify-end w-full items-center text-controls select-none my-2'>
|
<div className='flex items-center justify-end w-full my-2 text-sm select-none text-controls'>
|
||||||
<div className='flex items-center gap-1 mr-3'>
|
<div className='flex items-center gap-1 mr-3'>
|
||||||
<div className=''>
|
<div className=''>
|
||||||
{table.getState().pagination.pageIndex * table.getState().pagination.pageSize + 1}
|
{table.getState().pagination.pageIndex * table.getState().pagination.pageSize + 1}
|
||||||
|
@ -31,14 +32,14 @@ function PaginationTools<TData>({ table, paginationOptions, onChangePaginationOp
|
||||||
<div className=''>{table.getFilteredRowModel().rows.length}</div>
|
<div className=''>{table.getFilteredRowModel().rows.length}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex'>
|
<div className='flex'>
|
||||||
<button
|
<button type='button'
|
||||||
className='clr-hover text-controls'
|
className='clr-hover text-controls'
|
||||||
onClick={() => table.setPageIndex(0)}
|
onClick={() => table.setPageIndex(0)}
|
||||||
disabled={!table.getCanPreviousPage()}
|
disabled={!table.getCanPreviousPage()}
|
||||||
>
|
>
|
||||||
<GotoFirstIcon />
|
<GotoFirstIcon />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button type='button'
|
||||||
className='clr-hover text-controls'
|
className='clr-hover text-controls'
|
||||||
onClick={() => table.previousPage()}
|
onClick={() => table.previousPage()}
|
||||||
disabled={!table.getCanPreviousPage()}
|
disabled={!table.getCanPreviousPage()}
|
||||||
|
@ -47,7 +48,7 @@ function PaginationTools<TData>({ table, paginationOptions, onChangePaginationOp
|
||||||
</button>
|
</button>
|
||||||
<input type='text'
|
<input type='text'
|
||||||
title='Номер страницы. Выделите для ручного ввода'
|
title='Номер страницы. Выделите для ручного ввода'
|
||||||
className='w-6 clr-app text-center'
|
className='w-6 text-center clr-app'
|
||||||
value={table.getState().pagination.pageIndex + 1}
|
value={table.getState().pagination.pageIndex + 1}
|
||||||
onChange={event => {
|
onChange={event => {
|
||||||
const page = event.target.value ? Number(event.target.value) - 1 : 0;
|
const page = event.target.value ? Number(event.target.value) - 1 : 0;
|
||||||
|
@ -56,14 +57,15 @@ function PaginationTools<TData>({ table, paginationOptions, onChangePaginationOp
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<button
|
<button type='button'
|
||||||
className='clr-hover text-controls'
|
className='clr-hover text-controls'
|
||||||
onClick={() => table.nextPage()}
|
onClick={() => table.nextPage()}
|
||||||
disabled={!table.getCanNextPage()}
|
disabled={!table.getCanNextPage()}
|
||||||
>
|
>
|
||||||
<GotoNextIcon />
|
<GotoNextIcon />
|
||||||
</button>
|
</button>
|
||||||
<button className='clr-hover text-controls'
|
<button type='button'
|
||||||
|
className='clr-hover text-controls'
|
||||||
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
|
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
|
||||||
disabled={!table.getCanNextPage()}
|
disabled={!table.getCanNextPage()}
|
||||||
>
|
>
|
||||||
|
@ -73,11 +75,11 @@ function PaginationTools<TData>({ table, paginationOptions, onChangePaginationOp
|
||||||
<select
|
<select
|
||||||
value={table.getState().pagination.pageSize}
|
value={table.getState().pagination.pageSize}
|
||||||
onChange={handlePaginationOptionsChange}
|
onChange={handlePaginationOptionsChange}
|
||||||
className='clr-app mx-2 cursor-pointer'
|
className='mx-2 cursor-pointer clr-app'
|
||||||
>
|
>
|
||||||
{paginationOptions.map(
|
{paginationOptions.map(
|
||||||
pageSize => (
|
(pageSize) => (
|
||||||
<option key={pageSize} value={pageSize}>
|
<option key={`${prefixes.page_size}${pageSize}`} value={pageSize}>
|
||||||
{pageSize} на стр
|
{pageSize} на стр
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -8,16 +8,15 @@ interface SelectAllProps<TData> {
|
||||||
|
|
||||||
function SelectAll<TData>({ table }: SelectAllProps<TData>) {
|
function SelectAll<TData>({ table }: SelectAllProps<TData>) {
|
||||||
return (
|
return (
|
||||||
<Tristate
|
<Tristate tabIndex={-1}
|
||||||
tabIndex={-1}
|
tooltip='Выделить все'
|
||||||
value={
|
value={
|
||||||
!table.getIsAllPageRowsSelected() && table.getIsSomePageRowsSelected() ? null :
|
(!table.getIsAllPageRowsSelected() && table.getIsSomePageRowsSelected())
|
||||||
table.getIsAllPageRowsSelected()
|
? null
|
||||||
}
|
: table.getIsAllPageRowsSelected()
|
||||||
tooltip='Выделить все'
|
}
|
||||||
setValue={value => table.toggleAllPageRowsSelected(value !== false)}
|
setValue={value => table.toggleAllPageRowsSelected(value !== false)}
|
||||||
/>
|
/>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SelectAll;
|
export default SelectAll;
|
||||||
|
|
|
@ -8,12 +8,10 @@ interface SelectRowProps<TData> {
|
||||||
|
|
||||||
function SelectRow<TData>({ row }: SelectRowProps<TData>) {
|
function SelectRow<TData>({ row }: SelectRowProps<TData>) {
|
||||||
return (
|
return (
|
||||||
<Checkbox
|
<Checkbox tabIndex={-1}
|
||||||
tabIndex={-1}
|
value={row.getIsSelected()}
|
||||||
value={row.getIsSelected()}
|
setValue={row.getToggleSelectedHandler()}
|
||||||
setValue={row.getToggleSelectedHandler()}
|
/>);
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SelectRow;
|
export default SelectRow;
|
||||||
|
|
|
@ -12,7 +12,8 @@ function SortingIcon<TData>({ column }: SortingIconProps<TData>) {
|
||||||
desc: <DescendingIcon size={4} />,
|
desc: <DescendingIcon size={4} />,
|
||||||
asc: <AscendingIcon size={4}/>,
|
asc: <AscendingIcon size={4}/>,
|
||||||
}[column.getIsSorted() as string] ??
|
}[column.getIsSorted() as string] ??
|
||||||
<DescendingIcon size={4} color='opacity-0 hover:opacity-50' />}
|
<DescendingIcon size={4} color='opacity-0 hover:opacity-50' />
|
||||||
|
}
|
||||||
</>);
|
</>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col items-center antialiased clr-app' role='alert'>
|
<div className='flex flex-col items-center antialiased clr-app' role='alert'>
|
||||||
<h1 className='text-lg font-semibold'>Что-то пошло не так!</h1>
|
<h1 className='text-lg font-semibold'>Что-то пошло не так!</h1>
|
||||||
{ error }
|
{error}
|
||||||
<Button onClick={resetErrorBoundary} text='Попробовать еще раз' />
|
<Button onClick={resetErrorBoundary} text='Попробовать еще раз' />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -12,8 +12,7 @@ function Footer() {
|
||||||
<Link className='mx-2 hover:underline' to={urls.concept} tabIndex={-1}>Центр Концепт</Link>
|
<Link className='mx-2 hover:underline' to={urls.concept} tabIndex={-1}>Центр Концепт</Link>
|
||||||
<Link className='mx-2 hover:underline' to='/manuals?topic=exteor' tabIndex={-1}>Экстеор</Link>
|
<Link className='mx-2 hover:underline' to='/manuals?topic=exteor' tabIndex={-1}>Экстеор</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className=''>
|
<div>
|
||||||
|
|
||||||
<p className='mt-0.5 text-center'>© 2023 ЦИВТ КОНЦЕПТ</p>
|
<p className='mt-0.5 text-center'>© 2023 ЦИВТ КОНЦЕПТ</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -8,15 +8,32 @@ extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
|
|
||||||
function InfoConstituenta({ data, ...props }: InfoConstituentaProps) {
|
function InfoConstituenta({ data, ...props }: InfoConstituentaProps) {
|
||||||
return (
|
return (
|
||||||
<div {...props}>
|
<div {...props}>
|
||||||
<h1>Конституента {data.alias}</h1>
|
<h1>Конституента {data.alias}</h1>
|
||||||
<p><b>Типизация: </b>{labelCstTypification(data)}</p>
|
<p>
|
||||||
<p><b>Термин: </b>{data.term_resolved || data.term_raw}</p>
|
<b>Типизация: </b>
|
||||||
{data.definition_formal && <p><b>Выражение: </b>{data.definition_formal}</p>}
|
{labelCstTypification(data)}
|
||||||
{data.definition_resolved && <p><b>Определение: </b>{data.definition_resolved}</p>}
|
</p>
|
||||||
{data.convention && <p><b>Конвенция: </b>{data.convention}</p>}
|
<p>
|
||||||
</div>
|
<b>Термин: </b>
|
||||||
);
|
{data.term_resolved || data.term_raw}
|
||||||
|
</p>
|
||||||
|
{data.definition_formal ?
|
||||||
|
<p>
|
||||||
|
<b>Выражение: </b>
|
||||||
|
{data.definition_formal}
|
||||||
|
</p> : null}
|
||||||
|
{data.definition_resolved ?
|
||||||
|
<p>
|
||||||
|
<b>Определение: </b>
|
||||||
|
{data.definition_resolved}
|
||||||
|
</p> : null}
|
||||||
|
{data.convention ?
|
||||||
|
<p>
|
||||||
|
<b>Конвенция: </b>
|
||||||
|
{data.convention}
|
||||||
|
</p> : null}
|
||||||
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default InfoConstituenta;
|
export default InfoConstituenta;
|
||||||
|
|
|
@ -12,26 +12,25 @@ function InfoCstClass({ title }: InfoCstClassProps) {
|
||||||
const { colors } = useConceptTheme();
|
const { colors } = useConceptTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col gap-1 mb-2'>
|
<div className='flex flex-col gap-1 mb-2'>
|
||||||
{ title && <h1>{title}</h1>}
|
{title ? <h1>{title}</h1> : null}
|
||||||
{ Object.values(CstClass).map(
|
{Object.values(CstClass).map(
|
||||||
(cclass, index) => {
|
(cclass, index) => {
|
||||||
return (
|
return (
|
||||||
<p key={`${prefixes.cst_status_list}${index}`}>
|
<p key={`${prefixes.cst_status_list}${index}`}>
|
||||||
<span
|
<span
|
||||||
className='px-1 inline-block font-semibold min-w-[7rem] text-center border text-sm small-caps'
|
className='px-1 inline-block font-semibold min-w-[7rem] text-center border text-sm small-caps'
|
||||||
style={{backgroundColor: colorbgCstClass(cclass, colors)}}
|
style={{backgroundColor: colorbgCstClass(cclass, colors)}}
|
||||||
>
|
>
|
||||||
{labelCstClass(cclass)}
|
{labelCstClass(cclass)}
|
||||||
</span>
|
</span>
|
||||||
<span> - </span>
|
<span> - </span>
|
||||||
<span>
|
<span>
|
||||||
{describeCstClass(cclass)}
|
{describeCstClass(cclass)}
|
||||||
</span>
|
</span>
|
||||||
</p>);
|
</p>);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default InfoCstClass;
|
export default InfoCstClass;
|
||||||
|
|
|
@ -12,29 +12,28 @@ function InfoCstStatus({ title }: InfoCstStatusProps) {
|
||||||
const { colors } = useConceptTheme();
|
const { colors } = useConceptTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col gap-1 h-fit mb-2'>
|
<div className='flex flex-col gap-1 mb-2 h-fit'>
|
||||||
{ title && <h1>{title}</h1>}
|
{title ? <h1>{title}</h1> : null}
|
||||||
{ Object.values(ExpressionStatus)
|
{Object.values(ExpressionStatus)
|
||||||
.filter(status => status !== ExpressionStatus.UNDEFINED)
|
.filter(status => status !== ExpressionStatus.UNDEFINED)
|
||||||
.map(
|
.map(
|
||||||
(status, index) => {
|
(status, index) => {
|
||||||
return (
|
return (
|
||||||
<p key={`${prefixes.cst_status_list}${index}`}>
|
<p key={`${prefixes.cst_status_list}${index}`}>
|
||||||
<span
|
<span
|
||||||
className='px-1 inline-block font-semibold min-w-[7rem] text-center border text-sm small-caps'
|
className='px-1 inline-block font-semibold min-w-[7rem] text-center border text-sm small-caps'
|
||||||
style={{backgroundColor: colorbgCstStatus(status, colors)}}
|
style={{backgroundColor: colorbgCstStatus(status, colors)}}
|
||||||
>
|
>
|
||||||
{labelExpressionStatus(status)}
|
{labelExpressionStatus(status)}
|
||||||
</span>
|
</span>
|
||||||
<span> - </span>
|
<span> - </span>
|
||||||
<span>
|
<span>
|
||||||
{describeExpressionStatus(status)}
|
{describeExpressionStatus(status)}
|
||||||
</span>
|
</span>
|
||||||
</p>);
|
</p>);
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default InfoCstStatus;
|
export default InfoCstStatus;
|
||||||
|
|
|
@ -10,23 +10,25 @@ function Logo() {
|
||||||
const windowSize = useWindowSize();
|
const windowSize = useWindowSize();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to='/' className='flex items-center h-full mr-2' tabIndex={-1}>
|
<Link to='/' tabIndex={-1}
|
||||||
{ (windowSize.width && windowSize.width >= HIDE_LOGO_TEXT_LIMIT) && !darkMode &&
|
className='flex items-center h-full mr-2'
|
||||||
|
>
|
||||||
|
{(windowSize.width && windowSize.width >= HIDE_LOGO_TEXT_LIMIT && !darkMode) ?
|
||||||
<img alt=''
|
<img alt=''
|
||||||
src='/logo_full.svg'
|
src='/logo_full.svg'
|
||||||
className='max-h-[1.6rem] min-w-[1.6rem]'
|
className='max-h-[1.6rem] min-w-[1.6rem]'
|
||||||
/>}
|
/> : null}
|
||||||
{ (windowSize.width && windowSize.width >= HIDE_LOGO_TEXT_LIMIT) && darkMode &&
|
{(windowSize.width && windowSize.width >= HIDE_LOGO_TEXT_LIMIT && darkMode) ?
|
||||||
<img alt=''
|
<img alt=''
|
||||||
src='/logo_full_dark.svg'
|
src='/logo_full_dark.svg'
|
||||||
className='max-h-[1.6rem] min-w-[1.6rem]'
|
className='max-h-[1.6rem] min-w-[1.6rem]'
|
||||||
/>}
|
/> : null}
|
||||||
{ (!windowSize.width || windowSize.width < HIDE_LOGO_TEXT_LIMIT) &&
|
{(!windowSize.width || windowSize.width < HIDE_LOGO_TEXT_LIMIT) ?
|
||||||
<img alt=''
|
<img alt=''
|
||||||
src='/logo_sign.svg'
|
src='/logo_sign.svg'
|
||||||
className='max-h-[1.6rem] min-w-[2.2rem]'
|
className='max-h-[1.6rem] min-w-[2.2rem]'
|
||||||
|
|
||||||
/>}
|
/> : null}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,25 +15,23 @@ 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 &&
|
{noNavigation ?
|
||||||
<button
|
<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}
|
|
||||||
tabIndex={-1}
|
|
||||||
>
|
|
||||||
<p>{'>'}</p><p>{'>'}</p>
|
|
||||||
</button>}
|
|
||||||
{noNavigation &&
|
|
||||||
<button
|
|
||||||
title='Показать навигацию'
|
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'
|
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}
|
onClick={toggleNoNavigation}
|
||||||
tabIndex={-1}
|
|
||||||
>
|
>
|
||||||
{'∨∨∨'}
|
{'∨∨∨'}
|
||||||
</button>}
|
</button> : null}
|
||||||
{!noNavigation &&
|
{!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 ?
|
||||||
<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'>
|
||||||
<Logo />
|
<Logo />
|
||||||
|
@ -59,7 +57,7 @@ function Navigation () {
|
||||||
/>
|
/>
|
||||||
<UserMenu />
|
<UserMenu />
|
||||||
</div>
|
</div>
|
||||||
</div>}
|
</div> : null}
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,15 +8,13 @@ interface NavigationButtonProps {
|
||||||
|
|
||||||
function NavigationButton({ id, icon, description, onClick, text }: NavigationButtonProps) {
|
function NavigationButton({ id, icon, description, onClick, text }: NavigationButtonProps) {
|
||||||
return (
|
return (
|
||||||
<button id={id}
|
<button id={id} type='button' tabIndex={-1}
|
||||||
title={description}
|
title={description}
|
||||||
type='button'
|
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
tabIndex={-1}
|
|
||||||
className={`flex items-center h-full gap-1 ${text ? 'px-2' : 'px-4'} mr-1 small-caps whitespace-nowrap clr-btn-nav`}
|
className={`flex items-center h-full gap-1 ${text ? 'px-2' : 'px-4'} mr-1 small-caps whitespace-nowrap clr-btn-nav`}
|
||||||
>
|
>
|
||||||
{icon && <span>{icon}</span>}
|
{icon ? <span>{icon}</span> : null}
|
||||||
{text && <span className='font-semibold'>{text}</span>}
|
{text ? <span className='font-semibold'>{text}</span> : null}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,21 @@ import NavigationButton from './NavigationButton';
|
||||||
|
|
||||||
function ThemeSwitcher() {
|
function ThemeSwitcher() {
|
||||||
const { darkMode, toggleDarkMode } = useConceptTheme();
|
const { darkMode, toggleDarkMode } = useConceptTheme();
|
||||||
return (
|
if (darkMode) {
|
||||||
<>
|
return (
|
||||||
{darkMode && <NavigationButton icon={<LightThemeIcon />} description='Светлая тема' onClick={toggleDarkMode} />}
|
<NavigationButton
|
||||||
{!darkMode && <NavigationButton icon={<DarkThemeIcon />} description='Темная тема' onClick={toggleDarkMode} />}
|
description='Светлая тема'
|
||||||
</>
|
icon={<LightThemeIcon />}
|
||||||
);
|
onClick={toggleDarkMode}
|
||||||
|
/>);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<NavigationButton
|
||||||
|
description='Темная тема'
|
||||||
|
icon={<DarkThemeIcon />}
|
||||||
|
onClick={toggleDarkMode}
|
||||||
|
/>);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ThemeSwitcher;
|
export default ThemeSwitcher;
|
||||||
|
|
|
@ -14,24 +14,24 @@ function UserMenu() {
|
||||||
return (
|
return (
|
||||||
<div ref={menu.ref} className='h-full'>
|
<div ref={menu.ref} className='h-full'>
|
||||||
<div className='flex items-center justify-end h-full w-fit'>
|
<div className='flex items-center justify-end h-full w-fit'>
|
||||||
{ !user &&
|
{!user ?
|
||||||
<NavigationButton
|
<NavigationButton
|
||||||
text='Войти...'
|
text='Войти...'
|
||||||
description='Перейти на страницу логина'
|
description='Перейти на страницу логина'
|
||||||
icon={<InDoorIcon />}
|
icon={<InDoorIcon />}
|
||||||
onClick={navigateLogin}
|
onClick={navigateLogin}
|
||||||
/>}
|
/> : null}
|
||||||
{ user &&
|
{user ?
|
||||||
<NavigationButton
|
<NavigationButton
|
||||||
icon={<UserIcon />}
|
|
||||||
description={`Пользователь ${user?.username}`}
|
description={`Пользователь ${user?.username}`}
|
||||||
|
icon={<UserIcon />}
|
||||||
onClick={menu.toggle}
|
onClick={menu.toggle}
|
||||||
/>}
|
/> : null}
|
||||||
</div>
|
</div>
|
||||||
{ user && menu.isActive &&
|
{(user && menu.isActive) ?
|
||||||
<UserDropdown
|
<UserDropdown
|
||||||
hideDropdown={() => menu.hide()}
|
hideDropdown={() => menu.hide()}
|
||||||
/>}
|
/> : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,12 +152,12 @@ function RSInput({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`flex flex-col ${dimensions} ${cursor}`}>
|
<div className={`flex flex-col ${dimensions} ${cursor}`}>
|
||||||
{label &&
|
{label ?
|
||||||
<Label
|
<Label
|
||||||
text={label}
|
text={label}
|
||||||
htmlFor={id}
|
htmlFor={id}
|
||||||
className='mb-2'
|
className='mb-2'
|
||||||
/>}
|
/> : null}
|
||||||
<CodeMirror id={id}
|
<CodeMirror id={id}
|
||||||
ref={thisRef}
|
ref={thisRef}
|
||||||
basicSetup={editorSetup}
|
basicSetup={editorSetup}
|
||||||
|
|
|
@ -177,7 +177,7 @@ function RefsInput({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{ showEditor &&
|
{showEditor ?
|
||||||
<DlgEditReference
|
<DlgEditReference
|
||||||
hideWindow={() => setShowEditor(false)}
|
hideWindow={() => setShowEditor(false)}
|
||||||
items={items ?? []}
|
items={items ?? []}
|
||||||
|
@ -189,9 +189,8 @@ function RefsInput({
|
||||||
mainRefs: mainRefs
|
mainRefs: mainRefs
|
||||||
}}
|
}}
|
||||||
onSave={handleInputReference}
|
onSave={handleInputReference}
|
||||||
/>
|
/> : null}
|
||||||
}
|
{showResolve ?
|
||||||
{ showResolve &&
|
|
||||||
<Modal
|
<Modal
|
||||||
readonly
|
readonly
|
||||||
hideWindow={() => setShowResolve(false)}
|
hideWindow={() => setShowResolve(false)}
|
||||||
|
@ -199,16 +198,15 @@ function RefsInput({
|
||||||
<div className='max-h-[60vh] max-w-[80vw] overflow-auto'>
|
<div className='max-h-[60vh] max-w-[80vw] overflow-auto'>
|
||||||
<PrettyJson data={refsData} />
|
<PrettyJson data={refsData} />
|
||||||
</div>
|
</div>
|
||||||
</Modal>}
|
</Modal> : null}
|
||||||
<div className={`flex flex-col w-full ${cursor}`}>
|
<div className={`flex flex-col w-full ${cursor}`}>
|
||||||
{label &&
|
{label ?
|
||||||
<Label
|
<Label
|
||||||
text={label}
|
text={label}
|
||||||
htmlFor={id}
|
htmlFor={id}
|
||||||
className='mb-2'
|
className='mb-2'
|
||||||
/>}
|
/> : null}
|
||||||
<CodeMirror id={id}
|
<CodeMirror id={id} ref={thisRef}
|
||||||
ref={thisRef}
|
|
||||||
basicSetup={editorSetup}
|
basicSetup={editorSetup}
|
||||||
theme={customTheme}
|
theme={customTheme}
|
||||||
extensions={editorExtensions}
|
extensions={editorExtensions}
|
||||||
|
|
|
@ -8,19 +8,17 @@ interface RequireAuthProps {
|
||||||
function RequireAuth({ children }: RequireAuthProps) {
|
function RequireAuth({ children }: RequireAuthProps) {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
|
|
||||||
return (
|
if (user) {
|
||||||
<>
|
return children;
|
||||||
{user && children}
|
} else {
|
||||||
{!user &&
|
return (
|
||||||
<div className='flex flex-col items-center gap-1 mt-2'>
|
<div className='flex flex-col items-center gap-1 mt-2'>
|
||||||
<p className='mb-2'>Пожалуйста войдите в систему</p>
|
<p className='mb-2'>Пожалуйста войдите в систему</p>
|
||||||
<TextURL text='Войти в Портал' href='/login'/>
|
<TextURL text='Войти в Портал' href='/login'/>
|
||||||
<TextURL text='Зарегистрироваться' href='/signup'/>
|
<TextURL text='Зарегистрироваться' href='/signup'/>
|
||||||
<TextURL text='Начальная страница' href='/'/>
|
<TextURL text='Начальная страница' href='/'/>
|
||||||
</div>
|
</div>);
|
||||||
}
|
}
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RequireAuth;
|
export default RequireAuth;
|
||||||
|
|
|
@ -27,14 +27,18 @@ function ConstituentaBadge({ value, prefixID, shortTooltip, theme }: Constituent
|
||||||
>
|
>
|
||||||
{value.alias}
|
{value.alias}
|
||||||
</div>
|
</div>
|
||||||
{ !shortTooltip && <ConstituentaTooltip data={value} anchor={`#${prefixID}${value.alias}`} />}
|
{!shortTooltip ?
|
||||||
{ shortTooltip &&
|
<ConstituentaTooltip
|
||||||
|
anchor={`#${prefixID}${value.alias}`}
|
||||||
|
data={value}
|
||||||
|
/> : null}
|
||||||
|
{shortTooltip ?
|
||||||
<ConceptTooltip
|
<ConceptTooltip
|
||||||
anchorSelect={`#${prefixID}${value.alias}`}
|
anchorSelect={`#${prefixID}${value.alias}`}
|
||||||
place='right'
|
place='right'
|
||||||
>
|
>
|
||||||
<p><span className='font-semibold'>Статус</span>: {describeExpressionStatus(value.status)}</p>
|
<p><span className='font-semibold'>Статус</span>: {describeExpressionStatus(value.status)}</p>
|
||||||
</ConceptTooltip>}
|
</ConceptTooltip> : null}
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -127,7 +127,6 @@ function ArgumentsTab({ state, schema, partialUpdate }: ArgumentsTabProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col gap-3'>
|
<div className='flex flex-col gap-3'>
|
||||||
|
|
||||||
<div className='overflow-y-auto text-sm border select-none max-h-[5.8rem] min-h-[5.8rem]'>
|
<div className='overflow-y-auto text-sm border select-none max-h-[5.8rem] min-h-[5.8rem]'>
|
||||||
<DataTable dense noFooter
|
<DataTable dense noFooter
|
||||||
data={state.arguments}
|
data={state.arguments}
|
||||||
|
|
|
@ -58,8 +58,8 @@ function DlgCreateCst({ hideWindow, initial, schema, onCreate }: DlgCreateCstPro
|
||||||
<div className='flex justify-center w-full gap-6'>
|
<div className='flex justify-center w-full gap-6'>
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
className='my-2 min-w-[15rem] self-center'
|
className='my-2 min-w-[15rem] self-center'
|
||||||
options={SelectorCstType}
|
|
||||||
placeholder='Выберите тип'
|
placeholder='Выберите тип'
|
||||||
|
options={SelectorCstType}
|
||||||
value={{ value: cstData.cst_type, label: labelCstType(cstData.cst_type) }}
|
value={{ value: cstData.cst_type, label: labelCstType(cstData.cst_type) }}
|
||||||
onChange={data => updateCstData({ cst_type: data?.value ?? CstType.BASE})}
|
onChange={data => updateCstData({ cst_type: data?.value ?? CstType.BASE})}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -30,9 +30,9 @@ function DlgDeleteCst({ hideWindow, selected, onDelete }: DlgDeleteCstProps) {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title='Удаление конституент'
|
title='Удаление конституент'
|
||||||
|
submitText={expandOut ? 'Удалить с зависимыми' : 'Удалить'}
|
||||||
hideWindow={hideWindow}
|
hideWindow={hideWindow}
|
||||||
canSubmit={true}
|
canSubmit={true}
|
||||||
submitText={expandOut ? 'Удалить с зависимыми' : 'Удалить'}
|
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
>
|
>
|
||||||
<div className='max-w-[60vw] min-w-[20rem]'>
|
<div className='max-w-[60vw] min-w-[20rem]'>
|
||||||
|
@ -41,10 +41,10 @@ function DlgDeleteCst({ hideWindow, selected, onDelete }: DlgDeleteCstProps) {
|
||||||
{selected.map(
|
{selected.map(
|
||||||
(id) => {
|
(id) => {
|
||||||
const cst = schema!.items.find(cst => cst.id === id);
|
const cst = schema!.items.find(cst => cst.id === id);
|
||||||
return (cst &&
|
return (cst ?
|
||||||
<p key={`${prefixes.cst_delete_list}${cst.id}`}>
|
<p key={`${prefixes.cst_delete_list}${cst.id}`}>
|
||||||
{labelConstituenta(cst)}
|
{labelConstituenta(cst)}
|
||||||
</p>);
|
</p> : null);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<p className='mt-4'>Зависимые конституенты: <b>{expansion.length}</b></p>
|
<p className='mt-4'>Зависимые конституенты: <b>{expansion.length}</b></p>
|
||||||
|
@ -52,10 +52,10 @@ function DlgDeleteCst({ hideWindow, selected, onDelete }: DlgDeleteCstProps) {
|
||||||
{expansion.map(
|
{expansion.map(
|
||||||
(id) => {
|
(id) => {
|
||||||
const cst = schema!.items.find(cst => cst.id === id);
|
const cst = schema!.items.find(cst => cst.id === id);
|
||||||
return (cst &&
|
return (cst ?
|
||||||
<p key={`${prefixes.cst_dependant_list}${cst.id}`}>
|
<p key={`${prefixes.cst_dependant_list}${cst.id}`}>
|
||||||
{labelConstituenta(cst)}
|
{labelConstituenta(cst)}
|
||||||
</p>);
|
</p> : null);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|
|
@ -186,37 +186,31 @@ function DlgEditReference({ hideWindow, items, initial, onSave }: DlgEditReferen
|
||||||
<HelpTerminologyControl />
|
<HelpTerminologyControl />
|
||||||
</ConceptTooltip>
|
</ConceptTooltip>
|
||||||
</div>
|
</div>
|
||||||
{type !== ReferenceType.SYNTACTIC ? null :
|
{type === ReferenceType.SYNTACTIC ?
|
||||||
<div className='flex flex-col gap-2'>
|
<div className='flex flex-col gap-2'>
|
||||||
<div className='flex flex-start'>
|
<div className='flex flex-start'>
|
||||||
<TextInput id='offset' type='number'
|
<TextInput type='number' dense
|
||||||
label='Смещение'
|
label='Смещение'
|
||||||
dimensions='max-w-[10rem]'
|
dimensions='max-w-[10rem]'
|
||||||
dense
|
|
||||||
value={offset}
|
value={offset}
|
||||||
onChange={event => setOffset(event.target.valueAsNumber)}
|
onChange={event => setOffset(event.target.valueAsNumber)}
|
||||||
/>
|
/>
|
||||||
<div className='self-center ml-2 text-sm font-semibold whitespace-nowrap'>
|
<div className='self-center ml-2 text-sm font-semibold whitespace-nowrap'>
|
||||||
Основная ссылка:
|
Основная ссылка:
|
||||||
</div>
|
</div>
|
||||||
<TextInput
|
<TextInput disabled dense noBorder
|
||||||
dense
|
|
||||||
disabled
|
|
||||||
noBorder
|
|
||||||
value={mainLink}
|
value={mainLink}
|
||||||
dimensions='w-full text-sm'
|
dimensions='w-full text-sm'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<TextInput id='nominal' type='text'
|
<TextInput spellCheck
|
||||||
label='Начальная форма'
|
label='Начальная форма'
|
||||||
placeholder='зависимое слово в начальной форме'
|
placeholder='зависимое слово в начальной форме'
|
||||||
dimensions='w-full'
|
|
||||||
spellCheck
|
|
||||||
value={nominal}
|
value={nominal}
|
||||||
onChange={event => setNominal(event.target.value)}
|
onChange={event => setNominal(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>}
|
</div> : null}
|
||||||
{type !== ReferenceType.ENTITY ? null :
|
{type === ReferenceType.ENTITY ?
|
||||||
<div className='flex flex-col gap-2'>
|
<div className='flex flex-col gap-2'>
|
||||||
<ConstituentaPicker
|
<ConstituentaPicker
|
||||||
value={selectedCst}
|
value={selectedCst}
|
||||||
|
@ -230,11 +224,10 @@ function DlgEditReference({ hideWindow, items, initial, onSave }: DlgEditReferen
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className='flex gap-4 flex-start'>
|
<div className='flex gap-4 flex-start'>
|
||||||
<TextInput
|
<TextInput dense
|
||||||
label='Отсылаемая конституента'
|
label='Отсылаемая конституента'
|
||||||
placeholder='Имя'
|
placeholder='Имя'
|
||||||
dimensions='max-w-[16rem] min-w-[16rem] whitespace-nowrap'
|
dimensions='max-w-[16rem] min-w-[16rem] whitespace-nowrap'
|
||||||
dense
|
|
||||||
value={alias}
|
value={alias}
|
||||||
onChange={event => setAlias(event.target.value)}
|
onChange={event => setAlias(event.target.value)}
|
||||||
/>
|
/>
|
||||||
|
@ -242,10 +235,7 @@ function DlgEditReference({ hideWindow, items, initial, onSave }: DlgEditReferen
|
||||||
<div className='self-center text-sm font-semibold'>
|
<div className='self-center text-sm font-semibold'>
|
||||||
Термин:
|
Термин:
|
||||||
</div>
|
</div>
|
||||||
<TextInput
|
<TextInput disabled dense noBorder
|
||||||
dense
|
|
||||||
disabled
|
|
||||||
noBorder
|
|
||||||
value={term}
|
value={term}
|
||||||
tooltip={term}
|
tooltip={term}
|
||||||
dimensions='w-full text-sm'
|
dimensions='w-full text-sm'
|
||||||
|
@ -265,7 +255,7 @@ function DlgEditReference({ hideWindow, items, initial, onSave }: DlgEditReferen
|
||||||
onChange={newValue => setSelectedGrams([...newValue].sort(compareGrammemeOptions))}
|
onChange={newValue => setSelectedGrams([...newValue].sort(compareGrammemeOptions))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>}
|
</div> : null}
|
||||||
</div>
|
</div>
|
||||||
</Modal>);
|
</Modal>);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,10 +10,8 @@ interface WordformButtonProps {
|
||||||
|
|
||||||
function WordformButton({ text, example, grams, onSelectGrams, isSelected, ...props }: WordformButtonProps) {
|
function WordformButton({ text, example, grams, onSelectGrams, isSelected, ...props }: WordformButtonProps) {
|
||||||
return (
|
return (
|
||||||
<button
|
<button type='button' tabIndex={-1}
|
||||||
type='button'
|
|
||||||
onClick={() => onSelectGrams(grams)}
|
onClick={() => onSelectGrams(grams)}
|
||||||
tabIndex={-1}
|
|
||||||
className={`min-w-[6rem] p-1 border rounded-none cursor-pointer clr-btn-clear clr-hover ${isSelected ? 'clr-selected': ''}`}
|
className={`min-w-[6rem] p-1 border rounded-none cursor-pointer clr-btn-clear clr-hover ${isSelected ? 'clr-selected': ''}`}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { ArrowLeftIcon, ArrowRightIcon, CheckIcon, ChevronDoubleDownIcon, CrossI
|
||||||
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, wordFormEquals, parseGrammemes } from '../models/languageAPI';
|
import { getCompatibleGrams, parseGrammemes,wordFormEquals } from '../models/languageAPI';
|
||||||
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';
|
||||||
|
@ -169,12 +169,11 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
|
||||||
maxSize: 250,
|
maxSize: 250,
|
||||||
cell: props =>
|
cell: props =>
|
||||||
<div className='flex flex-wrap justify-start gap-1 select-none'>
|
<div className='flex flex-wrap justify-start gap-1 select-none'>
|
||||||
{ props.getValue().map(
|
{props.getValue().map(
|
||||||
gram =>
|
(gram) =>
|
||||||
<div
|
<div
|
||||||
key={`${props.cell.id}-${gram}`}
|
key={`${props.cell.id}-${gram}`}
|
||||||
className='min-w-[3rem] px-1 text-sm text-center rounded-md whitespace-nowrap'
|
className='min-w-[3rem] px-1 text-sm text-center rounded-md whitespace-nowrap'
|
||||||
title=''
|
|
||||||
style={{
|
style={{
|
||||||
borderWidth: '1px',
|
borderWidth: '1px',
|
||||||
borderColor: colorfgGrammeme(gram, colors),
|
borderColor: colorfgGrammeme(gram, colors),
|
||||||
|
@ -195,10 +194,9 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
|
||||||
maxSize: 50,
|
maxSize: 50,
|
||||||
cell: props =>
|
cell: props =>
|
||||||
<div>
|
<div>
|
||||||
<MiniButton
|
<MiniButton noHover
|
||||||
tooltip='Удалить словоформу'
|
tooltip='Удалить словоформу'
|
||||||
icon={<CrossIcon size={4} color='text-warning'/>}
|
icon={<CrossIcon size={4} color='text-warning'/>}
|
||||||
noHover
|
|
||||||
onClick={() => handleDeleteRow(props.row.index)}
|
onClick={() => handleDeleteRow(props.row.index)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -247,7 +245,6 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
|
||||||
placeholder='Введите текст'
|
placeholder='Введите текст'
|
||||||
rows={2}
|
rows={2}
|
||||||
dimensions='min-w-[18rem] w-full min-h-[4.2rem]'
|
dimensions='min-w-[18rem] w-full min-h-[4.2rem]'
|
||||||
|
|
||||||
value={inputText}
|
value={inputText}
|
||||||
onChange={event => setInputText(event.target.value)}
|
onChange={event => setInputText(event.target.value)}
|
||||||
/>
|
/>
|
||||||
|
@ -317,11 +314,10 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='border overflow-y-auto max-h-[17.4rem] min-h-[17.4rem] mb-2'>
|
<div className='border overflow-y-auto max-h-[17.4rem] min-h-[17.4rem] mb-2'>
|
||||||
<DataTable
|
<DataTable dense noFooter
|
||||||
data={forms}
|
data={forms}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
headPosition='0'
|
headPosition='0'
|
||||||
dense
|
|
||||||
noDataComponent={
|
noDataComponent={
|
||||||
<span className='flex flex-col justify-center p-2 text-center min-h-[2rem]'>
|
<span className='flex flex-col justify-center p-2 text-center min-h-[2rem]'>
|
||||||
<p>Список пуст</p>
|
<p>Список пуст</p>
|
||||||
|
|
|
@ -20,11 +20,10 @@ function DlgGraphOptions({ hideWindow, initial, onConfirm } : DlgGraphOptionsPro
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal canSubmit
|
||||||
hideWindow={hideWindow}
|
hideWindow={hideWindow}
|
||||||
title='Настройки графа термов'
|
title='Настройки графа термов'
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
canSubmit
|
|
||||||
submitText='Применить'
|
submitText='Применить'
|
||||||
>
|
>
|
||||||
<div className='flex gap-2'>
|
<div className='flex gap-2'>
|
||||||
|
@ -34,25 +33,25 @@ function DlgGraphOptions({ hideWindow, initial, onConfirm } : DlgGraphOptionsPro
|
||||||
label='Скрыть текст'
|
label='Скрыть текст'
|
||||||
tooltip='Не отображать термины'
|
tooltip='Не отображать термины'
|
||||||
value={params.noTerms}
|
value={params.noTerms}
|
||||||
setValue={ value => updateParams({noTerms: value}) }
|
setValue={value => updateParams({noTerms: value})}
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label='Скрыть несвязанные'
|
label='Скрыть несвязанные'
|
||||||
tooltip='Неиспользуемые конституенты'
|
tooltip='Неиспользуемые конституенты'
|
||||||
value={params.noHermits}
|
value={params.noHermits}
|
||||||
setValue={ value => updateParams({ noHermits: value}) }
|
setValue={value => updateParams({ noHermits: value})}
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label='Скрыть шаблоны'
|
label='Скрыть шаблоны'
|
||||||
tooltip='Терм-функции и предикат-функции с параметризованными аргументами'
|
tooltip='Терм-функции и предикат-функции с параметризованными аргументами'
|
||||||
value={params.noTemplates}
|
value={params.noTemplates}
|
||||||
setValue={ value => updateParams({ noTemplates: value}) }
|
setValue={value => updateParams({ noTemplates: value})}
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label='Транзитивная редукция'
|
label='Транзитивная редукция'
|
||||||
tooltip='Удалить связи, образующие транзитивные пути в графе'
|
tooltip='Удалить связи, образующие транзитивные пути в графе'
|
||||||
value={params.noTransitive}
|
value={params.noTransitive}
|
||||||
setValue={ value => updateParams({ noTransitive: value}) }
|
setValue={value => updateParams({ noTransitive: value})}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex flex-col gap-1'>
|
<div className='flex flex-col gap-1'>
|
||||||
|
@ -60,42 +59,42 @@ function DlgGraphOptions({ hideWindow, initial, onConfirm } : DlgGraphOptionsPro
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={labelCstType(CstType.BASE)}
|
label={labelCstType(CstType.BASE)}
|
||||||
value={params.allowBase}
|
value={params.allowBase}
|
||||||
setValue={ value => updateParams({ allowBase: value}) }
|
setValue={value => updateParams({ allowBase: value})}
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={labelCstType(CstType.STRUCTURED)}
|
label={labelCstType(CstType.STRUCTURED)}
|
||||||
value={params.allowStruct}
|
value={params.allowStruct}
|
||||||
setValue={ value => updateParams({ allowStruct: value}) }
|
setValue={value => updateParams({ allowStruct: value})}
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={labelCstType(CstType.TERM)}
|
label={labelCstType(CstType.TERM)}
|
||||||
value={params.allowTerm}
|
value={params.allowTerm}
|
||||||
setValue={ value => updateParams({ allowTerm: value}) }
|
setValue={value => updateParams({ allowTerm: value})}
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={labelCstType(CstType.AXIOM)}
|
label={labelCstType(CstType.AXIOM)}
|
||||||
value={params.allowAxiom}
|
value={params.allowAxiom}
|
||||||
setValue={ value => updateParams({ allowAxiom: value}) }
|
setValue={value => updateParams({ allowAxiom: value})}
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={labelCstType(CstType.FUNCTION)}
|
label={labelCstType(CstType.FUNCTION)}
|
||||||
value={params.allowFunction}
|
value={params.allowFunction}
|
||||||
setValue={ value => updateParams({ allowFunction: value}) }
|
setValue={value => updateParams({ allowFunction: value})}
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={labelCstType(CstType.PREDICATE)}
|
label={labelCstType(CstType.PREDICATE)}
|
||||||
value={params.allowPredicate}
|
value={params.allowPredicate}
|
||||||
setValue={ value => updateParams({ allowPredicate: value}) }
|
setValue={value => updateParams({ allowPredicate: value})}
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={labelCstType(CstType.CONSTANT)}
|
label={labelCstType(CstType.CONSTANT)}
|
||||||
value={params.allowConstant}
|
value={params.allowConstant}
|
||||||
setValue={ value => updateParams({ allowConstant: value}) }
|
setValue={value => updateParams({ allowConstant: value})}
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={labelCstType(CstType.THEOREM)}
|
label={labelCstType(CstType.THEOREM)}
|
||||||
value={params.allowTheorem}
|
value={params.allowTheorem}
|
||||||
setValue ={ value => updateParams({ allowTheorem: value}) }
|
setValue={value => updateParams({ allowTheorem: value})}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -42,23 +42,25 @@ function DlgRenameCst({ hideWindow, initial, onRename }: DlgRenameCstProps) {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title='Переименование конституенты'
|
title='Переименование конституенты'
|
||||||
|
submitText='Переименовать'
|
||||||
|
submitInvalidTooltip={'Введите незанятое имя, соответствующее типу'}
|
||||||
hideWindow={hideWindow}
|
hideWindow={hideWindow}
|
||||||
canSubmit={validated}
|
canSubmit={validated}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
submitInvalidTooltip={'Введите незанятое имя, соответствующее типу'}
|
|
||||||
submitText='Переименовать'
|
|
||||||
>
|
>
|
||||||
<div className='flex items-center gap-4 px-2 my-2 h-fit min-w-[25rem]'>
|
<div className='flex items-center gap-4 px-2 my-2 h-fit min-w-[25rem]'>
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
|
placeholder='Выберите тип'
|
||||||
className='min-w-[14rem] self-center z-modal-top'
|
className='min-w-[14rem] self-center z-modal-top'
|
||||||
options={SelectorCstType}
|
options={SelectorCstType}
|
||||||
placeholder='Выберите тип'
|
value={{
|
||||||
value={{ value: cstData.cst_type, label: labelCstType(cstData.cst_type) }}
|
value: cstData.cst_type,
|
||||||
|
label: labelCstType(cstData.cst_type)
|
||||||
|
}}
|
||||||
onChange={data => updateData({cst_type: data?.value ?? CstType.BASE})}
|
onChange={data => updateData({cst_type: data?.value ?? CstType.BASE})}
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<TextInput id='alias' label='Имя'
|
<TextInput id='alias' label='Имя' dense
|
||||||
dense
|
|
||||||
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})}
|
||||||
|
|
|
@ -59,16 +59,22 @@ function DlgShowAST({ hideWindow, syntaxTree, expression }: DlgShowASTProps) {
|
||||||
>
|
>
|
||||||
<div className='flex flex-col items-start gap-2'>
|
<div className='flex flex-col items-start gap-2'>
|
||||||
<div className='w-full text-lg text-center'>
|
<div className='w-full text-lg text-center'>
|
||||||
{!hoverNode && expression}
|
{!hoverNode ? expression : null}
|
||||||
{hoverNode &&
|
{hoverNode ?
|
||||||
<div>
|
<div>
|
||||||
<span>{expression.slice(0, hoverNode.start)}</span>
|
<span>{expression.slice(0, hoverNode.start)}</span>
|
||||||
<span className='clr-selected'>{expression.slice(hoverNode.start, hoverNode.finish)}</span>
|
<span className='clr-selected'>{expression.slice(hoverNode.start, hoverNode.finish)}</span>
|
||||||
<span>{expression.slice(hoverNode.finish)}</span>
|
<span>{expression.slice(hoverNode.finish)}</span>
|
||||||
</div>}
|
</div> : null}
|
||||||
</div>
|
</div>
|
||||||
<div className='flex-wrap w-full h-full overflow-auto'>
|
<div className='flex-wrap w-full h-full overflow-auto'>
|
||||||
<div className='relative' style={{width: 'calc(100vw - 6rem - 2px)', height: 'calc(100vh - 14rem - 2px)'}}>
|
<div
|
||||||
|
className='relative'
|
||||||
|
style={{
|
||||||
|
width: 'calc(100vw - 6rem - 2px)',
|
||||||
|
height: 'calc(100vh - 14rem - 2px)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
<GraphCanvas
|
<GraphCanvas
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
edges={edges}
|
edges={edges}
|
||||||
|
|
|
@ -38,28 +38,27 @@ function DlgUploadRSForm({ hideWindow }: DlgUploadRSFormProps) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title='Импорт схемы из Экстеора'
|
title='Импорт схемы из Экстеора'
|
||||||
hideWindow={hideWindow}
|
hideWindow={hideWindow}
|
||||||
canSubmit={!!file}
|
canSubmit={!!file}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
submitText='Загрузить'
|
submitText='Загрузить'
|
||||||
>
|
>
|
||||||
<div className='flex flex-col items-start min-w-[20rem] max-w-[20rem]'>
|
<div className='flex flex-col items-start min-w-[20rem] max-w-[20rem]'>
|
||||||
<FileInput
|
<FileInput
|
||||||
label='Выбрать файл'
|
label='Выбрать файл'
|
||||||
acceptType={EXTEOR_TRS_FILE}
|
acceptType={EXTEOR_TRS_FILE}
|
||||||
onChange={handleFile}
|
onChange={handleFile}
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label='Загружать название и комментарий'
|
label='Загружать название и комментарий'
|
||||||
value={loadMetadata}
|
dimensions='w-fit pb-2'
|
||||||
setValue={value => setLoadMetadata(value)}
|
value={loadMetadata}
|
||||||
dimensions='w-fit pb-2'
|
setValue={value => setLoadMetadata(value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DlgUploadRSForm;
|
export default DlgUploadRSForm;
|
||||||
|
|
|
@ -85,9 +85,7 @@ function CreateRSFormPage() {
|
||||||
>
|
>
|
||||||
<div className='relative w-full'>
|
<div className='relative w-full'>
|
||||||
<div className='absolute top-[-2.4rem] right-[-1rem] flex'>
|
<div className='absolute top-[-2.4rem] right-[-1rem] flex'>
|
||||||
<input
|
<input ref={inputRef} type='file'
|
||||||
type='file'
|
|
||||||
ref={inputRef}
|
|
||||||
style={{ display: 'none' }}
|
style={{ display: 'none' }}
|
||||||
accept={EXTEOR_TRS_FILE}
|
accept={EXTEOR_TRS_FILE}
|
||||||
onChange={handleFileChange}
|
onChange={handleFileChange}
|
||||||
|
@ -100,26 +98,29 @@ function CreateRSFormPage() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex flex-col gap-3'>
|
<div className='flex flex-col gap-3'>
|
||||||
{ fileName && <Label text={`Загружен файл: ${fileName}`} />}
|
{fileName ? <Label text={`Загружен файл: ${fileName}`} /> : null}
|
||||||
<TextInput id='title' label='Полное название' type='text'
|
<TextInput
|
||||||
required={!file}
|
label='Полное название'
|
||||||
placeholder={file && 'Загрузить из файла'}
|
placeholder={file && 'Загрузить из файла'}
|
||||||
|
required={!file}
|
||||||
value={title}
|
value={title}
|
||||||
onChange={event => setTitle(event.target.value)}
|
onChange={event => setTitle(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<TextInput id='alias' label='Сокращение' type='text'
|
<TextInput dense
|
||||||
dense
|
label='Сокращение'
|
||||||
|
placeholder={file && 'Загрузить из файла'}
|
||||||
required={!file}
|
required={!file}
|
||||||
value={alias}
|
value={alias}
|
||||||
placeholder={file && 'Загрузить из файла'}
|
|
||||||
onChange={event => setAlias(event.target.value)}
|
onChange={event => setAlias(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<TextArea id='comment' label='Комментарий'
|
<TextArea
|
||||||
value={comment}
|
label='Комментарий'
|
||||||
placeholder={file && 'Загрузить из файла'}
|
placeholder={file && 'Загрузить из файла'}
|
||||||
|
value={comment}
|
||||||
onChange={event => setComment(event.target.value)}
|
onChange={event => setComment(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<Checkbox id='common' label='Общедоступная схема'
|
<Checkbox
|
||||||
|
label='Общедоступная схема'
|
||||||
value={common}
|
value={common}
|
||||||
setValue={value => setCommon(value ?? false)}
|
setValue={value => setCommon(value ?? false)}
|
||||||
/>
|
/>
|
||||||
|
@ -131,11 +132,11 @@ function CreateRSFormPage() {
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
text='Отмена'
|
text='Отмена'
|
||||||
onClick={() => handleCancel()}
|
|
||||||
dimensions='min-w-[10rem]'
|
dimensions='min-w-[10rem]'
|
||||||
|
onClick={() => handleCancel()}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{ error && <BackendError error={error} />}
|
{error ? <BackendError error={error} /> : null}
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -21,10 +21,12 @@ function HomePage() {
|
||||||
}, [navigateTo, user])
|
}, [navigateTo, user])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col items-center justify-center w-full px-4 py-2'>
|
<div className='flex flex-col items-center justify-center w-full px-4 py-2'>
|
||||||
{ user?.is_staff && <p>Лендинг находится в разработке. Данная страница видна только пользователям с правами администратора.</p> }
|
{user?.is_staff ?
|
||||||
</div>
|
<p>
|
||||||
);
|
Лендинг находится в разработке. Данная страница видна только пользователям с правами администратора.
|
||||||
|
</p>: null}
|
||||||
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default HomePage;
|
export default HomePage;
|
||||||
|
|
|
@ -38,28 +38,26 @@ function LibraryPage() {
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full'>
|
<>
|
||||||
{ library.loading && <ConceptLoader /> }
|
{library.loading ? <ConceptLoader/> : null}
|
||||||
{ library.error && <BackendError error={library.error} />}
|
{library.error ? <BackendError error={library.error}/> : null}
|
||||||
{ !library.loading && library.items &&
|
{(!library.loading && library.items) ?
|
||||||
<div className='flex flex-col w-full'>
|
<div className='flex flex-col w-full'>
|
||||||
<SearchPanel
|
<SearchPanel
|
||||||
query={query}
|
query={query}
|
||||||
setQuery={setQuery}
|
setQuery={setQuery}
|
||||||
strategy={strategy}
|
strategy={strategy}
|
||||||
setStrategy={setStrategy}
|
setStrategy={setStrategy}
|
||||||
total={library.items.length ?? 0}
|
total={library.items.length ?? 0}
|
||||||
filtered={items.length}
|
filtered={items.length}
|
||||||
setFilter={setFilter}
|
setFilter={setFilter}
|
||||||
/>
|
/>
|
||||||
<ViewLibrary
|
<ViewLibrary
|
||||||
resetQuery={resetQuery}
|
resetQuery={resetQuery}
|
||||||
items={items}
|
items={items}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div> : null}
|
||||||
}
|
</>);
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LibraryPage;
|
export default LibraryPage;
|
||||||
|
|
|
@ -39,18 +39,16 @@ function PickerStrategy({ value, onChange }: PickerStrategyProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={strategyMenu.ref} className='h-full text-right'>
|
<div ref={strategyMenu.ref} className='h-full text-right'>
|
||||||
<SelectorButton
|
<SelectorButton transparent tabIndex={-1}
|
||||||
tooltip='Список фильтров'
|
tooltip='Список фильтров'
|
||||||
dimensions='w-fit h-full'
|
dimensions='w-fit h-full'
|
||||||
transparent
|
|
||||||
icon={<FilterIcon size={5} />}
|
icon={<FilterIcon size={5} />}
|
||||||
text={labelLibraryFilter(value)}
|
text={labelLibraryFilter(value)}
|
||||||
tabIndex={-1}
|
|
||||||
onClick={strategyMenu.toggle}
|
onClick={strategyMenu.toggle}
|
||||||
/>
|
/>
|
||||||
{ strategyMenu.isActive &&
|
{strategyMenu.isActive ?
|
||||||
<Dropdown>
|
<Dropdown>
|
||||||
{ Object.values(LibraryFilterStrategy).map(
|
{Object.values(LibraryFilterStrategy).map(
|
||||||
(enumValue, index) => {
|
(enumValue, index) => {
|
||||||
const strategy = enumValue as LibraryFilterStrategy;
|
const strategy = enumValue as LibraryFilterStrategy;
|
||||||
return (
|
return (
|
||||||
|
@ -63,7 +61,7 @@ function PickerStrategy({ value, onChange }: PickerStrategyProps) {
|
||||||
disabled={isStrategyDisabled(strategy)}
|
disabled={isStrategyDisabled(strategy)}
|
||||||
/>);
|
/>);
|
||||||
})}
|
})}
|
||||||
</Dropdown>}
|
</Dropdown> : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,33 +69,31 @@ function SearchPanel({ total, filtered, query, setQuery, strategy, setStrategy,
|
||||||
}, [strategy, navigateTo]);
|
}, [strategy, navigateTo]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='sticky top-0 left-0 right-0 flex items-stretch justify-start w-full border-b clr-input max-h-[2.3rem] pr-40'>
|
<div className='sticky top-0 left-0 right-0 flex items-stretch justify-start w-full border-b clr-input max-h-[2.3rem] pr-40'>
|
||||||
<div className='px-2 py-1 select-none whitespace-nowrap min-w-[10rem]'>
|
<div className='px-2 py-1 select-none whitespace-nowrap min-w-[10rem]'>
|
||||||
Фильтр
|
Фильтр
|
||||||
<span className='ml-2'>
|
<span className='ml-2'>
|
||||||
{filtered} из {total}
|
{filtered} из {total}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center justify-center w-full gap-1'>
|
<div className='flex items-center justify-center w-full gap-1'>
|
||||||
<div className='relative min-w-[10rem] select-none'>
|
<div className='relative min-w-[10rem] select-none'>
|
||||||
<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>
|
|
||||||
<input
|
|
||||||
type='text'
|
|
||||||
value={query}
|
|
||||||
className='w-full p-2 pl-10 text-sm outline-none clr-input'
|
|
||||||
placeholder='Поиск'
|
|
||||||
onChange={handleChangeQuery}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<PickerStrategy
|
<input
|
||||||
value={strategy}
|
placeholder='Поиск'
|
||||||
onChange={handleChangeStrategy}
|
value={query}
|
||||||
|
className='w-full p-2 pl-10 text-sm outline-none clr-input'
|
||||||
|
onChange={handleChangeQuery}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<PickerStrategy
|
||||||
|
value={strategy}
|
||||||
|
onChange={handleChangeStrategy}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SearchPanel;
|
export default SearchPanel;
|
||||||
|
|
|
@ -45,9 +45,18 @@ function ViewLibrary({ items, resetQuery: cleanQuery }: ViewLibraryProps) {
|
||||||
className='flex items-center justify-start gap-1 min-w-[2.75rem]'
|
className='flex items-center justify-start gap-1 min-w-[2.75rem]'
|
||||||
id={`${prefixes.library_list}${item.id}`}
|
id={`${prefixes.library_list}${item.id}`}
|
||||||
>
|
>
|
||||||
{user && user.subscriptions.includes(item.id) && <p title='Отслеживаемая'><SubscribedIcon size={3}/></p>}
|
{(user && user.subscriptions.includes(item.id)) ?
|
||||||
{item.is_common && <p title='Общедоступная'><GroupIcon size={3}/></p>}
|
<p title='Отслеживаемая'>
|
||||||
{item.is_canonical && <p title='Неизменная'><EducationIcon size={3}/></p>}
|
<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>
|
</div>
|
||||||
</>);
|
</>);
|
||||||
},
|
},
|
||||||
|
@ -94,51 +103,50 @@ function ViewLibrary({ items, resetQuery: cleanQuery }: ViewLibraryProps) {
|
||||||
], [intl, getUserLabel, user]);
|
], [intl, getUserLabel, user]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<>
|
||||||
{items.length > 0 &&
|
{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 flex gap-1 ml-3 z-pop'>
|
||||||
<div id='library-help' className='py-2 '>
|
<div id='library-help' className='py-2 '>
|
||||||
<HelpIcon color='text-primary' size={5} />
|
<HelpIcon color='text-primary' size={5} />
|
||||||
</div>
|
|
||||||
<ConceptTooltip anchorSelect='#library-help'>
|
|
||||||
<div className='max-w-[35rem]'>
|
|
||||||
<HelpLibrary />
|
|
||||||
</div>
|
|
||||||
</ConceptTooltip>
|
|
||||||
</div>
|
</div>
|
||||||
</div>}
|
<ConceptTooltip anchorSelect='#library-help'>
|
||||||
<DataTable
|
<div className='max-w-[35rem]'>
|
||||||
columns={columns}
|
<HelpLibrary />
|
||||||
data={items}
|
</div>
|
||||||
|
</ConceptTooltip>
|
||||||
headPosition='2.3rem'
|
|
||||||
noDataComponent={
|
|
||||||
<div className='flex flex-col gap-4 justify-center p-2 text-center min-h-[6rem]'>
|
|
||||||
<p>Список схем пуст</p>
|
|
||||||
<p className='flex justify-center gap-4'>
|
|
||||||
<TextURL text='Создать схему' href='/rsform-create'/>
|
|
||||||
<span className='cursor-pointer hover:underline text-url' onClick={cleanQuery}>
|
|
||||||
Очистить фильтр
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
</div>}
|
|
||||||
|
|
||||||
onRowClicked={openRSForm}
|
|
||||||
|
|
||||||
enableSorting
|
|
||||||
initialSorting={{
|
|
||||||
id: 'time_update',
|
|
||||||
desc: true
|
|
||||||
}}
|
|
||||||
|
|
||||||
enablePagination
|
|
||||||
paginationPerPage={itemsPerPage}
|
|
||||||
onChangePaginationOption={setItemsPerPage}
|
|
||||||
paginationOptions={[10, 20, 30, 50, 100]}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
</div> : null}
|
||||||
|
<DataTable
|
||||||
|
columns={columns}
|
||||||
|
data={items}
|
||||||
|
|
||||||
|
headPosition='2.3rem'
|
||||||
|
noDataComponent={
|
||||||
|
<div className='flex flex-col gap-4 justify-center p-2 text-center min-h-[6rem]'>
|
||||||
|
<p>Список схем пуст</p>
|
||||||
|
<p className='flex justify-center gap-4'>
|
||||||
|
<TextURL text='Создать схему' href='/rsform-create'/>
|
||||||
|
<span className='cursor-pointer hover:underline text-url' onClick={cleanQuery}>
|
||||||
|
Очистить фильтр
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>}
|
||||||
|
|
||||||
|
onRowClicked={openRSForm}
|
||||||
|
|
||||||
|
enableSorting
|
||||||
|
initialSorting={{
|
||||||
|
id: 'time_update',
|
||||||
|
desc: true
|
||||||
|
}}
|
||||||
|
|
||||||
|
enablePagination
|
||||||
|
paginationPerPage={itemsPerPage}
|
||||||
|
onChangePaginationOption={setItemsPerPage}
|
||||||
|
paginationOptions={[10, 20, 30, 50, 100]}
|
||||||
|
/>
|
||||||
|
</>);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ViewLibrary;
|
export default ViewLibrary;
|
||||||
|
|
|
@ -66,66 +66,62 @@ function LoginPage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex items-start justify-center w-full pt-8 select-none' style={{minHeight: mainHeight}}>
|
<div
|
||||||
{ user &&
|
className='flex items-start justify-center w-full pt-8 select-none'
|
||||||
<div className='flex flex-col items-center gap-2'>
|
style={{minHeight: mainHeight}}
|
||||||
<p className='font-semibold'>{`Вы вошли в систему как ${user.username}`}</p>
|
>
|
||||||
<p>
|
{user ?
|
||||||
<TextURL text='Создать схему' href='/rsform-create'/>
|
<div className='flex flex-col items-center gap-2'>
|
||||||
<span> | </span>
|
<p className='font-semibold'>{`Вы вошли в систему как ${user.username}`}</p>
|
||||||
<TextURL text='Библиотека' href='/library'/>
|
<p>
|
||||||
<span> | </span>
|
<TextURL text='Создать схему' href='/rsform-create'/>
|
||||||
<TextURL text='Справка' href='/manuals'/>
|
<span> | </span>
|
||||||
<span> | </span>
|
<TextURL text='Библиотека' href='/library'/>
|
||||||
<span
|
<span> | </span>
|
||||||
className='cursor-pointer hover:underline text-url'
|
<TextURL text='Справка' href='/manuals'/>
|
||||||
onClick={logoutAndRedirect}
|
<span> | </span>
|
||||||
>
|
<span
|
||||||
Выйти
|
className='cursor-pointer hover:underline text-url'
|
||||||
</span>
|
onClick={logoutAndRedirect}
|
||||||
</p>
|
>
|
||||||
</div>}
|
Выйти
|
||||||
{ !user &&
|
</span>
|
||||||
<Form
|
</p>
|
||||||
onSubmit={handleSubmit}
|
</div> : null}
|
||||||
dimensions='w-[24rem]'
|
{!user ?
|
||||||
>
|
<Form
|
||||||
<img alt='Концепт Портал'
|
onSubmit={handleSubmit}
|
||||||
src='/logo_full.svg'
|
dimensions='w-[24rem]'
|
||||||
className='max-h-[2.5rem] min-w-[2.5rem] mt-2 mb-4'
|
>
|
||||||
/>
|
<img alt='Концепт Портал'
|
||||||
<TextInput id='username' type='text'
|
src='/logo_full.svg'
|
||||||
label='Имя пользователя'
|
className='max-h-[2.5rem] min-w-[2.5rem] mt-2 mb-4'
|
||||||
required
|
/>
|
||||||
allowEnter
|
<TextInput id='username' autoFocus required allowEnter
|
||||||
value={username}
|
label='Имя пользователя'
|
||||||
autoFocus
|
value={username}
|
||||||
onChange={event => setUsername(event.target.value)}
|
onChange={event => setUsername(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<TextInput id='password' type='password'
|
<TextInput id='password' type='password' required allowEnter
|
||||||
label='Пароль'
|
label='Пароль'
|
||||||
required
|
value={password}
|
||||||
allowEnter
|
onChange={event => setPassword(event.target.value)}
|
||||||
value={password}
|
/>
|
||||||
onChange={event => setPassword(event.target.value)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className='flex justify-center w-full py-2'>
|
<div className='flex justify-center w-full py-2'>
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
text='Войти'
|
text='Войти'
|
||||||
dimensions='w-[12rem]'
|
dimensions='w-[12rem]'
|
||||||
loading={loading}
|
loading={loading}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex flex-col text-sm'>
|
<div className='flex flex-col text-sm'>
|
||||||
<TextURL text='Восстановить пароль...' href='/restore-password' />
|
<TextURL text='Восстановить пароль...' href='/restore-password' />
|
||||||
<TextURL text='Нет аккаунта? Зарегистрируйтесь...' href='/signup' />
|
<TextURL text='Нет аккаунта? Зарегистрируйтесь...' href='/signup' />
|
||||||
</div>
|
</div>
|
||||||
{ error && <ProcessError error={error} />}
|
{error ? <ProcessError error={error} /> : null}
|
||||||
</Form>
|
</Form> : null}
|
||||||
}
|
</div>);
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LoginPage;
|
export default LoginPage;
|
||||||
|
|
|
@ -34,16 +34,16 @@ function ManualsPage() {
|
||||||
}, [search, setActiveTopic, navigateTopic]);
|
}, [search, setActiveTopic, navigateTopic]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex w-full gap-2 justify-start items-start' style={{minHeight: mainHeight}}>
|
<div
|
||||||
<TopicsList
|
className='flex items-start justify-start w-full gap-2'
|
||||||
activeTopic={activeTopic}
|
style={{minHeight: mainHeight}}
|
||||||
onChangeTopic={topic => onSelectTopic(topic)}
|
>
|
||||||
/>
|
<TopicsList
|
||||||
<ViewTopic
|
activeTopic={activeTopic}
|
||||||
topic={activeTopic}
|
onChangeTopic={topic => onSelectTopic(topic)}
|
||||||
/>
|
/>
|
||||||
</div>
|
<ViewTopic topic={activeTopic} />
|
||||||
);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ManualsPage;
|
export default ManualsPage;
|
||||||
|
|
|
@ -11,16 +11,17 @@ function TopicsList({ activeTopic, onChangeTopic }: TopicsListProps) {
|
||||||
return (
|
return (
|
||||||
<div className='sticky top-0 left-0 border-x min-w-[13rem] select-none flex flex-col clr-controls small-caps h-fit'>
|
<div className='sticky top-0 left-0 border-x min-w-[13rem] select-none flex flex-col clr-controls small-caps h-fit'>
|
||||||
<div className='my-2 text-lg text-center'>Справка</div>
|
<div className='my-2 text-lg text-center'>Справка</div>
|
||||||
{ Object.values(HelpTopic).map(
|
{Object.values(HelpTopic).map(
|
||||||
(topic, index) => {
|
(topic, index) => {
|
||||||
|
const isActive = activeTopic === topic;
|
||||||
return (
|
return (
|
||||||
<div key={`${prefixes.topic_list}${index}`}
|
<div key={`${prefixes.topic_list}${index}`}
|
||||||
className={`px-3 py-1 border-y cursor-pointer clr-hover ${activeTopic === topic ? 'clr-selected ' : ''}`}
|
className={`px-3 py-1 border-y cursor-pointer clr-hover ${isActive ? 'clr-selected ' : ''}`}
|
||||||
title={describeHelpTopic(topic)}
|
title={describeHelpTopic(topic)}
|
||||||
onClick={() => onChangeTopic(topic)}
|
onClick={() => onChangeTopic(topic)}
|
||||||
>
|
>
|
||||||
{labelHelpTopic(topic)}
|
{labelHelpTopic(topic)}
|
||||||
</div>)
|
</div>);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -17,20 +17,19 @@ 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 />}
|
{topic === HelpTopic.MAIN ? <HelpMain /> : null}
|
||||||
{topic === HelpTopic.LIBRARY && <HelpLibrary />}
|
{topic === HelpTopic.LIBRARY ? <HelpLibrary /> : null}
|
||||||
{topic === HelpTopic.RSFORM && <HelpRSFormMeta />}
|
{topic === HelpTopic.RSFORM ? <HelpRSFormMeta /> : null}
|
||||||
{topic === HelpTopic.CSTLIST && <HelpRSFormItems />}
|
{topic === HelpTopic.CSTLIST ? <HelpRSFormItems /> : null}
|
||||||
{topic === HelpTopic.CONSTITUENTA && <HelpConstituenta />}
|
{topic === HelpTopic.CONSTITUENTA ? <HelpConstituenta /> : null}
|
||||||
{topic === HelpTopic.GRAPH_TERM && <HelpTermGraph />}
|
{topic === HelpTopic.GRAPH_TERM ? <HelpTermGraph /> : null}
|
||||||
{topic === HelpTopic.RSTEMPLATES && <HelpRSTemplates />}
|
{topic === HelpTopic.RSTEMPLATES ? <HelpRSTemplates /> : null}
|
||||||
{topic === HelpTopic.RSLANG && <HelpRSLang />}
|
{topic === HelpTopic.RSLANG ? <HelpRSLang /> : null}
|
||||||
{topic === HelpTopic.TERM_CONTROL && <HelpTerminologyControl />}
|
{topic === HelpTopic.TERM_CONTROL ? <HelpTerminologyControl /> : null}
|
||||||
{topic === HelpTopic.EXTEOR && <HelpExteor />}
|
{topic === HelpTopic.EXTEOR ? <HelpExteor /> : null}
|
||||||
{topic === HelpTopic.API && <HelpAPI />}
|
{topic === HelpTopic.API ? <HelpAPI /> : null}
|
||||||
</div>
|
</div>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ViewTopic;
|
export default ViewTopic;
|
||||||
|
|
|
@ -2,12 +2,11 @@ import TextURL from '../components/common/TextURL';
|
||||||
|
|
||||||
export function NotFoundPage() {
|
export function NotFoundPage() {
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col px-4 py-2'>
|
<div className='flex flex-col px-4 py-2'>
|
||||||
<h1 className='text-xl font-semibold'>Ошибка 404 - Страница не найдена</h1>
|
<h1 className='text-xl font-semibold'>Ошибка 404 - Страница не найдена</h1>
|
||||||
<p className='mt-2'>Данная страница не существует или запрашиваемый объект отсутствует в базе данных</p>
|
<p className='mt-2'>Данная страница не существует или запрашиваемый объект отсутствует в базе данных</p>
|
||||||
<TextURL href='/' text='Вернуться на Портал' />
|
<TextURL href='/' text='Вернуться на Портал' />
|
||||||
</div>
|
</div>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NotFoundPage;
|
export default NotFoundPage;
|
||||||
|
|
|
@ -161,8 +161,14 @@ function EditorConstituenta({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex max-w-[1500px]' tabIndex={-1} onKeyDown={handleInput}>
|
<div tabIndex={-1}
|
||||||
<form onSubmit={handleSubmit} className='min-w-[47.8rem] max-w-[47.8rem] px-4 py-1'>
|
className='flex max-w-[1500px]'
|
||||||
|
onKeyDown={handleInput}
|
||||||
|
>
|
||||||
|
<form
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
className='min-w-[47.8rem] max-w-[47.8rem] px-4 py-1'
|
||||||
|
>
|
||||||
<div className='relative w-full'>
|
<div className='relative w-full'>
|
||||||
<div className='absolute top-0 right-0 flex items-start justify-between w-full'>
|
<div className='absolute top-0 right-0 flex items-start justify-between w-full'>
|
||||||
{activeCst &&
|
{activeCst &&
|
||||||
|
@ -179,10 +185,9 @@ function EditorConstituenta({
|
||||||
<span className='small-caps'>Конституента </span>
|
<span className='small-caps'>Конституента </span>
|
||||||
<span className='ml-1 small-caps'>{alias}</span>
|
<span className='ml-1 small-caps'>{alias}</span>
|
||||||
</div>
|
</div>
|
||||||
<MiniButton
|
<MiniButton noHover
|
||||||
tooltip='Переименовать конституенту'
|
tooltip='Переименовать конституенту'
|
||||||
disabled={!isEnabled}
|
disabled={!isEnabled}
|
||||||
noHover
|
|
||||||
onClick={handleRename}
|
onClick={handleRename}
|
||||||
icon={<EditIcon size={4} color={isEnabled ? 'text-primary' : ''} />}
|
icon={<EditIcon size={4} color={isEnabled ? 'text-primary' : ''} />}
|
||||||
/>
|
/>
|
||||||
|
@ -228,7 +233,8 @@ function EditorConstituenta({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex flex-col gap-3 mt-1'>
|
<div className='flex flex-col gap-3 mt-1'>
|
||||||
<RefsInput id='term' label='Термин'
|
<RefsInput
|
||||||
|
label='Термин'
|
||||||
placeholder='Обозначение, используемое в текстовых определениях данной схемы'
|
placeholder='Обозначение, используемое в текстовых определениях данной схемы'
|
||||||
items={schema?.items}
|
items={schema?.items}
|
||||||
value={term}
|
value={term}
|
||||||
|
@ -237,8 +243,8 @@ function EditorConstituenta({
|
||||||
disabled={!isEnabled}
|
disabled={!isEnabled}
|
||||||
onChange={newValue => setTerm(newValue)}
|
onChange={newValue => setTerm(newValue)}
|
||||||
/>
|
/>
|
||||||
<TextArea id='typification' label='Типизация'
|
<TextArea dense noBorder
|
||||||
dense noBorder
|
label='Типизация'
|
||||||
rows={typification.length > 70 ? 2 : 1}
|
rows={typification.length > 70 ? 2 : 1}
|
||||||
value={typification}
|
value={typification}
|
||||||
colors='clr-app'
|
colors='clr-app'
|
||||||
|
@ -248,7 +254,8 @@ function EditorConstituenta({
|
||||||
}}
|
}}
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
<EditorRSExpression id='expression' label='Формальное определение'
|
<EditorRSExpression
|
||||||
|
label='Формальное определение'
|
||||||
activeCst={activeCst}
|
activeCst={activeCst}
|
||||||
placeholder='Родоструктурное выражение, задающее формальное определение'
|
placeholder='Родоструктурное выражение, задающее формальное определение'
|
||||||
value={expression}
|
value={expression}
|
||||||
|
@ -258,7 +265,8 @@ function EditorConstituenta({
|
||||||
onChange={newValue => setExpression(newValue)}
|
onChange={newValue => setExpression(newValue)}
|
||||||
setTypification={setTypification}
|
setTypification={setTypification}
|
||||||
/>
|
/>
|
||||||
<RefsInput id='definition' label='Текстовое определение'
|
<RefsInput
|
||||||
|
label='Текстовое определение'
|
||||||
placeholder='Лингвистическая интерпретация формального выражения'
|
placeholder='Лингвистическая интерпретация формального выражения'
|
||||||
items={schema?.items}
|
items={schema?.items}
|
||||||
value={textDefinition}
|
value={textDefinition}
|
||||||
|
@ -267,11 +275,11 @@ function EditorConstituenta({
|
||||||
disabled={!isEnabled}
|
disabled={!isEnabled}
|
||||||
onChange={newValue => setTextDefinition(newValue)}
|
onChange={newValue => setTextDefinition(newValue)}
|
||||||
/>
|
/>
|
||||||
<TextArea id='convention' label='Конвенция / Комментарий'
|
<TextArea spellCheck
|
||||||
|
label='Конвенция / Комментарий'
|
||||||
placeholder='Договоренность об интерпретации или пояснение'
|
placeholder='Договоренность об интерпретации или пояснение'
|
||||||
value={convention}
|
value={convention}
|
||||||
disabled={!isEnabled}
|
disabled={!isEnabled}
|
||||||
spellCheck
|
|
||||||
onChange={event => setConvention(event.target.value)}
|
onChange={event => setConvention(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<div className='flex justify-center w-full'>
|
<div className='flex justify-center w-full'>
|
||||||
|
@ -283,7 +291,7 @@ function EditorConstituenta({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{(windowSize.width ?? 0) >= SIDELIST_HIDE_THRESHOLD &&
|
{(windowSize.width ?? 0) >= SIDELIST_HIDE_THRESHOLD ?
|
||||||
<div className='w-full mt-[2.25rem] border h-fit'>
|
<div className='w-full mt-[2.25rem] border h-fit'>
|
||||||
<ViewSideConstituents
|
<ViewSideConstituents
|
||||||
expression={expression}
|
expression={expression}
|
||||||
|
@ -291,7 +299,7 @@ function EditorConstituenta({
|
||||||
activeID={activeID}
|
activeID={activeID}
|
||||||
onOpenEdit={onOpenEdit}
|
onOpenEdit={onOpenEdit}
|
||||||
/>
|
/>
|
||||||
</div>}
|
</div> : null}
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -290,10 +290,9 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst, onTemplates }: Edit
|
||||||
], [colors]);
|
], [colors]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div tabIndex={-1}
|
||||||
className='w-full outline-none'
|
className='w-full outline-none'
|
||||||
style={{minHeight: mainHeight}}
|
style={{minHeight: mainHeight}}
|
||||||
tabIndex={-1}
|
|
||||||
onKeyDown={handleTableKey}
|
onKeyDown={handleTableKey}
|
||||||
>
|
>
|
||||||
<div className='sticky top-0 flex justify-start w-full gap-1 px-2 py-1 border-b items-center h-[2.2rem] select-none clr-app'>
|
<div className='sticky top-0 flex justify-start w-full gap-1 px-2 py-1 border-b items-center h-[2.2rem] select-none clr-app'>
|
||||||
|
@ -313,11 +312,10 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst, onTemplates }: Edit
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='w-full h-full text-sm'>
|
<div className='w-full h-full text-sm'>
|
||||||
<DataTable
|
<DataTable dense noFooter
|
||||||
data={schema?.items ?? []}
|
data={schema?.items ?? []}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
headPosition='2.2rem'
|
headPosition='2.2rem'
|
||||||
dense
|
|
||||||
|
|
||||||
onRowDoubleClicked={handleRowDoubleClicked}
|
onRowDoubleClicked={handleRowDoubleClicked}
|
||||||
onRowClicked={handleRowClicked}
|
onRowClicked={handleRowClicked}
|
||||||
|
|
|
@ -20,7 +20,7 @@ import RSEditorControls from './elements/RSEditorControls';
|
||||||
import StatusBar from './elements/StatusBar';
|
import StatusBar from './elements/StatusBar';
|
||||||
|
|
||||||
interface EditorRSExpressionProps {
|
interface EditorRSExpressionProps {
|
||||||
id: string
|
id?: string
|
||||||
activeCst?: IConstituenta
|
activeCst?: IConstituenta
|
||||||
label: string
|
label: string
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
|
@ -141,12 +141,11 @@ function EditorRSExpression({
|
||||||
/>
|
/>
|
||||||
<div className='w-full max-h-[4.5rem] min-h-[4.5rem] flex'>
|
<div className='w-full max-h-[4.5rem] min-h-[4.5rem] flex'>
|
||||||
<div className='flex flex-col text-sm'>
|
<div className='flex flex-col text-sm'>
|
||||||
<Button
|
<Button noOutline
|
||||||
tooltip='Проверить формальное определение'
|
|
||||||
text='Проверить'
|
text='Проверить'
|
||||||
|
tooltip='Проверить формальное определение'
|
||||||
dimensions='w-[6.75rem] min-h-[3rem] z-pop rounded-none'
|
dimensions='w-[6.75rem] min-h-[3rem] z-pop rounded-none'
|
||||||
colors='clr-btn-default'
|
colors='clr-btn-default'
|
||||||
noOutline
|
|
||||||
onClick={() => handleCheckExpression()}
|
onClick={() => handleCheckExpression()}
|
||||||
/>
|
/>
|
||||||
<StatusBar
|
<StatusBar
|
||||||
|
@ -156,19 +155,18 @@ function EditorRSExpression({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='w-full overflow-y-auto text-sm border rounded-none'>
|
<div className='w-full overflow-y-auto text-sm border rounded-none'>
|
||||||
{ loading && <ConceptLoader size={6} />}
|
{loading ? <ConceptLoader size={6} /> : null}
|
||||||
{ !loading && parseData &&
|
{(!loading && parseData) ?
|
||||||
<ParsingResult
|
<ParsingResult
|
||||||
data={parseData}
|
data={parseData}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onShowError={onShowError}
|
onShowError={onShowError}
|
||||||
/>}
|
/> : null}
|
||||||
{ !loading && !parseData &&
|
{(!loading && !parseData) ?
|
||||||
<input
|
<input disabled
|
||||||
disabled={true}
|
|
||||||
className='w-full px-2 py-1 text-base select-none h-fit clr-app'
|
className='w-full px-2 py-1 text-base select-none h-fit clr-app'
|
||||||
placeholder='Результаты проверки выражения'
|
placeholder='Результаты проверки выражения'
|
||||||
/>}
|
/> : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>);
|
</div>);
|
||||||
|
|
|
@ -205,7 +205,9 @@ function EditorRSForm({ onDestroy, onClaim, onShare, isModified, setIsModified,
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{schema && <Divider vertical />}
|
|
||||||
|
<Divider vertical />
|
||||||
|
|
||||||
<RSFormStats stats={schema?.stats}/>
|
<RSFormStats stats={schema?.stats}/>
|
||||||
</div>
|
</div>
|
||||||
</div>);
|
</div>);
|
||||||
|
|
|
@ -471,7 +471,10 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='w-full h-full overflow-auto outline-none' tabIndex={-1} onKeyDown={handleKeyDown}>
|
<div tabIndex={-1}
|
||||||
|
className='w-full h-full overflow-auto outline-none'
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
className='relative'
|
className='relative'
|
||||||
style={{width: canvasWidth, height: canvasHeight}}
|
style={{width: canvasWidth, height: canvasHeight}}
|
||||||
|
|
|
@ -335,54 +335,54 @@ function RSTabs() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full'>
|
<div className='w-full'>
|
||||||
{ loading && <ConceptLoader /> }
|
{loading ? <ConceptLoader /> : null}
|
||||||
{ error && <ProcessError error={error} />}
|
{error ? <ProcessError error={error} /> : null}
|
||||||
{ schema && !loading && <>
|
{(schema && !loading) ? <>
|
||||||
{showUpload &&
|
{showUpload ?
|
||||||
<DlgUploadRSForm
|
<DlgUploadRSForm
|
||||||
hideWindow={() => setShowUpload(false)}
|
hideWindow={() => setShowUpload(false)}
|
||||||
/>}
|
/> : null}
|
||||||
{showClone &&
|
{showClone ?
|
||||||
<DlgCloneRSForm
|
<DlgCloneRSForm
|
||||||
hideWindow={() => setShowClone(false)}
|
hideWindow={() => setShowClone(false)}
|
||||||
/>}
|
/> : null}
|
||||||
{showAST &&
|
{showAST ?
|
||||||
<DlgShowAST
|
<DlgShowAST
|
||||||
expression={expression}
|
expression={expression}
|
||||||
syntaxTree={syntaxTree}
|
syntaxTree={syntaxTree}
|
||||||
hideWindow={() => setShowAST(false)}
|
hideWindow={() => setShowAST(false)}
|
||||||
/>}
|
/> : null}
|
||||||
{showCreateCst &&
|
{showCreateCst ?
|
||||||
<DlgCreateCst
|
<DlgCreateCst
|
||||||
hideWindow={() => setShowCreateCst(false)}
|
hideWindow={() => setShowCreateCst(false)}
|
||||||
onCreate={handleCreateCst}
|
onCreate={handleCreateCst}
|
||||||
schema={schema}
|
schema={schema}
|
||||||
initial={createInitialData}
|
initial={createInitialData}
|
||||||
/>}
|
/> : null}
|
||||||
{showRenameCst &&
|
{showRenameCst ?
|
||||||
<DlgRenameCst
|
<DlgRenameCst
|
||||||
hideWindow={() => setShowRenameCst(false)}
|
hideWindow={() => setShowRenameCst(false)}
|
||||||
onRename={handleRenameCst}
|
onRename={handleRenameCst}
|
||||||
initial={renameInitialData!}
|
initial={renameInitialData!}
|
||||||
/>}
|
/> : null}
|
||||||
{showDeleteCst &&
|
{showDeleteCst ?
|
||||||
<DlgDeleteCst
|
<DlgDeleteCst
|
||||||
hideWindow={() => setShowDeleteCst(false)}
|
hideWindow={() => setShowDeleteCst(false)}
|
||||||
onDelete={handleDeleteCst}
|
onDelete={handleDeleteCst}
|
||||||
selected={toBeDeleted}
|
selected={toBeDeleted}
|
||||||
/>}
|
/> : null}
|
||||||
{showEditTerm &&
|
{showEditTerm ?
|
||||||
<DlgEditWordForms
|
<DlgEditWordForms
|
||||||
hideWindow={() => setShowEditTerm(false)}
|
hideWindow={() => setShowEditTerm(false)}
|
||||||
onSave={handleSaveWordforms}
|
onSave={handleSaveWordforms}
|
||||||
target={activeCst!}
|
target={activeCst!}
|
||||||
/>}
|
/> : null}
|
||||||
{showTemplates &&
|
{showTemplates ?
|
||||||
<DlgConstituentaTemplate
|
<DlgConstituentaTemplate
|
||||||
schema={schema}
|
schema={schema}
|
||||||
hideWindow={() => setShowTemplates(false)}
|
hideWindow={() => setShowTemplates(false)}
|
||||||
onCreate={handleCreateCst}
|
onCreate={handleCreateCst}
|
||||||
/>}
|
/> : null}
|
||||||
<Tabs
|
<Tabs
|
||||||
selectedIndex={activeTab}
|
selectedIndex={activeTab}
|
||||||
onSelect={onSelectTab}
|
onSelect={onSelectTab}
|
||||||
|
@ -465,7 +465,7 @@ function RSTabs() {
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</div>
|
</div>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</>}
|
</> : null}
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,22 +13,23 @@ function ParsingResult({ data, disabled, onShowError }: ParsingResultProps) {
|
||||||
const warningsCount = data.errors.length - errorCount;
|
const warningsCount = data.errors.length - errorCount;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='px-2 py-1'>
|
<div className='px-2 py-1'>
|
||||||
<p>Ошибок: <b>{errorCount}</b> | Предупреждений: <b>{warningsCount}</b></p>
|
<p>Ошибок: <b>{errorCount}</b> | Предупреждений: <b>{warningsCount}</b></p>
|
||||||
{data.errors.map((error, index) => {
|
{data.errors.map(
|
||||||
return (
|
(error, index) => {
|
||||||
<p
|
return (
|
||||||
key={`error-${index}`}
|
<p
|
||||||
className={`text-warning ${disabled ? '' : 'cursor-pointer'}`}
|
key={`error-${index}`}
|
||||||
onClick={disabled ? undefined : () => onShowError(error)}
|
className={`text-warning ${disabled ? '' : 'cursor-pointer'}`}
|
||||||
>
|
onClick={disabled ? undefined : () => onShowError(error)}
|
||||||
<span className='mr-1 font-semibold underline'>{error.isCritical ? 'Ошибка' : 'Предупреждение'} {getRSErrorPrefix(error)}:</span>
|
>
|
||||||
<span> {describeRSError(error)}</span>
|
<span className='mr-1 font-semibold underline'>
|
||||||
</p>
|
{error.isCritical ? 'Ошибка' : 'Предупреждение'} {`${getRSErrorPrefix(error)}:`}
|
||||||
);
|
</span>
|
||||||
})}
|
<span>{` ${describeRSError(error)}`}</span>
|
||||||
</div>
|
</p>);
|
||||||
)
|
})}
|
||||||
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ParsingResult;
|
export default ParsingResult;
|
||||||
|
|
|
@ -11,81 +11,84 @@ function RSFormStats({ stats }: RSFormStatsProps) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col gap-1 px-4 py-2 mt-7 min-w-[16rem]'>
|
<div className='flex flex-col gap-1 px-4 py-2 mt-7 min-w-[16rem]'>
|
||||||
<LabeledText id='count_all'
|
<LabeledText id='count_all'
|
||||||
label='Всего конституент '
|
label='Всего конституент '
|
||||||
text={stats.count_all}
|
text={stats.count_all}
|
||||||
/>
|
/>
|
||||||
<LabeledText id='count_errors'
|
<LabeledText id='count_errors'
|
||||||
label='Ошибок '
|
label='Некорректных'
|
||||||
text={stats.count_errors}
|
text={stats.count_errors}
|
||||||
/>
|
/>
|
||||||
{ stats.count_property > 0 &&
|
{stats.count_property !== 0 ?
|
||||||
<LabeledText id='count_property'
|
<LabeledText id='count_property'
|
||||||
label='Только свойство '
|
label='Неразмерных'
|
||||||
text={stats.count_property}
|
text={stats.count_property}
|
||||||
/>}
|
/> : null}
|
||||||
{ stats.count_incalc > 0 &&
|
{stats.count_incalc !== 0 ?
|
||||||
<LabeledText id='count_incalc'
|
<LabeledText id='count_incalc'
|
||||||
label='Невычислимы '
|
label='Невычислимых'
|
||||||
text={stats.count_incalc}
|
text={stats.count_incalc}
|
||||||
/>}
|
/> : null}
|
||||||
<Divider margins='my-2' />
|
|
||||||
<LabeledText id='count_termin'
|
<Divider margins='my-2' />
|
||||||
label='Термины '
|
|
||||||
text={stats.count_termin}
|
<LabeledText id='count_termin'
|
||||||
/>
|
label='Термины'
|
||||||
<LabeledText id='count_definition'
|
text={stats.count_termin}
|
||||||
label='Определения '
|
/>
|
||||||
text={stats.count_definition}
|
<LabeledText id='count_definition'
|
||||||
/>
|
label='Определения'
|
||||||
<LabeledText id='count_convention'
|
text={stats.count_definition}
|
||||||
label='Конвенции '
|
/>
|
||||||
text={stats.count_convention}
|
<LabeledText id='count_convention'
|
||||||
/>
|
label='Конвенции'
|
||||||
<Divider margins='my-2' />
|
text={stats.count_convention}
|
||||||
{ stats.count_base > 0 &&
|
/>
|
||||||
<LabeledText id='count_base'
|
|
||||||
label='Базисные множества '
|
<Divider margins='my-2' />
|
||||||
text={stats.count_base}
|
|
||||||
/>}
|
{stats.count_base !== 0 ?
|
||||||
{ stats.count_constant > 0 &&
|
<LabeledText id='count_base'
|
||||||
<LabeledText id='count_constant'
|
label='Базисные множества '
|
||||||
label='Константные множества '
|
text={stats.count_base}
|
||||||
text={stats.count_constant}
|
/> : null}
|
||||||
/>}
|
{ stats.count_constant !== 0 ?
|
||||||
{ stats.count_structured > 0 &&
|
<LabeledText id='count_constant'
|
||||||
<LabeledText id='count_structured'
|
label='Константные множества '
|
||||||
label='Родовые структуры '
|
text={stats.count_constant}
|
||||||
text={stats.count_structured}
|
/> : null}
|
||||||
/>}
|
{stats.count_structured !== 0 ?
|
||||||
{ stats.count_axiom > 0 &&
|
<LabeledText id='count_structured'
|
||||||
<LabeledText id='count_axiom'
|
label='Родовые структуры '
|
||||||
label='Аксиомы '
|
text={stats.count_structured}
|
||||||
text={stats.count_axiom}
|
/> : null}
|
||||||
/>}
|
{stats.count_axiom !== 0 ?
|
||||||
{ stats.count_term > 0 &&
|
<LabeledText id='count_axiom'
|
||||||
<LabeledText id='count_term'
|
label='Аксиомы '
|
||||||
label='Термы '
|
text={stats.count_axiom}
|
||||||
text={stats.count_term}
|
/> : null}
|
||||||
/>}
|
{stats.count_term !== 0 ?
|
||||||
{ stats.count_function > 0 &&
|
<LabeledText id='count_term'
|
||||||
<LabeledText id='count_function'
|
label='Термы '
|
||||||
label='Терм-функции '
|
text={stats.count_term}
|
||||||
text={stats.count_function}
|
/> : null}
|
||||||
/>}
|
{stats.count_function !== 0 ?
|
||||||
{ stats.count_predicate > 0 &&
|
<LabeledText id='count_function'
|
||||||
<LabeledText id='count_predicate'
|
label='Терм-функции '
|
||||||
label='Предикат-функции '
|
text={stats.count_function}
|
||||||
text={stats.count_predicate}
|
/> : null}
|
||||||
/>}
|
{stats.count_predicate !== 0 ?
|
||||||
{ stats.count_theorem > 0 &&
|
<LabeledText id='count_predicate'
|
||||||
<LabeledText id='count_theorem'
|
label='Предикат-функции '
|
||||||
label='Теоремы '
|
text={stats.count_predicate}
|
||||||
text={stats.count_theorem}
|
/> : null}
|
||||||
/>}
|
{stats.count_theorem !== 0 ?
|
||||||
</div>
|
<LabeledText id='count_theorem'
|
||||||
);
|
label='Теоремы '
|
||||||
|
text={stats.count_theorem}
|
||||||
|
/> : null}
|
||||||
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RSFormStats;
|
export default RSFormStats;
|
||||||
|
|
|
@ -9,17 +9,14 @@ interface RSLocalButtonProps {
|
||||||
|
|
||||||
function RSLocalButton({ text, tooltip, disabled, onInsert }: RSLocalButtonProps) {
|
function RSLocalButton({ text, tooltip, disabled, onInsert }: RSLocalButtonProps) {
|
||||||
return (
|
return (
|
||||||
<button
|
<button type='button' tabIndex={-1}
|
||||||
type='button'
|
disabled={disabled}
|
||||||
disabled={disabled}
|
title={tooltip}
|
||||||
onClick={() => onInsert(TokenID.ID_LOCAL, text)}
|
className='w-[2rem] h-6 cursor-pointer disabled:cursor-default border rounded-none clr-hover clr-btn-clear'
|
||||||
title={tooltip}
|
onClick={() => onInsert(TokenID.ID_LOCAL, text)}
|
||||||
tabIndex={-1}
|
>
|
||||||
className='w-[2rem] h-6 cursor-pointer disabled:cursor-default border rounded-none clr-hover clr-btn-clear'
|
{text}
|
||||||
>
|
</button>);
|
||||||
{text}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RSLocalButton;
|
export default RSLocalButton;
|
||||||
|
|
|
@ -72,17 +72,14 @@ function RSTabsMenu({
|
||||||
return (
|
return (
|
||||||
<div className='flex items-stretch h-full w-fit'>
|
<div className='flex items-stretch h-full w-fit'>
|
||||||
<div ref={schemaMenu.ref}>
|
<div ref={schemaMenu.ref}>
|
||||||
<Button
|
<Button noBorder dense tabIndex={-1}
|
||||||
tooltip='Действия'
|
tooltip='Действия'
|
||||||
icon={<MenuIcon color='text-controls' size={5}/>}
|
icon={<MenuIcon color='text-controls' size={5}/>}
|
||||||
dimensions='h-full w-fit pl-2'
|
dimensions='h-full w-fit pl-2'
|
||||||
style={{outlineColor: 'transparent'}}
|
style={{outlineColor: 'transparent'}}
|
||||||
noBorder
|
|
||||||
dense
|
|
||||||
onClick={schemaMenu.toggle}
|
onClick={schemaMenu.toggle}
|
||||||
tabIndex={-1}
|
|
||||||
/>
|
/>
|
||||||
{ schemaMenu.isActive &&
|
{schemaMenu.isActive ?
|
||||||
<Dropdown>
|
<Dropdown>
|
||||||
<DropdownButton onClick={handleShare}>
|
<DropdownButton onClick={handleShare}>
|
||||||
<div className='inline-flex items-center justify-start gap-2'>
|
<div className='inline-flex items-center justify-start gap-2'>
|
||||||
|
@ -120,20 +117,17 @@ function RSTabsMenu({
|
||||||
<p>Создать новую схему</p>
|
<p>Создать новую схему</p>
|
||||||
</span>
|
</span>
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
</Dropdown>}
|
</Dropdown> : null}
|
||||||
</div>
|
</div>
|
||||||
<div ref={editMenu.ref}>
|
<div ref={editMenu.ref}>
|
||||||
<Button
|
<Button dense noBorder tabIndex={-1}
|
||||||
tooltip={'измнение: ' + (isEditable ? '[доступно]' : '[запрещено]')}
|
tooltip={'измнение: ' + (isEditable ? '[доступно]' : '[запрещено]')}
|
||||||
dimensions='h-full w-fit'
|
dimensions='h-full w-fit'
|
||||||
style={{outlineColor: 'transparent'}}
|
style={{outlineColor: 'transparent'}}
|
||||||
icon={<EditIcon size={5} color={isEditable ? 'text-success' : 'text-warning'}/>}
|
icon={<EditIcon size={5} color={isEditable ? 'text-success' : 'text-warning'}/>}
|
||||||
dense
|
|
||||||
noBorder
|
|
||||||
onClick={editMenu.toggle}
|
onClick={editMenu.toggle}
|
||||||
tabIndex={-1}
|
|
||||||
/>
|
/>
|
||||||
{ editMenu.isActive &&
|
{editMenu.isActive ?
|
||||||
<Dropdown>
|
<Dropdown>
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
disabled={!user || !isClaimable}
|
disabled={!user || !isClaimable}
|
||||||
|
@ -141,31 +135,33 @@ function RSTabsMenu({
|
||||||
tooltip={!user || !isClaimable ? 'Взять во владение можно общую изменяемую схему' : ''}
|
tooltip={!user || !isClaimable ? 'Взять во владение можно общую изменяемую схему' : ''}
|
||||||
>
|
>
|
||||||
<div className='flex items-center gap-2 pl-1'>
|
<div className='flex items-center gap-2 pl-1'>
|
||||||
<span><OwnerIcon size={5} color={isOwned ? 'text-success' : 'text-controls'} /></span>
|
<span>
|
||||||
|
<OwnerIcon size={5} color={isOwned ? 'text-success' : 'text-controls'} />
|
||||||
|
</span>
|
||||||
<p>
|
<p>
|
||||||
{ isOwned && <b>Владелец схемы</b> }
|
{isOwned ? <b>Владелец схемы</b> : null}
|
||||||
{ !isOwned && <b>Стать владельцем</b> }
|
{!isOwned ? <b>Стать владельцем</b> : null}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
{(isOwned || user?.is_staff) &&
|
{(isOwned || user?.is_staff) ?
|
||||||
<DropdownCheckbox
|
<DropdownCheckbox
|
||||||
value={isReadonly}
|
value={isReadonly}
|
||||||
setValue={toggleReadonly}
|
setValue={toggleReadonly}
|
||||||
label='Я — читатель!'
|
label='Я — читатель!'
|
||||||
tooltip='Режим чтения'
|
tooltip='Режим чтения'
|
||||||
/>}
|
/> : null}
|
||||||
{user?.is_staff &&
|
{user?.is_staff ?
|
||||||
<DropdownCheckbox
|
<DropdownCheckbox
|
||||||
value={isForceAdmin}
|
value={isForceAdmin}
|
||||||
setValue={toggleForceAdmin}
|
setValue={toggleForceAdmin}
|
||||||
label='Я — администратор!'
|
label='Я — администратор!'
|
||||||
tooltip='Режим редактирования для администраторов'
|
tooltip='Режим редактирования для администраторов'
|
||||||
/>}
|
/> : null}
|
||||||
</Dropdown>}
|
</Dropdown>: null}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button dense noBorder tabIndex={-1}
|
||||||
tooltip={'отслеживание: ' + (isTracking ? '[включено]' : '[выключено]')}
|
tooltip={'отслеживание: ' + (isTracking ? '[включено]' : '[выключено]')}
|
||||||
disabled={processing}
|
disabled={processing}
|
||||||
icon={isTracking
|
icon={isTracking
|
||||||
|
@ -174,10 +170,7 @@ function RSTabsMenu({
|
||||||
}
|
}
|
||||||
dimensions='h-full w-fit pr-2'
|
dimensions='h-full w-fit pr-2'
|
||||||
style={{outlineColor: 'transparent'}}
|
style={{outlineColor: 'transparent'}}
|
||||||
dense
|
|
||||||
noBorder
|
|
||||||
onClick={onToggleSubscribe}
|
onClick={onToggleSubscribe}
|
||||||
tabIndex={-1}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>);
|
</div>);
|
||||||
|
|
|
@ -11,17 +11,14 @@ function RSTokenButton({ token, disabled, onInsert }: RSTokenButtonProps) {
|
||||||
const label = labelToken(token);
|
const label = labelToken(token);
|
||||||
const width = label.length > 3 ? 'w-[4.5rem]' : 'w-[2.25rem]';
|
const width = label.length > 3 ? 'w-[4.5rem]' : 'w-[2.25rem]';
|
||||||
return (
|
return (
|
||||||
<button
|
<button type='button' tabIndex={-1}
|
||||||
type='button'
|
disabled={disabled}
|
||||||
disabled={disabled}
|
onClick={() => onInsert(token)}
|
||||||
onClick={() => onInsert(token)}
|
title={describeToken(token)}
|
||||||
title={describeToken(token)}
|
className={`px-1 cursor-pointer disabled:cursor-default border rounded-none h-6 ${width} outline-none clr-hover clr-btn-clear`}
|
||||||
tabIndex={-1}
|
>
|
||||||
className={`px-1 cursor-pointer disabled:cursor-default border rounded-none h-6 ${width} outline-none clr-hover clr-btn-clear`}
|
{label ? <span className='whitespace-nowrap'>{label}</span> : null}
|
||||||
>
|
</button>);
|
||||||
{label && <span className='whitespace-nowrap'>{label}</span>}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RSTokenButton;
|
export default RSTokenButton;
|
||||||
|
|
|
@ -206,18 +206,16 @@ function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }:
|
||||||
/>
|
/>
|
||||||
<div className='flex'>
|
<div className='flex'>
|
||||||
<div ref={matchModeMenu.ref}>
|
<div ref={matchModeMenu.ref}>
|
||||||
<SelectorButton
|
<SelectorButton transparent tabIndex={-1}
|
||||||
tooltip='Настройка атрибутов для фильтрации'
|
tooltip='Настройка атрибутов для фильтрации'
|
||||||
dimensions='w-fit h-full'
|
dimensions='w-fit h-full'
|
||||||
transparent
|
|
||||||
icon={<FilterIcon size={5} />}
|
icon={<FilterIcon size={5} />}
|
||||||
text={labelCstMathchMode(filterMatch)}
|
text={labelCstMathchMode(filterMatch)}
|
||||||
tabIndex={-1}
|
|
||||||
onClick={matchModeMenu.toggle}
|
onClick={matchModeMenu.toggle}
|
||||||
/>
|
/>
|
||||||
{ matchModeMenu.isActive &&
|
{ matchModeMenu.isActive &&
|
||||||
<Dropdown stretchLeft>
|
<Dropdown stretchLeft>
|
||||||
{ Object.values(CstMatchMode).filter(value => !isNaN(Number(value))).map(
|
{Object.values(CstMatchMode).filter(value => !isNaN(Number(value))).map(
|
||||||
(value, index) => {
|
(value, index) => {
|
||||||
const matchMode = value as CstMatchMode;
|
const matchMode = value as CstMatchMode;
|
||||||
return (
|
return (
|
||||||
|
@ -232,18 +230,16 @@ function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }:
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ref={sourceMenu.ref}>
|
<div ref={sourceMenu.ref}>
|
||||||
<SelectorButton
|
<SelectorButton transparent tabIndex={-1}
|
||||||
tooltip='Настройка фильтрации по графу термов'
|
tooltip='Настройка фильтрации по графу термов'
|
||||||
dimensions='w-fit h-full'
|
dimensions='w-fit h-full'
|
||||||
transparent
|
|
||||||
icon={<CogIcon size={4} />}
|
icon={<CogIcon size={4} />}
|
||||||
text={labelCstSource(filterSource)}
|
text={labelCstSource(filterSource)}
|
||||||
tabIndex={-1}
|
|
||||||
onClick={sourceMenu.toggle}
|
onClick={sourceMenu.toggle}
|
||||||
/>
|
/>
|
||||||
{ sourceMenu.isActive &&
|
{sourceMenu.isActive ?
|
||||||
<Dropdown stretchLeft>
|
<Dropdown stretchLeft>
|
||||||
{ Object.values(CstSource).filter(value => !isNaN(Number(value))).map(
|
{Object.values(CstSource).filter(value => !isNaN(Number(value))).map(
|
||||||
(value, index) => {
|
(value, index) => {
|
||||||
const source = value as CstSource;
|
const source = value as CstSource;
|
||||||
return (
|
return (
|
||||||
|
@ -254,18 +250,16 @@ function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }:
|
||||||
<p><span className='font-semibold'>{labelCstSource(source)}:</span> {describeCstSource(source)}</p>
|
<p><span className='font-semibold'>{labelCstSource(source)}:</span> {describeCstSource(source)}</p>
|
||||||
</DropdownButton>);
|
</DropdownButton>);
|
||||||
})}
|
})}
|
||||||
</Dropdown>}
|
</Dropdown> : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='overflow-y-auto text-sm overscroll-none' style={{maxHeight : `${maxHeight}`}}>
|
<div className='overflow-y-auto text-sm overscroll-none' style={{maxHeight : `${maxHeight}`}}>
|
||||||
<DataTable
|
<DataTable dense noFooter
|
||||||
data={filteredData}
|
data={filteredData}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
conditionalRowStyles={conditionalRowStyles}
|
conditionalRowStyles={conditionalRowStyles}
|
||||||
headPosition='0'
|
headPosition='0'
|
||||||
dense
|
|
||||||
noFooter
|
|
||||||
|
|
||||||
enableHiding
|
enableHiding
|
||||||
columnVisibility={columnVisibility}
|
columnVisibility={columnVisibility}
|
||||||
|
|
|
@ -54,69 +54,68 @@ function RegisterPage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex justify-center w-full py-2'>
|
<div className='flex justify-center w-full py-2'>
|
||||||
{ user &&
|
{user ? <b>{`Вы вошли в систему как ${user.username}`}</b> : null}
|
||||||
<b>{`Вы вошли в систему как ${user.username}`}</b>}
|
{!user ?
|
||||||
{ !user &&
|
<Form
|
||||||
<Form
|
title='Регистрация'
|
||||||
title='Регистрация'
|
onSubmit={handleSubmit}
|
||||||
onSubmit={handleSubmit}
|
dimensions='w-[24rem]'
|
||||||
dimensions='w-[24rem]'
|
>
|
||||||
>
|
<TextInput id='username' required
|
||||||
<TextInput id='username' label='Имя пользователя' type='text'
|
label='Имя пользователя'
|
||||||
required
|
value={username}
|
||||||
value={username}
|
onChange={event => setUsername(event.target.value)}
|
||||||
onChange={event => setUsername(event.target.value)}
|
/>
|
||||||
/>
|
<TextInput id='password' type='password' required
|
||||||
<TextInput id='password' label='Пароль' type='password'
|
label='Пароль'
|
||||||
required
|
value={password}
|
||||||
value={password}
|
onChange={event => setPassword(event.target.value)}
|
||||||
onChange={event => setPassword(event.target.value)}
|
/>
|
||||||
/>
|
<TextInput id='password2' required type='password'
|
||||||
<TextInput id='password2' label='Повторите пароль' type='password'
|
label='Повторите пароль'
|
||||||
required
|
value={password2}
|
||||||
value={password2}
|
onChange={event => setPassword2(event.target.value)}
|
||||||
onChange={event => setPassword2(event.target.value)}
|
/>
|
||||||
/>
|
<div className='text-sm'>
|
||||||
<div className='text-sm'>
|
<p>- используйте уникальный пароль</p>
|
||||||
<p>- используйте уникальный пароль</p>
|
<p>- портал функционирует в тестовом режиме</p>
|
||||||
<p>- портал функционирует в тестовом режиме</p>
|
<p className='font-semibold text-warning'>- безопасность информации не гарантируется</p>
|
||||||
<p className='font-semibold text-warning'>- безопасность информации не гарантируется</p>
|
{/* <p>- минимум 8 символов</p>
|
||||||
{/* <p>- минимум 8 символов</p>
|
<p>- большие, маленькие буквы, цифры</p>
|
||||||
<p>- большие, маленькие буквы, цифры</p>
|
<p>- минимум 1 спец. символ</p> */}
|
||||||
<p>- минимум 1 спец. символ</p> */}
|
</div>
|
||||||
</div>
|
<TextInput id='email' required
|
||||||
<TextInput id='email' label='email' type='text'
|
label='email'
|
||||||
required
|
value={email}
|
||||||
value={email}
|
onChange={event => setEmail(event.target.value)}
|
||||||
onChange={event => setEmail(event.target.value)}
|
/>
|
||||||
/>
|
<TextInput id='first_name'
|
||||||
<TextInput id='first_name' label='Имя' type='text'
|
label='Имя'
|
||||||
value={firstName}
|
value={firstName}
|
||||||
onChange={event => setFirstName(event.target.value)}
|
onChange={event => setFirstName(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<TextInput id='last_name' label='Фамилия' type='text'
|
<TextInput id='last_name'
|
||||||
value={lastName}
|
label='Фамилия'
|
||||||
onChange={event => setLastName(event.target.value)}
|
value={lastName}
|
||||||
/>
|
onChange={event => setLastName(event.target.value)}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className='flex items-center justify-center w-full gap-4 my-4'>
|
<div className='flex items-center justify-center w-full gap-4 my-4'>
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
text='Регистрировать'
|
text='Регистрировать'
|
||||||
loading={loading}
|
dimensions='min-w-[10rem]'
|
||||||
dimensions='min-w-[10rem]'
|
loading={loading}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
text='Отмена'
|
text='Отмена'
|
||||||
onClick={() => handleCancel()}
|
dimensions='min-w-[10rem]'
|
||||||
dimensions='min-w-[10rem]'
|
onClick={() => handleCancel()}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{ error && <BackendError error={error} />}
|
{error ? <BackendError error={error} /> : null}
|
||||||
</Form>
|
</Form> : null}
|
||||||
}
|
</div>);
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RegisterPage;
|
export default RegisterPage;
|
||||||
|
|
|
@ -31,7 +31,15 @@ function EditorPassword() {
|
||||||
|
|
||||||
const passwordColor = useMemo(
|
const passwordColor = useMemo(
|
||||||
() => {
|
() => {
|
||||||
return !!newPassword && !!newPasswordRepeat && newPassword !== newPasswordRepeat ? 'clr-warning' : 'clr-input';
|
if (
|
||||||
|
!!newPassword &&
|
||||||
|
!!newPasswordRepeat &&
|
||||||
|
newPassword !== newPasswordRepeat
|
||||||
|
) {
|
||||||
|
return 'clr-warning';
|
||||||
|
} else {
|
||||||
|
return 'clr-input';
|
||||||
|
}
|
||||||
}, [newPassword, newPasswordRepeat]);
|
}, [newPassword, newPasswordRepeat]);
|
||||||
|
|
||||||
const canSubmit = useMemo(
|
const canSubmit = useMemo(
|
||||||
|
@ -60,46 +68,44 @@ function EditorPassword() {
|
||||||
}, [newPassword, oldPassword, newPasswordRepeat, setError]);
|
}, [newPassword, oldPassword, newPasswordRepeat, setError]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex py-2 border-l-2 max-w-[14rem]'>
|
<div className='flex py-2 border-l-2 max-w-[14rem]'>
|
||||||
<form onSubmit={handleSubmit} className='flex flex-col justify-between px-6'>
|
<form
|
||||||
<div className='flex flex-col gap-3'>
|
className='flex flex-col justify-between px-6'
|
||||||
<TextInput id='old_password'
|
onSubmit={handleSubmit}
|
||||||
type='password'
|
>
|
||||||
label='Старый пароль'
|
<div className='flex flex-col gap-3'>
|
||||||
allowEnter
|
<TextInput id='old_password' type='password' allowEnter
|
||||||
value={oldPassword}
|
label='Старый пароль'
|
||||||
onChange={event => setOldPassword(event.target.value)}
|
value={oldPassword}
|
||||||
/>
|
onChange={event => setOldPassword(event.target.value)}
|
||||||
<TextInput id='new_password' type='password'
|
/>
|
||||||
colors={passwordColor}
|
<TextInput id='new_password' type='password' allowEnter
|
||||||
label='Новый пароль'
|
label='Новый пароль'
|
||||||
allowEnter
|
colors={passwordColor}
|
||||||
value={newPassword}
|
value={newPassword}
|
||||||
onChange={event => {
|
onChange={event => {
|
||||||
setNewPassword(event.target.value);
|
setNewPassword(event.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<TextInput id='new_password_repeat' type='password'
|
<TextInput id='new_password_repeat' type='password' allowEnter
|
||||||
colors={passwordColor}
|
label='Повторите новый'
|
||||||
label='Повторите новый'
|
colors={passwordColor}
|
||||||
allowEnter
|
value={newPasswordRepeat}
|
||||||
value={newPasswordRepeat}
|
onChange={event => {
|
||||||
onChange={event => {
|
setNewPasswordRepeat(event.target.value);
|
||||||
setNewPasswordRepeat(event.target.value);
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{ error && <ProcessError error={error} />}
|
|
||||||
<div className='flex justify-center w-full'>
|
|
||||||
<SubmitButton
|
|
||||||
disabled={!canSubmit}
|
|
||||||
loading={loading}
|
|
||||||
text='Сменить пароль'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
{error ? <ProcessError error={error} /> : null}
|
||||||
|
<div className='flex justify-center w-full'>
|
||||||
|
<SubmitButton
|
||||||
|
text='Сменить пароль'
|
||||||
|
disabled={!canSubmit}
|
||||||
|
loading={loading}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default EditorPassword;
|
export default EditorPassword;
|
||||||
|
|
|
@ -50,42 +50,37 @@ function EditorProfile() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit} className='px-6 py-2 flex flex-col gap-8 min-w-[18rem]'>
|
<form onSubmit={handleSubmit} className='px-6 py-2 flex flex-col gap-8 min-w-[18rem]'>
|
||||||
<div className='flex flex-col gap-3'>
|
<div className='flex flex-col gap-3'>
|
||||||
<TextInput id='username'
|
<TextInput id='username' disabled
|
||||||
label='Логин'
|
label='Логин'
|
||||||
tooltip='Логин изменить нельзя'
|
tooltip='Логин изменить нельзя'
|
||||||
disabled
|
value={username}
|
||||||
value={username}
|
/>
|
||||||
/>
|
<TextInput id='first_name' allowEnter
|
||||||
<TextInput id='first_name'
|
label='Имя'
|
||||||
label='Имя'
|
value={first_name}
|
||||||
value={first_name}
|
onChange={event => setFirstName(event.target.value)}
|
||||||
allowEnter
|
/>
|
||||||
onChange={event => setFirstName(event.target.value)}
|
<TextInput id='last_name' allowEnter
|
||||||
/>
|
label='Фамилия'
|
||||||
<TextInput id='last_name'
|
value={last_name}
|
||||||
label='Фамилия'
|
onChange={event => setLastName(event.target.value)}
|
||||||
value={last_name}
|
/>
|
||||||
allowEnter
|
<TextInput id='email' allowEnter
|
||||||
onChange={event => setLastName(event.target.value)}
|
label='Электронная почта'
|
||||||
/>
|
value={email}
|
||||||
<TextInput id='email'
|
onChange={event => setEmail(event.target.value)}
|
||||||
label='Электронная почта'
|
/>
|
||||||
allowEnter
|
</div>
|
||||||
value={email}
|
<div className='flex justify-center w-full'>
|
||||||
onChange={event => setEmail(event.target.value)}
|
<SubmitButton
|
||||||
/>
|
text='Сохранить данные'
|
||||||
</div>
|
loading={processing}
|
||||||
<div className='flex justify-center w-full'>
|
disabled={!isModified}
|
||||||
<SubmitButton
|
/>
|
||||||
text='Сохранить данные'
|
</div>
|
||||||
loading={processing}
|
</form>);
|
||||||
disabled={!isModified}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default EditorProfile;
|
export default EditorProfile;
|
||||||
|
|
|
@ -24,39 +24,37 @@ function UserTabs() {
|
||||||
}, [auth, items]);
|
}, [auth, items]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full'>
|
<div className='w-full'>
|
||||||
{ loading && <ConceptLoader /> }
|
{loading ? <ConceptLoader /> : null}
|
||||||
{ error && <BackendError error={error} />}
|
{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'>
|
<div className='relative w-full'>
|
||||||
<div className='absolute top-0 right-0 mt-2'>
|
<div className='absolute top-0 right-0 mt-2'>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Показать/Скрыть список отслеживаний'
|
tooltip='Показать/Скрыть список отслеживаний'
|
||||||
icon={showSubs
|
icon={showSubs
|
||||||
? <SubscribedIcon color='text-primary' size={5}/>
|
? <SubscribedIcon color='text-primary' size={5}/>
|
||||||
: <NotSubscribedIcon color='text-primary' size={5}/>
|
: <NotSubscribedIcon color='text-primary' size={5}/>
|
||||||
}
|
}
|
||||||
onClick={() => setShowSubs(prev => !prev)}
|
onClick={() => setShowSubs(prev => !prev)}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<h1>Учетные данные пользователя</h1>
|
|
||||||
<div className='flex justify-center py-2 max-w-fit'>
|
|
||||||
<EditorProfile />
|
|
||||||
<EditorPassword />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{subscriptions.length > 0 && showSubs &&
|
<h1>Учетные данные пользователя</h1>
|
||||||
<div className='flex flex-col w-full gap-6 pl-4'>
|
<div className='flex justify-center py-2 max-w-fit'>
|
||||||
<h1>Отслеживаемые схемы</h1>
|
<EditorProfile />
|
||||||
<ViewSubscriptions items={subscriptions} />
|
<EditorPassword />
|
||||||
</div>}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
{(subscriptions.length > 0 && showSubs) ?
|
||||||
</div>
|
<div className='flex flex-col w-full gap-6 pl-4'>
|
||||||
);
|
<h1>Отслеживаемые схемы</h1>
|
||||||
|
<ViewSubscriptions items={subscriptions} />
|
||||||
|
</div> : null}
|
||||||
|
</div> : null}
|
||||||
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default UserTabs;
|
export default UserTabs;
|
||||||
|
|
|
@ -47,26 +47,22 @@ function ViewSubscriptions({items}: ViewSubscriptionsProps) {
|
||||||
], [intl]);
|
], [intl]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='max-h-[23.8rem] overflow-auto text-sm border w-fit'>
|
<div className='max-h-[23.8rem] overflow-auto text-sm border w-fit'>
|
||||||
<DataTable
|
<DataTable dense noFooter
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={items}
|
data={items}
|
||||||
headPosition='0'
|
headPosition='0'
|
||||||
dense
|
|
||||||
|
|
||||||
enableSorting
|
enableSorting
|
||||||
initialSorting={{
|
initialSorting={{
|
||||||
id: 'time_update',
|
id: 'time_update',
|
||||||
desc: true
|
desc: true
|
||||||
}}
|
}}
|
||||||
noDataComponent={
|
noDataComponent={<div className='h-[10rem]'>Отслеживаемые схемы отсутствуют</div>}
|
||||||
<div className='h-[10rem]'>Отслеживаемые схемы отсутствуют</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
onRowClicked={openRSForm}
|
onRowClicked={openRSForm}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>);
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ViewSubscriptions;
|
export default ViewSubscriptions;
|
||||||
|
|
|
@ -66,6 +66,7 @@ export const globalIDs = {
|
||||||
* Prefixes for generating unique keys for lists.
|
* Prefixes for generating unique keys for lists.
|
||||||
*/
|
*/
|
||||||
export const prefixes = {
|
export const prefixes = {
|
||||||
|
page_size: 'page-size-',
|
||||||
cst_list: 'cst-list-',
|
cst_list: 'cst-list-',
|
||||||
cst_hidden_list: 'cst-hidden-list-',
|
cst_hidden_list: 'cst-hidden-list-',
|
||||||
cst_modal_list: 'cst-modal-list-',
|
cst_modal_list: 'cst-modal-list-',
|
||||||
|
|
Loading…
Reference in New Issue
Block a user