'use client'
import { type IDocsConfig, useDocsConfig } from '@duck-docs/context'
import type { INavItem, INavItemWithChildren } from '@duck-docs/types/nav'
import { cn } from '@gentleduck/libs/cn'
import { buttonVariants } from '@gentleduck/registry-ui/button'
import { ArrowLeft, ArrowRight, ChevronLeft, ChevronRight } from 'lucide-react'
import Link from 'next/link'
import * as React from 'react'
interface IDocsPagerProps {
doc: {
slug?: string
title: string
}
/** Override the global docsConfig with a package-specific sidebar (used by per-package slug pages). */
config?: IDocsConfig
}
export function DocsPagerBottom({ doc, config }: IDocsPagerProps) {
const fallbackConfig = useDocsConfig()
const resolvedConfig = config ?? fallbackConfig
const pager = React.useMemo(() => getPagerForDoc(doc, resolvedConfig), [doc, resolvedConfig])
if (!pager?.prev?.href && !pager?.next?.href) {
return null
}
return (
{pager?.prev?.href && (
{pager.prev.title}
)}
{pager?.next?.href && (
{pager.next.title}
)}
)
}
export function DocsPagerTop({ doc, config }: IDocsPagerProps) {
const fallbackConfig = useDocsConfig()
const resolvedConfig = config ?? fallbackConfig
const pager = React.useMemo(() => getPagerForDoc(doc, resolvedConfig), [doc, resolvedConfig])
if (!pager?.prev?.href && !pager?.next?.href) {
return null
}
return (
{pager?.prev?.href && (
svg]:!size-4 size-8 items-center md:size-7',
size: 'sm',
variant: 'secondary',
}),
)}
href={pager.prev.href}
scroll>
)}
{pager?.next?.href && (
svg]:!size-4 ml-2 size-8 items-center md:size-7',
size: 'sm',
variant: 'secondary',
}),
)}
href={pager.next.href}
scroll>
)}
)
}
export function getPagerForDoc(doc: IDocsPagerProps['doc'], docsConfig: IDocsConfig) {
const allNav = [...(docsConfig.sidebarNav ?? []), ...(docsConfig.chartsNav ?? [])]
const flattenedLinks = [null, ...flatten(allNav), null]
// Normalize slug: ensure leading /, strip leading /docs/ (velite prefix), strip trailing /index
const normalizedSlug = `/${doc.slug ?? ''}`
.replace(/\/+/g, '/')
.replace(/^\/docs\//, '/')
.replace(/\/index$/, '')
const activeIndex = flattenedLinks.findIndex((link) => {
if (!link?.href) return false
return link.href.replace(/\/index$/, '') === normalizedSlug
})
const prev = activeIndex > 0 ? flattenedLinks[activeIndex - 1] : null
const next = activeIndex < flattenedLinks.length - 1 ? flattenedLinks[activeIndex + 1] : null
return {
next,
prev,
}
}
function flatten(links: INavItemWithChildren[]): INavItem[] {
return links
.reduce((flat, link) => {
if (link.items?.length) {
// Include the parent itself if it has an href (it's a real page), then flatten children
return flat.concat(link.href ? [link] : [], flatten(link.items))
}
return flat.concat(link)
}, [])
.filter((link) => !link?.disabled)
}