Skip to content

Commit 2e84f05

Browse files
authored
Merge branch 'main' into fix-export-prefetch-lock
2 parents 1192c60 + 147e057 commit 2e84f05

4 files changed

Lines changed: 98 additions & 18 deletions

File tree

lib/db/notion/getPageTableOfContents.js

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,26 @@ import { getTextContent } from 'notion-utils'
33
const indentLevels = {
44
header: 0,
55
sub_header: 1,
6-
sub_sub_header: 2
6+
sub_sub_header: 2,
7+
heading_1: 0,
8+
heading_2: 1,
9+
heading_3: 2
710
}
811

12+
const unknownHeadingStats = new Map()
13+
914
/**
1015
* @see https://github.com/NotionX/react-notion-x/blob/master/packages/notion-utils/src/get-page-table-of-contents.ts
1116
* Gets the metadata for a table of contents block by parsing the page's
1217
* H1, H2, and H3 elements.
1318
*/
1419
export const getPageTableOfContents = (page, recordMap) => {
20+
const pageId = page?.id
21+
if (process.env.NODE_ENV !== 'production' && pageId) {
22+
unknownHeadingStats.set(pageId, 0)
23+
}
1524
const contents = page.content ?? []
16-
const toc = getBlockHeader(contents, recordMap)
25+
const toc = getBlockHeader(contents, recordMap, [], pageId)
1726
const indentLevelStack = [
1827
{
1928
actual: -1,
@@ -25,11 +34,18 @@ export const getPageTableOfContents = (page, recordMap) => {
2534
// This is a little tricky, but the key is that when increasing indent levels,
2635
// they should never jump more than one at a time.
2736
for (const tocItem of toc) {
28-
const { indentLevel } = tocItem
29-
const actual = indentLevel
37+
const actual = Number.isInteger(tocItem.indentLevel) ? tocItem.indentLevel : 0
3038

3139
do {
3240
const prevIndent = indentLevelStack[indentLevelStack.length - 1]
41+
if (!prevIndent) {
42+
tocItem.indentLevel = 0
43+
indentLevelStack.push({
44+
actual,
45+
effective: 0
46+
})
47+
break
48+
}
3349
const { actual: prevActual, effective: prevEffective } = prevIndent
3450

3551
if (actual > prevActual) {
@@ -49,13 +65,21 @@ export const getPageTableOfContents = (page, recordMap) => {
4965
} while (true)
5066
}
5167

68+
if (process.env.NODE_ENV !== 'production' && pageId) {
69+
const unknownCount = unknownHeadingStats.get(pageId) || 0
70+
if (unknownCount > 0) {
71+
console.warn('[TOC] unknown heading summary', { pageId, unknownCount })
72+
}
73+
unknownHeadingStats.delete(pageId)
74+
}
75+
5276
return toc
5377
}
5478

5579
/**
5680
* 重写获取目录方法
5781
*/
58-
function getBlockHeader(contents, recordMap, toc) {
82+
function getBlockHeader(contents, recordMap, toc, pageId) {
5983
if (!toc) {
6084
toc = []
6185
}
@@ -69,27 +93,55 @@ function getBlockHeader(contents, recordMap, toc) {
6993
continue
7094
}
7195
const { type } = block
96+
const isHeading =
97+
typeof type === 'string' &&
98+
(type.indexOf('header') >= 0 || /^heading_[123]$/.test(type))
99+
72100
if (block.content?.length > 0) {
73-
getBlockHeader(block.content, recordMap, toc)
101+
getBlockHeader(block.content, recordMap, toc, pageId)
74102
} else {
75-
if (type.indexOf('header') >= 0) {
103+
if (isHeading) {
76104
const existed = toc.find(e => e.id === blockId)
105+
const indentLevel = indentLevels[type]
106+
if (!Number.isInteger(indentLevel)) {
107+
// Emit debug signal only in development for quick reproduction.
108+
if (process.env.NODE_ENV !== 'production') {
109+
if (pageId) {
110+
unknownHeadingStats.set(
111+
pageId,
112+
(unknownHeadingStats.get(pageId) || 0) + 1
113+
)
114+
}
115+
console.warn('[TOC] unknown heading type', {
116+
pageId,
117+
blockId,
118+
type,
119+
title: getTextContent(block.properties?.title),
120+
parentId: block.parent_id
121+
})
122+
}
123+
continue
124+
}
77125
if (!existed) {
78126
toc.push({
79127
id: blockId,
80128
type,
81129
text: getTextContent(block.properties?.title),
82-
indentLevel: indentLevels[type]
130+
indentLevel
83131
})
84132
}
85-
} else if (type === 'transclusion_reference') {
133+
} else if (
134+
type === 'transclusion_reference' &&
135+
block.format?.transclusion_reference_pointer?.id
136+
) {
86137
getBlockHeader(
87138
[block.format.transclusion_reference_pointer.id],
88139
recordMap,
89-
toc
140+
toc,
141+
pageId
90142
)
91143
} else if (type === 'transclusion_container') {
92-
getBlockHeader(block.content, recordMap, toc)
144+
getBlockHeader(block.content, recordMap, toc, pageId)
93145
}
94146
}
95147
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "notion-next",
3-
"version": "4.9.4.2",
3+
"version": "4.9.4.3",
44
"homepage": "https://github.com/tangly1024/NotionNext.git",
55
"license": "MIT",
66
"engines": {

themes/proxio/components/MenuItem.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,23 @@ import { useState } from 'react'
77
* @param {*} param0
88
* @returns
99
*/
10-
export const MenuItem = ({ link }) => {
10+
export const MenuItem = ({ link, isOpen, toggleOpen }) => {
1111
const hasSubMenu = link?.subMenus?.length > 0
1212
const router = useRouter()
1313

1414
// 管理子菜单的展开状态
1515
const [isSubMenuOpen, setIsSubMenuOpen] = useState(false)
1616

1717
const toggleSubMenu = () => {
18-
setIsSubMenuOpen(prev => !prev) // 切换子菜单状态
18+
if (toggleOpen) {
19+
toggleOpen()
20+
} else {
21+
setIsSubMenuOpen(prev => !prev) // 切换子菜单状态
22+
}
1923
}
2024

25+
const open = isOpen !== undefined ? isOpen : isSubMenuOpen
26+
2127
return (
2228
<>
2329
{/* 普通 MenuItem */}
@@ -66,7 +72,7 @@ export const MenuItem = ({ link }) => {
6672
{/* 子菜单 */}
6773
<div
6874
className={`submenu dark:border-gray-600 relative left-0 top-full w-[250px] rounded-sm bg-white p-4 transition-all duration-300 dark:bg-dark-2 lg:absolute lg:shadow-lg ${
69-
isSubMenuOpen
75+
open
7076
? 'block opacity-100 visible'
7177
: 'hidden opacity-0 invisible'
7278
}`}>

themes/proxio/components/MenuList.js

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import BLOG from '@/blog.config'
22
import { siteConfig } from '@/lib/config'
33
import { useGlobal } from '@/lib/global'
44
import { useRouter } from 'next/router'
5-
import { useEffect, useState } from 'react'
5+
import { useEffect, useState, useRef } from 'react'
66
import { MenuItem } from './MenuItem'
77

88
/**
@@ -13,7 +13,10 @@ export const MenuList = props => {
1313
const { locale } = useGlobal()
1414

1515
const [showMenu, setShowMenu] = useState(false) // 控制菜单展开/收起状态
16+
const [openSubMenuIdx, setOpenSubMenuIdx] = useState(null) // 控制哪个子菜单处于展开状态
1617
const router = useRouter()
18+
const menuRef = useRef(null) // 监听点击外部区域
19+
1720

1821
let links = [
1922
{
@@ -57,14 +60,28 @@ export const MenuList = props => {
5760

5861
useEffect(() => {
5962
setShowMenu(false)
63+
setOpenSubMenuIdx(null)
6064
}, [router])
6165

66+
// 监听点击外部区域,收起子菜单
67+
useEffect(() => {
68+
const handleClickOutside = (event) => {
69+
if (menuRef.current && !menuRef.current.contains(event.target)) {
70+
setOpenSubMenuIdx(null)
71+
}
72+
}
73+
document.addEventListener('mousedown', handleClickOutside)
74+
return () => {
75+
document.removeEventListener('mousedown', handleClickOutside)
76+
}
77+
}, [])
78+
6279
if (!links || links.length === 0) {
6380
return null
6481
}
6582

6683
return (
67-
<div>
84+
<div ref={menuRef}>
6885
{/* 移动端菜单切换按钮 */}
6986
<button
7087
id='navbarToggler'
@@ -84,7 +101,12 @@ export const MenuList = props => {
84101
}`}>
85102
<ul className='blcok lg:flex 2xl:ml-20'>
86103
{links?.map((link, index) => (
87-
<MenuItem key={index} link={link} />
104+
<MenuItem
105+
key={index}
106+
link={link}
107+
isOpen={openSubMenuIdx === index}
108+
toggleOpen={() => setOpenSubMenuIdx(openSubMenuIdx === index ? null : index)}
109+
/>
88110
))}
89111
</ul>
90112
</nav>

0 commit comments

Comments
 (0)