@kaitify/core 0.0.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/examples/App.vue +342 -0
- package/examples/content.js +1 -0
- package/examples/main.ts +4 -0
- package/examples/test.html +23 -0
- package/lib/extensions/Extension.d.ts +172 -0
- package/lib/extensions/align/index.d.ts +10 -0
- package/lib/extensions/attachment/index.d.ts +29 -0
- package/lib/extensions/back-color/index.d.ts +9 -0
- package/lib/extensions/blockquote/index.d.ts +12 -0
- package/lib/extensions/bold/index.d.ts +9 -0
- package/lib/extensions/code/index.d.ts +12 -0
- package/lib/extensions/code-block/hljs.d.ts +12 -0
- package/lib/extensions/code-block/index.d.ts +15 -0
- package/lib/extensions/color/index.d.ts +9 -0
- package/lib/extensions/font-family/index.d.ts +9 -0
- package/lib/extensions/font-size/index.d.ts +9 -0
- package/lib/extensions/heading/index.d.ts +13 -0
- package/lib/extensions/history/index.d.ts +10 -0
- package/lib/extensions/horizontal/index.d.ts +7 -0
- package/lib/extensions/image/index.d.ts +26 -0
- package/lib/extensions/indent/index.d.ts +8 -0
- package/lib/extensions/index.d.ts +29 -0
- package/lib/extensions/italic/index.d.ts +9 -0
- package/lib/extensions/line-height/index.d.ts +9 -0
- package/lib/extensions/link/index.d.ts +27 -0
- package/lib/extensions/list/index.d.ts +18 -0
- package/lib/extensions/math/index.d.ts +11 -0
- package/lib/extensions/strikethrough/index.d.ts +9 -0
- package/lib/extensions/subscript/index.d.ts +9 -0
- package/lib/extensions/superscript/index.d.ts +9 -0
- package/lib/extensions/table/index.d.ts +21 -0
- package/lib/extensions/task/index.d.ts +12 -0
- package/lib/extensions/text/index.d.ts +14 -0
- package/lib/extensions/underline/index.d.ts +9 -0
- package/lib/extensions/video/index.d.ts +27 -0
- package/lib/index.d.ts +3 -0
- package/lib/kaitify-core.es.js +38337 -0
- package/lib/kaitify-core.umd.js +2 -0
- package/lib/model/Editor.d.ts +504 -0
- package/lib/model/History.d.ts +42 -0
- package/lib/model/KNode.d.ts +258 -0
- package/lib/model/Selection.d.ts +29 -0
- package/lib/model/config/dom-observe.d.ts +10 -0
- package/lib/model/config/event-handler.d.ts +33 -0
- package/lib/model/config/format-patch.d.ts +25 -0
- package/lib/model/config/format-rules.d.ts +37 -0
- package/lib/model/config/function.d.ts +84 -0
- package/lib/model/index.d.ts +6 -0
- package/lib/tools/index.d.ts +49 -0
- package/lib/view/index.d.ts +21 -0
- package/lib/view/js-render/dom-patch.d.ts +65 -0
- package/lib/view/js-render/index.d.ts +5 -0
- package/package.json +52 -0
- package/src/css/style.less +56 -0
- package/src/css/var.less +45 -0
- package/src/extensions/Extension.ts +200 -0
- package/src/extensions/align/index.ts +115 -0
- package/src/extensions/attachment/icon.svg +1 -0
- package/src/extensions/attachment/index.ts +293 -0
- package/src/extensions/attachment/style.less +25 -0
- package/src/extensions/back-color/index.ts +56 -0
- package/src/extensions/blockquote/index.ts +144 -0
- package/src/extensions/blockquote/style.less +16 -0
- package/src/extensions/bold/index.ts +77 -0
- package/src/extensions/code/index.ts +295 -0
- package/src/extensions/code/style.less +14 -0
- package/src/extensions/code-block/hljs.less +183 -0
- package/src/extensions/code-block/hljs.ts +95 -0
- package/src/extensions/code-block/index.ts +308 -0
- package/src/extensions/code-block/style.less +20 -0
- package/src/extensions/color/index.ts +56 -0
- package/src/extensions/font-family/index.ts +80 -0
- package/src/extensions/font-size/index.ts +56 -0
- package/src/extensions/heading/index.ts +164 -0
- package/src/extensions/heading/style.less +42 -0
- package/src/extensions/history/index.ts +96 -0
- package/src/extensions/horizontal/index.ts +45 -0
- package/src/extensions/horizontal/style.less +13 -0
- package/src/extensions/image/index.ts +242 -0
- package/src/extensions/image/style.less +8 -0
- package/src/extensions/indent/index.ts +98 -0
- package/src/extensions/index.ts +29 -0
- package/src/extensions/italic/index.ts +77 -0
- package/src/extensions/line-height/index.ts +113 -0
- package/src/extensions/link/index.ts +184 -0
- package/src/extensions/link/style.less +19 -0
- package/src/extensions/list/index.ts +410 -0
- package/src/extensions/list/style.less +19 -0
- package/src/extensions/math/index.ts +233 -0
- package/src/extensions/math/style.less +21 -0
- package/src/extensions/strikethrough/index.ts +78 -0
- package/src/extensions/subscript/index.ts +77 -0
- package/src/extensions/superscript/index.ts +77 -0
- package/src/extensions/table/index.ts +1148 -0
- package/src/extensions/table/style.less +71 -0
- package/src/extensions/task/index.ts +243 -0
- package/src/extensions/task/style.less +59 -0
- package/src/extensions/text/index.ts +359 -0
- package/src/extensions/underline/index.ts +78 -0
- package/src/extensions/video/index.ts +273 -0
- package/src/extensions/video/style.less +8 -0
- package/src/index.ts +9 -0
- package/src/model/Editor.ts +1963 -0
- package/src/model/History.ts +115 -0
- package/src/model/KNode.ts +677 -0
- package/src/model/Selection.ts +39 -0
- package/src/model/config/dom-observe.ts +184 -0
- package/src/model/config/event-handler.ts +237 -0
- package/src/model/config/format-patch.ts +215 -0
- package/src/model/config/format-rules.ts +218 -0
- package/src/model/config/function.ts +1018 -0
- package/src/model/index.ts +6 -0
- package/src/tools/index.ts +156 -0
- package/src/view/index.ts +46 -0
- package/src/view/js-render/dom-patch.ts +324 -0
- package/src/view/js-render/index.ts +210 -0
- package/vite-env.d.ts +2 -0
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
import { Editor, KNode, KNodeStylesType } from '@/model'
|
|
2
|
+
import { getSelectionBlockNodes } from '@/model/config/function'
|
|
3
|
+
import { Extension } from '../Extension'
|
|
4
|
+
import './style.less'
|
|
5
|
+
|
|
6
|
+
export type OrderedListType = 'decimal' | 'lower-alpha' | 'upper-alpha' | 'lower-roman' | 'upper-roman' | 'lower-greek' | 'cjk-ideographic'
|
|
7
|
+
|
|
8
|
+
export type UnorderListType = 'disc' | 'circle' | 'square'
|
|
9
|
+
|
|
10
|
+
export type ListOptionsType = {
|
|
11
|
+
ordered?: boolean
|
|
12
|
+
listType?: OrderedListType | UnorderListType
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
declare module '../../model' {
|
|
16
|
+
interface EditorCommandsType {
|
|
17
|
+
getList?: (options: ListOptionsType) => KNode | null
|
|
18
|
+
hasList?: (options: ListOptionsType) => boolean
|
|
19
|
+
allList?: (options: ListOptionsType) => boolean
|
|
20
|
+
setList?: (options: ListOptionsType) => Promise<void>
|
|
21
|
+
unsetList?: (options: ListOptionsType) => Promise<void>
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 块节点转为列表
|
|
27
|
+
*/
|
|
28
|
+
const toList = (editor: Editor, node: KNode, ordered?: boolean, listType?: OrderedListType | UnorderListType) => {
|
|
29
|
+
if (!node.isBlock()) {
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
//是列表项节点
|
|
33
|
+
if (node.isMatch({ tag: 'li' })) {
|
|
34
|
+
//如果是和当前要转的列表类型一致则不处理
|
|
35
|
+
if (listType) {
|
|
36
|
+
if (node.parent!.isMatch({ tag: ordered ? 'ol' : 'ul', styles: { listStyleType: listType } })) {
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
if (node.parent!.isMatch({ tag: ordered ? 'ol' : 'ul' })) {
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
//获取列表节点
|
|
46
|
+
const listNode = node.parent!
|
|
47
|
+
//获取当前块节点在列表项节点里的序列
|
|
48
|
+
const index = listNode.children!.findIndex(item => item.isEqual(node))
|
|
49
|
+
//复制当前块节点
|
|
50
|
+
const newNode = node.clone(false)
|
|
51
|
+
//将块节点的子节点都给复制的块节点
|
|
52
|
+
node.children!.forEach((item, index) => {
|
|
53
|
+
editor.addNode(item, newNode, index)
|
|
54
|
+
})
|
|
55
|
+
node.children = []
|
|
56
|
+
//创建新的列表节点
|
|
57
|
+
const newListNode = KNode.create({
|
|
58
|
+
type: 'block',
|
|
59
|
+
tag: ordered ? 'ol' : 'ul',
|
|
60
|
+
children: []
|
|
61
|
+
})
|
|
62
|
+
if (listType) {
|
|
63
|
+
newListNode.styles = {
|
|
64
|
+
listStyleType: listType
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//将复制的块节点给新的列表节点
|
|
68
|
+
editor.addNode(newNode, newListNode)
|
|
69
|
+
//该列表项节点是原列表节点的第一个子节点
|
|
70
|
+
if (index == 0) {
|
|
71
|
+
editor.addNodeBefore(newListNode, listNode)
|
|
72
|
+
}
|
|
73
|
+
//该列表项节点是原列表节点的最后一个子节点
|
|
74
|
+
else if (index == listNode.children!.length - 1) {
|
|
75
|
+
editor.addNodeAfter(newListNode, listNode)
|
|
76
|
+
}
|
|
77
|
+
//该列表项节点在原列表节点的中间
|
|
78
|
+
else {
|
|
79
|
+
//复制原列表节点
|
|
80
|
+
const sList = listNode.clone(false)
|
|
81
|
+
//截取原节点的前半部分数据给复制的列表节点
|
|
82
|
+
const sListItems = listNode.children!.splice(0, index)
|
|
83
|
+
sListItems.forEach((item, index) => {
|
|
84
|
+
editor.addNode(item, sList, index)
|
|
85
|
+
})
|
|
86
|
+
editor.addNodeBefore(newListNode, listNode)
|
|
87
|
+
editor.addNodeBefore(sList, newListNode)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
//是固定的块节点或者内嵌套的块节点
|
|
91
|
+
else if (node.fixed || node.nested) {
|
|
92
|
+
//创建列表节点
|
|
93
|
+
const listNode = KNode.create({
|
|
94
|
+
type: 'block',
|
|
95
|
+
tag: ordered ? 'ol' : 'ul',
|
|
96
|
+
children: [
|
|
97
|
+
{
|
|
98
|
+
type: 'block',
|
|
99
|
+
tag: 'li',
|
|
100
|
+
nested: true,
|
|
101
|
+
children: []
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
})
|
|
105
|
+
if (listType) {
|
|
106
|
+
listNode.styles = {
|
|
107
|
+
listStyleType: listType
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//将块节点的子节点给列表节点的列表项节点
|
|
111
|
+
node.children!.forEach((item, index) => {
|
|
112
|
+
editor.addNode(item, listNode.children![0], index)
|
|
113
|
+
})
|
|
114
|
+
//将列表节点添加到块节点下
|
|
115
|
+
listNode.parent = node
|
|
116
|
+
node.children = [listNode]
|
|
117
|
+
}
|
|
118
|
+
//非固定块节点
|
|
119
|
+
else {
|
|
120
|
+
//将块节点转为列表节点
|
|
121
|
+
editor.toParagraph(node)
|
|
122
|
+
node.tag = ordered ? 'ol' : 'ul'
|
|
123
|
+
if (listType) {
|
|
124
|
+
node.styles = {
|
|
125
|
+
listStyleType: listType
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
//创建列表项节点
|
|
129
|
+
const listItem = KNode.create({
|
|
130
|
+
type: 'block',
|
|
131
|
+
tag: 'li',
|
|
132
|
+
nested: true
|
|
133
|
+
})
|
|
134
|
+
//将列表节点的子节点都给列表项节点
|
|
135
|
+
node.children!.forEach((item, index) => {
|
|
136
|
+
editor.addNode(item, listItem, index)
|
|
137
|
+
})
|
|
138
|
+
//将列表项节点作为列表节点的子节点
|
|
139
|
+
node.children = [listItem]
|
|
140
|
+
listItem.parent = node
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* 取消当前列表项块节点的列表设置
|
|
146
|
+
*/
|
|
147
|
+
const ListItemToParagraph = (editor: Editor, node: KNode) => {
|
|
148
|
+
if (!node.isBlock()) {
|
|
149
|
+
return
|
|
150
|
+
}
|
|
151
|
+
if (!node.isMatch({ tag: 'li' })) {
|
|
152
|
+
return
|
|
153
|
+
}
|
|
154
|
+
//获取列表节点
|
|
155
|
+
const listNode = node.parent!
|
|
156
|
+
//列表项在列表节点中的序列
|
|
157
|
+
const index = listNode.children!.findIndex(item => item.isEqual(node))
|
|
158
|
+
//在列表节点的第一个位置
|
|
159
|
+
if (index == 0) {
|
|
160
|
+
editor.addNodeBefore(node, listNode)
|
|
161
|
+
listNode.children!.splice(index, 1)
|
|
162
|
+
}
|
|
163
|
+
//在列表节点的最后一个位置
|
|
164
|
+
else if (index == listNode.children!.length - 1) {
|
|
165
|
+
editor.addNodeAfter(node, listNode)
|
|
166
|
+
listNode.children!.splice(index, 1)
|
|
167
|
+
}
|
|
168
|
+
//在列表节点的中间位置
|
|
169
|
+
else {
|
|
170
|
+
//复制原列表节点
|
|
171
|
+
const sList = listNode.clone(false)
|
|
172
|
+
//截取原节点的前半部分数据给复制的列表节点
|
|
173
|
+
const sListItems = listNode.children!.splice(0, index)
|
|
174
|
+
sListItems.forEach((item, index) => {
|
|
175
|
+
editor.addNode(item, sList, index)
|
|
176
|
+
})
|
|
177
|
+
editor.addNodeBefore(node, listNode)
|
|
178
|
+
listNode.children!.splice(0, 1)
|
|
179
|
+
editor.addNodeBefore(sList, node)
|
|
180
|
+
}
|
|
181
|
+
editor.toParagraph(node)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* 节点合并处理
|
|
186
|
+
*/
|
|
187
|
+
const listMergeHandler = ({ editor, node }: { editor: Editor; node: KNode }) => {
|
|
188
|
+
//节点是有序列表
|
|
189
|
+
if (node.isMatch({ tag: 'ol' })) {
|
|
190
|
+
//前一个兄弟节点
|
|
191
|
+
const previousNode = node.getPrevious(node.parent ? node.parent.children! : editor.stackNodes)
|
|
192
|
+
//前一个兄弟节点是有序列表则将当前节点的子节点都给前一个节点
|
|
193
|
+
if (previousNode && previousNode.isMatch({ tag: 'ol' }) && previousNode.isEqualMarks(node) && previousNode.isEqualStyles(node)) {
|
|
194
|
+
const nodes = node.children!.map(item => {
|
|
195
|
+
item.parent = previousNode
|
|
196
|
+
return item
|
|
197
|
+
})
|
|
198
|
+
previousNode.children!.push(...nodes)
|
|
199
|
+
node.children = []
|
|
200
|
+
const nextNode = node.getNext(node.parent ? node.parent.children! : editor.stackNodes)
|
|
201
|
+
//如果此时后一个节点存在
|
|
202
|
+
if (nextNode) {
|
|
203
|
+
listMergeHandler({ editor, node: nextNode })
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
//节点是无序列表
|
|
208
|
+
if (node.isMatch({ tag: 'ul' })) {
|
|
209
|
+
//前一个兄弟节点
|
|
210
|
+
const previousNode = node.getPrevious(node.parent ? node.parent.children! : editor.stackNodes)
|
|
211
|
+
//前一个兄弟节点是无序列表则将当前节点的子节点都给前一个节点
|
|
212
|
+
if (previousNode && previousNode.isMatch({ tag: 'ul' }) && previousNode.isEqualMarks(node) && previousNode.isEqualStyles(node)) {
|
|
213
|
+
const nodes = node.children!.map(item => {
|
|
214
|
+
item.parent = previousNode
|
|
215
|
+
return item
|
|
216
|
+
})
|
|
217
|
+
previousNode.children!.push(...nodes)
|
|
218
|
+
node.children = []
|
|
219
|
+
const nextNode = node.getNext(node.parent ? node.parent.children! : editor.stackNodes)
|
|
220
|
+
//如果此时后一个节点存在
|
|
221
|
+
if (nextNode) {
|
|
222
|
+
listMergeHandler({ editor, node: nextNode })
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export const ListExtension = () =>
|
|
229
|
+
Extension.create({
|
|
230
|
+
name: 'list',
|
|
231
|
+
extraKeepTags: ['ul', 'ol', 'li'],
|
|
232
|
+
domParseNodeCallback(node) {
|
|
233
|
+
if (node.isMatch({ tag: 'ul' }) || node.isMatch({ tag: 'ol' })) {
|
|
234
|
+
node.type = 'block'
|
|
235
|
+
}
|
|
236
|
+
if (node.isMatch({ tag: 'li' })) {
|
|
237
|
+
node.type = 'block'
|
|
238
|
+
node.nested = true
|
|
239
|
+
}
|
|
240
|
+
return node
|
|
241
|
+
},
|
|
242
|
+
formatRules: [
|
|
243
|
+
//列表处理
|
|
244
|
+
({ node }) => {
|
|
245
|
+
if (node.isMatch({ tag: 'ol' }) || node.isMatch({ tag: 'ul' })) {
|
|
246
|
+
//必须是块节点
|
|
247
|
+
node.type = 'block'
|
|
248
|
+
//固定设置序标在内侧
|
|
249
|
+
if (node.hasStyles()) {
|
|
250
|
+
node.styles!.listStylePosition = 'inside'
|
|
251
|
+
} else {
|
|
252
|
+
node.styles = {
|
|
253
|
+
listStylePosition: 'inside'
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
//列表项处理
|
|
259
|
+
({ editor, node }) => {
|
|
260
|
+
if (node.isMatch({ tag: 'li' })) {
|
|
261
|
+
//必须是内嵌块节点
|
|
262
|
+
node.type = 'block'
|
|
263
|
+
node.nested = true
|
|
264
|
+
//如果li节点无父节点或者父节点不是有序列表也不是无序列表,则默认加入到无序列表中
|
|
265
|
+
if (!node.parent || !(node.parent.isMatch({ tag: 'ol' }) || node.parent.isMatch({ tag: 'ul' }))) {
|
|
266
|
+
//设为内嵌块节点
|
|
267
|
+
node.nested = true
|
|
268
|
+
//创建列表节点
|
|
269
|
+
const listNode = KNode.create({
|
|
270
|
+
type: 'block',
|
|
271
|
+
tag: 'ul'
|
|
272
|
+
})
|
|
273
|
+
//获取父节点
|
|
274
|
+
const parentNode = node.parent
|
|
275
|
+
//在父节点中的位置序列
|
|
276
|
+
const index = parentNode ? parentNode.children!.findIndex(item => item.isEqual(node)) : editor.stackNodes.findIndex(item => item.isEqual(node))
|
|
277
|
+
//从父节点中移除
|
|
278
|
+
parentNode ? parentNode.children!.splice(index, 1, listNode) : editor.stackNodes.splice(index, 1, listNode)
|
|
279
|
+
//加入到列表节点中
|
|
280
|
+
editor.addNode(node, listNode)
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
//列表合并处理
|
|
285
|
+
listMergeHandler
|
|
286
|
+
],
|
|
287
|
+
pasteKeepStyles(node) {
|
|
288
|
+
const styles: KNodeStylesType = {}
|
|
289
|
+
//保留序标类型样式
|
|
290
|
+
if ((node.isMatch({ tag: 'ol' }) || node.isMatch({ tag: 'ul' })) && node.hasStyles()) {
|
|
291
|
+
if (node.styles!.hasOwnProperty('listStyleType')) styles.listStyleType = node.styles!['listStyleType']
|
|
292
|
+
}
|
|
293
|
+
return styles
|
|
294
|
+
},
|
|
295
|
+
onDetachMentBlockFromParentCallback(node) {
|
|
296
|
+
//父节点存在并且是列表节点
|
|
297
|
+
if (node.parent && (node.parent.isMatch({ tag: 'ol' }) || node.parent.isMatch({ tag: 'ul' }))) {
|
|
298
|
+
//将该节点转为列表项节点
|
|
299
|
+
node.tag = 'li'
|
|
300
|
+
node.marks = {}
|
|
301
|
+
node.styles = {}
|
|
302
|
+
node.fixed = false
|
|
303
|
+
node.nested = true
|
|
304
|
+
node.locked = false
|
|
305
|
+
node.namespace = ''
|
|
306
|
+
return false
|
|
307
|
+
}
|
|
308
|
+
return true
|
|
309
|
+
},
|
|
310
|
+
addCommands() {
|
|
311
|
+
/**
|
|
312
|
+
* 获取光标所在的有序列表或者无序列表,如果光标不在一个有序列表或者无序列表内,返回null
|
|
313
|
+
*/
|
|
314
|
+
const getList = (options: ListOptionsType) => {
|
|
315
|
+
if (options.listType) {
|
|
316
|
+
return this.getMatchNodeBySelection({
|
|
317
|
+
tag: options.ordered ? 'ol' : 'ul',
|
|
318
|
+
styles: {
|
|
319
|
+
listStyleType: options.listType
|
|
320
|
+
}
|
|
321
|
+
})
|
|
322
|
+
}
|
|
323
|
+
return this.getMatchNodeBySelection({ tag: options.ordered ? 'ol' : 'ul' })
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* 判断光标范围内是否有有序列表或者无序列表
|
|
328
|
+
*/
|
|
329
|
+
const hasList = (options: ListOptionsType) => {
|
|
330
|
+
if (options.listType) {
|
|
331
|
+
return this.isSelectionNodesSomeMatch({
|
|
332
|
+
tag: options.ordered ? 'ol' : 'ul',
|
|
333
|
+
styles: {
|
|
334
|
+
listStyleType: options.listType
|
|
335
|
+
}
|
|
336
|
+
})
|
|
337
|
+
}
|
|
338
|
+
return this.isSelectionNodesSomeMatch({ tag: options.ordered ? 'ol' : 'ul' })
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* 判断光标范围内是否都是有序列表或者无序列表
|
|
343
|
+
*/
|
|
344
|
+
const allList = (options: ListOptionsType) => {
|
|
345
|
+
if (options.listType) {
|
|
346
|
+
return this.isSelectionNodesAllMatch({
|
|
347
|
+
tag: options.ordered ? 'ol' : 'ul',
|
|
348
|
+
styles: {
|
|
349
|
+
listStyleType: options.listType
|
|
350
|
+
}
|
|
351
|
+
})
|
|
352
|
+
}
|
|
353
|
+
return this.isSelectionNodesAllMatch({ tag: options.ordered ? 'ol' : 'ul' })
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* 设置有序列表或者无序列表
|
|
358
|
+
*/
|
|
359
|
+
const setList = async (options: ListOptionsType) => {
|
|
360
|
+
if (allList(options)) {
|
|
361
|
+
return
|
|
362
|
+
}
|
|
363
|
+
//起点和终点在一起
|
|
364
|
+
if (this.selection.collapsed()) {
|
|
365
|
+
const blockNode = this.selection.start!.node.getBlock()
|
|
366
|
+
toList(this, blockNode, options.ordered, options.listType)
|
|
367
|
+
}
|
|
368
|
+
//起点和终点不在一起
|
|
369
|
+
else {
|
|
370
|
+
const blockNodes = getSelectionBlockNodes.apply(this)
|
|
371
|
+
blockNodes.forEach(item => {
|
|
372
|
+
toList(this, item, options.ordered, options.listType)
|
|
373
|
+
})
|
|
374
|
+
}
|
|
375
|
+
await this.updateView()
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* 取消有序列表或者无序列表
|
|
380
|
+
*/
|
|
381
|
+
const unsetList = async (options: ListOptionsType) => {
|
|
382
|
+
if (!allList(options)) {
|
|
383
|
+
return
|
|
384
|
+
}
|
|
385
|
+
//起点和终点在一起
|
|
386
|
+
if (this.selection.collapsed()) {
|
|
387
|
+
const blockNode = this.selection.start!.node.getBlock()
|
|
388
|
+
const matchNode = blockNode.getMatchNode({ tag: 'li' })
|
|
389
|
+
if (matchNode) ListItemToParagraph(this, matchNode)
|
|
390
|
+
}
|
|
391
|
+
//起点和终点不在一起
|
|
392
|
+
else {
|
|
393
|
+
const blockNodes = getSelectionBlockNodes.apply(this)
|
|
394
|
+
blockNodes.forEach(item => {
|
|
395
|
+
const matchNode = item.getMatchNode({ tag: 'li' })
|
|
396
|
+
if (matchNode) ListItemToParagraph(this, matchNode)
|
|
397
|
+
})
|
|
398
|
+
}
|
|
399
|
+
await this.updateView()
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
return {
|
|
403
|
+
getList,
|
|
404
|
+
hasList,
|
|
405
|
+
allList,
|
|
406
|
+
setList,
|
|
407
|
+
unsetList
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
.Kaitify {
|
|
2
|
+
ol,
|
|
3
|
+
ul {
|
|
4
|
+
margin: 0 0 var(--kaitify-large-margin) 0;
|
|
5
|
+
padding: 0;
|
|
6
|
+
|
|
7
|
+
&:last-child {
|
|
8
|
+
margin-bottom: 0 !important;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
li {
|
|
12
|
+
margin: 0 0 var(--kaitify-large-margin) 0;
|
|
13
|
+
|
|
14
|
+
&:last-child {
|
|
15
|
+
margin-bottom: 0;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import KaTex from 'katex'
|
|
2
|
+
import { common as DapCommon } from 'dap-util'
|
|
3
|
+
import { KNode, KNodeMarksType, KNodeStylesType } from '@/model'
|
|
4
|
+
import { Extension } from '../Extension'
|
|
5
|
+
import 'katex/dist/katex.css'
|
|
6
|
+
import './style.less'
|
|
7
|
+
|
|
8
|
+
declare module '../../model' {
|
|
9
|
+
interface EditorCommandsType {
|
|
10
|
+
getMath?: () => KNode | null
|
|
11
|
+
hasMath?: () => boolean
|
|
12
|
+
setMath?: (value: string) => Promise<void>
|
|
13
|
+
updateMath?: (value: string) => Promise<void>
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const MathExtension = () =>
|
|
18
|
+
Extension.create({
|
|
19
|
+
name: 'math',
|
|
20
|
+
domParseNodeCallback(node) {
|
|
21
|
+
if (
|
|
22
|
+
node.isMatch({
|
|
23
|
+
tag: 'span',
|
|
24
|
+
marks: {
|
|
25
|
+
'kaitify-math': true
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
) {
|
|
29
|
+
//锁定节点防止合并
|
|
30
|
+
node.locked = true
|
|
31
|
+
//设为行内
|
|
32
|
+
node.type = 'inline'
|
|
33
|
+
//处理子孙节点
|
|
34
|
+
KNode.flat(node.children!).forEach(item => {
|
|
35
|
+
//锁定节点防止合并
|
|
36
|
+
item.locked = true
|
|
37
|
+
//非文本节点
|
|
38
|
+
if (!item.isText()) {
|
|
39
|
+
//有子节点转为行内
|
|
40
|
+
if (item.hasChildren()) {
|
|
41
|
+
item.type = 'inline'
|
|
42
|
+
}
|
|
43
|
+
//无子节点转为闭合
|
|
44
|
+
else {
|
|
45
|
+
item.type = 'closed'
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
return node
|
|
51
|
+
},
|
|
52
|
+
pasteKeepMarks(node) {
|
|
53
|
+
const marks: KNodeMarksType = {}
|
|
54
|
+
//数学公式内的标记全部保留
|
|
55
|
+
if (
|
|
56
|
+
!!node.getMatchNode({
|
|
57
|
+
tag: 'span',
|
|
58
|
+
marks: {
|
|
59
|
+
'kaitify-math': true
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
) {
|
|
63
|
+
Object.assign(marks, DapCommon.clone(node.marks!))
|
|
64
|
+
}
|
|
65
|
+
return marks
|
|
66
|
+
},
|
|
67
|
+
pasteKeepStyles(node) {
|
|
68
|
+
const styles: KNodeStylesType = {}
|
|
69
|
+
//数学公式内的样式全部保留
|
|
70
|
+
if (
|
|
71
|
+
!!node.getMatchNode({
|
|
72
|
+
tag: 'span',
|
|
73
|
+
marks: {
|
|
74
|
+
'kaitify-math': true
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
) {
|
|
78
|
+
Object.assign(styles, DapCommon.clone(node.styles!))
|
|
79
|
+
}
|
|
80
|
+
return styles
|
|
81
|
+
},
|
|
82
|
+
beforePatchNodeToFormat(node) {
|
|
83
|
+
const mathNode = node.getMatchNode({
|
|
84
|
+
tag: 'span',
|
|
85
|
+
marks: {
|
|
86
|
+
'kaitify-math': true
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
if (mathNode) {
|
|
90
|
+
return mathNode
|
|
91
|
+
}
|
|
92
|
+
return node
|
|
93
|
+
},
|
|
94
|
+
formatRules: [
|
|
95
|
+
({ editor, node }) => {
|
|
96
|
+
if (
|
|
97
|
+
!node.isEmpty() &&
|
|
98
|
+
node.isMatch({
|
|
99
|
+
tag: 'span',
|
|
100
|
+
marks: {
|
|
101
|
+
'kaitify-math': true
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
) {
|
|
105
|
+
//公式节点必须锁定
|
|
106
|
+
node.locked = true
|
|
107
|
+
//公式节点必须是行内
|
|
108
|
+
node.type = 'inline'
|
|
109
|
+
//保持对子孙节点的处理
|
|
110
|
+
KNode.flat(node.children!).forEach(item => {
|
|
111
|
+
//锁定节点防止合并
|
|
112
|
+
item.locked = true
|
|
113
|
+
//非文本节点
|
|
114
|
+
if (!item.isText()) {
|
|
115
|
+
//有子节点转为行内
|
|
116
|
+
if (item.hasChildren()) {
|
|
117
|
+
item.type = 'inline'
|
|
118
|
+
}
|
|
119
|
+
//无子节点转为闭合
|
|
120
|
+
else {
|
|
121
|
+
item.type = 'closed'
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
//没有不可编辑标记的话需要设置
|
|
126
|
+
if (node.marks!['contenteditable'] != 'false') {
|
|
127
|
+
node.marks!['contenteditable'] = 'false'
|
|
128
|
+
}
|
|
129
|
+
//两侧设置空白元素
|
|
130
|
+
const previousNode = node.getPrevious(node.parent ? node.parent!.children! : editor.stackNodes)
|
|
131
|
+
const nextNode = node.getNext(node.parent ? node.parent!.children! : editor.stackNodes)
|
|
132
|
+
//前一个节点不存在或者不是零宽度空白文本节点
|
|
133
|
+
if (!previousNode || !previousNode.isZeroWidthText()) {
|
|
134
|
+
const zeroWidthText = KNode.createZeroWidthText()
|
|
135
|
+
editor.addNodeBefore(zeroWidthText, node)
|
|
136
|
+
}
|
|
137
|
+
//后一个节点不存在或者不是零宽度空白文本节点
|
|
138
|
+
if (!nextNode || !nextNode.isZeroWidthText()) {
|
|
139
|
+
const zeroWidthText = KNode.createZeroWidthText()
|
|
140
|
+
editor.addNodeAfter(zeroWidthText, node)
|
|
141
|
+
}
|
|
142
|
+
//重置光标
|
|
143
|
+
if (editor.isSelectionInNode(node, 'start')) {
|
|
144
|
+
const newTextNode = node.getNext(node.parent ? node.parent!.children! : editor.stackNodes)
|
|
145
|
+
if (newTextNode) editor.setSelectionBefore(newTextNode, 'start')
|
|
146
|
+
}
|
|
147
|
+
if (editor.isSelectionInNode(node, 'end')) {
|
|
148
|
+
const newTextNode = node.getNext(node.parent ? node.parent!.children! : editor.stackNodes)
|
|
149
|
+
if (newTextNode) editor.setSelectionBefore(newTextNode, 'end')
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
],
|
|
154
|
+
addCommands() {
|
|
155
|
+
/**
|
|
156
|
+
* 获取光标所在的数学公式节点,如果光标不在一个数学公式节点内,返回null
|
|
157
|
+
*/
|
|
158
|
+
const getMath = () => {
|
|
159
|
+
return this.getMatchNodeBySelection({
|
|
160
|
+
tag: 'span',
|
|
161
|
+
marks: {
|
|
162
|
+
'kaitify-math': true
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* 判断光标范围内是否有数学公式节点
|
|
169
|
+
*/
|
|
170
|
+
const hasMath = () => {
|
|
171
|
+
return this.isSelectionNodesSomeMatch({
|
|
172
|
+
tag: 'span',
|
|
173
|
+
marks: {
|
|
174
|
+
'kaitify-math': true
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* 插入数学公式
|
|
181
|
+
*/
|
|
182
|
+
const setMath = async (value: string) => {
|
|
183
|
+
if (!value || !this.selection.focused() || hasMath()) {
|
|
184
|
+
return
|
|
185
|
+
}
|
|
186
|
+
const mathHtml = KaTex.renderToString(value, {
|
|
187
|
+
output: 'html',
|
|
188
|
+
throwOnError: true
|
|
189
|
+
})
|
|
190
|
+
//设置最终的html内容
|
|
191
|
+
const html = `<span kaitify-math="${value}" contenteditable="false">${mathHtml}</span>`
|
|
192
|
+
//html内容转为节点数组
|
|
193
|
+
const nodes = this.htmlParseNode(html)
|
|
194
|
+
//插入节点
|
|
195
|
+
this.insertNode(nodes[0])
|
|
196
|
+
await this.updateView()
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* 更新数学公式
|
|
201
|
+
*/
|
|
202
|
+
const updateMath = async (value: string) => {
|
|
203
|
+
if (!value || !this.selection.focused()) {
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
const mathNode = getMath()
|
|
207
|
+
if (!mathNode) {
|
|
208
|
+
return
|
|
209
|
+
}
|
|
210
|
+
const mathHtml = KaTex.renderToString(value, {
|
|
211
|
+
output: 'html',
|
|
212
|
+
throwOnError: true
|
|
213
|
+
})
|
|
214
|
+
//设置最终的html内容
|
|
215
|
+
const html = `<span kaitify-math="${value}" contenteditable="false">${mathHtml}</span>`
|
|
216
|
+
//html内容转为节点数组
|
|
217
|
+
const nodes = this.htmlParseNode(html)
|
|
218
|
+
//替换掉原来的数学公式
|
|
219
|
+
nodes[0].parent = mathNode.parent
|
|
220
|
+
const index = mathNode.parent ? mathNode.parent.children!.findIndex(item => item.isEqual(mathNode)) : this.stackNodes.findIndex(item => item.isEqual(mathNode))
|
|
221
|
+
mathNode.parent ? mathNode.parent.children!.splice(index, 1, nodes[0]) : this.stackNodes.splice(index, 1, nodes[0])
|
|
222
|
+
this.setSelectionAfter(nodes[0])
|
|
223
|
+
await this.updateView()
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
getMath,
|
|
228
|
+
hasMath,
|
|
229
|
+
setMath,
|
|
230
|
+
updateMath
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
})
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
.Kaitify {
|
|
2
|
+
span[kaitify-math] {
|
|
3
|
+
display: inline-block;
|
|
4
|
+
padding: var(--kaitify-small-padding) var(--kaitify-padding);
|
|
5
|
+
border-radius: var(--kaitify-border-radius);
|
|
6
|
+
margin: 0 var(--kaitify-sides-between);
|
|
7
|
+
transition: all 300ms;
|
|
8
|
+
max-width: 100%;
|
|
9
|
+
background: var(--kaitify-lightest-theme);
|
|
10
|
+
|
|
11
|
+
&:hover {
|
|
12
|
+
cursor: pointer !important;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.katex,
|
|
16
|
+
math {
|
|
17
|
+
width: 100%;
|
|
18
|
+
overflow: hidden;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|