diff --git a/src/components/layout/ModernLayout.tsx b/src/components/layout/ModernLayout.tsx index 5281493..ee17243 100644 --- a/src/components/layout/ModernLayout.tsx +++ b/src/components/layout/ModernLayout.tsx @@ -1,8 +1,9 @@ import React, { useState } from 'react'; -import { Link } from 'react-router-dom'; +import { Link, useLocation } from 'react-router-dom'; import { useIsMobile } from '@/hooks/use-mobile'; -import { ModernSidebar, SidebarBody, SidebarLink } from '@/components/ui/modern-sidebar'; +import { ModernSidebar, SidebarBody } from '@/components/ui/modern-sidebar'; +import { VerticalLimelightNav } from '@/components/ui/limelight-nav'; import OnboardingTour from '@/components/onboarding/OnboardingTour'; import HelpIcon from '@/components/help/HelpIcon'; import { useOnboardingTour } from '@/hooks/useOnboardingTour'; @@ -63,6 +64,7 @@ const LogoIcon = () => { export default function ModernLayout({ children }: ModernLayoutProps) { const [open, setOpen] = useState(false); + const location = useLocation(); const isMobile = useIsMobile(); const { @@ -73,65 +75,82 @@ export default function ModernLayout({ children }: ModernLayoutProps) { closeTour } = useOnboardingTour(); - const links = [ + const mainLinks = [ { + id: "dashboard", label: "Dashboard", href: "/", - icon: , + icon: , }, { + id: "transacoes", label: "Transações", href: "/transacoes", - icon: , + icon: , }, { + id: "cartoes", label: "Cartões de Crédito", href: "/cartoes", - icon: , + icon: , }, { + id: "categorias", label: "Categorias", href: "/categorias", - icon: , + icon: , }, { + id: "metas", label: "Metas", href: "/metas", - icon: , + icon: , }, { + id: "calendario", label: "Calendário", href: "/calendario", - icon: , + icon: , }, { + id: "assinatura", label: "Assinatura", href: "/assinatura", - icon: , + icon: , } ]; const whatsappLinks = [ { + id: "whatsapp", label: "Conectar WhatsApp", href: "/whatsapp", - icon: , + icon: , }, { + id: "grupos", label: "Grupos", href: "/grupos-whatsapp", - icon: , + icon: , } ]; const configLinks = [ { + id: "configuracoes", label: "Configurações", href: "/configuracoes", - icon: , + icon: , } ]; + // Determine active index based on current route + const getActiveIndex = () => { + const allLinks = [...mainLinks, ...whatsappLinks, ...configLinks]; + const activeIndex = allLinks.findIndex(link => link.href === location.pathname); + return activeIndex >= 0 ? activeIndex : 0; + }; + return (
@@ -139,43 +158,53 @@ export default function ModernLayout({ children }: ModernLayoutProps) {
{open ? : } -
- {links.map((link, idx) => ( - - ))} -
- -
- {open && ( - - WhatsApp - - )} -
- {whatsappLinks.map((link, idx) => ( - - ))} +
+ {/* Main Navigation */} +
+
-
-
- {open && ( - - Configurações - - )} -
- {configLinks.map((link, idx) => ( - - ))} + {/* WhatsApp Section */} +
+ {open && ( + + WhatsApp + + )} + = mainLinks.length && getActiveIndex() < mainLinks.length + whatsappLinks.length ? getActiveIndex() - mainLinks.length : -1} + showLabels={open} + className="space-y-1" + /> +
+ + {/* Config Section */} +
+ {open && ( + + Configurações + + )} + = mainLinks.length + whatsappLinks.length ? getActiveIndex() - mainLinks.length - whatsappLinks.length : -1} + showLabels={open} + className="space-y-1" + />
diff --git a/src/components/ui/limelight-nav.tsx b/src/components/ui/limelight-nav.tsx new file mode 100644 index 0000000..b7c8dcd --- /dev/null +++ b/src/components/ui/limelight-nav.tsx @@ -0,0 +1,145 @@ + +import React, { useState, useRef, useLayoutEffect, cloneElement } from 'react'; + +// --- Internal Types and Defaults --- + +const DefaultHomeIcon = (props: React.SVGProps) => ; +const DefaultCompassIcon = (props: React.SVGProps) => ; +const DefaultBellIcon = (props: React.SVGProps) => ; + +export type NavItem = { + id: string | number; + icon: React.ReactElement; + label?: string; + onClick?: () => void; + href?: string; +}; + +const defaultNavItems: NavItem[] = [ + { id: 'default-home', icon: , label: 'Home' }, + { id: 'default-explore', icon: , label: 'Explore' }, + { id: 'default-notifications', icon: , label: 'Notifications' }, +]; + +type VerticalLimelightNavProps = { + items?: NavItem[]; + defaultActiveIndex?: number; + onTabChange?: (index: number) => void; + className?: string; + limelightClassName?: string; + iconContainerClassName?: string; + iconClassName?: string; + showLabels?: boolean; +}; + +/** + * A vertical navigation bar with a "limelight" effect that highlights the active item. + */ +export const VerticalLimelightNav = ({ + items = defaultNavItems, + defaultActiveIndex = 0, + onTabChange, + className, + limelightClassName, + iconContainerClassName, + iconClassName, + showLabels = true, +}: VerticalLimelightNavProps) => { + const [activeIndex, setActiveIndex] = useState(defaultActiveIndex); + const [isReady, setIsReady] = useState(false); + const navItemRefs = useRef<(HTMLAnchorElement | null)[]>([]); + const limelightRef = useRef(null); + + useLayoutEffect(() => { + if (items.length === 0) return; + + const limelight = limelightRef.current; + const activeItem = navItemRefs.current[activeIndex]; + + if (limelight && activeItem) { + const newTop = activeItem.offsetTop + activeItem.offsetHeight / 2 - limelight.offsetHeight / 2; + limelight.style.top = `${newTop}px`; + + if (!isReady) { + setTimeout(() => setIsReady(true), 50); + } + } + }, [activeIndex, isReady, items]); + + if (items.length === 0) { + return null; + } + + const handleItemClick = (index: number, item: NavItem) => { + setActiveIndex(index); + onTabChange?.(index); + item.onClick?.(); + }; + + return ( + + ); +}; + +export { VerticalLimelightNav as LimelightNav };