@ckeditor/ckeditor5-table 38.1.1 → 38.2.0-alpha.1

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 (169) hide show
  1. package/build/table.js +1 -1
  2. package/package.json +3 -2
  3. package/src/augmentation.d.ts +76 -76
  4. package/src/augmentation.js +5 -5
  5. package/src/commands/insertcolumncommand.d.ts +55 -55
  6. package/src/commands/insertcolumncommand.js +67 -67
  7. package/src/commands/insertrowcommand.d.ts +54 -54
  8. package/src/commands/insertrowcommand.js +66 -66
  9. package/src/commands/inserttablecommand.d.ts +44 -44
  10. package/src/commands/inserttablecommand.js +69 -69
  11. package/src/commands/mergecellcommand.d.ts +68 -68
  12. package/src/commands/mergecellcommand.js +198 -198
  13. package/src/commands/mergecellscommand.d.ts +28 -28
  14. package/src/commands/mergecellscommand.js +94 -94
  15. package/src/commands/removecolumncommand.d.ts +29 -29
  16. package/src/commands/removecolumncommand.js +109 -109
  17. package/src/commands/removerowcommand.d.ts +29 -29
  18. package/src/commands/removerowcommand.js +82 -82
  19. package/src/commands/selectcolumncommand.d.ts +33 -33
  20. package/src/commands/selectcolumncommand.js +60 -60
  21. package/src/commands/selectrowcommand.d.ts +33 -33
  22. package/src/commands/selectrowcommand.js +56 -56
  23. package/src/commands/setheadercolumncommand.d.ts +50 -50
  24. package/src/commands/setheadercolumncommand.js +71 -71
  25. package/src/commands/setheaderrowcommand.d.ts +53 -53
  26. package/src/commands/setheaderrowcommand.js +79 -79
  27. package/src/commands/splitcellcommand.d.ts +43 -43
  28. package/src/commands/splitcellcommand.js +54 -54
  29. package/src/converters/downcast.d.ts +63 -63
  30. package/src/converters/downcast.js +146 -146
  31. package/src/converters/table-caption-post-fixer.d.ts +20 -20
  32. package/src/converters/table-caption-post-fixer.js +53 -53
  33. package/src/converters/table-cell-paragraph-post-fixer.d.ts +32 -32
  34. package/src/converters/table-cell-paragraph-post-fixer.js +107 -107
  35. package/src/converters/table-cell-refresh-handler.d.ts +18 -18
  36. package/src/converters/table-cell-refresh-handler.js +45 -45
  37. package/src/converters/table-headings-refresh-handler.d.ts +17 -17
  38. package/src/converters/table-headings-refresh-handler.js +49 -49
  39. package/src/converters/table-layout-post-fixer.d.ts +226 -226
  40. package/src/converters/table-layout-post-fixer.js +367 -367
  41. package/src/converters/tableproperties.d.ts +54 -54
  42. package/src/converters/tableproperties.js +159 -159
  43. package/src/converters/upcasttable.d.ts +49 -49
  44. package/src/converters/upcasttable.js +243 -243
  45. package/src/index.d.ts +60 -60
  46. package/src/index.js +30 -30
  47. package/src/plaintableoutput.d.ts +26 -26
  48. package/src/plaintableoutput.js +123 -123
  49. package/src/table.d.ts +40 -40
  50. package/src/table.js +44 -44
  51. package/src/tablecaption/tablecaptionediting.d.ts +63 -63
  52. package/src/tablecaption/tablecaptionediting.js +122 -122
  53. package/src/tablecaption/tablecaptionui.d.ts +21 -21
  54. package/src/tablecaption/tablecaptionui.js +57 -57
  55. package/src/tablecaption/toggletablecaptioncommand.d.ts +68 -68
  56. package/src/tablecaption/toggletablecaptioncommand.js +104 -104
  57. package/src/tablecaption/utils.d.ts +42 -42
  58. package/src/tablecaption/utils.js +67 -67
  59. package/src/tablecaption.d.ts +24 -24
  60. package/src/tablecaption.js +28 -28
  61. package/src/tablecellproperties/commands/tablecellbackgroundcolorcommand.d.ts +32 -32
  62. package/src/tablecellproperties/commands/tablecellbackgroundcolorcommand.js +30 -30
  63. package/src/tablecellproperties/commands/tablecellbordercolorcommand.d.ts +37 -37
  64. package/src/tablecellproperties/commands/tablecellbordercolorcommand.js +44 -44
  65. package/src/tablecellproperties/commands/tablecellborderstylecommand.d.ts +37 -37
  66. package/src/tablecellproperties/commands/tablecellborderstylecommand.js +44 -44
  67. package/src/tablecellproperties/commands/tablecellborderwidthcommand.d.ts +51 -51
  68. package/src/tablecellproperties/commands/tablecellborderwidthcommand.js +64 -64
  69. package/src/tablecellproperties/commands/tablecellheightcommand.d.ts +46 -46
  70. package/src/tablecellproperties/commands/tablecellheightcommand.js +51 -51
  71. package/src/tablecellproperties/commands/tablecellhorizontalalignmentcommand.d.ts +32 -32
  72. package/src/tablecellproperties/commands/tablecellhorizontalalignmentcommand.js +30 -30
  73. package/src/tablecellproperties/commands/tablecellpaddingcommand.d.ts +51 -51
  74. package/src/tablecellproperties/commands/tablecellpaddingcommand.js +64 -64
  75. package/src/tablecellproperties/commands/tablecellpropertycommand.d.ts +62 -62
  76. package/src/tablecellproperties/commands/tablecellpropertycommand.js +92 -92
  77. package/src/tablecellproperties/commands/tablecellverticalalignmentcommand.d.ts +40 -40
  78. package/src/tablecellproperties/commands/tablecellverticalalignmentcommand.js +38 -38
  79. package/src/tablecellproperties/tablecellpropertiesediting.d.ts +43 -43
  80. package/src/tablecellproperties/tablecellpropertiesediting.js +241 -241
  81. package/src/tablecellproperties/tablecellpropertiesui.d.ts +112 -112
  82. package/src/tablecellproperties/tablecellpropertiesui.js +330 -328
  83. package/src/tablecellproperties/ui/tablecellpropertiesview.d.ts +228 -227
  84. package/src/tablecellproperties/ui/tablecellpropertiesview.js +539 -537
  85. package/src/tablecellproperties.d.ts +30 -30
  86. package/src/tablecellproperties.js +34 -34
  87. package/src/tablecellwidth/commands/tablecellwidthcommand.d.ts +46 -46
  88. package/src/tablecellwidth/commands/tablecellwidthcommand.js +51 -51
  89. package/src/tablecellwidth/tablecellwidthediting.d.ts +29 -29
  90. package/src/tablecellwidth/tablecellwidthediting.js +45 -45
  91. package/src/tableclipboard.d.ts +65 -65
  92. package/src/tableclipboard.js +450 -450
  93. package/src/tablecolumnresize/constants.d.ts +20 -20
  94. package/src/tablecolumnresize/constants.js +20 -20
  95. package/src/tablecolumnresize/converters.d.ts +18 -18
  96. package/src/tablecolumnresize/converters.js +45 -45
  97. package/src/tablecolumnresize/tablecolumnresizeediting.d.ts +139 -139
  98. package/src/tablecolumnresize/tablecolumnresizeediting.js +571 -571
  99. package/src/tablecolumnresize/tablewidthscommand.d.ts +38 -38
  100. package/src/tablecolumnresize/tablewidthscommand.js +61 -61
  101. package/src/tablecolumnresize/utils.d.ts +141 -141
  102. package/src/tablecolumnresize/utils.js +330 -330
  103. package/src/tablecolumnresize.d.ts +26 -26
  104. package/src/tablecolumnresize.js +30 -30
  105. package/src/tableconfig.d.ts +343 -331
  106. package/src/tableconfig.js +5 -5
  107. package/src/tableediting.d.ts +98 -98
  108. package/src/tableediting.js +191 -191
  109. package/src/tablekeyboard.d.ts +68 -68
  110. package/src/tablekeyboard.js +279 -279
  111. package/src/tablemouse/mouseeventsobserver.d.ts +62 -62
  112. package/src/tablemouse/mouseeventsobserver.js +35 -35
  113. package/src/tablemouse.d.ts +48 -48
  114. package/src/tablemouse.js +172 -172
  115. package/src/tableproperties/commands/tablealignmentcommand.d.ts +32 -32
  116. package/src/tableproperties/commands/tablealignmentcommand.js +30 -30
  117. package/src/tableproperties/commands/tablebackgroundcolorcommand.d.ts +32 -32
  118. package/src/tableproperties/commands/tablebackgroundcolorcommand.js +30 -30
  119. package/src/tableproperties/commands/tablebordercolorcommand.d.ts +37 -37
  120. package/src/tableproperties/commands/tablebordercolorcommand.js +44 -44
  121. package/src/tableproperties/commands/tableborderstylecommand.d.ts +37 -37
  122. package/src/tableproperties/commands/tableborderstylecommand.js +44 -44
  123. package/src/tableproperties/commands/tableborderwidthcommand.d.ts +51 -51
  124. package/src/tableproperties/commands/tableborderwidthcommand.js +64 -64
  125. package/src/tableproperties/commands/tableheightcommand.d.ts +46 -46
  126. package/src/tableproperties/commands/tableheightcommand.js +54 -54
  127. package/src/tableproperties/commands/tablepropertycommand.d.ts +61 -61
  128. package/src/tableproperties/commands/tablepropertycommand.js +80 -80
  129. package/src/tableproperties/commands/tablewidthcommand.d.ts +46 -46
  130. package/src/tableproperties/commands/tablewidthcommand.js +54 -54
  131. package/src/tableproperties/tablepropertiesediting.d.ts +40 -40
  132. package/src/tableproperties/tablepropertiesediting.js +206 -206
  133. package/src/tableproperties/tablepropertiesui.d.ts +114 -114
  134. package/src/tableproperties/tablepropertiesui.js +321 -319
  135. package/src/tableproperties/ui/tablepropertiesview.d.ts +207 -203
  136. package/src/tableproperties/ui/tablepropertiesview.js +457 -455
  137. package/src/tableproperties.d.ts +30 -30
  138. package/src/tableproperties.js +34 -34
  139. package/src/tableselection.d.ts +107 -107
  140. package/src/tableselection.js +297 -297
  141. package/src/tabletoolbar.d.ts +32 -32
  142. package/src/tabletoolbar.js +57 -57
  143. package/src/tableui.d.ts +53 -53
  144. package/src/tableui.js +309 -309
  145. package/src/tableutils.d.ts +448 -448
  146. package/src/tableutils.js +1041 -1041
  147. package/src/tablewalker.d.ts +323 -323
  148. package/src/tablewalker.js +333 -333
  149. package/src/ui/colorinputview.d.ts +140 -143
  150. package/src/ui/colorinputview.js +265 -248
  151. package/src/ui/formrowview.d.ts +61 -61
  152. package/src/ui/formrowview.js +57 -57
  153. package/src/ui/inserttableview.d.ts +77 -77
  154. package/src/ui/inserttableview.js +169 -169
  155. package/src/utils/common.d.ts +42 -42
  156. package/src/utils/common.js +57 -57
  157. package/src/utils/structure.d.ts +245 -245
  158. package/src/utils/structure.js +426 -426
  159. package/src/utils/table-properties.d.ts +67 -67
  160. package/src/utils/table-properties.js +86 -86
  161. package/src/utils/ui/contextualballoon.d.ts +34 -34
  162. package/src/utils/ui/contextualballoon.js +106 -106
  163. package/src/utils/ui/table-properties.d.ts +195 -193
  164. package/src/utils/ui/table-properties.js +362 -360
  165. package/src/utils/ui/widget.d.ts +16 -16
  166. package/src/utils/ui/widget.js +38 -38
  167. package/theme/tablecaption.css +7 -7
  168. package/theme/tablecolumnresize.css +2 -2
  169. package/build/table.js.map +0 -1
