UNPKG

@kadconsulting/dry

Version:
154 lines 11.7 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { forwardRef, useCallback, useState, useMemo, useEffect } from 'react'; import { Link } from '../@primitives'; import { ChevronDown, ChevronUp, XClose, Menu01 } from '../Icons/paths'; import classnames from 'classnames'; import Icon from '../Icons/Icon/Icon'; import { IconSizes } from '../Icons/Icon/IconTypes'; import Dropdown from '../Dropdown'; import DesktopSubNavigationContent from './DesktopSubNavigationContent'; import TabletSubNavigationContent from './TabletSubNavigationContent'; import MobileNavContainer from './MobileNavContainer'; import MobileNavContent from './MobileNavContent'; import VerticalNavContent from './VerticalNavContent'; import { Button } from '../Button'; import Logo from '../Logo'; import './NavBar.scss'; import useWindowSize from '../../hooks/useWindowSize'; export const NAV_TABLET_BREAKPOINT = 834; export const NAV_DESKTOP_BREAKPOINT = 1300; const useDropdown = ({ tabletBreakpoint, desktopBreakpoint, }) => { const [openDropdown, setOpenDropdown] = useState(null); const [navOpen, setNavOpen] = useState(false); const toggleDropdown = (route) => { setOpenDropdown((prevRoute) => (prevRoute === route ? null : route)); }; const currentPath = window?.location?.pathname; const closeDropdown = () => setOpenDropdown(null); const { width } = useWindowSize(); /** useLayout hook uses scss-standardized breakpoints, while this component's layout needs tailored breakpoints */ const desktop = useMemo(() => width >= desktopBreakpoint, [width]); const tablet = useMemo(() => width >= tabletBreakpoint && width < desktopBreakpoint, [width]); const mobile = useMemo(() => width < tabletBreakpoint, [width]); /** Close the nav if the viewport changes to tablet or desktop */ useEffect(() => { if (!mobile) setNavOpen(false); }, [mobile]); /** Close the dropdown if the viewport size changes */ useEffect(() => { setOpenDropdown(null); }, [desktop, tablet, mobile]); /** Close the nav if the route changes, so that the user can see the new page */ useEffect(() => { setNavOpen(false); }, [currentPath]); return { openDropdown, toggleDropdown, closeDropdown, desktop, tablet, mobile, setNavOpen, navOpen, }; }; const NavBar = forwardRef(({ navRoutes, logoProps, topText, subText, setLoading, orientation = 'horizontal', tabletBreakpoint = NAV_TABLET_BREAKPOINT, desktopBreakpoint = NAV_DESKTOP_BREAKPOINT, contentClassName = '', mainClassName, loading, ...props }, ref) => { const { openDropdown, toggleDropdown, closeDropdown, desktop, tablet, mobile, setNavOpen, navOpen, } = useDropdown({ tabletBreakpoint, desktopBreakpoint, }); const [isLoading, setIsLoading] = useState(loading || false); const handleNavigation = useCallback(() => { setIsLoading(true); if (setLoading) setLoading(true); }, [setLoading]); const navRoutesWithInThumbBar = useMemo(() => { return Object.values(navRoutes)?.reduce((acc, route) => { if (route.inThumbBar) { acc = acc += 1; } return acc; }, 0); }, [navRoutes]); const SubNavigationContent = useCallback((route, routeObj) => { if (!routeObj.subNavigation) return null; return (_jsx(_Fragment, { children: openDropdown === route && (_jsxs(Dropdown, { className: 'nav-bar-link__dropdown', onClose: closeDropdown, children: [tablet && (_jsx(TabletSubNavigationContent, { subNavigation: navRoutes[route].subNavigation })), desktop && (_jsx(DesktopSubNavigationContent, { subNavigation: navRoutes[route].subNavigation }))] })) })); }, [desktop, navRoutes, openDropdown, orientation, tablet]); const renderNavLink = (routeDetails, route) => { const { buttonType } = routeDetails; if (routeDetails.subNavigation) { return (_jsxs("div", { className: 'nav-bar-link__dropdown-container', onClick: () => toggleDropdown(route), children: [_jsx(Button, { className: 'nav-bar-link', children: routeDetails?.noRoute ? (_jsx("span", { className: 'nav-bar-link', children: routeDetails?.name })) : (_jsx(Link, { onClick: handleNavigation, className: 'nav-bar-link', href: routeDetails.path ?? '/', value: routeDetails.name })) }), SubNavigationContent(route, routeDetails), openDropdown !== route && (_jsx(Icon, { color: '#8738b5', Path: ChevronDown, size: IconSizes.SMALL })), openDropdown === route && (_jsx(Icon, { color: '#8738b5', Path: ChevronUp, size: IconSizes.SMALL }))] })); } else if (buttonType) { return (_jsx(Button, { buttonType: buttonType, onClick: handleNavigation, children: _jsx("a", { className: 'nav-bar__buttonLink', href: routeDetails.path ?? '/', children: routeDetails.name }) })); } else { return (_jsx(Link, { onClick: handleNavigation, className: 'nav-bar-link', href: routeDetails.path ?? '/', value: routeDetails.name })); } }; const renderSkeletonNavLinks = () => { return (_jsx(_Fragment, { children: [...Array(10)].map((_, index) => (_jsx("div", { className: 'nav-loading__nav-link-skeleton', children: _jsx("div", { className: 'nav-loading__skeleton-text' }) }, index))) })); }; const displayNavLinks = useMemo(() => (_jsx("nav", { className: `nav-bar-links ${orientation}`, children: isLoading ? renderSkeletonNavLinks() : Object.keys(navRoutes).map((route) => { const routeDetails = navRoutes[route]; if (!routeDetails.visibleInNav) return null; return (_jsxs("div", { className: 'nav-bar-link-container', style: { position: 'relative' }, children: [routeDetails.icon && routeDetails.icon, _jsxs("div", { className: classnames('nav-bar-link-container__infoWrapper', routeDetails.infoWrapperClassName && routeDetails.infoWrapperClassName), children: [renderNavLink(routeDetails, route), routeDetails?.children && routeDetails?.children] })] }, route)); }) })), [ navRoutes, SubNavigationContent, openDropdown, orientation, handleNavigation, isLoading, ]); return (_jsxs("section", { ...props, className: classnames({ nav: true, [orientation]: true, 'horizontal-nav': orientation === 'horizontal', 'vertical-nav': orientation === 'vertical', [mainClassName ?? '']: true, 'nav-loading': isLoading, }), ref: ref, children: [orientation === 'horizontal' && (_jsxs(_Fragment, { children: [_jsx("div", { className: `nav-bar ${orientation}`, children: _jsxs("div", { className: classnames({ 'nav-bar-content': true, [orientation]: true, 'nav-bar-content-mobile': mobile, [contentClassName]: true, }), children: [((mobile && logoProps?.inThumbBar) || !mobile) && (_jsx(Link, { onClick: handleNavigation, className: 'nav-bar-logo', href: navRoutes.home?.path ?? '/', children: _jsx(Logo, { ...logoProps, isnextjs: props.isnextjs }) })), mobile && (_jsx(_Fragment, { children: Object.values(navRoutes)?.reduce((acc, route) => { if (route.inThumbBar) { acc.push(_jsxs("div", { className: 'nav-bar-link-container-mobile', style: { position: 'relative', // The +1 is to account for the button that toggles the nav width: `${100 / (navRoutesWithInThumbBar + 1)}%`, }, children: [route.icon && (_jsx(Link, { onClick: handleNavigation, href: route.path ?? '/', value: route.icon })), _jsx(Link, { onClick: handleNavigation, className: 'nav-bar-link-container-mobile__link', href: route.path ?? '/', value: route.name })] }, `route-${route.name} - ${route}`)); } return acc; }, []) })), _jsxs("nav", { className: `nav-bar-links ${orientation}`, children: [desktop && displayNavLinks, mobile && (_jsx(_Fragment, { children: _jsx(Button, { onClick: () => setNavOpen(!navOpen), buttonType: 'icon', children: navOpen ? (_jsx(Icon, { color: 'light-contrast', Path: XClose, size: IconSizes.SMALL })) : (_jsx(Icon, { color: 'light-contrast', Path: Menu01, size: IconSizes.SMALL })) }) }))] }), tablet && (_jsx("nav", { className: `tablet-nav-links ${orientation}`, children: displayNavLinks }))] }) }), mobile && (_jsx(MobileNavContainer, { open: navOpen, children: _jsx(MobileNavContent, { navRoutes: navRoutes, subText: subText, topText: topText }) }))] })), orientation === 'vertical' && (_jsxs(_Fragment, { children: [_jsx("div", { className: `nav-bar ${orientation}`, children: _jsxs("div", { className: classnames({ 'nav-bar-content': true, [orientation]: true, 'vertical-wrapper__nav-bar2': !mobile, 'vertical-wrapper__nav-bar1': mobile && !navOpen, [contentClassName]: true, }), children: [_jsx(Link, { onClick: handleNavigation, className: 'nav-bar-logo', href: navRoutes.home?.path ?? '/', children: _jsx(Logo, { ...logoProps, isnextjs: props.isnextjs }) }), mobile && (_jsx(_Fragment, { children: Object.values(navRoutes)?.reduce((acc, route) => { if (route.inThumbBar) { acc.push(_jsxs("div", { className: 'nav-bar-link-container-mobile', style: { position: 'relative', // The +1 is to account for the button that toggles the nav width: `${100 / (navRoutesWithInThumbBar + 1)}%`, }, children: [route.icon && (_jsx(Link, { onClick: handleNavigation, href: route.path ?? '/', value: route.icon })), _jsx(Link, { onClick: handleNavigation, className: 'nav-bar-link-container-mobile__link', href: route.path ?? '/', value: route.name })] }, `route-${route.name} - ${route}`)); } return acc; }, []) })), _jsxs("nav", { className: classnames({ 'nav-bar-links': true, [orientation]: true, }), children: [!mobile && _jsx(VerticalNavContent, { navRoutes: navRoutes }), mobile && (_jsx(_Fragment, { children: _jsx(Button, { onClick: () => setNavOpen(!navOpen), buttonType: 'icon', width: 'fit-content', children: navOpen ? (_jsx(Icon, { color: 'light-contrast', Path: XClose, size: IconSizes.SMALL })) : (_jsx(Icon, { color: 'light-contrast', Path: Menu01, size: IconSizes.SMALL })) }) }))] })] }) }), mobile && (_jsx(MobileNavContainer, { open: navOpen, children: _jsx(VerticalNavContent, { navRoutes: navRoutes }) }))] }))] })); }); export default NavBar; //# sourceMappingURL=NavBar.js.map