Skip to content

Commit

Permalink
feat: add prev and next links for doc pages
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinwolfcr committed Sep 6, 2023
1 parent 5532ed3 commit 130f022
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,14 @@ export function DocsNav({ title, repo, slug, menus }: DocsNavProps) {
>
{({ setIsExpanded }) =>
menus.map((menu) => (
<div key={menu.href} className="w-full flex flex-col mb-3">
<div key={menu.label} className="w-full flex flex-col mb-3">
<h4 className="mb-1 typography-2 font-medium">{menu.label}</h4>
{menu.items.map((item) => {
const href = menu.href.concat(item.href)

return (
<Link
key={item.href}
href={href}
aria-current={href === slug ? "page" : undefined}
href={item.href}
aria-current={item.href === slug ? "page" : undefined}
aria-label={item.label}
className="border-l border-base-6 aria-[current=page]:border-accent-8 typography-2 text-dimmed hover:text-base aria-[current=page]:text-base aria-[current=page]:font-medium py-1 pl-3 transition-colors"
onClick={() => setIsExpanded(false)}
Expand Down
26 changes: 26 additions & 0 deletions src/app/docs/[project]/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
@@ -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"

Expand All @@ -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"

Expand Down Expand Up @@ -71,7 +74,30 @@ export default async function DocsPage({ params: { project, slug = [] } }: DocsP
imgAlt={page.meta.imgAlt}
/>
<MDX source={page.content} />
<hr className="my-6 border-base-6" />
<div className="flex justify-between">
{page.prev ? <PageLink direction="prev" {...page.prev} /> : <div />}
{page.next ? <PageLink direction="next" {...page.next} /> : <div />}
</div>
</Main>
</Fragment>
)
}

function PageLink({ direction, href, label }: { direction: "prev" | "next"; href: string; label: string }) {
return (
<Link
href={href}
className={cn("flex flex-col group relative justify-center", direction === "prev" ? "text-left" : "text-right")}
>
{direction === "prev" ? (
<IconChevronLeft className="absolute hidden lg:block -translate-x-full group-hover:-translate-x-[150%] text-extradimmed opacity-0 group-hover:opacity-100 transition-all will-change-transform" />
) : null}
<span className="typography-2 text-dimmed">{direction === "prev" ? "Previous" : "Next"}</span>
<span className="text-base group-hover:text-accent transition-colors">{label}</span>
{direction === "next" ? (
<IconChevronRight className="absolute right-0 hidden lg:block translate-x-full group-hover:translate-x-[150%] text-extradimmed opacity-0 group-hover:opacity-100 transition-all will-change-transform" />
) : null}
</Link>
)
}
2 changes: 1 addition & 1 deletion src/components/mdx/mdx.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function MDX({ className, components, ...props }: MDXProps) {
</h1>
),
h2: ({ children, ...props }) => (
<h2 className="mt-7 border-b border-base-6 pb-2 typography-7 text-base font-semibold" {...props}>
<h2 className="mt-7 typography-7 text-base font-semibold" {...props}>
{children}
</h2>
),
Expand Down
35 changes: 24 additions & 11 deletions src/data/docs/docs.data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"}`)
Expand All @@ -27,19 +27,30 @@ 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() })),
}),
),
})

const config = projectConfigSchema.parse(JSON.parse(await getFile(new URL("./config.json", baseUrl))))
const pages: Record<string, URL> = {}

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<typeof projectConfigSchema>["menus"][number]["items"][number] | null
next: z.infer<typeof projectConfigSchema>["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,
}
}
}

Expand All @@ -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) })
}
}
}
Expand All @@ -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(),
Expand All @@ -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,
}
}

1 comment on commit 130f022

@vercel
Copy link

@vercel vercel bot commented on 130f022 Sep 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.