mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Refactoring: use clsx to clarify classNames
This commit is contained in:
parent
a9586a8e8e
commit
44b30a9bd8
|
@ -12,17 +12,18 @@ This readme file is used mostly to document project dependencies
|
||||||
<summary>npm install</summary>
|
<summary>npm install</summary>
|
||||||
<pre>
|
<pre>
|
||||||
- axios
|
- axios
|
||||||
|
- clsx
|
||||||
- react-router-dom
|
- react-router-dom
|
||||||
- react-toastify
|
- react-toastify
|
||||||
- react-loader-spinner
|
- react-loader-spinner
|
||||||
- js-file-download
|
|
||||||
- react-tabs
|
- react-tabs
|
||||||
- react-intl
|
- react-intl
|
||||||
- react-select
|
- react-select
|
||||||
- react-error-boundary
|
- react-error-boundary
|
||||||
- react-pdf
|
- react-pdf
|
||||||
- reagraph
|
|
||||||
- react-tooltip
|
- react-tooltip
|
||||||
|
- js-file-download
|
||||||
|
- reagraph
|
||||||
- @tanstack/react-table
|
- @tanstack/react-table
|
||||||
- @uiw/react-codemirror
|
- @uiw/react-codemirror
|
||||||
- @uiw/codemirror-themes
|
- @uiw/codemirror-themes
|
||||||
|
|
21
rsconcept/frontend/README.md
Normal file
21
rsconcept/frontend/README.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# Frontend Developer guidelines
|
||||||
|
|
||||||
|
Styling conventions
|
||||||
|
- static > conditional static > props. All dynamic styling should go in styles props
|
||||||
|
- dimensions = rectangle + outer layout
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>clsx className groupind and order</summary>
|
||||||
|
<pre>
|
||||||
|
- layer: z-position
|
||||||
|
- outer layout: fixed bottom-1/2 left-0 -translate-x-1/2
|
||||||
|
- rectangle: mt-3 w-full min-w-10 h-fit
|
||||||
|
- inner layout: px-3 py-2 flex flex-col gap-3 justify-start items-center
|
||||||
|
- overflow behavior: overflow-auto
|
||||||
|
- border: borer-2 outline-none shadow-md
|
||||||
|
- colors: clr-controls
|
||||||
|
- text: text-start text-sm font-semibold whitespace-nowrap
|
||||||
|
- behavior modifiers: select-none disabled:cursor-not-allowed
|
||||||
|
- transitions:
|
||||||
|
</pre>
|
||||||
|
</details>
|
1
rsconcept/frontend/package-lock.json
generated
1
rsconcept/frontend/package-lock.json
generated
|
@ -13,6 +13,7 @@
|
||||||
"@uiw/codemirror-themes": "^4.21.21",
|
"@uiw/codemirror-themes": "^4.21.21",
|
||||||
"@uiw/react-codemirror": "^4.21.21",
|
"@uiw/react-codemirror": "^4.21.21",
|
||||||
"axios": "^1.6.2",
|
"axios": "^1.6.2",
|
||||||
|
"clsx": "^2.0.0",
|
||||||
"js-file-download": "^0.4.12",
|
"js-file-download": "^0.4.12",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
"@uiw/codemirror-themes": "^4.21.21",
|
"@uiw/codemirror-themes": "^4.21.21",
|
||||||
"@uiw/react-codemirror": "^4.21.21",
|
"@uiw/react-codemirror": "^4.21.21",
|
||||||
"axios": "^1.6.2",
|
"axios": "^1.6.2",
|
||||||
|
"clsx": "^2.0.0",
|
||||||
"js-file-download": "^0.4.12",
|
"js-file-download": "^0.4.12",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
import { createBrowserRouter, Outlet, RouterProvider } from 'react-router-dom';
|
import { createBrowserRouter, Outlet, RouterProvider } from 'react-router-dom';
|
||||||
|
|
||||||
import ConceptToaster from './components/ConceptToaster';
|
import ConceptToaster from './components/ConceptToaster';
|
||||||
|
@ -21,7 +22,11 @@ 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={clsx(
|
||||||
|
'w-screen min-w-[30rem]',
|
||||||
|
'clr-app',
|
||||||
|
'antialiased'
|
||||||
|
)}>
|
||||||
|
|
||||||
<ConceptToaster
|
<ConceptToaster
|
||||||
className='mt-[4rem] text-sm'
|
className='mt-[4rem] text-sm'
|
||||||
|
@ -39,7 +44,13 @@ function Root() {
|
||||||
overflowY: showScroll ? 'scroll': 'auto'
|
overflowY: showScroll ? 'scroll': 'auto'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<main className='flex flex-col items-center w-full h-full min-w-fit' style={{minHeight: mainHeight}}>
|
<main
|
||||||
|
className={clsx(
|
||||||
|
'w-full h-full min-w-fit',
|
||||||
|
'flex flex-col items-center'
|
||||||
|
)}
|
||||||
|
style={{minHeight: mainHeight}}
|
||||||
|
>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { IColorsProps, IControlProps } from './commonInterfaces';
|
import { IColorsProps, IControlProps } from './commonInterfaces';
|
||||||
|
|
||||||
interface ButtonProps
|
interface ButtonProps
|
||||||
|
@ -17,21 +19,30 @@ function Button({
|
||||||
loading,
|
loading,
|
||||||
...restProps
|
...restProps
|
||||||
}: ButtonProps) {
|
}: ButtonProps) {
|
||||||
const borderClass = noBorder ? '' : 'border rounded';
|
|
||||||
const padding = dense ? 'px-1' : 'px-3 py-2';
|
|
||||||
const outlineClass = noOutline ? 'outline-none': 'clr-outline';
|
|
||||||
const cursor = 'disabled:cursor-not-allowed ' + (loading ? 'cursor-progress ' : 'cursor-pointer ');
|
|
||||||
return (
|
return (
|
||||||
<button type='button'
|
<button type='button'
|
||||||
disabled={disabled ?? loading}
|
disabled={disabled ?? loading}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
className={`inline-flex items-center gap-2 align-middle justify-center select-none ${padding} ${colors} ${outlineClass} ${borderClass} ${dimensions} ${cursor}`}
|
className={clsx(
|
||||||
|
'inline-flex gap-2 items-center justify-center',
|
||||||
|
'select-none disabled:cursor-not-allowed',
|
||||||
|
{
|
||||||
|
'border rounded': !noBorder,
|
||||||
|
'px-1': dense,
|
||||||
|
'px-3 py-2': !dense,
|
||||||
|
'cursor-progress': loading,
|
||||||
|
'cursor-pointer': !loading,
|
||||||
|
'outline-none': noOutline,
|
||||||
|
'clr-outline': !noOutline,
|
||||||
|
},
|
||||||
|
colors,
|
||||||
|
dimensions
|
||||||
|
)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
{icon ? icon : null}
|
{icon ? icon : null}
|
||||||
{text ? <span className='font-semibold'>{text}</span> : null}
|
{text ? <span className='font-semibold'>{text}</span> : null}
|
||||||
</button>
|
</button>);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Button;
|
export default Button;
|
|
@ -1,3 +1,4 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import { CheckboxCheckedIcon } from '../Icons';
|
import { CheckboxCheckedIcon } from '../Icons';
|
||||||
|
@ -25,13 +26,9 @@ function Checkbox({
|
||||||
} else if (setValue) {
|
} else if (setValue) {
|
||||||
return 'cursor-pointer';
|
return 'cursor-pointer';
|
||||||
} else {
|
} else {
|
||||||
return ''
|
return '';
|
||||||
}
|
}
|
||||||
}, [disabled, setValue]);
|
}, [disabled, setValue]);
|
||||||
const bgColor = useMemo(
|
|
||||||
() => {
|
|
||||||
return value !== false ? 'clr-primary' : 'clr-app'
|
|
||||||
}, [value]);
|
|
||||||
|
|
||||||
function handleClick(event: React.MouseEvent<HTMLButtonElement, MouseEvent>): void {
|
function handleClick(event: React.MouseEvent<HTMLButtonElement, MouseEvent>): void {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -43,26 +40,30 @@ function Checkbox({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button type='button' id={id}
|
<button type='button' id={id}
|
||||||
className={`flex items-center outline-none ${dimensions}`}
|
className={clsx(
|
||||||
|
'flex items-center gap-2',
|
||||||
|
'outline-none',
|
||||||
|
'text-start',
|
||||||
|
dimensions,
|
||||||
|
cursor
|
||||||
|
)}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
<div className={`max-w-[1rem] min-w-[1rem] h-4 mt-0.5 border rounded-sm ${bgColor} ${cursor}`} >
|
<div className={clsx(
|
||||||
{value ?
|
'max-w-[1rem] min-w-[1rem] h-4',
|
||||||
<div className='mt-[1px] ml-[1px]'>
|
'border rounded-sm',
|
||||||
<CheckboxCheckedIcon />
|
{
|
||||||
</div> : null}
|
'clr-primary': value !== false,
|
||||||
|
'clr-app': value === false
|
||||||
|
}
|
||||||
|
)}>
|
||||||
|
{value ? <div className='mt-[1px] ml-[1px]'><CheckboxCheckedIcon /></div> : null}
|
||||||
</div>
|
</div>
|
||||||
{label ?
|
<Label className={cursor} text={label} htmlFor={id} />
|
||||||
<Label
|
</button>);
|
||||||
className={`${cursor} px-2 text-start`}
|
|
||||||
text={label}
|
|
||||||
htmlFor={id}
|
|
||||||
/> : null}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Checkbox;
|
export default Checkbox;
|
|
@ -10,7 +10,6 @@ interface ConceptLoaderProps {
|
||||||
|
|
||||||
export function ConceptLoader({size=10}: ConceptLoaderProps) {
|
export function ConceptLoader({size=10}: ConceptLoaderProps) {
|
||||||
const {colors} = useConceptTheme();
|
const {colors} = useConceptTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex justify-center w-full h-full'>
|
<div className='flex justify-center w-full h-full'>
|
||||||
<ThreeDots
|
<ThreeDots
|
||||||
|
@ -19,6 +18,5 @@ export function ConceptLoader({size=10}: ConceptLoaderProps) {
|
||||||
width={size*10}
|
width={size*10}
|
||||||
radius={size}
|
radius={size}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>);
|
||||||
);
|
|
||||||
}
|
}
|
|
@ -5,25 +5,23 @@ import TextInput from './TextInput';
|
||||||
interface ConceptSearchProps {
|
interface ConceptSearchProps {
|
||||||
value: string
|
value: string
|
||||||
onChange?: (newValue: string) => void
|
onChange?: (newValue: string) => void
|
||||||
dense?: boolean
|
|
||||||
noBorder?: boolean
|
noBorder?: boolean
|
||||||
dimensions?: string
|
dimensions?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
function ConceptSearch({ value, onChange, noBorder, dimensions, dense }: ConceptSearchProps) {
|
function ConceptSearch({ value, onChange, noBorder, dimensions }: ConceptSearchProps) {
|
||||||
const borderClass = dense && !noBorder ? 'border-t border-x': '';
|
|
||||||
return (
|
return (
|
||||||
<div className={dimensions}>
|
<div className={dimensions}>
|
||||||
<Overlay
|
<Overlay
|
||||||
position='top-0 left-3 translate-y-1/2'
|
position='top-[-0.125rem] left-3 translate-y-1/2'
|
||||||
className='flex items-center pointer-events-none text-controls'
|
className='pointer-events-none clr-text-controls'
|
||||||
>
|
>
|
||||||
<MagnifyingGlassIcon size={5} />
|
<MagnifyingGlassIcon size={5} />
|
||||||
</Overlay>
|
</Overlay>
|
||||||
<TextInput noOutline
|
<TextInput noOutline
|
||||||
placeholder='Поиск'
|
placeholder='Поиск'
|
||||||
dimensions={`w-full pl-10 hover:text-clip outline-none ${borderClass}`}
|
dimensions='w-full pl-10'
|
||||||
noBorder={dense || noBorder}
|
noBorder={noBorder}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={event => (onChange ? onChange(event.target.value) : undefined)}
|
onChange={event => (onChange ? onChange(event.target.value) : undefined)}
|
||||||
/>
|
/>
|
||||||
|
@ -31,5 +29,3 @@ function ConceptSearch({ value, onChange, noBorder, dimensions, dense }: Concept
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ConceptSearch;
|
export default ConceptSearch;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
import type { TabProps } from 'react-tabs';
|
import type { TabProps } from 'react-tabs';
|
||||||
import { Tab } from 'react-tabs';
|
import { Tab } from 'react-tabs';
|
||||||
|
|
||||||
|
@ -11,7 +12,14 @@ extends Omit<TabProps, 'title' | 'children'> {
|
||||||
function ConceptTab({ label, tooltip, className, ...otherProps }: ConceptTabProps) {
|
function ConceptTab({ label, tooltip, className, ...otherProps }: ConceptTabProps) {
|
||||||
return (
|
return (
|
||||||
<Tab
|
<Tab
|
||||||
className={`px-2 py-1 h-full min-w-[6rem] flex justify-center text-sm hover:cursor-pointer clr-tab whitespace-nowrap small-caps select-none font-semibold ${className}`}
|
className={clsx(
|
||||||
|
'h-full min-w-[6rem]',
|
||||||
|
'px-2 py-1 flex justify-center',
|
||||||
|
'clr-tab',
|
||||||
|
'text-sm whitespace-nowrap small-caps font-semibold',
|
||||||
|
'select-none hover:cursor-pointer',
|
||||||
|
className
|
||||||
|
)}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import clsx from 'clsx';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
import { ITooltip, Tooltip } from 'react-tooltip';
|
import { ITooltip, Tooltip } from 'react-tooltip';
|
||||||
|
|
||||||
|
@ -26,7 +27,12 @@ function ConceptTooltip({
|
||||||
<Tooltip
|
<Tooltip
|
||||||
opacity={0.97}
|
opacity={0.97}
|
||||||
style={{...{ paddingTop: '2px', paddingBottom: '2px'}, ...style}}
|
style={{...{ paddingTop: '2px', paddingBottom: '2px'}, ...style}}
|
||||||
className={`overflow-auto border shadow-md ${layer} ${className}`}
|
className={clsx(
|
||||||
|
'overflow-auto',
|
||||||
|
'border shadow-md',
|
||||||
|
layer,
|
||||||
|
className
|
||||||
|
)}
|
||||||
variant={(darkMode ? 'dark' : 'light')}
|
variant={(darkMode ? 'dark' : 'light')}
|
||||||
place={place}
|
place={place}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
interface DividerProps {
|
interface DividerProps {
|
||||||
vertical?: boolean
|
vertical?: boolean
|
||||||
margins?: string
|
margins?: string
|
||||||
|
@ -5,8 +7,13 @@ interface DividerProps {
|
||||||
|
|
||||||
function Divider({ vertical, margins = 'mx-2' }: DividerProps) {
|
function Divider({ vertical, margins = 'mx-2' }: DividerProps) {
|
||||||
return (
|
return (
|
||||||
<div className={`${margins} ${vertical ? 'border-x h-full': 'border-y w-full'}`} />
|
<div className={clsx(
|
||||||
);
|
margins,
|
||||||
|
{
|
||||||
|
'border-x h-full': vertical,
|
||||||
|
'border-y w-full': !vertical
|
||||||
|
}
|
||||||
|
)}/>);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Divider;
|
export default Divider;
|
|
@ -1,16 +1,36 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
|
import Overlay from './Overlay';
|
||||||
|
|
||||||
interface DropdownProps {
|
interface DropdownProps {
|
||||||
children: React.ReactNode
|
|
||||||
stretchLeft?: boolean
|
stretchLeft?: boolean
|
||||||
dimensions?: string
|
dimensions?: string
|
||||||
|
children: React.ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
function Dropdown({ children, dimensions = 'w-fit', stretchLeft }: DropdownProps) {
|
function Dropdown({
|
||||||
|
dimensions = 'w-fit',
|
||||||
|
stretchLeft,
|
||||||
|
children
|
||||||
|
}: DropdownProps) {
|
||||||
return (
|
return (
|
||||||
<div className='relative text-sm'>
|
<Overlay
|
||||||
<div className={`absolute ${stretchLeft ? 'right-0' : 'left-0'} mt-3 z-modal-tooltip flex flex-col items-stretch justify-start origin-top-right border rounded-md shadow-lg clr-input ${dimensions}`}>
|
layer='z-modal-tooltip'
|
||||||
|
position='mt-3'
|
||||||
|
className={clsx(
|
||||||
|
'flex flex-col items-stretch justify-start',
|
||||||
|
'border rounded-md shadow-lg',
|
||||||
|
'text-sm',
|
||||||
|
'clr-input',
|
||||||
|
{
|
||||||
|
'right-0': stretchLeft,
|
||||||
|
'left-0': !stretchLeft
|
||||||
|
},
|
||||||
|
dimensions
|
||||||
|
)}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</Overlay>);
|
||||||
</div>);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Dropdown;
|
export default Dropdown;
|
|
@ -1,3 +1,5 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
interface DropdownButtonProps {
|
interface DropdownButtonProps {
|
||||||
tooltip?: string | undefined
|
tooltip?: string | undefined
|
||||||
onClick?: () => void
|
onClick?: () => void
|
||||||
|
@ -6,14 +8,21 @@ 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 text = disabled ? 'text-controls' : '';
|
|
||||||
return (
|
return (
|
||||||
<button type='button'
|
<button type='button'
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className={`px-3 py-1 text-left overflow-ellipsis whitespace-nowrap ${behavior} ${text}`}
|
className={clsx(
|
||||||
|
'px-3 py-1',
|
||||||
|
'text-left overflow-ellipsis whitespace-nowrap',
|
||||||
|
'disabled:clr-text-controls',
|
||||||
|
{
|
||||||
|
'clr-hover': onClick,
|
||||||
|
'cursor-pointer disabled:cursor-not-allowed': onClick,
|
||||||
|
'cursor-default': !onClick
|
||||||
|
}
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</button>);
|
</button>);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import Checkbox from './Checkbox';
|
import Checkbox from './Checkbox';
|
||||||
|
|
||||||
interface DropdownCheckboxProps {
|
interface DropdownCheckboxProps {
|
||||||
|
@ -9,11 +11,15 @@ interface DropdownCheckboxProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function DropdownCheckbox({ tooltip, setValue, disabled, ...restProps }: DropdownCheckboxProps) {
|
function DropdownCheckbox({ tooltip, setValue, disabled, ...restProps }: DropdownCheckboxProps) {
|
||||||
const behavior = (setValue && !disabled) ? 'clr-hover' : '';
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
className={`px-4 py-1 text-left overflow-ellipsis ${behavior} w-full whitespace-nowrap`}
|
className={clsx(
|
||||||
|
'px-3 py-1',
|
||||||
|
'text-left overflow-ellipsis whitespace-nowrap',
|
||||||
|
'disabled:clr-text-controls',
|
||||||
|
!!setValue && !disabled && 'clr-hover'
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
dimensions='w-full'
|
dimensions='w-full'
|
||||||
|
|
|
@ -11,13 +11,20 @@ function EmbedYoutube({ videoID, pxHeight, pxWidth }: EmbedYoutubeProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className='relative'
|
className='relative'
|
||||||
style={{height: 0, paddingBottom: `${pxHeight}px`, paddingLeft: `${pxWidth}px`}}
|
style={{
|
||||||
|
height: 0,
|
||||||
|
paddingBottom: `${pxHeight}px`,
|
||||||
|
paddingLeft: `${pxWidth}px`
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<iframe allowFullScreen
|
<iframe allowFullScreen
|
||||||
title='Встроенное видео Youtube'
|
title='Встроенное видео Youtube'
|
||||||
allow='accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture'
|
allow='accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture'
|
||||||
className='absolute top-0 left-0 border'
|
className='absolute top-0 left-0 border'
|
||||||
style={{minHeight: `${pxHeight}px`, minWidth: `${pxWidth}px`}}
|
style={{
|
||||||
|
minHeight: `${pxHeight}px`,
|
||||||
|
minWidth: `${pxWidth}px`
|
||||||
|
}}
|
||||||
width={`${pxWidth}px`}
|
width={`${pxWidth}px`}
|
||||||
height={`${pxHeight}px`}
|
height={`${pxHeight}px`}
|
||||||
src={`https://www.youtube.com/embed/${videoID}`}
|
src={`https://www.youtube.com/embed/${videoID}`}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import clsx from 'clsx';
|
||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
|
|
||||||
import { UploadIcon } from '../Icons';
|
import { UploadIcon } from '../Icons';
|
||||||
|
@ -40,7 +41,11 @@ function FileInput({
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`flex flex-col gap-2 py-2 items-start ${dimensions}`}>
|
<div className={clsx(
|
||||||
|
'py-2',
|
||||||
|
'flex flex-col gap-2 items-start',
|
||||||
|
dimensions
|
||||||
|
)}>
|
||||||
<input type='file'
|
<input type='file'
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
style={{ display: 'none' }}
|
style={{ display: 'none' }}
|
||||||
|
@ -54,9 +59,7 @@ function FileInput({
|
||||||
onClick={handleUploadClick}
|
onClick={handleUploadClick}
|
||||||
tooltip={tooltip}
|
tooltip={tooltip}
|
||||||
/>
|
/>
|
||||||
<Label
|
<Label text={fileName} />
|
||||||
text={fileName}
|
|
||||||
/>
|
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// Reexporting reagraph types to wrap in 'use client'.
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { GraphCanvas as GraphUI } from 'reagraph';
|
import { GraphCanvas as GraphUI } from 'reagraph';
|
||||||
|
|
|
@ -1,15 +1,22 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
import { LabelHTMLAttributes } from 'react';
|
import { LabelHTMLAttributes } from 'react';
|
||||||
|
|
||||||
interface LabelProps
|
interface LabelProps
|
||||||
extends Omit<React.DetailedHTMLProps<LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>, 'children' | 'title'> {
|
extends Omit<React.DetailedHTMLProps<LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>, 'children' | 'title'> {
|
||||||
text: string
|
text?: string
|
||||||
tooltip?: string
|
tooltip?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
function Label({ text, tooltip, className, ...restProps }: LabelProps) {
|
function Label({ text, tooltip, className, ...restProps }: LabelProps) {
|
||||||
|
if (!text) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<label
|
<label
|
||||||
className={`text-sm font-semibold whitespace-nowrap ${className}`}
|
className={clsx(
|
||||||
|
'text-sm font-semibold whitespace-nowrap',
|
||||||
|
className
|
||||||
|
)}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
interface LabeledTextProps {
|
interface LabeledValueProps {
|
||||||
id?: string
|
id?: string
|
||||||
label: string
|
label: string
|
||||||
text: string | number
|
text: string | number
|
||||||
tooltip?: string
|
tooltip?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
function LabeledText({ id, label, text, tooltip }: LabeledTextProps) {
|
function LabeledValue({ id, label, text, tooltip }: LabeledValueProps) {
|
||||||
return (
|
return (
|
||||||
<div className='flex justify-between gap-4'>
|
<div className='flex justify-between gap-3'>
|
||||||
<label
|
<label
|
||||||
className='font-semibold'
|
className='font-semibold'
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
|
@ -21,4 +21,4 @@ function LabeledText({ id, label, text, tooltip }: LabeledTextProps) {
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LabeledText;
|
export default LabeledValue;
|
|
@ -1,3 +1,5 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
interface MiniButtonProps
|
interface MiniButtonProps
|
||||||
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'className' | 'title' | 'children' > {
|
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'className' | 'title' | 'children' > {
|
||||||
icon: React.ReactNode
|
icon: React.ReactNode
|
||||||
|
@ -15,7 +17,17 @@ function MiniButton({
|
||||||
<button type='button'
|
<button type='button'
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
tabIndex={tabIndex ?? -1}
|
tabIndex={tabIndex ?? -1}
|
||||||
className={`px-1 py-1 rounded-full cursor-pointer disabled:cursor-not-allowed clr-btn-clear ${noHover ? 'outline-none' : 'clr-hover'} ${dimensions}`}
|
className={clsx(
|
||||||
|
'px-1 py-1',
|
||||||
|
'rounded-full',
|
||||||
|
'clr-btn-clear',
|
||||||
|
'cursor-pointer disabled:cursor-not-allowed',
|
||||||
|
{
|
||||||
|
'outline-none': noHover,
|
||||||
|
'clr-hover': !noHover
|
||||||
|
},
|
||||||
|
dimensions
|
||||||
|
)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
{icon}
|
{icon}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import clsx from 'clsx';
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
|
|
||||||
import useEscapeKey from '@/hooks/useEscapeKey';
|
import useEscapeKey from '@/hooks/useEscapeKey';
|
||||||
|
@ -44,9 +45,19 @@ function Modal({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className='fixed top-0 left-0 w-full h-full z-navigation clr-modal-backdrop' />
|
<div className={clsx(
|
||||||
|
'z-navigation',
|
||||||
|
'fixed top-0 left-0',
|
||||||
|
'w-full h-full',
|
||||||
|
'clr-modal-backdrop'
|
||||||
|
)}/>
|
||||||
<div ref={ref}
|
<div ref={ref}
|
||||||
className='fixed -translate-x-1/2 translate-y-1/2 border shadow-md bottom-1/2 left-1/2 z-modal clr-app'
|
className={clsx(
|
||||||
|
'z-modal',
|
||||||
|
'fixed bottom-1/2 left-1/2 -translate-x-1/2 translate-y-1/2',
|
||||||
|
'border shadow-md',
|
||||||
|
'clr-app'
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<Overlay position='right-[0.3rem] top-2' className='text-disabled'>
|
<Overlay position='right-[0.3rem] top-2' className='text-disabled'>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
|
@ -56,20 +67,27 @@ function Modal({
|
||||||
/>
|
/>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
|
||||||
{title ? <h1 className='px-12 py-2 text-lg select-none'>{title}</h1> : null}
|
{title ? <h1 className='px-12 py-2 select-none'>{title}</h1> : null}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={`w-full h-fit ${className}`}
|
className={clsx(
|
||||||
|
'w-full h-fit',
|
||||||
|
'overflow-auto',
|
||||||
|
className
|
||||||
|
)}
|
||||||
style={{
|
style={{
|
||||||
maxHeight: 'calc(100vh - 8rem)',
|
maxHeight: 'calc(100vh - 8rem)',
|
||||||
maxWidth: 'calc(100vw - 2rem)',
|
maxWidth: 'calc(100vw - 2rem)',
|
||||||
overflow: 'auto'
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex justify-center w-full gap-12 px-6 py-3 z-modal-controls min-w-fit'>
|
<div className={clsx(
|
||||||
|
'w-full min-w-fit',
|
||||||
|
'z-modal-controls',
|
||||||
|
'px-6 py-3 flex gap-12 justify-center'
|
||||||
|
)}>
|
||||||
{!readonly ?
|
{!readonly ?
|
||||||
<Button autoFocus
|
<Button autoFocus
|
||||||
text={submitText}
|
text={submitText}
|
||||||
|
|
|
@ -40,7 +40,10 @@ function PDFViewer({ file }: PDFViewerProps) {
|
||||||
loading='Загрузка PDF файла...'
|
loading='Загрузка PDF файла...'
|
||||||
error='Не удалось загрузить файл.'
|
error='Не удалось загрузить файл.'
|
||||||
>
|
>
|
||||||
<Overlay position='top-6 left-1/2 -translate-x-1/2' className='flex select-none'>
|
<Overlay
|
||||||
|
position='top-6 left-1/2 -translate-x-1/2'
|
||||||
|
className='flex select-none'
|
||||||
|
>
|
||||||
<PageControls
|
<PageControls
|
||||||
pageCount={pageCount}
|
pageCount={pageCount}
|
||||||
pageNumber={pageNumber}
|
pageNumber={pageNumber}
|
||||||
|
|
|
@ -12,14 +12,14 @@ function PageControls({
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button type='button'
|
<button type='button'
|
||||||
className='clr-hover text-controls'
|
className='clr-hover clr-text-controls'
|
||||||
onClick={() => setPageNumber(1)}
|
onClick={() => setPageNumber(1)}
|
||||||
disabled={pageNumber < 2}
|
disabled={pageNumber < 2}
|
||||||
>
|
>
|
||||||
<GotoFirstIcon />
|
<GotoFirstIcon />
|
||||||
</button>
|
</button>
|
||||||
<button type='button'
|
<button type='button'
|
||||||
className='clr-hover text-controls'
|
className='clr-hover clr-text-controls'
|
||||||
onClick={() => setPageNumber(prev => prev - 1)}
|
onClick={() => setPageNumber(prev => prev - 1)}
|
||||||
disabled={pageNumber < 2}
|
disabled={pageNumber < 2}
|
||||||
>
|
>
|
||||||
|
@ -27,14 +27,14 @@ function PageControls({
|
||||||
</button>
|
</button>
|
||||||
<p className='px-3 text-black'>Страница {pageNumber} из {pageCount}</p>
|
<p className='px-3 text-black'>Страница {pageNumber} из {pageCount}</p>
|
||||||
<button type='button'
|
<button type='button'
|
||||||
className='clr-hover text-controls'
|
className='clr-hover clr-text-controls'
|
||||||
onClick={() => setPageNumber(prev => prev + 1)}
|
onClick={() => setPageNumber(prev => prev + 1)}
|
||||||
disabled={pageNumber >= pageCount}
|
disabled={pageNumber >= pageCount}
|
||||||
>
|
>
|
||||||
<GotoNextIcon />
|
<GotoNextIcon />
|
||||||
</button>
|
</button>
|
||||||
<button type='button'
|
<button type='button'
|
||||||
className='clr-hover text-controls'
|
className='clr-hover clr-text-controls'
|
||||||
onClick={() => setPageNumber(pageCount)}
|
onClick={() => setPageNumber(pageCount)}
|
||||||
disabled={pageNumber >= pageCount}
|
disabled={pageNumber >= pageCount}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
interface SelectorButtonProps
|
interface SelectorButtonProps
|
||||||
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'className' | 'children' | 'title' | 'type'> {
|
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'className' | 'children' | 'title' | 'type'> {
|
||||||
text?: string
|
text?: string
|
||||||
|
@ -9,7 +11,6 @@ extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'className' | 'child
|
||||||
transparent?: boolean
|
transparent?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function SelectorButton({
|
function SelectorButton({
|
||||||
text, icon, tooltip,
|
text, icon, tooltip,
|
||||||
colors = 'clr-btn-default',
|
colors = 'clr-btn-default',
|
||||||
|
@ -17,12 +18,20 @@ function SelectorButton({
|
||||||
transparent,
|
transparent,
|
||||||
...restProps
|
...restProps
|
||||||
}: SelectorButtonProps) {
|
}: SelectorButtonProps) {
|
||||||
const cursor = 'disabled:cursor-not-allowed cursor-pointer';
|
|
||||||
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 select-none ${cursor} ${position} ${design}`}
|
className={clsx(
|
||||||
|
'px-1 flex flex-start items-center gap-1',
|
||||||
|
'text-sm small-caps select-none',
|
||||||
|
'text-btn clr-text-controls',
|
||||||
|
'disabled:cursor-not-allowed cursor-pointer',
|
||||||
|
{
|
||||||
|
'clr-hover': transparent,
|
||||||
|
'border': !transparent,
|
||||||
|
},
|
||||||
|
!transparent && colors,
|
||||||
|
dimensions
|
||||||
|
)}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
interface SubmitButtonProps
|
interface SubmitButtonProps
|
||||||
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'className' | 'children' | 'title'> {
|
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'className' | 'children' | 'title'> {
|
||||||
text?: string
|
text?: string
|
||||||
|
@ -14,7 +16,16 @@ function SubmitButton({
|
||||||
return (
|
return (
|
||||||
<button type='submit'
|
<button type='submit'
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
className={`px-3 py-2 inline-flex items-center gap-2 align-middle justify-center font-semibold select-none disabled:cursor-not-allowed border clr-btn-primary ${dimensions} ${loading ? ' cursor-progress' : ''}`}
|
className={clsx(
|
||||||
|
'px-3 py-2',
|
||||||
|
'inline-flex items-center gap-2 align-middle justify-center',
|
||||||
|
'border',
|
||||||
|
'font-semibold',
|
||||||
|
'clr-btn-primary',
|
||||||
|
'select-none disabled:cursor-not-allowed',
|
||||||
|
loading && 'cursor-progress',
|
||||||
|
dimensions
|
||||||
|
)}
|
||||||
disabled={disabled ?? loading}
|
disabled={disabled ?? loading}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
interface SwitchButtonProps<ValueType> {
|
interface SwitchButtonProps<ValueType> {
|
||||||
id?: string
|
id?: string
|
||||||
value: ValueType
|
value: ValueType
|
||||||
|
@ -19,7 +21,15 @@ function SwitchButton<ValueType>({
|
||||||
<button type='button' tabIndex={-1}
|
<button type='button' tabIndex={-1}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
onClick={() => onSelect(value)}
|
onClick={() => onSelect(value)}
|
||||||
className={`px-2 py-1 border font-semibold small-caps rounded-none cursor-pointer clr-btn-clear clr-hover ${dimensions} ${isSelected ? 'clr-selected': ''}`}
|
className={clsx(
|
||||||
|
'px-2 py-1',
|
||||||
|
'border rounded-none',
|
||||||
|
'font-semibold small-caps',
|
||||||
|
'clr-btn-clear clr-hover',
|
||||||
|
'cursor-pointer',
|
||||||
|
isSelected && 'clr-selected',
|
||||||
|
dimensions
|
||||||
|
)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
{icon ? icon : null}
|
{icon ? icon : null}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
import { TextareaHTMLAttributes } from 'react';
|
import { TextareaHTMLAttributes } from 'react';
|
||||||
|
|
||||||
import { IColorsProps, IEditorProps } from './commonInterfaces';
|
import { IColorsProps, IEditorProps } from './commonInterfaces';
|
||||||
|
@ -15,18 +16,28 @@ function TextArea({
|
||||||
colors = 'clr-input',
|
colors = 'clr-input',
|
||||||
...restProps
|
...restProps
|
||||||
}: TextAreaProps) {
|
}: TextAreaProps) {
|
||||||
const borderClass = noBorder ? '': 'border';
|
|
||||||
const outlineClass = noOutline ? '': 'clr-outline';
|
|
||||||
return (
|
return (
|
||||||
<div className={`flex ${dense ? 'items-center gap-4 ' + dimensions : 'flex-col items-start gap-2'}`}>
|
<div className={clsx(
|
||||||
{label &&
|
{
|
||||||
<Label
|
'flex items-center gap-3': dense,
|
||||||
text={label}
|
'flex flex-col items-start gap-2': !dense
|
||||||
htmlFor={id}
|
},
|
||||||
/>}
|
dense && dimensions,
|
||||||
|
)}>
|
||||||
|
<Label text={label} htmlFor={id} />
|
||||||
<textarea id={id}
|
<textarea id={id}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
className={`px-3 py-2 leading-tight ${outlineClass} ${borderClass} ${colors} ${dense ? 'w-full' : dimensions}`}
|
className={clsx(
|
||||||
|
'px-3 py-2',
|
||||||
|
'leading-tight',
|
||||||
|
{
|
||||||
|
'w-full': dense,
|
||||||
|
'border': !noBorder,
|
||||||
|
'clr-outline': !noOutline
|
||||||
|
},
|
||||||
|
colors,
|
||||||
|
!dense && dimensions
|
||||||
|
)}
|
||||||
rows={rows}
|
rows={rows}
|
||||||
required={required}
|
required={required}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { IColorsProps, IEditorProps } from './commonInterfaces';
|
import { IColorsProps, IEditorProps } from './commonInterfaces';
|
||||||
import Label from './Label';
|
import Label from './Label';
|
||||||
|
|
||||||
|
@ -14,24 +16,37 @@ function preventEnterCapture(event: React.KeyboardEvent<HTMLInputElement>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function TextInput({
|
function TextInput({
|
||||||
id, label, dense, tooltip, noBorder, noOutline, allowEnter, onKeyDown,
|
id, label, dense, tooltip, noBorder, noOutline, allowEnter, disabled,
|
||||||
dimensions = 'w-full',
|
dimensions = 'w-full',
|
||||||
colors = 'clr-input',
|
colors = 'clr-input',
|
||||||
|
onKeyDown,
|
||||||
...restProps
|
...restProps
|
||||||
}: TextInputProps) {
|
}: TextInputProps) {
|
||||||
const borderClass = noBorder ? '' : 'border px-3';
|
|
||||||
const outlineClass = noOutline ? '' : 'clr-outline';
|
|
||||||
return (
|
return (
|
||||||
<div className={`flex ${dense ? 'items-center gap-4 ' + dimensions : 'flex-col items-start gap-2'}`}>
|
<div className={clsx(
|
||||||
{label ?
|
{
|
||||||
<Label
|
'flex flex-col items-start gap-2': !dense,
|
||||||
text={label}
|
'flex items-center gap-3': dense,
|
||||||
htmlFor={id}
|
},
|
||||||
/> : null}
|
dense && dimensions
|
||||||
|
)}>
|
||||||
|
<Label text={label} htmlFor={id} />
|
||||||
<input id={id}
|
<input id={id}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
|
className={clsx(
|
||||||
|
'py-2',
|
||||||
|
'leading-tight truncate hover:text-clip',
|
||||||
|
{
|
||||||
|
'px-3': !noBorder || !disabled,
|
||||||
|
'w-full': dense,
|
||||||
|
'border': !noBorder,
|
||||||
|
'clr-outline': !noOutline
|
||||||
|
},
|
||||||
|
colors,
|
||||||
|
!dense && dimensions
|
||||||
|
)}
|
||||||
onKeyDown={!allowEnter && !onKeyDown ? preventEnterCapture : onKeyDown}
|
onKeyDown={!allowEnter && !onKeyDown ? preventEnterCapture : onKeyDown}
|
||||||
className={`py-2 leading-tight truncate hover:text-clip ${colors} ${outlineClass} ${borderClass} ${dense ? 'w-full' : dimensions}`}
|
disabled={disabled}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>
|
||||||
</div>);
|
</div>);
|
||||||
|
|
|
@ -9,7 +9,7 @@ interface TextURLProps {
|
||||||
onClick?: () => void
|
onClick?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function TextURL({ text, href, tooltip, color='text-url', onClick }: TextURLProps) {
|
function TextURL({ text, href, tooltip, color='clr-text-url', onClick }: TextURLProps) {
|
||||||
const design = `cursor-pointer hover:underline ${color}`;
|
const design = `cursor-pointer hover:underline ${color}`;
|
||||||
if (href) {
|
if (href) {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
|
|
||||||
|
import clsx from 'clsx';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import { CheckboxCheckedIcon, CheckboxNullIcon } from '../Icons';
|
import { CheckboxCheckedIcon, CheckboxNullIcon } from '../Icons';
|
||||||
|
@ -27,10 +28,6 @@ function Tristate({
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
}, [disabled, setValue]);
|
}, [disabled, setValue]);
|
||||||
const bgColor = useMemo(
|
|
||||||
() => {
|
|
||||||
return value !== false ? 'clr-primary' : 'clr-app'
|
|
||||||
}, [value]);
|
|
||||||
|
|
||||||
function handleClick(event: React.MouseEvent<HTMLButtonElement, MouseEvent>): void {
|
function handleClick(event: React.MouseEvent<HTMLButtonElement, MouseEvent>): void {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -48,22 +45,29 @@ function Tristate({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button type='button' id={id}
|
<button type='button' id={id}
|
||||||
className={`flex items-center clr-outline focus:outline-dotted focus:outline-1 ${dimensions}`}
|
className={clsx(
|
||||||
|
'flex items-center gap-2 text-start',
|
||||||
|
'outline-none',
|
||||||
|
dimensions,
|
||||||
|
cursor
|
||||||
|
)}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
<div className={`w-4 h-4 shrink-0 mt-0.5 border rounded-sm ${bgColor} ${cursor}`} >
|
<div className={clsx(
|
||||||
|
'w-4 h-4',
|
||||||
|
'border rounded-sm',
|
||||||
|
{
|
||||||
|
'clr-primary': value !== false,
|
||||||
|
'clr-app': value === false
|
||||||
|
}
|
||||||
|
)}>
|
||||||
{value ? <div className='mt-[1px] ml-[1px]'><CheckboxCheckedIcon /></div> : null}
|
{value ? <div className='mt-[1px] ml-[1px]'><CheckboxCheckedIcon /></div> : null}
|
||||||
{value == null ? <div className='mt-[1px] ml-[1px]'><CheckboxNullIcon /></div> : null}
|
{value == null ? <div className='mt-[1px] ml-[1px]'><CheckboxNullIcon /></div> : null}
|
||||||
</div>
|
</div>
|
||||||
{label ?
|
<Label className={cursor} text={label} htmlFor={id} />
|
||||||
<Label
|
|
||||||
className={`${cursor} px-2 text-start`}
|
|
||||||
text={label}
|
|
||||||
htmlFor={id}
|
|
||||||
/> : null}
|
|
||||||
</button>);
|
</button>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Table } from '@tanstack/react-table';
|
import { Table } from '@tanstack/react-table';
|
||||||
|
import clsx from 'clsx';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
|
@ -24,7 +25,13 @@ function PaginationTools<TData>({ table, paginationOptions, onChangePaginationOp
|
||||||
}, [onChangePaginationOption, table]);
|
}, [onChangePaginationOption, table]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex items-center justify-end w-full my-2 text-sm select-none text-controls'>
|
<div className={clsx(
|
||||||
|
'flex justify-end items-center',
|
||||||
|
'w-full my-2',
|
||||||
|
'text-sm',
|
||||||
|
'clr-text-controls',
|
||||||
|
'select-none'
|
||||||
|
)}>
|
||||||
<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}
|
||||||
|
@ -36,14 +43,14 @@ function PaginationTools<TData>({ table, paginationOptions, onChangePaginationOp
|
||||||
</div>
|
</div>
|
||||||
<div className='flex'>
|
<div className='flex'>
|
||||||
<button type='button'
|
<button type='button'
|
||||||
className='clr-hover text-controls'
|
className='clr-hover clr-text-controls'
|
||||||
onClick={() => table.setPageIndex(0)}
|
onClick={() => table.setPageIndex(0)}
|
||||||
disabled={!table.getCanPreviousPage()}
|
disabled={!table.getCanPreviousPage()}
|
||||||
>
|
>
|
||||||
<GotoFirstIcon />
|
<GotoFirstIcon />
|
||||||
</button>
|
</button>
|
||||||
<button type='button'
|
<button type='button'
|
||||||
className='clr-hover text-controls'
|
className='clr-hover clr-text-controls'
|
||||||
onClick={() => table.previousPage()}
|
onClick={() => table.previousPage()}
|
||||||
disabled={!table.getCanPreviousPage()}
|
disabled={!table.getCanPreviousPage()}
|
||||||
>
|
>
|
||||||
|
@ -61,14 +68,14 @@ function PaginationTools<TData>({ table, paginationOptions, onChangePaginationOp
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<button type='button'
|
<button type='button'
|
||||||
className='clr-hover text-controls'
|
className='clr-hover clr-text-controls'
|
||||||
onClick={() => table.nextPage()}
|
onClick={() => table.nextPage()}
|
||||||
disabled={!table.getCanNextPage()}
|
disabled={!table.getCanNextPage()}
|
||||||
>
|
>
|
||||||
<GotoNextIcon />
|
<GotoNextIcon />
|
||||||
</button>
|
</button>
|
||||||
<button type='button'
|
<button type='button'
|
||||||
className='clr-hover text-controls'
|
className='clr-hover clr-text-controls'
|
||||||
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
|
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
|
||||||
disabled={!table.getCanNextPage()}
|
disabled={!table.getCanNextPage()}
|
||||||
>
|
>
|
||||||
|
|
|
@ -6,7 +6,7 @@ import InfoError from './InfoError';
|
||||||
function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
|
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>Что-то пошло не так!</h1>
|
||||||
<Button
|
<Button
|
||||||
onClick={resetErrorBoundary}
|
onClick={resetErrorBoundary}
|
||||||
text='Попробовать еще раз'
|
text='Попробовать еще раз'
|
||||||
|
|
|
@ -22,7 +22,7 @@ function ExpectedAnonymous() {
|
||||||
<TextURL text='Справка' href='/manuals'/>
|
<TextURL text='Справка' href='/manuals'/>
|
||||||
<span> | </span>
|
<span> | </span>
|
||||||
<span
|
<span
|
||||||
className='cursor-pointer hover:underline text-url'
|
className='cursor-pointer hover:underline clr-text-url'
|
||||||
onClick={logoutAndRedirect}
|
onClick={logoutAndRedirect}
|
||||||
>
|
>
|
||||||
Выйти
|
Выйти
|
||||||
|
|
|
@ -1,17 +1,26 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { urls } from '@/utils/constants';
|
import { urls } from '@/utils/constants';
|
||||||
|
|
||||||
import TextURL from './Common/TextURL';
|
import TextURL from './Common/TextURL';
|
||||||
|
|
||||||
function Footer() {
|
function Footer() {
|
||||||
return (
|
return (
|
||||||
<footer tabIndex={-1} className='flex flex-col items-center w-full gap-1 px-4 py-2 text-sm select-none z-navigation whitespace-nowrap'>
|
<footer tabIndex={-1}
|
||||||
<div className='flex gap-3 text-center'>
|
className={clsx(
|
||||||
|
'w-full z-navigation',
|
||||||
|
'px-4 py-2 flex flex-col items-center gap-1',
|
||||||
|
'text-sm select-none whitespace-nowrap'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className='flex gap-3'>
|
||||||
<TextURL text='Библиотека' href='/library' color='clr-footer'/>
|
<TextURL text='Библиотека' href='/library' color='clr-footer'/>
|
||||||
<TextURL text='Справка' href='/manuals' color='clr-footer'/>
|
<TextURL text='Справка' href='/manuals' color='clr-footer'/>
|
||||||
<TextURL text='Центр Концепт' href={urls.concept} color='clr-footer'/>
|
<TextURL text='Центр Концепт' href={urls.concept} color='clr-footer'/>
|
||||||
<TextURL text='Экстеор' href='/manuals?topic=exteor' color='clr-footer'/>
|
<TextURL text='Экстеор' href='/manuals?topic=exteor' color='clr-footer'/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className='mt-0.5 text-center clr-footer'>© 2023 ЦИВТ КОНЦЕПТ</p>
|
<p className='clr-footer'>© 2023 ЦИВТ КОНЦЕПТ</p>
|
||||||
</div>
|
</div>
|
||||||
</footer>);
|
</footer>);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,9 @@ function HelpButton({ topic, offset, dimensions }: HelpButtonProps) {
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
id={`help-${topic}`}
|
id={`help-${topic}`}
|
||||||
className='px-1 py-1'
|
className='p-1'
|
||||||
>
|
>
|
||||||
<HelpIcon color='text-primary' size={5} />
|
<HelpIcon color='clr-text-primary' size={5} />
|
||||||
</div>
|
</div>
|
||||||
<ConceptTooltip clickable
|
<ConceptTooltip clickable
|
||||||
anchorSelect={`#help-${topic}`}
|
anchorSelect={`#help-${topic}`}
|
||||||
|
|
|
@ -46,7 +46,7 @@ function DescribeError({error} : {error: ErrorData}) {
|
||||||
|
|
||||||
function InfoError({ error }: InfoErrorProps) {
|
function InfoError({ error }: InfoErrorProps) {
|
||||||
return (
|
return (
|
||||||
<div className='px-3 py-2 min-w-[15rem] text-sm font-semibold select-text text-warning'>
|
<div className='px-3 py-2 min-w-[15rem] text-sm font-semibold select-text clr-text-warning'>
|
||||||
<DescribeError error={error} />
|
<DescribeError error={error} />
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ function Logo() {
|
||||||
const { darkMode } = useConceptTheme();
|
const { darkMode } = useConceptTheme();
|
||||||
return (
|
return (
|
||||||
<img alt='Логотип КонцептПортал'
|
<img alt='Логотип КонцептПортал'
|
||||||
className='max-h-[1.6rem]'
|
className='max-h-[1.6rem] min-w-[11rem]'
|
||||||
src={!darkMode ? '/logo_full.svg' : '/logo_full_dark.svg'}
|
src={!darkMode ? '/logo_full.svg' : '/logo_full_dark.svg'}
|
||||||
/>);
|
/>);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
|
import { EducationIcon, LibraryIcon, PlusIcon } from '@/components/Icons';
|
||||||
import { useConceptNavigation } from '@/context/NagivationContext';
|
import { useConceptNavigation } from '@/context/NagivationContext';
|
||||||
import { useConceptTheme } from '@/context/ThemeContext';
|
import { useConceptTheme } from '@/context/ThemeContext';
|
||||||
|
|
||||||
import { EducationIcon, LibraryIcon, PlusIcon } from '@/components/Icons';
|
|
||||||
import Logo from './Logo';
|
import Logo from './Logo';
|
||||||
import NavigationButton from './NavigationButton';
|
import NavigationButton from './NavigationButton';
|
||||||
import ToggleNavigationButton from './ToggleNavigationButton';
|
import ToggleNavigationButton from './ToggleNavigationButton';
|
||||||
|
@ -17,14 +19,25 @@ function Navigation () {
|
||||||
const navigateCreateNew = () => router.push('/library/create');
|
const navigateCreateNew = () => router.push('/library/create');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className='sticky top-0 left-0 right-0 select-none clr-app z-navigation'>
|
<nav className={clsx(
|
||||||
|
'z-navigation',
|
||||||
|
'sticky top-0 left-0 right-0',
|
||||||
|
'clr-app',
|
||||||
|
'select-none'
|
||||||
|
)}>
|
||||||
<ToggleNavigationButton />
|
<ToggleNavigationButton />
|
||||||
{!noNavigation ?
|
{!noNavigation ?
|
||||||
<div className='flex items-stretch justify-between pl-2 pr-[0.8rem] border-b-2 rounded-none h-[3rem]'>
|
<div
|
||||||
|
className={clsx(
|
||||||
|
'pl-2 pr-[0.8rem] h-[3rem]',
|
||||||
|
'flex justify-between',
|
||||||
|
'border-b-2 rounded-none'
|
||||||
|
)}
|
||||||
|
>
|
||||||
<div className='flex items-center mr-2 cursor-pointer' onClick={navigateHome} tabIndex={-1}>
|
<div className='flex items-center mr-2 cursor-pointer' onClick={navigateHome} tabIndex={-1}>
|
||||||
<Logo />
|
<Logo />
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center h-full'>
|
<div className='flex'>
|
||||||
<NavigationButton
|
<NavigationButton
|
||||||
text='Новая схема'
|
text='Новая схема'
|
||||||
description='Создать новую схему'
|
description='Создать новую схему'
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
interface NavigationButtonProps {
|
interface NavigationButtonProps {
|
||||||
id?: string
|
id?: string
|
||||||
text?: string
|
text?: string
|
||||||
|
@ -11,7 +13,16 @@ function NavigationButton({ id, icon, description, onClick, text }: NavigationBu
|
||||||
<button id={id} type='button' tabIndex={-1}
|
<button id={id} type='button' tabIndex={-1}
|
||||||
title={description}
|
title={description}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className={`flex items-center h-full gap-1 ${text ? 'px-2' : 'px-4'} mr-1 small-caps whitespace-nowrap clr-btn-nav`}
|
className={clsx(
|
||||||
|
'mr-1 h-full',
|
||||||
|
'flex items-center gap-1',
|
||||||
|
'clr-btn-nav',
|
||||||
|
'small-caps whitespace-nowrap',
|
||||||
|
{
|
||||||
|
'px-2': text,
|
||||||
|
'px-4': !text
|
||||||
|
}
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{icon ? <span>{icon}</span> : null}
|
{icon ? <span>{icon}</span> : null}
|
||||||
{text ? <span className='font-semibold'>{text}</span> : null}
|
{text ? <span className='font-semibold'>{text}</span> : null}
|
||||||
|
|
|
@ -1,19 +1,32 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import { useConceptTheme } from '@/context/ThemeContext';
|
import { useConceptTheme } from '@/context/ThemeContext';
|
||||||
|
|
||||||
function ToggleNavigationButton() {
|
function ToggleNavigationButton() {
|
||||||
const { noNavigation, toggleNoNavigation } = useConceptTheme();
|
const { noNavigation, toggleNoNavigation } = useConceptTheme();
|
||||||
const dimensions = useMemo(() => (noNavigation ? 'px-1 h-[1.6rem]' : 'w-[1.2rem] h-[3rem]'), [noNavigation]);
|
|
||||||
const text = useMemo(() => (
|
const text = useMemo(() => (
|
||||||
noNavigation ? '∨∨∨' : <><p>{'>'}</p><p>{'>'}</p></>), [noNavigation]
|
noNavigation ?
|
||||||
|
'∨∨∨'
|
||||||
|
:
|
||||||
|
<>
|
||||||
|
<p>{'>'}</p>
|
||||||
|
<p>{'>'}</p>
|
||||||
|
</>
|
||||||
|
), [noNavigation]
|
||||||
);
|
);
|
||||||
const tooltip = useMemo(() => (noNavigation ? 'Показать навигацию' : 'Скрыть навигацию'), [noNavigation]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button type='button' tabIndex={-1}
|
<button type='button' tabIndex={-1}
|
||||||
title={tooltip}
|
title={noNavigation ? 'Показать навигацию' : 'Скрыть навигацию'}
|
||||||
className={`absolute top-0 right-0 border-b-2 border-l-2 rounded-none z-navigation clr-btn-nav ${dimensions}`}
|
className={clsx(
|
||||||
|
'absolute top-0 right-0 z-navigation',
|
||||||
|
'border-b-2 border-l-2 rounded-none',
|
||||||
|
'clr-btn-nav',
|
||||||
|
{
|
||||||
|
'px-1 h-[1.6rem]': noNavigation,
|
||||||
|
'w-[1.2rem] h-[3rem]': !noNavigation
|
||||||
|
}
|
||||||
|
)}
|
||||||
onClick={toggleNoNavigation}
|
onClick={toggleNoNavigation}
|
||||||
>
|
>
|
||||||
{text}
|
{text}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
import { Extension } from '@codemirror/state';
|
import { Extension } from '@codemirror/state';
|
||||||
import { tags } from '@lezer/highlight';
|
import { tags } from '@lezer/highlight';
|
||||||
import { createTheme } from '@uiw/codemirror-themes';
|
import { createTheme } from '@uiw/codemirror-themes';
|
||||||
import CodeMirror, { BasicSetupOptions, ReactCodeMirrorProps, ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
import CodeMirror, { BasicSetupOptions, ReactCodeMirrorProps, ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
||||||
|
import clsx from 'clsx';
|
||||||
import { EditorView } from 'codemirror';
|
import { EditorView } from 'codemirror';
|
||||||
import { RefObject, useCallback, useMemo, useRef } from 'react';
|
import { RefObject, useCallback, useMemo, useRef } from 'react';
|
||||||
|
|
||||||
|
@ -122,12 +124,12 @@ function RSInput({
|
||||||
}, [thisRef]);
|
}, [thisRef]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`flex flex-col gap-2 ${dimensions} ${cursor}`}>
|
<div className={clsx(
|
||||||
{label ?
|
'flex flex-col gap-2',
|
||||||
<Label
|
dimensions,
|
||||||
text={label}
|
cursor
|
||||||
htmlFor={id}
|
)}>
|
||||||
/> : null}
|
<Label text={label} htmlFor={id}/>
|
||||||
<CodeMirror id={id}
|
<CodeMirror id={id}
|
||||||
ref={thisRef}
|
ref={thisRef}
|
||||||
basicSetup={editorSetup}
|
basicSetup={editorSetup}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { EditorState } from '@uiw/react-codemirror';
|
||||||
import { IConstituenta } from '@/models/rsform';
|
import { IConstituenta } from '@/models/rsform';
|
||||||
import { findEnvelopingNodes } from '@/utils/codemirror';
|
import { findEnvelopingNodes } from '@/utils/codemirror';
|
||||||
import { domTooltipConstituenta } from '@/utils/codemirror';
|
import { domTooltipConstituenta } from '@/utils/codemirror';
|
||||||
|
|
||||||
import { GlobalTokens } from './rslang';
|
import { GlobalTokens } from './rslang';
|
||||||
|
|
||||||
function findAliasAt(pos: number, state: EditorState) {
|
function findAliasAt(pos: number, state: EditorState) {
|
||||||
|
|
|
@ -1,18 +1,17 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
import { Extension } from '@codemirror/state';
|
import { Extension } from '@codemirror/state';
|
||||||
import { tags } from '@lezer/highlight';
|
import { tags } from '@lezer/highlight';
|
||||||
import { createTheme } from '@uiw/codemirror-themes';
|
import { createTheme } from '@uiw/codemirror-themes';
|
||||||
import CodeMirror, { BasicSetupOptions, ReactCodeMirrorProps, ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
import CodeMirror, { BasicSetupOptions, ReactCodeMirrorProps, ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
||||||
|
import clsx from 'clsx';
|
||||||
import { EditorView } from 'codemirror';
|
import { EditorView } from 'codemirror';
|
||||||
import { RefObject, useCallback, useMemo, useRef, useState } from 'react';
|
import { RefObject, useCallback, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
import Label from '@/components/Common/Label';
|
import Label from '@/components/Common/Label';
|
||||||
import Modal from '@/components/Common/Modal';
|
|
||||||
import PrettyJson from '@/components/Common/PrettyJSON';
|
|
||||||
import { useRSForm } from '@/context/RSFormContext';
|
import { useRSForm } from '@/context/RSFormContext';
|
||||||
import { useConceptTheme } from '@/context/ThemeContext';
|
import { useConceptTheme } from '@/context/ThemeContext';
|
||||||
import DlgEditReference from '@/dialogs/DlgEditReference';
|
import DlgEditReference from '@/dialogs/DlgEditReference';
|
||||||
import useResolveText from '@/hooks/useResolveText';
|
|
||||||
import { ReferenceType } from '@/models/language';
|
import { ReferenceType } from '@/models/language';
|
||||||
import { IConstituenta } from '@/models/rsform';
|
import { IConstituenta } from '@/models/rsform';
|
||||||
import { CodeMirrorWrapper } from '@/utils/codemirror';
|
import { CodeMirrorWrapper } from '@/utils/codemirror';
|
||||||
|
@ -73,9 +72,6 @@ function RefsInput({
|
||||||
const { darkMode, colors } = useConceptTheme();
|
const { darkMode, colors } = useConceptTheme();
|
||||||
const { schema } = useRSForm();
|
const { schema } = useRSForm();
|
||||||
|
|
||||||
const { resolveText, refsData } = useResolveText({schema: schema});
|
|
||||||
|
|
||||||
const [showResolve, setShowResolve] = useState(false);
|
|
||||||
const [isFocused, setIsFocused] = useState(false);
|
const [isFocused, setIsFocused] = useState(false);
|
||||||
|
|
||||||
const [showEditor, setShowEditor] = useState(false);
|
const [showEditor, setShowEditor] = useState(false);
|
||||||
|
@ -135,15 +131,6 @@ function RefsInput({
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event.altKey) {
|
|
||||||
if (event.key === 'r' && value) {
|
|
||||||
event.preventDefault();
|
|
||||||
resolveText(value, () => {
|
|
||||||
setShowResolve(true);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (event.ctrlKey && event.code === 'Space') {
|
if (event.ctrlKey && event.code === 'Space') {
|
||||||
const wrap = new CodeMirrorWrapper(thisRef.current as Required<ReactCodeMirrorRef>);
|
const wrap = new CodeMirrorWrapper(thisRef.current as Required<ReactCodeMirrorRef>);
|
||||||
wrap.fixSelection(ReferenceTokens);
|
wrap.fixSelection(ReferenceTokens);
|
||||||
|
@ -164,7 +151,7 @@ function RefsInput({
|
||||||
|
|
||||||
setShowEditor(true);
|
setShowEditor(true);
|
||||||
}
|
}
|
||||||
}, [thisRef, resolveText, value]);
|
}, [thisRef]);
|
||||||
|
|
||||||
const handleInputReference = useCallback(
|
const handleInputReference = useCallback(
|
||||||
(referenceText: string) => {
|
(referenceText: string) => {
|
||||||
|
@ -176,8 +163,7 @@ function RefsInput({
|
||||||
wrap.replaceWith(referenceText);
|
wrap.replaceWith(referenceText);
|
||||||
}, [thisRef]);
|
}, [thisRef]);
|
||||||
|
|
||||||
return (
|
return (<>
|
||||||
<>
|
|
||||||
{showEditor ?
|
{showEditor ?
|
||||||
<DlgEditReference
|
<DlgEditReference
|
||||||
hideWindow={() => setShowEditor(false)}
|
hideWindow={() => setShowEditor(false)}
|
||||||
|
@ -191,22 +177,12 @@ function RefsInput({
|
||||||
}}
|
}}
|
||||||
onSave={handleInputReference}
|
onSave={handleInputReference}
|
||||||
/> : null}
|
/> : null}
|
||||||
{showResolve ?
|
<div className={clsx(
|
||||||
<Modal
|
'w-full',
|
||||||
readonly
|
'flex flex-col gap-2',
|
||||||
hideWindow={() => setShowResolve(false)}
|
cursor
|
||||||
>
|
)}>
|
||||||
<div className='max-h-[60vh] max-w-[80vw] overflow-auto'>
|
<Label text={label} htmlFor={id} />
|
||||||
<PrettyJson data={refsData} />
|
|
||||||
</div>
|
|
||||||
</Modal> : null}
|
|
||||||
<div className={`flex flex-col w-full ${cursor}`}>
|
|
||||||
{label ?
|
|
||||||
<Label
|
|
||||||
text={label}
|
|
||||||
htmlFor={id}
|
|
||||||
className='mb-2'
|
|
||||||
/> : null}
|
|
||||||
<CodeMirror id={id} ref={thisRef}
|
<CodeMirror id={id} ref={thisRef}
|
||||||
basicSetup={editorSetup}
|
basicSetup={editorSetup}
|
||||||
theme={customTheme}
|
theme={customTheme}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { parseEntityReference, parseSyntacticReference } from '@/models/language
|
||||||
import { IConstituenta } from '@/models/rsform';
|
import { IConstituenta } from '@/models/rsform';
|
||||||
import { domTooltipEntityReference, domTooltipSyntacticReference, findContainedNodes, findEnvelopingNodes } from '@/utils/codemirror';
|
import { domTooltipEntityReference, domTooltipSyntacticReference, findContainedNodes, findEnvelopingNodes } from '@/utils/codemirror';
|
||||||
import { IColorTheme } from '@/utils/color';
|
import { IColorTheme } from '@/utils/color';
|
||||||
|
|
||||||
import { ReferenceTokens } from './parse';
|
import { ReferenceTokens } from './parse';
|
||||||
import { RefEntity, RefSyntactic } from './parse/parser.terms';
|
import { RefEntity, RefSyntactic } from './parse/parser.terms';
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import ConceptTooltip from '@/components/Common/ConceptTooltip';
|
import ConceptTooltip from '@/components/Common/ConceptTooltip';
|
||||||
import ConstituentaTooltip from '@/components/Help/ConstituentaTooltip';
|
import ConstituentaTooltip from '@/components/Help/ConstituentaTooltip';
|
||||||
import { IConstituenta } from '@/models/rsform';
|
import { IConstituenta } from '@/models/rsform';
|
||||||
|
@ -17,13 +19,16 @@ function ConstituentaBadge({ value, prefixID, shortTooltip, theme }: Constituent
|
||||||
<div className='w-fit'>
|
<div className='w-fit'>
|
||||||
<div
|
<div
|
||||||
id={`${prefixID}${value.alias}`}
|
id={`${prefixID}${value.alias}`}
|
||||||
className='min-w-[3.1rem] max-w-[3.1rem] px-1 text-center rounded-md whitespace-nowrap'
|
className={clsx(
|
||||||
|
'min-w-[3.1rem] max-w-[3.1rem]',
|
||||||
|
'px-1',
|
||||||
|
'border rounded-md',
|
||||||
|
'text-center font-semibold whitespace-nowrap'
|
||||||
|
)}
|
||||||
style={{
|
style={{
|
||||||
borderWidth: '1px',
|
|
||||||
borderColor: colorfgCstStatus(value.status, theme),
|
borderColor: colorfgCstStatus(value.status, theme),
|
||||||
color: colorfgCstStatus(value.status, theme),
|
color: colorfgCstStatus(value.status, theme),
|
||||||
backgroundColor: isMockCst(value) ? theme.bgWarning : theme.bgInput,
|
backgroundColor: isMockCst(value) ? theme.bgWarning : theme.bgInput
|
||||||
fontWeight: 600
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{value.alias}
|
{value.alias}
|
||||||
|
@ -38,7 +43,7 @@ function ConstituentaBadge({ value, prefixID, shortTooltip, theme }: Constituent
|
||||||
anchorSelect={`#${prefixID}${value.alias}`}
|
anchorSelect={`#${prefixID}${value.alias}`}
|
||||||
place='right'
|
place='right'
|
||||||
>
|
>
|
||||||
<p><span className='font-semibold'>Статус</span>: {describeExpressionStatus(value.status)}</p>
|
<p><b>Статус</b>: {describeExpressionStatus(value.status)}</p>
|
||||||
</ConceptTooltip> : null}
|
</ConceptTooltip> : null}
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,8 +82,8 @@ function ConstituentaPicker({
|
||||||
}], [value, colors]);
|
}], [value, colors]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div>
|
||||||
<ConceptSearch dense
|
<ConceptSearch
|
||||||
value={filterText}
|
value={filterText}
|
||||||
onChange={newValue => setFilterText(newValue)}
|
onChange={newValue => setFilterText(newValue)}
|
||||||
/>
|
/>
|
||||||
|
@ -96,7 +96,7 @@ function ConstituentaPicker({
|
||||||
columns={columns}
|
columns={columns}
|
||||||
conditionalRowStyles={conditionalRowStyles}
|
conditionalRowStyles={conditionalRowStyles}
|
||||||
noDataComponent={
|
noDataComponent={
|
||||||
<span className='flex flex-col justify-center p-2 text-center min-h-[5rem]'>
|
<span className='p-2 min-h-[5rem] flex flex-col justify-center text-center'>
|
||||||
<p>Список конституент пуст</p>
|
<p>Список конституент пуст</p>
|
||||||
<p>Измените параметры фильтра</p>
|
<p>Измените параметры фильтра</p>
|
||||||
</span>
|
</span>
|
||||||
|
@ -104,7 +104,7 @@ function ConstituentaPicker({
|
||||||
onRowClicked={onSelectValue}
|
onRowClicked={onSelectValue}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ConstituentaPicker;
|
export default ConstituentaPicker;
|
|
@ -1,3 +1,5 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { useConceptTheme } from '@/context/ThemeContext';
|
import { useConceptTheme } from '@/context/ThemeContext';
|
||||||
import { GramData } from '@/models/language';
|
import { GramData } from '@/models/language';
|
||||||
import { colorfgGrammeme } from '@/utils/color';
|
import { colorfgGrammeme } from '@/utils/color';
|
||||||
|
@ -13,12 +15,15 @@ function GrammemeBadge({ key, grammeme }: GrammemeBadgeProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={key}
|
key={key}
|
||||||
className='min-w-[3rem] px-1 text-sm text-center rounded-md whitespace-nowrap'
|
className={clsx(
|
||||||
|
'min-w-[3rem]',
|
||||||
|
'px-1',
|
||||||
|
'border rounded-md',
|
||||||
|
'text-sm font-semibold text-center whitespace-nowrap'
|
||||||
|
)}
|
||||||
style={{
|
style={{
|
||||||
borderWidth: '1px',
|
|
||||||
borderColor: colorfgGrammeme(grammeme, colors),
|
borderColor: colorfgGrammeme(grammeme, colors),
|
||||||
color: colorfgGrammeme(grammeme, colors),
|
color: colorfgGrammeme(grammeme, colors),
|
||||||
fontWeight: 600,
|
|
||||||
backgroundColor: colors.bgInput
|
backgroundColor: colors.bgInput
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { useConceptTheme } from '@/context/ThemeContext';
|
import { useConceptTheme } from '@/context/ThemeContext';
|
||||||
import { CstClass } from '@/models/rsform';
|
import { CstClass } from '@/models/rsform';
|
||||||
import { colorbgCstClass } from '@/utils/color';
|
import { colorbgCstClass } from '@/utils/color';
|
||||||
|
@ -19,7 +21,13 @@ function InfoCstClass({ title }: InfoCstClassProps) {
|
||||||
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={clsx(
|
||||||
|
'inline-block',
|
||||||
|
'min-w-[7rem]',
|
||||||
|
'px-1',
|
||||||
|
'border',
|
||||||
|
'text-center text-sm small-caps font-semibold'
|
||||||
|
)}
|
||||||
style={{backgroundColor: colorbgCstClass(cclass, colors)}}
|
style={{backgroundColor: colorbgCstClass(cclass, colors)}}
|
||||||
>
|
>
|
||||||
{labelCstClass(cclass)}
|
{labelCstClass(cclass)}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { useConceptTheme } from '@/context/ThemeContext';
|
import { useConceptTheme } from '@/context/ThemeContext';
|
||||||
import { ExpressionStatus } from '@/models/rsform';
|
import { ExpressionStatus } from '@/models/rsform';
|
||||||
import { colorbgCstStatus } from '@/utils/color';
|
import { colorbgCstStatus } from '@/utils/color';
|
||||||
|
@ -17,11 +19,16 @@ function InfoCstStatus({ title }: InfoCstStatusProps) {
|
||||||
{Object.values(ExpressionStatus)
|
{Object.values(ExpressionStatus)
|
||||||
.filter(status => status !== ExpressionStatus.UNDEFINED)
|
.filter(status => status !== ExpressionStatus.UNDEFINED)
|
||||||
.map(
|
.map(
|
||||||
(status, index) => {
|
(status, index) =>
|
||||||
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={clsx(
|
||||||
|
'inline-block',
|
||||||
|
'min-w-[7rem]',
|
||||||
|
'px-1',
|
||||||
|
'border',
|
||||||
|
'text-center text-sm small-caps font-semibold'
|
||||||
|
)}
|
||||||
style={{backgroundColor: colorbgCstStatus(status, colors)}}
|
style={{backgroundColor: colorbgCstStatus(status, colors)}}
|
||||||
>
|
>
|
||||||
{labelExpressionStatus(status)}
|
{labelExpressionStatus(status)}
|
||||||
|
@ -30,8 +37,7 @@ function InfoCstStatus({ title }: InfoCstStatusProps) {
|
||||||
<span>
|
<span>
|
||||||
{describeExpressionStatus(status)}
|
{describeExpressionStatus(status)}
|
||||||
</span>
|
</span>
|
||||||
</p>);
|
</p>
|
||||||
}
|
|
||||||
)}
|
)}
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { createColumnHelper } from '@tanstack/react-table';
|
import { createColumnHelper } from '@tanstack/react-table';
|
||||||
|
import clsx from 'clsx';
|
||||||
import { Dispatch, useCallback, useEffect, useMemo, useState } from 'react';
|
import { Dispatch, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import MiniButton from '@/components/Common/MiniButton';
|
import MiniButton from '@/components/Common/MiniButton';
|
||||||
|
@ -95,7 +96,10 @@ function ArgumentsTab({ state, schema, partialUpdate }: ArgumentsTabProps) {
|
||||||
size: 40,
|
size: 40,
|
||||||
minSize: 40,
|
minSize: 40,
|
||||||
maxSize: 40,
|
maxSize: 40,
|
||||||
cell: props => <div className='w-full text-center'>{props.getValue()}</div>
|
cell: props =>
|
||||||
|
<div className='w-full text-center'>
|
||||||
|
{props.getValue()}
|
||||||
|
</div>
|
||||||
}),
|
}),
|
||||||
argumentsHelper.accessor(arg => arg.value || 'свободный аргумент', {
|
argumentsHelper.accessor(arg => arg.value || 'свободный аргумент', {
|
||||||
id: 'value',
|
id: 'value',
|
||||||
|
@ -111,7 +115,13 @@ function ArgumentsTab({ state, schema, partialUpdate }: ArgumentsTabProps) {
|
||||||
minSize: 150,
|
minSize: 150,
|
||||||
maxSize: 150,
|
maxSize: 150,
|
||||||
enableHiding: true,
|
enableHiding: true,
|
||||||
cell: props => <div className='text-sm min-w-[9.3rem] max-w-[9.3rem] break-words'>{props.getValue()}</div>
|
cell: props =>
|
||||||
|
<div className={clsx(
|
||||||
|
'min-w-[9.3rem] max-w-[9.3rem]',
|
||||||
|
'text-sm break-words'
|
||||||
|
)}>
|
||||||
|
{props.getValue()}
|
||||||
|
</div>
|
||||||
}),
|
}),
|
||||||
argumentsHelper.display({
|
argumentsHelper.display({
|
||||||
id: 'actions',
|
id: 'actions',
|
||||||
|
@ -120,13 +130,13 @@ function ArgumentsTab({ state, schema, partialUpdate }: ArgumentsTabProps) {
|
||||||
maxSize: 50,
|
maxSize: 50,
|
||||||
cell: props =>
|
cell: props =>
|
||||||
<div className='max-h-[1.2rem]'>
|
<div className='max-h-[1.2rem]'>
|
||||||
{props.row.original.value &&
|
{props.row.original.value ?
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Очистить значение'
|
tooltip='Очистить значение'
|
||||||
icon={<CrossIcon size={3} color='text-warning'/>}
|
icon={<CrossIcon size={3} color='clr-text-warning'/>}
|
||||||
noHover
|
noHover
|
||||||
onClick={() => handleClearArgument(props.row.original)}
|
onClick={() => handleClearArgument(props.row.original)}
|
||||||
/>}
|
/> : null}
|
||||||
</div>
|
</div>
|
||||||
})
|
})
|
||||||
], [handleClearArgument]);
|
], [handleClearArgument]);
|
||||||
|
@ -139,21 +149,35 @@ 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={clsx(
|
||||||
|
'max-h-[5.8rem] min-h-[5.8rem]',
|
||||||
|
'overflow-y-auto',
|
||||||
|
'text-sm',
|
||||||
|
'border',
|
||||||
|
'select-none'
|
||||||
|
)}>
|
||||||
<DataTable dense noFooter
|
<DataTable dense noFooter
|
||||||
data={state.arguments}
|
data={state.arguments}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
conditionalRowStyles={conditionalRowStyles}
|
conditionalRowStyles={conditionalRowStyles}
|
||||||
noDataComponent={
|
noDataComponent={
|
||||||
<span className='flex flex-col justify-center p-2 text-center min-h-[3.6rem]'>
|
<p className={clsx(
|
||||||
<p>Аргументы отсутствуют</p>
|
'min-h-[3.6rem] w-full',
|
||||||
</span>
|
'p-2',
|
||||||
|
'text-center'
|
||||||
|
)}>
|
||||||
|
Аргументы отсутствуют
|
||||||
|
</p>
|
||||||
}
|
}
|
||||||
onRowClicked={handleSelectArgument}
|
onRowClicked={handleSelectArgument}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex items-center justify-center w-full gap-2 py-1 select-none'>
|
<div className={clsx(
|
||||||
|
'py-1 flex gap-2 justify-center items-center',
|
||||||
|
'w-full',
|
||||||
|
'select-none'
|
||||||
|
)}>
|
||||||
<span title='Выберите аргумент из списка сверху и значение из списка снизу'
|
<span title='Выберите аргумент из списка сверху и значение из списка снизу'
|
||||||
className='font-semibold text-center'
|
className='font-semibold text-center'
|
||||||
>
|
>
|
||||||
|
@ -168,7 +192,7 @@ function ArgumentsTab({ state, schema, partialUpdate }: ArgumentsTabProps) {
|
||||||
<div className='flex'>
|
<div className='flex'>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Подставить значение аргумента'
|
tooltip='Подставить значение аргумента'
|
||||||
icon={<CheckIcon size={5} color={!argumentValue || !selectedArgument ? 'text-disabled' : 'text-success'} />}
|
icon={<CheckIcon size={5} color={!argumentValue || !selectedArgument ? 'text-disabled' : 'clr-text-success'} />}
|
||||||
disabled={!argumentValue || !selectedArgument}
|
disabled={!argumentValue || !selectedArgument}
|
||||||
onClick={() => handleAssignArgument(selectedArgument!, argumentValue)}
|
onClick={() => handleAssignArgument(selectedArgument!, argumentValue)}
|
||||||
/>
|
/>
|
||||||
|
@ -176,12 +200,12 @@ function ArgumentsTab({ state, schema, partialUpdate }: ArgumentsTabProps) {
|
||||||
tooltip='Откатить значение'
|
tooltip='Откатить значение'
|
||||||
disabled={!isModified}
|
disabled={!isModified}
|
||||||
onClick={handleReset}
|
onClick={handleReset}
|
||||||
icon={<ArrowsRotateIcon size={5} color={isModified ? 'text-primary' : ''} />}
|
icon={<ArrowsRotateIcon size={5} color={isModified ? 'clr-text-primary' : ''} />}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Очистить значение аргумента'
|
tooltip='Очистить значение аргумента'
|
||||||
disabled={!selectedClearable}
|
disabled={!selectedClearable}
|
||||||
icon={<CrossIcon size={5} color={!selectedClearable ? 'text-disabled' : 'text-warning'}/>}
|
icon={<CrossIcon size={5} color={!selectedClearable ? 'text-disabled' : 'clr-text-warning'}/>}
|
||||||
onClick={() => selectedArgument ? handleClearArgument(selectedArgument) : undefined}
|
onClick={() => selectedArgument ? handleClearArgument(selectedArgument) : undefined}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -117,7 +117,7 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
|
||||||
canSubmit={validated}
|
canSubmit={validated}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
submitText='Создать'
|
submitText='Создать'
|
||||||
className='max-w-[43rem] min-w-[43rem] min-h-[35rem] px-6'
|
className='max-w-[43rem] min-w-[43rem] min-h-[36rem] px-6'
|
||||||
>
|
>
|
||||||
<Tabs defaultFocus forceRenderTabPanel
|
<Tabs defaultFocus forceRenderTabPanel
|
||||||
selectedTabClassName='clr-selected'
|
selectedTabClassName='clr-selected'
|
||||||
|
@ -129,16 +129,16 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
|
||||||
<TabList className='flex justify-center mb-3'>
|
<TabList className='flex justify-center mb-3'>
|
||||||
<div className='flex border w-fit'>
|
<div className='flex border divide-x rounded-none w-fit'>
|
||||||
<ConceptTab
|
<ConceptTab
|
||||||
label='Шаблон'
|
label='Шаблон'
|
||||||
tooltip='Выбор шаблона выражения'
|
tooltip='Выбор шаблона выражения'
|
||||||
className='border-r w-[8rem]'
|
className='w-[8rem]'
|
||||||
/>
|
/>
|
||||||
<ConceptTab
|
<ConceptTab
|
||||||
label='Аргументы'
|
label='Аргументы'
|
||||||
tooltip='Подстановка аргументов шаблона'
|
tooltip='Подстановка аргументов шаблона'
|
||||||
className='border-r w-[8rem]'
|
className='w-[8rem]'
|
||||||
/>
|
/>
|
||||||
<ConceptTab
|
<ConceptTab
|
||||||
label='Конституента'
|
label='Конституента'
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import clsx from 'clsx';
|
||||||
import { useEffect, useLayoutEffect, useState } from 'react';
|
import { useEffect, useLayoutEffect, useState } from 'react';
|
||||||
|
|
||||||
import Modal, { ModalProps } from '@/components/Common/Modal';
|
import Modal, { ModalProps } from '@/components/Common/Modal';
|
||||||
|
@ -54,7 +55,10 @@ function DlgCreateCst({ hideWindow, initial, schema, onCreate }: DlgCreateCstPro
|
||||||
canSubmit={validated}
|
canSubmit={validated}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
submitText='Создать'
|
submitText='Создать'
|
||||||
className='h-fit min-w-[35rem] py-2 flex flex-col justify-stretch gap-3 px-6'
|
className={clsx(
|
||||||
|
'h-fit min-w-[35rem]',
|
||||||
|
'py-2 px-6 flex flex-col gap-3 justify-stretch'
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<div className='flex justify-center w-full gap-6'>
|
<div className='flex justify-center w-full gap-6'>
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { IConstituenta } from '@/models/rsform';
|
import { IConstituenta } from '@/models/rsform';
|
||||||
import { labelConstituenta } from '@/utils/labels';
|
import { labelConstituenta } from '@/utils/labels';
|
||||||
|
|
||||||
|
@ -15,7 +17,13 @@ function ConstituentsList({ list, items, title, prefix }: ConstituentsListProps)
|
||||||
<p className='pb-1'>
|
<p className='pb-1'>
|
||||||
{title}: <b>{list.length}</b>
|
{title}: <b>{list.length}</b>
|
||||||
</p> : null}
|
</p> : null}
|
||||||
<div className='px-3 border h-[9rem] overflow-y-auto whitespace-nowrap'>
|
<div className={clsx(
|
||||||
|
'h-[9rem]',
|
||||||
|
'px-3',
|
||||||
|
'overflow-y-auto',
|
||||||
|
'border',
|
||||||
|
'whitespace-nowrap'
|
||||||
|
)}>
|
||||||
{list.map(
|
{list.map(
|
||||||
(id) => {
|
(id) => {
|
||||||
const cst = items.find(cst => cst.id === id);
|
const cst = items.find(cst => cst.id === id);
|
||||||
|
|
|
@ -50,7 +50,7 @@ function DlgEditReference({ hideWindow, items, initial, onSave }: DlgEditReferen
|
||||||
hideWindow={hideWindow}
|
hideWindow={hideWindow}
|
||||||
canSubmit={isValid}
|
canSubmit={isValid}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
className='items-center min-w-[40rem] max-w-[40rem] px-6 min-h-[34rem]'
|
className='min-w-[40rem] max-w-[40rem] px-6 min-h-[34rem]'
|
||||||
>
|
>
|
||||||
<Tabs defaultFocus
|
<Tabs defaultFocus
|
||||||
selectedTabClassName='clr-selected'
|
selectedTabClassName='clr-selected'
|
||||||
|
@ -62,15 +62,15 @@ function DlgEditReference({ hideWindow, items, initial, onSave }: DlgEditReferen
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
|
||||||
<TabList className='flex justify-center mb-3'>
|
<TabList className='flex justify-center mb-3'>
|
||||||
<div className='flex border w-fit'>
|
<div className='flex border divide-x rounded-none w-fit'>
|
||||||
<ConceptTab
|
<ConceptTab
|
||||||
label={labelReferenceType(ReferenceType.ENTITY)}
|
|
||||||
tooltip='Отсылка на термин в заданной словоформе'
|
tooltip='Отсылка на термин в заданной словоформе'
|
||||||
className='w-[12rem] border-r-2'
|
label={labelReferenceType(ReferenceType.ENTITY)}
|
||||||
|
className='w-[12rem]'
|
||||||
/>
|
/>
|
||||||
<ConceptTab
|
<ConceptTab
|
||||||
label={labelReferenceType(ReferenceType.SYNTACTIC)}
|
|
||||||
tooltip='Установление синтаксической связи с отсылкой на термин'
|
tooltip='Установление синтаксической связи с отсылкой на термин'
|
||||||
|
label={labelReferenceType(ReferenceType.SYNTACTIC)}
|
||||||
className='w-[12rem]'
|
className='w-[12rem]'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -73,11 +73,11 @@ function EntityTab({ initial, items, setIsValid, setReference }: EntityTabProps)
|
||||||
rows={8}
|
rows={8}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className='flex gap-4 flex-start'>
|
<div className='flex gap-6 flex-start'>
|
||||||
<TextInput dense
|
<TextInput dense
|
||||||
label='Отсылаемая конституента'
|
label='Отсылаемая конституента'
|
||||||
placeholder='Имя'
|
placeholder='Имя'
|
||||||
dimensions='max-w-[16rem] min-w-[16rem] whitespace-nowrap'
|
dimensions='max-w-[17rem] min-w-[17rem]'
|
||||||
value={alias}
|
value={alias}
|
||||||
onChange={event => setAlias(event.target.value)}
|
onChange={event => setAlias(event.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { Grammeme } from '@/models/language';
|
import { Grammeme } from '@/models/language';
|
||||||
|
|
||||||
interface WordformButtonProps {
|
interface WordformButtonProps {
|
||||||
|
@ -12,7 +14,14 @@ function WordformButton({ text, example, grams, onSelectGrams, isSelected, ...re
|
||||||
return (
|
return (
|
||||||
<button type='button' tabIndex={-1}
|
<button type='button' tabIndex={-1}
|
||||||
onClick={() => onSelectGrams(grams)}
|
onClick={() => onSelectGrams(grams)}
|
||||||
className={`min-w-[6rem] p-1 border rounded-none cursor-pointer clr-btn-clear clr-hover ${isSelected ? 'clr-selected': ''}`}
|
className={clsx(
|
||||||
|
'min-w-[6rem]',
|
||||||
|
'p-1',
|
||||||
|
'border rounded-none',
|
||||||
|
'cursor-pointer',
|
||||||
|
'clr-btn-clear clr-hover',
|
||||||
|
isSelected && 'clr-selected'
|
||||||
|
)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
<p className='font-semibold'>{text}</p>
|
<p className='font-semibold'>{text}</p>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import clsx from 'clsx';
|
||||||
import { useLayoutEffect, useState } from 'react';
|
import { useLayoutEffect, useState } from 'react';
|
||||||
|
|
||||||
import Label from '@/components/Common/Label';
|
import Label from '@/components/Common/Label';
|
||||||
|
@ -151,7 +152,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
|
||||||
<div className='max-w-min'>
|
<div className='max-w-min'>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Генерировать словоформу'
|
tooltip='Генерировать словоформу'
|
||||||
icon={<ArrowLeftIcon size={5} color={inputGrams.length == 0 ? 'text-disabled' : 'text-primary'} />}
|
icon={<ArrowLeftIcon size={5} color={inputGrams.length == 0 ? 'text-disabled' : 'clr-text-primary'} />}
|
||||||
disabled={textProcessor.loading || inputGrams.length == 0}
|
disabled={textProcessor.loading || inputGrams.length == 0}
|
||||||
onClick={handleInflect}
|
onClick={handleInflect}
|
||||||
/>
|
/>
|
||||||
|
@ -159,7 +160,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
|
||||||
tooltip='Определить граммемы'
|
tooltip='Определить граммемы'
|
||||||
icon={<ArrowRightIcon
|
icon={<ArrowRightIcon
|
||||||
size={5}
|
size={5}
|
||||||
color={!inputText ? 'text-disabled' : 'text-primary'}
|
color={!inputText ? 'text-disabled' : 'clr-text-primary'}
|
||||||
/>}
|
/>}
|
||||||
disabled={textProcessor.loading || !inputText}
|
disabled={textProcessor.loading || !inputText}
|
||||||
onClick={handleParse}
|
onClick={handleParse}
|
||||||
|
@ -180,7 +181,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
|
||||||
tooltip='Внести словоформу'
|
tooltip='Внести словоформу'
|
||||||
icon={<CheckIcon
|
icon={<CheckIcon
|
||||||
size={5}
|
size={5}
|
||||||
color={!inputText || inputGrams.length == 0 ? 'text-disabled' : 'text-success'}
|
color={!inputText || inputGrams.length == 0 ? 'text-disabled' : 'clr-text-success'}
|
||||||
/>}
|
/>}
|
||||||
disabled={textProcessor.loading || !inputText || inputGrams.length == 0}
|
disabled={textProcessor.loading || !inputText || inputGrams.length == 0}
|
||||||
onClick={handleAddForm}
|
onClick={handleAddForm}
|
||||||
|
@ -189,16 +190,26 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
|
||||||
tooltip='Генерировать стандартные словоформы'
|
tooltip='Генерировать стандартные словоформы'
|
||||||
icon={<ChevronDoubleDownIcon
|
icon={<ChevronDoubleDownIcon
|
||||||
size={5}
|
size={5}
|
||||||
color={!inputText ? 'text-disabled' : 'text-primary'}
|
color={!inputText ? 'text-disabled' : 'clr-text-primary'}
|
||||||
/>}
|
/>}
|
||||||
disabled={textProcessor.loading || !inputText}
|
disabled={textProcessor.loading || !inputText}
|
||||||
onClick={handleGenerateLexeme}
|
onClick={handleGenerateLexeme}
|
||||||
/>
|
/>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
|
||||||
<h1 className='mt-3 mb-2 text-sm'>Заданные вручную словоформы [{forms.length}]</h1>
|
<div className={clsx(
|
||||||
|
'mt-3 mb-2',
|
||||||
|
'text-sm text-center font-semibold'
|
||||||
|
)}>
|
||||||
|
Заданные вручную словоформы [{forms.length}]
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className='border overflow-y-auto max-h-[17.4rem] min-h-[17.4rem] mb-2'>
|
<div className={clsx(
|
||||||
|
'mb-2',
|
||||||
|
'max-h-[17.4rem] min-h-[17.4rem]',
|
||||||
|
'border',
|
||||||
|
'overflow-y-auto'
|
||||||
|
)}>
|
||||||
<WordFormsTable
|
<WordFormsTable
|
||||||
forms={forms}
|
forms={forms}
|
||||||
setForms={setForms}
|
setForms={setForms}
|
||||||
|
|
|
@ -89,7 +89,7 @@ function WordFormsTable({ forms, setForms, onFormSelect, loading }: WordFormsTab
|
||||||
columns={columns}
|
columns={columns}
|
||||||
headPosition='0'
|
headPosition='0'
|
||||||
noDataComponent={
|
noDataComponent={
|
||||||
<span className='flex flex-col justify-center p-2 text-center min-h-[2rem]'>
|
<span className='p-2 text-center min-h-[2rem]'>
|
||||||
<p>Список пуст</p>
|
<p>Список пуст</p>
|
||||||
<p>Добавьте словоформу</p>
|
<p>Добавьте словоформу</p>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import clsx from 'clsx';
|
||||||
import { useLayoutEffect, useState } from 'react';
|
import { useLayoutEffect, useState } from 'react';
|
||||||
|
|
||||||
import Modal, { ModalProps } from '@/components/Common/Modal';
|
import Modal, { ModalProps } from '@/components/Common/Modal';
|
||||||
|
@ -49,7 +50,10 @@ function DlgRenameCst({ hideWindow, initial, onRename }: DlgRenameCstProps) {
|
||||||
hideWindow={hideWindow}
|
hideWindow={hideWindow}
|
||||||
canSubmit={validated}
|
canSubmit={validated}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
className='flex justify-center items-center gap-6 w-full min-w-[24rem] py-6 px-6'
|
className={clsx(
|
||||||
|
'w-full min-w-[24rem]',
|
||||||
|
'py-6 px-6 flex gap-6 justify-center items-center'
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
placeholder='Выберите тип'
|
placeholder='Выберите тип'
|
||||||
|
|
|
@ -68,7 +68,7 @@ function DlgShowAST({ hideWindow, syntaxTree, expression }: DlgShowASTProps) {
|
||||||
<span>{expression.slice(hoverNode.finish)}</span>
|
<span>{expression.slice(hoverNode.finish)}</span>
|
||||||
</div> : null}
|
</div> : null}
|
||||||
</div>
|
</div>
|
||||||
<div className='flex-wrap w-full h-full overflow-auto'>
|
<div className='w-full h-full overflow-auto'>
|
||||||
<div
|
<div
|
||||||
className='relative'
|
className='relative'
|
||||||
style={{
|
style={{
|
||||||
|
|
|
@ -167,6 +167,7 @@
|
||||||
.divide-x, .divide-y, .divide-x-2, .divide-y-2
|
.divide-x, .divide-y, .divide-x-2, .divide-y-2
|
||||||
) {
|
) {
|
||||||
border-color: var(--cl-bg-40);
|
border-color: var(--cl-bg-40);
|
||||||
|
@apply divide-inherit;
|
||||||
.dark & {
|
.dark & {
|
||||||
border-color: var(--cd-bg-40);
|
border-color: var(--cd-bg-40);
|
||||||
}
|
}
|
||||||
|
@ -264,8 +265,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:is(.text-primary,
|
:is(.clr-text-primary,
|
||||||
.text-url
|
.clr-text-url
|
||||||
) {
|
) {
|
||||||
color: var(--cl-prim-fg-80);
|
color: var(--cl-prim-fg-80);
|
||||||
.dark & {
|
.dark & {
|
||||||
|
@ -280,7 +281,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:is(.text-controls,
|
:is(.clr-text-controls,
|
||||||
.clr-btn-nav,
|
.clr-btn-nav,
|
||||||
.clr-btn-clear
|
.clr-btn-clear
|
||||||
) {
|
) {
|
||||||
|
@ -303,21 +304,21 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-default {
|
.clr-text-default {
|
||||||
color: var(--cl-fg-100);
|
color: var(--cl-fg-100);
|
||||||
.dark & {
|
.dark & {
|
||||||
color: var(--cd-fg-100);
|
color: var(--cd-fg-100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-warning {
|
.clr-text-warning {
|
||||||
color: var(--cl-red-fg-100);
|
color: var(--cl-red-fg-100);
|
||||||
.dark & {
|
.dark & {
|
||||||
color: var(--cd-red-fg-100);
|
color: var(--cd-red-fg-100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-success {
|
.clr-text-success {
|
||||||
color: var(--cl-green-fg-100);
|
color: var(--cl-green-fg-100);
|
||||||
.dark & {
|
.dark & {
|
||||||
color: var(--cd-green-fg-100);
|
color: var(--cd-green-fg-100);
|
||||||
|
@ -341,7 +342,7 @@
|
||||||
border-color: var(--cd-bg-40);
|
border-color: var(--cd-bg-40);
|
||||||
outline-color: var(--cd-prim-bg-100);
|
outline-color: var(--cd-prim-bg-100);
|
||||||
}
|
}
|
||||||
@apply border rounded outline-2 outline
|
@apply outline-2 outline
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-editor .cm-placeholder {
|
.cm-editor .cm-placeholder {
|
||||||
|
|
|
@ -195,19 +195,17 @@ export function inferClass(type: CstType, isTemplate: boolean): CstClass {
|
||||||
* Creates a mock {@link IConstituenta} object with the provided parameters and default values for other properties.
|
* Creates a mock {@link IConstituenta} object with the provided parameters and default values for other properties.
|
||||||
*/
|
*/
|
||||||
export function createMockConstituenta(
|
export function createMockConstituenta(
|
||||||
schema: number,
|
|
||||||
id: number,
|
id: number,
|
||||||
alias: string,
|
alias: string,
|
||||||
type: CstType,
|
|
||||||
comment: string
|
comment: string
|
||||||
): IConstituenta {
|
): IConstituenta {
|
||||||
return {
|
return {
|
||||||
id: id,
|
id: id,
|
||||||
order: -1,
|
order: -1,
|
||||||
schema: schema,
|
schema: -1,
|
||||||
alias: alias,
|
alias: alias,
|
||||||
convention: comment,
|
convention: comment,
|
||||||
cst_type: type,
|
cst_type: CstType.BASE,
|
||||||
term_raw: '',
|
term_raw: '',
|
||||||
term_resolved: '',
|
term_resolved: '',
|
||||||
term_forms: [],
|
term_forms: [],
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import clsx from 'clsx';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
|
@ -79,7 +80,10 @@ function CreateRSFormPage() {
|
||||||
return (
|
return (
|
||||||
<RequireAuth>
|
<RequireAuth>
|
||||||
<form
|
<form
|
||||||
className='flex flex-col w-full max-w-lg gap-3 px-6 py-3'
|
className={clsx(
|
||||||
|
'w-full max-w-lg',
|
||||||
|
'px-6 py-3 flex flex-col gap-3'
|
||||||
|
)}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
>
|
>
|
||||||
<h1>Создание концептуальной схемы</h1>
|
<h1>Создание концептуальной схемы</h1>
|
||||||
|
@ -91,7 +95,7 @@ function CreateRSFormPage() {
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Загрузить из Экстеор'
|
tooltip='Загрузить из Экстеор'
|
||||||
icon={<DownloadIcon size={5} color='text-primary'/>}
|
icon={<DownloadIcon size={5} color='clr-text-primary'/>}
|
||||||
onClick={() => inputRef.current?.click()}
|
onClick={() => inputRef.current?.click()}
|
||||||
/>
|
/>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
import { useLayoutEffect } from 'react';
|
import { useLayoutEffect } from 'react';
|
||||||
|
|
||||||
import { useAuth } from '@/context/AuthContext';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
|
@ -21,7 +22,10 @@ function HomePage() {
|
||||||
}, [router, user])
|
}, [router, user])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col items-center justify-center w-full px-4 py-2'>
|
<div className={clsx(
|
||||||
|
'w-full',
|
||||||
|
'px-4 py-2 flex flex-col justify-center items-center'
|
||||||
|
)}>
|
||||||
{user?.is_staff ?
|
{user?.is_staff ?
|
||||||
<p>
|
<p>
|
||||||
Лендинг находится в разработке. Данная страница видна только пользователям с правами администратора.
|
Лендинг находится в разработке. Данная страница видна только пользователям с правами администратора.
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { EducationIcon, GroupIcon, SubscribedIcon } from '@/components/Icons';
|
import { EducationIcon, GroupIcon, SubscribedIcon } from '@/components/Icons';
|
||||||
import { ICurrentUser, ILibraryItem } from '@/models/library';
|
import { ICurrentUser, ILibraryItem } from '@/models/library';
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
|
@ -10,7 +12,10 @@ interface ItemIconsProps {
|
||||||
function ItemIcons({ user, item }: ItemIconsProps) {
|
function ItemIcons({ user, item }: ItemIconsProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className='inline-flex items-center justify-start gap-1 min-w-[2.75rem]'
|
className={clsx(
|
||||||
|
'min-w-[2.75rem]',
|
||||||
|
'inline-flex gap-1'
|
||||||
|
)}
|
||||||
id={`${prefixes.library_list}${item.id}`}
|
id={`${prefixes.library_list}${item.id}`}
|
||||||
>
|
>
|
||||||
{(user && user.subscriptions.includes(item.id)) ?
|
{(user && user.subscriptions.includes(item.id)) ?
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import clsx from 'clsx';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import ConceptSearch from '@/components/Common/ConceptSearch';
|
import ConceptSearch from '@/components/Common/ConceptSearch';
|
||||||
|
@ -41,18 +42,32 @@ function SearchPanel({ total, filtered, query, setQuery, strategy, setFilter }:
|
||||||
}, [strategy, router]);
|
}, [strategy, router]);
|
||||||
|
|
||||||
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={clsx(
|
||||||
<div className='px-2 py-1 select-none whitespace-nowrap min-w-[10rem]'>
|
'sticky top-0',
|
||||||
|
'w-full max-h-[2.3rem]',
|
||||||
|
'pr-40 flex justify-start items-stretch',
|
||||||
|
'border-b',
|
||||||
|
'clr-input'
|
||||||
|
)}>
|
||||||
|
<div className={clsx(
|
||||||
|
'min-w-[10rem]',
|
||||||
|
'px-2 self-center',
|
||||||
|
'select-none',
|
||||||
|
'whitespace-nowrap',
|
||||||
|
)}>
|
||||||
Фильтр
|
Фильтр
|
||||||
<span className='ml-2'>
|
<span className='ml-2'>
|
||||||
{filtered} из {total}
|
{filtered} из {total}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center justify-center w-full gap-1'>
|
<div className={clsx(
|
||||||
|
'w-full',
|
||||||
|
'flex gap-1 justify-center items-center'
|
||||||
|
)}>
|
||||||
<ConceptSearch noBorder
|
<ConceptSearch noBorder
|
||||||
|
dimensions='min-w-[10rem]'
|
||||||
value={query}
|
value={query}
|
||||||
onChange={handleChangeQuery}
|
onChange={handleChangeQuery}
|
||||||
dimensions='min-w-[10rem] '
|
|
||||||
/>
|
/>
|
||||||
<PickerStrategy
|
<PickerStrategy
|
||||||
value={strategy}
|
value={strategy}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import clsx from 'clsx';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
@ -92,7 +93,12 @@ function ViewLibrary({ items, resetQuery: cleanQuery }: ViewLibraryProps) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className='sticky top-[2.3rem] w-full'>
|
<div className='sticky top-[2.3rem] w-full'>
|
||||||
<div className='absolute top-[0.125rem] left-[0.25rem] flex gap-1 ml-3 z-pop'>
|
<div className={clsx(
|
||||||
|
'z-pop',
|
||||||
|
'absolute top-[0.125rem] left-[0.25rem]',
|
||||||
|
'ml-3',
|
||||||
|
'flex gap-1'
|
||||||
|
)}>
|
||||||
<HelpButton
|
<HelpButton
|
||||||
topic={HelpTopic.LIBRARY}
|
topic={HelpTopic.LIBRARY}
|
||||||
dimensions='max-w-[35rem]'
|
dimensions='max-w-[35rem]'
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import clsx from 'clsx';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import SubmitButton from '@/components/Common/SubmitButton';
|
import SubmitButton from '@/components/Common/SubmitButton';
|
||||||
|
@ -18,7 +19,7 @@ import { resources } from '@/utils/constants';
|
||||||
function ProcessError({error}: { error: ErrorData }): React.ReactElement {
|
function ProcessError({error}: { error: ErrorData }): React.ReactElement {
|
||||||
if (axios.isAxiosError(error) && error.response && error.response.status === 400) {
|
if (axios.isAxiosError(error) && error.response && error.response.status === 400) {
|
||||||
return (
|
return (
|
||||||
<div className='text-sm select-text text-warning'>
|
<div className='text-sm select-text clr-text-warning'>
|
||||||
На Портале отсутствует такое сочетание имени пользователя и пароля
|
На Портале отсутствует такое сочетание имени пользователя и пароля
|
||||||
</div>);
|
</div>);
|
||||||
} else {
|
} else {
|
||||||
|
@ -65,7 +66,10 @@ function LoginPage() {
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
className='pt-12 pb-6 px-6 flex flex-col gap-3 w-[24rem]'
|
className={clsx(
|
||||||
|
'w-[24rem]',
|
||||||
|
'pt-12 pb-6 px-6 flex flex-col gap-3'
|
||||||
|
)}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
>
|
>
|
||||||
<img alt='Концепт Портал'
|
<img alt='Концепт Портал'
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { HelpTopic } from '@/models/miscelanious';
|
import { HelpTopic } from '@/models/miscelanious';
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
import { describeHelpTopic, labelHelpTopic } from '@/utils/labels';
|
import { describeHelpTopic, labelHelpTopic } from '@/utils/labels';
|
||||||
|
@ -9,20 +11,32 @@ interface TopicsListProps {
|
||||||
|
|
||||||
function TopicsList({ activeTopic, onChangeTopic }: TopicsListProps) {
|
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={clsx(
|
||||||
<div className='my-2 text-lg text-center'>Справка</div>
|
'sticky top-0 left-0',
|
||||||
|
'min-w-[13rem] h-fit',
|
||||||
|
'flex flex-col',
|
||||||
|
'border-x',
|
||||||
|
'clr-controls',
|
||||||
|
'small-caps',
|
||||||
|
'select-none'
|
||||||
|
)}>
|
||||||
|
<h1 className='mt-2 mb-1'>Справка</h1>
|
||||||
{Object.values(HelpTopic).map(
|
{Object.values(HelpTopic).map(
|
||||||
(topic, index) => {
|
(topic, index) =>
|
||||||
const isActive = activeTopic === topic;
|
|
||||||
return (
|
|
||||||
<div key={`${prefixes.topic_list}${index}`}
|
<div key={`${prefixes.topic_list}${index}`}
|
||||||
className={`px-3 py-1 border-y cursor-pointer clr-hover ${isActive ? 'clr-selected ' : ''}`}
|
className={clsx(
|
||||||
|
'px-3 py-1',
|
||||||
|
'border-y',
|
||||||
|
'clr-hover',
|
||||||
|
'cursor-pointer',
|
||||||
|
activeTopic === topic && 'clr-selected'
|
||||||
|
)}
|
||||||
title={describeHelpTopic(topic)}
|
title={describeHelpTopic(topic)}
|
||||||
onClick={() => onChangeTopic(topic)}
|
onClick={() => onChangeTopic(topic)}
|
||||||
>
|
>
|
||||||
{labelHelpTopic(topic)}
|
{labelHelpTopic(topic)}
|
||||||
</div>);
|
</div>
|
||||||
})}
|
)}
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,30 +34,30 @@ function ConstituentaToolbar({
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Сохранить изменения [Ctrl + S]'
|
tooltip='Сохранить изменения [Ctrl + S]'
|
||||||
disabled={!canSave}
|
disabled={!canSave}
|
||||||
icon={<SaveIcon size={5} color={canSave ? 'text-primary' : ''}/>}
|
icon={<SaveIcon size={5} color={canSave ? 'clr-text-primary' : ''}/>}
|
||||||
onClick={onSubmit}
|
onClick={onSubmit}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Сбросить несохраненные изменения'
|
tooltip='Сбросить несохраненные изменения'
|
||||||
disabled={!canSave}
|
disabled={!canSave}
|
||||||
onClick={onReset}
|
onClick={onReset}
|
||||||
icon={<ArrowsRotateIcon size={5} color={canSave ? 'text-primary' : ''} />}
|
icon={<ArrowsRotateIcon size={5} color={canSave ? 'clr-text-primary' : ''} />}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Создать конституенту после данной'
|
tooltip='Создать конституенту после данной'
|
||||||
disabled={!isMutable}
|
disabled={!isMutable}
|
||||||
onClick={onCreate}
|
onClick={onCreate}
|
||||||
icon={<SmallPlusIcon size={5} color={isMutable ? 'text-success' : ''} />}
|
icon={<SmallPlusIcon size={5} color={isMutable ? 'clr-text-success' : ''} />}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Клонировать конституенту [Alt + V]'
|
tooltip='Клонировать конституенту [Alt + V]'
|
||||||
disabled={!isMutable}
|
disabled={!isMutable}
|
||||||
onClick={onClone}
|
onClick={onClone}
|
||||||
icon={<CloneIcon size={5} color={isMutable ? 'text-success' : ''} />}
|
icon={<CloneIcon size={5} color={isMutable ? 'clr-text-success' : ''} />}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Создать конституенту из шаблона [Alt + E]'
|
tooltip='Создать конституенту из шаблона [Alt + E]'
|
||||||
icon={<DiamondIcon color={isMutable ? 'text-primary': ''} size={5}/>}
|
icon={<DiamondIcon color={isMutable ? 'clr-text-primary': ''} size={5}/>}
|
||||||
disabled={!isMutable}
|
disabled={!isMutable}
|
||||||
onClick={onTemplates}
|
onClick={onTemplates}
|
||||||
/>
|
/>
|
||||||
|
@ -65,7 +65,7 @@ function ConstituentaToolbar({
|
||||||
tooltip='Удалить редактируемую конституенту'
|
tooltip='Удалить редактируемую конституенту'
|
||||||
disabled={!isMutable}
|
disabled={!isMutable}
|
||||||
onClick={onDelete}
|
onClick={onDelete}
|
||||||
icon={<DumpBinIcon size={5} color={isMutable ? 'text-warning' : ''} />}
|
icon={<DumpBinIcon size={5} color={isMutable ? 'clr-text-warning' : ''} />}
|
||||||
/>
|
/>
|
||||||
<HelpButton topic={HelpTopic.CONSTITUENTA} offset={4} />
|
<HelpButton topic={HelpTopic.CONSTITUENTA} offset={4} />
|
||||||
</Overlay>);
|
</Overlay>);
|
||||||
|
|
|
@ -109,9 +109,9 @@ function FormConstituenta({
|
||||||
disabled={!readyForEdit}
|
disabled={!readyForEdit}
|
||||||
noHover
|
noHover
|
||||||
onClick={onEditTerm}
|
onClick={onEditTerm}
|
||||||
icon={<EditIcon size={4} color={readyForEdit ? 'text-primary' : ''} />}
|
icon={<EditIcon size={4} color={readyForEdit ? 'clr-text-primary' : ''} />}
|
||||||
/>
|
/>
|
||||||
<div className='pt-1 pl-6 text-sm font-semibold w-fit'>
|
<div className='pt-1 pl-[1.375rem] text-sm font-semibold w-fit'>
|
||||||
<span>Имя </span>
|
<span>Имя </span>
|
||||||
<span className='ml-1'>{constituenta?.alias ?? ''}</span>
|
<span className='ml-1'>{constituenta?.alias ?? ''}</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -119,7 +119,7 @@ function FormConstituenta({
|
||||||
tooltip='Переименовать конституенту'
|
tooltip='Переименовать конституенту'
|
||||||
disabled={!readyForEdit}
|
disabled={!readyForEdit}
|
||||||
onClick={handleRename}
|
onClick={handleRename}
|
||||||
icon={<EditIcon size={4} color={readyForEdit ? 'text-primary' : ''} />}
|
icon={<EditIcon size={4} color={readyForEdit ? 'clr-text-primary' : ''} />}
|
||||||
/>
|
/>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
<form id={id}
|
<form id={id}
|
||||||
|
|
|
@ -135,7 +135,7 @@ function EditorRSExpression({
|
||||||
<MiniButton noHover
|
<MiniButton noHover
|
||||||
tooltip='Дерево разбора выражения'
|
tooltip='Дерево разбора выражения'
|
||||||
onClick={handleShowAST}
|
onClick={handleShowAST}
|
||||||
icon={<ASTNetworkIcon size={5} color='text-primary' />}
|
icon={<ASTNetworkIcon size={5} color='clr-text-primary' />}
|
||||||
/>
|
/>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ function ParsingResult({ data, disabled, onShowError }: ParsingResultProps) {
|
||||||
return (
|
return (
|
||||||
<p
|
<p
|
||||||
key={`error-${index}`}
|
key={`error-${index}`}
|
||||||
className={`text-warning ${disabled ? '' : 'cursor-pointer'}`}
|
className={`clr-text-warning ${disabled ? '' : 'cursor-pointer'}`}
|
||||||
onClick={disabled ? undefined : () => onShowError(error)}
|
onClick={disabled ? undefined : () => onShowError(error)}
|
||||||
>
|
>
|
||||||
<span className='mr-1 font-semibold underline'>
|
<span className='mr-1 font-semibold underline'>
|
||||||
|
|
|
@ -27,7 +27,7 @@ function RSAnalyzer({
|
||||||
}: RSAnalyzerProps) {
|
}: RSAnalyzerProps) {
|
||||||
return (
|
return (
|
||||||
<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'>
|
||||||
<Button noOutline
|
<Button noOutline
|
||||||
text='Проверить'
|
text='Проверить'
|
||||||
tooltip='Проверить формальное определение'
|
tooltip='Проверить формальное определение'
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import clsx from 'clsx';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import { useConceptTheme } from '@/context/ThemeContext';
|
import { useConceptTheme } from '@/context/ThemeContext';
|
||||||
|
@ -31,7 +32,12 @@ function StatusBar({ isModified, constituenta, parseData }: StatusBarProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div title={describeExpressionStatus(status)}
|
<div title={describeExpressionStatus(status)}
|
||||||
className='inline-flex items-center justify-center w-full h-full text-sm font-semibold align-middle border rounded-none select-none small-caps'
|
className={clsx(
|
||||||
|
'w-full h-full',
|
||||||
|
'border rounded-none',
|
||||||
|
'text-sm font-semibold small-caps text-center',
|
||||||
|
'select-none'
|
||||||
|
)}
|
||||||
style={{backgroundColor: colorbgCstStatus(status, colors)}}
|
style={{backgroundColor: colorbgCstStatus(status, colors)}}
|
||||||
>
|
>
|
||||||
{labelExpressionStatus(status)}
|
{labelExpressionStatus(status)}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Divider from '@/components/Common/Divider';
|
import Divider from '@/components/Common/Divider';
|
||||||
import LabeledText from '@/components/Common/LabeledText';
|
import LabeledValue from '@/components/Common/LabeledValue';
|
||||||
import { type IRSFormStats } from '@/models/rsform';
|
import { type IRSFormStats } from '@/models/rsform';
|
||||||
|
|
||||||
interface RSFormStatsProps {
|
interface RSFormStatsProps {
|
||||||
|
@ -12,36 +12,36 @@ function RSFormStats({ stats }: RSFormStatsProps) {
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col gap-1 px-4 mt-8 min-w-[16rem]'>
|
<div className='flex flex-col gap-1 px-4 mt-8 min-w-[16rem]'>
|
||||||
<LabeledText id='count_all'
|
<LabeledValue id='count_all'
|
||||||
label='Всего конституент '
|
label='Всего конституент '
|
||||||
text={stats.count_all}
|
text={stats.count_all}
|
||||||
/>
|
/>
|
||||||
<LabeledText id='count_errors'
|
<LabeledValue 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'
|
<LabeledValue id='count_property'
|
||||||
label='Неразмерных'
|
label='Неразмерных'
|
||||||
text={stats.count_property}
|
text={stats.count_property}
|
||||||
/> : null}
|
/> : null}
|
||||||
{stats.count_incalc !== 0 ?
|
{stats.count_incalc !== 0 ?
|
||||||
<LabeledText id='count_incalc'
|
<LabeledValue id='count_incalc'
|
||||||
label='Невычислимых'
|
label='Невычислимых'
|
||||||
text={stats.count_incalc}
|
text={stats.count_incalc}
|
||||||
/> : null}
|
/> : null}
|
||||||
|
|
||||||
<Divider margins='my-2' />
|
<Divider margins='my-2' />
|
||||||
|
|
||||||
<LabeledText id='count_termin'
|
<LabeledValue id='count_termin'
|
||||||
label='Термины'
|
label='Термины'
|
||||||
text={stats.count_termin}
|
text={stats.count_termin}
|
||||||
/>
|
/>
|
||||||
<LabeledText id='count_definition'
|
<LabeledValue id='count_definition'
|
||||||
label='Определения'
|
label='Определения'
|
||||||
text={stats.count_definition}
|
text={stats.count_definition}
|
||||||
/>
|
/>
|
||||||
<LabeledText id='count_convention'
|
<LabeledValue id='count_convention'
|
||||||
label='Конвенции'
|
label='Конвенции'
|
||||||
text={stats.count_convention}
|
text={stats.count_convention}
|
||||||
/>
|
/>
|
||||||
|
@ -49,42 +49,42 @@ function RSFormStats({ stats }: RSFormStatsProps) {
|
||||||
<Divider margins='my-2' />
|
<Divider margins='my-2' />
|
||||||
|
|
||||||
{stats.count_base !== 0 ?
|
{stats.count_base !== 0 ?
|
||||||
<LabeledText id='count_base'
|
<LabeledValue id='count_base'
|
||||||
label='Базисные множества '
|
label='Базисные множества '
|
||||||
text={stats.count_base}
|
text={stats.count_base}
|
||||||
/> : null}
|
/> : null}
|
||||||
{ stats.count_constant !== 0 ?
|
{ stats.count_constant !== 0 ?
|
||||||
<LabeledText id='count_constant'
|
<LabeledValue id='count_constant'
|
||||||
label='Константные множества '
|
label='Константные множества '
|
||||||
text={stats.count_constant}
|
text={stats.count_constant}
|
||||||
/> : null}
|
/> : null}
|
||||||
{stats.count_structured !== 0 ?
|
{stats.count_structured !== 0 ?
|
||||||
<LabeledText id='count_structured'
|
<LabeledValue id='count_structured'
|
||||||
label='Родовые структуры '
|
label='Родовые структуры '
|
||||||
text={stats.count_structured}
|
text={stats.count_structured}
|
||||||
/> : null}
|
/> : null}
|
||||||
{stats.count_axiom !== 0 ?
|
{stats.count_axiom !== 0 ?
|
||||||
<LabeledText id='count_axiom'
|
<LabeledValue id='count_axiom'
|
||||||
label='Аксиомы '
|
label='Аксиомы '
|
||||||
text={stats.count_axiom}
|
text={stats.count_axiom}
|
||||||
/> : null}
|
/> : null}
|
||||||
{stats.count_term !== 0 ?
|
{stats.count_term !== 0 ?
|
||||||
<LabeledText id='count_term'
|
<LabeledValue id='count_term'
|
||||||
label='Термы '
|
label='Термы '
|
||||||
text={stats.count_term}
|
text={stats.count_term}
|
||||||
/> : null}
|
/> : null}
|
||||||
{stats.count_function !== 0 ?
|
{stats.count_function !== 0 ?
|
||||||
<LabeledText id='count_function'
|
<LabeledValue id='count_function'
|
||||||
label='Терм-функции '
|
label='Терм-функции '
|
||||||
text={stats.count_function}
|
text={stats.count_function}
|
||||||
/> : null}
|
/> : null}
|
||||||
{stats.count_predicate !== 0 ?
|
{stats.count_predicate !== 0 ?
|
||||||
<LabeledText id='count_predicate'
|
<LabeledValue id='count_predicate'
|
||||||
label='Предикат-функции '
|
label='Предикат-функции '
|
||||||
text={stats.count_predicate}
|
text={stats.count_predicate}
|
||||||
/> : null}
|
/> : null}
|
||||||
{stats.count_theorem !== 0 ?
|
{stats.count_theorem !== 0 ?
|
||||||
<LabeledText id='count_theorem'
|
<LabeledValue id='count_theorem'
|
||||||
label='Теоремы '
|
label='Теоремы '
|
||||||
text={stats.count_theorem}
|
text={stats.count_theorem}
|
||||||
/> : null}
|
/> : null}
|
||||||
|
|
|
@ -32,22 +32,22 @@ function RSFormToolbar({
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Сохранить изменения [Ctrl + S]'
|
tooltip='Сохранить изменения [Ctrl + S]'
|
||||||
disabled={!canSave}
|
disabled={!canSave}
|
||||||
icon={<SaveIcon size={5} color={canSave ? 'text-primary' : ''}/>}
|
icon={<SaveIcon size={5} color={canSave ? 'clr-text-primary' : ''}/>}
|
||||||
onClick={onSubmit}
|
onClick={onSubmit}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Поделиться схемой'
|
tooltip='Поделиться схемой'
|
||||||
icon={<ShareIcon size={5} color='text-primary'/>}
|
icon={<ShareIcon size={5} color='clr-text-primary'/>}
|
||||||
onClick={onShare}
|
onClick={onShare}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Скачать TRS файл'
|
tooltip='Скачать TRS файл'
|
||||||
icon={<DownloadIcon size={5} color='text-primary'/>}
|
icon={<DownloadIcon size={5} color='clr-text-primary'/>}
|
||||||
onClick={onDownload}
|
onClick={onDownload}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip={claimable ? 'Стать владельцем' : 'Невозможно стать владельцем' }
|
tooltip={claimable ? 'Стать владельцем' : 'Невозможно стать владельцем' }
|
||||||
icon={<OwnerIcon size={5} color={!claimable ? '' : 'text-success'}/>}
|
icon={<OwnerIcon size={5} color={!claimable ? '' : 'clr-text-success'}/>}
|
||||||
disabled={!claimable || anonymous}
|
disabled={!claimable || anonymous}
|
||||||
onClick={onClaim}
|
onClick={onClaim}
|
||||||
/>
|
/>
|
||||||
|
@ -55,7 +55,7 @@ function RSFormToolbar({
|
||||||
tooltip='Удалить схему'
|
tooltip='Удалить схему'
|
||||||
disabled={!isMutable}
|
disabled={!isMutable}
|
||||||
onClick={onDestroy}
|
onClick={onDestroy}
|
||||||
icon={<DumpBinIcon size={5} color={isMutable ? 'text-warning' : ''} />}
|
icon={<DumpBinIcon size={5} color={isMutable ? 'clr-text-warning' : ''} />}
|
||||||
/>
|
/>
|
||||||
<HelpButton topic={HelpTopic.RSFORM} offset={4} />
|
<HelpButton topic={HelpTopic.RSFORM} offset={4} />
|
||||||
</Overlay>);
|
</Overlay>);
|
||||||
|
|
|
@ -52,13 +52,13 @@ function RSListToolbar({
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Клонировать конституенту [Alt + V]'
|
tooltip='Клонировать конституенту [Alt + V]'
|
||||||
icon={<CloneIcon color={isMutable && selectedCount === 1 ? 'text-success': ''} size={5}/>}
|
icon={<CloneIcon color={isMutable && selectedCount === 1 ? 'clr-text-success': ''} size={5}/>}
|
||||||
disabled={!isMutable || selectedCount !== 1}
|
disabled={!isMutable || selectedCount !== 1}
|
||||||
onClick={onClone}
|
onClick={onClone}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Добавить новую конституенту... [Alt + `]'
|
tooltip='Добавить новую конституенту... [Alt + `]'
|
||||||
icon={<SmallPlusIcon color={isMutable ? 'text-success': ''} size={5}/>}
|
icon={<SmallPlusIcon color={isMutable ? 'clr-text-success': ''} size={5}/>}
|
||||||
disabled={!isMutable}
|
disabled={!isMutable}
|
||||||
onClick={() => onCreate()}
|
onClick={() => onCreate()}
|
||||||
/>
|
/>
|
||||||
|
@ -66,7 +66,7 @@ function RSListToolbar({
|
||||||
<div>
|
<div>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Добавить пустую конституенту'
|
tooltip='Добавить пустую конституенту'
|
||||||
icon={<ArrowDropdownIcon color={isMutable ? 'text-success': ''} size={5}/>}
|
icon={<ArrowDropdownIcon color={isMutable ? 'clr-text-success': ''} size={5}/>}
|
||||||
disabled={!isMutable}
|
disabled={!isMutable}
|
||||||
onClick={insertMenu.toggle}
|
onClick={insertMenu.toggle}
|
||||||
/>
|
/>
|
||||||
|
@ -89,19 +89,19 @@ function RSListToolbar({
|
||||||
</div>
|
</div>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Создать конституенту из шаблона [Alt + E]'
|
tooltip='Создать конституенту из шаблона [Alt + E]'
|
||||||
icon={<DiamondIcon color={isMutable ? 'text-primary': ''} size={5}/>}
|
icon={<DiamondIcon color={isMutable ? 'clr-text-primary': ''} size={5}/>}
|
||||||
disabled={!isMutable}
|
disabled={!isMutable}
|
||||||
onClick={onTemplates}
|
onClick={onTemplates}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Сброс имен: присвоить порядковые имена [Alt + R]'
|
tooltip='Сброс имен: присвоить порядковые имена [Alt + R]'
|
||||||
icon={<UpdateIcon color={isMutable ? 'text-primary': ''} size={5}/>}
|
icon={<UpdateIcon color={isMutable ? 'clr-text-primary': ''} size={5}/>}
|
||||||
disabled={!isMutable}
|
disabled={!isMutable}
|
||||||
onClick={onReindex}
|
onClick={onReindex}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Удалить выбранные [Delete]'
|
tooltip='Удалить выбранные [Delete]'
|
||||||
icon={<DumpBinIcon color={isMutable && !nothingSelected ? 'text-warning' : ''} size={5}/>}
|
icon={<DumpBinIcon color={isMutable && !nothingSelected ? 'clr-text-warning' : ''} size={5}/>}
|
||||||
disabled={!isMutable || nothingSelected}
|
disabled={!isMutable || nothingSelected}
|
||||||
onClick={onDelete}
|
onClick={onDelete}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -146,7 +146,7 @@ function RSTable({
|
||||||
<span className='flex flex-col justify-center p-2 text-center'>
|
<span className='flex flex-col justify-center p-2 text-center'>
|
||||||
<p>Список пуст</p>
|
<p>Список пуст</p>
|
||||||
<p
|
<p
|
||||||
className='cursor-pointer text-primary hover:underline'
|
className='cursor-pointer clr-text-primary hover:underline'
|
||||||
onClick={() => onCreateNew()}
|
onClick={() => onCreateNew()}
|
||||||
>
|
>
|
||||||
Создать новую конституенту
|
Создать новую конституенту
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import clsx from 'clsx';
|
||||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
import { GraphEdge, GraphNode, LayoutTypes } from 'reagraph';
|
import { GraphEdge, GraphNode, LayoutTypes } from 'reagraph';
|
||||||
|
|
||||||
|
@ -230,16 +231,28 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
||||||
|
|
||||||
{hoverCst ?
|
{hoverCst ?
|
||||||
<Overlay
|
<Overlay
|
||||||
position='top-[1.6rem] left-[2.6rem] px-3 w-[25rem] h-fit min-h-[11rem] overflow-y-auto'
|
|
||||||
layer='z-tooltip'
|
layer='z-tooltip'
|
||||||
className='border shadow-md clr-app'
|
position='top-[1.6rem] left-[2.6rem]'
|
||||||
|
className={clsx(
|
||||||
|
'w-[25rem] h-fit min-h-[11rem]',
|
||||||
|
'px-3',
|
||||||
|
'overflow-y-auto',
|
||||||
|
'border shadow-md',
|
||||||
|
'clr-app'
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<InfoConstituenta
|
<InfoConstituenta
|
||||||
data={hoverCst}
|
data={hoverCst}
|
||||||
/>
|
/>
|
||||||
</Overlay> : null}
|
</Overlay> : null}
|
||||||
|
|
||||||
<Overlay position='top-0 left-0 max-w-[13.5rem] min-w-[13.5rem]' className='flex flex-col gap-3'>
|
<Overlay
|
||||||
|
position='top-0 left-0'
|
||||||
|
className={clsx(
|
||||||
|
'max-w-[13.5rem] min-w-[13.5rem]',
|
||||||
|
'flex flex-col gap-3'
|
||||||
|
)}
|
||||||
|
>
|
||||||
<GraphSidebar
|
<GraphSidebar
|
||||||
coloring={coloringScheme}
|
coloring={coloringScheme}
|
||||||
layout={layout}
|
layout={layout}
|
||||||
|
|
|
@ -34,37 +34,37 @@ function GraphToolbar({
|
||||||
<Overlay position='w-full top-1 right-0 flex items-start justify-center'>
|
<Overlay position='w-full top-1 right-0 flex items-start justify-center'>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Настройки фильтрации узлов и связей'
|
tooltip='Настройки фильтрации узлов и связей'
|
||||||
icon={<FilterIcon color='text-primary' size={5} />}
|
icon={<FilterIcon color='clr-text-primary' size={5} />}
|
||||||
onClick={showParamsDialog}
|
onClick={showParamsDialog}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip={!noText ? 'Скрыть текст' : 'Отобразить текст'}
|
tooltip={!noText ? 'Скрыть текст' : 'Отобразить текст'}
|
||||||
icon={
|
icon={
|
||||||
!noText
|
!noText
|
||||||
? <LetterALinesIcon color='text-success' size={5} />
|
? <LetterALinesIcon color='clr-text-success' size={5} />
|
||||||
: <LetterAIcon color='text-primary' size={5} />
|
: <LetterAIcon color='clr-text-primary' size={5} />
|
||||||
}
|
}
|
||||||
onClick={toggleNoText}
|
onClick={toggleNoText}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Новая конституента'
|
tooltip='Новая конституента'
|
||||||
icon={<SmallPlusIcon color={isMutable ? 'text-success' : ''} size={5} />}
|
icon={<SmallPlusIcon color={isMutable ? 'clr-text-success' : ''} size={5} />}
|
||||||
disabled={!isMutable}
|
disabled={!isMutable}
|
||||||
onClick={onCreate}
|
onClick={onCreate}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Удалить выбранные'
|
tooltip='Удалить выбранные'
|
||||||
icon={<DumpBinIcon color={isMutable && !nothingSelected ? 'text-warning' : ''} size={5} />}
|
icon={<DumpBinIcon color={isMutable && !nothingSelected ? 'clr-text-warning' : ''} size={5} />}
|
||||||
disabled={!isMutable || nothingSelected}
|
disabled={!isMutable || nothingSelected}
|
||||||
onClick={onDelete}
|
onClick={onDelete}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
icon={<ArrowsFocusIcon color='text-primary' size={5} />}
|
icon={<ArrowsFocusIcon color='clr-text-primary' size={5} />}
|
||||||
tooltip='Восстановить камеру'
|
tooltip='Восстановить камеру'
|
||||||
onClick={onResetViewpoint}
|
onClick={onResetViewpoint}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
icon={<PlanetIcon color={!is3D ? '' : orbit ? 'text-success' : 'text-primary'} size={5} />}
|
icon={<PlanetIcon color={!is3D ? '' : orbit ? 'clr-text-success' : 'clr-text-primary'} size={5} />}
|
||||||
tooltip='Анимация вращения'
|
tooltip='Анимация вращения'
|
||||||
disabled={!is3D}
|
disabled={!is3D}
|
||||||
onClick={toggleOrbit}
|
onClick={toggleOrbit}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import clsx from 'clsx';
|
||||||
import fileDownload from 'js-file-download';
|
import fileDownload from 'js-file-download';
|
||||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
||||||
|
@ -377,7 +378,11 @@ function RSTabs() {
|
||||||
className='flex flex-col w-full'
|
className='flex flex-col w-full'
|
||||||
>
|
>
|
||||||
<div className='flex justify-center w-[100vw]'>
|
<div className='flex justify-center w-[100vw]'>
|
||||||
<TabList className='flex border-b-2 border-x-2 justify-stretch w-fit h-[1.9rem]'>
|
<TabList className={clsx(
|
||||||
|
'w-fit h-[1.9rem]',
|
||||||
|
'flex justify-stretch',
|
||||||
|
'border-b-2 border-x-2 divide-x-2'
|
||||||
|
)}>
|
||||||
<RSTabsMenu
|
<RSTabsMenu
|
||||||
onDownload={onDownloadSchema}
|
onDownload={onDownloadSchema}
|
||||||
onDestroy={onDestroySchema}
|
onDestroy={onDestroySchema}
|
||||||
|
@ -389,25 +394,28 @@ function RSTabs() {
|
||||||
/>
|
/>
|
||||||
<ConceptTab
|
<ConceptTab
|
||||||
label='Карточка'
|
label='Карточка'
|
||||||
className='border-x-2'
|
|
||||||
tooltip={`Название схемы: ${schema.title ?? ''}`}
|
tooltip={`Название схемы: ${schema.title ?? ''}`}
|
||||||
/>
|
/>
|
||||||
<ConceptTab
|
<ConceptTab
|
||||||
label='Содержание'
|
label='Содержание'
|
||||||
className='border-r-2'
|
tooltip={[
|
||||||
tooltip={`Всего конституент: ${schema.stats?.count_all ?? 0}\nКоличество ошибок: ${schema.stats?.count_errors ?? 0}`}
|
`Всего конституент: ${schema.stats?.count_all ?? 0}`,
|
||||||
/>
|
`Количество ошибок: ${schema.stats?.count_errors ?? 0}`
|
||||||
<ConceptTab
|
].join('\n')}
|
||||||
label='Редактор'
|
|
||||||
className='border-r-2'
|
|
||||||
/>
|
|
||||||
<ConceptTab
|
|
||||||
label='Граф термов'
|
|
||||||
/>
|
/>
|
||||||
|
<ConceptTab label='Редактор' />
|
||||||
|
<ConceptTab label='Граф термов' />
|
||||||
</TabList>
|
</TabList>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='overflow-y-auto min-w-[48rem] w-[100vw] flex justify-center' style={{ maxHeight: panelHeight}}>
|
<div
|
||||||
|
className={clsx(
|
||||||
|
'min-w-[48rem] w-[100vw]',
|
||||||
|
'flex justify-center',
|
||||||
|
'overflow-y-auto'
|
||||||
|
)}
|
||||||
|
style={{ maxHeight: panelHeight}}
|
||||||
|
>
|
||||||
<TabPanel forceRender style={{ display: activeTab === RSTabID.CARD ? '': 'none' }}>
|
<TabPanel forceRender style={{ display: activeTab === RSTabID.CARD ? '': 'none' }}>
|
||||||
<EditorRSForm
|
<EditorRSForm
|
||||||
isModified={isModified}
|
isModified={isModified}
|
||||||
|
|
|
@ -76,7 +76,7 @@ function RSTabsMenu({
|
||||||
<div ref={schemaMenu.ref}>
|
<div ref={schemaMenu.ref}>
|
||||||
<Button noBorder dense tabIndex={-1}
|
<Button noBorder dense tabIndex={-1}
|
||||||
tooltip='Действия'
|
tooltip='Действия'
|
||||||
icon={<MenuIcon color='text-controls' size={5}/>}
|
icon={<MenuIcon color='clr-text-controls' size={5}/>}
|
||||||
dimensions='h-full w-fit pl-2'
|
dimensions='h-full w-fit pl-2'
|
||||||
style={{outlineColor: 'transparent'}}
|
style={{outlineColor: 'transparent'}}
|
||||||
onClick={schemaMenu.toggle}
|
onClick={schemaMenu.toggle}
|
||||||
|
@ -85,37 +85,37 @@ function RSTabsMenu({
|
||||||
<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'>
|
||||||
<ShareIcon color='text-primary' size={4}/>
|
<ShareIcon color='clr-text-primary' size={4}/>
|
||||||
<p>Поделиться</p>
|
<p>Поделиться</p>
|
||||||
</div>
|
</div>
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
<DropdownButton onClick={handleClone} disabled={!user} >
|
<DropdownButton onClick={handleClone} disabled={!user} >
|
||||||
<div className='inline-flex items-center justify-start gap-2'>
|
<div className='inline-flex items-center justify-start gap-2'>
|
||||||
<CloneIcon color='text-primary' size={4}/>
|
<CloneIcon color='clr-text-primary' size={4}/>
|
||||||
<p>Клонировать</p>
|
<p>Клонировать</p>
|
||||||
</div>
|
</div>
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
<DropdownButton onClick={handleDownload}>
|
<DropdownButton onClick={handleDownload}>
|
||||||
<div className='inline-flex items-center justify-start gap-2'>
|
<div className='inline-flex items-center justify-start gap-2'>
|
||||||
<DownloadIcon color='text-primary' size={4}/>
|
<DownloadIcon color='clr-text-primary' size={4}/>
|
||||||
<p>Выгрузить в Экстеор</p>
|
<p>Выгрузить в Экстеор</p>
|
||||||
</div>
|
</div>
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
<DropdownButton disabled={!isMutable} onClick={handleUpload}>
|
<DropdownButton disabled={!isMutable} onClick={handleUpload}>
|
||||||
<div className='inline-flex items-center justify-start gap-2'>
|
<div className='inline-flex items-center justify-start gap-2'>
|
||||||
<UploadIcon color={isMutable ? 'text-warning' : ''} size={4}/>
|
<UploadIcon color={isMutable ? 'clr-text-warning' : ''} size={4}/>
|
||||||
<p>Загрузить из Экстеора</p>
|
<p>Загрузить из Экстеора</p>
|
||||||
</div>
|
</div>
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
<DropdownButton disabled={!isMutable} onClick={handleDelete}>
|
<DropdownButton disabled={!isMutable} onClick={handleDelete}>
|
||||||
<span className='inline-flex items-center justify-start gap-2'>
|
<span className='inline-flex items-center justify-start gap-2'>
|
||||||
<DumpBinIcon color={isMutable ? 'text-warning' : ''} size={4} />
|
<DumpBinIcon color={isMutable ? 'clr-text-warning' : ''} size={4} />
|
||||||
<p>Удалить схему</p>
|
<p>Удалить схему</p>
|
||||||
</span>
|
</span>
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
<DropdownButton onClick={handleCreateNew}>
|
<DropdownButton onClick={handleCreateNew}>
|
||||||
<span className='inline-flex items-center justify-start gap-2'>
|
<span className='inline-flex items-center justify-start gap-2'>
|
||||||
<SmallPlusIcon color='text-url' size={4} />
|
<SmallPlusIcon color='clr-text-url' size={4} />
|
||||||
<p>Создать новую схему</p>
|
<p>Создать новую схему</p>
|
||||||
</span>
|
</span>
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
|
@ -126,7 +126,7 @@ function RSTabsMenu({
|
||||||
tooltip={'измнение: ' + (isMutable ? '[доступно]' : '[запрещено]')}
|
tooltip={'измнение: ' + (isMutable ? '[доступно]' : '[запрещено]')}
|
||||||
dimensions='h-full w-fit'
|
dimensions='h-full w-fit'
|
||||||
style={{outlineColor: 'transparent'}}
|
style={{outlineColor: 'transparent'}}
|
||||||
icon={<EditIcon size={5} color={isMutable ? 'text-success' : 'text-warning'}/>}
|
icon={<EditIcon size={5} color={isMutable ? 'clr-text-success' : 'clr-text-warning'}/>}
|
||||||
onClick={editMenu.toggle}
|
onClick={editMenu.toggle}
|
||||||
/>
|
/>
|
||||||
{editMenu.isActive ?
|
{editMenu.isActive ?
|
||||||
|
@ -136,12 +136,12 @@ function RSTabsMenu({
|
||||||
onClick={!isOwned ? handleClaimOwner : undefined}
|
onClick={!isOwned ? handleClaimOwner : undefined}
|
||||||
tooltip={!user || !isClaimable ? 'Взять во владение можно общую изменяемую схему' : ''}
|
tooltip={!user || !isClaimable ? 'Взять во владение можно общую изменяемую схему' : ''}
|
||||||
>
|
>
|
||||||
<div className='flex items-center gap-2 pl-1 text-default'>
|
<div className='flex items-center gap-2 clr-text-default'>
|
||||||
<span>
|
<span>
|
||||||
<OwnerIcon size={4} color={isOwned ? 'text-success' : 'text-controls'} />
|
<OwnerIcon size={4} color={isOwned ? 'clr-text-success' : 'clr-text-controls'} />
|
||||||
</span>
|
</span>
|
||||||
<div>
|
<div>
|
||||||
{isOwned ? <b className='text-default'>Вы — владелец</b> : null}
|
{isOwned ? <b className='clr-text-default'>Вы — владелец</b> : null}
|
||||||
{!isOwned ? <b>Стать владельцем</b> : null}
|
{!isOwned ? <b>Стать владельцем</b> : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -167,8 +167,8 @@ function RSTabsMenu({
|
||||||
tooltip={'отслеживание: ' + (isTracking ? '[включено]' : '[выключено]')}
|
tooltip={'отслеживание: ' + (isTracking ? '[включено]' : '[выключено]')}
|
||||||
disabled={processing}
|
disabled={processing}
|
||||||
icon={isTracking
|
icon={isTracking
|
||||||
? <SubscribedIcon color='text-primary' size={5}/>
|
? <SubscribedIcon color='clr-text-primary' size={5}/>
|
||||||
: <NotSubscribedIcon color='text-controls' size={5}/>
|
: <NotSubscribedIcon color='clr-text-controls' size={5}/>
|
||||||
}
|
}
|
||||||
dimensions='h-full w-fit pr-2'
|
dimensions='h-full w-fit pr-2'
|
||||||
style={{outlineColor: 'transparent'}}
|
style={{outlineColor: 'transparent'}}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import useDropdown from '@/hooks/useDropdown';
|
||||||
import useLocalStorage from '@/hooks/useLocalStorage';
|
import useLocalStorage from '@/hooks/useLocalStorage';
|
||||||
import { CstMatchMode, DependencyMode } from '@/models/miscelanious';
|
import { CstMatchMode, DependencyMode } from '@/models/miscelanious';
|
||||||
import { applyGraphFilter } from '@/models/miscelaniousAPI';
|
import { applyGraphFilter } from '@/models/miscelaniousAPI';
|
||||||
import { CstType, IConstituenta, IRSForm } from '@/models/rsform';
|
import { IConstituenta, IRSForm } from '@/models/rsform';
|
||||||
import { createMockConstituenta, matchConstituenta } from '@/models/rsformAPI';
|
import { createMockConstituenta, matchConstituenta } from '@/models/rsformAPI';
|
||||||
import { extractGlobals } from '@/models/rslangAPI';
|
import { extractGlobals } from '@/models/rslangAPI';
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
|
@ -42,18 +42,12 @@ function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }:
|
||||||
if (filterSource === DependencyMode.EXPRESSION) {
|
if (filterSource === DependencyMode.EXPRESSION) {
|
||||||
const aliases = extractGlobals(activeExpression);
|
const aliases = extractGlobals(activeExpression);
|
||||||
result = schema.items.filter((cst) => aliases.has(cst.alias));
|
result = schema.items.filter((cst) => aliases.has(cst.alias));
|
||||||
const names = result.map(cst => cst.alias)
|
const names = result.map(cst => cst.alias);
|
||||||
const diff = Array.from(aliases).filter(name => !names.includes(name));
|
const diff = Array.from(aliases).filter(name => !names.includes(name));
|
||||||
if (diff.length > 0) {
|
if (diff.length > 0) {
|
||||||
diff.forEach(
|
diff.forEach(
|
||||||
(alias, index) => result.push(
|
(alias, index) => result.push(
|
||||||
createMockConstituenta(
|
createMockConstituenta(-index, alias, 'Конституента отсутствует')
|
||||||
-1,
|
|
||||||
-index,
|
|
||||||
alias,
|
|
||||||
CstType.BASE,
|
|
||||||
'Конституента отсутствует'
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -83,9 +77,9 @@ function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }:
|
||||||
return (
|
return (
|
||||||
<div className='flex items-stretch border-b clr-input'>
|
<div className='flex items-stretch border-b clr-input'>
|
||||||
<ConceptSearch noBorder
|
<ConceptSearch noBorder
|
||||||
|
dimensions='min-w-[6rem] pr-2 w-full'
|
||||||
value={filterText}
|
value={filterText}
|
||||||
onChange={setFilterText}
|
onChange={setFilterText}
|
||||||
dimensions='min-w-[6rem] pr-2 w-full'
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div ref={matchModeMenu.ref}>
|
<div ref={matchModeMenu.ref}>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import clsx from 'clsx';
|
||||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import DataTable, { createColumnHelper,IConditionalStyle, VisibilityState } from '@/components/DataTable';
|
import DataTable, { createColumnHelper,IConditionalStyle, VisibilityState } from '@/components/DataTable';
|
||||||
|
@ -67,7 +68,7 @@ function ConstituentsTable({
|
||||||
<ConstituentaBadge
|
<ConstituentaBadge
|
||||||
theme={colors}
|
theme={colors}
|
||||||
value={props.row.original}
|
value={props.row.original}
|
||||||
prefixID={prefixes.cst_list}
|
prefixID={prefixes.cst_sidetable}
|
||||||
/>
|
/>
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor(cst => describeConstituenta(cst), {
|
columnHelper.accessor(cst => describeConstituenta(cst), {
|
||||||
|
@ -123,10 +124,15 @@ function ConstituentsTable({
|
||||||
onColumnVisibilityChange={setColumnVisibility}
|
onColumnVisibilityChange={setColumnVisibility}
|
||||||
|
|
||||||
noDataComponent={
|
noDataComponent={
|
||||||
<span className='flex flex-col justify-center p-2 text-center min-h-[5rem] select-none'>
|
<div className={clsx(
|
||||||
|
'min-h-[5rem]',
|
||||||
|
'p-2 flex flex-col justify-center',
|
||||||
|
'text-center',
|
||||||
|
'select-none'
|
||||||
|
)}>
|
||||||
<p>Список конституент пуст</p>
|
<p>Список конституент пуст</p>
|
||||||
<p>Измените параметры фильтра</p>
|
<p>Измените параметры фильтра</p>
|
||||||
</span>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
onRowDoubleClicked={handleDoubleClick}
|
onRowDoubleClicked={handleDoubleClick}
|
||||||
|
|
|
@ -77,7 +77,7 @@ function RegisterPage() {
|
||||||
id={globalIDs.password_tooltip}
|
id={globalIDs.password_tooltip}
|
||||||
position='top-[4.8rem] left-[3.4rem] absolute'
|
position='top-[4.8rem] left-[3.4rem] absolute'
|
||||||
>
|
>
|
||||||
<HelpIcon color='text-primary' size={5} />
|
<HelpIcon color='clr-text-primary' size={5} />
|
||||||
</Overlay>
|
</Overlay>
|
||||||
<ConceptTooltip
|
<ConceptTooltip
|
||||||
anchorSelect={`#${globalIDs.password_tooltip}`}
|
anchorSelect={`#${globalIDs.password_tooltip}`}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import clsx from 'clsx';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
|
@ -14,7 +15,7 @@ import { IUserUpdatePassword } from '@/models/library';
|
||||||
function ProcessError({error}: {error: ErrorData}): React.ReactElement {
|
function ProcessError({error}: {error: ErrorData}): React.ReactElement {
|
||||||
if (axios.isAxiosError(error) && error.response && error.response.status === 400) {
|
if (axios.isAxiosError(error) && error.response && error.response.status === 400) {
|
||||||
return (
|
return (
|
||||||
<div className='text-sm select-text text-warning'>
|
<div className='text-sm select-text clr-text-warning'>
|
||||||
Неверно введен старый пароль
|
Неверно введен старый пароль
|
||||||
</div>);
|
</div>);
|
||||||
} else {
|
} else {
|
||||||
|
@ -70,7 +71,11 @@ function EditorPassword() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
className='flex flex-col justify-between px-6 border-l-2 max-w-[14rem] py-2'
|
className={clsx(
|
||||||
|
'max-w-[14rem]',
|
||||||
|
'px-6 py-2 flex flex-col justify-between',
|
||||||
|
'border-l-2'
|
||||||
|
)}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
>
|
>
|
||||||
<div className='flex flex-col gap-3'>
|
<div className='flex flex-col gap-3'>
|
||||||
|
|
|
@ -51,7 +51,10 @@ 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' disabled
|
<TextInput id='username' disabled
|
||||||
label='Логин'
|
label='Логин'
|
||||||
|
|
|
@ -38,8 +38,8 @@ function UserTabs() {
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Показать/Скрыть список отслеживаний'
|
tooltip='Показать/Скрыть список отслеживаний'
|
||||||
icon={showSubs
|
icon={showSubs
|
||||||
? <SubscribedIcon color='text-primary' size={5}/>
|
? <SubscribedIcon color='clr-text-primary' size={5}/>
|
||||||
: <NotSubscribedIcon color='text-primary' size={5}/>
|
: <NotSubscribedIcon color='clr-text-primary' size={5}/>
|
||||||
}
|
}
|
||||||
onClick={() => setShowSubs(prev => !prev)}
|
onClick={() => setShowSubs(prev => !prev)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -43,13 +43,16 @@ function ViewSubscriptions({items}: ViewSubscriptionsProps) {
|
||||||
minSize: 150,
|
minSize: 150,
|
||||||
size: 150,
|
size: 150,
|
||||||
maxSize: 150,
|
maxSize: 150,
|
||||||
cell: props => <div className='min-w-[8.25rem]'>{new Date(props.cell.getValue()).toLocaleString(intl.locale)}</div>,
|
cell: props =>
|
||||||
|
<div className='min-w-[8.25rem]'>
|
||||||
|
{new Date(props.cell.getValue()).toLocaleString(intl.locale)}
|
||||||
|
</div>,
|
||||||
enableSorting: true
|
enableSorting: true
|
||||||
})
|
})
|
||||||
], [intl]);
|
], [intl]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='max-h-[23.8rem] overflow-auto text-sm border w-fit'>
|
<div className='max-h-[23.8rem] w-fit overflow-auto text-sm border'>
|
||||||
<DataTable dense noFooter
|
<DataTable dense noFooter
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={items}
|
data={items}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import { syntaxTree } from '@codemirror/language';
|
import { syntaxTree } from '@codemirror/language';
|
||||||
import { NodeType, Tree, TreeCursor } from '@lezer/common';
|
import { NodeType, Tree, TreeCursor } from '@lezer/common';
|
||||||
import { ReactCodeMirrorRef, SelectionRange } from '@uiw/react-codemirror';
|
import { ReactCodeMirrorRef, SelectionRange } from '@uiw/react-codemirror';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { IEntityReference, ISyntacticReference } from '@/models/language';
|
import { IEntityReference, ISyntacticReference } from '@/models/language';
|
||||||
import { parseGrammemes } from '@/models/languageAPI';
|
import { parseGrammemes } from '@/models/languageAPI';
|
||||||
|
@ -134,7 +135,14 @@ export function findContainedNodes(start: number, finish: number, tree: Tree, fi
|
||||||
*/
|
*/
|
||||||
export function domTooltipConstituenta(cst: IConstituenta) {
|
export function domTooltipConstituenta(cst: IConstituenta) {
|
||||||
const dom = document.createElement('div');
|
const dom = document.createElement('div');
|
||||||
dom.className = 'overflow-y-auto border shadow-md max-h-[25rem] max-w-[25rem] min-w-[10rem] w-fit z-tooltip text-sm px-2 py-2';
|
dom.className = clsx(
|
||||||
|
'z-tooltip',
|
||||||
|
'max-h-[25rem] max-w-[25rem] min-w-[10rem] w-fit',
|
||||||
|
'p-2',
|
||||||
|
'border shadow-md',
|
||||||
|
'overflow-y-auto',
|
||||||
|
'text-sm'
|
||||||
|
);
|
||||||
|
|
||||||
const alias = document.createElement('p');
|
const alias = document.createElement('p');
|
||||||
alias.innerHTML = `<b>${cst.alias}:</b> ${labelCstTypification(cst)}`;
|
alias.innerHTML = `<b>${cst.alias}:</b> ${labelCstTypification(cst)}`;
|
||||||
|
@ -170,11 +178,16 @@ export function domTooltipConstituenta(cst: IConstituenta) {
|
||||||
* Create DOM tooltip for {@link IEntityReference}.
|
* Create DOM tooltip for {@link IEntityReference}.
|
||||||
*/
|
*/
|
||||||
export function domTooltipEntityReference(ref: IEntityReference, cst: IConstituenta | undefined, colors: IColorTheme) {
|
export function domTooltipEntityReference(ref: IEntityReference, cst: IConstituenta | undefined, colors: IColorTheme) {
|
||||||
const DIMENSIONS = 'max-h-[25rem] max-w-[25rem] min-w-[10rem] w-fit z-tooltip px-2 py-2';
|
|
||||||
const LAYOUT = 'flex flex-col overflow-y-auto';
|
|
||||||
|
|
||||||
const dom = document.createElement('div');
|
const dom = document.createElement('div');
|
||||||
dom.className = `${DIMENSIONS} ${LAYOUT} border shadow-md text-sm select-none cursor-auto`;
|
dom.className = clsx(
|
||||||
|
'z-tooltip',
|
||||||
|
'max-h-[25rem] max-w-[25rem] min-w-[10rem] w-fit',
|
||||||
|
'p-2 flex flex-col',
|
||||||
|
'border shadow-md',
|
||||||
|
'overflow-y-auto',
|
||||||
|
'text-sm',
|
||||||
|
'select-none cursor-auto'
|
||||||
|
);
|
||||||
|
|
||||||
const title = document.createElement('p');
|
const title = document.createElement('p');
|
||||||
title.innerHTML = '<b>Ссылка на конституенту</b>';
|
title.innerHTML = '<b>Ссылка на конституенту</b>';
|
||||||
|
@ -190,7 +203,12 @@ export function domTooltipEntityReference(ref: IEntityReference, cst: IConstitue
|
||||||
gramStr => {
|
gramStr => {
|
||||||
const gram = document.createElement('div');
|
const gram = document.createElement('div');
|
||||||
gram.id =`tooltip-${gramStr}`;
|
gram.id =`tooltip-${gramStr}`;
|
||||||
gram.className='min-w-[3rem] px-1 text-sm text-center rounded-md whitespace-nowrap';
|
gram.className = clsx(
|
||||||
|
'min-w-[3rem]',
|
||||||
|
'px-1',
|
||||||
|
'border rounded-md',
|
||||||
|
'text-sm text-center whitespace-nowrap'
|
||||||
|
);
|
||||||
gram.style.borderWidth = '1px';
|
gram.style.borderWidth = '1px';
|
||||||
gram.style.borderColor = colorfgGrammeme(gramStr, colors);
|
gram.style.borderColor = colorfgGrammeme(gramStr, colors);
|
||||||
gram.style.color = colorfgGrammeme(gramStr, colors);
|
gram.style.color = colorfgGrammeme(gramStr, colors);
|
||||||
|
@ -207,11 +225,17 @@ export function domTooltipEntityReference(ref: IEntityReference, cst: IConstitue
|
||||||
* Create DOM tooltip for {@link ISyntacticReference}.
|
* Create DOM tooltip for {@link ISyntacticReference}.
|
||||||
*/
|
*/
|
||||||
export function domTooltipSyntacticReference(ref: ISyntacticReference, masterRef: string | undefined) {
|
export function domTooltipSyntacticReference(ref: ISyntacticReference, masterRef: string | undefined) {
|
||||||
const DIMENSIONS = 'max-h-[25rem] max-w-[25rem] min-w-[10rem] w-fit z-tooltip px-2 py-2';
|
|
||||||
const LAYOUT = 'flex flex-col overflow-y-auto'
|
|
||||||
|
|
||||||
const dom = document.createElement('div');
|
const dom = document.createElement('div');
|
||||||
dom.className = `${DIMENSIONS} ${LAYOUT} border shadow-md text-sm select-none cursor-auto`;
|
dom.className = clsx(
|
||||||
|
'z-tooltip',
|
||||||
|
'max-h-[25rem] max-w-[25rem] min-w-[10rem] w-fit',
|
||||||
|
'p-2 flex flex-col',
|
||||||
|
'border shadow-md',
|
||||||
|
'overflow-y-auto',
|
||||||
|
'text-sm',
|
||||||
|
'select-none cursor-auto'
|
||||||
|
);
|
||||||
|
|
||||||
const title = document.createElement('p');
|
const title = document.createElement('p');
|
||||||
title.innerHTML = '<b>Связывание слов</b>';
|
title.innerHTML = '<b>Связывание слов</b>';
|
||||||
|
|
|
@ -87,6 +87,7 @@ export const globalIDs = {
|
||||||
export const prefixes = {
|
export const prefixes = {
|
||||||
page_size: 'page-size-',
|
page_size: 'page-size-',
|
||||||
cst_list: 'cst-list-',
|
cst_list: 'cst-list-',
|
||||||
|
cst_sidetable: 'cst-sidetable-',
|
||||||
cst_hidden_list: 'cst-hidden-list-',
|
cst_hidden_list: 'cst-hidden-list-',
|
||||||
cst_modal_list: 'cst-modal-list-',
|
cst_modal_list: 'cst-modal-list-',
|
||||||
cst_template_ist: 'cst-template-list-',
|
cst_template_ist: 'cst-template-list-',
|
||||||
|
|
Loading…
Reference in New Issue
Block a user