@ckeditor/ckeditor5-table 40.0.0 → 40.2.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 (171) hide show
  1. package/CHANGELOG.md +15 -15
  2. package/LICENSE.md +3 -3
  3. package/build/table.js +1 -1
  4. package/build/translations/fi.js +1 -1
  5. package/lang/translations/fi.po +3 -3
  6. package/package.json +2 -2
  7. package/src/augmentation.d.ts +76 -76
  8. package/src/augmentation.js +5 -5
  9. package/src/commands/insertcolumncommand.d.ts +55 -55
  10. package/src/commands/insertcolumncommand.js +67 -67
  11. package/src/commands/insertrowcommand.d.ts +54 -54
  12. package/src/commands/insertrowcommand.js +66 -66
  13. package/src/commands/inserttablecommand.d.ts +44 -44
  14. package/src/commands/inserttablecommand.js +69 -69
  15. package/src/commands/mergecellcommand.d.ts +68 -68
  16. package/src/commands/mergecellcommand.js +198 -198
  17. package/src/commands/mergecellscommand.d.ts +28 -28
  18. package/src/commands/mergecellscommand.js +94 -94
  19. package/src/commands/removecolumncommand.d.ts +29 -29
  20. package/src/commands/removecolumncommand.js +109 -109
  21. package/src/commands/removerowcommand.d.ts +29 -29
  22. package/src/commands/removerowcommand.js +82 -82
  23. package/src/commands/selectcolumncommand.d.ts +33 -33
  24. package/src/commands/selectcolumncommand.js +60 -60
  25. package/src/commands/selectrowcommand.d.ts +33 -33
  26. package/src/commands/selectrowcommand.js +56 -56
  27. package/src/commands/setheadercolumncommand.d.ts +50 -50
  28. package/src/commands/setheadercolumncommand.js +71 -71
  29. package/src/commands/setheaderrowcommand.d.ts +53 -53
  30. package/src/commands/setheaderrowcommand.js +79 -79
  31. package/src/commands/splitcellcommand.d.ts +43 -43
  32. package/src/commands/splitcellcommand.js +54 -54
  33. package/src/converters/downcast.d.ts +63 -63
  34. package/src/converters/downcast.js +146 -146
  35. package/src/converters/table-caption-post-fixer.d.ts +20 -20
  36. package/src/converters/table-caption-post-fixer.js +53 -53
  37. package/src/converters/table-cell-paragraph-post-fixer.d.ts +32 -32
  38. package/src/converters/table-cell-paragraph-post-fixer.js +107 -107
  39. package/src/converters/table-cell-refresh-handler.d.ts +18 -18
  40. package/src/converters/table-cell-refresh-handler.js +45 -45
  41. package/src/converters/table-headings-refresh-handler.d.ts +17 -17
  42. package/src/converters/table-headings-refresh-handler.js +49 -49
  43. package/src/converters/table-layout-post-fixer.d.ts +226 -226
  44. package/src/converters/table-layout-post-fixer.js +367 -367
  45. package/src/converters/tableproperties.d.ts +54 -54
  46. package/src/converters/tableproperties.js +159 -159
  47. package/src/converters/upcasttable.d.ts +49 -49
  48. package/src/converters/upcasttable.js +243 -243
  49. package/src/index.d.ts +60 -60
  50. package/src/index.js +30 -30
  51. package/src/plaintableoutput.d.ts +26 -26
  52. package/src/plaintableoutput.js +123 -123
  53. package/src/table.d.ts +40 -40
  54. package/src/table.js +44 -44
  55. package/src/tablecaption/tablecaptionediting.d.ts +63 -63
  56. package/src/tablecaption/tablecaptionediting.js +122 -122
  57. package/src/tablecaption/tablecaptionui.d.ts +21 -21
  58. package/src/tablecaption/tablecaptionui.js +57 -57
  59. package/src/tablecaption/toggletablecaptioncommand.d.ts +68 -68
  60. package/src/tablecaption/toggletablecaptioncommand.js +105 -104
  61. package/src/tablecaption/utils.d.ts +38 -42
  62. package/src/tablecaption/utils.js +57 -67
  63. package/src/tablecaption.d.ts +24 -24
  64. package/src/tablecaption.js +28 -28
  65. package/src/tablecellproperties/commands/tablecellbackgroundcolorcommand.d.ts +32 -32
  66. package/src/tablecellproperties/commands/tablecellbackgroundcolorcommand.js +30 -30
  67. package/src/tablecellproperties/commands/tablecellbordercolorcommand.d.ts +37 -37
  68. package/src/tablecellproperties/commands/tablecellbordercolorcommand.js +44 -44
  69. package/src/tablecellproperties/commands/tablecellborderstylecommand.d.ts +37 -37
  70. package/src/tablecellproperties/commands/tablecellborderstylecommand.js +44 -44
  71. package/src/tablecellproperties/commands/tablecellborderwidthcommand.d.ts +51 -51
  72. package/src/tablecellproperties/commands/tablecellborderwidthcommand.js +64 -64
  73. package/src/tablecellproperties/commands/tablecellheightcommand.d.ts +46 -46
  74. package/src/tablecellproperties/commands/tablecellheightcommand.js +51 -51
  75. package/src/tablecellproperties/commands/tablecellhorizontalalignmentcommand.d.ts +32 -32
  76. package/src/tablecellproperties/commands/tablecellhorizontalalignmentcommand.js +30 -30
  77. package/src/tablecellproperties/commands/tablecellpaddingcommand.d.ts +51 -51
  78. package/src/tablecellproperties/commands/tablecellpaddingcommand.js +64 -64
  79. package/src/tablecellproperties/commands/tablecellpropertycommand.d.ts +62 -62
  80. package/src/tablecellproperties/commands/tablecellpropertycommand.js +92 -92
  81. package/src/tablecellproperties/commands/tablecellverticalalignmentcommand.d.ts +40 -40
  82. package/src/tablecellproperties/commands/tablecellverticalalignmentcommand.js +38 -38
  83. package/src/tablecellproperties/tablecellpropertiesediting.d.ts +43 -43
  84. package/src/tablecellproperties/tablecellpropertiesediting.js +241 -241
  85. package/src/tablecellproperties/tablecellpropertiesui.d.ts +112 -112
  86. package/src/tablecellproperties/tablecellpropertiesui.js +330 -330
  87. package/src/tablecellproperties/ui/tablecellpropertiesview.d.ts +228 -228
  88. package/src/tablecellproperties/ui/tablecellpropertiesview.js +548 -548
  89. package/src/tablecellproperties.d.ts +30 -30
  90. package/src/tablecellproperties.js +34 -34
  91. package/src/tablecellwidth/commands/tablecellwidthcommand.d.ts +46 -46
  92. package/src/tablecellwidth/commands/tablecellwidthcommand.js +51 -51
  93. package/src/tablecellwidth/tablecellwidthediting.d.ts +29 -29
  94. package/src/tablecellwidth/tablecellwidthediting.js +45 -45
  95. package/src/tableclipboard.d.ts +65 -65
  96. package/src/tableclipboard.js +450 -450
  97. package/src/tablecolumnresize/constants.d.ts +20 -20
  98. package/src/tablecolumnresize/constants.js +20 -20
  99. package/src/tablecolumnresize/converters.d.ts +18 -18
  100. package/src/tablecolumnresize/converters.js +46 -46
  101. package/src/tablecolumnresize/tablecolumnresizeediting.d.ts +139 -139
  102. package/src/tablecolumnresize/tablecolumnresizeediting.js +583 -583
  103. package/src/tablecolumnresize/tablewidthscommand.d.ts +38 -38
  104. package/src/tablecolumnresize/tablewidthscommand.js +61 -61
  105. package/src/tablecolumnresize/utils.d.ts +148 -148
  106. package/src/tablecolumnresize/utils.js +358 -358
  107. package/src/tablecolumnresize.d.ts +26 -26
  108. package/src/tablecolumnresize.js +30 -30
  109. package/src/tableconfig.d.ts +343 -343
  110. package/src/tableconfig.js +5 -5
  111. package/src/tableediting.d.ts +98 -98
  112. package/src/tableediting.js +191 -191
  113. package/src/tablekeyboard.d.ts +68 -68
  114. package/src/tablekeyboard.js +279 -279
  115. package/src/tablemouse/mouseeventsobserver.d.ts +62 -62
  116. package/src/tablemouse/mouseeventsobserver.js +35 -35
  117. package/src/tablemouse.d.ts +48 -48
  118. package/src/tablemouse.js +172 -172
  119. package/src/tableproperties/commands/tablealignmentcommand.d.ts +32 -32
  120. package/src/tableproperties/commands/tablealignmentcommand.js +30 -30
  121. package/src/tableproperties/commands/tablebackgroundcolorcommand.d.ts +32 -32
  122. package/src/tableproperties/commands/tablebackgroundcolorcommand.js +30 -30
  123. package/src/tableproperties/commands/tablebordercolorcommand.d.ts +37 -37
  124. package/src/tableproperties/commands/tablebordercolorcommand.js +44 -44
  125. package/src/tableproperties/commands/tableborderstylecommand.d.ts +37 -37
  126. package/src/tableproperties/commands/tableborderstylecommand.js +44 -44
  127. package/src/tableproperties/commands/tableborderwidthcommand.d.ts +51 -51
  128. package/src/tableproperties/commands/tableborderwidthcommand.js +64 -64
  129. package/src/tableproperties/commands/tableheightcommand.d.ts +46 -46
  130. package/src/tableproperties/commands/tableheightcommand.js +54 -54
  131. package/src/tableproperties/commands/tablepropertycommand.d.ts +61 -61
  132. package/src/tableproperties/commands/tablepropertycommand.js +81 -80
  133. package/src/tableproperties/commands/tablewidthcommand.d.ts +46 -46
  134. package/src/tableproperties/commands/tablewidthcommand.js +54 -54
  135. package/src/tableproperties/tablepropertiesediting.d.ts +40 -40
  136. package/src/tableproperties/tablepropertiesediting.js +206 -206
  137. package/src/tableproperties/tablepropertiesui.d.ts +114 -114
  138. package/src/tableproperties/tablepropertiesui.js +321 -321
  139. package/src/tableproperties/ui/tablepropertiesview.d.ts +207 -207
  140. package/src/tableproperties/ui/tablepropertiesview.js +466 -466
  141. package/src/tableproperties.d.ts +30 -30
  142. package/src/tableproperties.js +34 -34
  143. package/src/tableselection.d.ts +107 -107
  144. package/src/tableselection.js +297 -297
  145. package/src/tabletoolbar.d.ts +32 -32
  146. package/src/tabletoolbar.js +57 -57
  147. package/src/tableui.d.ts +53 -53
  148. package/src/tableui.js +309 -309
  149. package/src/tableutils.d.ts +448 -448
  150. package/src/tableutils.js +1055 -1055
  151. package/src/tablewalker.d.ts +362 -362
  152. package/src/tablewalker.js +393 -393
  153. package/src/ui/colorinputview.d.ts +140 -140
  154. package/src/ui/colorinputview.js +271 -271
  155. package/src/ui/formrowview.d.ts +61 -61
  156. package/src/ui/formrowview.js +57 -57
  157. package/src/ui/inserttableview.d.ts +77 -77
  158. package/src/ui/inserttableview.js +169 -169
  159. package/src/utils/common.d.ts +46 -42
  160. package/src/utils/common.js +68 -57
  161. package/src/utils/structure.d.ts +245 -245
  162. package/src/utils/structure.js +426 -426
  163. package/src/utils/table-properties.d.ts +67 -67
  164. package/src/utils/table-properties.js +86 -86
  165. package/src/utils/ui/contextualballoon.d.ts +34 -34
  166. package/src/utils/ui/contextualballoon.js +110 -106
  167. package/src/utils/ui/table-properties.d.ts +195 -195
  168. package/src/utils/ui/table-properties.js +362 -362
  169. package/src/utils/ui/widget.d.ts +20 -16
  170. package/src/utils/ui/widget.js +48 -38
  171. package/build/table.js.map +0 -1
