UNPKG

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

Version:
322 lines • 15.2 kB
import React from "react"; import PropTypes from "prop-types"; import noop from "lodash/noop"; import { useClasses, useClassesMapper, compose } from "@xo-union/react-css-modules"; import classesPropType from "@xo-union/classes-prop-type"; import { useMobileMedia } from "@xo-union/tk-ui-breakpoints"; import { ThemeProvider as ButtonThemeProvider } from "@xo-union/tk-component-buttons"; import { PillBadge } from "@xo-union/tk-component-badges"; import { useGlobalKeyboardEscapeEffect, useNoScrollEffect } from "@xo-union/ui-accessibility"; import cx from "classnames"; import FocusLock from "react-focus-lock"; import useNavigationMenus from "./useNavigationMenus.js"; import { Provider } from "./Context.js"; import TopLevelNav from "./TopLevelNav/index.js"; import TopLevelButton, { TopLevelLink } from "./TopLevelButton/index.js"; import TheKnotLogo from "./TheKnotLogo/index.js"; import TopLevelList from "./TopLevelList/index.js"; import TopLevelAside, { TopLevelAsideLink } from "./TopLevelAside/index.js"; import TopLevelMessageBar from "./TopLevelMessageBar/index.js"; import { IdeasAdviceMegaMenu, InvitationsMegaMenu, PlanningToolsMegaMenu, RegistryMegaMenu, AttireRingsMegaMenu, VendorsMegaMenu, WeddingWebsiteMegaMenu } from "./MegaMenus/index.js"; import AccountSettingsMenu from "./AccountSettings/index.js"; import { AccountCTAsContainer, SignUpButton, LogInButton, LogOutButton } from "./AccountCTAs/index.js"; import Avatar from "./Avatar/index.js"; import HamburgerButton from "./HamburgerButton/index.js"; import ConversationsIcon from "./ConversationsIcon/index.js"; import Overlay from "./Overlay/index.js"; import useAnimationEnabled from "./useAnimationEnabled.js"; import styles from "./styles.module.css"; import imageIds from "./constants/imageIds/index.js"; import shouldShowPrideLogo from "../../utils/shouldShowPrideLogo/index.js"; import useHeaderClickTracker from "./useHeaderClickTracker.js"; import useWeddingVisionData from "./useWeddingVisionData.js"; import useWeddingWebsiteData from "./useWeddingWebsiteData.js"; import isRequiredAndOneOfOrNull from "../../utils/isRequiredAndOneOfOrNull.js"; import ResizeObserverNav from "./ResizeObserverNav/index.js"; import { SharedMegaMenuContainer } from "./MegaMenu/index.js"; import { menuTypes } from "./types.js"; import { provideExperiments } from "./Experiments/index.js"; import { FavoritesLink } from "./FavoritesLink/index.js"; import { useDefaultProps } from "./useDefaultProps.js"; const HeaderNav = props => { var _membership$data, _membership$data2, _membership$data2$mem, _membership$data3; const mainMenuID = 'header-main-menu'; const mainMenuHeadingID = 'header-main-menu-heading'; const { membership, showRewardsBar, loggedIn = !!((_membership$data = membership.data) !== null && _membership$data !== void 0 && _membership$data.member), couplePhotoUrl = (_membership$data2 = membership.data) === null || _membership$data2 === void 0 ? void 0 : (_membership$data2$mem = _membership$data2.member) === null || _membership$data2$mem === void 0 ? void 0 : _membership$data2$mem.couple_photo_url, memberIsLoading = (_membership$data3 = membership.data) === null || _membership$data3 === void 0 ? void 0 : _membership$data3.isLoading, analyticsProps, activeProduct, prideLogo = shouldShowPrideLogo(), onClickLogOut = membership.actions.logOut, onClickLogIn = noop, onClickSignUp = noop, unreadMessagesCount, classes: propsClasses, favorites, links, __openOnLoad__ = null, __weddingVisionData__, __weddingWebsiteData__, __renderDraftsLink__, __planningToolsFeaturedElements__ = 3 } = useDefaultProps(props); const handleAnalyticsClick = useHeaderClickTracker(analyticsProps); const animationEnabled = useAnimationEnabled(); const classes = useClasses(styles, propsClasses); const buttonClasses = useClassesMapper(compose({ btn: classes['btn-override'], 'animated-element': classes['btn-override-animated-element'] }), propsClasses); const mobileMedia = useMobileMedia(); const [navMenuState, navMenuActions, navMenuSelectors, navMenuRefs] = useNavigationMenus({ mobileMedia, activeProduct, __openOnLoad__ }); useNoScrollEffect({ enabled: navMenuState.isHamburgerOpenInMobile }); useGlobalKeyboardEscapeEffect({ enabled: navMenuState.isHamburgerOpenInMobile || navMenuState.isAnyMenuOpen, onEscapeKey: evt => { evt.stopImmediatePropagation(); navMenuActions.closeLast(); } }); const [weddingVisionData, fetchWeddingVisionData] = useWeddingVisionData(__weddingVisionData__); const [weddingWebsiteData, fetchWeddingWebsiteData] = useWeddingWebsiteData(__weddingWebsiteData__); const closeHamburgerOnClick = handler => event => { if (navMenuState.isHamburgerOpen) { navMenuActions.closeHamburger(); } handler(event); }; const fetchAppData = function () { let productToFetch = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : activeProduct; if (productToFetch === 'planning-tools') { fetchWeddingVisionData(loggedIn); } if (productToFetch === 'wedding-website') { fetchWeddingWebsiteData(loggedIn); } }; const context = { mainMenuID, mainMenuHeadingID, classes, mobileMedia, navMenuActions, navMenuSelectors, navMenuState, navMenuRefs, // Move to membershipData object? loggedIn, weddingWebsiteData, weddingVisionData, fetchAppData, memberIsLoading, menuTypes, links, imageIds, prideLogo, activeProduct, __renderDraftsLink__, __planningToolsFeaturedElements__ }; return /*#__PURE__*/React.createElement(Provider, { value: context }, /*#__PURE__*/React.createElement(TopLevelMessageBar, { isEnabled: showRewardsBar }), /*#__PURE__*/React.createElement(ButtonThemeProvider, { classes: buttonClasses }, /*#__PURE__*/React.createElement(FocusLock, { disabled: !navMenuState.isHamburgerOpenInMobile, autoFocus: false, returnFocus: true }, /*#__PURE__*/React.createElement(ResizeObserverNav, { "aria-label": "Header", className: cx(classes['nav-container'], !navMenuState.isHamburgerOpen && classes['hamburger-is-closed'], navMenuState.isAnyMegaMenuOpen && classes['mega-menu-is-open'], !animationEnabled && classes['no-animation'], loggedIn && classes['is-logged-in'], memberIsLoading && classes['member-is-loading']), isHamburgerOpenInMobile: navMenuState.isHamburgerOpenInMobile, onClick: handleAnalyticsClick }, /*#__PURE__*/React.createElement(HamburgerButton, null), /*#__PURE__*/React.createElement(Overlay, null, /*#__PURE__*/React.createElement("div", { className: classes['top-level-nav-container'] }, /*#__PURE__*/React.createElement(TopLevelNav, { headingContent: "Main menu" }, /*#__PURE__*/React.createElement(TheKnotLogo, { className: classes['hide-in-mobile'] }), /*#__PURE__*/React.createElement(TopLevelList, null, /*#__PURE__*/React.createElement(TopLevelButton, { menuID: menuTypes.PLANNING_TOOLS }, "Planning Tools"), /*#__PURE__*/React.createElement(TopLevelButton, { menuID: menuTypes.VENDORS }, "Vendors"), /*#__PURE__*/React.createElement(TopLevelButton, { menuID: menuTypes.WEDDING_WEBSITE }, "Wedding Website"), /*#__PURE__*/React.createElement(TopLevelButton, { menuID: menuTypes.INVITATIONS }, "Invitations"), /*#__PURE__*/React.createElement(TopLevelButton, { menuID: menuTypes.REGISTRY }, "Registry"), /*#__PURE__*/React.createElement(TopLevelButton, { menuID: menuTypes.ATTIRE_RINGS }, "Attire & Rings"), /*#__PURE__*/React.createElement(TopLevelButton, { menuID: menuTypes.IDEAS_ADVICE }, "Ideas & Advice"), /*#__PURE__*/React.createElement(TopLevelLink, { href: "https://weddingshop.theknot.com/?itm_source=theknot.com&itm_medium=referral&itm_campaign=TKmainnav" }, "Gifts & Favors")), /*#__PURE__*/React.createElement(TopLevelAside, null, loggedIn ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", { className: cx(classes['account-container'], classes['hide-in-mobile']) }, favorites !== false && /*#__PURE__*/React.createElement(FavoritesLink, favorites), /*#__PURE__*/React.createElement(ConversationsIcon, { unreadMessagesCount: unreadMessagesCount || 0 }), /*#__PURE__*/React.createElement(TopLevelButton, { menuID: menuTypes.ACCOUNT_SETTINGS }, /*#__PURE__*/React.createElement(PillBadge, { className: classes['account-settings-button'] }, /*#__PURE__*/React.createElement(Avatar, { couplePhotoUrl: couplePhotoUrl }), "Your account"))), /*#__PURE__*/React.createElement(AccountSettingsMenu, { menuID: menuTypes.ACCOUNT_SETTINGS, headingContent: "Account Settings", couplePhotoUrl: couplePhotoUrl }, /*#__PURE__*/React.createElement(AccountSettingsMenu.SubMenuLink, { href: links.WEDDING_SETTINGS }, "Account settings"), /*#__PURE__*/React.createElement(AccountSettingsMenu.SubMenuLink, { href: "https://theknot.zendesk.com" }, "Customer service"), /*#__PURE__*/React.createElement(LogOutButton, { onClick: onClickLogOut }))) : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", { className: classes['top-level-nav-right-aside-link-container'] }, /*#__PURE__*/React.createElement(TopLevelAsideLink, { iconName: "guests_selected", href: links.FIND_A_COUPLES_REGISTRY, hideLinkInDesktop: true }, "Find a couple"), /*#__PURE__*/React.createElement(TopLevelAsideLink, { className: classes['hide-in-desktop'], href: "https://helpcenter.theknot.com", hideLinkInDesktop: true }, "Customer service")), /*#__PURE__*/React.createElement(AccountCTAsContainer, null, /*#__PURE__*/React.createElement(LogInButton, { onClick: closeHamburgerOnClick(onClickLogIn), size: "sm", hideLoadingInMobile: true }), /*#__PURE__*/React.createElement(SignUpButton, { onClick: closeHamburgerOnClick(onClickSignUp), size: "sm", hideLoadingInMobile: true }))))), /*#__PURE__*/React.createElement(SharedMegaMenuContainer, null, /*#__PURE__*/React.createElement(PlanningToolsMegaMenu, null), /*#__PURE__*/React.createElement(VendorsMegaMenu, null), /*#__PURE__*/React.createElement(WeddingWebsiteMegaMenu, null), /*#__PURE__*/React.createElement(InvitationsMegaMenu, null), /*#__PURE__*/React.createElement(RegistryMegaMenu, null), /*#__PURE__*/React.createElement(AttireRingsMegaMenu, null), /*#__PURE__*/React.createElement(IdeasAdviceMegaMenu, null)))), /*#__PURE__*/React.createElement("div", { className: classes['mobile-app-bar-items'], ref: navMenuRefs.mobileAppBarItemsRef }, /*#__PURE__*/React.createElement(TheKnotLogo, null), /*#__PURE__*/React.createElement(AccountCTAsContainer, null, loggedIn ? /*#__PURE__*/React.createElement(React.Fragment, null, favorites !== false && /*#__PURE__*/React.createElement(FavoritesLink, favorites), /*#__PURE__*/React.createElement(ConversationsIcon, { unreadMessagesCount: unreadMessagesCount || 0 }), /*#__PURE__*/React.createElement("a", { href: links.ACCOUNT_SETTINGS, "aria-label": "Your account", title: "Your account" }, /*#__PURE__*/React.createElement(Avatar, { couplePhotoUrl: couplePhotoUrl }))) : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(LogInButton, { onClick: closeHamburgerOnClick(onClickLogIn), hideLoadingInMediaSmLow: true }), /*#__PURE__*/React.createElement(SignUpButton, { onClick: closeHamburgerOnClick(onClickSignUp) })))))))); }; process.env.NODE_ENV !== "production" ? HeaderNav.propTypes = { /** * Whether or not to render the logged in version */ loggedIn: PropTypes.bool, /** * Sets the menu that should be shown when the hamburger menu is opened and adds an active underline for the menu button on desktop */ activeProduct: isRequiredAndOneOfOrNull(['planning-tools', 'vendors', 'wedding-website', 'invitations', 'registry', 'attire-rings', 'ideas-advice']), /** * Callback for clicking log in CTA */ onClickLogIn: PropTypes.func, /** * Callback for clicking sign up CTA */ onClickSignUp: PropTypes.func, /** * Callback for clicking log out CTA */ onClickLogOut: PropTypes.func, classes: classesPropType(), /** * Environment to be used to determine which host for The Knot to use (www vs qa-beta). Supported values are `qa` and `production`. When not supplied, URLs default to being relative to the current host. **/ knotHostEnv: PropTypes.oneOf(['qa', 'production']), /** * Whether or not to display the TK logo as the pride logo (with rainbow gradient). * Defaults to true during June unless explicitly set */ prideLogo: PropTypes.bool, /** * Props for analytics component. See [@xo-union/tk-component-analytics](/packages/@xo-union/tk-component-analytics) */ analyticsProps: PropTypes.shape({ product: PropTypes.string.isRequired }).isRequired, /** * Media API image id of couple photo. Will be displayed in place of generic avatar. */ couplePhotoUrl: PropTypes.string, /** * A number greater than 0 will render the ConversationsActive icons state. */ unreadMessagesCount: PropTypes.number, /** * Renders the "Your Favorites Inspiration" link (with a heart icon) in the Main navigation nav * for all pages. By default, it avoids rendering in "https://www.theknot.com/<marketplace | review-wedding-vendors | inbox>" * but allows opting in to render by specifying `true` or an object with `isFilled` and `href` properties. */ favorites: PropTypes.oneOf([PropTypes.bool, PropTypes.shape({ isFilled: PropTypes.bool, href: PropTypes.string })]), /** * Extend the list of links. Can be used to override specific links */ links: PropTypes.objectOf(PropTypes.string), /** * Override the browser location object. * Primarily used for testing purposes. */ location: PropTypes.shape({ pathname: PropTypes.string, search: PropTypes.string }), /** * Membership data object */ membershipData: PropTypes.objectOf(PropTypes.any), /** * Whether or not to show the rewards bar */ showRewardsBar: PropTypes.bool, /** * Open menu on load. For internal testing purposes only, do not use in production. */ __openOnLoad__: PropTypes.string, /** * The loading state for when the member is loading on page load. */ memberIsLoading: PropTypes.bool, /** * Wedding Vision Data on load. For internal testing purposes only, do not use in production. */ __weddingVisionData__: PropTypes.objectOf(PropTypes.any), /** * Wedding Vision Data on load. For internal testing purposes only, do not use in production. */ __weddingWebsiteData__: PropTypes.objectOf(PropTypes.any), /** * Renders the "Your Drafts" item on "Invitations" tab of the Main navigation nav. */ __renderDraftsLink__: PropTypes.bool, /** * Number of featured elements to display in the Planning Tools Mega Menu. */ __planningToolsFeaturedElements__: PropTypes.number } : void 0; export default provideExperiments(HeaderNav);