vue-editify 0.2.17 → 0.2.19
Sign up to get free protection for your applications and to get access to all the features.
- package/examples/App.vue +4 -12
- package/lib/components/colors/colors.vue.d.ts +9 -0
- package/lib/components/colors/props.d.ts +4 -0
- package/lib/components/tooltip/tooltip.vue.d.ts +1 -1
- package/lib/core/function.d.ts +112 -64
- package/lib/core/rule.d.ts +23 -17
- package/lib/core/shortcut.d.ts +36 -0
- package/lib/core/tool.d.ts +12 -16
- package/lib/editify/editify.vue.d.ts +162 -15
- package/lib/editify/props.d.ts +1 -5
- package/lib/editify/toolbar/props.d.ts +1 -1
- package/lib/editify/toolbar/toolbar.vue.d.ts +3 -3
- package/lib/editify.es.js +13660 -12954
- package/lib/editify.umd.js +2 -2
- package/lib/feature/align.d.ts +0 -14
- package/lib/feature/heading.d.ts +0 -14
- package/lib/feature/lineHeight.d.ts +0 -14
- package/lib/feature/orderList.d.ts +1 -3
- package/lib/feature/task.d.ts +0 -14
- package/lib/feature/unorderList.d.ts +1 -3
- package/lib/index.d.ts +164 -17
- package/package.json +2 -2
- package/src/components/button/button.vue +3 -3
- package/src/components/checkbox/checkbox.vue +1 -1
- package/src/components/colors/colors.vue +4 -4
- package/src/components/colors/props.ts +6 -1
- package/src/components/insertAttachment/insertAttachment.vue +1 -1
- package/src/components/insertImage/insertImage.vue +1 -1
- package/src/components/insertLink/insertLink.vue +1 -1
- package/src/components/insertVideo/insertVideo.vue +1 -1
- package/src/components/layer/layer.vue +9 -3
- package/src/components/tooltip/tooltip.vue +1 -1
- package/src/components/updateLink/updateLink.vue +1 -1
- package/src/core/function.ts +873 -491
- package/src/core/rule.ts +86 -368
- package/src/core/shortcut.ts +386 -0
- package/src/core/tool.ts +111 -159
- package/src/css/var.less +0 -10
- package/src/editify/editify.less +85 -39
- package/src/editify/editify.vue +204 -88
- package/src/editify/menu/menu.vue +2 -3
- package/src/editify/props.ts +1 -6
- package/src/editify/toolbar/props.ts +2 -2
- package/src/editify/toolbar/toolbar.vue +12 -12
- package/src/feature/align.ts +2 -62
- package/src/feature/attachment.ts +14 -27
- package/src/feature/backColor.ts +2 -1
- package/src/feature/bold.ts +1 -1
- package/src/feature/code.ts +1 -1
- package/src/feature/codeBlock.ts +3 -3
- package/src/feature/fontFamily.ts +1 -1
- package/src/feature/fontSize.ts +1 -1
- package/src/feature/foreColor.ts +2 -1
- package/src/feature/formatClear.ts +1 -1
- package/src/feature/fullScreen.ts +1 -1
- package/src/feature/heading.ts +5 -76
- package/src/feature/image.ts +1 -1
- package/src/feature/indent.ts +1 -1
- package/src/feature/infoBlock.ts +6 -37
- package/src/feature/italic.ts +1 -1
- package/src/feature/lineHeight.ts +2 -78
- package/src/feature/link.ts +1 -1
- package/src/feature/mathformula.ts +4 -51
- package/src/feature/orderList.ts +168 -37
- package/src/feature/quote.ts +3 -3
- package/src/feature/redo.ts +1 -1
- package/src/feature/separator.ts +1 -1
- package/src/feature/sourceView.ts +1 -1
- package/src/feature/strikethrough.ts +1 -1
- package/src/feature/sub.ts +1 -1
- package/src/feature/super.ts +1 -1
- package/src/feature/table.ts +3 -3
- package/src/feature/task.ts +4 -58
- package/src/feature/underline.ts +1 -1
- package/src/feature/undo.ts +1 -1
- package/src/feature/unorderList.ts +108 -37
- package/src/feature/video.ts +1 -1
- package/src/icon/iconfont.css +39 -3
- package/src/icon/iconfont.ttf +0 -0
- package/src/icon/iconfont.woff +0 -0
- package/src/index.ts +13 -11
- package/src/locale/en_US.ts +109 -110
- package/src/locale/zh_CN.ts +11 -12
- package/lib/feature/panel.d.ts +0 -18
- package/src/feature/panel.ts +0 -107
package/src/editify/editify.less
CHANGED
@@ -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
|
-
|
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:
|
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:
|
317
|
-
height:
|
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:
|
402
|
+
height: 5px;
|
336
403
|
position: absolute;
|
337
404
|
content: '';
|
338
|
-
left:
|
405
|
+
left: 2px;
|
339
406
|
top: 50%;
|
340
|
-
margin-top: -
|
341
|
-
border:
|
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
|
-
|
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: '\
|
501
|
+
content: '\e600';
|
456
502
|
font-family: 'editify-icon' !important;
|
457
503
|
font-size: 1.25em;
|
458
504
|
padding: 0 20px;
|
package/src/editify/editify.vue
CHANGED
@@ -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,
|
26
|
-
import {
|
27
|
-
import { elementToParagraph, getMatchElementByRange,
|
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
|
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
|
-
|
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
|
-
|
280
|
+
listHandle(editor.value!, el)
|
245
281
|
},
|
246
282
|
el => {
|
247
|
-
|
283
|
+
imageHandle(editor.value!, el)
|
248
284
|
},
|
249
285
|
el => {
|
250
|
-
|
286
|
+
videoHandle(editor.value!, el)
|
251
287
|
},
|
252
288
|
el => {
|
253
|
-
|
289
|
+
separatorHandle(editor.value!, el)
|
254
290
|
},
|
255
291
|
el => {
|
256
|
-
|
292
|
+
linkHandle(editor.value!, el)
|
257
293
|
},
|
258
294
|
el => {
|
259
|
-
|
295
|
+
codeHandle(editor.value!, el)
|
260
296
|
},
|
261
297
|
el => {
|
262
|
-
|
298
|
+
tableHandle(editor.value!, el)
|
263
299
|
},
|
264
300
|
el => {
|
265
|
-
|
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)
|
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,
|
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,
|
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:
|
740
|
+
const handleEditorKeydown = (val: string, e: KeyboardEvent) => {
|
691
741
|
if (isDisabled.value) {
|
692
742
|
return
|
693
743
|
}
|
694
|
-
|
695
|
-
|
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
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
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
|
-
|
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
|
-
|
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,
|
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,
|
package/src/editify/props.ts
CHANGED
@@ -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
|
//工具条配置
|