@@ -1,330 +1,330 @@
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
- * Currently, only widths provided as percentage values are supported.
192
- *
193
- * @param columnWidths An array of column widths.
194
- * @returns An array of column widths guaranteed to sum up to 100%.
195
- */
196
- export function normalizeColumnWidths(columnWidths) {
197
- const widths = columnWidths.map(width => {
198
- // Possible values are 'auto' or string ending with '%'
199
- if (width === 'auto') {
200
- return width;
201
- }
202
- return parseFloat(width.replace('%', ''));
203
- });
204
- let normalizedWidths = calculateMissingColumnWidths(widths);
205
- const totalWidth = sumArray(normalizedWidths);
206
- if (totalWidth !== 100) {
207
- normalizedWidths = normalizedWidths
208
- // Adjust all the columns proportionally.
209
- .map(width => toPrecision(width * 100 / totalWidth))
210
- // Due to rounding of numbers it may happen that the sum of the widths of all columns will not be exactly 100%.
211
- // Therefore, the width of the last column is explicitly adjusted (narrowed or expanded), since all the columns
212
- // have been proportionally changed already.
213
- .map((columnWidth, columnIndex, width) => {
214
- const isLastColumn = columnIndex === width.length - 1;
215
- if (!isLastColumn) {
216
- return columnWidth;
217
- }
218
- const totalWidth = sumArray(width);
219
- return toPrecision(columnWidth + 100 - totalWidth);
220
- });
221
- }
222
- return normalizedWidths.map(width => width + '%');
223
- }
224
- /**
225
- * Initializes the column widths by parsing the attribute value and calculating the uninitialized column widths. The special value 'auto'
226
- * indicates that width for the column must be calculated. The width of such uninitialized column is calculated as follows:
227
- * - 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,
228
- * then set this width equally for all uninitialized columns.
229
- * - Otherwise, just set the minimum allowed width for all uninitialized columns. The sum of all column widths will be greater than 100%,
230
- * but then it will be adjusted proportionally to 100% in {@link #normalizeColumnWidths `normalizeColumnWidths()`}.
231
- *
232
- * @param columnWidths An array of column widths.
233
- * @returns An array with 'auto' values replaced with calculated widths.
234
- */
235
- function calculateMissingColumnWidths(columnWidths) {
236
- const numberOfUninitializedColumns = columnWidths.filter(columnWidth => columnWidth === 'auto').length;
237
- if (numberOfUninitializedColumns === 0) {
238
- return columnWidths.map(columnWidth => toPrecision(columnWidth));
239
- }
240
- const totalWidthOfInitializedColumns = sumArray(columnWidths);
241
- const widthForUninitializedColumn = Math.max((100 - totalWidthOfInitializedColumns) / numberOfUninitializedColumns, COLUMN_MIN_WIDTH_AS_PERCENTAGE);
242
- return columnWidths
243
- .map(columnWidth => columnWidth === 'auto' ? widthForUninitializedColumn : columnWidth)
244
- .map(columnWidth => toPrecision(columnWidth));
245
- }
246
- /**
247
- * Calculates the total horizontal space taken by the cell. That includes:
248
- * * width,
249
- * * left and red padding,
250
- * * border width.
251
- *
252
- * @param domCell A DOM cell element.
253
- * @returns Width in pixels without `px` at the end.
254
- */
255
- export function getDomCellOuterWidth(domCell) {
256
- const styles = global.window.getComputedStyle(domCell);
257
- // In the 'border-box' box sizing algorithm, the element's width
258
- // already includes the padding and border width (#12335).
259
- if (styles.boxSizing === 'border-box') {
260
- return parseInt(styles.width);
261
- }
262
- else {
263
- return parseFloat(styles.width) +
264
- parseFloat(styles.paddingLeft) +
265
- parseFloat(styles.paddingRight) +
266
- parseFloat(styles.borderWidth);
267
- }
268
- }
269
- /**
270
- * Updates column elements to match columns widths.
271
- *
272
- * @param columns
273
- * @param tableColumnGroup
274
- * @param normalizedWidths
275
- * @param writer
276
- */
277
- export function updateColumnElements(columns, tableColumnGroup, normalizedWidths, writer) {
278
- for (let i = 0; i < Math.max(normalizedWidths.length, columns.length); i++) {
279
- const column = columns[i];
280
- const columnWidth = normalizedWidths[i];
281
- if (!columnWidth) {
282
- // Number of `<tableColumn>` elements exceeds actual number of columns.
283
- writer.remove(column);
284
- }
285
- else if (!column) {
286
- // There is fewer `<tableColumn>` elements than actual columns.
287
- writer.appendElement('tableColumn', { columnWidth }, tableColumnGroup);
288
- }
289
- else {
290
- // Update column width.
291
- writer.setAttribute('columnWidth', columnWidth, column);
292
- }
293
- }
294
- }
295
- /**
296
- * Returns a 'tableColumnGroup' element from the 'table'.
297
- *
298
- * @internal
299
- * @param element A 'table' or 'tableColumnGroup' element.
300
- * @returns A 'tableColumnGroup' element.
301
- */
302
- export function getColumnGroupElement(element) {
303
- if (element.is('element', 'tableColumnGroup')) {
304
- return element;
305
- }
306
- const children = element.getChildren();
307
- return Array
308
- .from(children)
309
- .find(element => element.is('element', 'tableColumnGroup'));
310
- }
311
- /**
312
- * Returns an array of 'tableColumn' elements.
313
- *
314
- * @internal
315
- * @param element A 'table' or 'tableColumnGroup' element.
316
- * @returns An array of 'tableColumn' elements.
317
- */
318
- export function getTableColumnElements(element) {
319
- return Array.from(getColumnGroupElement(element).getChildren());
320
- }
321
- /**
322
- * Returns an array of table column widths.
323
- *
324
- * @internal
325
- * @param element A 'table' or 'tableColumnGroup' element.
326
- * @returns An array of table column widths.
327
- */
328
- export function getTableColumnsWidths(element) {
329
- return getTableColumnElements(element).map(column => column.getAttribute('columnWidth'));
330
- }
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.js';
6
+ import { COLUMN_WIDTH_PRECISION, COLUMN_MIN_WIDTH_AS_PERCENTAGE, COLUMN_MIN_WIDTH_IN_PIXELS } from './constants.js';
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
+ * Currently, only widths provided as percentage values are supported.
192
+ *
193
+ * @param columnWidths An array of column widths.
194
+ * @returns An array of column widths guaranteed to sum up to 100%.
195
+ */
196
+ export function normalizeColumnWidths(columnWidths) {
197
+ const widths = columnWidths.map(width => {
198
+ // Possible values are 'auto' or string ending with '%'
199
+ if (width === 'auto') {
200
+ return width;
201
+ }
202
+ return parseFloat(width.replace('%', ''));
203
+ });
204
+ let normalizedWidths = calculateMissingColumnWidths(widths);
205
+ const totalWidth = sumArray(normalizedWidths);
206
+ if (totalWidth !== 100) {
207
+ normalizedWidths = normalizedWidths
208
+ // Adjust all the columns proportionally.
209
+ .map(width => toPrecision(width * 100 / totalWidth))
210
+ // Due to rounding of numbers it may happen that the sum of the widths of all columns will not be exactly 100%.
211
+ // Therefore, the width of the last column is explicitly adjusted (narrowed or expanded), since all the columns
212
+ // have been proportionally changed already.
213
+ .map((columnWidth, columnIndex, width) => {
214
+ const isLastColumn = columnIndex === width.length - 1;
215
+ if (!isLastColumn) {
216
+ return columnWidth;
217
+ }
218
+ const totalWidth = sumArray(width);
219
+ return toPrecision(columnWidth + 100 - totalWidth);
220
+ });
221
+ }
222
+ return normalizedWidths.map(width => width + '%');
223
+ }
224
+ /**
225
+ * Initializes the column widths by parsing the attribute value and calculating the uninitialized column widths. The special value 'auto'
226
+ * indicates that width for the column must be calculated. The width of such uninitialized column is calculated as follows:
227
+ * - 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,
228
+ * then set this width equally for all uninitialized columns.
229
+ * - Otherwise, just set the minimum allowed width for all uninitialized columns. The sum of all column widths will be greater than 100%,
230
+ * but then it will be adjusted proportionally to 100% in {@link #normalizeColumnWidths `normalizeColumnWidths()`}.
231
+ *
232
+ * @param columnWidths An array of column widths.
233
+ * @returns An array with 'auto' values replaced with calculated widths.
234
+ */
235
+ function calculateMissingColumnWidths(columnWidths) {
236
+ const numberOfUninitializedColumns = columnWidths.filter(columnWidth => columnWidth === 'auto').length;
237
+ if (numberOfUninitializedColumns === 0) {
238
+ return columnWidths.map(columnWidth => toPrecision(columnWidth));
239
+ }
240
+ const totalWidthOfInitializedColumns = sumArray(columnWidths);
241
+ const widthForUninitializedColumn = Math.max((100 - totalWidthOfInitializedColumns) / numberOfUninitializedColumns, COLUMN_MIN_WIDTH_AS_PERCENTAGE);
242
+ return columnWidths
243
+ .map(columnWidth => columnWidth === 'auto' ? widthForUninitializedColumn : columnWidth)
244
+ .map(columnWidth => toPrecision(columnWidth));
245
+ }
246
+ /**
247
+ * Calculates the total horizontal space taken by the cell. That includes:
248
+ * * width,
249
+ * * left and red padding,
250
+ * * border width.
251
+ *
252
+ * @param domCell A DOM cell element.
253
+ * @returns Width in pixels without `px` at the end.
254
+ */
255
+ export function getDomCellOuterWidth(domCell) {
256
+ const styles = global.window.getComputedStyle(domCell);
257
+ // In the 'border-box' box sizing algorithm, the element's width
258
+ // already includes the padding and border width (#12335).
259
+ if (styles.boxSizing === 'border-box') {
260
+ return parseInt(styles.width);
261
+ }
262
+ else {
263
+ return parseFloat(styles.width) +
264
+ parseFloat(styles.paddingLeft) +
265
+ parseFloat(styles.paddingRight) +
266
+ parseFloat(styles.borderWidth);
267
+ }
268
+ }
269
+ /**
270
+ * Updates column elements to match columns widths.
271
+ *
272
+ * @param columns
273
+ * @param tableColumnGroup
274
+ * @param normalizedWidths
275
+ * @param writer
276
+ */
277
+ export function updateColumnElements(columns, tableColumnGroup, normalizedWidths, writer) {
278
+ for (let i = 0; i < Math.max(normalizedWidths.length, columns.length); i++) {
279
+ const column = columns[i];
280
+ const columnWidth = normalizedWidths[i];
281
+ if (!columnWidth) {
282
+ // Number of `<tableColumn>` elements exceeds actual number of columns.
283
+ writer.remove(column);
284
+ }
285
+ else if (!column) {
286
+ // There is fewer `<tableColumn>` elements than actual columns.
287
+ writer.appendElement('tableColumn', { columnWidth }, tableColumnGroup);
288
+ }
289
+ else {
290
+ // Update column width.
291
+ writer.setAttribute('columnWidth', columnWidth, column);
292
+ }
293
+ }
294
+ }
295
+ /**
296
+ * Returns a 'tableColumnGroup' element from the 'table'.
297
+ *
298
+ * @internal
299
+ * @param element A 'table' or 'tableColumnGroup' element.
300
+ * @returns A 'tableColumnGroup' element.
301
+ */
302
+ export function getColumnGroupElement(element) {
303
+ if (element.is('element', 'tableColumnGroup')) {
304
+ return element;
305
+ }
306
+ const children = element.getChildren();
307
+ return Array
308
+ .from(children)
309
+ .find(element => element.is('element', 'tableColumnGroup'));
310
+ }
311
+ /**
312
+ * Returns an array of 'tableColumn' elements.
313
+ *
314
+ * @internal
315
+ * @param element A 'table' or 'tableColumnGroup' element.
316
+ * @returns An array of 'tableColumn' elements.
317
+ */
318
+ export function getTableColumnElements(element) {
319
+ return Array.from(getColumnGroupElement(element).getChildren());
320
+ }
321
+ /**
322
+ * Returns an array of table column widths.
323
+ *
324
+ * @internal
325
+ * @param element A 'table' or 'tableColumnGroup' element.
326
+ * @returns An array of table column widths.
327
+ */
328
+ export function getTableColumnsWidths(element) {
329
+ return getTableColumnElements(element).map(column => column.getAttribute('columnWidth'));
330
+ }