151 lines
No EOL
8.6 KiB
TypeScript
151 lines
No EOL
8.6 KiB
TypeScript
import styles from './SidebarStyles/SidebarSubmenu.module.css';
|
|
import { Link } from 'react-router-dom';
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
import { useRef, useEffect, useState } from 'react';
|
|
import { SidebarSubmenuProps } from './sidebarTypes';
|
|
|
|
const SidebarSubmenu: React.FC<SidebarSubmenuProps> = ({ item, isOpen, isMinimized = false }) => {
|
|
if (!item.submenu) return null;
|
|
|
|
return (
|
|
<AnimatePresence>
|
|
{isOpen && (
|
|
<motion.div
|
|
initial={{ height: 0, opacity: 0 }}
|
|
animate={{
|
|
height: "auto",
|
|
opacity: 1,
|
|
transition: {
|
|
height: { duration: 0.3, ease: "easeInOut", delay: 0 },
|
|
opacity: { duration: 0.25, ease: "easeInOut", delay: 0.3 }
|
|
}
|
|
}}
|
|
exit={{
|
|
height: 0,
|
|
opacity: 0,
|
|
transition: {
|
|
opacity: { duration: 0.25, ease: "easeInOut", delay: 0 },
|
|
height: { duration: 0.3, ease: "easeInOut", delay: 0.25 }
|
|
}
|
|
}}
|
|
style={{ overflow: "hidden" }}
|
|
className={`${styles.submenu} ${isMinimized ? styles.minimized : ''}`}
|
|
>
|
|
{isMinimized ? (
|
|
// Horizontal layout for minimized sidebar
|
|
<motion.div
|
|
className={styles.submenuHorizontalContainer}
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1, transition: { delay: 0.3, duration: 0.25 } }}
|
|
exit={{ opacity: 0, transition: { duration: 0.25, delay: 0 } }}
|
|
>
|
|
<ul className={styles.submenuHorizontalList}>
|
|
{item.submenu.map(subitem => {
|
|
const SubIcon = subitem.icon as React.ComponentType<React.SVGProps<SVGSVGElement>>;
|
|
|
|
return (
|
|
<li key={subitem.id} className={styles.submenuHorizontalItem}>
|
|
<Link
|
|
to={subitem.link || '#'}
|
|
title={subitem.name}
|
|
className={styles.submenuHorizontalLink}
|
|
>
|
|
{SubIcon && (
|
|
<SubIcon
|
|
className={styles.submenuHorizontalIcon}
|
|
style={{
|
|
width: '16px',
|
|
height: '16px',
|
|
color: '#181818',
|
|
display: 'block'
|
|
}}
|
|
/>
|
|
)}
|
|
|
|
</Link>
|
|
</li>
|
|
);
|
|
})}
|
|
</ul>
|
|
</motion.div>
|
|
) : (
|
|
// Vertical layout for expanded sidebar
|
|
<motion.div
|
|
className={styles.submenuLineContainer}
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1, transition: { delay: 0.3, duration: 0.25 } }}
|
|
exit={{ opacity: 0, transition: { duration: 0.25, delay: 0 } }}
|
|
>
|
|
<ul className={styles.submenuList}>
|
|
{item.submenu.map(subitem => {
|
|
const textRef = useRef<HTMLSpanElement>(null);
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
const [isOverflowing, setIsOverflowing] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const checkOverflow = () => {
|
|
if (textRef.current && containerRef.current) {
|
|
const textWidth = textRef.current.scrollWidth;
|
|
const containerWidth = containerRef.current.clientWidth;
|
|
setIsOverflowing(textWidth > containerWidth);
|
|
}
|
|
};
|
|
|
|
checkOverflow();
|
|
// Also check on window resize
|
|
window.addEventListener('resize', checkOverflow);
|
|
return () => window.removeEventListener('resize', checkOverflow);
|
|
}, [subitem.name]);
|
|
|
|
const SubIcon = subitem.icon as React.ComponentType<React.SVGProps<SVGSVGElement>>;
|
|
|
|
return (
|
|
<li key={subitem.id}>
|
|
<Link
|
|
to={subitem.link || '#'}
|
|
title={subitem.name}
|
|
>
|
|
<div
|
|
ref={containerRef}
|
|
className={styles.textContainer}
|
|
>
|
|
<motion.span
|
|
ref={textRef}
|
|
style={{
|
|
display: 'block',
|
|
whiteSpace: 'nowrap',
|
|
}}
|
|
initial={{ x: 0 }}
|
|
animate={{ x: 0 }}
|
|
{...(isOverflowing && {
|
|
whileHover: {
|
|
x: -(textRef.current?.scrollWidth || 0) + (containerRef.current?.clientWidth || 153),
|
|
transition: {
|
|
duration: 2,
|
|
ease: "linear"
|
|
}
|
|
}
|
|
})}
|
|
>
|
|
<div style={{ display: 'flex', alignItems: 'center', paddingRight: '10px' }}>
|
|
{SubIcon && <SubIcon className={styles.submenuIcon} />}
|
|
<span style={{ marginLeft: SubIcon ? '8px' : '0' }}>
|
|
{subitem.name}
|
|
</span>
|
|
</div>
|
|
</motion.span>
|
|
</div>
|
|
</Link>
|
|
</li>
|
|
);
|
|
})}
|
|
</ul>
|
|
</motion.div>
|
|
)}
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
);
|
|
};
|
|
|
|
export default SidebarSubmenu; |