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}
);
}