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:
@@ -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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user