/**
* 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;