docs: redesigned navigation (#9525)
Redesign navigation bar to reflect new design and allow for dropdowns Closes DX-943
This commit is contained in:
@@ -1,115 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import React, { useMemo } from "react"
|
||||
import { Button, CurrentItemsState, useMainNav, useSidebar } from "../../.."
|
||||
import clsx from "clsx"
|
||||
import Link from "next/link"
|
||||
import { SidebarItemLink } from "types"
|
||||
|
||||
export const MainNavBreadcrumbs = () => {
|
||||
const { currentItems, activeItem } = useSidebar()
|
||||
const {
|
||||
activeItem: mainNavActiveItem,
|
||||
breadcrumbOptions: { showCategories },
|
||||
} = useMainNav()
|
||||
|
||||
const getLinkPath = (item?: SidebarItemLink): string | undefined => {
|
||||
if (!item) {
|
||||
return
|
||||
}
|
||||
return item.isPathHref ? item.path : `#${item.path}`
|
||||
}
|
||||
|
||||
const getBreadcrumbsOfItem = (
|
||||
item: CurrentItemsState
|
||||
): Map<string, string> => {
|
||||
let tempBreadcrumbItems: Map<string, string> = new Map()
|
||||
if (item.previousSidebar) {
|
||||
tempBreadcrumbItems = getBreadcrumbsOfItem(item.previousSidebar)
|
||||
}
|
||||
|
||||
const parentPath =
|
||||
item.parentItem?.type === "link"
|
||||
? getLinkPath(item.parentItem)
|
||||
: (item.parentItem?.type === "category" && showCategories) ||
|
||||
item.parentItem?.type === "sub-category"
|
||||
? "#"
|
||||
: undefined
|
||||
const firstItemPath =
|
||||
item.default[0].type === "link"
|
||||
? getLinkPath(item.default[0])
|
||||
: (item.default[0].type === "category" && showCategories) ||
|
||||
item.default[0].type === "sub-category"
|
||||
? "#"
|
||||
: undefined
|
||||
|
||||
const breadcrumbPath = parentPath || firstItemPath || "/"
|
||||
|
||||
if (!mainNavActiveItem?.path.endsWith(breadcrumbPath)) {
|
||||
tempBreadcrumbItems.set(
|
||||
breadcrumbPath,
|
||||
item.parentItem?.childSidebarTitle || item.parentItem?.title || ""
|
||||
)
|
||||
}
|
||||
|
||||
return tempBreadcrumbItems
|
||||
}
|
||||
|
||||
const breadcrumbItems = useMemo(() => {
|
||||
const tempBreadcrumbItems: Map<string, string> = new Map()
|
||||
if (currentItems) {
|
||||
getBreadcrumbsOfItem(currentItems).forEach((value, key) =>
|
||||
tempBreadcrumbItems.set(key, value)
|
||||
)
|
||||
}
|
||||
|
||||
if (activeItem && !mainNavActiveItem?.path.endsWith(activeItem.path)) {
|
||||
if (
|
||||
activeItem.parentItem &&
|
||||
(activeItem.parentItem.type !== "category" || showCategories)
|
||||
) {
|
||||
tempBreadcrumbItems.set(
|
||||
activeItem.parentItem.type === "link"
|
||||
? getLinkPath(activeItem.parentItem) || "#"
|
||||
: "#",
|
||||
activeItem.parentItem.title || ""
|
||||
)
|
||||
}
|
||||
tempBreadcrumbItems.set(
|
||||
getLinkPath(activeItem) || "/",
|
||||
activeItem.title || ""
|
||||
)
|
||||
}
|
||||
|
||||
return tempBreadcrumbItems
|
||||
}, [currentItems, activeItem])
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"flex items-center gap-docs_0.25",
|
||||
"text-medusa-fg-muted text-compact-small"
|
||||
)}
|
||||
>
|
||||
{Array.from(breadcrumbItems).map(([link, title]) => (
|
||||
<React.Fragment key={link}>
|
||||
<span>/</span>
|
||||
<Button
|
||||
variant="transparent-clear"
|
||||
className={clsx(
|
||||
"px-docs_0.5 py-docs_0.25",
|
||||
link === "#" && "hover:!bg-transparent hover:cursor-default"
|
||||
)}
|
||||
>
|
||||
<Link
|
||||
href={link}
|
||||
className={clsx(link === "#" && "hover:cursor-default")}
|
||||
>
|
||||
{title}
|
||||
</Link>
|
||||
</Button>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import React from "react"
|
||||
import { useColorMode } from "../../../providers"
|
||||
import { Button, Tooltip } from "../../.."
|
||||
import { Moon, Sun } from "@medusajs/icons"
|
||||
import clsx from "clsx"
|
||||
|
||||
export const MainNavColorMode = () => {
|
||||
const { colorMode, toggleColorMode } = useColorMode()
|
||||
|
||||
return (
|
||||
<Tooltip place="bottom" tooltipChildren="Change Theme">
|
||||
<Button
|
||||
variant="transparent-clear"
|
||||
className={clsx("!p-[6.5px] text-medusa-fg-muted")}
|
||||
onClick={toggleColorMode}
|
||||
>
|
||||
{colorMode === "light" && <Sun />}
|
||||
{colorMode === "dark" && <Moon />}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
"use client"
|
||||
|
||||
import React from "react"
|
||||
import { useColorMode } from "../../../../providers"
|
||||
import clsx from "clsx"
|
||||
import { EllipseMiniSolid } from "@medusajs/icons"
|
||||
|
||||
export const MainNavThemeMenu = () => {
|
||||
const { colorMode, setColorMode } = useColorMode()
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={clsx(
|
||||
"flex items-center gap-docs_0.5",
|
||||
"py-docs_0.25 px-docs_0.5",
|
||||
"rounded-docs_xs text-compact-x-small-plus",
|
||||
"text-medusa-fg-subtle"
|
||||
)}
|
||||
>
|
||||
Theme
|
||||
</div>
|
||||
<div className="px-docs_0.25">
|
||||
<div
|
||||
className={clsx(
|
||||
"flex items-center gap-docs_0.5",
|
||||
"py-docs_0.25 px-docs_0.5 cursor-pointer",
|
||||
"rounded-docs_xs text-medusa-fg-base",
|
||||
"hover:bg-medusa-bg-component-hover"
|
||||
)}
|
||||
tabIndex={-1}
|
||||
onClick={() => setColorMode("light")}
|
||||
>
|
||||
<EllipseMiniSolid
|
||||
className={clsx(colorMode !== "light" && "invisible")}
|
||||
/>
|
||||
<span
|
||||
className={clsx(
|
||||
colorMode !== "light" && "text-compact-small",
|
||||
colorMode === "light" && "text-compact-small-plus"
|
||||
)}
|
||||
>
|
||||
Light
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-docs_0.25">
|
||||
<div
|
||||
className={clsx(
|
||||
"flex items-center gap-docs_0.5",
|
||||
"py-docs_0.25 px-docs_0.5 cursor-pointer",
|
||||
"rounded-docs_xs text-medusa-fg-base",
|
||||
"hover:bg-medusa-bg-component-hover"
|
||||
)}
|
||||
tabIndex={-1}
|
||||
onClick={() => setColorMode("dark")}
|
||||
>
|
||||
<EllipseMiniSolid
|
||||
className={clsx(colorMode !== "dark" && "invisible")}
|
||||
/>
|
||||
<span
|
||||
className={clsx(
|
||||
colorMode !== "dark" && "text-compact-small",
|
||||
colorMode === "dark" && "text-compact-small-plus"
|
||||
)}
|
||||
>
|
||||
Dark
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
"use client"
|
||||
|
||||
import {
|
||||
BarsThree,
|
||||
QuestionMarkCircle,
|
||||
SidebarLeft,
|
||||
TimelineVertical,
|
||||
} from "@medusajs/icons"
|
||||
import React, { useRef, useState } from "react"
|
||||
import {
|
||||
Button,
|
||||
getOsShortcut,
|
||||
Menu,
|
||||
useClickOutside,
|
||||
useSidebar,
|
||||
} from "../../.."
|
||||
import clsx from "clsx"
|
||||
import { HouseIcon } from "../../Icons/House"
|
||||
import { MainNavThemeMenu } from "./ThemeMenu"
|
||||
|
||||
export const MainNavDesktopMenu = () => {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const { setDesktopSidebarOpen } = useSidebar()
|
||||
const ref = useRef(null)
|
||||
|
||||
useClickOutside({
|
||||
elmRef: ref,
|
||||
onClickOutside: () => setIsOpen(false),
|
||||
})
|
||||
|
||||
return (
|
||||
<div
|
||||
className="relative hidden lg:flex justify-center items-center"
|
||||
ref={ref}
|
||||
>
|
||||
<Button
|
||||
variant="transparent"
|
||||
onClick={() => setIsOpen((prev) => !prev)}
|
||||
className="!p-[6.5px]"
|
||||
>
|
||||
<BarsThree className="text-medusa-fg-subtle" />
|
||||
</Button>
|
||||
<Menu
|
||||
className={clsx(
|
||||
"absolute top-[calc(100%+8px)] right-0 min-w-[200px]",
|
||||
!isOpen && "hidden"
|
||||
)}
|
||||
items={[
|
||||
{
|
||||
type: "link",
|
||||
icon: <HouseIcon />,
|
||||
title: "Homepage",
|
||||
link: "https://medusajs.com",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
icon: <TimelineVertical />,
|
||||
title: "Changelog",
|
||||
link: "https://medusajs.com/changelog",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
icon: <QuestionMarkCircle />,
|
||||
title: "Troubleshooting",
|
||||
link: "https://docs.medusajs.com/v2/resources/troubleshooting",
|
||||
},
|
||||
{
|
||||
type: "divider",
|
||||
},
|
||||
{
|
||||
type: "action",
|
||||
title: "Hide Sidebar",
|
||||
icon: <SidebarLeft />,
|
||||
shortcut: `${getOsShortcut()}\\`,
|
||||
action: () => {
|
||||
setDesktopSidebarOpen((prev) => !prev)
|
||||
setIsOpen(false)
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "divider",
|
||||
},
|
||||
{
|
||||
type: "custom",
|
||||
content: <MainNavThemeMenu />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import clsx from "clsx"
|
||||
import React from "react"
|
||||
|
||||
export const MainNavDivider = () => {
|
||||
return (
|
||||
<span
|
||||
className={clsx("h-docs_0.75 w-px block bg-medusa-border-base")}
|
||||
></span>
|
||||
)
|
||||
}
|
||||
@@ -18,7 +18,7 @@ export const MainNavEditDate = ({ date }: MainNavEditDateProps) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className="text-compact-small">
|
||||
<span className="text-compact-small-plus">
|
||||
Edited {dateMatch.groups.month} {dateObj.getDate()}
|
||||
{dateObj.getFullYear() !== today.getFullYear()
|
||||
? `, ${dateObj.getFullYear()}`
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import React, { useRef, useState } from "react"
|
||||
import { Button, Kbd, Tooltip } from "@/components"
|
||||
import { Bug, Discord, QuestionMark } from "@medusajs/icons"
|
||||
import { Menu, useClickOutside } from "../../.."
|
||||
import { MenuItem } from "types"
|
||||
import clsx from "clsx"
|
||||
import { GithubIcon } from "../../Icons/Github"
|
||||
|
||||
export const MainNavHelpButton = () => {
|
||||
const [showMenu, setShowMenu] = useState(false)
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
useClickOutside({
|
||||
elmRef: ref,
|
||||
onClickOutside: () => {
|
||||
setShowMenu(false)
|
||||
},
|
||||
})
|
||||
|
||||
const menuItems: MenuItem[] = [
|
||||
{
|
||||
type: "link",
|
||||
title: "Create a GitHub Issue",
|
||||
link: "https://github.com/medusajs/medusa/issues/new/choose",
|
||||
icon: <GithubIcon className="text-medusa-fg-base" />,
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
title: "Get Support on Discord",
|
||||
link: "https://discord.gg/medusajs",
|
||||
icon: <Discord />,
|
||||
},
|
||||
{
|
||||
type: "divider",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
title: "Troubleshooting Guides",
|
||||
link: "https://docs.medusajs.com/v2/resources/troubleshooting",
|
||||
icon: <Bug />,
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="relative" ref={ref}>
|
||||
<Tooltip
|
||||
tooltipChildren={
|
||||
<span className="flex gap-[6px] items-center">
|
||||
<span>Need help?</span>
|
||||
<Kbd className="w-docs_1 h-docs_1">?</Kbd>
|
||||
</span>
|
||||
}
|
||||
place="bottom"
|
||||
hidden={showMenu}
|
||||
>
|
||||
<Button
|
||||
variant="transparent-clear"
|
||||
className="text-medusa-fg-muted !px-[6.5px] !py-[6.5px]"
|
||||
onClick={() => setShowMenu((prev) => !prev)}
|
||||
>
|
||||
<QuestionMark />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<div
|
||||
className={clsx(
|
||||
"absolute bottom-0 left-0",
|
||||
"z-50 -translate-x-[40%] translate-y-[calc(100%+8px)]",
|
||||
!showMenu && "hidden"
|
||||
)}
|
||||
>
|
||||
<Menu items={menuItems} className="w-[200px]" />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
"use client"
|
||||
|
||||
import { TriangleDownMini } from "@medusajs/icons"
|
||||
import clsx from "clsx"
|
||||
import React, { useRef, useState } from "react"
|
||||
import { NavigationItemDropdown } from "types"
|
||||
import { Menu, useClickOutside } from "../../../.."
|
||||
|
||||
type MainNavItemDropdownProps = {
|
||||
item: NavigationItemDropdown
|
||||
isActive: boolean
|
||||
}
|
||||
|
||||
export const MainNavItemDropdown = ({
|
||||
item,
|
||||
isActive,
|
||||
}: MainNavItemDropdownProps) => {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const ref = useRef(null)
|
||||
|
||||
useClickOutside({
|
||||
elmRef: ref,
|
||||
onClickOutside: () => setIsOpen(false),
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={clsx("relative")} ref={ref}>
|
||||
<div
|
||||
className={clsx(
|
||||
"cursor-pointer flex gap-docs_0.25 items-center",
|
||||
isActive && "text-medusa-fg-base",
|
||||
!isActive && [
|
||||
"text-medusa-fg-muted hover:text-medusa-fg-subtle",
|
||||
isOpen && "text-medusa-fg-subtle",
|
||||
]
|
||||
)}
|
||||
tabIndex={-1}
|
||||
onClick={() => setIsOpen((prev) => !prev)}
|
||||
>
|
||||
<span className="text-compact-small-plus">{item.title}</span>
|
||||
<TriangleDownMini
|
||||
className={clsx("transition-transform", isOpen && "rotate-180")}
|
||||
/>
|
||||
</div>
|
||||
<Menu
|
||||
className={clsx(
|
||||
"absolute top-[calc(100%+4px)] -left-docs_0.75 min-w-[190px]",
|
||||
!isOpen && "hidden"
|
||||
)}
|
||||
items={item.children}
|
||||
itemsOnClick={() => setIsOpen(false)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
"use client"
|
||||
|
||||
import React from "react"
|
||||
import { NavigationItemLink } from "types"
|
||||
import { LinkButton } from "../../../.."
|
||||
import clsx from "clsx"
|
||||
|
||||
type MainNavItemLinkProps = {
|
||||
item: NavigationItemLink
|
||||
isActive: boolean
|
||||
}
|
||||
|
||||
export const MainNavItemLink = ({ item, isActive }: MainNavItemLinkProps) => {
|
||||
return (
|
||||
<LinkButton
|
||||
href={item.path}
|
||||
className={clsx(
|
||||
isActive && "text-medusa-fg-base",
|
||||
!isActive && "text-medusa-fg-muted hover:text-medusa-fg-subtle"
|
||||
)}
|
||||
>
|
||||
{item.title}
|
||||
</LinkButton>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
"use client"
|
||||
|
||||
import React from "react"
|
||||
import { useMainNav } from "../../.."
|
||||
import clsx from "clsx"
|
||||
import { MainNavItemLink } from "./Link"
|
||||
import { MainNavItemDropdown } from "./Dropdown"
|
||||
|
||||
export const MainNavItems = () => {
|
||||
const { navItems, activeItemIndex } = useMainNav()
|
||||
|
||||
return (
|
||||
<ul
|
||||
className={clsx(
|
||||
"hidden lg:flex justify-start gap-docs_1 items-center",
|
||||
"my-docs_0.75"
|
||||
)}
|
||||
>
|
||||
{navItems.map((item, index) => {
|
||||
const isActive = index === activeItemIndex
|
||||
|
||||
return (
|
||||
<li className={clsx("flex items-center group")} key={index}>
|
||||
{item.type === "link" && (
|
||||
<MainNavItemLink item={item} isActive={isActive} />
|
||||
)}
|
||||
{item.type === "dropdown" && (
|
||||
<MainNavItemDropdown item={item} isActive={isActive} />
|
||||
)}
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
"use client"
|
||||
|
||||
import React from "react"
|
||||
import { useMainNav } from "../../../../providers"
|
||||
import Link from "next/link"
|
||||
import { TriangleRightMini } from "@medusajs/icons"
|
||||
import clsx from "clsx"
|
||||
import { SelectedMenu } from ".."
|
||||
|
||||
type MainNavMobileMainMenu = {
|
||||
setSelectedMenu: (menu: SelectedMenu) => void
|
||||
}
|
||||
|
||||
export const MainNavMobileMainMenu = ({
|
||||
setSelectedMenu,
|
||||
}: MainNavMobileMainMenu) => {
|
||||
const { navItems } = useMainNav()
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-[23px]">
|
||||
<span className="text-compact-small-plus text-medusa-fg-muted uppercase">
|
||||
Menu
|
||||
</span>
|
||||
<ul className="flex flex-col gap-[18px]">
|
||||
{navItems.map((item, index) => (
|
||||
<li
|
||||
key={index}
|
||||
className={clsx(
|
||||
"text-h1 text-medusa-fg-base cursor-pointer",
|
||||
"flex justify-between gap-docs_1"
|
||||
)}
|
||||
onClick={() => {
|
||||
if (item.type !== "dropdown") {
|
||||
return
|
||||
}
|
||||
|
||||
setSelectedMenu({
|
||||
title: item.title,
|
||||
menu: item.children,
|
||||
})
|
||||
}}
|
||||
>
|
||||
{item.type === "link" && (
|
||||
<Link href={item.path} className="block w-full">
|
||||
{item.title}
|
||||
</Link>
|
||||
)}
|
||||
{item.type === "dropdown" && (
|
||||
<>
|
||||
<span>{item.title}</span>
|
||||
<TriangleRightMini />
|
||||
</>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
"use client"
|
||||
|
||||
import clsx from "clsx"
|
||||
import Link from "next/link"
|
||||
import React, { useMemo } from "react"
|
||||
import { MenuItem, MenuItemLink } from "types"
|
||||
|
||||
type MainNavMobileSubMenuProps = {
|
||||
menu: MenuItem[]
|
||||
title: string
|
||||
}
|
||||
|
||||
export const MainNavMobileSubMenu = ({
|
||||
menu,
|
||||
title,
|
||||
}: MainNavMobileSubMenuProps) => {
|
||||
const filteredItems: MenuItemLink[] = useMemo(() => {
|
||||
return menu.filter((item) => item.type === "link") as MenuItemLink[]
|
||||
}, [menu])
|
||||
return (
|
||||
<div className="flex flex-col gap-[23px]">
|
||||
<span className="text-compact-small-plus text-medusa-fg-muted uppercase">
|
||||
{title}
|
||||
</span>
|
||||
<ul className="flex flex-col gap-[18px]">
|
||||
{filteredItems.map((item, index) => (
|
||||
<li
|
||||
key={index}
|
||||
className={clsx(
|
||||
"text-h1 text-medusa-fg-base cursor-pointer",
|
||||
"flex justify-between gap-docs_1"
|
||||
)}
|
||||
>
|
||||
<Link href={item.link} className="block w-full">
|
||||
{item.title}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
"use client"
|
||||
|
||||
import React, { useRef, useState } from "react"
|
||||
import { Button } from "../../Button"
|
||||
import { ArrowUturnLeft, BarsThree, XMark } from "@medusajs/icons"
|
||||
import clsx from "clsx"
|
||||
import { MenuItem } from "types"
|
||||
import { CSSTransition, SwitchTransition } from "react-transition-group"
|
||||
import { MainNavMobileMainMenu } from "./Main"
|
||||
import { MainNavMobileSubMenu } from "./SubMenu"
|
||||
|
||||
export type SelectedMenu = {
|
||||
title: string
|
||||
menu: MenuItem[]
|
||||
}
|
||||
|
||||
export const MainNavMobileMenu = () => {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [selectedMenu, setSelectedMenu] = useState<SelectedMenu>()
|
||||
const ref = useRef(null)
|
||||
|
||||
return (
|
||||
<div className="flex lg:hidden justify-center items-center">
|
||||
<Button
|
||||
variant="transparent"
|
||||
onClick={() => setIsOpen((prev) => !prev)}
|
||||
className="text-medusa-fg-subtle !p-[6.5px]"
|
||||
>
|
||||
{!isOpen && <BarsThree />}
|
||||
{isOpen && <XMark />}
|
||||
</Button>
|
||||
<div
|
||||
className={clsx(
|
||||
"flex items-center justify-center fixed w-full h-[calc(100vh-52px)]",
|
||||
"top-[52px] transition-[left] bg-medusa-bg-subtle z-50",
|
||||
!isOpen && "-left-full",
|
||||
isOpen && "left-0"
|
||||
)}
|
||||
>
|
||||
<SwitchTransition>
|
||||
<CSSTransition
|
||||
key={!selectedMenu ? "main" : "sub"}
|
||||
classNames={{
|
||||
enter: "animate-fadeInLeft animate-fast",
|
||||
exit: "animate-fadeOutRight animate-fast",
|
||||
}}
|
||||
nodeRef={ref}
|
||||
timeout={250}
|
||||
>
|
||||
<div ref={ref} className="w-full px-docs_1.5">
|
||||
{!selectedMenu && (
|
||||
<MainNavMobileMainMenu setSelectedMenu={setSelectedMenu} />
|
||||
)}
|
||||
{selectedMenu && (
|
||||
<>
|
||||
<div
|
||||
className={clsx(
|
||||
"flex items-center gap-docs_0.5",
|
||||
"text-medusa-fg-base my-[14px]",
|
||||
"cursor-pointer"
|
||||
)}
|
||||
tabIndex={-1}
|
||||
onClick={() => setSelectedMenu(undefined)}
|
||||
>
|
||||
<ArrowUturnLeft />
|
||||
<span className="text-h1">Back</span>
|
||||
</div>
|
||||
<MainNavMobileSubMenu {...selectedMenu} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</CSSTransition>
|
||||
</SwitchTransition>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import React from "react"
|
||||
import { BorderedIcon } from "@/components"
|
||||
import { IconProps } from "@medusajs/icons/dist/types"
|
||||
|
||||
export type MainNavigationDropdownIconProps = {
|
||||
icon: React.FC<IconProps>
|
||||
inDropdown?: boolean
|
||||
}
|
||||
|
||||
export const MainNavigationDropdownIcon = ({
|
||||
icon,
|
||||
inDropdown = false,
|
||||
}: MainNavigationDropdownIconProps) => {
|
||||
return (
|
||||
<BorderedIcon
|
||||
IconComponent={icon}
|
||||
iconClassName={inDropdown ? "w-docs_1 h-docs_1" : ""}
|
||||
iconWrapperClassName="rounded-docs_xs"
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import React from "react"
|
||||
import { NavigationDropdownItem } from "types"
|
||||
import Link from "next/link"
|
||||
import clsx from "clsx"
|
||||
import { EllipseMiniSolid } from "@medusajs/icons"
|
||||
import { MainNavigationDropdownIcon } from "../../Icon"
|
||||
import { DottedSeparator } from "../../../.."
|
||||
|
||||
export type MainNavigationDropdownMenuItemProps = {
|
||||
item: NavigationDropdownItem
|
||||
onSelect: () => void
|
||||
}
|
||||
|
||||
export const MainNavigationDropdownMenuItem = ({
|
||||
item,
|
||||
onSelect,
|
||||
}: MainNavigationDropdownMenuItemProps) => {
|
||||
switch (item.type) {
|
||||
case "divider":
|
||||
return <DottedSeparator className="my-docs_0.25" />
|
||||
case "link":
|
||||
return (
|
||||
<Link
|
||||
href={item.path}
|
||||
className={clsx(
|
||||
"hover:bg-medusa-bg-component-hover",
|
||||
"block rounded-docs_xs"
|
||||
)}
|
||||
prefetch={false}
|
||||
onClick={onSelect}
|
||||
>
|
||||
<li
|
||||
className={clsx(
|
||||
"px-docs_0.5 py-docs_0.25",
|
||||
"rounded-docs_xs text-medusa-fg-base",
|
||||
"flex gap-docs_0.5 justify-start items-center"
|
||||
)}
|
||||
>
|
||||
<span className={clsx(!item.isActive && "invisible")}>
|
||||
<EllipseMiniSolid />
|
||||
</span>
|
||||
<MainNavigationDropdownIcon icon={item.icon} inDropdown={true} />
|
||||
<span className="whitespace-nowrap">{item.title}</span>
|
||||
</li>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import clsx from "clsx"
|
||||
import React from "react"
|
||||
import { NavigationDropdownItem } from "types"
|
||||
import { MainNavigationDropdownMenuItem } from "./Item"
|
||||
|
||||
export type MainNavigationDropdownMenuProps = {
|
||||
items: NavigationDropdownItem[]
|
||||
open: boolean
|
||||
onSelect: () => void
|
||||
}
|
||||
|
||||
export const MainNavigationDropdownMenu = ({
|
||||
items,
|
||||
open,
|
||||
onSelect,
|
||||
}: MainNavigationDropdownMenuProps) => {
|
||||
return (
|
||||
<ul
|
||||
className={clsx(
|
||||
"absolute top-[calc(100%+4px)] p-docs_0.25 z-50 lg:-left-docs_0.25",
|
||||
"bg-medusa-bg-component rounded shadow-elevation-flyout",
|
||||
!open && "hidden"
|
||||
)}
|
||||
>
|
||||
{items.map((item, index) => (
|
||||
<MainNavigationDropdownMenuItem
|
||||
item={item}
|
||||
key={index}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import React from "react"
|
||||
import { NavigationDropdownItem } from "types"
|
||||
import { TrianglesMini } from "@medusajs/icons"
|
||||
import clsx from "clsx"
|
||||
import { MainNavigationDropdownIcon } from "../Icon"
|
||||
|
||||
export type MainNavigationDropdownSelectedProps = {
|
||||
item: NavigationDropdownItem
|
||||
onClick: () => void
|
||||
isActive: boolean
|
||||
}
|
||||
|
||||
export const MainNavigationDropdownSelected = ({
|
||||
item,
|
||||
onClick,
|
||||
isActive,
|
||||
}: MainNavigationDropdownSelectedProps) => {
|
||||
if (item.type === "divider") {
|
||||
return <></>
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"flex justify-between items-center gap-docs_0.25",
|
||||
"cursor-pointer rounded-docs_sm group"
|
||||
)}
|
||||
tabIndex={-1}
|
||||
onClick={onClick}
|
||||
>
|
||||
<MainNavigationDropdownIcon icon={item.icon} />
|
||||
<div
|
||||
className={clsx(
|
||||
"flex gap-[6px] py-docs_0.25 px-docs_0.5",
|
||||
"items-center group-hover:bg-medusa-button-transparent-hover",
|
||||
"rounded-docs_sm",
|
||||
isActive && "bg-medusa-button-transparent-hover"
|
||||
)}
|
||||
>
|
||||
<span className="text-medusa-fg-base whitespace-nowrap flex-1 text-compact-small-plus">
|
||||
{item.title}
|
||||
</span>
|
||||
<TrianglesMini className="text-medusa-fg-muted" />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import clsx from "clsx"
|
||||
import React, { useMemo, useRef, useState } from "react"
|
||||
import { MainNavigationDropdownSelected } from "./Selected"
|
||||
import { MainNavigationDropdownMenu } from "./Menu"
|
||||
import { useAnalytics, useClickOutside, useMainNav } from "../../.."
|
||||
|
||||
export const MainNavigationDropdown = () => {
|
||||
const { navItems: items } = useMainNav()
|
||||
const navigationRef = useRef<HTMLDivElement>(null)
|
||||
const [menuOpen, setMenuOpen] = useState(false)
|
||||
const { track } = useAnalytics()
|
||||
useClickOutside({
|
||||
elmRef: navigationRef,
|
||||
onClickOutside: () => {
|
||||
setMenuOpen(false)
|
||||
},
|
||||
})
|
||||
|
||||
const selectedItem = useMemo(() => {
|
||||
const activeItem = items.find(
|
||||
(item) => item.type === "link" && item.isActive
|
||||
)
|
||||
|
||||
if (!activeItem) {
|
||||
return items.length ? items[0] : undefined
|
||||
}
|
||||
|
||||
return activeItem
|
||||
}, [items])
|
||||
return (
|
||||
<div className={clsx("relative z-50")} ref={navigationRef}>
|
||||
{selectedItem && (
|
||||
<MainNavigationDropdownSelected
|
||||
item={selectedItem}
|
||||
onClick={() => {
|
||||
setMenuOpen((prev) => !prev)
|
||||
if (!menuOpen) {
|
||||
track("nav_main_open", {
|
||||
url: window.location.href,
|
||||
})
|
||||
}
|
||||
}}
|
||||
isActive={menuOpen}
|
||||
/>
|
||||
)}
|
||||
<MainNavigationDropdownMenu
|
||||
items={items}
|
||||
open={menuOpen}
|
||||
onSelect={() => setMenuOpen(false)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import React from "react"
|
||||
import { Button, useSidebar } from "../../.."
|
||||
import clsx from "clsx"
|
||||
import { SidebarLeft } from "@medusajs/icons"
|
||||
|
||||
export const MainNavSidebarOpener = () => {
|
||||
const { desktopSidebarOpen, setDesktopSidebarOpen } = useSidebar()
|
||||
|
||||
if (desktopSidebarOpen) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="transparent-clear"
|
||||
className={clsx("!p-[6.5px] text-medusa-fg-muted", "mr-docs_0.5")}
|
||||
onClick={() => setDesktopSidebarOpen(true)}
|
||||
>
|
||||
<SidebarLeft />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
@@ -2,57 +2,65 @@
|
||||
|
||||
import clsx from "clsx"
|
||||
import React from "react"
|
||||
import { MainNavigationDropdown } from "./NavigationDropdown"
|
||||
import { MainNavBreadcrumbs } from "./Breadcrumb"
|
||||
import {
|
||||
BorderedIcon,
|
||||
Button,
|
||||
LinkButton,
|
||||
SearchModalOpener,
|
||||
useMainNav,
|
||||
useSidebar,
|
||||
} from "../.."
|
||||
import { MainNavColorMode } from "./ColorMode"
|
||||
import { MainNavDivider } from "./Divider"
|
||||
import { MainNavSidebarOpener } from "./SidebarOpener"
|
||||
import { MainNavHelpButton } from "./HelpButton"
|
||||
import { SidebarLeftIcon } from "../Icons/SidebarLeft"
|
||||
import { MainNavEditDate } from "./EditDate"
|
||||
import { MainNavItems } from "./Items"
|
||||
import { MedusaIcon } from "../Icons/MedusaLogo"
|
||||
import { MainNavDesktopMenu } from "./DesktopMenu"
|
||||
import { SidebarLeftIcon } from "../Icons/SidebarLeft"
|
||||
import { MainNavMobileMenu } from "./MobileMenu"
|
||||
|
||||
export const MainNav = () => {
|
||||
const { reportIssueLink, editDate } = useMainNav()
|
||||
const { setMobileSidebarOpen } = useSidebar()
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"hidden sm:flex justify-between items-center",
|
||||
"px-docs_1 py-docs_0.75 w-full z-20",
|
||||
"flex justify-between items-center",
|
||||
"px-docs_1 w-full z-20",
|
||||
"sticky top-0 bg-medusa-bg-base"
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-docs_0.25">
|
||||
<Button
|
||||
className="lg:hidden"
|
||||
variant="transparent-clear"
|
||||
onClick={() => setMobileSidebarOpen(true)}
|
||||
>
|
||||
<SidebarLeftIcon />
|
||||
</Button>
|
||||
<MainNavSidebarOpener />
|
||||
<MainNavigationDropdown />
|
||||
<MainNavBreadcrumbs />
|
||||
<div className="flex items-center gap-docs_1">
|
||||
<div className="flex items-center gap-[10px]">
|
||||
<Button
|
||||
className="lg:hidden my-docs_0.75 !p-[6.5px]"
|
||||
variant="transparent-clear"
|
||||
onClick={() => setMobileSidebarOpen(true)}
|
||||
>
|
||||
<SidebarLeftIcon />
|
||||
</Button>
|
||||
<BorderedIcon
|
||||
IconComponent={MedusaIcon}
|
||||
iconWrapperClassName="my-[14px]"
|
||||
/>
|
||||
</div>
|
||||
<MainNavItems />
|
||||
</div>
|
||||
<div className="flex items-center gap-docs_0.75">
|
||||
<div className="flex items-center gap-[6px] text-medusa-fg-muted">
|
||||
<div className="flex items-center gap-docs_0.75 my-docs_0.75">
|
||||
<div className="lg:flex items-center gap-docs_0.5 text-medusa-fg-subtle hidden">
|
||||
{editDate && <MainNavEditDate date={editDate} />}
|
||||
<LinkButton href={reportIssueLink} variant="muted" target="_blank">
|
||||
<LinkButton
|
||||
href={reportIssueLink}
|
||||
variant="subtle"
|
||||
target="_blank"
|
||||
className="text-compact-small-plus"
|
||||
>
|
||||
Report Issue
|
||||
</LinkButton>
|
||||
</div>
|
||||
<MainNavDivider />
|
||||
<div className="flex items-center gap-docs_0.25">
|
||||
<MainNavHelpButton />
|
||||
<MainNavColorMode />
|
||||
<SearchModalOpener />
|
||||
<MainNavDesktopMenu />
|
||||
<MainNavMobileMenu />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user