UNPKG

@xo-union/tk-component-header-nav

Version:
159 lines (156 loc) 5.93 kB
import _extends from "@babel/runtime-corejs3/helpers/esm/extends"; import _setTimeout from "@babel/runtime-corejs3/core-js/set-timeout"; import React from "react"; import PropTypes from "prop-types"; import Icon from "@xo-union/tk-component-icons"; import BlankButton from "@xo-union/component-blank-button"; import { Body2 } from "@xo-union/tk-ui-typography"; import FocusLock from "react-focus-lock"; import { useImageLazyLoadingBoundary } from "@xo-union/tk-component-picture"; import { compose } from "@xo-union/react-css-modules"; import cx from "classnames"; // eslint-disable-line no-restricted-imports import { FullBleedContainer, TopLevelContainer, Row, Column } from "@xo-union/tk-component-grid"; import { useOutsideClickEffect } from "@xo-union/ui-accessibility"; import validateLayout from "./validateLayout.js"; import { useHeaderNavContext } from "../Context.js"; import { MenuPopUp } from "./MenuSections.js"; const MegaMenuIDContext = /*#__PURE__*/React.createContext(); const MegaMenuLayoutContext = /*#__PURE__*/React.createContext(); const useMegaMenuLayout = () => React.useContext(MegaMenuLayoutContext); const useMegaMenuID = () => { const result = React.useContext(MegaMenuIDContext); if (!result) { throw new Error('`useMegaMenuID` must be used somewhere in the tree created by `MegaMenu`'); } return result; }; const BackToMainMenuButton = /*#__PURE__*/React.forwardRef((props, ref) => { const { classes } = useHeaderNavContext(); const menuID = useMegaMenuID(); return /*#__PURE__*/React.createElement("div", { className: classes['back-to-main-menu-container'] }, /*#__PURE__*/React.createElement(BlankButton, _extends({ ref: ref, className: classes['back-to-main-menu-button'], type: "button", "aria-controls": `header-${menuID}-mega-menu-popup` }, props), /*#__PURE__*/React.createElement(Body2, { className: cx(classes['back-to-main-menu-button-mobile-content'], classes['hide-in-desktop']) }, /*#__PURE__*/React.createElement(Icon, { size: "sm", name: "caret_left", "aria-hidden": "true" }), /*#__PURE__*/React.createElement("span", { className: classes['back-to-main-menu-button-text'] }, "Back to Main menu")), /*#__PURE__*/React.createElement("div", { className: classes['hide-in-mobile'] }, /*#__PURE__*/React.createElement(Icon, { size: "sm", name: "close", "aria-label": "close menu" })))); }); /* eslint-disable react/require-default-props */ const MegaMenu = _ref => { let { layout, children, menuID, containerProps } = _ref; const { classes, mobileMedia, navMenuSelectors, navMenuActions } = useHeaderNavContext(); const isActive = navMenuSelectors.isMenuActive(menuID); const backButtonRef = React.useRef(); const containerRef = React.useRef(); useImageLazyLoadingBoundary({ containerRef, enabled: isActive }); React.useEffect(() => { let timeout; if (isActive && mobileMedia.yes) { timeout = _setTimeout(() => { backButtonRef.current.focus(); }, 300); } return () => clearTimeout(timeout); }, [isActive]); // eslint-disable-line react-hooks/exhaustive-deps const menuIDClassName = `${menuID}-menu-container`; validateLayout(menuID, layout); return /*#__PURE__*/React.createElement(MegaMenuIDContext.Provider, { value: menuID }, /*#__PURE__*/React.createElement(MegaMenuLayoutContext.Provider, { value: layout }, /*#__PURE__*/React.createElement(FullBleedContainer, _extends({}, containerProps, { as: MenuPopUp, ref: containerRef, id: `header-${menuID}-mega-menu-popup`, "aria-labelledby": `header-${menuID}-mega-menu-heading`, classes: compose({ 'full-bleed-container': cx(classes['mega-menu-dropdown'], isActive && classes['is-active'], menuIDClassName in classes && classes[menuIDClassName]) }) }), /*#__PURE__*/React.createElement(TopLevelContainer, { classes: compose({ 'top-level-container': cx(classes['mega-menu-content']) }) }, /*#__PURE__*/React.createElement(Row, null, /*#__PURE__*/React.createElement(Column, { xs: "12" }, /*#__PURE__*/React.createElement(BackToMainMenuButton, { ref: backButtonRef, onClick: navMenuActions.closeMenu })), children))))); }; process.env.NODE_ENV !== "production" ? MegaMenu.propTypes = { children: PropTypes.node, menuID: PropTypes.string, containerProps: PropTypes.shape({}), layout: PropTypes.shape({ alignSectionsBreakpoint: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl', 'xxl']).isRequired, featuredSection: PropTypes.shape({ unitSize: PropTypes.oneOf(['sm', 'lg', 'xl']).isRequired, numberOfColumns: PropTypes.shape({ xs: PropTypes.oneOf([1, 2]), md: PropTypes.oneOf([2, 4]), lg: PropTypes.oneOf([1, 2]) }).isRequired }).isRequired, linkColumnProps: PropTypes.shape({}) }).isRequired } : void 0; const SharedMegaMenuContainer = _ref2 => { let { children } = _ref2; const { mobileMedia, navMenuActions, navMenuState, navMenuRefs } = useHeaderNavContext(); const isAnyMegaMenuOpenOnDesktop = navMenuState.isAnyMegaMenuOpen && mobileMedia.no; const focusLockEnabled = isAnyMegaMenuOpenOnDesktop && navMenuState.wasOpenedWithKeyboard; useOutsideClickEffect({ enabled: isAnyMegaMenuOpenOnDesktop, onClickOutside: () => { navMenuActions.closeLast({ returnFocus: false }); } }, [navMenuRefs.megaMenuContainerRef, navMenuRefs.getButtonRef()]); return /*#__PURE__*/React.createElement(FocusLock, { disabled: !focusLockEnabled, ref: navMenuRefs.megaMenuContainerRef }, children); }; /* eslint-enable react/require-default-props */ process.env.NODE_ENV !== "production" ? SharedMegaMenuContainer.propTypes = { children: PropTypes.node } : void 0; export { MegaMenu, SharedMegaMenuContainer, useMegaMenuID, useMegaMenuLayout };