@ckeditor/ckeditor5-table 31.0.0 → 33.0.0

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 (146) hide show
  1. package/LICENSE.md +2 -2
  2. package/build/table.js +3 -3
  3. package/build/table.js.map +1 -0
  4. package/build/translations/cs.js +1 -1
  5. package/build/translations/el.js +1 -0
  6. package/build/translations/es.js +1 -1
  7. package/build/translations/id.js +1 -1
  8. package/build/translations/pt-br.js +1 -1
  9. package/build/translations/uz.js +1 -0
  10. package/build/translations/zh.js +1 -1
  11. package/lang/translations/ar.po +1 -1
  12. package/lang/translations/az.po +1 -1
  13. package/lang/translations/bg.po +1 -1
  14. package/lang/translations/cs.po +4 -4
  15. package/lang/translations/da.po +1 -1
  16. package/lang/translations/de-ch.po +1 -1
  17. package/lang/translations/de.po +1 -1
  18. package/lang/translations/el.po +261 -0
  19. package/lang/translations/en-au.po +1 -1
  20. package/lang/translations/en-gb.po +1 -1
  21. package/lang/translations/en.po +1 -1
  22. package/lang/translations/es.po +43 -43
  23. package/lang/translations/et.po +1 -1
  24. package/lang/translations/fa.po +1 -1
  25. package/lang/translations/fi.po +1 -1
  26. package/lang/translations/fr.po +1 -1
  27. package/lang/translations/gl.po +1 -1
  28. package/lang/translations/hi.po +1 -1
  29. package/lang/translations/hr.po +1 -1
  30. package/lang/translations/hu.po +1 -1
  31. package/lang/translations/id.po +11 -11
  32. package/lang/translations/it.po +1 -1
  33. package/lang/translations/ja.po +1 -1
  34. package/lang/translations/ko.po +1 -1
  35. package/lang/translations/ku.po +1 -1
  36. package/lang/translations/lt.po +1 -1
  37. package/lang/translations/lv.po +1 -1
  38. package/lang/translations/nb.po +1 -1
  39. package/lang/translations/ne.po +1 -1
  40. package/lang/translations/nl.po +1 -1
  41. package/lang/translations/no.po +1 -1
  42. package/lang/translations/pl.po +1 -1
  43. package/lang/translations/pt-br.po +4 -4
  44. package/lang/translations/ro.po +1 -1
  45. package/lang/translations/ru.po +1 -1
  46. package/lang/translations/sk.po +1 -1
  47. package/lang/translations/sq.po +1 -1
  48. package/lang/translations/sr-latn.po +1 -1
  49. package/lang/translations/sr.po +1 -1
  50. package/lang/translations/sv.po +1 -1
  51. package/lang/translations/th.po +1 -1
  52. package/lang/translations/tk.po +1 -1
  53. package/lang/translations/tr.po +1 -1
  54. package/lang/translations/ug.po +1 -1
  55. package/lang/translations/uk.po +1 -1
  56. package/lang/translations/uz.po +261 -0
  57. package/lang/translations/vi.po +1 -1
  58. package/lang/translations/zh-cn.po +1 -1
  59. package/lang/translations/zh.po +4 -4
  60. package/package.json +26 -24
  61. package/src/commands/insertcolumncommand.js +5 -5
  62. package/src/commands/insertrowcommand.js +5 -5
  63. package/src/commands/inserttablecommand.js +1 -1
  64. package/src/commands/mergecellcommand.js +5 -6
  65. package/src/commands/mergecellscommand.js +6 -5
  66. package/src/commands/removecolumncommand.js +9 -8
  67. package/src/commands/removerowcommand.js +6 -7
  68. package/src/commands/selectcolumncommand.js +5 -5
  69. package/src/commands/selectrowcommand.js +6 -6
  70. package/src/commands/setheadercolumncommand.js +6 -5
  71. package/src/commands/setheaderrowcommand.js +8 -5
  72. package/src/commands/splitcellcommand.js +5 -5
  73. package/src/converters/downcast.js +77 -408
  74. package/src/converters/table-caption-post-fixer.js +1 -1
  75. package/src/converters/table-cell-paragraph-post-fixer.js +1 -1
  76. package/src/converters/{table-cell-refresh-post-fixer.js → table-cell-refresh-handler.js} +9 -20
  77. package/src/converters/table-headings-refresh-handler.js +68 -0
  78. package/src/converters/table-layout-post-fixer.js +1 -1
  79. package/src/converters/tableproperties.js +6 -5
  80. package/src/converters/upcasttable.js +7 -3
  81. package/src/index.js +1 -1
  82. package/src/plaintableoutput.js +151 -0
  83. package/src/table.js +1 -1
  84. package/src/tablecaption/tablecaptionediting.js +1 -1
  85. package/src/tablecaption/tablecaptionui.js +3 -3
  86. package/src/tablecaption/toggletablecaptioncommand.js +3 -3
  87. package/src/tablecaption/utils.js +1 -1
  88. package/src/tablecaption.js +1 -1
  89. package/src/tablecellproperties/commands/tablecellbackgroundcolorcommand.js +2 -2
  90. package/src/tablecellproperties/commands/tablecellbordercolorcommand.js +2 -2
  91. package/src/tablecellproperties/commands/tablecellborderstylecommand.js +2 -2
  92. package/src/tablecellproperties/commands/tablecellborderwidthcommand.js +2 -2
  93. package/src/tablecellproperties/commands/tablecellheightcommand.js +2 -2
  94. package/src/tablecellproperties/commands/tablecellhorizontalalignmentcommand.js +2 -2
  95. package/src/tablecellproperties/commands/tablecellpaddingcommand.js +2 -2
  96. package/src/tablecellproperties/commands/tablecellpropertycommand.js +6 -5
  97. package/src/tablecellproperties/commands/tablecellverticalalignmentcommand.js +2 -2
  98. package/src/tablecellproperties/commands/tablecellwidthcommand.js +2 -2
  99. package/src/tablecellproperties/tablecellpropertiesediting.js +42 -35
  100. package/src/tablecellproperties/tablecellpropertiesui.js +1 -1
  101. package/src/tablecellproperties/ui/tablecellpropertiesview.js +11 -1
  102. package/src/tablecellproperties.js +1 -1
  103. package/src/tableclipboard.js +19 -16
  104. package/src/tableediting.js +49 -28
  105. package/src/tablekeyboard.js +7 -6
  106. package/src/tablemouse/mouseeventsobserver.js +1 -1
  107. package/src/tablemouse.js +7 -5
  108. package/src/tableproperties/commands/tablealignmentcommand.js +2 -2
  109. package/src/tableproperties/commands/tablebackgroundcolorcommand.js +2 -2
  110. package/src/tableproperties/commands/tablebordercolorcommand.js +2 -2
  111. package/src/tableproperties/commands/tableborderstylecommand.js +2 -2
  112. package/src/tableproperties/commands/tableborderwidthcommand.js +2 -2
  113. package/src/tableproperties/commands/tableheightcommand.js +2 -2
  114. package/src/tableproperties/commands/tablepropertycommand.js +2 -2
  115. package/src/tableproperties/commands/tablewidthcommand.js +2 -2
  116. package/src/tableproperties/tablepropertiesediting.js +29 -21
  117. package/src/tableproperties/tablepropertiesui.js +1 -1
  118. package/src/tableproperties/ui/tablepropertiesview.js +11 -1
  119. package/src/tableproperties.js +1 -1
  120. package/src/tableselection.js +10 -9
  121. package/src/tabletoolbar.js +1 -1
  122. package/src/tableui.js +1 -1
  123. package/src/tableutils.js +311 -1
  124. package/src/tablewalker.js +1 -1
  125. package/src/ui/colorinputview.js +1 -1
  126. package/src/ui/formrowview.js +1 -1
  127. package/src/ui/inserttableview.js +1 -1
  128. package/src/utils/common.js +1 -1
  129. package/src/utils/structure.js +1 -1
  130. package/src/utils/table-properties.js +1 -1
  131. package/src/utils/ui/contextualballoon.js +1 -1
  132. package/src/utils/ui/table-properties.js +1 -1
  133. package/src/utils/ui/widget.js +1 -1
  134. package/theme/colorinput.css +1 -1
  135. package/theme/form.css +1 -1
  136. package/theme/formrow.css +1 -1
  137. package/theme/inserttable.css +1 -1
  138. package/theme/table.css +2 -2
  139. package/theme/tablecaption.css +1 -1
  140. package/theme/tablecellproperties.css +1 -1
  141. package/theme/tableediting.css +1 -1
  142. package/theme/tableform.css +1 -1
  143. package/theme/tableproperties.css +1 -1
  144. package/theme/tableselection.css +1 -1
  145. package/src/converters/table-heading-rows-refresh-post-fixer.js +0 -72
  146. package/src/utils/selection.js +0 -276
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -13,123 +13,57 @@ import { toWidget, toWidgetEditable } from 'ckeditor5/src/widget';
13
13
  /**
14
14
  * Model table element to view table element conversion helper.
15
15
  *
16
- * This conversion helper creates the whole table element with child elements.
17
- *
18
- * @param {Object} options
19
- * @param {Boolean} options.asWidget If set to `true`, the downcast conversion will produce a widget.
20
- * @returns {Function} Conversion helper.
16
+ * @param {module:table/tableutils~TableUtils} tableUtils The `TableUtils` plugin instance.
17
+ * @param {Object} [options]
18
+ * @param {Boolean} [options.asWidget] If set to `true`, the downcast conversion will produce a widget.
19
+ * @returns {Function} Element creator.
21
20
  */
