@ckeditor/ckeditor5-table 34.2.0 → 35.1.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 (72) hide show
  1. package/CHANGELOG.md +261 -0
  2. package/LICENSE.md +4 -0
  3. package/build/table.js +1 -1
  4. package/build/translations/ar.js +1 -1
  5. package/build/translations/bg.js +1 -1
  6. package/build/translations/bn.js +1 -0
  7. package/build/translations/ca.js +1 -0
  8. package/build/translations/da.js +1 -1
  9. package/build/translations/et.js +1 -1
  10. package/build/translations/fi.js +1 -1
  11. package/build/translations/fr.js +1 -1
  12. package/build/translations/he.js +1 -0
  13. package/build/translations/hi.js +1 -1
  14. package/build/translations/id.js +1 -1
  15. package/build/translations/ja.js +1 -1
  16. package/build/translations/ko.js +1 -1
  17. package/build/translations/lt.js +1 -1
  18. package/build/translations/lv.js +1 -1
  19. package/build/translations/ms.js +1 -0
  20. package/build/translations/nl.js +1 -1
  21. package/build/translations/no.js +1 -1
  22. package/build/translations/pt.js +1 -1
  23. package/build/translations/sv.js +1 -1
  24. package/build/translations/th.js +1 -1
  25. package/build/translations/tr.js +1 -1
  26. package/build/translations/tt.js +1 -0
  27. package/build/translations/uk.js +1 -1
  28. package/build/translations/ur.js +1 -1
  29. package/build/translations/vi.js +1 -1
  30. package/lang/translations/ar.po +11 -11
  31. package/lang/translations/bg.po +43 -43
  32. package/lang/translations/bn.po +263 -0
  33. package/lang/translations/ca.po +261 -0
  34. package/lang/translations/da.po +6 -6
  35. package/lang/translations/et.po +3 -3
  36. package/lang/translations/fi.po +41 -41
  37. package/lang/translations/fr.po +3 -3
  38. package/lang/translations/he.po +261 -0
  39. package/lang/translations/hi.po +3 -3
  40. package/lang/translations/id.po +21 -21
  41. package/lang/translations/ja.po +45 -45
  42. package/lang/translations/ko.po +60 -60
  43. package/lang/translations/lt.po +43 -43
  44. package/lang/translations/lv.po +2 -2
  45. package/lang/translations/ms.po +261 -0
  46. package/lang/translations/nl.po +25 -25
  47. package/lang/translations/no.po +3 -3
  48. package/lang/translations/pt.po +59 -59
  49. package/lang/translations/sv.po +56 -56
  50. package/lang/translations/th.po +42 -42
  51. package/lang/translations/tr.po +3 -3
  52. package/lang/translations/tt.po +261 -0
  53. package/lang/translations/uk.po +3 -3
  54. package/lang/translations/ur.po +9 -9
  55. package/lang/translations/vi.po +28 -28
  56. package/package.json +27 -26
  57. package/src/tablecaption/tablecaptionui.js +2 -0
  58. package/src/tablecellproperties/ui/tablecellpropertiesview.js +2 -2
  59. package/src/tablecolumnresize/constants.js +0 -2
  60. package/src/tablecolumnresize/converters.js +29 -28
  61. package/src/tablecolumnresize/tablecolumnresizeediting.js +399 -425
  62. package/src/tablecolumnresize/tablecolumnwidthscommand.js +55 -0
  63. package/src/tablecolumnresize/tablewidthresizecommand.js +60 -0
  64. package/src/tablecolumnresize/utils.js +131 -167
  65. package/src/tablecolumnresize.js +2 -2
  66. package/src/tableproperties/ui/tablepropertiesview.js +1 -1
  67. package/src/tableui.js +6 -8
  68. package/src/ui/colorinputview.js +2 -1
  69. package/src/ui/inserttableview.js +81 -14
  70. package/src/utils/ui/contextualballoon.js +3 -3
  71. package/src/utils/ui/widget.js +7 -1
  72. package/theme/tablecolumnresize.css +13 -10
@@ -7,16 +7,18 @@
7
7
  * @module table/tablecolumnresize/tablecolumnresizeediting
8
8
  */
9
9
 
10
- /* istanbul ignore file */
11
-
12
10
  import { throttle } from 'lodash-es';
13
11
  import { global, DomEmitterMixin } from 'ckeditor5/src/utils';
14
12
  import { Plugin } from 'ckeditor5/src/core';
15
13
 
16
14
  import MouseEventsObserver from '../../src/tablemouse/mouseeventsobserver';
17
15
  import TableEditing from '../tableediting';
16
+ import TableUtils from '../tableutils';
18
17
  import TableWalker from '../tablewalker';
19
18
 
