72 lines
2.4 KiB
TypeScript
72 lines
2.4 KiB
TypeScript
|
|
import React from 'react'
|
|||
|
|
import type { TOCItem } from '@/lib/utils'
|
|||
|
|
import { useWallpaper } from '@/stores'
|
|||
|
|
|
|||
|
|
export interface TOCProps {
|
|||
|
|
isOpen: boolean
|
|||
|
|
onClose: () => void
|
|||
|
|
tocItems: TOCItem[]
|
|||
|
|
width: number
|
|||
|
|
onResizeStart: (e: React.MouseEvent) => void
|
|||
|
|
onTOCItemClick: (id: string) => void
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const renderTOCItem = (item: TOCItem, onTOCItemClick: (id: string) => void) => {
|
|||
|
|
const indent = (item.level - 1) * 16
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div key={item.id} className="mb-1">
|
|||
|
|
<div
|
|||
|
|
className="text-gray-600 dark:text-gray-200 hover:text-gray-900 dark:hover:text-white cursor-pointer px-2 py-1 rounded hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
|
|||
|
|
style={{ marginLeft: `${indent}px` }}
|
|||
|
|
onClick={() => onTOCItemClick(item.id)}
|
|||
|
|
>
|
|||
|
|
{item.text}
|
|||
|
|
</div>
|
|||
|
|
{item.children.length > 0 && (
|
|||
|
|
<div className="mt-1">
|
|||
|
|
{item.children.map(child => renderTOCItem(child, onTOCItemClick))}
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export const TOC: React.FC<TOCProps> = ({ isOpen, onClose, tocItems, width, onResizeStart, onTOCItemClick }) => {
|
|||
|
|
const { opacity } = useWallpaper()
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div
|
|||
|
|
className={`absolute left-0 top-0 bottom-0 border-r border-gray-200 dark:border-gray-700/60 flex flex-col z-30 transition-transform duration-300 ease-in-out transform backdrop-blur-sm ${isOpen ? 'translate-x-0' : '-translate-x-full'}`}
|
|||
|
|
style={{
|
|||
|
|
width,
|
|||
|
|
backgroundColor: `rgba(var(--app-toc-bg-rgb), ${opacity})`,
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
<div className="h-12 flex items-center px-4 border-b border-gray-200 dark:border-gray-700/60 font-semibold text-gray-700 dark:text-gray-200 flex justify-between">
|
|||
|
|
<span className="text-base">目录</span>
|
|||
|
|
<button
|
|||
|
|
onClick={onClose}
|
|||
|
|
className="w-8 h-8 hover:bg-gray-100 dark:hover:bg-gray-700 rounded flex items-center justify-center text-2xl"
|
|||
|
|
>
|
|||
|
|
×
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
<div className="flex-1 overflow-y-auto p-4 no-scrollbar">
|
|||
|
|
{tocItems.length > 0 ? (
|
|||
|
|
<div>
|
|||
|
|
{tocItems.map(item => renderTOCItem(item, onTOCItemClick))}
|
|||
|
|
</div>
|
|||
|
|
) : (
|
|||
|
|
<div className="text-gray-600 dark:text-gray-200"></div>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div
|
|||
|
|
className="absolute right-0 top-0 bottom-0 w-1 cursor-col-resize z-10"
|
|||
|
|
onMouseDown={onResizeStart}
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
)
|
|||
|
|
}
|