vue-editify 0.2.17 → 0.2.19

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 (85) hide show
  1. package/examples/App.vue +4 -12
  2. package/lib/components/colors/colors.vue.d.ts +9 -0
  3. package/lib/components/colors/props.d.ts +4 -0
  4. package/lib/components/tooltip/tooltip.vue.d.ts +1 -1
  5. package/lib/core/function.d.ts +112 -64
  6. package/lib/core/rule.d.ts +23 -17
  7. package/lib/core/shortcut.d.ts +36 -0
  8. package/lib/core/tool.d.ts +12 -16
  9. package/lib/editify/editify.vue.d.ts +162 -15
  10. package/lib/editify/props.d.ts +1 -5
  11. package/lib/editify/toolbar/props.d.ts +1 -1
  12. package/lib/editify/toolbar/toolbar.vue.d.ts +3 -3
  13. package/lib/editify.es.js +13660 -12954
  14. package/lib/editify.umd.js +2 -2
  15. package/lib/feature/align.d.ts +0 -14
  16. package/lib/feature/heading.d.ts +0 -14
  17. package/lib/feature/lineHeight.d.ts +0 -14
  18. package/lib/feature/orderList.d.ts +1 -3
  19. package/lib/feature/task.d.ts +0 -14
  20. package/lib/feature/unorderList.d.ts +1 -3
  21. package/lib/index.d.ts +164 -17
  22. package/package.json +2 -2
  23. package/src/components/button/button.vue +3 -3
  24. package/src/components/checkbox/checkbox.vue +1 -1
  25. package/src/components/colors/colors.vue +4 -4
  26. package/src/components/colors/props.ts +6 -1
  27. package/src/components/insertAttachment/insertAttachment.vue +1 -1
  28. package/src/components/insertImage/insertImage.vue +1 -1
  29. package/src/components/insertLink/insertLink.vue +1 -1
  30. package/src/components/insertVideo/insertVideo.vue +1 -1
  31. package/src/components/layer/layer.vue +9 -3
  32. package/src/components/tooltip/tooltip.vue +1 -1
  33. package/src/components/updateLink/updateLink.vue +1 -1
  34. package/src/core/function.ts +873 -491
  35. package/src/core/rule.ts +86 -368
  36. package/src/core/shortcut.ts +386 -0
  37. package/src/core/tool.ts +111 -159
  38. package/src/css/var.less +0 -10
  39. package/src/editify/editify.less +85 -39
  40. package/src/editify/editify.vue +204 -88
  41. package/src/editify/menu/menu.vue +2 -3
  42. package/src/editify/props.ts +1 -6
  43. package/src/editify/toolbar/props.ts +2 -2
  44. package/src/editify/toolbar/toolbar.vue +12 -12
  45. package/src/feature/align.ts +2 -62
  46. package/src/feature/attachment.ts +14 -27
  47. package/src/feature/backColor.ts +2 -1
  48. package/src/feature/bold.ts +1 -1
  49. package/src/feature/code.ts +1 -1
  50. package/src/feature/codeBlock.ts +3 -3
  51. package/src/feature/fontFamily.ts +1 -1
  52. package/src/feature/fontSize.ts +1 -1
  53. package/src/feature/foreColor.ts +2 -1
  54. package/src/feature/formatClear.ts +1 -1
  55. package/src/feature/fullScreen.ts +1 -1
  56. package/src/feature/heading.ts +5 -76
  57. package/src/feature/image.ts +1 -1
  58. package/src/feature/indent.ts +1 -1
  59. package/src/feature/infoBlock.ts +6 -37
  60. package/src/feature/italic.ts +1 -1
  61. package/src/feature/lineHeight.ts +2 -78
  62. package/src/feature/link.ts +1 -1
  63. package/src/feature/mathformula.ts +4 -51
  64. package/src/feature/orderList.ts +168 -37
  65. package/src/feature/quote.ts +3 -3
  66. package/src/feature/redo.ts +1 -1
  67. package/src/feature/separator.ts +1 -1
  68. package/src/feature/sourceView.ts +1 -1
  69. package/src/feature/strikethrough.ts +1 -1
  70. package/src/feature/sub.ts +1 -1
  71. package/src/feature/super.ts +1 -1
  72. package/src/feature/table.ts +3 -3
  73. package/src/feature/task.ts +4 -58
  74. package/src/feature/underline.ts +1 -1
  75. package/src/feature/undo.ts +1 -1
  76. package/src/feature/unorderList.ts +108 -37
  77. package/src/feature/video.ts +1 -1
  78. package/src/icon/iconfont.css +39 -3
  79. package/src/icon/iconfont.ttf +0 -0
  80. package/src/icon/iconfont.woff +0 -0
  81. package/src/index.ts +13 -11
  82. package/src/locale/en_US.ts +109 -110
  83. package/src/locale/zh_CN.ts +11 -12
  84. package/lib/feature/panel.d.ts +0 -18
  85. package/src/feature/panel.ts +0 -107
@@ -3,8 +3,8 @@
3
3
  */
4
4
  import { common as DapCommon } from 'dap-util'
5
5
  import { AlexEditor, AlexElement, AlexElementsRangeType, AlexElementCreateConfigType } from 'alex-editor'
6
- import { ButtonOptionsItemType } from '@/components/button'
7
- import { cloneData, queryHasValue, getButtonOptionsConfig, ObjectType } from './tool'
6
+ import KaTex from 'katex'
7
+ import { queryHasValue, ObjectType } from './tool'
8
8
 
