@xo-union/tk-component-header-nav
Version:
159 lines (156 loc) • 5.93 kB
JavaScript
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 };