2024-04-05 15:15:10 +08:00
|
|
|
import React from 'react';
|
2023-12-17 15:33:54 +08:00
|
|
|
import { Anchor } from 'antd';
|
|
|
|
import { createStyles, useTheme } from 'antd-style';
|
2024-04-05 15:15:10 +08:00
|
|
|
import type { AnchorLinkItemProps } from 'antd/es/anchor/Anchor';
|
2023-12-26 12:08:50 +08:00
|
|
|
import classNames from 'classnames';
|
2023-12-17 15:33:54 +08:00
|
|
|
import { useRouteMeta, useTabMeta } from 'dumi';
|
|
|
|
|
2024-08-29 14:05:07 +08:00
|
|
|
export const useStyle = createStyles(({ token, css }) => {
|
2023-12-17 15:33:54 +08:00
|
|
|
const { antCls } = token;
|
|
|
|
return {
|
2024-05-03 13:52:04 +08:00
|
|
|
anchorToc: css`
|
|
|
|
scrollbar-width: thin;
|
|
|
|
scrollbar-color: unset;
|
2023-12-17 15:33:54 +08:00
|
|
|
${antCls}-anchor {
|
|
|
|
${antCls}-anchor-link-title {
|
2024-04-06 16:11:17 +08:00
|
|
|
font-size: ${token.fontSizeSM}px;
|
2023-12-17 15:33:54 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
tocWrapper: css`
|
|
|
|
position: fixed;
|
2024-08-29 14:05:07 +08:00
|
|
|
top: ${token.headerHeight + token.contentMarginTop - 4}px;
|
2023-12-17 15:33:54 +08:00
|
|
|
inset-inline-end: 0;
|
2024-08-29 14:05:07 +08:00
|
|
|
width: 148px;
|
|
|
|
padding: 0;
|
2023-12-17 15:33:54 +08:00
|
|
|
border-radius: ${token.borderRadius}px;
|
|
|
|
box-sizing: border-box;
|
2024-08-29 14:05:07 +08:00
|
|
|
margin-inline-end: calc(8px - 100vw + 100%);
|
2024-03-26 14:12:01 +08:00
|
|
|
z-index: 10;
|
2023-12-17 15:33:54 +08:00
|
|
|
.toc-debug {
|
|
|
|
color: ${token.purple6};
|
|
|
|
&:hover {
|
|
|
|
color: ${token.purple5};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
> div {
|
|
|
|
box-sizing: border-box;
|
|
|
|
width: 100%;
|
2024-02-02 13:46:26 +08:00
|
|
|
max-height: calc(100vh - ${token.headerHeight + token.contentMarginTop + 24}px) !important;
|
2024-04-05 15:15:10 +08:00
|
|
|
margin: auto;
|
2023-12-17 15:33:54 +08:00
|
|
|
overflow: auto;
|
2024-04-05 15:15:10 +08:00
|
|
|
padding: ${token.paddingXXS}px;
|
2024-04-05 11:41:02 +08:00
|
|
|
backdrop-filter: blur(8px);
|
2023-12-17 15:33:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
@media only screen and (max-width: ${token.screenLG}px) {
|
|
|
|
display: none;
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
articleWrapper: css`
|
2024-08-29 14:05:07 +08:00
|
|
|
padding-inline: 48px 164px;
|
|
|
|
padding-block: 0 32px;
|
2023-12-17 15:33:54 +08:00
|
|
|
|
|
|
|
@media only screen and (max-width: ${token.screenLG}px) {
|
2024-08-29 14:05:07 +08:00
|
|
|
& {
|
2024-04-06 16:11:17 +08:00
|
|
|
padding: 0 ${token.paddingLG * 2}px;
|
2023-12-17 15:33:54 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
interface DocAnchorProps {
|
|
|
|
showDebug?: boolean;
|
|
|
|
debugDemos?: string[];
|
|
|
|
}
|
|
|
|
|
2024-04-05 15:15:10 +08:00
|
|
|
interface AnchorItem {
|
2023-12-17 15:33:54 +08:00
|
|
|
id: string;
|
|
|
|
title: string;
|
|
|
|
children?: AnchorItem[];
|
2024-04-05 15:15:10 +08:00
|
|
|
}
|
2023-12-17 15:33:54 +08:00
|
|
|
|
|
|
|
const DocAnchor: React.FC<DocAnchorProps> = ({ showDebug, debugDemos = [] }) => {
|
|
|
|
const { styles } = useStyle();
|
|
|
|
const token = useTheme();
|
|
|
|
const meta = useRouteMeta();
|
|
|
|
const tab = useTabMeta();
|
|
|
|
|
2024-04-05 15:15:10 +08:00
|
|
|
const renderAnchorItem = (item: AnchorItem): AnchorLinkItemProps => ({
|
2023-12-17 15:33:54 +08:00
|
|
|
href: `#${item.id}`,
|
|
|
|
title: item.title,
|
|
|
|
key: item.id,
|
|
|
|
children: item.children
|
|
|
|
?.filter((child) => showDebug || !debugDemos.includes(child.id))
|
2024-04-05 15:15:10 +08:00
|
|
|
.map<AnchorLinkItemProps>((child) => ({
|
2023-12-17 15:33:54 +08:00
|
|
|
key: child.id,
|
|
|
|
href: `#${child.id}`,
|
|
|
|
title: (
|
2024-04-05 15:15:10 +08:00
|
|
|
<span className={classNames({ 'toc-debug': debugDemos.includes(child.id) })}>
|
2023-12-17 15:33:54 +08:00
|
|
|
{child?.title}
|
|
|
|
</span>
|
|
|
|
),
|
|
|
|
})),
|
|
|
|
});
|
|
|
|
|
2024-04-05 15:15:10 +08:00
|
|
|
const anchorItems = React.useMemo<AnchorItem[]>(
|
2023-12-17 15:33:54 +08:00
|
|
|
() =>
|
|
|
|
(tab?.toc || meta.toc).reduce<AnchorItem[]>((result, item) => {
|
|
|
|
if (item.depth === 2) {
|
|
|
|
result.push({ ...item });
|
|
|
|
} else if (item.depth === 3) {
|
|
|
|
const parent = result[result.length - 1];
|
|
|
|
if (parent) {
|
|
|
|
parent.children = parent.children || [];
|
|
|
|
parent.children.push({ ...item });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}, []),
|
|
|
|
[tab?.toc, meta.toc],
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!meta.frontmatter.toc) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<section className={styles.tocWrapper}>
|
|
|
|
<Anchor
|
|
|
|
affix={false}
|
2024-05-03 13:52:04 +08:00
|
|
|
className={styles.anchorToc}
|
2023-12-17 15:33:54 +08:00
|
|
|
targetOffset={token.anchorTop}
|
|
|
|
showInkInFixed
|
2024-04-05 15:15:10 +08:00
|
|
|
items={anchorItems.map<AnchorLinkItemProps>(renderAnchorItem)}
|
2023-12-17 15:33:54 +08:00
|
|
|
/>
|
|
|
|
</section>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
export default DocAnchor;
|