9
9
  export type ElementMatchConfigType = {
10
10
  parsedom?: string
@@ -12,6 +12,257 @@ export type ElementMatchConfigType = {
12
12
  styles?: ObjectType
13
13
  }
14
14
 
15
+ /** --------------------------------代码块操作相关函数------------------------------------------------ */
16
+
17
+ /**
18
+ * 更新代码块内的光标位置
19
+ * @param editor
20
+ * @param element
21
+ * @param originalTextElements
22
+ * @param newElements
23
+ * @returns
24
+ */
25
+ export const updateRangeInPre = (editor: AlexEditor, element: AlexElement, originalTextElements: AlexElement[], newElements: AlexElement[]) => {
26
+ if (!editor.range) {
27
+ return
28
+ }
29
+ //如果虚拟光标的起点在代码块内对虚拟光标的起点进行重新定位
30
+ if (editor.range.anchor.element.getBlock().isEqual(element)) {
31
+ //获取起点所在文本元素的在所有文本元素中的序列
32
+ const elIndex = originalTextElements.findIndex(el => editor.range!.anchor.element.isEqual(el))
33
+ //起点在整个代码内容中的位置
34
+ const offset = originalTextElements.filter((_el, i) => i < elIndex).reduce((total, item) => total + item.textContent!.length, 0) + editor.range.anchor.offset
35
+ //获取pre下新的子孙元素中全部的文本元素
36
+ const newTextElements = AlexElement.flatElements(newElements).filter(el => el.isText() && !el.isEmpty())
37
+ let i = 0
38
+ let index = 0
39
+ //遍历
40
+ while (i < newTextElements.length) {
41
+ let newIndex = index + newTextElements[i].textContent!.length
42
+ if (offset >= index && offset <= newIndex) {
43
+ editor.range.anchor.element = newTextElements[i]
44
+ editor.range.anchor.offset = offset - index
45
+ break
46
+ }
47
+ i++
48
+ index = newIndex
49
+ }
50
+ }
51
+ //如果虚拟光标的终点在代码块内需要对虚拟光标的终点进行重新定位
52
+ if (editor.range.focus.element.getBlock().isEqual(element)) {
53
+ //获取终点所在文本元素的在所有文本元素中的序列
54
+ const elIndex = originalTextElements.findIndex(el => editor.range!.focus.element.isEqual(el))
55
+ //终点在整个代码内容中的位置
56
+ const offset = originalTextElements.filter((_el, i) => i < elIndex).reduce((total, item) => total + item.textContent!.length, 0) + editor.range.focus.offset
57
+ //获取全部的新文本元素
58
+ const newTextElements = AlexElement.flatElements(newElements).filter(el => el.isText() && !el.isEmpty())
59
+ let i = 0
60
+ let index = 0
61
+ //遍历
62
+ while (i < newTextElements.length) {
63
+ let newIndex = index + newTextElements[i].textContent!.length
64
+ if (offset >= index && offset <= newIndex) {
65
+ editor.range.focus.element = newTextElements[i]
66
+ editor.range.focus.offset = offset - index
67
+ break
68
+ }
69
+ i++
70
+ index = newIndex
71
+ }
72
+ }
73
+ }
74
+
75
+ /** --------------------------------表格操作相关函数--------------------------------------------- */
76
+
77
+ /**
78
+ * 自动隐藏被合并的单元格
79
+ * @param editor
80
+ * @param rowElements
81
+ */
82
+ export const autoHideMergedTableCells = (editor: AlexEditor, rowElements: AlexElement[]) => {
83
+ const cells = AlexElement.flatElements(rowElements).filter(item => item.parsedom == 'td')
84
+ cells.forEach(cell => {
85
+ if (cell.hasMarks() && !cell.marks!['data-editify-merged']) {
86
+ //获取colspan
87
+ const colspan = isNaN(Number(cell.marks!['colspan'])) ? 1 : Number(cell.marks!['colspan'])
88
+ //获取rowspan
89
+ const rowspan = isNaN(Number(cell.marks!['rowspan'])) ? 1 : Number(cell.marks!['rowspan'])
90
+ //如果是跨列单元格,隐藏该单元格同行后的colspan-1个单元格
91
+ if (colspan > 1) {
92
+ let el = cell
93
+ let i = 1
94
+ while (i < colspan) {
95
+ const nextCell = editor.getNextElement(el)!
96
+ if (nextCell) {
97
+ if (nextCell.hasMarks()) {
98
+ nextCell.marks!['data-editify-merged'] = 'true'
99
+ } else {
100
+ nextCell.marks = {
101
+ 'data-editify-merged': 'true'
102
+ }
103
+ }
104
+ el = nextCell
105
+ i++
106
+ } else {
107
+ break
108
+ }
109
+ }
110
+ }
111
+ //如果是跨行单元格,隐藏该单元格同列后的rowspan-1行单元格
112
+ if (rowspan > 1) {
113
+ const index = cell.parent!.children!.findIndex(item => item.isEqual(cell))
114
+ let el = cell
115
+ let i = 1
116
+ while (i < rowspan && el && editor.getNextElement(el.parent!)) {
117
+ const nextRow = editor.getNextElement(el.parent!)!
118
+ //根据跨行单元格占据的列数,在后的rowspan-1行中隐藏colspan个单元格
119
+ for (let j = index; j < index + colspan; j++) {
120
+ const current = nextRow.children![j]
121
+ if (current) {
122
+ if (current.hasMarks()) {
123
+ current.marks!['data-editify-merged'] = 'true'
124
+ } else {
125
+ current.marks = {
126
+ 'data-editify-merged': 'true'
127
+ }
128
+ }
129
+ }
130
+ }
131
+ el = nextRow.children![index]
132
+ i++
133
+ }
134
+ }
135
+ }
136
+ })
137
+ }
138
+
139
+ /**
140
+ * 自动补全表格行和列
141
+ * @param editor
142
+ * @param rowElements
143
+ * @param rowNumber
144
+ * @param columnNumber
145
+ */
146
+ export const autocompleteTableCells = (editor: AlexEditor, rowElements: AlexElement[], rowNumber: number, columnNumber: number) => {
147
+ //遍历所有的单元格
148
+ AlexElement.flatElements(rowElements).forEach(item => {
149
+ if (item.parsedom == 'td' && item.hasMarks()) {
150
+ //删除被合并的标识
151
+ if (item.marks!['data-editify-merged']) {
152
+ delete item.marks!['data-editify-merged']
153
+ }
154
+ //获取colspan
155
+ const colspan = isNaN(Number(item.marks!['colspan'])) ? 1 : Number(item.marks!['colspan'])
156
+ //获取rowspan
157
+ const rowspan = isNaN(Number(item.marks!['rowspan'])) ? 1 : Number(item.marks!['rowspan'])
158
+ //针对colspan>1的单元格在后面补全隐藏的单元格
159
+ if (colspan > 1) {
160
+ let i = 1
161
+ //补全的数量小于需要补全的数量并且列总数量小于理论数量
162
+ while (i < colspan && item.parent!.children!.length < columnNumber) {
163
+ const column = AlexElement.create({
164
+ type: 'inblock',
165
+ parsedom: 'td',
166
+ marks: {
167
+ 'data-editify-merged': 'true'
168
+ },
169
+ children: [
170
+ {
171
+ type: 'closed',
172
+ parsedom: 'br'
173
+ }
174
+ ]
175
+ })
176
+ editor.addElementAfter(column, item)
177
+ i++
178
+ }
179
+ }
180
+ //针对rowspan>1的单元格在后面的行中对应位置补全隐藏的单元格
181
+ if (rowspan > 1) {
182
+ let el = item
183
+ let i = 1
184
+ while (i < rowspan && editor.getNextElement(el.parent!) && editor.getNextElement(el.parent!)!.children!.length < columnNumber) {
185
+ //下一行
186
+ const nextRow = editor.getNextElement(el.parent!)!
187
+ //单元格在行中的序列
188
+ const index = el.parent!.children!.findIndex(item => item.isEqual(el))
189
+ //下一行对应的单元格
190
+ const nextCell = nextRow.children![index]
191
+ //根据当前单元格的跨列数补充符合跨列数的隐藏单元格
192
+ for (let j = 0; j < colspan; j++) {
193
+ const column = AlexElement.create({
194
+ type: 'inblock',
195
+ parsedom: 'td',
196
+ marks: {
197
+ 'data-editify-merged': 'true'
198
+ },
199
+ children: [
200
+ {
201
+ type: 'closed',
202
+ parsedom: 'br'
203
+ }
204
+ ]
205
+ })
206
+ if (nextCell) {
207
+ editor.addElementBefore(column, nextCell)
208
+ } else {
209
+ editor.addElementTo(column, nextRow, nextRow.children!.length)
210
+ }
211
+ }
212
+ el = nextRow.children![index]
213
+ i++
214
+ }
215
+ }
216
+ }
217
+ })
218
+ //遍历每一行,如果还缺少列则在后面补全列
219
+ rowElements.forEach(rowElement => {
220
+ //遍历该行的单元格获取总列数
221
+ const number = rowElement.children!.length
222
+ if (number < columnNumber) {
223
+ for (let i = 0; i < columnNumber - number; i++) {
224
+ const column = AlexElement.create({
225
+ type: 'inblock',
226
+ parsedom: 'td',
227
+ children: [
228
+ {
229
+ type: 'closed',
230
+ parsedom: 'br'
231
+ }
232
+ ]
233
+ })
234
+ editor.addElementTo(column, rowElement, rowElement.children!.length)
235
+ }
236
+ }
237
+ })
238
+ //获取总行数
239
+ const length = rowElements.length
240
+ //判断总行数是否小于实际行数则补全行
241
+ if (length < rowNumber) {
242
+ for (let i = 0; i < rowNumber - length; i++) {
243
+ const children: AlexElementCreateConfigType[] = []
244
+ for (let j = 0; j < columnNumber; j++) {
245
+ children.push({
246
+ type: 'inblock',
247
+ parsedom: 'td',
248
+ children: [
249
+ {
250
+ type: 'closed',
251
+ parsedom: 'br'
252
+ }
253
+ ]
254
+ })
255
+ }
256
+ const row = AlexElement.create({
257
+ type: 'inblock',
258
+ parsedom: 'tr',
259
+ children
260
+ })
261
+ rowElements.push(row)
262
+ }
263
+ }
264
+ }
265
+
15
266
  /**
16
267
  * 清空单元格的内容并隐藏
17
268
  * @param editor
@@ -184,6 +435,8 @@ export const getTableSize = (rowElements: AlexElement[]) => {
184
435
  }
185
436
  }
186
437
 
438
+ /** --------------------------------通用的元素判断函数----------------------------------------------- */
439
+
187
440
  /**
188
441
  * Open API:判断元素是否符合指定的条件
189
442
  * @param element
@@ -292,6 +545,8 @@ export const getMatchElementByRange = (editor: AlexEditor, dataRangeCaches: Alex
292
545
  return null
293
546
  }
294
547
 
548
+ /** --------------------------------列表判断函数---------------------------------------------------------- */
549
+
295
550
  /**
296
551
  * Open API:判断元素是否有序或者无序列表,不做空元素判断
297
552
  * @param element
@@ -360,6 +615,8 @@ export const rangeIsInList = (editor: AlexEditor, dataRangeCaches: AlexElementsR
360
615
  })
361
616
  }
362
617
 
618
+ /** --------------------------------任务列表判断函数----------------------------------------------- */
619
+
363
620
  /**
364
621
  * Open API:判断元素是否任务列表,不做空元素判断
365
622
  * @param element
@@ -424,6 +681,8 @@ export const rangeIsInTask = (editor: AlexEditor, dataRangeCaches: AlexElementsR
424
681
  })
425
682
  }
426
683
 
684
+ /** --------------------------------附件判断函数------------------------------------------------- */
685
+
427
686
  /**
428
687
  * Open API:判断元素是否附件,不做空元素判断
429
688
  * @param element
@@ -456,6 +715,8 @@ export const hasAttachmentInRange = (editor: AlexEditor, dataRangeCaches: AlexEl
456
715
  })
457
716
  }
458
717
 
718
+ /** --------------------------------数学公式判断函数--------------------------------------------- */
719
+
459
720
  /**
460
721
  * Open API:判断元素是否数学公式,不做空元素判断
461
722
  * @param element
@@ -502,51 +763,7 @@ export const hasMathformulaInRange = (editor: AlexEditor, dataRangeCaches: AlexE
502
763
  })
503
764
  }
504
765
 
505
- /**
506
- * Open API:判断元素是否面板,不做空元素判断
507
- * @param el
508
- * @returns
509
- */
510
- export const elementIsPanel = (element: AlexElement) => {
511
- return elementIsMatch(element, {
512
- parsedom: 'div',
513
- marks: {
514
- 'data-editify-panel': true
515
- }
516
- })
517
- }
518
-
519
- /**
520
- * Open API:判断元素是否在面板内,是的话返回面板元素,否则返回null
521
- * @param el
522
- * @returns
523
- */
524
- export const getPanelByElement = (element: AlexElement): AlexElement | null => {
525
- return getMatchElementByElement(element, {
526
- parsedom: 'div',
527
- marks: {
528
- 'data-editify-panel': true
529
- }
530
- })
531
- }
532
-
533
- /**
534
- * Open API:选区是否含有面板,不一定是同一个面板,只要含有面板即返回true
535
- * @param editor
536
- * @param dataRangeCaches
537
- * @returns
538
- */
539
- export const hasPanelInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
540
- if (!editor.range) {
541
- return false
542
- }
543
- if (editor.range.anchor.isEqual(editor.range.focus)) {
544
- return !!getPanelByElement(editor.range.anchor.element)
545
- }
546
- return dataRangeCaches.flatList.some(item => {
547
- return !!getPanelByElement(item.element)
548
- })
549
- }
766
+ /** --------------------------------信息块判断函数---------------------------------------------- */
550
767
 
