docs: redesigned navigation (#9525)

Redesign navigation bar to reflect new design and allow for dropdowns

Closes DX-943
This commit is contained in:
Shahed Nasser
2024-10-11 07:10:00 +00:00
committed by GitHub
parent 7c5415ba3a
commit 49a91fd40e
63 changed files with 934 additions and 978 deletions
@@ -1,29 +1,55 @@
"use client"
import React, { useMemo } from "react"
import { CurrentItemsState, useSidebar } from "@/providers"
import { TriangleRightMini } from "@medusajs/icons"
import Link from "next/link"
import clsx from "clsx"
type BreadcrumbItem = Map<string, string>
import Link from "next/link"
import { SidebarItemLink } from "types"
import { CurrentItemsState, useSidebar, useSiteConfig } from "../../providers"
import { Button } from "../Button"
import { TriangleRightMini } from "@medusajs/icons"
export const Breadcrumbs = () => {
const { currentItems } = useSidebar()
const { currentItems, activeItem: sidebarActiveItem } = useSidebar()
const {
config: { breadcrumbOptions, project, baseUrl, basePath },
} = useSiteConfig()
const getBreadcrumbsOfItem = (item: CurrentItemsState): BreadcrumbItem => {
let tempBreadcrumbItems: BreadcrumbItem = new Map()
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" ? item.parentItem.path : undefined
item.parentItem?.type === "link"
? getLinkPath(item.parentItem)
: (item.parentItem?.type === "category" &&
breadcrumbOptions?.showCategories) ||
item.parentItem?.type === "sub-category"
? "#"
: undefined
const firstItemPath =
item.default[0].type === "link" ? item.default[0].path : undefined
item.default[0].type === "link"
? getLinkPath(item.default[0])
: (item.default[0].type === "category" &&
breadcrumbOptions?.showCategories) ||
item.default[0].type === "sub-category"
? "#"
: undefined
const breadcrumbPath = parentPath || firstItemPath || "/"
tempBreadcrumbItems.set(
parentPath || firstItemPath || "/",
breadcrumbPath,
item.parentItem?.childSidebarTitle || item.parentItem?.title || ""
)
@@ -31,51 +57,65 @@ export const Breadcrumbs = () => {
}
const breadcrumbItems = useMemo(() => {
const tempBreadcrumbItems: BreadcrumbItem = new Map()
if (!currentItems) {
return tempBreadcrumbItems
const tempBreadcrumbItems: Map<string, string> = new Map()
tempBreadcrumbItems.set(`${baseUrl}${basePath}`, project.title)
if (currentItems) {
getBreadcrumbsOfItem(currentItems).forEach((value, key) =>
tempBreadcrumbItems.set(key, value)
)
}
tempBreadcrumbItems.set("/", "Home")
getBreadcrumbsOfItem(currentItems).forEach((value, key) =>
tempBreadcrumbItems.set(key, value)
)
if (sidebarActiveItem) {
if (
sidebarActiveItem.parentItem &&
(sidebarActiveItem.parentItem.type !== "category" ||
breadcrumbOptions?.showCategories)
) {
tempBreadcrumbItems.set(
sidebarActiveItem.parentItem.type === "link"
? getLinkPath(sidebarActiveItem.parentItem) || "#"
: "#",
sidebarActiveItem.parentItem.title || ""
)
}
tempBreadcrumbItems.set(
getLinkPath(sidebarActiveItem) || "/",
sidebarActiveItem.title || ""
)
}
return tempBreadcrumbItems
}, [currentItems])
const getBreadcrumbItemElms = (): React.ReactNode[] => {
const elms: React.ReactNode[] = []
breadcrumbItems.forEach((title, path) => {
elms.push(
<React.Fragment key={path}>
{elms.length !== 0 && (
<TriangleRightMini className={clsx("text-medusa-fg-muted")} />
)}
<Link
href={path}
className={clsx(
"text-compact-x-small-plus text-medusa-fg-muted",
"hover:text-medusa-fg-subtle transition-colors"
)}
>
{title}
</Link>
</React.Fragment>
)
})
return elms
}
}, [currentItems, sidebarActiveItem, breadcrumbOptions])
return (
<>
{breadcrumbItems.size > 0 && (
<div className="flex gap-docs_0.25 items-center mb-docs_1">
{...getBreadcrumbItemElms()}
</div>
<div
className={clsx(
"flex items-center gap-docs_0.25",
"text-medusa-fg-muted text-compact-small",
"mb-docs_1"
)}
</>
>
{Array.from(breadcrumbItems).map(([link, title], index) => (
<React.Fragment key={link}>
{index > 0 && <TriangleRightMini />}
<Button
variant="transparent-clear"
className={clsx(
"px-docs_0.5 py-docs_0.25",
link === "#" && "hover:cursor-default",
"!p-0 hover:!bg-transparent hover:!text-medusa-fg-subtle"
)}
>
<Link
href={link}
className={clsx(link === "#" && "hover:cursor-default")}
>
{title}
</Link>
</Button>
</React.Fragment>
))}
</div>
)
}
@@ -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>
@@ -2,13 +2,14 @@
import clsx from "clsx"
import React from "react"
import { MenuItemAction } from "types"
import { MenuItem, MenuItemAction } from "types"
export type MenuActionProps = {
item: MenuItemAction
onClick?: (item: MenuItem) => void
}
export const MenuAction = ({ item }: MenuActionProps) => {
export const MenuAction = ({ item, onClick }: MenuActionProps) => {
return (
<div className="px-docs_0.25">
<span
@@ -19,7 +20,10 @@ export const MenuAction = ({ item }: MenuActionProps) => {
"text-medusa-fg-base cursor-pointer"
)}
tabIndex={-1}
onClick={item.action}
onClick={() => {
item.action()
onClick?.(item)
}}
>
<span className="text-medusa-fg-subtle mt-[2.5px] block">
{item.icon}
@@ -3,13 +3,14 @@
import clsx from "clsx"
import Link from "next/link"
import React from "react"
import { MenuItemLink } from "types"
import { MenuItem as MenuItemType, MenuItemLink } from "types"
export type MenuItemProps = {
item: MenuItemLink
onClick?: (item: MenuItemType) => void
}
export const MenuItem = ({ item }: MenuItemProps) => {
export const MenuItem = ({ item, onClick }: MenuItemProps) => {
return (
<div className="px-docs_0.25">
<Link
@@ -20,10 +21,13 @@ export const MenuItem = ({ item }: MenuItemProps) => {
"text-medusa-fg-base"
)}
href={item.link}
onClick={() => onClick?.(item)}
>
<span className="text-medusa-fg-subtle mt-[2.5px] block">
{item.icon}
</span>
{item.icon && (
<span className="text-medusa-fg-subtle mt-[2.5px] block">
{item.icon}
</span>
)}
<span className="text-compact-small">{item.title}</span>
</Link>
</div>
@@ -8,9 +8,10 @@ import { MenuAction } from "./Action"
export type MenuProps = {
items: MenuItemType[]
className?: string
itemsOnClick?: (item: MenuItemType) => void
}
export const Menu = ({ items, className }: MenuProps) => {
export const Menu = ({ items, className, itemsOnClick }: MenuProps) => {
return (
<div
className={clsx(
@@ -21,9 +22,14 @@ export const Menu = ({ items, className }: MenuProps) => {
>
{items.map((item, index) => (
<React.Fragment key={index}>
{item.type === "link" && <MenuItem item={item} />}
{item.type === "action" && <MenuAction item={item} />}
{item.type === "link" && (
<MenuItem item={item} onClick={itemsOnClick} />
)}
{item.type === "action" && (
<MenuAction item={item} onClick={itemsOnClick} />
)}
{item.type === "divider" && <MenuDivider />}
{item.type === "custom" && item.content}
</React.Fragment>
))}
</div>
@@ -1,32 +0,0 @@
"use client"
import clsx from "clsx"
import React from "react"
import { SidebarLeftIcon } from "../Icons/SidebarLeft"
import { Button, SearchModalOpener, useSidebar } from "../.."
import { MainNavigationDropdown } from "../MainNav/NavigationDropdown"
export const MobileNavigation = () => {
const { setMobileSidebarOpen } = useSidebar()
return (
<div
className={clsx(
"sm:hidden bg-medusa-bg-base",
"sticky top-0 w-full z-40 h-min",
"px-docs_0.75 py-docs_0.5",
"flex justify-between items-center",
"border-b border-medusa-border-base"
)}
>
<Button
variant="transparent-clear"
onClick={() => setMobileSidebarOpen(true)}
>
<SidebarLeftIcon />
</Button>
<MainNavigationDropdown />
<SearchModalOpener />
</div>
)
}
@@ -1,9 +1,8 @@
"use client"
import React, { MouseEvent, useMemo } from "react"
import clsx from "clsx"
import { useMobile, useSearch } from "@/providers"
import { Button, Tooltip } from "@/components"
import React, { MouseEvent } from "react"
import { useSearch } from "@/providers"
import { Button } from "@/components"
import { MagnifyingGlass } from "@medusajs/icons"
import { useKeyboardShortcut } from "@/hooks"
@@ -14,15 +13,8 @@ export type SearchModalOpenerProps = {
export const SearchModalOpener = ({
isLoading = false,
className,
}: SearchModalOpenerProps) => {
const { isMobile } = useMobile()
const { setIsOpen } = useSearch()
const isApple = useMemo(() => {
return typeof navigator !== "undefined"
? navigator.userAgent.toLowerCase().indexOf("mac") !== 0
: true
}, [])
useKeyboardShortcut({
shortcutKeys: ["k"],
action: () => setIsOpen((prev) => !prev),
@@ -46,29 +38,12 @@ export const SearchModalOpener = ({
}
return (
<>
{isMobile && (
<Button variant="transparent" onClick={handleOpen}>
<MagnifyingGlass className="text-medusa-fg-muted" />
</Button>
)}
{!isMobile && (
<Tooltip place="bottom" tooltipChildren="Ask or search">
<Button
className={clsx(
"relative hover:cursor-pointer group",
"flex gap-[6px] !py-docs_0.25 !px-docs_0.5",
"justify-between items-center text-medusa-fg-muted",
className
)}
variant="transparent-clear"
onClick={handleOpen}
>
<MagnifyingGlass />
<span>{isApple ? "⌘" : "Ctrl"}K</span>
</Button>
</Tooltip>
)}
</>
<Button
variant="transparent"
onClick={handleOpen}
className="hidden sm:flex !p-[6.5px]"
>
<MagnifyingGlass className="text-medusa-fg-subtle" />
</Button>
)
}
@@ -4,7 +4,6 @@ import React from "react"
import { SidebarChild } from "../Child"
import { InteractiveSidebarItem } from "types"
import { SidebarTopMobileClose } from "./MobileClose"
import { SidebarTopMedusaMenu } from "./MedusaMenu"
import { DottedSeparator } from "../../.."
export type SidebarTopProps = {
@@ -17,14 +16,12 @@ export const SidebarTop = React.forwardRef<HTMLDivElement, SidebarTopProps>(
<div className="pt-docs_0.25">
<SidebarTopMobileClose />
<div ref={ref}>
<SidebarTopMedusaMenu />
{parentItem && (
<>
<DottedSeparator />
<SidebarChild item={parentItem} />
<DottedSeparator wrapperClassName="!my-0" />
</>
)}
<DottedSeparator wrapperClassName="!my-0" />
</div>
</div>
)
@@ -118,8 +118,8 @@ export const Sidebar = ({
}}
id="sidebar"
>
{/* MOBILE SIDEBAR */}
<div className={clsx("lg:hidden")}>
{/* MOBILE SIDEBAR - keeping this in case we need it in the future */}
{/* <div className={clsx("lg:hidden")}>
{!sidebarItems.mobile.length && !staticSidebarItems && (
<Loading className="px-0" />
)}
@@ -131,8 +131,8 @@ export const Sidebar = ({
hasNextItems={index !== sidebarItems.default.length - 1}
/>
))}
<DottedSeparator />
</div>
{sidebarItems.mobile.length > 0 && <DottedSeparator />}
</div> */}
{/* DESKTOP SIDEBAR */}
<div className="mt-docs_0.75 lg:mt-0">
{!sidebarItems.default.length && !staticSidebarItems && (
+98 -202
View File
@@ -1,195 +1,118 @@
import React from "react"
import { Badge } from "@/components"
import { OptionType } from "@/hooks"
import { NavigationDropdownItem, SidebarItem } from "types"
import { NavigationDropdownDocIcon } from "./components/Icons/NavigationDropdown/Doc"
import { NavigationDropdownStoreIcon } from "./components/Icons/NavigationDropdown/Store"
import { NavigationDropdownAdminIcon } from "./components/Icons/NavigationDropdown/Admin"
import { NavigationDropdownUiIcon } from "./components/Icons/NavigationDropdown/Ui"
import { NavigationDropdownDocV1Icon } from "./components/Icons/NavigationDropdown/DocV1"
import { NavigationDropdownUserIcon } from "./components/Icons/NavigationDropdown/User"
import { NavigationDropdownResourcesIcon } from "./components/Icons/NavigationDropdown/Resources"
import { NavigationDropdownModulesIcon } from "./components/Icons/NavigationDropdown/Modules"
import { NavigationItem } from "types"
export const GITHUB_ISSUES_PREFIX = `https://github.com/medusajs/medusa/issues/new?assignees=&labels=type%3A+docs&template=docs.yml`
export const GITHUB_UI_ISSUES_PREFIX = `https://github.com/medusajs/ui/issues/new?labels=documentation`
export const navDropdownItemsV2: NavigationDropdownItem[] = [
export const navDropdownItems: NavigationItem[] = [
{
type: "link",
path: `/v2`,
icon: NavigationDropdownDocIcon,
title: "Documentation",
title: "Get Started",
project: "book",
},
{
type: "link",
path: `/v2/resources/commerce-modules`,
icon: NavigationDropdownModulesIcon,
title: "Commerce Modules",
type: "dropdown",
title: "Product",
children: [
{
type: "link",
title: "Commerce Modules",
link: "/v2/resources/commerce-modules",
},
{
type: "link",
title: "Architectural Modules",
link: "/v2/resources/architectural-modules",
},
],
},
{
type: "link",
path: `/v2/resources`,
icon: NavigationDropdownResourcesIcon,
title: "Development Resources",
type: "dropdown",
title: "Resources",
children: [
{
type: "link",
title: "Guides",
link: "/v2/resources",
useAsFallback: true,
},
{
type: "link",
title: "Recipes",
link: "/v2/resources/recipes",
},
{
type: "link",
title: "UI Library",
link: "/ui",
},
{
type: "divider",
},
{
type: "link",
title: "Storefront Development",
link: "/v2/resources/storefront-development",
},
],
},
{
type: "link",
path: `/v2/api/store`,
icon: NavigationDropdownStoreIcon,
title: "Store API",
type: "dropdown",
title: "Tools & SDKs",
children: [
{
type: "link",
title: "Medusa CLI",
link: "/v2/resources/medusa-cli",
},
{
type: "link",
title: "Next.js Starter",
link: "/v2/resources/nextjs-starter",
},
{
type: "divider",
},
{
type: "link",
title: "Integrations",
link: "/v2/resources/integrations",
},
],
},
{
type: "link",
path: `/v2/api/admin`,
icon: NavigationDropdownAdminIcon,
title: "Admin API",
},
{
type: "link",
path: `/ui`,
icon: NavigationDropdownUiIcon,
title: "UI",
},
{
type: "link",
path: `/`,
icon: NavigationDropdownDocV1Icon,
title: "Medusa v1",
type: "dropdown",
title: "Reference",
children: [
{
type: "link",
title: "Admin API",
link: "/v2/api/admin",
},
{
type: "link",
title: "Store API",
link: "/v2/api/store",
},
{
type: "divider",
},
{
type: "link",
title: "Workflows",
link: "/v2/resources/medusa-workflows-reference",
},
{
type: "link",
title: "Data Model API",
link: "/v2/resources/references/data-model",
},
],
},
]
export const navDropdownItemsV1: NavigationDropdownItem[] = [
{
type: "link",
path: `/`,
icon: NavigationDropdownDocV1Icon,
title: "Documentation",
},
{
type: "link",
path: `/user-guide`,
icon: NavigationDropdownUserIcon,
title: "User Guide",
},
{
type: "link",
path: `/api/store`,
icon: NavigationDropdownStoreIcon,
title: "Store API",
},
{
type: "link",
path: `/api/admin`,
icon: NavigationDropdownAdminIcon,
title: "Admin API",
},
{
type: "link",
path: `/ui`,
icon: NavigationDropdownUiIcon,
title: "UI",
},
{
type: "link",
path: `/v2`,
icon: NavigationDropdownDocIcon,
title: "Medusa v2",
},
]
export const mobileSidebarItemsV1: SidebarItem[] = [
{
type: "link",
title: "Docs",
path: `/`,
loaded: true,
isPathHref: true,
},
{
type: "link",
title: "User Guide",
path: `/user-guide`,
loaded: true,
isPathHref: true,
},
{
type: "link",
title: "Store API",
path: `/api/store`,
loaded: true,
isPathHref: true,
},
{
type: "link",
title: "Admin API",
path: `/api/admin`,
loaded: true,
isPathHref: true,
},
{
type: "link",
title: "UI",
path: `/ui`,
loaded: true,
isPathHref: true,
},
{
type: "link",
title: "Learn Medusa V2",
path: `/v2`,
loaded: true,
isPathHref: true,
additionalElms: <Badge variant="blue">v2</Badge>,
},
]
export const mobileSidebarItemsV2: SidebarItem[] = [
{
type: "link",
title: "Docs",
path: `/v2`,
loaded: true,
isPathHref: true,
},
{
type: "link",
title: "Learning Resources",
path: `/v2/resources`,
loaded: true,
isPathHref: true,
},
{
type: "link",
title: "Store API",
path: `/v2/api/store`,
loaded: true,
isPathHref: true,
},
{
type: "link",
title: "Admin API",
path: `/v2/api/admin`,
loaded: true,
isPathHref: true,
},
{
type: "link",
title: "UI",
path: `/ui`,
loaded: true,
isPathHref: true,
},
{
type: "link",
title: "Medusa v1",
path: `/`,
loaded: true,
isPathHref: true,
},
]
export const searchFiltersV2: OptionType[] = [
export const searchFilters: OptionType[] = [
{
value: "guides",
label: "Guides",
@@ -206,36 +129,9 @@ export const searchFiltersV2: OptionType[] = [
value: "store-v2",
label: "Store API (v2)",
},
// TODO add more filters
]
export const searchFiltersV1: OptionType[] = [
{
value: "admin",
label: "Admin API",
},
{
value: "store",
label: "Store API",
},
{
value: "docs",
label: "Docs",
},
{
value: "user-guide",
label: "User Guide",
},
{
value: "plugins",
label: "Plugins",
},
{
value: "reference",
label: "References",
},
{
value: "ui",
label: "UI",
label: "Medusa UI",
},
// TODO add more filters
]
@@ -50,7 +50,8 @@ export const MainContentLayout = ({
"flex-col items-center",
"h-full w-full",
"overflow-y-scroll overflow-x-hidden",
"md:rounded-t-docs_DEFAULT shadow-elevation-card-rest",
"md:rounded-t-docs_DEFAULT",
"shadow-elevation-card-rest dark:shadow-elevation-card-rest-dark",
mainWrapperClasses
)}
id="main"
+1 -2
View File
@@ -1,7 +1,6 @@
import React from "react"
import clsx from "clsx"
import { RootProviders, Sidebar, SidebarProps } from "@/components"
import { MobileNavigation } from "../components/MobileNavigation"
import { Toc } from "../components/Toc"
import { MainContentLayout, MainContentLayoutProps } from "./main-content"
@@ -14,6 +13,7 @@ export type RootLayoutProps = {
showPagination?: boolean
feedbackComponent?: React.ReactNode
editComponent?: React.ReactNode
showBreadcrumbs?: boolean
} & MainContentLayoutProps
export const RootLayout = ({
@@ -38,7 +38,6 @@ export const RootLayout = ({
>
<RootProviders>
<ProvidersComponent>
<MobileNavigation />
<Sidebar {...sidebarProps} />
<div className={clsx("relative", "h-screen", "flex")}>
<MainContentLayout {...mainProps} />
+3 -1
View File
@@ -1,13 +1,14 @@
import React from "react"
import { RootLayout, RootLayoutProps } from "./root"
import clsx from "clsx"
import { Pagination } from ".."
import { Breadcrumbs, Pagination } from ".."
export const TightLayout = ({
children,
showPagination,
feedbackComponent,
editComponent,
showBreadcrumbs = true,
...props
}: RootLayoutProps) => {
return (
@@ -21,6 +22,7 @@ export const TightLayout = ({
"px-docs_1 md:px-docs_4 lg:px-0"
)}
>
{showBreadcrumbs && <Breadcrumbs />}
{children}
{feedbackComponent}
{showPagination && <Pagination />}
+3 -1
View File
@@ -1,13 +1,14 @@
import React from "react"
import { RootLayout, RootLayoutProps } from "./root"
import clsx from "clsx"
import { Pagination } from ".."
import { Breadcrumbs, Pagination } from ".."
export const WideLayout = ({
children,
showPagination,
feedbackComponent,
editComponent,
showBreadcrumbs = true,
...props
}: RootLayoutProps) => {
return (
@@ -22,6 +23,7 @@ export const WideLayout = ({
"lg:max-w-lg-wide-content xl:max-w-xl-wide-content relative mt-4 w-full flex-1 lg:mt-7"
)}
>
{showBreadcrumbs && <Breadcrumbs />}
{children}
{feedbackComponent}
{showPagination && <Pagination />}
@@ -1,27 +1,24 @@
"use client"
import { usePathname } from "next/navigation"
import React, { createContext, useContext, useMemo } from "react"
import {
BreadcrumbOptions,
NavigationDropdownItem,
NavigationDropdownItemLink,
} from "types"
import { NavigationItem } from "types"
import { useSiteConfig } from "../SiteConifg"
export type MainNavContext = {
navItems: NavigationDropdownItem[]
activeItem?: NavigationDropdownItemLink
navItems: NavigationItem[]
activeItemIndex?: number
activeItem?: NavigationItem
reportIssueLink: string
editDate?: string
breadcrumbOptions: BreadcrumbOptions
}
const MainNavContext = createContext<MainNavContext | null>(null)
export type MainNavProviderProps = {
navItems: NavigationDropdownItem[]
navItems: NavigationItem[]
reportIssueLink: string
editDate?: string
breadcrumbOptions?: BreadcrumbOptions
children?: React.ReactNode
}
@@ -30,26 +27,72 @@ export const MainNavProvider = ({
reportIssueLink,
children,
editDate,
breadcrumbOptions = {
showCategories: true,
},
}: MainNavProviderProps) => {
const activeItem = useMemo(
() =>
navItems.find(
(item) => item.type === "link" && item.isActive
) as NavigationDropdownItemLink,
[navItems]
)
const pathname = usePathname()
const { config } = useSiteConfig()
const baseUrl = `${config.baseUrl}${config.basePath}`
const activeItemIndex = useMemo(() => {
const currentUrl = `${baseUrl}${pathname}`.replace(/\/$/, "")
let fallbackIndex: number | undefined
const index = navItems.findIndex((item, index) => {
if (item.type === "dropdown") {
return item.children.some((childItem) => {
if (childItem.type !== "link") {
return
}
const isItemActive = currentUrl.startsWith(childItem.link)
if (
isItemActive &&
childItem.useAsFallback &&
fallbackIndex === undefined
) {
fallbackIndex = index
return false
}
return isItemActive
})
}
if (item.project && item.project !== config.project.key) {
return false
}
const isItemActive = currentUrl.startsWith(item.path)
if (isItemActive && item.useAsFallback && fallbackIndex === undefined) {
fallbackIndex = index
return false
}
return isItemActive
})
return index !== -1 ? index : fallbackIndex
}, [navItems, pathname, baseUrl, config])
const activeItem = useMemo(() => {
if (activeItemIndex === undefined) {
return
}
return navItems[activeItemIndex]
}, [navItems, activeItemIndex])
return (
<MainNavContext.Provider
value={{
navItems,
activeItem,
activeItemIndex,
reportIssueLink,
editDate,
breadcrumbOptions,
activeItem,
}}
>
{children}
@@ -26,6 +26,13 @@ export const SiteConfigProvider = ({
default: [],
mobile: [],
},
project: {
title: "",
key: "",
},
breadcrumbOptions: {
showCategories: true,
},
}
)
@@ -1,25 +0,0 @@
import { SidebarItem } from "types"
import { mobileSidebarItemsV1, mobileSidebarItemsV2 } from ".."
type Options = {
baseUrl: string
version?: "v1" | "v2"
}
export function getMobileSidebarItems({
baseUrl,
version = "v1",
}: Options): SidebarItem[] {
const mobileItems =
version === "v2" ? mobileSidebarItemsV2 : mobileSidebarItemsV1
return mobileItems.map((item) => {
if (item.type !== "link") {
return item
}
return {
...item,
path: `${baseUrl}${item.path}`,
}
})
}
@@ -1,27 +1,31 @@
import { NavigationDropdownItem } from "types"
import { navDropdownItemsV1, navDropdownItemsV2 } from ".."
import { NavigationItem } from "types"
import { navDropdownItems } from ".."
type Options = {
basePath: string
activePath: string
version?: "v1" | "v2"
}
export function getNavDropdownItems({
basePath,
activePath,
version = "v1",
}: Options): NavigationDropdownItem[] {
const items = version === "v2" ? navDropdownItemsV2 : navDropdownItemsV1
return items.map((item) => {
if (item.type === "divider") {
return item
export function getNavDropdownItems({ basePath }: Options): NavigationItem[] {
return navDropdownItems.map((item) => {
const newItem = {
...item,
}
return {
...item,
isActive: activePath === item.path,
path: `${basePath}${item.path}`,
if (newItem.type === "link") {
newItem.path = `${basePath}${newItem.path}`
} else {
newItem.children = newItem.children.map((childItem) => {
if (childItem.type !== "link") {
return childItem
}
return {
...childItem,
link: `${basePath}${childItem.link}`,
}
})
}
return newItem
})
}
-1
View File
@@ -5,7 +5,6 @@ export * from "./decode-str"
export * from "./dom-utils"
export * from "./format-report-link"
export * from "./get-link-with-base-path"
export * from "./get-mobile-sidebar-items"
export * from "./get-navbar-items"
export * from "./get-os-shortcut"
export * from "./get-scrolled-top"