diff --git a/README.md b/README.md
index 9ce3c227..b8cab60c 100644
--- a/README.md
+++ b/README.md
@@ -24,6 +24,7 @@ This readme file is used mostly to document project dependencies
- react-pdf
- react-tooltip
- js-file-download
+ - framer-motion
- reagraph
- @tanstack/react-table
- @uiw/react-codemirror
diff --git a/rsconcept/frontend/package-lock.json b/rsconcept/frontend/package-lock.json
index 48a32112..572eeb16 100644
--- a/rsconcept/frontend/package-lock.json
+++ b/rsconcept/frontend/package-lock.json
@@ -14,6 +14,7 @@
"@uiw/react-codemirror": "^4.21.21",
"axios": "^1.6.2",
"clsx": "^2.0.0",
+ "framer-motion": "^10.16.16",
"js-file-download": "^0.4.12",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -4985,6 +4986,44 @@
"url": "https://github.com/sponsors/rawify"
}
},
+ "node_modules/framer-motion": {
+ "version": "10.16.16",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.16.16.tgz",
+ "integrity": "sha512-je6j91rd7NmUX7L1XHouwJ4v3R+SO4umso2LUcgOct3rHZ0PajZ80ETYZTajzEXEl9DlKyzjyt4AvGQ+lrebOw==",
+ "dependencies": {
+ "tslib": "^2.4.0"
+ },
+ "optionalDependencies": {
+ "@emotion/is-prop-valid": "^0.8.2"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0",
+ "react-dom": "^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/framer-motion/node_modules/@emotion/is-prop-valid": {
+ "version": "0.8.8",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
+ "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
+ "optional": true,
+ "dependencies": {
+ "@emotion/memoize": "0.7.4"
+ }
+ },
+ "node_modules/framer-motion/node_modules/@emotion/memoize": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
+ "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==",
+ "optional": true
+ },
"node_modules/fs-minipass": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
diff --git a/rsconcept/frontend/package.json b/rsconcept/frontend/package.json
index 0f52ec01..73e8c5e3 100644
--- a/rsconcept/frontend/package.json
+++ b/rsconcept/frontend/package.json
@@ -18,6 +18,7 @@
"@uiw/react-codemirror": "^4.21.21",
"axios": "^1.6.2",
"clsx": "^2.0.0",
+ "framer-motion": "^10.16.16",
"js-file-download": "^0.4.12",
"react": "^18.2.0",
"react-dom": "^18.2.0",
diff --git a/rsconcept/frontend/src/components/Common/Button.tsx b/rsconcept/frontend/src/components/Common/Button.tsx
index a9239940..4233a3c2 100644
--- a/rsconcept/frontend/src/components/Common/Button.tsx
+++ b/rsconcept/frontend/src/components/Common/Button.tsx
@@ -38,7 +38,7 @@ function Button({
className,
colors
)}
- data-tooltip-id={title ? (globalIDs.tooltip) : undefined}
+ data-tooltip-id={title ? globalIDs.tooltip : undefined}
data-tooltip-content={title}
{...restProps}
>
diff --git a/rsconcept/frontend/src/components/Common/ConceptTooltip.tsx b/rsconcept/frontend/src/components/Common/ConceptTooltip.tsx
index e08ce60d..8d44e18e 100644
--- a/rsconcept/frontend/src/components/Common/ConceptTooltip.tsx
+++ b/rsconcept/frontend/src/components/Common/ConceptTooltip.tsx
@@ -22,7 +22,6 @@ function ConceptTooltip({
...restProps
}: ConceptTooltipProps) {
const { darkMode } = useConceptTheme();
-
if (typeof window === 'undefined') {
return null;
}
diff --git a/rsconcept/frontend/src/components/Common/Dropdown.tsx b/rsconcept/frontend/src/components/Common/Dropdown.tsx
index 4d018fbb..8e4ebf9c 100644
--- a/rsconcept/frontend/src/components/Common/Dropdown.tsx
+++ b/rsconcept/frontend/src/components/Common/Dropdown.tsx
@@ -1,25 +1,29 @@
import clsx from 'clsx';
+import { motion } from 'framer-motion';
+
+import { animateDropdown } from '@/utils/animations';
import { CProps } from '../props';
-import Overlay from './Overlay';
interface DropdownProps
extends CProps.Styling {
stretchLeft?: boolean
+ isOpen: boolean
children: React.ReactNode
}
function Dropdown({
+ isOpen, stretchLeft,
className,
- stretchLeft,
children,
...restProps
-}: DropdownProps) {
+}: DropdownProps) {
return (
-
+
{children}
- );
+
+ );
}
export default Dropdown;
\ No newline at end of file
diff --git a/rsconcept/frontend/src/components/Common/DropdownButton.tsx b/rsconcept/frontend/src/components/Common/DropdownButton.tsx
index c0d4b15f..0adeb5c2 100644
--- a/rsconcept/frontend/src/components/Common/DropdownButton.tsx
+++ b/rsconcept/frontend/src/components/Common/DropdownButton.tsx
@@ -1,11 +1,13 @@
import clsx from 'clsx';
+import { motion } from 'framer-motion';
+import { animateDropdownItem } from '@/utils/animations';
import { globalIDs } from '@/utils/constants';
import { CProps } from '../props';
interface DropdownButtonProps
-extends CProps.Button {
+extends CProps.AnimatedButton {
text?: string
icon?: React.ReactNode
@@ -20,7 +22,7 @@ function DropdownButton({
...restProps
}: DropdownButtonProps) {
return (
- );
+ );
}
export default DropdownButton;
\ No newline at end of file
diff --git a/rsconcept/frontend/src/components/Common/DropdownCheckbox.tsx b/rsconcept/frontend/src/components/Common/DropdownCheckbox.tsx
index edb917b2..a87e3598 100644
--- a/rsconcept/frontend/src/components/Common/DropdownCheckbox.tsx
+++ b/rsconcept/frontend/src/components/Common/DropdownCheckbox.tsx
@@ -1,4 +1,7 @@
import clsx from 'clsx';
+import { motion } from 'framer-motion';
+
+import { animateDropdownItem } from '@/utils/animations';
import Checkbox from './Checkbox';
@@ -12,7 +15,8 @@ interface DropdownCheckboxProps {
function DropdownCheckbox({ title, setValue, disabled, ...restProps }: DropdownCheckboxProps) {
return (
-
- );
+ );
}
export default DropdownCheckbox;
\ No newline at end of file
diff --git a/rsconcept/frontend/src/components/Common/Modal.tsx b/rsconcept/frontend/src/components/Common/Modal.tsx
index e1c0eb73..48d87365 100644
--- a/rsconcept/frontend/src/components/Common/Modal.tsx
+++ b/rsconcept/frontend/src/components/Common/Modal.tsx
@@ -1,10 +1,12 @@
'use client';
import clsx from 'clsx';
+import { motion } from 'framer-motion';
import { useRef } from 'react';
import { BiX } from 'react-icons/bi';
import useEscapeKey from '@/hooks/useEscapeKey';
+import { animateModal } from '@/utils/animations';
import { CProps } from '../props';
import Button from './Button';
@@ -56,13 +58,16 @@ function Modal({
'w-full h-full',
'clr-modal-backdrop'
)}/>
-
@@ -107,7 +112,7 @@ function Modal({
onClick={handleCancel}
/>
-
+
>);
}
diff --git a/rsconcept/frontend/src/components/Navigation/Navigation.tsx b/rsconcept/frontend/src/components/Navigation/Navigation.tsx
index 7c66a494..a348bce9 100644
--- a/rsconcept/frontend/src/components/Navigation/Navigation.tsx
+++ b/rsconcept/frontend/src/components/Navigation/Navigation.tsx
@@ -1,10 +1,12 @@
import clsx from 'clsx';
+import { motion } from 'framer-motion';
import { FaSquarePlus } from 'react-icons/fa6';
import { IoLibrary } from 'react-icons/io5';
import { EducationIcon } from '@/components/Icons';
import { useConceptNavigation } from '@/context/NagivationContext';
import { useConceptTheme } from '@/context/ThemeContext';
+import { animateNavigation } from '@/utils/animations';
import Logo from './Logo';
import NavigationButton from './NavigationButton';
@@ -13,13 +15,13 @@ import UserMenu from './UserMenu';
function Navigation () {
const router = useConceptNavigation();
- const { noNavigation } = useConceptTheme();
+ const { noNavigationAnimation } = useConceptTheme();
const navigateHome = () => router.push('/');
const navigateLibrary = () => router.push('/library');
const navigateHelp = () => router.push('/manuals');
const navigateCreateNew = () => router.push('/library/create');
-
+
return (
);
}
diff --git a/rsconcept/frontend/src/components/Navigation/ToggleNavigationButton.tsx b/rsconcept/frontend/src/components/Navigation/ToggleNavigationButton.tsx
index e573ae14..14410676 100644
--- a/rsconcept/frontend/src/components/Navigation/ToggleNavigationButton.tsx
+++ b/rsconcept/frontend/src/components/Navigation/ToggleNavigationButton.tsx
@@ -1,36 +1,28 @@
import clsx from 'clsx';
-import { useMemo } from 'react';
+import { motion } from 'framer-motion';
+import { RiPushpinFill, RiUnpinLine } from 'react-icons/ri';
import { useConceptTheme } from '@/context/ThemeContext';
+import { animateNavigationToggle } from '@/utils/animations';
function ToggleNavigationButton() {
- const { noNavigation, toggleNoNavigation } = useConceptTheme();
- const text = useMemo(() => (
- noNavigation ?
- '∨∨∨'
- :
- <>
- {'>'}
- {'>'}
- >
- ), [noNavigation]
- );
+ const { noNavigationAnimation, toggleNoNavigation } = useConceptTheme();
return (
- );
+ {!noNavigationAnimation ? : null}
+ {noNavigationAnimation ? : null}
+ );
}
export default ToggleNavigationButton;
\ No newline at end of file
diff --git a/rsconcept/frontend/src/components/Navigation/UserDropdown.tsx b/rsconcept/frontend/src/components/Navigation/UserDropdown.tsx
index 298da73e..89a7e5dc 100644
--- a/rsconcept/frontend/src/components/Navigation/UserDropdown.tsx
+++ b/rsconcept/frontend/src/components/Navigation/UserDropdown.tsx
@@ -5,10 +5,11 @@ import { useConceptNavigation } from '@/context/NagivationContext';
import { useConceptTheme } from '@/context/ThemeContext';
interface UserDropdownProps {
+ isOpen: boolean
hideDropdown: () => void
}
-function UserDropdown({ hideDropdown }: UserDropdownProps) {
+function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
const { darkMode, toggleDarkMode } = useConceptTheme();
const router = useConceptNavigation();
const { user, logout } = useAuth();
@@ -25,7 +26,7 @@ function UserDropdown({ hideDropdown }: UserDropdownProps) {
};
return (
-
+
}
onClick={menu.toggle}
/> : null}
- {(user && menu.isActive) ?
menu.hide()}
- /> : null}
+ />
);
}
diff --git a/rsconcept/frontend/src/components/RefsInput/RefsInput.tsx b/rsconcept/frontend/src/components/RefsInput/RefsInput.tsx
index 060baf87..91d486d7 100644
--- a/rsconcept/frontend/src/components/RefsInput/RefsInput.tsx
+++ b/rsconcept/frontend/src/components/RefsInput/RefsInput.tsx
@@ -6,6 +6,7 @@ import { createTheme } from '@uiw/codemirror-themes';
import CodeMirror, { BasicSetupOptions, ReactCodeMirrorProps, ReactCodeMirrorRef } from '@uiw/react-codemirror';
import clsx from 'clsx';
import { EditorView } from 'codemirror';
+import { AnimatePresence } from 'framer-motion';
import { RefObject, useCallback, useMemo, useRef, useState } from 'react';
import Label from '@/components/Common/Label';
@@ -164,6 +165,7 @@ function RefsInput({
}, [thisRef]);
return (<>
+
{showEditor ?
setShowEditor(false)}
@@ -177,6 +179,8 @@ function RefsInput({
}}
onSave={handleInputReference}
/> : null}
+
+
, HTMLTextAreaElement>;
export type Input = React.DetailedHTMLProps, HTMLInputElement>;
+export type AnimatedButton = Omit, 'type'>;
+
}
\ No newline at end of file
diff --git a/rsconcept/frontend/src/context/ThemeContext.tsx b/rsconcept/frontend/src/context/ThemeContext.tsx
index e7b3f9c2..873b41a4 100644
--- a/rsconcept/frontend/src/context/ThemeContext.tsx
+++ b/rsconcept/frontend/src/context/ThemeContext.tsx
@@ -1,10 +1,11 @@
'use client';
import clsx from 'clsx';
-import { createContext, useContext, useLayoutEffect, useMemo, useState } from 'react';
+import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useState } from 'react';
import ConceptTooltip from '@/components/Common/ConceptTooltip';
import useLocalStorage from '@/hooks/useLocalStorage';
+import { animationDuration } from '@/utils/animations';
import { darkT, IColorTheme, lightT } from '@/utils/color';
import { globalIDs } from '@/utils/constants';
@@ -17,6 +18,7 @@ interface IThemeContext {
darkMode: boolean
toggleDarkMode: () => void
+ noNavigationAnimation: boolean
noNavigation: boolean
toggleNoNavigation: () => void
@@ -44,6 +46,7 @@ export const ThemeState = ({ children }: ThemeStateProps) => {
const [darkMode, setDarkMode] = useLocalStorage('darkMode', false);
const [colors, setColors] = useState(lightT);
const [noNavigation, setNoNavigation] = useState(false);
+ const [noNavigationAnimation, setNoNavigationAnimation] = useState(false);
const [noFooter, setNoFooter] = useState(false);
const [showScroll, setShowScroll] = useState(false);
@@ -65,6 +68,17 @@ export const ThemeState = ({ children }: ThemeStateProps) => {
setColors(darkMode ? darkT : lightT)
}, [darkMode, setColors]);
+ const toggleNoNavigation = useCallback(
+ () => {
+ if (noNavigation) {
+ setNoNavigationAnimation(false);
+ setNoNavigation(false);
+ } else {
+ setNoNavigationAnimation(true);
+ setTimeout(() => setNoNavigation(true), animationDuration.navigationToggle);
+ }
+ }, [noNavigation]);
+
const mainHeight = useMemo(
() => {
return !noNavigation ?
@@ -82,9 +96,9 @@ export const ThemeState = ({ children }: ThemeStateProps) => {
return (
setDarkMode(prev => !prev),
- toggleNoNavigation: () => setNoNavigation(prev => !prev),
+ toggleNoNavigation: toggleNoNavigation,
setNoFooter, setShowScroll,
viewportHeight, mainHeight
}}>
diff --git a/rsconcept/frontend/src/hooks/useDropdown.ts b/rsconcept/frontend/src/hooks/useDropdown.ts
index 6e52e9be..5797494f 100644
--- a/rsconcept/frontend/src/hooks/useDropdown.ts
+++ b/rsconcept/frontend/src/hooks/useDropdown.ts
@@ -5,17 +5,17 @@ import { useRef, useState } from 'react';
import useClickedOutside from './useClickedOutside';
function useDropdown() {
- const [isActive, setIsActive] = useState(false);
+ const [isOpen, setIsOpen] = useState(false);
const ref = useRef(null);
- useClickedOutside({ ref, callback: () => setIsActive(false) });
+ useClickedOutside({ ref, callback: () => setIsOpen(false) });
return {
ref,
- isActive,
- setIsActive,
- toggle: () => setIsActive(!isActive),
- hide: () => setIsActive(false)
+ isOpen,
+ setIsOpen,
+ toggle: () => setIsOpen(!isOpen),
+ hide: () => setIsOpen(false)
};
}
diff --git a/rsconcept/frontend/src/pages/LibraryPage/PickerStrategy.tsx b/rsconcept/frontend/src/pages/LibraryPage/PickerStrategy.tsx
index 636217f9..e702e4cb 100644
--- a/rsconcept/frontend/src/pages/LibraryPage/PickerStrategy.tsx
+++ b/rsconcept/frontend/src/pages/LibraryPage/PickerStrategy.tsx
@@ -48,8 +48,7 @@ function PickerStrategy({ value, onChange }: PickerStrategyProps) {
text={labelLibraryFilter(value)}
onClick={strategyMenu.toggle}
/>
- {strategyMenu.isActive ?
-
+
{Object.values(LibraryFilterStrategy).map(
(enumValue, index) => {
const strategy = enumValue as LibraryFilterStrategy;
@@ -63,7 +62,7 @@ function PickerStrategy({ value, onChange }: PickerStrategyProps) {
disabled={isStrategyDisabled(strategy)}
/>);
})}
- : null}
+
);
}
diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/EditorConstituenta.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/EditorConstituenta.tsx
index 8c4e5a21..f66e8c10 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/EditorConstituenta.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/EditorConstituenta.tsx
@@ -1,5 +1,6 @@
'use client';
+import { AnimatePresence } from 'framer-motion';
import { Dispatch, SetStateAction, useMemo, useState } from 'react';
import { useRSForm } from '@/context/RSFormContext';
@@ -149,6 +150,7 @@ function EditorConstituenta({
onEditTerm={onEditTerm}
onRenameCst={onRenameCst}
/>
+
{(showList && windowSize.width && windowSize.width >= SIDELIST_HIDE_THRESHOLD) ?
: null}
+
>);
}
diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/EditorRSExpression.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/EditorRSExpression.tsx
index edbfc05a..bcd475c0 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/EditorRSExpression.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/EditorRSExpression.tsx
@@ -1,6 +1,7 @@
'use client';
import { ReactCodeMirrorRef } from '@uiw/react-codemirror';
+import { AnimatePresence } from 'framer-motion';
import { useCallback, useLayoutEffect, useRef, useState } from 'react';
import { BiListUl } from 'react-icons/bi';
import { FaRegKeyboard } from 'react-icons/fa6';
@@ -31,7 +32,7 @@ interface EditorRSExpressionProps {
value: string
label: string
placeholder?: string
-
+
disabled?: boolean
toggleReset?: boolean
showList: boolean
@@ -134,12 +135,14 @@ function EditorRSExpression({
}
return (<>
+
{showAST ?
setShowAST(false)}
/> : null}
+
@@ -179,18 +182,18 @@ function EditorRSExpression({
{...restProps}
/>
- {showControls ?
- : null}
+ />
- {(parseData && parseData.errors.length > 0) ?
0}
data={parseData}
disabled={disabled}
onShowError={onShowError}
- />: null}
+ />
>);
}
diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/ParsingResult.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/ParsingResult.tsx
index 42e52420..ab365fff 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/ParsingResult.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/ParsingResult.tsx
@@ -1,23 +1,39 @@
'use client';
+import clsx from 'clsx';
+import { motion } from 'framer-motion';
+
import { IExpressionParse, IRSErrorDescription } from '@/models/rslang';
+import { animateRSControl } from '@/utils/animations';
import { describeRSError } from '@/utils/labels';
import { getRSErrorPrefix } from '@/utils/misc';
interface ParsingResultProps {
- data: IExpressionParse
+ data: IExpressionParse | undefined
disabled?: boolean
+ isOpen: boolean
onShowError: (error: IRSErrorDescription) => void
}
-function ParsingResult({ data, disabled, onShowError }: ParsingResultProps) {
- const errorCount = data.errors.reduce((total, error) => (error.isCritical ? total + 1 : total), 0);
- const warningsCount = data.errors.length - errorCount;
+function ParsingResult({ isOpen, data, disabled, onShowError }: ParsingResultProps) {
+ const errorCount = data ? data.errors.reduce((total, error) => (error.isCritical ? total + 1 : total), 0) : 0;
+ const warningsCount = data ? data.errors.length - errorCount : 0;
return (
-
+
Ошибок: {errorCount} | Предупреждений: {warningsCount}
- {data.errors.map(
+ {data?.errors.map(
(error, index) => {
return (
{` ${describeRSError(error)}`}
);
})}
- );
+ );
}
export default ParsingResult;
\ No newline at end of file
diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/RSEditControls.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/RSEditControls.tsx
index 990e3500..5cf454a8 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/RSEditControls.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSExpression/RSEditControls.tsx
@@ -1,4 +1,7 @@
+import { motion } from 'framer-motion';
+
import { TokenID } from '@/models/rslang';
+import { animateRSControl } from '@/utils/animations';
import { prefixes } from '@/utils/constants';
import RSLocalButton from './RSLocalButton';
@@ -79,13 +82,19 @@ const SECONDARY_THIRD_ROW = [
];
interface RSEditorControlsProps {
- onEdit: (id: TokenID, key?: string) => void
+ isOpen: boolean
disabled?: boolean
+ onEdit: (id: TokenID, key?: string) => void
}
-function RSEditorControls({ onEdit, disabled }: RSEditorControlsProps) {
+function RSEditorControls({ isOpen, disabled, onEdit }: RSEditorControlsProps) {
return (
-
+
{MAIN_FIRST_ROW.map(
(token) =>
)}
-
);
+ );
}
export default RSEditorControls;
\ No newline at end of file
diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/RSListToolbar.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/RSListToolbar.tsx
index 4a5f3a09..726c4a4c 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/RSListToolbar.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/EditorRSList/RSListToolbar.tsx
@@ -70,8 +70,7 @@ function RSListToolbar({
disabled={!isMutable}
onClick={insertMenu.toggle}
/>
- {insertMenu.isActive ?
-
+
{(Object.values(CstType)).map(
(typeStr) =>
)}
- : null}
+
+
{showParamsDialog ?
setShowParamsDialog(false)}
initial={filterParams}
onConfirm={handleChangeParams}
/> : null}
+
{loading ? : null}
{error ? : null}
-
- {showUpload ?
- setShowUpload(false)}
- /> : null}
- {showClone ?
- setShowClone(false)}
- /> : null}
- {showCreateCst ?
- setShowCreateCst(false)}
- onCreate={handleCreateCst}
- schema={schema!}
- initial={createInitialData}
- /> : null}
- {showRenameCst ?
- setShowRenameCst(false)}
- onRename={handleRenameCst}
- initial={renameInitialData!}
- /> : null}
- {showDeleteCst ?
- setShowDeleteCst(false)}
- onDelete={handleDeleteCst}
- selected={toBeDeleted}
- /> : null}
- {showEditTerm ?
- setShowEditTerm(false)}
- onSave={handleSaveWordforms}
- target={activeCst!}
- /> : null}
- {showTemplates ?
- setShowTemplates(false)}
- insertAfter={insertCstID}
- onCreate={handleCreateCst}
- /> : null}
+
+ {showUpload ?
+ setShowUpload(false)}
+ /> : null}
+ {showClone ?
+ setShowClone(false)}
+ /> : null}
+ {showCreateCst ?
+ setShowCreateCst(false)}
+ onCreate={handleCreateCst}
+ schema={schema!}
+ initial={createInitialData}
+ /> : null}
+ {showRenameCst ?
+ setShowRenameCst(false)}
+ onRename={handleRenameCst}
+ initial={renameInitialData!}
+ /> : null}
+ {showDeleteCst ?
+ setShowDeleteCst(false)}
+ onDelete={handleDeleteCst}
+ selected={toBeDeleted}
+ /> : null}
+ {showEditTerm ?
+ setShowEditTerm(false)}
+ onSave={handleSaveWordforms}
+ target={activeCst!}
+ /> : null}
+ {showTemplates ?
+ setShowTemplates(false)}
+ insertAfter={insertCstID}
+ onCreate={handleCreateCst}
+ /> : null}
+
{(schema && !loading) ?
- {schemaMenu.isActive ?
-
-
+ }
+ icon={}
onClick={(!isOwned && user && isClaimable) ? handleClaimOwner : undefined}
/>
}
onClick={handleCreateNew}
/>
- : null}
+
@@ -153,8 +151,7 @@ function RSTabsMenu({
icon={}
onClick={editMenu.toggle}
/>
- {editMenu.isActive ?
-
+
}
onClick={handleTemplates}
/>
- : null}
+
@@ -182,8 +179,7 @@ function RSTabsMenu({
}
onClick={accessMenu.toggle}
/>
- {accessMenu.isActive ?
-
+
}
onClick={() => handleChangeMode(UserAccessMode.ADMIN)}
/>
- : null}
+
);
}
diff --git a/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ConstituentsSearch.tsx b/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ConstituentsSearch.tsx
index e6234709..55514633 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ConstituentsSearch.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ConstituentsSearch.tsx
@@ -90,8 +90,7 @@ function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }:
text={labelCstMathchMode(filterMatch)}
onClick={matchModeMenu.toggle}
/>
- {matchModeMenu.isActive ?
-
+
{Object.values(CstMatchMode).filter(value => !isNaN(Number(value))).map(
(value, index) => {
const matchMode = value as CstMatchMode;
@@ -103,7 +102,7 @@ function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }:
{labelCstMathchMode(matchMode)}: {describeCstMathchMode(matchMode)}
);
})}
- : null}
+
@@ -114,8 +113,7 @@ function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }:
text={labelCstSource(filterSource)}
onClick={sourceMenu.toggle}
/>
- {sourceMenu.isActive ?
-
+
{Object.values(DependencyMode).filter(value => !isNaN(Number(value))).map(
(value, index) => {
const source = value as DependencyMode;
@@ -127,7 +125,7 @@ function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }:
{labelCstSource(source)}: {describeCstSource(source)}
);
})}
- : null}
+
);
}
diff --git a/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ViewConstituents.tsx b/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ViewConstituents.tsx
index a44bf5b5..012ae98d 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ViewConstituents.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/ViewConstituents.tsx
@@ -1,9 +1,11 @@
'use client';
+import { motion } from 'framer-motion';
import { useMemo, useState } from 'react';
import { useConceptTheme } from '@/context/ThemeContext';
import { IConstituenta, IRSForm } from '@/models/rsform';
+import { animateSideView } from '@/utils/animations';
import ConstituentsSearch from './ConstituentsSearch';
import ConstituentsTable from './ConstituentsTable';
@@ -36,7 +38,12 @@ function ViewConstituents({ expression, baseHeight, schema, activeID, onOpenEdit
}, [noNavigation, baseHeight]);
return (
-
+
-
);
+ );
}
export default ViewConstituents;
\ No newline at end of file
diff --git a/rsconcept/frontend/src/pages/UserProfilePage/UserTabs.tsx b/rsconcept/frontend/src/pages/UserProfilePage/UserTabs.tsx
index c95cd3dd..2f4cba72 100644
--- a/rsconcept/frontend/src/pages/UserProfilePage/UserTabs.tsx
+++ b/rsconcept/frontend/src/pages/UserProfilePage/UserTabs.tsx
@@ -1,5 +1,6 @@
'use client';
+import { AnimatePresence } from 'framer-motion';
import { useMemo, useState } from 'react';
import { FiBell, FiBellOff } from 'react-icons/fi';
@@ -50,11 +51,12 @@ function UserTabs() {
+
{(subscriptions.length > 0 && showSubs) ?
-
-
Отслеживаемые схемы
-
- : null}
+ : null}
+
: null}
>);
}
diff --git a/rsconcept/frontend/src/pages/UserProfilePage/ViewSubscriptions.tsx b/rsconcept/frontend/src/pages/UserProfilePage/ViewSubscriptions.tsx
index 695e6dcb..a57cbd18 100644
--- a/rsconcept/frontend/src/pages/UserProfilePage/ViewSubscriptions.tsx
+++ b/rsconcept/frontend/src/pages/UserProfilePage/ViewSubscriptions.tsx
@@ -1,11 +1,13 @@
'use client';
+import { motion } from 'framer-motion';
import { useMemo } from 'react';
import { useIntl } from 'react-intl';
import DataTable, { createColumnHelper } from '@/components/DataTable';
import { useConceptNavigation } from '@/context/NagivationContext';
import { ILibraryItem } from '@/models/library';
+import { animateSideView } from '@/utils/animations';
interface ViewSubscriptionsProps {
items: ILibraryItem[]
@@ -52,25 +54,32 @@ function ViewSubscriptions({items}: ViewSubscriptionsProps) {
], [intl]);
return (
-
+ Отслеживаемые схемы
+
- Отслеживаемые схемы отсутствуют
-
- }
+ enableSorting
+ initialSorting={{
+ id: 'time_update',
+ desc: true
+ }}
+ noDataComponent={
+
+ Отслеживаемые схемы отсутствуют
+
+ }
- onRowClicked={openRSForm}
- />);
+ onRowClicked={openRSForm}
+ />
+ );
}
export default ViewSubscriptions;
\ No newline at end of file
diff --git a/rsconcept/frontend/src/utils/animations.ts b/rsconcept/frontend/src/utils/animations.ts
new file mode 100644
index 00000000..3a97a3fe
--- /dev/null
+++ b/rsconcept/frontend/src/utils/animations.ts
@@ -0,0 +1,163 @@
+/**
+ * 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 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%)',
+ height: 'max-content',
+ transition: {
+ type: 'spring',
+ bounce: 0,
+ duration: 0.4
+ }
+ },
+ closed: {
+ clipPath: 'inset(0% 0% 100% 0%)',
+ height: 0,
+ transition: {
+ type: 'spring',
+ bounce: 0,
+ duration: 0.3
+ }
+ }
+};
+
+export const animateSideView = {
+ initial: {
+ clipPath: 'inset(0% 100% 0% 0%)',
+ },
+ animate: {
+ clipPath: 'inset(0% 0% 0% 0%)',
+ transition: {
+ type: 'spring',
+ bounce: 0,
+ duration: 1
+ }
+ },
+ exit: {
+ clipPath: 'inset(0% 100% 0% 0%)',
+ transition: {
+ type: 'spring',
+ bounce: 0,
+ duration: 1
+ }
+ }
+};
+
+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
+ }
+ }
+};
\ No newline at end of file