551
768
  /**
552
769
  * Open API:判断元素是否信息块,不做空元素判断
@@ -612,6 +829,8 @@ export const rangeIsInInfoBlock = (editor: AlexEditor, dataRangeCaches: AlexElem
612
829
  })
613
830
  }
614
831
 
832
+ /** --------------------------------代码块判断函数------------------------------------------------ */
833
+
615
834
  /**
616
835
  * Open API:选区是否含有代码块,不一定是同一个代码块,只要含有代码块即返回true
617
836
  * @param editor
@@ -630,6 +849,28 @@ export const hasPreInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsR
630
849
  })
631
850
  }
632
851
 
852
+ /** --------------------------------表格判断函数------------------------------------------------- */
853
+
854
+ /**
855
+ * Open API:选区是否含有表格,不一定是同一个表格,只要含有表格即返回true
856
+ * @param editor
857
+ * @param dataRangeCaches
858
+ * @returns
859
+ */
860
+ export const hasTableInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
861
+ if (!editor.range) {
862
+ return false
863
+ }
864
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
865
+ return !!getMatchElementByElement(editor.range.anchor.element, { parsedom: 'table' })
866
+ }
867
+ return dataRangeCaches.flatList.some(item => {
868
+ return !!getMatchElementByElement(item.element, { parsedom: 'table' })
869
+ })
870
+ }
871
+
872
+ /** --------------------------------引用判断函数----------------------------------------------- */
873
+
633
874
  /**
634
875
  * Open API:选区是否含有引用,不一定是同一个引用,只要含有引用即返回true
635
876
  * @param editor
@@ -649,41 +890,45 @@ export const hasQuoteInRange = (editor: AlexEditor, dataRangeCaches: AlexElement
649
890
  }
650
891
 
651
892
  /**
652
- * Open API:选区是否含有链接,不一定是同一个链接,只要含有链接即返回true
893
+ * Open API:选区是否全部在引用内,不一定是同一个引用
653
894
  * @param editor
654
895
  * @param dataRangeCaches
655
896
  * @returns
656
897
  */
657
- export const hasLinkInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
898
+ export const rangeIsInQuote = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
658
899
  if (!editor.range) {
659
900
  return false
660
901
  }
661
902
  if (editor.range.anchor.isEqual(editor.range.focus)) {
662
- return !!getMatchElementByElement(editor.range.anchor.element, { parsedom: 'a' })
903
+ return !!getMatchElementByElement(editor.range.anchor.element, { parsedom: 'blockquote' })
663
904
  }
664
- return dataRangeCaches.flatList.some(item => {
665
- return !!getMatchElementByElement(item.element, { parsedom: 'a' })
905
+ return dataRangeCaches.list.every(item => {
906
+ return !!getMatchElementByElement(item.element, { parsedom: 'blockquote' })
666
907
  })
667
908
  }
668
909
 
910
+ /** --------------------------------链接判断函数-------------------------------------------------- */
911
+
669
912
  /**
670
- * Open API:选区是否含有表格,不一定是同一个表格,只要含有表格即返回true
913
+ * Open API:选区是否含有链接,不一定是同一个链接,只要含有链接即返回true
671
914
  * @param editor
672
915
  * @param dataRangeCaches
673
916
  * @returns
674
917
  */
675
- export const hasTableInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
918
+ export const hasLinkInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
676
919
  if (!editor.range) {
677
920
  return false
678
921
  }
679
922
  if (editor.range.anchor.isEqual(editor.range.focus)) {
680
- return !!getMatchElementByElement(editor.range.anchor.element, { parsedom: 'table' })
923
+ return !!getMatchElementByElement(editor.range.anchor.element, { parsedom: 'a' })
681
924
  }
682
925
  return dataRangeCaches.flatList.some(item => {
683
- return !!getMatchElementByElement(item.element, { parsedom: 'table' })
926
+ return !!getMatchElementByElement(item.element, { parsedom: 'a' })
684
927
  })
685
928
  }
686
929
 
930
+ /** --------------------------------图片视频判断函数--------------------------------------------- */
931
+
687
932
  /**
688
933
  * Open API:选区是否含有图片,不一定是同一个图片,只要含有图片即返回true
689
934
  * @param editor
@@ -720,22 +965,72 @@ export const hasVideoInRange = (editor: AlexEditor, dataRangeCaches: AlexElement
720
965
  })
721
966
  }
722
967
 
968
+ /** --------------------文本元素标记和样式相关函数--------------------------------------- */
969
+
723
970
  /**
724
- * Open API:选区是否全部在引用内,不一定是同一个引用
971
+ * 获取光标选取内的扁平化元素数组(可能会分割文本元素导致stack变更,同时也会更新选取元素和光标位置)
725
972
  * @param editor
726
973
  * @param dataRangeCaches
727
974
  * @returns
728
975
  */
729
- export const rangeIsInQuote = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
976
+ export const getFlatElementsByRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
730
977
  if (!editor.range) {
731
- return false
978
+ return []
732
979
  }
733
- if (editor.range.anchor.isEqual(editor.range.focus)) {
734
- return !!getMatchElementByElement(editor.range.anchor.element, { parsedom: 'blockquote' })
980
+ //获取选区数据的长度
981
+ let length = dataRangeCaches.flatList.length
982
+ //返回的元素数组
983
+ let elements = []
984
+ //遍历选区数据
985
+ for (let i = 0; i < length; i++) {
986
+ const item = dataRangeCaches.flatList[i]
987
+ //如果存在offset那么一定是文本元素
988
+ if (item.offset) {
989
+ let selectEl = null
990
+ //文本元素前面一部分在光标范围内
991
+ if (item.offset[0] == 0 && item.offset[1] < item.element.textContent!.length) {
992
+ const el = item.element.clone()
993
+ item.element.textContent = item.element.textContent!.substring(0, item.offset[1])
994
+ el.textContent = el.textContent!.substring(item.offset[1])
995
+ editor.addElementAfter(el, item.element)
996
+ selectEl = item.element
997
+ }
998
+ //文本元素后面一部分在光标范围内
999
+ else if (item.offset[1] == item.element.textContent!.length && item.offset[0] > 0) {
1000
+ const el = item.element.clone()
1001
+ item.element.textContent = item.element.textContent!.substring(0, item.offset[0])
1002
+ el.textContent = el.textContent!.substring(item.offset[0])
1003
+ editor.addElementAfter(el, item.element)
1004
+ selectEl = el
1005
+ }
1006
+ //文本元素的中间一部分在光标范围内
1007
+ else if (item.offset[0] > 0 && item.offset[1] < item.element.textContent!.length) {
1008
+ const el = item.element.clone()
1009
+ const el2 = item.element.clone()
1010
+ item.element.textContent = item.element.textContent!.substring(0, item.offset[0])
1011
+ el.textContent = el.textContent!.substring(item.offset[0], item.offset[1])
1012
+ el2.textContent = el2.textContent!.substring(item.offset[1])
1013
+ editor.addElementAfter(el, item.element)
1014
+ editor.addElementAfter(el2, el)
1015
+ selectEl = el
1016
+ }
1017
+ //如果selectEl存在证明文本元素被分割了
1018
+ if (selectEl) {
1019
+ //如果i为0的话肯定是起点
1020
+ if (i == 0) {
1021
+ editor.range.anchor.moveToStart(selectEl)
1022
+ }
1023
+ //如果i是最后一个序列的话肯定是终点
1024
+ if (i == length - 1) {
1025
+ editor.range.focus.moveToEnd(selectEl)
1026
+ }
1027
+ elements.push(selectEl)
1028
+ }
1029
+ } else {
1030
+ elements.push(item.element)
1031
+ }
735
1032
  }
736
- return dataRangeCaches.list.every(item => {
737
- return !!getMatchElementByElement(item.element, { parsedom: 'blockquote' })
738
- })
1033
+ return elements
739
1034
  }
