diff --git a/src/components/docs-breadcrumbs.jsx b/src/components/docs-breadcrumbs.jsx new file mode 100644 index 00000000..7ab8ccbb --- /dev/null +++ b/src/components/docs-breadcrumbs.jsx @@ -0,0 +1,69 @@ +import { ChevronRightIcon } from "@heroicons/react/24/outline"; +import { useRouter } from "next/router"; +import Link from "@/components/link"; +import { normalizeHref } from "@/utils/strings"; + +export default function DocsBreadcrumbs({ routes }) { + const generateBreadcrumbs = (breadcrumbRoutes, currentRoute) => { + const breadcrumbs = []; + + const traverse = (navigationRoutes, path) => { + const currentIndex = navigationRoutes.findIndex( + (route) => normalizeHref(route.route) === normalizeHref(path), + ); + + if (currentIndex === -1) { + return; + } + + breadcrumbs.unshift(navigationRoutes[currentIndex]); + + const parentPath = navigationRoutes[currentIndex].parentPath; + if (!parentPath) { + return; + } + + traverse(navigationRoutes, parentPath); + }; + + traverse(routes, currentRoute); + return breadcrumbs; + }; + + const router = useRouter(); + const currentPath = router.pathname; + const breadcrumbLinks = generateBreadcrumbs(routes, currentPath); + + if (breadcrumbLinks.length === 0 || currentPath === "/docs") { + return; + } + + const homeRoute = routes[0]; + homeRoute.title = "Docs"; + breadcrumbLinks.unshift(homeRoute); + + return ( +
+
+ {breadcrumbLinks.map((breadcrumb, index) => + normalizeHref(breadcrumb.route) === normalizeHref(currentPath) ? ( + {breadcrumb.title} + ) : ( + + {breadcrumb.title} + + + + + ), + )} +
+
+ ); +} diff --git a/src/components/docs-layout.jsx b/src/components/docs-layout.jsx index 9ea471a3..37f9006b 100644 --- a/src/components/docs-layout.jsx +++ b/src/components/docs-layout.jsx @@ -4,17 +4,38 @@ import { DisclosurePanel, } from "@headlessui/react"; import { ChevronRightIcon, ChevronDownIcon } from "@heroicons/react/24/outline"; +import DocsBreadcrumbs from "./docs-breadcrumbs"; +import DocsPreviousNextLinks from "./docs-previous-next-link"; import OnThisPageNav from "./on-this-page-nav"; import DocsNav from "@/components/docs-nav"; import routes from "@/pages/docs/nav.json"; import "rehype-callouts/theme/vitepress"; +const flattenRoutes = (routeConfig) => { + const flatRoutes = []; + + const traverse = (innerRoutes, parentPath = "") => { + for (const route of innerRoutes) { + const fullPath = `${parentPath}${route.route}`; + flatRoutes.push({ ...route, fullPath, parentPath }); + + if (route.children) { + traverse(route.children, `${parentPath}${route.route}`); + } + } + }; + + traverse(routeConfig); + return flatRoutes; +}; + export default function DocumentPage({ children, metadata }) { + const flatRoutes = flattenRoutes(routes); return ( <> @@ -22,21 +43,26 @@ export default function DocumentPage({ children, metadata }) { Menu - + -
+
-
+
+ {metadata?.title && ( -

{metadata.title}

+

{metadata.title}

)} {children} +
diff --git a/src/components/docs-nav.jsx b/src/components/docs-nav.jsx index a36d0ae2..e8b6189e 100644 --- a/src/components/docs-nav.jsx +++ b/src/components/docs-nav.jsx @@ -1,6 +1,6 @@ import { Fragment } from "react"; import Link from "@/components/link"; -import { classNames } from "@/utils/strings"; +import { normalizeHref, classNames } from "@/utils/strings"; export default function DocsNav({ as, routes, level = 0, className }) { const As = as || Fragment; @@ -29,7 +29,7 @@ function NavItem({ item, level, ...props }) {
  • normalizeHref(route.route) === normalizeHref(currentPath), + ); + + if (currentIndex === -1) { + return; + } + + const previousPage = routes[currentIndex - 1]; + const nextPage = routes[currentIndex + 1]; + + return ( + + ); +} diff --git a/src/components/footer.jsx b/src/components/footer.jsx index d08daedb..68a9d100 100644 --- a/src/components/footer.jsx +++ b/src/components/footer.jsx @@ -7,7 +7,7 @@ export default function Footer() {
    -
    +

    Powered by{" "} +

    - + - +
    {socialIcons.map(({ url, name, icon: Icon }) => ( { ); }); -export default function PrimaryMenu({ className }) { +export default function PrimaryMenu({ isMenuOpen, setIsMenuOpen, className }) { return (