/
table-cell-paragraph-post-fixer.ts
136 lines (111 loc) · 3.92 KB
/
table-cell-paragraph-post-fixer.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/**
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @module table/converters/table-cell-paragraph-post-fixer
*/
import type { Model, Writer, Element, DiffItemInsert, DiffItemRemove } from 'ckeditor5/src/engine.js';
/**
* Injects a table cell post-fixer into the model which inserts a `paragraph` element into empty table cells.
*
* A table cell must contain at least one block element as a child. An empty table cell will have an empty `paragraph` as a child.
*
* ```xml
* <table>
* <tableRow>
* <tableCell></tableCell>
* </tableRow>
* </table>
* ```
*
* Will be fixed to:
*
* ```xml
* <table>
* <tableRow>
* <tableCell><paragraph></paragraph></tableCell>
* </tableRow>
* </table>
* ```
*/
export default function injectTableCellParagraphPostFixer( model: Model ): void {
model.document.registerPostFixer( writer => tableCellContentsPostFixer( writer, model ) );
}
/**
* The table cell contents post-fixer.
*/
function tableCellContentsPostFixer( writer: Writer, model: Model ) {
const changes = model.document.differ.getChanges();
let wasFixed = false;
for ( const entry of changes ) {
if ( entry.type == 'insert' && entry.name == 'table' ) {
wasFixed = fixTable( entry.position.nodeAfter as Element, writer ) || wasFixed;
}
if ( entry.type == 'insert' && entry.name == 'tableRow' ) {
wasFixed = fixTableRow( entry.position.nodeAfter as Element, writer ) || wasFixed;
}
if ( entry.type == 'insert' && entry.name == 'tableCell' ) {
wasFixed = fixTableCellContent( entry.position.nodeAfter as Element, writer ) || wasFixed;
}
if ( ( entry.type == 'remove' || entry.type == 'insert' ) && checkTableCellChange( entry ) ) {
wasFixed = fixTableCellContent( entry.position.parent as Element, writer ) || wasFixed;
}
}
return wasFixed;
}
/**
* Fixes all table cells in a table.
*/
function fixTable( table: Element, writer: Writer ) {
let wasFixed = false;
for ( const row of table.getChildren() ) {
if ( row.is( 'element', 'tableRow' ) ) {
wasFixed = fixTableRow( row, writer ) || wasFixed;
}
}
return wasFixed;
}
/**
* Fixes all table cells in a table row.
*/
function fixTableRow( tableRow: Element, writer: Writer ) {
let wasFixed = false;
for ( const tableCell of tableRow.getChildren() as IterableIterator<Element> ) {
wasFixed = fixTableCellContent( tableCell, writer ) || wasFixed;
}
return wasFixed;
}
/**
* Fixes all table cell content by:
* - Adding a paragraph to a table cell without any child.
* - Wrapping direct $text in a `<paragraph>`.
*/
function fixTableCellContent( tableCell: Element, writer: Writer ) {
// Insert paragraph to an empty table cell.
if ( tableCell.childCount == 0 ) {
// @if CK_DEBUG_TABLE // console.log( 'Post-fixing table: insert paragraph in empty cell.' );
writer.insertElement( 'paragraph', tableCell );
return true;
}
// Check table cell children for directly placed text nodes.
// Temporary solution. See https://github.com/ckeditor/ckeditor5/issues/1464.
const textNodes = Array.from( tableCell.getChildren() ).filter( child => child.is( '$text' ) );
// @if CK_DEBUG_TABLE // textNodes.length && console.log( 'Post-fixing table: wrap cell content with paragraph.' );
for ( const child of textNodes ) {
writer.wrap( writer.createRangeOn( child ), 'paragraph' );
}
// Return true when there were text nodes to fix.
return !!textNodes.length;
}
/**
* Checks if a differ change should fix the table cell. This happens on:
* - Removing content from the table cell (i.e. `tableCell` can be left empty).
* - Adding a text node directly into a table cell.
*/
function checkTableCellChange( entry: DiffItemInsert | DiffItemRemove ) {
if ( !entry.position.parent.is( 'element', 'tableCell' ) ) {
return false;
}
return entry.type == 'insert' && entry.name == '$text' || entry.type == 'remove';
}