@kadconsulting/dry
Version:
KAD Reusable Component Library
154 lines • 11.7 kB
JavaScript
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