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:
Shahed Nasser
2025-03-07 15:47:38 +02:00
committed by GitHub
parent 2a0bd86204
commit 5deb8eaf50
108 changed files with 37634 additions and 36749 deletions

View File

@@ -3,13 +3,13 @@
import { useScrollController, useSidebar, H2 as UiH2 } from "docs-ui"
import { useEffect, useMemo, useRef, useState } from "react"
import getSectionId from "../../../utils/get-section-id"
import { SidebarItem } from "types"
import { Sidebar } from "types"
type H2Props = React.HTMLAttributes<HTMLHeadingElement>
const H2 = ({ children, ...props }: H2Props) => {
const headingRef = useRef<HTMLHeadingElement>(null)
const { activePath, addItems, removeItems } = useSidebar()
const { activePath, addItems, removeItems, shownSidebar } = useSidebar()
const { scrollableElement, scrollToElement } = useScrollController()
const [scrolledFirstTime, setScrolledFirstTime] = useState(false)
@@ -29,7 +29,10 @@ const H2 = ({ children, ...props }: H2Props) => {
}, [scrollableElement, headingRef, id])
useEffect(() => {
const item: SidebarItem[] = [
if (!shownSidebar) {
return
}
const items: Sidebar.SidebarItem[] = [
{
type: "link",
path: `${id}`,
@@ -37,12 +40,17 @@ const H2 = ({ children, ...props }: H2Props) => {
loaded: true,
},
]
addItems(item)
addItems(items, {
sidebar_id: shownSidebar.sidebar_id,
})
return () => {
removeItems(item)
removeItems({
items,
sidebar_id: shownSidebar.sidebar_id,
})
}
}, [id])
}, [id, shownSidebar?.sidebar_id])
return (
<UiH2 {...props} id={id} passRef={headingRef}>

View File

@@ -60,11 +60,12 @@ const TagOperation = ({
}, [isBrowser, scrollableElement])
const scrollIntoView = useCallback(() => {
if (!isBrowser) {
if (!isBrowser || !nodeRef.current) {
// repeat timeout
setTimeout(scrollIntoView, 200)
return
}
if (nodeRef.current && !checkElementInViewport(nodeRef.current, 0)) {
if (!checkElementInViewport(nodeRef.current, 0)) {
const elm = nodeRef.current as HTMLElement
scrollToTop(
elm.offsetTop + (elm.offsetParent as HTMLElement)?.offsetTop,

View File

@@ -1,8 +1,7 @@
"use client"
import type { OpenAPIV3 } from "openapi-types"
import type { Operation, PathsObject } from "@/types/openapi"
import { useSidebar } from "docs-ui"
import type { Operation, PathsObject, TagObject } from "@/types/openapi"
import { findSidebarItem, useSidebar } from "docs-ui"
import { Fragment, Suspense, useEffect } from "react"
import dynamic from "next/dynamic"
import type { TagOperationProps } from "../Operation"
@@ -10,32 +9,41 @@ import clsx from "clsx"
import getTagChildSidebarItems from "@/utils/get-tag-child-sidebar-items"
import { useLoading } from "@/providers/loading"
import DividedLoading from "@/components/DividedLoading"
import { SidebarItemSections, SidebarItem, SidebarItemCategory } from "types"
import { Sidebar } from "types"
const TagOperation = dynamic<TagOperationProps>(
async () => import("../Operation")
) as React.FC<TagOperationProps>
export type TagPathsProps = {
tag: OpenAPIV3.TagObject
tag: TagObject
paths: PathsObject
} & React.HTMLAttributes<HTMLDivElement>
const TagPaths = ({ tag, className, paths }: TagPathsProps) => {
const { items, addItems, findItemInSection } = useSidebar()
const { shownSidebar, addItems } = useSidebar()
const { loading } = useLoading()
useEffect(() => {
if (!shownSidebar) {
return
}
if (paths) {
const parentItem = findItemInSection(
items[SidebarItemSections.DEFAULT],
{ title: tag.name },
false
) as SidebarItemCategory
const pathItems: SidebarItem[] = getTagChildSidebarItems(paths)
if ((parentItem?.children?.length || 0) < pathItems.length) {
const parentItem = findSidebarItem({
sidebarItems:
"items" in shownSidebar
? shownSidebar.items
: shownSidebar.children || [],
item: { title: tag.name, type: "category" },
checkChildren: false,
}) as Sidebar.SidebarItemCategory
const pathItems: Sidebar.SidebarItem[] = getTagChildSidebarItems(paths)
const targetLength =
pathItems.length + (tag["x-associatedSchema"] ? 1 : 0)
if ((parentItem.children?.length || 0) < targetLength) {
addItems(pathItems, {
section: SidebarItemSections.DEFAULT,
sidebar_id: shownSidebar.sidebar_id,
parent: {
type: "category",
title: tag.name,
@@ -46,7 +54,7 @@ const TagPaths = ({ tag, className, paths }: TagPathsProps) => {
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [paths])
}, [paths, shownSidebar?.sidebar_id])
return (
<Suspense>

View File

@@ -1,6 +1,6 @@
"use client"
import { Suspense, useEffect, useMemo, useRef } from "react"
import { Suspense, useEffect, useMemo } from "react"
import { SchemaObject } from "../../../../types/openapi"
import TagOperationParameters from "../../Operation/Parameters"
import {
@@ -13,7 +13,6 @@ import {
useScrollController,
useSidebar,
} from "docs-ui"
import { SidebarItemSections } from "types"
import getSectionId from "../../../../utils/get-section-id"
import DividedLayout from "../../../../layouts/Divided"
import SectionContainer from "../../../Section/Container"
@@ -30,7 +29,7 @@ export type TagSectionSchemaProps = {
}
const TagSectionSchema = ({ schema, tagName }: TagSectionSchemaProps) => {
const { addItems, setActivePath, activePath } = useSidebar()
const { addItems, setActivePath, activePath, shownSidebar } = useSidebar()
const { displayedArea } = useArea()
const formattedName = useMemo(
() => singular(tagName).replaceAll(" ", ""),
@@ -58,6 +57,9 @@ const TagSectionSchema = ({ schema, tagName }: TagSectionSchemaProps) => {
}, [isBrowser, scrollableElement])
useEffect(() => {
if (!shownSidebar) {
return
}
addItems(
[
{
@@ -69,7 +71,7 @@ const TagSectionSchema = ({ schema, tagName }: TagSectionSchemaProps) => {
},
],
{
section: SidebarItemSections.DEFAULT,
sidebar_id: shownSidebar.sidebar_id,
parent: {
type: "category",
title: tagName,
@@ -80,7 +82,7 @@ const TagSectionSchema = ({ schema, tagName }: TagSectionSchemaProps) => {
}
)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [formattedName])
}, [formattedName, shownSidebar?.sidebar_id])
useEffect(() => {
if (!isBrowser) {

View File

@@ -85,23 +85,24 @@ const TagSectionComponent = ({ tag }: TagSectionProps) => {
)
useEffect(() => {
if (!isBrowser) {
if (!isBrowser || !activePath || !activePath.includes(slugTagName)) {
return
}
if (activePath && activePath.includes(slugTagName)) {
const tagName = activePath.split("_")
if (tagName.length === 1 && tagName[0] === slugTagName) {
const elm = document.getElementById(tagName[0])
if (elm && !checkElementInViewport(elm, 0)) {
scrollToTop(
elm.offsetTop + (elm.offsetParent as HTMLElement)?.offsetTop,
0
)
}
} else if (tagName.length > 1 && tagName[0] === slugTagName) {
setLoadData(true)
const tagName = activePath.split("_")
if (tagName[0] !== slugTagName) {
return
}
if (tagName.length === 1) {
const elm = document.getElementById(tagName[0])
if (elm && !checkElementInViewport(elm, 0)) {
scrollToTop(
elm.offsetTop + (elm.offsetParent as HTMLElement)?.offsetTop,
0
)
}
} else if (tagName.length > 1) {
setLoadData(true)
}
}, [slugTagName, activePath, isBrowser])