/** * DocumentOutline Component * * Displays a hierarchical document outline (table of contents) with: * - Nested heading structure * - Active item highlighting * - Click navigation to sections * - Section numbering * * @module components/DocumentOutline */ import React from 'react'; import { Tree } from 'antd'; import type { DataNode } from 'antd/es/tree'; import type { OutlineItem } from '../../services/documentContentService'; import { FileTextOutlined } from '@ant-design/icons'; // ── Styles ──────────────────────────────────────────────────────────────── const containerStyle: React.CSSProperties = { height: '100%', display: 'flex', flexDirection: 'column', overflow: 'hidden', backgroundColor: '#fafafa', }; const headerStyle: React.CSSProperties = { padding: '16px', borderBottom: '1px solid #e8e8e8', backgroundColor: '#ffffff', fontWeight: 600, fontSize: '14px', color: '#262626', display: 'flex', alignItems: 'center', gap: '8px', }; const treeContainerStyle: React.CSSProperties = { flex: 1, overflow: 'auto', padding: '12px', }; // ── Component Props ───────────────────────────────────────────────────────── export interface DocumentOutlineProps { /** Outline data (hierarchical structure) */ outline: OutlineItem[]; /** Currently active item ID */ activeId?: string; /** Callback when outline item is clicked */ onItemClick: (item: OutlineItem) => void; } // ── Helper Functions ──────────────────────────────────────────────────────── /** * Convert OutlineItem to Ant Design Tree DataNode */ const convertToTreeData = (items: OutlineItem[], onItemClick: (item: OutlineItem) => void): DataNode[] => { return items.map((item) => ({ key: item.id, title: ( onItemClick(item)} > {item.sectionNumber && {item.sectionNumber}} {item.text} ), children: item.children && item.children.length > 0 ? convertToTreeData(item.children, onItemClick) : undefined, })); }; // ── Component ──────────────────────────────────────────────────────────────── /** * DocumentOutline * * Renders a hierarchical outline of the document with navigation support. * * @example * ```tsx * scrollToSection(item.id)} * /> * ``` */ export const DocumentOutline: React.FC = ({ outline, activeId, onItemClick, }) => { // Convert outline to tree data const treeData = convertToTreeData(outline, onItemClick); // Get all keys for default expansion const getAllKeys = (items: OutlineItem[]): string[] => { let keys: string[] = []; items.forEach((item) => { keys.push(item.id); if (item.children && item.children.length > 0) { keys = keys.concat(getAllKeys(item.children)); } }); return keys; }; const expandedKeys = getAllKeys(outline); // Handle empty outline if (!outline || outline.length === 0) { return (
文档大纲
暂无大纲
); } return (
{/* Header */}
文档大纲
{/* Tree */}
); }; export default DocumentOutline;