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
@@ -136,23 +136,92 @@
136
136
  :deep(h6) {
137
137
  font-size: 16px;
138
138
  }
139
+
140
+ //不是有序列表元素则重置后面的有序列表序列
141
+ :deep(:not(div[data-editify-list='ol'])) + div[data-editify-list='ol'] {
142
+ counter-reset: item 0;
143
+ }
144
+
139
145
  //有序列表样式
140
146
  :deep(div[data-editify-list='ol']) {
141
147
  margin-bottom: 15px;
142
148
 
149
+ //第一个元素重置序列
150
+ &:first-of-type {
151
+ counter-reset: item 0;
152
+ }
153
+
143
154
  &::before {
144
- content: attr(data-editify-value) '.';
155
+ counter-increment: item;
156
+ content: counter(item) '.';
145
157
  margin-right: 10px;
146
158
  }
159
+
160
+ &[data-editify-list-style='decimal'] {
161
+ &::before {
162
+ content: counter(item, decimal) '.';
163
+ }
164
+ }
165
+ &[data-editify-list-style='decimal-leading-zero'] {
166
+ &::before {
167
+ content: counter(item, decimal-leading-zero) '.';
168
+ }
169
+ }
170
+ &[data-editify-list-style='lower-roman'] {
171
+ &::before {
172
+ content: counter(item, lower-roman) '.';
173
+ }
174
+ }
175
+ &[data-editify-list-style='upper-roman'] {
176
+ &::before {
177
+ content: counter(item, upper-roman) '.';
178
+ }
179
+ }
180
+ &[data-editify-list-style='lower-alpha'] {
181
+ &::before {
182
+ content: counter(item, lower-alpha) '.';
183
+ }
184
+ }
185
+ &[data-editify-list-style='upper-alpha'] {
186
+ &::before {
187
+ content: counter(item, upper-alpha) '.';
188
+ }
189
+ }
190
+ &[data-editify-list-style='lower-greek'] {
191
+ &::before {
192
+ content: counter(item, lower-greek) '.';
193
+ }
194
+ }
195
+ &[data-editify-list-style='cjk-ideographic'] {
196
+ &::before {
197
+ content: counter(item, cjk-ideographic) '.';
198
+ }
199
+ }
147
200
  }
148
201
  //无序列表样式
149
202
  :deep(div[data-editify-list='ul']) {
150
203
  margin-bottom: 15px;
151
204
 
152
205
  &::before {
153
- content: '\2022';
206
+ content: counter(item, disc);
154
207
  margin-right: 10px;
155
208
  }
209
+
210
+ &[data-editify-list-style='disc'] {
211
+ &::before {
212
+ content: counter(item, disc);
213
+ }
214
+ }
215
+ &[data-editify-list-style='circle'] {
216
+ &::before {
217
+ content: counter(item, circle);
218
+ }
219
+ }
220
+ &[data-editify-list-style='square'] {
221
+ &::before {
222
+ content: counter(item, square);
223
+ }
224
+ }
156
225
  }
157
226
  //代码样式
158
227
  :deep([data-editify-code]) {
@@ -309,15 +378,13 @@
309
378
  padding-left: 26px;
310
379
  font-size: var(--editify-font-size);
311
380
  color: var(--editify-font-color-dark);
312
- transition: all 300ms;
313
381
 
314
382
  &::before {
315
383
  display: block;
316
- width: 16px;
317
- height: 16px;
384
+ width: 14px;
385
+ height: 14px;
318
386
  border-radius: 2px;
319
387
  border: 1px solid var(--editify-font-color-light);
320
- transition: all 300ms;
321
388
  box-sizing: border-box;
322
389
  user-select: none;
323
390
  content: '';
@@ -332,29 +399,32 @@
332
399
  &::after {
333
400
  display: inline-block;
334
401
  width: 10px;
335
- height: 6px;
402
+ height: 5px;
336
403
  position: absolute;
337
404
  content: '';
338
- left: 3px;
405
+ left: 2px;
339
406
  top: 50%;
340
- margin-top: -2px;
341
- border: 1px solid var(--editify-font-color-light);
407
+ margin-top: -1px;
408
+ border: 2px solid transparent;
342
409
  border-top: none;
343
410
  border-right: none;
344
411
  transform: translateY(-50%) rotate(-45deg);
345
412
  transform-origin: center;
346
- box-sizing: border-box;
347
413
  z-index: 2;
348
414
  cursor: pointer;
349
- opacity: 0;
350
- transition: all 300ms;
351
415
  }
352
416
 
353
417
  &[data-editify-task='checked'] {
354
418
  text-decoration: line-through;
355
419
  color: var(--editify-font-color-light);
420
+
421
+ &::before {
422
+ background-color: var(--editify-font-color-disabled);
423
+ border-color: var(--editify-font-color-disabled);
424
+ }
425
+
356
426
  &::after {
357
- opacity: 1;
427
+ border-color: #fff;
358
428
  }
359
429
  }
360
430
  }
@@ -407,30 +477,6 @@
407
477
  background: var(--editify-background-darker);
408
478
  }
