docs: redesign sidebar (#8408)

* initial changes

* redesign the sidebar + nav drawer

* changes to sidebar items

* finish up sidebar redesign

* support new sidebar in resources

* general fixes

* integrate in ui

* support api reference

* refactor

* integrate in user guide

* docs: fix build errors

* fix user guide build

* more refactoring

* added banner

* added bottom logo + icon

* fix up sidebar

* fix up paddings

* fix shadow bottom

* docs: add table of content (#8445)

* add toc types

* implement toc functionality

* finished toc redesign

* redesigned table of content

* mobile fixes

* truncate text in toc

* mobile fixes

* merge fixes

* implement redesign

* add hide sidebar

* add menu action item

* finish up hide sidebar design

* implement redesign in resources

* integrate in api reference

* integrate changes in ui

* fixes to api reference scrolling

* fix build error

* fix build errors

* fixes

* fixes to sidebar

* general fixes

* fix active category not closing

* fix long titles
This commit is contained in:
Shahed Nasser
2024-08-15 12:13:13 +03:00
committed by GitHub
parent 4cb28531e5
commit b4f3b8a79d
157 changed files with 5080 additions and 2010 deletions

View File

@@ -0,0 +1,84 @@
"use client"
import React, { useMemo } from "react"
import { CurrentItemsState, useSidebar } from "../../.."
import clsx from "clsx"
import Link from "next/link"
import { SidebarItemLink } from "types"
export const MainNavBreadcrumbs = () => {
const { currentItems, getActiveItem } = useSidebar()
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)
: undefined
const firstItemPath =
item.default[0].type === "link" ? getLinkPath(item.default[0]) : undefined
tempBreadcrumbItems.set(
parentPath || firstItemPath || "/",
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)
)
}
const activeItem = getActiveItem()
if (activeItem) {
tempBreadcrumbItems.set(
getLinkPath(activeItem) || "/",
activeItem?.title || ""
)
}
return tempBreadcrumbItems
}, [currentItems, getActiveItem])
return (
<div
className={clsx(
"flex items-center",
"text-medusa-fg-muted text-compact-small"
)}
>
{Array.from(breadcrumbItems).map(([link, title]) => (
<React.Fragment key={link}>
<span>/</span>
<Link
href={link}
className={clsx(
"hover:text-medusa-fg-base transition-colors",
"px-docs_0.5 py-docs_0.25"
)}
>
{title}
</Link>
</React.Fragment>
))}
</div>
)
}

View File

@@ -0,0 +1,22 @@
"use client"
import React from "react"
import { useColorMode } from "../../../providers"
import { Button } from "../../.."
import { Moon, Sun } from "@medusajs/icons"
import clsx from "clsx"
export const MainNavColorMode = () => {
const { colorMode, toggleColorMode } = useColorMode()
return (
<Button
variant="transparent-clear"
className={clsx("!p-[6.5px] text-medusa-fg-muted")}
onClick={toggleColorMode}
>
{colorMode === "light" && <Sun />}
{colorMode === "dark" && <Moon />}
</Button>
)
}

View File

@@ -0,0 +1,13 @@
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",
"mx-docs_0.5"
)}
></span>
)
}

View File

@@ -0,0 +1,21 @@
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"
/>
)
}

View File

@@ -0,0 +1,50 @@
"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 { SidebarSeparator } from "../../../../Sidebar/Separator"
export type MainNavigationDropdownMenuItemProps = {
item: NavigationDropdownItem
onSelect: () => void
}
export const MainNavigationDropdownMenuItem = ({
item,
onSelect,
}: MainNavigationDropdownMenuItemProps) => {
switch (item.type) {
case "divider":
return <SidebarSeparator 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>
)
}
}

View File

@@ -0,0 +1,36 @@
"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>
)
}

View File

@@ -0,0 +1,40 @@
"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
}
export const MainNavigationDropdownSelected = ({
item,
onClick,
}: MainNavigationDropdownSelectedProps) => {
if (item.type === "divider") {
return <></>
}
return (
<div
className={clsx(
"flex justify-between items-center",
"cursor-pointer rounded-docs_sm hover:bg-medusa-bg-hover"
)}
tabIndex={-1}
onClick={onClick}
>
<MainNavigationDropdownIcon icon={item.icon} />
<div className="flex gap-[6px] py-docs_0.25 px-docs_0.5 items-center">
<span className="text-medusa-fg-base whitespace-nowrap flex-1">
{item.title}
</span>
<TrianglesMini className="text-medusa-fg-muted" />
</div>
</div>
)
}

View File

@@ -0,0 +1,46 @@
"use client"
import clsx from "clsx"
import React, { useMemo, useRef, useState } from "react"
import { MainNavigationDropdownSelected } from "./Selected"
import { MainNavigationDropdownMenu } from "./Menu"
import { useClickOutside, useMainNav } from "../../.."
export const MainNavigationDropdown = () => {
const { navItems: items } = useMainNav()
const navigationRef = useRef<HTMLDivElement>(null)
const [menuOpen, setMenuOpen] = useState(false)
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)}
/>
)}
<MainNavigationDropdownMenu
items={items}
open={menuOpen}
onSelect={() => setMenuOpen(false)}
/>
</div>
)
}

View File

@@ -0,0 +1,24 @@
"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>
)
}

View File

@@ -0,0 +1,38 @@
"use client"
import clsx from "clsx"
import React from "react"
import { MainNavigationDropdown } from "./NavigationDropdown"
import { MainNavBreadcrumbs } from "./Breadcrumb"
import { SearchModalOpener, useMainNav } from "../.."
import { MainNavColorMode } from "./ColorMode"
import Link from "next/link"
import { MainNavDivider } from "./Divider"
import { MainNavSidebarOpener } from "./SidebarOpener"
export const MainNav = () => {
const { reportIssueLink } = useMainNav()
return (
<div
className={clsx(
"hidden sm:flex justify-between items-center",
"px-docs_1 py-docs_0.75 w-full z-20",
"sticky top-0 bg-medusa-bg-base"
)}
>
<div className="flex items-center gap-docs_0.25">
<MainNavSidebarOpener />
<MainNavigationDropdown />
<MainNavBreadcrumbs />
</div>
<div className="flex items-center gap-docs_0.25">
<Link href={reportIssueLink} className="text-medusa-fg-muted">
Report Issue
</Link>
<MainNavDivider />
<MainNavColorMode />
<SearchModalOpener />
</div>
</div>
)
}