frontend_nyla/src/components/Sidebar/SidebarSubmenu.tsx

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;