docs: DX and performance improvements in API reference (#9430)
- Improve scroll behavior between active sections - Improve lag when clicking on a sidebar item - Refactor internal working of the `SidebarProvider` to find active items faster. - Use Next.js's `useRouter` hook for changing the hash (since they added the option to disable scroll) - Change `isBrowser` from a hook to a provider since it's widely used across applications. - Other general improvements and fixes. Closes DOCS-952
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
import clsx from "clsx"
|
||||
import { useActiveOnScroll, useSidebar } from "docs-ui"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { useEffect, useRef } from "react"
|
||||
|
||||
export type SectionProps = {
|
||||
@@ -20,6 +21,7 @@ const Section = ({
|
||||
useDefaultIfNoActive: false,
|
||||
})
|
||||
const { setActivePath } = useSidebar()
|
||||
const router = useRouter()
|
||||
|
||||
useEffect(() => {
|
||||
if ("scrollRestoration" in history) {
|
||||
@@ -30,7 +32,9 @@ const Section = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (activeItemId.length) {
|
||||
history.pushState({}, "", `#${activeItemId}`)
|
||||
router.push(`#${activeItemId}`, {
|
||||
scroll: false,
|
||||
})
|
||||
setActivePath(activeItemId)
|
||||
}
|
||||
}, [activeItemId])
|
||||
|
||||
@@ -12,6 +12,7 @@ import type { TagOperationCodeSectionProps } from "./CodeSection"
|
||||
import TagsOperationDescriptionSection from "./DescriptionSection"
|
||||
import DividedLayout from "@/layouts/Divided"
|
||||
import { useLoading } from "@/providers/loading"
|
||||
import { useRouter } from "next/navigation"
|
||||
import SectionDivider from "../../Section/Divider"
|
||||
import checkElementInViewport from "../../../utils/check-element-in-viewport"
|
||||
|
||||
@@ -33,7 +34,8 @@ const TagOperation = ({
|
||||
endpointPath,
|
||||
className,
|
||||
}: TagOperationProps) => {
|
||||
const { setActivePath } = useSidebar()
|
||||
const { activePath, setActivePath } = useSidebar()
|
||||
const router = useRouter()
|
||||
const [show, setShow] = useState(false)
|
||||
const path = useMemo(
|
||||
() => getSectionId([...(operation.tags || []), operation.operationId]),
|
||||
@@ -57,10 +59,14 @@ const TagOperation = ({
|
||||
}
|
||||
setShow(true)
|
||||
}
|
||||
// can't use next router as it doesn't support
|
||||
// changing url without scrolling
|
||||
history.replaceState({}, "", `#${path}`)
|
||||
setActivePath(path)
|
||||
if (location.hash !== path) {
|
||||
router.push(`#${path}`, {
|
||||
scroll: false,
|
||||
})
|
||||
}
|
||||
if (activePath !== path) {
|
||||
setActivePath(path)
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -77,7 +83,7 @@ const TagOperation = ({
|
||||
)
|
||||
|
||||
const scrollIntoView = useCallback(() => {
|
||||
if (nodeRef.current && !checkElementInViewport(nodeRef.current, 10)) {
|
||||
if (nodeRef.current && !checkElementInViewport(nodeRef.current, 0)) {
|
||||
const elm = nodeRef.current as HTMLElement
|
||||
scrollToTop(
|
||||
elm.offsetTop + (elm.offsetParent as HTMLElement)?.offsetTop,
|
||||
|
||||
@@ -72,7 +72,7 @@ const TagSectionSchema = ({ schema, tagName }: TagSectionSchemaProps) => {
|
||||
useEffect(() => {
|
||||
if (schemaSlug === (activePath || location.hash.replace("#", ""))) {
|
||||
const elm = document.getElementById(schemaSlug) as HTMLElement
|
||||
if (!checkElementInViewport(elm, 40)) {
|
||||
if (!checkElementInViewport(elm, 0)) {
|
||||
scrollToElement(elm)
|
||||
}
|
||||
}
|
||||
@@ -85,7 +85,7 @@ const TagSectionSchema = ({ schema, tagName }: TagSectionSchemaProps) => {
|
||||
const section = entry.target
|
||||
|
||||
if (
|
||||
(inView || checkElementInViewport(section, 40)) &&
|
||||
(inView || checkElementInViewport(section, 10)) &&
|
||||
activePath !== schemaSlug
|
||||
) {
|
||||
// can't use next router as it doesn't support
|
||||
|
||||
@@ -20,7 +20,7 @@ import { useArea } from "@/providers/area"
|
||||
import SectionDivider from "../../Section/Divider"
|
||||
import clsx from "clsx"
|
||||
import { Feedback, Loading, Link } from "docs-ui"
|
||||
import { usePathname } from "next/navigation"
|
||||
import { usePathname, useRouter } from "next/navigation"
|
||||
import formatReportLink from "@/utils/format-report-link"
|
||||
import { SchemaObject, TagObject } from "@/types/openapi"
|
||||
import useSWR from "swr"
|
||||
@@ -49,11 +49,12 @@ const MDXContentClient = dynamic<MDXContentClientProps>(
|
||||
|
||||
const TagSection = ({ tag }: TagSectionProps) => {
|
||||
const { activePath, setActivePath } = useSidebar()
|
||||
const router = useRouter()
|
||||
const [loadPaths, setLoadPaths] = useState(false)
|
||||
const slugTagName = useMemo(() => getSectionId([tag.name]), [tag])
|
||||
const { area } = useArea()
|
||||
const pathname = usePathname()
|
||||
const { scrollableElement, scrollToElement } = useScrollController()
|
||||
const { scrollableElement, scrollToTop } = useScrollController()
|
||||
const { data } = useSWR<{
|
||||
schema: SchemaObject
|
||||
}>(
|
||||
@@ -84,10 +85,14 @@ const TagSection = ({ tag }: TagSectionProps) => {
|
||||
// ensure that the hash link doesn't change if it links to an inner path
|
||||
const currentHashArr = location.hash.replace("#", "").split("_")
|
||||
if (currentHashArr.length < 2 || currentHashArr[0] !== slugTagName) {
|
||||
// can't use next router as it doesn't support
|
||||
// changing url without scrolling
|
||||
history.replaceState({}, "", `#${slugTagName}`)
|
||||
setActivePath(slugTagName)
|
||||
if (location.hash !== slugTagName) {
|
||||
router.push(`#${slugTagName}`, {
|
||||
scroll: false,
|
||||
})
|
||||
}
|
||||
if (activePath !== slugTagName) {
|
||||
setActivePath(slugTagName)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -98,8 +103,11 @@ const TagSection = ({ tag }: TagSectionProps) => {
|
||||
const tagName = activePath.split("_")
|
||||
if (tagName.length === 1 && tagName[0] === slugTagName) {
|
||||
const elm = document.getElementById(tagName[0])
|
||||
if (elm && !checkElementInViewport(elm, 10)) {
|
||||
scrollToElement(elm)
|
||||
if (elm && !checkElementInViewport(elm, 0)) {
|
||||
scrollToTop(
|
||||
elm.offsetTop + (elm.offsetParent as HTMLElement)?.offsetTop,
|
||||
0
|
||||
)
|
||||
}
|
||||
} else if (tagName.length > 1 && tagName[0] === slugTagName) {
|
||||
setLoadPaths(true)
|
||||
|
||||
@@ -13,6 +13,7 @@ import { ExpandedDocument } from "@/types/openapi"
|
||||
import getTagChildSidebarItems from "@/utils/get-tag-child-sidebar-items"
|
||||
import { SidebarItem, SidebarItemSections } from "types"
|
||||
import basePathUrl from "../../utils/base-path-url"
|
||||
import { useRouter } from "next/navigation"
|
||||
|
||||
const TagSection = dynamic<TagSectionProps>(
|
||||
async () => import("./Section")
|
||||
@@ -31,8 +32,9 @@ const Tags = () => {
|
||||
const [loadData, setLoadData] = useState<boolean>(false)
|
||||
const [expand, setExpand] = useState<string>("")
|
||||
const { baseSpecs, setBaseSpecs } = useBaseSpecs()
|
||||
const { addItems, setActivePath } = useSidebar()
|
||||
const { activePath, addItems, setActivePath } = useSidebar()
|
||||
const { area, prevArea } = useArea()
|
||||
const router = useRouter()
|
||||
|
||||
const { data } = useSWR<ExpandedDocument>(
|
||||
loadData && !baseSpecs
|
||||
@@ -89,8 +91,14 @@ const Tags = () => {
|
||||
children: childItems,
|
||||
loaded: childItems.length > 0,
|
||||
onOpen: () => {
|
||||
history.pushState({}, "", `#${tagPathName}`)
|
||||
setActivePath(tagPathName)
|
||||
if (location.hash !== tagPathName) {
|
||||
router.push(`#${tagPathName}`, {
|
||||
scroll: false,
|
||||
})
|
||||
}
|
||||
if (activePath !== tagPathName) {
|
||||
setActivePath(tagPathName)
|
||||
}
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user