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
@@ -0,0 +1,33 @@
"use client"
import clsx from "clsx"
import React, { useMemo } from "react"
import { ToCItemUi } from "types"
import { TocList } from "../List"
export type TocItemProps = {
item: ToCItemUi
activeItem: string
}
export const TocItem = ({ item, activeItem }: TocItemProps) => {
const isActive = useMemo(() => item.id === activeItem, [item, activeItem])
return (
<li>
<span
className={clsx(
"h-docs_0.125 rounded-full transition-colors",
isActive && "bg-medusa-fg-subtle",
!isActive && "bg-medusa-fg-disabled",
item.level === 2 && "w-[20px]",
item.level === 3 && "w-[10px]",
"block"
)}
></span>
{(item.children?.length || 0) > 0 && (
<TocList items={item.children!} activeItem={activeItem} />
)}
</li>
)
}
@@ -0,0 +1,29 @@
import clsx from "clsx"
import React from "react"
import { ToCItemUi } from "types"
import { TocItem } from "../Item"
export type TocListProps = {
items: ToCItemUi[]
topLevel?: boolean
activeItem: string
}
export const TocList = ({
items,
topLevel = false,
activeItem,
}: TocListProps) => {
return (
<ul
className={clsx(
"flex flex-col gap-docs_0.75 items-end",
!topLevel && "mt-docs_0.75"
)}
>
{items.map((item, key) => (
<TocItem item={item} key={key} activeItem={activeItem} />
))}
</ul>
)
}
@@ -0,0 +1,80 @@
"use client"
import { EllipseMiniSolid } from "@medusajs/icons"
import clsx from "clsx"
import React from "react"
import { ToCItemUi } from "types"
import { Button, useScrollController } from "../../.."
export type TocMenuProps = {
items: ToCItemUi[]
activeItem: string
show: boolean
setShow: (value: boolean) => void
}
export const TocMenu = ({ items, activeItem, show, setShow }: TocMenuProps) => {
const { scrollToElement } = useScrollController()
const getItemElm = (item: ToCItemUi) => {
const isActive = item.id === activeItem
const hasChildren = item.children?.length || 0 > 0
return (
<li className={clsx("text-medusa-fg-base w-full")}>
<Button
variant="transparent-clear"
className={clsx(
"gap-docs_0.5 flex-1",
"cursor-pointer rounded-docs_sm py-docs_0.25",
"px-docs_0.5 hover:bg-medusa-bg-component-hover",
"!text-inherit max-w-full w-full",
"focus:!outline-none focus:!shadow-none focus:dark:!shadow-none",
"!flex !justify-start !items-center",
isActive && "!text-compact-small-plus",
!isActive && "!text-compact-small"
)}
onClick={() => {
history.pushState({}, "", `#${item.id}`)
const elm = document.getElementById(item.id) as HTMLElement
scrollToElement(elm)
}}
>
<EllipseMiniSolid className={clsx(!isActive && "invisible")} />
<span className="truncate flex-1 text-left">{item.title}</span>
</Button>
{hasChildren && (
<ul className="pl-docs_0.5">
{item.children!.map((childItem, index) => (
<React.Fragment key={index}>
{getItemElm(childItem)}
</React.Fragment>
))}
</ul>
)}
</li>
)
}
return (
<div
className={clsx(
"hidden lg:flex relative transition-[width] lg:h-full",
"w-0 z-50 bg-medusa-bg-subtle overflow-hidden flex flex-col justify-center",
show && "lg:w-toc"
)}
onMouseLeave={() => setShow(false)}
>
<ul
className={clsx(
"p-docs_0.75 lg:w-toc max-h-full overflow-y-scroll",
"absolute lg:-right-full transition-[right,opacity] opacity-0",
show && "lg:right-0 lg:opacity-100"
)}
>
{items.map((item, index) => (
<React.Fragment key={index}>{getItemElm(item)}</React.Fragment>
))}
</ul>
</div>
)
}
@@ -0,0 +1,99 @@
"use client"
import React, { useEffect, useState } from "react"
import { ToCItemUi } from "types"
import {
ActiveOnScrollItem,
isElmWindow,
useActiveOnScroll,
useIsBrowser,
useScrollController,
} from "../.."
import { TocList } from "./List"
import clsx from "clsx"
import { TocMenu } from "./Menu"
export const Toc = () => {
const [items, setItems] = useState<ToCItemUi[]>([])
const [showMenu, setShowMenu] = useState(false)
const isBrowser = useIsBrowser()
const { items: headingItems, activeItemId } = useActiveOnScroll({})
const [maxHeight, setMaxHeight] = useState(0)
const { scrollableElement } = useScrollController()
const formatHeadingContent = (content: string | null): string => {
return content?.replaceAll(/#$/g, "") || ""
}
const formatHeadingObject = ({
heading,
children,
}: ActiveOnScrollItem): ToCItemUi => {
const level = parseInt(heading.tagName.replace("H", ""))
return {
title: formatHeadingContent(heading.textContent),
id: heading.id,
level,
children: children?.map(formatHeadingObject),
associatedHeading: heading as HTMLHeadingElement,
}
}
useEffect(() => {
setItems(headingItems.map(formatHeadingObject))
}, [headingItems])
const handleResize = () => {
const offset =
(scrollableElement instanceof HTMLElement
? scrollableElement.offsetTop
: 0) + 56
setMaxHeight(
(isElmWindow(scrollableElement)
? scrollableElement.innerHeight
: scrollableElement?.clientHeight || 0) - offset
)
}
useEffect(() => {
if (!isBrowser) {
return
}
handleResize()
window.addEventListener("resize", handleResize)
return () => {
window.removeEventListener("resize", handleResize)
}
}, [isBrowser])
return (
<div className="hidden lg:block" onMouseOver={() => setShowMenu(true)}>
<div
className={clsx(
"fixed top-1/2 right-[20px]",
"hidden lg:flex justify-center items-center",
"overflow-hidden z-10",
showMenu && "lg:hidden",
maxHeight < 1000 && "-translate-y-[40%]",
maxHeight >= 1000 && "-translate-y-1/2"
)}
onMouseOver={() => setShowMenu(true)}
style={{
maxHeight,
}}
>
<TocList items={items} topLevel={true} activeItem={activeItemId} />
</div>
<TocMenu
items={items}
activeItem={activeItemId}
show={showMenu}
setShow={setShowMenu}
/>
</div>
)
}