- Pin all @milkdown/* packages to exact 7.21.1, add phantom-dep plugins as direct deps - Fix TextSelection.create type error in useMilkdown.ts - Fix Backspace auto-pair dead-code bug (unreachable due to PAIRS check order) - Extract parseHeadings/Heading to outlineUtils.ts (react-refresh fix) - Extract StatusBar item components to StatusBarItems.tsx (react-refresh fix) - Configure npm allowScripts for electron and esbuild postinstall - Update QUALITY_REVIEW_REPORT.json with all fixes applied - Bump version to 0.3.12
72 lines
1.9 KiB
TypeScript
72 lines
1.9 KiB
TypeScript
import React, { memo } from 'react'
|
|
import type { Heading } from './outlineUtils'
|
|
|
|
// --- Component ---
|
|
|
|
interface OutlinePanelProps {
|
|
headings: Heading[]
|
|
onNavigate: (heading: Heading, index: number) => void
|
|
activeHeadingIndex: number | null
|
|
}
|
|
|
|
interface OutlineItemProps {
|
|
heading: Heading
|
|
index: number
|
|
isActive: boolean
|
|
onNavigate: (heading: Heading, index: number) => void
|
|
}
|
|
|
|
const OutlineItem = memo(function OutlineItem({
|
|
heading,
|
|
index,
|
|
isActive,
|
|
onNavigate
|
|
}: OutlineItemProps) {
|
|
return (
|
|
<button
|
|
className={`outline-item outline-level-${heading.level}${isActive ? ' active' : ''}`}
|
|
onClick={() => onNavigate(heading, index)}
|
|
title={heading.text}
|
|
aria-label={`跳转到标题:${heading.text}`}
|
|
style={{ paddingLeft: `${8 + (heading.level - 1) * 12}px` }}
|
|
>
|
|
<span className="outline-level-dot" />
|
|
<span className="outline-item-text">{heading.text}</span>
|
|
</button>
|
|
)
|
|
})
|
|
|
|
export const OutlinePanel = memo(function OutlinePanel({
|
|
headings,
|
|
onNavigate,
|
|
activeHeadingIndex
|
|
}: OutlinePanelProps) {
|
|
if (headings.length === 0) {
|
|
return (
|
|
<div className="outline-panel" role="region" aria-label="文档大纲">
|
|
<div className="outline-header">文档大纲</div>
|
|
<div className="outline-empty">当前文档无标题</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="outline-panel" role="region" aria-label="文档大纲">
|
|
<div className="outline-header">文档大纲</div>
|
|
<div className="outline-list" role="list" aria-label="标题列表">
|
|
{headings.map((h, i) => (
|
|
<OutlineItem
|
|
key={`${h.text}-${i}`}
|
|
heading={h}
|
|
index={i}
|
|
isActive={i === activeHeadingIndex}
|
|
onNavigate={onNavigate}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)
|
|
})
|
|
|
|
OutlinePanel.displayName = 'OutlinePanel'
|