vue-editify 0.1.43 → 0.1.45

Sign up to get free protection for your applications and to get access to all the features.
package/src/core/rule.ts CHANGED
@@ -1,8 +1,175 @@
1
1
  import { AlexEditor, AlexElement } from 'alex-editor'
2
2
  import { LanguagesItemType, getHljsHtml } from '../hljs'
3
- import { isList, isTask } from './function'
3
+ import { isList, isTask, getTableSize, getCellSpanNumber } from './function'
4
4
  import { common as DapCommon } from 'dap-util'
5
5
 
6
+ /**
7
+ * 自动补全表格行和列
8
+ * @param editor
9
+ * @param rowElements
10
+ * @param rowNumber
11
+ * @param columnNumber
12
+ */
13
+ const autocompleteTableCells = (editor: AlexEditor, rowElements: AlexElement[], rowNumber: number, columnNumber: number) => {
14
+ //遍历所有的单元格
15
+ AlexElement.flatElements(rowElements).forEach(item => {
16
+ if (item.parsedom == 'td' && item.hasMarks()) {
17
+ //删除被合并的标识
18
+ if (item.marks!['data-editify-merged']) {
19
+ delete item.marks!['data-editify-merged']
20
+ }
21
+ //获取colspan
22
+ const colspan = isNaN(Number(item.marks!['colspan'])) ? 1 : Number(item.marks!['colspan'])
23
+ //获取rowspan
24
+ const rowspan = isNaN(Number(item.marks!['rowspan'])) ? 1 : Number(item.marks!['rowspan'])
25
+ //针对colspan>1的单元格在后面补全隐藏的单元格
26
+ if (colspan > 1) {
27
+ let i = 1
28
+ //补全的数量小于需要补全的数量并且列总数量小于理论数量
29
+ while (i < colspan && item.parent!.children!.length < columnNumber) {
30
+ const column = new AlexElement(
31
+ 'inblock',
32
+ 'td',
33
+ {
34
+ 'data-editify-merged': 'true'
35
+ },
36
+ null,
37
+ null
38
+ )
39
+ const breakElement = new AlexElement('closed', 'br', null, null, null)
40
+ editor.addElementTo(breakElement, column)
41
+ editor.addElementAfter(column, item)
42
+ i++
43
+ }
44
+ }
45
+ //针对rowspan>1的单元格在后面的行中对应位置补全隐藏的单元格
46
+ if (rowspan > 1) {
47
+ let el = item
48
+ let i = 1
49
+ while (i < rowspan && editor.getNextElement(el.parent!) && editor.getNextElement(el.parent!)!.children!.length < columnNumber) {
50
+ //下一行
51
+ const nextRow = editor.getNextElement(el.parent!)!
52
+ //单元格在行中的序列
53
+ const index = el.parent!.children!.findIndex(item => item.isEqual(el))
54
+ //下一行对应的单元格
55
+ const nextCell = nextRow.children![index]
56
+ //根据当前单元格的跨列数补充符合跨列数的隐藏单元格
57
+ for (let j = 0; j < colspan; j++) {
58
+ const column = new AlexElement(
59
+ 'inblock',
60
+ 'td',
61
+ {
62
+ 'data-editify-merged': 'true'
63
+ },
64
+ null,
65
+ null
66
+ )
67
+ const breakElement = new AlexElement('closed', 'br', null, null, null)
68
+ editor.addElementTo(breakElement, column)
69
+ if (nextCell) {
70
+ editor.addElementBefore(column, nextCell)
71
+ } else {
72
+ editor.addElementTo(column, nextRow, nextRow.children!.length)
73
+ }
74
+ }
75
+ el = nextRow.children![index]
76
+ i++
77
+ }
78
+ }
79
+ }
80
+ })
81
+ //遍历每一行,如果还缺少列则在后面补全列
82
+ rowElements.forEach(rowElement => {
83
+ //遍历该行的单元格获取总列数
84
+ const number = rowElement.children!.length
85
+ if (number < columnNumber) {
86
+ for (let i = 0; i < columnNumber - number; i++) {
87
+ const column = new AlexElement('inblock', 'td', null, null, null)
88
+ const breakElement = new AlexElement('closed', 'br', null, null, null)
89
+ editor.addElementTo(breakElement, column)
90
+ editor.addElementTo(column, rowElement, rowElement.children!.length)
91
+ }
92
+ }
93
+ })
94
+ //获取总行数
95
+ const length = rowElements.length
96
+ //判断总行数是否小于实际行数则补全行
97
+ if (length < rowNumber) {
98
+ for (let i = 0; i < rowNumber - length; i++) {
99
+ const row = new AlexElement('inblock', 'tr', null, null, null)
100
+ for (let j = 0; j < columnNumber; j++) {
101
+ const column = new AlexElement('inblock', 'td', null, null, null)
102
+ const breakElement = new AlexElement('closed', 'br', null, null, null)
103
+ editor.addElementTo(breakElement, column)
104
+ editor.addElementTo(column, row)
105
+ }
106
+ rowElements.push(row)
107
+ }
108
+ }
109
+ }
110
+
111
+ /**
112
+ * 自动隐藏被合并的单元格
113
+ * @param editor
114
+ * @param rowElements
115
+ */
116
+ const autoHideMergedTableCells = (editor: AlexEditor, rowElements: AlexElement[]) => {
117
+ const cells = AlexElement.flatElements(rowElements).filter(item => item.parsedom == 'td')
118
+ cells.forEach(cell => {
119
+ if (cell.hasMarks() && !cell.marks!['data-editify-merged']) {
120
+ //获取colspan
121
+ const colspan = isNaN(Number(cell.marks!['colspan'])) ? 1 : Number(cell.marks!['colspan'])
122
+ //获取rowspan
123
+ const rowspan = isNaN(Number(cell.marks!['rowspan'])) ? 1 : Number(cell.marks!['rowspan'])
124
+ //如果是跨列单元格,隐藏该单元格同行后的colspan-1个单元格
125
+ if (colspan > 1) {
126
+ let el = cell
127
+ let i = 1
128
+ while (i < colspan) {
129
+ const nextCell = editor.getNextElement(el)!
130
+ if (nextCell) {
131
+ if (nextCell.hasMarks()) {
132
+ nextCell.marks!['data-editify-merged'] = 'true'
133
+ } else {
134
+ nextCell.marks = {
135
+ 'data-editify-merged': 'true'
136
+ }
137
+ }
138
+ el = nextCell
139
+ i++
140
+ } else {
141
+ break
142
+ }
143
+ }
144
+ }
145
+ //如果是跨行单元格,隐藏该单元格同列后的rowspan-1行单元格
146
+ if (rowspan > 1) {
147
+ const index = cell.parent!.children!.findIndex(item => item.isEqual(cell))
148
+ let el = cell
149
+ let i = 1
150
+ while (i < rowspan && el && editor.getNextElement(el.parent!)) {
151
+ const nextRow = editor.getNextElement(el.parent!)!
152
+ //根据跨行单元格占据的列数,在后的rowspan-1行中隐藏colspan个单元格
153
+ for (let j = index; j < index + colspan; j++) {
154
+ const current = nextRow.children![j]
155
+ if (current) {
156
+ if (current.hasMarks()) {
157
+ current.marks!['data-editify-merged'] = 'true'
158
+ } else {
159
+ current.marks = {
160
+ 'data-editify-merged': 'true'
161
+ }
162
+ }
163
+ }
164
+ }
165
+ el = nextRow.children![index]
166
+ i++
167
+ }
168
+ }
169
+ }
170
+ })
171
+ }
172
+
6
173
  /**
7
174
  * 更新代码块内的光标位置
8
175
  * @param editor
@@ -94,7 +261,7 @@ export const parseList = (editor: AlexEditor, element: AlexElement) => {
94
261
  * @param editor
95
262
  * @param element
96
263
  */