409
479
  }
410
- //面板样式
411
- :deep(div[data-editify-panel]) {
412
- display: block;
413
- position: relative;
414
- width: 100%;
415
- background-color: var(--editify-background-dark);
416
- color: var(--editify-font-color);
417
- border-radius: 4px;
418
- margin-bottom: 15px;
419
- padding: 10px 10px 1px 10px;
420
- border: 1px solid var(--editify-border-color);
421
-
422
- & > div {
423
- margin: 0 0 10px 0;
424
- font-size: var(--editify-font-size);
425
-
426
- &:first-child {
427
- margin: 0 0 15px 0;
428
- font-size: 18px;
429
- border-bottom: 1px solid var(--editify-border-color);
430
- padding-bottom: 8px;
431
- }
432
- }
433
- }
434
480
  //信息块样式
435
481
  :deep(div[data-editify-info]) {
436
482
  display: block;
@@ -452,7 +498,7 @@
452
498
  display: flex;
453
499
  justify-content: center;
454
500
  align-items: center;
455
- content: '\e610';
501
+ content: '\e600';
456
502
  font-family: 'editify-icon' !important;
457
503
  font-size: 1.25em;
458
504
  padding: 0 20px;
@@ -21,10 +21,10 @@
21
21
  <script setup lang="ts">
22
22
  import { computed, getCurrentInstance, nextTick, onBeforeUnmount, onMounted, provide, ref, watch } from 'vue'
23
23
  import { AlexEditor, AlexElement, AlexElementRangeType, AlexElementsRangeType } from 'alex-editor'
24
- import { element as DapElement, event as DapEvent, data as DapData, number as DapNumber, color as DapColor } from 'dap-util'
25
- import { mergeObject, getToolbarConfig, getMenuConfig, MenuConfigType, ObjectType, ToolbarConfigType, clickIsOut, cloneData } from '@/core/tool'
26
- import { parseList, orderdListHandle, commonElementHandle, tableThTdHandle, tableFormatHandle, tableRangeMergedHandle, preHandle, specialInblockHandle, attachmentHandle, mathformulaHandle, infoBlockHandle } from '@/core/rule'
27
- import { elementToParagraph, getMatchElementByRange, hasTableInRange, hasLinkInRange, hasPreInRange, hasImageInRange, hasVideoInRange, elementIsTask, elementIsAttachment, elementIsList, elementIsMathformula, getMathformulaByElement, elementIsPanel, elementIsInfoBlock } from '@/core/function'
24
+ import { element as DapElement, event as DapEvent, data as DapData, number as DapNumber, color as DapColor, common as DapCommon } from 'dap-util'
25
+ import { mergeObject, getToolbarConfig, getMenuConfig, MenuConfigType, ObjectType, ToolbarConfigType, clickIsOut, ShortcutType, MenuExtendType } from '@/core/tool'
26
+ import { listHandle, imageHandle, videoHandle, separatorHandle, linkHandle, codeHandle, tableHandle, preHandle, attachmentHandle, mathformulaHandle, infoBlockHandle, specialInblockHandle } from '@/core/rule'
27
+ import { elementToParagraph, getMatchElementByRange, elementIsTask, elementIsAttachment, elementIsList, elementIsMathformula, getMathformulaByElement, elementIsInfoBlock, getMatchElementByElement, hasTableInRange, hasPreInRange } from '@/core/function'
28
28
  import { trans } from '@/locale'
29
29
  import { LanguagesItemType } from '@/hljs'
30
30
  import { extraKeepTagsForMathformula } from '@/feature/mathformula'
@@ -125,7 +125,7 @@ const showBorder = computed<boolean>(() => {
125
125
  })
126
126
  //最终生效的工具栏配置
127
127
  const toolbarConfig = computed<ToolbarConfigType>(() => {
128
- return mergeObject(getToolbarConfig($editTrans, props.locale), props.toolbar || {}) as ToolbarConfigType
128
+ return mergeObject(getToolbarConfig($editTrans), props.toolbar || {}) as ToolbarConfigType
129
129
  })
130
130
  //最终生效的菜单栏配置
131
131
  const menuConfig = computed<MenuConfigType>(() => {
@@ -163,9 +163,7 @@ const handleToolbar = () => {
163
163
  }
164
164
  hideToolbar()
165
165
  nextTick(() => {
166
- const table = getMatchElementByRange(editor.value!, dataRangeCaches.value, { parsedom: 'table' })
167
166
  const codeBlock = getMatchElementByRange(editor.value!, dataRangeCaches.value, { parsedom: 'pre' })
168
- const link = getMatchElementByRange(editor.value!, dataRangeCaches.value, { parsedom: 'a' })
169
167
  const image = getMatchElementByRange(editor.value!, dataRangeCaches.value, { parsedom: 'img' })
170
168
  const video = getMatchElementByRange(editor.value!, dataRangeCaches.value, { parsedom: 'video' })
171
169
 
@@ -189,26 +187,6 @@ const handleToolbar = () => {
189
187
  toolbarOptions.value.show = true
190
188
  }
191
189
  }
192
- //显示链接工具条
193
- else if (link) {
194
- toolbarOptions.value.type = 'link'
195
- toolbarOptions.value.node = `[data-editify-uid="${instance.uid}"] [data-editify-element="${link.key}"]`
196
- if (toolbarOptions.value.show) {
197
- toolbarRef.value!.layerRef!.setPosition()
198
- } else {
199
- toolbarOptions.value.show = true
200
- }
201
- }
202
- //显示表格工具条
203
- else if (table) {
204
- toolbarOptions.value.type = 'table'
205
- toolbarOptions.value.node = `[data-editify-uid="${instance.uid}"] [data-editify-element="${table.key}"]`
206
- if (toolbarOptions.value.show) {
207
- toolbarRef.value!.layerRef!.setPosition()
208
- } else {
209
- toolbarOptions.value.show = true
210
- }
211
- }
212
190
  //显示代码块工具条
213
191
  else if (codeBlock) {
214
192
  toolbarOptions.value.type = 'codeBlock'
@@ -219,10 +197,11 @@ const handleToolbar = () => {
219
197
  toolbarOptions.value.show = true
220
198
  }
221
199
  }
222
- //显示文本工具条
200
+ //以下是选区时显示文本工具条,非选区时显示其他工具条的情况
223
201
  else {
224
202
  const result = dataRangeCaches.value.flatList.filter((item: AlexElementRangeType) => item.element.isText())
225
- if (result.length && !hasTableInRange(editor.value!, dataRangeCaches.value) && !hasPreInRange(editor.value!, dataRangeCaches.value) && !hasLinkInRange(editor.value!, dataRangeCaches.value) && !hasImageInRange(editor.value!, dataRangeCaches.value) && !hasVideoInRange(editor.value!, dataRangeCaches.value)) {
203
+ //显示文本工具条
204
+ if (result.length) {
226
205
  toolbarOptions.value.type = 'text'
227
206
  if (toolbarOptions.value.show) {
228
207
  toolbarRef.value!.layerRef!.setPosition()
@@ -230,6 +209,63 @@ const handleToolbar = () => {
230
209
  toolbarOptions.value.show = true
231
210
  }
232
211
  }
212
+ //显示其他工具条
213
+ else {
214
+ const table = getMatchElementByRange(editor.value!, dataRangeCaches.value, { parsedom: 'table' })
215
+ const link = getMatchElementByRange(editor.value!, dataRangeCaches.value, { parsedom: 'a' })
216
+ const orderList = getMatchElementByRange(editor.value!, dataRangeCaches.value, {
217
+ parsedom: 'div',
218
+ marks: {
219
+ 'data-editify-list': 'ol'
220
+ }
221
+ })
222
+ const unorderList = getMatchElementByRange(editor.value!, dataRangeCaches.value, {
223
+ parsedom: 'div',
224
+ marks: {
225
+ 'data-editify-list': 'ul'
226
+ }
227
+ })
228
+ //显示链接工具条
229
+ if (link) {
230
+ toolbarOptions.value.type = 'link'
231
+ toolbarOptions.value.node = `[data-editify-uid="${instance.uid}"] [data-editify-element="${link.key}"]`
232
+ if (toolbarOptions.value.show) {
233
+ toolbarRef.value!.layerRef!.setPosition()
234
+ } else {
235
+ toolbarOptions.value.show = true
236
+ }
237
+ }
238
+ //显示表格工具条
239
+ else if (table) {
240
+ toolbarOptions.value.type = 'table'
241
+ toolbarOptions.value.node = `[data-editify-uid="${instance.uid}"] [data-editify-element="${table.key}"]`
242
+ if (toolbarOptions.value.show) {
243
+ toolbarRef.value!.layerRef!.setPosition()
244
+ } else {
245
+ toolbarOptions.value.show = true
246
+ }
247
+ }
248
+ //显示有序列表工具条
249
+ else if (orderList) {
250
+ toolbarOptions.value.type = 'orderList'
251
+ toolbarOptions.value.node = `[data-editify-uid="${instance.uid}"] [data-editify-element="${orderList.key}"]`
252
+ if (toolbarOptions.value.show) {
253
+ toolbarRef.value!.layerRef!.setPosition()
254
+ } else {
255
+ toolbarOptions.value.show = true
256
+ }
257
+ }
258
+ //显示无序列表工具条
259
+ else if (unorderList) {
260
+ toolbarOptions.value.type = 'unorderList'
261
+ toolbarOptions.value.node = `[data-editify-uid="${instance.uid}"] [data-editify-element="${unorderList.key}"]`
262
+ if (toolbarOptions.value.show) {
263
+ toolbarRef.value!.layerRef!.setPosition()
264
+ } else {
265
+ toolbarOptions.value.show = true
266
+ }
267
+ }
268
+ }
233
269
  }
234
270
  })
235
271
  }
@@ -241,28 +277,28 @@ const createEditor = () => {
241
277
  disabled: isDisabled.value,
242
278
  renderRules: [
243
279
  el => {
244
- parseList(editor.value!, el)
280
+ listHandle(editor.value!, el)
245
281
  },
246
282
  el => {
247
- orderdListHandle(editor.value!, el)
283
+ imageHandle(editor.value!, el)
248
284
  },
249
285
  el => {
250
- commonElementHandle(editor.value!, el)
286
+ videoHandle(editor.value!, el)
251
287
  },
252
288
  el => {
253
- tableThTdHandle(editor.value!, el)
289
+ separatorHandle(editor.value!, el)
254
290
  },
255
291
  el => {
256
- tableFormatHandle(editor.value!, el)
292
+ linkHandle(editor.value!, el)
257
293
  },
258
294
  el => {
259
- tableRangeMergedHandle(editor.value!, el)
295
+ codeHandle(editor.value!, el)
260
296
  },
261
297
  el => {
262
- preHandle(editor.value!, el, !!(toolbarConfig.value?.use && toolbarConfig.value?.codeBlock?.languages?.show), toolbarConfig.value?.codeBlock?.languages?.options as (string | LanguagesItemType)[])
298
+ tableHandle(editor.value!, el)
263
299
  },
264
300
  el => {
265
- specialInblockHandle(editor.value!, el)
301
+ preHandle(editor.value!, el, !!(toolbarConfig.value?.use && toolbarConfig.value?.codeBlock?.languages?.show), toolbarConfig.value?.codeBlock?.languages?.options as (string | LanguagesItemType)[])
266
302
  },
267
303
  el => {
268
304
  attachmentHandle(editor.value!, el, $editTrans)
@@ -273,6 +309,9 @@ const createEditor = () => {
273
309
  el => {
274
310
  infoBlockHandle(editor.value!, el, props.color)
275
311
  },
312
+ el => {
313
+ specialInblockHandle(editor.value!, el)
314
+ },
276
315
  ...props.renderRules
277
316
  ],
278
317
  extraKeepTags: [...extraKeepTagsForMathformula, ...props.extraKeepTags],
@@ -473,7 +512,7 @@ const documentClick = (e: Event) => {
473
512
  if (key) {
474
513
  const element = editor.value!.getElementByKey(key)!
475
514
  //如果是任务列表元素
476
- if (elementIsTask(element)) {
515
+ if (element && elementIsTask(element)) {
477
516
  const rect = DapElement.getElementBounding(elm)
478
517
  //在复选框范围内
479
518
  if (event.pageX >= Math.abs(rect.left) && event.pageX <= Math.abs(rect.left + 16) && event.pageY >= Math.abs(rect.top + elm.offsetHeight / 2 - 8) && event.pageY <= Math.abs(rect.top + elm.offsetHeight / 2 + 8)) {
@@ -499,7 +538,8 @@ const documentClick = (e: Event) => {
499
538
  }
500
539
  //重新定义编辑器粘贴html
501
540
  const handleCustomHtmlPaste = async (elements: AlexElement[]) => {
502
- AlexElement.flatElements(elements).forEach(el => {
541
+ const flatElements = AlexElement.flatElements(elements)
542
+ flatElements.forEach(el => {
503
543
  if (!el.isText()) {
504
544
  let marks: ObjectType = {}
505
545
  let styles: ObjectType = {}
@@ -552,10 +592,7 @@ const handleCustomHtmlPaste = async (elements: AlexElement[]) => {
552
592
  //有序和无序列表属性保留
553
593
  if (elementIsList(el, true) || elementIsList(el, false)) {
554
594
  marks['data-editify-list'] = el.marks!['data-editify-list']
555
- //有序列表保留序列号标记
556
- if (el.marks!['data-editify-value']) {
557
- marks['data-editify-value'] = el.marks!['data-editify-value']
558
- }
595
+ marks['data-editify-list-style'] = el.marks!['data-editify-list-style']
559
596
  }
560
597
  //行内代码属性保留
561
598
  if (el.parsedom == AlexElement.TEXT_NODE && el.marks!['data-editify-code']) {
@@ -590,11 +627,7 @@ const handleCustomHtmlPaste = async (elements: AlexElement[]) => {
590
627
  }
591
628
  //数学公式内的属性全部保留
592
629
  if (!!getMathformulaByElement(el)) {
593
- marks = mergeObject(marks, cloneData(el.marks!))!
594
- }
595
- //面板属性保留
596
- if (elementIsPanel(el)) {
597
- marks['data-editify-panel'] = el.marks!['data-editify-panel']
630
+ marks = mergeObject(marks, DapCommon.clone(el.marks!))!
598
631
  }
599
632
  //信息块属性保留
600
633
  if (elementIsInfoBlock(el)) {
@@ -621,7 +654,7 @@ const handleCustomHtmlPaste = async (elements: AlexElement[]) => {
621
654
  }
622
655
  //数学公式内的样式全部保留
623
656
  if (!!getMathformulaByElement(el)) {
624
- styles = mergeObject(styles, cloneData(el.styles!))!
657
+ styles = mergeObject(styles, DapCommon.clone(el.styles!))!
625
658
  }
626
659
  }
627
660
  //对外的自定义属性和样式保留
@@ -635,7 +668,24 @@ const handleCustomHtmlPaste = async (elements: AlexElement[]) => {
635
668
  el.marks = marks
636
669
  el.styles = styles
637
670
  }
671
+ //对于不在table下的这些表格元素直接置空
672
+ if (el.parsedom && ['tbody', 'tr', 'th', 'td', 'thead', 'tfooter', 'colgroup', 'col'].includes(el.parsedom)) {
673
+ const flag = !!getMatchElementByElement(el, {
674
+ parsedom: 'table'
675
+ })
676
+ if (!flag) {
677
+ el.toEmpty()
678
+ if (el.parent) {
679
+ const index = el.parent.children!.findIndex(item => item.isEqual(el))
680
+ el.parent.children!.splice(index, 1)
681
+ }
682
+ }
683
+ }
638
684
  })
685
+ elements = elements.filter(el => !el.isEmpty())
686
+ if (!elements.length) {
687
+ return
688
+ }
639
689
  //如果使用了自定义粘贴html的功能
640
690
  if (typeof props.customHtmlPaste == 'function') {
641
691
  await props.customHtmlPaste(elements)
@@ -687,12 +737,94 @@ const handleCustomParseNode = (ele: AlexElement) => {
687
737
  return ele
688
738
  }
689
739
  //编辑区域键盘按下
690
- const handleEditorKeydown = (val: string, e: Event) => {
740
+ const handleEditorKeydown = (val: string, e: KeyboardEvent) => {
691
741
  if (isDisabled.value) {
692
742
  return
693
743
  }
694
- //单独按下tab键
695
- if ((e as KeyboardEvent).key.toLocaleLowerCase() == 'tab' && !(e as KeyboardEvent).metaKey && !(e as KeyboardEvent).shiftKey && !(e as KeyboardEvent).ctrlKey && !(e as KeyboardEvent).altKey && props.tab) {
744
+ //遍历菜单栏配置,设置快捷键
745
+ for (let key in menuConfig.value) {
746
+ //如果是拓展菜单
747
+ if (key == 'extends') {
748
+ //获取拓展菜单列表
749
+ const extendMenus = (menuConfig.value as any)[key] as MenuExtendType
750
+ //遍历拓展菜单列表
751
+ Object.keys(extendMenus).forEach(extendKey => {
752
+ //获取拓展菜单配置
753
+ const item = extendMenus[extendKey]
754
+ //获取该菜单按钮的快捷键配置
755
+ const shortcut = item.shortcut as ShortcutType | undefined
756
+ //如果快捷键配置存在并且定义了define方法
757
+ if (shortcut && typeof shortcut.define == 'function') {
758
+ //获取define方法执行结果
759
+ const res = shortcut.define(e) as boolean | { [code: string]: boolean }
760
+ //如果执行结果是true,则表示该快捷键按下
761
+ if (res === true) {
762
+ //阻止默认行为
763
+ e.preventDefault()
764
+ //没有被禁用则执行对应的操作
765
+ if (!item.disabled) {
766
+ shortcut.operation?.(editor.value!, dataRangeCaches.value, isSourceView, isFullScreen)
767
+ }
768
+ }
769
+ //如果是对象,则表示有多个快捷键操作
770
+ else if (res && DapCommon.isObject(res)) {
771
+ //遍历
772
+ Object.keys(res).forEach(code => {
773
+ //如果是true,则执行对应的操作
774
+ if (!!res[code]) {
775
+ //阻止默认行为
776
+ e.preventDefault()
777
+ //没有被禁用则执行对应的操作
778
+ if (!item.disabled) {
779
+ shortcut.operation?.(editor.value!, dataRangeCaches.value, isSourceView, isFullScreen, code)
780
+ }
781
+ }
782
+ })
783
+ }
784
+ }
785
+ })
786
+ }
787
+ //如果是内置菜单
788
+ else if (!['use', 'tooltip', 'mode', 'style', 'sequence'].includes(key)) {
789
+ const item = (menuConfig.value as any)[key]
790
+ //这里多做一步判断:判断是否菜单并且是否有快捷键配置
791
+ if (DapCommon.isObject(item) && item.show === true && DapCommon.isObject(item.shortcut)) {
792
+ //获取该菜单按钮的快捷键配置
793
+ const shortcut = item.shortcut as ShortcutType
794
+ //如果定义了define方法
795
+ if (typeof shortcut.define == 'function') {
796
+ //获取define方法执行结果
797
+ const res = shortcut.define(e) as boolean | { [code: string]: boolean }
798
+ //如果执行结果是true,则表示该快捷键按下
799
+ if (res === true) {
800
+ //阻止默认行为
801
+ e.preventDefault()
802
+ //没有被禁用则执行对应的操作
803
+ if (!item.disabled) {
804
+ shortcut.operation?.(editor.value!, dataRangeCaches.value, isSourceView, isFullScreen)
805
+ }
806
+ }
807
+ //如果是对象,则表示有多个快捷键操作
808
+ else if (res && DapCommon.isObject(res)) {
809
+ //遍历
810
+ Object.keys(res).forEach(code => {
811
+ //如果是true,则执行对应的操作
812
+ if (!!res[code]) {
813
+ //阻止默认行为
814
+ e.preventDefault()
815
+ //没有被禁用则执行对应的操作
816
+ if (!item.disabled) {
817
+ shortcut.operation?.(editor.value!, dataRangeCaches.value, isSourceView, isFullScreen, code)
818
+ }
819
+ }
820
+ })
821
+ }
822
+ }
823
+ }
824
+ }
825
+ }
826
+ //代码块和表格中单独按下tab键,插入4个空格
827
+ if (e.key.toLocaleLowerCase() == 'tab' && !e.metaKey && !e.shiftKey && !e.ctrlKey && !e.altKey && (hasTableInRange(editor.value!, dataRangeCaches.value) || hasPreInRange(editor.value!, dataRangeCaches.value))) {
696
828
  e.preventDefault()
697
829
  editor.value!.insertText(' ')
698
830
  editor.value!.domRender()
@@ -862,27 +994,25 @@ const handleAfterRender = () => {
862
994
  //设定视频高度
863
995
  setVideoHeight()
864
996
  //附件元素下载事件设置
865
- AlexElement.flatElements(editor.value!.stack).forEach(el => {
866
- if (elementIsAttachment(el)) {
867
- DapEvent.off(el.elm as HTMLElement, 'click')
868
- //单击下载
869
- DapEvent.on(el.elm as HTMLElement, 'click', async () => {
870
- //获取文件地址
871
- const url = el.marks!['data-editify-attachment']
872
- //使用fetch读取文件地址
873
- const res = await fetch(url, {
874
- method: 'GET'
875
- })
876
- //获取blob数据
877
- const blob = await res.blob()
878
- //创建a标签进行下载
879
- const a = document.createElement('a')
880
- a.setAttribute('target', '_blank')
881
- a.setAttribute('href', URL.createObjectURL(blob))
882
- a.setAttribute('download', el.marks!['data-editify-attachment-name'])
883
- a.click()
997
+ contentRef.value!.querySelectorAll('span[data-editify-attachment]').forEach(dom => {
998
+ DapEvent.off(dom as HTMLElement, 'click')
999
+ //单击下载
1000
+ DapEvent.on(dom as HTMLElement, 'click', async () => {
1001
+ //获取文件地址
1002
+ const url = dom.getAttribute('data-editify-attachment')!
1003
+ //使用fetch读取文件地址
1004
+ const res = await fetch(url, {
1005
+ method: 'GET'
884
1006
  })
885
- }
1007
+ //获取blob数据
1008
+ const blob = await res.blob()
1009
+ //创建a标签进行下载
1010
+ const a = document.createElement('a')
1011
+ a.setAttribute('target', '_blank')
1012
+ a.setAttribute('href', URL.createObjectURL(blob))
1013
+ a.setAttribute('download', dom.getAttribute('data-editify-attachment-name')!)
1014
+ a.click()
1015
+ })
886
1016
  })
887
1017
  emits('updateview')
888
1018
  }
@@ -919,28 +1049,14 @@ const undo = () => {
919
1049
  if (isDisabled.value) {
920
1050
  return
921
1051
  }
922
- const historyRecord = editor.value!.history.get(-1)
923
- if (historyRecord) {
924
- editor.value!.history.current = historyRecord.current
925
- editor.value!.stack = historyRecord.stack
926
- editor.value!.range = historyRecord.range
927
- editor.value!.domRender(true)
928
- editor.value!.rangeRender()
929
- }
1052
+ editor.value!.undo()
930
1053
  }
931
1054
  //api:重做
932
1055
  const redo = () => {
933
1056
  if (isDisabled.value) {
934
1057
  return
935
1058
  }
936
- const historyRecord = editor.value!.history.get(1)
937
- if (historyRecord) {
938
- editor.value!.history.current = historyRecord.current
939
- editor.value!.stack = historyRecord.stack
940
- editor.value!.range = historyRecord.range
941
- editor.value!.domRender(true)
942
- editor.value!.rangeRender()
943
- }
1059
+ editor.value!.redo()
944
1060
  }
945
1061
 
946
1062
  //监听编辑的值变更
@@ -46,7 +46,6 @@ import { SourceViewMenuButton } from '@/feature/sourceView'
46
46
  import { FullScreenMenuButton } from '@/feature/fullScreen'
47
47
  import { AttachmentMenuButton } from '@/feature/attachment'
48
48
  import { MathformulaMenuButton } from '@/feature/mathformula'
49
- import { PanelMenuButton } from '@/feature/panel'
50
49
  import { InfoBlockMenuButton } from '@/feature/infoBlock'
51
50
 
52
51
  defineOptions({
@@ -84,7 +83,7 @@ const menuNames = computed<string[]>(() => {
84
83
  })
85
84
  })
86
85
  //内置菜单组件的数组
87
- const defaultMenus = shallowRef([UndoMenuButton, RedoMenuButton, HeadingMenuButton, IndentMenuButton, QuoteMenuButton, SeparatorMenuButton, AlignMenuButton, OrderListMenuButton, UnorderListMenuButton, TaskMenuButton, BoldMenuButton, UnderlineMenuButton, ItalicMenuButton, StrikethroughMenuButton, CodeMenuButton, SuperMenuButton, SubMenuButton, FormatClearMenuButton, FontSizeMenuButton, FontFamilyMenuButton, LineHeightMenuButton, ForeColorMenuButton, BackColorMenuButton, LinkMenuButton, ImageMenuButton, VideoMenuButton, TableMenuButton, CodeBlockMenuButton, SourceViewMenuButton, FullScreenMenuButton, AttachmentMenuButton, MathformulaMenuButton, PanelMenuButton, InfoBlockMenuButton])
86
+ const defaultMenus = shallowRef([UndoMenuButton, RedoMenuButton, HeadingMenuButton, IndentMenuButton, QuoteMenuButton, SeparatorMenuButton, AlignMenuButton, OrderListMenuButton, UnorderListMenuButton, TaskMenuButton, BoldMenuButton, UnderlineMenuButton, ItalicMenuButton, StrikethroughMenuButton, CodeMenuButton, SuperMenuButton, SubMenuButton, FormatClearMenuButton, FontSizeMenuButton, FontFamilyMenuButton, LineHeightMenuButton, ForeColorMenuButton, BackColorMenuButton, LinkMenuButton, ImageMenuButton, VideoMenuButton, TableMenuButton, CodeBlockMenuButton, SourceViewMenuButton, FullScreenMenuButton, AttachmentMenuButton, MathformulaMenuButton, InfoBlockMenuButton])
88
87
  //根据菜单名称获取对应的内置菜单组件
89
88
  const currentDefaultMenu = computed(() => {
90
89
  return (name: string) => defaultMenus.value.find(item => item.name == `_${name}`)
@@ -128,7 +127,7 @@ const ExtendMenuButton = defineComponent(
128
127
  zIndex: props.zIndex + 1,
129
128
  ref: btnRef,
130
129
  type: configuration.type || 'default',
131
- title: configuration.title || '',
130
+ title: `${configuration.title || ''}${configuration.shortcut?.title ? `【${configuration.shortcut?.title}】` : ''}`,
132
131
  leftBorder: configuration.leftBorder || false,
133
132
  rightBorder: configuration.rightBorder || false,
134
133
  hideScroll: configuration.hideScroll || false,
@@ -12,7 +12,7 @@ export type EditifyResizeParamsType = {
12
12
  export type EditifyToolbarOptionsType = {
13
13
  show: boolean
14
14
  node: string | null
15
- type: 'text' | 'link' | 'image' | 'video' | 'table' | 'codeBlock'
15
+ type: 'text' | 'link' | 'image' | 'video' | 'table' | 'codeBlock' | 'orderList' | 'unorderList'
16
16
  }
17
17
 
18
18
  export const EditifyProps = {
@@ -156,11 +156,6 @@ export const EditifyProps = {
156
156
  type: Boolean,
157
157
  default: false
158
158
  },
159
- //是否使用tab快捷键
160
- tab: {
161
- type: Boolean,
162
- default: true
163
- },
164
159
  //是否使用深色模式
165
160
  dark: {
166
161
  type: Boolean,
@@ -19,10 +19,10 @@ export const ToolbarProps = {
19
19
  },
20
20
  //类型
21
21
  type: {
22
- type: String as PropType<'text' | 'table' | 'link' | 'codeBlock' | 'image' | 'video'>,
22
+ type: String as PropType<'text' | 'table' | 'link' | 'codeBlock' | 'image' | 'video' | 'orderList' | 'unorderList'>,
23
23
  default: 'text',
24
24
  validator(value: any) {
25
- return ['text', 'table', 'link', 'codeBlock', 'image', 'video'].includes(value)
25
+ return ['text', 'table', 'link', 'codeBlock', 'image', 'video', 'orderList', 'unorderList'].includes(value)
26
26
  }
27
27
  },
28
28
  //工具条配置