mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Major UI improvement and colors refactoring
This commit is contained in:
parent
8a596ccccf
commit
c18e34d2eb
|
@ -1,5 +1,7 @@
|
|||
export default {
|
||||
plugins: {
|
||||
'postcss-import': {},
|
||||
'tailwindcss/nesting': {},
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { createBrowserRouter, Outlet, RouterProvider } from 'react-router-dom';
|
||||
|
||||
import ConceptToaster from './components/ConceptToaster';
|
||||
import Footer from './components/Footer';
|
||||
import Navigation from './components/Navigation/Navigation';
|
||||
import ToasterThemed from './components/ToasterThemed';
|
||||
import { useConceptTheme } from './context/ThemeContext';
|
||||
import CreateRSFormPage from './pages/CreateRSFormPage';
|
||||
import HomePage from './pages/HomePage';
|
||||
|
@ -20,7 +20,7 @@ function Root() {
|
|||
return (
|
||||
<div className='w-screen antialiased clr-app'>
|
||||
<Navigation />
|
||||
<ToasterThemed
|
||||
<ConceptToaster
|
||||
className='mt-[4rem] text-sm'
|
||||
autoClose={3000}
|
||||
draggable={false}
|
||||
|
|
|
@ -59,7 +59,7 @@ function DescribeError(error: ErrorInfo) {
|
|||
|
||||
function BackendError({ error }: BackendErrorProps) {
|
||||
return (
|
||||
<div className='py-2 text-sm font-semibold text-red-600 dark:text-red-400'>
|
||||
<div className='py-2 text-sm font-semibold select-text text-warning'>
|
||||
{DescribeError(error)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -6,7 +6,7 @@ interface CardProps {
|
|||
|
||||
function Card({ title, widthClass = 'min-w-fit', children }: CardProps) {
|
||||
return (
|
||||
<div className={`border shadow-md py-2 clr-card px-6 ${widthClass}`}>
|
||||
<div className={`border shadow-md py-2 clr-app px-6 ${widthClass}`}>
|
||||
{ title && <h1 className='mb-2 text-xl font-bold whitespace-nowrap'>{title}</h1> }
|
||||
{children}
|
||||
</div>
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
import { ThreeDots } from 'react-loader-spinner';
|
||||
|
||||
interface LoaderProps {
|
||||
import { useConceptTheme } from '../../context/ThemeContext';
|
||||
|
||||
interface ConceptLoaderProps {
|
||||
size?: number
|
||||
}
|
||||
|
||||
export function Loader({size=10}: LoaderProps) {
|
||||
export function ConceptLoader({size=10}: ConceptLoaderProps) {
|
||||
const {colors} = useConceptTheme()
|
||||
|
||||
return (
|
||||
<div className='flex justify-center w-full h-full'>
|
||||
<ThreeDots
|
||||
color='rgb(96 165 250)'
|
||||
color={colors.bgSelected}
|
||||
height={size*10}
|
||||
width={size*10}
|
||||
radius={size}
|
|
@ -9,7 +9,7 @@ extends Omit<PropsWithRef<SelectProps<T>>, 'noDataLabel'> {
|
|||
function ConceptSelect<T extends object | string>({ className, ...props }: ConceptSelectProps<T>) {
|
||||
return (
|
||||
<Select
|
||||
className={`overflow-x-ellipsis whitespace-nowrap ${className}`}
|
||||
className={`overflow-x-ellipsis whitespace-nowrap clr-border clr-input outline-none ${className}`}
|
||||
{...props}
|
||||
noDataLabel='Список пуст'
|
||||
/>
|
||||
|
|
|
@ -9,7 +9,7 @@ extends Omit<TabProps, 'className'> {
|
|||
function ConceptTab({ children, className, ...otherProps }: ConceptTabProps) {
|
||||
return (
|
||||
<Tab
|
||||
className={`px-2 py-1 text-sm hover:cursor-pointer clr-tab whitespace-nowrap ${className}`}
|
||||
className={`px-2 py-1 h-full text-sm hover:cursor-pointer clr-tab whitespace-nowrap ${className}`}
|
||||
{...otherProps}
|
||||
>
|
||||
{children}
|
||||
|
|
|
@ -6,8 +6,8 @@ interface DividerProps {
|
|||
function Divider({ vertical, margins = 'mx-2' }: DividerProps) {
|
||||
return (
|
||||
<>
|
||||
{vertical && <div className={`${margins} border-x-2 clr-border`} />}
|
||||
{!vertical && <div className={`${margins} border-y-2 clr-border`} />}
|
||||
{vertical && <div className={`${margins} border-x-2`} />}
|
||||
{!vertical && <div className={`${margins} border-y-2`} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ interface DropdownProps {
|
|||
function Dropdown({ children, widthClass = 'w-fit', stretchLeft }: DropdownProps) {
|
||||
return (
|
||||
<div className='relative text-sm'>
|
||||
<div className={`absolute ${stretchLeft ? 'right-0' : 'left-0'} mt-2 py-1 z-40 flex flex-col items-stretch justify-start origin-top-right border divide-y rounded-md shadow-lg clr-input clr-border ${widthClass}`}>
|
||||
<div className={`absolute ${stretchLeft ? 'right-0' : 'left-0'} mt-2 py-1 z-40 flex flex-col items-stretch justify-start origin-top-right border divide-y divide-inherit rounded-md shadow-lg clr-input ${widthClass}`}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -13,10 +13,10 @@ function DropdownCheckbox({ tooltip, onChange, disabled, ...props }: DropdownChe
|
|||
return (
|
||||
<div
|
||||
title={tooltip}
|
||||
className={`px-4 py-1 text-left overflow-ellipsis ${behavior} whitespace-nowrap`}
|
||||
className={`px-4 py-1 text-left overflow-ellipsis ${behavior} w-full whitespace-nowrap`}
|
||||
>
|
||||
<Checkbox
|
||||
widthClass='w-fit'
|
||||
widthClass='w-full'
|
||||
disabled={disabled}
|
||||
onChange={onChange}
|
||||
{...props}
|
||||
|
|
|
@ -14,7 +14,7 @@ function EmbedYoutube({ videoID, pxHeight, pxWidth }: EmbedYoutubeProps) {
|
|||
style={{height: 0, paddingBottom: `${pxHeight}px`, paddingLeft: `${pxWidth}px`}}
|
||||
>
|
||||
<iframe
|
||||
className='absolute top-0 left-0 clr-border'
|
||||
className='absolute top-0 left-0 border'
|
||||
style={{minHeight: `${pxHeight}px`, minWidth: `${pxWidth}px`}}
|
||||
width={`${pxWidth}px`}
|
||||
height={`${pxHeight}px`}
|
||||
|
|
|
@ -9,7 +9,7 @@ extends Omit<React.DetailedHTMLProps<LabelHTMLAttributes<HTMLLabelElement>, HTML
|
|||
function Label({ text, required, title, className, ...props }: LabelProps) {
|
||||
return (
|
||||
<label
|
||||
className={`${className} text-sm font-semibold`}
|
||||
className={`text-sm font-semibold ${className}`}
|
||||
title={ (required && !title) ? 'обязательное поле' : title }
|
||||
{...props}
|
||||
>
|
||||
|
|
|
@ -37,16 +37,16 @@ function Modal({
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className='fixed top-0 left-0 z-50 w-full h-full opacity-50 clr-modal' />
|
||||
<div className='fixed top-0 left-0 z-50 w-full h-full clr-modal-backdrop' />
|
||||
<div
|
||||
ref={ref}
|
||||
className='fixed bottom-1/2 left-1/2 translate-y-1/2 -translate-x-1/2 px-6 py-4 flex flex-col w-fit max-w-[95vw] h-fit z-[60] clr-card border shadow-md mb-[5rem]'
|
||||
className='fixed bottom-1/2 left-1/2 translate-y-1/2 -translate-x-1/2 px-6 py-4 flex flex-col w-fit max-w-[95vw] h-fit z-[60] clr-app border shadow-md mb-[5rem]'
|
||||
>
|
||||
{ title && <h1 className='mb-2 text-xl font-bold text-center'>{title}</h1> }
|
||||
{ title && <h1 className='mb-2 text-xl'>{title}</h1> }
|
||||
<div className='max-h-[calc(95vh-15rem)]'>
|
||||
{children}
|
||||
</div>
|
||||
<div className='flex justify-center w-full gap-4 pt-4 mt-2 border-t-4 clr-border'>
|
||||
<div className='flex justify-center w-full gap-4 pt-4 mt-2 border-t-2'>
|
||||
{!readonly &&
|
||||
<Button
|
||||
text={submitText}
|
||||
|
|
|
@ -26,7 +26,7 @@ function TextArea({
|
|||
/>
|
||||
<textarea id={id}
|
||||
title={tooltip}
|
||||
className={`px-3 py-2 mt-2 leading-tight border shadow ${colorClass} ${widthClass}`}
|
||||
className={`px-3 py-2 mt-2 leading-tight border shadow clr-outline ${colorClass} ${widthClass}`}
|
||||
rows={rows}
|
||||
required={required}
|
||||
{...props}
|
||||
|
|
|
@ -27,7 +27,7 @@ function TextInput({
|
|||
/>
|
||||
<input id={id}
|
||||
title={tooltip}
|
||||
className={`px-3 py-2 leading-tight border shadow truncate hover:text-clip ${colorClass} ${singleRow ? '' : 'mt-2 ' + widthClass}`}
|
||||
className={`px-3 py-2 leading-tight border shadow truncate hover:text-clip clr-outline ${colorClass} ${singleRow ? '' : 'mt-2 ' + widthClass}`}
|
||||
required={required}
|
||||
{...props}
|
||||
/>
|
||||
|
|
|
@ -6,9 +6,9 @@ function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
|
|||
reportError(error);
|
||||
return (
|
||||
<div className='flex flex-col items-center antialiased clr-app' role='alert'>
|
||||
<h1 className='text-lg font-semibold'>Something went wrong!</h1>
|
||||
<h1 className='text-lg font-semibold'>Что-то пошло не так!</h1>
|
||||
{ error }
|
||||
<Button onClick={resetErrorBoundary} text='Try again' />
|
||||
<Button onClick={resetErrorBoundary} text='Попробовать еще раз' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import { urls } from '../utils/constants';
|
|||
|
||||
function Footer() {
|
||||
return (
|
||||
<footer className='z-50 px-4 pt-2 pb-4 text-sm select-none whitespace-nowrap clr-footer'>
|
||||
<footer className='z-50 px-4 py-2 text-sm select-none whitespace-nowrap clr-footer'>
|
||||
<div className='justify-center w-full mx-auto'>
|
||||
<div className='mb-2 text-center'>
|
||||
<Link className='mx-2 hover:underline' to='/library' tabIndex={-1}>Библиотека</Link>
|
||||
|
|
|
@ -5,7 +5,7 @@ function HelpConstituenta() {
|
|||
return (
|
||||
<div className=''>
|
||||
<h1>Подсказки</h1>
|
||||
<p><b className='text-red'>Изменения сохраняются ПОСЛЕ нажатия на кнопку снизу или слева вверху</b></p>
|
||||
<p><b className='text-warning'>Изменения сохраняются ПОСЛЕ нажатия на кнопку снизу или слева вверху</b></p>
|
||||
<p><b>Клик на формальное выражение</b> - обратите внимание на кнопки снизу.<br/>Для каждой есть горячая клавиша в подсказке</p>
|
||||
<p><b>Список конституент справа</b> - обратите внимание на настройки фильтрации</p>
|
||||
<p>- слева от ввода текста настраивается набор атрибутов конституенты</p>
|
||||
|
|
|
@ -18,7 +18,7 @@ function HelpLibrary() {
|
|||
</div>
|
||||
<div className='flex items-center gap-2'>
|
||||
<EducationIcon size={4}/>
|
||||
<p>Аттрибут <b>неизменяемая</b> выделяет неизменяемые стандартные схемы.</p>
|
||||
<p>Аттрибут <b>неизменная</b> выделяет стандартные схемы.</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -4,19 +4,19 @@ import TextURL from '../Common/TextURL';
|
|||
|
||||
function HelpMain() {
|
||||
return (
|
||||
<div className='w-full'>
|
||||
<div className='flex flex-col w-full'>
|
||||
<h1>Портал</h1>
|
||||
<p className='lg:text-left indent-10'>Портал позволяет анализировать предметные области, формально записывать системы определений (концептуальные схемы) и синтезировать их с помощью математического аппарата родов структур.</p>
|
||||
<p className='lg:text-left indent-10'>Навигация по порталу осуществляется верхнюю панель или ссылки в "подвале" страницы. Их можно скрыть с помощью кнопки в правом верхнем углу</p>
|
||||
<p className='lg:text-left indent-10'>В меню пользователя (правый верхний угол) редактирование данных пользователя и изменение цветовой темы.</p>
|
||||
<p className='lg:text-justify'>Портал позволяет анализировать предметные области, формально записывать системы определений (концептуальные схемы) и синтезировать их с помощью математического аппарата родов структур.</p>
|
||||
<p className='mt-2 lg:text-justify'>Навигация по порталу осуществляется верхнюю панель или ссылки в "подвале" страницы. Их можно скрыть с помощью кнопки в правом верхнем углу.</p>
|
||||
<p className='mt-2 lg:text-justify'>В меню пользователя (правый верхний угол) доступно редактирование данных пользователя и изменение цветовой темы.</p>
|
||||
<p className='mt-4 mb-1 text-center'><b>Основные разделы</b></p>
|
||||
<li className='lg:text-left indent-5'><TextURL text='Библиотека' href='/library' /> - все схемы доступные пользователю</li>
|
||||
<li className='lg:text-left indent-5'><TextURL text='Общие схемы' href={`/library?filter=${LibraryFilterStrategy.COMMON}`} /> - общедоступные схемы и инструменты поиска и навигации по ним</li>
|
||||
<li className='lg:text-left indent-5'><TextURL text='Мои схемы' href={`/library?filter=${LibraryFilterStrategy.PERSONAL}`} /> - отслеживаемые и редактируемые схемы. Основной рабочий раздел</li>
|
||||
<li className='lg:text-left indent-5'><TextURL text='Профиль' href='/profile' /> - данные пользователя и смена пароля</li>
|
||||
<li className='text-left'><TextURL text='Библиотека' href='/library' /> - все схемы доступные пользователю</li>
|
||||
<li className='text-left'><TextURL text='Общие схемы' href={`/library?filter=${LibraryFilterStrategy.COMMON}`} /> - общедоступные схемы и инструменты поиска и навигации по ним</li>
|
||||
<li className='text-left'><TextURL text='Мои схемы' href={`/library?filter=${LibraryFilterStrategy.PERSONAL}`} /> - отслеживаемые и редактируемые схемы. Основной рабочий раздел</li>
|
||||
<li className='text-left'><TextURL text='Профиль' href='/profile' /> - данные пользователя и смена пароля</li>
|
||||
<p className='mt-4 mb-1 text-center'><b>Поддержка</b></p>
|
||||
<p className='lg:text-left indent-10'>Портал разрабатывается <TextURL text='Центром Концепт' href={urls.concept}/> и является проектом с открытым исходным кодом, доступным на <TextURL text='Github' href={urls.gitrepo}/>.</p>
|
||||
<p className='lg:text-left indent-10'>Ждём Ваши пожелания по доработке, найденные ошибки и иные предложения по адресу <TextURL href={urls.mailportal} text='portal@acconcept.ru'/></p>
|
||||
<p className='lg:text-justify'>Портал разрабатывается <TextURL text='Центром Концепт' href={urls.concept}/> и является проектом с открытым исходным кодом, доступным на <TextURL text='Github' href={urls.gitrepo}/>.</p>
|
||||
<p className='mt-2 lg:text-justify'>Ждём Ваши пожелания по доработке, найденные ошибки и иные предложения по адресу <TextURL href={urls.mailportal} text='portal@acconcept.ru'/></p>
|
||||
<p></p>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -300,3 +300,20 @@ export function GithubIcon(props: IconProps) {
|
|||
</IconSVG>
|
||||
);
|
||||
}
|
||||
|
||||
export function MeshIcon(props: IconProps) {
|
||||
return (
|
||||
<IconSVG viewbox='0 0 1024 1024' {...props}>
|
||||
<path d='M872 394c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8H708V152c0-4.4-3.6-8-8-8h-64c-4.4 0-8 3.6-8 8v166H400V152c0-4.4-3.6-8-8-8h-64c-4.4 0-8 3.6-8 8v166H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h168v236H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h168v166c0 4.4 3.6 8 8 8h64c4.4 0 8-3.6 8-8V706h228v166c0 4.4 3.6 8 8 8h64c4.4 0 8-3.6 8-8V706h164c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8H708V394h164zM628 630H400V394h228v236z' />
|
||||
</IconSVG>
|
||||
);
|
||||
}
|
||||
|
||||
export function InDoor(props: IconProps) {
|
||||
return (
|
||||
<IconSVG viewbox='0 0 24 24' {...props}>
|
||||
<path fill='none' d='M0 0h24v24H0z' />
|
||||
<path d='M10 11H4V3a1 1 0 011-1h14a1 1 0 011 1v18a1 1 0 01-1 1H5a1 1 0 01-1-1v-8h6v3l5-4-5-4v3z' />
|
||||
</IconSVG>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { useConceptTheme } from '../../context/ThemeContext';
|
||||
import { EducationIcon, LibraryIcon } from '../Icons';
|
||||
import { EducationIcon, LibraryIcon, PlusIcon } from '../Icons';
|
||||
import Logo from './Logo'
|
||||
import NavigationButton from './NavigationButton';
|
||||
import UserMenu from './UserMenu';
|
||||
|
@ -12,13 +12,14 @@ function Navigation () {
|
|||
|
||||
const navigateLibrary = () => navigate('/library');
|
||||
const navigateHelp = () => navigate('/manuals');
|
||||
const navigateCreateNew = () => navigate('/rsform-create');
|
||||
|
||||
return (
|
||||
<nav className='sticky top-0 left-0 right-0 z-50 select-none h-fit'>
|
||||
{!noNavigation &&
|
||||
<button
|
||||
title='Скрыть навигацию'
|
||||
className='absolute top-0 right-0 z-[60] w-[1.2rem] border-b-2 border-l-2 clr-nav rounded-none'
|
||||
className='absolute top-0 right-0 z-[60] w-[1.2rem] h-[3rem] border-b-2 border-l-2 clr-btn-nav rounded-none'
|
||||
onClick={toggleNoNavigation}
|
||||
>
|
||||
<p>{'>'}</p><p>{'>'}</p>
|
||||
|
@ -26,18 +27,23 @@ function Navigation () {
|
|||
{noNavigation &&
|
||||
<button
|
||||
title='Показать навигацию'
|
||||
className='absolute top-0 right-0 z-[60] px-1 h-[1.6rem] border-b-2 border-l-2 clr-nav rounded-none'
|
||||
className='absolute top-0 right-0 z-[60] px-1 h-[1.6rem] border-b-2 border-l-2 clr-btn-nav rounded-none'
|
||||
onClick={toggleNoNavigation}
|
||||
>
|
||||
{'∨∨∨'}
|
||||
</button>}
|
||||
{!noNavigation &&
|
||||
<div className='flex items-center justify-between py-1 pl-2 pr-6 border-b-2 rounded-none clr-nav'>
|
||||
<div className='flex items-stretch justify-between pl-2 pr-[0.8rem] border-b-2 rounded-none h-[3rem]'>
|
||||
<div className='flex items-center justify-start'>
|
||||
<Logo title='КонцептПортал' />
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
<div className='flex items-center pl-2'>
|
||||
<div className='flex items-center h-full'>
|
||||
<NavigationButton
|
||||
text='Новая схема'
|
||||
description='Создать новую схему'
|
||||
icon={<PlusIcon />}
|
||||
onClick={navigateCreateNew}
|
||||
/>
|
||||
<NavigationButton
|
||||
text='Библиотека'
|
||||
description='Библиотека концептуальных схем'
|
||||
|
@ -52,7 +58,6 @@ function Navigation () {
|
|||
/>
|
||||
<UserMenu />
|
||||
</div>
|
||||
</div>
|
||||
</div>}
|
||||
</nav>
|
||||
);
|
||||
|
|
|
@ -12,7 +12,7 @@ function NavigationButton({ id, icon, description, onClick, text }: NavigationBu
|
|||
title={description}
|
||||
type='button'
|
||||
onClick={onClick}
|
||||
className='flex gap-1 p-2 mr-1 rounded-lg min-w-fit whitespace-nowrap clr-btn-nav'
|
||||
className={`flex items-center h-full gap-1 ${text ? 'px-2' : 'px-4'} mr-1 min-w-fit whitespace-nowrap clr-btn-nav`}
|
||||
>
|
||||
{icon && <span>{icon}</span>}
|
||||
{text && <span className='font-semibold'>{text}</span>}
|
||||
|
|
|
@ -1,26 +1,27 @@
|
|||
import { Link } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { useAuth } from '../../context/AuthContext';
|
||||
import useDropdown from '../../hooks/useDropdown';
|
||||
import { UserIcon } from '../Icons';
|
||||
import { InDoor, UserIcon } from '../Icons';
|
||||
import NavigationButton from './NavigationButton';
|
||||
import UserDropdown from './UserDropdown';
|
||||
|
||||
function LoginRef() {
|
||||
return (
|
||||
<Link to='login' className='inline-block h-full px-1 py-2 font-semibold rounded-lg hover:underline clr-btn-nav text-primary'>
|
||||
Войти...
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
function UserMenu() {
|
||||
const navigate = useNavigate();
|
||||
const { user } = useAuth();
|
||||
const menu = useDropdown();
|
||||
|
||||
const navigateLogin = () => navigate('/login');
|
||||
return (
|
||||
<div ref={menu.ref}>
|
||||
<div className='w-[4.2rem] flex justify-end'>
|
||||
{ !user && <LoginRef />}
|
||||
<div ref={menu.ref} className='h-full'>
|
||||
<div className='flex items-center justify-end h-full w-fit'>
|
||||
{ !user &&
|
||||
<NavigationButton
|
||||
text='Войти...'
|
||||
description='Перейти на страницу логина'
|
||||
icon={<InDoor />}
|
||||
onClick={navigateLogin}
|
||||
/>}
|
||||
{ user &&
|
||||
<NavigationButton
|
||||
icon={<UserIcon />}
|
||||
|
|
|
@ -3,8 +3,8 @@ import { Decoration, EditorView } from '@codemirror/view';
|
|||
|
||||
import { bracketsDarkT, bracketsLightT } from '../../utils/color';
|
||||
|
||||
const matchingMark = Decoration.mark({class: "cc-matchingBracket"});
|
||||
const nonmatchingMark = Decoration.mark({class: "cc-nonmatchingBracket"});
|
||||
const matchingMark = Decoration.mark({class: 'cc-matchingBracket'});
|
||||
const nonmatchingMark = Decoration.mark({class: 'cc-nonmatchingBracket'});
|
||||
|
||||
function bracketRender(match: MatchResult) {
|
||||
const decorations = [];
|
||||
|
|
|
@ -69,10 +69,9 @@ function RSInput({
|
|||
theme: 'light',
|
||||
settings: {
|
||||
fontFamily: 'inherit',
|
||||
background: editable ? colors.input : colors.inputDisabled,
|
||||
foreground: colors.text,
|
||||
selection: colors.selection,
|
||||
caret: '#5d00ff',
|
||||
background: editable ? colors.bgInput : colors.bgDisabled,
|
||||
foreground: colors.fgDefault,
|
||||
selection: colors.bgHover
|
||||
},
|
||||
styles: [
|
||||
{ tag: t.name, class: 'text-[#b266ff] cursor-default' }, // GlobalID
|
||||
|
@ -90,10 +89,9 @@ function RSInput({
|
|||
theme: 'dark',
|
||||
settings: {
|
||||
fontFamily: 'inherit',
|
||||
background: editable ? colors.input : colors.inputDisabled,
|
||||
foreground: colors.text,
|
||||
selection: colors.selection,
|
||||
caret: '#ffaa00'
|
||||
background: editable ? colors.bgInput : colors.bgDisabled,
|
||||
foreground: colors.fgDefault,
|
||||
selection: colors.bgHover
|
||||
},
|
||||
styles: [
|
||||
{ tag: t.name, class: 'text-[#dfbfff] cursor-default' }, // GlobalID
|
||||
|
@ -139,12 +137,13 @@ function RSInput({
|
|||
}, [thisRef]);
|
||||
|
||||
return (
|
||||
<div className={`w-full ${cursor}`}>
|
||||
<div className={`flex flex-col w-full ${cursor}`}>
|
||||
{label &&
|
||||
<Label
|
||||
text={label}
|
||||
required={false}
|
||||
htmlFor={id}
|
||||
className='mb-2'
|
||||
/>}
|
||||
<CodeMirror id={id}
|
||||
ref={thisRef}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {LRLanguage} from "@codemirror/language"
|
||||
import {LRLanguage} from '@codemirror/language'
|
||||
|
||||
import { parser } from './parser';
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { Extension } from "@codemirror/state";
|
||||
import { hoverTooltip } from "@codemirror/view";
|
||||
import { Extension } from '@codemirror/state';
|
||||
import { hoverTooltip } from '@codemirror/view';
|
||||
|
||||
import { IConstituenta } from '../../utils/models';
|
||||
import { getCstTypificationLabel } from '../../utils/staticUI';
|
||||
|
||||
function createTooltipFor(cst: IConstituenta) {
|
||||
const dom = document.createElement('div');
|
||||
dom.className = 'overflow-y-auto border shadow-md max-h-[25rem] max-w-[25rem] min-w-[10rem] w-fit z-20 text-sm clr-border px-2 py-2';
|
||||
dom.className = 'overflow-y-auto border shadow-md max-h-[25rem] max-w-[25rem] min-w-[10rem] w-fit z-20 text-sm px-2 py-2';
|
||||
const alias = document.createElement('p');
|
||||
alias.innerHTML = `<b>${cst.alias}:</b> ${getCstTypificationLabel(cst)}`;
|
||||
dom.appendChild(alias);
|
||||
|
|
|
@ -12,10 +12,10 @@ function RequireAuth({ children }: RequireAuthProps) {
|
|||
<>
|
||||
{user && children}
|
||||
{!user &&
|
||||
<div className='flex flex-col items-center mt-2 gap-1'>
|
||||
<div className='flex flex-col items-center gap-1 mt-2'>
|
||||
<p><b>Данная страница доступна только зарегистрированным пользователям</b></p>
|
||||
<p className='mb-2'>Пожалуйста войдите в систему</p>
|
||||
<TextURL text='Войти в систему' href='/login'/>
|
||||
<TextURL text='Войти в Портал' href='/login'/>
|
||||
<TextURL text='Зарегистрироваться' href='/signup'/>
|
||||
<TextURL text='Начальная страница' href='/'/>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
|
||||
function useWindowSize() {
|
||||
const isClient = typeof window === "object";
|
||||
const isClient = typeof window === 'object';
|
||||
|
||||
function getSize() {
|
||||
return {
|
||||
|
@ -20,8 +20,8 @@ function useWindowSize() {
|
|||
function handleResize() {
|
||||
setWindowSize(getSize());
|
||||
}
|
||||
window.addEventListener("resize", handleResize);
|
||||
return () => window.removeEventListener("resize", handleResize);
|
||||
window.addEventListener('resize', handleResize);
|
||||
return () => window.removeEventListener('resize', handleResize);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
|
|
|
@ -2,8 +2,75 @@
|
|||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
.rdt_TableCell{
|
||||
font-size: 0.875rem;
|
||||
:root {
|
||||
/* Light Theme */
|
||||
--cl-bg-120: #ffffff;
|
||||
--cl-bg-100: #f9fafb;
|
||||
--cl-bg-80: #f3f4f6;
|
||||
--cl-bg-60: #e5e7eb;
|
||||
--cl-bg-40: #d1d5db;
|
||||
|
||||
--cl-fg-40: #8c8c8c;
|
||||
--cl-fg-60: #777777;
|
||||
--cl-fg-80: #333333;
|
||||
--cl-fg-100: #000000;
|
||||
|
||||
--cl-prim-bg-100: #3377ff;
|
||||
--cl-prim-bg-80: #ccddff;
|
||||
--cl-prim-bg-60: #e0ebff;
|
||||
|
||||
--cl-prim-fg-60: #1a63ff;
|
||||
--cl-prim-fg-80: #0051ff;
|
||||
--cl-prim-fg-100: #ffffff;
|
||||
|
||||
--cl-red-bg-100: #ffe5e5;
|
||||
--cl-red-fg-100: #dc2626;
|
||||
--cl-green-fg-100: #4ade80;
|
||||
|
||||
/* Dark Theme */
|
||||
--cd-bg-120: #0d0d0d;
|
||||
--cd-bg-100: #181818;
|
||||
--cd-bg-80: #272727;
|
||||
--cd-bg-60: #383838;
|
||||
--cd-bg-40: #595959;
|
||||
|
||||
--cd-fg-40: #878792;
|
||||
--cd-fg-60: #bcbcc2;
|
||||
--cd-fg-80: #d4d4d8;
|
||||
--cd-fg-100: #e4e4e7;
|
||||
|
||||
--cd-prim-bg-100: #e66000;
|
||||
--cd-prim-bg-80: #b36800;
|
||||
--cd-prim-bg-60: #663c00;
|
||||
|
||||
--cd-prim-fg-60: #ffa666;
|
||||
--cd-prim-fg-80: #ff6a00;
|
||||
--cd-prim-fg-100: #ffffff;
|
||||
|
||||
--cd-red-bg-100: #4d0000;
|
||||
--cd-red-fg-100: #ff334b;
|
||||
--cd-green-fg-100: #22c55e;
|
||||
}
|
||||
|
||||
:root {
|
||||
color: var(--cl-fg-100);
|
||||
border-color: var(--cl-bg-40);
|
||||
background-color: var(--cl-bg-100);
|
||||
|
||||
&.dark {
|
||||
color: var(--cd-fg-100);
|
||||
border-color: var(--cd-bg-40);
|
||||
background-color: var(--cd-bg-100);
|
||||
}
|
||||
}
|
||||
|
||||
:focus {
|
||||
outline-width: 2px;
|
||||
outline-style: solid;
|
||||
outline-color: transparent;
|
||||
.dark & {
|
||||
outline-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
[data-color-scheme="dark"] {
|
||||
|
@ -14,127 +81,213 @@
|
|||
color-scheme: light;
|
||||
}
|
||||
|
||||
.react-dropdown-select-item {
|
||||
@apply bg-gray-100 dark:bg-gray-600 dark:text-zinc-200 hover:bg-gray-50 hover:text-gray-700 dark:hover:text-white dark:hover:bg-gray-500
|
||||
}
|
||||
|
||||
.cm-editor {
|
||||
@apply border shadow rounded clr-border px-1
|
||||
}
|
||||
.cm-editor.cm-focused {
|
||||
@apply border shadow rounded outline-2 outline
|
||||
}
|
||||
|
||||
@layer components {
|
||||
:root {
|
||||
@apply bg-gray-50;
|
||||
}
|
||||
|
||||
.dark {
|
||||
@apply text-zinc-200 bg-[#181818]
|
||||
}
|
||||
|
||||
h1 {
|
||||
@apply text-lg font-bold text-center
|
||||
}
|
||||
|
||||
.border {
|
||||
@apply clr-border rounded
|
||||
@apply rounded
|
||||
}
|
||||
|
||||
.text-btn {
|
||||
@apply text-gray-600 dark:text-zinc-200 dark:disabled:text-zinc-400 disabled:text-gray-400
|
||||
.clr-modal-backdrop {
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.clr-border {
|
||||
@apply border-gray-300 dark:border-[#434343]
|
||||
:is(.clr-border,
|
||||
.border, .border-x, .border-y, .border-b, .border-t, .border-l, .border-r,
|
||||
.border-2, .border-x-2, .border-y-2, .border-b-2, .border-t-2, .border-l-2, .border-r-2,
|
||||
.divide-x, .divide-y, .divide-x-2, .divide-y-2
|
||||
) {
|
||||
border-color: var(--cl-bg-40);
|
||||
.dark & {
|
||||
border-color: var(--cd-bg-40);
|
||||
}
|
||||
}
|
||||
|
||||
.clr-border-nav {
|
||||
@apply border-gray-400 dark:border-[#434343]
|
||||
:is(.clr-app,
|
||||
.clr-footer,
|
||||
.clr-modal-backdrop,
|
||||
.clr-btn-nav,
|
||||
.clr-checkbox
|
||||
) {
|
||||
background-color: var(--cl-bg-100);
|
||||
.dark & {
|
||||
background-color: var(--cd-bg-100);
|
||||
}
|
||||
}
|
||||
|
||||
.clr-app {
|
||||
@apply bg-gray-50 dark:bg-[#181818]
|
||||
:is(.clr-input
|
||||
) {
|
||||
background-color: var(--cl-bg-120);
|
||||
.dark & {
|
||||
background-color: var(--cd-bg-120);
|
||||
}
|
||||
}
|
||||
|
||||
.clr-bg-pop {
|
||||
@apply bg-gray-100 dark:bg-[#272727] dark:border-[#434343]
|
||||
:is(.clr-controls,
|
||||
.clr-btn-default
|
||||
) {
|
||||
background-color: var(--cl-bg-80);
|
||||
.dark & {
|
||||
background-color: var(--cd-bg-80);
|
||||
}
|
||||
}
|
||||
|
||||
.clr-modal {
|
||||
@apply bg-gray-300 dark:bg-[#272727]
|
||||
:is(.clr-primary,
|
||||
.clr-btn-primary,
|
||||
.clr-checkbox:checked
|
||||
) {
|
||||
color: var(--cl-prim-fg-100);
|
||||
background-color: var(--cl-prim-bg-100);
|
||||
.dark & {
|
||||
color: var(--cd-prim-fg-100);
|
||||
background-color: var(--cd-prim-bg-100);
|
||||
}
|
||||
}
|
||||
|
||||
.clr-nav {
|
||||
@apply border-gray-400 dark:border-[#434343] bg-white dark:bg-[#181818]
|
||||
:is(.clr-selected
|
||||
) {
|
||||
color: var(--cl-fg-100);
|
||||
background-color: var(--cl-prim-bg-80);
|
||||
.dark & {
|
||||
color: var(--cd-fg-100);
|
||||
background-color: var(--cd-prim-bg-80);
|
||||
}
|
||||
}
|
||||
|
||||
.clr-input {
|
||||
@apply dark:bg-[#181818] bg-white disabled:bg-[#c6c6c6] dark:disabled:bg-[#181818]
|
||||
:is(.clr-disabled,
|
||||
.clr-input,
|
||||
.clr-btn-default,
|
||||
.clr-btn-primary
|
||||
):disabled {
|
||||
color: var(--cl-fg-60);
|
||||
background-color: var(--cl-bg-60);
|
||||
.dark & {
|
||||
color: var(--cd-fg-60);
|
||||
background-color: var(--cd-bg-60);
|
||||
}
|
||||
}
|
||||
|
||||
:is(.clr-hover,
|
||||
.clr-tab,
|
||||
.clr-btn-nav,
|
||||
.clr-btn-default,
|
||||
.clr-btn-primary,
|
||||
.clr-btn-clear
|
||||
):hover:not(:disabled) {
|
||||
color: var(--cl-fg-100);
|
||||
background-color: var(--cl-prim-bg-60);
|
||||
.dark & {
|
||||
color: var(--cd-fg-100);
|
||||
background-color: var(--cd-prim-bg-60);
|
||||
}
|
||||
}
|
||||
|
||||
:is(.clr-outline
|
||||
):focus {
|
||||
outline-width: 2px;
|
||||
outline-style: solid;
|
||||
outline-color: var(--cl-bg-40);
|
||||
.dark & {
|
||||
outline-color: var(--cd-bg-40);
|
||||
}
|
||||
}
|
||||
|
||||
:is(.text-primary,
|
||||
.text-url
|
||||
) {
|
||||
color: var(--cl-prim-fg-80);
|
||||
.dark & {
|
||||
color: var(--cd-prim-fg-80);
|
||||
}
|
||||
}
|
||||
|
||||
.clr-footer {
|
||||
@apply clr-app text-gray-600 border-gray-400 dark:border-[#434343] dark:text-[#aaaaaa]
|
||||
color: var(--cl-fg-40);
|
||||
.dark & {
|
||||
color: var(--cd-fg-40);
|
||||
}
|
||||
|
||||
.clr-card {
|
||||
@apply bg-gray-50 dark:bg-[#272727]
|
||||
}
|
||||
|
||||
.clr-tab {
|
||||
@apply clr-border text-gray-700 dark:text-zinc-200 hover:bg-blue-200 dark:hover:bg-[#EA580C]
|
||||
}
|
||||
|
||||
.clr-hover {
|
||||
@apply hover:bg-gray-200 hover:text-gray-700 dark:hover:text-white dark:hover:bg-gray-500
|
||||
}
|
||||
|
||||
.clr-btn-nav {
|
||||
@apply text-gray-500 hover:text-gray-900 dark:text-gray-200 dark:hover:text-white focus:ring-gray-300 dark:focus:ring-gray-400 hover:bg-gray-100 dark:hover:bg-gray-600
|
||||
color: var(--cl-fg-60);
|
||||
.dark & {
|
||||
color: var(--cd-fg-60);
|
||||
}
|
||||
}
|
||||
|
||||
.clr-btn-primary {
|
||||
@apply text-white bg-blue-400 hover:bg-blue-600 dark:bg-orange-600 dark:hover:bg-orange-400 disabled:bg-gray-400 dark:disabled:bg-gray-600
|
||||
}
|
||||
|
||||
.clr-btn-green {
|
||||
@apply text-white bg-green-400 hover:bg-green-600 dark:bg-green-600 dark:hover:bg-green-400 dark:text-black disabled:bg-gray-400 dark:disabled:bg-gray-600
|
||||
}
|
||||
|
||||
.clr-btn-blue {
|
||||
@apply text-white bg-blue-400 hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-400 dark:text-black disabled:bg-gray-400 dark:disabled:bg-gray-600
|
||||
}
|
||||
.clr-btn-default {
|
||||
@apply bg-[#f0f2f7] hover:bg-gray-300 dark:bg-[#434343] dark:hover:bg-[#606060] text-btn
|
||||
}
|
||||
|
||||
/* Transparent button */
|
||||
.clr-btn-clear {
|
||||
@apply dark:disabled:text-zinc-400 disabled:text-gray-400 text-gray-500 dark:text-zinc-200
|
||||
color: var(--cl-fg-60);
|
||||
&:disabled {
|
||||
color: var(--cl-fg-40);
|
||||
}
|
||||
.dark & {
|
||||
color: var(--cd-fg-60);
|
||||
&:disabled {
|
||||
color: var(--cd-fg-40);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.clr-checkbox {
|
||||
@apply bg-white dark:bg-[#181818] checked:bg-blue-700 dark:checked:bg-orange-500
|
||||
.clr-warning {
|
||||
background-color: var(--cl-red-bg-100);
|
||||
.dark & {
|
||||
background-color: var(--cd-red-bg-100);
|
||||
}
|
||||
}
|
||||
|
||||
.clr-input-red {
|
||||
@apply bg-red-300 dark:bg-red-700
|
||||
.text-warning {
|
||||
color: var(--cl-red-fg-100);
|
||||
.dark & {
|
||||
color: var(--cd-red-fg-100);
|
||||
}
|
||||
}
|
||||
|
||||
.text-url {
|
||||
@apply hover:text-blue-600 text-blue-400 dark:text-orange-500 dark:hover:text-orange-300
|
||||
.text-success {
|
||||
color: var(--cl-green-fg-100);
|
||||
.dark & {
|
||||
color: var(--cd-green-fg-100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cm-editor {
|
||||
border-color: var(--cl-bg-40);
|
||||
.dark & {
|
||||
border-color: var(--cd-bg-40);
|
||||
}
|
||||
@apply border shadow rounded px-1
|
||||
}
|
||||
.cm-editor.cm-focused {
|
||||
border-color: var(--cl-bg-40);
|
||||
outline-color: var(--cl-bg-40);
|
||||
.dark & {
|
||||
border-color: var(--cd-bg-40);
|
||||
outline-color: var(--cd-bg-40);
|
||||
}
|
||||
@apply border shadow rounded outline-2 outline
|
||||
}
|
||||
|
||||
.rdt_TableCell{
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.react-dropdown-select-item {
|
||||
color: var(--cl-fg-100);
|
||||
border-color: var(--cl-bg-40);
|
||||
background-color: var(--cl-bg-100);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--cl-prim-bg-60);
|
||||
}
|
||||
|
||||
.dark & {
|
||||
color: var(--cd-fg-100);
|
||||
border-color: var(--cd-bg-40);
|
||||
background-color: var(--cd-bg-100);
|
||||
&:hover {
|
||||
background-color: var(--cd-prim-bg-60);
|
||||
}
|
||||
|
||||
.text-red {
|
||||
@apply text-red-600 dark:text-red-400
|
||||
}
|
||||
|
||||
.text-green {
|
||||
@apply text-green-400 dark:text-green-500
|
||||
}
|
||||
|
||||
.text-primary {
|
||||
@apply text-blue-600 dark:text-orange-400
|
||||
}
|
||||
}
|
|
@ -38,7 +38,7 @@ function CreateRSFormPage() {
|
|||
}
|
||||
|
||||
function handleCancel() {
|
||||
if (location.key !== "default") {
|
||||
if (location.key !== 'default') {
|
||||
navigate(-1);
|
||||
} else {
|
||||
navigate('/library');
|
||||
|
|
|
@ -50,7 +50,7 @@ function PickerStrategy({ value, onChange }: PickerStrategyProps) {
|
|||
onChange={() => handleChange(LibraryFilterStrategy.CANONICAL)}
|
||||
value={value === LibraryFilterStrategy.CANONICAL}
|
||||
label='Неизменные'
|
||||
tooltip='Отображать только неизменные схемы'
|
||||
tooltip='Отображать только стандартные схемы'
|
||||
/>
|
||||
<DropdownCheckbox
|
||||
onChange={() => handleChange(LibraryFilterStrategy.PERSONAL)}
|
||||
|
|
|
@ -4,10 +4,9 @@ import { useNavigate } from 'react-router-dom';
|
|||
|
||||
import ConceptDataTable from '../../components/Common/ConceptDataTable';
|
||||
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
||||
import MiniButton from '../../components/Common/MiniButton';
|
||||
import TextURL from '../../components/Common/TextURL';
|
||||
import HelpLibrary from '../../components/Help/HelpLibrary';
|
||||
import { EducationIcon, EyeIcon, GroupIcon, HelpIcon, PlusIcon } from '../../components/Icons';
|
||||
import { EducationIcon, EyeIcon, GroupIcon, HelpIcon } from '../../components/Icons';
|
||||
import { useAuth } from '../../context/AuthContext';
|
||||
import { useUsers } from '../../context/UsersContext';
|
||||
import { prefixes } from '../../utils/constants';
|
||||
|
@ -26,10 +25,6 @@ function ViewLibrary({ items, cleanQuery }: ViewLibraryProps) {
|
|||
|
||||
const openRSForm = (item: ILibraryItem) => navigate(`/rsforms/${item.id}`);
|
||||
|
||||
function handleCreateNew() {
|
||||
navigate('/rsform-create');
|
||||
}
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
|
@ -43,9 +38,9 @@ function ViewLibrary({ items, cleanQuery }: ViewLibraryProps) {
|
|||
className='flex items-center justify-start gap-1'
|
||||
id={`${prefixes.library_list}${item.id}`}
|
||||
>
|
||||
{user && user.subscriptions.includes(item.id) && <p title="Отслеживаемая"><EyeIcon size={3}/></p>}
|
||||
{item.is_common && <p title="Общедоступная"><GroupIcon size={3}/></p>}
|
||||
{item.is_canonical && <p title="Неизменяемая"><EducationIcon size={3}/></p>}
|
||||
{user && user.subscriptions.includes(item.id) && <p title='Отслеживаемая'><EyeIcon size={3}/></p>}
|
||||
{item.is_common && <p title='Общедоступная'><GroupIcon size={3}/></p>}
|
||||
{item.is_canonical && <p title='Неизменная'><EducationIcon size={3}/></p>}
|
||||
</div>
|
||||
</>);
|
||||
},
|
||||
|
@ -91,16 +86,9 @@ function ViewLibrary({ items, cleanQuery }: ViewLibraryProps) {
|
|||
return (
|
||||
<div>
|
||||
<div className='relative w-full'>
|
||||
<div className='absolute top-0 left-0 z-20 flex gap-1 mt-2 ml-1'>
|
||||
<MiniButton
|
||||
onClick={handleCreateNew}
|
||||
tooltip='Создать схему'
|
||||
noHover
|
||||
disabled={!user || !user.id}
|
||||
icon={<PlusIcon color={!user || !user.id ? '' : 'text-primary'} size={5} />}
|
||||
/>
|
||||
<div className='absolute top-0 left-0 z-20 flex gap-1 mt-1 ml-5'>
|
||||
<div id='library-help' className='py-2'>
|
||||
<HelpIcon color='text-primary' size={5} />
|
||||
<HelpIcon color='text-primary' size={6} />
|
||||
</div>
|
||||
<ConceptTooltip anchorSelect='#library-help'>
|
||||
<div className='max-w-[35rem]'>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useLayoutEffect, useState } from 'react';
|
||||
|
||||
import BackendError from '../../components/BackendError'
|
||||
import { Loader } from '../../components/Common/Loader'
|
||||
import { ConceptLoader } from '../../components/Common/ConceptLoader'
|
||||
import { useLibrary } from '../../context/LibraryContext';
|
||||
import { ILibraryFilter, ILibraryItem } from '../../utils/models';
|
||||
import SearchPanel from './SearchPanel';
|
||||
|
@ -20,7 +20,7 @@ function LibraryPage() {
|
|||
|
||||
return (
|
||||
<div className='w-full'>
|
||||
{ library.loading && <Loader /> }
|
||||
{ library.loading && <ConceptLoader /> }
|
||||
{ library.error && <BackendError error={library.error} />}
|
||||
{ !library.loading && library.items &&
|
||||
<div className='flex flex-col w-full'>
|
||||
|
|
|
@ -1,15 +1,30 @@
|
|||
import axios from 'axios';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
|
||||
import BackendError from '../components/BackendError';
|
||||
import BackendError, { ErrorInfo } from '../components/BackendError';
|
||||
import Form from '../components/Common/Form';
|
||||
import SubmitButton from '../components/Common/SubmitButton';
|
||||
import TextInput from '../components/Common/TextInput';
|
||||
import TextURL from '../components/Common/TextURL';
|
||||
import { useAuth } from '../context/AuthContext';
|
||||
import { useConceptTheme } from '../context/ThemeContext';
|
||||
import { IUserLoginData } from '../utils/models';
|
||||
|
||||
function ProcessError({error}: {error: ErrorInfo}): React.ReactElement {
|
||||
if (axios.isAxiosError(error) && error.response && error.response.status === 400) {
|
||||
return (
|
||||
<div className='mt-2 text-sm select-text text-warning'>
|
||||
На Портале отсутствует такое сочетание имени пользователя и пароля
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return ( <BackendError error={error} />);
|
||||
}
|
||||
}
|
||||
|
||||
function LoginPage() {
|
||||
const {mainHeight} = useConceptTheme();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const search = useLocation().search;
|
||||
|
@ -36,7 +51,7 @@ function LoginPage() {
|
|||
password: password
|
||||
};
|
||||
login(data, () => {
|
||||
if (location.key !== "default") {
|
||||
if (location.key !== 'default') {
|
||||
navigate(-1);
|
||||
} else {
|
||||
navigate('/library');
|
||||
|
@ -46,10 +61,12 @@ function LoginPage() {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='flex justify-center w-full'>
|
||||
<div className='py-2'> { user
|
||||
? <b>{`Вы вошли в систему как ${user.username}`}</b>
|
||||
:
|
||||
<div className='flex items-center justify-center w-full select-none' style={{minHeight: mainHeight}}>
|
||||
{ user &&
|
||||
<div className='py-2 font-semibold'>
|
||||
{`Вы вошли в систему как ${user.username}`}
|
||||
</div>}
|
||||
{ !user &&
|
||||
<Form
|
||||
title='Вход в Портал'
|
||||
onSubmit={handleSubmit}
|
||||
|
@ -82,9 +99,9 @@ function LoginPage() {
|
|||
<TextURL text='Восстановить пароль...' href='/restore-password' />
|
||||
<TextURL text='Нет аккаунта? Зарегистрируйтесь...' href='/signup' />
|
||||
</div>
|
||||
{ error && <BackendError error={error} />}
|
||||
{ error && <ProcessError error={error} />}
|
||||
</Form>
|
||||
}</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,13 +9,13 @@ interface TopicsListProps {
|
|||
|
||||
function TopicsList({ activeTopic, onChangeTopic }: TopicsListProps) {
|
||||
return (
|
||||
<div className='sticky top-0 left-0 border-r border-b min-w-[13rem] pt-2 select-none flex flex-col clr-bg-pop clr-border'>
|
||||
<div className='mb-2 font-bold text-center'>Справка</div>
|
||||
<div className='sticky top-0 left-0 border-r border-b min-w-[13rem] pt-2 select-none flex flex-col clr-controls'>
|
||||
<div className='mb-2 font-semibold text-center'>Справка</div>
|
||||
{ [... mapTopicInfo.entries()].map(
|
||||
([topic, info], index) => {
|
||||
return (
|
||||
<div key={`${prefixes.topic_list}${index}`}
|
||||
className={`px-3 py-1 border-y cursor-pointer hover:bg-blue-200 dark:hover:bg-[#EA580C] clr-border ${activeTopic === topic ? 'font-bold bg-blue-200 dark:bg-[#EA580C] ' : ''}`}
|
||||
className={`px-3 py-1 border-y cursor-pointer clr-hover ${activeTopic === topic ? 'font-semibold clr-selected ' : ''}`}
|
||||
title={info.tooltip}
|
||||
onClick={() => onChangeTopic(topic)}
|
||||
>
|
||||
|
|
|
@ -74,7 +74,6 @@ function DlgCreateCst({ hideWindow, initial, onCreate }: DlgCreateCstProps) {
|
|||
/>
|
||||
<RSInput id='expression' label='Формальное выражение'
|
||||
editable
|
||||
className='mt-2'
|
||||
height='5.5rem'
|
||||
value={expression}
|
||||
onChange={value => setExpression(value)}
|
||||
|
|
|
@ -133,7 +133,7 @@ function EditorConstituenta({
|
|||
|
||||
return (
|
||||
<div className='flex items-stretch w-full gap-2 mb-2 justify-stretch'>
|
||||
<form onSubmit={handleSubmit} className='min-w-[50rem] max-w-min px-4 py-2 border-y border-r clr-border'>
|
||||
<form onSubmit={handleSubmit} className='min-w-[50rem] max-w-min px-4 py-2 border-y border-r'>
|
||||
<div className='relative'>
|
||||
<div className='absolute top-0 left-0'>
|
||||
<MiniButton
|
||||
|
@ -151,13 +151,13 @@ function EditorConstituenta({
|
|||
tooltip='Удалить редактируемую конституенту'
|
||||
disabled={!isEnabled}
|
||||
onClick={handleDelete}
|
||||
icon={<DumpBinIcon size={5} color={isEnabled ? 'text-red' : ''} />}
|
||||
icon={<DumpBinIcon size={5} color={isEnabled ? 'text-warning' : ''} />}
|
||||
/>
|
||||
<MiniButton
|
||||
tooltip='Создать конституенты после данной'
|
||||
disabled={!isEnabled}
|
||||
onClick={handleCreateCst}
|
||||
icon={<SmallPlusIcon size={5} color={isEnabled ? 'text-green' : ''} />}
|
||||
icon={<SmallPlusIcon size={5} color={isEnabled ? 'text-success' : ''} />}
|
||||
/>
|
||||
<div id='cst-help' className='flex items-center ml-[6px]'>
|
||||
<HelpIcon color='text-primary' size={5} />
|
||||
|
@ -167,7 +167,7 @@ function EditorConstituenta({
|
|||
</ConceptTooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-center justify-center w-full pr-10'>
|
||||
<div className='flex items-center justify-center w-full gap-1 pr-10'>
|
||||
<div className='font-semibold w-fit'>
|
||||
<span className=''>Конституента </span>
|
||||
<span className='ml-4'>{alias}</span>
|
||||
|
@ -194,6 +194,7 @@ function EditorConstituenta({
|
|||
<TextArea id='typification' label='Типизация'
|
||||
rows={1}
|
||||
value={typification}
|
||||
colorClass='clr-app'
|
||||
disabled
|
||||
/>
|
||||
<EditorRSExpression id='expression' label='Формальное выражение'
|
||||
|
|
|
@ -6,7 +6,7 @@ import ConceptDataTable from '../../components/Common/ConceptDataTable';
|
|||
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
||||
import Divider from '../../components/Common/Divider';
|
||||
import HelpRSFormItems from '../../components/Help/HelpRSFormItems';
|
||||
import { ArrowDownIcon, ArrowsRotateIcon, ArrowUpIcon, DumpBinIcon, HelpIcon, SmallPlusIcon } from '../../components/Icons';
|
||||
import { ArrowDownIcon, ArrowUpIcon, DumpBinIcon, HelpIcon, MeshIcon, SmallPlusIcon } from '../../components/Icons';
|
||||
import { useRSForm } from '../../context/RSFormContext';
|
||||
import { useConceptTheme } from '../../context/ThemeContext';
|
||||
import { prefixes } from '../../utils/constants';
|
||||
|
@ -90,7 +90,7 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps)
|
|||
|
||||
// Generate new names for all constituents
|
||||
function handleReindex() {
|
||||
resetAliases(() => toast.success('Переиндексация конституент успешна'));
|
||||
resetAliases(() => toast.success('Имена конституент обновлены'));
|
||||
}
|
||||
|
||||
function handleCreateCst(type?: CstType) {
|
||||
|
@ -185,7 +185,7 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps)
|
|||
return (<>
|
||||
<div
|
||||
id={`${prefixes.cst_list}${cst.alias}`}
|
||||
className='w-full text-center rounded-md'
|
||||
className='w-full px-1 text-center rounded-md whitespace-nowrap'
|
||||
style={{backgroundColor: getCstStatusColor(cst.status, colors)}}
|
||||
>
|
||||
{cst.alias}
|
||||
|
@ -284,22 +284,22 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps)
|
|||
/>
|
||||
<Button
|
||||
tooltip='Удалить выбранные'
|
||||
icon={<DumpBinIcon color={!nothingSelected ? 'text-red' : ''} size={6}/>}
|
||||
icon={<DumpBinIcon color={isEditable && !nothingSelected ? 'text-warning' : ''} size={6}/>}
|
||||
disabled={!isEditable || nothingSelected}
|
||||
dense
|
||||
onClick={handleDelete}
|
||||
/>
|
||||
<Divider vertical margins='my-1' />
|
||||
<Button
|
||||
tooltip='Переиндексировать имена'
|
||||
icon={<ArrowsRotateIcon color='text-primary' size={6}/>}
|
||||
tooltip='Сбросить имена'
|
||||
icon={<MeshIcon color={isEditable ? 'text-primary': ''} size={6}/>}
|
||||
dense
|
||||
disabled={!isEditable}
|
||||
onClick={handleReindex}
|
||||
/>
|
||||
<Button
|
||||
tooltip='Новая конституента'
|
||||
icon={<SmallPlusIcon color='text-green' size={6}/>}
|
||||
icon={<SmallPlusIcon color={isEditable ? 'text-success': ''} size={6}/>}
|
||||
dense
|
||||
disabled={!isEditable}
|
||||
onClick={() => handleCreateCst()}
|
||||
|
@ -320,7 +320,7 @@ function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps)
|
|||
<div id='items-table-help'>
|
||||
<HelpIcon color='text-primary' size={6} />
|
||||
</div>
|
||||
<ConceptTooltip anchorSelect='#items-table-help'>
|
||||
<ConceptTooltip anchorSelect='#items-table-help' offset={30}>
|
||||
<HelpRSFormItems />
|
||||
</ConceptTooltip>
|
||||
</div>
|
||||
|
|
|
@ -2,7 +2,7 @@ import { ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
|||
import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import Button from '../../components/Common/Button';
|
||||
import { Loader } from '../../components/Common/Loader';
|
||||
import { ConceptLoader } from '../../components/Common/ConceptLoader';
|
||||
import RSInput from '../../components/RSInput';
|
||||
import { TextWrapper } from '../../components/RSInput/textEditing';
|
||||
import { useRSForm } from '../../context/RSFormContext';
|
||||
|
@ -187,7 +187,7 @@ function EditorRSExpression({
|
|||
return (
|
||||
<div className='flex flex-col items-start [&:not(:first-child)]:mt-3 w-full min-h-[15.75rem]'>
|
||||
<div className='relative w-full'>
|
||||
<div className='absolute top-[-0.1rem] right-0'>
|
||||
<div className='absolute top-[-0.3rem] right-0'>
|
||||
<StatusBar
|
||||
isModified={isModified}
|
||||
constituenta={activeCst}
|
||||
|
@ -196,7 +196,7 @@ function EditorRSExpression({
|
|||
</div>
|
||||
</div>
|
||||
<RSInput innerref={rsInput}
|
||||
className='mt-2 text-lg'
|
||||
className='text-lg'
|
||||
height='10.1rem'
|
||||
value={value}
|
||||
editable={!disabled}
|
||||
|
@ -218,7 +218,7 @@ function EditorRSExpression({
|
|||
</div>
|
||||
{ (isActive || loading || parseData) &&
|
||||
<div className='w-full overflow-y-auto border mt-2 max-h-[14rem] min-h-[4.2rem]'>
|
||||
{ loading && <Loader size={6} />}
|
||||
{ loading && <ConceptLoader size={6} />}
|
||||
{ !loading && parseData &&
|
||||
<ParsingResult
|
||||
data={parseData}
|
||||
|
|
|
@ -80,7 +80,7 @@ function EditorRSForm({ onDestroy, onClaim, onShare, isModified, setIsModified,
|
|||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className='flex-grow max-w-[35.3rem] px-4 py-2 border-y border-r clr-border min-w-fit'>
|
||||
<form onSubmit={handleSubmit} className='flex-grow max-w-[35.3rem] px-4 py-2 border-y border-r min-w-fit'>
|
||||
<div className='relative w-full'>
|
||||
<div className='absolute top-0 right-0 flex'>
|
||||
<MiniButton
|
||||
|
@ -95,7 +95,7 @@ function EditorRSForm({ onDestroy, onClaim, onShare, isModified, setIsModified,
|
|||
/>
|
||||
<MiniButton
|
||||
tooltip={isClaimable ? 'Стать владельцем' : 'Невозможно стать владельцем' }
|
||||
icon={<CrownIcon size={5} color={!isClaimable ? '' : 'text-green'}/>}
|
||||
icon={<CrownIcon size={5} color={!isClaimable ? '' : 'text-success'}/>}
|
||||
disabled={!isClaimable || !user}
|
||||
onClick={onClaim}
|
||||
/>
|
||||
|
@ -103,7 +103,7 @@ function EditorRSForm({ onDestroy, onClaim, onShare, isModified, setIsModified,
|
|||
tooltip='Удалить схему'
|
||||
disabled={!isEditable}
|
||||
onClick={onDestroy}
|
||||
icon={<DumpBinIcon size={5} color={isEditable ? 'text-red' : ''} />}
|
||||
icon={<DumpBinIcon size={5} color={isEditable ? 'text-warning' : ''} />}
|
||||
/>
|
||||
<div id='rsform-help' className='py-1 ml-1'>
|
||||
<HelpIcon color='text-primary' size={5} />
|
||||
|
|
|
@ -66,7 +66,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
|||
const { darkMode, colors, noNavigation } = useConceptTheme();
|
||||
|
||||
const [ layout, setLayout ] = useLocalStorage<LayoutTypes>('graph_layout', 'treeTd2d');
|
||||
const [ coloringScheme, setColoringScheme ] = useLocalStorage<ColoringScheme>('graph_coloring', 'none');
|
||||
const [ coloringScheme, setColoringScheme ] = useLocalStorage<ColoringScheme>('graph_coloring', 'type');
|
||||
const [ orbit, setOrbit ] = useState(false);
|
||||
|
||||
const [ noHermits, setNoHermits ] = useLocalStorage('graph_no_hermits', true);
|
||||
|
@ -355,7 +355,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
|||
initial={getOptions()}
|
||||
onConfirm={handleChangeOptions}
|
||||
/>}
|
||||
<div className='flex flex-col border-r border-b min-w-[13.5rem] max-w-min px-2 pb-2 text-sm select-none clr-border' style={{height: canvasHeight}}>
|
||||
<div className='flex flex-col border-r border-b min-w-[13.5rem] max-w-min px-2 pb-2 text-sm select-none' style={{height: canvasHeight}}>
|
||||
{hoverCst &&
|
||||
<div className='relative'>
|
||||
<InfoConstituenta
|
||||
|
@ -374,13 +374,13 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
|||
<div>
|
||||
<MiniButton
|
||||
tooltip='Удалить выбранные'
|
||||
icon={<DumpBinIcon color={!nothingSelected ? 'text-red' : ''} size={5}/>}
|
||||
icon={<DumpBinIcon color={!nothingSelected ? 'text-warning' : ''} size={5}/>}
|
||||
disabled={!isEditable || nothingSelected}
|
||||
onClick={handleDeleteCst}
|
||||
/>
|
||||
<MiniButton
|
||||
tooltip='Новая конституента'
|
||||
icon={<SmallPlusIcon color='text-green' size={5}/>}
|
||||
icon={<SmallPlusIcon color='text-success' size={5}/>}
|
||||
disabled={!isEditable}
|
||||
onClick={handleCreateCst}
|
||||
/>
|
||||
|
@ -460,7 +460,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='w-full h-full overflow-auto border'>
|
||||
<div className='w-full h-full overflow-auto'>
|
||||
<div
|
||||
className='relative'
|
||||
style={{width: canvasWidth, height: canvasHeight, borderBottomWidth: noNavigation ? '1px': ''}}
|
||||
|
|
|
@ -6,7 +6,7 @@ import { toast } from 'react-toastify';
|
|||
|
||||
import BackendError from '../../components/BackendError';
|
||||
import ConceptTab from '../../components/Common/ConceptTab';
|
||||
import { Loader } from '../../components/Common/Loader';
|
||||
import { ConceptLoader } from '../../components/Common/ConceptLoader';
|
||||
import { useLibrary } from '../../context/LibraryContext';
|
||||
import { useRSForm } from '../../context/RSFormContext';
|
||||
import { useConceptTheme } from '../../context/ThemeContext';
|
||||
|
@ -91,12 +91,18 @@ function RSTabs() {
|
|||
|
||||
const navigateTo = useCallback(
|
||||
(tab: RSTabID, activeID?: number) => {
|
||||
if (!schema) {
|
||||
return;
|
||||
}
|
||||
if (activeID) {
|
||||
navigate(`/rsforms/${schema!.id}?tab=${tab}&active=${activeID}`, {
|
||||
navigate(`/rsforms/${schema.id}?tab=${tab}&active=${activeID}`, {
|
||||
replace: tab === activeTab && tab !== RSTabID.CST_EDIT
|
||||
});
|
||||
} else if (tab !== activeTab && tab === RSTabID.CST_EDIT && schema.items.length > 0) {
|
||||
activeID = schema.items[0].id;
|
||||
navigate(`/rsforms/${schema.id}?tab=${tab}&active=${activeID}`, { replace: true });
|
||||
} else {
|
||||
navigate(`/rsforms/${schema!.id}?tab=${tab}`);
|
||||
navigate(`/rsforms/${schema.id}?tab=${tab}`);
|
||||
}
|
||||
}, [navigate, schema, activeTab]);
|
||||
|
||||
|
@ -272,7 +278,7 @@ function RSTabs() {
|
|||
|
||||
return (
|
||||
<div className='w-full'>
|
||||
{ loading && <Loader /> }
|
||||
{ loading && <ConceptLoader /> }
|
||||
{ error && <BackendError error={error} />}
|
||||
{ schema && !loading && <>
|
||||
{showUpload &&
|
||||
|
@ -311,9 +317,9 @@ function RSTabs() {
|
|||
selectedIndex={activeTab}
|
||||
onSelect={onSelectTab}
|
||||
defaultFocus={true}
|
||||
selectedTabClassName='font-bold bg-blue-200 dark:bg-[#EA580C]'
|
||||
selectedTabClassName='font-semibold clr-selected'
|
||||
>
|
||||
<TabList className='flex items-start pl-2 border-b border-r-2 select-none w-fit clr-bg-pop clr-border'>
|
||||
<TabList className='flex items-start pl-2 border-b border-r-2 select-none justify-stretch w-fit clr-controls h-[1.9rem]'>
|
||||
<RSTabsMenu
|
||||
onDownload={onDownloadSchema}
|
||||
onDestroy={onDestroySchema}
|
||||
|
|
|
@ -67,7 +67,7 @@ function RSTabsMenu({
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='flex items-stretch w-fit'>
|
||||
<div className='flex items-stretch h-full w-fit'>
|
||||
<div ref={schemaMenu.ref}>
|
||||
<Button
|
||||
tooltip='Действия'
|
||||
|
@ -99,13 +99,13 @@ function RSTabsMenu({
|
|||
</DropdownButton>
|
||||
<DropdownButton disabled={!isEditable} onClick={handleUpload}>
|
||||
<div className='inline-flex items-center justify-start gap-2'>
|
||||
<UploadIcon color={isEditable ? 'text-red' : ''} size={4}/>
|
||||
<UploadIcon color={isEditable ? 'text-warning' : ''} size={4}/>
|
||||
<p>Загрузить из Экстеора</p>
|
||||
</div>
|
||||
</DropdownButton>
|
||||
<DropdownButton disabled={!isEditable} onClick={handleDelete}>
|
||||
<span className='inline-flex items-center justify-start gap-2'>
|
||||
<DumpBinIcon color={isEditable ? 'text-red' : ''} size={4} />
|
||||
<DumpBinIcon color={isEditable ? 'text-warning' : ''} size={4} />
|
||||
<p>Удалить схему</p>
|
||||
</span>
|
||||
</DropdownButton>
|
||||
|
@ -122,7 +122,7 @@ function RSTabsMenu({
|
|||
tooltip={'измнение: ' + (isEditable ? '[доступно]' : '[запрещено]')}
|
||||
borderClass=''
|
||||
widthClass='h-full w-fit'
|
||||
icon={<PenIcon size={5} color={isEditable ? 'text-green' : 'text-red'}/>}
|
||||
icon={<PenIcon size={5} color={isEditable ? 'text-success' : 'text-warning'}/>}
|
||||
dense
|
||||
onClick={editMenu.toggle}
|
||||
/>
|
||||
|
@ -134,7 +134,7 @@ function RSTabsMenu({
|
|||
tooltip={!user || !isClaimable ? 'Стать владельцем можно только для общей изменяемой схемы' : ''}
|
||||
>
|
||||
<div className='inline-flex items-center gap-1 justify-normal'>
|
||||
<span className={isOwned ? 'text-green' : ''}><CrownIcon size={4} /></span>
|
||||
<span className={isOwned ? 'text-success' : ''}><CrownIcon size={4} /></span>
|
||||
<p>
|
||||
{ isOwned && <b>Владелец схемы</b> }
|
||||
{ !isOwned && <b>Стать владельцем</b> }
|
||||
|
|
|
@ -20,7 +20,7 @@ function ParsingResult({ data, onShowAST, onShowError }: ParsingResultProps) {
|
|||
<p>Ошибок: <b>{errorCount}</b> | Предупреждений: <b>{warningsCount}</b></p>
|
||||
{data.errors.map((error, index) => {
|
||||
return (
|
||||
<p key={`error-${index}`} className='cursor-pointer text-red' onClick={() => onShowError(error)}>
|
||||
<p key={`error-${index}`} className='cursor-pointer text-warning' onClick={() => onShowError(error)}>
|
||||
<span className='mr-1 font-semibold underline'>{error.isCritical ? 'Ошибка' : 'Предупреждение'} {getRSErrorPrefix(error)}:</span>
|
||||
<span> {getRSErrorMessage(error)}</span>
|
||||
</p>
|
||||
|
|
|
@ -17,7 +17,7 @@ function RSTokenButton({ id, disabled, onInsert }: RSTokenButtonProps) {
|
|||
onClick={() => onInsert(id)}
|
||||
title={data.tooltip}
|
||||
tabIndex={-1}
|
||||
className={`px-1 cursor-pointer border rounded-none h-7 ${width} clr-btn-clear`}
|
||||
className={`px-1 cursor-pointer border rounded-none h-7 ${width} clr-outline clr-btn-clear`}
|
||||
>
|
||||
{data.text && <span className='whitespace-nowrap'>{data.text}</span>}
|
||||
</button>
|
||||
|
|
|
@ -90,7 +90,7 @@ function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }:
|
|||
{
|
||||
when: (cst: IConstituenta) => cst.id === activeID,
|
||||
style: {
|
||||
backgroundColor: colors.selection,
|
||||
backgroundColor: colors.bgSelected,
|
||||
},
|
||||
}
|
||||
], [activeID, colors]);
|
||||
|
@ -109,7 +109,7 @@ function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }:
|
|||
return (<>
|
||||
<div
|
||||
id={`${prefixes.cst_list}${cst.alias}`}
|
||||
className='w-full text-center rounded-md'
|
||||
className='w-full px-1 text-center rounded-md min-w-fit whitespace-nowrap'
|
||||
style={{backgroundColor: getCstStatusColor(cst.status, colors)}}
|
||||
>
|
||||
{cst.alias}
|
||||
|
@ -122,7 +122,7 @@ function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }:
|
|||
conditionalCellStyles: [
|
||||
{
|
||||
when: (cst: IConstituenta) => isMockCst(cst),
|
||||
style: {backgroundColor: colors.selectionError}
|
||||
style: {backgroundColor: colors.bgWarning}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -135,7 +135,7 @@ function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }:
|
|||
conditionalCellStyles: [
|
||||
{
|
||||
when: (cst: IConstituenta) => isMockCst(cst),
|
||||
style: {backgroundColor: colors.selectionError}
|
||||
style: {backgroundColor: colors.bgWarning}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -150,7 +150,7 @@ function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }:
|
|||
conditionalCellStyles: [
|
||||
{
|
||||
when: (cst: IConstituenta) => isMockCst(cst),
|
||||
style: {backgroundColor: colors.selectionError}
|
||||
style: {backgroundColor: colors.fgWarning}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -165,7 +165,7 @@ function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }:
|
|||
}, [noNavigation, baseHeight]);
|
||||
|
||||
return (<>
|
||||
<div className='sticky top-0 left-0 right-0 z-10 flex items-start justify-between w-full gap-1 px-2 py-1 border-b rounded clr-input clr-border'>
|
||||
<div className='sticky top-0 left-0 right-0 z-10 flex items-start justify-between w-full gap-1 px-2 py-1 border-b rounded clr-input'>
|
||||
<MatchModePicker
|
||||
value={filterMatch}
|
||||
onChange={setFilterMatch}
|
||||
|
|
|
@ -27,7 +27,7 @@ function RegisterPage() {
|
|||
}, [username, email, password, password2, setError]);
|
||||
|
||||
function handleCancel() {
|
||||
if (location.key !== "default") {
|
||||
if (location.key !== 'default') {
|
||||
navigate(-1);
|
||||
} else {
|
||||
navigate('/library');
|
||||
|
@ -80,7 +80,7 @@ function RegisterPage() {
|
|||
<div className='text-sm'>
|
||||
<p>- используйте уникальный пароль</p>
|
||||
<p>- портал функционирует в тестовом режиме</p>
|
||||
<p className='font-semibold text-red'>- безопасность информации не гарантируется</p>
|
||||
<p className='font-semibold text-warning'>- безопасность информации не гарантируется</p>
|
||||
{/* <p>- минимум 8 символов</p>
|
||||
<p>- большие, маленькие буквы, цифры</p>
|
||||
<p>- минимум 1 спец. символ</p> */}
|
||||
|
|
|
@ -19,7 +19,7 @@ function EditorPassword() {
|
|||
|
||||
const passwordColor = useMemo(
|
||||
() => {
|
||||
return !!newPassword && !!newPasswordRepeat && newPassword !== newPasswordRepeat ? 'clr-input-red' : 'clr-input';
|
||||
return !!newPassword && !!newPasswordRepeat && newPassword !== newPasswordRepeat ? 'clr-warning' : 'clr-input';
|
||||
}, [newPassword, newPasswordRepeat]);
|
||||
|
||||
const canSubmit = useMemo(
|
||||
|
@ -48,7 +48,7 @@ function EditorPassword() {
|
|||
}, [newPassword, oldPassword, newPasswordRepeat, setError]);
|
||||
|
||||
return (
|
||||
<div className='flex py-2 border-l-2 clr-border max-w-[14rem]'>
|
||||
<div className='flex py-2 border-l-2 max-w-[14rem]'>
|
||||
<form onSubmit={handleSubmit} className='flex flex-col justify-between px-6 min-w-fit'>
|
||||
<div>
|
||||
<TextInput id='old_password'
|
||||
|
@ -59,7 +59,7 @@ function EditorPassword() {
|
|||
/>
|
||||
<TextInput id='new_password' type='password'
|
||||
colorClass={passwordColor}
|
||||
label="Новый пароль"
|
||||
label='Новый пароль'
|
||||
value={newPassword}
|
||||
onChange={event => {
|
||||
setNewPassword(event.target.value);
|
||||
|
@ -67,7 +67,7 @@ function EditorPassword() {
|
|||
/>
|
||||
<TextInput id='new_password_repeat' type='password'
|
||||
colorClass={passwordColor}
|
||||
label="Повторите новый"
|
||||
label='Повторите новый'
|
||||
value={newPasswordRepeat}
|
||||
onChange={event => {
|
||||
setNewPasswordRepeat(event.target.value);
|
||||
|
|
|
@ -61,12 +61,12 @@ function EditorProfile() {
|
|||
onChange={event => setUsername(event.target.value)}
|
||||
/>
|
||||
<TextInput id='first_name'
|
||||
label="Имя"
|
||||
label='Имя'
|
||||
value={first_name}
|
||||
onChange={event => setFirstName(event.target.value)}
|
||||
/>
|
||||
<TextInput id='last_name' label="Фамилия" value={last_name} onChange={event => setLastName(event.target.value)}/>
|
||||
<TextInput id='email' label="Электронная почта" value={email} onChange={event => setEmail(event.target.value)}/>
|
||||
<TextInput id='last_name' label='Фамилия' value={last_name} onChange={event => setLastName(event.target.value)}/>
|
||||
<TextInput id='email' label='Электронная почта' value={email} onChange={event => setEmail(event.target.value)}/>
|
||||
</div>
|
||||
<div className='flex justify-center w-full mt-10'>
|
||||
<SubmitButton
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useMemo, useState } from 'react';
|
||||
|
||||
import BackendError from '../../components/BackendError';
|
||||
import { Loader } from '../../components/Common/Loader';
|
||||
import { ConceptLoader } from '../../components/Common/ConceptLoader';
|
||||
import MiniButton from '../../components/Common/MiniButton';
|
||||
import { EyeIcon, EyeOffIcon } from '../../components/Icons';
|
||||
import { useAuth } from '../../context/AuthContext';
|
||||
|
@ -25,7 +25,7 @@ function UserTabs() {
|
|||
|
||||
return (
|
||||
<div className='w-full'>
|
||||
{ loading && <Loader /> }
|
||||
{ loading && <ConceptLoader /> }
|
||||
{ error && <BackendError error={error} />}
|
||||
{ user &&
|
||||
<div className='flex justify-center gap-2 py-2'>
|
||||
|
|
|
@ -45,7 +45,7 @@ function ViewSubscriptions({items}: ViewSubscriptionsProps) {
|
|||
|
||||
return (
|
||||
<ConceptDataTable
|
||||
className='h-full overflow-auto border clr-border'
|
||||
className='h-full overflow-auto border'
|
||||
columns={columns}
|
||||
data={items}
|
||||
defaultSortFieldId='time_update'
|
||||
|
|
|
@ -5,23 +5,19 @@ export interface IColorTheme {
|
|||
teal: string
|
||||
orange: string
|
||||
|
||||
text: string
|
||||
bgDefault: string
|
||||
bgInput: string
|
||||
bgControls: string
|
||||
bgDisabled: string
|
||||
bgHover: string
|
||||
bgSelected: string
|
||||
bgWarning: string
|
||||
|
||||
input: string
|
||||
inputDisabled: string
|
||||
selection: string
|
||||
selectionError: string
|
||||
border: string
|
||||
|
||||
// bg100: string
|
||||
// bg70: string
|
||||
// bg50: string
|
||||
|
||||
// fg100: string
|
||||
// fg70: string
|
||||
// fg50: string
|
||||
|
||||
// primary: string
|
||||
// secondary: string
|
||||
fgDefault: string
|
||||
fgDisabled: string
|
||||
fgWarning: string
|
||||
}
|
||||
|
||||
// =========== GENERAL THEMES =========
|
||||
|
@ -32,63 +28,94 @@ export const lightT: IColorTheme = {
|
|||
teal: '#a5e9fa',
|
||||
orange: '#ffbb80',
|
||||
|
||||
text: '#000000',
|
||||
bgDefault: 'var(--cl-bg-100)',
|
||||
bgInput: 'var(--cl-bg-120)',
|
||||
bgControls: 'var(--cl-bg-80)',
|
||||
bgDisabled: 'var(--cl-bg-60)',
|
||||
bgHover: 'var(--cl-prim-bg-60)',
|
||||
bgSelected: 'var(--cl-prim-bg-80)',
|
||||
bgWarning: 'var(--cl-red-bg-100)',
|
||||
|
||||
input: '#ffffff',
|
||||
inputDisabled: '#f0f2f7',
|
||||
selection: '#def1ff',
|
||||
selectionError: '#ffc9c9'
|
||||
border: 'var(--cl-bg-40)',
|
||||
|
||||
fgDefault: 'var(--cl-fg-100)',
|
||||
fgDisabled: 'var(--cl-fg-60)',
|
||||
fgWarning: 'var(--cl-red-fg-100)'
|
||||
};
|
||||
|
||||
export const darkT: IColorTheme = {
|
||||
red: '#bf0d00',
|
||||
green: '#2b8000',
|
||||
blue: '#bf0d00',
|
||||
teal: '#0099bf',
|
||||
blue: '#394bbf',
|
||||
teal: '#007a99',
|
||||
orange: '#964600',
|
||||
|
||||
text: '#e4e4e7',
|
||||
bgDefault: 'var(--cd-bg-100)',
|
||||
bgInput: 'var(--cd-bg-120)',
|
||||
bgControls: 'var(--cd-bg-80)',
|
||||
bgDisabled: 'var(--cd-bg-60)',
|
||||
bgHover: 'var(--cd-prim-bg-60)',
|
||||
bgSelected: 'var(--cd-prim-bg-80)',
|
||||
bgWarning: 'var(--cd-red-bg-100)',
|
||||
|
||||
input: '#181818',
|
||||
inputDisabled: '#272727', // bg-gray-700
|
||||
selection: '#394bbf',
|
||||
selectionError: '#592b2b'
|
||||
border: 'var(--cd-bg-40)',
|
||||
|
||||
fgDefault: 'var(--cd-fg-100)',
|
||||
fgDisabled: 'var(--cd-fg-60)',
|
||||
fgWarning: 'var(--cd-red-fg-100)'
|
||||
};
|
||||
|
||||
|
||||
// ========= DATA TABLE THEMES ========
|
||||
export const dataTableLightT = {
|
||||
text: {
|
||||
primary: lightT.fgDefault,
|
||||
secondary: lightT.fgDefault,
|
||||
disabled: lightT.fgDisabled
|
||||
},
|
||||
background: {
|
||||
default: lightT.bgDefault
|
||||
},
|
||||
highlightOnHover: {
|
||||
default: lightT.bgHover,
|
||||
text: lightT.fgDefault
|
||||
},
|
||||
divider: {
|
||||
default: '#d1d5db'
|
||||
default: lightT.border
|
||||
},
|
||||
striped: {
|
||||
default: '#f0f2f7'
|
||||
default: lightT.bgControls,
|
||||
text: lightT.fgDefault
|
||||
},
|
||||
selected: {
|
||||
default: lightT.bgSelected,
|
||||
text: lightT.fgDefault
|
||||
}
|
||||
}
|
||||
|
||||
export const dataTableDarkT = {
|
||||
text: {
|
||||
primary: 'rgba(228, 228, 231, 1)',
|
||||
secondary: 'rgba(228, 228, 231, 0.87)',
|
||||
disabled: 'rgba(228, 228, 231, 0.54)'
|
||||
primary: darkT.fgDefault,
|
||||
secondary: darkT.fgDefault,
|
||||
disabled: darkT.fgDisabled
|
||||
},
|
||||
background: {
|
||||
default: '#181818'
|
||||
default: darkT.bgDefault
|
||||
},
|
||||
highlightOnHover: {
|
||||
default: '#606060',
|
||||
text: 'rgba(228, 228, 231, 1)'
|
||||
default: darkT.bgHover,
|
||||
text: darkT.fgDefault
|
||||
},
|
||||
divider: {
|
||||
default: '#6b6b6b'
|
||||
default: darkT.border
|
||||
},
|
||||
striped: {
|
||||
default: '#272727',
|
||||
text: 'rgba(228, 228, 228, 1)'
|
||||
default: darkT.bgControls,
|
||||
text: darkT.fgDefault
|
||||
},
|
||||
selected: {
|
||||
default: '#181818',
|
||||
text: 'rgba(228, 228, 231, 1)'
|
||||
default: darkT.bgSelected,
|
||||
text: darkT.fgDefault
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -144,7 +171,7 @@ export const graphLightT = {
|
|||
|
||||
export const graphDarkT = {
|
||||
canvas: {
|
||||
background: '#1f2937'
|
||||
background: '#181818' // var(--cd-bg-100)
|
||||
},
|
||||
node: {
|
||||
fill: '#7a8c9e',
|
||||
|
@ -193,27 +220,21 @@ export const graphDarkT = {
|
|||
|
||||
// ======== Bracket Matching Themes ===========
|
||||
export const bracketsLightT = {
|
||||
'.cc-matchingBracket': {
|
||||
fontWeight: 600,
|
||||
},
|
||||
'.cc-nonmatchingBracket': {
|
||||
color: '#ef4444',
|
||||
color: lightT.fgWarning,
|
||||
fontWeight: 700,
|
||||
},
|
||||
'&.cm-focused .cc-matchingBracket': {
|
||||
backgroundColor: '#dae6f2',
|
||||
backgroundColor: lightT.bgSelected,
|
||||
},
|
||||
};
|
||||
|
||||
export const bracketsDarkT = {
|
||||
'.cc-matchingBracket': {
|
||||
fontWeight: 600,
|
||||
},
|
||||
'.cc-nonmatchingBracket': {
|
||||
color: '#ef4444',
|
||||
color: darkT.fgWarning,
|
||||
fontWeight: 700,
|
||||
},
|
||||
'&.cm-focused .cc-matchingBracket': {
|
||||
backgroundColor: '#734f00',
|
||||
backgroundColor: darkT.bgSelected,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { NodeType, Tree, TreeCursor } from "@lezer/common"
|
||||
import { NodeType, Tree, TreeCursor } from '@lezer/common'
|
||||
|
||||
export type CursorNode = {
|
||||
type: NodeType
|
||||
|
@ -44,16 +44,16 @@ export function traverseTree(tree: Tree, { beforeEnter, onEnter, onLeave, }: Tre
|
|||
|
||||
export function printTree(tree: Tree): string {
|
||||
const state = {
|
||||
output: "",
|
||||
output: '',
|
||||
prefixes: [] as string[]
|
||||
}
|
||||
traverseTree(tree, {
|
||||
onEnter: node => {
|
||||
state.output += "[";
|
||||
state.output += '[';
|
||||
state.output += node.type.name;
|
||||
},
|
||||
onLeave: () => {
|
||||
state.output += "]";
|
||||
state.output += ']';
|
||||
},
|
||||
})
|
||||
return state.output;
|
||||
|
|
Loading…
Reference in New Issue
Block a user