@ckeditor/ckeditor5-table 39.0.0 → 39.0.2

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 (70) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +3 -3
  3. package/build/table.js +1 -1
  4. package/lang/translations/ar.po +1 -0
  5. package/lang/translations/az.po +1 -0
  6. package/lang/translations/bg.po +1 -0
  7. package/lang/translations/bn.po +1 -0
  8. package/lang/translations/ca.po +1 -0
  9. package/lang/translations/cs.po +1 -0
  10. package/lang/translations/da.po +1 -0
  11. package/lang/translations/de-ch.po +1 -0
  12. package/lang/translations/de.po +1 -0
  13. package/lang/translations/el.po +1 -0
  14. package/lang/translations/en-au.po +1 -0
  15. package/lang/translations/en-gb.po +1 -0
  16. package/lang/translations/en.po +1 -0
  17. package/lang/translations/es.po +1 -0
  18. package/lang/translations/et.po +1 -0
  19. package/lang/translations/fa.po +1 -0
  20. package/lang/translations/fi.po +1 -0
  21. package/lang/translations/fr.po +1 -0
  22. package/lang/translations/gl.po +1 -0
  23. package/lang/translations/he.po +1 -0
  24. package/lang/translations/hi.po +1 -0
  25. package/lang/translations/hr.po +1 -0
  26. package/lang/translations/hu.po +1 -0
  27. package/lang/translations/id.po +1 -0
  28. package/lang/translations/it.po +1 -0
  29. package/lang/translations/ja.po +1 -0
  30. package/lang/translations/ko.po +1 -0
  31. package/lang/translations/ku.po +1 -0
  32. package/lang/translations/lt.po +1 -0
  33. package/lang/translations/lv.po +1 -0
  34. package/lang/translations/ms.po +1 -0
  35. package/lang/translations/nb.po +1 -0
  36. package/lang/translations/ne.po +1 -0
  37. package/lang/translations/nl.po +1 -0
  38. package/lang/translations/no.po +1 -0
  39. package/lang/translations/pl.po +1 -0
  40. package/lang/translations/pt-br.po +1 -0
  41. package/lang/translations/pt.po +1 -0
  42. package/lang/translations/ro.po +1 -0
  43. package/lang/translations/ru.po +1 -0
  44. package/lang/translations/sk.po +1 -0
  45. package/lang/translations/sl.po +1 -0
  46. package/lang/translations/sq.po +1 -0
  47. package/lang/translations/sr-latn.po +1 -0
  48. package/lang/translations/sr.po +1 -0
  49. package/lang/translations/sv.po +1 -0
  50. package/lang/translations/th.po +1 -0
  51. package/lang/translations/tk.po +1 -0
  52. package/lang/translations/tr.po +1 -0
  53. package/lang/translations/tt.po +1 -0
  54. package/lang/translations/ug.po +1 -0
  55. package/lang/translations/uk.po +1 -0
  56. package/lang/translations/ur.po +1 -0
  57. package/lang/translations/uz.po +1 -0
  58. package/lang/translations/vi.po +1 -0
  59. package/lang/translations/zh-cn.po +1 -0
  60. package/lang/translations/zh.po +1 -0
  61. package/package.json +2 -6
  62. package/src/tablecolumnresize/converters.js +3 -2
  63. package/src/tablecolumnresize/tablecolumnresizeediting.js +14 -2
  64. package/src/tablecolumnresize/utils.d.ts +10 -3
  65. package/src/tablecolumnresize/utils.js +33 -5
  66. package/src/tableutils.js +14 -0
  67. package/src/tablewalker.d.ts +39 -0
  68. package/src/tablewalker.js +60 -0
  69. package/src/utils/ui/table-properties.js +1 -1
  70. package/theme/tablecolumnresize.css +8 -6
@@ -90,8 +90,6 @@ export declare function sumArray(array: Array<number | string>): number;
90
90
  * changed proportionally so that they all sum back to 100%. If there are columns without specified width, the amount remaining
91
91
  * after assigning the known widths will be distributed equally between them.
92
92
  *
93
- * Currently, only widths provided as percentage values are supported.
94
- *
95
93
  * @param columnWidths An array of column widths.
96
94
  * @returns An array of column widths guaranteed to sum up to 100%.
97
95
  */