97
- export const orderdListHandle = function (editor: AlexEditor, element: AlexElement) {
264
+ export const orderdListHandle = (editor: AlexEditor, element: AlexElement) => {
98
265
  //有序列表的序号处理
99
266
  if (isList(element, true)) {
100
267
  //获取前一个元素
@@ -116,7 +283,7 @@ export const orderdListHandle = function (editor: AlexEditor, element: AlexEleme
116
283
  * @param editor
117
284
  * @param element
118
285
  */
119
- export const commonElementHandle = function (editor: AlexEditor, element: AlexElement) {
286
+ export const commonElementHandle = (editor: AlexEditor, element: AlexElement) => {
120
287
  //图片、视频和链接设置marks
121
288
  if (element.parsedom == 'img' || element.parsedom == 'video' || element.parsedom == 'a') {
122
289
  const marks = {
@@ -167,11 +334,22 @@ export const commonElementHandle = function (editor: AlexEditor, element: AlexEl
167
334
  }
168
335
 
169
336
  /**
170
- * 元素格式化时处理表格
337
+ * 元素格式化时处理表格:th转为td
338
+ * @param editor
339
+ * @param element
340
+ */
341
+ export const tableThTdHandle = (_editor: AlexEditor, element: AlexElement) => {
342
+ if (element.parsedom == 'th') {
343
+ element.parsedom = 'td'
344
+ }
345
+ }
346
+
347
+ /**
348
+ * 元素格式化时处理表格:格式化表格
171
349
  * @param editor
172
350
  * @param element
173
351
  */
174
- export const tableHandle = function (editor: AlexEditor, element: AlexElement) {
352
+ export const tableFormatHandle = (editor: AlexEditor, element: AlexElement) => {
175
353
  if (element.parsedom == 'table') {
176
354
  const marks = {
177
355
  'data-editify-element': element.key
@@ -195,16 +373,12 @@ export const tableHandle = function (editor: AlexEditor, element: AlexElement) {
195
373
  const rows = elements.filter(el => {
196
374
  return el.parsedom == 'tr'
197
375
  })
376
+ //获取表格实际应该的规格
377
+ const { rowNumber, columnNumber } = getTableSize(rows)
198
378
  //colgroup元素
199
379
  let colgroup = elements.find(el => {
200
380
  return el.parsedom == 'colgroup'
201
381
  })
202
- //理论上的col的数量:取最多列数
203
- const colNumber = Math.max(
204
- ...rows.map(row => {
205
- return row.children!.length
206
- })
207
- )
208
382
  //如果colgroup元素存在
209
383
  if (colgroup) {
210
384
  //遍历每个col元素设置默认的width:'auto
@@ -222,8 +396,8 @@ export const tableHandle = function (editor: AlexEditor, element: AlexElement) {
222
396
  })
223
397
  //对缺少的col元素进行补全
224
398
  const length = colgroup.children!.length
225
- if (length < colNumber) {
226
- for (let i = 0; i < colNumber - length; i++) {
399
+ if (length < columnNumber) {
400
+ for (let i = 0; i < columnNumber - length; i++) {
227
401
  const col = new AlexElement(
228
402
  'closed',
229
403
  'col',
@@ -240,7 +414,7 @@ export const tableHandle = function (editor: AlexEditor, element: AlexElement) {
240
414
  //如果colgroup元素不存在则新建
241
415
  else {
242
416
  colgroup = new AlexElement('inblock', 'colgroup', null, null, null)
243
- for (let i = colNumber - 1; i >= 0; i--) {
417
+ for (let i = columnNumber - 1; i >= 0; i--) {
244
418
  const col = new AlexElement(
245
419
  'closed',
246
420
  'col',
@@ -253,41 +427,106 @@ export const tableHandle = function (editor: AlexEditor, element: AlexElement) {
253
427
  editor.addElementTo(col, colgroup)
254
428
  }
255
429
  }
430
+ //自动补全表格的单元格
431
+ autocompleteTableCells(editor, rows, rowNumber, columnNumber)
432
+ //清空表格
256
433
  element.children = []
434
+ //创建tbody元素
257
435
  const tbody = new AlexElement('inblock', 'tbody', null, null, null)
258
- rows.reverse().forEach(row => {
259
- //对缺少的列进行补全
260
- const length = row.children!.length
261
- if (length < colNumber) {
262
- for (let i = 0; i < colNumber - length; i++) {
263
- const column = new AlexElement('inblock', 'td', null, null, null)
264
- const breakElement = new AlexElement('closed', 'br', null, null, null)
265
- editor.addElementTo(breakElement, column)
266
- editor.addElementTo(column, row, row.children!.length)
267
- }
268
- }
269
- editor.addElementTo(row, tbody)
436
+ //将rows全部加入表格中
437
+ rows.forEach(row => {
438
+ const index = tbody.hasChildren() ? tbody.children!.length : 0
439
+ editor.addElementTo(row, tbody, index)
270
440
  })
271
441
  editor.addElementTo(tbody, element)
272
442
  editor.addElementTo(colgroup, element)
443
+ //对表格单元格合并状态进行处理
444
+ autoHideMergedTableCells(editor, rows)
273
445
  }
274
- if (element.parsedom == 'th') {
275
- element.parsedom = 'td'
276
- }
277
- if (element.parsedom == 'td') {
278
- //移除列的rowspan和colspan属性
279
- if (element.hasMarks()) {
280
- if (element.marks!['rowspan']) {
281
- delete element.marks!['rowspan']
446
+ }
447
+
448
+ /**
449
+ * 元素格式化时处理表格:处理光标在表格隐藏单元格内的情况
450
+ * @param editor
451
+ * @param element
452
+ */
453
+ export const tableRangeMergedHandle = (editor: AlexEditor, element: AlexElement) => {
454
+ //如果元素是被隐藏的单元格,并且光标在该单元格内
455
+ if (element.parsedom == 'td' && element.hasMarks() && element.marks!['data-editify-merged'] && editor.range) {
456
+ //单元格向左查找设置焦点
457
+ const queryLeftSetRange = (_element: AlexElement, callback: (ele: AlexElement) => void) => {
458
+ //是否已查找到
459
+ let success = false
460
+ //获取前一个单元格
461
+ let el = editor.getPreviousElement(_element)
462
+ let tempIndex = 1
463
+ //如果前一个单元格存在则循环
464
+ while (el) {
465
+ //获取单元格的colspan
466
+ const { colspan } = getCellSpanNumber(el)
467
+ //如果单元格是跨列的并且是跨当前单元格的
468
+ if (el.hasMarks() && !el.marks!['data-editify-merged'] && colspan > tempIndex) {
469
+ success = true
470
+ callback(el)
471
+ break
472
+ }
473
+ //不是则继续向上查找
474
+ else {
475
+ el = editor.getPreviousElement(el)
476
+ tempIndex++
477
+ }
478
+ }
479
+ return success
480
+ }
481
+ //单元格向上查找设置焦点
482
+ const queryUpSetRange = (_element: AlexElement, callback: (ele: AlexElement) => void) => {
483
+ //是否已查找到
484
+ let success = false
485
+ //单元格在行中的序列
486
+ const index = _element.parent!.children!.findIndex(item => item.isEqual(_element))
487
+ //获取前一行元素
488
+ let el = editor.getPreviousElement(_element.parent!)
489
+ let tempIndex = 1
490
+ //如果前一行元素存在则循环
491
+ while (el) {
492
+ //获取前一行中同列的单元格
493
+ const previousColumn = el.children![index]
494
+ //获取单元格的rowspan
495
+ const { rowspan } = getCellSpanNumber(previousColumn)
496
+ //如果单元格是跨行的并且是跨当前单元格的
497
+ if (previousColumn.hasMarks() && !previousColumn.marks!['data-editify-merged'] && rowspan > tempIndex) {
498
+ success = true
499
+ callback(previousColumn)
500
+ break
501
+ }
502
+ //不是则继续向上查找
503
+ else {
504
+ el = editor.getPreviousElement(el)
505
+ tempIndex++
506
+ }
282
507
  }
283
- if (element.marks!['colspan']) {
284
- delete element.marks!['colspan']
508
+ return success
509
+ }
510
+ //起点在该单元格下
511
+ if (element.isContains(editor.range.anchor.element)) {
512
+ const success = queryLeftSetRange(element, ele => {
513
+ editor.range!.anchor.moveToEnd(ele)
514
+ })
515
+ if (!success) {
516
+ queryUpSetRange(element, ele => {
517
+ editor.range!.anchor.moveToEnd(ele)
518
+ })
285
519
  }
286
520
  }
287
- //移除列的display样式
288
- if (element.hasStyles()) {
289
- if (element.styles!['display']) {
290
- delete element.styles!['display']
521
+ //终点在该单元格下
522
+ if (element.isContains(editor.range.focus.element)) {
523
+ const success = queryLeftSetRange(element, ele => {
524
+ editor.range!.focus.moveToEnd(ele)
525
+ })
526
+ if (!success) {
527
+ queryUpSetRange(element, ele => {
528
+ editor.range!.focus.moveToEnd(ele)
529
+ })
291
530
  }
292
531
  }
293
532
  }
@@ -300,7 +539,7 @@ export const tableHandle = function (editor: AlexEditor, element: AlexElement) {
300
539
  * @param highlight
301
540
  * @param languages
302
541
  */
303
- export const preHandle = function (editor: AlexEditor, element: AlexElement, highlight: boolean, languages: (string | LanguagesItemType)[]) {
542
+ export const preHandle = (editor: AlexEditor, element: AlexElement, highlight: boolean, languages: (string | LanguagesItemType)[]) => {
304
543
  //如果是代码块进行处理
305
544
  if (element.parsedom == 'pre') {
306
545
  const marks = {
@@ -365,7 +604,7 @@ export const preHandle = function (editor: AlexEditor, element: AlexElement, hig
365
604
  * @param editor
366
605
  * @param element
367
606
  */
368
- export const specialInblockHandle = function (editor: AlexEditor, element: AlexElement) {
607
+ export const specialInblockHandle = (editor: AlexEditor, element: AlexElement) => {
369
608
  if (element.hasChildren()) {
370
609
  element.children!.forEach(el => {
371
610
  if (isList(el, true) || isList(el, false) || isTask(el) || ['blockquote', 'pre', 'table', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p'].includes(el.parsedom!)) {
package/src/core/tool.ts CHANGED
@@ -224,7 +224,7 @@ export type PluginType = (editifyInstance: ComponentInternalInstance, editTrans:
224
224
  * @param o2
225
225
  * @returns
226
226
  */
227
- export const mergeObject = function (o1: ObjectType, o2: ObjectType) {
227
+ export const mergeObject = (o1: ObjectType, o2: ObjectType) => {
228
228
  if (!DapCommon.isObject(o1) && DapCommon.isObject(o2)) {
229
229
  return null
230
230
  }
@@ -248,7 +248,7 @@ export const mergeObject = function (o1: ObjectType, o2: ObjectType) {
248
248
  * @param value
249
249
  * @returns
250
250
  */
251
- export const queryHasValue = function (obj: ObjectType, name: string, value?: string | number) {
251
+ export const queryHasValue = (obj: ObjectType, name: string, value?: string | number) => {
252
252
  //如果value不存在则判断是否拥有属性name
253
253
  if (value == null || value == undefined) {
254
254
  return obj.hasOwnProperty(name)
@@ -293,7 +293,7 @@ export const queryHasValue = function (obj: ObjectType, name: string, value?: st
293
293
  * @param data
294
294
  * @returns
295
295
  */
296
- export const cloneData = function (data: any) {
296
+ export const cloneData = (data: any) => {
297
297
  if (DapCommon.isObject(data) || Array.isArray(data)) {
298
298
  return JSON.parse(JSON.stringify(data))
299
299
  }
@@ -305,7 +305,7 @@ export const cloneData = function (data: any) {
305
305
  * @param editTrans
306
306
  * @returns
307
307
  */
308
- export const getButtonOptionsConfig = function (editTrans: (key: string) => any): ButtonOptionsConfigType {
308
+ export const getButtonOptionsConfig = (editTrans: (key: string) => any): ButtonOptionsConfigType => {
309
309
  return {
310
310
  //标题配置
311
311
  heading: [
@@ -506,7 +506,7 @@ export const getButtonOptionsConfig = function (editTrans: (key: string) => any)
506
506
  * @param editLocale
507
507
  * @returns
508
508
  */
509
- export const getToolbarConfig = function (editTrans: (key: string) => any, editLocale: LocaleType): ToolbarConfigType {
509
+ export const getToolbarConfig = (editTrans: (key: string) => any, editLocale: LocaleType): ToolbarConfigType => {
510
510
  return {
511
511
  //是否使用工具条
512
512
  use: true,
@@ -756,7 +756,7 @@ export const getToolbarConfig = function (editTrans: (key: string) => any, editL
756
756
  * @param editLocale
757
757
  * @returns
758
758
  */
759
- export const getMenuConfig = function (editTrans: (key: string) => any, editLocale: LocaleType): MenuConfigType {
759
+ export const getMenuConfig = (editTrans: (key: string) => any, editLocale: LocaleType): MenuConfigType => {
760
760
  return {
761
761
  //是否使用菜单栏
762
762
  use: true,
@@ -232,6 +232,10 @@
232
232
  cursor: col-resize;
233
233
  user-select: none;
234
234
  }
235
+
236
+ &[data-editify-merged] {
237
+ display: none;
238
+ }
235
239
  }
236
240
  }
237
241
  }
@@ -261,6 +265,11 @@
261
265
  vertical-align: text-bottom;
262
266
  margin: 0 2px;
263
267
  max-width: 100%;
268
+ min-width: 100px;
269
+
270
+ &:hover {
271
+ cursor: pointer;
272
+ }
264
273
  }
265
274
  //视频样式
266
275
  :deep(video) {
@@ -273,6 +282,7 @@
273
282
  object-fit: contain;
274
283
  margin: 0 2px;
275
284
  max-width: 100%;
285
+ min-width: 100px;
276
286
  }
277
287
  //引用样式
278
288
  :deep(blockquote) {