F: Animation rework pt2
This commit is contained in:
parent
725b21091c
commit
ce8f2584db
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",
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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]);
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
@ -187,7 +188,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'
|
||||||
|
@ -219,7 +220,7 @@ function LibraryPage() {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className='flex'>
|
<div className='flex'>
|
||||||
<AnimatePresence initial={false}>{options.folderMode ? viewLocations : null}</AnimatePresence>
|
{viewLocations}
|
||||||
{viewLibrary}
|
{viewLibrary}
|
||||||
</div>
|
</div>
|
||||||
</DataLoader>
|
</DataLoader>
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
@ -55,17 +53,6 @@ function TopicsDropdown({ activeTopic, onChangeTopic }: TopicsDropdownProps) {
|
||||||
className={clsx('w-[3rem] h-7 rounded-none border-l-0', menu.isOpen && 'border-b-0')}
|
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 border-l-0 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'
|
||||||
|
|
|
@ -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,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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ 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}
|
||||||
|
|
|
@ -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 { 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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,97 +4,6 @@
|
||||||
|
|
||||||
import { Variants } from 'framer-motion';
|
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 = {
|
export const animateDropdown: Variants = {
|
||||||
open: {
|
open: {
|
||||||
clipPath: 'inset(0% 0% 0% 0%)',
|
clipPath: 'inset(0% 0% 0% 0%)',
|
||||||
|
@ -136,120 +45,6 @@ export const animateDropdownItem: Variants = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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 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 = {
|
export const animateModal = {
|
||||||
initial: {
|
initial: {
|
||||||
clipPath: 'inset(50% 50% 50% 50%)',
|
clipPath: 'inset(50% 50% 50% 50%)',
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
--text-max-width: 75ch;
|
--text-max-width: 75ch;
|
||||||
--scroll-padding: 3rem;
|
--scroll-padding: 3rem;
|
||||||
|
|
||||||
|
--duration-move: 400ms;
|
||||||
|
|
||||||
/* 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%);
|
||||||
|
|
|
@ -248,4 +248,10 @@
|
||||||
.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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,9 @@ 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
|
fadeDuration: 300, // milliseconds - duration of fade animation
|
||||||
moveDuration: 700, // milliseconds - duration of move 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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user