mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
F: Animation rework
move to simple CSS animations and react-spring
This commit is contained in:
parent
bc4a68f4d1
commit
ca8aba2b9d
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
@ -66,6 +66,7 @@
|
||||||
"ADVB",
|
"ADVB",
|
||||||
"Analyse",
|
"Analyse",
|
||||||
"Backquote",
|
"Backquote",
|
||||||
|
"bezier",
|
||||||
"BIGPR",
|
"BIGPR",
|
||||||
"cctext",
|
"cctext",
|
||||||
"Certbot",
|
"Certbot",
|
||||||
|
|
|
@ -43,13 +43,13 @@ This readme file is used mostly to document project dependencies and conventions
|
||||||
- reactflow
|
- reactflow
|
||||||
- js-file-download
|
- js-file-download
|
||||||
- use-debounce
|
- use-debounce
|
||||||
- framer-motion
|
|
||||||
- html-to-image
|
- html-to-image
|
||||||
- @tanstack/react-table
|
- @tanstack/react-table
|
||||||
- @uiw/react-codemirror
|
- @uiw/react-codemirror
|
||||||
- @uiw/codemirror-themes
|
- @uiw/codemirror-themes
|
||||||
- @lezer/lr
|
- @lezer/lr
|
||||||
- @dagrejs/dagre
|
- @dagrejs/dagre
|
||||||
|
- @react-spring/web
|
||||||
</pre>
|
</pre>
|
||||||
</details>
|
</details>
|
||||||
<details>
|
<details>
|
||||||
|
|
944
rsconcept/frontend/package-lock.json
generated
944
rsconcept/frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -14,12 +14,12 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dagrejs/dagre": "^1.1.4",
|
"@dagrejs/dagre": "^1.1.4",
|
||||||
"@lezer/lr": "^1.4.2",
|
"@lezer/lr": "^1.4.2",
|
||||||
|
"@react-spring/web": "^9.7.5",
|
||||||
"@tanstack/react-table": "^8.20.5",
|
"@tanstack/react-table": "^8.20.5",
|
||||||
"@uiw/codemirror-themes": "^4.23.6",
|
"@uiw/codemirror-themes": "^4.23.6",
|
||||||
"@uiw/react-codemirror": "^4.23.6",
|
"@uiw/react-codemirror": "^4.23.6",
|
||||||
"axios": "^1.7.8",
|
"axios": "^1.7.8",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"framer-motion": "^11.13.1",
|
|
||||||
"html-to-image": "^1.11.11",
|
"html-to-image": "^1.11.11",
|
||||||
"js-file-download": "^0.4.12",
|
"js-file-download": "^0.4.12",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
|
@ -44,12 +44,12 @@
|
||||||
"@types/react": "^18.3.12",
|
"@types/react": "^18.3.12",
|
||||||
"@types/react-dom": "^18.3.1",
|
"@types/react-dom": "^18.3.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.0.1",
|
"@typescript-eslint/eslint-plugin": "^8.0.1",
|
||||||
"eslint-plugin-react-hooks": "^5.0.0",
|
|
||||||
"@typescript-eslint/parser": "^8.0.1",
|
"@typescript-eslint/parser": "^8.0.1",
|
||||||
"@vitejs/plugin-react": "^4.3.4",
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"eslint": "^9.16.0",
|
"eslint": "^9.16.0",
|
||||||
"eslint-plugin-react": "^7.37.2",
|
"eslint-plugin-react": "^7.37.2",
|
||||||
|
"eslint-plugin-react-hooks": "^5.0.0",
|
||||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||||
"globals": "^15.13.0",
|
"globals": "^15.13.0",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import clsx from 'clsx';
|
|
||||||
|
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||||
import useWindowSize from '@/hooks/useWindowSize';
|
import useWindowSize from '@/hooks/useWindowSize';
|
||||||
|
|
||||||
|
@ -10,7 +8,7 @@ function Logo() {
|
||||||
return (
|
return (
|
||||||
<img
|
<img
|
||||||
alt=''
|
alt=''
|
||||||
className={clsx('max-h-[1.6rem] w-fit max-w-[11.4rem]')}
|
className='max-h-[1.6rem] w-fit max-w-[11.4rem]'
|
||||||
src={size.isSmall ? '/logo_sign.svg' : !darkMode ? '/logo_full.svg' : '/logo_full_dark.svg'}
|
src={size.isSmall ? '/logo_sign.svg' : !darkMode ? '/logo_full.svg' : '/logo_full_dark.svg'}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { motion } from 'framer-motion';
|
|
||||||
|
|
||||||
import { IconLibrary2, IconManuals, IconNewItem2 } from '@/components/Icons';
|
import { IconLibrary2, IconManuals, IconNewItem2 } from '@/components/Icons';
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||||
import { animateNavigation } from '@/styling/animations';
|
import useWindowSize from '@/hooks/useWindowSize';
|
||||||
|
import { PARAMETER } from '@/utils/constants';
|
||||||
|
|
||||||
import { urls } from '../urls';
|
import { urls } from '../urls';
|
||||||
import Logo from './Logo';
|
import Logo from './Logo';
|
||||||
|
@ -15,6 +15,7 @@ import UserMenu from './UserMenu';
|
||||||
|
|
||||||
function Navigation() {
|
function Navigation() {
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
|
const size = useWindowSize();
|
||||||
const { noNavigationAnimation } = useConceptOptions();
|
const { noNavigationAnimation } = useConceptOptions();
|
||||||
|
|
||||||
const navigateHome = (event: CProps.EventMouse) => router.push(urls.home, event.ctrlKey || event.metaKey);
|
const navigateHome = (event: CProps.EventMouse) => router.push(urls.home, event.ctrlKey || event.metaKey);
|
||||||
|
@ -33,17 +34,24 @@ function Navigation() {
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ToggleNavigation />
|
<ToggleNavigation />
|
||||||
<motion.div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'pl-2 pr-[1.5rem] sm:pr-[0.9rem] h-[3rem]', // prettier: split lines
|
'pl-2 pr-[1.5rem] sm:pr-[0.9rem] h-[3rem]', // prettier: split lines
|
||||||
'flex',
|
'flex',
|
||||||
'cc-shadow-border'
|
'cc-shadow-border'
|
||||||
)}
|
)}
|
||||||
initial={false}
|
style={{
|
||||||
animate={!noNavigationAnimation ? 'open' : 'closed'}
|
transitionProperty: 'height, translate',
|
||||||
variants={animateNavigation}
|
transitionDuration: `${PARAMETER.moveDuration}ms`,
|
||||||
|
height: noNavigationAnimation ? '0rem' : '3rem',
|
||||||
|
translate: noNavigationAnimation ? '0 -1.5rem' : '0'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
tabIndex={-1}
|
||||||
|
className={clsx('flex items-center mr-auto', !size.isSmall && 'cursor-pointer')}
|
||||||
|
onClick={!size.isSmall ? navigateHome : undefined}
|
||||||
>
|
>
|
||||||
<div tabIndex={-1} className='flex items-center mr-auto cursor-pointer' onClick={navigateHome}>
|
|
||||||
<Logo />
|
<Logo />
|
||||||
</div>
|
</div>
|
||||||
<div className='flex gap-1 py-[0.3rem]'>
|
<div className='flex gap-1 py-[0.3rem]'>
|
||||||
|
@ -52,7 +60,7 @@ function Navigation() {
|
||||||
<NavigationButton text='Справка' icon={<IconManuals size='1.5rem' />} onClick={navigateHelp} />
|
<NavigationButton text='Справка' icon={<IconManuals size='1.5rem' />} onClick={navigateHelp} />
|
||||||
<UserMenu />
|
<UserMenu />
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,22 @@ import clsx from 'clsx';
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
import { globals } from '@/utils/constants';
|
import { globals } from '@/utils/constants';
|
||||||
|
|
||||||
interface NavigationButtonProps extends CProps.Titled {
|
interface NavigationButtonProps extends CProps.Titled, CProps.Styling {
|
||||||
text?: string;
|
text?: string;
|
||||||
icon: React.ReactNode;
|
icon: React.ReactNode;
|
||||||
onClick?: (event: CProps.EventMouse) => void;
|
onClick?: (event: CProps.EventMouse) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function NavigationButton({ icon, title, titleHtml, hideTitle, onClick, text }: NavigationButtonProps) {
|
function NavigationButton({
|
||||||
|
icon,
|
||||||
|
title,
|
||||||
|
className,
|
||||||
|
style,
|
||||||
|
titleHtml,
|
||||||
|
hideTitle,
|
||||||
|
onClick,
|
||||||
|
text
|
||||||
|
}: NavigationButtonProps) {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
|
@ -29,8 +38,10 @@ function NavigationButton({ icon, title, titleHtml, hideTitle, onClick, text }:
|
||||||
{
|
{
|
||||||
'px-2': text,
|
'px-2': text,
|
||||||
'px-4': !text
|
'px-4': !text
|
||||||
}
|
},
|
||||||
|
className
|
||||||
)}
|
)}
|
||||||
|
style={style}
|
||||||
>
|
>
|
||||||
{icon ? <span>{icon}</span> : null}
|
{icon ? <span>{icon}</span> : null}
|
||||||
{text ? <span className='hidden sm:inline'>{text}</span> : null}
|
{text ? <span className='hidden sm:inline'>{text}</span> : null}
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { motion } from 'framer-motion';
|
|
||||||
|
|
||||||
import { IconPin, IconUnpin } from '@/components/Icons';
|
import { IconPin, IconUnpin } from '@/components/Icons';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||||
import { animateNavigationToggle } from '@/styling/animations';
|
import { globals, PARAMETER } from '@/utils/constants';
|
||||||
import { globals } from '@/utils/constants';
|
|
||||||
|
|
||||||
function ToggleNavigation() {
|
function ToggleNavigation() {
|
||||||
const { noNavigationAnimation, toggleNoNavigation } = useConceptOptions();
|
const { noNavigationAnimation, toggleNoNavigation } = useConceptOptions();
|
||||||
return (
|
return (
|
||||||
<motion.button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
|
@ -20,15 +18,18 @@ function ToggleNavigation() {
|
||||||
'select-none'
|
'select-none'
|
||||||
)}
|
)}
|
||||||
onClick={toggleNoNavigation}
|
onClick={toggleNoNavigation}
|
||||||
initial={false}
|
|
||||||
animate={noNavigationAnimation ? 'off' : 'on'}
|
|
||||||
variants={animateNavigationToggle}
|
|
||||||
data-tooltip-id={globals.tooltip}
|
data-tooltip-id={globals.tooltip}
|
||||||
data-tooltip-content={noNavigationAnimation ? 'Показать навигацию' : 'Скрыть навигацию'}
|
data-tooltip-content={noNavigationAnimation ? 'Показать навигацию' : 'Скрыть навигацию'}
|
||||||
|
style={{
|
||||||
|
transitionProperty: 'height, width',
|
||||||
|
transitionDuration: `${PARAMETER.moveDuration}ms`,
|
||||||
|
height: noNavigationAnimation ? '1.2rem' : '3rem',
|
||||||
|
width: noNavigationAnimation ? '3rem' : '1.2rem'
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{!noNavigationAnimation ? <IconPin /> : null}
|
{!noNavigationAnimation ? <IconPin /> : null}
|
||||||
{noNavigationAnimation ? <IconUnpin /> : null}
|
{noNavigationAnimation ? <IconUnpin /> : null}
|
||||||
</motion.button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
import { AnimatePresence } from 'framer-motion';
|
|
||||||
|
|
||||||
import { IconLogin, IconUser2 } from '@/components/Icons';
|
import { IconLogin, IconUser2 } from '@/components/Icons';
|
||||||
import Loader from '@/components/ui/Loader';
|
import Loader from '@/components/ui/Loader';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import { useAuth } from '@/context/AuthContext';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||||
|
@ -21,30 +18,22 @@ function UserMenu() {
|
||||||
const navigateLogin = () => router.push(urls.login);
|
const navigateLogin = () => router.push(urls.login);
|
||||||
return (
|
return (
|
||||||
<div ref={menu.ref} className='h-full w-[4rem] flex items-center justify-center'>
|
<div ref={menu.ref} className='h-full w-[4rem] flex items-center justify-center'>
|
||||||
<AnimatePresence mode='wait'>
|
{loading ? <Loader circular scale={1.5} /> : null}
|
||||||
{loading ? (
|
|
||||||
<AnimateFade key='nav_user_badge_loader'>
|
|
||||||
<Loader circular scale={1.5} />
|
|
||||||
</AnimateFade>
|
|
||||||
) : null}
|
|
||||||
{!user && !loading ? (
|
{!user && !loading ? (
|
||||||
<AnimateFade key='nav_user_badge_login' className='h-full'>
|
|
||||||
<NavigationButton
|
<NavigationButton
|
||||||
|
className='cc-fade-in'
|
||||||
title='Перейти на страницу логина'
|
title='Перейти на страницу логина'
|
||||||
icon={<IconLogin size='1.5rem' className='icon-primary' />}
|
icon={<IconLogin size='1.5rem' className='icon-primary' />}
|
||||||
onClick={navigateLogin}
|
onClick={navigateLogin}
|
||||||
/>
|
/>
|
||||||
</AnimateFade>
|
|
||||||
) : null}
|
) : null}
|
||||||
{user ? (
|
{user ? (
|
||||||
<AnimateFade key='nav_user_badge_profile' className='h-full'>
|
|
||||||
<NavigationButton
|
<NavigationButton
|
||||||
|
className='cc-fade-in'
|
||||||
icon={<IconUser2 size='1.5rem' className={adminMode && user.is_staff ? 'icon-primary' : ''} />}
|
icon={<IconUser2 size='1.5rem' className={adminMode && user.is_staff ? 'icon-primary' : ''} />}
|
||||||
onClick={menu.toggle}
|
onClick={menu.toggle}
|
||||||
/>
|
/>
|
||||||
</AnimateFade>
|
|
||||||
) : null}
|
) : null}
|
||||||
</AnimatePresence>
|
|
||||||
<UserDropdown isOpen={!!user && menu.isOpen} hideDropdown={() => menu.hide()} />
|
<UserDropdown isOpen={!!user && menu.isOpen} hideDropdown={() => menu.hide()} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,7 +6,6 @@ import { createTheme } from '@uiw/codemirror-themes';
|
||||||
import CodeMirror, { BasicSetupOptions, ReactCodeMirrorProps, ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
import CodeMirror, { BasicSetupOptions, ReactCodeMirrorProps, ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { EditorView } from 'codemirror';
|
import { EditorView } from 'codemirror';
|
||||||
import { AnimatePresence } from 'framer-motion';
|
|
||||||
import { forwardRef, useCallback, useMemo, useRef, useState } from 'react';
|
import { forwardRef, useCallback, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
import Label from '@/components/ui/Label';
|
import Label from '@/components/ui/Label';
|
||||||
|
@ -207,7 +206,6 @@ const RefsInput = forwardRef<ReactCodeMirrorRef, RefsInputInputProps>(
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={clsx('flex flex-col gap-2', cursor)}>
|
<div className={clsx('flex flex-col gap-2', cursor)}>
|
||||||
<AnimatePresence>
|
|
||||||
{showEditor && schema ? (
|
{showEditor && schema ? (
|
||||||
<DlgEditReference
|
<DlgEditReference
|
||||||
hideWindow={hideEditReference}
|
hideWindow={hideEditReference}
|
||||||
|
@ -222,7 +220,6 @@ const RefsInput = forwardRef<ReactCodeMirrorRef, RefsInputInputProps>(
|
||||||
onSave={handleInputReference}
|
onSave={handleInputReference}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</AnimatePresence>
|
|
||||||
<Label text={label} />
|
<Label text={label} />
|
||||||
<CodeMirror
|
<CodeMirror
|
||||||
id={id}
|
id={id}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import clsx from 'clsx';
|
||||||
import { isResponseHtml } from '@/utils/utils';
|
import { isResponseHtml } from '@/utils/utils';
|
||||||
|
|
||||||
import PrettyJson from '../ui/PrettyJSON';
|
import PrettyJson from '../ui/PrettyJSON';
|
||||||
import AnimateFade from '../wrap/AnimateFade';
|
|
||||||
|
|
||||||
export type ErrorData = string | Error | AxiosError | undefined;
|
export type ErrorData = string | Error | AxiosError | undefined;
|
||||||
|
|
||||||
|
@ -59,8 +58,9 @@ function DescribeError({ error }: { error: ErrorData }) {
|
||||||
|
|
||||||
function InfoError({ error }: InfoErrorProps) {
|
function InfoError({ error }: InfoErrorProps) {
|
||||||
return (
|
return (
|
||||||
<AnimateFade
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
|
'cc-fade-in',
|
||||||
'min-w-[25rem]',
|
'min-w-[25rem]',
|
||||||
'px-3 py-2 flex flex-col',
|
'px-3 py-2 flex flex-col',
|
||||||
'clr-text-red',
|
'clr-text-red',
|
||||||
|
@ -75,7 +75,7 @@ function InfoError({ error }: InfoErrorProps) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DescribeError error={error} />
|
<DescribeError error={error} />
|
||||||
</AnimateFade>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
rsconcept/frontend/src/components/props.d.ts
vendored
10
rsconcept/frontend/src/components/props.d.ts
vendored
|
@ -1,5 +1,6 @@
|
||||||
// =========== Module contains interfaces for common UI elements. ==========
|
// =========== Module contains interfaces for common UI elements. ==========
|
||||||
import { HTMLMotionProps } from 'framer-motion';
|
import { animated } from '@react-spring/web';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
export namespace CProps {
|
export namespace CProps {
|
||||||
/**
|
/**
|
||||||
|
@ -88,15 +89,10 @@ export namespace CProps {
|
||||||
*/
|
*/
|
||||||
export type Input = Titled & React.ComponentProps<'input'>;
|
export type Input = Titled & React.ComponentProps<'input'>;
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents `button` component with optional title and animation properties.
|
|
||||||
*/
|
|
||||||
export type AnimatedButton = Titled & Omit<HTMLMotionProps<'button'>, 'type'>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents `div` component with animation properties.
|
* Represents `div` component with animation properties.
|
||||||
*/
|
*/
|
||||||
export type AnimatedDiv = HTMLMotionProps<'div'>;
|
export type AnimatedDiv = React.ComponentPropsWithoutRef<typeof animated.div>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents `mouse event` in React.
|
* Represents `mouse event` in React.
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { motion } from 'framer-motion';
|
|
||||||
|
|
||||||
import { animateDropdown } from '@/styling/animations';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
|
|
||||||
import { CProps } from '../props';
|
import { CProps } from '../props';
|
||||||
|
|
||||||
|
@ -25,11 +24,12 @@ function Dropdown({
|
||||||
stretchTop,
|
stretchTop,
|
||||||
className,
|
className,
|
||||||
children,
|
children,
|
||||||
|
style,
|
||||||
...restProps
|
...restProps
|
||||||
}: React.PropsWithChildren<DropdownProps>) {
|
}: React.PropsWithChildren<DropdownProps>) {
|
||||||
return (
|
return (
|
||||||
<div className='relative'>
|
<div className='relative'>
|
||||||
<motion.div
|
<div
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'z-topmost',
|
'z-topmost',
|
||||||
|
@ -45,13 +45,18 @@ function Dropdown({
|
||||||
},
|
},
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
initial={false}
|
style={{
|
||||||
animate={isOpen ? 'open' : 'closed'}
|
transitionProperty: 'clip-path, transform',
|
||||||
variants={animateDropdown}
|
transitionDuration: `${PARAMETER.dropdownDuration}ms`,
|
||||||
|
transitionTimingFunction: 'ease-in-out',
|
||||||
|
transform: isOpen ? 'translateY(0)' : 'translateY(-10%)',
|
||||||
|
clipPath: isOpen ? 'inset(0% 0% 0% 0%)' : 'inset(10% 0% 90% 0%)',
|
||||||
|
...style
|
||||||
|
}}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</motion.div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { motion } from 'framer-motion';
|
|
||||||
|
|
||||||
import { animateDropdownItem } from '@/styling/animations';
|
|
||||||
import { globals } from '@/utils/constants';
|
import { globals } from '@/utils/constants';
|
||||||
|
|
||||||
import { CProps } from '../props';
|
import { CProps } from '../props';
|
||||||
|
|
||||||
interface DropdownButtonProps extends CProps.AnimatedButton {
|
interface DropdownButtonProps extends CProps.Button {
|
||||||
/** Icon to display first (not used if children are provided). */
|
/** Icon to display first (not used if children are provided). */
|
||||||
icon?: React.ReactNode;
|
icon?: React.ReactNode;
|
||||||
|
|
||||||
|
@ -18,7 +16,7 @@ interface DropdownButtonProps extends CProps.AnimatedButton {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Animated `button` with optional text, icon, and click functionality.
|
* `button` with optional text, icon, and click functionality styled for use in a {@link Dropdown}.
|
||||||
* It supports optional children for custom content or the default text/icon display.
|
* It supports optional children for custom content or the default text/icon display.
|
||||||
*/
|
*/
|
||||||
function DropdownButton({
|
function DropdownButton({
|
||||||
|
@ -33,7 +31,7 @@ function DropdownButton({
|
||||||
...restProps
|
...restProps
|
||||||
}: DropdownButtonProps) {
|
}: DropdownButtonProps) {
|
||||||
return (
|
return (
|
||||||
<motion.button
|
<button
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
type='button'
|
type='button'
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
|
@ -48,7 +46,6 @@ function DropdownButton({
|
||||||
},
|
},
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
variants={animateDropdownItem}
|
|
||||||
data-tooltip-id={!!title || !!titleHtml ? globals.tooltip : undefined}
|
data-tooltip-id={!!title || !!titleHtml ? globals.tooltip : undefined}
|
||||||
data-tooltip-html={titleHtml}
|
data-tooltip-html={titleHtml}
|
||||||
data-tooltip-content={title}
|
data-tooltip-content={title}
|
||||||
|
@ -58,7 +55,7 @@ function DropdownButton({
|
||||||
{children ? children : null}
|
{children ? children : null}
|
||||||
{!children && icon ? icon : null}
|
{!children && icon ? icon : null}
|
||||||
{!children && text ? <span>{text}</span> : null}
|
{!children && text ? <span>{text}</span> : null}
|
||||||
</motion.button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { motion } from 'framer-motion';
|
|
||||||
|
|
||||||
import { animateDropdownItem } from '@/styling/animations';
|
|
||||||
|
|
||||||
import Checkbox, { CheckboxProps } from './Checkbox';
|
import Checkbox, { CheckboxProps } from './Checkbox';
|
||||||
|
|
||||||
/** Animated {@link Checkbox} inside a {@link Dropdown} item. */
|
/** Animated {@link Checkbox} inside a {@link Dropdown} item. */
|
||||||
function DropdownCheckbox({ setValue, disabled, ...restProps }: CheckboxProps) {
|
function DropdownCheckbox({ setValue, disabled, ...restProps }: CheckboxProps) {
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<div
|
||||||
variants={animateDropdownItem}
|
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'px-3 py-1',
|
'px-3 py-1',
|
||||||
'text-left overflow-ellipsis whitespace-nowrap',
|
'text-left overflow-ellipsis whitespace-nowrap',
|
||||||
|
@ -18,7 +14,7 @@ function DropdownCheckbox({ setValue, disabled, ...restProps }: CheckboxProps) {
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Checkbox tabIndex={-1} disabled={disabled} setValue={setValue} {...restProps} />
|
<Checkbox tabIndex={-1} disabled={disabled} setValue={setValue} {...restProps} />
|
||||||
</motion.div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
import clsx from 'clsx';
|
|
||||||
import { motion } from 'framer-motion';
|
|
||||||
|
|
||||||
import { animateDropdownItem } from '@/styling/animations';
|
|
||||||
|
|
||||||
import { DividerProps } from './Divider';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link Divider} with animation inside {@link Dropdown}.
|
|
||||||
*/
|
|
||||||
function DropdownDivider({ vertical, margins = 'mx-2', className, ...restProps }: DividerProps) {
|
|
||||||
return (
|
|
||||||
<motion.div
|
|
||||||
variants={animateDropdownItem}
|
|
||||||
className={clsx(
|
|
||||||
margins, //prettier: split-lines
|
|
||||||
className,
|
|
||||||
{
|
|
||||||
'border-x': vertical,
|
|
||||||
'border-y': !vertical
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DropdownDivider;
|
|
|
@ -4,8 +4,6 @@ import { ThreeCircles, ThreeDots } from 'react-loader-spinner';
|
||||||
|
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||||
|
|
||||||
import AnimateFade from '../wrap/AnimateFade';
|
|
||||||
|
|
||||||
interface LoaderProps {
|
interface LoaderProps {
|
||||||
/** Scale of the loader from 1 to 10. */
|
/** Scale of the loader from 1 to 10. */
|
||||||
scale?: number;
|
scale?: number;
|
||||||
|
@ -19,15 +17,26 @@ interface LoaderProps {
|
||||||
*/
|
*/
|
||||||
function Loader({ scale = 5, circular }: LoaderProps) {
|
function Loader({ scale = 5, circular }: LoaderProps) {
|
||||||
const { colors } = useConceptOptions();
|
const { colors } = useConceptOptions();
|
||||||
|
if (circular) {
|
||||||
return (
|
return (
|
||||||
<AnimateFade noFadeIn className='flex justify-center'>
|
<ThreeCircles
|
||||||
{circular ? (
|
color={colors.bgPrimary}
|
||||||
<ThreeCircles color={colors.bgPrimary} height={scale * 20} width={scale * 20} />
|
height={scale * 20}
|
||||||
) : (
|
width={scale * 20}
|
||||||
<ThreeDots color={colors.bgPrimary} height={scale * 20} width={scale * 20} radius={scale * 2} />
|
wrapperClass='flex justify-center'
|
||||||
)}
|
/>
|
||||||
</AnimateFade>
|
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<ThreeDots
|
||||||
|
color={colors.bgPrimary}
|
||||||
|
height={scale * 20}
|
||||||
|
width={scale * 20}
|
||||||
|
radius={scale * 2}
|
||||||
|
wrapperClass='flex justify-center'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Loader;
|
export default Loader;
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { motion } from 'framer-motion';
|
|
||||||
|
|
||||||
import useEscapeKey from '@/hooks/useEscapeKey';
|
import useEscapeKey from '@/hooks/useEscapeKey';
|
||||||
import { HelpTopic } from '@/models/miscellaneous';
|
import { HelpTopic } from '@/models/miscellaneous';
|
||||||
import { animateModal } from '@/styling/animations';
|
|
||||||
import { PARAMETER } from '@/utils/constants';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
import { prepareTooltip } from '@/utils/labels';
|
import { prepareTooltip } from '@/utils/labels';
|
||||||
|
|
||||||
|
@ -100,17 +98,12 @@ function Modal({
|
||||||
className={clsx('z-navigation', 'fixed top-0 left-0', 'w-full h-full', 'cc-modal-backdrop')}
|
className={clsx('z-navigation', 'fixed top-0 left-0', 'w-full h-full', 'cc-modal-backdrop')}
|
||||||
onClick={hideWindow}
|
onClick={hideWindow}
|
||||||
/>
|
/>
|
||||||
<motion.div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'z-modal',
|
'cc-animate-modal',
|
||||||
'absolute bottom-1/2 left-1/2 -translate-x-1/2 translate-y-1/2',
|
'z-modal absolute bottom-1/2 left-1/2 -translate-x-1/2 translate-y-1/2',
|
||||||
'border rounded-xl',
|
'border rounded-xl clr-app'
|
||||||
'clr-app'
|
|
||||||
)}
|
)}
|
||||||
initial={{ ...animateModal.initial }}
|
|
||||||
animate={{ ...animateModal.animate }}
|
|
||||||
exit={{ ...animateModal.exit }}
|
|
||||||
{...restProps}
|
|
||||||
>
|
>
|
||||||
<Overlay position='right-2 top-2'>
|
<Overlay position='right-2 top-2'>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
|
@ -130,13 +123,14 @@ function Modal({
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'overscroll-contain max-h-[calc(100svh-8rem)] max-w-[100svw] xs:max-w-[calc(100svw-2rem)]',
|
'overscroll-contain max-h-[calc(100svh-8rem)] max-w-[100svw] xs:max-w-[calc(100svw-2rem)] outline-none',
|
||||||
{
|
{
|
||||||
'overflow-auto': !overflowVisible,
|
'overflow-auto': !overflowVisible,
|
||||||
'overflow-visible': overflowVisible
|
'overflow-visible': overflowVisible
|
||||||
},
|
},
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
|
{...restProps}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
@ -155,7 +149,7 @@ function Modal({
|
||||||
) : null}
|
) : null}
|
||||||
<Button text={readonly ? 'Закрыть' : 'Отмена'} className='min-w-[7rem]' onClick={handleCancel} />
|
<Button text={readonly ? 'Закрыть' : 'Отмена'} className='min-w-[7rem]' onClick={handleCancel} />
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { AnimatePresence, motion } from 'framer-motion';
|
|
||||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { animateSideAppear } from '@/styling/animations';
|
import { globals, PARAMETER } from '@/utils/constants';
|
||||||
import { globals } from '@/utils/constants';
|
|
||||||
|
|
||||||
import { IconDropArrow, IconPageRight } from '../Icons';
|
import { IconDropArrow, IconPageRight } from '../Icons';
|
||||||
import { CProps } from '../props';
|
import { CProps } from '../props';
|
||||||
|
@ -94,25 +92,31 @@ function SelectTree<ItemType>({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div {...restProps}>
|
<div {...restProps}>
|
||||||
<AnimatePresence initial={false}>
|
{items.map((item, index) => {
|
||||||
{items.map((item, index) =>
|
const isActive = getParent(item) === item || !folded.includes(getParent(item));
|
||||||
getParent(item) === item || !folded.includes(getParent(item)) ? (
|
return (
|
||||||
<motion.div
|
<div
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
key={`${prefix}${index}`}
|
key={`${prefix}${index}`}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'pr-3 pl-6 py-1',
|
'pr-3 pl-6 border-b',
|
||||||
'cc-scroll-row',
|
'cc-scroll-row',
|
||||||
'clr-controls clr-hover',
|
'clr-controls clr-hover',
|
||||||
'cursor-pointer',
|
'cursor-pointer',
|
||||||
value === item && 'clr-selected'
|
value === item && 'clr-selected'
|
||||||
)}
|
)}
|
||||||
data-tooltip-id={globals.tooltip}
|
data-tooltip-id={globals.tooltip}
|
||||||
data-tooltip-html={getDescription(item)}
|
data-tooltip-html={isActive ? getDescription(item) : undefined}
|
||||||
onClick={event => handleSetValue(event, item)}
|
onClick={isActive ? event => handleSetValue(event, item) : undefined}
|
||||||
initial={{ ...animateSideAppear.initial }}
|
style={{
|
||||||
animate={{ ...animateSideAppear.animate }}
|
borderBottomWidth: isActive ? '1px' : '0px',
|
||||||
exit={{ ...animateSideAppear.exit }}
|
transitionProperty: 'height, opacity, padding',
|
||||||
|
transitionDuration: `${PARAMETER.moveDuration}ms`,
|
||||||
|
paddingTop: isActive ? '0.25rem' : '0',
|
||||||
|
paddingBottom: isActive ? '0.25rem' : '0',
|
||||||
|
height: isActive ? 'min-content' : '0',
|
||||||
|
opacity: isActive ? '1' : '0'
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{foldable.has(item) ? (
|
{foldable.has(item) ? (
|
||||||
<Overlay position='left-[-1.3rem]' className={clsx(!folded.includes(item) && 'top-[0.1rem]')}>
|
<Overlay position='left-[-1.3rem]' className={clsx(!folded.includes(item) && 'top-[0.1rem]')}>
|
||||||
|
@ -125,10 +129,9 @@ function SelectTree<ItemType>({
|
||||||
</Overlay>
|
</Overlay>
|
||||||
) : null}
|
) : null}
|
||||||
{getParent(item) === item ? getLabel(item) : `- ${getLabel(item).toLowerCase()}`}
|
{getParent(item) === item ? getLabel(item) : `- ${getLabel(item).toLowerCase()}`}
|
||||||
</motion.div>
|
</div>
|
||||||
) : null
|
);
|
||||||
)}
|
})}
|
||||||
</AnimatePresence>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import { motion } from 'framer-motion';
|
'use client';
|
||||||
|
|
||||||
import { animateFade } from '@/styling/animations';
|
import { animated, useSpring } from '@react-spring/web';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { CProps } from '../props';
|
import { CProps } from '@/components/props';
|
||||||
|
import { PARAMETER } from '@/utils/constants';
|
||||||
|
|
||||||
interface AnimateFadeProps extends CProps.AnimatedDiv {
|
interface AnimateFadeProps extends CProps.AnimatedDiv {
|
||||||
noFadeIn?: boolean;
|
noFadeIn?: boolean;
|
||||||
|
@ -10,19 +12,30 @@ interface AnimateFadeProps extends CProps.AnimatedDiv {
|
||||||
hideContent?: boolean;
|
hideContent?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function AnimateFade({ style, noFadeIn, noFadeOut, children, hideContent, ...restProps }: AnimateFadeProps) {
|
function AnimateFade({ style, noFadeIn, noFadeOut, hideContent, children, ...restProps }: AnimateFadeProps) {
|
||||||
|
const [isAnimating, setIsAnimating] = useState(false);
|
||||||
|
|
||||||
|
const springs = useSpring({
|
||||||
|
config: { duration: PARAMETER.fadeDuration, clamp: true },
|
||||||
|
from: { opacity: noFadeIn ? 1 : 0 },
|
||||||
|
to: {
|
||||||
|
opacity: hideContent ? 0 : 1,
|
||||||
|
display: hideContent === undefined ? undefined : !isAnimating || (noFadeOut && hideContent) ? 'none' : 'block'
|
||||||
|
},
|
||||||
|
enter: { opacity: 0 },
|
||||||
|
leave: { opacity: noFadeOut ? 1 : 0 },
|
||||||
|
onStart: () => {
|
||||||
|
if (!hideContent) setIsAnimating(true);
|
||||||
|
},
|
||||||
|
onRest: () => {
|
||||||
|
if (hideContent) setIsAnimating(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<animated.div tabIndex={-1} style={{ ...springs, ...style }} {...restProps}>
|
||||||
tabIndex={-1}
|
|
||||||
initial={{ ...(!noFadeIn ? animateFade.initial : {}) }}
|
|
||||||
animate={hideContent ? 'hidden' : 'active'}
|
|
||||||
variants={animateFade.variants}
|
|
||||||
exit={{ ...(!noFadeOut ? animateFade.exit : {}) }}
|
|
||||||
style={{ display: hideContent ? 'none' : '', willChange: 'auto', ...style }}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{children}
|
{children}
|
||||||
</motion.div>
|
</animated.div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,43 +1,25 @@
|
||||||
import { AnimatePresence } from 'framer-motion';
|
|
||||||
|
|
||||||
import InfoError, { ErrorData } from '../info/InfoError';
|
import InfoError, { ErrorData } from '../info/InfoError';
|
||||||
import { CProps } from '../props';
|
|
||||||
import Loader from '../ui/Loader';
|
import Loader from '../ui/Loader';
|
||||||
import AnimateFade from './AnimateFade';
|
|
||||||
|
|
||||||
interface DataLoaderProps extends CProps.AnimatedDiv {
|
|
||||||
id: string;
|
|
||||||
|
|
||||||
|
interface DataLoaderProps {
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
error?: ErrorData;
|
error?: ErrorData;
|
||||||
hasNoData?: boolean;
|
hasNoData?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function DataLoader({
|
function DataLoader({ isLoading, hasNoData, error, children }: React.PropsWithChildren<DataLoaderProps>) {
|
||||||
id,
|
if (isLoading) {
|
||||||
isLoading,
|
return <Loader />;
|
||||||
hasNoData,
|
}
|
||||||
error,
|
if (error) {
|
||||||
className,
|
return <InfoError error={error} />;
|
||||||
children,
|
}
|
||||||
...restProps
|
|
||||||
}: React.PropsWithChildren<DataLoaderProps>) {
|
if (hasNoData) {
|
||||||
return (
|
return <div className='cc-fade-in w-full text-center p-1'>Данные не загружены</div>;
|
||||||
<AnimatePresence mode='wait'>
|
} else {
|
||||||
{!isLoading && !error && !hasNoData ? (
|
return <>{children}</>;
|
||||||
<AnimateFade id={id} key={`${id}-data`} className={className} {...restProps}>
|
}
|
||||||
{children}
|
|
||||||
</AnimateFade>
|
|
||||||
) : null}
|
|
||||||
{!isLoading && !error && hasNoData ? (
|
|
||||||
<AnimateFade key={`${id}-no-data`} className='w-full text-center p-1' {...restProps}>
|
|
||||||
Данные не загружены
|
|
||||||
</AnimateFade>
|
|
||||||
) : null}
|
|
||||||
{isLoading ? <Loader key={`${id}-loader`} /> : null}
|
|
||||||
{error ? <InfoError key={`${id}-error`} error={error} /> : null}
|
|
||||||
</AnimatePresence>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DataLoader;
|
export default DataLoader;
|
||||||
|
|
|
@ -13,7 +13,7 @@ function ExpectedAnonymous() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col items-center gap-3 py-6'>
|
<div className='cc-fade-in flex flex-col items-center gap-3 py-6'>
|
||||||
<p className='font-semibold'>{`Вы вошли в систему как ${user?.username ?? ''}`}</p>
|
<p className='font-semibold'>{`Вы вошли в систему как ${user?.username ?? ''}`}</p>
|
||||||
<div className='flex gap-3'>
|
<div className='flex gap-3'>
|
||||||
<TextURL text='Новая схема' href='/library/create' />
|
<TextURL text='Новая схема' href='/library/create' />
|
||||||
|
|
|
@ -1,30 +1,28 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { AnimatePresence } from 'framer-motion';
|
|
||||||
|
|
||||||
import { useAuth } from '@/context/AuthContext';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
|
|
||||||
import Loader from '../ui/Loader';
|
import Loader from '../ui/Loader';
|
||||||
import TextURL from '../ui/TextURL';
|
import TextURL from '../ui/TextURL';
|
||||||
import AnimateFade from './AnimateFade';
|
|
||||||
|
|
||||||
function RequireAuth({ children }: React.PropsWithChildren) {
|
function RequireAuth({ children }: React.PropsWithChildren) {
|
||||||
const { user, loading } = useAuth();
|
const { user, loading } = useAuth();
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <Loader key='auth-loader' />;
|
||||||
|
}
|
||||||
|
if (user) {
|
||||||
|
return <>{children}</>;
|
||||||
|
} else {
|
||||||
return (
|
return (
|
||||||
<AnimatePresence mode='wait'>
|
<div key='auth-no-user' className='flex flex-col items-center gap-1 mt-2'>
|
||||||
{loading ? <Loader key='auth-loader' /> : null}
|
|
||||||
{!loading && user ? <AnimateFade key='auth-data'>{children}</AnimateFade> : null}
|
|
||||||
{!loading && !user ? (
|
|
||||||
<AnimateFade key='auth-no-user' className='flex flex-col items-center gap-1 mt-2'>
|
|
||||||
<p className='mb-2'>Пожалуйста войдите в систему</p>
|
<p className='mb-2'>Пожалуйста войдите в систему</p>
|
||||||
<TextURL text='Войти в Портал' href='/login' />
|
<TextURL text='Войти в Портал' href='/login' />
|
||||||
<TextURL text='Зарегистрироваться' href='/signup' />
|
<TextURL text='Зарегистрироваться' href='/signup' />
|
||||||
<TextURL text='Начальная страница' href='/' />
|
<TextURL text='Начальная страница' href='/' />
|
||||||
</AnimateFade>
|
</div>
|
||||||
) : null}
|
|
||||||
</AnimatePresence>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default RequireAuth;
|
export default RequireAuth;
|
||||||
|
|
|
@ -4,9 +4,8 @@ import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useSt
|
||||||
|
|
||||||
import Tooltip from '@/components/ui/Tooltip';
|
import Tooltip from '@/components/ui/Tooltip';
|
||||||
import useLocalStorage from '@/hooks/useLocalStorage';
|
import useLocalStorage from '@/hooks/useLocalStorage';
|
||||||
import { animationDuration } from '@/styling/animations';
|
|
||||||
import { darkT, IColorTheme, lightT } from '@/styling/color';
|
import { darkT, IColorTheme, lightT } from '@/styling/color';
|
||||||
import { globals, storage } from '@/utils/constants';
|
import { globals, PARAMETER, storage } from '@/utils/constants';
|
||||||
import { contextOutsideScope } from '@/utils/labels';
|
import { contextOutsideScope } from '@/utils/labels';
|
||||||
|
|
||||||
interface IOptionsContext {
|
interface IOptionsContext {
|
||||||
|
@ -91,7 +90,7 @@ export const OptionsState = ({ children }: React.PropsWithChildren) => {
|
||||||
setNoNavigation(false);
|
setNoNavigation(false);
|
||||||
} else {
|
} else {
|
||||||
setNoNavigationAnimation(true);
|
setNoNavigationAnimation(true);
|
||||||
setTimeout(() => setNoNavigation(true), animationDuration.navigationToggle);
|
setTimeout(() => setNoNavigation(true), PARAMETER.moveDuration);
|
||||||
}
|
}
|
||||||
}, [noNavigation]);
|
}, [noNavigation]);
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import { TabList, TabPanel, Tabs } from 'react-tabs';
|
||||||
|
|
||||||
import Modal, { ModalProps } from '@/components/ui/Modal';
|
import Modal, { ModalProps } from '@/components/ui/Modal';
|
||||||
import TabLabel from '@/components/ui/TabLabel';
|
import TabLabel from '@/components/ui/TabLabel';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import { useLibrary } from '@/context/LibraryContext';
|
import { useLibrary } from '@/context/LibraryContext';
|
||||||
import usePartialUpdate from '@/hooks/usePartialUpdate';
|
import usePartialUpdate from '@/hooks/usePartialUpdate';
|
||||||
import { HelpTopic } from '@/models/miscellaneous';
|
import { HelpTopic } from '@/models/miscellaneous';
|
||||||
|
@ -145,9 +144,9 @@ function DlgConstituentaTemplate({ hideWindow, schema, onCreate, insertAfter }:
|
||||||
const editorPanel = useMemo(
|
const editorPanel = useMemo(
|
||||||
() => (
|
() => (
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<AnimateFade className='cc-column'>
|
<div className='cc-fade-in cc-column'>
|
||||||
<FormCreateCst state={constituenta} partialUpdate={updateConstituenta} schema={schema} />
|
<FormCreateCst state={constituenta} partialUpdate={updateConstituenta} schema={schema} />
|
||||||
</AnimateFade>
|
</div>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
),
|
),
|
||||||
[constituenta, updateConstituenta, schema]
|
[constituenta, updateConstituenta, schema]
|
||||||
|
|
|
@ -10,7 +10,6 @@ import PickConstituenta from '@/components/select/PickConstituenta';
|
||||||
import DataTable, { IConditionalStyle } from '@/components/ui/DataTable';
|
import DataTable, { IConditionalStyle } from '@/components/ui/DataTable';
|
||||||
import MiniButton from '@/components/ui/MiniButton';
|
import MiniButton from '@/components/ui/MiniButton';
|
||||||
import NoData from '@/components/ui/NoData';
|
import NoData from '@/components/ui/NoData';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||||
import { IConstituenta, IRSForm } from '@/models/rsform';
|
import { IConstituenta, IRSForm } from '@/models/rsform';
|
||||||
import { IArgumentValue } from '@/models/rslang';
|
import { IArgumentValue } from '@/models/rslang';
|
||||||
|
@ -147,7 +146,7 @@ function TabArguments({ state, schema, partialUpdate }: TabArgumentsProps) {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimateFade>
|
<div className='cc-fade-in'>
|
||||||
<DataTable
|
<DataTable
|
||||||
dense
|
dense
|
||||||
noFooter
|
noFooter
|
||||||
|
@ -224,7 +223,7 @@ function TabArguments({ state, schema, partialUpdate }: TabArgumentsProps) {
|
||||||
height='5.1rem'
|
height='5.1rem'
|
||||||
value={state.definition}
|
value={state.definition}
|
||||||
/>
|
/>
|
||||||
</AnimateFade>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import RSInput from '@/components/RSInput';
|
||||||
import PickConstituenta from '@/components/select/PickConstituenta';
|
import PickConstituenta from '@/components/select/PickConstituenta';
|
||||||
import SelectSingle from '@/components/ui/SelectSingle';
|
import SelectSingle from '@/components/ui/SelectSingle';
|
||||||
import TextArea from '@/components/ui/TextArea';
|
import TextArea from '@/components/ui/TextArea';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import { useLibrary } from '@/context/LibraryContext';
|
import { useLibrary } from '@/context/LibraryContext';
|
||||||
import { CATEGORY_CST_TYPE, IConstituenta, IRSForm } from '@/models/rsform';
|
import { CATEGORY_CST_TYPE, IConstituenta, IRSForm } from '@/models/rsform';
|
||||||
import { applyFilterCategory } from '@/models/rsformAPI';
|
import { applyFilterCategory } from '@/models/rsformAPI';
|
||||||
|
@ -78,7 +77,7 @@ function TabTemplate({ state, partialUpdate, templateSchema }: TabTemplateProps)
|
||||||
}, [state.filterCategory, templateSchema]);
|
}, [state.filterCategory, templateSchema]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimateFade>
|
<div className='cc-fade-in'>
|
||||||
<div className='flex border-t border-x rounded-t-md clr-input'>
|
<div className='flex border-t border-x rounded-t-md clr-input'>
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
noBorder
|
noBorder
|
||||||
|
@ -138,7 +137,7 @@ function TabTemplate({ state, partialUpdate, templateSchema }: TabTemplateProps)
|
||||||
height='5.1rem'
|
height='5.1rem'
|
||||||
value={state.prototype?.definition_formal}
|
value={state.prototype?.definition_formal}
|
||||||
/>
|
/>
|
||||||
</AnimateFade>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { AnimatePresence } from 'framer-motion';
|
|
||||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import BadgeHelp from '@/components/info/BadgeHelp';
|
import BadgeHelp from '@/components/info/BadgeHelp';
|
||||||
|
@ -9,7 +8,6 @@ import RSInput from '@/components/RSInput';
|
||||||
import SelectSingle from '@/components/ui/SelectSingle';
|
import SelectSingle from '@/components/ui/SelectSingle';
|
||||||
import TextArea from '@/components/ui/TextArea';
|
import TextArea from '@/components/ui/TextArea';
|
||||||
import TextInput from '@/components/ui/TextInput';
|
import TextInput from '@/components/ui/TextInput';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import { HelpTopic } from '@/models/miscellaneous';
|
import { HelpTopic } from '@/models/miscellaneous';
|
||||||
import { CstType, ICstCreateData, IRSForm } from '@/models/rsform';
|
import { CstType, ICstCreateData, IRSForm } from '@/models/rsform';
|
||||||
import { generateAlias, isBaseSet, isBasicConcept, isFunctional, validateNewAlias } from '@/models/rsformAPI';
|
import { generateAlias, isBaseSet, isBasicConcept, isFunctional, validateNewAlias } from '@/models/rsformAPI';
|
||||||
|
@ -48,8 +46,8 @@ function FormCreateCst({ schema, state, partialUpdate, setValidated }: FormCreat
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimatePresence>
|
<>
|
||||||
<div key='dlg_cst_alias_picker' className='flex items-center self-center gap-3'>
|
<div className='flex items-center self-center gap-3'>
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
id='dlg_cst_type'
|
id='dlg_cst_type'
|
||||||
placeholder='Выберите тип'
|
placeholder='Выберите тип'
|
||||||
|
@ -72,8 +70,8 @@ function FormCreateCst({ schema, state, partialUpdate, setValidated }: FormCreat
|
||||||
className={clsx(PARAMETER.TOOLTIP_WIDTH, 'sm:max-w-[40rem]')}
|
className={clsx(PARAMETER.TOOLTIP_WIDTH, 'sm:max-w-[40rem]')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<TextArea
|
<TextArea
|
||||||
key='dlg_cst_term'
|
|
||||||
id='dlg_cst_term'
|
id='dlg_cst_term'
|
||||||
fitContent
|
fitContent
|
||||||
spellCheck
|
spellCheck
|
||||||
|
@ -83,7 +81,8 @@ function FormCreateCst({ schema, state, partialUpdate, setValidated }: FormCreat
|
||||||
value={state.term_raw}
|
value={state.term_raw}
|
||||||
onChange={event => partialUpdate({ term_raw: event.target.value })}
|
onChange={event => partialUpdate({ term_raw: event.target.value })}
|
||||||
/>
|
/>
|
||||||
<AnimateFade key='dlg_cst_expression' hideContent={!state.definition_formal && isElementary}>
|
|
||||||
|
{!!state.definition_formal || !isElementary ? (
|
||||||
<RSInput
|
<RSInput
|
||||||
id='dlg_cst_expression'
|
id='dlg_cst_expression'
|
||||||
noTooltip
|
noTooltip
|
||||||
|
@ -101,8 +100,9 @@ function FormCreateCst({ schema, state, partialUpdate, setValidated }: FormCreat
|
||||||
onChange={value => partialUpdate({ definition_formal: value })}
|
onChange={value => partialUpdate({ definition_formal: value })}
|
||||||
schema={schema}
|
schema={schema}
|
||||||
/>
|
/>
|
||||||
</AnimateFade>
|
) : null}
|
||||||
<AnimateFade key='dlg_cst_definition' hideContent={!state.definition_raw && isElementary}>
|
|
||||||
|
{!!state.definition_raw || !isElementary ? (
|
||||||
<TextArea
|
<TextArea
|
||||||
id='dlg_cst_definition'
|
id='dlg_cst_definition'
|
||||||
spellCheck
|
spellCheck
|
||||||
|
@ -113,10 +113,10 @@ function FormCreateCst({ schema, state, partialUpdate, setValidated }: FormCreat
|
||||||
value={state.definition_raw}
|
value={state.definition_raw}
|
||||||
onChange={event => partialUpdate({ definition_raw: event.target.value })}
|
onChange={event => partialUpdate({ definition_raw: event.target.value })}
|
||||||
/>
|
/>
|
||||||
</AnimateFade>
|
) : null}
|
||||||
|
|
||||||
{!showConvention ? (
|
{!showConvention ? (
|
||||||
<button
|
<button
|
||||||
key='dlg_cst_show_comment'
|
|
||||||
id='dlg_cst_show_comment'
|
id='dlg_cst_show_comment'
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
type='button'
|
type='button'
|
||||||
|
@ -125,10 +125,8 @@ function FormCreateCst({ schema, state, partialUpdate, setValidated }: FormCreat
|
||||||
>
|
>
|
||||||
Добавить комментарий
|
Добавить комментарий
|
||||||
</button>
|
</button>
|
||||||
) : null}
|
) : (
|
||||||
<AnimateFade hideContent={!showConvention}>
|
|
||||||
<TextArea
|
<TextArea
|
||||||
key='dlg_cst_convention'
|
|
||||||
id='dlg_cst_convention'
|
id='dlg_cst_convention'
|
||||||
spellCheck
|
spellCheck
|
||||||
fitContent
|
fitContent
|
||||||
|
@ -138,8 +136,8 @@ function FormCreateCst({ schema, state, partialUpdate, setValidated }: FormCreat
|
||||||
value={state.convention}
|
value={state.convention}
|
||||||
onChange={event => partialUpdate({ convention: event.target.value })}
|
onChange={event => partialUpdate({ convention: event.target.value })}
|
||||||
/>
|
/>
|
||||||
</AnimateFade>
|
)}
|
||||||
</AnimatePresence>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ import Label from '@/components/ui/Label';
|
||||||
import MiniButton from '@/components/ui/MiniButton';
|
import MiniButton from '@/components/ui/MiniButton';
|
||||||
import TextArea from '@/components/ui/TextArea';
|
import TextArea from '@/components/ui/TextArea';
|
||||||
import TextInput from '@/components/ui/TextInput';
|
import TextInput from '@/components/ui/TextInput';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import { useLibrary } from '@/context/LibraryContext';
|
import { useLibrary } from '@/context/LibraryContext';
|
||||||
import { ILibraryItem, LibraryItemID, LibraryItemType } from '@/models/library';
|
import { ILibraryItem, LibraryItemID, LibraryItemType } from '@/models/library';
|
||||||
import { IOperationSchema } from '@/models/oss';
|
import { IOperationSchema } from '@/models/oss';
|
||||||
|
@ -53,7 +52,7 @@ function TabInputOperation({
|
||||||
}, [createSchema, onChangeAttachedID]);
|
}, [createSchema, onChangeAttachedID]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimateFade className='cc-column'>
|
<div className='cc-fade-in cc-column'>
|
||||||
<TextInput
|
<TextInput
|
||||||
id='operation_title'
|
id='operation_title'
|
||||||
label='Полное название'
|
label='Полное название'
|
||||||
|
@ -111,7 +110,7 @@ function TabInputOperation({
|
||||||
baseFilter={baseFilter}
|
baseFilter={baseFilter}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</AnimateFade>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ import FlexColumn from '@/components/ui/FlexColumn';
|
||||||
import Label from '@/components/ui/Label';
|
import Label from '@/components/ui/Label';
|
||||||
import TextArea from '@/components/ui/TextArea';
|
import TextArea from '@/components/ui/TextArea';
|
||||||
import TextInput from '@/components/ui/TextInput';
|
import TextInput from '@/components/ui/TextInput';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import { IOperationSchema, OperationID } from '@/models/oss';
|
import { IOperationSchema, OperationID } from '@/models/oss';
|
||||||
|
|
||||||
import PickMultiOperation from '../../components/select/PickMultiOperation';
|
import PickMultiOperation from '../../components/select/PickMultiOperation';
|
||||||
|
@ -31,7 +30,7 @@ function TabSynthesisOperation({
|
||||||
setInputs
|
setInputs
|
||||||
}: TabSynthesisOperationProps) {
|
}: TabSynthesisOperationProps) {
|
||||||
return (
|
return (
|
||||||
<AnimateFade className='cc-column'>
|
<div className='cc-fade-in cc-column'>
|
||||||
<TextInput
|
<TextInput
|
||||||
id='operation_title'
|
id='operation_title'
|
||||||
label='Полное название'
|
label='Полное название'
|
||||||
|
@ -61,7 +60,7 @@ function TabSynthesisOperation({
|
||||||
<Label text={`Выбор аргументов: [ ${inputs.length} ]`} />
|
<Label text={`Выбор аргументов: [ ${inputs.length} ]`} />
|
||||||
<PickMultiOperation items={oss.items} selected={inputs} setSelected={setInputs} rows={6} />
|
<PickMultiOperation items={oss.items} selected={inputs} setSelected={setInputs} rows={6} />
|
||||||
</FlexColumn>
|
</FlexColumn>
|
||||||
</AnimateFade>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { useMemo } from 'react';
|
||||||
import PickMultiOperation from '@/components/select/PickMultiOperation';
|
import PickMultiOperation from '@/components/select/PickMultiOperation';
|
||||||
import FlexColumn from '@/components/ui/FlexColumn';
|
import FlexColumn from '@/components/ui/FlexColumn';
|
||||||
import Label from '@/components/ui/Label';
|
import Label from '@/components/ui/Label';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import { IOperationSchema, OperationID } from '@/models/oss';
|
import { IOperationSchema, OperationID } from '@/models/oss';
|
||||||
|
|
||||||
interface TabArgumentsProps {
|
interface TabArgumentsProps {
|
||||||
|
@ -22,12 +21,12 @@ function TabArguments({ oss, inputs, target, setInputs }: TabArgumentsProps) {
|
||||||
[oss.items, potentialCycle]
|
[oss.items, potentialCycle]
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<AnimateFade className='cc-column'>
|
<div className='cc-fade-in cc-column'>
|
||||||
<FlexColumn>
|
<FlexColumn>
|
||||||
<Label text={`Выбор аргументов: [ ${inputs.length} ]`} />
|
<Label text={`Выбор аргументов: [ ${inputs.length} ]`} />
|
||||||
<PickMultiOperation items={filtered} selected={inputs} setSelected={setInputs} rows={8} />
|
<PickMultiOperation items={filtered} selected={inputs} setSelected={setInputs} rows={8} />
|
||||||
</FlexColumn>
|
</FlexColumn>
|
||||||
</AnimateFade>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import TextArea from '@/components/ui/TextArea';
|
import TextArea from '@/components/ui/TextArea';
|
||||||
import TextInput from '@/components/ui/TextInput';
|
import TextInput from '@/components/ui/TextInput';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
|
|
||||||
interface TabOperationProps {
|
interface TabOperationProps {
|
||||||
alias: string;
|
alias: string;
|
||||||
|
@ -13,7 +12,7 @@ interface TabOperationProps {
|
||||||
|
|
||||||
function TabOperation({ alias, onChangeAlias, title, onChangeTitle, comment, onChangeComment }: TabOperationProps) {
|
function TabOperation({ alias, onChangeAlias, title, onChangeTitle, comment, onChangeComment }: TabOperationProps) {
|
||||||
return (
|
return (
|
||||||
<AnimateFade className='cc-column'>
|
<div className='cc-fade-in cc-column'>
|
||||||
<TextInput
|
<TextInput
|
||||||
id='operation_title'
|
id='operation_title'
|
||||||
label='Полное название'
|
label='Полное название'
|
||||||
|
@ -38,7 +37,7 @@ function TabOperation({ alias, onChangeAlias, title, onChangeTitle, comment, onC
|
||||||
onChange={event => onChangeComment(event.target.value)}
|
onChange={event => onChangeComment(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</AnimateFade>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,8 @@ function TabSynthesis({
|
||||||
}: TabSynthesisProps) {
|
}: TabSynthesisProps) {
|
||||||
const { colors } = useConceptOptions();
|
const { colors } = useConceptOptions();
|
||||||
return (
|
return (
|
||||||
<DataLoader id='dlg-synthesis-tab' className='cc-column mt-3' isLoading={loading} error={error}>
|
<DataLoader isLoading={loading} error={error}>
|
||||||
|
<div className='cc-fade-in cc-column mt-3'>
|
||||||
<PickSubstitutions
|
<PickSubstitutions
|
||||||
schemas={schemas}
|
schemas={schemas}
|
||||||
prefixID={prefixes.dlg_cst_substitutes_list}
|
prefixID={prefixes.dlg_cst_substitutes_list}
|
||||||
|
@ -46,6 +47,7 @@ function TabSynthesis({
|
||||||
rows={4}
|
rows={4}
|
||||||
style={{ borderColor: isCorrect ? undefined : colors.fgRed }}
|
style={{ borderColor: isCorrect ? undefined : colors.fgRed }}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</DataLoader>
|
</DataLoader>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import PickConstituenta from '@/components/select/PickConstituenta';
|
||||||
import SelectMultiGrammeme from '@/components/select/SelectMultiGrammeme';
|
import SelectMultiGrammeme from '@/components/select/SelectMultiGrammeme';
|
||||||
import Label from '@/components/ui/Label';
|
import Label from '@/components/ui/Label';
|
||||||
import TextInput from '@/components/ui/TextInput';
|
import TextInput from '@/components/ui/TextInput';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import { ReferenceType } from '@/models/language';
|
import { ReferenceType } from '@/models/language';
|
||||||
import { parseEntityReference, parseGrammemes } from '@/models/languageAPI';
|
import { parseEntityReference, parseGrammemes } from '@/models/languageAPI';
|
||||||
import { CstMatchMode } from '@/models/miscellaneous';
|
import { CstMatchMode } from '@/models/miscellaneous';
|
||||||
|
@ -59,7 +58,7 @@ function TabEntityReference({ initial, schema, onChangeValid, onChangeReference
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimateFade className='cc-column'>
|
<div className='cc-fade-in cc-column'>
|
||||||
<PickConstituenta
|
<PickConstituenta
|
||||||
id='dlg_reference_entity_picker'
|
id='dlg_reference_entity_picker'
|
||||||
initialFilter={initial.text}
|
initialFilter={initial.text}
|
||||||
|
@ -108,7 +107,7 @@ function TabEntityReference({ initial, schema, onChangeValid, onChangeReference
|
||||||
onChangeValue={setSelectedGrams}
|
onChangeValue={setSelectedGrams}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</AnimateFade>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
import { useEffect, useLayoutEffect, useMemo, useState } from 'react';
|
import { useEffect, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import TextInput from '@/components/ui/TextInput';
|
import TextInput from '@/components/ui/TextInput';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import { ReferenceType } from '@/models/language';
|
import { ReferenceType } from '@/models/language';
|
||||||
import { parseSyntacticReference } from '@/models/languageAPI';
|
import { parseSyntacticReference } from '@/models/languageAPI';
|
||||||
|
|
||||||
|
@ -44,7 +43,7 @@ function TabSyntacticReference({ initial, onChangeValid, onChangeReference }: Ta
|
||||||
}, [nominal, offset, onChangeValid, onChangeReference]);
|
}, [nominal, offset, onChangeValid, onChangeReference]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimateFade className='flex flex-col gap-2'>
|
<div className='cc-fade-in flex flex-col gap-2'>
|
||||||
<TextInput
|
<TextInput
|
||||||
id='dlg_reference_offset'
|
id='dlg_reference_offset'
|
||||||
type='number'
|
type='number'
|
||||||
|
@ -70,7 +69,7 @@ function TabSyntacticReference({ initial, onChangeValid, onChangeReference }: Ta
|
||||||
value={nominal}
|
value={nominal}
|
||||||
onChange={event => setNominal(event.target.value)}
|
onChange={event => setNominal(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</AnimateFade>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ interface TabConstituentsProps {
|
||||||
|
|
||||||
function TabConstituents({ schema, error, loading, selected, setSelected }: TabConstituentsProps) {
|
function TabConstituents({ schema, error, loading, selected, setSelected }: TabConstituentsProps) {
|
||||||
return (
|
return (
|
||||||
<DataLoader id='dlg-constituents-tab' isLoading={loading} error={error} hasNoData={!schema}>
|
<DataLoader isLoading={loading} error={error} hasNoData={!schema}>
|
||||||
{schema ? (
|
{schema ? (
|
||||||
<PickMultiConstituenta
|
<PickMultiConstituenta
|
||||||
schema={schema}
|
schema={schema}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { useMemo } from 'react';
|
||||||
|
|
||||||
import PickSchema from '@/components/select/PickSchema';
|
import PickSchema from '@/components/select/PickSchema';
|
||||||
import TextInput from '@/components/ui/TextInput';
|
import TextInput from '@/components/ui/TextInput';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import { useLibrary } from '@/context/LibraryContext';
|
import { useLibrary } from '@/context/LibraryContext';
|
||||||
import { LibraryItemID, LibraryItemType } from '@/models/library';
|
import { LibraryItemID, LibraryItemType } from '@/models/library';
|
||||||
import { IRSForm } from '@/models/rsform';
|
import { IRSForm } from '@/models/rsform';
|
||||||
|
@ -22,7 +21,7 @@ function TabSchema({ selected, receiver, setSelected }: TabSchemaProps) {
|
||||||
const sortedItems = useMemo(() => sortItemsForInlineSynthesis(receiver, library.items), [receiver, library.items]);
|
const sortedItems = useMemo(() => sortItemsForInlineSynthesis(receiver, library.items), [receiver, library.items]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimateFade className='flex flex-col'>
|
<div className='cc-fade-in flex flex-col'>
|
||||||
<PickSchema
|
<PickSchema
|
||||||
id='dlg_schema_picker' // prettier: split lines
|
id='dlg_schema_picker' // prettier: split lines
|
||||||
items={sortedItems}
|
items={sortedItems}
|
||||||
|
@ -43,7 +42,7 @@ function TabSchema({ selected, receiver, setSelected }: TabSchemaProps) {
|
||||||
dense
|
dense
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</AnimateFade>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ function TabSubstitutions({
|
||||||
const schemas = useMemo(() => [...(source ? [source] : []), ...(receiver ? [receiver] : [])], [source, receiver]);
|
const schemas = useMemo(() => [...(source ? [source] : []), ...(receiver ? [receiver] : [])], [source, receiver]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataLoader id='dlg-substitutions-tab' className='cc-column' isLoading={loading} error={error} hasNoData={!source}>
|
<DataLoader isLoading={loading} error={error} hasNoData={!source}>
|
||||||
<PickSubstitutions
|
<PickSubstitutions
|
||||||
substitutions={substitutions}
|
substitutions={substitutions}
|
||||||
setSubstitutions={setSubstitutions}
|
setSubstitutions={setSubstitutions}
|
||||||
|
|
|
@ -118,7 +118,7 @@ function DlgRelocateConstituents({ oss, hideWindow, initialTarget, onSubmit }: D
|
||||||
onSelectValue={handleSelectDestination}
|
onSelectValue={handleSelectDestination}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<DataLoader id='dlg-relocate-constituents' isLoading={sourceData.loading} error={sourceData.error}>
|
<DataLoader isLoading={sourceData.loading} error={sourceData.error}>
|
||||||
{sourceData.schema ? (
|
{sourceData.schema ? (
|
||||||
<PickMultiConstituenta
|
<PickMultiConstituenta
|
||||||
noBorder
|
noBorder
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import RequireAuth from '@/components/wrap/RequireAuth';
|
import RequireAuth from '@/components/wrap/RequireAuth';
|
||||||
|
|
||||||
import FormCreateItem from './FormCreateItem';
|
import FormCreateItem from './FormCreateItem';
|
||||||
|
|
||||||
function CreateItemPage() {
|
function CreateItemPage() {
|
||||||
return (
|
return (
|
||||||
<AnimateFade>
|
|
||||||
<RequireAuth>
|
<RequireAuth>
|
||||||
<FormCreateItem />
|
<FormCreateItem />
|
||||||
</RequireAuth>
|
</RequireAuth>
|
||||||
</AnimateFade>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,10 @@ function FormCreateItem() {
|
||||||
}, [itemType]);
|
}, [itemType]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className={clsx('cc-column', 'min-w-[30rem] max-w-[30rem] mx-auto', 'px-6 py-3')} onSubmit={handleSubmit}>
|
<form
|
||||||
|
className={clsx('cc-fade-in cc-column', 'min-w-[30rem] max-w-[30rem] mx-auto', 'px-6 py-3')}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
>
|
||||||
<h1 className='select-none'>
|
<h1 className='select-none'>
|
||||||
{itemType == LibraryItemType.RSFORM ? (
|
{itemType == LibraryItemType.RSFORM ? (
|
||||||
<Overlay position='top-0 right-[0.5rem]'>
|
<Overlay position='top-0 right-[0.5rem]'>
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
import { useLayoutEffect, useMemo } from 'react';
|
import { useLayoutEffect, useMemo } from 'react';
|
||||||
import { TransformComponent, TransformWrapper } from 'react-zoom-pan-pinch';
|
import { TransformComponent, TransformWrapper } from 'react-zoom-pan-pinch';
|
||||||
|
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||||
import { resources } from '@/utils/constants';
|
import { resources } from '@/utils/constants';
|
||||||
|
|
||||||
|
@ -18,13 +17,13 @@ function DatabaseSchemaPage() {
|
||||||
}, [setNoFooter]);
|
}, [setNoFooter]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimateFade className='flex justify-center overflow-hidden' style={{ maxHeight: panelHeight }}>
|
<div className='cc-fade-in flex justify-center overflow-hidden' style={{ maxHeight: panelHeight }}>
|
||||||
<TransformWrapper>
|
<TransformWrapper>
|
||||||
<TransformComponent>
|
<TransformComponent>
|
||||||
<img alt='Схема базы данных' src={resources.db_schema} className='w-fit h-fit' />
|
<img alt='Схема базы данных' src={resources.db_schema} className='w-fit h-fit' />
|
||||||
</TransformComponent>
|
</TransformComponent>
|
||||||
</TransformWrapper>
|
</TransformWrapper>
|
||||||
</AnimateFade>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { AnimatePresence } from 'framer-motion';
|
|
||||||
import fileDownload from 'js-file-download';
|
import fileDownload from 'js-file-download';
|
||||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
@ -150,6 +149,7 @@ function LibraryPage() {
|
||||||
const viewLocations = useMemo(
|
const viewLocations = useMemo(
|
||||||
() => (
|
() => (
|
||||||
<ViewSideLocation
|
<ViewSideLocation
|
||||||
|
isVisible={options.folderMode}
|
||||||
activeLocation={options.location}
|
activeLocation={options.location}
|
||||||
onChangeActiveLocation={options.setLocation}
|
onChangeActiveLocation={options.setLocation}
|
||||||
subfolders={subfolders}
|
subfolders={subfolders}
|
||||||
|
@ -163,6 +163,7 @@ function LibraryPage() {
|
||||||
options.location,
|
options.location,
|
||||||
library.folders,
|
library.folders,
|
||||||
options.setLocation,
|
options.setLocation,
|
||||||
|
options.folderMode,
|
||||||
toggleFolderMode,
|
toggleFolderMode,
|
||||||
promptRenameLocation,
|
promptRenameLocation,
|
||||||
toggleSubfolders,
|
toggleSubfolders,
|
||||||
|
@ -171,12 +172,7 @@ function LibraryPage() {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataLoader
|
<DataLoader isLoading={library.loading} error={library.loadingError} hasNoData={library.items.length === 0}>
|
||||||
id='library-page' // prettier: split lines
|
|
||||||
isLoading={library.loading}
|
|
||||||
error={library.loadingError}
|
|
||||||
hasNoData={library.items.length === 0}
|
|
||||||
>
|
|
||||||
{showRenameLocation ? (
|
{showRenameLocation ? (
|
||||||
<DlgChangeLocation
|
<DlgChangeLocation
|
||||||
initial={options.location}
|
initial={options.location}
|
||||||
|
@ -187,7 +183,7 @@ function LibraryPage() {
|
||||||
<Overlay
|
<Overlay
|
||||||
position={options.noNavigation ? 'top-[0.25rem] right-[3rem]' : 'top-[0.25rem] right-0'}
|
position={options.noNavigation ? 'top-[0.25rem] right-[3rem]' : 'top-[0.25rem] right-0'}
|
||||||
layer='z-tooltip'
|
layer='z-tooltip'
|
||||||
className='transition-all'
|
className='cc-animate-position'
|
||||||
>
|
>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Выгрузить в формате CSV'
|
title='Выгрузить в формате CSV'
|
||||||
|
@ -218,8 +214,8 @@ function LibraryPage() {
|
||||||
toggleFolderMode={toggleFolderMode}
|
toggleFolderMode={toggleFolderMode}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className='flex'>
|
<div className='cc-fade-in flex'>
|
||||||
<AnimatePresence initial={false}>{options.folderMode ? viewLocations : null}</AnimatePresence>
|
{viewLocations}
|
||||||
{viewLibrary}
|
{viewLibrary}
|
||||||
</div>
|
</div>
|
||||||
</DataLoader>
|
</DataLoader>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { motion } from 'framer-motion';
|
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
import { LocationIcon, VisibilityIcon } from '@/components/DomainIcons';
|
import { LocationIcon, VisibilityIcon } from '@/components/DomainIcons';
|
||||||
|
@ -25,7 +24,6 @@ import { useUsers } from '@/context/UsersContext';
|
||||||
import useDropdown from '@/hooks/useDropdown';
|
import useDropdown from '@/hooks/useDropdown';
|
||||||
import { LocationHead } from '@/models/library';
|
import { LocationHead } from '@/models/library';
|
||||||
import { UserID } from '@/models/user';
|
import { UserID } from '@/models/user';
|
||||||
import { animateDropdownItem } from '@/styling/animations';
|
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
import { describeLocationHead, labelLocationHead } from '@/utils/labels';
|
import { describeLocationHead, labelLocationHead } from '@/utils/labels';
|
||||||
import { tripleToggleColor } from '@/utils/utils';
|
import { tripleToggleColor } from '@/utils/utils';
|
||||||
|
@ -163,16 +161,14 @@ function ToolbarSearch({
|
||||||
icon={<IconEditor size='1.25rem' className={tripleToggleColor(isEditor)} />}
|
icon={<IconEditor size='1.25rem' className={tripleToggleColor(isEditor)} />}
|
||||||
onClick={toggleEditor}
|
onClick={toggleEditor}
|
||||||
/>
|
/>
|
||||||
<motion.div className='px-1 pb-1' variants={animateDropdownItem}>
|
|
||||||
<SelectUser
|
<SelectUser
|
||||||
noBorder
|
noBorder
|
||||||
placeholder='Выберите владельца'
|
placeholder='Выберите владельца'
|
||||||
className='min-w-[15rem] text-sm'
|
className='min-w-[15rem] text-sm mx-1 mb-1'
|
||||||
items={users}
|
items={users}
|
||||||
value={filterUser}
|
value={filterUser}
|
||||||
onSelectValue={onChangeFilterUser}
|
onSelectValue={onChangeFilterUser}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { motion } from 'framer-motion';
|
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
|
@ -15,12 +14,12 @@ import { useLibrary } from '@/context/LibraryContext';
|
||||||
import useWindowSize from '@/hooks/useWindowSize';
|
import useWindowSize from '@/hooks/useWindowSize';
|
||||||
import { FolderNode, FolderTree } from '@/models/FolderTree';
|
import { FolderNode, FolderTree } from '@/models/FolderTree';
|
||||||
import { HelpTopic } from '@/models/miscellaneous';
|
import { HelpTopic } from '@/models/miscellaneous';
|
||||||
import { animateSideMinWidth } from '@/styling/animations';
|
|
||||||
import { PARAMETER, prefixes } from '@/utils/constants';
|
import { PARAMETER, prefixes } from '@/utils/constants';
|
||||||
import { information } from '@/utils/labels';
|
import { information } from '@/utils/labels';
|
||||||
|
|
||||||
interface ViewSideLocationProps {
|
interface ViewSideLocationProps {
|
||||||
folderTree: FolderTree;
|
folderTree: FolderTree;
|
||||||
|
isVisible: boolean;
|
||||||
subfolders: boolean;
|
subfolders: boolean;
|
||||||
activeLocation: string;
|
activeLocation: string;
|
||||||
onChangeActiveLocation: (newValue: string) => void;
|
onChangeActiveLocation: (newValue: string) => void;
|
||||||
|
@ -33,6 +32,7 @@ function ViewSideLocation({
|
||||||
folderTree,
|
folderTree,
|
||||||
activeLocation,
|
activeLocation,
|
||||||
subfolders,
|
subfolders,
|
||||||
|
isVisible,
|
||||||
onChangeActiveLocation,
|
onChangeActiveLocation,
|
||||||
toggleFolderMode,
|
toggleFolderMode,
|
||||||
toggleSubfolders,
|
toggleSubfolders,
|
||||||
|
@ -57,7 +57,6 @@ function ViewSideLocation({
|
||||||
return located.length !== 0;
|
return located.length !== 0;
|
||||||
}, [activeLocation, user, items]);
|
}, [activeLocation, user, items]);
|
||||||
|
|
||||||
const animations = useMemo(() => animateSideMinWidth(windowSize.isSmall ? '10rem' : '15rem'), [windowSize]);
|
|
||||||
const maxHeight = useMemo(() => calculateHeight('4.5rem'), [calculateHeight]);
|
const maxHeight = useMemo(() => calculateHeight('4.5rem'), [calculateHeight]);
|
||||||
|
|
||||||
const handleClickFolder = useCallback(
|
const handleClickFolder = useCallback(
|
||||||
|
@ -77,11 +76,16 @@ function ViewSideLocation({
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<div
|
||||||
className={clsx('max-w-[10rem] sm:max-w-[15rem]', 'flex flex-col', 'text:xs sm:text-sm', 'select-none')}
|
className={clsx('max-w-[10rem] sm:max-w-[15rem]', 'flex flex-col', 'text:xs sm:text-sm', 'select-none')}
|
||||||
initial={{ ...animations.initial }}
|
style={{
|
||||||
animate={{ ...animations.animate }}
|
transitionProperty: 'width, min-width, opacity',
|
||||||
exit={{ ...animations.exit }}
|
transitionDuration: `${PARAMETER.moveDuration}ms`,
|
||||||
|
transitionTimingFunction: 'ease-out',
|
||||||
|
minWidth: isVisible ? (windowSize.isSmall ? '10rem' : '15rem') : '0',
|
||||||
|
width: isVisible ? '100%' : '0',
|
||||||
|
opacity: isVisible ? 1 : 0
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div className='h-[2.08rem] flex justify-between items-center pr-1 pl-[0.125rem]'>
|
<div className='h-[2.08rem] flex justify-between items-center pr-1 pl-[0.125rem]'>
|
||||||
<BadgeHelp
|
<BadgeHelp
|
||||||
|
@ -117,7 +121,7 @@ function ViewSideLocation({
|
||||||
onClick={handleClickFolder}
|
onClick={handleClickFolder}
|
||||||
style={{ maxHeight: maxHeight }}
|
style={{ maxHeight: maxHeight }}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ import InfoError, { ErrorData } from '@/components/info/InfoError';
|
||||||
import SubmitButton from '@/components/ui/SubmitButton';
|
import SubmitButton from '@/components/ui/SubmitButton';
|
||||||
import TextInput from '@/components/ui/TextInput';
|
import TextInput from '@/components/ui/TextInput';
|
||||||
import TextURL from '@/components/ui/TextURL';
|
import TextURL from '@/components/ui/TextURL';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import ExpectedAnonymous from '@/components/wrap/ExpectedAnonymous';
|
import ExpectedAnonymous from '@/components/wrap/ExpectedAnonymous';
|
||||||
import { useAuth } from '@/context/AuthContext';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||||
|
@ -52,8 +51,7 @@ function LoginPage() {
|
||||||
return <ExpectedAnonymous />;
|
return <ExpectedAnonymous />;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<AnimateFade>
|
<form className={clsx('cc-column cc-fade-in', 'w-[24rem] mx-auto', 'pt-12 pb-6 px-6')} onSubmit={handleSubmit}>
|
||||||
<form className={clsx('cc-column', 'w-[24rem] mx-auto', 'pt-12 pb-6 px-6')} onSubmit={handleSubmit}>
|
|
||||||
<img alt='Концепт Портал' src={resources.logo} className='max-h-[2.5rem] min-w-[2.5rem] mb-3' />
|
<img alt='Концепт Портал' src={resources.logo} className='max-h-[2.5rem] min-w-[2.5rem] mb-3' />
|
||||||
<TextInput
|
<TextInput
|
||||||
id='username'
|
id='username'
|
||||||
|
@ -89,7 +87,6 @@ function LoginPage() {
|
||||||
</div>
|
</div>
|
||||||
{error ? <ProcessError error={error} /> : null}
|
{error ? <ProcessError error={error} /> : null}
|
||||||
</form>
|
</form>
|
||||||
</AnimateFade>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { motion } from 'framer-motion';
|
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { IconMenuFold, IconMenuUnfold } from '@/components/Icons';
|
import { IconMenuFold, IconMenuUnfold } from '@/components/Icons';
|
||||||
|
@ -10,8 +9,7 @@ import SelectTree from '@/components/ui/SelectTree';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||||
import useDropdown from '@/hooks/useDropdown';
|
import useDropdown from '@/hooks/useDropdown';
|
||||||
import { HelpTopic, topicParent } from '@/models/miscellaneous';
|
import { HelpTopic, topicParent } from '@/models/miscellaneous';
|
||||||
import { animateSlideLeft } from '@/styling/animations';
|
import { PARAMETER, prefixes } from '@/utils/constants';
|
||||||
import { prefixes } from '@/utils/constants';
|
|
||||||
import { describeHelpTopic, labelHelpTopic } from '@/utils/labels';
|
import { describeHelpTopic, labelHelpTopic } from '@/utils/labels';
|
||||||
|
|
||||||
interface TopicsDropdownProps {
|
interface TopicsDropdownProps {
|
||||||
|
@ -52,20 +50,9 @@ function TopicsDropdown({ activeTopic, onChangeTopic }: TopicsDropdownProps) {
|
||||||
title='Список тем'
|
title='Список тем'
|
||||||
hideTitle={menu.isOpen}
|
hideTitle={menu.isOpen}
|
||||||
icon={!menu.isOpen ? <IconMenuUnfold size='1.25rem' /> : <IconMenuFold size='1.25rem' />}
|
icon={!menu.isOpen ? <IconMenuUnfold size='1.25rem' /> : <IconMenuFold size='1.25rem' />}
|
||||||
className='w-[3rem] h-7 rounded-none'
|
className={clsx('w-[3rem] h-7 rounded-none border-l-0', menu.isOpen && 'border-b-0')}
|
||||||
onClick={menu.toggle}
|
onClick={menu.toggle}
|
||||||
/>
|
/>
|
||||||
<motion.div
|
|
||||||
className={clsx(
|
|
||||||
'border divide-y rounded-none', // prettier: split-lines
|
|
||||||
'cc-scroll-y',
|
|
||||||
'clr-controls'
|
|
||||||
)}
|
|
||||||
style={{ maxHeight: calculateHeight('4rem + 2px') }}
|
|
||||||
initial={false}
|
|
||||||
animate={menu.isOpen ? 'open' : 'closed'}
|
|
||||||
variants={animateSlideLeft}
|
|
||||||
>
|
|
||||||
<SelectTree
|
<SelectTree
|
||||||
items={Object.values(HelpTopic).map(item => item as HelpTopic)}
|
items={Object.values(HelpTopic).map(item => item as HelpTopic)}
|
||||||
value={activeTopic}
|
value={activeTopic}
|
||||||
|
@ -74,8 +61,18 @@ function TopicsDropdown({ activeTopic, onChangeTopic }: TopicsDropdownProps) {
|
||||||
getParent={item => topicParent.get(item) ?? item}
|
getParent={item => topicParent.get(item) ?? item}
|
||||||
getLabel={labelHelpTopic}
|
getLabel={labelHelpTopic}
|
||||||
getDescription={describeHelpTopic}
|
getDescription={describeHelpTopic}
|
||||||
|
className={clsx(
|
||||||
|
'border-r border-t rounded-none', // prettier: split-lines
|
||||||
|
'cc-scroll-y',
|
||||||
|
'clr-controls'
|
||||||
|
)}
|
||||||
|
style={{
|
||||||
|
maxHeight: calculateHeight('4rem + 2px'),
|
||||||
|
transitionProperty: 'clip-path',
|
||||||
|
transitionDuration: `${PARAMETER.moveDuration}ms`,
|
||||||
|
clipPath: menu.isOpen ? 'inset(0% 0% 0% 0%)' : 'inset(0% 100% 0% 0%)'
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ function TopicsStatic({ activeTopic, onChangeTopic }: TopicsStaticProps) {
|
||||||
'min-w-[14.5rem] max-w-[14.5rem] sm:min-w-[12.5rem] sm:max-w-[12.5rem] md:min-w-[14.5rem] md:max-w-[14.5rem]',
|
'min-w-[14.5rem] max-w-[14.5rem] sm:min-w-[12.5rem] sm:max-w-[12.5rem] md:min-w-[14.5rem] md:max-w-[14.5rem]',
|
||||||
'cc-scroll-y',
|
'cc-scroll-y',
|
||||||
'self-start',
|
'self-start',
|
||||||
'border divide-y rounded-none',
|
'border-x border-t rounded-none',
|
||||||
'clr-controls',
|
'clr-controls',
|
||||||
'text-xs sm:text-sm',
|
'text-xs sm:text-sm',
|
||||||
'select-none'
|
'select-none'
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||||
import { HelpTopic } from '@/models/miscellaneous';
|
import { HelpTopic } from '@/models/miscellaneous';
|
||||||
import TopicPage from '@/pages/ManualsPage/TopicPage';
|
import TopicPage from '@/pages/ManualsPage/TopicPage';
|
||||||
|
@ -12,13 +11,13 @@ interface ViewTopicProps {
|
||||||
function ViewTopic({ topic }: ViewTopicProps) {
|
function ViewTopic({ topic }: ViewTopicProps) {
|
||||||
const { mainHeight } = useConceptOptions();
|
const { mainHeight } = useConceptOptions();
|
||||||
return (
|
return (
|
||||||
<AnimateFade
|
<div
|
||||||
key={topic}
|
key={topic}
|
||||||
className='py-2 px-6 mx-auto sm:mx-0 lg:px-12 overflow-y-auto'
|
className='cc-fade-in py-2 px-6 mx-auto sm:mx-0 lg:px-12 overflow-y-auto'
|
||||||
style={{ maxHeight: mainHeight }}
|
style={{ maxHeight: mainHeight }}
|
||||||
>
|
>
|
||||||
<TopicPage topic={topic} />
|
<TopicPage topic={topic} />
|
||||||
</AnimateFade>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import FlexColumn from '@/components/ui/FlexColumn';
|
import FlexColumn from '@/components/ui/FlexColumn';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import { useOSS } from '@/context/OssContext';
|
import { useOSS } from '@/context/OssContext';
|
||||||
import EditorLibraryItem from '@/pages/RSFormPage/EditorRSFormCard/EditorLibraryItem';
|
import EditorLibraryItem from '@/pages/RSFormPage/EditorRSFormCard/EditorLibraryItem';
|
||||||
import ToolbarRSFormCard from '@/pages/RSFormPage/EditorRSFormCard/ToolbarRSFormCard';
|
import ToolbarRSFormCard from '@/pages/RSFormPage/EditorRSFormCard/ToolbarRSFormCard';
|
||||||
|
@ -47,9 +46,14 @@ function EditorOssCard({ isModified, onDestroy, setIsModified }: EditorOssCardPr
|
||||||
onDestroy={onDestroy}
|
onDestroy={onDestroy}
|
||||||
controller={controller}
|
controller={controller}
|
||||||
/>
|
/>
|
||||||
<AnimateFade
|
<div
|
||||||
onKeyDown={handleInput}
|
onKeyDown={handleInput}
|
||||||
className={clsx('md:w-fit md:max-w-fit max-w-[32rem]', 'mx-auto pt-[1.9rem]', 'flex flex-row flex-wrap px-6')}
|
className={clsx(
|
||||||
|
'cc-fade-in',
|
||||||
|
'md:w-fit md:max-w-fit max-w-[32rem]',
|
||||||
|
'mx-auto pt-[1.9rem]',
|
||||||
|
'flex flex-row flex-wrap px-6'
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<FlexColumn className='px-3'>
|
<FlexColumn className='px-3'>
|
||||||
<FormOSS id={globals.library_item_editor} isModified={isModified} setIsModified={setIsModified} />
|
<FormOSS id={globals.library_item_editor} isModified={isModified} setIsModified={setIsModified} />
|
||||||
|
@ -57,7 +61,7 @@ function EditorOssCard({ isModified, onDestroy, setIsModified }: EditorOssCardPr
|
||||||
</FlexColumn>
|
</FlexColumn>
|
||||||
|
|
||||||
{schema ? <OssStats stats={schema.stats} /> : null}
|
{schema ? <OssStats stats={schema.stats} /> : null}
|
||||||
</AnimateFade>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ import {
|
||||||
|
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
import Overlay from '@/components/ui/Overlay';
|
import Overlay from '@/components/ui/Overlay';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||||
import { useOSS } from '@/context/OssContext';
|
import { useOSS } from '@/context/OssContext';
|
||||||
import useLocalStorage from '@/hooks/useLocalStorage';
|
import useLocalStorage from '@/hooks/useLocalStorage';
|
||||||
|
@ -349,7 +348,7 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimateFade tabIndex={-1} onKeyDown={handleKeyDown}>
|
<div tabIndex={-1} onKeyDown={handleKeyDown}>
|
||||||
<Overlay position='top-[1.9rem] pt-1 right-1/2 translate-x-1/2' className='rounded-b-2xl cc-blur'>
|
<Overlay position='top-[1.9rem] pt-1 right-1/2 translate-x-1/2' className='rounded-b-2xl cc-blur'>
|
||||||
<ToolbarOssGraph
|
<ToolbarOssGraph
|
||||||
isModified={isModified}
|
isModified={isModified}
|
||||||
|
@ -381,10 +380,10 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
|
||||||
{...menuProps}
|
{...menuProps}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<div className='relative w-[100vw]' style={{ height: mainHeight, fontFamily: 'Rubik' }}>
|
<div className='cc-fade-in relative w-[100vw]' style={{ height: mainHeight, fontFamily: 'Rubik' }}>
|
||||||
{graph}
|
{graph}
|
||||||
</div>
|
</div>
|
||||||
</AnimateFade>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,9 @@ import {
|
||||||
IconShare
|
IconShare
|
||||||
} from '@/components/Icons';
|
} from '@/components/Icons';
|
||||||
import Button from '@/components/ui/Button';
|
import Button from '@/components/ui/Button';
|
||||||
|
import Divider from '@/components/ui/Divider';
|
||||||
import Dropdown from '@/components/ui/Dropdown';
|
import Dropdown from '@/components/ui/Dropdown';
|
||||||
import DropdownButton from '@/components/ui/DropdownButton';
|
import DropdownButton from '@/components/ui/DropdownButton';
|
||||||
import DropdownDivider from '@/components/ui/DropdownDivider';
|
|
||||||
import { useAccessMode } from '@/context/AccessModeContext';
|
import { useAccessMode } from '@/context/AccessModeContext';
|
||||||
import { useAuth } from '@/context/AuthContext';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||||
|
@ -102,7 +102,7 @@ function MenuOssTabs({ onDestroy }: MenuOssTabsProps) {
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<DropdownDivider margins='mx-3 my-1' />
|
<Divider margins='mx-3 my-1' />
|
||||||
|
|
||||||
{user ? (
|
{user ? (
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { AnimatePresence } from 'framer-motion';
|
|
||||||
import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useState } from 'react';
|
import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
|
@ -418,7 +417,7 @@ export const OssEditState = ({ selected, setSelected, children }: React.PropsWit
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{model.schema ? (
|
{model.schema ? (
|
||||||
<AnimatePresence>
|
<>
|
||||||
{showEditEditors ? (
|
{showEditEditors ? (
|
||||||
<DlgEditEditors
|
<DlgEditEditors
|
||||||
hideWindow={() => setShowEditEditors(false)}
|
hideWindow={() => setShowEditEditors(false)}
|
||||||
|
@ -472,8 +471,7 @@ export const OssEditState = ({ selected, setSelected, children }: React.PropsWit
|
||||||
onSubmit={handleRelocateConstituents}
|
onSubmit={handleRelocateConstituents}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
)
|
</>
|
||||||
</AnimatePresence>
|
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
|
|
|
@ -12,7 +12,6 @@ import Loader from '@/components/ui/Loader';
|
||||||
import Overlay from '@/components/ui/Overlay';
|
import Overlay from '@/components/ui/Overlay';
|
||||||
import TabLabel from '@/components/ui/TabLabel';
|
import TabLabel from '@/components/ui/TabLabel';
|
||||||
import TextURL from '@/components/ui/TextURL';
|
import TextURL from '@/components/ui/TextURL';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import { useAuth } from '@/context/AuthContext';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||||
import { useLibrary } from '@/context/LibraryContext';
|
import { useLibrary } from '@/context/LibraryContext';
|
||||||
|
@ -151,10 +150,10 @@ function OssTabs() {
|
||||||
</TabList>
|
</TabList>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
|
||||||
<AnimateFade>
|
<div className='overflow-x-hidden'>
|
||||||
{cardPanel}
|
{cardPanel}
|
||||||
{graphPanel}
|
{graphPanel}
|
||||||
</AnimateFade>
|
</div>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
) : null}
|
) : null}
|
||||||
</OssEditState>
|
</OssEditState>
|
||||||
|
|
|
@ -65,12 +65,8 @@ function PasswordChangePage() {
|
||||||
return <ProcessError error={error} />;
|
return <ProcessError error={error} />;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<DataLoader
|
<DataLoader isLoading={loading} hasNoData={!isTokenValid}>
|
||||||
id='password-change-page' //
|
<form className={clsx('cc-fade-in cc-column', 'w-[24rem] mx-auto', 'px-6 mt-3')} onSubmit={handleSubmit}>
|
||||||
isLoading={loading}
|
|
||||||
hasNoData={!isTokenValid}
|
|
||||||
>
|
|
||||||
<form className={clsx('cc-column', 'w-[24rem] mx-auto', 'px-6 mt-3')} onSubmit={handleSubmit}>
|
|
||||||
<TextInput
|
<TextInput
|
||||||
id='new_password'
|
id='new_password'
|
||||||
type='password'
|
type='password'
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { AnimatePresence } from 'framer-motion';
|
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||||
|
@ -88,14 +87,16 @@ function EditorConstituenta({ activeCst, isModified, setIsModified, onOpenEdit }
|
||||||
onReset={() => setToggleReset(prev => !prev)}
|
onReset={() => setToggleReset(prev => !prev)}
|
||||||
onToggleList={() => setShowList(prev => !prev)}
|
onToggleList={() => setShowList(prev => !prev)}
|
||||||
/>
|
/>
|
||||||
<div className='pt-[1.9rem] overflow-y-auto overflow-x-clip min-h-[20rem]' style={{ maxHeight: mainHeight }}>
|
|
||||||
<div
|
<div
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'max-w-[95rem] mx-auto', // prettier: split lines
|
'cc-fade-in',
|
||||||
'flex',
|
'min-h-[20rem] max-w-[95rem] mx-auto',
|
||||||
|
'flex pt-[1.9rem]',
|
||||||
|
'overflow-y-auto overflow-x-clip',
|
||||||
{ 'flex-col md:items-center': isNarrow }
|
{ 'flex-col md:items-center': isNarrow }
|
||||||
)}
|
)}
|
||||||
|
style={{ maxHeight: mainHeight }}
|
||||||
onKeyDown={handleInput}
|
onKeyDown={handleInput}
|
||||||
>
|
>
|
||||||
<FormConstituenta
|
<FormConstituenta
|
||||||
|
@ -109,18 +110,14 @@ function EditorConstituenta({ activeCst, isModified, setIsModified, onOpenEdit }
|
||||||
onRename={controller.renameCst}
|
onRename={controller.renameCst}
|
||||||
onOpenEdit={onOpenEdit}
|
onOpenEdit={onOpenEdit}
|
||||||
/>
|
/>
|
||||||
<AnimatePresence initial={false}>
|
|
||||||
{showList ? (
|
|
||||||
<ViewConstituents
|
<ViewConstituents
|
||||||
|
isMounted={showList}
|
||||||
schema={controller.schema}
|
schema={controller.schema}
|
||||||
expression={activeCst?.definition_formal ?? ''}
|
expression={activeCst?.definition_formal ?? ''}
|
||||||
isBottom={isNarrow}
|
isBottom={isNarrow}
|
||||||
activeCst={activeCst}
|
activeCst={activeCst}
|
||||||
onOpenEdit={onOpenEdit}
|
onOpenEdit={onOpenEdit}
|
||||||
/>
|
/>
|
||||||
) : null}
|
|
||||||
</AnimatePresence>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { AnimatePresence } from 'framer-motion';
|
|
||||||
import { useEffect, useLayoutEffect, useMemo, useState } from 'react';
|
import { useEffect, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
|
@ -155,12 +154,10 @@ function FormConstituenta({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimateFade className='mx-0 md:mx-auto pt-[2rem] xs:pt-0'>
|
<div className='mx-0 md:mx-auto pt-[2rem] xs:pt-0'>
|
||||||
<AnimatePresence>
|
|
||||||
{showTypification && state ? (
|
{showTypification && state ? (
|
||||||
<DlgShowTypeGraph items={typeInfo ? [typeInfo] : []} hideWindow={() => setShowTypification(false)} />
|
<DlgShowTypeGraph items={typeInfo ? [typeInfo] : []} hideWindow={() => setShowTypification(false)} />
|
||||||
) : null}
|
) : null}
|
||||||
</AnimatePresence>
|
|
||||||
{state ? (
|
{state ? (
|
||||||
<ControlsOverlay
|
<ControlsOverlay
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
@ -201,12 +198,8 @@ function FormConstituenta({
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{state ? (
|
{state ? (
|
||||||
<AnimatePresence>
|
<>
|
||||||
<AnimateFade
|
<AnimateFade hideContent={!state.definition_formal && isElementary}>
|
||||||
key='cst_expression_fade'
|
|
||||||
hideContent={!state.definition_formal && isElementary}
|
|
||||||
style={{ willChange: 'auto' }}
|
|
||||||
>
|
|
||||||
<EditorRSExpression
|
<EditorRSExpression
|
||||||
id='cst_expression'
|
id='cst_expression'
|
||||||
label={
|
label={
|
||||||
|
@ -230,7 +223,7 @@ function FormConstituenta({
|
||||||
onShowTypeGraph={handleTypeGraph}
|
onShowTypeGraph={handleTypeGraph}
|
||||||
/>
|
/>
|
||||||
</AnimateFade>
|
</AnimateFade>
|
||||||
<AnimateFade key='cst_definition_fade' hideContent={!state.definition_raw && isElementary}>
|
<AnimateFade hideContent={!state.definition_raw && isElementary}>
|
||||||
<RefsInput
|
<RefsInput
|
||||||
id='cst_definition'
|
id='cst_definition'
|
||||||
label='Текстовое определение'
|
label='Текстовое определение'
|
||||||
|
@ -246,7 +239,7 @@ function FormConstituenta({
|
||||||
onChange={newValue => setTextDefinition(newValue)}
|
onChange={newValue => setTextDefinition(newValue)}
|
||||||
/>
|
/>
|
||||||
</AnimateFade>
|
</AnimateFade>
|
||||||
<AnimateFade key='cst_convention_fade' hideContent={!showConvention}>
|
<AnimateFade hideContent={!showConvention}>
|
||||||
<TextArea
|
<TextArea
|
||||||
id='cst_convention'
|
id='cst_convention'
|
||||||
fitContent
|
fitContent
|
||||||
|
@ -259,7 +252,7 @@ function FormConstituenta({
|
||||||
onChange={event => setConvention(event.target.value)}
|
onChange={event => setConvention(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</AnimateFade>
|
</AnimateFade>
|
||||||
<AnimateFade key='cst_convention_button' hideContent={showConvention || (disabled && !processing)}>
|
<AnimateFade noFadeOut hideContent={showConvention || (disabled && !processing)}>
|
||||||
<button
|
<button
|
||||||
key='cst_disable_comment'
|
key='cst_disable_comment'
|
||||||
id='cst_disable_comment'
|
id='cst_disable_comment'
|
||||||
|
@ -297,10 +290,10 @@ function FormConstituenta({
|
||||||
</Overlay>
|
</Overlay>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</AnimatePresence>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
</form>
|
</form>
|
||||||
</AnimateFade>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ function ToolbarConstituenta({
|
||||||
return (
|
return (
|
||||||
<Overlay
|
<Overlay
|
||||||
position='cc-tab-tools right-1/2 translate-x-1/2 xs:right-4 xs:translate-x-0 md:right-1/2 md:translate-x-1/2'
|
position='cc-tab-tools right-1/2 translate-x-1/2 xs:right-4 xs:translate-x-0 md:right-1/2 md:translate-x-1/2'
|
||||||
className='cc-icons outline-none transition-all duration-500 cc-blur px-1 rounded-b-2xl'
|
className='cc-icons cc-animate-position outline-none cc-blur px-1 rounded-b-2xl'
|
||||||
>
|
>
|
||||||
{controller.schema && controller.schema?.oss.length > 0 ? (
|
{controller.schema && controller.schema?.oss.length > 0 ? (
|
||||||
<MiniSelectorOSS
|
<MiniSelectorOSS
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
import { ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
||||||
import { AnimatePresence } from 'framer-motion';
|
|
||||||
import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
|
@ -162,11 +161,9 @@ function EditorRSExpression({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<AnimatePresence>
|
|
||||||
{showAST ? (
|
{showAST ? (
|
||||||
<DlgShowAST expression={expression} syntaxTree={syntaxTree} hideWindow={() => setShowAST(false)} />
|
<DlgShowAST expression={expression} syntaxTree={syntaxTree} hideWindow={() => setShowAST(false)} />
|
||||||
) : null}
|
) : null}
|
||||||
</AnimatePresence>
|
|
||||||
|
|
||||||
<ToolbarRSExpression
|
<ToolbarRSExpression
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
import { motion } from 'framer-motion';
|
|
||||||
|
|
||||||
import { IExpressionParse, IRSErrorDescription } from '@/models/rslang';
|
import { IExpressionParse, IRSErrorDescription } from '@/models/rslang';
|
||||||
import { getRSErrorPrefix } from '@/models/rslangAPI';
|
import { getRSErrorPrefix } from '@/models/rslangAPI';
|
||||||
import { animateParseResults } from '@/styling/animations';
|
|
||||||
import { describeRSError } from '@/utils/labels';
|
import { describeRSError } from '@/utils/labels';
|
||||||
|
|
||||||
interface ParsingResultProps {
|
interface ParsingResultProps {
|
||||||
|
@ -17,12 +14,16 @@ function ParsingResult({ isOpen, data, disabled, onShowError }: ParsingResultPro
|
||||||
const warningsCount = data ? data.errors.length - errorCount : 0;
|
const warningsCount = data ? data.errors.length - errorCount : 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<div
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
className='text-sm border dense cc-scroll-y'
|
className='text-sm border dense cc-scroll-y transition-all duration-300'
|
||||||
initial={false}
|
style={{
|
||||||
animate={isOpen ? 'open' : 'closed'}
|
clipPath: isOpen ? 'inset(0% 0% 0% 0%)' : 'inset(0% 0% 100% 0%)',
|
||||||
variants={animateParseResults}
|
marginTop: isOpen ? '0.75rem' : '0rem',
|
||||||
|
padding: isOpen ? '0.25rem 0.5rem 0.25rem 0.5rem' : '0rem 0rem 0rem 0rem',
|
||||||
|
borderWidth: isOpen ? '1px' : '0px',
|
||||||
|
height: isOpen ? '4.5rem' : '0rem'
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<p>
|
<p>
|
||||||
Ошибок: <b>{errorCount}</b> | Предупреждений: <b>{warningsCount}</b>
|
Ошибок: <b>{errorCount}</b> | Предупреждений: <b>{warningsCount}</b>
|
||||||
|
@ -42,7 +43,7 @@ function ParsingResult({ isOpen, data, disabled, onShowError }: ParsingResultPro
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</motion.div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { motion } from 'framer-motion';
|
|
||||||
|
|
||||||
import { TokenID } from '@/models/rslang';
|
import { TokenID } from '@/models/rslang';
|
||||||
import { animateRSControl } from '@/styling/animations';
|
import { PARAMETER, prefixes } from '@/utils/constants';
|
||||||
import { prefixes } from '@/utils/constants';
|
|
||||||
|
|
||||||
import RSLocalButton from './RSLocalButton';
|
import RSLocalButton from './RSLocalButton';
|
||||||
import RSTokenButton from './RSTokenButton';
|
import RSTokenButton from './RSTokenButton';
|
||||||
|
@ -90,7 +88,7 @@ interface RSEditorControlsProps {
|
||||||
|
|
||||||
function RSEditorControls({ isOpen, disabled, onEdit }: RSEditorControlsProps) {
|
function RSEditorControls({ isOpen, disabled, onEdit }: RSEditorControlsProps) {
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'max-w-[28rem] min-w-[28rem] xs:max-w-[38.5rem] xs:min-w-[38.5rem] sm:max-w-[40rem] sm:min-w-[40rem] md:max-w-fit mx-1 sm:mx-0',
|
'max-w-[28rem] min-w-[28rem] xs:max-w-[38.5rem] xs:min-w-[38.5rem] sm:max-w-[40rem] sm:min-w-[40rem] md:max-w-fit mx-1 sm:mx-0',
|
||||||
'flex-wrap',
|
'flex-wrap',
|
||||||
|
@ -98,9 +96,13 @@ function RSEditorControls({ isOpen, disabled, onEdit }: RSEditorControlsProps) {
|
||||||
'text-xs md:text-sm',
|
'text-xs md:text-sm',
|
||||||
'select-none'
|
'select-none'
|
||||||
)}
|
)}
|
||||||
initial={false}
|
style={{
|
||||||
animate={isOpen ? 'open' : 'closed'}
|
transitionProperty: 'clipPath, height',
|
||||||
variants={animateRSControl}
|
transitionDuration: `${PARAMETER.moveDuration}ms`,
|
||||||
|
clipPath: isOpen ? 'inset(0% 0% 0% 0%)' : 'inset(0% 0% 100% 0%)',
|
||||||
|
marginTop: isOpen ? '0.25rem' : '0rem',
|
||||||
|
height: isOpen ? 'max-content' : '0rem'
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{MAIN_FIRST_ROW.map(token => (
|
{MAIN_FIRST_ROW.map(token => (
|
||||||
<RSTokenButton key={`${prefixes.rsedit_btn}${token}`} token={token} onInsert={onEdit} disabled={disabled} />
|
<RSTokenButton key={`${prefixes.rsedit_btn}${token}`} token={token} onInsert={onEdit} disabled={disabled} />
|
||||||
|
@ -143,7 +145,7 @@ function RSEditorControls({ isOpen, disabled, onEdit }: RSEditorControlsProps) {
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</motion.div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { animated, useTransition } from '@react-spring/web';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { AnimatePresence } from 'framer-motion';
|
import { useMemo, useState } from 'react';
|
||||||
import { useMemo } from 'react';
|
|
||||||
|
|
||||||
import { StatusIcon } from '@/components/DomainIcons';
|
import { StatusIcon } from '@/components/DomainIcons';
|
||||||
import Loader from '@/components/ui/Loader';
|
import Loader from '@/components/ui/Loader';
|
||||||
|
@ -12,7 +12,7 @@ import { type IConstituenta } from '@/models/rsform';
|
||||||
import { inferStatus } from '@/models/rsformAPI';
|
import { inferStatus } from '@/models/rsformAPI';
|
||||||
import { IExpressionParse, ParsingStatus } from '@/models/rslang';
|
import { IExpressionParse, ParsingStatus } from '@/models/rslang';
|
||||||
import { colorStatusBar } from '@/styling/color';
|
import { colorStatusBar } from '@/styling/color';
|
||||||
import { globals } from '@/utils/constants';
|
import { globals, PARAMETER } from '@/utils/constants';
|
||||||
import { labelExpressionStatus, prepareTooltip } from '@/utils/labels';
|
import { labelExpressionStatus, prepareTooltip } from '@/utils/labels';
|
||||||
|
|
||||||
interface StatusBarProps {
|
interface StatusBarProps {
|
||||||
|
@ -36,6 +36,16 @@ function StatusBar({ isModified, processing, activeCst, parseData, onAnalyze }:
|
||||||
return inferStatus(activeCst.parse.status, activeCst.parse.valueClass);
|
return inferStatus(activeCst.parse.status, activeCst.parse.valueClass);
|
||||||
}, [isModified, activeCst, parseData]);
|
}, [isModified, activeCst, parseData]);
|
||||||
|
|
||||||
|
const [isAnimating, setIsAnimating] = useState(false);
|
||||||
|
const transitions = useTransition(processing, {
|
||||||
|
from: { opacity: 1 },
|
||||||
|
enter: { opacity: 1 },
|
||||||
|
leave: { opacity: 0 },
|
||||||
|
onStart: () => setIsAnimating(true),
|
||||||
|
onRest: () => setIsAnimating(false),
|
||||||
|
config: { duration: PARAMETER.fadeDuration }
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
|
@ -46,24 +56,26 @@ function StatusBar({ isModified, processing, activeCst, parseData, onAnalyze }:
|
||||||
'select-none',
|
'select-none',
|
||||||
'cursor-pointer',
|
'cursor-pointer',
|
||||||
'focus-frame',
|
'focus-frame',
|
||||||
'duration-500 transition-colors'
|
'transition-colors duration-500'
|
||||||
)}
|
)}
|
||||||
style={{ backgroundColor: processing ? colors.bgDefault : colorStatusBar(status, colors) }}
|
style={{ backgroundColor: processing ? colors.bgDefault : colorStatusBar(status, colors) }}
|
||||||
data-tooltip-id={globals.tooltip}
|
data-tooltip-id={globals.tooltip}
|
||||||
data-tooltip-html={prepareTooltip('Проверить определение', 'Ctrl + Q')}
|
data-tooltip-html={prepareTooltip('Проверить определение', 'Ctrl + Q')}
|
||||||
onClick={onAnalyze}
|
onClick={onAnalyze}
|
||||||
>
|
>
|
||||||
<AnimatePresence mode='wait'>
|
{transitions((style, flag) =>
|
||||||
{processing ? <Loader key='status-loader' scale={3} /> : null}
|
flag ? (
|
||||||
{!processing ? (
|
<animated.div style={style}>
|
||||||
|
<Loader scale={3} />
|
||||||
|
</animated.div>
|
||||||
|
) : null
|
||||||
|
)}
|
||||||
|
{!processing && !isAnimating ? (
|
||||||
<>
|
<>
|
||||||
<StatusIcon key='status-icon' size='1rem' value={status} />
|
<StatusIcon size='1rem' value={status} />
|
||||||
<span key='status-text' className='pb-[0.125rem] font-controls pr-2'>
|
<span className='pb-[0.125rem] font-controls pr-2'>{labelExpressionStatus(status)}</span>
|
||||||
{labelExpressionStatus(status)}
|
|
||||||
</span>
|
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
</AnimatePresence>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import FlexColumn from '@/components/ui/FlexColumn';
|
import FlexColumn from '@/components/ui/FlexColumn';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import { useRSForm } from '@/context/RSFormContext';
|
import { useRSForm } from '@/context/RSFormContext';
|
||||||
import { globals } from '@/utils/constants';
|
import { globals } from '@/utils/constants';
|
||||||
|
|
||||||
|
@ -47,9 +46,13 @@ function EditorRSFormCard({ isModified, onDestroy, setIsModified }: EditorRSForm
|
||||||
onDestroy={onDestroy}
|
onDestroy={onDestroy}
|
||||||
controller={controller}
|
controller={controller}
|
||||||
/>
|
/>
|
||||||
<AnimateFade
|
<div
|
||||||
onKeyDown={handleInput}
|
onKeyDown={handleInput}
|
||||||
className={clsx('md:w-fit md:max-w-fit max-w-[32rem] mx-auto', 'flex flex-row flex-wrap px-6 pt-[1.9rem]')}
|
className={clsx(
|
||||||
|
'cc-fade-in',
|
||||||
|
'md:w-fit md:max-w-fit max-w-[32rem] mx-auto',
|
||||||
|
'flex flex-row flex-wrap px-6 pt-[1.9rem]'
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<FlexColumn className='flex-shrink'>
|
<FlexColumn className='flex-shrink'>
|
||||||
<FormRSForm id={globals.library_item_editor} isModified={isModified} setIsModified={setIsModified} />
|
<FormRSForm id={globals.library_item_editor} isModified={isModified} setIsModified={setIsModified} />
|
||||||
|
@ -57,7 +60,7 @@ function EditorRSFormCard({ isModified, onDestroy, setIsModified }: EditorRSForm
|
||||||
</FlexColumn>
|
</FlexColumn>
|
||||||
|
|
||||||
{model.schema ? <RSFormStats stats={model.schema.stats} isArchive={model.isArchive} /> : null}
|
{model.schema ? <RSFormStats stats={model.schema.stats} isArchive={model.isArchive} /> : null}
|
||||||
</AnimateFade>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import { type RowSelectionState } from '@/components/ui/DataTable';
|
||||||
import MiniButton from '@/components/ui/MiniButton';
|
import MiniButton from '@/components/ui/MiniButton';
|
||||||
import Overlay from '@/components/ui/Overlay';
|
import Overlay from '@/components/ui/Overlay';
|
||||||
import SearchBar from '@/components/ui/SearchBar';
|
import SearchBar from '@/components/ui/SearchBar';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||||
import { CstMatchMode } from '@/models/miscellaneous';
|
import { CstMatchMode } from '@/models/miscellaneous';
|
||||||
import { ConstituentaID, CstType, IConstituenta } from '@/models/rsform';
|
import { ConstituentaID, CstType, IConstituenta } from '@/models/rsform';
|
||||||
|
@ -142,7 +141,7 @@ function EditorRSList({ onOpenEdit }: EditorRSListProps) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{controller.isContentEditable ? <ToolbarRSList /> : null}
|
{controller.isContentEditable ? <ToolbarRSList /> : null}
|
||||||
<AnimateFade tabIndex={-1} onKeyDown={handleKeyDown} className='pt-[1.9rem]'>
|
<div tabIndex={-1} onKeyDown={handleKeyDown} className='cc-fade-in pt-[1.9rem]'>
|
||||||
{controller.isContentEditable ? (
|
{controller.isContentEditable ? (
|
||||||
<div className='flex items-center border-b'>
|
<div className='flex items-center border-b'>
|
||||||
<div className='px-2'>
|
<div className='px-2'>
|
||||||
|
@ -175,7 +174,7 @@ function EditorRSList({ onOpenEdit }: EditorRSListProps) {
|
||||||
onEdit={onOpenEdit}
|
onEdit={onOpenEdit}
|
||||||
onCreateNew={() => controller.createCst(undefined, false)}
|
onCreateNew={() => controller.createCst(undefined, false)}
|
||||||
/>
|
/>
|
||||||
</AnimateFade>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ function ToolbarRSList() {
|
||||||
return (
|
return (
|
||||||
<Overlay
|
<Overlay
|
||||||
position='cc-tab-tools right-4 translate-x-0 md:right-1/2 md:translate-x-1/2'
|
position='cc-tab-tools right-4 translate-x-0 md:right-1/2 md:translate-x-1/2'
|
||||||
className='cc-icons items-start outline-none transition-all duration-500'
|
className='cc-icons cc-animate-position items-start outline-none'
|
||||||
>
|
>
|
||||||
{controller.schema && controller.schema?.oss.length > 0 ? (
|
{controller.schema && controller.schema?.oss.length > 0 ? (
|
||||||
<MiniSelectorOSS
|
<MiniSelectorOSS
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { AnimatePresence } from 'framer-motion';
|
|
||||||
import { toPng } from 'html-to-image';
|
import { toPng } from 'html-to-image';
|
||||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
@ -25,7 +24,6 @@ import SelectedCounter from '@/components/info/SelectedCounter';
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
import ToolbarGraphSelection from '@/components/select/ToolbarGraphSelection';
|
import ToolbarGraphSelection from '@/components/select/ToolbarGraphSelection';
|
||||||
import Overlay from '@/components/ui/Overlay';
|
import Overlay from '@/components/ui/Overlay';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||||
import DlgGraphParams from '@/dialogs/DlgGraphParams';
|
import DlgGraphParams from '@/dialogs/DlgGraphParams';
|
||||||
import useLocalStorage from '@/hooks/useLocalStorage';
|
import useLocalStorage from '@/hooks/useLocalStorage';
|
||||||
|
@ -376,7 +374,6 @@ function TGFlow({ onOpenEdit }: TGFlowProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<AnimatePresence>
|
|
||||||
{showParamsDialog ? (
|
{showParamsDialog ? (
|
||||||
<DlgGraphParams
|
<DlgGraphParams
|
||||||
hideWindow={() => setShowParamsDialog(false)}
|
hideWindow={() => setShowParamsDialog(false)}
|
||||||
|
@ -384,9 +381,7 @@ function TGFlow({ onOpenEdit }: TGFlowProps) {
|
||||||
onConfirm={handleChangeParams}
|
onConfirm={handleChangeParams}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</AnimatePresence>
|
|
||||||
|
|
||||||
<AnimateFade tabIndex={-1} onKeyDown={handleKeyDown}>
|
|
||||||
<Overlay position='cc-tab-tools' className='flex flex-col items-center rounded-b-2xl cc-blur'>
|
<Overlay position='cc-tab-tools' className='flex flex-col items-center rounded-b-2xl cc-blur'>
|
||||||
<ToolbarTermGraph
|
<ToolbarTermGraph
|
||||||
noText={filterParams.noText}
|
noText={filterParams.noText}
|
||||||
|
@ -440,6 +435,7 @@ function TGFlow({ onOpenEdit }: TGFlowProps) {
|
||||||
) : null}
|
) : null}
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
|
||||||
|
<div className='cc-fade-in' tabIndex={-1} onKeyDown={handleKeyDown}>
|
||||||
<SelectedCounter
|
<SelectedCounter
|
||||||
hideZero
|
hideZero
|
||||||
totalCount={controller.schema?.stats?.count_all ?? 0}
|
totalCount={controller.schema?.stats?.count_all ?? 0}
|
||||||
|
@ -470,8 +466,9 @@ function TGFlow({ onOpenEdit }: TGFlowProps) {
|
||||||
{viewHidden}
|
{viewHidden}
|
||||||
</div>
|
</div>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
|
||||||
{graph}
|
{graph}
|
||||||
</AnimateFade>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { motion } from 'framer-motion';
|
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
import { IconDropArrow, IconDropArrowUp } from '@/components/Icons';
|
import { IconDropArrow, IconDropArrowUp } from '@/components/Icons';
|
||||||
|
@ -14,9 +13,8 @@ import useLocalStorage from '@/hooks/useLocalStorage';
|
||||||
import useWindowSize from '@/hooks/useWindowSize';
|
import useWindowSize from '@/hooks/useWindowSize';
|
||||||
import { GraphColoring } from '@/models/miscellaneous';
|
import { GraphColoring } from '@/models/miscellaneous';
|
||||||
import { ConstituentaID, IRSForm } from '@/models/rsform';
|
import { ConstituentaID, IRSForm } from '@/models/rsform';
|
||||||
import { animateDropdown, animateHiddenHeader } from '@/styling/animations';
|
|
||||||
import { colorBgGraphNode } from '@/styling/color';
|
import { colorBgGraphNode } from '@/styling/color';
|
||||||
import { prefixes, storage } from '@/utils/constants';
|
import { PARAMETER, prefixes, storage } from '@/utils/constants';
|
||||||
|
|
||||||
interface ViewHiddenProps {
|
interface ViewHiddenProps {
|
||||||
items: ConstituentaID[];
|
items: ConstituentaID[];
|
||||||
|
@ -60,39 +58,35 @@ function ViewHidden({ items, selected, toggleSelection, setFocus, schema, colori
|
||||||
onClick={() => setIsFolded(prev => !prev)}
|
onClick={() => setIsFolded(prev => !prev)}
|
||||||
/>
|
/>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
<div className={clsx('pt-2 clr-input border-x pb-2', { 'border-b rounded-b-md': isFolded })}>
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className='w-fit select-none'
|
||||||
'pt-2', //
|
style={{
|
||||||
'border-x',
|
transitionProperty: 'margin, translate',
|
||||||
'clr-input',
|
transitionDuration: `${PARAMETER.fastAnimation}ms`,
|
||||||
'select-none',
|
transitionTimingFunction: 'ease-out',
|
||||||
{
|
marginLeft: isFolded ? '0.75rem' : '0',
|
||||||
'pb-2 border-b': isFolded
|
translate: isFolded ? '0' : 'calc(6.5rem - 50%)'
|
||||||
}
|
}}
|
||||||
)}
|
|
||||||
>
|
|
||||||
<motion.div
|
|
||||||
className='w-fit'
|
|
||||||
animate={!isFolded ? 'open' : 'closed'}
|
|
||||||
variants={animateHiddenHeader}
|
|
||||||
initial={false}
|
|
||||||
>
|
>
|
||||||
{`Скрытые [${localSelected.length} | ${items.length}]`}
|
{`Скрытые [${localSelected.length} | ${items.length}]`}
|
||||||
</motion.div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<motion.div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'flex flex-wrap justify-center gap-2 py-2',
|
'flex flex-wrap justify-center gap-2 py-2 mt-[-0.5rem]',
|
||||||
'border-x border-b rounded-b-md',
|
|
||||||
'clr-input',
|
|
||||||
'text-sm',
|
'text-sm',
|
||||||
|
'clr-input border-x border-b rounded-b-md',
|
||||||
'cc-scroll-y'
|
'cc-scroll-y'
|
||||||
)}
|
)}
|
||||||
style={{ maxHeight: calculateHeight(windowSize.isSmall ? '10.4rem + 2px' : '12.5rem + 2px') }}
|
style={{
|
||||||
initial={false}
|
maxHeight: calculateHeight(windowSize.isSmall ? '10.4rem + 2px' : '12.5rem + 2px'),
|
||||||
animate={!isFolded ? 'open' : 'closed'}
|
transitionProperty: 'clip-path',
|
||||||
variants={animateDropdown}
|
transitionDuration: `${PARAMETER.fastAnimation}ms`,
|
||||||
|
transitionTimingFunction: 'ease-out',
|
||||||
|
clipPath: isFolded ? 'inset(10% 0% 90% 0%)' : 'inset(0% 0% 0% 0%)'
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{items.map(cstID => {
|
{items.map(cstID => {
|
||||||
const cst = schema.cstByID.get(cstID)!;
|
const cst = schema.cstByID.get(cstID)!;
|
||||||
|
@ -124,7 +118,7 @@ function ViewHidden({ items, selected, toggleSelection, setFocus, schema, colori
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</motion.div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,9 @@ import {
|
||||||
IconUpload
|
IconUpload
|
||||||
} from '@/components/Icons';
|
} from '@/components/Icons';
|
||||||
import Button from '@/components/ui/Button';
|
import Button from '@/components/ui/Button';
|
||||||
|
import Divider from '@/components/ui/Divider';
|
||||||
import Dropdown from '@/components/ui/Dropdown';
|
import Dropdown from '@/components/ui/Dropdown';
|
||||||
import DropdownButton from '@/components/ui/DropdownButton';
|
import DropdownButton from '@/components/ui/DropdownButton';
|
||||||
import DropdownDivider from '@/components/ui/DropdownDivider';
|
|
||||||
import { useAccessMode } from '@/context/AccessModeContext';
|
import { useAccessMode } from '@/context/AccessModeContext';
|
||||||
import { useAuth } from '@/context/AuthContext';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
import { useGlobalOss } from '@/context/GlobalOssContext';
|
import { useGlobalOss } from '@/context/GlobalOssContext';
|
||||||
|
@ -191,7 +191,7 @@ function MenuRSTabs({ onDestroy }: MenuRSTabsProps) {
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<DropdownDivider margins='mx-3 my-1' />
|
<Divider margins='mx-3 my-1' />
|
||||||
|
|
||||||
{user ? (
|
{user ? (
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
|
@ -244,7 +244,7 @@ function MenuRSTabs({ onDestroy }: MenuRSTabsProps) {
|
||||||
onClick={handleInlineSynthesis}
|
onClick={handleInlineSynthesis}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DropdownDivider margins='mx-3 my-1' />
|
<Divider margins='mx-3 my-1' />
|
||||||
|
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
text='Упорядочить список'
|
text='Упорядочить список'
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { AnimatePresence } from 'framer-motion';
|
|
||||||
import fileDownload from 'js-file-download';
|
import fileDownload from 'js-file-download';
|
||||||
import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useState } from 'react';
|
import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
@ -684,7 +683,7 @@ export const RSEditState = ({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{model.schema ? (
|
{model.schema ? (
|
||||||
<AnimatePresence>
|
<>
|
||||||
{showUpload ? <DlgUploadRSForm hideWindow={() => setShowUpload(false)} /> : null}
|
{showUpload ? <DlgUploadRSForm hideWindow={() => setShowUpload(false)} /> : null}
|
||||||
{showClone ? (
|
{showClone ? (
|
||||||
<DlgCloneLibraryItem
|
<DlgCloneLibraryItem
|
||||||
|
@ -782,7 +781,7 @@ export const RSEditState = ({
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{showTypeGraph ? <DlgShowTypeGraph items={typeInfo} hideWindow={() => setShowTypeGraph(false)} /> : null}
|
{showTypeGraph ? <DlgShowTypeGraph items={typeInfo} hideWindow={() => setShowTypeGraph(false)} /> : null}
|
||||||
</AnimatePresence>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
|
|
|
@ -13,7 +13,6 @@ import Loader from '@/components/ui/Loader';
|
||||||
import Overlay from '@/components/ui/Overlay';
|
import Overlay from '@/components/ui/Overlay';
|
||||||
import TabLabel from '@/components/ui/TabLabel';
|
import TabLabel from '@/components/ui/TabLabel';
|
||||||
import TextURL from '@/components/ui/TextURL';
|
import TextURL from '@/components/ui/TextURL';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||||
import { useGlobalOss } from '@/context/GlobalOssContext';
|
import { useGlobalOss } from '@/context/GlobalOssContext';
|
||||||
import { useLibrary } from '@/context/LibraryContext';
|
import { useLibrary } from '@/context/LibraryContext';
|
||||||
|
@ -270,12 +269,12 @@ function RSTabs() {
|
||||||
</TabList>
|
</TabList>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
|
||||||
<AnimateFade className='overflow-x-hidden'>
|
<div className='overflow-x-hidden'>
|
||||||
{cardPanel}
|
{cardPanel}
|
||||||
{listPanel}
|
{listPanel}
|
||||||
{editorPanel}
|
{editorPanel}
|
||||||
{graphPanel}
|
{graphPanel}
|
||||||
</AnimateFade>
|
</div>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
) : null}
|
) : null}
|
||||||
</RSEditState>
|
</RSEditState>
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { animated, useSpring } from '@react-spring/web';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { motion } from 'framer-motion';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useMemo, useState } from 'react';
|
|
||||||
|
|
||||||
import { useAccessMode } from '@/context/AccessModeContext';
|
import { useAccessMode } from '@/context/AccessModeContext';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||||
import useWindowSize from '@/hooks/useWindowSize';
|
import useWindowSize from '@/hooks/useWindowSize';
|
||||||
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
|
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
|
||||||
import { UserLevel } from '@/models/user';
|
import { UserLevel } from '@/models/user';
|
||||||
import { animateSideView } from '@/styling/animations';
|
import { PARAMETER } from '@/utils/constants';
|
||||||
|
|
||||||
import ConstituentsSearch from './ConstituentsSearch';
|
import ConstituentsSearch from './ConstituentsSearch';
|
||||||
import TableSideConstituents from './TableSideConstituents';
|
import TableSideConstituents from './TableSideConstituents';
|
||||||
|
@ -23,15 +23,47 @@ interface ViewConstituentsProps {
|
||||||
activeCst?: IConstituenta;
|
activeCst?: IConstituenta;
|
||||||
schema?: IRSForm;
|
schema?: IRSForm;
|
||||||
onOpenEdit: (cstID: ConstituentaID) => void;
|
onOpenEdit: (cstID: ConstituentaID) => void;
|
||||||
|
isMounted: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ViewConstituents({ expression, schema, activeCst, isBottom, onOpenEdit }: ViewConstituentsProps) {
|
function ViewConstituents({ expression, schema, activeCst, isBottom, onOpenEdit, isMounted }: ViewConstituentsProps) {
|
||||||
const { calculateHeight } = useConceptOptions();
|
const { calculateHeight } = useConceptOptions();
|
||||||
const windowSize = useWindowSize();
|
const windowSize = useWindowSize();
|
||||||
const { accessLevel } = useAccessMode();
|
const { accessLevel } = useAccessMode();
|
||||||
|
|
||||||
const [filteredData, setFilteredData] = useState<IConstituenta[]>(schema?.items ?? []);
|
const [filteredData, setFilteredData] = useState<IConstituenta[]>(schema?.items ?? []);
|
||||||
|
|
||||||
|
const [isVisible, setIsVisible] = useState(true);
|
||||||
|
const isFirstRender = useRef(true);
|
||||||
|
const springs = useSpring({
|
||||||
|
from: { opacity: 0, width: '0' },
|
||||||
|
to: async next => {
|
||||||
|
if (isFirstRender.current) {
|
||||||
|
await next({ opacity: isMounted ? 1 : 0, width: isMounted ? '100%' : '0', config: { duration: 0 } });
|
||||||
|
isFirstRender.current = false;
|
||||||
|
} else {
|
||||||
|
if (isMounted) {
|
||||||
|
await next({ width: '100%', config: { duration: PARAMETER.moveDuration } });
|
||||||
|
await next({ opacity: 1, config: { duration: PARAMETER.fadeDuration } });
|
||||||
|
} else {
|
||||||
|
await next({ opacity: 0, config: { duration: PARAMETER.fadeDuration } });
|
||||||
|
await next({ width: '0', config: { duration: PARAMETER.moveDuration } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRest: props => {
|
||||||
|
if (props.finished && !isMounted) {
|
||||||
|
setIsVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isMounted) {
|
||||||
|
setIsVisible(true);
|
||||||
|
}
|
||||||
|
}, [isMounted]);
|
||||||
|
|
||||||
const table = useMemo(
|
const table = useMemo(
|
||||||
() => (
|
() => (
|
||||||
<TableSideConstituents
|
<TableSideConstituents
|
||||||
|
@ -49,8 +81,12 @@ function ViewConstituents({ expression, schema, activeCst, isBottom, onOpenEdit
|
||||||
[isBottom, filteredData, activeCst, onOpenEdit, calculateHeight, accessLevel]
|
[isBottom, filteredData, activeCst, onOpenEdit, calculateHeight, accessLevel]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!isVisible) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<animated.div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'border', // prettier: split-lines
|
'border', // prettier: split-lines
|
||||||
{
|
{
|
||||||
|
@ -58,9 +94,7 @@ function ViewConstituents({ expression, schema, activeCst, isBottom, onOpenEdit
|
||||||
'mt-3 mx-6 rounded-md md:max-w-[45.8rem] overflow-hidden': isBottom
|
'mt-3 mx-6 rounded-md md:max-w-[45.8rem] overflow-hidden': isBottom
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
initial={{ ...animateSideView.initial }}
|
style={springs}
|
||||||
animate={{ ...animateSideView.animate }}
|
|
||||||
exit={{ ...animateSideView.exit }}
|
|
||||||
>
|
>
|
||||||
<ConstituentsSearch
|
<ConstituentsSearch
|
||||||
dense={windowSize.width && windowSize.width < COLUMN_DENSE_SEARCH_THRESHOLD ? true : undefined}
|
dense={windowSize.width && windowSize.width < COLUMN_DENSE_SEARCH_THRESHOLD ? true : undefined}
|
||||||
|
@ -70,7 +104,7 @@ function ViewConstituents({ expression, schema, activeCst, isBottom, onOpenEdit
|
||||||
setFiltered={setFilteredData}
|
setFiltered={setFilteredData}
|
||||||
/>
|
/>
|
||||||
{table}
|
{table}
|
||||||
</motion.div>
|
</animated.div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ function FormSignup() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<form className={clsx('cc-column', 'mx-auto w-[36rem]', 'px-6 py-3')} onSubmit={handleSubmit}>
|
<form className={clsx('cc-fade-in cc-column', 'mx-auto w-[36rem]', 'px-6 py-3')} onSubmit={handleSubmit}>
|
||||||
<h1>
|
<h1>
|
||||||
<span>Новый пользователь</span>
|
<span>Новый пользователь</span>
|
||||||
<Overlay id={globals.password_tooltip} position='top-[5.4rem] left-[3.5rem]'>
|
<Overlay id={globals.password_tooltip} position='top-[5.4rem] left-[3.5rem]'>
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
import { AnimatePresence } from 'framer-motion';
|
|
||||||
|
|
||||||
import Loader from '@/components/ui/Loader';
|
import Loader from '@/components/ui/Loader';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import ExpectedAnonymous from '@/components/wrap/ExpectedAnonymous';
|
import ExpectedAnonymous from '@/components/wrap/ExpectedAnonymous';
|
||||||
import { useAuth } from '@/context/AuthContext';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
|
|
||||||
|
@ -13,28 +10,11 @@ function RegisterPage() {
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <Loader />;
|
return <Loader />;
|
||||||
}
|
}
|
||||||
if (user && !loading) {
|
if (user) {
|
||||||
return (
|
return <ExpectedAnonymous />;
|
||||||
<AnimateFade>
|
} else {
|
||||||
<ExpectedAnonymous />
|
return <FormSignup />;
|
||||||
</AnimateFade>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return (
|
|
||||||
<AnimatePresence mode='wait'>
|
|
||||||
{loading ? <Loader key='signup-loader' /> : null}
|
|
||||||
{!loading && user ? (
|
|
||||||
<AnimateFade key='signup-has-user'>
|
|
||||||
<ExpectedAnonymous />
|
|
||||||
</AnimateFade>
|
|
||||||
) : null}
|
|
||||||
{!loading && !user ? (
|
|
||||||
<AnimateFade key='signup-no-user'>
|
|
||||||
<FormSignup />
|
|
||||||
</AnimateFade>
|
|
||||||
) : null}
|
|
||||||
</AnimatePresence>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RegisterPage;
|
export default RegisterPage;
|
||||||
|
|
|
@ -8,7 +8,6 @@ import InfoError, { ErrorData } from '@/components/info/InfoError';
|
||||||
import SubmitButton from '@/components/ui/SubmitButton';
|
import SubmitButton from '@/components/ui/SubmitButton';
|
||||||
import TextInput from '@/components/ui/TextInput';
|
import TextInput from '@/components/ui/TextInput';
|
||||||
import TextURL from '@/components/ui/TextURL';
|
import TextURL from '@/components/ui/TextURL';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import { useAuth } from '@/context/AuthContext';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
import { IRequestPasswordData } from '@/models/user';
|
import { IRequestPasswordData } from '@/models/user';
|
||||||
|
|
||||||
|
@ -32,10 +31,17 @@ function RestorePasswordPage() {
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
}, [email, setError]);
|
}, [email, setError]);
|
||||||
|
|
||||||
|
if (isCompleted) {
|
||||||
return (
|
return (
|
||||||
<AnimateFade>
|
<div className='cc-fade-in flex flex-col items-center gap-1 mt-3'>
|
||||||
{!isCompleted ? (
|
<p>На указанную почту отправлены инструкции по восстановлению пароля.</p>
|
||||||
<form className={clsx('cc-column', 'w-[24rem] mx-auto', 'px-6 mt-3')} onSubmit={handleSubmit}>
|
<TextURL text='Войти в Портал' href='/login' />
|
||||||
|
<TextURL text='Начальная страница' href='/' />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<form className={clsx('cc-fade-in cc-column', 'w-[24rem] mx-auto', 'px-6 mt-3')} onSubmit={handleSubmit}>
|
||||||
<TextInput
|
<TextInput
|
||||||
id='email'
|
id='email'
|
||||||
autoComplete='email'
|
autoComplete='email'
|
||||||
|
@ -54,17 +60,9 @@ function RestorePasswordPage() {
|
||||||
/>
|
/>
|
||||||
{error ? <ProcessError error={error} /> : null}
|
{error ? <ProcessError error={error} /> : null}
|
||||||
</form>
|
</form>
|
||||||
) : null}
|
|
||||||
{isCompleted ? (
|
|
||||||
<div className='flex flex-col items-center gap-1 mt-3'>
|
|
||||||
<p>На указанную почту отправлены инструкции по восстановлению пароля.</p>
|
|
||||||
<TextURL text='Войти в Портал' href='/login' />
|
|
||||||
<TextURL text='Начальная страница' href='/' />
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</AnimateFade>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default RestorePasswordPage;
|
export default RestorePasswordPage;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
|
||||||
import DataLoader from '@/components/wrap/DataLoader';
|
import DataLoader from '@/components/wrap/DataLoader';
|
||||||
import { useUserProfile } from '@/context/UserProfileContext';
|
import { useUserProfile } from '@/context/UserProfileContext';
|
||||||
|
|
||||||
|
@ -11,21 +10,14 @@ function UserContents() {
|
||||||
const { user, error, loading } = useUserProfile();
|
const { user, error, loading } = useUserProfile();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataLoader
|
<DataLoader isLoading={loading} error={error} hasNoData={!user}>
|
||||||
id='profile-page' // prettier: split lines
|
<div className='cc-fade-in flex gap-6 py-2 mx-auto w-fit'>
|
||||||
isLoading={loading}
|
|
||||||
error={error}
|
|
||||||
hasNoData={!user}
|
|
||||||
>
|
|
||||||
<AnimateFade className='flex gap-6 py-2 mx-auto w-fit'>
|
|
||||||
<div className='w-fit'>
|
|
||||||
<h1 className='mb-4 select-none'>Учетные данные пользователя</h1>
|
<h1 className='mb-4 select-none'>Учетные данные пользователя</h1>
|
||||||
<div className='flex py-2'>
|
<div className='flex py-2'>
|
||||||
<EditorProfile />
|
<EditorProfile />
|
||||||
<EditorPassword />
|
<EditorPassword />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</AnimateFade>
|
|
||||||
</DataLoader>
|
</DataLoader>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,340 +0,0 @@
|
||||||
/**
|
|
||||||
* Module: animations parameters.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Variants } from 'framer-motion';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Duration constants in ms.
|
|
||||||
*/
|
|
||||||
export const animationDuration = {
|
|
||||||
navigationToggle: 500
|
|
||||||
};
|
|
||||||
|
|
||||||
export const animateNavigation: Variants = {
|
|
||||||
open: {
|
|
||||||
height: '3rem',
|
|
||||||
translateY: 0,
|
|
||||||
transition: {
|
|
||||||
type: 'spring',
|
|
||||||
bounce: 0,
|
|
||||||
duration: animationDuration.navigationToggle / 1000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
closed: {
|
|
||||||
height: 0,
|
|
||||||
translateY: '-1.5rem',
|
|
||||||
transition: {
|
|
||||||
type: 'spring',
|
|
||||||
bounce: 0,
|
|
||||||
duration: animationDuration.navigationToggle / 1000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const animateNavigationToggle: Variants = {
|
|
||||||
on: {
|
|
||||||
height: '3rem',
|
|
||||||
width: '1.2rem',
|
|
||||||
transition: {
|
|
||||||
type: 'spring',
|
|
||||||
bounce: 0,
|
|
||||||
duration: animationDuration.navigationToggle / 1000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
off: {
|
|
||||||
height: '1.2rem',
|
|
||||||
width: '3rem',
|
|
||||||
transition: {
|
|
||||||
type: 'spring',
|
|
||||||
bounce: 0,
|
|
||||||
duration: animationDuration.navigationToggle / 1000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const animateSlideLeft: Variants = {
|
|
||||||
open: {
|
|
||||||
clipPath: 'inset(0% 0% 0% 0%)',
|
|
||||||
transition: {
|
|
||||||
type: 'spring',
|
|
||||||
bounce: 0,
|
|
||||||
duration: 0.4,
|
|
||||||
delayChildren: 0.2,
|
|
||||||
staggerChildren: 0.05
|
|
||||||
}
|
|
||||||
},
|
|
||||||
closed: {
|
|
||||||
clipPath: 'inset(0% 100% 0% 0%)',
|
|
||||||
transition: {
|
|
||||||
type: 'spring',
|
|
||||||
bounce: 0,
|
|
||||||
duration: 0.3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const animateHiddenHeader: Variants = {
|
|
||||||
open: {
|
|
||||||
translateX: 'calc(6.5rem - 50%)',
|
|
||||||
marginLeft: 0,
|
|
||||||
transition: {
|
|
||||||
type: 'spring',
|
|
||||||
bounce: 0,
|
|
||||||
duration: 0.3
|
|
||||||
}
|
|
||||||
},
|
|
||||||
closed: {
|
|
||||||
translateX: 0,
|
|
||||||
marginLeft: '0.75rem',
|
|
||||||
transition: {
|
|
||||||
type: 'spring',
|
|
||||||
bounce: 0,
|
|
||||||
duration: 0.3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const animateDropdown: Variants = {
|
|
||||||
open: {
|
|
||||||
clipPath: 'inset(0% 0% 0% 0%)',
|
|
||||||
transition: {
|
|
||||||
type: 'spring',
|
|
||||||
bounce: 0,
|
|
||||||
duration: 0.4,
|
|
||||||
delayChildren: 0.2,
|
|
||||||
staggerChildren: 0.05
|
|
||||||
}
|
|
||||||
},
|
|
||||||
closed: {
|
|
||||||
clipPath: 'inset(10% 0% 90% 0%)',
|
|
||||||
transition: {
|
|
||||||
type: 'spring',
|
|
||||||
bounce: 0,
|
|
||||||
duration: 0.3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const animateDropdownItem: Variants = {
|
|
||||||
open: {
|
|
||||||
opacity: 1,
|
|
||||||
y: 0,
|
|
||||||
transition: {
|
|
||||||
type: 'spring',
|
|
||||||
duration: 0.1,
|
|
||||||
stiffness: 300,
|
|
||||||
damping: 24
|
|
||||||
}
|
|
||||||
},
|
|
||||||
closed: {
|
|
||||||
opacity: 0,
|
|
||||||
y: 10,
|
|
||||||
transition: {
|
|
||||||
duration: 0.1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const animateRSControl: Variants = {
|
|
||||||
open: {
|
|
||||||
clipPath: 'inset(0% 0% 0% 0%)',
|
|
||||||
marginTop: '0.25rem',
|
|
||||||
height: 'max-content',
|
|
||||||
transition: {
|
|
||||||
type: 'spring',
|
|
||||||
bounce: 0,
|
|
||||||
duration: 0.4
|
|
||||||
}
|
|
||||||
},
|
|
||||||
closed: {
|
|
||||||
clipPath: 'inset(0% 0% 100% 0%)',
|
|
||||||
marginTop: '0',
|
|
||||||
height: 0,
|
|
||||||
transition: {
|
|
||||||
type: 'spring',
|
|
||||||
bounce: 0,
|
|
||||||
duration: 0.3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const animateParseResults: Variants = {
|
|
||||||
open: {
|
|
||||||
clipPath: 'inset(0% 0% 0% 0%)',
|
|
||||||
marginTop: '0.75rem',
|
|
||||||
padding: '0.25rem 0.5rem 0.25rem 0.5rem',
|
|
||||||
borderWidth: '1px',
|
|
||||||
height: '4.5rem',
|
|
||||||
transition: {
|
|
||||||
type: 'spring',
|
|
||||||
bounce: 0,
|
|
||||||
duration: 0.4
|
|
||||||
}
|
|
||||||
},
|
|
||||||
closed: {
|
|
||||||
clipPath: 'inset(0% 0% 100% 0%)',
|
|
||||||
marginTop: '0',
|
|
||||||
borderWidth: '0',
|
|
||||||
padding: '0 0 0 0',
|
|
||||||
height: 0,
|
|
||||||
transition: {
|
|
||||||
type: 'spring',
|
|
||||||
bounce: 0,
|
|
||||||
duration: 0.3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const animateSideView = {
|
|
||||||
initial: {
|
|
||||||
width: 0,
|
|
||||||
opacity: 0
|
|
||||||
},
|
|
||||||
animate: {
|
|
||||||
width: 'auto',
|
|
||||||
opacity: 1,
|
|
||||||
transition: {
|
|
||||||
width: {
|
|
||||||
duration: 0.4
|
|
||||||
},
|
|
||||||
opacity: {
|
|
||||||
delay: 0.4,
|
|
||||||
duration: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
exit: {
|
|
||||||
width: 0,
|
|
||||||
opacity: 0,
|
|
||||||
transition: {
|
|
||||||
width: {
|
|
||||||
duration: 0.4
|
|
||||||
},
|
|
||||||
opacity: {
|
|
||||||
duration: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const animateSideMinWidth = (width: string) => ({
|
|
||||||
initial: {
|
|
||||||
minWidth: 0,
|
|
||||||
opacity: 0
|
|
||||||
},
|
|
||||||
animate: {
|
|
||||||
minWidth: width,
|
|
||||||
opacity: 1,
|
|
||||||
transition: {
|
|
||||||
width: {
|
|
||||||
duration: 0.4
|
|
||||||
},
|
|
||||||
opacity: {
|
|
||||||
delay: 0.4,
|
|
||||||
duration: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
exit: {
|
|
||||||
minWidth: 0,
|
|
||||||
opacity: 0,
|
|
||||||
transition: {
|
|
||||||
width: {
|
|
||||||
duration: 0.4
|
|
||||||
},
|
|
||||||
opacity: {
|
|
||||||
duration: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export const animateSideAppear = {
|
|
||||||
initial: {
|
|
||||||
height: 0,
|
|
||||||
opacity: 0
|
|
||||||
},
|
|
||||||
animate: {
|
|
||||||
height: 'auto',
|
|
||||||
opacity: 1,
|
|
||||||
transition: {
|
|
||||||
height: {
|
|
||||||
duration: 0.25
|
|
||||||
},
|
|
||||||
opacity: {
|
|
||||||
delay: 0.25,
|
|
||||||
duration: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
exit: {
|
|
||||||
height: 0,
|
|
||||||
opacity: 0,
|
|
||||||
transition: {
|
|
||||||
height: {
|
|
||||||
duration: 0.25
|
|
||||||
},
|
|
||||||
opacity: {
|
|
||||||
duration: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const animateModal = {
|
|
||||||
initial: {
|
|
||||||
clipPath: 'inset(50% 50% 50% 50%)',
|
|
||||||
opacity: 0
|
|
||||||
},
|
|
||||||
animate: {
|
|
||||||
clipPath: 'inset(0% 0% 0% 0%)',
|
|
||||||
opacity: 1,
|
|
||||||
transition: {
|
|
||||||
type: 'spring',
|
|
||||||
bounce: 0,
|
|
||||||
duration: 0.3
|
|
||||||
}
|
|
||||||
},
|
|
||||||
exit: {
|
|
||||||
opacity: 0,
|
|
||||||
clipPath: 'inset(50% 50% 50% 50%)',
|
|
||||||
transition: {
|
|
||||||
type: 'spring',
|
|
||||||
bounce: 0,
|
|
||||||
duration: 0.2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const animateFade = {
|
|
||||||
initial: {
|
|
||||||
opacity: 0
|
|
||||||
},
|
|
||||||
variants: {
|
|
||||||
active: {
|
|
||||||
opacity: 1,
|
|
||||||
transition: {
|
|
||||||
type: 'tween',
|
|
||||||
ease: 'linear',
|
|
||||||
duration: 0.4
|
|
||||||
}
|
|
||||||
},
|
|
||||||
hidden: {
|
|
||||||
opacity: 0,
|
|
||||||
transition: {
|
|
||||||
type: 'tween',
|
|
||||||
ease: 'linear',
|
|
||||||
duration: 0.4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
exit: {
|
|
||||||
opacity: 0,
|
|
||||||
transition: {
|
|
||||||
type: 'tween',
|
|
||||||
ease: 'linear',
|
|
||||||
duration: 0.4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -16,6 +16,10 @@
|
||||||
--text-max-width: 75ch;
|
--text-max-width: 75ch;
|
||||||
--scroll-padding: 3rem;
|
--scroll-padding: 3rem;
|
||||||
|
|
||||||
|
--duration-move: 500ms;
|
||||||
|
--duration-modal: 300ms;
|
||||||
|
--duration-fade: 300ms;
|
||||||
|
|
||||||
/* Light Theme */
|
/* Light Theme */
|
||||||
--cl-bg-120: hsl(000, 000%, 100%);
|
--cl-bg-120: hsl(000, 000%, 100%);
|
||||||
--cl-bg-100: hsl(000, 000%, 098%);
|
--cl-bg-100: hsl(000, 000%, 098%);
|
||||||
|
|
|
@ -214,11 +214,14 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.cc-column {
|
.cc-column {
|
||||||
@apply flex flex-col gap-3;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cc-icons {
|
.cc-icons {
|
||||||
@apply flex gap-1;
|
display: flex;
|
||||||
|
gap: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cc-fit-content {
|
.cc-fit-content {
|
||||||
|
@ -248,4 +251,36 @@
|
||||||
.cc-shadow-border {
|
.cc-shadow-border {
|
||||||
@apply shadow-sm shadow-[var(--cl-bg-40)] dark:shadow-[var(--cd-bg-40)];
|
@apply shadow-sm shadow-[var(--cl-bg-40)] dark:shadow-[var(--cd-bg-40)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cc-animate-position {
|
||||||
|
transition-property: transform top left bottom right margin padding;
|
||||||
|
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
transition-duration: var(--duration-move);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-fade-in {
|
||||||
|
opacity: 1;
|
||||||
|
|
||||||
|
transition-property: opacity;
|
||||||
|
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
transition-duration: var(--duration-fade);
|
||||||
|
|
||||||
|
@starting-style {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-animate-modal {
|
||||||
|
clip-path: inset(0% 0% 0% 0%);
|
||||||
|
opacity: 1;
|
||||||
|
|
||||||
|
transition-property: clip-path, opacity;
|
||||||
|
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
transition-duration: var(--duration-modal);
|
||||||
|
|
||||||
|
@starting-style {
|
||||||
|
clip-path: inset(50% 50% 50% 50%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,11 @@ export const PARAMETER = {
|
||||||
ossDistanceX: 180, // pixels - insert x-distance between node centers
|
ossDistanceX: 180, // pixels - insert x-distance between node centers
|
||||||
ossDistanceY: 100, // pixels - insert y-distance between node centers
|
ossDistanceY: 100, // pixels - insert y-distance between node centers
|
||||||
|
|
||||||
|
fastAnimation: 200, // milliseconds - duration of fast animation
|
||||||
|
fadeDuration: 300, // milliseconds - duration of fade animation
|
||||||
|
dropdownDuration: 300, // milliseconds - duration of dropdown animation
|
||||||
|
moveDuration: 500, // milliseconds - duration of move animation
|
||||||
|
|
||||||
graphHandleSize: 3, // pixels - size of graph connection handle
|
graphHandleSize: 3, // pixels - size of graph connection handle
|
||||||
graphNodeRadius: 20, // pixels - radius of graph node
|
graphNodeRadius: 20, // pixels - radius of graph node
|
||||||
graphNodePadding: 5, // pixels - padding of graph node
|
graphNodePadding: 5, // pixels - padding of graph node
|
||||||
|
|
Loading…
Reference in New Issue
Block a user