@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.
Files changed (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/examples/App.vue +342 -0
  4. package/examples/content.js +1 -0
  5. package/examples/main.ts +4 -0
  6. package/examples/test.html +23 -0
  7. package/lib/extensions/Extension.d.ts +172 -0
  8. package/lib/extensions/align/index.d.ts +10 -0
  9. package/lib/extensions/attachment/index.d.ts +29 -0
  10. package/lib/extensions/back-color/index.d.ts +9 -0
  11. package/lib/extensions/blockquote/index.d.ts +12 -0
  12. package/lib/extensions/bold/index.d.ts +9 -0
  13. package/lib/extensions/code/index.d.ts +12 -0
  14. package/lib/extensions/code-block/hljs.d.ts +12 -0
  15. package/lib/extensions/code-block/index.d.ts +15 -0
  16. package/lib/extensions/color/index.d.ts +9 -0
  17. package/lib/extensions/font-family/index.d.ts +9 -0
  18. package/lib/extensions/font-size/index.d.ts +9 -0
  19. package/lib/extensions/heading/index.d.ts +13 -0
  20. package/lib/extensions/history/index.d.ts +10 -0
  21. package/lib/extensions/horizontal/index.d.ts +7 -0
  22. package/lib/extensions/image/index.d.ts +26 -0
  23. package/lib/extensions/indent/index.d.ts +8 -0
  24. package/lib/extensions/index.d.ts +29 -0
  25. package/lib/extensions/italic/index.d.ts +9 -0
  26. package/lib/extensions/line-height/index.d.ts +9 -0
  27. package/lib/extensions/link/index.d.ts +27 -0
  28. package/lib/extensions/list/index.d.ts +18 -0
  29. package/lib/extensions/math/index.d.ts +11 -0
  30. package/lib/extensions/strikethrough/index.d.ts +9 -0
  31. package/lib/extensions/subscript/index.d.ts +9 -0
  32. package/lib/extensions/superscript/index.d.ts +9 -0
  33. package/lib/extensions/table/index.d.ts +21 -0
  34. package/lib/extensions/task/index.d.ts +12 -0
  35. package/lib/extensions/text/index.d.ts +14 -0
  36. package/lib/extensions/underline/index.d.ts +9 -0
  37. package/lib/extensions/video/index.d.ts +27 -0
  38. package/lib/index.d.ts +3 -0
  39. package/lib/kaitify-core.es.js +38337 -0
  40. package/lib/kaitify-core.umd.js +2 -0
  41. package/lib/model/Editor.d.ts +504 -0
  42. package/lib/model/History.d.ts +42 -0
  43. package/lib/model/KNode.d.ts +258 -0
  44. package/lib/model/Selection.d.ts +29 -0
  45. package/lib/model/config/dom-observe.d.ts +10 -0
  46. package/lib/model/config/event-handler.d.ts +33 -0
  47. package/lib/model/config/format-patch.d.ts +25 -0
  48. package/lib/model/config/format-rules.d.ts +37 -0
  49. package/lib/model/config/function.d.ts +84 -0
  50. package/lib/model/index.d.ts +6 -0
  51. package/lib/tools/index.d.ts +49 -0
  52. package/lib/view/index.d.ts +21 -0
  53. package/lib/view/js-render/dom-patch.d.ts +65 -0
  54. package/lib/view/js-render/index.d.ts +5 -0
  55. package/package.json +52 -0
  56. package/src/css/style.less +56 -0
  57. package/src/css/var.less +45 -0
  58. package/src/extensions/Extension.ts +200 -0
  59. package/src/extensions/align/index.ts +115 -0
  60. package/src/extensions/attachment/icon.svg +1 -0
  61. package/src/extensions/attachment/index.ts +293 -0
  62. package/src/extensions/attachment/style.less +25 -0
  63. package/src/extensions/back-color/index.ts +56 -0
  64. package/src/extensions/blockquote/index.ts +144 -0
  65. package/src/extensions/blockquote/style.less +16 -0
  66. package/src/extensions/bold/index.ts +77 -0
  67. package/src/extensions/code/index.ts +295 -0
  68. package/src/extensions/code/style.less +14 -0
  69. package/src/extensions/code-block/hljs.less +183 -0
  70. package/src/extensions/code-block/hljs.ts +95 -0
  71. package/src/extensions/code-block/index.ts +308 -0
  72. package/src/extensions/code-block/style.less +20 -0
  73. package/src/extensions/color/index.ts +56 -0
  74. package/src/extensions/font-family/index.ts +80 -0
  75. package/src/extensions/font-size/index.ts +56 -0
  76. package/src/extensions/heading/index.ts +164 -0
  77. package/src/extensions/heading/style.less +42 -0
  78. package/src/extensions/history/index.ts +96 -0
  79. package/src/extensions/horizontal/index.ts +45 -0
  80. package/src/extensions/horizontal/style.less +13 -0
  81. package/src/extensions/image/index.ts +242 -0
  82. package/src/extensions/image/style.less +8 -0
  83. package/src/extensions/indent/index.ts +98 -0
  84. package/src/extensions/index.ts +29 -0
  85. package/src/extensions/italic/index.ts +77 -0
  86. package/src/extensions/line-height/index.ts +113 -0
  87. package/src/extensions/link/index.ts +184 -0
  88. package/src/extensions/link/style.less +19 -0
  89. package/src/extensions/list/index.ts +410 -0
  90. package/src/extensions/list/style.less +19 -0
  91. package/src/extensions/math/index.ts +233 -0
  92. package/src/extensions/math/style.less +21 -0
  93. package/src/extensions/strikethrough/index.ts +78 -0
  94. package/src/extensions/subscript/index.ts +77 -0
  95. package/src/extensions/superscript/index.ts +77 -0
  96. package/src/extensions/table/index.ts +1148 -0
  97. package/src/extensions/table/style.less +71 -0
  98. package/src/extensions/task/index.ts +243 -0
  99. package/src/extensions/task/style.less +59 -0
  100. package/src/extensions/text/index.ts +359 -0
  101. package/src/extensions/underline/index.ts +78 -0
  102. package/src/extensions/video/index.ts +273 -0
  103. package/src/extensions/video/style.less +8 -0
  104. package/src/index.ts +9 -0
  105. package/src/model/Editor.ts +1963 -0
  106. package/src/model/History.ts +115 -0
  107. package/src/model/KNode.ts +677 -0
  108. package/src/model/Selection.ts +39 -0
  109. package/src/model/config/dom-observe.ts +184 -0
  110. package/src/model/config/event-handler.ts +237 -0
  111. package/src/model/config/format-patch.ts +215 -0
  112. package/src/model/config/format-rules.ts +218 -0
  113. package/src/model/config/function.ts +1018 -0
  114. package/src/model/index.ts +6 -0
  115. package/src/tools/index.ts +156 -0
  116. package/src/view/index.ts +46 -0
  117. package/src/view/js-render/dom-patch.ts +324 -0
  118. package/src/view/js-render/index.ts +210 -0
  119. package/vite-env.d.ts +2 -0
@@ -0,0 +1,293 @@
1
+ import { event as DapEvent } from 'dap-util'
2
+ import { Editor, KNode, KNodeMarksType, KNodeStylesType } from '@/model'
3
+ import { Extension } from '../Extension'
4
+ import defaultIcon from './icon.svg?raw'
5
+ import './style.less'
6
+
7
+ export type SetAttachmentOptionType = {
8
+ url: string
9
+ text: string
10
+ icon?: string
11
+ }
12
+
13
+ export type UpdateAttachmentOptionType = {
14
+ url?: string
15
+ text?: string
16
+ icon?: string
17
+ }
18
+
19
+ export type AttachmentExtensionPropsType = {
20
+ icon: string
21
+ }
22
+
23
+ declare module '../../model' {
24
+ interface EditorCommandsType {
25
+ getAttachment?: () => KNode | null
26
+ hasAttachment?: () => boolean
27
+ setAttachment?: (options: SetAttachmentOptionType) => Promise<void>
28
+ updateAttachment?: (options: UpdateAttachmentOptionType) => Promise<void>
29
+ getAttachmentInfo?: () => { url: string; text: string; icon: string } | null
30
+ }
31
+ }
32
+
33
+ /**
34
+ * 下载附件
35
+ */
36
+ const downloadAttachment = (editor: Editor) => {
37
+ DapEvent.off(editor.$el!, 'click.attachment')
38
+ DapEvent.on(editor.$el!, 'click.attachment', async e => {
39
+ //可编辑状态下无法下载
40
+ if (editor.isEditable()) {
41
+ return
42
+ }
43
+ const event = e as MouseEvent
44
+ const elm = event.target as HTMLElement
45
+ if (elm === editor.$el) {
46
+ return
47
+ }
48
+ const node = editor.findNode(elm)
49
+ const matchNode = node.getMatchNode({
50
+ tag: 'span',
51
+ marks: {
52
+ 'kaitify-attachment': true
53
+ }
54
+ })
55
+ //点击的是附件
56
+ if (matchNode) {
57
+ //获取文件地址
58
+ const url = matchNode.marks!['kaitify-attachment'] as string
59
+ //使用fetch读取文件地址
60
+ const res = await fetch(url, {
61
+ method: 'GET'
62
+ })
63
+ //获取blob数据
64
+ const blob = await res.blob()
65
+ //创建a标签进行下载
66
+ const a = document.createElement('a')
67
+ a.setAttribute('target', '_blank')
68
+ a.setAttribute('href', URL.createObjectURL(blob))
69
+ a.setAttribute(
70
+ 'download',
71
+ matchNode.children!.reduce((val, item) => {
72
+ return val + item.textContent
73
+ }, '')
74
+ )
75
+ a.click()
76
+ }
77
+ })
78
+ }
79
+
80
+ export const AttachmentExtension = (props?: AttachmentExtensionPropsType) =>
81
+ Extension.create({
82
+ name: 'attachment',
83
+ pasteKeepStyles(node) {
84
+ const styles: KNodeStylesType = {}
85
+ if (node.isMatch({ tag: 'span', marks: { 'kaitify-attachment': true } })) {
86
+ styles.backgroundImage = node.styles!.backgroundImage
87
+ }
88
+ return styles
89
+ },
90
+ pasteKeepMarks(node) {
91
+ const marks: KNodeMarksType = {}
92
+ if (node.isMatch({ tag: 'span', marks: { 'kaitify-attachment': true } })) {
93
+ marks['kaitify-attachment'] = node.marks!['kaitify-attachment']
94
+ }
95
+ return marks
96
+ },
97
+ domParseNodeCallback(node) {
98
+ if (node.isMatch({ tag: 'span', marks: { 'kaitify-attachment': true } })) {
99
+ //锁定节点
100
+ node.locked = true
101
+ //设为行内
102
+ node.type = 'inline'
103
+ //处理子孙节点
104
+ KNode.flat(node.children!).forEach(item => {
105
+ //锁定节点
106
+ item.locked = true
107
+ //非文本节点
108
+ if (!item.isText()) {
109
+ //有子节点转为行内
110
+ if (item.hasChildren()) {
111
+ item.type = 'inline'
112
+ }
113
+ //无子节点转为闭合
114
+ else {
115
+ item.type = 'closed'
116
+ }
117
+ }
118
+ })
119
+ }
120
+ return node
121
+ },
122
+ formatRules: [
123
+ ({ editor, node }) => {
124
+ if (
125
+ !node.isEmpty() &&
126
+ node.isMatch({
127
+ tag: 'span',
128
+ marks: {
129
+ 'kaitify-attachment': true
130
+ }
131
+ })
132
+ ) {
133
+ //附件节点必须是锁定的
134
+ node.locked = true
135
+ //附件节点必须行内
136
+ node.type = 'inline'
137
+ //保持子孙节点的类型
138
+ KNode.flat(node.children!).forEach(item => {
139
+ //锁定节点
140
+ item.locked = true
141
+ //非文本节点
142
+ if (!item.isText()) {
143
+ //有子节点转为行内
144
+ if (item.hasChildren()) {
145
+ item.type = 'inline'
146
+ }
147
+ //无子节点转为闭合
148
+ else {
149
+ item.type = 'closed'
150
+ }
151
+ }
152
+ })
153
+ //没有不可编辑标记的话需要设置
154
+ if (node.marks!['contenteditable'] != 'false') {
155
+ node.marks!['contenteditable'] = 'false'
156
+ }
157
+ //两侧设置空白元素
158
+ const previousNode = node.getPrevious(node.parent ? node.parent!.children! : editor.stackNodes)
159
+ const nextNode = node.getNext(node.parent ? node.parent!.children! : editor.stackNodes)
160
+ //前一个节点不存在或者不是零宽度空白文本节点
161
+ if (!previousNode || !previousNode.isZeroWidthText()) {
162
+ const zeroWidthText = KNode.createZeroWidthText()
163
+ editor.addNodeBefore(zeroWidthText, node)
164
+ }
165
+ //后一个节点不存在或者不是零宽度空白文本节点
166
+ if (!nextNode || !nextNode.isZeroWidthText()) {
167
+ const zeroWidthText = KNode.createZeroWidthText()
168
+ editor.addNodeAfter(zeroWidthText, node)
169
+ }
170
+ //重置光标
171
+ if (editor.isSelectionInNode(node, 'start')) {
172
+ const newTextNode = node.getNext(node.parent ? node.parent!.children! : editor.stackNodes)
173
+ if (newTextNode) editor.setSelectionBefore(newTextNode, 'start')
174
+ }
175
+ if (editor.isSelectionInNode(node, 'end')) {
176
+ const newTextNode = node.getNext(node.parent ? node.parent!.children! : editor.stackNodes)
177
+ if (newTextNode) editor.setSelectionBefore(newTextNode, 'end')
178
+ }
179
+ }
180
+ }
181
+ ],
182
+ afterUpdateView() {
183
+ //下载附件
184
+ downloadAttachment(this)
185
+ },
186
+ addCommands() {
187
+ /**
188
+ * 获取光标所在的附件节点,如果光标不在一个附件节点内,返回null
189
+ */
190
+ const getAttachment = () => {
191
+ return this.getMatchNodeBySelection({
192
+ tag: 'span',
193
+ marks: {
194
+ 'kaitify-attachment': true
195
+ }
196
+ })
197
+ }
198
+
199
+ /**
200
+ * 判断光标范围内是否有附件节点
201
+ */
202
+ const hasAttachment = () => {
203
+ return this.isSelectionNodesSomeMatch({
204
+ tag: 'span',
205
+ marks: {
206
+ 'kaitify-attachment': true
207
+ }
208
+ })
209
+ }
210
+
211
+ /**
212
+ * 插入附件
213
+ */
214
+ const setAttachment = async (options: SetAttachmentOptionType) => {
215
+ if (!this.selection.focused() || hasAttachment()) {
216
+ return
217
+ }
218
+ if (!options.url || !options.text) {
219
+ return
220
+ }
221
+ const defaultIconBase64 = `data:image/svg+xml;base64,${btoa(defaultIcon)}`
222
+ //设置html内容
223
+ const html = `<span kaitify-attachment="${options.url}" contenteditable="false" style="background-image:url(${options.icon || props?.icon || defaultIconBase64})"><span>${options.text}</span></span>`
224
+ //html内容转为节点数组
225
+ const nodes = this.htmlParseNode(html)
226
+ //插入节点
227
+ this.insertNode(nodes[0])
228
+ //更新视图
229
+ await this.updateView()
230
+ }
231
+
232
+ /**
233
+ * 更新附件
234
+ */
235
+ const updateAttachment = async (options: UpdateAttachmentOptionType) => {
236
+ if (!this.selection.focused()) {
237
+ return
238
+ }
239
+ if (!options.url && !options.text && !options.icon) {
240
+ return
241
+ }
242
+ const attachmentNode = getAttachment()
243
+ if (!attachmentNode) {
244
+ return
245
+ }
246
+ //更新url
247
+ if (options.url) {
248
+ attachmentNode.marks!['kaitify-attachment'] = options.url
249
+ }
250
+ //更新text
251
+ if (options.text) {
252
+ const textNode = KNode.create({
253
+ type: 'text',
254
+ textContent: options.text
255
+ })
256
+ textNode.parent = attachmentNode
257
+ attachmentNode.children = [textNode]
258
+ }
259
+ if (options.icon) {
260
+ attachmentNode.styles!['backgroundImage'] = `url(${options.icon})`
261
+ }
262
+ //更新视图
263
+ await this.updateView()
264
+ }
265
+
266
+ /**
267
+ * 获取附件信息
268
+ */
269
+ const getAttachmentInfo = () => {
270
+ if (!this.selection.focused()) {
271
+ return null
272
+ }
273
+ const attachmentNode = getAttachment()
274
+ if (!attachmentNode) {
275
+ return null
276
+ }
277
+ const url = attachmentNode.marks!['kaitify-attachment'] as string
278
+ const text = attachmentNode.children!.reduce((val, item) => {
279
+ return val + item.textContent
280
+ }, '')
281
+ const icon = attachmentNode.styles!['backgroundImage']!.match(/url\(["']?(.*?)["']?\)/)?.[1]!
282
+ return { url, text, icon }
283
+ }
284
+
285
+ return {
286
+ getAttachment,
287
+ hasAttachment,
288
+ setAttachment,
289
+ updateAttachment,
290
+ getAttachmentInfo
291
+ }
292
+ }
293
+ })
@@ -0,0 +1,25 @@
1
+ .Kaitify {
2
+ span[kaitify-attachment] {
3
+ display: inline-flex;
4
+ justify-content: flex-start;
5
+ flex-wrap: wrap;
6
+ align-items: center;
7
+ margin: 0 var(--kaitify-sides-between);
8
+ color: #06c27c;
9
+ background-position: left center;
10
+ background-repeat: no-repeat;
11
+ background-size: 20px;
12
+ padding-left: 25px;
13
+
14
+ &:hover {
15
+ cursor: pointer !important;
16
+ }
17
+ }
18
+
19
+ //非编辑状态下
20
+ &:not([contenteditable='true']) {
21
+ span[kaitify-attachment]:hover {
22
+ text-decoration: underline;
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,56 @@
1
+ import { KNodeStylesType } from '@/model'
2
+ import { Extension } from '../Extension'
3
+
4
+ declare module '../../model' {
5
+ interface EditorCommandsType {
6
+ isBackColor?: (value: string) => boolean
7
+ setBackColor?: (value: string) => Promise<void>
8
+ unsetBackColor?: (value: string) => Promise<void>
9
+ }
10
+ }
11
+
12
+ export const BackColorExtension = () =>
13
+ Extension.create({
14
+ name: 'backColor',
15
+ pasteKeepStyles(node) {
16
+ const styles: KNodeStylesType = {}
17
+ if (node.isText() && node.hasStyles()) {
18
+ if (node.styles!.hasOwnProperty('backgroundColor')) styles.backgroundColor = node.styles!.backgroundColor
19
+ }
20
+ return styles
21
+ },
22
+ addCommands() {
23
+ /**
24
+ * 光标所在文本的背景颜色是否与入参一致
25
+ */
26
+ const isBackColor = (value: string) => {
27
+ return this.commands.isTextStyle!('backgroundColor', value) || this.commands.isTextStyle!('background', value)
28
+ }
29
+ /**
30
+ * 设置背景颜色
31
+ */
32
+ const setBackColor = async (value: string) => {
33
+ if (isBackColor(value)) {
34
+ return
35
+ }
36
+ await this.commands.setTextStyle!({
37
+ backgroundColor: value
38
+ })
39
+ }
40
+ /**
41
+ * 取消背景颜色
42
+ */
43
+ const unsetBackColor = async (value: string) => {
44
+ if (!isBackColor(value)) {
45
+ return
46
+ }
47
+ await this.commands.removeTextStyle!(['backgroundColor', 'background'])
48
+ }
49
+
50
+ return {
51
+ isBackColor,
52
+ setBackColor,
53
+ unsetBackColor
54
+ }
55
+ }
56
+ })
@@ -0,0 +1,144 @@
1
+ import { Editor, KNode } from '@/model'
2
+ import { getSelectionBlockNodes } from '@/model/config/function'
3
+ import { Extension } from '../Extension'
4
+ import './style.less'
5
+
6
+ declare module '../../model' {
7
+ interface EditorCommandsType {
8
+ getBlockquote?: () => KNode | null
9
+ hasBlockquote?: () => boolean
10
+ allBlockquote?: () => boolean
11
+ setBlockquote?: () => Promise<void>
12
+ unsetBlockquote?: () => Promise<void>
13
+ }
14
+ }
15
+
16
+ /**
17
+ * 块节点转为引用
18
+ */
19
+ const toBlockquote = (editor: Editor, node: KNode) => {
20
+ if (!node.isBlock()) {
21
+ return
22
+ }
23
+ //是固定的块节点或者内嵌套的块节点
24
+ if (node.fixed || node.nested) {
25
+ //创建引用节点
26
+ const blockquoteNode = KNode.create({
27
+ type: 'block',
28
+ tag: 'blockquote',
29
+ children: []
30
+ })
31
+ //将块节点的子节点给引用节点
32
+ node.children!.forEach((item, index) => {
33
+ editor.addNode(item, blockquoteNode, index)
34
+ })
35
+ //将引用节点添加到块节点下
36
+ blockquoteNode.parent = node
37
+ node.children = [blockquoteNode]
38
+ }
39
+ //非固定块节点
40
+ else {
41
+ editor.toParagraph(node)
42
+ node.tag = 'blockquote'
43
+ }
44
+ }
45
+
46
+ export const BlockquoteExtension = () =>
47
+ Extension.create({
48
+ name: 'blockquote',
49
+ extraKeepTags: ['blockquote'],
50
+ domParseNodeCallback(node) {
51
+ if (node.isMatch({ tag: 'blockquote' })) {
52
+ node.type = 'block'
53
+ }
54
+ return node
55
+ },
56
+ formatRules: [
57
+ ({ node }) => {
58
+ if (node.isMatch({ tag: 'blockquote' })) {
59
+ node.type = 'block'
60
+ }
61
+ }
62
+ ],
63
+ addCommands() {
64
+ /**
65
+ * 获取光标所在的引用节点,如果光标不在一个引用节点内,返回null
66
+ */
67
+ const getBlockquote = () => {
68
+ return this.getMatchNodeBySelection({
69
+ tag: 'blockquote'
70
+ })
71
+ }
72
+
73
+ /**
74
+ * 判断光标范围内是否有引用节点
75
+ */
76
+ const hasBlockquote = () => {
77
+ return this.isSelectionNodesSomeMatch({
78
+ tag: 'blockquote'
79
+ })
80
+ }
81
+
82
+ /**
83
+ * 光标范围内是否都是引用节点
84
+ */
85
+ const allBlockquote = () => {
86
+ return this.isSelectionNodesAllMatch({
87
+ tag: 'blockquote'
88
+ })
89
+ }
90
+
91
+ /**
92
+ * 设置引用
93
+ */
94
+ const setBlockquote = async () => {
95
+ if (allBlockquote()) {
96
+ return
97
+ }
98
+ //起点和终点在一起
99
+ if (this.selection.collapsed()) {
100
+ const blockNode = this.selection.start!.node.getBlock()
101
+ toBlockquote(this, blockNode)
102
+ }
103
+ //起点和终点不在一起
104
+ else {
105
+ const blockNodes = getSelectionBlockNodes.apply(this)
106
+ blockNodes.forEach(item => {
107
+ toBlockquote(this, item)
108
+ })
109
+ }
110
+ await this.updateView()
111
+ }
112
+
113
+ /**
114
+ * 取消引用
115
+ */
116
+ const unsetBlockquote = async () => {
117
+ if (!allBlockquote()) {
118
+ return
119
+ }
120
+ //起点和终点在一起
121
+ if (this.selection.collapsed()) {
122
+ const matchNode = this.selection.start!.node.getMatchNode({ tag: 'blockquote' })
123
+ if (matchNode) this.toParagraph(matchNode)
124
+ }
125
+ //起点和终点不在一起
126
+ else {
127
+ const blockNodes = getSelectionBlockNodes.apply(this)
128
+ blockNodes.forEach(item => {
129
+ const matchNode = item.getMatchNode({ tag: 'blockquote' })
130
+ if (matchNode) this.toParagraph(matchNode)
131
+ })
132
+ }
133
+ await this.updateView()
134
+ }
135
+
136
+ return {
137
+ getBlockquote,
138
+ hasBlockquote,
139
+ allBlockquote,
140
+ setBlockquote,
141
+ unsetBlockquote
142
+ }
143
+ }
144
+ })
@@ -0,0 +1,16 @@
1
+ .Kaitify blockquote {
2
+ line-height: var(--kaitify-line-height);
3
+ margin: 0 0 var(--kaitify-large-margin) 0 !important;
4
+ padding: 0 0 0 var(--kaitify-large-padding);
5
+ border-left: 6px solid fade(#000, 10);
6
+ color: #666;
7
+
8
+ &:last-child {
9
+ margin-bottom: 0 !important;
10
+ }
11
+ }
12
+
13
+ :root[kaitify-dark] .Kaitify blockquote {
14
+ color: fade(#fff, 90);
15
+ border-left-color: fade(#fff, 30);
16
+ }
@@ -0,0 +1,77 @@
1
+ import { KNodeStylesType } from '@/model'
2
+ import { splitNodeToNodes } from '@/model/config/function'
3
+ import { Extension } from '../Extension'
4
+
5
+ declare module '../../model' {
6
+ interface EditorCommandsType {
7
+ isBold?: () => boolean
8
+ setBold?: () => Promise<void>
9
+ unsetBold?: () => Promise<void>
10
+ }
11
+ }
12
+
13
+ export const BoldExtension = () =>
14
+ Extension.create({
15
+ name: 'bold',
16
+ extraKeepTags: ['b', 'strong'],
17
+ domParseNodeCallback(node) {
18
+ if (node.isMatch({ tag: 'b' }) || node.isMatch({ tag: 'strong' })) {
19
+ node.type = 'inline'
20
+ }
21
+ return node
22
+ },
23
+ pasteKeepStyles(node) {
24
+ const styles: KNodeStylesType = {}
25
+ if (node.isText() && node.hasStyles()) {
26
+ if (node.styles!.hasOwnProperty('fontWeight')) styles.fontWeight = node.styles!.fontWeight
27
+ }
28
+ return styles
29
+ },
30
+ formatRules: [
31
+ ({ editor, node }) => {
32
+ if (!node.isEmpty() && (node.isMatch({ tag: 'b' }) || node.isMatch({ tag: 'strong' }))) {
33
+ const styles: KNodeStylesType = node.styles || {}
34
+ node.styles = {
35
+ ...styles,
36
+ fontWeight: 'bold'
37
+ }
38
+ node.tag = editor.textRenderTag
39
+ splitNodeToNodes.apply(editor, [node])
40
+ }
41
+ }
42
+ ],
43
+ addCommands() {
44
+ /**
45
+ * 光标所在文本是否加粗
46
+ */
47
+ const isBold = () => {
48
+ return this.commands.isTextStyle!('fontWeight', 'bold') || this.commands.isTextStyle!('fontWeight', 'bolder') || this.commands.isTextStyle!('fontWeight', '700') || this.commands.isTextStyle!('fontWeight', '800') || this.commands.isTextStyle!('fontWeight', '900')
49
+ }
50
+ /**
51
+ * 设置加粗
52
+ */
53
+ const setBold = async () => {
54
+ if (isBold()) {
55
+ return
56
+ }
57
+ await this.commands.setTextStyle!({
58
+ fontWeight: 'bold'
59
+ })
60
+ }
61
+ /**
62
+ * 取消加粗
63
+ */
64
+ const unsetBold = async () => {
65
+ if (!isBold()) {
66
+ return
67
+ }
68
+ await this.commands.removeTextStyle!(['fontWeight'])
69
+ }
70
+
71
+ return {
72
+ isBold,
73
+ setBold,
74
+ unsetBold
75
+ }
76
+ }
77
+ })