-
-
Notifications
You must be signed in to change notification settings - Fork 57
/
Copy pathTableOfContent.ts
120 lines (104 loc) · 2.86 KB
/
TableOfContent.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import { Node, mergeAttributes } from '@tiptap/core'
import { ReactNodeViewRenderer } from '@tiptap/react'
import { NodeViewTableOfContent } from '@/extensions/TableOfContent/components/NodeViewTableOfContent'
import { findNode, isTitleNode } from '@/utils/node'
import { TableOfContentActionButton } from '@/extensions/TableOfContent/components/TableOfContentActionButton'
declare module '@tiptap/core' {
interface Commands<ReturnType> {
tableOfContents: {
setTableOfContents: () => ReturnType
removeTableOfContents: () => ReturnType
}
}
}
interface Options {
onHasOneBeforeInsert?: () => void
}
export const TableOfContents = Node.create<Options>({
name: 'tableOfContents',
group: 'block',
atom: true,
addOptions() {
return {
...this.parent?.(),
onHasOneBeforeInsert: () => {},
resizable: true,
lastColumnResizable: true,
allowTableNodeSelection: false,
button: ({ editor, t }: any) => ({
component: TableOfContentActionButton,
componentProps: {
disabled: false,
icon: 'BookMarked',
tooltip: t('editor.table.tooltip'),
editor,
},
}),
}
},
parseHTML() {
return [
{
tag: 'toc',
},
]
},
renderHTML({ HTMLAttributes }) {
return ['toc', mergeAttributes(HTMLAttributes)]
},
addNodeView() {
return ReactNodeViewRenderer(NodeViewTableOfContent)
},
// @ts-expect-error
addCommands() {
return {
setTableOfContents:
() =>
({ commands, editor, view }) => {
const nodes = findNode(editor, this.name)
if (nodes.length) {
// @ts-expect-error
this.options.onHasOneBeforeInsert()
return
}
const titleNode = view.props.state.doc.content.firstChild as any
if (isTitleNode(titleNode)) {
const pos = ((titleNode.firstChild && titleNode.firstChild.nodeSize) || 0) + 1
return commands.insertContentAt(pos, { type: this.name })
}
return commands.insertContent({
type: this.name,
})
},
removeTableOfContents: () =>
({ state, dispatch }: any) => {
const { tr } = state
const nodeType = state.schema.nodes.tableOfContents
state.doc.descendants((node: any, pos: any) => {
if (node.type === nodeType) {
const from = pos
const to = pos + node.nodeSize
tr.delete(from, to)
}
})
if (tr.docChanged) {
dispatch(tr)
return true
}
return false
},
}
},
addGlobalAttributes() {
return [
{
types: ['heading'],
attributes: {
id: {
default: null,
},
},
},
]
},
})