740
1035
 
741
1036
  /**
@@ -780,132 +1075,287 @@ export const queryTextStyle = (editor: AlexEditor, dataRangeCaches: AlexElements
780
1075
  }
781
1076
 
782
1077
  /**
783
- * Open API:查询光标所在的文本元素是否具有某个标记
1078
+ * Open API:设置文本元素的样式
784
1079
  * @param editor
785
1080
  * @param dataRangeCaches
786
- * @param name
787
- * @param value
1081
+ * @param styles 值为{ 'font-weight':'bold' }这类格式
788
1082
  * @returns
789
1083
  */
790
- export const queryTextMark = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, name: string, value?: string | number) => {
1084
+ export const setTextStyle = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, styles: ObjectType) => {
791
1085
  if (!editor.range) {
792
- return false
1086
+ return
793
1087
  }
794
1088
  //起点和终点在一起
795
1089
  if (editor.range.anchor.isEqual(editor.range.focus)) {
796
- //如果是文本元素并且具有标记
797
- if (editor.range.anchor.element.isText() && editor.range.anchor.element.hasMarks()) {
798
- return queryHasValue(editor.range.anchor.element.marks!, name, value)
1090
+ //如果是空白文本元素直接设置样式
1091
+ if (editor.range.anchor.element.isSpaceText()) {
1092
+ if (editor.range.anchor.element.hasStyles()) {
1093
+ Object.assign(editor.range.anchor.element.styles!, DapCommon.clone(styles))
1094
+ } else {
1095
+ editor.range.anchor.element.styles = DapCommon.clone(styles)
1096
+ }
799
1097
  }
800
- //不是文本元素或者没有标记直接返回
801
- return false
802
- }
803
- //起点和终点不在一起获取选区中的文本元素
804
- let result = dataRangeCaches.flatList.filter(item => {
805
- return item.element.isText()
806
- })
807
- //如果不包含文本元素直接返回false
808
- if (result.length == 0) {
809
- return false
810
- }
811
- //判断每个文本元素是否都具有该标记
812
- let flag = result.every(item => {
813
- //文本元素含有标记进一步判断
814
- if (item.element.hasMarks()) {
815
- return queryHasValue(item.element.marks!, name, value)
1098
+ //如果是文本元素
1099
+ else if (editor.range.anchor.element.isText()) {
1100
+ //新建一个空白文本元素
1101
+ const el = AlexElement.getSpaceElement()
1102
+ //继承文本元素的样式和标记
1103
+ el.styles = DapCommon.clone(editor.range.anchor.element.styles)
1104
+ el.marks = DapCommon.clone(editor.range.anchor.element.marks)
1105
+ //设置样式
1106
+ if (el.hasStyles()) {
1107
+ Object.assign(el.styles!, DapCommon.clone(styles))
1108
+ } else {
1109
+ el.styles = DapCommon.clone(styles)
1110
+ }
1111
+ //插入空白文本元素
1112
+ editor.insertElement(el)
816
1113
  }
817
- //文本元素没有标记直接返回false
818
- return false
819
- })
820
- return flag
1114
+ //如果是自闭合元素
1115
+ else {
1116
+ const el = AlexElement.getSpaceElement()
1117
+ el.styles = DapCommon.clone(styles)
1118
+ editor.insertElement(el)
1119
+ }
1120
+ }
1121
+ //不在同一个点
1122
+ else {
1123
+ const elements = getFlatElementsByRange(editor, dataRangeCaches)
1124
+ elements.forEach(ele => {
1125
+ if (ele.isText()) {
1126
+ if (ele.hasStyles()) {
1127
+ Object.assign(ele.styles!, DapCommon.clone(styles))
1128
+ } else {
1129
+ ele.styles = DapCommon.clone(styles)
1130
+ }
1131
+ }
1132
+ })
1133
+ }
821
1134
  }
822
1135
 
823
1136
  /**
824
- * Open API:获取选区内的文字内容
1137
+ * Open API:移除文本元素的样式
1138
+ * @param editor
825
1139
  * @param dataRangeCaches
1140
+ * @param styleNames 样式名称数组,如果不存在则移除全部样式
826
1141
  * @returns
827
1142
  */
828
- export const getRangeText = (dataRangeCaches: AlexElementsRangeType) => {
829
- //存在选区的情况下预置链接文本值
830
- let text = ''
831
- dataRangeCaches.flatList.forEach(item => {
832
- if (item.element.isText()) {
833
- if (item.offset) {
834
- text += item.element.textContent!.substring(item.offset[0], item.offset[1])
835
- } else {
836
- text += item.element.textContent || ''
1143
+ export const removeTextStyle = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, styleNames?: string[]) => {
1144
+ if (!editor.range) {
1145
+ return
1146
+ }
1147
+ //移除样式的方法
1148
+ const removeFn = (el: AlexElement) => {
1149
+ //如果参数是数组,表示删除指定的样式
1150
+ if (Array.isArray(styleNames)) {
1151
+ if (el.hasStyles()) {
1152
+ let styles: ObjectType = {}
1153
+ Object.keys(el.styles!).forEach(key => {
1154
+ if (!styleNames.includes(key)) {
1155
+ styles[key] = el.styles![key]
1156
+ }
1157
+ })
1158
+ el.styles = styles
1159
+ }
1160
+ }
1161
+ //如果没有参数,则表示删除所有的样式
1162
+ else {
1163
+ el.styles = null
1164
+ }
1165
+ }
1166
+ //如果起点和终点在一起
1167
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
1168
+ //如果是空白文本元素直接移除样式
1169
+ if (editor.range.anchor.element.isSpaceText()) {
1170
+ removeFn(editor.range.anchor.element)
1171
+ }
1172
+ //如果是文本元素则新建一个空白文本元素
1173
+ else if (editor.range.anchor.element.isText()) {
1174
+ const el = AlexElement.getSpaceElement()
1175
+ //继承文本元素的样式和标记
1176
+ el.styles = DapCommon.clone(editor.range.anchor.element.styles)
1177
+ el.marks = DapCommon.clone(editor.range.anchor.element.marks)
1178
+ //移除样式
1179
+ removeFn(el)
1180
+ //插入
1181
+ editor.insertElement(el)
1182
+ }
1183
+ }
1184
+ //起点和终点不在一起
1185
+ else {
1186
+ const elements = getFlatElementsByRange(editor, dataRangeCaches)
1187
+ elements.forEach(ele => {
1188
+ if (ele.isText()) {
1189
+ removeFn(ele)
837
1190
  }
1191
+ })
1192
+ }
1193
+ }
1194
+
1195
+ /**
1196
+ * Open API:查询光标所在的文本元素是否具有某个标记
1197
+ * @param editor
1198
+ * @param dataRangeCaches
1199
+ * @param name
1200
+ * @param value
1201
+ * @returns
1202
+ */
1203
+ export const queryTextMark = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, name: string, value?: string | number) => {
1204
+ if (!editor.range) {
1205
+ return false
1206
+ }
1207
+ //起点和终点在一起
1208
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
1209
+ //如果是文本元素并且具有标记
1210
+ if (editor.range.anchor.element.isText() && editor.range.anchor.element.hasMarks()) {
1211
+ return queryHasValue(editor.range.anchor.element.marks!, name, value)
838
1212
  }
1213
+ //不是文本元素或者没有标记直接返回
1214
+ return false
1215
+ }
1216
+ //起点和终点不在一起获取选区中的文本元素
1217
+ let result = dataRangeCaches.flatList.filter(item => {
1218
+ return item.element.isText()
839
1219
  })
840
- return text
1220
+ //如果不包含文本元素直接返回false
1221
+ if (result.length == 0) {
1222
+ return false
1223
+ }
1224
+ //判断每个文本元素是否都具有该标记
1225
+ let flag = result.every(item => {
1226
+ //文本元素含有标记进一步判断
1227
+ if (item.element.hasMarks()) {
1228
+ return queryHasValue(item.element.marks!, name, value)
1229
+ }
1230
+ //文本元素没有标记直接返回false
1231
+ return false
1232
+ })
1233
+ return flag
841
1234
  }
842
1235
 
843
1236
  /**
844
- * 获取光标选取内的扁平化元素数组(可能会分割文本元素导致stack变更,同时也会更新选取元素和光标位置)
1237
+ * Open API:设置文本元素的标记
845
1238
  * @param editor
846
1239
  * @param dataRangeCaches
1240
+ * @param marks 值为{ 'class':'a' }这类格式
847
1241
  * @returns
848
1242
  */
849
- export const getFlatElementsByRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
1243
+ export const setTextMark = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, marks: ObjectType) => {
850
1244
  if (!editor.range) {
851
- return []
1245
+ return
852
1246
  }
853
- //获取选区数据的长度
854
- let length = dataRangeCaches.flatList.length
855
- //返回的元素数组
856
- let elements = []
857
- //遍历选区数据
858
- for (let i = 0; i < length; i++) {
859
- const item = dataRangeCaches.flatList[i]
860
- //如果存在offset那么一定是文本元素
861
- if (item.offset) {
862
- let selectEl = null
863
- //文本元素前面一部分在光标范围内
864
- if (item.offset[0] == 0 && item.offset[1] < item.element.textContent!.length) {
865
- const el = item.element.clone()
866
- item.element.textContent = item.element.textContent!.substring(0, item.offset[1])
867
- el.textContent = el.textContent!.substring(item.offset[1])
868
- editor.addElementAfter(el, item.element)
869
- selectEl = item.element
870
- }
871
- //文本元素后面一部分在光标范围内
872
- else if (item.offset[1] == item.element.textContent!.length && item.offset[0] > 0) {
873
- const el = item.element.clone()
874
- item.element.textContent = item.element.textContent!.substring(0, item.offset[0])
875
- el.textContent = el.textContent!.substring(item.offset[0])
876
- editor.addElementAfter(el, item.element)
877
- selectEl = el
1247
+ if (!DapCommon.isObject(marks)) {
1248
+ throw new Error('The argument must be an object')
1249
+ }
1250
+ //起点和终点在一起
1251
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
1252
+ //如果是空白文本元素直接设置标记
1253
+ if (editor.range.anchor.element.isSpaceText()) {
1254
+ if (editor.range.anchor.element.hasMarks()) {
1255
+ Object.assign(editor.range.anchor.element.marks!, DapCommon.clone(marks))
1256
+ } else {
1257
+ editor.range.anchor.element.marks = DapCommon.clone(marks)
878
1258
  }
879
- //文本元素的中间一部分在光标范围内
880
- else if (item.offset[0] > 0 && item.offset[1] < item.element.textContent!.length) {
881
- const el = item.element.clone()
882
- const el2 = item.element.clone()
883
- item.element.textContent = item.element.textContent!.substring(0, item.offset[0])
884
- el.textContent = el.textContent!.substring(item.offset[0], item.offset[1])
885
- el2.textContent = el2.textContent!.substring(item.offset[1])
886
- editor.addElementAfter(el, item.element)
887
- editor.addElementAfter(el2, el)
888
- selectEl = el
1259
+ }
1260
+ //如果是文本元素
1261
+ else if (editor.range.anchor.element.isText()) {
1262
+ //新建一个空白文本元素
1263
+ const el = AlexElement.getSpaceElement()
1264
+ //继承文本元素的样式和标记
1265
+ el.styles = DapCommon.clone(editor.range.anchor.element.styles)
1266
+ el.marks = DapCommon.clone(editor.range.anchor.element.marks)
1267
+ //设置标记
1268
+ if (el.hasMarks()) {
1269
+ Object.assign(el.marks!, DapCommon.clone(marks))
1270
+ } else {
1271
+ el.marks = DapCommon.clone(marks)
889
1272
  }
890
- //如果selectEl存在证明文本元素被分割了
891
- if (selectEl) {
892
- //如果i为0的话肯定是起点
893
- if (i == 0) {
894
- editor.range.anchor.moveToStart(selectEl)
895
- }
896
- //如果i是最后一个序列的话肯定是终点
897
- if (i == length - 1) {
898
- editor.range.focus.moveToEnd(selectEl)
1273
+ //插入空白文本元素
1274
+ editor.insertElement(el)
1275
+ }
1276
+ //如果是自闭合元素
1277
+ else {
1278
+ const el = AlexElement.getSpaceElement()
1279
+ el.marks = DapCommon.clone(marks)
1280
+ editor.insertElement(el)
1281
+ }
1282
+ }
1283
+ //不在同一个点
1284
+ else {
1285
+ const elements = getFlatElementsByRange(editor, dataRangeCaches)
1286
+ elements.forEach(ele => {
1287
+ if (ele.isText()) {
1288
+ if (ele.hasMarks()) {
1289
+ Object.assign(ele.marks!, DapCommon.clone(marks))
1290
+ } else {
1291
+ ele.marks = DapCommon.clone(marks)
899
1292
  }
900
- elements.push(selectEl)
901
1293
  }
902
- } else {
903
- elements.push(item.element)
1294
+ })
1295
+ }
1296
+ }
1297
+
1298
+ /**
1299
+ * Open API:移除文本元素的标记
1300
+ * @param editor
1301
+ * @param dataRangeCaches
1302
+ * @param markNames 标记名称数组,如果不存在则移除全部标记
1303
+ * @returns
1304
+ */
1305
+ export const removeTextMark = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, markNames?: string[]) => {
1306
+ if (!editor.range) {
1307
+ return
1308
+ }
1309
+ //移除样式的方法
1310
+ const removeFn = (el: AlexElement) => {
1311
+ //如果参数是数组,表示删除指定的样式
1312
+ if (Array.isArray(markNames)) {
1313
+ if (el.hasMarks()) {
1314
+ let marks: ObjectType = {}
1315
+ Object.keys(el.marks!).forEach(key => {
1316
+ if (!markNames.includes(key)) {
1317
+ marks[key] = el.marks![key]
1318
+ }
1319
+ })
1320
+ el.marks = marks
1321
+ }
1322
+ }
1323
+ //如果没有参数,则表示删除所有的样式
1324
+ else {
1325
+ el.marks = null
904
1326
  }
905
1327
  }
906
- return elements
1328
+ //如果起点和终点在一起
1329
+ if (editor.range.anchor.isEqual(editor.range.focus)) {
1330
+ //如果是空白文本元素直接移除标记
1331
+ if (editor.range.anchor.element.isSpaceText()) {
1332
+ removeFn(editor.range.anchor.element)
1333
+ }
1334
+ //如果是文本元素则新建一个空白文本元素
1335
+ else if (editor.range.anchor.element.isText()) {
1336
+ const el = AlexElement.getSpaceElement()
1337
+ //继承文本元素的样式和标记
1338
+ el.styles = DapCommon.clone(editor.range.anchor.element.styles)
1339
+ el.marks = DapCommon.clone(editor.range.anchor.element.marks)
1340
+ //移除标记
1341
+ removeFn(el)
1342
+ //插入
1343
+ editor.insertElement(el)
1344
+ }
1345
+ }
1346
+ //起点和终点不在一起
1347
+ else {
1348
+ const elements = getFlatElementsByRange(editor, dataRangeCaches)
1349
+ elements.forEach(ele => {
1350
+ if (ele.isText()) {
1351
+ removeFn(ele)
1352
+ }
1353
+ })
1354
+ }
907
1355
  }
908
1356
 
1357
+ /** --------------------------------元素转换函数-------------------------------------------------- */
1358
+
909
1359
  /**
910
1360
  * 将某个元素转为段落标签
911
1361
  * @param element
@@ -957,22 +1407,70 @@ export const elementToTask = (element: AlexElement) => {
957
1407
  element.marks!['data-editify-task'] = 'uncheck'
958
1408
  }
959
1409
 
1410
+ /** --------------------------------封装的功能函数----------------------------------------------- */
1411
+
1412
+ /**
1413
+ * Open API:获取选区内的文字内容
1414
+ * @param dataRangeCaches
1415
+ * @returns
1416
+ */
1417
+ export const getRangeText = (dataRangeCaches: AlexElementsRangeType) => {
1418
+ //存在选区的情况下预置链接文本值
1419
+ let text = ''
1420
+ dataRangeCaches.flatList.forEach(item => {
1421
+ if (item.element.isText()) {
1422
+ if (item.offset) {
1423
+ text += item.element.textContent!.substring(item.offset[0], item.offset[1])
1424
+ } else {
1425
+ text += item.element.textContent || ''
1426
+ }
1427
+ }
1428
+ })
1429
+ return text
1430
+ }
1431
+
960
1432
  /**
961
- * 设置标题
1433
+ * Open API:给元素两侧加上空白文本元素
1434
+ * @param editor
1435
+ * @param element
1436
+ */
1437
+ export const addSpaceTextToBothSides = (editor: AlexEditor, element: AlexElement) => {
1438
+ const previousElement = editor.getPreviousElement(element)
1439
+ const newTextElement = editor.getNextElement(element)
1440
+ //如果不存在前一个元素或者前一个元素不是空白元素则设置空白元素
1441
+ if (!previousElement || !previousElement.isSpaceText()) {
1442
+ const spaceText = AlexElement.getSpaceElement()
1443
+ editor.addElementBefore(spaceText, element)
1444
+ }
1445
+ //如果不存在后一个元素或者后一个元素不是空白元素则设置空白元素
1446
+ if (!newTextElement || !newTextElement.isSpaceText()) {
1447
+ const spaceText = AlexElement.getSpaceElement()
1448
+ editor.addElementAfter(spaceText, element)
1449
+ }
1450
+ //如果光标在视频上则更新光标位置
1451
+ if (editor.range && element.isContains(editor.range.anchor.element)) {
1452
+ editor.range.anchor.moveToEnd(editor.getNextElement(element)!)
1453
+ }
1454
+ if (editor.range && element.isContains(editor.range.focus.element)) {
1455
+ editor.range.focus.moveToEnd(editor.getNextElement(element)!)
1456
+ }
1457
+ }
1458
+
1459
+ /** --------------------------------菜单功能函数----------------------------------------------------- */
1460
+
1461
+ /**
1462
+ * Open API:设置标题,支持h1-h6和p
962
1463
  * @param editor
963
1464
  * @param dataRangeCaches
964
1465
  * @param editTrans
965
1466
  * @param parsedom
966
1467
  * @returns
967
1468
  */
968
- export const setHeading = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, editTrans: (key: string) => any, parsedom: string) => {
1469
+ export const setHeading = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, parsedom: string) => {
969
1470
  if (!editor.range) {
970
1471
  return
971
1472
  }
972
- const values = getButtonOptionsConfig(editTrans).heading!.map(item => {
973
- return (<ButtonOptionsItemType>item).value
974
- })
975
- if (!values.includes(parsedom)) {
1473
+ if (!['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p'].includes(parsedom)) {
976
1474
  throw new Error('The parameter supports only h1-h6 and p')
977
1475
  }
978
1476
  if (editor.range.anchor.isEqual(editor.range.focus)) {
@@ -1130,266 +1628,62 @@ export const setQuote = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeT
1130
1628
  * Open API:设置对齐方式
1131
1629
  * @param editor
1132
1630
  * @param dataRangeCaches
1133
- * @param value 取值justify/left/right/center
1134
- * @returns
1135
- */
1136
- export const setAlign = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, value: 'justify' | 'left' | 'right' | 'center') => {
1137
- if (!editor.range) {
1138
- return
1139
- }
1140
- if (editor.range.anchor.isEqual(editor.range.focus)) {
1141
- const block = editor.range.anchor.element.getBlock()
1142
- const inblock = editor.range.anchor.element.getInblock()
1143
- if (inblock) {
1144
- if (inblock.hasStyles()) {
1145
- inblock.styles!['text-align'] = value
1146
- } else {
1147
- inblock.styles = {
1148
- 'text-align': value
1149
- }
1150
- }
1151
- } else {
1152
- if (block.hasStyles()) {
1153
- block.styles!['text-align'] = value
1154
- } else {
1155
- block.styles = {
1156
- 'text-align': value
1157
- }
1158
- }
1159
- }
1160
- } else {
1161
- dataRangeCaches.list.forEach(el => {
1162
- if (el.element.isBlock() || el.element.isInblock()) {
1163
- if (el.element.hasStyles()) {
1164
- el.element.styles!['text-align'] = value
1165
- } else {
1166
- el.element.styles = {
1167
- 'text-align': value
1168
- }
1169
- }
1170
- } else {
1171
- const block = el.element.getBlock()
1172
- const inblock = el.element.getInblock()
1173
- if (inblock) {
1174
- if (inblock.hasStyles()) {
1175
- inblock.styles!['text-align'] = value
1176
- } else {
1177
- inblock.styles = {
1178
- 'text-align': value
1179
- }
1180
- }
1181
- } else {
1182
- if (block.hasStyles()) {
1183
- block.styles!['text-align'] = value
1184
- } else {
1185
- block.styles = {
1186
- 'text-align': value
1187
- }
1188
- }
1189
- }
1190
- }
1191
- })
1192
- }
1193
- }
1194
-
1195
- /**
1196
- * Open API:插入或者取消 有序或者无序列表
1197
- * @param editor
1198
- * @param dataRangeCaches
1199
- * @param ordered 为true表示有序列表
1200
- * @returns
1201
- */
1202
- export const setList = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, ordered: boolean) => {
1203
- if (!editor.range) {
1204
- return
1205
- }
1206
- //是否都在列表内
1207
- const flag = rangeIsInList(editor, dataRangeCaches, ordered)
1208
- //起点和终点在一起
1209
- if (editor.range.anchor.isEqual(editor.range.focus)) {
1210
- const block = editor.range.anchor.element.getBlock()
1211
- if (flag) {
1212
- elementToParagraph(block)
1213
- } else {
1214
- elementToList(block, ordered)
1215
- }
1216
- }
1217
- //起点和终点不在一起
1218
- else {
1219
- let blocks: AlexElement[] = []
1220
- dataRangeCaches.list.forEach(item => {
1221
- const block = item.element.getBlock()
1222
- const exist = blocks.some(el => block.isEqual(el))
1223
- if (!exist) {
1224
- blocks.push(block)
1225
- }
1226
- })
1227
- blocks.forEach(block => {
1228
- if (flag) {
1229
- elementToParagraph(block)
1230
- } else {
1231
- elementToList(block, ordered)
1232
- }
1233
- })
1234
- }
1235
- }
1236
-
1237
- /**
1238
- * Open API:插入或者取消任务列表
1239
- * @param editor
1240
- * @param dataRangeCaches
1241
- * @returns
1242
- */
1243
- export const setTask = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
1244
- if (!editor.range) {
1245
- return
1246
- }
1247
- //是否都在任务列表里
1248
- const flag = rangeIsInTask(editor, dataRangeCaches)
1249
- //起点和终点在一起
1250
- if (editor.range.anchor.isEqual(editor.range.focus)) {
1251
- const block = editor.range.anchor.element.getBlock()
1252
- if (flag) {
1253
- elementToParagraph(block)
1254
- } else {
1255
- elementToTask(block)
1256
- }
1257
- }
1258
- //起点和终点不在一起
1259
- else {
1260
- let blocks: AlexElement[] = []
1261
- dataRangeCaches.list.forEach(item => {
1262
- const block = item.element.getBlock()
1263
- const exist = blocks.some(el => block.isEqual(el))
1264
- if (!exist) {
1265
- blocks.push(block)
1266
- }
1267
- })
1268
- blocks.forEach(block => {
1269
- if (flag) {
1270
- elementToParagraph(block)
1271
- } else {
1272
- elementToTask(block)
1273
- }
1274
- })
1275
- }
1276
- }
1277
-
1278
- /**
1279
- * Open API:设置文本元素的样式
1280
- * @param editor
1281
- * @param dataRangeCaches
1282
- * @param styles 值为{ 'font-weight':'bold' }这类格式
1283
- * @returns
1284
- */
1285
- export const setTextStyle = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, styles: ObjectType) => {
1286
- if (!editor.range) {
1287
- return
1288
- }
1289
- //起点和终点在一起
1290
- if (editor.range.anchor.isEqual(editor.range.focus)) {
1291
- //如果是空白文本元素直接设置样式
1292
- if (editor.range.anchor.element.isSpaceText()) {
1293
- if (editor.range.anchor.element.hasStyles()) {
1294
- Object.assign(editor.range.anchor.element.styles!, cloneData(styles))
1295
- } else {
1296
- editor.range.anchor.element.styles = cloneData(styles)
1297
- }
1298
- }
1299
- //如果是文本元素
1300
- else if (editor.range.anchor.element.isText()) {
1301
- //新建一个空白文本元素
1302
- const el = AlexElement.getSpaceElement()
1303
- //继承文本元素的样式和标记
1304
- el.styles = cloneData(editor.range.anchor.element.styles)
1305
- el.marks = cloneData(editor.range.anchor.element.marks)
1306
- //设置样式
1307
- if (el.hasStyles()) {
1308
- Object.assign(el.styles!, cloneData(styles))
1309
- } else {
1310
- el.styles = cloneData(styles)
1311
- }
1312
- //插入空白文本元素
1313
- editor.insertElement(el)
1314
- }
1315
- //如果是自闭合元素
1316
- else {
1317
- const el = AlexElement.getSpaceElement()
1318
- el.styles = cloneData(styles)
1319
- editor.insertElement(el)
1320
- }
1321
- }
1322
- //不在同一个点
1323
- else {
1324
- const elements = getFlatElementsByRange(editor, dataRangeCaches)
1325
- elements.forEach(ele => {
1326
- if (ele.isText()) {
1327
- if (ele.hasStyles()) {
1328
- Object.assign(ele.styles!, cloneData(styles))
1329
- } else {
1330
- ele.styles = cloneData(styles)
1331
- }
1332
- }
1333
- })
1334
- }
1335
- }
1336
-
1337
- /**
1338
- * Open API:设置文本元素的标记
1339
- * @param editor
1340
- * @param dataRangeCaches
1341
- * @param marks 值为{ 'class':'a' }这类格式
1631
+ * @param value 取值justify/left/right/center
1342
1632
  * @returns
1343
1633
  */
1344
- export const setTextMark = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, marks: ObjectType) => {
1634
+ export const setAlign = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, value: 'justify' | 'left' | 'right' | 'center') => {
1345
1635
  if (!editor.range) {
1346
1636
  return
1347
1637
  }
1348
- if (!DapCommon.isObject(marks)) {
1349
- throw new Error('The argument must be an object')
1350
- }
1351
- //起点和终点在一起
1352
1638
  if (editor.range.anchor.isEqual(editor.range.focus)) {
1353
- //如果是空白文本元素直接设置标记
1354
- if (editor.range.anchor.element.isSpaceText()) {
1355
- if (editor.range.anchor.element.hasMarks()) {
1356
- Object.assign(editor.range.anchor.element.marks!, cloneData(marks))
1639
+ const block = editor.range.anchor.element.getBlock()
1640
+ const inblock = editor.range.anchor.element.getInblock()
1641
+ if (inblock) {
1642
+ if (inblock.hasStyles()) {
1643
+ inblock.styles!['text-align'] = value
1357
1644
  } else {
1358
- editor.range.anchor.element.marks = cloneData(marks)
1645
+ inblock.styles = {
1646
+ 'text-align': value
1647
+ }
1359
1648
  }
1360
- }
1361
- //如果是文本元素
1362
- else if (editor.range.anchor.element.isText()) {
1363
- //新建一个空白文本元素
1364
- const el = AlexElement.getSpaceElement()
1365
- //继承文本元素的样式和标记
1366
- el.styles = cloneData(editor.range.anchor.element.styles)
1367
- el.marks = cloneData(editor.range.anchor.element.marks)
1368
- //设置标记
1369
- if (el.hasMarks()) {
1370
- Object.assign(el.marks!, cloneData(marks))
1649
+ } else {
1650
+ if (block.hasStyles()) {
1651
+ block.styles!['text-align'] = value
1371
1652
  } else {
1372
- el.marks = cloneData(marks)
1653
+ block.styles = {
1654
+ 'text-align': value
1655
+ }
1373
1656
  }
1374
- //插入空白文本元素
1375
- editor.insertElement(el)
1376
- }
1377
- //如果是自闭合元素
1378
- else {
1379
- const el = AlexElement.getSpaceElement()
1380
- el.marks = cloneData(marks)
1381
- editor.insertElement(el)
1382
1657
  }
1383
- }
1384
- //不在同一个点
1385
- else {
1386
- const elements = getFlatElementsByRange(editor, dataRangeCaches)
1387
- elements.forEach(ele => {
1388
- if (ele.isText()) {
1389
- if (ele.hasMarks()) {
1390
- Object.assign(ele.marks!, cloneData(marks))
1658
+ } else {
1659
+ dataRangeCaches.list.forEach(el => {
1660
+ if (el.element.isBlock() || el.element.isInblock()) {
1661
+ if (el.element.hasStyles()) {
1662
+ el.element.styles!['text-align'] = value
1663
+ } else {
1664
+ el.element.styles = {
1665
+ 'text-align': value
1666
+ }
1667
+ }
1668
+ } else {
1669
+ const block = el.element.getBlock()
1670
+ const inblock = el.element.getInblock()
1671
+ if (inblock) {
1672
+ if (inblock.hasStyles()) {
1673
+ inblock.styles!['text-align'] = value
1674
+ } else {
1675
+ inblock.styles = {
1676
+ 'text-align': value
1677
+ }
1678
+ }
1391
1679
  } else {
1392
- ele.marks = cloneData(marks)
1680
+ if (block.hasStyles()) {
1681
+ block.styles!['text-align'] = value
1682
+ } else {
1683
+ block.styles = {
1684
+ 'text-align': value
1685
+ }
1686
+ }
1393
1687
  }
1394
1688
  }
1395
1689
  })
@@ -1397,125 +1691,90 @@ export const setTextMark = (editor: AlexEditor, dataRangeCaches: AlexElementsRan
1397
1691
  }
1398
1692
 
1399
1693
  /**
1400
- * Open API:移除文本元素的样式
1694
+ * Open API:插入或者取消 有序或者无序列表
1401
1695
  * @param editor
1402
1696
  * @param dataRangeCaches
1403
- * @param styleNames 样式名称数组,如果不存在则移除全部样式
1697
+ * @param ordered 为true表示有序列表
1404
1698
  * @returns
1405
1699
  */
1406
- export const removeTextStyle = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, styleNames?: string[]) => {
1700
+ export const setList = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, ordered: boolean) => {
1407
1701
  if (!editor.range) {
1408
1702
  return
1409
1703
  }
1410
- //移除样式的方法
1411
- const removeFn = (el: AlexElement) => {
1412
- //如果参数是数组,表示删除指定的样式
1413
- if (Array.isArray(styleNames)) {
1414
- if (el.hasStyles()) {
1415
- let styles: ObjectType = {}
1416
- Object.keys(el.styles!).forEach(key => {
1417
- if (!styleNames.includes(key)) {
1418
- styles[key] = el.styles![key]
1419
- }
1420
- })
1421
- el.styles = styles
1422
- }
1423
- }
1424
- //如果没有参数,则表示删除所有的样式
1425
- else {
1426
- el.styles = null
1427
- }
1428
- }
1429
- //如果起点和终点在一起
1704
+ //是否都在列表内
1705
+ const flag = rangeIsInList(editor, dataRangeCaches, ordered)
1706
+ //起点和终点在一起
1430
1707
  if (editor.range.anchor.isEqual(editor.range.focus)) {
1431
- //如果是空白文本元素直接移除样式
1432
- if (editor.range.anchor.element.isSpaceText()) {
1433
- removeFn(editor.range.anchor.element)
1434
- }
1435
- //如果是文本元素则新建一个空白文本元素
1436
- else if (editor.range.anchor.element.isText()) {
1437
- const el = AlexElement.getSpaceElement()
1438
- //继承文本元素的样式和标记
1439
- el.styles = cloneData(editor.range.anchor.element.styles)
1440
- el.marks = cloneData(editor.range.anchor.element.marks)
1441
- //移除样式
1442
- removeFn(el)
1443
- //插入
1444
- editor.insertElement(el)
1708
+ const block = editor.range.anchor.element.getBlock()
1709
+ if (flag) {
1710
+ elementToParagraph(block)
1711
+ } else {
1712
+ elementToList(block, ordered)
1445
1713
  }
1446
1714
  }
1447
1715
  //起点和终点不在一起
1448
1716
  else {
1449
- const elements = getFlatElementsByRange(editor, dataRangeCaches)
1450
- elements.forEach(ele => {
1451
- if (ele.isText()) {
1452
- removeFn(ele)
1717
+ let blocks: AlexElement[] = []
1718
+ dataRangeCaches.list.forEach(item => {
1719
+ const block = item.element.getBlock()
1720
+ const exist = blocks.some(el => block.isEqual(el))
1721
+ if (!exist) {
1722
+ blocks.push(block)
1723
+ }
1724
+ })
1725
+ blocks.forEach(block => {
1726
+ if (flag) {
1727
+ elementToParagraph(block)
1728
+ } else {
1729
+ elementToList(block, ordered)
1453
1730
  }
1454
1731
  })
1455
1732
  }
1456
1733
  }
1457
1734
 
1458
1735
  /**
1459
- * Open API:移除文本元素的标记
1736
+ * Open API:插入或者取消任务列表
1460
1737
  * @param editor
1461
1738
  * @param dataRangeCaches
1462
- * @param markNames 标记名称数组,如果不存在则移除全部标记
1463
1739
  * @returns
1464
1740
  */
1465
- export const removeTextMark = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, markNames?: string[]) => {
1741
+ export const setTask = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
1466
1742
  if (!editor.range) {
1467
1743
  return
1468
1744
  }
1469
- //移除样式的方法
1470
- const removeFn = (el: AlexElement) => {
1471
- //如果参数是数组,表示删除指定的样式
1472
- if (Array.isArray(markNames)) {
1473
- if (el.hasMarks()) {
1474
- let marks: ObjectType = {}
1475
- Object.keys(el.marks!).forEach(key => {
1476
- if (!markNames.includes(key)) {
1477
- marks[key] = el.marks![key]
1478
- }
1479
- })
1480
- el.marks = marks
1481
- }
1482
- }
1483
- //如果没有参数,则表示删除所有的样式
1484
- else {
1485
- el.marks = null
1486
- }
1487
- }
1488
- //如果起点和终点在一起
1745
+ //是否都在任务列表里
1746
+ const flag = rangeIsInTask(editor, dataRangeCaches)
1747
+ //起点和终点在一起
1489
1748
  if (editor.range.anchor.isEqual(editor.range.focus)) {
1490
- //如果是空白文本元素直接移除标记
1491
- if (editor.range.anchor.element.isSpaceText()) {
1492
- removeFn(editor.range.anchor.element)
1493
- }
1494
- //如果是文本元素则新建一个空白文本元素
1495
- else if (editor.range.anchor.element.isText()) {
1496
- const el = AlexElement.getSpaceElement()
1497
- //继承文本元素的样式和标记
1498
- el.styles = cloneData(editor.range.anchor.element.styles)
1499
- el.marks = cloneData(editor.range.anchor.element.marks)
1500
- //移除标记
1501
- removeFn(el)
1502
- //插入
1503
- editor.insertElement(el)
1749
+ const block = editor.range.anchor.element.getBlock()
1750
+ if (flag) {
1751
+ elementToParagraph(block)
1752
+ } else {
1753
+ elementToTask(block)
1504
1754
  }
1505
1755
  }
1506
1756
  //起点和终点不在一起
1507
1757
  else {
1508
- const elements = getFlatElementsByRange(editor, dataRangeCaches)
1509
- elements.forEach(ele => {
1510
- if (ele.isText()) {
1511
- removeFn(ele)
1758
+ let blocks: AlexElement[] = []
1759
+ dataRangeCaches.list.forEach(item => {
1760
+ const block = item.element.getBlock()
1761
+ const exist = blocks.some(el => block.isEqual(el))
1762
+ if (!exist) {
1763
+ blocks.push(block)
1764
+ }
1765
+ })
1766
+ blocks.forEach(block => {
1767
+ if (flag) {
1768
+ elementToParagraph(block)
1769
+ } else {
1770
+ elementToTask(block)
1512
1771
  }
1513
1772
  })
1514
1773
  }
1515
1774
  }
1516
1775
 
1517
1776
  /**
1518
- * Open API:设置块元素或者根级块元素的行高
1777
+ * Open API:设置内部块元素或者根级块元素的行高
1519
1778
  * @param editor
1520
1779
  * @param dataRangeCaches
1521
1780
  * @param value
@@ -1844,3 +2103,126 @@ export const insertSeparator = (editor: AlexEditor) => {
1844
2103
  editor.range.anchor.moveToEnd(separator)
1845
2104
  editor.range.focus.moveToEnd(separator)
1846
2105
  }
2106
+
2107
+ /**
2108
+ * Open API:插入附件
2109
+ * @param editor
2110
+ * @param url 附件地址
2111
+ * @param name 附件名称
2112
+ */
2113
+ export const insertAttachment = (editor: AlexEditor, url: string, name: string) => {
2114
+ const marks: ObjectType = {
2115
+ 'data-editify-attachment': url,
2116
+ 'data-editify-attachment-name': name
2117
+ }
2118
+ //创建元素
2119
+ const attachmentElement = AlexElement.create({
2120
+ type: 'closed',
2121
+ parsedom: 'span',
2122
+ marks
2123
+ })
2124
+ //插入编辑器
2125
+ editor.insertElement(attachmentElement)
2126
+ //移动光标到新插入的元素
2127
+ editor.range!.anchor.moveToEnd(attachmentElement)
2128
+ editor.range!.focus.moveToEnd(attachmentElement)
2129
+ }
2130
+
2131
+ /**
2132
+ * Open API:插入数学公式
2133
+ * @param editor
2134
+ * @param dataRangeCaches
2135
+ * @param mathContent 数学公式字符串
2136
+ * @param errorCallback 错误处理
2137
+ */
2138
+ export const insertMathformula = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType, mathContent: string, errorCallback?: (err: Error) => void) => {
2139
+ if (!mathContent) {
2140
+ return
2141
+ }
2142
+ //获取选区所在的数学公式元素
2143
+ const mathformulaElement = getMatchElementByRange(editor, dataRangeCaches, {
2144
+ parsedom: 'span',
2145
+ marks: {
2146
+ 'data-editify-mathformula': true
2147
+ }
2148
+ })
2149
+ //如果在数学公式下
2150
+ if (mathformulaElement) {
2151
+ //清除该数学公式
2152
+ mathformulaElement.toEmpty()
2153
+ //移动光标到后一个元素上
2154
+ const nextElement = editor.getNextElement(mathformulaElement)!
2155
+ editor.range!.anchor.moveToStart(nextElement)
2156
+ editor.range!.focus.moveToStart(nextElement)
2157
+ }
2158
+ //定义转换后的mathml内容
2159
+ let mathml: string = ''
2160
+ try {
2161
+ //获取转换后的mathml
2162
+ mathml = KaTex.renderToString(mathContent, {
2163
+ output: 'mathml',
2164
+ throwOnError: true
2165
+ })
2166
+ } catch (error) {
2167
+ mathml = ''
2168
+ if (typeof errorCallback == 'function') {
2169
+ errorCallback(error as Error)
2170
+ }
2171
+ }
2172
+ //如果mathml存在则表示数学公式渲染成功则插入到编辑器
2173
+ if (mathml) {
2174
+ //设置最终的html内容
2175
+ const html = `<span data-editify-mathformula="${mathContent}" contenteditable="false">${mathml}</span>`
2176
+ //html内容转为元素数组
2177
+ const elements = editor.parseHtml(html)
2178
+ //插入编辑器
2179
+ editor.insertElement(elements[0])
2180
+ //移动光标到新插入的元素
2181
+ editor.range!.anchor.moveToEnd(elements[0])
2182
+ editor.range!.focus.moveToEnd(elements[0])
2183
+ //渲染
2184
+ editor.domRender()
2185
+ editor.rangeRender()
2186
+ }
2187
+ }
2188
+
2189
+ /**
2190
+ * Open API:插入信息块
2191
+ * @param editor
2192
+ * @param dataRangeCaches
2193
+ */
2194
+ export const insertInfoBlock = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
2195
+ //是否都在信息块里
2196
+ const flag = rangeIsInInfoBlock(editor, dataRangeCaches)
2197
+ //起点和终点在一起
2198
+ if (editor.range!.anchor.isEqual(editor.range!.focus)) {
2199
+ const block = editor.range!.anchor.element.getBlock()
2200
+ elementToParagraph(block)
2201
+ if (!flag) {
2202
+ block.parsedom = 'div'
2203
+ block.marks = {
2204
+ 'data-editify-info': 'true'
2205
+ }
2206
+ }
2207
+ }
2208
+ //起点和终点不在一起
2209
+ else {
2210
+ let blocks: AlexElement[] = []
2211
+ dataRangeCaches.list.forEach(item => {
2212
+ const block = item.element.getBlock()
2213
+ const exist = blocks.some(el => block.isEqual(el))
2214
+ if (!exist) {
2215
+ blocks.push(block)
2216
+ }
2217
+ })
2218
+ blocks.forEach(block => {
2219
+ elementToParagraph(block)
2220
+ if (!flag) {
2221
+ block.parsedom = 'div'
2222
+ block.marks = {
2223
+ 'data-editify-info': 'true'
2224
+ }
2225
+ }
2226
+ })
2227
+ }
2228
+ }