19
+ import TableWidthResizeCommand from './tablewidthresizecommand';
20
+ import TableColumnWidthsCommand from './tablecolumnwidthscommand';
21
+
20
22
  import {
21
23
  upcastColgroupElement,
22
24
  downcastTableColumnWidthsAttribute
@@ -24,20 +26,17 @@ import {
24
26
 
25
27
  import {
26
28
  clamp,
27
- fillArray,
29
+ createFilledArray,
28
30
  sumArray,
29
- getAffectedTables,
30
- getColumnIndex,
31
- getColumnWidthsInPixels,
31
+ getColumnEdgesIndexes,
32
+ getChangedResizedTables,
32
33
  getColumnMinWidthAsPercentage,
33
34
  getElementWidthInPixels,
34
35
  getTableWidthInPixels,
35
- getNumberOfColumn,
36
- isTableRendered,
37
- normalizeColumnWidthsAttribute,
36
+ normalizeColumnWidths,
38
37
  toPrecision,
39
- insertColumnResizerElements,
40
- removeColumnResizerElements
38
+ ensureColumnResizerElement,
39
+ getDomCellOuterWidth
41
40
  } from './utils';
42
41
 
43
42
  import { COLUMN_MIN_WIDTH_IN_PIXELS } from './constants';
@@ -52,7 +51,7 @@ export default class TableColumnResizeEditing extends Plugin {
52
51
  * @inheritDoc
53
52
  */
54
53
  static get requires() {
55
- return [ TableEditing ];
54
+ return [ TableEditing, TableUtils ];
56
55
  }
57
56
 
58
57
  /**
@@ -77,13 +76,14 @@ export default class TableColumnResizeEditing extends Plugin {
77
76
  this._isResizingActive = false;
78
77
 
79
78
  /**
80
- * A flag indicating if the column resizing is allowed. It is not allowed if the editor is in read-only mode or the
81
- * `TableColumnResize` plugin is disabled.
79
+ * A flag indicating if the column resizing is allowed. It is not allowed if the editor is in read-only
80
+ * or comments-only mode or the `TableColumnResize` plugin is disabled.
82
81
  *
83
82
  * @private
83
+ * @observable
84
84
  * @member {Boolean}
85
85
  */
86
- this._isResizingAllowed = true;
86
+ this.set( '_isResizingAllowed', true );
87
87
 
88
88
  /**
89
89
  * A temporary storage for the required data needed to correctly calculate the widths of the resized columns. This storage is
@@ -95,22 +95,27 @@ export default class TableColumnResizeEditing extends Plugin {
95
95
  this._resizingData = null;
96
96
 
97
97
  /**
98
- * Internal map to store reference between a cell and its columnIndex. This information is required in postfixer to properly
99
- * recognize if the cell was inserted or deleted.
98
+ * DOM emitter.
100
99
  *
101
100
  * @private
102
- * @member {Map}
101
+ * @member {DomEmitterMixin}
103
102
  */
104
- this._columnIndexMap = new Map();
103
+ this._domEmitter = Object.create( DomEmitterMixin );
105
104
 
106
105
  /**
107
- * Internal map to store reference between a cell and operation that was performed on it (insert/remove). This is required
108
- * in order to add/remove resizers based on operation performed (which is done on 'render').
106
+ * A local reference to the {@link module:table/tableutils~TableUtils} plugin.
109
107
  *
110
108
  * @private
111
- * @member {Map}
109
+ * @member {module:table/tableutils~TableUtils}
112
110
  */
113
- this._cellsModified = new Map();
111
+ this._tableUtilsPlugin = editor.plugins.get( 'TableUtils' );
112
+
113
+ this.on( 'change:_isResizingAllowed', ( evt, name, value ) => {
114
+ // Toggling the `ck-column-resize_disabled` class shows and hides the resizers through CSS.
115
+ editor.editing.view.change( writer => {
116
+ writer[ value ? 'removeClass' : 'addClass' ]( 'ck-column-resize_disabled', editor.editing.view.document.getRoot() );
117
+ } );
118
+ } );
114
119
  }
115
120
 
116
121
  /**
@@ -118,362 +123,239 @@ export default class TableColumnResizeEditing extends Plugin {
118
123
  */
119
124
  init() {
120
125
  this._extendSchema();
121
- this._setupConversion();
122
- this._setupPostFixer();
123
- this._setupColumnResizers();
126
+ this._registerPostFixer();
127
+ this._registerConverters();
128
+ this._registerResizingListeners();
124
129
  this._registerColgroupFixer();
125
130
  this._registerResizerInserter();
126
131
 
127
132
  const editor = this.editor;
128
133
  const columnResizePlugin = editor.plugins.get( 'TableColumnResize' );
129
134
 
135
+ editor.commands.add( 'resizeTableWidth', new TableWidthResizeCommand( editor ) );
136
+ editor.commands.add( 'resizeColumnWidths', new TableColumnWidthsCommand( editor ) );
137
+
138
+ const resizeTableWidthCommand = editor.commands.get( 'resizeTableWidth' );
139
+ const resizeColumnWidthsCommand = editor.commands.get( 'resizeColumnWidths' );
140
+
141
+ // Currently the states of column resize and table resize (which is actually the last column resize) features
142
+ // are bound together. They can be separated in the future by adding distinct listeners and applying
143
+ // different CSS classes (e.g. `ck-column-resize_disabled` and `ck-table-resize_disabled`) to the editor root.
144
+ // See #12148 for the details.
130
145
  this.bind( '_isResizingAllowed' ).to(
131
146
  editor, 'isReadOnly',
132
147
  columnResizePlugin, 'isEnabled',
133
- ( isEditorReadOnly, isPluginEnabled ) => !isEditorReadOnly && isPluginEnabled
148
+ resizeTableWidthCommand, 'isEnabled',
149
+ resizeColumnWidthsCommand, 'isEnabled',
150
+ ( isEditorReadOnly, isPluginEnabled, isResizeTableWidthCommandEnabled, isResizeColumnWidthsCommandEnabled ) =>
151
+ !isEditorReadOnly && isPluginEnabled && isResizeTableWidthCommandEnabled && isResizeColumnWidthsCommandEnabled
134
152
  );
135
153
  }
136
154
 
137
155
  /**
138
- * Registers new attributes for a table and a table cell model elements.
139
- *
140
- * @private
156
+ * @inheritDoc
141
157
  */
142
- _extendSchema() {
143
- const editor = this.editor;
144
- const schema = editor.model.schema;
145
-
146
- schema.extend( 'table', {
147
- allowAttributes: [ 'tableWidth', 'columnWidths' ]
148
- } );
158
+ destroy() {
159
+ this._domEmitter.stopListening();
160
+ super.destroy();
149
161
  }
150
162
 
151
163
  /**
152
- * Registers table column resizer converters.
164
+ * Registers new attributes for a table model element.
153
165
  *
154
166
  * @private
155
167
  */
156
- _setupConversion() {
157
- const editor = this.editor;
158
- const conversion = editor.conversion;
159
-
160
- conversion.for( 'upcast' ).attributeToAttribute( {
161
- view: {
162
- name: 'figure',
163
- key: 'style',
164
- value: {
165
- width: /[\s\S]+/
166
- }
167
- },
168
- model: {
169
- name: 'table',
170
- key: 'tableWidth',
171
- value: viewElement => viewElement.getStyle( 'width' )
172
- }
173
- } );
174
-
175
- conversion.for( 'downcast' ).attributeToAttribute( {
176
- model: {
177
- name: 'table',
178
- key: 'tableWidth'
179
- },
180
- view: width => ( {
181
- name: 'figure',
182
- key: 'style',
183
- value: {
184
- width
185
- }
186
- } )
168
+ _extendSchema() {
169
+ this.editor.model.schema.extend( 'table', {
170
+ allowAttributes: [ 'tableWidth', 'columnWidths' ]
187
171
  } );
188
-
189
- conversion.for( 'upcast' ).add( upcastColgroupElement( editor ) );
190
- conversion.for( 'downcast' ).add( downcastTableColumnWidthsAttribute() );
191
172
  }
192
173
 
193
174
  /**
194
- * Registers table column resizer post-fixer.
175
+ * Registers table column resize post-fixer.
195
176
  *
196
- * It checks if the change from the differ concerns a table-related element or an attribute. If yes, then it is responsible for the
197
- * following:
198
- * (1) Depending on whether the `enableResize` event is not prevented...
199
- * (1.1) ...removing the `columnWidths` attribute from the table and all the cells from column index map, or
200
- * (1.2) ...adding the `columnWidths` attribute to the table.
201
- * (2) Adjusting the `columnWidths` attribute to guarantee that the sum of the widths from all columns is 100%.
202
- * (2.1) Add all cells to column index map with its column index (to properly handle column insertion and deletion).
203
- * (3) Checking if columns have been added or removed...
204
- * (3.1) ... in the middle of the table, or
205
- * (3.2) ... at the table end.
206
- * (4) Checking if the inline cell width has been configured and transferring its value to the appropriate column, but currently only
207
- * for a cell that is not spanned horizontally.
177
+ * It checks if the change from the differ concerns a table-related element or attribute. For detected changes it:
178
+ * * Adjusts the `columnWidths` attribute to guarantee that the sum of the widths from all columns is 100%.
179
+ * * Checks if the `columnWidths` attribute gets updated accordingly after columns have been added or removed.
208
180
  *
209
181
  * @private
210
182
  */
211
- _setupPostFixer() {
183
+ _registerPostFixer() {
212
184
  const editor = this.editor;
213
- const columnIndexMap = this._columnIndexMap;
214
- const cellsModified = this._cellsModified;
215
-
216
- editor.model.document.registerPostFixer( writer => {
217
- const changes = editor.model.document.differ.getChanges();
185
+ const model = editor.model;
218
186
 
187
+ model.document.registerPostFixer( writer => {
219
188
  let changed = false;
220
189
 
221
- for ( const table of getAffectedTables( changes, editor.model ) ) {
222
- // (1.1) Remove the `columnWidths` attribute from the table and all the cells from column index map if the
223
- // manual width is not allowed for a given cell. There is no need to process the given table anymore.
224
- if ( this.fire( 'disableResize', table ) ) {
225
- if ( table.hasAttribute( 'columnWidths' ) ) {
226
- writer.removeAttribute( 'columnWidths', table );
190
+ for ( const table of getChangedResizedTables( model ) ) {
191
+ // (1) Adjust the `columnWidths` attribute to guarantee that the sum of the widths from all columns is 100%.
192
+ const columnWidths = normalizeColumnWidths( table.getAttribute( 'columnWidths' ).split( ',' ) );
227
193
 
228
- for ( const { cell } of new TableWalker( table ) ) {
229
- columnIndexMap.delete( cell );
230
- cellsModified.set( cell, 'remove' );
231
- }
194
+ // (2) If the number of columns has changed, then we need to adjust the widths of the affected columns.
195
+ adjustColumnWidths( columnWidths, table, this );
232
196
 
233
- changed = true;
234
- }
197
+ const columnWidthsAttribute = columnWidths.map( width => `${ width }%` ).join( ',' );
235
198
 
199
+ if ( table.getAttribute( 'columnWidths' ) === columnWidthsAttribute ) {
236
200
  continue;
237
201
  }
238
202
 
239
- // (1.2) Add the `columnWidths` attribute to the table with the 'auto' special value for each column, what means that it is
240
- // calculated proportionally to the whole table width.
241
- const numberOfColumns = getNumberOfColumn( table, editor );
242
-
243
- if ( !table.hasAttribute( 'columnWidths' ) ) {
244
- const columnWidthsAttribute = fillArray( numberOfColumns, 'auto' ).join( ',' );
245
-
246
- writer.setAttribute( 'columnWidths', columnWidthsAttribute, table );
247
-
248
- changed = true;
249
- }
250
-
251
- // (2) Adjust the `columnWidths` attribute to guarantee that the sum of the widths from all columns is 100%.
252
- const columnWidths = normalizeColumnWidthsAttribute( table.getAttribute( 'columnWidths' ) );
253
-
254
- let removedColumnWidths = null;
255
- let isColumnInsertionHandled = false;
256
- let isColumnDeletionHandled = false;
257
-
258
- for ( const { cell, cellWidth: cellColumnWidth, column } of new TableWalker( table ) ) {
259
- // (2.1) Add all cells to column index map with its column index. Do not process the given cell anymore, because the
260
- // `columnIndex` reference in the map is required to properly handle column insertion and deletion.
261
- if ( !columnIndexMap.has( cell ) ) {
262
- columnIndexMap.set( cell, column );
263
- cellsModified.set( cell, 'insert' );
264
-
265
- changed = true;
266
-
267
- continue;
268
- }
269
-
270
- const previousColumn = columnIndexMap.get( cell );
271
-
272
- const isColumnInsertion = previousColumn < column;
273
- const isColumnDeletion = previousColumn > column;
274
-
275
- // (3.1) Handle column insertion and update the `columnIndex` references in column index map for affected cells.
276
- if ( isColumnInsertion ) {
277
- if ( !isColumnInsertionHandled ) {
278
- const columnMinWidthAsPercentage = getColumnMinWidthAsPercentage( table, editor );
279
- const isColumnSwapped = columnIndexMap.get( cell.previousSibling ) === column;
280
- const columnWidthsToInsert = isColumnSwapped ?
281
- removedColumnWidths :
282
- fillArray( column - previousColumn, columnMinWidthAsPercentage );
283
-
284
- columnWidths.splice( previousColumn, 0, ...columnWidthsToInsert );
285
-
286
- isColumnInsertionHandled = true;
287
- }
288
-
289
- columnIndexMap.set( cell, column );
290
- cellsModified.set( cell, 'insert' );
291
-
292
- changed = true;
293
- }
294
-
295
- // (3.1) Handle column deletion and update the `columnIndex` references in column index map for affected cells.
296
- if ( isColumnDeletion ) {
297
- if ( !isColumnDeletionHandled ) {
298
- removedColumnWidths = columnWidths.splice( column, previousColumn - column );
299
-
300
- const isColumnSwapped = cell.nextSibling && columnIndexMap.get( cell.nextSibling ) === column;
301
-
302
- if ( !isColumnSwapped ) {
303
- const columnToExpand = column > 0 ? column - 1 : column;
304
-
305
- columnWidths[ columnToExpand ] += sumArray( removedColumnWidths );
306
- }
307
-
308
- isColumnDeletionHandled = true;
309
- }
310
-
311
- columnIndexMap.set( cell, column );
312
- cellsModified.set( cell, 'insert' );
313
-
314
- changed = true;
315
- }
316
-
317
- // (4) Check if the inline cell width has been configured and transfer its value to the appropriate column.
318
- if ( cell.hasAttribute( 'width' ) ) {
319
- // Currently, only the inline width from the cells that are not horizontally spanned are supported.
320
- if ( cellColumnWidth !== 1 ) {
321
- continue;
322
- }
323
-
324
- // It may happen that the table is not yet fully rendered in the editing view (i.e. it does not contain the
325
- // `<colgroup>` yet), but the cell has an inline width set. In that case it is not possible to properly convert the
326
- // inline cell width as a percentage value to the whole table width. Currently, we just ignore this case and
327
- // initialize the table with all the default (equal) column widths.
328
- if ( !isTableRendered( table, editor ) ) {
329
- writer.removeAttribute( 'width', cell );
330
-
331
- changed = true;
332
-
333
- continue;
334
- }
335
-
336
- const tableWidthInPixels = getTableWidthInPixels( table, editor );
337
- const columnWidthsInPixels = getColumnWidthsInPixels( table, editor );
338
- const columnMinWidthAsPercentage = getColumnMinWidthAsPercentage( table, editor );
339
-
340
- const cellWidth = parseFloat( cell.getAttribute( 'width' ) );
341
-
342
- const isWidthInPixels = cell.getAttribute( 'width' ).endsWith( 'px' );
343
- const isWidthAsPercentage = cell.getAttribute( 'width' ).endsWith( '%' );
344
-
345
- // Currently, only inline width in pixels or as percentage is supported.
346
- if ( !isWidthInPixels && !isWidthAsPercentage ) {
347
- continue;
348
- }
349
-
350
- const isRightEdge = !cell.nextSibling;
351
-
352
- if ( isRightEdge ) {
353
- const rootWidthInPixels = getElementWidthInPixels( editor.editing.view.getDomRoot() );
354
- const lastColumnIndex = numberOfColumns - 1;
355
- const lastColumnWidthInPixels = columnWidthsInPixels[ lastColumnIndex ];
356
-
357
- let tableWidthNew;
358
-
359
- if ( isWidthInPixels ) {
360
- const cellWidthLowerBound = COLUMN_MIN_WIDTH_IN_PIXELS;
361
- const cellWidthUpperBound = rootWidthInPixels - ( tableWidthInPixels - lastColumnWidthInPixels );
362
-
363
- columnWidthsInPixels[ lastColumnIndex ] = clamp( cellWidth, cellWidthLowerBound, cellWidthUpperBound );
364
-
365
- tableWidthNew = sumArray( columnWidthsInPixels );
366
-
367
- // Update all the column widths.
368
- for ( let columnIndex = 0; columnIndex <= lastColumnIndex; columnIndex++ ) {
369
- columnWidths[ columnIndex ] = toPrecision( columnWidthsInPixels[ columnIndex ] * 100 / tableWidthNew );
370
- }
371
- } else {
372
- const cellWidthLowerBound = columnMinWidthAsPercentage;
373
- const cellWidthUpperBound = 100 - ( tableWidthInPixels - lastColumnWidthInPixels ) * 100 /
374
- rootWidthInPixels;
375
-
376
- columnWidths[ lastColumnIndex ] = clamp( cellWidth, cellWidthLowerBound, cellWidthUpperBound );
203
+ writer.setAttribute( 'columnWidths', columnWidthsAttribute, table );
377
204
 
378
- tableWidthNew = ( tableWidthInPixels - lastColumnWidthInPixels ) * 100 /
379
- ( 100 - columnWidths[ lastColumnIndex ] );
205
+ changed = true;
206
+ }
380
207
 
381
- // Update all the column widths, except the last one, which has been already adjusted.
382
- for ( let columnIndex = 0; columnIndex <= lastColumnIndex - 1; columnIndex++ ) {
383
- columnWidths[ columnIndex ] = toPrecision( columnWidthsInPixels[ columnIndex ] * 100 / tableWidthNew );
384
- }
385
- }
208
+ return changed;
209
+ } );
386
210
 
387
- writer.setAttribute( 'width', `${ toPrecision( tableWidthNew * 100 / rootWidthInPixels ) }%`, table );
388
- } else {
389
- const currentColumnWidth = columnWidthsInPixels[ column ];
390
- const nextColumnWidth = columnWidthsInPixels[ column + 1 ];
391
- const bothColumnWidth = currentColumnWidth + nextColumnWidth;
211
+ // Adjusts if necessary the `columnWidths` in case if the number of column has changed.
212
+ //
213
+ // @private
214
+ // @param {Array.<Number>} columnWidths Note: this array **may be modified** by the function.
215
+ // @param {module:engine/model/element~Element} table Table to be checked.
216
+ // @param {module:table/tablecolumnresize/tablecolumnresizeediting~TableColumnResizeEditing} plugin
217
+ function adjustColumnWidths( columnWidths, table, plugin ) {
218
+ const newTableColumnsCount = plugin._tableUtilsPlugin.getColumns( table );
219
+ const columnsCountDelta = newTableColumnsCount - columnWidths.length;
220
+
221
+ if ( columnsCountDelta === 0 ) {
222
+ return;
223
+ }
392
224
 
393
- const cellMaxWidthAsPercentage = ( bothColumnWidth - COLUMN_MIN_WIDTH_IN_PIXELS ) * 100 / tableWidthInPixels;
225
+ // Collect all cells that are affected by the change.
226
+ const cellSet = getAffectedCells( plugin.editor.model.document.differ, table );
394
227
 
395
- let cellWidthAsPercentage = isWidthInPixels ?
396
- cellWidth * 100 / tableWidthInPixels :
397
- cellWidth;
228
+ for ( const cell of cellSet ) {
229
+ const currentColumnsDelta = newTableColumnsCount - columnWidths.length;
398
230
 
399
- cellWidthAsPercentage = clamp( cellWidthAsPercentage, columnMinWidthAsPercentage, cellMaxWidthAsPercentage );
231
+ if ( currentColumnsDelta === 0 ) {
232
+ continue;
233
+ }
400
234
 
401
- const dxAsPercentage = cellWidthAsPercentage - columnWidths[ column ];
235
+ // If the column count in the table changed, adjust the widths of the affected columns.
236
+ const hasMoreColumns = currentColumnsDelta > 0;
237
+ const currentColumnIndex = plugin._tableUtilsPlugin.getCellLocation( cell ).column;
402
238
 
403
- columnWidths[ column ] += dxAsPercentage;
404
- columnWidths[ column + 1 ] -= dxAsPercentage;
405
- }
239
+ if ( hasMoreColumns ) {
240
+ const columnMinWidthAsPercentage = getColumnMinWidthAsPercentage( table, plugin.editor );
241
+ const columnWidthsToInsert = createFilledArray( currentColumnsDelta, columnMinWidthAsPercentage );
406
242
 
407
- writer.removeAttribute( 'width', cell );
243
+ columnWidths.splice( currentColumnIndex, 0, ...columnWidthsToInsert );
244
+ } else {
245
+ // Moves the widths of the removed columns to the preceding one.
246
+ // Other editors either reduce the width of the whole table or adjust the widths
247
+ // proportionally, so change of this behavior can be considered in the future.
248
+ const removedColumnWidths = columnWidths.splice( currentColumnIndex, Math.abs( currentColumnsDelta ) );
408
249
 
409
- changed = true;
410
- }
250
+ columnWidths[ currentColumnIndex ] += sumArray( removedColumnWidths );
411
251
  }
252
+ }
253
+ }
412
254
 
413
- const isColumnInsertionAtEnd = numberOfColumns > columnWidths.length;
414
- const isColumnDeletionAtEnd = numberOfColumns < columnWidths.length;
415
-
416
- // (3.2) Handle column insertion at table end.
417
- if ( isColumnInsertionAtEnd ) {
418
- const columnMinWidthAsPercentage = getColumnMinWidthAsPercentage( table, editor );
419
- const numberOfInsertedColumns = numberOfColumns - columnWidths.length;
420
- const insertedColumnWidths = fillArray( numberOfInsertedColumns, columnMinWidthAsPercentage );
421
-
422
- columnWidths.splice( columnWidths.length, 0, ...insertedColumnWidths );
255
+ // Returns a set of cells that have been changed in a given table.
256
+ //
257
+ // @private
258
+ // @param {module:engine/model/differ~Differ} differ
259
+ // @param {module:engine/model/element~Element} table
260
+ // @returns {Set.<module:engine/model/element~Element>}
261
+ function getAffectedCells( differ, table ) {
262
+ const cellSet = new Set();
263
+
264
+ for ( const change of differ.getChanges() ) {
265
+ if (
266
+ change.type == 'insert' &&
267
+ change.position.nodeAfter &&
268
+ change.position.nodeAfter.name == 'tableCell' &&
269
+ change.position.nodeAfter.getAncestors().includes( table )
270
+ ) {
271
+ cellSet.add( change.position.nodeAfter );
272
+ } else if ( change.type == 'remove' ) {
273
+ // If the first cell was removed, use the node after the change position instead.
274
+ const referenceNode = change.position.nodeBefore || change.position.nodeAfter;
275
+
276
+ if ( referenceNode.name == 'tableCell' && referenceNode.getAncestors().includes( table ) ) {
277
+ cellSet.add( referenceNode );
278
+ }
423
279
  }
280
+ }
424
281
 
425
- // (3.2) Handle column deletion at table end.
426
- if ( isColumnDeletionAtEnd ) {
427
- const removedColumnWidths = columnWidths.splice( numberOfColumns );
282
+ return cellSet;
283
+ }
284
+ }
428
285
 
429
- columnWidths[ numberOfColumns - 1 ] += sumArray( removedColumnWidths );
286
+ /**
287
+ * Registers table column resize converters.
288
+ *
289
+ * @private
290
+ */
291
+ _registerConverters() {
292
+ const editor = this.editor;
293
+ const conversion = editor.conversion;
294
+ const widthStyleToTableWidthDefinition = {
295
+ view: {
296
+ name: 'figure',
297
+ key: 'style',
298
+ value: {
299
+ width: /[\s\S]+/
430
300
  }
431
-
432
- const columnWidthsAttribute = columnWidths.map( width => `${ width }%` ).join( ',' );
433
-
434
- if ( table.getAttribute( 'columnWidths' ) === columnWidthsAttribute ) {
435
- continue;
301
+ },
302
+ model: {
303
+ name: 'table',
304
+ key: 'tableWidth',
305
+ value: viewElement => viewElement.getStyle( 'width' )
306
+ }
307
+ };
308
+ const tableWidthToWidthStyleDefinition = {
309
+ model: {
310
+ name: 'table',
311
+ key: 'tableWidth'
312
+ },
313
+ view: width => ( {
314
+ name: 'figure',
315
+ key: 'style',
316
+ value: {
317
+ width
436
318
  }
319
+ } )
320
+ };
437
321
 
438
- writer.setAttribute( 'columnWidths', columnWidthsAttribute, table );
439
-
440
- changed = true;
441
- }
322
+ conversion.for( 'upcast' ).attributeToAttribute( widthStyleToTableWidthDefinition );
323
+ conversion.for( 'upcast' ).add( upcastColgroupElement( this._tableUtilsPlugin ) );
442
324
 
443
- return changed;
444
- } );
325
+ conversion.for( 'downcast' ).attributeToAttribute( tableWidthToWidthStyleDefinition );
326
+ conversion.for( 'downcast' ).add( downcastTableColumnWidthsAttribute() );
445
327
  }
446
328
 
447
329
  /**
448
- * Initializes column resizing feature by registering mouse event handlers for `mousedown`, `mouseup` and `mousemove` events.
330
+ * Registers listeners to handle resizing process.
449
331
  *
450
332
  * @private
451
333
  */
452
- _setupColumnResizers() {
453
- const editor = this.editor;
454
- const editingView = editor.editing.view;
334
+ _registerResizingListeners() {
335
+ const editingView = this.editor.editing.view;
455
336
 
456
337
  editingView.addObserver( MouseEventsObserver );
457
338
  editingView.document.on( 'mousedown', this._onMouseDownHandler.bind( this ), { priority: 'high' } );
458
339
 
459
- const domEmitter = Object.create( DomEmitterMixin );
460
-
461
- domEmitter.listenTo( global.window.document, 'mouseup', this._onMouseUpHandler.bind( this ) );
462
- domEmitter.listenTo( global.window.document, 'mousemove', throttle( this._onMouseMoveHandler.bind( this ), 50 ) );
340
+ this._domEmitter.listenTo( global.window.document, 'mousemove', throttle( this._onMouseMoveHandler.bind( this ), 50 ) );
341
+ this._domEmitter.listenTo( global.window.document, 'mouseup', this._onMouseUpHandler.bind( this ) );
463
342
  }
464
343
 
465
344
  /**
466
- * Handles the `mousedown` event on column resizer element.
345
+ * Handles the `mousedown` event on column resizer element:
346
+ * * calculates the initial column pixel widths,
347
+ * * inserts the `<colgroup>` element if it is not present in the `<table>`,
348
+ * * puts the necessary data in the temporary storage,
349
+ * * applies the attributes to the `<table>` view element.
467
350
  *
468
351
  * @private
469
- * @param {module:utils/eventinfo~EventInfo} eventInfo
470
- * @param {module:engine/view/observer/domeventdata~DomEventData} domEventData
352
+ * @param {module:utils/eventinfo~EventInfo} eventInfo An object containing information about the fired event.
353
+ * @param {module:engine/view/observer/domeventdata~DomEventData} domEventData The data related to the DOM event.
471
354
  */
472
355
  _onMouseDownHandler( eventInfo, domEventData ) {
473
- const editor = this.editor;
474
- const editingView = editor.editing.view;
356
+ const target = domEventData.target;
475
357
 
476
- if ( !domEventData.target.hasClass( 'table-column-resizer' ) ) {
358
+ if ( !target.hasClass( 'ck-table-column-resizer' ) ) {
477
359
  return;
478
360
  }
479
361
 
@@ -484,101 +366,100 @@ export default class TableColumnResizeEditing extends Plugin {
484
366
  domEventData.preventDefault();
485
367
  eventInfo.stop();
486
368
 
487
- this._isResizingActive = true;
488
- this._resizingData = this._getResizingData( domEventData );
489
-
490
- editingView.change( writer => {
491
- writer.addClass( 'table-column-resizer__active', this._resizingData.elements.viewResizer );
492
- } );
493
- }
494
-
495
- /**
496
- * Handles the `mouseup` event if previously the `mousedown` event was triggered from the column resizer element.
497
- *
498
- * @private
499
- * @param {module:utils/eventinfo~EventInfo} eventInfo
500
- * @param {module:engine/view/observer/domeventdata~DomEventData} domEventData
501
- */
502
- _onMouseUpHandler() {
503
369
  const editor = this.editor;
370
+ const modelTable = editor.editing.mapper.toModelElement( target.findAncestor( 'figure' ) );
371
+
372
+ // The column widths are calculated upon mousedown to allow lazy applying the `columnWidths` attribute on the table.
373
+ const columnWidthsInPx = _calculateDomColumnWidths( modelTable, this._tableUtilsPlugin, editor );
374
+ const viewTable = target.findAncestor( 'table' );
504
375
  const editingView = editor.editing.view;
505
376
 
506
- if ( !this._isResizingActive ) {
507
- return;
377
+ // Insert colgroup for the table that is resized for the first time.
378
+ if ( ![ ...viewTable.getChildren() ].find( viewCol => viewCol.is( 'element', 'colgroup' ) ) ) {
379
+ editingView.change( viewWriter => {
380
+ _insertColgroupElement( viewWriter, columnWidthsInPx, viewTable );
381
+ } );
508
382
  }
509
383
 
510
- const {
511
- modelTable,
512
- viewColgroup,
513
- viewFigure,
514
- viewResizer
515
- } = this._resizingData.elements;
516
-
517
- const columnWidthsAttributeOld = modelTable.getAttribute( 'columnWidths' );
518
- const columnWidthsAttributeNew = [ ...viewColgroup.getChildren() ]
519
- .map( viewCol => viewCol.getStyle( 'width' ) )
520
- .join( ',' );
521
-
522
- const isColumnWidthsAttributeChanged = columnWidthsAttributeOld !== columnWidthsAttributeNew;
523
-
524
- const tableWidthAttributeOld = modelTable.getAttribute( 'tableWidth' );
525
- const tableWidthAttributeNew = viewFigure.getStyle( 'width' );
526
-
527
- const isTableWidthAttributeChanged = tableWidthAttributeOld !== tableWidthAttributeNew;
528
-
529
- if ( isColumnWidthsAttributeChanged || isTableWidthAttributeChanged ) {
530
- if ( this._isResizingAllowed ) {
531
- // Commit all changes to the model.
532
- editor.model.change( writer => {
533
- if ( isColumnWidthsAttributeChanged ) {
534
- writer.setAttribute( 'columnWidths', columnWidthsAttributeNew, modelTable );
535
- }
536
-
537
- if ( isTableWidthAttributeChanged ) {
538
- writer.setAttribute( 'tableWidth', `${ toPrecision( tableWidthAttributeNew ) }%`, modelTable );
539
- }
540
- } );
541
- } else {
542
- // In read-only mode revert all changes in the editing view. The model is not touched so it does not need to be restored.
543
- editingView.change( writer => {
544
- if ( isColumnWidthsAttributeChanged ) {
545
- const columnWidths = columnWidthsAttributeOld.split( ',' );
384
+ this._isResizingActive = true;
385
+ this._resizingData = this._getResizingData( domEventData, columnWidthsInPx );
386
+
387
+ // At this point we change only the editor view - we don't want other users to see our changes yet,
388
+ // so we can't apply them in the model.
389
+ editingView.change( writer => _applyResizingAttributesToTable( writer, viewTable, this._resizingData ) );
390
+
391
+ // Calculates the DOM columns' widths. It is done by taking the width of the widest cell
392
+ // from each table column (we rely on the {@link module:table/tablewalker~TableWalker}
393
+ // to determine which column the cell belongs to).
394
+ //
395
+ // @private
396
+ // @param {module:engine/model/element~Element} modelTable A table which columns should be measured.
397
+ // @param {module:table/tableutils~TableUtils} tableUtils The Table Utils plugin instance.
398
+ // @param {module:core/editor/editor~Editor} editor The editor instance.
399
+ // @returns {Array.<Number>} Columns' widths expressed in pixels (without unit).
400
+ function _calculateDomColumnWidths( modelTable, tableUtilsPlugin, editor ) {
401
+ const columnWidthsInPx = Array( tableUtilsPlugin.getColumns( modelTable ) );
402
+ const tableWalker = new TableWalker( modelTable );
403
+
404
+ for ( const cellSlot of tableWalker ) {
405
+ const viewCell = editor.editing.mapper.toViewElement( cellSlot.cell );
406
+ const domCell = editor.editing.view.domConverter.mapViewToDom( viewCell );
407
+ const domCellWidth = getDomCellOuterWidth( domCell );
408
+
409
+ if ( !columnWidthsInPx[ cellSlot.column ] || domCellWidth < columnWidthsInPx[ cellSlot.column ] ) {
410
+ columnWidthsInPx[ cellSlot.column ] = toPrecision( domCellWidth );
411
+ }
412
+ }
546
413
 
547
- for ( const viewCol of viewColgroup.getChildren() ) {
548
- writer.setStyle( 'width', columnWidths.shift(), viewCol );
549
- }
550
- }
414
+ return columnWidthsInPx;
415
+ }
551
416
 
552
- if ( isTableWidthAttributeChanged ) {
553
- if ( tableWidthAttributeOld ) {
554
- writer.setStyle( 'width', tableWidthAttributeOld, viewFigure );
555
- } else {
556
- writer.removeStyle( 'width', viewFigure );
557
- }
558
- }
559
- } );
417
+ // Creates a `<colgroup>` element with `<col>`s and inserts it into a given view table.
418
+ //
419
+ // @private
420
+ // @param {module:engine/view/downcastwriter~DowncastWriter} viewWriter A writer instance.
421
+ // @param {Array.<Number>} columnWidthsInPx Column widths.
422
+ // @param {module:engine/view/element~Element} viewTable A table view element.
423
+ function _insertColgroupElement( viewWriter, columnWidthsInPx, viewTable ) {
424
+ const colgroup = viewWriter.createContainerElement( 'colgroup' );
425
+
426
+ for ( let i = 0; i < columnWidthsInPx.length; i++ ) {
427
+ const viewColElement = viewWriter.createEmptyElement( 'col' );
428
+ const columnWidthInPc = `${ toPrecision( columnWidthsInPx[ i ] / sumArray( columnWidthsInPx ) * 100 ) }%`;
429
+
430
+ viewWriter.setStyle( 'width', columnWidthInPc, viewColElement );
431
+ viewWriter.insert( viewWriter.createPositionAt( colgroup, 'end' ), viewColElement );
560
432
  }
561
- }
562
433
 
563
- editingView.change( writer => {
564
- writer.removeClass( 'table-column-resizer__active', viewResizer );
565
- } );
434
+ viewWriter.insert( viewWriter.createPositionAt( viewTable, 'start' ), colgroup );
435
+ }
566
436
 
567
- this._isResizingActive = false;
568
- this._resizingData = null;
437
+ // Applies the style and classes to the view table as the resizing begun.
438
+ //
439
+ // @private
440
+ // @param {module:engine/view/downcastwriter~DowncastWriter} viewWriter A writer instance.
441
+ // @param {module:engine/view/element~Element} viewTable A table containing the clicked resizer.
442
+ // @param {Object} resizingData Data related to the resizing.
443
+ function _applyResizingAttributesToTable( viewWriter, viewTable, resizingData ) {
444
+ const figureInitialPcWidth = resizingData.widths.viewFigureWidth / resizingData.widths.viewFigureParentWidth;
445
+
446
+ viewWriter.addClass( 'ck-table-resized', viewTable );
447
+ viewWriter.addClass( 'ck-table-column-resizer__active', resizingData.elements.viewResizer );
448
+ viewWriter.setStyle( 'width', `${ toPrecision( figureInitialPcWidth * 100 ) }%`, viewTable.findAncestor( 'figure' ) );
449
+ }
569
450
  }
570
451
 
571
452
  /**
572
- * Handles the `mousemove` event if previously the `mousedown` event was triggered from the column resizer element.
453
+ * Handles the `mousemove` event.
454
+ * * If resizing process is not in progress, it does nothing.
455
+ * * If resizing is active but not allowed, it stops the resizing process instantly calling the `mousedown` event handler.
456
+ * * Otherwise it dynamically updates the widths of the resized columns.
573
457
  *
574
458
  * @private
575
- * @param {module:utils/eventinfo~EventInfo} eventInfo
576
- * @param {module:engine/view/observer/domeventdata~DomEventData} domEventData
459
+ * @param {module:utils/eventinfo~EventInfo} eventInfo An object containing information about the fired event.
460
+ * @param {Event} mouseEventData The native DOM event.
577
461
  */
578
- _onMouseMoveHandler( eventInfo, domEventData ) {
579
- const editor = this.editor;
580
- const editingView = editor.editing.view;
581
-
462
+ _onMouseMoveHandler( eventInfo, mouseEventData ) {
582
463
  if ( !this._isResizingActive ) {
583
464
  return;
584
465
  }
@@ -593,19 +474,19 @@ export default class TableColumnResizeEditing extends Plugin {
593
474
  columnPosition,
594
475
  flags: {
595
476
  isRightEdge,
596
- isLtrContent,
597
- isTableCentered
477
+ isTableCentered,
478
+ isLtrContent
479
+ },
480
+ elements: {
481
+ viewFigure,
482
+ viewLeftColumn,
483
+ viewRightColumn
598
484
  },
599
485
  widths: {
600
486
  viewFigureParentWidth,
601
487
  tableWidth,
602
488
  leftColumnWidth,
603
489
  rightColumnWidth
604
- },
605
- elements: {
606
- viewFigure,
607
- viewLeftColumn,
608
- viewRightColumn
609
490
  }
610
491
  } = this._resizingData;
611
492
 
@@ -621,7 +502,7 @@ export default class TableColumnResizeEditing extends Plugin {
621
502
  const multiplier = ( isLtrContent ? 1 : -1 ) * ( isRightEdge && isTableCentered ? 2 : 1 );
622
503
 
623
504
  const dx = clamp(
624
- ( domEventData.clientX - columnPosition ) * multiplier,
505
+ ( mouseEventData.clientX - columnPosition ) * multiplier,
625
506
  Math.min( dxLowerBound, 0 ),
626
507
  Math.max( dxUpperBound, 0 )
627
508
  );
@@ -630,7 +511,7 @@ export default class TableColumnResizeEditing extends Plugin {
630
511
  return;
631
512
  }
632
513
 
633
- editingView.change( writer => {
514
+ this.editor.editing.view.change( writer => {
634
515
  const leftColumnWidthAsPercentage = toPrecision( ( leftColumnWidth + dx ) * 100 / tableWidth );
635
516
 
636
517
  writer.setStyle( 'width', `${ leftColumnWidthAsPercentage }%`, viewLeftColumn );
@@ -648,13 +529,110 @@ export default class TableColumnResizeEditing extends Plugin {
648
529
  }
649
530
 
650
531
  /**
651
- * Retrieves and returns required data needed to correctly calculate the widths of the resized columns.
532
+ * Handles the `mouseup` event.
533
+ * * If resizing process is not in progress, it does nothing.
534
+ * * If resizing is active but not allowed, it cancels the resizing process restoring the original widths.
535
+ * * Otherwise it propagates the changes from view to the model by executing the adequate commands.
652
536
  *
653
537
  * @private
654
- * @param {module:engine/view/observer/domeventdata~DomEventData} domEventData
655
- * @returns {Object}
656
538
  */
657
- _getResizingData( domEventData ) {
539
+ _onMouseUpHandler() {
540
+ if ( !this._isResizingActive ) {
541
+ return;
542
+ }
543
+
544
+ const {
545
+ viewResizer,
546
+ modelTable,
547
+ viewFigure,
548
+ viewColgroup
549
+ } = this._resizingData.elements;
550
+
551
+ const editor = this.editor;
552
+ const editingView = editor.editing.view;
553
+
554
+ const columnWidthsAttributeOld = modelTable.getAttribute( 'columnWidths' );
555
+ const columnWidthsAttributeNew = [ ...viewColgroup.getChildren() ]
556
+ .map( viewCol => viewCol.getStyle( 'width' ) )
557
+ .join( ',' );
558
+
559
+ const isColumnWidthsAttributeChanged = columnWidthsAttributeOld !== columnWidthsAttributeNew;
560
+
561
+ const tableWidthAttributeOld = modelTable.getAttribute( 'tableWidth' );
562
+ const tableWidthAttributeNew = viewFigure.getStyle( 'width' );
563
+
564
+ const isTableWidthAttributeChanged = tableWidthAttributeOld !== tableWidthAttributeNew;
565
+
566
+ if ( isColumnWidthsAttributeChanged || isTableWidthAttributeChanged ) {
567
+ if ( this._isResizingAllowed ) {
568
+ // Commit all changes to the model.
569
+ if ( isTableWidthAttributeChanged ) {
570
+ editor.execute(
571
+ 'resizeTableWidth',
572
+ {
573
+ table: modelTable,
574
+ tableWidth: `${ toPrecision( tableWidthAttributeNew ) }%`,
575
+ columnWidths: columnWidthsAttributeNew
576
+ }
577
+ );
578
+ } else {
579
+ editor.execute( 'resizeColumnWidths', { columnWidths: columnWidthsAttributeNew, table: modelTable } );
580
+ }
581
+ } else {
582
+ // In read-only mode revert all changes in the editing view. The model is not touched so it does not need to be restored.
583
+ // This case can occur if the read-only mode kicks in during the resizing process.
584
+ editingView.change( writer => {
585
+ // If table had resized columns before, restore the previous column widths.
586
+ // Otherwise clean up the view from the temporary column resizing markup.
587
+ if ( columnWidthsAttributeOld ) {
588
+ const columnWidths = columnWidthsAttributeOld.split( ',' );
589
+
590
+ for ( const viewCol of viewColgroup.getChildren() ) {
591
+ writer.setStyle( 'width', columnWidths.shift(), viewCol );
592
+ }
593
+ } else {
594
+ writer.remove( viewColgroup );
595
+ }
596
+
597
+ if ( isTableWidthAttributeChanged ) {
598
+ // If the whole table was already resized before, restore the previous table width.
599
+ // Otherwise clean up the view from the temporary table resizing markup.
600
+ if ( tableWidthAttributeOld ) {
601
+ writer.setStyle( 'width', tableWidthAttributeOld, viewFigure );
602
+ } else {
603
+ writer.removeStyle( 'width', viewFigure );
604
+ }
605
+ }
606
+
607
+ // If a table and its columns weren't resized before,
608
+ // prune the remaining common resizing markup.
609
+ if ( !columnWidthsAttributeOld && !tableWidthAttributeOld ) {
610
+ writer.removeClass(
611
+ 'ck-table-resized',
612
+ [ ...viewFigure.getChildren() ].find( element => element.name === 'table' )
613
+ );
614
+ }
615
+ } );
616
+ }
617
+ }
618
+
619
+ editingView.change( writer => {
620
+ writer.removeClass( 'ck-table-column-resizer__active', viewResizer );
621
+ } );
622
+
623
+ this._isResizingActive = false;
624
+ this._resizingData = null;
625
+ }
626
+
627
+ /**
628
+ * Retrieves and returns required data needed for the resizing process.
629
+ *
630
+ * @private
631
+ * @param {module:engine/view/observer/domeventdata~DomEventData} domEventData The data of the `mousedown` event.
632
+ * @param {Array.<Number>} columnWidths The current widths of the columns.
633
+ * @returns {Object} The data needed for the resizing process.
634
+ */
635
+ _getResizingData( domEventData, columnWidths ) {
658
636
  const editor = this.editor;
659
637
 
660
638
  const columnPosition = domEventData.domEvent.clientX;
@@ -664,8 +642,8 @@ export default class TableColumnResizeEditing extends Plugin {
664
642
  const modelLeftCell = editor.editing.mapper.toModelElement( viewLeftCell );
665
643
  const modelTable = modelLeftCell.findAncestor( 'table' );
666
644
 
667
- const leftColumnIndex = getColumnIndex( modelLeftCell, this._columnIndexMap ).rightEdge;
668
- const lastColumnIndex = getNumberOfColumn( modelTable, editor ) - 1;
645
+ const leftColumnIndex = getColumnEdgesIndexes( modelLeftCell, this._tableUtilsPlugin ).rightEdge;
646
+ const lastColumnIndex = this._tableUtilsPlugin.getColumns( modelTable ) - 1;
669
647
 
670
648
  const isRightEdge = leftColumnIndex === lastColumnIndex;
671
649
  const isTableCentered = !modelTable.hasAttribute( 'tableAlignment' );
@@ -678,37 +656,38 @@ export default class TableColumnResizeEditing extends Plugin {
678
656
  const viewRightColumn = isRightEdge ? undefined : viewColgroup.getChild( leftColumnIndex + 1 );
679
657
 
680
658
  const viewFigureParentWidth = getElementWidthInPixels( editor.editing.view.domConverter.mapViewToDom( viewFigure.parent ) );
659
+ const viewFigureWidth = getElementWidthInPixels( editor.editing.view.domConverter.mapViewToDom( viewFigure ) );
681
660
  const tableWidth = getTableWidthInPixels( modelTable, editor );
682
- const columnWidths = getColumnWidthsInPixels( modelTable, editor );
683
661
  const leftColumnWidth = columnWidths[ leftColumnIndex ];
684
662
  const rightColumnWidth = isRightEdge ? undefined : columnWidths[ leftColumnIndex + 1 ];
685
663
 
686
664
  return {
687
665
  columnPosition,
666
+ flags: {
667
+ isRightEdge,
668
+ isTableCentered,
669
+ isLtrContent
670
+ },
688
671
  elements: {
672
+ viewResizer,
689
673
  modelTable,
690
674
  viewFigure,
691
675
  viewColgroup,
692
676
  viewLeftColumn,
693
- viewRightColumn,
694
- viewResizer
677
+ viewRightColumn
695
678
  },
696
679
  widths: {
697
680
  viewFigureParentWidth,
681
+ viewFigureWidth,
698
682
  tableWidth,
699
683
  leftColumnWidth,
700
684
  rightColumnWidth
701
- },
702
- flags: {
703
- isRightEdge,
704
- isTableCentered,
705
- isLtrContent
706
685
  }
707
686
  };
708
687
  }
709
688
 
710
689
  /**
711
- * Inserts colgroup if it is missing from table (e.g. after table insertion into table).
690
+ * Inserts the `<colgroup>` element if it is missing in the view table (e.g. after table insertion into table).
712
691
  *
713
692
  * @private
714
693
  */
@@ -716,43 +695,38 @@ export default class TableColumnResizeEditing extends Plugin {
716
695
  const editor = this.editor;
717
696
 
718
697
  this.listenTo( editor.editing.view.document, 'layoutChanged', () => {
719
- const table = editor.model.document.selection.getFirstPosition().findAncestor( 'table' );
720
- const tableView = editor.editing.view.document.selection.getFirstPosition().getAncestors().reverse().find(
721
- element => element.name === 'table'
698
+ const viewTable = editor.editing.view.document.selection.getFirstPosition().getAncestors().reverse().find(
699
+ viewElement => viewElement.name === 'table'
722
700
  );
723
- const tableViewContainsColgroup = tableView && [ ...tableView.getChildren() ].find(
701
+ const viewTableContainsColgroup = viewTable && [ ...viewTable.getChildren() ].find(
724
702
  viewElement => viewElement.is( 'element', 'colgroup' )
725
703
  );
704
+ const modelTable = editor.model.document.selection.getFirstPosition().findAncestor( 'table' );
726
705
 
727
- if ( table && table.hasAttribute( 'columnWidths' ) && tableView && !tableViewContainsColgroup ) {
728
- editor.editing.reconvertItem( table );
706
+ if ( modelTable && modelTable.hasAttribute( 'columnWidths' ) && viewTable && !viewTableContainsColgroup ) {
707
+ editor.editing.reconvertItem( modelTable );
729
708
  }
730
709
  }, { priority: 'low' } );
731
710
  }
732
711
 
733
712
  /**
734
- * Registers a handler on 'render' to properly insert/remove resizers after all postfixers finished their job.
713
+ * Registers a listener ensuring that each resizable cell have a resizer handle.
735
714
  *
736
715
  * @private
737
716
  */
738
717
  _registerResizerInserter() {
739
- const editor = this.editor;
740
- const view = editor.editing.view;
741
- const cellsModified = this._cellsModified;
718
+ const view = this.editor.editing.view;
742
719
 
743
720
  view.on( 'render', () => {
744
- for ( const [ cell, operation ] of cellsModified.entries() ) {
745
- const viewCell = editor.editing.mapper.toViewElement( cell );
721
+ for ( const item of view.createRangeIn( view.document.getRoot() ) ) {
722
+ if ( ![ 'td', 'th' ].includes( item.item.name ) ) {
723
+ continue;
724
+ }
746
725
 
747
726
  view.change( viewWriter => {
748
- if ( operation === 'insert' ) {
749
- insertColumnResizerElements( viewWriter, viewCell );
750
- } else if ( operation === 'remove' ) {
751
- removeColumnResizerElements( viewWriter, viewCell );
752
- }
727
+ ensureColumnResizerElement( viewWriter, item.item );
753
728
  } );
754
729
  }
755
- cellsModified.clear();
756
730
  }, { priority: 'lowest' } );
757
731
  }
758
732
  }