mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Improve animations
This commit is contained in:
parent
ead0418564
commit
dfdbd4b17c
25
rsconcept/frontend/src/components/AnimateFade.tsx
Normal file
25
rsconcept/frontend/src/components/AnimateFade.tsx
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
|
||||||
|
import { animateFade } from '@/styling/animations';
|
||||||
|
|
||||||
|
import { CProps } from './props';
|
||||||
|
|
||||||
|
interface AnimateFadeProps extends CProps.AnimatedDiv {
|
||||||
|
noFadeIn?: boolean;
|
||||||
|
noFadeOut?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function AnimateFade({ noFadeIn, noFadeOut, children, ...restProps }: AnimateFadeProps) {
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
initial={{ ...(!noFadeIn ? animateFade.initial : {}) }}
|
||||||
|
animate={{ ...animateFade.animate }}
|
||||||
|
exit={{ ...(!noFadeOut ? animateFade.exit : {}) }}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AnimateFade;
|
|
@ -1,22 +0,0 @@
|
||||||
import { motion } from 'framer-motion';
|
|
||||||
|
|
||||||
import { animateFadeIn } from '@/styling/animations';
|
|
||||||
|
|
||||||
import { CProps } from './props';
|
|
||||||
|
|
||||||
interface AnimateFadeInProps extends CProps.AnimatedDiv {}
|
|
||||||
|
|
||||||
function AnimateFadeIn({ children, ...restProps }: AnimateFadeInProps) {
|
|
||||||
return (
|
|
||||||
<motion.div
|
|
||||||
initial={{ ...animateFadeIn.initial }}
|
|
||||||
animate={{ ...animateFadeIn.animate }}
|
|
||||||
exit={{ ...animateFadeIn.exit }}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</motion.div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AnimateFadeIn;
|
|
31
rsconcept/frontend/src/components/DataLoader.tsx
Normal file
31
rsconcept/frontend/src/components/DataLoader.tsx
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { AnimatePresence } from 'framer-motion';
|
||||||
|
|
||||||
|
import AnimateFade from './AnimateFade';
|
||||||
|
import InfoError, { ErrorData } from './InfoError';
|
||||||
|
import Loader from './ui/Loader';
|
||||||
|
|
||||||
|
interface DataLoaderProps {
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
isLoading: boolean;
|
||||||
|
error?: ErrorData;
|
||||||
|
hasNoData?: boolean;
|
||||||
|
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DataLoader({ id, isLoading, hasNoData, error, children }: DataLoaderProps) {
|
||||||
|
return (
|
||||||
|
<AnimatePresence mode='wait'>
|
||||||
|
{isLoading ? <Loader key={`${id}-loader`} /> : null}
|
||||||
|
{error ? <InfoError key={`${id}-error`} error={error} /> : null}
|
||||||
|
{!isLoading && !error && !hasNoData ? (
|
||||||
|
<AnimateFade id={id} key={`${id}-data`}>
|
||||||
|
{children}
|
||||||
|
</AnimateFade>
|
||||||
|
) : null}
|
||||||
|
</AnimatePresence>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DataLoader;
|
|
@ -2,6 +2,7 @@ import axios, { type AxiosError } from 'axios';
|
||||||
|
|
||||||
import { isResponseHtml } from '@/utils/utils';
|
import { isResponseHtml } from '@/utils/utils';
|
||||||
|
|
||||||
|
import AnimateFade from './AnimateFade';
|
||||||
import PrettyJson from './ui/PrettyJSON';
|
import PrettyJson from './ui/PrettyJSON';
|
||||||
|
|
||||||
export type ErrorData = string | Error | AxiosError | undefined;
|
export type ErrorData = string | Error | AxiosError | undefined;
|
||||||
|
@ -49,9 +50,9 @@ function DescribeError({ error }: { error: ErrorData }) {
|
||||||
|
|
||||||
function InfoError({ error }: InfoErrorProps) {
|
function InfoError({ error }: InfoErrorProps) {
|
||||||
return (
|
return (
|
||||||
<div className='px-3 py-2 min-w-[15rem] text-sm font-semibold select-text clr-text-warning'>
|
<AnimateFade className='px-3 py-2 min-w-[15rem] text-sm font-semibold select-text clr-text-warning'>
|
||||||
<DescribeError error={error} />
|
<DescribeError error={error} />
|
||||||
</div>
|
</AnimateFade>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,15 +4,19 @@ import { ThreeDots } from 'react-loader-spinner';
|
||||||
|
|
||||||
import { useConceptTheme } from '@/context/ThemeContext';
|
import { useConceptTheme } from '@/context/ThemeContext';
|
||||||
|
|
||||||
|
import AnimateFade from '../AnimateFade';
|
||||||
|
|
||||||
interface LoaderProps {
|
interface LoaderProps {
|
||||||
size?: number;
|
size?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Loader({ size = 10 }: LoaderProps) {
|
function Loader({ size = 10 }: LoaderProps) {
|
||||||
const { colors } = useConceptTheme();
|
const { colors } = useConceptTheme();
|
||||||
return (
|
return (
|
||||||
<div className='flex justify-center'>
|
<AnimateFade noFadeIn className='flex justify-center'>
|
||||||
<ThreeDots color={colors.bgSelected} height={size * 10} width={size * 10} radius={size} />
|
<ThreeDots color={colors.bgPrimary} height={size * 10} width={size * 10} radius={size} />
|
||||||
</div>
|
</AnimateFade>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default Loader;
|
||||||
|
|
|
@ -97,7 +97,7 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
getRSFormDetails(String(templateID), {
|
getRSFormDetails(String(templateID), {
|
||||||
showError: true,
|
showError: true,
|
||||||
setLoading: setLoading,
|
setLoading: setProcessing,
|
||||||
onError: setError,
|
onError: setError,
|
||||||
onSuccess: data => {
|
onSuccess: data => {
|
||||||
const schema = loadRSFormData(data);
|
const schema = loadRSFormData(data);
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { useEffect, useRef, useState } from 'react';
|
||||||
import { BiDownload } from 'react-icons/bi';
|
import { BiDownload } from 'react-icons/bi';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import AnimateFadeIn from '@/components/AnimateFadeIn';
|
import AnimateFade from '@/components/AnimateFade';
|
||||||
import InfoError from '@/components/InfoError';
|
import InfoError from '@/components/InfoError';
|
||||||
import RequireAuth from '@/components/RequireAuth';
|
import RequireAuth from '@/components/RequireAuth';
|
||||||
import Button from '@/components/ui/Button';
|
import Button from '@/components/ui/Button';
|
||||||
|
@ -79,7 +79,7 @@ function CreateRSFormPage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimateFadeIn>
|
<AnimateFade>
|
||||||
<RequireAuth>
|
<RequireAuth>
|
||||||
<form className={clsx('px-6 py-3', classnames.flex_col)} onSubmit={handleSubmit}>
|
<form className={clsx('px-6 py-3', classnames.flex_col)} onSubmit={handleSubmit}>
|
||||||
<h1>Создание концептуальной схемы</h1>
|
<h1>Создание концептуальной схемы</h1>
|
||||||
|
@ -130,7 +130,7 @@ function CreateRSFormPage() {
|
||||||
{error ? <InfoError error={error} /> : null}
|
{error ? <InfoError error={error} /> : null}
|
||||||
</form>
|
</form>
|
||||||
</RequireAuth>
|
</RequireAuth>
|
||||||
</AnimateFadeIn>
|
</AnimateFade>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,7 @@
|
||||||
|
|
||||||
import { useCallback, useLayoutEffect, useState } from 'react';
|
import { useCallback, useLayoutEffect, useState } from 'react';
|
||||||
|
|
||||||
import AnimateFadeIn from '@/components/AnimateFadeIn';
|
import DataLoader from '@/components/DataLoader';
|
||||||
import InfoError from '@/components/InfoError';
|
|
||||||
import { Loader } from '@/components/ui/Loader';
|
|
||||||
import { useAuth } from '@/context/AuthContext';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
import { useLibrary } from '@/context/LibraryContext';
|
import { useLibrary } from '@/context/LibraryContext';
|
||||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||||
|
@ -65,11 +63,12 @@ function LibraryPage() {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<DataLoader
|
||||||
{library.loading ? <Loader /> : null}
|
id='library-page' //
|
||||||
{library.error ? <InfoError error={library.error} /> : null}
|
isLoading={library.loading}
|
||||||
{!library.loading && library.items ? (
|
error={library.error}
|
||||||
<AnimateFadeIn>
|
hasNoData={library.items.length === 0}
|
||||||
|
>
|
||||||
<SearchPanel
|
<SearchPanel
|
||||||
query={query}
|
query={query}
|
||||||
setQuery={setQuery}
|
setQuery={setQuery}
|
||||||
|
@ -78,10 +77,11 @@ function LibraryPage() {
|
||||||
filtered={items.length}
|
filtered={items.length}
|
||||||
setFilter={setFilter}
|
setFilter={setFilter}
|
||||||
/>
|
/>
|
||||||
<ViewLibrary resetQuery={resetQuery} items={items} />
|
<ViewLibrary
|
||||||
</AnimateFadeIn>
|
resetQuery={resetQuery} //
|
||||||
) : null}
|
items={items}
|
||||||
</>
|
/>
|
||||||
|
</DataLoader>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import axios from 'axios';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import AnimateFadeIn from '@/components/AnimateFadeIn';
|
import AnimateFade from '@/components/AnimateFade';
|
||||||
import ExpectedAnonymous from '@/components/ExpectedAnonymous';
|
import ExpectedAnonymous from '@/components/ExpectedAnonymous';
|
||||||
import InfoError, { ErrorData } from '@/components/InfoError';
|
import InfoError, { ErrorData } from '@/components/InfoError';
|
||||||
import SubmitButton from '@/components/ui/SubmitButton';
|
import SubmitButton from '@/components/ui/SubmitButton';
|
||||||
|
@ -63,7 +63,7 @@ function LoginPage() {
|
||||||
return <ExpectedAnonymous />;
|
return <ExpectedAnonymous />;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<AnimateFadeIn>
|
<AnimateFade>
|
||||||
<form className={clsx('w-[24rem]', 'pt-12 pb-6 px-6', classnames.flex_col)} onSubmit={handleSubmit}>
|
<form className={clsx('w-[24rem]', 'pt-12 pb-6 px-6', classnames.flex_col)} 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
|
||||||
|
@ -97,7 +97,7 @@ function LoginPage() {
|
||||||
</div>
|
</div>
|
||||||
{error ? <ProcessError error={error} /> : null}
|
{error ? <ProcessError error={error} /> : null}
|
||||||
</form>
|
</form>
|
||||||
</AnimateFadeIn>
|
</AnimateFade>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import AnimateFadeIn from '@/components/AnimateFadeIn';
|
import AnimateFade from '@/components/AnimateFade';
|
||||||
import InfoTopic from '@/components/InfoTopic';
|
import InfoTopic from '@/components/InfoTopic';
|
||||||
import { HelpTopic } from '@/models/miscellaneous';
|
import { HelpTopic } from '@/models/miscellaneous';
|
||||||
|
|
||||||
|
@ -8,9 +8,9 @@ interface ViewTopicProps {
|
||||||
|
|
||||||
function ViewTopic({ topic }: ViewTopicProps) {
|
function ViewTopic({ topic }: ViewTopicProps) {
|
||||||
return (
|
return (
|
||||||
<AnimateFadeIn key={topic} className='px-2 py-2 mx-auto'>
|
<AnimateFade key={topic} className='px-2 py-2 mx-auto'>
|
||||||
<InfoTopic topic={topic} />
|
<InfoTopic topic={topic} />
|
||||||
</AnimateFadeIn>
|
</AnimateFade>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
import { AnimatePresence } from 'framer-motion';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import { Loader } from '@/components/ui/Loader';
|
import Loader from '@/components/ui/Loader';
|
||||||
import { useConceptTheme } from '@/context/ThemeContext';
|
import { useConceptTheme } from '@/context/ThemeContext';
|
||||||
import { ExpressionStatus } from '@/models/rsform';
|
import { ExpressionStatus } from '@/models/rsform';
|
||||||
import { type IConstituenta } from '@/models/rsform';
|
import { type IConstituenta } from '@/models/rsform';
|
||||||
|
@ -51,14 +52,15 @@ function StatusBar({ isModified, processing, constituenta, parseData, onAnalyze
|
||||||
data-tooltip-content='Проверить определение [Ctrl + Q]'
|
data-tooltip-content='Проверить определение [Ctrl + Q]'
|
||||||
onClick={onAnalyze}
|
onClick={onAnalyze}
|
||||||
>
|
>
|
||||||
{processing ? (
|
<AnimatePresence mode='wait'>
|
||||||
<Loader size={3} />
|
{processing ? <Loader key='status-loader' size={3} /> : null}
|
||||||
) : (
|
{!processing ? (
|
||||||
<>
|
<>
|
||||||
<StatusIcon status={status} />
|
<StatusIcon status={status} />
|
||||||
<span className='pb-[0.125rem] font-controls pr-2'>{labelExpressionStatus(status)}</span>
|
<span className='pb-[0.125rem] font-controls pr-2'>{labelExpressionStatus(status)}</span>
|
||||||
</>
|
</>
|
||||||
)}
|
) : null}
|
||||||
|
</AnimatePresence>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,9 @@ import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
import { TabList, TabPanel, Tabs } from 'react-tabs';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import AnimateFadeIn from '@/components/AnimateFadeIn';
|
import AnimateFade from '@/components/AnimateFade';
|
||||||
import InfoError, { ErrorData } from '@/components/InfoError';
|
import InfoError, { ErrorData } from '@/components/InfoError';
|
||||||
import { Loader } from '@/components/ui/Loader';
|
import Loader from '@/components/ui/Loader';
|
||||||
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 { useAccessMode } from '@/context/AccessModeContext';
|
import { useAccessMode } from '@/context/AccessModeContext';
|
||||||
|
@ -45,19 +45,6 @@ export enum RSTabID {
|
||||||
TERM_GRAPH = 3
|
TERM_GRAPH = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
|
|
||||||
if (axios.isAxiosError(error) && error.response && error.response.status === 404) {
|
|
||||||
return (
|
|
||||||
<div className='p-2 text-center'>
|
|
||||||
<p>Схема с указанным идентификатором отсутствует на портале.</p>
|
|
||||||
<TextURL text='Перейти в Библиотеку' href='/library' />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return <InfoError error={error} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function RSTabs() {
|
function RSTabs() {
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
const query = useQueryStrings();
|
const query = useQueryStrings();
|
||||||
|
@ -361,8 +348,6 @@ function RSTabs() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{loading ? <Loader /> : null}
|
|
||||||
{error ? <ProcessError error={error} /> : null}
|
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{showUpload ? <DlgUploadRSForm hideWindow={() => setShowUpload(false)} /> : null}
|
{showUpload ? <DlgUploadRSForm hideWindow={() => setShowUpload(false)} /> : null}
|
||||||
{showClone ? <DlgCloneLibraryItem base={schema!} hideWindow={() => setShowClone(false)} /> : null}
|
{showClone ? <DlgCloneLibraryItem base={schema!} hideWindow={() => setShowClone(false)} /> : null}
|
||||||
|
@ -406,6 +391,8 @@ function RSTabs() {
|
||||||
) : null}
|
) : null}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
|
||||||
|
{loading ? <Loader /> : null}
|
||||||
|
{error ? <ProcessError error={error} /> : null}
|
||||||
{schema && !loading ? (
|
{schema && !loading ? (
|
||||||
<Tabs
|
<Tabs
|
||||||
selectedIndex={activeTab}
|
selectedIndex={activeTab}
|
||||||
|
@ -435,7 +422,7 @@ function RSTabs() {
|
||||||
<TabLabel label='Граф термов' />
|
<TabLabel label='Граф термов' />
|
||||||
</TabList>
|
</TabList>
|
||||||
|
|
||||||
<AnimateFadeIn>
|
<AnimateFade>
|
||||||
<TabPanel forceRender style={{ display: activeTab === RSTabID.CARD ? '' : 'none' }}>
|
<TabPanel forceRender style={{ display: activeTab === RSTabID.CARD ? '' : 'none' }}>
|
||||||
<EditorRSForm
|
<EditorRSForm
|
||||||
isMutable={isMutable}
|
isMutable={isMutable}
|
||||||
|
@ -481,7 +468,7 @@ function RSTabs() {
|
||||||
onDeleteCst={promptDeleteCst}
|
onDeleteCst={promptDeleteCst}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</AnimateFadeIn>
|
</AnimateFade>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
|
@ -491,6 +478,19 @@ function RSTabs() {
|
||||||
export default RSTabs;
|
export default RSTabs;
|
||||||
|
|
||||||
// ====== Internals =========
|
// ====== Internals =========
|
||||||
|
function ProcessError({ error }: { error: ErrorData }): React.ReactElement {
|
||||||
|
if (axios.isAxiosError(error) && error.response && error.response.status === 404) {
|
||||||
|
return (
|
||||||
|
<div className='p-2 text-center'>
|
||||||
|
<p>Схема с указанным идентификатором отсутствует на портале.</p>
|
||||||
|
<TextURL text='Перейти в Библиотеку' href='/library' />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <InfoError error={error} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getNextActiveOnDelete(
|
function getNextActiveOnDelete(
|
||||||
activeID: number | undefined,
|
activeID: number | undefined,
|
||||||
items: IConstituenta[],
|
items: IConstituenta[],
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { useEffect, useState } from 'react';
|
||||||
import { BiInfoCircle } from 'react-icons/bi';
|
import { BiInfoCircle } from 'react-icons/bi';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import AnimateFadeIn from '@/components/AnimateFadeIn';
|
import AnimateFade from '@/components/AnimateFade';
|
||||||
import ExpectedAnonymous from '@/components/ExpectedAnonymous';
|
import ExpectedAnonymous from '@/components/ExpectedAnonymous';
|
||||||
import InfoError from '@/components/InfoError';
|
import InfoError from '@/components/InfoError';
|
||||||
import Button from '@/components/ui/Button';
|
import Button from '@/components/ui/Button';
|
||||||
|
@ -68,7 +68,7 @@ function RegisterPage() {
|
||||||
return <ExpectedAnonymous />;
|
return <ExpectedAnonymous />;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<AnimateFadeIn>
|
<AnimateFade>
|
||||||
<form className={clsx('px-6 py-3', classnames.flex_col)} onSubmit={handleSubmit}>
|
<form className={clsx('px-6 py-3', classnames.flex_col)} onSubmit={handleSubmit}>
|
||||||
<h1>Новый пользователь</h1>
|
<h1>Новый пользователь</h1>
|
||||||
<div className='flex gap-12'>
|
<div className='flex gap-12'>
|
||||||
|
@ -148,7 +148,7 @@ function RegisterPage() {
|
||||||
</div>
|
</div>
|
||||||
{error ? <InfoError error={error} /> : null}
|
{error ? <InfoError error={error} /> : null}
|
||||||
</form>
|
</form>
|
||||||
</AnimateFadeIn>
|
</AnimateFade>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import AnimateFadeIn from '@/components/AnimateFadeIn';
|
import AnimateFade from '@/components/AnimateFade';
|
||||||
import TextURL from '@/components/ui/TextURL';
|
import TextURL from '@/components/ui/TextURL';
|
||||||
import { urls } from '@/utils/constants';
|
import { urls } from '@/utils/constants';
|
||||||
|
|
||||||
function RestorePasswordPage() {
|
function RestorePasswordPage() {
|
||||||
return (
|
return (
|
||||||
<AnimateFadeIn className='py-3'>
|
<AnimateFade className='py-3'>
|
||||||
<p>Автоматическое восстановление пароля не доступно.</p>
|
<p>Автоматическое восстановление пароля не доступно.</p>
|
||||||
<p>
|
<p>
|
||||||
Возможно восстановление пароля через обращение на <TextURL href={urls.mail_portal} text='portal@acconcept.ru' />
|
Возможно восстановление пароля через обращение на <TextURL href={urls.mail_portal} text='portal@acconcept.ru' />
|
||||||
</p>
|
</p>
|
||||||
</AnimateFadeIn>
|
</AnimateFade>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,8 @@ import { AnimatePresence } from 'framer-motion';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { FiBell, FiBellOff } from 'react-icons/fi';
|
import { FiBell, FiBellOff } from 'react-icons/fi';
|
||||||
|
|
||||||
import AnimateFadeIn from '@/components/AnimateFadeIn';
|
import AnimateFade from '@/components/AnimateFade';
|
||||||
import InfoError from '@/components/InfoError';
|
import DataLoader from '@/components/DataLoader';
|
||||||
import { Loader } from '@/components/ui/Loader';
|
|
||||||
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 { useAuth } from '@/context/AuthContext';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
|
@ -29,11 +28,13 @@ function UserTabs() {
|
||||||
}, [auth, items]);
|
}, [auth, items]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<DataLoader
|
||||||
{loading ? <Loader /> : null}
|
id='profile-page' //
|
||||||
{error ? <InfoError error={error} /> : null}
|
isLoading={loading}
|
||||||
{user ? (
|
error={error}
|
||||||
<AnimateFadeIn className='flex gap-6 py-2'>
|
hasNoData={!user}
|
||||||
|
>
|
||||||
|
<AnimateFade className='flex gap-6 py-2'>
|
||||||
<div>
|
<div>
|
||||||
<Overlay position='top-0 right-0'>
|
<Overlay position='top-0 right-0'>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
|
@ -57,9 +58,8 @@ function UserTabs() {
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{subscriptions.length > 0 && showSubs ? <ViewSubscriptions items={subscriptions} /> : null}
|
{subscriptions.length > 0 && showSubs ? <ViewSubscriptions items={subscriptions} /> : null}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</AnimateFadeIn>
|
</AnimateFade>
|
||||||
) : null}
|
</DataLoader>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -191,7 +191,7 @@ export const animateModal = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const animateFadeIn = {
|
export const animateFade = {
|
||||||
initial: {
|
initial: {
|
||||||
opacity: 0
|
opacity: 0
|
||||||
},
|
},
|
||||||
|
@ -208,7 +208,7 @@ export const animateFadeIn = {
|
||||||
transition: {
|
transition: {
|
||||||
type: 'tween',
|
type: 'tween',
|
||||||
ease: 'linear',
|
ease: 'linear',
|
||||||
duration: 2
|
duration: 0.3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user