@@ -124,7 +122,7 @@ export declare function updateColumnElements(columns: Array<Element>, tableColum
124
122
  */
125
123
  export declare function getColumnGroupElement(element: Element): Element;
126
124
  /**
127
- * Returns an array of 'tableColumn' elements.
125
+ * Returns an array of 'tableColumn' elements. It may be empty if there's no `tableColumnGroup` element.
128
126
  *
129
127
  * @internal
130
128
  * @param element A 'table' or 'tableColumnGroup' element.
@@ -139,3 +137,12 @@ export declare function getTableColumnElements(element: Element): Array<Element>
139
137
  * @returns An array of table column widths.
140
138
  */
141
139
  export declare function getTableColumnsWidths(element: Element): Array<string>;
140
+ /**
141
+ * Translates the `colSpan` model attribute into additional column widths and returns the resulting array.
142
+ *
143
+ * @internal
144
+ * @param element A 'table' or 'tableColumnGroup' element.
145
+ * @param writer A writer instance.
146
+ * @returns An array of table column widths.
147
+ */
148
+ export declare function translateColSpanAttribute(element: Element, writer: Writer): Array<string>;
@@ -188,14 +188,11 @@ export function sumArray(array) {
188
188
  * changed proportionally so that they all sum back to 100%. If there are columns without specified width, the amount remaining
189
189
  * after assigning the known widths will be distributed equally between them.
190
190
  *
191
- * Currently, only widths provided as percentage values are supported.
192
- *
193
191
  * @param columnWidths An array of column widths.
194
192
  * @returns An array of column widths guaranteed to sum up to 100%.
195
193
  */
196
194
  export function normalizeColumnWidths(columnWidths) {
197
195
  const widths = columnWidths.map(width => {
198
- // Possible values are 'auto' or string ending with '%'
199
196
  if (width === 'auto') {
200
197
  return width;
201
198
  }
@@ -309,14 +306,18 @@ export function getColumnGroupElement(element) {
309
306
  .find(element => element.is('element', 'tableColumnGroup'));
310
307
  }
311
308
  /**
312
- * Returns an array of 'tableColumn' elements.
309
+ * Returns an array of 'tableColumn' elements. It may be empty if there's no `tableColumnGroup` element.
313
310
  *
314
311
  * @internal
315
312
  * @param element A 'table' or 'tableColumnGroup' element.
316
313
  * @returns An array of 'tableColumn' elements.
317
314
  */
318
315
  export function getTableColumnElements(element) {
319
- return Array.from(getColumnGroupElement(element).getChildren());
316
+ const columnGroupElement = getColumnGroupElement(element);
317
+ if (!columnGroupElement) {
318
+ return [];
319
+ }
320
+ return Array.from(columnGroupElement.getChildren());
320
321
  }
321
322
  /**
322
323
  * Returns an array of table column widths.
@@ -328,3 +329,30 @@ export function getTableColumnElements(element) {
328
329
  export function getTableColumnsWidths(element) {
329
330
  return getTableColumnElements(element).map(column => column.getAttribute('columnWidth'));
330
331
  }
332
+ /**
333
+ * Translates the `colSpan` model attribute into additional column widths and returns the resulting array.
334
+ *
335
+ * @internal
336
+ * @param element A 'table' or 'tableColumnGroup' element.
337
+ * @param writer A writer instance.
338
+ * @returns An array of table column widths.
339
+ */
340
+ export function translateColSpanAttribute(element, writer) {
341
+ const tableColumnElements = getTableColumnElements(element);
342
+ return tableColumnElements.reduce((acc, element) => {
343
+ const columnWidth = element.getAttribute('columnWidth');
344
+ const colSpan = element.getAttribute('colSpan');
345
+ if (!colSpan) {
346
+ acc.push(columnWidth);
347
+ return acc;
348
+ }
349
+ // Translate the `colSpan` model attribute on to the proper number of column widths
350
+ // and remove it from the element.
351
+ // See https://github.com/ckeditor/ckeditor5/issues/14521#issuecomment-1662102889 for more details.
352
+ for (let i = 0; i < colSpan; i++) {
353
+ acc.push(columnWidth);
354
+ }
355
+ writer.removeAttribute('colSpan', element);
356
+ return acc;
357
+ }, []);
358
+ }
package/src/tableutils.js CHANGED
@@ -10,6 +10,7 @@ import { Plugin } from 'ckeditor5/src/core';
10
10
  import TableWalker from './tablewalker';
11
11
  import { createEmptyTableCell, updateNumericAttribute } from './utils/common';
12
12
  import { removeEmptyColumns, removeEmptyRows } from './utils/structure';
13
+ import { getTableColumnElements } from './tablecolumnresize/utils';
13
14
  /**
14
15
  * The table utilities plugin.
15
16
  */
@@ -378,6 +379,7 @@ export default class TableUtils extends Plugin {
378
379
  const last = options.at + columnsToRemove - 1;
379
380
  model.change(writer => {
380
381
  adjustHeadingColumns(table, { first, last }, writer);
382
+ const tableColumns = getTableColumnElements(table);
381
383
  for (let removedColumnIndex = last; removedColumnIndex >= first; removedColumnIndex--) {
382
384
  for (const { cell, column, cellWidth } of [...new TableWalker(table)]) {
383
385
  // If colspaned cell overlaps removed column decrease its span.
@@ -389,6 +391,18 @@ export default class TableUtils extends Plugin {
389
391
  writer.remove(cell);
390
392
  }
391
393
  }
394
+ // If table has `tableColumn` elements, we need to update it manually.
395
+ // See https://github.com/ckeditor/ckeditor5/issues/14521#issuecomment-1662102889 for details.
396
+ if (tableColumns[removedColumnIndex]) {
397
+ // If the removed column is the first one then we need to add its width to the next column.
398
+ // Otherwise we add it to the previous column.
399
+ const adjacentColumn = removedColumnIndex === 0 ? tableColumns[1] : tableColumns[removedColumnIndex - 1];
400
+ const removedColumnWidth = parseFloat(tableColumns[removedColumnIndex].getAttribute('columnWidth'));
401
+ const adjacentColumnWidth = parseFloat(adjacentColumn.getAttribute('columnWidth'));
402
+ writer.remove(tableColumns[removedColumnIndex]);
403
+ // Add the removed column width (in %) to the adjacent column.
404
+ writer.setAttribute('columnWidth', removedColumnWidth + adjacentColumnWidth + '%', adjacentColumn);
405
+ }
392
406
  }
393
407
  // Remove empty rows that could appear after removing columns.
394
408
  if (!removeEmptyRows(table, this)) {
@@ -107,6 +107,10 @@ export default class TableWalker implements IterableIterator<TableSlot> {
107
107
  * Index of the next column where a cell is anchored.
108
108
  */
109
109
  private _nextCellAtColumn;
110
+ /**
111
+ * Indicates whether the iterator jumped to (or close to) the start row, ignoring rows that don't need to be traversed.
112
+ */
113
+ private _jumpedToStartRow;
110
114
  /**
111
115
  * Creates an instance of the table walker.
112
116
  *
@@ -245,6 +249,41 @@ export default class TableWalker implements IterableIterator<TableSlot> {
245
249
  * @param data A spanned cell details (cell element, anchor row and column).
246
250
  */
247
251
  private _markSpannedCell;
252
+ /**
253
+ * Checks if part of the table can be skipped.
254
+ */
255
+ private _canJumpToStartRow;
256
+ /**
257
+ * Sets the current row to `this._startRow` or the first row before it that has the number of cells
258
+ * equal to the number of columns in the table.
259
+ *
260
+ * Example:
261
+ * +----+----+----+
262
+ * | 00 | 01 | 02 |
263
+ * |----+----+----+
264
+ * | 10 | 12 |
265
+ * | +----+
266
+ * | | 22 |
267
+ * | +----+
268
+ * | | 32 | <--- Start row
269
+ * +----+----+----+
270
+ * | 40 | 41 | 42 |
271
+ * +----+----+----+
272
+ *
273
+ * If the 4th row is a `this._startRow`, this method will:
274
+ * 1.) Count the number of columns this table has based on the first row (3 columns in this case).
275
+ * 2.) Check if the 4th row contains 3 cells. It doesn't, so go to the row before it.
276
+ * 3.) Check if the 3rd row contains 3 cells. It doesn't, so go to the row before it.
277
+ * 4.) Check if the 2nd row contains 3 cells. It does, so set the current row to that row.
278
+ *
279
+ * Setting the current row this way is necessary to let the `next()` method loop over the cells
280
+ * spanning multiple rows or columns and update the `this._spannedCells` property.
281
+ */
282
+ private _jumpToNonSpannedRowClosestToStartRow;
283
+ /**
284
+ * Returns a number of columns in a row taking `colspan` into consideration.
285
+ */
286
+ private _getRowLength;
248
287
  }
249
288
  /**
250
289
  * An object returned by {@link module:table/tablewalker~TableWalker} when traversing table cells.
@@ -84,6 +84,10 @@ export default class TableWalker {
84
84
  * @param options.includeAllSlots Also return values for spanned cells. Default value is "false".
85
85
  */
86
86
  constructor(table, options = {}) {
87
+ /**
88
+ * Indicates whether the iterator jumped to (or close to) the start row, ignoring rows that don't need to be traversed.
89
+ */
90
+ this._jumpedToStartRow = false;
87
91
  this._table = table;
88
92
  this._startRow = options.row !== undefined ? options.row : options.startRow || 0;
89
93
  this._endRow = options.row !== undefined ? options.row : options.endRow;
@@ -110,6 +114,9 @@ export default class TableWalker {
110
114
  * @returns The next table walker's value.
111
115
  */
112
116
  next() {
117
+ if (this._canJumpToStartRow()) {
118
+ this._jumpToNonSpannedRowClosestToStartRow();
119
+ }
113
120
  const row = this._table.getChild(this._rowIndex);
114
121
  // Iterator is done when there's no row (table ended) or the row is after `endRow` limit.
115
122
  if (!row || this._isOverEndRow()) {
@@ -259,6 +266,59 @@ export default class TableWalker {
259
266
  const rowSpans = this._spannedCells.get(row);
260
267
  rowSpans.set(column, data);
261
268
  }
269
+ /**
270
+ * Checks if part of the table can be skipped.
271
+ */
272
+ _canJumpToStartRow() {
273
+ return !!this._startRow &&
274
+ this._startRow > 0 &&
275
+ !this._jumpedToStartRow;
276
+ }
277
+ /**
278
+ * Sets the current row to `this._startRow` or the first row before it that has the number of cells
279
+ * equal to the number of columns in the table.
280
+ *
281
+ * Example:
282
+ * +----+----+----+
283
+ * | 00 | 01 | 02 |
284
+ * |----+----+----+
285
+ * | 10 | 12 |
286
+ * | +----+
287
+ * | | 22 |
288
+ * | +----+
289
+ * | | 32 | <--- Start row
290
+ * +----+----+----+
291
+ * | 40 | 41 | 42 |
292
+ * +----+----+----+
293
+ *
294
+ * If the 4th row is a `this._startRow`, this method will:
295
+ * 1.) Count the number of columns this table has based on the first row (3 columns in this case).
296
+ * 2.) Check if the 4th row contains 3 cells. It doesn't, so go to the row before it.
297
+ * 3.) Check if the 3rd row contains 3 cells. It doesn't, so go to the row before it.
298
+ * 4.) Check if the 2nd row contains 3 cells. It does, so set the current row to that row.
299
+ *
300
+ * Setting the current row this way is necessary to let the `next()` method loop over the cells
301
+ * spanning multiple rows or columns and update the `this._spannedCells` property.
302
+ */
303
+ _jumpToNonSpannedRowClosestToStartRow() {
304
+ const firstRowLength = this._getRowLength(0);
305
+ for (let i = this._startRow; !this._jumpedToStartRow; i--) {
306
+ if (firstRowLength === this._getRowLength(i)) {
307
+ this._row = i;
308
+ this._rowIndex = i;
309
+ this._jumpedToStartRow = true;
310
+ }
311
+ }
312
+ }
313
+ /**
314
+ * Returns a number of columns in a row taking `colspan` into consideration.
315
+ */
316
+ _getRowLength(rowIndex) {
317
+ const row = this._table.getChild(rowIndex);
318
+ return [...row.getChildren()].reduce((cols, row) => {
319
+ return cols + parseInt(row.getAttribute('colspan') || '1');
320
+ }, 0);
321
+ }
262
322
  }
263
323
  /**
264
324
  * An object returned by {@link module:table/tablewalker~TableWalker} when traversing table cells.
@@ -55,7 +55,7 @@ export function getLocalizedLengthErrorText(t) {
55
55
  * See {@link module:engine/view/styles/utils~isColor}.
56
56
  */
57
57
  export function colorFieldValidator(value) {
58
- value = value.trim();
58
+ value = value.trim().toLowerCase();
59
59
  return isEmpty(value) || isColor(value);
60
60
  }
61
61
  /**
@@ -30,12 +30,8 @@
30
30
 
31
31
  .ck.ck-editor__editable .table .ck-table-column-resizer {
32
32
  position: absolute;
33
- /* The resizer element resides in each cell so to occupy the entire height of the table, which is unknown from a CSS point of view,
34
- it is extended to an extremely high height. Even for screens with a very high pixel density, the resizer will fulfill its role as
35
- it should, i.e. for a screen of 476 ppi the total height of the resizer will take over 350 sheets of A4 format, which is totally
36
- unrealistic height for a single table. */
37
- top: -999999px;
38
- bottom: -999999px;
33
+ top: 0;
34
+ bottom: 0;
39
35
  right: var(--ck-table-column-resizer-position-offset);
40
36
  width: var(--ck-table-column-resizer-width);
41
37
  cursor: col-resize;
@@ -57,6 +53,12 @@
57
53
  .ck.ck-editor__editable .table .ck-table-column-resizer__active {
58
54
  background-color: var(--ck-color-selector-column-resizer-hover);
59
55
  opacity: 0.25;
56
+ /* The resizer element resides in each cell so to occupy the entire height of the table, which is unknown from a CSS point of view,
57
+ it is extended to an extremely high height. Even for screens with a very high pixel density, the resizer will fulfill its role as
58
+ it should, i.e. for a screen of 476 ppi the total height of the resizer will take over 350 sheets of A4 format, which is totally
59
+ unrealistic height for a single table. */
60
+ top: -999999px;
61
+ bottom: -999999px;
60
62
  }
61
63
 
62
64
  .ck.ck-editor__editable[dir=rtl] .table .ck-table-column-resizer {