@massds/mayflower-react
Version:
React versions of Mayflower design system UI components
312 lines (288 loc) • 12.2 kB
JavaScript
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;