import { Box } from '@luke-ui/react/box'; import { Grid } from '@luke-ui/react/grid'; import { Icon } from '@luke-ui/react/icon'; import { Lockup, LockupBody, LockupRail } from '@luke-ui/react/lockup'; import { Stack } from '@luke-ui/react/stack'; import { Text } from '@luke-ui/react/text'; import { vars } from '@luke-ui/react/theme'; import { useRouterState } from '@tanstack/react-router'; import type { Folder, Item, Node, Root } from 'fumadocs-core/page-tree'; import { findPath, flattenTree } from 'fumadocs-core/page-tree'; import type { CSSProperties, ReactNode } from 'react'; import { DocsCardLink } from '../docs-card.js'; import * as styles from './docs-article.css.js'; /** * Links to the pages before and after the current one. The order is the sidebar order within the * section that contains the page, so a section's index page comes first. */ export function DocsPager({ tree }: { tree: Root }) { const pathname = useRouterState({ select: (state) => state.location.pathname }); const { next, previous } = findNeighbours(tree, pathname); if (!previous && !next) return null; return ( {previous ? : null} {next ? : null} ); } const footerRule = { borderBlockStart: `1px solid ${vars.color.border.decorative}`, } as const satisfies CSSProperties; const TRAILING_SLASH_PATTERN = /\/+$/; function normalisePathname(pathname: string) { return pathname.replace(TRAILING_SLASH_PATTERN, '') || '/'; } function findNeighbours(tree: Root, pathname: string): { next?: Item; previous?: Item } { const url = normalisePathname(pathname); const isCurrentPage = (node: Node) => node.type === 'page' && normalisePathname(node.url) === url; for (let current: Root | undefined = tree; current; current = current.fallback) { const path = findPath(current.children, isCurrentPage); if (!path) continue; const section = path .filter((node): node is Folder => node.type === 'folder' && node.root === true) .at(-1); const pages = flattenTree(section ? [section] : current.children).filter( (page) => !page.external, ); const index = pages.findIndex((page) => normalisePathname(page.url) === url); return { next: pages[index + 1], previous: pages[index - 1] }; } return {}; } type PagerDirection = 'next' | 'previous'; const pagerDirection = { previous: { align: 'start', chevron: 'chevronLeft', fallbackDescription: 'Previous Page', }, next: { align: 'end', chevron: 'chevronRight', fallbackDescription: 'Next Page', }, } as const satisfies Record< PagerDirection, { align: 'end' | 'start'; chevron: 'chevronLeft' | 'chevronRight'; fallbackDescription: string; } >; function PagerLink({ direction, page }: { direction: PagerDirection; page: Item }) { const { align, fallbackDescription } = pagerDirection[direction]; const description: string = (() => { if (typeof page.description === 'string' && page.description.length > 0) { return page.description; } return fallbackDescription; })(); return ( {description} ); } function PagerTitle({ direction, name }: { direction: PagerDirection; name: ReactNode }) { const chevron = ( ); return ( {direction === 'previous' && chevron} {name} {direction === 'next' && chevron} ); }