diff --git a/themes/Fukasawa/LayoutSlug.js b/themes/Fukasawa/LayoutSlug.js index afa8f278..6834da3e 100644 --- a/themes/Fukasawa/LayoutSlug.js +++ b/themes/Fukasawa/LayoutSlug.js @@ -1,4 +1,5 @@ import BLOG from '@/blog.config' +import { getPageTableOfContents } from 'notion-utils' import 'prismjs' import 'prismjs/components/prism-bash' import 'prismjs/components/prism-javascript' @@ -17,6 +18,11 @@ export const LayoutSlug = (props) => { tags: post.tags } + if (post?.blockMap?.block) { + post.content = Object.keys(post.blockMap.block) + post.toc = getPageTableOfContents(post, post.blockMap) + } + return ( diff --git a/themes/Fukasawa/components/AsideLeft.js b/themes/Fukasawa/components/AsideLeft.js index 2f668d43..2b46e616 100644 --- a/themes/Fukasawa/components/AsideLeft.js +++ b/themes/Fukasawa/components/AsideLeft.js @@ -5,11 +5,13 @@ import GroupMenu from './GroupMenu' import GroupTag from './GroupTag' import SearchInput from './SearchInput' import SiteInfo from './SiteInfo' +import Catalog from './Catalog' function AsideLeft (props) { - const { tags, currentTag, categories, currentCategory } = props - return
+ const { tags, currentTag, categories, currentCategory, post } = props + console.log(post) + return
@@ -42,6 +44,10 @@ function AsideLeft (props) {
+
+ +
+
} diff --git a/themes/Fukasawa/components/Catalog.js b/themes/Fukasawa/components/Catalog.js new file mode 100644 index 00000000..c433e0d1 --- /dev/null +++ b/themes/Fukasawa/components/Catalog.js @@ -0,0 +1,81 @@ +import React from 'react' +import throttle from 'lodash.throttle' +import { uuidToId } from 'notion-utils' + +/** + * 目录导航组件 + * @param toc + * @returns {JSX.Element} + * @constructor + */ +const Catalog = ({ toc }) => { + // 无目录就直接返回空 + if (!toc || toc.length < 1) { + return <> + } + // 监听滚动事件 + React.useEffect(() => { + window.addEventListener('scroll', actionSectionScrollSpy) + actionSectionScrollSpy() + return () => { + window.removeEventListener('scroll', actionSectionScrollSpy) + } + }, []) + + // 同步选中目录事件 + const [activeSection, setActiveSection] = React.useState(null) + const throttleMs = 100 + const actionSectionScrollSpy = React.useCallback(throttle(() => { + const sections = document.getElementsByClassName('notion-h') + let prevBBox = null + let currentSectionId = activeSection + for (let i = 0; i < sections.length; ++i) { + const section = sections[i] + if (!section || !(section instanceof Element)) continue + if (!currentSectionId) { + currentSectionId = section.getAttribute('data-id') + } + const bbox = section.getBoundingClientRect() + const prevHeight = prevBBox ? bbox.top - prevBBox.bottom : 0 + const offset = Math.max(150, prevHeight / 4) + // GetBoundingClientRect returns values relative to viewport + if (bbox.top - offset < 0) { + currentSectionId = section.getAttribute('data-id') + prevBBox = bbox + continue + } + // No need to continue loop, if last element has been detected + break + } + setActiveSection(currentSectionId) + }, throttleMs)) + + return
+
目录
+ +
+} + +export default Catalog