@@ -1,358 +1,358 @@
1
- /**
2
- * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- import { global } from 'ckeditor5/src/utils';
6
- import { COLUMN_WIDTH_PRECISION, COLUMN_MIN_WIDTH_AS_PERCENTAGE, COLUMN_MIN_WIDTH_IN_PIXELS } from './constants';
7
- /**
8
- * Returns all the inserted or changed table model elements in a given change set. Only the tables
9
- * with 'columnsWidth' attribute are taken into account. The returned set may be empty.
10
- *
11
- * Most notably if an entire table is removed it will not be included in returned set.
12
- *
13
- * @param model The model to collect the affected elements from.
14
- * @returns A set of table model elements.
15
- */
16
- export function getChangedResizedTables(model) {
17
- const affectedTables = new Set();
18
- for (const change of model.document.differ.getChanges()) {
19
- let referencePosition = null;
20
- // Checks if the particular change from the differ is:
21
- // - an insertion or removal of a table, a row or a cell,
22
- // - an attribute change on a table, a row or a cell.
23
- switch (change.type) {
24
- case 'insert':
25
- referencePosition = ['table', 'tableRow', 'tableCell'].includes(change.name) ?
26
- change.position :
27
- null;
28
- break;
29
- case 'remove':
30
- // If the whole table is removed, there's no need to update its column widths (#12201).
31
- referencePosition = ['tableRow', 'tableCell'].includes(change.name) ?
32
- change.position :
33
- null;
34
- break;
35
- case 'attribute':
36
- if (change.range.start.nodeAfter) {
37
- referencePosition = ['table', 'tableRow', 'tableCell'].includes(change.range.start.nodeAfter.name) ?
38
- change.range.start :
39
- null;
40
- }
41
- break;
42
- }
43
- if (!referencePosition) {
44
- continue;
45
- }
46
- const tableNode = (referencePosition.nodeAfter && referencePosition.nodeAfter.is('element', 'table')) ?
47
- referencePosition.nodeAfter : referencePosition.findAncestor('table');
48
- // We iterate over the whole table looking for the nested tables that are also affected.
49
- for (const node of model.createRangeOn(tableNode).getItems()) {
50
- if (!node.is('element', 'table')) {
51
- continue;
52
- }
53
- if (!getColumnGroupElement(node)) {
54
- continue;
55
- }
56
- affectedTables.add(node);
57
- }
58
- }
59
- return affectedTables;
60
- }
61
- /**
62
- * Calculates the percentage of the minimum column width given in pixels for a given table.
63
- *
64
- * @param modelTable A table model element.
65
- * @param editor The editor instance.
66
- * @returns The minimal column width in percentage.
67
- */
68
- export function getColumnMinWidthAsPercentage(modelTable, editor) {
69
- return COLUMN_MIN_WIDTH_IN_PIXELS * 100 / getTableWidthInPixels(modelTable, editor);
70
- }
71
- /**
72
- * Calculates the table width in pixels.
73
- *
74
- * @param modelTable A table model element.
75
- * @param editor The editor instance.
76
- * @returns The width of the table in pixels.
77
- */
78
- export function getTableWidthInPixels(modelTable, editor) {
79
- // It is possible for a table to not have a <tbody> element - see #11878.
80
- const referenceElement = getChildrenViewElement(modelTable, 'tbody', editor) || getChildrenViewElement(modelTable, 'thead', editor);
81
- const domReferenceElement = editor.editing.view.domConverter.mapViewToDom(referenceElement);
82
- return getElementWidthInPixels(domReferenceElement);
83
- }
84
- /**
85
- * Returns the a view element with a given name that is nested directly in a `<table>` element
86
- * related to a given `modelTable`.
87
- *
88
- * @param elementName Name of a view to be looked for, e.g. `'colgroup`', `'thead`'.
89
- * @returns Matched view or `undefined` otherwise.
90
- */
91
- function getChildrenViewElement(modelTable, elementName, editor) {
92
- const viewFigure = editor.editing.mapper.toViewElement(modelTable);
93
- const viewTable = [...viewFigure.getChildren()]
94
- .find((node) => node.is('element', 'table'));
95
- return [...viewTable.getChildren()]
96
- .find((node) => node.is('element', elementName));
97
- }
98
- /**
99
- * Returns the computed width (in pixels) of the DOM element without padding and borders.
100
- *
101
- * @param domElement A DOM element.
102
- * @returns The width of the DOM element in pixels.
103
- */
104
- export function getElementWidthInPixels(domElement) {
105
- const styles = global.window.getComputedStyle(domElement);
106
- // In the 'border-box' box sizing algorithm, the element's width
107
- // already includes the padding and border width (#12335).
108
- if (styles.boxSizing === 'border-box') {
109
- return parseFloat(styles.width) -
110
- parseFloat(styles.paddingLeft) -
111
- parseFloat(styles.paddingRight) -
112
- parseFloat(styles.borderLeftWidth) -
113
- parseFloat(styles.borderRightWidth);
114
- }
115
- else {
116
- return parseFloat(styles.width);
117
- }
118
- }
119
- /**
120
- * Returns the column indexes on the left and right edges of a cell. They differ if the cell spans
121
- * across multiple columns.
122
- *
123
- * @param cell A table cell model element.
124
- * @param tableUtils The Table Utils plugin instance.
125
- * @returns An object containing the indexes of the left and right edges of the cell.
126
- */
127
- export function getColumnEdgesIndexes(cell, tableUtils) {
128
- const cellColumnIndex = tableUtils.getCellLocation(cell).column;
129
- const cellWidth = cell.getAttribute('colspan') || 1;
130
- return {
131
- leftEdge: cellColumnIndex,
132
- rightEdge: cellColumnIndex + cellWidth - 1
133
- };
134
- }
135
- /**
136
- * Rounds the provided value to a fixed-point number with defined number of digits after the decimal point.
137
- *
138
- * @param value A number to be rounded.
139
- * @returns The rounded number.
140
- */
141
- export function toPrecision(value) {
142
- const multiplier = Math.pow(10, COLUMN_WIDTH_PRECISION);
143
- const number = typeof value === 'number' ? value : parseFloat(value);
144
- return Math.round(number * multiplier) / multiplier;
145
- }
146
- /**
147
- * Clamps the number within the inclusive lower (min) and upper (max) bounds. Returned number is rounded using the
148
- * {@link ~toPrecision `toPrecision()`} function.
149
- *
150
- * @param number A number to be clamped.
151
- * @param min A lower bound.
152
- * @param max An upper bound.
153
- * @returns The clamped number.
154
- */
155
- export function clamp(number, min, max) {
156
- if (number <= min) {
157
- return toPrecision(min);
158
- }
159
- if (number >= max) {
160
- return toPrecision(max);
161
- }
162
- return toPrecision(number);
163
- }
164
- /**
165
- * Creates an array with defined length and fills all elements with defined value.
166
- *
167
- * @param length The length of the array.
168
- * @param value The value to fill the array with.
169
- * @returns An array with defined length and filled with defined value.
170
- */
171
- export function createFilledArray(length, value) {
172
- return Array(length).fill(value);
173
- }
174
- /**
175
- * Sums all array values that can be parsed to a float.
176
- *
177
- * @param array An array of numbers.
178
- * @returns The sum of all array values.
179
- */
180
- export function sumArray(array) {
181
- return array
182
- .map(value => typeof value === 'number' ? value : parseFloat(value))
183
- .filter(value => !Number.isNaN(value))
184
- .reduce((result, item) => result + item, 0);
185
- }
186
- /**
187
- * Makes sure that the sum of the widths from all columns is 100%. If the sum of all the widths is not equal 100%, all the widths are
188
- * changed proportionally so that they all sum back to 100%. If there are columns without specified width, the amount remaining
189
- * after assigning the known widths will be distributed equally between them.
190
- *
191
- * @param columnWidths An array of column widths.
192
- * @returns An array of column widths guaranteed to sum up to 100%.
193
- */
194
- export function normalizeColumnWidths(columnWidths) {
195
- const widths = columnWidths.map(width => {
196
- if (width === 'auto') {
197
- return width;
198
- }
199
- return parseFloat(width.replace('%', ''));
200
- });
201
- let normalizedWidths = calculateMissingColumnWidths(widths);
202
- const totalWidth = sumArray(normalizedWidths);
203
- if (totalWidth !== 100) {
204
- normalizedWidths = normalizedWidths
205
- // Adjust all the columns proportionally.
206
- .map(width => toPrecision(width * 100 / totalWidth))
207
- // Due to rounding of numbers it may happen that the sum of the widths of all columns will not be exactly 100%.
208
- // Therefore, the width of the last column is explicitly adjusted (narrowed or expanded), since all the columns
209
- // have been proportionally changed already.
210
- .map((columnWidth, columnIndex, width) => {
211
- const isLastColumn = columnIndex === width.length - 1;
212
- if (!isLastColumn) {
213
- return columnWidth;
214
- }
215
- const totalWidth = sumArray(width);
216
- return toPrecision(columnWidth + 100 - totalWidth);
217
- });
218
- }
219
- return normalizedWidths.map(width => width + '%');
220
- }
221
- /**
222
- * Initializes the column widths by parsing the attribute value and calculating the uninitialized column widths. The special value 'auto'
223
- * indicates that width for the column must be calculated. The width of such uninitialized column is calculated as follows:
224
- * - If there is enough free space in the table for all uninitialized columns to have at least the minimum allowed width for all of them,
225
- * then set this width equally for all uninitialized columns.
226
- * - Otherwise, just set the minimum allowed width for all uninitialized columns. The sum of all column widths will be greater than 100%,
227
- * but then it will be adjusted proportionally to 100% in {@link #normalizeColumnWidths `normalizeColumnWidths()`}.
228
- *
229
- * @param columnWidths An array of column widths.
230
- * @returns An array with 'auto' values replaced with calculated widths.
231
- */
232
- function calculateMissingColumnWidths(columnWidths) {
233
- const numberOfUninitializedColumns = columnWidths.filter(columnWidth => columnWidth === 'auto').length;
234
- if (numberOfUninitializedColumns === 0) {
235
- return columnWidths.map(columnWidth => toPrecision(columnWidth));
236
- }
237
- const totalWidthOfInitializedColumns = sumArray(columnWidths);
238
- const widthForUninitializedColumn = Math.max((100 - totalWidthOfInitializedColumns) / numberOfUninitializedColumns, COLUMN_MIN_WIDTH_AS_PERCENTAGE);
239
- return columnWidths
240
- .map(columnWidth => columnWidth === 'auto' ? widthForUninitializedColumn : columnWidth)
241
- .map(columnWidth => toPrecision(columnWidth));
242
- }
243
- /**
244
- * Calculates the total horizontal space taken by the cell. That includes:
245
- * * width,
246
- * * left and red padding,
247
- * * border width.
248
- *
249
- * @param domCell A DOM cell element.
250
- * @returns Width in pixels without `px` at the end.
251
- */
252
- export function getDomCellOuterWidth(domCell) {
253
- const styles = global.window.getComputedStyle(domCell);
254
- // In the 'border-box' box sizing algorithm, the element's width
255
- // already includes the padding and border width (#12335).
256
- if (styles.boxSizing === 'border-box') {
257
- return parseInt(styles.width);
258
- }
259
- else {
260
- return parseFloat(styles.width) +
261
- parseFloat(styles.paddingLeft) +
262
- parseFloat(styles.paddingRight) +
263
- parseFloat(styles.borderWidth);
264
- }
265
- }
266
- /**
267
- * Updates column elements to match columns widths.
268
- *
269
- * @param columns
270
- * @param tableColumnGroup
271
- * @param normalizedWidths
272
- * @param writer
273
- */
274
- export function updateColumnElements(columns, tableColumnGroup, normalizedWidths, writer) {
275
- for (let i = 0; i < Math.max(normalizedWidths.length, columns.length); i++) {
276
- const column = columns[i];
277
- const columnWidth = normalizedWidths[i];
278
- if (!columnWidth) {
279
- // Number of `<tableColumn>` elements exceeds actual number of columns.
280
- writer.remove(column);
281
- }
282
- else if (!column) {
283
- // There is fewer `<tableColumn>` elements than actual columns.
284
- writer.appendElement('tableColumn', { columnWidth }, tableColumnGroup);
285
- }
286
- else {
287
- // Update column width.
288
- writer.setAttribute('columnWidth', columnWidth, column);
289
- }
290
- }
291
- }
292
- /**
293
- * Returns a 'tableColumnGroup' element from the 'table'.
294
- *
295
- * @internal
296
- * @param element A 'table' or 'tableColumnGroup' element.
297
- * @returns A 'tableColumnGroup' element.
298
- */
299
- export function getColumnGroupElement(element) {
300
- if (element.is('element', 'tableColumnGroup')) {
301
- return element;
302
- }
303
- const children = element.getChildren();
304
- return Array
305
- .from(children)
306
- .find(element => element.is('element', 'tableColumnGroup'));
307
- }
308
- /**
309
- * Returns an array of 'tableColumn' elements. It may be empty if there's no `tableColumnGroup` element.
310
- *
311
- * @internal
312
- * @param element A 'table' or 'tableColumnGroup' element.
313
- * @returns An array of 'tableColumn' elements.
314
- */
315
- export function getTableColumnElements(element) {
316
- const columnGroupElement = getColumnGroupElement(element);
317
- if (!columnGroupElement) {
318
- return [];
319
- }
320
- return Array.from(columnGroupElement.getChildren());
321
- }
322
- /**
323
- * Returns an array of table column widths.
324
- *
325
- * @internal
326
- * @param element A 'table' or 'tableColumnGroup' element.
327
- * @returns An array of table column widths.
328
- */
329
- export function getTableColumnsWidths(element) {
330
- return getTableColumnElements(element).map(column => column.getAttribute('columnWidth'));
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
- }
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ import { global } from 'ckeditor5/src/utils';
6
+ import { COLUMN_WIDTH_PRECISION, COLUMN_MIN_WIDTH_AS_PERCENTAGE, COLUMN_MIN_WIDTH_IN_PIXELS } from './constants';
7
+ /**
8
+ * Returns all the inserted or changed table model elements in a given change set. Only the tables
9
+ * with 'columnsWidth' attribute are taken into account. The returned set may be empty.
10
+ *
11
+ * Most notably if an entire table is removed it will not be included in returned set.
12
+ *
13
+ * @param model The model to collect the affected elements from.
14
+ * @returns A set of table model elements.
15
+ */
16
+ export function getChangedResizedTables(model) {
17
+ const affectedTables = new Set();
18
+ for (const change of model.document.differ.getChanges()) {
19
+ let referencePosition = null;
20
+ // Checks if the particular change from the differ is:
21
+ // - an insertion or removal of a table, a row or a cell,
22
+ // - an attribute change on a table, a row or a cell.
23
+ switch (change.type) {
24
+ case 'insert':
25
+ referencePosition = ['table', 'tableRow', 'tableCell'].includes(change.name) ?
26
+ change.position :
27
+ null;
28
+ break;
29
+ case 'remove':
30
+ // If the whole table is removed, there's no need to update its column widths (#12201).
31
+ referencePosition = ['tableRow', 'tableCell'].includes(change.name) ?
32
+ change.position :
33
+ null;
34
+ break;
35
+ case 'attribute':
36
+ if (change.range.start.nodeAfter) {
37
+ referencePosition = ['table', 'tableRow', 'tableCell'].includes(change.range.start.nodeAfter.name) ?
38
+ change.range.start :
39
+ null;
40
+ }
41
+ break;
42
+ }
43
+ if (!referencePosition) {
44
+ continue;
45
+ }
46
+ const tableNode = (referencePosition.nodeAfter && referencePosition.nodeAfter.is('element', 'table')) ?
47
+ referencePosition.nodeAfter : referencePosition.findAncestor('table');
48
+ // We iterate over the whole table looking for the nested tables that are also affected.
49
+ for (const node of model.createRangeOn(tableNode).getItems()) {
50
+ if (!node.is('element', 'table')) {
51
+ continue;
52
+ }
53
+ if (!getColumnGroupElement(node)) {
54
+ continue;
55
+ }
56
+ affectedTables.add(node);
57
+ }
58
+ }
59
+ return affectedTables;
60
+ }
61
+ /**
62
+ * Calculates the percentage of the minimum column width given in pixels for a given table.
63
+ *
64
+ * @param modelTable A table model element.
65
+ * @param editor The editor instance.
66
+ * @returns The minimal column width in percentage.
67
+ */
68
+ export function getColumnMinWidthAsPercentage(modelTable, editor) {
69
+ return COLUMN_MIN_WIDTH_IN_PIXELS * 100 / getTableWidthInPixels(modelTable, editor);
70
+ }
71
+ /**
72
+ * Calculates the table width in pixels.
73
+ *
74
+ * @param modelTable A table model element.
75
+ * @param editor The editor instance.
76
+ * @returns The width of the table in pixels.
77
+ */
78
+ export function getTableWidthInPixels(modelTable, editor) {
79
+ // It is possible for a table to not have a <tbody> element - see #11878.
80
+ const referenceElement = getChildrenViewElement(modelTable, 'tbody', editor) || getChildrenViewElement(modelTable, 'thead', editor);
81
+ const domReferenceElement = editor.editing.view.domConverter.mapViewToDom(referenceElement);
82
+ return getElementWidthInPixels(domReferenceElement);
83
+ }
84
+ /**
85
+ * Returns the a view element with a given name that is nested directly in a `<table>` element
86
+ * related to a given `modelTable`.
87
+ *
88
+ * @param elementName Name of a view to be looked for, e.g. `'colgroup`', `'thead`'.
89
+ * @returns Matched view or `undefined` otherwise.
90
+ */
91
+ function getChildrenViewElement(modelTable, elementName, editor) {
92
+ const viewFigure = editor.editing.mapper.toViewElement(modelTable);
93
+ const viewTable = [...viewFigure.getChildren()]
94
+ .find((node) => node.is('element', 'table'));
95
+ return [...viewTable.getChildren()]
96
+ .find((node) => node.is('element', elementName));
97
+ }
98
+ /**
99
+ * Returns the computed width (in pixels) of the DOM element without padding and borders.
100
+ *
101
+ * @param domElement A DOM element.
102
+ * @returns The width of the DOM element in pixels.
103
+ */
104
+ export function getElementWidthInPixels(domElement) {
105
+ const styles = global.window.getComputedStyle(domElement);
106
+ // In the 'border-box' box sizing algorithm, the element's width
107
+ // already includes the padding and border width (#12335).
108
+ if (styles.boxSizing === 'border-box') {
109
+ return parseFloat(styles.width) -
110
+ parseFloat(styles.paddingLeft) -
111
+ parseFloat(styles.paddingRight) -
112
+ parseFloat(styles.borderLeftWidth) -
113
+ parseFloat(styles.borderRightWidth);
114
+ }
115
+ else {
116
+ return parseFloat(styles.width);
117
+ }
118
+ }
119
+ /**
120
+ * Returns the column indexes on the left and right edges of a cell. They differ if the cell spans
121
+ * across multiple columns.
122
+ *
123
+ * @param cell A table cell model element.
124
+ * @param tableUtils The Table Utils plugin instance.
125
+ * @returns An object containing the indexes of the left and right edges of the cell.
126
+ */
127
+ export function getColumnEdgesIndexes(cell, tableUtils) {
128
+ const cellColumnIndex = tableUtils.getCellLocation(cell).column;
129
+ const cellWidth = cell.getAttribute('colspan') || 1;
130
+ return {
131
+ leftEdge: cellColumnIndex,
132
+ rightEdge: cellColumnIndex + cellWidth - 1
133
+ };
134
+ }
135
+ /**
136
+ * Rounds the provided value to a fixed-point number with defined number of digits after the decimal point.
137
+ *
138
+ * @param value A number to be rounded.
139
+ * @returns The rounded number.
140
+ */
141
+ export function toPrecision(value) {
142
+ const multiplier = Math.pow(10, COLUMN_WIDTH_PRECISION);
143
+ const number = typeof value === 'number' ? value : parseFloat(value);
144
+ return Math.round(number * multiplier) / multiplier;
145
+ }
146
+ /**
147
+ * Clamps the number within the inclusive lower (min) and upper (max) bounds. Returned number is rounded using the
148
+ * {@link ~toPrecision `toPrecision()`} function.
149
+ *
150
+ * @param number A number to be clamped.
151
+ * @param min A lower bound.
152
+ * @param max An upper bound.
153
+ * @returns The clamped number.
154
+ */
155
+ export function clamp(number, min, max) {
156
+ if (number <= min) {
157
+ return toPrecision(min);
158
+ }
159
+ if (number >= max) {
160
+ return toPrecision(max);
161
+ }
162
+ return toPrecision(number);
163
+ }
164
+ /**
165
+ * Creates an array with defined length and fills all elements with defined value.
166
+ *
167
+ * @param length The length of the array.
168
+ * @param value The value to fill the array with.
169
+ * @returns An array with defined length and filled with defined value.
170
+ */
171
+ export function createFilledArray(length, value) {
172
+ return Array(length).fill(value);
173
+ }
174
+ /**
175
+ * Sums all array values that can be parsed to a float.
176
+ *
177
+ * @param array An array of numbers.
178
+ * @returns The sum of all array values.
179
+ */
180
+ export function sumArray(array) {
181
+ return array
182
+ .map(value => typeof value === 'number' ? value : parseFloat(value))
183
+ .filter(value => !Number.isNaN(value))
184
+ .reduce((result, item) => result + item, 0);
185
+ }
186
+ /**
187
+ * Makes sure that the sum of the widths from all columns is 100%. If the sum of all the widths is not equal 100%, all the widths are
188
+ * changed proportionally so that they all sum back to 100%. If there are columns without specified width, the amount remaining
189
+ * after assigning the known widths will be distributed equally between them.
190
+ *
191
+ * @param columnWidths An array of column widths.
192
+ * @returns An array of column widths guaranteed to sum up to 100%.
193
+ */
194
+ export function normalizeColumnWidths(columnWidths) {
195
+ const widths = columnWidths.map(width => {
196
+ if (width === 'auto') {
197
+ return width;
198
+ }
199
+ return parseFloat(width.replace('%', ''));
200
+ });
201
+ let normalizedWidths = calculateMissingColumnWidths(widths);
202
+ const totalWidth = sumArray(normalizedWidths);
203
+ if (totalWidth !== 100) {
204
+ normalizedWidths = normalizedWidths
205
+ // Adjust all the columns proportionally.
206
+ .map(width => toPrecision(width * 100 / totalWidth))
207
+ // Due to rounding of numbers it may happen that the sum of the widths of all columns will not be exactly 100%.
208
+ // Therefore, the width of the last column is explicitly adjusted (narrowed or expanded), since all the columns
209
+ // have been proportionally changed already.
210
+ .map((columnWidth, columnIndex, width) => {
211
+ const isLastColumn = columnIndex === width.length - 1;
212
+ if (!isLastColumn) {
213
+ return columnWidth;
214
+ }
215
+ const totalWidth = sumArray(width);
216
+ return toPrecision(columnWidth + 100 - totalWidth);
217
+ });
218
+ }
219
+ return normalizedWidths.map(width => width + '%');
220
+ }
221
+ /**
222
+ * Initializes the column widths by parsing the attribute value and calculating the uninitialized column widths. The special value 'auto'
223
+ * indicates that width for the column must be calculated. The width of such uninitialized column is calculated as follows:
224
+ * - If there is enough free space in the table for all uninitialized columns to have at least the minimum allowed width for all of them,
225
+ * then set this width equally for all uninitialized columns.
226
+ * - Otherwise, just set the minimum allowed width for all uninitialized columns. The sum of all column widths will be greater than 100%,
227
+ * but then it will be adjusted proportionally to 100% in {@link #normalizeColumnWidths `normalizeColumnWidths()`}.
228
+ *
229
+ * @param columnWidths An array of column widths.
230
+ * @returns An array with 'auto' values replaced with calculated widths.
231
+ */
232
+ function calculateMissingColumnWidths(columnWidths) {
233
+ const numberOfUninitializedColumns = columnWidths.filter(columnWidth => columnWidth === 'auto').length;
234
+ if (numberOfUninitializedColumns === 0) {
235
+ return columnWidths.map(columnWidth => toPrecision(columnWidth));
236
+ }
237
+ const totalWidthOfInitializedColumns = sumArray(columnWidths);
238
+ const widthForUninitializedColumn = Math.max((100 - totalWidthOfInitializedColumns) / numberOfUninitializedColumns, COLUMN_MIN_WIDTH_AS_PERCENTAGE);
239
+ return columnWidths
240
+ .map(columnWidth => columnWidth === 'auto' ? widthForUninitializedColumn : columnWidth)
241
+ .map(columnWidth => toPrecision(columnWidth));
242
+ }
243
+ /**
244
+ * Calculates the total horizontal space taken by the cell. That includes:
245
+ * * width,
246
+ * * left and red padding,
247
+ * * border width.
248
+ *
249
+ * @param domCell A DOM cell element.
250
+ * @returns Width in pixels without `px` at the end.
251
+ */
252
+ export function getDomCellOuterWidth(domCell) {
253
+ const styles = global.window.getComputedStyle(domCell);
254
+ // In the 'border-box' box sizing algorithm, the element's width
255
+ // already includes the padding and border width (#12335).
256
+ if (styles.boxSizing === 'border-box') {
257
+ return parseInt(styles.width);
258
+ }
259
+ else {
260
+ return parseFloat(styles.width) +
261
+ parseFloat(styles.paddingLeft) +
262
+ parseFloat(styles.paddingRight) +
263
+ parseFloat(styles.borderWidth);
264
+ }
265
+ }
266
+ /**
267
+ * Updates column elements to match columns widths.
268
+ *
269
+ * @param columns
270
+ * @param tableColumnGroup
271
+ * @param normalizedWidths
272
+ * @param writer
273
+ */
274
+ export function updateColumnElements(columns, tableColumnGroup, normalizedWidths, writer) {
275
+ for (let i = 0; i < Math.max(normalizedWidths.length, columns.length); i++) {
276
+ const column = columns[i];
277
+ const columnWidth = normalizedWidths[i];
278
+ if (!columnWidth) {
279
+ // Number of `<tableColumn>` elements exceeds actual number of columns.
280
+ writer.remove(column);
281
+ }
282
+ else if (!column) {
283
+ // There is fewer `<tableColumn>` elements than actual columns.
284
+ writer.appendElement('tableColumn', { columnWidth }, tableColumnGroup);
285
+ }
286
+ else {
287
+ // Update column width.
288
+ writer.setAttribute('columnWidth', columnWidth, column);
289
+ }
290
+ }
291
+ }
292
+ /**
293
+ * Returns a 'tableColumnGroup' element from the 'table'.
294
+ *
295
+ * @internal
296
+ * @param element A 'table' or 'tableColumnGroup' element.
297
+ * @returns A 'tableColumnGroup' element.
298
+ */
299
+ export function getColumnGroupElement(element) {
300
+ if (element.is('element', 'tableColumnGroup')) {
301
+ return element;
302
+ }
303
+ const children = element.getChildren();
304
+ return Array
305
+ .from(children)
306
+ .find(element => element.is('element', 'tableColumnGroup'));
307
+ }
308
+ /**
309
+ * Returns an array of 'tableColumn' elements. It may be empty if there's no `tableColumnGroup` element.
310
+ *
311
+ * @internal
312
+ * @param element A 'table' or 'tableColumnGroup' element.
313
+ * @returns An array of 'tableColumn' elements.
314
+ */
315
+ export function getTableColumnElements(element) {
316
+ const columnGroupElement = getColumnGroupElement(element);
317
+ if (!columnGroupElement) {
318
+ return [];
319
+ }
320
+ return Array.from(columnGroupElement.getChildren());
321
+ }
322
+ /**
323
+ * Returns an array of table column widths.
324
+ *
325
+ * @internal
326
+ * @param element A 'table' or 'tableColumnGroup' element.
327
+ * @returns An array of table column widths.
328
+ */
329
+ export function getTableColumnsWidths(element) {
330
+ return getTableColumnElements(element).map(column => column.getAttribute('columnWidth'));
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
+ }