UNPKG

@massds/mayflower-react

Version:

React versions of Mayflower design system UI components

312 lines (288 loc) 12.2 kB
import React from "react"; import propTypes from "prop-types"; import classNames from "classnames"; import NavContainer from "../NavContainer/index.mjs"; import SiteLogo from "../SiteLogo/index.mjs"; import ButtonWithIcon from "../ButtonWithIcon/index.mjs"; import IconSearch from "../Icon/IconSearch.mjs"; import { HeaderMainNav, HeaderNavItem } from "../HeaderNav/main-nav.mjs"; import { HamburgerNavSearch } from "../HamburgerNav/index.mjs"; import useWindowWidth from "../hooks/use-window-width.mjs"; import getFallbackComponent from "../utilities/getFallbackComponent.mjs"; const HeaderNav = (_ref) => { let UtilityNav = _ref.UtilityNav, UtilityItem = _ref.UtilityItem, MainNav = _ref.MainNav, NavItem = _ref.NavItem, Logo = _ref.Logo, NavSearch = _ref.NavSearch, ButtonContainer = _ref.ButtonContainer, _ref$mainItems = _ref.mainItems, mainItems = _ref$mainItems === void 0 ? [] : _ref$mainItems, _ref$utilityItems = _ref.utilityItems, utilityItems = _ref$utilityItems === void 0 ? [] : _ref$utilityItems; const RenderedMainNav = getFallbackComponent(MainNav, HeaderMainNav); const RenderedNavSearch = getFallbackComponent(NavSearch, HeaderNavSearch); const RenderedUtilityNav = getFallbackComponent(UtilityNav, HeaderUtilityNav); const RenderedUtilityItem = getFallbackComponent(UtilityItem, HeaderUtilityItem); const RenderedNavItem = getFallbackComponent(NavItem, HeaderNavItem); const RenderedLogo = getFallbackComponent(Logo, HeaderLogo); const RenderedButtonContainer = getFallbackComponent(ButtonContainer, HeaderButtonContainer); const utilityNav = RenderedUtilityNav !== null && utilityItems.length > 0 ? /*#__PURE__*/React.createElement(RenderedUtilityNav, { UtilityItem: RenderedUtilityItem, items: utilityItems, narrow: true }) : null; const mainNav = RenderedMainNav !== null && mainItems.length > 0 ? /*#__PURE__*/React.createElement(RenderedMainNav, { NavItem: RenderedNavItem, items: mainItems }) : null; const logo = RenderedLogo !== null ? /*#__PURE__*/React.createElement(RenderedLogo, null) : null; const navSearch = RenderedNavSearch !== null ? /*#__PURE__*/React.createElement(RenderedNavSearch, { narrow: true }) : null; const buttonContainer = RenderedButtonContainer !== null ? /*#__PURE__*/React.createElement(RenderedButtonContainer, null) : null; return /*#__PURE__*/React.createElement("nav", { className: "ma__header__nav", "aria-label": "main navigation", id: "header-main-navigation", role: "navigation" }, buttonContainer, /*#__PURE__*/React.createElement(NavContainer, { navSearch: navSearch, logo: logo, mainNav: mainNav, utilityNav: utilityNav, className: "ma__header__nav-container" })); }; HeaderNav.propTypes = process.env.NODE_ENV !== "production" ? { /** An uninstantiated component which handles displaying the utility nav. */ UtilityNav: propTypes.elementType, /** An uninstantiated component which handles displaying individual items within the utility nav. */ UtilityItem: propTypes.elementType, /** An uninstantiated component which handles displaying menu portion of the header. */ MainNav: propTypes.elementType, /** An uninstantiated component which handles displaying individual menu items within the menu. */ NavItem: propTypes.elementType, /** An uninstantiated component which handles displaying the site logo. */ Logo: propTypes.elementType, /** An uninstantiated component which handles search functionality. */ NavSearch: propTypes.elementType, /** An uninstantiated component which handles displaying the menu button on mobile. */ ButtonContainer: propTypes.elementType, /** An array of items used to create the menu. */ mainItems: propTypes.arrayOf(propTypes.shape({ href: propTypes.string, text: propTypes.string, // Active main nav item eccentuated with an styled underline active: propTypes.bool, subNav: propTypes.arrayOf(propTypes.shape({ href: propTypes.string, text: propTypes.string })) })), /** An array of uninstantiated components to render within the utility navigation. */ utilityItems: propTypes.arrayOf(propTypes.elementType) } : {}; export const HeaderButtonContainer = () => { const menuButtonRef = React.useRef(); const closeButtonRef = React.useRef(); const windowWidth = useWindowWidth(); const hide = React.useCallback($content => { const body = document.querySelector('body'); const mainNav = document.querySelector('.ma__main-nav__items'); const submenuClass = 'show-submenu'; const openClass = 'is-open'; const closeClass = 'is-closed'; const breakpoint = 840; body.classList.remove(submenuClass); const open = mainNav.querySelector("." + openClass); if (open) { open.classList.remove(openClass); } if (windowWidth <= breakpoint) { $content.classList.add(closeClass); } else { // @todo animate here! $content.classList.add(closeClass); } }, [windowWidth]); React.useEffect(() => { const menuButton = menuButtonRef.current; const closeButton = closeButtonRef.current; const openClass = 'is-open'; const mainNav = document.querySelector('.ma__main-nav__items'); const toggleMobileMenu = () => { const body = document.querySelector('body'); if (body.classList.contains('show-menu')) { body.classList.remove('show-menu'); } else { body.classList.add('show-menu'); } }; if (menuButton) { menuButton.addEventListener('click', () => { const $openContent = mainNav.querySelector(".js-main-nav-content." + openClass); if ($openContent) { hide($openContent); } document.querySelector('.ma__utility-nav__content').classList.add('is-closed'); if (windowWidth < 841 && document.querySelector('.ma__header')) { toggleMobileMenu(); } }); } if (closeButton) { closeButton.addEventListener('click', () => { const $openContent = mainNav.querySelector(".js-main-nav-content." + openClass); if ($openContent) { hide($openContent); } }); } }, [windowWidth, menuButtonRef, closeButtonRef]); return /*#__PURE__*/React.createElement("div", { className: "ma__header__button-container js-sticky-header" }, /*#__PURE__*/React.createElement("button", { type: "button", ref: closeButtonRef, className: "ma__header__back-button js-close-sub-nav" }, /*#__PURE__*/React.createElement("span", null, "Back")), /*#__PURE__*/React.createElement("button", { type: "button", ref: menuButtonRef, className: "ma__header__menu-button js-header-menu-button" }, /*#__PURE__*/React.createElement("span", null, "Menu"), /*#__PURE__*/React.createElement("span", { className: "ma__header__menu-icon" }))); }; export const HeaderLogo = () => { const logoProps = { url: { domain: 'https://www.mass.gov/' }, image: { src: 'https://unpkg.com/@massds/mayflower-assets/static/images/logo/stateseal.png', alt: 'Massachusetts state seal', width: 45, height: 45 }, siteName: 'Mass.gov', title: 'Mass.gov homepage', Wrapper: (_ref2) => { let children = _ref2.children; return /*#__PURE__*/React.createElement("div", { className: "ma__header__logo" }, children); } }; return /*#__PURE__*/React.createElement(SiteLogo, logoProps); }; export const HeaderNavSearch = (_ref3) => { let _ref3$narrow = _ref3.narrow, narrow = _ref3$narrow === void 0 ? false : _ref3$narrow; const classes = classNames({ 'ma__header__nav-search': narrow, 'ma__header__search js-header-search-menu': !narrow }); return /*#__PURE__*/React.createElement("div", { className: classes }, /*#__PURE__*/React.createElement("div", { className: "ma__header-search" }, /*#__PURE__*/React.createElement("form", { action: "#", className: "ma__form js-header-search-form", role: "search" }, /*#__PURE__*/React.createElement("label", { htmlFor: "header-search", className: "ma__header-search__label" }, "Search terms"), /*#__PURE__*/React.createElement("input", { id: "header-search", className: "ma__header-search__input", placeholder: "Search Mass.gov", type: "search", inputMode: "search" }), /*#__PURE__*/React.createElement(ButtonWithIcon, { usage: "secondary", icon: /*#__PURE__*/React.createElement(IconSearch, null) }, "Search")))); }; // For whatever reason, Header has a different nav search for mobile. export const HeaderMobileNavSearch = () => /*#__PURE__*/React.createElement("div", { className: "ma__header__nav-search js-header__nav-search" }, /*#__PURE__*/React.createElement("div", { className: "ma__header-search" }, /*#__PURE__*/React.createElement("form", { action: "#", className: "ma__form js-header-search-form", role: "search" }, /*#__PURE__*/React.createElement("label", { htmlFor: "header-mobile-search", className: "ma__header-search__label" }, "Search terms"), /*#__PURE__*/React.createElement("input", { id: "header-mobile-search", className: "ma__header-search__input", placeholder: "Search Mass.gov", type: "search", inputMode: "search" }), /*#__PURE__*/React.createElement(ButtonWithIcon, { usage: "secondary", icon: /*#__PURE__*/React.createElement(IconSearch, null) }, "Search")))); export const HeaderUtilityItem = (_ref4) => { let children = _ref4.children; return /*#__PURE__*/React.createElement("li", { className: "ma__utility-nav__item" }, children); }; HeaderUtilityItem.propTypes = process.env.NODE_ENV !== "production" ? { children: propTypes.node } : {}; export const HeaderUtilityNav = (_ref5) => { let UtilityItem = _ref5.UtilityItem, _ref5$items = _ref5.items, items = _ref5$items === void 0 ? [] : _ref5$items, _ref5$narrow = _ref5.narrow, narrow = _ref5$narrow === void 0 ? true : _ref5$narrow; const RenderedUtilityItem = getFallbackComponent(UtilityItem, HeaderUtilityItem); const classes = classNames('ma__header__utility-nav', { 'ma__header__utility-nav--narrow': narrow, 'ma__header__utility-nav--wide': !narrow }); return /*#__PURE__*/React.createElement("div", { className: classes }, /*#__PURE__*/React.createElement("div", { className: "ma__utility-nav js-util-nav" }, /*#__PURE__*/React.createElement("ul", { className: "ma__utility-nav__items" }, items.map((ItemComponent, index) => /*#__PURE__*/ // eslint-disable-next-line react/no-array-index-key React.createElement(RenderedUtilityItem, { key: "header-utility-item-" + index }, /*#__PURE__*/React.createElement(ItemComponent, { narrow: narrow })))))); }; HeaderUtilityNav.propTypes = process.env.NODE_ENV !== "production" ? { /** An uninstantiated component which handles displaying individual items within the utility nav. */ UtilityItem: propTypes.elementType, /** An array of uninstantiated components to render within the utility navigation. */ items: propTypes.arrayOf(propTypes.elementType), /** A boolean representing when the UtilityNav is being displayed within a narrow screen. */ narrow: propTypes.bool } : {}; export const HeaderContainer = (_ref6) => { let Logo = _ref6.Logo, NavSearch = _ref6.NavSearch; const RenderedLogo = Logo !== null ? Logo : null; const RenderedNavSearch = getFallbackComponent(NavSearch, HamburgerNavSearch); return /*#__PURE__*/React.createElement("div", { className: "ma__header__container" }, RenderedLogo !== null && /*#__PURE__*/React.createElement(RenderedLogo, null), RenderedNavSearch !== null && /*#__PURE__*/React.createElement(RenderedNavSearch, null)); }; HeaderContainer.propTypes = process.env.NODE_ENV !== "production" ? { /** An uninstantiated component which handles displaying the site logo. */ Logo: propTypes.elementType, /** An uninstantiated component which handles search functionality. */ NavSearch: propTypes.elementType } : {}; export default HeaderNav;