75 lines
2.1 KiB
TypeScript
75 lines
2.1 KiB
TypeScript
|
|
import { useState, useEffect, useCallback } from 'react'
|
||
|
|
import { DocTree } from './DocTree'
|
||
|
|
import { DocContent } from './DocContent'
|
||
|
|
import { parseMarkdown, buildFileTree } from '@/lib/parser'
|
||
|
|
import type { DocFile, ParsedDoc } from '@/lib/types'
|
||
|
|
import { docs } from '@/data/docs'
|
||
|
|
|
||
|
|
const DOCS_FILES: Record<string, string> = docs
|
||
|
|
|
||
|
|
export const ApiDocViewer = () => {
|
||
|
|
const [fileTree, setFileTree] = useState<DocFile[]>([])
|
||
|
|
const [selectedPath, setSelectedPath] = useState<string | undefined>()
|
||
|
|
const [currentDoc, setCurrentDoc] = useState<ParsedDoc | null>(null)
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const files = Object.keys(DOCS_FILES)
|
||
|
|
const basePath = '/docs'
|
||
|
|
const tree = buildFileTree(files, basePath)
|
||
|
|
setFileTree(tree)
|
||
|
|
|
||
|
|
if (files.length > 0) {
|
||
|
|
setSelectedPath(files[0])
|
||
|
|
}
|
||
|
|
}, [])
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (selectedPath) {
|
||
|
|
const content = DOCS_FILES[selectedPath]
|
||
|
|
if (content) {
|
||
|
|
const parsed = parseMarkdown(content)
|
||
|
|
setCurrentDoc(parsed)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}, [selectedPath])
|
||
|
|
|
||
|
|
const handleSelect = useCallback((file: DocFile) => {
|
||
|
|
if (!file.isDir) {
|
||
|
|
setSelectedPath(file.relativePath)
|
||
|
|
}
|
||
|
|
}, [])
|
||
|
|
|
||
|
|
const handleReferenceClick = useCallback((ref: string) => {
|
||
|
|
const normalizedRef = ref.replace('.md', '')
|
||
|
|
const allFiles = Object.keys(DOCS_FILES).map(k => k.replace('/docs/', ''))
|
||
|
|
const match = allFiles.find(f => f.replace('.md', '') === normalizedRef)
|
||
|
|
if (match) {
|
||
|
|
setSelectedPath(match)
|
||
|
|
}
|
||
|
|
}, [])
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="flex h-screen bg-[#1e1e1e]">
|
||
|
|
<aside className="w-64 bg-[#252526] border-r border-[#3c3c3c] flex flex-col">
|
||
|
|
<div className="p-3 border-b border-[#3c3c3c]">
|
||
|
|
<h2 className="text-sm font-semibold text-gray-200">API 文档</h2>
|
||
|
|
</div>
|
||
|
|
<div className="flex-1 overflow-hidden">
|
||
|
|
<DocTree
|
||
|
|
files={fileTree}
|
||
|
|
selectedPath={selectedPath}
|
||
|
|
onSelect={handleSelect}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</aside>
|
||
|
|
|
||
|
|
<main className="flex-1 overflow-hidden">
|
||
|
|
<DocContent
|
||
|
|
doc={currentDoc}
|
||
|
|
onReferenceClick={handleReferenceClick}
|
||
|
|
/>
|
||
|
|
</main>
|
||
|
|
</div>
|
||
|
|
)
|
||
|
|
}
|