From 130f022aa4ba225bad5b339ae572be6297a3d278 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 6 Sep 2023 01:57:01 -0600 Subject: [PATCH] feat: add prev and next links for doc pages --- .../docs-nav/docs-nav.component.tsx | 8 ++--- src/app/docs/[project]/[[...slug]]/page.tsx | 26 ++++++++++++++ src/components/mdx/mdx.component.tsx | 2 +- src/data/docs/docs.data.ts | 35 +++++++++++++------ 4 files changed, 54 insertions(+), 17 deletions(-) diff --git a/src/app/docs/[project]/[[...slug]]/_components/docs-nav/docs-nav.component.tsx b/src/app/docs/[project]/[[...slug]]/_components/docs-nav/docs-nav.component.tsx index 8909c1b..9dd1f12 100644 --- a/src/app/docs/[project]/[[...slug]]/_components/docs-nav/docs-nav.component.tsx +++ b/src/app/docs/[project]/[[...slug]]/_components/docs-nav/docs-nav.component.tsx @@ -40,16 +40,14 @@ export function DocsNav({ title, repo, slug, menus }: DocsNavProps) { > {({ setIsExpanded }) => menus.map((menu) => ( -
+

{menu.label}

{menu.items.map((item) => { - const href = menu.href.concat(item.href) - return ( setIsExpanded(false)} diff --git a/src/app/docs/[project]/[[...slug]]/page.tsx b/src/app/docs/[project]/[[...slug]]/page.tsx index 22f5864..48425b9 100644 --- a/src/app/docs/[project]/[[...slug]]/page.tsx +++ b/src/app/docs/[project]/[[...slug]]/page.tsx @@ -1,3 +1,5 @@ +import { IconChevronLeft, IconChevronRight } from "@tabler/icons-react" +import Link from "next/link" import { notFound } from "next/navigation" import { Fragment } from "react" @@ -6,6 +8,7 @@ import { Main } from "@/components/main" import { MDX } from "@/components/mdx" import { getDocsConfig, getDocsPage, getDocsParams } from "@/data/docs" import { mergeMetadata, siteUrl } from "@/utils/seo" +import { cn } from "@/utils/ui" import { DocsNav } from "./_components/docs-nav" @@ -71,7 +74,30 @@ export default async function DocsPage({ params: { project, slug = [] } }: DocsP imgAlt={page.meta.imgAlt} /> +
+
+ {page.prev ? :
} + {page.next ? :
} +
) } + +function PageLink({ direction, href, label }: { direction: "prev" | "next"; href: string; label: string }) { + return ( + + {direction === "prev" ? ( + + ) : null} + {direction === "prev" ? "Previous" : "Next"} + {label} + {direction === "next" ? ( + + ) : null} + + ) +} diff --git a/src/components/mdx/mdx.component.tsx b/src/components/mdx/mdx.component.tsx index 746f60b..b64476c 100644 --- a/src/components/mdx/mdx.component.tsx +++ b/src/components/mdx/mdx.component.tsx @@ -28,7 +28,7 @@ export function MDX({ className, components, ...props }: MDXProps) { ), h2: ({ children, ...props }) => ( -

+

{children}

), diff --git a/src/data/docs/docs.data.ts b/src/data/docs/docs.data.ts index 477f8e7..8ce63b3 100644 --- a/src/data/docs/docs.data.ts +++ b/src/data/docs/docs.data.ts @@ -5,7 +5,7 @@ const projects = [{ id: "devtools", repo: "kevinwolfcr/devtools" }] async function getFile(url: URL) { try { - const request = await fetch(url, { next: { revalidate: 60 } }) + const request = await fetch(url, { next: { revalidate: process.env.NODE_ENV === "development" ? 0 : 60 } }) return await request.text() } catch (err) { throw new Error(`Error fetching ${url.toString()}: ${err instanceof Error ? err.message : "Unknown error"}`) @@ -27,7 +27,6 @@ export async function getDocsConfig(projectId: string) { description: z.string(), menus: z.array( z.object({ - href: z.string(), label: z.string(), items: z.array(z.object({ href: z.string(), label: z.string(), file: z.string() })), }), @@ -35,11 +34,23 @@ export async function getDocsConfig(projectId: string) { }) const config = projectConfigSchema.parse(JSON.parse(await getFile(new URL("./config.json", baseUrl)))) - const pages: Record = {} - for (const menu of config.menus) { - for (const item of menu.items) { - pages[menu.href.concat(item.href).replace(/^\//, "")] = new URL(item.file, baseUrl) + const pages: Record< + string, + { + url: URL + prev: z.infer["menus"][number]["items"][number] | null + next: z.infer["menus"][number]["items"][number] | null + } + > = {} + + for (const [menuIndex, menu] of config.menus.entries()) { + for (const [itemIndex, item] of menu.items.entries()) { + pages[item.href] = { + url: new URL(item.file, baseUrl), + prev: menu.items[itemIndex - 1] || config.menus[menuIndex - 1]?.items.findLast(Boolean) || null, + next: menu.items[itemIndex + 1] || config.menus[menuIndex + 1]?.items[0] || null, + } } } @@ -57,7 +68,7 @@ export async function getDocsParams() { for (const project of projects) { for (const menu of (await getDocsConfig(project.id))?.menus || []) { for (const item of menu.items) { - params.push({ project: project.id, slug: menu.href.concat(item.href).replace(/^\//, "").split("/") }) + params.push({ project: project.id, slug: item.href.split("/").filter(Boolean) }) } } } @@ -70,10 +81,10 @@ export async function getDocsPage(projectId: string, slug: string[]) { const config = await getDocsConfig(projectId) if (!config) return null - const url = config.pages[slug.join("/")] - if (!url) return null + const page = config.pages[`/${slug.join("/")}`] + if (!page) return null - const { data, content } = matter(await getFile(url)) + const { data, content } = matter(await getFile(page.url)) const pageMetaSchema = z.object({ title: z.string().optional(), @@ -83,8 +94,10 @@ export async function getDocsPage(projectId: string, slug: string[]) { }) return { - url, + url: page.url, meta: pageMetaSchema.parse(data), content, + prev: page.prev, + next: page.next, } }