import Link from "next/link"; import { CategoryNavigation } from "./CategoryNavigation"; import { Icon } from "@/components/Icon"; import { useEffect, useRef, useState, type ReactNode } from "react"; import type { DocLink } from "@/lib/docs"; import { collectionEntries, collectionIcon, collectionLabel, collectionTone } from "@/lib/collections"; export function Brand() { return ( Tabularium Docs ); } export function Navigation({ docs, current, onNavigate, }: { docs: DocLink[]; current?: string; onNavigate?: () => void; }) { const collection = docs.find((d) => d.slug === current)?.collection; const scopedDocs = docs.filter((d) => d.collection === collection); const groups = [...new Set(scopedDocs.map((d) => d.group))]; const collections = collectionEntries(docs); return ( {collection ? <> c.name === collection)!.entry.slug}`} onClick={onNavigate}> {collectionLabel(collection)} > : 文档中心} {!current && collections.map(({ name, entry }) => {collectionLabel(name)})} {groups.map((group) => ( {group === collection ? "章节目录" : group} {scopedDocs.filter((d) => d.group === group).map((d) => ( {d.slug.endsWith("/overview") ? "概览" : d.title.replace("(草案)", "").replace(/^\d+\s*·\s*/, "")} {d.draft && } ))} ))} ); } export function Shell({ docs, current, children, }: { docs: DocLink[]; current?: string; children: ReactNode; }) { const dialog = useRef(null); const input = useRef(null); const trigger = useRef(null); const [query, setQuery] = useState(""); const [searchDocs, setSearchDocs] = useState(docs); const [searchLoaded, setSearchLoaded] = useState(false); const [searchStatus, setSearchStatus] = useState(""); const [selected, setSelected] = useState(0); const [menu, setMenu] = useState(false); const collection = docs.find((d) => d.slug === current)?.collection; const results = searchDocs.filter((d) => (!collection || d.collection === collection) && `${d.title} ${d.description} ${d.searchText ?? ""}` .toLocaleLowerCase() .includes(query.trim().toLocaleLowerCase()), ); async function openSearch() { setQuery(""); setSelected(0); dialog.current?.showModal(); input.current?.focus(); if (searchLoaded) return; setSearchStatus("正在加载全文索引…"); try { const response = await fetch("/api/search"); if (!response.ok) throw new Error("Search unavailable"); setSearchDocs(await response.json()); setSearchLoaded(true); setSearchStatus(""); } catch { setSearchStatus("全文搜索暂时不可用,当前仅搜索标题和摘要。关闭后重新打开可重试。"); } } function closeSearch() { dialog.current?.close(); trigger.current?.focus(); } useEffect(() => { function keyboard(e: KeyboardEvent) { if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") { e.preventDefault(); trigger.current?.click(); } } document.addEventListener("keydown", keyboard); return () => document.removeEventListener("keydown", keyboard); }, []); return ( 跳到正文 {collection ? `搜索${collectionLabel(collection)}…` : "搜索全部文档…"} ⌘ K { const response = await fetch("/api/auth/logout", { method: "POST", }); if (response.ok) { // Drop cached private document data when ending the session. // eslint-disable-next-line @next/next/no-location-assign-relative-destination window.location.assign("/login"); } }} > 退出 setMenu(!menu)} aria-expanded={menu} aria-controls="mobile-navigation" > {collection ? collectionLabel(collection) : "全部文档"} {menu ? "−" : "+"} {menu && ( setMenu(false)} /> )} {children} { if (e.target === e.currentTarget) closeSearch(); }} onClose={() => trigger.current?.focus()} > { setQuery(e.target.value); setSelected(0); }} onKeyDown={(e) => { if (e.key === "ArrowDown") { e.preventDefault(); setSelected((i) => Math.max(0, Math.min(i + 1, results.length - 1)), ); } if (e.key === "ArrowUp") { e.preventDefault(); setSelected((i) => Math.max(i - 1, 0)); } if (e.key === "Enter") { e.preventDefault(); dialog.current ?.querySelector( `[data-result="${selected}"]`, ) ?.click(); } }} /> Esc {searchStatus && {searchStatus}} {query ? `${results.length} 篇匹配文档` : "浏览文档"} {results.map((d, i) => ( {d.group} {d.title} ↗ {d.description} ))} {!results.length && ( 没有找到相关内容,请尝试其他关键词。 )} ↑ ↓ 选择 Enter 打开 · Esc 关闭 ); }
{group === collection ? "章节目录" : group}
{searchStatus}
{query ? `${results.length} 篇匹配文档` : "浏览文档"}
没有找到相关内容,请尝试其他关键词。