mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Rework UI toolbars and icons
This commit is contained in:
parent
7282063738
commit
ada335ee21
|
@ -13,6 +13,7 @@ This readme file is used mostly to document project dependencies
|
||||||
<pre>
|
<pre>
|
||||||
- axios
|
- axios
|
||||||
- clsx
|
- clsx
|
||||||
|
- react-icons
|
||||||
- react-router-dom
|
- react-router-dom
|
||||||
- react-toastify
|
- react-toastify
|
||||||
- react-loader-spinner
|
- react-loader-spinner
|
||||||
|
|
9
rsconcept/frontend/package-lock.json
generated
9
rsconcept/frontend/package-lock.json
generated
|
@ -18,6 +18,7 @@
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-error-boundary": "^4.0.11",
|
"react-error-boundary": "^4.0.11",
|
||||||
|
"react-icons": "^4.12.0",
|
||||||
"react-intl": "^6.5.5",
|
"react-intl": "^6.5.5",
|
||||||
"react-loader-spinner": "^5.4.5",
|
"react-loader-spinner": "^5.4.5",
|
||||||
"react-pdf": "^7.6.0",
|
"react-pdf": "^7.6.0",
|
||||||
|
@ -8389,6 +8390,14 @@
|
||||||
"react": ">=16.13.1"
|
"react": ">=16.13.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-icons": {
|
||||||
|
"version": "4.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.12.0.tgz",
|
||||||
|
"integrity": "sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-intl": {
|
"node_modules/react-intl": {
|
||||||
"version": "6.5.5",
|
"version": "6.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.5.5.tgz",
|
"resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.5.5.tgz",
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-error-boundary": "^4.0.11",
|
"react-error-boundary": "^4.0.11",
|
||||||
|
"react-icons": "^4.12.0",
|
||||||
"react-intl": "^6.5.5",
|
"react-intl": "^6.5.5",
|
||||||
"react-loader-spinner": "^5.4.5",
|
"react-loader-spinner": "^5.4.5",
|
||||||
"react-pdf": "^7.6.0",
|
"react-pdf": "^7.6.0",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { MagnifyingGlassIcon } from '../Icons';
|
import { BiSearchAlt2 } from 'react-icons/bi';
|
||||||
|
|
||||||
import Overlay from './Overlay';
|
import Overlay from './Overlay';
|
||||||
import TextInput from './TextInput';
|
import TextInput from './TextInput';
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ function ConceptSearch({ value, onChange, noBorder, dimensions }: ConceptSearchP
|
||||||
position='top-[-0.125rem] left-3 translate-y-1/2'
|
position='top-[-0.125rem] left-3 translate-y-1/2'
|
||||||
className='pointer-events-none clr-text-controls'
|
className='pointer-events-none clr-text-controls'
|
||||||
>
|
>
|
||||||
<MagnifyingGlassIcon size={5} />
|
<BiSearchAlt2 size='1.25rem' />
|
||||||
</Overlay>
|
</Overlay>
|
||||||
<TextInput noOutline
|
<TextInput noOutline
|
||||||
placeholder='Поиск'
|
placeholder='Поиск'
|
||||||
|
|
|
@ -25,14 +25,15 @@ function ConceptTooltip({
|
||||||
}
|
}
|
||||||
return createPortal(
|
return createPortal(
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
delayShow={500}
|
||||||
opacity={0.97}
|
opacity={0.97}
|
||||||
style={{...{ paddingTop: '2px', paddingBottom: '2px'}, ...style}}
|
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'overflow-auto',
|
'overflow-auto',
|
||||||
'border shadow-md',
|
'border shadow-md',
|
||||||
layer,
|
layer,
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
|
style={{...{ paddingTop: '2px', paddingBottom: '2px'}, ...style}}
|
||||||
variant={(darkMode ? 'dark' : 'light')}
|
variant={(darkMode ? 'dark' : 'light')}
|
||||||
place={place}
|
place={place}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
|
|
|
@ -1,30 +1,43 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
interface DropdownButtonProps {
|
interface DropdownButtonProps {
|
||||||
|
text?: string
|
||||||
|
icon?: React.ReactNode
|
||||||
|
|
||||||
|
className?: string
|
||||||
tooltip?: string | undefined
|
tooltip?: string | undefined
|
||||||
onClick?: () => void
|
onClick?: () => void
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
children: React.ReactNode
|
|
||||||
|
children?: React.ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
function DropdownButton({ tooltip, onClick, disabled, children }: DropdownButtonProps) {
|
function DropdownButton({
|
||||||
|
text, icon, children,
|
||||||
|
tooltip, className,
|
||||||
|
disabled,
|
||||||
|
onClick
|
||||||
|
}: DropdownButtonProps) {
|
||||||
return (
|
return (
|
||||||
<button type='button'
|
<button type='button'
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'px-3 py-1',
|
'px-3 py-1 inline-flex items-center gap-2',
|
||||||
'text-left overflow-ellipsis whitespace-nowrap',
|
'text-left text-sm overflow-ellipsis whitespace-nowrap',
|
||||||
'disabled:clr-text-controls',
|
'disabled:clr-text-controls',
|
||||||
{
|
{
|
||||||
'clr-hover': onClick,
|
'clr-hover': onClick,
|
||||||
'cursor-pointer disabled:cursor-not-allowed': onClick,
|
'cursor-pointer disabled:cursor-not-allowed': onClick,
|
||||||
'cursor-default': !onClick
|
'cursor-default': !onClick
|
||||||
}
|
},
|
||||||
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{children}
|
{children ? children : null}
|
||||||
|
{!children && icon ? icon : null}
|
||||||
|
{!children && text ? <span>{text}</span> : null}
|
||||||
</button>);
|
</button>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
|
import { BiUpload } from 'react-icons/bi';
|
||||||
|
|
||||||
import { UploadIcon } from '../Icons';
|
|
||||||
import Button from './Button';
|
import Button from './Button';
|
||||||
import Label from './Label';
|
import Label from './Label';
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ function FileInput({
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
text={label}
|
text={label}
|
||||||
icon={<UploadIcon/>}
|
icon={<BiUpload size='1.5rem' />}
|
||||||
onClick={handleUploadClick}
|
onClick={handleUploadClick}
|
||||||
tooltip={tooltip}
|
tooltip={tooltip}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
|
import { BiX } from 'react-icons/bi';
|
||||||
|
|
||||||
import useEscapeKey from '@/hooks/useEscapeKey';
|
import useEscapeKey from '@/hooks/useEscapeKey';
|
||||||
|
|
||||||
import { CrossIcon } from '../Icons';
|
|
||||||
import Button from './Button';
|
import Button from './Button';
|
||||||
import MiniButton from './MiniButton';
|
import MiniButton from './MiniButton';
|
||||||
import Overlay from './Overlay';
|
import Overlay from './Overlay';
|
||||||
|
@ -62,7 +62,7 @@ function Modal({
|
||||||
<Overlay position='right-[0.3rem] top-2' className='text-disabled'>
|
<Overlay position='right-[0.3rem] top-2' className='text-disabled'>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Закрыть диалоговое окно [ESC]'
|
tooltip='Закрыть диалоговое окно [ESC]'
|
||||||
icon={<CrossIcon size={5}/>}
|
icon={<BiX size='1.25rem'/>}
|
||||||
onClick={handleCancel}
|
onClick={handleCancel}
|
||||||
/>
|
/>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { GotoFirstIcon, GotoLastIcon, GotoNextIcon, GotoPrevIcon } from '@/components/Icons';
|
import { BiChevronLeft, BiChevronRight, BiFirstPage, BiLastPage } from 'react-icons/bi';
|
||||||
|
|
||||||
interface PageControlsProps {
|
interface PageControlsProps {
|
||||||
pageNumber: number
|
pageNumber: number
|
||||||
|
@ -16,14 +16,14 @@ function PageControls({
|
||||||
onClick={() => setPageNumber(1)}
|
onClick={() => setPageNumber(1)}
|
||||||
disabled={pageNumber < 2}
|
disabled={pageNumber < 2}
|
||||||
>
|
>
|
||||||
<GotoFirstIcon />
|
<BiFirstPage size='1.5rem' />
|
||||||
</button>
|
</button>
|
||||||
<button type='button'
|
<button type='button'
|
||||||
className='clr-hover clr-text-controls'
|
className='clr-hover clr-text-controls'
|
||||||
onClick={() => setPageNumber(prev => prev - 1)}
|
onClick={() => setPageNumber(prev => prev - 1)}
|
||||||
disabled={pageNumber < 2}
|
disabled={pageNumber < 2}
|
||||||
>
|
>
|
||||||
<GotoPrevIcon />
|
<BiChevronLeft size='1.5rem' />
|
||||||
</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'
|
||||||
|
@ -31,14 +31,14 @@ function PageControls({
|
||||||
onClick={() => setPageNumber(prev => prev + 1)}
|
onClick={() => setPageNumber(prev => prev + 1)}
|
||||||
disabled={pageNumber >= pageCount}
|
disabled={pageNumber >= pageCount}
|
||||||
>
|
>
|
||||||
<GotoNextIcon />
|
<BiChevronRight size='1.5rem' />
|
||||||
</button>
|
</button>
|
||||||
<button type='button'
|
<button type='button'
|
||||||
className='clr-hover clr-text-controls'
|
className='clr-hover clr-text-controls'
|
||||||
onClick={() => setPageNumber(pageCount)}
|
onClick={() => setPageNumber(pageCount)}
|
||||||
disabled={pageNumber >= pageCount}
|
disabled={pageNumber >= pageCount}
|
||||||
>
|
>
|
||||||
<GotoLastIcon />
|
<BiLastPage size='1.5rem' />
|
||||||
</button>
|
</button>
|
||||||
</>);
|
</>);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,10 @@
|
||||||
import { Table } from '@tanstack/react-table';
|
import { Table } from '@tanstack/react-table';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
import { BiChevronLeft, BiChevronRight, BiFirstPage, BiLastPage } from 'react-icons/bi';
|
||||||
|
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
|
|
||||||
import { GotoFirstIcon, GotoLastIcon, GotoNextIcon, GotoPrevIcon } from '../Icons';
|
|
||||||
|
|
||||||
interface PaginationToolsProps<TData> {
|
interface PaginationToolsProps<TData> {
|
||||||
table: Table<TData>
|
table: Table<TData>
|
||||||
paginationOptions: number[]
|
paginationOptions: number[]
|
||||||
|
@ -47,14 +46,14 @@ function PaginationTools<TData>({ table, paginationOptions, onChangePaginationOp
|
||||||
onClick={() => table.setPageIndex(0)}
|
onClick={() => table.setPageIndex(0)}
|
||||||
disabled={!table.getCanPreviousPage()}
|
disabled={!table.getCanPreviousPage()}
|
||||||
>
|
>
|
||||||
<GotoFirstIcon />
|
<BiFirstPage size='1.5rem' />
|
||||||
</button>
|
</button>
|
||||||
<button type='button'
|
<button type='button'
|
||||||
className='clr-hover clr-text-controls'
|
className='clr-hover clr-text-controls'
|
||||||
onClick={() => table.previousPage()}
|
onClick={() => table.previousPage()}
|
||||||
disabled={!table.getCanPreviousPage()}
|
disabled={!table.getCanPreviousPage()}
|
||||||
>
|
>
|
||||||
<GotoPrevIcon />
|
<BiChevronLeft size='1.5rem' />
|
||||||
</button>
|
</button>
|
||||||
<input
|
<input
|
||||||
title='Номер страницы. Выделите для ручного ввода'
|
title='Номер страницы. Выделите для ручного ввода'
|
||||||
|
@ -72,14 +71,14 @@ function PaginationTools<TData>({ table, paginationOptions, onChangePaginationOp
|
||||||
onClick={() => table.nextPage()}
|
onClick={() => table.nextPage()}
|
||||||
disabled={!table.getCanNextPage()}
|
disabled={!table.getCanNextPage()}
|
||||||
>
|
>
|
||||||
<GotoNextIcon />
|
<BiChevronRight size='1.5rem' />
|
||||||
</button>
|
</button>
|
||||||
<button type='button'
|
<button type='button'
|
||||||
className='clr-hover clr-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()}
|
||||||
>
|
>
|
||||||
<GotoLastIcon />
|
<BiLastPage size='1.5rem' />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<select
|
<select
|
||||||
|
|
|
@ -9,10 +9,10 @@ interface SortingIconProps<TData> {
|
||||||
function SortingIcon<TData>({ column }: SortingIconProps<TData>) {
|
function SortingIcon<TData>({ column }: SortingIconProps<TData>) {
|
||||||
return (<>
|
return (<>
|
||||||
{{
|
{{
|
||||||
desc: <DescendingIcon size={4} />,
|
desc: <DescendingIcon size='1rem' />,
|
||||||
asc: <AscendingIcon size={4}/>,
|
asc: <AscendingIcon size='1rem'/>,
|
||||||
}[column.getIsSorted() as string] ??
|
}[column.getIsSorted() as string] ??
|
||||||
<DescendingIcon size={4} color='opacity-0 hover:opacity-50' />
|
<DescendingIcon size='1rem' className='opacity-0 hover:opacity-50' />
|
||||||
}
|
}
|
||||||
</>);
|
</>);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ interface ConstituentaTooltipProps {
|
||||||
|
|
||||||
function ConstituentaTooltip({ data, anchor }: ConstituentaTooltipProps) {
|
function ConstituentaTooltip({ data, anchor }: ConstituentaTooltipProps) {
|
||||||
return (
|
return (
|
||||||
<ConceptTooltip
|
<ConceptTooltip clickable
|
||||||
anchorSelect={anchor}
|
anchorSelect={anchor}
|
||||||
className='max-w-[25rem] min-w-[25rem]'
|
className='max-w-[25rem] min-w-[25rem]'
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
import { BiInfoCircle } from 'react-icons/bi';
|
||||||
|
|
||||||
import ConceptTooltip from '@/components/Common/ConceptTooltip';
|
import ConceptTooltip from '@/components/Common/ConceptTooltip';
|
||||||
import TextURL from '@/components/Common/TextURL';
|
import TextURL from '@/components/Common/TextURL';
|
||||||
import { HelpIcon } from '@/components/Icons';
|
|
||||||
import { HelpTopic } from '@/models/miscelanious';
|
import { HelpTopic } from '@/models/miscelanious';
|
||||||
|
|
||||||
import InfoTopic from './InfoTopic';
|
import InfoTopic from './InfoTopic';
|
||||||
|
@ -18,7 +19,7 @@ function HelpButton({ topic, offset, dimensions }: HelpButtonProps) {
|
||||||
id={`help-${topic}`}
|
id={`help-${topic}`}
|
||||||
className='p-1'
|
className='p-1'
|
||||||
>
|
>
|
||||||
<HelpIcon color='clr-text-primary' size={5} />
|
<BiInfoCircle size='1.25rem' className='clr-text-primary' />
|
||||||
</div>
|
</div>
|
||||||
<ConceptTooltip clickable
|
<ConceptTooltip clickable
|
||||||
anchorSelect={`#help-${topic}`}
|
anchorSelect={`#help-${topic}`}
|
||||||
|
|
|
@ -9,15 +9,15 @@ function HelpLibrary() {
|
||||||
<p>На текущем этапе происходит наполнение Библиотеки концептуальными схемами.</p>
|
<p>На текущем этапе происходит наполнение Библиотеки концептуальными схемами.</p>
|
||||||
<p>Поиск осуществлеяется с помощью инструментов в верхней части страницы.</p>
|
<p>Поиск осуществлеяется с помощью инструментов в верхней части страницы.</p>
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
<SubscribedIcon size={4}/>
|
<SubscribedIcon size='1rem'/>
|
||||||
<p>Аттрибут <b>отслеживаемая</b> обозначает отслеживание схемы.</p>
|
<p>Аттрибут <b>отслеживаемая</b> обозначает отслеживание схемы.</p>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
<GroupIcon size={4}/>
|
<GroupIcon size='1rem'/>
|
||||||
<p>Аттрибут <b>общедоступная</b> делает схему видимой в разделе библиотека.</p>
|
<p>Аттрибут <b>общедоступная</b> делает схему видимой в разделе библиотека.</p>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
<EducationIcon size={4}/>
|
<EducationIcon size='1rem'/>
|
||||||
<p>Аттрибут <b>неизменная</b> выделяет стандартные схемы.</p>
|
<p>Аттрибут <b>неизменная</b> выделяет стандартные схемы.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>);
|
</div>);
|
||||||
|
|
|
@ -2,24 +2,23 @@
|
||||||
|
|
||||||
interface IconSVGProps {
|
interface IconSVGProps {
|
||||||
viewbox: string
|
viewbox: string
|
||||||
size?: number
|
size?: string
|
||||||
color?: string
|
className?: string
|
||||||
props?: React.SVGProps<SVGSVGElement>
|
props?: React.SVGProps<SVGSVGElement>
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IconProps {
|
export interface IconProps {
|
||||||
size?: number
|
size?: string
|
||||||
color?: string
|
className?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
function IconSVG({ viewbox, size = 6, color, props, children }: IconSVGProps) {
|
function IconSVG({ viewbox, size = '1.5rem', className, props, children }: IconSVGProps) {
|
||||||
const width = `${size * 1 / 4}rem`;
|
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
width={width}
|
width={size}
|
||||||
height={width}
|
height={size}
|
||||||
className={`w-[${width}] h-[${width}] ${color}`}
|
className={`w-[${size}] h-[${size}] ${className}`}
|
||||||
fill='currentColor'
|
fill='currentColor'
|
||||||
viewBox={viewbox}
|
viewBox={viewbox}
|
||||||
{...props}
|
{...props}
|
||||||
|
@ -28,39 +27,6 @@ function IconSVG({ viewbox, size = 6, color, props, children }: IconSVGProps) {
|
||||||
</svg>);
|
</svg>);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MagnifyingGlassIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 20 20' {...props} >
|
|
||||||
<path d='M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z'/>
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function BellIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 20 20' {...props}>
|
|
||||||
<path d='M10 2a6 6 0 00-6 6v3.586l-.707.707A1 1 0 004 14h12a1 1 0 00.707-1.707L16 11.586V8a6 6 0 00-6-6zM10 18a3 3 0 01-3-3h6a3 3 0 01-3 3z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function EyeIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M14 12c-1.095 0-2-.905-2-2 0-.354.103-.683.268-.973C12.178 9.02 12.092 9 12 9a3.02 3.02 0 00-3 3c0 1.642 1.358 3 3 3 1.641 0 3-1.358 3-3 0-.092-.02-.178-.027-.268-.29.165-.619.268-.973.268z' />
|
|
||||||
<path d='M12 5c-7.633 0-9.927 6.617-9.948 6.684L1.946 12l.105.316C2.073 12.383 4.367 19 12 19s9.927-6.617 9.948-6.684l.106-.316-.105-.316C21.927 11.617 19.633 5 12 5zm0 12c-5.351 0-7.424-3.846-7.926-5C4.578 10.842 6.652 7 12 7c5.351 0 7.424 3.846 7.926 5-.504 1.158-2.578 5-7.926 5z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function EyeOffIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M12 19c.946 0 1.81-.103 2.598-.281l-1.757-1.757c-.273.021-.55.038-.841.038-5.351 0-7.424-3.846-7.926-5a8.642 8.642 0 011.508-2.297L4.184 8.305c-1.538 1.667-2.121 3.346-2.132 3.379a.994.994 0 000 .633C2.073 12.383 4.367 19 12 19zm0-14c-1.837 0-3.346.396-4.604.981L3.707 2.293 2.293 3.707l18 18 1.414-1.414-3.319-3.319c2.614-1.951 3.547-4.615 3.561-4.657a.994.994 0 000-.633C21.927 11.617 19.633 5 12 5zm4.972 10.558l-2.28-2.28c.19-.39.308-.819.308-1.278 0-1.641-1.359-3-3-3-.459 0-.888.118-1.277.309L8.915 7.501A9.26 9.26 0 0112 7c5.351 0 7.424 3.846 7.926 5-.302.692-1.166 2.342-2.954 3.558z'/>
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SubscribedIcon(props: IconProps) {
|
export function SubscribedIcon(props: IconProps) {
|
||||||
return (
|
return (
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
<IconSVG viewbox='0 0 24 24' {...props}>
|
||||||
|
@ -85,22 +51,6 @@ export function ASTNetworkIcon(props: IconProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function EditIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M6.3 12.3l10-10a1 1 0 011.4 0l4 4a1 1 0 010 1.4l-10 10a1 1 0 01-.7.3H7a1 1 0 01-1-1v-4a1 1 0 01.3-.7zM8 16h2.59l9-9L17 4.41l-9 9V16zm10-2a1 1 0 012 0v6a2 2 0 01-2 2H4a2 2 0 01-2-2V6c0-1.1.9-2 2-2h6a1 1 0 010 2H4v14h14v-6z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SquaresIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 20 20' {...props}>
|
|
||||||
<path d='M5 3a2 2 0 00-2 2v2a2 2 0 002 2h2a2 2 0 002-2V5a2 2 0 00-2-2H5zM5 11a2 2 0 00-2 2v2a2 2 0 002 2h2a2 2 0 002-2v-2a2 2 0 00-2-2H5zM11 5a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V5zM11 13a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function GroupIcon(props: IconProps) {
|
export function GroupIcon(props: IconProps) {
|
||||||
return (
|
return (
|
||||||
<IconSVG viewbox='0 0 20 20' {...props}>
|
<IconSVG viewbox='0 0 20 20' {...props}>
|
||||||
|
@ -109,30 +59,6 @@ export function GroupIcon(props: IconProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FrameIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 20 20' {...props}>
|
|
||||||
<path d='M5 3a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2V5a2 2 0 00-2-2H5zm0 2h10v7h-2l-1 2H8l-1-2H5V5z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function AsteriskIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 20 20' {...props}>
|
|
||||||
<path d='M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function MenuIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M4 6h16v2H4zm0 5h16v2H4zm0 5h16v2H4z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ShareIcon(props: IconProps) {
|
export function ShareIcon(props: IconProps) {
|
||||||
return (
|
return (
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
<IconSVG viewbox='0 0 24 24' {...props}>
|
||||||
|
@ -141,14 +67,6 @@ export function ShareIcon(props: IconProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FilterIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M21 3H5a1 1 0 00-1 1v2.59c0 .523.213 1.037.583 1.407L10 13.414V21a1.001 1.001 0 001.447.895l4-2c.339-.17.553-.516.553-.895v-5.586l5.417-5.417c.37-.37.583-.884.583-1.407V4a1 1 0 00-1-1zm-6.707 9.293A.996.996 0 0014 13v5.382l-2 1V13a.996.996 0 00-.293-.707L6 6.59V5h14.001l.002 1.583-5.71 5.71z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SortIcon(props: IconProps) {
|
export function SortIcon(props: IconProps) {
|
||||||
return (
|
return (
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
<IconSVG viewbox='0 0 24 24' {...props}>
|
||||||
|
@ -157,14 +75,6 @@ export function SortIcon(props: IconProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function BookmarkIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 20 20' {...props}>
|
|
||||||
<path d='M5 2a2 2 0 00-2 2v14l3.5-2 3.5 2 3.5-2 3.5 2V4a2 2 0 00-2-2H5zm2.5 3a1.5 1.5 0 100 3 1.5 1.5 0 000-3zm6.207.293a1 1 0 00-1.414 0l-6 6a1 1 0 101.414 1.414l6-6a1 1 0 000-1.414zM12.5 10a1.5 1.5 0 100 3 1.5 1.5 0 000-3z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function UserIcon(props: IconProps) {
|
export function UserIcon(props: IconProps) {
|
||||||
return (
|
return (
|
||||||
<IconSVG viewbox='0 0 512 512' {...props}>
|
<IconSVG viewbox='0 0 512 512' {...props}>
|
||||||
|
@ -181,22 +91,6 @@ export function EducationIcon(props: IconProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DarkThemeIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 20 20' {...props}>
|
|
||||||
<path d='M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function LightThemeIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 20 20' {...props}>
|
|
||||||
<path d='M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function LibraryIcon(props: IconProps) {
|
export function LibraryIcon(props: IconProps) {
|
||||||
return (
|
return (
|
||||||
<IconSVG viewbox='0 0 512 512' {...props}>
|
<IconSVG viewbox='0 0 512 512' {...props}>
|
||||||
|
@ -215,66 +109,6 @@ export function PlusIcon(props: IconProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SmallPlusIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10S2 17.523 2 12zm10-8a8 8 0 100 16 8 8 0 000-16z'/>
|
|
||||||
<path d='M13 7a1 1 0 10-2 0v4H7a1 1 0 100 2h4v4a1 1 0 102 0v-4h4a1 1 0 100-2h-4V7z'/>
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ArrowDropdownIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M12 1.993C6.486 1.994 2 6.48 2 11.994c0 5.513 4.486 9.999 10 10 5.514 0 10-4.486 10-10s-4.485-10-10-10.001zm0 18.001c-4.411-.001-8-3.59-8-8 0-4.411 3.589-8 8-8.001 4.411.001 8 3.59 8 8.001s-3.589 8-8 8z' />
|
|
||||||
<path d='M13 8h-2v4H7.991l4.005 4.005L16 12h-3z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function UploadIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M11 15h2V9h3l-4-5-4 5h3z'/>
|
|
||||||
<path d='M20 18H4v-7H2v7c0 1.103.897 2 2 2h16c1.103 0 2-.897 2-2v-7h-2v7z'/>
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function DownloadIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M12 16l4-5h-3V4h-2v7H8z'/>
|
|
||||||
<path d='M20 18H4v-7H2v7c0 1.103.897 2 2 2h16c1.103 0 2-.897 2-2v-7h-2v7z'/>
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function OwnerIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M20.787 9.023c-.125.027-1.803.418-3.953 1.774-.323-1.567-1.279-4.501-4.108-7.485L12 2.546l-.726.767C8.435 6.308 7.483 9.25 7.163 10.827 5.005 9.448 3.34 9.052 3.218 9.024L2 8.752V10c0 7.29 3.925 12 10 12 5.981 0 10-4.822 10-12V8.758l-1.213.265zM8.999 12.038c.002-.033.152-3.1 3.001-6.532C14.814 8.906 14.999 12 15 12v.125a18.933 18.933 0 00-3.01 3.154 19.877 19.877 0 00-2.991-3.113v-.128zM12 20c-5.316 0-7.549-4.196-7.937-8.564 1.655.718 4.616 2.426 7.107 6.123l.841 1.249.825-1.26c2.426-3.708 5.425-5.411 7.096-6.122C19.534 15.654 17.304 20 12 20z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ArrowUpIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M12.781 2.375c-.381-.475-1.181-.475-1.562 0l-8 10A1.001 1.001 0 004 14h4v7a1 1 0 001 1h6a1 1 0 001-1v-7h4a1.001 1.001 0 00.781-1.625l-8-10zM15 12h-1v8h-4v-8H6.081L12 4.601 17.919 12H15z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ArrowDownIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M20.901 10.566A1.001 1.001 0 0020 10h-4V3a1 1 0 00-1-1H9a1 1 0 00-1 1v7H4a1.001 1.001 0 00-.781 1.625l8 10a1 1 0 001.562 0l8-10c.24-.301.286-.712.12-1.059zM12 19.399L6.081 12H10V4h4v8h3.919L12 19.399z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ArrowLeftIcon(props: IconProps) {
|
export function ArrowLeftIcon(props: IconProps) {
|
||||||
return (
|
return (
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
<IconSVG viewbox='0 0 24 24' {...props}>
|
||||||
|
@ -291,49 +125,6 @@ export function ArrowRightIcon(props: IconProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CloneIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M11 10H9v3H6v2h3v3h2v-3h3v-2h-3z' />
|
|
||||||
<path d='M4 22h12c1.103 0 2-.897 2-2V8c0-1.103-.897-2-2-2H4c-1.103 0-2 .897-2 2v12c0 1.103.897 2 2 2zM4 8h12l.002 12H4V8z' />
|
|
||||||
<path d='M20 2H8v2h12v12h2V4c0-1.103-.897-2-2-2z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function DiamondIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M17.813 3.838A2 2 0 0016.187 3H7.813c-.644 0-1.252.313-1.667.899l-4 6.581a.999.999 0 00.111 1.188l9 10a.995.995 0 001.486.001l9-10a.997.997 0 00.111-1.188l-4.041-6.643zM12 19.505L5.245 12h13.509L12 19.505zM4.777 10l3.036-5 8.332-.062L19.222 10H4.777z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function DumpBinIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M5 20a2 2 0 002 2h10a2 2 0 002-2V8h2V6h-4V4a2 2 0 00-2-2H9a2 2 0 00-2 2v2H3v2h2zM9 4h6v2H9zM8 8h9v12H7V8z' />
|
|
||||||
<path d='M9 10h2v8H9zm4 0h2v8h-2z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ArrowsRotateIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M12 6v3l4-4-4-4v3a8 8 0 00-8 8c0 1.57.46 3.03 1.24 4.26L6.7 14.8A5.9 5.9 0 016 12a6 6 0 016-6m6.76 1.74L17.3 9.2c.44.84.7 1.8.7 2.8a6 6 0 01-6 6v-3l-4 4 4 4v-3a8 8 0 008-8c0-1.57-.46-3.03-1.24-4.26z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ArrowsFocusIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M16.121 6.465L14 4.344V10h5.656l-2.121-2.121 3.172-3.172-1.414-1.414zM4.707 3.293L3.293 4.707l3.172 3.172L4.344 10H10V4.344L7.879 6.465zM19.656 14H14v5.656l2.121-2.121 3.172 3.172 1.414-1.414-3.172-3.172zM6.465 16.121l-3.172 3.172 1.414 1.414 3.172-3.172L10 19.656V14H4.344z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function LetterAIcon(props: IconProps) {
|
export function LetterAIcon(props: IconProps) {
|
||||||
return (
|
return (
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
<IconSVG viewbox='0 0 24 24' {...props}>
|
||||||
|
@ -350,48 +141,6 @@ export function LetterALinesIcon(props: IconProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PlanetIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M2.76 20.2a2.73 2.73 0 002.15.85 8.86 8.86 0 003.37-.86 9 9 0 0012.27-10.9c1.31-2.23 1.75-4.26.67-5.48a2.94 2.94 0 00-2.57-1A5 5 0 0016.1 4 9 9 0 003.58 15.14c-1.06 1.21-2.05 3.68-.82 5.06zm1.5-1.32c-.22-.25 0-1.07.37-1.76a9.26 9.26 0 001.57 1.74c-1.03.3-1.71.28-1.94.02zm14.51-5.17A7 7 0 0115.58 18 7.12 7.12 0 0112 19a6.44 6.44 0 01-1.24-.13 30.73 30.73 0 004.42-3.29 31.5 31.5 0 003.8-4 6.88 6.88 0 01-.21 2.13zm.09-8.89a.94.94 0 01.87.32c.23.26.16.94-.26 1.93a9.2 9.2 0 00-1.61-1.86 2.48 2.48 0 011-.39zM5.22 10.31A6.94 6.94 0 018.41 6 7 7 0 0112 5a6.9 6.9 0 016 3.41 5.19 5.19 0 01.35.66 27.43 27.43 0 01-4.49 5A27.35 27.35 0 018.35 18a7 7 0 01-3.13-7.65z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SaveIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M5 21h14a2 2 0 002-2V8a1 1 0 00-.29-.71l-4-4A1 1 0 0016 3H5a2 2 0 00-2 2v14a2 2 0 002 2zm10-2H9v-5h6zM13 7h-2V5h2zM5 5h2v4h8V5h.59L19 8.41V19h-2v-5a2 2 0 00-2-2H9a2 2 0 00-2 2v5H5z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function HelpIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M12 2C6.486 2 2 6.486 2 12s4.486 10 10 10 10-4.486 10-10S17.514 2 12 2zm0 18c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8z' />
|
|
||||||
<path d='M11 11h2v6h-2zm0-4h2v2h-2z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function GithubIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M12 2.247a10 10 0 00-3.162 19.487c.5.088.687-.212.687-.475 0-.237-.012-1.025-.012-1.862-2.513.462-3.163-.613-3.363-1.175a3.636 3.636 0 00-1.025-1.413c-.35-.187-.85-.65-.013-.662a2.001 2.001 0 011.538 1.025 2.137 2.137 0 002.912.825 2.104 2.104 0 01.638-1.338c-2.225-.25-4.55-1.112-4.55-4.937a3.892 3.892 0 011.025-2.688 3.594 3.594 0 01.1-2.65s.837-.262 2.75 1.025a9.427 9.427 0 015 0c1.912-1.3 2.75-1.025 2.75-1.025a3.593 3.593 0 01.1 2.65 3.869 3.869 0 011.025 2.688c0 3.837-2.338 4.687-4.563 4.937a2.368 2.368 0 01.675 1.85c0 1.338-.012 2.413-.012 2.75 0 .263.187.575.687.475A10.005 10.005 0 0012 2.247z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function UpdateIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M2 12h2a7.986 7.986 0 012.337-5.663 7.91 7.91 0 012.542-1.71 8.12 8.12 0 016.13-.041A2.488 2.488 0 0017.5 7C18.886 7 20 5.886 20 4.5S18.886 2 17.5 2c-.689 0-1.312.276-1.763.725-2.431-.973-5.223-.958-7.635.059a9.928 9.928 0 00-3.18 2.139 9.92 9.92 0 00-2.14 3.179A10.005 10.005 0 002 12zm17.373 3.122c-.401.952-.977 1.808-1.71 2.541s-1.589 1.309-2.542 1.71a8.12 8.12 0 01-6.13.041A2.488 2.488 0 006.5 17C5.114 17 4 18.114 4 19.5S5.114 22 6.5 22c.689 0 1.312-.276 1.763-.725A9.965 9.965 0 0012 22a9.983 9.983 0 009.217-6.102A9.992 9.992 0 0022 12h-2a7.993 7.993 0 01-.627 3.122z' />
|
|
||||||
<path d='M12 7.462c-2.502 0-4.538 2.036-4.538 4.538S9.498 16.538 12 16.538s4.538-2.036 4.538-4.538S14.502 7.462 12 7.462zm0 7.076c-1.399 0-2.538-1.139-2.538-2.538S10.601 9.462 12 9.462s2.538 1.139 2.538 2.538-1.139 2.538-2.538 2.538z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function InDoorIcon(props: IconProps) {
|
export function InDoorIcon(props: IconProps) {
|
||||||
return (
|
return (
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
<IconSVG viewbox='0 0 24 24' {...props}>
|
||||||
|
@ -401,38 +150,6 @@ export function InDoorIcon(props: IconProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GotoLastIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M7.707 17.707L13.414 12 7.707 6.293 6.293 7.707 10.586 12l-4.293 4.293zM15 6h2v12h-2z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function GotoFirstIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M16.293 17.707l1.414-1.414L13.414 12l4.293-4.293-1.414-1.414L10.586 12zM7 6h2v12H7z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function GotoNextIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M10.707 17.707L16.414 12l-5.707-5.707-1.414 1.414L13.586 12l-4.293 4.293z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function GotoPrevIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M13.293 6.293L7.586 12l5.707 5.707 1.414-1.414L10.414 12l4.293-4.293z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function DescendingIcon(props: IconProps) {
|
export function DescendingIcon(props: IconProps) {
|
||||||
return (
|
return (
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
<IconSVG viewbox='0 0 24 24' {...props}>
|
||||||
|
@ -472,61 +189,3 @@ export function CheckboxNullIcon() {
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ChevronUpIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M6.293 13.293l1.414 1.414L12 10.414l4.293 4.293 1.414-1.414L12 7.586z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ChevronDoubleUpIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M6.293 11.293l1.414 1.414L12 8.414l4.293 4.293 1.414-1.414L12 5.586z' />
|
|
||||||
<path d='M6.293 16.293l1.414 1.414L12 13.414l4.293 4.293 1.414-1.414L12 10.586z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ChevronDoubleDownIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M12 15.586l-4.293-4.293-1.414 1.414L12 18.414l5.707-5.707-1.414-1.414z' />
|
|
||||||
<path d='M17.707 7.707l-1.414-1.414L12 10.586 7.707 6.293 6.293 7.707 12 13.414z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function CheckIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M10 15.586l-3.293-3.293-1.414 1.414L10 18.414l9.707-9.707-1.414-1.414z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function CogIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
|
||||||
<path d='M12 16c2.206 0 4-1.794 4-4s-1.794-4-4-4-4 1.794-4 4 1.794 4 4 4zm0-6c1.084 0 2 .916 2 2s-.916 2-2 2-2-.916-2-2 .916-2 2-2z' />
|
|
||||||
<path d='M2.845 16.136l1 1.73c.531.917 1.809 1.261 2.73.73l.529-.306A8.1 8.1 0 009 19.402V20c0 1.103.897 2 2 2h2c1.103 0 2-.897 2-2v-.598a8.132 8.132 0 001.896-1.111l.529.306c.923.53 2.198.188 2.731-.731l.999-1.729a2.001 2.001 0 00-.731-2.732l-.505-.292a7.718 7.718 0 000-2.224l.505-.292a2.002 2.002 0 00.731-2.732l-.999-1.729c-.531-.92-1.808-1.265-2.731-.732l-.529.306A8.1 8.1 0 0015 4.598V4c0-1.103-.897-2-2-2h-2c-1.103 0-2 .897-2 2v.598a8.132 8.132 0 00-1.896 1.111l-.529-.306c-.924-.531-2.2-.187-2.731.732l-.999 1.729a2.001 2.001 0 00.731 2.732l.505.292a7.683 7.683 0 000 2.223l-.505.292a2.003 2.003 0 00-.731 2.733zm3.326-2.758A5.703 5.703 0 016 12c0-.462.058-.926.17-1.378a.999.999 0 00-.47-1.108l-1.123-.65.998-1.729 1.145.662a.997.997 0 001.188-.142 6.071 6.071 0 012.384-1.399A1 1 0 0011 5.3V4h2v1.3a1 1 0 00.708.956 6.083 6.083 0 012.384 1.399.999.999 0 001.188.142l1.144-.661 1 1.729-1.124.649a1 1 0 00-.47 1.108c.112.452.17.916.17 1.378 0 .461-.058.925-.171 1.378a1 1 0 00.471 1.108l1.123.649-.998 1.729-1.145-.661a.996.996 0 00-1.188.142 6.071 6.071 0 01-2.384 1.399A1 1 0 0013 18.7l.002 1.3H11v-1.3a1 1 0 00-.708-.956 6.083 6.083 0 01-2.384-1.399.992.992 0 00-1.188-.141l-1.144.662-1-1.729 1.124-.651a1 1 0 00.471-1.108z' />
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function CrossIcon(props: IconProps) {
|
|
||||||
return (
|
|
||||||
<IconSVG viewbox='0 0 21 21' {...props}>
|
|
||||||
<g
|
|
||||||
fillRule='evenodd'
|
|
||||||
stroke='currentColor'
|
|
||||||
strokeLinecap='round'
|
|
||||||
strokeLinejoin='round'
|
|
||||||
>
|
|
||||||
<path d='M15.5 15.5l-10-10zM15.5 5.5l-10 10' />
|
|
||||||
</g>
|
|
||||||
</IconSVG>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -4,7 +4,7 @@ function Logo() {
|
||||||
const { darkMode } = useConceptTheme();
|
const { darkMode } = useConceptTheme();
|
||||||
return (
|
return (
|
||||||
<img alt='Логотип КонцептПортал'
|
<img alt='Логотип КонцептПортал'
|
||||||
className='max-h-[1.6rem] min-w-[11rem]'
|
className='max-h-[1.6rem] min-w-[11.5rem]'
|
||||||
src={!darkMode ? '/logo_full.svg' : '/logo_full_dark.svg'}
|
src={!darkMode ? '/logo_full.svg' : '/logo_full_dark.svg'}
|
||||||
/>);
|
/>);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ function Navigation () {
|
||||||
{!noNavigation ?
|
{!noNavigation ?
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'pl-2 pr-[0.8rem] h-[3rem]',
|
'pl-2 pr-[0.9rem] h-[3rem]',
|
||||||
'flex justify-between',
|
'flex justify-between',
|
||||||
'border-b-2 rounded-none'
|
'border-b-2 rounded-none'
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
import { DarkThemeIcon, LightThemeIcon } from '@/components/Icons';
|
|
||||||
import { useConceptTheme } from '@/context/ThemeContext';
|
|
||||||
|
|
||||||
import NavigationButton from './NavigationButton';
|
|
||||||
|
|
||||||
function ThemeSwitcher() {
|
|
||||||
const { darkMode, toggleDarkMode } = useConceptTheme();
|
|
||||||
if (darkMode) {
|
|
||||||
return (
|
|
||||||
<NavigationButton
|
|
||||||
description='Светлая тема'
|
|
||||||
icon={<LightThemeIcon />}
|
|
||||||
onClick={toggleDarkMode}
|
|
||||||
/>);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<NavigationButton
|
|
||||||
description='Темная тема'
|
|
||||||
icon={<DarkThemeIcon />}
|
|
||||||
onClick={toggleDarkMode}
|
|
||||||
/>);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ThemeSwitcher;
|
|
|
@ -27,20 +27,20 @@ function UserDropdown({ hideDropdown }: UserDropdownProps) {
|
||||||
return (
|
return (
|
||||||
<Dropdown dimensions='w-36' stretchLeft>
|
<Dropdown dimensions='w-36' stretchLeft>
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
|
text={user?.username}
|
||||||
tooltip='Профиль пользователя'
|
tooltip='Профиль пользователя'
|
||||||
onClick={navigateProfile}
|
onClick={navigateProfile}
|
||||||
>
|
/>
|
||||||
{user?.username}
|
|
||||||
</DropdownButton>
|
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
|
text={darkMode ? 'Светлая тема' : 'Темная тема'}
|
||||||
tooltip='Переключение темы оформления'
|
tooltip='Переключение темы оформления'
|
||||||
onClick={toggleDarkMode}
|
onClick={toggleDarkMode}
|
||||||
>
|
/>
|
||||||
{darkMode ? 'Светлая тема' : 'Темная тема'}
|
<DropdownButton
|
||||||
</DropdownButton>
|
text='Выйти...'
|
||||||
<DropdownButton onClick={logoutAndRedirect}>
|
className='font-semibold'
|
||||||
<b>Выйти...</b>
|
onClick={logoutAndRedirect}
|
||||||
</DropdownButton>
|
/>
|
||||||
</Dropdown>);
|
</Dropdown>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
function InfoConstituenta({ data, ...restProps }: InfoConstituentaProps) {
|
function InfoConstituenta({ data, ...restProps }: InfoConstituentaProps) {
|
||||||
return (
|
return (
|
||||||
<div {...restProps}>
|
<div {...restProps}>
|
||||||
<h1>Конституента {data.alias}</h1>
|
<h2>Конституента {data.alias}</h2>
|
||||||
<p>
|
<p>
|
||||||
<b>Типизация: </b>
|
<b>Типизация: </b>
|
||||||
{labelCstTypification(data)}
|
{labelCstTypification(data)}
|
||||||
|
|
36
rsconcept/frontend/src/context/AccessModeContext.tsx
Normal file
36
rsconcept/frontend/src/context/AccessModeContext.tsx
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { createContext, useContext, useState } from 'react';
|
||||||
|
|
||||||
|
import { UserAccessMode } from '@/models/miscelanious';
|
||||||
|
|
||||||
|
interface IAccessModeContext {
|
||||||
|
mode: UserAccessMode
|
||||||
|
setMode: React.Dispatch<React.SetStateAction<UserAccessMode>>
|
||||||
|
}
|
||||||
|
|
||||||
|
const AccessContext = createContext<IAccessModeContext | null>(null);
|
||||||
|
export const useAccessMode = () => {
|
||||||
|
const context = useContext(AccessContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error(
|
||||||
|
'useAccessMode has to be used within <AccessModeState.Provider>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AccessModeStateProps {
|
||||||
|
children: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AccessModeState = ({ children }: AccessModeStateProps) => {
|
||||||
|
const [mode, setMode] = useState<UserAccessMode>(UserAccessMode.READER);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AccessContext.Provider
|
||||||
|
value={{ mode, setMode }}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</AccessContext.Provider>);
|
||||||
|
};
|
|
@ -30,15 +30,9 @@ interface IRSFormContext {
|
||||||
loading: boolean
|
loading: boolean
|
||||||
processing: boolean
|
processing: boolean
|
||||||
|
|
||||||
isMutable: boolean
|
|
||||||
isOwned: boolean
|
isOwned: boolean
|
||||||
isClaimable: boolean
|
isClaimable: boolean
|
||||||
isTracking: boolean
|
isSubscribed: boolean
|
||||||
|
|
||||||
adminMode: boolean
|
|
||||||
toggleAdminMode: () => void
|
|
||||||
readerMode: boolean
|
|
||||||
toggleReaderMode: () => void
|
|
||||||
|
|
||||||
update: (data: ILibraryUpdateData, callback?: DataCallback<ILibraryItem>) => void
|
update: (data: ILibraryUpdateData, callback?: DataCallback<ILibraryItem>) => void
|
||||||
claim: (callback?: DataCallback<ILibraryItem>) => void
|
claim: (callback?: DataCallback<ILibraryItem>) => void
|
||||||
|
@ -76,8 +70,6 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
||||||
const { schema, reload, error, setError, setSchema, loading } = useRSFormDetails({ target: schemaID });
|
const { schema, reload, error, setError, setSchema, loading } = useRSFormDetails({ target: schemaID });
|
||||||
const [processing, setProcessing] = useState(false);
|
const [processing, setProcessing] = useState(false);
|
||||||
|
|
||||||
const [adminMode, setAdminMode] = useState(false);
|
|
||||||
const [readerMode, setReaderMode] = useState(false);
|
|
||||||
const [toggleTracking, setToggleTracking] = useState(false);
|
const [toggleTracking, setToggleTracking] = useState(false);
|
||||||
|
|
||||||
const isOwned = useMemo(
|
const isOwned = useMemo(
|
||||||
|
@ -90,15 +82,7 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
||||||
return (user?.id !== schema?.owner && schema?.is_common && !schema?.is_canonical) ?? false;
|
return (user?.id !== schema?.owner && schema?.is_common && !schema?.is_canonical) ?? false;
|
||||||
}, [user, schema?.owner, schema?.is_common, schema?.is_canonical]);
|
}, [user, schema?.owner, schema?.is_common, schema?.is_canonical]);
|
||||||
|
|
||||||
const isMutable = useMemo(
|
const isSubscribed = useMemo(
|
||||||
() => {
|
|
||||||
return (
|
|
||||||
!loading && !processing && !readerMode &&
|
|
||||||
((isOwned || (adminMode && user?.is_staff)) ?? false)
|
|
||||||
);
|
|
||||||
}, [user?.is_staff, readerMode, adminMode, isOwned, loading, processing]);
|
|
||||||
|
|
||||||
const isTracking = useMemo(
|
|
||||||
() => {
|
() => {
|
||||||
if (!user || !schema || !user.id) {
|
if (!user || !schema || !user.id) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -324,12 +308,10 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
||||||
<RSFormContext.Provider value={{
|
<RSFormContext.Provider value={{
|
||||||
schema,
|
schema,
|
||||||
error, loading, processing,
|
error, loading, processing,
|
||||||
adminMode, readerMode, isOwned, isMutable,
|
isOwned,
|
||||||
isClaimable, isTracking,
|
isClaimable, isSubscribed,
|
||||||
update, download, upload, claim, resetAliases, subscribe, unsubscribe,
|
update, download, upload, claim, resetAliases, subscribe, unsubscribe,
|
||||||
cstUpdate, cstCreate, cstRename, cstDelete, cstMoveTo,
|
cstUpdate, cstCreate, cstRename, cstDelete, cstMoveTo,
|
||||||
toggleAdminMode: () => setAdminMode(prev => !prev),
|
|
||||||
toggleReaderMode: () => setReaderMode(prev => !prev)
|
|
||||||
}}>
|
}}>
|
||||||
{ children }
|
{ children }
|
||||||
</RSFormContext.Provider>);
|
</RSFormContext.Provider>);
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
import { createColumnHelper } from '@tanstack/react-table';
|
import { createColumnHelper } from '@tanstack/react-table';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { Dispatch, useCallback, useEffect, useMemo, useState } from 'react';
|
import { Dispatch, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
import { BiCheck, BiRefresh, BiX } from 'react-icons/bi';
|
||||||
|
|
||||||
import MiniButton from '@/components/Common/MiniButton';
|
import MiniButton from '@/components/Common/MiniButton';
|
||||||
import DataTable, { IConditionalStyle } from '@/components/DataTable';
|
import DataTable, { IConditionalStyle } from '@/components/DataTable';
|
||||||
import { ArrowsRotateIcon, CheckIcon, CrossIcon } from '@/components/Icons';
|
|
||||||
import RSInput from '@/components/RSInput';
|
import RSInput from '@/components/RSInput';
|
||||||
import ConstituentaPicker from '@/components/Shared/ConstituentaPicker';
|
import ConstituentaPicker from '@/components/Shared/ConstituentaPicker';
|
||||||
import { useConceptTheme } from '@/context/ThemeContext';
|
import { useConceptTheme } from '@/context/ThemeContext';
|
||||||
|
@ -133,7 +133,7 @@ function ArgumentsTab({ state, schema, partialUpdate }: ArgumentsTabProps) {
|
||||||
{props.row.original.value ?
|
{props.row.original.value ?
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Очистить значение'
|
tooltip='Очистить значение'
|
||||||
icon={<CrossIcon size={3} color='clr-text-warning'/>}
|
icon={<BiX size='0.75rem' className='clr-text-warning'/>}
|
||||||
noHover
|
noHover
|
||||||
onClick={() => handleClearArgument(props.row.original)}
|
onClick={() => handleClearArgument(props.row.original)}
|
||||||
/> : null}
|
/> : null}
|
||||||
|
@ -192,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' : 'clr-text-success'} />}
|
icon={<BiCheck size='1.25rem' className={!argumentValue || !selectedArgument ? 'text-disabled' : 'clr-text-success'} />}
|
||||||
disabled={!argumentValue || !selectedArgument}
|
disabled={!argumentValue || !selectedArgument}
|
||||||
onClick={() => handleAssignArgument(selectedArgument!, argumentValue)}
|
onClick={() => handleAssignArgument(selectedArgument!, argumentValue)}
|
||||||
/>
|
/>
|
||||||
|
@ -200,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 ? 'clr-text-primary' : ''} />}
|
icon={<BiRefresh size='1.25rem' className={isModified ? 'clr-text-primary' : ''} />}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Очистить значение аргумента'
|
tooltip='Очистить значение аргумента'
|
||||||
disabled={!selectedClearable}
|
disabled={!selectedClearable}
|
||||||
icon={<CrossIcon size={5} color={!selectedClearable ? 'text-disabled' : 'clr-text-warning'}/>}
|
icon={<BiX size='1.25rem' className={!selectedClearable ? 'text-disabled' : 'clr-text-warning'}/>}
|
||||||
onClick={() => selectedArgument ? handleClearArgument(selectedArgument) : undefined}
|
onClick={() => selectedArgument ? handleClearArgument(selectedArgument) : undefined}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useLayoutEffect, useState } from 'react';
|
import { useLayoutEffect, useState } from 'react';
|
||||||
|
import { BiCheck, BiChevronsDown } from 'react-icons/bi';
|
||||||
|
|
||||||
import Label from '@/components/Common/Label';
|
import Label from '@/components/Common/Label';
|
||||||
import MiniButton from '@/components/Common/MiniButton';
|
import MiniButton from '@/components/Common/MiniButton';
|
||||||
|
@ -9,7 +10,7 @@ import Modal from '@/components/Common/Modal';
|
||||||
import Overlay from '@/components/Common/Overlay';
|
import Overlay from '@/components/Common/Overlay';
|
||||||
import TextArea from '@/components/Common/TextArea';
|
import TextArea from '@/components/Common/TextArea';
|
||||||
import HelpButton from '@/components/Help/HelpButton';
|
import HelpButton from '@/components/Help/HelpButton';
|
||||||
import { ArrowLeftIcon, ArrowRightIcon, CheckIcon, ChevronDoubleDownIcon } from '@/components/Icons';
|
import { ArrowLeftIcon, ArrowRightIcon } from '@/components/Icons';
|
||||||
import SelectGrammeme from '@/components/Shared/SelectGrammeme';
|
import SelectGrammeme from '@/components/Shared/SelectGrammeme';
|
||||||
import useConceptText from '@/hooks/useConceptText';
|
import useConceptText from '@/hooks/useConceptText';
|
||||||
import { Grammeme, ITextRequest, IWordForm, IWordFormPlain } from '@/models/language';
|
import { Grammeme, ITextRequest, IWordForm, IWordFormPlain } from '@/models/language';
|
||||||
|
@ -152,15 +153,15 @@ 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' : 'clr-text-primary'} />}
|
icon={<ArrowLeftIcon size='1.25rem' className={inputGrams.length == 0 ? 'text-disabled' : 'clr-text-primary'} />}
|
||||||
disabled={textProcessor.loading || inputGrams.length == 0}
|
disabled={textProcessor.loading || inputGrams.length == 0}
|
||||||
onClick={handleInflect}
|
onClick={handleInflect}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Определить граммемы'
|
tooltip='Определить граммемы'
|
||||||
icon={<ArrowRightIcon
|
icon={<ArrowRightIcon
|
||||||
size={5}
|
size='1.25rem'
|
||||||
color={!inputText ? 'text-disabled' : 'clr-text-primary'}
|
className={!inputText ? 'text-disabled' : 'clr-text-primary'}
|
||||||
/>}
|
/>}
|
||||||
disabled={textProcessor.loading || !inputText}
|
disabled={textProcessor.loading || !inputText}
|
||||||
onClick={handleParse}
|
onClick={handleParse}
|
||||||
|
@ -179,18 +180,16 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
|
||||||
<Overlay position='top-2 left-0'>
|
<Overlay position='top-2 left-0'>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Внести словоформу'
|
tooltip='Внести словоформу'
|
||||||
icon={<CheckIcon
|
icon={<BiCheck
|
||||||
size={5}
|
size='1.25rem'
|
||||||
color={!inputText || inputGrams.length == 0 ? 'text-disabled' : 'clr-text-success'}
|
className={!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}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Генерировать стандартные словоформы'
|
tooltip='Генерировать стандартные словоформы'
|
||||||
icon={<ChevronDoubleDownIcon
|
icon={<BiChevronsDown size='1.25rem' className={!inputText ? 'text-disabled' : 'clr-text-primary'}
|
||||||
size={5}
|
|
||||||
color={!inputText ? 'text-disabled' : 'clr-text-primary'}
|
|
||||||
/>}
|
/>}
|
||||||
disabled={textProcessor.loading || !inputText}
|
disabled={textProcessor.loading || !inputText}
|
||||||
onClick={handleGenerateLexeme}
|
onClick={handleGenerateLexeme}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
import { BiX } from 'react-icons/bi';
|
||||||
|
|
||||||
import MiniButton from '@/components/Common/MiniButton';
|
import MiniButton from '@/components/Common/MiniButton';
|
||||||
import Overlay from '@/components/Common/Overlay';
|
import Overlay from '@/components/Common/Overlay';
|
||||||
import DataTable, { createColumnHelper } from '@/components/DataTable';
|
import DataTable, { createColumnHelper } from '@/components/DataTable';
|
||||||
import { CrossIcon } from '@/components/Icons';
|
|
||||||
import WordFormBadge from '@/components/Shared/WordFormBadge';
|
import WordFormBadge from '@/components/Shared/WordFormBadge';
|
||||||
import { IWordForm } from '@/models/language';
|
import { IWordForm } from '@/models/language';
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ function WordFormsTable({ forms, setForms, onFormSelect, loading }: WordFormsTab
|
||||||
cell: props =>
|
cell: props =>
|
||||||
<MiniButton noHover
|
<MiniButton noHover
|
||||||
tooltip='Удалить словоформу'
|
tooltip='Удалить словоформу'
|
||||||
icon={<CrossIcon size={4} color='text-warning'/>}
|
icon={<BiX size='1rem' className='text-warning'/>}
|
||||||
onClick={() => handleDeleteRow(props.row.index)}
|
onClick={() => handleDeleteRow(props.row.index)}
|
||||||
/>
|
/>
|
||||||
})
|
})
|
||||||
|
@ -79,7 +79,7 @@ function WordFormsTable({ forms, setForms, onFormSelect, loading }: WordFormsTab
|
||||||
<Overlay position='top-1 right-4'>
|
<Overlay position='top-1 right-4'>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Сбросить все словоформы'
|
tooltip='Сбросить все словоформы'
|
||||||
icon={<CrossIcon size={4} color={forms.length === 0 ? 'text-disabled' : 'text-warning'} />}
|
icon={<BiX size='1rem' className={forms.length === 0 ? 'text-disabled' : 'text-warning'} />}
|
||||||
disabled={loading || forms.length === 0}
|
disabled={loading || forms.length === 0}
|
||||||
onClick={handleResetAll}
|
onClick={handleResetAll}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -145,6 +145,10 @@
|
||||||
@apply text-lg font-semibold text-center
|
@apply text-lg font-semibold text-center
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
@apply font-semibold text-center
|
||||||
|
}
|
||||||
|
|
||||||
b {
|
b {
|
||||||
@apply font-semibold
|
@apply font-semibold
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,15 @@
|
||||||
* Module: Miscellanious frontend model types. Future tagets for refactoring aimed at extracting modules.
|
* Module: Miscellanious frontend model types. Future tagets for refactoring aimed at extracting modules.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents user access mode.
|
||||||
|
*/
|
||||||
|
export enum UserAccessMode {
|
||||||
|
READER = 0,
|
||||||
|
OWNER,
|
||||||
|
ADMIN
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents graph dependency mode.
|
* Represents graph dependency mode.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { BiDownload } from 'react-icons/bi';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import Button from '@/components/Common/Button';
|
import Button from '@/components/Common/Button';
|
||||||
|
@ -12,7 +13,6 @@ import Overlay from '@/components/Common/Overlay';
|
||||||
import SubmitButton from '@/components/Common/SubmitButton';
|
import SubmitButton from '@/components/Common/SubmitButton';
|
||||||
import TextArea from '@/components/Common/TextArea';
|
import TextArea from '@/components/Common/TextArea';
|
||||||
import TextInput from '@/components/Common/TextInput';
|
import TextInput from '@/components/Common/TextInput';
|
||||||
import { DownloadIcon } from '@/components/Icons';
|
|
||||||
import InfoError from '@/components/InfoError';
|
import InfoError from '@/components/InfoError';
|
||||||
import RequireAuth from '@/components/RequireAuth';
|
import RequireAuth from '@/components/RequireAuth';
|
||||||
import { useLibrary } from '@/context/LibraryContext';
|
import { useLibrary } from '@/context/LibraryContext';
|
||||||
|
@ -95,7 +95,7 @@ function CreateRSFormPage() {
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Загрузить из Экстеор'
|
tooltip='Загрузить из Экстеор'
|
||||||
icon={<DownloadIcon size={5} color='clr-text-primary'/>}
|
icon={<BiDownload size='1.25rem' className='clr-text-primary'/>}
|
||||||
onClick={() => inputRef.current?.click()}
|
onClick={() => inputRef.current?.click()}
|
||||||
/>
|
/>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
|
|
@ -20,15 +20,15 @@ function ItemIcons({ user, item }: ItemIconsProps) {
|
||||||
>
|
>
|
||||||
{(user && user.subscriptions.includes(item.id)) ?
|
{(user && user.subscriptions.includes(item.id)) ?
|
||||||
<span title='Отслеживаемая'>
|
<span title='Отслеживаемая'>
|
||||||
<SubscribedIcon size={3} />
|
<SubscribedIcon size='0.75rem' />
|
||||||
</span> : null}
|
</span> : null}
|
||||||
{item.is_common ?
|
{item.is_common ?
|
||||||
<span title='Общедоступная'>
|
<span title='Общедоступная'>
|
||||||
<GroupIcon size={3}/>
|
<GroupIcon size='0.75rem'/>
|
||||||
</span> : null}
|
</span> : null}
|
||||||
{item.is_canonical ?
|
{item.is_canonical ?
|
||||||
<span title='Неизменная'>
|
<span title='Неизменная'>
|
||||||
<EducationIcon size={3}/>
|
<EducationIcon size='0.75rem'/>
|
||||||
</span> : null}
|
</span> : null}
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
import { BiFilterAlt } from 'react-icons/bi';
|
||||||
|
|
||||||
import Dropdown from '@/components/Common/Dropdown';
|
import Dropdown from '@/components/Common/Dropdown';
|
||||||
import DropdownCheckbox from '@/components/Common/DropdownCheckbox';
|
import DropdownCheckbox from '@/components/Common/DropdownCheckbox';
|
||||||
import SelectorButton from '@/components/Common/SelectorButton';
|
import SelectorButton from '@/components/Common/SelectorButton';
|
||||||
import { FilterIcon } from '@/components/Icons';
|
|
||||||
import { useAuth } from '@/context/AuthContext';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
import useDropdown from '@/hooks/useDropdown';
|
import useDropdown from '@/hooks/useDropdown';
|
||||||
import { LibraryFilterStrategy } from '@/models/miscelanious';
|
import { LibraryFilterStrategy } from '@/models/miscelanious';
|
||||||
|
@ -44,7 +44,7 @@ function PickerStrategy({ value, onChange }: PickerStrategyProps) {
|
||||||
<SelectorButton transparent tabIndex={-1}
|
<SelectorButton transparent tabIndex={-1}
|
||||||
tooltip='Список фильтров'
|
tooltip='Список фильтров'
|
||||||
dimensions='w-fit h-full'
|
dimensions='w-fit h-full'
|
||||||
icon={<FilterIcon size={5} />}
|
icon={<BiFilterAlt size='1.25rem' />}
|
||||||
text={labelLibraryFilter(value)}
|
text={labelLibraryFilter(value)}
|
||||||
onClick={strategyMenu.toggle}
|
onClick={strategyMenu.toggle}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
import { BiDiamond, BiDuplicate, BiPlusCircle, BiReset, BiTrash } from 'react-icons/bi';
|
||||||
|
import { FiSave } from "react-icons/fi";
|
||||||
|
|
||||||
import MiniButton from '@/components/Common/MiniButton';
|
import MiniButton from '@/components/Common/MiniButton';
|
||||||
import Overlay from '@/components/Common/Overlay';
|
import Overlay from '@/components/Common/Overlay';
|
||||||
import HelpButton from '@/components/Help/HelpButton';
|
import HelpButton from '@/components/Help/HelpButton';
|
||||||
import {
|
|
||||||
ArrowsRotateIcon, CloneIcon, DiamondIcon, DumpBinIcon, SaveIcon, SmallPlusIcon
|
|
||||||
} from '@/components/Icons';
|
|
||||||
import { HelpTopic } from '@/models/miscelanious';
|
import { HelpTopic } from '@/models/miscelanious';
|
||||||
|
|
||||||
interface ConstituentaToolbarProps {
|
interface ConstituentaToolbarProps {
|
||||||
|
@ -34,30 +33,30 @@ function ConstituentaToolbar({
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Сохранить изменения [Ctrl + S]'
|
tooltip='Сохранить изменения [Ctrl + S]'
|
||||||
disabled={!canSave}
|
disabled={!canSave}
|
||||||
icon={<SaveIcon size={5} color={canSave ? 'clr-text-primary' : ''}/>}
|
icon={<FiSave size='1.25rem' className={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 ? 'clr-text-primary' : ''} />}
|
icon={<BiReset size='1.25rem' className={canSave ? 'clr-text-primary' : ''} />}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Создать конституенту после данной'
|
tooltip='Создать конституенту после данной'
|
||||||
disabled={!isMutable}
|
disabled={!isMutable}
|
||||||
onClick={onCreate}
|
onClick={onCreate}
|
||||||
icon={<SmallPlusIcon size={5} color={isMutable ? 'clr-text-success' : ''} />}
|
icon={<BiPlusCircle size={'1.25rem'} className={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 ? 'clr-text-success' : ''} />}
|
icon={<BiDuplicate size='1.25rem' className={isMutable ? 'clr-text-success' : ''} />}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Создать конституенту из шаблона [Alt + E]'
|
tooltip='Создать конституенту из шаблона [Alt + E]'
|
||||||
icon={<DiamondIcon color={isMutable ? 'clr-text-primary': ''} size={5}/>}
|
icon={<BiDiamond className={isMutable ? 'clr-text-primary': ''} size={'1.25rem'}/>}
|
||||||
disabled={!isMutable}
|
disabled={!isMutable}
|
||||||
onClick={onTemplates}
|
onClick={onTemplates}
|
||||||
/>
|
/>
|
||||||
|
@ -65,7 +64,7 @@ function ConstituentaToolbar({
|
||||||
tooltip='Удалить редактируемую конституенту'
|
tooltip='Удалить редактируемую конституенту'
|
||||||
disabled={!isMutable}
|
disabled={!isMutable}
|
||||||
onClick={onDelete}
|
onClick={onDelete}
|
||||||
icon={<DumpBinIcon size={5} color={isMutable ? 'clr-text-warning' : ''} />}
|
icon={<BiTrash size='1.25rem' className={isMutable ? 'clr-text-warning' : ''} />}
|
||||||
/>
|
/>
|
||||||
<HelpButton topic={HelpTopic.CONSTITUENTA} offset={4} />
|
<HelpButton topic={HelpTopic.CONSTITUENTA} offset={4} />
|
||||||
</Overlay>);
|
</Overlay>);
|
||||||
|
|
|
@ -18,6 +18,8 @@ const UNFOLDED_HEIGHT = '59.1rem';
|
||||||
const SIDELIST_HIDE_THRESHOLD = 1100; // px
|
const SIDELIST_HIDE_THRESHOLD = 1100; // px
|
||||||
|
|
||||||
interface EditorConstituentaProps {
|
interface EditorConstituentaProps {
|
||||||
|
isMutable: boolean
|
||||||
|
|
||||||
activeID?: number
|
activeID?: number
|
||||||
activeCst?: IConstituenta | undefined
|
activeCst?: IConstituenta | undefined
|
||||||
isModified: boolean
|
isModified: boolean
|
||||||
|
@ -32,15 +34,15 @@ interface EditorConstituentaProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function EditorConstituenta({
|
function EditorConstituenta({
|
||||||
isModified, setIsModified, activeID, activeCst, onEditTerm,
|
isMutable, isModified, setIsModified, activeID, activeCst, onEditTerm,
|
||||||
onCreateCst, onRenameCst, onOpenEdit, onDeleteCst, onTemplates
|
onCreateCst, onRenameCst, onOpenEdit, onDeleteCst, onTemplates
|
||||||
}: EditorConstituentaProps) {
|
}: EditorConstituentaProps) {
|
||||||
const windowSize = useWindowSize();
|
const windowSize = useWindowSize();
|
||||||
const { schema, isMutable } = useRSForm();
|
const { schema } = useRSForm();
|
||||||
|
|
||||||
const [toggleReset, setToggleReset] = useState(false);
|
const [toggleReset, setToggleReset] = useState(false);
|
||||||
|
|
||||||
const readyForEdit = useMemo(() => (!!activeCst && isMutable), [activeCst, isMutable]);
|
const disabled = useMemo(() => (!activeCst || !isMutable), [activeCst, isMutable]);
|
||||||
|
|
||||||
function handleDelete() {
|
function handleDelete() {
|
||||||
if (!schema || !activeID) {
|
if (!schema || !activeID) {
|
||||||
|
@ -84,7 +86,7 @@ function EditorConstituenta({
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleInput(event: React.KeyboardEvent<HTMLDivElement>) {
|
function handleInput(event: React.KeyboardEvent<HTMLDivElement>) {
|
||||||
if (!isMutable) {
|
if (disabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event.ctrlKey && event.code === 'KeyS') {
|
if (event.ctrlKey && event.code === 'KeyS') {
|
||||||
|
@ -120,7 +122,7 @@ function EditorConstituenta({
|
||||||
|
|
||||||
return (<>
|
return (<>
|
||||||
<ConstituentaToolbar
|
<ConstituentaToolbar
|
||||||
isMutable={readyForEdit}
|
isMutable={!disabled}
|
||||||
isModified={isModified}
|
isModified={isModified}
|
||||||
|
|
||||||
onSubmit={initiateSubmit}
|
onSubmit={initiateSubmit}
|
||||||
|
@ -136,7 +138,8 @@ function EditorConstituenta({
|
||||||
onKeyDown={handleInput}
|
onKeyDown={handleInput}
|
||||||
>
|
>
|
||||||
<div className='min-w-[47.8rem] max-w-[47.8rem] px-4 py-1'>
|
<div className='min-w-[47.8rem] max-w-[47.8rem] px-4 py-1'>
|
||||||
<FormConstituenta id={globalIDs.constituenta_editor}
|
<FormConstituenta disabled={disabled}
|
||||||
|
id={globalIDs.constituenta_editor}
|
||||||
constituenta={activeCst}
|
constituenta={activeCst}
|
||||||
isModified={isModified}
|
isModified={isModified}
|
||||||
toggleReset={toggleReset}
|
toggleReset={toggleReset}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Dispatch, SetStateAction, useLayoutEffect, useMemo, useState } from 'react';
|
import { Dispatch, SetStateAction, useLayoutEffect, useState } from 'react';
|
||||||
|
import { FiSave } from 'react-icons/fi';
|
||||||
|
import { LiaEdit } from 'react-icons/lia';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import MiniButton from '@/components/Common/MiniButton';
|
import MiniButton from '@/components/Common/MiniButton';
|
||||||
import Overlay from '@/components/Common/Overlay';
|
import Overlay from '@/components/Common/Overlay';
|
||||||
import SubmitButton from '@/components/Common/SubmitButton';
|
import SubmitButton from '@/components/Common/SubmitButton';
|
||||||
import TextArea from '@/components/Common/TextArea';
|
import TextArea from '@/components/Common/TextArea';
|
||||||
import { EditIcon, SaveIcon } from '@/components/Icons';
|
|
||||||
import RefsInput from '@/components/RefsInput';
|
import RefsInput from '@/components/RefsInput';
|
||||||
import { useRSForm } from '@/context/RSFormContext';
|
import { useRSForm } from '@/context/RSFormContext';
|
||||||
import { IConstituenta, ICstRenameData, ICstUpdateData } from '@/models/rsform';
|
import { IConstituenta, ICstRenameData, ICstUpdateData } from '@/models/rsform';
|
||||||
|
@ -16,6 +17,8 @@ import { labelCstTypification } from '@/utils/labels';
|
||||||
import EditorRSExpression from '../EditorRSExpression';
|
import EditorRSExpression from '../EditorRSExpression';
|
||||||
|
|
||||||
interface FormConstituentaProps {
|
interface FormConstituentaProps {
|
||||||
|
disabled?: boolean
|
||||||
|
|
||||||
id?: string
|
id?: string
|
||||||
constituenta?: IConstituenta
|
constituenta?: IConstituenta
|
||||||
|
|
||||||
|
@ -28,13 +31,12 @@ interface FormConstituentaProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function FormConstituenta({
|
function FormConstituenta({
|
||||||
|
disabled,
|
||||||
id, isModified, setIsModified,
|
id, isModified, setIsModified,
|
||||||
constituenta, toggleReset,
|
constituenta, toggleReset,
|
||||||
onRenameCst, onEditTerm
|
onRenameCst, onEditTerm
|
||||||
}: FormConstituentaProps) {
|
}: FormConstituentaProps) {
|
||||||
const { schema, cstUpdate, isMutable, processing } = useRSForm();
|
const { schema, cstUpdate, processing } = useRSForm();
|
||||||
|
|
||||||
const readyForEdit = useMemo(() => (!!constituenta && isMutable), [constituenta, isMutable]);
|
|
||||||
|
|
||||||
const [alias, setAlias] = useState('');
|
const [alias, setAlias] = useState('');
|
||||||
const [term, setTerm] = useState('');
|
const [term, setTerm] = useState('');
|
||||||
|
@ -106,10 +108,10 @@ function FormConstituenta({
|
||||||
<Overlay position='top-0 left-[3rem]' className='flex justify-start select-none' >
|
<Overlay position='top-0 left-[3rem]' className='flex justify-start select-none' >
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip={`Редактировать словоформы термина: ${constituenta?.term_forms.length ?? 0}`}
|
tooltip={`Редактировать словоформы термина: ${constituenta?.term_forms.length ?? 0}`}
|
||||||
disabled={!readyForEdit}
|
disabled={disabled}
|
||||||
noHover
|
noHover
|
||||||
onClick={onEditTerm}
|
onClick={onEditTerm}
|
||||||
icon={<EditIcon size={4} color={readyForEdit ? 'clr-text-primary' : ''} />}
|
icon={<LiaEdit size='1rem' className={!disabled ? 'clr-text-primary' : ''} />}
|
||||||
/>
|
/>
|
||||||
<div className='pt-1 pl-[1.375rem] text-sm font-semibold w-fit'>
|
<div className='pt-1 pl-[1.375rem] text-sm font-semibold w-fit'>
|
||||||
<span>Имя </span>
|
<span>Имя </span>
|
||||||
|
@ -117,9 +119,9 @@ function FormConstituenta({
|
||||||
</div>
|
</div>
|
||||||
<MiniButton noHover
|
<MiniButton noHover
|
||||||
tooltip='Переименовать конституенту'
|
tooltip='Переименовать конституенту'
|
||||||
disabled={!readyForEdit}
|
disabled={disabled}
|
||||||
onClick={handleRename}
|
onClick={handleRename}
|
||||||
icon={<EditIcon size={4} color={readyForEdit ? 'clr-text-primary' : ''} />}
|
icon={<LiaEdit size='1rem' className={!disabled ? 'clr-text-primary' : ''} />}
|
||||||
/>
|
/>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
<form id={id}
|
<form id={id}
|
||||||
|
@ -133,7 +135,7 @@ function FormConstituenta({
|
||||||
value={term}
|
value={term}
|
||||||
initialValue={constituenta?.term_raw ?? ''}
|
initialValue={constituenta?.term_raw ?? ''}
|
||||||
resolved={constituenta?.term_resolved ?? ''}
|
resolved={constituenta?.term_resolved ?? ''}
|
||||||
disabled={!readyForEdit}
|
disabled={disabled}
|
||||||
onChange={newValue => setTerm(newValue)}
|
onChange={newValue => setTerm(newValue)}
|
||||||
/>
|
/>
|
||||||
<TextArea dense noBorder
|
<TextArea dense noBorder
|
||||||
|
@ -152,7 +154,7 @@ function FormConstituenta({
|
||||||
activeCst={constituenta}
|
activeCst={constituenta}
|
||||||
placeholder='Родоструктурное выражение, задающее формальное определение'
|
placeholder='Родоструктурное выражение, задающее формальное определение'
|
||||||
value={expression}
|
value={expression}
|
||||||
disabled={!readyForEdit}
|
disabled={disabled}
|
||||||
toggleReset={toggleReset}
|
toggleReset={toggleReset}
|
||||||
onChange={newValue => setExpression(newValue)}
|
onChange={newValue => setExpression(newValue)}
|
||||||
setTypification={setTypification}
|
setTypification={setTypification}
|
||||||
|
@ -164,21 +166,21 @@ function FormConstituenta({
|
||||||
value={textDefinition}
|
value={textDefinition}
|
||||||
initialValue={constituenta?.definition_raw ?? ''}
|
initialValue={constituenta?.definition_raw ?? ''}
|
||||||
resolved={constituenta?.definition_resolved ?? ''}
|
resolved={constituenta?.definition_resolved ?? ''}
|
||||||
disabled={!readyForEdit}
|
disabled={disabled}
|
||||||
onChange={newValue => setTextDefinition(newValue)}
|
onChange={newValue => setTextDefinition(newValue)}
|
||||||
/>
|
/>
|
||||||
<TextArea spellCheck
|
<TextArea spellCheck
|
||||||
label='Конвенция / Комментарий'
|
label='Конвенция / Комментарий'
|
||||||
placeholder='Договоренность об интерпретации или пояснение'
|
placeholder='Договоренность об интерпретации или пояснение'
|
||||||
value={convention}
|
value={convention}
|
||||||
disabled={!readyForEdit}
|
disabled={disabled}
|
||||||
onChange={event => setConvention(event.target.value)}
|
onChange={event => setConvention(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<div className='flex justify-center w-full'>
|
<div className='flex justify-center w-full'>
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
text='Сохранить изменения'
|
text='Сохранить изменения'
|
||||||
disabled={!isModified || !readyForEdit}
|
disabled={!isModified || disabled}
|
||||||
icon={<SaveIcon size={6} />}
|
icon={<FiSave size='1.5rem' />}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
import { ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
import { ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
||||||
import { useCallback, useLayoutEffect, useRef, useState } from 'react';
|
import { useCallback, useLayoutEffect, useRef, useState } from 'react';
|
||||||
|
import { PiGraphLight } from "react-icons/pi";
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import MiniButton from '@/components/Common/MiniButton';
|
import MiniButton from '@/components/Common/MiniButton';
|
||||||
import Overlay from '@/components/Common/Overlay';
|
import Overlay from '@/components/Common/Overlay';
|
||||||
import { ASTNetworkIcon } from '@/components/Icons';
|
|
||||||
import RSInput from '@/components/RSInput';
|
import RSInput from '@/components/RSInput';
|
||||||
import { RSTextWrapper } from '@/components/RSInput/textEditing';
|
import { RSTextWrapper } from '@/components/RSInput/textEditing';
|
||||||
import { useRSForm } from '@/context/RSFormContext';
|
import { useRSForm } from '@/context/RSFormContext';
|
||||||
|
@ -131,11 +131,11 @@ function EditorRSExpression({
|
||||||
syntaxTree={syntaxTree}
|
syntaxTree={syntaxTree}
|
||||||
hideWindow={() => setShowAST(false)}
|
hideWindow={() => setShowAST(false)}
|
||||||
/> : null}
|
/> : null}
|
||||||
<Overlay position='top-[-0.2rem] left-[11rem]'>
|
<Overlay position='top-[-0.375rem] left-[11rem]'>
|
||||||
<MiniButton noHover
|
<MiniButton noHover
|
||||||
tooltip='Дерево разбора выражения'
|
tooltip='Дерево разбора выражения'
|
||||||
onClick={handleShowAST}
|
onClick={handleShowAST}
|
||||||
icon={<ASTNetworkIcon size={5} color='clr-text-primary' />}
|
icon={<PiGraphLight size='1.25rem' className='clr-text-primary' />}
|
||||||
/>
|
/>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
|
||||||
|
|
|
@ -13,16 +13,23 @@ import RSFormStats from './RSFormStats';
|
||||||
import RSFormToolbar from './RSFormToolbar';
|
import RSFormToolbar from './RSFormToolbar';
|
||||||
|
|
||||||
interface EditorRSFormProps {
|
interface EditorRSFormProps {
|
||||||
|
isModified: boolean
|
||||||
|
isMutable: boolean
|
||||||
|
|
||||||
|
setIsModified: Dispatch<SetStateAction<boolean>>
|
||||||
onDestroy: () => void
|
onDestroy: () => void
|
||||||
onClaim: () => void
|
onClaim: () => void
|
||||||
onShare: () => void
|
onShare: () => void
|
||||||
onDownload: () => void
|
onDownload: () => void
|
||||||
isModified: boolean
|
onToggleSubscribe: () => void
|
||||||
setIsModified: Dispatch<SetStateAction<boolean>>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function EditorRSForm({ onDestroy, onClaim, onShare, isModified, setIsModified, onDownload }: EditorRSFormProps) {
|
function EditorRSForm({
|
||||||
const { schema, isMutable, isClaimable } = useRSForm();
|
isModified, isMutable,
|
||||||
|
onDestroy, onClaim, onShare, setIsModified,
|
||||||
|
onDownload, onToggleSubscribe
|
||||||
|
}: EditorRSFormProps) {
|
||||||
|
const { schema, isClaimable, isSubscribed, processing } = useRSForm();
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
|
|
||||||
function initiateSubmit() {
|
function initiateSubmit() {
|
||||||
|
@ -45,6 +52,8 @@ function EditorRSForm({ onDestroy, onClaim, onShare, isModified, setIsModified,
|
||||||
<div tabIndex={-1} onKeyDown={handleInput}>
|
<div tabIndex={-1} onKeyDown={handleInput}>
|
||||||
<RSFormToolbar
|
<RSFormToolbar
|
||||||
isMutable={isMutable}
|
isMutable={isMutable}
|
||||||
|
processing={processing}
|
||||||
|
isSubscribed={isSubscribed}
|
||||||
modified={isModified}
|
modified={isModified}
|
||||||
claimable={isClaimable}
|
claimable={isClaimable}
|
||||||
anonymous={!user}
|
anonymous={!user}
|
||||||
|
@ -54,11 +63,13 @@ function EditorRSForm({ onDestroy, onClaim, onShare, isModified, setIsModified,
|
||||||
onDownload={onDownload}
|
onDownload={onDownload}
|
||||||
onClaim={onClaim}
|
onClaim={onClaim}
|
||||||
onDestroy={onDestroy}
|
onDestroy={onDestroy}
|
||||||
|
onToggleSubscribe={onToggleSubscribe}
|
||||||
/>
|
/>
|
||||||
<div className='flex w-full'>
|
<div className='flex w-full'>
|
||||||
<div className='flex-grow max-w-[40rem] min-w-[30rem] px-4 pb-2'>
|
<div className='flex-grow max-w-[40rem] min-w-[30rem] px-4 pb-2'>
|
||||||
<div className='flex flex-col gap-3'>
|
<div className='flex flex-col gap-3'>
|
||||||
<FormRSForm id={globalIDs.library_item_editor}
|
<FormRSForm disabled={!isMutable}
|
||||||
|
id={globalIDs.library_item_editor}
|
||||||
isModified={isModified}
|
isModified={isModified}
|
||||||
setIsModified={setIsModified}
|
setIsModified={setIsModified}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Dispatch, SetStateAction, useLayoutEffect, useState } from 'react';
|
import { Dispatch, SetStateAction, useLayoutEffect, useState } from 'react';
|
||||||
|
import { FiSave } from 'react-icons/fi';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import Checkbox from '@/components/Common/Checkbox';
|
import Checkbox from '@/components/Common/Checkbox';
|
||||||
import SubmitButton from '@/components/Common/SubmitButton';
|
import SubmitButton from '@/components/Common/SubmitButton';
|
||||||
import TextArea from '@/components/Common/TextArea';
|
import TextArea from '@/components/Common/TextArea';
|
||||||
import TextInput from '@/components/Common/TextInput';
|
import TextInput from '@/components/Common/TextInput';
|
||||||
import { SaveIcon } from '@/components/Icons';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
import { useRSForm } from '@/context/RSFormContext';
|
import { useRSForm } from '@/context/RSFormContext';
|
||||||
import { LibraryItemType } from '@/models/library';
|
import { LibraryItemType } from '@/models/library';
|
||||||
import { IRSFormCreateData } from '@/models/rsform';
|
import { IRSFormCreateData } from '@/models/rsform';
|
||||||
|
@ -15,17 +16,16 @@ import { limits, patterns } from '@/utils/constants';
|
||||||
|
|
||||||
interface FormRSFormProps {
|
interface FormRSFormProps {
|
||||||
id?: string
|
id?: string
|
||||||
|
disabled: boolean
|
||||||
isModified: boolean
|
isModified: boolean
|
||||||
setIsModified: Dispatch<SetStateAction<boolean>>
|
setIsModified: Dispatch<SetStateAction<boolean>>
|
||||||
}
|
}
|
||||||
|
|
||||||
function FormRSForm({
|
function FormRSForm({
|
||||||
id, isModified, setIsModified,
|
id, disabled, isModified, setIsModified,
|
||||||
}: FormRSFormProps) {
|
}: FormRSFormProps) {
|
||||||
const {
|
const { schema, update, processing } = useRSForm();
|
||||||
schema, update, adminMode: adminMode,
|
const { user } = useAuth();
|
||||||
isMutable: isMutable, processing
|
|
||||||
} = useRSForm();
|
|
||||||
|
|
||||||
const [title, setTitle] = useState('');
|
const [title, setTitle] = useState('');
|
||||||
const [alias, setAlias] = useState('');
|
const [alias, setAlias] = useState('');
|
||||||
|
@ -85,7 +85,7 @@ function FormRSForm({
|
||||||
<TextInput required
|
<TextInput required
|
||||||
label='Полное название'
|
label='Полное название'
|
||||||
value={title}
|
value={title}
|
||||||
disabled={!isMutable}
|
disabled={disabled}
|
||||||
onChange={event => setTitle(event.target.value)}
|
onChange={event => setTitle(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<TextInput required
|
<TextInput required
|
||||||
|
@ -93,14 +93,14 @@ function FormRSForm({
|
||||||
dimensions='w-[14rem]'
|
dimensions='w-[14rem]'
|
||||||
pattern={patterns.alias}
|
pattern={patterns.alias}
|
||||||
tooltip={`не более ${limits.alias_len} символов`}
|
tooltip={`не более ${limits.alias_len} символов`}
|
||||||
disabled={!isMutable}
|
disabled={disabled}
|
||||||
value={alias}
|
value={alias}
|
||||||
onChange={event => setAlias(event.target.value)}
|
onChange={event => setAlias(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<TextArea
|
<TextArea
|
||||||
label='Комментарий'
|
label='Комментарий'
|
||||||
value={comment}
|
value={comment}
|
||||||
disabled={!isMutable}
|
disabled={disabled}
|
||||||
onChange={event => setComment(event.target.value)}
|
onChange={event => setComment(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<div className='flex justify-between whitespace-nowrap'>
|
<div className='flex justify-between whitespace-nowrap'>
|
||||||
|
@ -108,7 +108,7 @@ function FormRSForm({
|
||||||
label='Общедоступная схема'
|
label='Общедоступная схема'
|
||||||
tooltip='Общедоступные схемы видны всем пользователям и могут быть изменены'
|
tooltip='Общедоступные схемы видны всем пользователям и могут быть изменены'
|
||||||
dimensions='w-fit'
|
dimensions='w-fit'
|
||||||
disabled={!isMutable}
|
disabled={disabled}
|
||||||
value={common}
|
value={common}
|
||||||
setValue={value => setCommon(value)}
|
setValue={value => setCommon(value)}
|
||||||
/>
|
/>
|
||||||
|
@ -116,7 +116,7 @@ function FormRSForm({
|
||||||
label='Неизменная схема'
|
label='Неизменная схема'
|
||||||
tooltip='Только администраторы могут присваивать схемам неизменный статус'
|
tooltip='Только администраторы могут присваивать схемам неизменный статус'
|
||||||
dimensions='w-fit'
|
dimensions='w-fit'
|
||||||
disabled={!isMutable || !adminMode}
|
disabled={disabled || !user?.is_staff}
|
||||||
value={canonical}
|
value={canonical}
|
||||||
setValue={value => setCanonical(value)}
|
setValue={value => setCanonical(value)}
|
||||||
/>
|
/>
|
||||||
|
@ -125,8 +125,8 @@ function FormRSForm({
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
text='Сохранить изменения'
|
text='Сохранить изменения'
|
||||||
loading={processing}
|
loading={processing}
|
||||||
disabled={!isModified || !isMutable}
|
disabled={!isModified || disabled}
|
||||||
icon={<SaveIcon size={6} />}
|
icon={<FiSave size='1.5rem' />}
|
||||||
dimensions='my-2 w-fit'
|
dimensions='my-2 w-fit'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,28 +1,35 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
import { BiDownload, BiTrash } from 'react-icons/bi';
|
||||||
|
import { FiSave } from 'react-icons/fi';
|
||||||
|
import { LuCrown } from 'react-icons/lu';
|
||||||
|
|
||||||
import MiniButton from '@/components/Common/MiniButton';
|
import MiniButton from '@/components/Common/MiniButton';
|
||||||
import Overlay from '@/components/Common/Overlay';
|
import Overlay from '@/components/Common/Overlay';
|
||||||
import HelpButton from '@/components/Help/HelpButton';
|
import HelpButton from '@/components/Help/HelpButton';
|
||||||
import { DownloadIcon, DumpBinIcon, OwnerIcon, SaveIcon, ShareIcon } from '@/components/Icons';
|
import { NotSubscribedIcon, ShareIcon, SubscribedIcon } from '@/components/Icons';
|
||||||
import { HelpTopic } from '@/models/miscelanious';
|
import { HelpTopic } from '@/models/miscelanious';
|
||||||
|
|
||||||
interface RSFormToolbarProps {
|
interface RSFormToolbarProps {
|
||||||
isMutable: boolean
|
isMutable: boolean
|
||||||
|
isSubscribed: boolean
|
||||||
modified: boolean
|
modified: boolean
|
||||||
claimable: boolean
|
claimable: boolean
|
||||||
anonymous: boolean
|
anonymous: boolean
|
||||||
|
processing: boolean
|
||||||
|
|
||||||
onSubmit: () => void
|
onSubmit: () => void
|
||||||
onShare: () => void
|
onShare: () => void
|
||||||
onDownload: () => void
|
onDownload: () => void
|
||||||
onClaim: () => void
|
onClaim: () => void
|
||||||
onDestroy: () => void
|
onDestroy: () => void
|
||||||
|
onToggleSubscribe: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function RSFormToolbar({
|
function RSFormToolbar({
|
||||||
isMutable, modified, claimable, anonymous,
|
isMutable, modified, claimable, anonymous,
|
||||||
|
isSubscribed, onToggleSubscribe, processing,
|
||||||
onSubmit, onShare, onDownload,
|
onSubmit, onShare, onDownload,
|
||||||
onClaim, onDestroy
|
onClaim, onDestroy
|
||||||
}: RSFormToolbarProps) {
|
}: RSFormToolbarProps) {
|
||||||
|
@ -32,30 +39,41 @@ function RSFormToolbar({
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Сохранить изменения [Ctrl + S]'
|
tooltip='Сохранить изменения [Ctrl + S]'
|
||||||
disabled={!canSave}
|
disabled={!canSave}
|
||||||
icon={<SaveIcon size={5} color={canSave ? 'clr-text-primary' : ''}/>}
|
icon={<FiSave size='1.25rem' className={canSave ? 'clr-text-primary' : ''}/>}
|
||||||
onClick={onSubmit}
|
onClick={onSubmit}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Поделиться схемой'
|
tooltip='Поделиться схемой'
|
||||||
icon={<ShareIcon size={5} color='clr-text-primary'/>}
|
icon={<ShareIcon size='1.25rem' className='clr-text-primary'/>}
|
||||||
onClick={onShare}
|
onClick={onShare}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Скачать TRS файл'
|
tooltip='Скачать TRS файл'
|
||||||
icon={<DownloadIcon size={5} color='clr-text-primary'/>}
|
icon={<BiDownload size='1.25rem' className='clr-text-primary'/>}
|
||||||
onClick={onDownload}
|
onClick={onDownload}
|
||||||
/>
|
/>
|
||||||
|
<MiniButton
|
||||||
|
tooltip={'отслеживание: ' + (isSubscribed ? '[включено]' : '[выключено]')}
|
||||||
|
disabled={anonymous || processing}
|
||||||
|
icon={isSubscribed
|
||||||
|
? <SubscribedIcon size='1.25rem' className='clr-text-primary' />
|
||||||
|
: <NotSubscribedIcon size='1.25rem' className='clr-text-controls' />
|
||||||
|
}
|
||||||
|
dimensions='h-full w-fit pr-2'
|
||||||
|
style={{outlineColor: 'transparent'}}
|
||||||
|
onClick={onToggleSubscribe}
|
||||||
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip={claimable ? 'Стать владельцем' : 'Невозможно стать владельцем' }
|
tooltip={claimable ? 'Стать владельцем' : 'Невозможно стать владельцем' }
|
||||||
icon={<OwnerIcon size={5} color={!claimable ? '' : 'clr-text-success'}/>}
|
icon={<LuCrown size='1.25rem' className={!claimable ? '' : 'clr-text-success'}/>}
|
||||||
disabled={!claimable || anonymous}
|
disabled={!claimable || anonymous || processing}
|
||||||
onClick={onClaim}
|
onClick={onClaim}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Удалить схему'
|
tooltip='Удалить схему'
|
||||||
disabled={!isMutable}
|
disabled={!isMutable}
|
||||||
onClick={onDestroy}
|
onClick={onDestroy}
|
||||||
icon={<DumpBinIcon size={5} color={isMutable ? 'clr-text-warning' : ''} />}
|
icon={<BiTrash size='1.25rem' className={isMutable ? 'clr-text-warning' : ''} />}
|
||||||
/>
|
/>
|
||||||
<HelpButton topic={HelpTopic.RSFORM} offset={4} />
|
<HelpButton topic={HelpTopic.RSFORM} offset={4} />
|
||||||
</Overlay>);
|
</Overlay>);
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useLayoutEffect, useState } from 'react';
|
import { useLayoutEffect, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
|
|
||||||
import { type RowSelectionState } from '@/components/DataTable';
|
import { type RowSelectionState } from '@/components/DataTable';
|
||||||
import SelectedCounter from '@/components/Shared/SelectedCounter';
|
import SelectedCounter from '@/components/Shared/SelectedCounter';
|
||||||
|
@ -12,14 +11,20 @@ import RSListToolbar from './RSListToolbar';
|
||||||
import RSTable from './RSTable';
|
import RSTable from './RSTable';
|
||||||
|
|
||||||
interface EditorRSListProps {
|
interface EditorRSListProps {
|
||||||
|
isMutable: boolean
|
||||||
onOpenEdit: (cstID: number) => void
|
onOpenEdit: (cstID: number) => void
|
||||||
onTemplates: (insertAfter?: number) => void
|
onTemplates: (insertAfter?: number) => void
|
||||||
onCreateCst: (initial: ICstCreateData, skipDialog?: boolean) => void
|
onCreateCst: (initial: ICstCreateData, skipDialog?: boolean) => void
|
||||||
onDeleteCst: (selected: number[], callback: (items: number[]) => void) => void
|
onDeleteCst: (selected: number[], callback: (items: number[]) => void) => void
|
||||||
|
onReindex: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function EditorRSList({ onOpenEdit, onCreateCst, onDeleteCst, onTemplates }: EditorRSListProps) {
|
function EditorRSList({
|
||||||
const { schema, isMutable, cstMoveTo, resetAliases } = useRSForm();
|
isMutable,
|
||||||
|
onOpenEdit, onCreateCst,
|
||||||
|
onDeleteCst, onTemplates, onReindex
|
||||||
|
}: EditorRSListProps) {
|
||||||
|
const { schema, cstMoveTo } = useRSForm();
|
||||||
const [selected, setSelected] = useState<number[]>([]);
|
const [selected, setSelected] = useState<number[]>([]);
|
||||||
|
|
||||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
||||||
|
@ -107,11 +112,6 @@ function EditorRSList({ onOpenEdit, onCreateCst, onDeleteCst, onTemplates }: Edi
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate new names for all constituents
|
|
||||||
function handleReindex() {
|
|
||||||
resetAliases(() => toast.success('Имена конституент обновлены'));
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCreateCst(type?: CstType) {
|
function handleCreateCst(type?: CstType) {
|
||||||
if (!schema) {
|
if (!schema) {
|
||||||
return;
|
return;
|
||||||
|
@ -186,7 +186,7 @@ function EditorRSList({ onOpenEdit, onCreateCst, onDeleteCst, onTemplates }: Edi
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 'Backquote': handleCreateCst(); return true;
|
case 'Backquote': handleCreateCst(); return true;
|
||||||
case 'KeyE': onTemplates(); return true;
|
case 'KeyE': onTemplates(); return true;
|
||||||
case 'KeyR': handleReindex(); return true;
|
case 'KeyR': onReindex(); return true;
|
||||||
|
|
||||||
case 'Digit1': handleCreateCst(CstType.BASE); return true;
|
case 'Digit1': handleCreateCst(CstType.BASE); return true;
|
||||||
case 'Digit2': handleCreateCst(CstType.STRUCTURED); return true;
|
case 'Digit2': handleCreateCst(CstType.STRUCTURED); return true;
|
||||||
|
@ -214,7 +214,7 @@ function EditorRSList({ onOpenEdit, onCreateCst, onDeleteCst, onTemplates }: Edi
|
||||||
onCreate={handleCreateCst}
|
onCreate={handleCreateCst}
|
||||||
onDelete={handleDelete}
|
onDelete={handleDelete}
|
||||||
onTemplates={() => onTemplates(selected.length !== 0 ? selected[selected.length-1] : undefined)}
|
onTemplates={() => onTemplates(selected.length !== 0 ? selected[selected.length-1] : undefined)}
|
||||||
onReindex={handleReindex}
|
onReindex={onReindex}
|
||||||
/>
|
/>
|
||||||
<SelectedCounter
|
<SelectedCounter
|
||||||
total={schema?.stats?.count_all ?? 0}
|
total={schema?.stats?.count_all ?? 0}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
import { BiAnalyse, BiDiamond, BiDownArrowCircle, BiDownvote, BiDuplicate, BiPlusCircle, BiTrash, BiUpvote } from "react-icons/bi";
|
||||||
|
|
||||||
import Dropdown from '@/components/Common/Dropdown';
|
import Dropdown from '@/components/Common/Dropdown';
|
||||||
import DropdownButton from '@/components/Common/DropdownButton';
|
import DropdownButton from '@/components/Common/DropdownButton';
|
||||||
import MiniButton from '@/components/Common/MiniButton';
|
import MiniButton from '@/components/Common/MiniButton';
|
||||||
import Overlay from '@/components/Common/Overlay';
|
import Overlay from '@/components/Common/Overlay';
|
||||||
import HelpButton from '@/components/Help/HelpButton';
|
import HelpButton from '@/components/Help/HelpButton';
|
||||||
import { ArrowDownIcon, ArrowDropdownIcon, ArrowUpIcon, CloneIcon, DiamondIcon, DumpBinIcon, SmallPlusIcon, UpdateIcon } from '@/components/Icons';
|
|
||||||
import useDropdown from '@/hooks/useDropdown';
|
import useDropdown from '@/hooks/useDropdown';
|
||||||
import { HelpTopic } from '@/models/miscelanious';
|
import { HelpTopic } from '@/models/miscelanious';
|
||||||
import { CstType } from '@/models/rsform';
|
import { CstType } from '@/models/rsform';
|
||||||
|
@ -40,25 +40,25 @@ function RSListToolbar({
|
||||||
<Overlay position='w-full top-1 flex items-start justify-center'>
|
<Overlay position='w-full top-1 flex items-start justify-center'>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Переместить вверх [Alt + вверх]'
|
tooltip='Переместить вверх [Alt + вверх]'
|
||||||
icon={<ArrowUpIcon size={5}/>}
|
icon={<BiUpvote size='1.25rem'/>}
|
||||||
disabled={!isMutable || nothingSelected}
|
disabled={!isMutable || nothingSelected}
|
||||||
onClick={onMoveUp}
|
onClick={onMoveUp}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Переместить вниз [Alt + вниз]'
|
tooltip='Переместить вниз [Alt + вниз]'
|
||||||
icon={<ArrowDownIcon size={5}/>}
|
icon={<BiDownvote size='1.25rem'/>}
|
||||||
disabled={!isMutable || nothingSelected}
|
disabled={!isMutable || nothingSelected}
|
||||||
onClick={onMoveDown}
|
onClick={onMoveDown}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Клонировать конституенту [Alt + V]'
|
tooltip='Клонировать конституенту [Alt + V]'
|
||||||
icon={<CloneIcon color={isMutable && selectedCount === 1 ? 'clr-text-success': ''} size={5}/>}
|
icon={<BiDuplicate size='1.25rem' className={isMutable && selectedCount === 1 ? 'clr-text-success': ''} />}
|
||||||
disabled={!isMutable || selectedCount !== 1}
|
disabled={!isMutable || selectedCount !== 1}
|
||||||
onClick={onClone}
|
onClick={onClone}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Добавить новую конституенту... [Alt + `]'
|
tooltip='Добавить новую конституенту... [Alt + `]'
|
||||||
icon={<SmallPlusIcon color={isMutable ? 'clr-text-success': ''} size={5}/>}
|
icon={<BiPlusCircle size='1.25rem' className={isMutable ? 'clr-text-success': ''} />}
|
||||||
disabled={!isMutable}
|
disabled={!isMutable}
|
||||||
onClick={() => onCreate()}
|
onClick={() => onCreate()}
|
||||||
/>
|
/>
|
||||||
|
@ -66,42 +66,39 @@ function RSListToolbar({
|
||||||
<div>
|
<div>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Добавить пустую конституенту'
|
tooltip='Добавить пустую конституенту'
|
||||||
icon={<ArrowDropdownIcon color={isMutable ? 'clr-text-success': ''} size={5}/>}
|
icon={<BiDownArrowCircle size='1.25rem' className={isMutable ? 'clr-text-success': ''} />}
|
||||||
disabled={!isMutable}
|
disabled={!isMutable}
|
||||||
onClick={insertMenu.toggle}
|
onClick={insertMenu.toggle}
|
||||||
/>
|
/>
|
||||||
{insertMenu.isActive ?
|
{insertMenu.isActive ?
|
||||||
<Dropdown>
|
<Dropdown>
|
||||||
{(Object.values(CstType)).map(
|
{(Object.values(CstType)).map(
|
||||||
(typeStr) => {
|
(typeStr) =>
|
||||||
const type = typeStr as CstType;
|
|
||||||
return (
|
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
key={`${prefixes.csttype_list}${typeStr}`}
|
key={`${prefixes.csttype_list}${typeStr}`}
|
||||||
onClick={() => onCreate(type)}
|
text={`${getCstTypePrefix(typeStr as CstType)}1 — ${labelCstType(typeStr as CstType)}`}
|
||||||
tooltip={getCstTypeShortcut(type)}
|
onClick={() => onCreate(typeStr as CstType)}
|
||||||
>
|
tooltip={getCstTypeShortcut(typeStr as CstType)}
|
||||||
{`${getCstTypePrefix(type)}1 — ${labelCstType(type)}`}
|
/>
|
||||||
</DropdownButton>);
|
)}
|
||||||
})}
|
|
||||||
</Dropdown> : null}
|
</Dropdown> : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Создать конституенту из шаблона [Alt + E]'
|
tooltip='Создать конституенту из шаблона [Alt + E]'
|
||||||
icon={<DiamondIcon color={isMutable ? 'clr-text-primary': ''} size={5}/>}
|
icon={<BiDiamond size='1.25rem' className={isMutable ? 'clr-text-primary': ''} />}
|
||||||
disabled={!isMutable}
|
disabled={!isMutable}
|
||||||
onClick={onTemplates}
|
onClick={onTemplates}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Сброс имен: присвоить порядковые имена [Alt + R]'
|
tooltip='Сброс имён: присвоить порядковые имена [Alt + R]'
|
||||||
icon={<UpdateIcon color={isMutable ? 'clr-text-primary': ''} size={5}/>}
|
icon={<BiAnalyse size='1.25rem' className={isMutable ? 'clr-text-primary': ''} />}
|
||||||
disabled={!isMutable}
|
disabled={!isMutable}
|
||||||
onClick={onReindex}
|
onClick={onReindex}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Удалить выбранные [Delete]'
|
tooltip='Удалить выбранные [Delete]'
|
||||||
icon={<DumpBinIcon color={isMutable && !nothingSelected ? 'clr-text-warning' : ''} size={5}/>}
|
icon={<BiTrash size='1.25rem' className={isMutable && !nothingSelected ? 'clr-text-warning' : ''} />}
|
||||||
disabled={!isMutable || nothingSelected}
|
disabled={!isMutable || nothingSelected}
|
||||||
onClick={onDelete}
|
onClick={onDelete}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -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,RowSelectionState,VisibilityState } from '@/components/DataTable';
|
import DataTable, { createColumnHelper,RowSelectionState,VisibilityState } from '@/components/DataTable';
|
||||||
|
@ -71,7 +72,6 @@ function RSTable({
|
||||||
theme={colors}
|
theme={colors}
|
||||||
value={props.row.original}
|
value={props.row.original}
|
||||||
prefixID={prefixes.cst_list}
|
prefixID={prefixes.cst_list}
|
||||||
shortTooltip
|
|
||||||
/>
|
/>
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor(cst => labelCstTypification(cst), {
|
columnHelper.accessor(cst => labelCstTypification(cst), {
|
||||||
|
@ -125,7 +125,15 @@ function RSTable({
|
||||||
}, [noNavigation]);
|
}, [noNavigation]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full h-full overflow-auto text-sm min-h-[20rem]' style={{maxHeight: tableHeight}}>
|
<div
|
||||||
|
className={clsx(
|
||||||
|
'w-full h-full min-h-[20rem]',
|
||||||
|
'overflow-auto',
|
||||||
|
'text-sm',
|
||||||
|
'select-none'
|
||||||
|
)}
|
||||||
|
style={{maxHeight: tableHeight}}
|
||||||
|
>
|
||||||
<DataTable dense noFooter
|
<DataTable dense noFooter
|
||||||
data={items ?? []}
|
data={items ?? []}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
|
|
@ -23,13 +23,14 @@ import useGraphFilter from './useGraphFilter';
|
||||||
import ViewHidden from './ViewHidden';
|
import ViewHidden from './ViewHidden';
|
||||||
|
|
||||||
interface EditorTermGraphProps {
|
interface EditorTermGraphProps {
|
||||||
|
isMutable: boolean
|
||||||
onOpenEdit: (cstID: number) => void
|
onOpenEdit: (cstID: number) => void
|
||||||
onCreateCst: (initial: ICstCreateData, skipDialog?: boolean) => void
|
onCreateCst: (initial: ICstCreateData, skipDialog?: boolean) => void
|
||||||
onDeleteCst: (selected: number[], callback: (items: number[]) => void) => void
|
onDeleteCst: (selected: number[], callback: (items: number[]) => void) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGraphProps) {
|
function EditorTermGraph({ isMutable, onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGraphProps) {
|
||||||
const { schema, isMutable } = useRSForm();
|
const { schema } = useRSForm();
|
||||||
const { colors } = useConceptTheme();
|
const { colors } = useConceptTheme();
|
||||||
|
|
||||||
const [toggleDataUpdate, setToggleDataUpdate] = useState(false);
|
const [toggleDataUpdate, setToggleDataUpdate] = useState(false);
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { BiCollapse, BiFilterAlt, BiPlanet, BiPlusCircle, BiTrash } from 'react-icons/bi';
|
||||||
|
|
||||||
import MiniButton from '@/components/Common/MiniButton';
|
import MiniButton from '@/components/Common/MiniButton';
|
||||||
import Overlay from '@/components/Common/Overlay';
|
import Overlay from '@/components/Common/Overlay';
|
||||||
import HelpButton from '@/components/Help/HelpButton';
|
import HelpButton from '@/components/Help/HelpButton';
|
||||||
import { ArrowsFocusIcon, DumpBinIcon, FilterIcon, LetterAIcon, LetterALinesIcon, PlanetIcon, SmallPlusIcon } from '@/components/Icons';
|
import { LetterAIcon, LetterALinesIcon } from '@/components/Icons';
|
||||||
import { HelpTopic } from '@/models/miscelanious';
|
import { HelpTopic } from '@/models/miscelanious';
|
||||||
|
|
||||||
interface GraphToolbarProps {
|
interface GraphToolbarProps {
|
||||||
|
@ -34,37 +36,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='clr-text-primary' size={5} />}
|
icon={<BiFilterAlt size='1.25rem' className='clr-text-primary' />}
|
||||||
onClick={showParamsDialog}
|
onClick={showParamsDialog}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip={!noText ? 'Скрыть текст' : 'Отобразить текст'}
|
tooltip={!noText ? 'Скрыть текст' : 'Отобразить текст'}
|
||||||
icon={
|
icon={
|
||||||
!noText
|
!noText
|
||||||
? <LetterALinesIcon color='clr-text-success' size={5} />
|
? <LetterALinesIcon size='1.25rem' className='clr-text-success' />
|
||||||
: <LetterAIcon color='clr-text-primary' size={5} />
|
: <LetterAIcon size='1.25rem' className='clr-text-primary' />
|
||||||
}
|
}
|
||||||
onClick={toggleNoText}
|
onClick={toggleNoText}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Новая конституента'
|
tooltip='Новая конституента'
|
||||||
icon={<SmallPlusIcon color={isMutable ? 'clr-text-success' : ''} size={5} />}
|
icon={<BiPlusCircle size='1.25rem' className={isMutable ? 'clr-text-success' : ''} />}
|
||||||
disabled={!isMutable}
|
disabled={!isMutable}
|
||||||
onClick={onCreate}
|
onClick={onCreate}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Удалить выбранные'
|
tooltip='Удалить выбранные'
|
||||||
icon={<DumpBinIcon color={isMutable && !nothingSelected ? 'clr-text-warning' : ''} size={5} />}
|
icon={<BiTrash size='1.25rem' className={isMutable && !nothingSelected ? 'clr-text-warning' : ''} />}
|
||||||
disabled={!isMutable || nothingSelected}
|
disabled={!isMutable || nothingSelected}
|
||||||
onClick={onDelete}
|
onClick={onDelete}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
icon={<ArrowsFocusIcon color='clr-text-primary' size={5} />}
|
icon={<BiCollapse size='1.25rem' className='clr-text-primary' />}
|
||||||
tooltip='Восстановить камеру'
|
tooltip='Восстановить камеру'
|
||||||
onClick={onResetViewpoint}
|
onClick={onResetViewpoint}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
icon={<PlanetIcon color={!is3D ? '' : orbit ? 'clr-text-success' : 'clr-text-primary'} size={5} />}
|
icon={<BiPlanet size='1.25rem' className={!is3D ? '' : orbit ? 'clr-text-success' : 'clr-text-primary'} />}
|
||||||
tooltip='Анимация вращения'
|
tooltip='Анимация вращения'
|
||||||
disabled={!is3D}
|
disabled={!is3D}
|
||||||
onClick={toggleOrbit}
|
onClick={toggleOrbit}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { AccessModeState } from '@/context/AccessModeContext';
|
||||||
import { RSFormState } from '@/context/RSFormContext';
|
import { RSFormState } from '@/context/RSFormContext';
|
||||||
|
|
||||||
import RSTabs from './RSTabs';
|
import RSTabs from './RSTabs';
|
||||||
|
@ -9,9 +10,11 @@ import RSTabs from './RSTabs';
|
||||||
function RSFormPage() {
|
function RSFormPage() {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
return (
|
return (
|
||||||
|
<AccessModeState>
|
||||||
<RSFormState schemaID={params.id ?? ''}>
|
<RSFormState schemaID={params.id ?? ''}>
|
||||||
<RSTabs />
|
<RSTabs />
|
||||||
</RSFormState>);
|
</RSFormState>
|
||||||
|
</AccessModeState>);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RSFormPage;
|
export default RSFormPage;
|
|
@ -11,6 +11,8 @@ import { ConceptLoader } from '@/components/Common/ConceptLoader';
|
||||||
import ConceptTab from '@/components/Common/ConceptTab';
|
import ConceptTab from '@/components/Common/ConceptTab';
|
||||||
import TextURL from '@/components/Common/TextURL';
|
import TextURL from '@/components/Common/TextURL';
|
||||||
import InfoError, { ErrorData } from '@/components/InfoError';
|
import InfoError, { ErrorData } from '@/components/InfoError';
|
||||||
|
import { useAccessMode } from '@/context/AccessModeContext';
|
||||||
|
import { useAuth } from '@/context/AuthContext';
|
||||||
import { useLibrary } from '@/context/LibraryContext';
|
import { useLibrary } from '@/context/LibraryContext';
|
||||||
import { useBlockNavigation, useConceptNavigation } from '@/context/NagivationContext';
|
import { useBlockNavigation, useConceptNavigation } from '@/context/NagivationContext';
|
||||||
import { useRSForm } from '@/context/RSFormContext';
|
import { useRSForm } from '@/context/RSFormContext';
|
||||||
|
@ -23,6 +25,7 @@ import DlgEditWordForms from '@/dialogs/DlgEditWordForms';
|
||||||
import DlgRenameCst from '@/dialogs/DlgRenameCst';
|
import DlgRenameCst from '@/dialogs/DlgRenameCst';
|
||||||
import DlgUploadRSForm from '@/dialogs/DlgUploadRSForm';
|
import DlgUploadRSForm from '@/dialogs/DlgUploadRSForm';
|
||||||
import useQueryStrings from '@/hooks/useQueryStrings';
|
import useQueryStrings from '@/hooks/useQueryStrings';
|
||||||
|
import { UserAccessMode } from '@/models/miscelanious';
|
||||||
import { IConstituenta, ICstCreateData, ICstRenameData, ICstUpdateData, TermForm } from '@/models/rsform';
|
import { IConstituenta, ICstCreateData, ICstRenameData, ICstUpdateData, TermForm } from '@/models/rsform';
|
||||||
import { EXTEOR_TRS_FILE, prefixes, TIMEOUT_UI_REFRESH } from '@/utils/constants';
|
import { EXTEOR_TRS_FILE, prefixes, TIMEOUT_UI_REFRESH } from '@/utils/constants';
|
||||||
import { createAliasFor } from '@/utils/misc';
|
import { createAliasFor } from '@/utils/misc';
|
||||||
|
@ -56,20 +59,30 @@ function ProcessError({error}: {error: ErrorData}): React.ReactElement {
|
||||||
function RSTabs() {
|
function RSTabs() {
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
const query = useQueryStrings();
|
const query = useQueryStrings();
|
||||||
const tabQuery = (Number(query.get('tab')) ?? RSTabID.CARD) as RSTabID;
|
const activeTab = (Number(query.get('tab')) ?? RSTabID.CARD) as RSTabID;
|
||||||
const cstQuery = query.get('active');
|
const cstQuery = query.get('active');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
error, schema, loading, claim, download, isTracking,
|
error, schema, loading, processing, isOwned,
|
||||||
cstCreate, cstDelete, cstRename, subscribe, unsubscribe, cstUpdate
|
claim, download, isSubscribed,
|
||||||
|
cstCreate, cstDelete, cstRename, subscribe, unsubscribe, cstUpdate, resetAliases
|
||||||
} = useRSForm();
|
} = useRSForm();
|
||||||
const { destroyItem } = useLibrary();
|
const { destroyItem } = useLibrary();
|
||||||
const { setNoFooter, noNavigation } = useConceptTheme();
|
const { setNoFooter, noNavigation } = useConceptTheme();
|
||||||
|
const { user } = useAuth();
|
||||||
|
const { mode, setMode } = useAccessMode();
|
||||||
|
|
||||||
const [isModified, setIsModified] = useState(false);
|
const [isModified, setIsModified] = useState(false);
|
||||||
useBlockNavigation(isModified);
|
useBlockNavigation(isModified);
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState(RSTabID.CARD);
|
const isMutable = useMemo(
|
||||||
|
() => {
|
||||||
|
return (
|
||||||
|
!loading && !processing && mode !== UserAccessMode.READER &&
|
||||||
|
((isOwned || (mode === UserAccessMode.ADMIN && user?.is_staff)) ?? false)
|
||||||
|
);
|
||||||
|
}, [user?.is_staff, mode, isOwned, loading, processing]);
|
||||||
|
|
||||||
const [activeID, setActiveID] = useState<number | undefined>(undefined);
|
const [activeID, setActiveID] = useState<number | undefined>(undefined);
|
||||||
const activeCst = useMemo(
|
const activeCst = useMemo(
|
||||||
() => schema?.items?.find(cst => cst.id === activeID)
|
() => schema?.items?.find(cst => cst.id === activeID)
|
||||||
|
@ -111,12 +124,22 @@ function RSTabs() {
|
||||||
}, [schema, schema?.title]);
|
}, [schema, schema?.title]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
setActiveTab(tabQuery);
|
setNoFooter(activeTab === RSTabID.CST_EDIT || activeTab === RSTabID.CST_LIST);
|
||||||
setNoFooter(tabQuery === RSTabID.CST_EDIT || tabQuery === RSTabID.CST_LIST);
|
|
||||||
setActiveID(Number(cstQuery) ?? ((schema && schema?.items.length > 0) ? schema.items[0].id : undefined));
|
setActiveID(Number(cstQuery) ?? ((schema && schema?.items.length > 0) ? schema.items[0].id : undefined));
|
||||||
setIsModified(false);
|
setIsModified(false);
|
||||||
return () => setNoFooter(false);
|
return () => setNoFooter(false);
|
||||||
}, [tabQuery, cstQuery, setActiveTab, setActiveID, schema, setNoFooter, setIsModified]);
|
}, [activeTab, cstQuery, setActiveID, schema, setNoFooter, setIsModified]);
|
||||||
|
|
||||||
|
useLayoutEffect(
|
||||||
|
() => setMode((prev) => {
|
||||||
|
if (prev === UserAccessMode.ADMIN) {
|
||||||
|
return prev;
|
||||||
|
} else if(isOwned) {
|
||||||
|
return UserAccessMode.OWNER;
|
||||||
|
} else {
|
||||||
|
return UserAccessMode.READER;
|
||||||
|
}
|
||||||
|
}), [schema, setMode, isOwned]);
|
||||||
|
|
||||||
function onSelectTab(index: number) {
|
function onSelectTab(index: number) {
|
||||||
navigateTab(index, activeID);
|
navigateTab(index, activeID);
|
||||||
|
@ -186,6 +209,11 @@ function RSTabs() {
|
||||||
setShowRenameCst(true);
|
setShowRenameCst(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const onReindex = useCallback(
|
||||||
|
() => resetAliases(
|
||||||
|
() => toast.success('Имена конституент обновлены')
|
||||||
|
), [resetAliases]);
|
||||||
|
|
||||||
const handleDeleteCst = useCallback(
|
const handleDeleteCst = useCallback(
|
||||||
(deleted: number[]) => {
|
(deleted: number[]) => {
|
||||||
if (!schema) {
|
if (!schema) {
|
||||||
|
@ -290,12 +318,12 @@ function RSTabs() {
|
||||||
|
|
||||||
const handleToggleSubscribe = useCallback(
|
const handleToggleSubscribe = useCallback(
|
||||||
() => {
|
() => {
|
||||||
if (isTracking) {
|
if (isSubscribed) {
|
||||||
unsubscribe(() => toast.success('Отслеживание отключено'));
|
unsubscribe(() => toast.success('Отслеживание отключено'));
|
||||||
} else {
|
} else {
|
||||||
subscribe(() => toast.success('Отслеживание включено'));
|
subscribe(() => toast.success('Отслеживание включено'));
|
||||||
}
|
}
|
||||||
}, [isTracking, subscribe, unsubscribe]);
|
}, [isSubscribed, subscribe, unsubscribe]);
|
||||||
|
|
||||||
const promptShowEditTerm = useCallback(
|
const promptShowEditTerm = useCallback(
|
||||||
() => {
|
() => {
|
||||||
|
@ -383,12 +411,13 @@ function RSTabs() {
|
||||||
'flex justify-stretch',
|
'flex justify-stretch',
|
||||||
'border-b-2 border-x-2 divide-x-2'
|
'border-b-2 border-x-2 divide-x-2'
|
||||||
)}>
|
)}>
|
||||||
<RSTabsMenu
|
<RSTabsMenu isMutable={isMutable}
|
||||||
|
onTemplates={onShowTemplates}
|
||||||
onDownload={onDownloadSchema}
|
onDownload={onDownloadSchema}
|
||||||
onDestroy={onDestroySchema}
|
onDestroy={onDestroySchema}
|
||||||
onClaim={onClaimSchema}
|
onClaim={onClaimSchema}
|
||||||
onShare={onShareSchema}
|
onShare={onShareSchema}
|
||||||
onToggleSubscribe={handleToggleSubscribe}
|
onReindex={onReindex}
|
||||||
showCloneDialog={promptClone}
|
showCloneDialog={promptClone}
|
||||||
showUploadDialog={() => setShowUpload(true)}
|
showUploadDialog={() => setShowUpload(true)}
|
||||||
/>
|
/>
|
||||||
|
@ -418,8 +447,10 @@ function RSTabs() {
|
||||||
>
|
>
|
||||||
<TabPanel forceRender style={{ display: activeTab === RSTabID.CARD ? '': 'none' }}>
|
<TabPanel forceRender style={{ display: activeTab === RSTabID.CARD ? '': 'none' }}>
|
||||||
<EditorRSForm
|
<EditorRSForm
|
||||||
|
isMutable={isMutable}
|
||||||
isModified={isModified}
|
isModified={isModified}
|
||||||
setIsModified={setIsModified}
|
setIsModified={setIsModified}
|
||||||
|
onToggleSubscribe={handleToggleSubscribe}
|
||||||
onDownload={onDownloadSchema}
|
onDownload={onDownloadSchema}
|
||||||
onDestroy={onDestroySchema}
|
onDestroy={onDestroySchema}
|
||||||
onClaim={onClaimSchema}
|
onClaim={onClaimSchema}
|
||||||
|
@ -429,15 +460,18 @@ function RSTabs() {
|
||||||
|
|
||||||
<TabPanel forceRender style={{ display: activeTab === RSTabID.CST_LIST ? '': 'none' }}>
|
<TabPanel forceRender style={{ display: activeTab === RSTabID.CST_LIST ? '': 'none' }}>
|
||||||
<EditorRSList
|
<EditorRSList
|
||||||
|
isMutable={isMutable}
|
||||||
onOpenEdit={onOpenCst}
|
onOpenEdit={onOpenCst}
|
||||||
onCreateCst={promptCreateCst}
|
onCreateCst={promptCreateCst}
|
||||||
onDeleteCst={promptDeleteCst}
|
onDeleteCst={promptDeleteCst}
|
||||||
onTemplates={onShowTemplates}
|
onTemplates={onShowTemplates}
|
||||||
|
onReindex={onReindex}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
<TabPanel forceRender style={{ display: activeTab === RSTabID.CST_EDIT ? '': 'none' }}>
|
<TabPanel forceRender style={{ display: activeTab === RSTabID.CST_EDIT ? '': 'none' }}>
|
||||||
<EditorConstituenta
|
<EditorConstituenta
|
||||||
|
isMutable={isMutable}
|
||||||
isModified={isModified}
|
isModified={isModified}
|
||||||
setIsModified={setIsModified}
|
setIsModified={setIsModified}
|
||||||
activeID={activeID}
|
activeID={activeID}
|
||||||
|
@ -453,6 +487,7 @@ function RSTabs() {
|
||||||
|
|
||||||
<TabPanel style={{ display: activeTab === RSTabID.TERM_GRAPH ? '': 'none' }}>
|
<TabPanel style={{ display: activeTab === RSTabID.TERM_GRAPH ? '': 'none' }}>
|
||||||
<EditorTermGraph
|
<EditorTermGraph
|
||||||
|
isMutable={isMutable}
|
||||||
onOpenEdit={onOpenCst}
|
onOpenEdit={onOpenCst}
|
||||||
onCreateCst={promptCreateCst}
|
onCreateCst={promptCreateCst}
|
||||||
onDeleteCst={promptDeleteCst}
|
onDeleteCst={promptDeleteCst}
|
||||||
|
|
|
@ -1,19 +1,24 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { BiAnalyse, BiDiamond, BiDownload, BiDuplicate, BiMenu, BiMeteor, BiPlusCircle, BiTrash, BiUpload } from 'react-icons/bi';
|
||||||
|
import { FiEdit } from 'react-icons/fi';
|
||||||
|
import { LuCrown, LuGlasses } from 'react-icons/lu';
|
||||||
|
|
||||||
import Button from '@/components/Common/Button';
|
import Button from '@/components/Common/Button';
|
||||||
import Dropdown from '@/components/Common/Dropdown';
|
import Dropdown from '@/components/Common/Dropdown';
|
||||||
import DropdownButton from '@/components/Common/DropdownButton';
|
import DropdownButton from '@/components/Common/DropdownButton';
|
||||||
import DropdownCheckbox from '@/components/Common/DropdownCheckbox';
|
import { ShareIcon } from '@/components/Icons';
|
||||||
import {
|
import { useAccessMode } from '@/context/AccessModeContext';
|
||||||
CloneIcon, DownloadIcon, DumpBinIcon, EditIcon, MenuIcon, NotSubscribedIcon,
|
|
||||||
OwnerIcon, ShareIcon, SmallPlusIcon, SubscribedIcon, UploadIcon
|
|
||||||
} from '@/components/Icons';
|
|
||||||
import { useAuth } from '@/context/AuthContext';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
import { useConceptNavigation } from '@/context/NagivationContext';
|
import { useConceptNavigation } from '@/context/NagivationContext';
|
||||||
import { useRSForm } from '@/context/RSFormContext';
|
import { useRSForm } from '@/context/RSFormContext';
|
||||||
import useDropdown from '@/hooks/useDropdown';
|
import useDropdown from '@/hooks/useDropdown';
|
||||||
|
import { UserAccessMode } from '@/models/miscelanious';
|
||||||
|
import { describeAccessMode, labelAccessMode } from '@/utils/labels';
|
||||||
|
|
||||||
interface RSTabsMenuProps {
|
interface RSTabsMenuProps {
|
||||||
|
isMutable: boolean
|
||||||
|
|
||||||
showUploadDialog: () => void
|
showUploadDialog: () => void
|
||||||
showCloneDialog: () => void
|
showCloneDialog: () => void
|
||||||
|
|
||||||
|
@ -21,21 +26,25 @@ interface RSTabsMenuProps {
|
||||||
onClaim: () => void
|
onClaim: () => void
|
||||||
onShare: () => void
|
onShare: () => void
|
||||||
onDownload: () => void
|
onDownload: () => void
|
||||||
onToggleSubscribe: () => void
|
onReindex: () => void
|
||||||
|
onTemplates: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function RSTabsMenu({
|
function RSTabsMenu({
|
||||||
|
isMutable,
|
||||||
showUploadDialog, showCloneDialog,
|
showUploadDialog, showCloneDialog,
|
||||||
onDestroy, onShare, onDownload, onClaim, onToggleSubscribe
|
onDestroy, onShare, onDownload,
|
||||||
|
onClaim, onReindex, onTemplates
|
||||||
}: RSTabsMenuProps) {
|
}: RSTabsMenuProps) {
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const {
|
const { isOwned, isClaimable } = useRSForm();
|
||||||
isOwned, isMutable, isTracking, readerMode, isClaimable, adminMode,
|
|
||||||
toggleAdminMode, toggleReaderMode, processing
|
const { mode, setMode } = useAccessMode();
|
||||||
} = useRSForm();
|
|
||||||
const schemaMenu = useDropdown();
|
const schemaMenu = useDropdown();
|
||||||
const editMenu = useDropdown();
|
const editMenu = useDropdown();
|
||||||
|
const accessMenu = useDropdown();
|
||||||
|
|
||||||
function handleClaimOwner() {
|
function handleClaimOwner() {
|
||||||
editMenu.hide();
|
editMenu.hide();
|
||||||
|
@ -67,6 +76,21 @@ function RSTabsMenu({
|
||||||
onShare();
|
onShare();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleReindex() {
|
||||||
|
editMenu.hide();
|
||||||
|
onReindex();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTemplates() {
|
||||||
|
editMenu.hide();
|
||||||
|
onTemplates();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChangeMode(newMode: UserAccessMode) {
|
||||||
|
accessMenu.hide();
|
||||||
|
setMode(newMode);
|
||||||
|
}
|
||||||
|
|
||||||
function handleCreateNew() {
|
function handleCreateNew() {
|
||||||
router.push('/rsform-create');
|
router.push('/rsform-create');
|
||||||
}
|
}
|
||||||
|
@ -75,105 +99,111 @@ function RSTabsMenu({
|
||||||
<div className='flex items-stretch h-full w-fit'>
|
<div className='flex items-stretch h-full w-fit'>
|
||||||
<div ref={schemaMenu.ref}>
|
<div ref={schemaMenu.ref}>
|
||||||
<Button noBorder dense tabIndex={-1}
|
<Button noBorder dense tabIndex={-1}
|
||||||
tooltip='Действия'
|
tooltip='Меню'
|
||||||
icon={<MenuIcon color='clr-text-controls' size={5}/>}
|
icon={<BiMenu size='1.25rem' className='clr-text-controls' />}
|
||||||
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}
|
||||||
/>
|
/>
|
||||||
{schemaMenu.isActive ?
|
{schemaMenu.isActive ?
|
||||||
<Dropdown>
|
<Dropdown>
|
||||||
<DropdownButton onClick={handleShare}>
|
<DropdownButton
|
||||||
<div className='inline-flex items-center justify-start gap-2'>
|
text={isOwned ? 'Вы — владелец' : 'Стать владельцем'}
|
||||||
<ShareIcon color='clr-text-primary' size={4}/>
|
tooltip={!user || !isClaimable ? 'Взять во владение можно общую изменяемую схему' : ''}
|
||||||
<p>Поделиться</p>
|
icon={<LuCrown size='1rem' className={isOwned ? 'clr-text-success' : 'clr-text-controls'} />}
|
||||||
</div>
|
onClick={(!isOwned && user && isClaimable) ? handleClaimOwner : undefined}
|
||||||
</DropdownButton>
|
/>
|
||||||
<DropdownButton onClick={handleClone} disabled={!user} >
|
<DropdownButton
|
||||||
<div className='inline-flex items-center justify-start gap-2'>
|
text='Поделиться'
|
||||||
<CloneIcon color='clr-text-primary' size={4}/>
|
icon={<ShareIcon size='1rem' className='clr-text-primary' />}
|
||||||
<p>Клонировать</p>
|
onClick={handleShare}
|
||||||
</div>
|
/>
|
||||||
</DropdownButton>
|
<DropdownButton disabled={!user}
|
||||||
<DropdownButton onClick={handleDownload}>
|
text='Клонировать'
|
||||||
<div className='inline-flex items-center justify-start gap-2'>
|
icon={<BiDuplicate size='1rem' className='clr-text-primary' />}
|
||||||
<DownloadIcon color='clr-text-primary' size={4}/>
|
onClick={handleClone}
|
||||||
<p>Выгрузить в Экстеор</p>
|
/>
|
||||||
</div>
|
<DropdownButton
|
||||||
</DropdownButton>
|
text='Выгрузить в Экстеор'
|
||||||
<DropdownButton disabled={!isMutable} onClick={handleUpload}>
|
icon={<BiDownload size='1rem' className='clr-text-primary'/>}
|
||||||
<div className='inline-flex items-center justify-start gap-2'>
|
onClick={handleDownload}
|
||||||
<UploadIcon color={isMutable ? 'clr-text-warning' : ''} size={4}/>
|
/>
|
||||||
<p>Загрузить из Экстеора</p>
|
<DropdownButton disabled={!isMutable}
|
||||||
</div>
|
text='Загрузить из Экстеора'
|
||||||
</DropdownButton>
|
icon={<BiUpload size='1rem' className={isMutable ? 'clr-text-warning' : ''} />}
|
||||||
<DropdownButton disabled={!isMutable} onClick={handleDelete}>
|
onClick={handleUpload}
|
||||||
<span className='inline-flex items-center justify-start gap-2'>
|
/>
|
||||||
<DumpBinIcon color={isMutable ? 'clr-text-warning' : ''} size={4} />
|
<DropdownButton disabled={!isMutable}
|
||||||
<p>Удалить схему</p>
|
text='Удалить схему'
|
||||||
</span>
|
icon={<BiTrash size='1rem' className={isMutable ? 'clr-text-warning' : ''} />}
|
||||||
</DropdownButton>
|
onClick={handleDelete}
|
||||||
<DropdownButton onClick={handleCreateNew}>
|
/>
|
||||||
<span className='inline-flex items-center justify-start gap-2'>
|
<DropdownButton
|
||||||
<SmallPlusIcon color='clr-text-url' size={4} />
|
text='Создать новую схему'
|
||||||
<p>Создать новую схему</p>
|
icon={<BiPlusCircle size='1rem' className='clr-text-url' />}
|
||||||
</span>
|
onClick={handleCreateNew}
|
||||||
</DropdownButton>
|
/>
|
||||||
</Dropdown> : null}
|
</Dropdown> : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ref={editMenu.ref}>
|
<div ref={editMenu.ref}>
|
||||||
<Button dense noBorder tabIndex={-1}
|
<Button dense noBorder tabIndex={-1}
|
||||||
tooltip={'измнение: ' + (isMutable ? '[доступно]' : '[запрещено]')}
|
tooltip={'Редактирование'}
|
||||||
dimensions='h-full w-fit'
|
dimensions='h-full w-fit'
|
||||||
style={{outlineColor: 'transparent'}}
|
style={{outlineColor: 'transparent'}}
|
||||||
icon={<EditIcon size={5} color={isMutable ? 'clr-text-success' : 'clr-text-warning'}/>}
|
icon={<FiEdit size='1.25rem' className={isMutable ? 'clr-text-success' : 'clr-text-warning'}/>}
|
||||||
onClick={editMenu.toggle}
|
onClick={editMenu.toggle}
|
||||||
/>
|
/>
|
||||||
{editMenu.isActive ?
|
{editMenu.isActive ?
|
||||||
<Dropdown>
|
<Dropdown>
|
||||||
<DropdownButton
|
<DropdownButton disabled={!isMutable}
|
||||||
disabled={!user || !isClaimable}
|
text='Сброс имён'
|
||||||
onClick={!isOwned ? handleClaimOwner : undefined}
|
tooltip='Присвоить порядковые имена и обновить выражения'
|
||||||
tooltip={!user || !isClaimable ? 'Взять во владение можно общую изменяемую схему' : ''}
|
icon={<BiAnalyse size='1rem' className={isMutable ? 'clr-text-primary': ''} />}
|
||||||
>
|
onClick={handleReindex}
|
||||||
<div className='flex items-center gap-2 clr-text-default'>
|
/>
|
||||||
<span>
|
<DropdownButton disabled={!isMutable}
|
||||||
<OwnerIcon size={4} color={isOwned ? 'clr-text-success' : 'clr-text-controls'} />
|
text='Банк выражений'
|
||||||
</span>
|
tooltip='Создать конституенту из шаблона'
|
||||||
<div>
|
icon={<BiDiamond size='1rem' className={isMutable ? 'clr-text-success': ''} />}
|
||||||
{isOwned ? <b className='clr-text-default'>Вы — владелец</b> : null}
|
onClick={handleTemplates}
|
||||||
{!isOwned ? <b>Стать владельцем</b> : null}
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DropdownButton>
|
|
||||||
{(isOwned || user?.is_staff) ?
|
|
||||||
<DropdownCheckbox
|
|
||||||
value={readerMode}
|
|
||||||
setValue={toggleReaderMode}
|
|
||||||
label='Я — читатель!'
|
|
||||||
tooltip='Режим чтения'
|
|
||||||
/> : null}
|
|
||||||
{user?.is_staff ?
|
|
||||||
<DropdownCheckbox
|
|
||||||
value={adminMode}
|
|
||||||
setValue={toggleAdminMode}
|
|
||||||
label='Я — администратор!'
|
|
||||||
tooltip='Режим редактирования для администраторов'
|
|
||||||
/> : null}
|
|
||||||
</Dropdown>: null}
|
</Dropdown>: null}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
|
<div ref={accessMenu.ref}>
|
||||||
<Button dense noBorder tabIndex={-1}
|
<Button dense noBorder tabIndex={-1}
|
||||||
tooltip={'отслеживание: ' + (isTracking ? '[включено]' : '[выключено]')}
|
tooltip={`режим ${labelAccessMode(mode)}`}
|
||||||
disabled={processing}
|
|
||||||
icon={isTracking
|
|
||||||
? <SubscribedIcon color='clr-text-primary' 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'}}
|
||||||
onClick={onToggleSubscribe}
|
icon={
|
||||||
|
mode === UserAccessMode.ADMIN ? <BiMeteor size='1.25rem' className='clr-text-primary'/>
|
||||||
|
: mode === UserAccessMode.OWNER ? <LuCrown size='1.25rem' className='clr-text-primary'/>
|
||||||
|
: <LuGlasses size='1.25rem' className='clr-text-primary'/>
|
||||||
|
}
|
||||||
|
onClick={accessMenu.toggle}
|
||||||
|
/>
|
||||||
|
{accessMenu.isActive ?
|
||||||
|
<Dropdown>
|
||||||
|
<DropdownButton
|
||||||
|
text={labelAccessMode(UserAccessMode.READER)}
|
||||||
|
tooltip={describeAccessMode(UserAccessMode.READER)}
|
||||||
|
icon={<LuGlasses size='1rem' className='clr-text-primary' />}
|
||||||
|
onClick={() => handleChangeMode(UserAccessMode.READER)}
|
||||||
/>
|
/>
|
||||||
|
<DropdownButton disabled={!isOwned}
|
||||||
|
text={labelAccessMode(UserAccessMode.OWNER)}
|
||||||
|
tooltip={describeAccessMode(UserAccessMode.OWNER)}
|
||||||
|
icon={<LuCrown size='1rem' className={isOwned ? 'clr-text-primary': ''} />}
|
||||||
|
onClick={() => handleChangeMode(UserAccessMode.OWNER)}
|
||||||
|
/>
|
||||||
|
<DropdownButton disabled={!user?.is_staff}
|
||||||
|
text={labelAccessMode(UserAccessMode.ADMIN)}
|
||||||
|
tooltip={describeAccessMode(UserAccessMode.ADMIN)}
|
||||||
|
icon={<BiMeteor size='1rem' className={user?.is_staff ? 'clr-text-primary': ''} />}
|
||||||
|
onClick={() => handleChangeMode(UserAccessMode.ADMIN)}
|
||||||
|
/>
|
||||||
|
</Dropdown>: null}
|
||||||
</div>
|
</div>
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useCallback, useLayoutEffect } from 'react';
|
import { useCallback, useLayoutEffect } from 'react';
|
||||||
|
import { BiCog, BiFilterAlt } from 'react-icons/bi';
|
||||||
|
|
||||||
import ConceptSearch from '@/components/Common/ConceptSearch';
|
import ConceptSearch from '@/components/Common/ConceptSearch';
|
||||||
import Dropdown from '@/components/Common/Dropdown';
|
import Dropdown from '@/components/Common/Dropdown';
|
||||||
import DropdownButton from '@/components/Common/DropdownButton';
|
import DropdownButton from '@/components/Common/DropdownButton';
|
||||||
import SelectorButton from '@/components/Common/SelectorButton';
|
import SelectorButton from '@/components/Common/SelectorButton';
|
||||||
import { CogIcon, FilterIcon } from '@/components/Icons';
|
|
||||||
import useDropdown from '@/hooks/useDropdown';
|
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';
|
||||||
|
@ -86,7 +86,7 @@ function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }:
|
||||||
<SelectorButton transparent tabIndex={-1}
|
<SelectorButton transparent tabIndex={-1}
|
||||||
tooltip='Настройка атрибутов для фильтрации'
|
tooltip='Настройка атрибутов для фильтрации'
|
||||||
dimensions='w-fit h-full'
|
dimensions='w-fit h-full'
|
||||||
icon={<FilterIcon size={5} />}
|
icon={<BiFilterAlt size='1.25rem' />}
|
||||||
text={labelCstMathchMode(filterMatch)}
|
text={labelCstMathchMode(filterMatch)}
|
||||||
onClick={matchModeMenu.toggle}
|
onClick={matchModeMenu.toggle}
|
||||||
/>
|
/>
|
||||||
|
@ -100,7 +100,7 @@ function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }:
|
||||||
key={`${prefixes.cst_match_mode_list}${index}`}
|
key={`${prefixes.cst_match_mode_list}${index}`}
|
||||||
onClick={() => handleMatchModeChange(matchMode)}
|
onClick={() => handleMatchModeChange(matchMode)}
|
||||||
>
|
>
|
||||||
<p><span className='font-semibold'>{labelCstMathchMode(matchMode)}:</span> {describeCstMathchMode(matchMode)}</p>
|
<p><b>{labelCstMathchMode(matchMode)}:</b> {describeCstMathchMode(matchMode)}</p>
|
||||||
</DropdownButton>);
|
</DropdownButton>);
|
||||||
})}
|
})}
|
||||||
</Dropdown> : null}
|
</Dropdown> : null}
|
||||||
|
@ -110,7 +110,7 @@ function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }:
|
||||||
<SelectorButton transparent tabIndex={-1}
|
<SelectorButton transparent tabIndex={-1}
|
||||||
tooltip='Настройка фильтрации по графу термов'
|
tooltip='Настройка фильтрации по графу термов'
|
||||||
dimensions='w-fit h-full pr-2'
|
dimensions='w-fit h-full pr-2'
|
||||||
icon={<CogIcon size={4} />}
|
icon={<BiCog size='1.25rem' />}
|
||||||
text={labelCstSource(filterSource)}
|
text={labelCstSource(filterSource)}
|
||||||
onClick={sourceMenu.toggle}
|
onClick={sourceMenu.toggle}
|
||||||
/>
|
/>
|
||||||
|
@ -124,7 +124,7 @@ function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }:
|
||||||
key={`${prefixes.cst_source_list}${index}`}
|
key={`${prefixes.cst_source_list}${index}`}
|
||||||
onClick={() => handleSourceChange(source)}
|
onClick={() => handleSourceChange(source)}
|
||||||
>
|
>
|
||||||
<p><span className='font-semibold'>{labelCstSource(source)}:</span> {describeCstSource(source)}</p>
|
<p><b>{labelCstSource(source)}:</b> {describeCstSource(source)}</p>
|
||||||
</DropdownButton>);
|
</DropdownButton>);
|
||||||
})}
|
})}
|
||||||
</Dropdown> : null}
|
</Dropdown> : null}
|
||||||
|
|
|
@ -43,13 +43,6 @@ function ConstituentsTable({
|
||||||
}, [windowSize, denseThreshold]);
|
}, [windowSize, denseThreshold]);
|
||||||
|
|
||||||
const handleRowClicked = useCallback(
|
const handleRowClicked = useCallback(
|
||||||
(cst: IConstituenta, event: React.MouseEvent<Element, MouseEvent>) => {
|
|
||||||
if (event.altKey && !isMockCst(cst)) {
|
|
||||||
onOpenEdit(cst.id);
|
|
||||||
}
|
|
||||||
}, [onOpenEdit]);
|
|
||||||
|
|
||||||
const handleDoubleClick = useCallback(
|
|
||||||
(cst: IConstituenta) => {
|
(cst: IConstituenta) => {
|
||||||
if (!isMockCst(cst)) {
|
if (!isMockCst(cst)) {
|
||||||
onOpenEdit(cst.id);
|
onOpenEdit(cst.id);
|
||||||
|
@ -135,7 +128,6 @@ function ConstituentsTable({
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
onRowDoubleClicked={handleDoubleClick}
|
|
||||||
onRowClicked={handleRowClicked}
|
onRowClicked={handleRowClicked}
|
||||||
/>);
|
/>);
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ function ViewConstituents({ expression, baseHeight, schema, activeID, onOpenEdit
|
||||||
activeExpression={expression}
|
activeExpression={expression}
|
||||||
setFiltered={setFilteredData}
|
setFiltered={setFilteredData}
|
||||||
/>
|
/>
|
||||||
<div className='overflow-y-auto text-sm overscroll-none' style={{maxHeight : `${maxHeight}`}}>
|
<div className='overflow-y-auto text-sm select-none overscroll-none' style={{maxHeight : `${maxHeight}`}}>
|
||||||
<ConstituentsTable
|
<ConstituentsTable
|
||||||
items={filteredData}
|
items={filteredData}
|
||||||
activeID={activeID}
|
activeID={activeID}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
import { BiInfoCircle } from 'react-icons/bi';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import Button from '@/components/Common/Button';
|
import Button from '@/components/Common/Button';
|
||||||
|
@ -11,7 +12,6 @@ import SubmitButton from '@/components/Common/SubmitButton';
|
||||||
import TextInput from '@/components/Common/TextInput';
|
import TextInput from '@/components/Common/TextInput';
|
||||||
import TextURL from '@/components/Common/TextURL';
|
import TextURL from '@/components/Common/TextURL';
|
||||||
import ExpectedAnonymous from '@/components/ExpectedAnonymous';
|
import ExpectedAnonymous from '@/components/ExpectedAnonymous';
|
||||||
import { HelpIcon } from '@/components/Icons';
|
|
||||||
import InfoError from '@/components/InfoError';
|
import InfoError from '@/components/InfoError';
|
||||||
import { useAuth } from '@/context/AuthContext';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
import { useConceptNavigation } from '@/context/NagivationContext';
|
import { useConceptNavigation } from '@/context/NagivationContext';
|
||||||
|
@ -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='clr-text-primary' size={5} />
|
<BiInfoCircle size='1.25rem' className='clr-text-primary' />
|
||||||
</Overlay>
|
</Overlay>
|
||||||
<ConceptTooltip
|
<ConceptTooltip
|
||||||
anchorSelect={`#${globalIDs.password_tooltip}`}
|
anchorSelect={`#${globalIDs.password_tooltip}`}
|
||||||
|
|
|
@ -38,8 +38,8 @@ function UserTabs() {
|
||||||
<MiniButton
|
<MiniButton
|
||||||
tooltip='Показать/Скрыть список отслеживаний'
|
tooltip='Показать/Скрыть список отслеживаний'
|
||||||
icon={showSubs
|
icon={showSubs
|
||||||
? <SubscribedIcon color='clr-text-primary' size={5}/>
|
? <SubscribedIcon size='1.25rem' className='clr-text-primary' />
|
||||||
: <NotSubscribedIcon color='clr-text-primary' size={5}/>
|
: <NotSubscribedIcon size='1.25rem' className='clr-text-primary' />
|
||||||
}
|
}
|
||||||
onClick={() => setShowSubs(prev => !prev)}
|
onClick={() => setShowSubs(prev => !prev)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* Description is a long description used in tooltips.
|
* Description is a long description used in tooltips.
|
||||||
*/
|
*/
|
||||||
import { GramData,Grammeme, ReferenceType } from '@/models/language';
|
import { GramData,Grammeme, ReferenceType } from '@/models/language';
|
||||||
import { CstMatchMode, DependencyMode, HelpTopic, LibraryFilterStrategy } from '@/models/miscelanious';
|
import { CstMatchMode, DependencyMode, HelpTopic, LibraryFilterStrategy, UserAccessMode } from '@/models/miscelanious';
|
||||||
import { CstClass, CstType, ExpressionStatus, IConstituenta } from '@/models/rsform';
|
import { CstClass, CstType, ExpressionStatus, IConstituenta } from '@/models/rsform';
|
||||||
import { IArgumentInfo, IRSErrorDescription, ISyntaxTreeNode, ParsingStatus, RSErrorType, TokenID } from '@/models/rslang';
|
import { IArgumentInfo, IRSErrorDescription, ISyntaxTreeNode, ParsingStatus, RSErrorType, TokenID } from '@/models/rslang';
|
||||||
|
|
||||||
|
@ -653,3 +653,28 @@ export function describeRSError(error: IRSErrorDescription): string {
|
||||||
}
|
}
|
||||||
return 'UNKNOWN ERROR';
|
return 'UNKNOWN ERROR';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves label for {@link UserAccessMode}.
|
||||||
|
*/
|
||||||
|
export function labelAccessMode(mode: UserAccessMode): string {
|
||||||
|
switch (mode) {
|
||||||
|
case UserAccessMode.READER: return 'Читатель';
|
||||||
|
case UserAccessMode.OWNER: return 'Владелец';
|
||||||
|
case UserAccessMode.ADMIN: return 'Администратор';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves description for {@link UserAccessMode}.
|
||||||
|
*/
|
||||||
|
export function describeAccessMode(mode: UserAccessMode): string {
|
||||||
|
switch (mode) {
|
||||||
|
case UserAccessMode.READER:
|
||||||
|
return 'Режим запрещает редактирование';
|
||||||
|
case UserAccessMode.OWNER:
|
||||||
|
return 'Режим редактирования владельцем';
|
||||||
|
case UserAccessMode.ADMIN:
|
||||||
|
return 'Режим редактирования администратором';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user