import React, { useState } from "react"; import { useLocation, useNavigate } from "react-router-dom"; import { Icon } from "@nice/iconer"; import { theme } from "antd"; import { motion } from "framer-motion"; // Import Framer Motion // Define types for the props interface CollapsibleSectionProps { items: Array; className?: string; defaultExpandedKeys?: string[]; } interface MenuItem { key: string; link?: string; blank?: boolean; icon?: React.ReactNode; label: string; children?: Array; extra?: React.ReactNode; } const CollapsibleSection: React.FC = ({ items, className, defaultExpandedKeys = [], }) => { const location = useLocation(); const navigate = useNavigate(); const currentPath = location.pathname; const currentSearchParams = new URLSearchParams(location.search); const { token } = theme.useToken(); const [expandedSections, setExpandedSections] = useState<{ [key: string]: boolean; }>(() => defaultExpandedKeys.reduce( (acc, key) => { acc[key] = true; return acc; }, {} as { [key: string]: boolean } ) ); const toggleChildCollapse = (key: string): void => { setExpandedSections((prevState) => ({ ...prevState, [key]: !prevState[key], })); }; const renderItems = ( items: Array, level: number ): React.ReactNode => { return items.map((item) => { const itemUrl = new URL(item.link, window.location.origin); const itemPath = itemUrl.pathname; const itemSearchParams = new URLSearchParams(itemUrl.search); const hasChildren = item.children && item.children.length > 0; const isActive = currentPath === itemPath && Array.from(itemSearchParams.entries()).every( ([key, value]) => currentSearchParams.get(key) === value ); const isChildCollapsed = !expandedSections[item.key]; return (
{ if (hasChildren) { toggleChildCollapse(item.key); } if (item.link) { if (!item.blank) { navigate(item.link); } else { window.open(item.link, "_blank"); } } }} initial={false} animate={{ backgroundColor: isActive ? token.colorPrimaryBorder : token.colorPrimary, }} whileHover={{ backgroundColor: token.colorPrimaryHover, }} transition={{ type: "spring", stiffness: 300, damping: 25, duration: 0.3, }} style={{ marginLeft: `${level * 16}px` }}>
{item.icon && {item.icon}} {item.label}
{hasChildren && ( )}
{item.extra &&
{item.extra}
}
{hasChildren && ( {renderItems(item.children, level + 1)} )}
); }); }; return
{renderItems(items, 0)}
; }; export default CollapsibleSection;