first commit
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
import { Dropdown } from 'react-bootstrap-5'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import getMeta from '@/utils/meta'
|
||||
import type { NavbarSessionUser } from '@/features/ui/components/types/navbar'
|
||||
import DropdownListItem from '@/features/ui/components/bootstrap-5/dropdown-list-item'
|
||||
import NavDropdownDivider from './nav-dropdown-divider'
|
||||
import NavDropdownLinkItem from './nav-dropdown-link-item'
|
||||
import { useDsNavStyle } from '@/features/project-list/components/use-is-ds-nav'
|
||||
import { SignOut } from '@phosphor-icons/react'
|
||||
|
||||
export function AccountMenuItems({
|
||||
sessionUser,
|
||||
showSubscriptionLink,
|
||||
}: {
|
||||
sessionUser: NavbarSessionUser
|
||||
showSubscriptionLink: boolean
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
const logOutFormId = 'logOutForm'
|
||||
const dsNavStyle = useDsNavStyle()
|
||||
return (
|
||||
<>
|
||||
<Dropdown.Item as="li" disabled role="menuitem">
|
||||
{sessionUser.email}
|
||||
</Dropdown.Item>
|
||||
<NavDropdownDivider />
|
||||
<NavDropdownLinkItem href="/user/settings">
|
||||
{t('Account Settings')}
|
||||
</NavDropdownLinkItem>
|
||||
{showSubscriptionLink ? (
|
||||
<NavDropdownLinkItem href="/user/subscription">
|
||||
{t('subscription')}
|
||||
</NavDropdownLinkItem>
|
||||
) : null}
|
||||
<NavDropdownDivider />
|
||||
<DropdownListItem>
|
||||
{
|
||||
// The button is outside the form but still belongs to it via the
|
||||
// form attribute. The reason to do this is that if the button is
|
||||
// inside the form, screen readers will not count it in the total
|
||||
// number of menu items
|
||||
}
|
||||
<Dropdown.Item
|
||||
as="button"
|
||||
type="submit"
|
||||
form={logOutFormId}
|
||||
role="menuitem"
|
||||
className="d-flex align-items-center justify-content-between"
|
||||
>
|
||||
<span>{t('log_out')}</span>
|
||||
{dsNavStyle && <SignOut size={16} />}
|
||||
</Dropdown.Item>
|
||||
<form id={logOutFormId} method="POST" action="/logout">
|
||||
<input type="hidden" name="_csrf" value={getMeta('ol-csrfToken')} />
|
||||
</form>
|
||||
</DropdownListItem>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import type { DefaultNavbarMetadata } from '@/features/ui/components/types/default-navbar-metadata'
|
||||
import NavDropdownMenu from '@/features/ui/components/bootstrap-5/navbar/nav-dropdown-menu'
|
||||
import NavDropdownLinkItem from '@/features/ui/components/bootstrap-5/navbar/nav-dropdown-link-item'
|
||||
import { useSendProjectListMB } from '@/features/project-list/components/project-list-events'
|
||||
|
||||
export default function AdminMenu({
|
||||
canDisplayAdminMenu,
|
||||
canDisplayAdminRedirect,
|
||||
canDisplaySplitTestMenu,
|
||||
canDisplaySurveyMenu,
|
||||
canDisplayScriptLogMenu,
|
||||
adminUrl,
|
||||
}: Pick<
|
||||
DefaultNavbarMetadata,
|
||||
| 'canDisplayAdminMenu'
|
||||
| 'canDisplayAdminRedirect'
|
||||
| 'canDisplaySplitTestMenu'
|
||||
| 'canDisplaySurveyMenu'
|
||||
| 'canDisplayScriptLogMenu'
|
||||
| 'adminUrl'
|
||||
>) {
|
||||
const sendProjectListMB = useSendProjectListMB()
|
||||
return (
|
||||
<NavDropdownMenu
|
||||
title="Admin"
|
||||
className="subdued"
|
||||
onToggle={nextShow => {
|
||||
if (nextShow) {
|
||||
sendProjectListMB('menu-expand', {
|
||||
item: 'admin',
|
||||
location: 'top-menu',
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
{canDisplayAdminMenu ? (
|
||||
<>
|
||||
<NavDropdownLinkItem href="/admin">Manage Site</NavDropdownLinkItem>
|
||||
<NavDropdownLinkItem href="/admin/user">
|
||||
Manage Users
|
||||
</NavDropdownLinkItem>
|
||||
<NavDropdownLinkItem href="/admin/project">
|
||||
Project URL lookup
|
||||
</NavDropdownLinkItem>
|
||||
</>
|
||||
) : null}
|
||||
{canDisplayAdminRedirect && adminUrl ? (
|
||||
<NavDropdownLinkItem href={adminUrl}>
|
||||
Switch to Admin
|
||||
</NavDropdownLinkItem>
|
||||
) : null}
|
||||
{canDisplaySplitTestMenu ? (
|
||||
<NavDropdownLinkItem href="/admin/split-test">
|
||||
Manage Feature Flags
|
||||
</NavDropdownLinkItem>
|
||||
) : null}
|
||||
{canDisplaySurveyMenu ? (
|
||||
<NavDropdownLinkItem href="/admin/survey">
|
||||
Manage Surveys
|
||||
</NavDropdownLinkItem>
|
||||
) : null}
|
||||
{canDisplayScriptLogMenu ? (
|
||||
<NavDropdownLinkItem href="/admin/script-logs">
|
||||
View Script Logs
|
||||
</NavDropdownLinkItem>
|
||||
) : null}
|
||||
</NavDropdownMenu>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { DropdownItem } from 'react-bootstrap-5'
|
||||
import DropdownListItem from '@/features/ui/components/bootstrap-5/dropdown-list-item'
|
||||
import {
|
||||
type ExtraSegmentations,
|
||||
useSendProjectListMB,
|
||||
} from '@/features/project-list/components/project-list-events'
|
||||
|
||||
export default function ContactUsItem({
|
||||
showModal,
|
||||
location,
|
||||
}: {
|
||||
showModal: (event?: Event) => void
|
||||
location: ExtraSegmentations['menu-click']['location']
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
const sendMB = useSendProjectListMB()
|
||||
|
||||
return (
|
||||
<DropdownListItem>
|
||||
<DropdownItem
|
||||
as="button"
|
||||
role="menuitem"
|
||||
onClick={() => {
|
||||
sendMB('menu-click', { item: 'contact', location })
|
||||
showModal()
|
||||
}}
|
||||
>
|
||||
{t('contact_us')}
|
||||
</DropdownItem>
|
||||
</DropdownListItem>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
import { useState } from 'react'
|
||||
import { sendMB } from '@/infrastructure/event-tracking'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Button, Container, Nav, Navbar } from 'react-bootstrap-5'
|
||||
import useWaitForI18n from '@/shared/hooks/use-wait-for-i18n'
|
||||
import AdminMenu from '@/features/ui/components/bootstrap-5/navbar/admin-menu'
|
||||
import type { DefaultNavbarMetadata } from '@/features/ui/components/types/default-navbar-metadata'
|
||||
import NavItemFromData from '@/features/ui/components/bootstrap-5/navbar/nav-item-from-data'
|
||||
import LoggedInItems from '@/features/ui/components/bootstrap-5/navbar/logged-in-items'
|
||||
import LoggedOutItems from '@/features/ui/components/bootstrap-5/navbar/logged-out-items'
|
||||
import HeaderLogoOrTitle from '@/features/ui/components/bootstrap-5/navbar/header-logo-or-title'
|
||||
import MaterialIcon from '@/shared/components/material-icon'
|
||||
import { useContactUsModal } from '@/shared/hooks/use-contact-us-modal'
|
||||
import { UserProvider } from '@/shared/context/user-context'
|
||||
import { X } from '@phosphor-icons/react'
|
||||
|
||||
function DefaultNavbar(props: DefaultNavbarMetadata) {
|
||||
const {
|
||||
customLogo,
|
||||
title,
|
||||
canDisplayAdminMenu,
|
||||
canDisplayAdminRedirect,
|
||||
canDisplaySplitTestMenu,
|
||||
canDisplaySurveyMenu,
|
||||
canDisplayScriptLogMenu,
|
||||
enableUpgradeButton,
|
||||
suppressNavbarRight,
|
||||
suppressNavContentLinks,
|
||||
showCloseIcon = false,
|
||||
showSubscriptionLink,
|
||||
showSignUpLink,
|
||||
sessionUser,
|
||||
adminUrl,
|
||||
items,
|
||||
} = props
|
||||
const { t } = useTranslation()
|
||||
const { isReady } = useWaitForI18n()
|
||||
const [expanded, setExpanded] = useState(false)
|
||||
|
||||
// The Contact Us modal is rendered at this level rather than inside the nav
|
||||
// bar because otherwise the help wiki search results dropdown doesn't show up
|
||||
const { modal: contactUsModal, showModal: showContactUsModal } =
|
||||
useContactUsModal({
|
||||
autofillProjectUrl: false,
|
||||
})
|
||||
|
||||
if (!isReady) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar
|
||||
className="navbar-default navbar-main"
|
||||
expand="lg"
|
||||
onToggle={expanded => setExpanded(expanded)}
|
||||
>
|
||||
<Container className="navbar-container" fluid>
|
||||
<div className="navbar-header">
|
||||
<HeaderLogoOrTitle title={title} customLogo={customLogo} />
|
||||
{enableUpgradeButton ? (
|
||||
<Button
|
||||
as="a"
|
||||
href="/user/subscription/plans"
|
||||
className="me-2 d-md-none"
|
||||
onClick={() => {
|
||||
sendMB('upgrade-button-click', {
|
||||
source: 'dashboard-top',
|
||||
'project-dashboard-react': 'enabled',
|
||||
'is-dashboard-sidebar-hidden': 'true',
|
||||
'is-screen-width-less-than-768px': 'true',
|
||||
})
|
||||
}}
|
||||
>
|
||||
{t('upgrade')}
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
{suppressNavbarRight ? null : (
|
||||
<>
|
||||
<Navbar.Toggle
|
||||
aria-controls="navbar-main-collapse"
|
||||
aria-expanded="false"
|
||||
aria-label={t('main_navigation')}
|
||||
>
|
||||
{showCloseIcon && expanded ? (
|
||||
<X />
|
||||
) : (
|
||||
<MaterialIcon type="menu" />
|
||||
)}
|
||||
</Navbar.Toggle>
|
||||
<Navbar.Collapse
|
||||
id="navbar-main-collapse"
|
||||
className="justify-content-end"
|
||||
>
|
||||
<Nav as="ul" className="ms-auto" role="menubar">
|
||||
{canDisplayAdminMenu ||
|
||||
canDisplayAdminRedirect ||
|
||||
canDisplaySplitTestMenu ? (
|
||||
<AdminMenu
|
||||
canDisplayAdminMenu={canDisplayAdminMenu}
|
||||
canDisplayAdminRedirect={canDisplayAdminRedirect}
|
||||
canDisplaySplitTestMenu={canDisplaySplitTestMenu}
|
||||
canDisplaySurveyMenu={canDisplaySurveyMenu}
|
||||
canDisplayScriptLogMenu={canDisplayScriptLogMenu}
|
||||
adminUrl={adminUrl}
|
||||
/>
|
||||
) : null}
|
||||
{items.map((item, index) => {
|
||||
const showNavItem =
|
||||
(item.only_when_logged_in && sessionUser) ||
|
||||
(item.only_when_logged_out && sessionUser) ||
|
||||
(!item.only_when_logged_out &&
|
||||
!item.only_when_logged_in &&
|
||||
!item.only_content_pages) ||
|
||||
(item.only_content_pages && !suppressNavContentLinks)
|
||||
|
||||
return showNavItem ? (
|
||||
<NavItemFromData
|
||||
item={item}
|
||||
key={index}
|
||||
showContactUsModal={showContactUsModal}
|
||||
/>
|
||||
) : null
|
||||
})}
|
||||
{sessionUser ? (
|
||||
<LoggedInItems
|
||||
sessionUser={sessionUser}
|
||||
showSubscriptionLink={showSubscriptionLink}
|
||||
/>
|
||||
) : (
|
||||
<LoggedOutItems showSignUpLink={showSignUpLink} />
|
||||
)}
|
||||
</Nav>
|
||||
</Navbar.Collapse>
|
||||
</>
|
||||
)}
|
||||
</Container>
|
||||
</Navbar>
|
||||
<UserProvider>{contactUsModal}</UserProvider>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default DefaultNavbar
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { DefaultNavbarMetadata } from '@/features/ui/components/types/default-navbar-metadata'
|
||||
import getMeta from '@/utils/meta'
|
||||
|
||||
export default function HeaderLogoOrTitle({
|
||||
customLogo,
|
||||
title,
|
||||
}: Pick<DefaultNavbarMetadata, 'customLogo' | 'title'>) {
|
||||
const { appName } = getMeta('ol-ExposedSettings')
|
||||
|
||||
if (customLogo) {
|
||||
return (
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content
|
||||
<a
|
||||
href="/"
|
||||
aria-label={appName}
|
||||
className="navbar-brand"
|
||||
style={{ backgroundImage: `url("${customLogo}")` }}
|
||||
/>
|
||||
)
|
||||
} else if (title) {
|
||||
return (
|
||||
<a href="/" aria-label={appName} className="navbar-title">
|
||||
{title}
|
||||
</a>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content
|
||||
<a href="/" aria-label={appName} className="navbar-brand" />
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import NavDropdownMenu from '@/features/ui/components/bootstrap-5/navbar/nav-dropdown-menu'
|
||||
import type { NavbarSessionUser } from '@/features/ui/components/types/navbar'
|
||||
import NavLinkItem from '@/features/ui/components/bootstrap-5/navbar/nav-link-item'
|
||||
import { AccountMenuItems } from './account-menu-items'
|
||||
import { useSendProjectListMB } from '@/features/project-list/components/project-list-events'
|
||||
|
||||
export default function LoggedInItems({
|
||||
sessionUser,
|
||||
showSubscriptionLink,
|
||||
}: {
|
||||
sessionUser: NavbarSessionUser
|
||||
showSubscriptionLink: boolean
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
const sendProjectListMB = useSendProjectListMB()
|
||||
return (
|
||||
<>
|
||||
<NavLinkItem href="/project" className="nav-item-projects">
|
||||
{t('projects')}
|
||||
</NavLinkItem>
|
||||
<NavDropdownMenu
|
||||
title={t('Account')}
|
||||
className="nav-item-account"
|
||||
onToggle={nextShow => {
|
||||
if (nextShow) {
|
||||
sendProjectListMB('menu-expand', {
|
||||
item: 'account',
|
||||
location: 'top-menu',
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
<AccountMenuItems
|
||||
sessionUser={sessionUser}
|
||||
showSubscriptionLink={showSubscriptionLink}
|
||||
/>
|
||||
</NavDropdownMenu>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import NavLinkItem from '@/features/ui/components/bootstrap-5/navbar/nav-link-item'
|
||||
import { useSendProjectListMB } from '@/features/project-list/components/project-list-events'
|
||||
|
||||
export default function LoggedOutItems({
|
||||
showSignUpLink,
|
||||
}: {
|
||||
showSignUpLink: boolean
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
const sendMB = useSendProjectListMB()
|
||||
|
||||
return (
|
||||
<>
|
||||
{showSignUpLink ? (
|
||||
<NavLinkItem
|
||||
href="/register"
|
||||
className="primary nav-account-item"
|
||||
onClick={() => {
|
||||
sendMB('menu-click', { item: 'register', location: 'top-menu' })
|
||||
}}
|
||||
>
|
||||
{t('sign_up')}
|
||||
</NavLinkItem>
|
||||
) : null}
|
||||
<NavLinkItem
|
||||
href="/login"
|
||||
className="nav-account-item"
|
||||
onClick={() => {
|
||||
sendMB('menu-click', { item: 'login', location: 'top-menu' })
|
||||
}}
|
||||
>
|
||||
{t('log_in')}
|
||||
</NavLinkItem>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { DropdownDivider } from '@/features/ui/components/bootstrap-5/dropdown-menu'
|
||||
|
||||
export default function NavDropdownDivider() {
|
||||
return <DropdownDivider className="d-none d-lg-block" />
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import type {
|
||||
NavbarDropdownItemData,
|
||||
NavbarItemDropdownData,
|
||||
} from '@/features/ui/components/types/navbar'
|
||||
import NavDropdownDivider from '@/features/ui/components/bootstrap-5/navbar/nav-dropdown-divider'
|
||||
import { isDropdownLinkItem } from '@/features/ui/components/bootstrap-5/navbar/util'
|
||||
import NavDropdownLinkItem from '@/features/ui/components/bootstrap-5/navbar/nav-dropdown-link-item'
|
||||
import DropdownListItem from '@/features/ui/components/bootstrap-5/dropdown-list-item'
|
||||
import NavDropdownMenu from '@/features/ui/components/bootstrap-5/navbar/nav-dropdown-menu'
|
||||
import ContactUsItem from '@/features/ui/components/bootstrap-5/navbar/contact-us-item'
|
||||
import {
|
||||
type ExtraSegmentations,
|
||||
useSendProjectListMB,
|
||||
} from '@/features/project-list/components/project-list-events'
|
||||
|
||||
export default function NavDropdownFromData({
|
||||
item,
|
||||
showContactUsModal,
|
||||
}: {
|
||||
item: NavbarDropdownItemData
|
||||
showContactUsModal: (event?: Event) => void
|
||||
}) {
|
||||
const sendProjectListMB = useSendProjectListMB()
|
||||
return (
|
||||
<NavDropdownMenu
|
||||
title={item.translatedText}
|
||||
className={item.class}
|
||||
onToggle={nextShow => {
|
||||
if (nextShow) {
|
||||
sendProjectListMB('menu-expand', {
|
||||
item: item.trackingKey,
|
||||
location: 'top-menu',
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
<NavDropdownMenuItems
|
||||
dropdown={item.dropdown}
|
||||
showContactUsModal={showContactUsModal}
|
||||
location="top-menu"
|
||||
/>
|
||||
</NavDropdownMenu>
|
||||
)
|
||||
}
|
||||
|
||||
export function NavDropdownMenuItems({
|
||||
dropdown,
|
||||
showContactUsModal,
|
||||
location,
|
||||
}: {
|
||||
dropdown: NavbarItemDropdownData
|
||||
showContactUsModal: (event?: Event) => void
|
||||
location: ExtraSegmentations['menu-expand']['location']
|
||||
}) {
|
||||
const sendProjectListMB = useSendProjectListMB()
|
||||
return (
|
||||
<>
|
||||
{dropdown.map((child, index) => {
|
||||
if ('divider' in child) {
|
||||
return <NavDropdownDivider key={index} />
|
||||
} else if ('isContactUs' in child) {
|
||||
return (
|
||||
<ContactUsItem
|
||||
key={index}
|
||||
showModal={showContactUsModal}
|
||||
location={location}
|
||||
/>
|
||||
)
|
||||
} else if (isDropdownLinkItem(child)) {
|
||||
return (
|
||||
<NavDropdownLinkItem
|
||||
key={index}
|
||||
href={child.url}
|
||||
onClick={() => {
|
||||
sendProjectListMB('menu-click', {
|
||||
item: child.trackingKey as ExtraSegmentations['menu-click']['item'],
|
||||
location,
|
||||
destinationURL: child.url,
|
||||
})
|
||||
}}
|
||||
>
|
||||
{child.translatedText}
|
||||
</NavDropdownLinkItem>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<DropdownListItem key={index}>
|
||||
{child.translatedText}
|
||||
</DropdownListItem>
|
||||
)
|
||||
}
|
||||
})}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { ReactNode } from 'react'
|
||||
import DropdownListItem from '@/features/ui/components/bootstrap-5/dropdown-list-item'
|
||||
import { DropdownItem } from 'react-bootstrap-5'
|
||||
import { DropdownItemProps } from 'react-bootstrap-5/DropdownItem'
|
||||
|
||||
export default function NavDropdownLinkItem({
|
||||
href,
|
||||
onClick,
|
||||
children,
|
||||
}: {
|
||||
href: string
|
||||
onClick?: DropdownItemProps['onClick']
|
||||
children: ReactNode
|
||||
}) {
|
||||
return (
|
||||
<DropdownListItem>
|
||||
<DropdownItem href={href} role="menuitem" onClick={onClick}>
|
||||
{children}
|
||||
</DropdownItem>
|
||||
</DropdownListItem>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { type ReactNode, useState } from 'react'
|
||||
import { Dropdown } from 'react-bootstrap-5'
|
||||
import { CaretUp, CaretDown } from '@phosphor-icons/react'
|
||||
import { useDsNavStyle } from '@/features/project-list/components/use-is-ds-nav'
|
||||
|
||||
export default function NavDropdownMenu({
|
||||
title,
|
||||
className,
|
||||
children,
|
||||
onToggle,
|
||||
}: {
|
||||
title: string
|
||||
className?: string
|
||||
children: ReactNode
|
||||
onToggle?: (nextShow: boolean) => void
|
||||
}) {
|
||||
const [show, setShow] = useState(false)
|
||||
const dsNavStyle = useDsNavStyle()
|
||||
// Can't use a NavDropdown here because it's impossible to render the menu as
|
||||
// a <ul> element using NavDropdown
|
||||
const Caret = show ? CaretUp : CaretDown
|
||||
return (
|
||||
<Dropdown
|
||||
as="li"
|
||||
role="none"
|
||||
className={className}
|
||||
onToggle={nextShow => {
|
||||
setShow(nextShow)
|
||||
onToggle?.(nextShow)
|
||||
}}
|
||||
>
|
||||
<Dropdown.Toggle role="menuitem">
|
||||
{title}
|
||||
{dsNavStyle && <Caret weight="bold" className="ms-2" />}
|
||||
</Dropdown.Toggle>
|
||||
<Dropdown.Menu as="ul" role="menu" align="end">
|
||||
{children}
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { NavbarItemData } from '@/features/ui/components/types/navbar'
|
||||
import {
|
||||
isDropdownItem,
|
||||
isLinkItem,
|
||||
} from '@/features/ui/components/bootstrap-5/navbar/util'
|
||||
import NavDropdownFromData from '@/features/ui/components/bootstrap-5/navbar/nav-dropdown-from-data'
|
||||
import NavItem from '@/features/ui/components/bootstrap-5/navbar/nav-item'
|
||||
import NavLinkItem from '@/features/ui/components/bootstrap-5/navbar/nav-link-item'
|
||||
import { useSendProjectListMB } from '@/features/project-list/components/project-list-events'
|
||||
|
||||
export default function NavItemFromData({
|
||||
item,
|
||||
showContactUsModal,
|
||||
}: {
|
||||
item: NavbarItemData
|
||||
showContactUsModal: (event?: Event) => void
|
||||
}) {
|
||||
const sendProjectListMB = useSendProjectListMB()
|
||||
if (isDropdownItem(item)) {
|
||||
return (
|
||||
<NavDropdownFromData
|
||||
item={item}
|
||||
showContactUsModal={showContactUsModal}
|
||||
/>
|
||||
)
|
||||
} else if (isLinkItem(item)) {
|
||||
return (
|
||||
<NavLinkItem
|
||||
className={item.class}
|
||||
href={item.url}
|
||||
onClick={() => {
|
||||
sendProjectListMB('menu-click', {
|
||||
item: item.trackingKey as any,
|
||||
location: 'top-menu',
|
||||
destinationURL: item.url,
|
||||
})
|
||||
}}
|
||||
>
|
||||
{item.translatedText}
|
||||
</NavLinkItem>
|
||||
)
|
||||
} else {
|
||||
return <NavItem className={item.class}>{item.translatedText}</NavItem>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Nav, NavItemProps } from 'react-bootstrap-5'
|
||||
|
||||
export default function NavItem(props: Omit<NavItemProps, 'as'>) {
|
||||
const { children, ...rest } = props
|
||||
return (
|
||||
<Nav.Item as="li" role="none" {...rest}>
|
||||
{children}
|
||||
</Nav.Item>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ReactNode } from 'react'
|
||||
import { Nav } from 'react-bootstrap-5'
|
||||
import NavItem from '@/features/ui/components/bootstrap-5/navbar/nav-item'
|
||||
|
||||
export default function NavLinkItem({
|
||||
href,
|
||||
className,
|
||||
onClick,
|
||||
children,
|
||||
}: {
|
||||
href: string
|
||||
className?: string
|
||||
onClick?: React.ComponentProps<typeof Nav.Link>['onClick']
|
||||
children: ReactNode
|
||||
}) {
|
||||
return (
|
||||
<NavItem className={className}>
|
||||
<Nav.Link role="menuitem" href={href} onClick={onClick}>
|
||||
{children}
|
||||
</Nav.Link>
|
||||
</NavItem>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import type {
|
||||
NavbarDropdownItem,
|
||||
NavbarDropdownItemData,
|
||||
NavbarDropdownLinkItem,
|
||||
NavbarItemData,
|
||||
NavbarLinkItemData,
|
||||
} from '@/features/ui/components/types/navbar'
|
||||
|
||||
export function isDropdownLinkItem(
|
||||
item: NavbarDropdownItem
|
||||
): item is NavbarDropdownLinkItem {
|
||||
return 'url' in item
|
||||
}
|
||||
|
||||
export function isDropdownItem(
|
||||
item: NavbarItemData
|
||||
): item is NavbarDropdownItemData {
|
||||
return 'dropdown' in item
|
||||
}
|
||||
|
||||
export function isLinkItem(item: NavbarItemData): item is NavbarLinkItemData {
|
||||
return 'url' in item
|
||||
}
|
||||
Reference in New Issue
Block a user