oak-general-business/es/components/article/toc/tocView.js

106 lines
5.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useEffect } from "react";
import { Button, Tooltip } from "antd";
import { CaretDownOutlined, CloseOutlined, MenuOutlined } from "@ant-design/icons";
import classNames from "classnames";
import Style from './tocView.module.less';
export function TocView(props) {
const { toc, showToc, tocPosition, setShowToc, highlightBgColor, headerTop = 0, scrollId, fixed = false, closed = false } = props;
useEffect(() => {
document.documentElement.style.setProperty('--highlight-bg-color', highlightBgColor);
}, [highlightBgColor]);
const generateTocList = (items, currentLevel = 1, parentId = null) => {
//递归生成嵌套列表
const result = [];
let lastId = parentId;
while (items.length > 0) {
const item = items[0];
//有无子级标题,有则显示展开图标
const hasChildren = items.length > 1 && items[1].level > item.level;
if (item.level > currentLevel) {
// 递归生成子列表
const subList = generateTocList(items, item.level, item.id);
result.push(<li key={item.id} id={`ul-${lastId}`}>
<ul style={{ listStyleType: 'none', paddingInlineStart: '6px' }}>
{subList}
</ul>
</li>);
}
else if (item.level < currentLevel) {
// 结束当前层级
break;
}
else {
// 添加当前层级的 <li>
result.push(<li className={Style.listItem} key={item.id} id={`li-${item.id}`} style={{ paddingLeft: `${(item.level - 1) * 6}px`, }}>
<CaretDownOutlined id={`icon-${item.id}`} className={Style.icon} style={{ visibility: hasChildren && item.level < 5 ? 'visible' : 'hidden', color: '#A5A5A5' }} onClick={() => {
const iconElem = document.getElementById(`icon-${item.id}`);
const ulElem = document.getElementById(`ul-${item.id}`);
const isFolded = iconElem?.className.includes('iconFold') || ulElem?.className.includes('fold');
if (isFolded) {
iconElem?.classList.remove(Style.iconFold);
ulElem?.classList.remove(Style.fold);
}
else {
iconElem?.classList.add(Style.iconFold);
ulElem?.classList.add(Style.fold);
}
}}/>
<div style={{ fontSize: '1em', fontWeight: item.level === 1 ? 'bold' : 'normal' }} className={Style.tocItem} onClick={(event) => {
//页面滚动到对应元素
const elem = document.getElementById(item.id);
const elemTop = elem?.getBoundingClientRect().top;
if (scrollId) {
const scrollContainer = document.getElementById(scrollId);
const containerTop = scrollContainer?.getBoundingClientRect().top;
scrollContainer?.scrollBy({
top: elemTop - containerTop,
behavior: 'smooth',
});
}
else {
// const containerTop = document.body.getBoundingClientRect().top;
window.scrollBy({
top: elemTop - headerTop,
behavior: 'smooth',
});
}
//添加背景色
elem?.classList.add(Style.highlight);
//移除背景色类名
setTimeout(function () {
elem?.classList.remove(Style.highlight);
}, 1000);
event.preventDefault();
event.stopPropagation();
}}>
{item.text}
</div>
</li>);
items.shift(); // 移除已处理的项
lastId = item.id;
}
}
return result;
};
return (<div className={classNames(Style.tocContainer, {
[Style.fixed]: fixed
})}>
{showToc ? (<>
<div className={Style.catalogTitle}>
<div style={{ color: '#A5A5A5' }}>大纲</div>
{closed ? (<CloseOutlined style={{ color: '#A5A5A5' }} onClick={() => setShowToc(false)}/>) : null}
</div>
{(toc && toc.length > 0) ? (<ul style={{ listStyleType: 'none', paddingInlineStart: '0px' }}>{generateTocList([...toc])}</ul>) : (<div style={{ display: 'flex', alignItems: 'center', color: '#B1B1B1', height: '200px' }}>
<div>
对文档内容应用标题样式即可生成大纲
</div>
</div>)}
</>) : (<div className={classNames(Style.tocButton, { [Style.tocButtonRight]: tocPosition === 'right' })}>
<Tooltip title="显示大纲" placement={tocPosition === 'right' ? 'left' : 'right'}>
<Button size="small" icon={<MenuOutlined />} onClick={() => setShowToc(true)}/>
</Tooltip>
</div>)}
</div>);
}