docs: support multiple sidebars in a project (#11768)
* changed to new sidebar across projects except resources * finalize multi sidebar support * clean up * remove redundant property * small changes * fixes * generate * fix error * fix initial open
This commit is contained in:
@@ -3,94 +3,53 @@
|
||||
import React, { useMemo } from "react"
|
||||
import clsx from "clsx"
|
||||
import Link from "next/link"
|
||||
import { SidebarItemLink } from "types"
|
||||
import {
|
||||
CurrentItemsState,
|
||||
isSidebarItemLink,
|
||||
useSidebar,
|
||||
useSiteConfig,
|
||||
} from "../../providers"
|
||||
import { useSidebar, useSiteConfig } from "../../providers"
|
||||
import { Button } from "../Button"
|
||||
import { TriangleRightMini } from "@medusajs/icons"
|
||||
import { Sidebar } from "types"
|
||||
|
||||
type BreadcrumbItems = {
|
||||
title: string
|
||||
link: string
|
||||
}[]
|
||||
|
||||
export const Breadcrumbs = () => {
|
||||
const { currentItems, activeItem: sidebarActiveItem } = useSidebar()
|
||||
const { sidebarHistory, getSidebarFirstLinkChild, getSidebar } = useSidebar()
|
||||
const {
|
||||
config: { breadcrumbOptions, project, baseUrl, basePath },
|
||||
config: { breadcrumbOptions },
|
||||
} = useSiteConfig()
|
||||
|
||||
const getLinkPath = (item?: SidebarItemLink): string | undefined => {
|
||||
if (!item) {
|
||||
return
|
||||
}
|
||||
const getLinkPath = (item: Sidebar.SidebarItemLink): string => {
|
||||
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 = isSidebarItemLink(item.parentItem)
|
||||
? getLinkPath(item.parentItem)
|
||||
: (item.parentItem?.type === "category" &&
|
||||
breadcrumbOptions?.showCategories) ||
|
||||
item.parentItem?.type === "sub-category"
|
||||
? "#"
|
||||
: undefined
|
||||
const firstItemPath = isSidebarItemLink(item.default[0])
|
||||
? getLinkPath(item.default[0])
|
||||
: (item.default[0].type === "category" &&
|
||||
breadcrumbOptions?.showCategories) ||
|
||||
item.default[0].type === "sub-category"
|
||||
? "#"
|
||||
: undefined
|
||||
|
||||
const breadcrumbPath = parentPath || firstItemPath || "/"
|
||||
|
||||
tempBreadcrumbItems.set(
|
||||
breadcrumbPath,
|
||||
item.parentItem?.childSidebarTitle ||
|
||||
item.parentItem?.chapterTitle ||
|
||||
item.parentItem?.title ||
|
||||
""
|
||||
)
|
||||
|
||||
return tempBreadcrumbItems
|
||||
}
|
||||
|
||||
const breadcrumbItems = useMemo(() => {
|
||||
const tempBreadcrumbItems: Map<string, string> = new Map()
|
||||
tempBreadcrumbItems.set(`${baseUrl}${basePath}`, project.title)
|
||||
|
||||
if (currentItems) {
|
||||
getBreadcrumbsOfItem(currentItems).forEach((value, key) =>
|
||||
tempBreadcrumbItems.set(key, value)
|
||||
)
|
||||
const items: BreadcrumbItems = []
|
||||
if (breadcrumbOptions?.startItems) {
|
||||
items.push(...breadcrumbOptions.startItems)
|
||||
}
|
||||
|
||||
if (sidebarActiveItem) {
|
||||
if (
|
||||
sidebarActiveItem.parentItem &&
|
||||
(sidebarActiveItem.parentItem.type !== "category" ||
|
||||
breadcrumbOptions?.showCategories)
|
||||
) {
|
||||
tempBreadcrumbItems.set(
|
||||
isSidebarItemLink(sidebarActiveItem.parentItem)
|
||||
? getLinkPath(sidebarActiveItem.parentItem) || "#"
|
||||
: "#",
|
||||
sidebarActiveItem.parentItem.chapterTitle ||
|
||||
sidebarActiveItem.parentItem.title ||
|
||||
""
|
||||
)
|
||||
sidebarHistory.forEach((sidebar_id) => {
|
||||
const sidebar = getSidebar(sidebar_id)
|
||||
|
||||
if (!sidebar) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return tempBreadcrumbItems
|
||||
}, [currentItems, sidebarActiveItem, breadcrumbOptions])
|
||||
const sidebarFirstChild = getSidebarFirstLinkChild(sidebar)
|
||||
|
||||
if (!sidebarFirstChild) {
|
||||
return
|
||||
}
|
||||
|
||||
items.push({
|
||||
title: sidebar.title,
|
||||
link: getLinkPath(sidebarFirstChild),
|
||||
})
|
||||
})
|
||||
|
||||
return items
|
||||
}, [sidebarHistory, breadcrumbOptions])
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -100,7 +59,7 @@ export const Breadcrumbs = () => {
|
||||
"mb-docs_1 flex-wrap"
|
||||
)}
|
||||
>
|
||||
{Array.from(breadcrumbItems).map(([link, title], index) => (
|
||||
{breadcrumbItems.map(({ title, link }, index) => (
|
||||
<React.Fragment key={link}>
|
||||
{index > 0 && <TriangleRightMini />}
|
||||
<Button
|
||||
|
||||
@@ -1,17 +1,26 @@
|
||||
"use client"
|
||||
|
||||
import React from "react"
|
||||
import React, { useMemo } from "react"
|
||||
import clsx from "clsx"
|
||||
import { InteractiveSidebarItem } from "types"
|
||||
import { ArrowUturnLeft } from "@medusajs/icons"
|
||||
import { useSidebar } from "../../.."
|
||||
import { useSidebar } from "../../../providers"
|
||||
|
||||
type SidebarTitleProps = {
|
||||
item: InteractiveSidebarItem
|
||||
}
|
||||
export const SidebarChild = () => {
|
||||
const { goBack, shownSidebar } = useSidebar()
|
||||
|
||||
export const SidebarChild = ({ item }: SidebarTitleProps) => {
|
||||
const { goBack } = useSidebar()
|
||||
const title = useMemo(() => {
|
||||
if (!shownSidebar) {
|
||||
return ""
|
||||
}
|
||||
|
||||
return "childSidebarTitle" in shownSidebar
|
||||
? shownSidebar.childSidebarTitle || shownSidebar.title
|
||||
: shownSidebar.title
|
||||
}, [shownSidebar])
|
||||
|
||||
if (!shownSidebar) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="px-docs_0.75">
|
||||
@@ -25,9 +34,7 @@ export const SidebarChild = ({ item }: SidebarTitleProps) => {
|
||||
tabIndex={-1}
|
||||
>
|
||||
<ArrowUturnLeft />
|
||||
<span className="truncate flex-1">
|
||||
{item.childSidebarTitle || item.title}
|
||||
</span>
|
||||
<span className="truncate flex-1">{title}</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -3,34 +3,37 @@
|
||||
// @refresh reset
|
||||
|
||||
import React, { useEffect, useMemo, useState } from "react"
|
||||
import { SidebarItemCategory as SidebarItemCategoryType } from "types"
|
||||
import { Sidebar } from "types"
|
||||
import { Loading, SidebarItem, useSidebar } from "../../../.."
|
||||
import clsx from "clsx"
|
||||
import { MinusMini, PlusMini } from "@medusajs/icons"
|
||||
|
||||
export type SidebarItemCategory = {
|
||||
item: SidebarItemCategoryType
|
||||
expandItems?: boolean
|
||||
export type SidebarItemCategoryProps = {
|
||||
item: Sidebar.SidebarItemCategory
|
||||
} & React.AllHTMLAttributes<HTMLDivElement>
|
||||
|
||||
export const SidebarItemCategory = ({
|
||||
item,
|
||||
expandItems = true,
|
||||
className,
|
||||
}: SidebarItemCategory) => {
|
||||
}: SidebarItemCategoryProps) => {
|
||||
const [showLoading, setShowLoading] = useState(false)
|
||||
const [open, setOpen] = useState(
|
||||
item.initialOpen !== undefined ? item.initialOpen : expandItems
|
||||
item.initialOpen !== undefined ? item.initialOpen : true
|
||||
)
|
||||
const {
|
||||
isChildrenActive,
|
||||
isItemActive,
|
||||
updatePersistedCategoryState,
|
||||
getPersistedCategoryState,
|
||||
persistState,
|
||||
persistCategoryState,
|
||||
} = useSidebar()
|
||||
const itemShowLoading = useMemo(() => {
|
||||
return !item.loaded || (item.showLoadingIfEmpty && !item.children?.length)
|
||||
}, [item])
|
||||
const isActive = useMemo(() => {
|
||||
return isItemActive({
|
||||
item,
|
||||
})
|
||||
}, [isItemActive, item])
|
||||
|
||||
useEffect(() => {
|
||||
if (open && itemShowLoading) {
|
||||
@@ -45,22 +48,20 @@ export const SidebarItemCategory = ({
|
||||
}, [itemShowLoading, showLoading])
|
||||
|
||||
useEffect(() => {
|
||||
const isActive = isChildrenActive(item)
|
||||
|
||||
if (isActive && !open) {
|
||||
setOpen(true)
|
||||
}
|
||||
}, [isChildrenActive, item.children])
|
||||
}, [isActive, item.children])
|
||||
|
||||
useEffect(() => {
|
||||
if (!persistState) {
|
||||
if (!persistCategoryState) {
|
||||
return
|
||||
}
|
||||
const persistedOpen = getPersistedCategoryState(item.title)
|
||||
if (persistedOpen !== undefined) {
|
||||
if (persistedOpen !== undefined && !isActive) {
|
||||
setOpen(persistedOpen)
|
||||
}
|
||||
}, [persistState])
|
||||
}, [persistCategoryState])
|
||||
|
||||
const handleOpen = () => {
|
||||
item.onOpen?.()
|
||||
@@ -90,7 +91,7 @@ export const SidebarItemCategory = ({
|
||||
if (!open) {
|
||||
handleOpen()
|
||||
}
|
||||
if (persistState) {
|
||||
if (persistCategoryState) {
|
||||
updatePersistedCategoryState(item.title, !open)
|
||||
}
|
||||
setOpen((prev) => !prev)
|
||||
@@ -133,7 +134,7 @@ export const SidebarItemCategory = ({
|
||||
<SidebarItem
|
||||
item={childItem}
|
||||
key={index}
|
||||
expandItems={expandItems}
|
||||
isParentCategoryOpen={open}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
// @refresh reset
|
||||
|
||||
import React, { useEffect, useMemo, useRef } from "react"
|
||||
import { SidebarItemLink as SidebarItemlinkType } from "types"
|
||||
import React, { useCallback, useEffect, useMemo, useRef } from "react"
|
||||
import { Sidebar } from "types"
|
||||
import {
|
||||
checkSidebarItemVisibility,
|
||||
SidebarItem,
|
||||
@@ -14,27 +14,36 @@ import clsx from "clsx"
|
||||
import Link from "next/link"
|
||||
|
||||
export type SidebarItemLinkProps = {
|
||||
item: SidebarItemlinkType
|
||||
item: Sidebar.SidebarItemLink
|
||||
nested?: boolean
|
||||
isParentCategoryOpen?: boolean
|
||||
} & React.AllHTMLAttributes<HTMLLIElement>
|
||||
|
||||
export const SidebarItemLink = ({
|
||||
item,
|
||||
className,
|
||||
nested = false,
|
||||
isParentCategoryOpen,
|
||||
}: SidebarItemLinkProps) => {
|
||||
const {
|
||||
isLinkActive,
|
||||
isItemActive,
|
||||
setMobileSidebarOpen: setSidebarOpen,
|
||||
disableActiveTransition,
|
||||
sidebarRef,
|
||||
sidebarTopHeight,
|
||||
} = useSidebar()
|
||||
const { isMobile } = useMobile()
|
||||
const active = useMemo(() => isLinkActive(item, true), [isLinkActive, item])
|
||||
const active = useMemo(
|
||||
() =>
|
||||
isItemActive({
|
||||
item,
|
||||
checkLinkChildren: false,
|
||||
}),
|
||||
[isItemActive, item]
|
||||
)
|
||||
const ref = useRef<HTMLLIElement>(null)
|
||||
|
||||
const newTopCalculator = useMemo(() => {
|
||||
const getNewTopCalculator = useCallback(() => {
|
||||
if (!sidebarRef.current || !ref.current) {
|
||||
return 0
|
||||
}
|
||||
@@ -48,33 +57,43 @@ export const SidebarItemLink = ({
|
||||
sidebarRef.current.scrollTop -
|
||||
10 // remove extra margin just in case
|
||||
)
|
||||
}, [sidebarTopHeight, sidebarRef, ref])
|
||||
}, [sidebarTopHeight, sidebarRef.current, ref.current])
|
||||
|
||||
useEffect(() => {
|
||||
if (active && ref.current && sidebarRef.current && !isMobile) {
|
||||
const isVisible = checkSidebarItemVisibility(
|
||||
(ref.current.children.item(0) as HTMLElement) || ref.current,
|
||||
!disableActiveTransition
|
||||
)
|
||||
if (isVisible) {
|
||||
return
|
||||
}
|
||||
if (!disableActiveTransition) {
|
||||
ref.current.scrollIntoView({
|
||||
block: "center",
|
||||
})
|
||||
} else {
|
||||
sidebarRef.current.scrollTo({
|
||||
top: newTopCalculator,
|
||||
})
|
||||
}
|
||||
if (
|
||||
!active ||
|
||||
!ref.current ||
|
||||
!sidebarRef.current ||
|
||||
isMobile ||
|
||||
!isParentCategoryOpen
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
const isVisible = checkSidebarItemVisibility(
|
||||
(ref.current.children.item(0) as HTMLElement) || ref.current,
|
||||
!disableActiveTransition
|
||||
)
|
||||
if (isVisible) {
|
||||
return
|
||||
}
|
||||
if (!disableActiveTransition) {
|
||||
ref.current.scrollIntoView({
|
||||
block: "center",
|
||||
})
|
||||
} else {
|
||||
sidebarRef.current.scrollTo({
|
||||
top: getNewTopCalculator(),
|
||||
})
|
||||
}
|
||||
}, [
|
||||
active,
|
||||
sidebarRef.current,
|
||||
disableActiveTransition,
|
||||
isMobile,
|
||||
newTopCalculator,
|
||||
ref.current,
|
||||
getNewTopCalculator,
|
||||
isParentCategoryOpen,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
@@ -84,11 +103,7 @@ export const SidebarItemLink = ({
|
||||
}, [active, isMobile])
|
||||
|
||||
const hasChildren = useMemo(() => {
|
||||
return (
|
||||
!item.isChildSidebar &&
|
||||
!item.hideChildren &&
|
||||
(item.children?.length || 0) > 0
|
||||
)
|
||||
return !item.hideChildren && (item.children?.length || 0) > 0
|
||||
}, [item.children])
|
||||
|
||||
const isTitleOneWord = useMemo(
|
||||
@@ -147,6 +162,7 @@ export const SidebarItemLink = ({
|
||||
item={childItem}
|
||||
key={index}
|
||||
nested={!item.childrenSameLevel}
|
||||
isParentCategoryOpen={isParentCategoryOpen}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
"use client"
|
||||
|
||||
// @refresh reset
|
||||
|
||||
import React, { useMemo } from "react"
|
||||
import { Sidebar } from "types"
|
||||
import { useSidebar } from "../../../.."
|
||||
import clsx from "clsx"
|
||||
import Link from "next/link"
|
||||
|
||||
export type SidebarItemSidebarProps = {
|
||||
item: Sidebar.SidebarItemSidebar
|
||||
nested?: boolean
|
||||
} & React.AllHTMLAttributes<HTMLLIElement>
|
||||
|
||||
export const SidebarItemSidebar = ({
|
||||
item,
|
||||
className,
|
||||
nested = false,
|
||||
}: SidebarItemSidebarProps) => {
|
||||
const { getSidebarFirstLinkChild: getSidebarFirstChild } = useSidebar()
|
||||
|
||||
const isTitleOneWord = useMemo(
|
||||
() => item.title.split(" ").length === 1,
|
||||
[item.title]
|
||||
)
|
||||
|
||||
const firstChild = useMemo(() => getSidebarFirstChild(item), [item])
|
||||
|
||||
return (
|
||||
<li>
|
||||
<span className="block px-docs_0.75">
|
||||
<Link
|
||||
href={
|
||||
firstChild?.isPathHref ? firstChild.path : `#${firstChild?.path}`
|
||||
}
|
||||
className={clsx(
|
||||
"py-docs_0.25 px-docs_0.5",
|
||||
"block w-full rounded-docs_sm",
|
||||
!isTitleOneWord && "break-words",
|
||||
!nested && "text-medusa-fg-subtle",
|
||||
nested && "text-medusa-fg-muted",
|
||||
"hover:bg-medusa-bg-base-hover lg:hover:bg-medusa-bg-subtle-hover",
|
||||
"text-compact-small-plus",
|
||||
"flex justify-between items-center gap-[6px]",
|
||||
className
|
||||
)}
|
||||
{...firstChild?.linkProps}
|
||||
>
|
||||
<span
|
||||
className={clsx(
|
||||
isTitleOneWord && "truncate",
|
||||
nested && "inline-block pl-docs_1.5"
|
||||
)}
|
||||
>
|
||||
{item.title}
|
||||
</span>
|
||||
{item.additionalElms}
|
||||
</Link>
|
||||
</span>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
@@ -2,90 +2,25 @@
|
||||
|
||||
// @refresh reset
|
||||
|
||||
import React, { useEffect, useMemo, useRef } from "react"
|
||||
import { SidebarItemSubCategory as SidebarItemSubCategoryType } from "types"
|
||||
import {
|
||||
checkSidebarItemVisibility,
|
||||
SidebarItem,
|
||||
useMobile,
|
||||
useSidebar,
|
||||
} from "../../../.."
|
||||
import React, { useMemo, useRef } from "react"
|
||||
import { Sidebar } from "types"
|
||||
import { SidebarItem } from "../../../.."
|
||||
import clsx from "clsx"
|
||||
|
||||
export type SidebarItemLinkProps = {
|
||||
item: SidebarItemSubCategoryType
|
||||
export type SidebarItemSubCategoryProps = {
|
||||
item: Sidebar.SidebarItemSubCategory
|
||||
nested?: boolean
|
||||
isParentCategoryOpen?: boolean
|
||||
} & React.AllHTMLAttributes<HTMLLIElement>
|
||||
|
||||
export const SidebarItemSubCategory = ({
|
||||
item,
|
||||
className,
|
||||
nested = false,
|
||||
}: SidebarItemLinkProps) => {
|
||||
const { isLinkActive, disableActiveTransition, sidebarRef } = useSidebar()
|
||||
const { isMobile } = useMobile()
|
||||
const active = useMemo(
|
||||
() => !isMobile && isLinkActive(item, true),
|
||||
[isLinkActive, item, isMobile]
|
||||
)
|
||||
isParentCategoryOpen,
|
||||
}: SidebarItemSubCategoryProps) => {
|
||||
const ref = useRef<HTMLLIElement>(null)
|
||||
|
||||
/**
|
||||
* Tries to place the item in the center of the sidebar
|
||||
*/
|
||||
const newTopCalculator = (): number => {
|
||||
if (!sidebarRef.current || !ref.current) {
|
||||
return 0
|
||||
}
|
||||
const sidebarBoundingRect = sidebarRef.current.getBoundingClientRect()
|
||||
const sidebarHalf = sidebarBoundingRect.height / 2
|
||||
const itemTop = ref.current.offsetTop
|
||||
const itemBottom =
|
||||
itemTop + (ref.current.children.item(0) as HTMLElement)?.clientHeight
|
||||
|
||||
// try deducting half
|
||||
let newTop = itemTop - sidebarHalf
|
||||
let newBottom = newTop + sidebarBoundingRect.height
|
||||
if (newTop <= itemTop && newBottom >= itemBottom) {
|
||||
return newTop
|
||||
}
|
||||
|
||||
// try adding half
|
||||
newTop = itemTop + sidebarHalf
|
||||
newBottom = newTop + sidebarBoundingRect.height
|
||||
if (newTop <= itemTop && newBottom >= itemBottom) {
|
||||
return newTop
|
||||
}
|
||||
|
||||
//return the item's top minus some top margin
|
||||
return itemTop - sidebarBoundingRect.top
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
active &&
|
||||
ref.current &&
|
||||
sidebarRef.current &&
|
||||
window.innerWidth >= 1025
|
||||
) {
|
||||
if (
|
||||
!disableActiveTransition &&
|
||||
!checkSidebarItemVisibility(
|
||||
(ref.current.children.item(0) as HTMLElement) || ref.current,
|
||||
!disableActiveTransition
|
||||
)
|
||||
) {
|
||||
ref.current.scrollIntoView({
|
||||
block: "center",
|
||||
})
|
||||
} else if (disableActiveTransition) {
|
||||
sidebarRef.current.scrollTo({
|
||||
top: newTopCalculator(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [active, sidebarRef.current, disableActiveTransition])
|
||||
|
||||
const hasChildren = useMemo(() => {
|
||||
return !item.hideChildren && (item.children?.length || 0) > 0
|
||||
}, [item.children])
|
||||
@@ -103,15 +38,8 @@ export const SidebarItemSubCategory = ({
|
||||
"py-docs_0.25 px-docs_0.5",
|
||||
"block w-full",
|
||||
!isTitleOneWord && "break-words",
|
||||
active && [
|
||||
"rounded-docs_sm",
|
||||
"shadow-borders-base dark:shadow-borders-base-dark",
|
||||
"text-medusa-fg-base",
|
||||
],
|
||||
!active && [
|
||||
!nested && "text-medusa-fg-subtle",
|
||||
nested && "text-medusa-fg-muted",
|
||||
],
|
||||
!nested && "text-medusa-fg-subtle",
|
||||
nested && "text-medusa-fg-muted",
|
||||
"text-compact-small-plus",
|
||||
className
|
||||
)}
|
||||
@@ -140,6 +68,7 @@ export const SidebarItemSubCategory = ({
|
||||
item={childItem}
|
||||
key={index}
|
||||
nested={!item.childrenSameLevel}
|
||||
isParentCategoryOpen={isParentCategoryOpen}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
"use client"
|
||||
|
||||
import React from "react"
|
||||
import { SidebarItem as SidebarItemType } from "types"
|
||||
import { SidebarItemCategory } from "./Category"
|
||||
import { Sidebar } from "types"
|
||||
import { SidebarItemLink } from "./Link"
|
||||
import { SidebarItemSubCategory } from "./SubCategory"
|
||||
import { DottedSeparator } from "../.."
|
||||
import { SidebarItemCategory } from "./Category"
|
||||
import { SidebarItemSidebar } from "./Sidebar"
|
||||
|
||||
export type SidebarItemProps = {
|
||||
item: SidebarItemType
|
||||
item: Sidebar.SidebarItem
|
||||
nested?: boolean
|
||||
expandItems?: boolean
|
||||
hasNextItems?: boolean
|
||||
isParentCategoryOpen?: boolean
|
||||
} & React.AllHTMLAttributes<HTMLElement>
|
||||
|
||||
export const SidebarItem = ({
|
||||
@@ -33,6 +34,8 @@ export const SidebarItem = ({
|
||||
case "ref":
|
||||
case "external":
|
||||
return <SidebarItemLink item={item} {...props} />
|
||||
case "sidebar":
|
||||
return <SidebarItemSidebar item={item} {...props} />
|
||||
case "separator":
|
||||
return <DottedSeparator />
|
||||
}
|
||||
|
||||
@@ -2,17 +2,14 @@
|
||||
|
||||
import React from "react"
|
||||
import { SidebarChild } from "../Child"
|
||||
import { InteractiveSidebarItem } from "types"
|
||||
import { SidebarTopMobileClose } from "./MobileClose"
|
||||
import { DottedSeparator } from "../../.."
|
||||
import { DottedSeparator, useSidebar } from "../../.."
|
||||
import clsx from "clsx"
|
||||
|
||||
export type SidebarTopProps = {
|
||||
parentItem?: InteractiveSidebarItem
|
||||
}
|
||||
export const SidebarTop = React.forwardRef<HTMLDivElement>(
|
||||
function SidebarTop(props, ref) {
|
||||
const { sidebarHistory } = useSidebar()
|
||||
|
||||
export const SidebarTop = React.forwardRef<HTMLDivElement, SidebarTopProps>(
|
||||
function SidebarTop({ parentItem }, ref) {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
@@ -23,9 +20,9 @@ export const SidebarTop = React.forwardRef<HTMLDivElement, SidebarTopProps>(
|
||||
>
|
||||
<SidebarTopMobileClose />
|
||||
<div>
|
||||
{parentItem && (
|
||||
{sidebarHistory.length > 1 && (
|
||||
<>
|
||||
<SidebarChild item={parentItem} />
|
||||
<SidebarChild />
|
||||
<DottedSeparator wrapperClassName="!my-0" />
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -1,39 +1,35 @@
|
||||
"use client"
|
||||
|
||||
import React, { Suspense, useMemo, useRef } from "react"
|
||||
import { isSidebarItemLink, useSidebar } from "@/providers"
|
||||
import { useSidebar } from "@/providers"
|
||||
import clsx from "clsx"
|
||||
import { Loading } from "@/components"
|
||||
import { SidebarItem } from "./Item"
|
||||
// @ts-expect-error can't install the types package because it doesn't support React v19
|
||||
import { CSSTransition, SwitchTransition } from "react-transition-group"
|
||||
import { SidebarTop, SidebarTopProps } from "./Top"
|
||||
import { SidebarTop } from "./Top"
|
||||
import { useClickOutside, useKeyboardShortcut } from "@/hooks"
|
||||
import useResizeObserver from "@react-hook/resize-observer"
|
||||
import { isSidebarItemLink } from "../../utils/sidebar-utils"
|
||||
|
||||
export type SidebarProps = {
|
||||
className?: string
|
||||
expandItems?: boolean
|
||||
sidebarTopProps?: Omit<SidebarTopProps, "parentItem">
|
||||
}
|
||||
|
||||
export const Sidebar = ({
|
||||
className = "",
|
||||
expandItems = true,
|
||||
sidebarTopProps,
|
||||
}: SidebarProps) => {
|
||||
export const Sidebar = ({ className = "" }: SidebarProps) => {
|
||||
const sidebarWrapperRef = useRef(null)
|
||||
const sidebarTopRef = useRef<HTMLDivElement>(null)
|
||||
const {
|
||||
items,
|
||||
currentItems,
|
||||
sidebars,
|
||||
shownSidebar,
|
||||
mobileSidebarOpen,
|
||||
setMobileSidebarOpen,
|
||||
staticSidebarItems,
|
||||
isSidebarStatic,
|
||||
sidebarRef,
|
||||
desktopSidebarOpen,
|
||||
setDesktopSidebarOpen,
|
||||
setSidebarTopHeight,
|
||||
sidebarHistory,
|
||||
} = useSidebar()
|
||||
useClickOutside({
|
||||
elmRef: sidebarWrapperRef,
|
||||
@@ -51,15 +47,16 @@ export const Sidebar = ({
|
||||
},
|
||||
})
|
||||
|
||||
const sidebarItems = useMemo(
|
||||
() => currentItems || items,
|
||||
[items, currentItems]
|
||||
)
|
||||
|
||||
useResizeObserver(sidebarTopRef as React.RefObject<HTMLElement>, () => {
|
||||
setSidebarTopHeight(sidebarTopRef.current?.clientHeight || 0)
|
||||
})
|
||||
|
||||
const sidebarItems = useMemo(() => {
|
||||
return shownSidebar && "items" in shownSidebar
|
||||
? shownSidebar.items
|
||||
: shownSidebar?.children || []
|
||||
}, [shownSidebar])
|
||||
|
||||
return (
|
||||
<>
|
||||
{mobileSidebarOpen && (
|
||||
@@ -94,7 +91,11 @@ export const Sidebar = ({
|
||||
<ul className={clsx("h-full w-full", "flex flex-col")}>
|
||||
<SwitchTransition>
|
||||
<CSSTransition
|
||||
key={sidebarItems.parentItem?.title || "home"}
|
||||
key={
|
||||
sidebarHistory.length
|
||||
? sidebarHistory[sidebarHistory.length - 1]
|
||||
: sidebars[0].sidebar_id
|
||||
}
|
||||
nodeRef={sidebarRef}
|
||||
classNames={{
|
||||
enter: "animate-fadeInLeft animate-fast",
|
||||
@@ -110,32 +111,12 @@ export const Sidebar = ({
|
||||
ref={sidebarRef}
|
||||
id="sidebar"
|
||||
>
|
||||
<SidebarTop
|
||||
{...sidebarTopProps}
|
||||
parentItem={sidebarItems.parentItem}
|
||||
ref={sidebarTopRef}
|
||||
/>
|
||||
{/* 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" />
|
||||
)}
|
||||
{sidebarItems.mobile.map((item, index) => (
|
||||
<SidebarItem
|
||||
item={item}
|
||||
key={index}
|
||||
expandItems={expandItems}
|
||||
hasNextItems={index !== sidebarItems.default.length - 1}
|
||||
/>
|
||||
))}
|
||||
{sidebarItems.mobile.length > 0 && <DottedSeparator />}
|
||||
</div> */}
|
||||
{/* DESKTOP SIDEBAR */}
|
||||
<SidebarTop ref={sidebarTopRef} />
|
||||
<div className="pt-docs_0.75">
|
||||
{!sidebarItems.default.length && !staticSidebarItems && (
|
||||
<Loading className="px-0" />
|
||||
{!sidebarItems.length && !isSidebarStatic && (
|
||||
<Loading className="px-docs_0.75" />
|
||||
)}
|
||||
{sidebarItems.default.map((item, index) => {
|
||||
{sidebarItems.map((item, index) => {
|
||||
const itemKey =
|
||||
item.type === "separator"
|
||||
? index
|
||||
@@ -155,10 +136,7 @@ export const Sidebar = ({
|
||||
>
|
||||
<SidebarItem
|
||||
item={item}
|
||||
expandItems={expandItems}
|
||||
hasNextItems={
|
||||
index !== sidebarItems.default.length - 1
|
||||
}
|
||||
hasNextItems={index !== sidebarItems.length - 1}
|
||||
/>
|
||||
</Suspense>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user