22
- export function downcastInsertTable( options = {} ) {
23
- return dispatcher => dispatcher.on( 'insert:table', ( evt, data, conversionApi ) => {
24
- const table = data.item;
25
-
26
- if ( !conversionApi.consumable.consume( table, 'insert' ) ) {
27
- return;
21
+ export function downcastTable( tableUtils, options = {} ) {
22
+ return ( table, { writer } ) => {
23
+ const headingRows = table.getAttribute( 'headingRows' ) || 0;
24
+ const tableSections = [];
25
+
26
+ // Table head slot.
27
+ if ( headingRows > 0 ) {
28
+ tableSections.push(
29
+ writer.createContainerElement( 'thead', null,
30
+ writer.createSlot( element => element.is( 'element', 'tableRow' ) && element.index < headingRows )
31
+ )
32
+ );
28
33
  }
29
34
 
30
- // Consume attributes if present to not fire attribute change downcast
31
- conversionApi.consumable.consume( table, 'attribute:headingRows:table' );
32
- conversionApi.consumable.consume( table, 'attribute:headingColumns:table' );
33
-
34
- const asWidget = options && options.asWidget;
35
-
36
- const figureElement = conversionApi.writer.createContainerElement( 'figure', { class: 'table' } );
37
- const tableElement = conversionApi.writer.createContainerElement( 'table' );
38
- conversionApi.writer.insert( conversionApi.writer.createPositionAt( figureElement, 0 ), tableElement );
39
-
40
- let tableWidget;
41
-
42
- if ( asWidget ) {
43
- tableWidget = toTableWidget( figureElement, conversionApi.writer );
35
+ // Table body slot.
36
+ if ( headingRows < tableUtils.getRows( table ) ) {
37
+ tableSections.push(
38
+ writer.createContainerElement( 'tbody', null,
39
+ writer.createSlot( element => element.is( 'element', 'tableRow' ) && element.index >= headingRows )
40
+ )
41
+ );
44
42
  }
45
43
 
46
- const tableWalker = new TableWalker( table );
47
-
48
- const tableAttributes = {
49
- headingRows: table.getAttribute( 'headingRows' ) || 0,
50
- headingColumns: table.getAttribute( 'headingColumns' ) || 0
51
- };
52
-
53
- // Cache for created table rows.
54
- const viewRows = new Map();
44
+ const figureElement = writer.createContainerElement( 'figure', { class: 'table' }, [
45
+ // Table with proper sections (thead, tbody).
46
+ writer.createContainerElement( 'table', null, tableSections ),
55
47
 
56
- for ( const tableSlot of tableWalker ) {
57
- const { row, cell } = tableSlot;
58
-
59
- const tableRow = table.getChild( row );
60
- const trElement = viewRows.get( row ) || createTr( tableElement, tableRow, row, tableAttributes, conversionApi );
61
- viewRows.set( row, trElement );
62
-
63
- // Consume table cell - it will be always consumed as we convert whole table at once.
64
- conversionApi.consumable.consume( cell, 'insert' );
48
+ // Slot for the rest (for example caption).
49
+ writer.createSlot( element => !element.is( 'element', 'tableRow' ) )
50
+ ] );
65
51
 
66
- const insertPosition = conversionApi.writer.createPositionAt( trElement, 'end' );
67
-
68
- createViewTableCellElement( tableSlot, tableAttributes, insertPosition, conversionApi, options );
69
- }
70
-
71
- // Insert empty TR elements if there are any rows without anchored cells. Since the model is always normalized
72
- // this can happen only in the document fragment that only part of the table is down-casted.
73
- for ( const tableRow of table.getChildren() ) {
74
- const rowIndex = tableRow.index;
75
-
76
- // Make sure that this is a table row and not some other element (i.e., caption).
77
- if ( tableRow.is( 'element', 'tableRow' ) && !viewRows.has( rowIndex ) ) {
78
- viewRows.set( rowIndex, createTr( tableElement, tableRow, rowIndex, tableAttributes, conversionApi ) );
79
- }
80
- }
81
-
82
- const viewPosition = conversionApi.mapper.toViewPosition( data.range.start );
83
-
84
- conversionApi.mapper.bindElements( table, asWidget ? tableWidget : figureElement );
85
- conversionApi.writer.insert( viewPosition, asWidget ? tableWidget : figureElement );
86
- } );
52
+ return options.asWidget ? toTableWidget( figureElement, writer ) : figureElement;
53
+ };
87
54
  }
88
55
 
89
56
  /**
90
- * Model row element to view `<tr>` element conversion helper.
91
- *
92
- * This conversion helper creates the whole `<tr>` element with child elements.
57
+ * Model table row element to view `<tr>` element conversion helper.
93
58
  *
94
- * @returns {Function} Conversion helper.
59
+ * @returns {Function} Element creator.
95
60
  */
96
- export function downcastInsertRow() {
97
- return dispatcher => dispatcher.on( 'insert:tableRow', ( evt, data, conversionApi ) => {
98
- const tableRow = data.item;
99
-
100
- if ( !conversionApi.consumable.consume( tableRow, 'insert' ) ) {
101
- return;
102
- }
103
-
104
- const table = tableRow.parent;
105
-
106
- const figureElement = conversionApi.mapper.toViewElement( table );
107
- const tableElement = getViewTable( figureElement );
108
-
109
- const row = table.getChildIndex( tableRow );
110
-
111
- const tableWalker = new TableWalker( table, { row } );
112
-
113
- const tableAttributes = {
114
- headingRows: table.getAttribute( 'headingRows' ) || 0,
115
- headingColumns: table.getAttribute( 'headingColumns' ) || 0
116
- };
117
-
118
- // Cache for created table rows.
119
- const viewRows = new Map();
120
-
121
- for ( const tableSlot of tableWalker ) {
122
- const trElement = viewRows.get( row ) || createTr( tableElement, tableRow, row, tableAttributes, conversionApi );
123
- viewRows.set( row, trElement );
124
-
125
- // Consume table cell - it will be always consumed as we convert whole row at once.
126
- conversionApi.consumable.consume( tableSlot.cell, 'insert' );
127
-
128
- const insertPosition = conversionApi.writer.createPositionAt( trElement, 'end' );
129
-
130
- createViewTableCellElement( tableSlot, tableAttributes, insertPosition, conversionApi, { asWidget: true } );
131
- }
132
- } );
61
+ export function downcastRow() {
62
+ return ( tableRow, { writer } ) => {
63
+ return tableRow.isEmpty ?
64
+ writer.createEmptyElement( 'tr' ) :
65
+ writer.createContainerElement( 'tr' );
66
+ };
133
67
  }
134
68
 
135
69
  /**
@@ -138,129 +72,65 @@ export function downcastInsertRow() {
138
72
  * This conversion helper will create proper `<th>` elements for table cells that are in the heading section (heading row or column)
139
73
  * and `<td>` otherwise.
140
74
  *
141
- * @returns {Function} Conversion helper.
75
+ * @param {Object} [options]
76
+ * @param {Boolean} [options.asWidget] If set to `true`, the downcast conversion will produce a widget.
77
+ * @returns {Function} Element creator.
142
78
  */
143
- export function downcastInsertCell() {
144
- return dispatcher => dispatcher.on( 'insert:tableCell', ( evt, data, conversionApi ) => {
145
- const tableCell = data.item;
146
-
147
- if ( !conversionApi.consumable.consume( tableCell, 'insert' ) ) {
148
- return;
149
- }
150
-
79
+ export function downcastCell( options = {} ) {
80
+ return ( tableCell, { writer } ) => {
151
81
  const tableRow = tableCell.parent;
152
82
  const table = tableRow.parent;
153
83
  const rowIndex = table.getChildIndex( tableRow );
154
84
 
155
85
  const tableWalker = new TableWalker( table, { row: rowIndex } );
86
+ const headingRows = table.getAttribute( 'headingRows' ) || 0;
87
+ const headingColumns = table.getAttribute( 'headingColumns' ) || 0;
156
88
 
157
- const tableAttributes = {
158
- headingRows: table.getAttribute( 'headingRows' ) || 0,
159
- headingColumns: table.getAttribute( 'headingColumns' ) || 0
160
- };
161
-
162
- // We need to iterate over a table in order to get proper row & column values from a walker
89
+ // We need to iterate over a table in order to get proper row & column values from a walker.
163
90
  for ( const tableSlot of tableWalker ) {
164
- if ( tableSlot.cell === tableCell ) {
165
- const trElement = conversionApi.mapper.toViewElement( tableRow );
166
- const insertPosition = conversionApi.writer.createPositionAt( trElement, tableRow.getChildIndex( tableCell ) );
91
+ if ( tableSlot.cell == tableCell ) {
92
+ const isHeading = tableSlot.row < headingRows || tableSlot.column < headingColumns;
93
+ const cellElementName = isHeading ? 'th' : 'td';
167
94
 
168
- createViewTableCellElement( tableSlot, tableAttributes, insertPosition, conversionApi, { asWidget: true } );
169
-
170
- // No need to iterate further.
171
- return;
95
+ return options.asWidget ?
96
+ toWidgetEditable( writer.createEditableElement( cellElementName ), writer ) :
97
+ writer.createContainerElement( cellElementName );
172
98
  }
173
99
  }
174
- } );
175
- }
176
-
177
- /**
178
- * Conversion helper that acts on heading column table attribute change.
179
- *
180
- * Depending on changed attributes this converter will rename `<td` to `<th>` elements or vice versa depending on the cell column index.
181
- *
182
- * @returns {Function} Conversion helper.
183
- */
184
- export function downcastTableHeadingColumnsChange() {
185
- return dispatcher => dispatcher.on( 'attribute:headingColumns:table', ( evt, data, conversionApi ) => {
186
- const table = data.item;
187
-
188
- if ( !conversionApi.consumable.consume( data.item, evt.name ) ) {
189
- return;
190
- }
191
-
192
- const tableAttributes = {
193
- headingRows: table.getAttribute( 'headingRows' ) || 0,
194
- headingColumns: table.getAttribute( 'headingColumns' ) || 0
195
- };
196
-
197
- const oldColumns = data.attributeOldValue;
198
- const newColumns = data.attributeNewValue;
199
-
200
- const lastColumnToCheck = ( oldColumns > newColumns ? oldColumns : newColumns ) - 1;
201
-
202
- for ( const tableSlot of new TableWalker( table, { endColumn: lastColumnToCheck } ) ) {
203
- renameViewTableCellIfRequired( tableSlot, tableAttributes, conversionApi );
204
- }
205
- } );
206
- }
207
-
208
- /**
209
- * Conversion helper that acts on a removed row.
210
- *
211
- * @returns {Function} Conversion helper.
212
- */
213
- export function downcastRemoveRow() {
214
- return dispatcher => dispatcher.on( 'remove:tableRow', ( evt, data, conversionApi ) => {
215
- // Prevent default remove converter.
216
- evt.stop();
217
- const viewWriter = conversionApi.writer;
218
- const mapper = conversionApi.mapper;
219
-
220
- const viewStart = mapper.toViewPosition( data.position ).getLastMatchingPosition( value => !value.item.is( 'element', 'tr' ) );
221
- const viewItem = viewStart.nodeAfter;
222
- const tableSection = viewItem.parent;
223
- const viewTable = tableSection.parent;
224
-
225
- // Remove associated <tr> from the view.
226
- const removeRange = viewWriter.createRangeOn( viewItem );
227
- const removed = viewWriter.remove( removeRange );
228
-
229
- for ( const child of viewWriter.createRangeIn( removed ).getItems() ) {
230
- mapper.unbindViewElement( child );
231
- }
232
-
233
- // Cleanup: Ensure that thead & tbody sections are removed if left empty after removing rows. See #6437, #6391.
234
- removeTableSectionIfEmpty( 'thead', viewTable, conversionApi );
235
- removeTableSectionIfEmpty( 'tbody', viewTable, conversionApi );
236
- }, { priority: 'higher' } );
100
+ };
237
101
  }
238
102
 
239
103
  /**
240
104
  * Overrides paragraph inside table cell conversion.
241
105
  *
242
106
  * This converter:
243
- * * should be used to override default paragraph conversion in the editing view.
244
- * * It will only convert <paragraph> placed directly inside <tableCell>.
107
+ * * should be used to override default paragraph conversion.
108
+ * * It will only convert `<paragraph>` placed directly inside `<tableCell>`.
245
109
  * * For a single paragraph without attributes it returns `<span>` to simulate data table.
246
110
  * * For all other cases it returns `<p>` element.
247
111
  *
248
- * @param {module:engine/model/element~Element} modelElement
249
- * @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi
250
- * @returns {module:engine/view/containerelement~ContainerElement|undefined}
112
+ * @param {Object} [options]
113
+ * @param {Boolean} [options.asWidget] If set to `true`, the downcast conversion will produce a widget.
114
+ * @returns {Function} Element creator.
251
115
  */
252
- export function convertParagraphInTableCell( modelElement, conversionApi ) {
253
- const { writer } = conversionApi;
116
+ export function convertParagraphInTableCell( options = {} ) {
117
+ return ( modelElement, { writer, consumable, mapper } ) => {
118
+ if ( !modelElement.parent.is( 'element', 'tableCell' ) ) {
119
+ return;
120
+ }
254
121
 
255
- if ( !modelElement.parent.is( 'element', 'tableCell' ) ) {
256
- return;
257
- }
122
+ if ( !isSingleParagraphWithoutAttributes( modelElement ) ) {
123
+ return;
124
+ }
258
125
 
259
- if ( isSingleParagraphWithoutAttributes( modelElement ) ) {
260
- return writer.createContainerElement( 'span', { class: 'ck-table-bogus-paragraph' } );
261
- } else {
262
- return writer.createContainerElement( 'p' );
263
- }
126
+ if ( options.asWidget ) {
127
+ return writer.createContainerElement( 'span', { class: 'ck-table-bogus-paragraph' } );
128
+ } else {
129
+ // Additional requirement for data pipeline to have backward compatible data tables.
130
+ consumable.consume( modelElement, 'insert' );
131
+ mapper.bindElements( modelElement, mapper.toViewElement( modelElement.parent ) );
132
+ }
133
+ };
264
134
  }
265
135
 
266
136
  /**
@@ -277,7 +147,7 @@ export function convertParagraphInTableCell( modelElement, conversionApi ) {
277
147
  export function isSingleParagraphWithoutAttributes( modelElement ) {
278
148
  const tableCell = modelElement.parent;
279
149
 
280
- const isSingleParagraph = tableCell.childCount === 1;
150
+ const isSingleParagraph = tableCell.childCount == 1;
281
151
 
282
152
  return isSingleParagraph && !hasAnyAttribute( modelElement );
283
153
  }
@@ -296,207 +166,6 @@ function toTableWidget( viewElement, writer ) {
296
166
  return toWidget( viewElement, writer, { hasSelectionHandle: true } );
297
167
  }
298
168
 
299
- // Renames an existing table cell in the view to a given element name.
300
- //
301
- // **Note** This method will not do anything if a view table cell has not been converted yet.
302
- //
303
- // @param {module:engine/model/element~Element} tableCell
304
- // @param {String} desiredCellElementName
305
- // @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi
306
- function renameViewTableCell( tableCell, desiredCellElementName, conversionApi ) {
307
- const viewWriter = conversionApi.writer;
308
- const viewCell = conversionApi.mapper.toViewElement( tableCell );
309
-
310
- const editable = viewWriter.createEditableElement( desiredCellElementName, viewCell.getAttributes() );
311
- const renamedCell = toWidgetEditable( editable, viewWriter );
312
-
313
- viewWriter.insert( viewWriter.createPositionAfter( viewCell ), renamedCell );
314
- viewWriter.move( viewWriter.createRangeIn( viewCell ), viewWriter.createPositionAt( renamedCell, 0 ) );
315
- viewWriter.remove( viewWriter.createRangeOn( viewCell ) );
316
-
317
- conversionApi.mapper.unbindViewElement( viewCell );
318
- conversionApi.mapper.bindElements( tableCell, renamedCell );
319
- }
320
-
321
- // Renames a table cell element in the view according to its location in the table.
322
- //
323
- // @param {module:table/tablewalker~TableSlot} tableSlot
324
- // @param {{headingColumns, headingRows}} tableAttributes
325
- // @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi
326
- function renameViewTableCellIfRequired( tableSlot, tableAttributes, conversionApi ) {
327
- const { cell } = tableSlot;
328
-
329
- // Check whether current columnIndex is overlapped by table cells from previous rows.
330
- const desiredCellElementName = getCellElementName( tableSlot, tableAttributes );
331
-
332
- const viewCell = conversionApi.mapper.toViewElement( cell );
333
-
334
- // If in single change we're converting attribute changes and inserting cell the table cell might not be inserted into view
335
- // because of child conversion is done after parent.
336
- if ( viewCell && viewCell.name !== desiredCellElementName ) {
337
- renameViewTableCell( cell, desiredCellElementName, conversionApi );
338
- }
339
- }
340
-
341
- // Creates a table cell element in the view.
342
- //
343
- // @param {module:table/tablewalker~TableSlot} tableSlot
344
- // @param {module:engine/view/position~Position} insertPosition
345
- // @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi
346
- function createViewTableCellElement( tableSlot, tableAttributes, insertPosition, conversionApi, options ) {
347
- const asWidget = options && options.asWidget;
348
- const cellElementName = getCellElementName( tableSlot, tableAttributes );
349
-
350
- const cellElement = asWidget ?
351
- toWidgetEditable( conversionApi.writer.createEditableElement( cellElementName ), conversionApi.writer ) :
352
- conversionApi.writer.createContainerElement( cellElementName );
353
-
354
- const tableCell = tableSlot.cell;
355
-
356
- const firstChild = tableCell.getChild( 0 );
357
- const isSingleParagraph = tableCell.childCount === 1 && firstChild.name === 'paragraph';
358
-
359
- conversionApi.writer.insert( insertPosition, cellElement );
360
-
361
- conversionApi.mapper.bindElements( tableCell, cellElement );
362
-
363
- // Additional requirement for data pipeline to have backward compatible data tables.
364
- if ( !asWidget && isSingleParagraph && !hasAnyAttribute( firstChild ) ) {
365
- const innerParagraph = tableCell.getChild( 0 );
366
-
367
- conversionApi.consumable.consume( innerParagraph, 'insert' );
368
-
369
- conversionApi.mapper.bindElements( innerParagraph, cellElement );
370
- }
371
- }
372
-
373
- // Creates a `<tr>` view element.
374
- //
375
- // @param {module:engine/view/element~Element} tableElement
376
- // @param {module:engine/model/element~Element} tableRow
377
- // @param {Number} rowIndex
378
- // @param {{headingColumns, headingRows}} tableAttributes
379
- // @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi
380
- // @returns {module:engine/view/element~Element}
381
- function createTr( tableElement, tableRow, rowIndex, tableAttributes, conversionApi ) {
382
- // Will always consume since we're converting <tableRow> element from a parent <table>.
383
- conversionApi.consumable.consume( tableRow, 'insert' );
384
-
385
- const trElement = tableRow.isEmpty ?
386
- conversionApi.writer.createEmptyElement( 'tr' ) :
387
- conversionApi.writer.createContainerElement( 'tr' );
388
-
389
- conversionApi.mapper.bindElements( tableRow, trElement );
390
-
391
- const headingRows = tableAttributes.headingRows;
392
- const tableSection = getOrCreateTableSection( getSectionName( rowIndex, tableAttributes ), tableElement, conversionApi );
393
-
394
- const offset = headingRows > 0 && rowIndex >= headingRows ? rowIndex - headingRows : rowIndex;
395
- const position = conversionApi.writer.createPositionAt( tableSection, offset );
396
-
397
- conversionApi.writer.insert( position, trElement );
398
-
399
- return trElement;
400
- }
401
-
402
- // Returns `th` for heading cells and `td` for other cells for the current table walker value.
403
- //
404
- // @param {module:table/tablewalker~TableSlot} tableSlot
405
- // @param {{headingColumns, headingRows}} tableAttributes
406
- // @returns {String}
407
- function getCellElementName( tableSlot, tableAttributes ) {
408
- const { row, column } = tableSlot;
409
- const { headingColumns, headingRows } = tableAttributes;
410
-
411
- // Column heading are all tableCells in the first `columnHeading` rows.
412
- const isColumnHeading = headingRows && headingRows > row;
413
-
414
- // So a whole row gets <th> element.
415
- if ( isColumnHeading ) {
416
- return 'th';
417
- }
418
-
419
- // Row heading are tableCells which columnIndex is lower then headingColumns.
420
- const isRowHeading = headingColumns && headingColumns > column;
421
-
422
- return isRowHeading ? 'th' : 'td';
423
- }
424
-
425
- // Returns the table section name for the current table walker value.
426
- //
427
- // @param {Number} row
428
- // @param {{headingColumns, headingRows}} tableAttributes
429
- // @returns {String}
430
- function getSectionName( row, tableAttributes ) {
431
- return row < tableAttributes.headingRows ? 'thead' : 'tbody';
432
- }
433
-
434
- // Creates or returns an existing `<tbody>` or `<thead>` element with caching.
435
- //
436
- // @param {String} sectionName
437
- // @param {module:engine/view/element~Element} viewTable
438
- // @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi
439
- // @param {Object} cachedTableSection An object that stores cached elements.
440
- // @returns {module:engine/view/containerelement~ContainerElement}
441
- function getOrCreateTableSection( sectionName, viewTable, conversionApi ) {
442
- const viewTableSection = getExistingTableSectionElement( sectionName, viewTable );
443
-
444
- return viewTableSection ? viewTableSection : createTableSection( sectionName, viewTable, conversionApi );
445
- }
446
-
447
- // Finds an existing `<tbody>` or `<thead>` element or returns undefined.
448
- //
449
- // @param {String} sectionName
450
- // @param {module:engine/view/element~Element} tableElement
451
- // @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi
452
- function getExistingTableSectionElement( sectionName, tableElement ) {
453
- for ( const tableSection of tableElement.getChildren() ) {
454
- if ( tableSection.name == sectionName ) {
455
- return tableSection;
456
- }
457
- }
458
- }
459
-
460
- // Creates a table section at the end of the table.
461
- //
462
- // @param {String} sectionName
463
- // @param {module:engine/view/element~Element} tableElement
464
- // @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi
465
- // @returns {module:engine/view/containerelement~ContainerElement}
466
- function createTableSection( sectionName, tableElement, conversionApi ) {
467
- const tableChildElement = conversionApi.writer.createContainerElement( sectionName );
468
-
469
- const insertPosition = conversionApi.writer.createPositionAt( tableElement, sectionName == 'tbody' ? 'end' : 0 );
470
-
471
- conversionApi.writer.insert( insertPosition, tableChildElement );
472
-
473
- return tableChildElement;
474
- }
475
-
476
- // Removes an existing `<tbody>` or `<thead>` element if it is empty.
477
- //
478
- // @param {String} sectionName
479
- // @param {module:engine/view/element~Element} tableElement
480
- // @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi
481
- function removeTableSectionIfEmpty( sectionName, tableElement, conversionApi ) {
482
- const tableSection = getExistingTableSectionElement( sectionName, tableElement );
483
-
484
- if ( tableSection && tableSection.childCount === 0 ) {
485
- conversionApi.writer.remove( conversionApi.writer.createRangeOn( tableSection ) );
486
- }
487
- }
488
-
489
- // Finds a '<table>' element inside the `<figure>` widget.
490
- //
491
- // @param {module:engine/view/element~Element} viewFigure
492
- function getViewTable( viewFigure ) {
493
- for ( const child of viewFigure.getChildren() ) {
494
- if ( child.name === 'table' ) {
495
- return child;
496
- }
497
- }
498
- }
499
-
500
169
  // Checks if an element has any attributes set.
501
170
  //
502
171
  // @param {module:engine/model/element~Element element
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -10,7 +10,7 @@
10
10
  import { isSingleParagraphWithoutAttributes } from './downcast';
11
11
 
12
12
  /**
13
- * Injects a table cell post-fixer into the model which marks the table cell in the differ to have it re-rendered.
13
+ * A table cell refresh handler which marks the table cell in the differ to have it re-rendered.
14
14
  *
15
15
  * Model `paragraph` inside a table cell can be rendered as `<span>` or `<p>`. It is rendered as `<span>` if this is the only block
16
16
  * element in that table cell and it does not have any attributes. It is rendered as `<p>` otherwise.
@@ -19,16 +19,12 @@ import { isSingleParagraphWithoutAttributes } from './downcast';
19
19
  * re-rendered so it changes from `<span>` to `<p>`. The easiest way to do it is to re-render the entire table cell.
20
20
  *
21
21
  * @param {module:engine/model/model~Model} model
22
- * @param {module:engine/conversion/mapper~Mapper} mapper
22
+ * @param {module:engine/controller/editingcontroller~EditingController} editing
23
23
  */
24
- export default function injectTableCellRefreshPostFixer( model, mapper ) {
25
- model.document.registerPostFixer( () => tableCellRefreshPostFixer( model.document.differ, mapper ) );
26
- }
24
+ export default function tableCellRefreshHandler( model, editing ) {
25
+ const differ = model.document.differ;
27
26
 
28
- function tableCellRefreshPostFixer( differ, mapper ) {
29
27
  // Stores cells to be refreshed, so the table cell will be refreshed once for multiple changes.
30
-
31
- // 1. Gather all changes inside table cell.
32
28
  const cellsToCheck = new Set();
33
29
 
34
30
  for ( const change of differ.getChanges() ) {
@@ -39,20 +35,13 @@ function tableCellRefreshPostFixer( differ, mapper ) {
39
35
  }
40
36
  }
41
37
 
42
- // @if CK_DEBUG_TABLE // console.log( `Post-fixing table: Checking table cell to refresh (${ cellsToCheck.size }).` );
43
- // @if CK_DEBUG_TABLE // let paragraphsRefreshed = 0;
44
-
45
38
  for ( const tableCell of cellsToCheck.values() ) {
46
- for ( const paragraph of [ ...tableCell.getChildren() ].filter( child => shouldRefresh( child, mapper ) ) ) {
47
- // @if CK_DEBUG_TABLE // console.log( `Post-fixing table: refreshing paragraph in table cell (${++paragraphsRefreshed}).` );
48
- differ.refreshItem( paragraph );
39
+ const paragraphsToRefresh = Array.from( tableCell.getChildren() ).filter( child => shouldRefresh( child, editing.mapper ) );
40
+
41
+ for ( const paragraph of paragraphsToRefresh ) {
42
+ editing.reconvertItem( paragraph );
49
43
  }
50
44
  }
51
-
52
- // Always return false to prevent the refresh post-fixer from re-running on the same set of changes and going into an infinite loop.
53
- // This "post-fixer" does not change the model structure so there shouldn't be need to run other post-fixers again.
54
- // See https://github.com/ckeditor/ckeditor5/issues/1936 & https://github.com/ckeditor/ckeditor5/issues/8200.
55
- return false;
56
45
  }
57
46
 
58
47
  // Check if given model element needs refreshing.