import { FC, useCallback, useMemo, ReactNode } from "react"; import { Box, Collapse, Flex, forwardRef, HStack, Icon, Link, Menu, MenuButton, MenuItem, MenuList, Text, Stack, useColorModeValue, useDisclosure, Container, useBreakpointValue, } from "@chakra-ui/react"; import { intl } from "locale"; import { FiHome, FiSettings, FiUser, FiBook, FiLock, FiShield, FiMonitor, FiChevronDown, } from "react-icons/fi"; import { Link as RouterLink, useLocation } from "react-router-dom"; interface NavItem { /** Displayed label */ label: string; /** Icon shown before the label */ icon: ReactNode; /** Link where to navigate to */ to?: string; subItems?: { label: string; to: string }[]; } const navItems: NavItem[] = [ { label: intl.formatMessage({ id: "dashboard.title" }), icon: , to: "/", }, { label: intl.formatMessage({ id: "hosts.title" }), icon: , subItems: [ { label: intl.formatMessage({ id: "hosts.title" }), to: "/hosts", }, { label: intl.formatMessage({ id: "upstreams.title" }), to: "/upstreams", }, ], }, { label: intl.formatMessage({ id: "access-lists.title" }), icon: , to: "/access-lists", }, { label: intl.formatMessage({ id: "ssl.title" }), icon: , subItems: [ { label: intl.formatMessage({ id: "certificates.title" }), to: "/ssl/certificates", }, { label: intl.formatMessage({ id: "certificate-authorities.title" }), to: "/ssl/authorities", }, { label: intl.formatMessage({ id: "dns-providers.title" }), to: "/ssl/dns-providers", }, ], }, { label: intl.formatMessage({ id: "audit-log.title" }), icon: , to: "/audit-log", }, { label: intl.formatMessage({ id: "users.title" }), icon: , to: "/users", }, { label: intl.formatMessage({ id: "settings.title" }), icon: , subItems: [ { label: intl.formatMessage({ id: "general-settings.title" }), to: "/settings/general", }, { label: intl.formatMessage({ id: "nginx-templates.title" }), to: "/settings/nginx-templates", }, ], }, ]; interface NavigationMenuProps { /** Navigation is currently hidden on mobile */ mobileNavIsOpen: boolean; closeMobileNav: () => void; } function NavigationMenu({ mobileNavIsOpen, closeMobileNav, }: NavigationMenuProps) { const isMobile = useBreakpointValue({ base: true, md: false }); return ( <> {isMobile ? ( ) : ( )} ); } /** Single tab element for desktop navigation */ type NavTabProps = Omit & { active?: boolean }; const NavTab = forwardRef( ({ label, icon, to, active, ...props }, ref) => { const linkColor = useColorModeValue("gray.500", "gray.200"); const linkHoverColor = useColorModeValue("gray.900", "white"); return ( {icon} {label} ); }, ); const DesktopNavigation: FC = () => { const path = useLocation().pathname; const activeNavItemIndex = useMemo( () => navItems.findIndex((item) => { // Find the nav item whose location / sub items location is the beginning of the currently active path if (item.to) { if (item.to === "/") { return path === item.to; } return path.startsWith(item.to !== "" ? item.to : "/dashboard"); } else if (item.subItems) { return item.subItems.some((subItem) => path.startsWith(subItem.to)); } return false; }), [path], ); return ( {navItems.map((navItem, index) => { const { subItems, ...propsWithoutSubItems } = navItem; const additionalProps: Partial = {}; if (index === activeNavItemIndex) { additionalProps["active"] = true; } if (subItems) { return ( {subItems && ( {subItems.map((item, subIndex) => ( {item.label} ))} )} ); } else { return ( ); } })} ); }; const MobileNavigation: FC> = ({ closeMobileNav, }) => { return ( {navItems.map((navItem, index) => ( ))} ); }; const MobileNavItem: FC< NavItem & { index: number; closeMobileNav: NavigationMenuProps["closeMobileNav"]; } > = ({ closeMobileNav, ...props }) => { const { isOpen, onToggle } = useDisclosure(); const onClickHandler = useCallback(() => { if (props.subItems) { // Toggle accordeon onToggle(); } else { // Close menu on navigate closeMobileNav(); } }, [closeMobileNav, onToggle, props.subItems]); return ( {props.icon} {props.label} {props.subItems && ( )} {props.subItems && props.subItems.map((subItem, subIndex) => ( {subItem.label} ))} ); }; export { NavigationMenu };