@ckeditor/ckeditor5-table 31.0.0 → 33.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +2 -2
- package/build/table.js +3 -3
- package/build/table.js.map +1 -0
- package/build/translations/cs.js +1 -1
- package/build/translations/el.js +1 -0
- package/build/translations/es.js +1 -1
- package/build/translations/id.js +1 -1
- package/build/translations/pt-br.js +1 -1
- package/build/translations/uz.js +1 -0
- package/build/translations/zh.js +1 -1
- package/lang/translations/ar.po +1 -1
- package/lang/translations/az.po +1 -1
- package/lang/translations/bg.po +1 -1
- package/lang/translations/cs.po +4 -4
- package/lang/translations/da.po +1 -1
- package/lang/translations/de-ch.po +1 -1
- package/lang/translations/de.po +1 -1
- package/lang/translations/el.po +261 -0
- package/lang/translations/en-au.po +1 -1
- package/lang/translations/en-gb.po +1 -1
- package/lang/translations/en.po +1 -1
- package/lang/translations/es.po +43 -43
- package/lang/translations/et.po +1 -1
- package/lang/translations/fa.po +1 -1
- package/lang/translations/fi.po +1 -1
- package/lang/translations/fr.po +1 -1
- package/lang/translations/gl.po +1 -1
- package/lang/translations/hi.po +1 -1
- package/lang/translations/hr.po +1 -1
- package/lang/translations/hu.po +1 -1
- package/lang/translations/id.po +11 -11
- package/lang/translations/it.po +1 -1
- package/lang/translations/ja.po +1 -1
- package/lang/translations/ko.po +1 -1
- package/lang/translations/ku.po +1 -1
- package/lang/translations/lt.po +1 -1
- package/lang/translations/lv.po +1 -1
- package/lang/translations/nb.po +1 -1
- package/lang/translations/ne.po +1 -1
- package/lang/translations/nl.po +1 -1
- package/lang/translations/no.po +1 -1
- package/lang/translations/pl.po +1 -1
- package/lang/translations/pt-br.po +4 -4
- package/lang/translations/ro.po +1 -1
- package/lang/translations/ru.po +1 -1
- package/lang/translations/sk.po +1 -1
- package/lang/translations/sq.po +1 -1
- package/lang/translations/sr-latn.po +1 -1
- package/lang/translations/sr.po +1 -1
- package/lang/translations/sv.po +1 -1
- package/lang/translations/th.po +1 -1
- package/lang/translations/tk.po +1 -1
- package/lang/translations/tr.po +1 -1
- package/lang/translations/ug.po +1 -1
- package/lang/translations/uk.po +1 -1
- package/lang/translations/uz.po +261 -0
- package/lang/translations/vi.po +1 -1
- package/lang/translations/zh-cn.po +1 -1
- package/lang/translations/zh.po +4 -4
- package/package.json +26 -24
- package/src/commands/insertcolumncommand.js +5 -5
- package/src/commands/insertrowcommand.js +5 -5
- package/src/commands/inserttablecommand.js +1 -1
- package/src/commands/mergecellcommand.js +5 -6
- package/src/commands/mergecellscommand.js +6 -5
- package/src/commands/removecolumncommand.js +9 -8
- package/src/commands/removerowcommand.js +6 -7
- package/src/commands/selectcolumncommand.js +5 -5
- package/src/commands/selectrowcommand.js +6 -6
- package/src/commands/setheadercolumncommand.js +6 -5
- package/src/commands/setheaderrowcommand.js +8 -5
- package/src/commands/splitcellcommand.js +5 -5
- package/src/converters/downcast.js +77 -408
- package/src/converters/table-caption-post-fixer.js +1 -1
- package/src/converters/table-cell-paragraph-post-fixer.js +1 -1
- package/src/converters/{table-cell-refresh-post-fixer.js → table-cell-refresh-handler.js} +9 -20
- package/src/converters/table-headings-refresh-handler.js +68 -0
- package/src/converters/table-layout-post-fixer.js +1 -1
- package/src/converters/tableproperties.js +6 -5
- package/src/converters/upcasttable.js +7 -3
- package/src/index.js +1 -1
- package/src/plaintableoutput.js +151 -0
- package/src/table.js +1 -1
- package/src/tablecaption/tablecaptionediting.js +1 -1
- package/src/tablecaption/tablecaptionui.js +3 -3
- package/src/tablecaption/toggletablecaptioncommand.js +3 -3
- package/src/tablecaption/utils.js +1 -1
- package/src/tablecaption.js +1 -1
- package/src/tablecellproperties/commands/tablecellbackgroundcolorcommand.js +2 -2
- package/src/tablecellproperties/commands/tablecellbordercolorcommand.js +2 -2
- package/src/tablecellproperties/commands/tablecellborderstylecommand.js +2 -2
- package/src/tablecellproperties/commands/tablecellborderwidthcommand.js +2 -2
- package/src/tablecellproperties/commands/tablecellheightcommand.js +2 -2
- package/src/tablecellproperties/commands/tablecellhorizontalalignmentcommand.js +2 -2
- package/src/tablecellproperties/commands/tablecellpaddingcommand.js +2 -2
- package/src/tablecellproperties/commands/tablecellpropertycommand.js +6 -5
- package/src/tablecellproperties/commands/tablecellverticalalignmentcommand.js +2 -2
- package/src/tablecellproperties/commands/tablecellwidthcommand.js +2 -2
- package/src/tablecellproperties/tablecellpropertiesediting.js +42 -35
- package/src/tablecellproperties/tablecellpropertiesui.js +1 -1
- package/src/tablecellproperties/ui/tablecellpropertiesview.js +11 -1
- package/src/tablecellproperties.js +1 -1
- package/src/tableclipboard.js +19 -16
- package/src/tableediting.js +49 -28
- package/src/tablekeyboard.js +7 -6
- package/src/tablemouse/mouseeventsobserver.js +1 -1
- package/src/tablemouse.js +7 -5
- package/src/tableproperties/commands/tablealignmentcommand.js +2 -2
- package/src/tableproperties/commands/tablebackgroundcolorcommand.js +2 -2
- package/src/tableproperties/commands/tablebordercolorcommand.js +2 -2
- package/src/tableproperties/commands/tableborderstylecommand.js +2 -2
- package/src/tableproperties/commands/tableborderwidthcommand.js +2 -2
- package/src/tableproperties/commands/tableheightcommand.js +2 -2
- package/src/tableproperties/commands/tablepropertycommand.js +2 -2
- package/src/tableproperties/commands/tablewidthcommand.js +2 -2
- package/src/tableproperties/tablepropertiesediting.js +29 -21
- package/src/tableproperties/tablepropertiesui.js +1 -1
- package/src/tableproperties/ui/tablepropertiesview.js +11 -1
- package/src/tableproperties.js +1 -1
- package/src/tableselection.js +10 -9
- package/src/tabletoolbar.js +1 -1
- package/src/tableui.js +1 -1
- package/src/tableutils.js +311 -1
- package/src/tablewalker.js +1 -1
- package/src/ui/colorinputview.js +1 -1
- package/src/ui/formrowview.js +1 -1
- package/src/ui/inserttableview.js +1 -1
- package/src/utils/common.js +1 -1
- package/src/utils/structure.js +1 -1
- package/src/utils/table-properties.js +1 -1
- package/src/utils/ui/contextualballoon.js +1 -1
- package/src/utils/ui/table-properties.js +1 -1
- package/src/utils/ui/widget.js +1 -1
- package/theme/colorinput.css +1 -1
- package/theme/form.css +1 -1
- package/theme/formrow.css +1 -1
- package/theme/inserttable.css +1 -1
- package/theme/table.css +2 -2
- package/theme/tablecaption.css +1 -1
- package/theme/tablecellproperties.css +1 -1
- package/theme/tableediting.css +1 -1
- package/theme/tableform.css +1 -1
- package/theme/tableproperties.css +1 -1
- package/theme/tableselection.css +1 -1
- package/src/converters/table-heading-rows-refresh-post-fixer.js +0 -72
- package/src/utils/selection.js +0 -276
package/src/utils/structure.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Copyright (c) 2003-
|
|
2
|
+
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Copyright (c) 2003-
|
|
2
|
+
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Copyright (c) 2003-
|
|
2
|
+
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Copyright (c) 2003-
|
|
2
|
+
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
5
|
|
package/src/utils/ui/widget.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Copyright (c) 2003-
|
|
2
|
+
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
5
|
|
package/theme/colorinput.css
CHANGED
package/theme/form.css
CHANGED
package/theme/formrow.css
CHANGED
package/theme/inserttable.css
CHANGED
package/theme/table.css
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright (c) 2003-
|
|
2
|
+
* Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/* Text alignment of the table header should match the editor settings and override the native browser styling,
|
|
46
|
-
when content is available outside the
|
|
46
|
+
when content is available outside the editor. See https://github.com/ckeditor/ckeditor5/issues/6638 */
|
|
47
47
|
.ck-content[dir="rtl"] .table th {
|
|
48
48
|
text-align: right;
|
|
49
49
|
}
|
package/theme/tablecaption.css
CHANGED
package/theme/tableediting.css
CHANGED
package/theme/tableform.css
CHANGED
package/theme/tableselection.css
CHANGED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
|
|
3
|
-
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @module table/converters/table-heading-rows-refresh-post-fixer
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Injects a table post-fixer into the model which marks the table in the differ to have it re-rendered.
|
|
12
|
-
*
|
|
13
|
-
* Table heading rows are represented in the model by a `headingRows` attribute. However, in the view, it's represented as separate
|
|
14
|
-
* sections of the table (`<thead>` or `<tbody>`) and changing `headingRows` attribute requires moving table rows between two sections.
|
|
15
|
-
* This causes problems with structural changes in a table (like adding and removing rows) thus atomic converters cannot be used.
|
|
16
|
-
*
|
|
17
|
-
* When table `headingRows` attribute changes, the entire table is re-rendered.
|
|
18
|
-
*
|
|
19
|
-
* @param {module:engine/model/model~Model} model
|
|
20
|
-
*/
|
|
21
|
-
export default function injectTableHeadingRowsRefreshPostFixer( model ) {
|
|
22
|
-
model.document.registerPostFixer( () => tableHeadingRowsRefreshPostFixer( model ) );
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function tableHeadingRowsRefreshPostFixer( model ) {
|
|
26
|
-
const differ = model.document.differ;
|
|
27
|
-
|
|
28
|
-
// Stores tables to be refreshed so the table will be refreshed once for multiple changes.
|
|
29
|
-
const tablesToRefresh = new Set();
|
|
30
|
-
|
|
31
|
-
for ( const change of differ.getChanges() ) {
|
|
32
|
-
if ( change.type === 'attribute' ) {
|
|
33
|
-
const element = change.range.start.nodeAfter;
|
|
34
|
-
|
|
35
|
-
if ( element && element.is( 'element', 'table' ) && change.attributeKey === 'headingRows' ) {
|
|
36
|
-
tablesToRefresh.add( element );
|
|
37
|
-
}
|
|
38
|
-
} else {
|
|
39
|
-
/* istanbul ignore else */
|
|
40
|
-
if ( change.type === 'insert' || change.type === 'remove' ) {
|
|
41
|
-
if ( change.name === 'tableRow' ) {
|
|
42
|
-
const table = change.position.findAncestor( 'table' );
|
|
43
|
-
const headingRows = table.getAttribute( 'headingRows' ) || 0;
|
|
44
|
-
|
|
45
|
-
if ( change.position.offset < headingRows ) {
|
|
46
|
-
tablesToRefresh.add( table );
|
|
47
|
-
}
|
|
48
|
-
} else if ( change.name === 'tableCell' ) {
|
|
49
|
-
const table = change.position.findAncestor( 'table' );
|
|
50
|
-
const headingColumns = table.getAttribute( 'headingColumns' ) || 0;
|
|
51
|
-
|
|
52
|
-
if ( change.position.offset < headingColumns ) {
|
|
53
|
-
tablesToRefresh.add( table );
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if ( tablesToRefresh.size ) {
|
|
61
|
-
// @if CK_DEBUG_TABLE // console.log( `Post-fixing table: refreshing heading rows (${ tablesToRefresh.size }).` );
|
|
62
|
-
|
|
63
|
-
for ( const table of tablesToRefresh.values() ) {
|
|
64
|
-
// Should be handled by a `triggerBy` configuration. See: https://github.com/ckeditor/ckeditor5/issues/8138.
|
|
65
|
-
differ.refreshItem( table );
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return true;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return false;
|
|
72
|
-
}
|
package/src/utils/selection.js
DELETED
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
|
|
3
|
-
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @module table/utils/selection
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import TableWalker from '../tablewalker';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Returns all model table cells that are fully selected (from the outside)
|
|
14
|
-
* within the provided model selection's ranges.
|
|
15
|
-
*
|
|
16
|
-
* To obtain the cells selected from the inside, use
|
|
17
|
-
* {@link module:table/utils/selection~getTableCellsContainingSelection}.
|
|
18
|
-
*
|
|
19
|
-
* @param {module:engine/model/selection~Selection} selection
|
|
20
|
-
* @returns {Array.<module:engine/model/element~Element>}
|
|
21
|
-
*/
|
|
22
|
-
export function getSelectedTableCells( selection ) {
|
|
23
|
-
const cells = [];
|
|
24
|
-
|
|
25
|
-
for ( const range of sortRanges( selection.getRanges() ) ) {
|
|
26
|
-
const element = range.getContainedElement();
|
|
27
|
-
|
|
28
|
-
if ( element && element.is( 'element', 'tableCell' ) ) {
|
|
29
|
-
cells.push( element );
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return cells;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Returns all model table cells that the provided model selection's ranges
|
|
38
|
-
* {@link module:engine/model/range~Range#start} inside.
|
|
39
|
-
*
|
|
40
|
-
* To obtain the cells selected from the outside, use
|
|
41
|
-
* {@link module:table/utils/selection~getSelectedTableCells}.
|
|
42
|
-
*
|
|
43
|
-
* @param {module:engine/model/selection~Selection} selection
|
|
44
|
-
* @returns {Array.<module:engine/model/element~Element>}
|
|
45
|
-
*/
|
|
46
|
-
export function getTableCellsContainingSelection( selection ) {
|
|
47
|
-
const cells = [];
|
|
48
|
-
|
|
49
|
-
for ( const range of selection.getRanges() ) {
|
|
50
|
-
const cellWithSelection = range.start.findAncestor( 'tableCell' );
|
|
51
|
-
|
|
52
|
-
if ( cellWithSelection ) {
|
|
53
|
-
cells.push( cellWithSelection );
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return cells;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Returns all model table cells that are either completely selected
|
|
62
|
-
* by selection ranges or host selection range
|
|
63
|
-
* {@link module:engine/model/range~Range#start start positions} inside them.
|
|
64
|
-
*
|
|
65
|
-
* Combines {@link module:table/utils/selection~getTableCellsContainingSelection} and
|
|
66
|
-
* {@link module:table/utils/selection~getSelectedTableCells}.
|
|
67
|
-
*
|
|
68
|
-
* @param {module:engine/model/selection~Selection} selection
|
|
69
|
-
* @returns {Array.<module:engine/model/element~Element>}
|
|
70
|
-
*/
|
|
71
|
-
export function getSelectionAffectedTableCells( selection ) {
|
|
72
|
-
const selectedCells = getSelectedTableCells( selection );
|
|
73
|
-
|
|
74
|
-
if ( selectedCells.length ) {
|
|
75
|
-
return selectedCells;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return getTableCellsContainingSelection( selection );
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Returns an object with the `first` and `last` row index contained in the given `tableCells`.
|
|
83
|
-
*
|
|
84
|
-
* const selectedTableCells = getSelectedTableCells( editor.model.document.selection );
|
|
85
|
-
*
|
|
86
|
-
* const { first, last } = getRowIndexes( selectedTableCells );
|
|
87
|
-
*
|
|
88
|
-
* console.log( `Selected rows: ${ first } to ${ last }` );
|
|
89
|
-
*
|
|
90
|
-
* @param {Array.<module:engine/model/element~Element>} tableCells
|
|
91
|
-
* @returns {Object} Returns an object with the `first` and `last` table row indexes.
|
|
92
|
-
*/
|
|
93
|
-
export function getRowIndexes( tableCells ) {
|
|
94
|
-
const indexes = tableCells.map( cell => cell.parent.index );
|
|
95
|
-
|
|
96
|
-
return getFirstLastIndexesObject( indexes );
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Returns an object with the `first` and `last` column index contained in the given `tableCells`.
|
|
101
|
-
*
|
|
102
|
-
* const selectedTableCells = getSelectedTableCells( editor.model.document.selection );
|
|
103
|
-
*
|
|
104
|
-
* const { first, last } = getColumnIndexes( selectedTableCells );
|
|
105
|
-
*
|
|
106
|
-
* console.log( `Selected columns: ${ first } to ${ last }` );
|
|
107
|
-
*
|
|
108
|
-
* @param {Array.<module:engine/model/element~Element>} tableCells
|
|
109
|
-
* @returns {Object} Returns an object with the `first` and `last` table column indexes.
|
|
110
|
-
*/
|
|
111
|
-
export function getColumnIndexes( tableCells ) {
|
|
112
|
-
const table = tableCells[ 0 ].findAncestor( 'table' );
|
|
113
|
-
const tableMap = [ ...new TableWalker( table ) ];
|
|
114
|
-
|
|
115
|
-
const indexes = tableMap
|
|
116
|
-
.filter( entry => tableCells.includes( entry.cell ) )
|
|
117
|
-
.map( entry => entry.column );
|
|
118
|
-
|
|
119
|
-
return getFirstLastIndexesObject( indexes );
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Checks if the selection contains cells that do not exceed rectangular selection.
|
|
124
|
-
*
|
|
125
|
-
* In a table below:
|
|
126
|
-
*
|
|
127
|
-
* ┌───┬───┬───┬───┐
|
|
128
|
-
* │ a │ b │ c │ d │
|
|
129
|
-
* ├───┴───┼───┤ │
|
|
130
|
-
* │ e │ f │ │
|
|
131
|
-
* │ ├───┼───┤
|
|
132
|
-
* │ │ g │ h │
|
|
133
|
-
* └───────┴───┴───┘
|
|
134
|
-
*
|
|
135
|
-
* Valid selections are these which create a solid rectangle (without gaps), such as:
|
|
136
|
-
* - a, b (two horizontal cells)
|
|
137
|
-
* - c, f (two vertical cells)
|
|
138
|
-
* - a, b, e (cell "e" spans over four cells)
|
|
139
|
-
* - c, d, f (cell d spans over a cell in the row below)
|
|
140
|
-
*
|
|
141
|
-
* While an invalid selection would be:
|
|
142
|
-
* - a, c (the unselected cell "b" creates a gap)
|
|
143
|
-
* - f, g, h (cell "d" spans over a cell from the row of "f" cell - thus creates a gap)
|
|
144
|
-
*
|
|
145
|
-
* @param {Array.<module:engine/model/element~Element>} selectedTableCells
|
|
146
|
-
* @param {module:table/tableutils~TableUtils} tableUtils
|
|
147
|
-
* @returns {Boolean}
|
|
148
|
-
*/
|
|
149
|
-
export function isSelectionRectangular( selectedTableCells, tableUtils ) {
|
|
150
|
-
if ( selectedTableCells.length < 2 || !areCellInTheSameTableSection( selectedTableCells ) ) {
|
|
151
|
-
return false;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// A valid selection is a fully occupied rectangle composed of table cells.
|
|
155
|
-
// Below we will calculate the area of a selected table cells and the area of valid selection.
|
|
156
|
-
// The area of a valid selection is defined by top-left and bottom-right cells.
|
|
157
|
-
const rows = new Set();
|
|
158
|
-
const columns = new Set();
|
|
159
|
-
|
|
160
|
-
let areaOfSelectedCells = 0;
|
|
161
|
-
|
|
162
|
-
for ( const tableCell of selectedTableCells ) {
|
|
163
|
-
const { row, column } = tableUtils.getCellLocation( tableCell );
|
|
164
|
-
const rowspan = parseInt( tableCell.getAttribute( 'rowspan' ) || 1 );
|
|
165
|
-
const colspan = parseInt( tableCell.getAttribute( 'colspan' ) || 1 );
|
|
166
|
-
|
|
167
|
-
// Record row & column indexes of current cell.
|
|
168
|
-
rows.add( row );
|
|
169
|
-
columns.add( column );
|
|
170
|
-
|
|
171
|
-
// For cells that spans over multiple rows add also the last row that this cell spans over.
|
|
172
|
-
if ( rowspan > 1 ) {
|
|
173
|
-
rows.add( row + rowspan - 1 );
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// For cells that spans over multiple columns add also the last column that this cell spans over.
|
|
177
|
-
if ( colspan > 1 ) {
|
|
178
|
-
columns.add( column + colspan - 1 );
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
areaOfSelectedCells += ( rowspan * colspan );
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// We can only merge table cells that are in adjacent rows...
|
|
185
|
-
const areaOfValidSelection = getBiggestRectangleArea( rows, columns );
|
|
186
|
-
|
|
187
|
-
return areaOfValidSelection == areaOfSelectedCells;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Returns array of sorted ranges.
|
|
192
|
-
*
|
|
193
|
-
* @param {Iterable.<module:engine/model/range~Range>} ranges
|
|
194
|
-
* @return {Array.<module:engine/model/range~Range>}
|
|
195
|
-
*/
|
|
196
|
-
export function sortRanges( ranges ) {
|
|
197
|
-
return Array.from( ranges ).sort( compareRangeOrder );
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Helper method to get an object with `first` and `last` indexes from an unsorted array of indexes.
|
|
201
|
-
function getFirstLastIndexesObject( indexes ) {
|
|
202
|
-
const allIndexesSorted = indexes.sort( ( indexA, indexB ) => indexA - indexB );
|
|
203
|
-
|
|
204
|
-
const first = allIndexesSorted[ 0 ];
|
|
205
|
-
const last = allIndexesSorted[ allIndexesSorted.length - 1 ];
|
|
206
|
-
|
|
207
|
-
return { first, last };
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function compareRangeOrder( rangeA, rangeB ) {
|
|
211
|
-
// Since table cell ranges are disjoint, it's enough to check their start positions.
|
|
212
|
-
const posA = rangeA.start;
|
|
213
|
-
const posB = rangeB.start;
|
|
214
|
-
|
|
215
|
-
// Checking for equal position (returning 0) is not needed because this would be either:
|
|
216
|
-
// a. Intersecting range (not allowed by model)
|
|
217
|
-
// b. Collapsed range on the same position (allowed by model but should not happen).
|
|
218
|
-
return posA.isBefore( posB ) ? -1 : 1;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Calculates the area of a maximum rectangle that can span over the provided row & column indexes.
|
|
222
|
-
//
|
|
223
|
-
// @param {Array.<Number>} rows
|
|
224
|
-
// @param {Array.<Number>} columns
|
|
225
|
-
// @returns {Number}
|
|
226
|
-
function getBiggestRectangleArea( rows, columns ) {
|
|
227
|
-
const rowsIndexes = Array.from( rows.values() );
|
|
228
|
-
const columnIndexes = Array.from( columns.values() );
|
|
229
|
-
|
|
230
|
-
const lastRow = Math.max( ...rowsIndexes );
|
|
231
|
-
const firstRow = Math.min( ...rowsIndexes );
|
|
232
|
-
const lastColumn = Math.max( ...columnIndexes );
|
|
233
|
-
const firstColumn = Math.min( ...columnIndexes );
|
|
234
|
-
|
|
235
|
-
return ( lastRow - firstRow + 1 ) * ( lastColumn - firstColumn + 1 );
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Checks if the selection does not mix a header (column or row) with other cells.
|
|
239
|
-
//
|
|
240
|
-
// For instance, in the table below valid selections consist of cells with the same letter only.
|
|
241
|
-
// So, a-a (same heading row and column) or d-d (body cells) are valid while c-d or a-b are not.
|
|
242
|
-
//
|
|
243
|
-
// header columns
|
|
244
|
-
// ↓ ↓
|
|
245
|
-
// ┌───┬───┬───┬───┐
|
|
246
|
-
// │ a │ a │ b │ b │ ← header row
|
|
247
|
-
// ├───┼───┼───┼───┤
|
|
248
|
-
// │ c │ c │ d │ d │
|
|
249
|
-
// ├───┼───┼───┼───┤
|
|
250
|
-
// │ c │ c │ d │ d │
|
|
251
|
-
// └───┴───┴───┴───┘
|
|
252
|
-
function areCellInTheSameTableSection( tableCells ) {
|
|
253
|
-
const table = tableCells[ 0 ].findAncestor( 'table' );
|
|
254
|
-
|
|
255
|
-
const rowIndexes = getRowIndexes( tableCells );
|
|
256
|
-
const headingRows = parseInt( table.getAttribute( 'headingRows' ) || 0 );
|
|
257
|
-
|
|
258
|
-
// Calculating row indexes is a bit cheaper so if this check fails we can't merge.
|
|
259
|
-
if ( !areIndexesInSameSection( rowIndexes, headingRows ) ) {
|
|
260
|
-
return false;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
const headingColumns = parseInt( table.getAttribute( 'headingColumns' ) || 0 );
|
|
264
|
-
const columnIndexes = getColumnIndexes( tableCells );
|
|
265
|
-
|
|
266
|
-
// Similarly cells must be in same column section.
|
|
267
|
-
return areIndexesInSameSection( columnIndexes, headingColumns );
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Unified check if table rows/columns indexes are in the same heading/body section.
|
|
271
|
-
function areIndexesInSameSection( { first, last }, headingSectionSize ) {
|
|
272
|
-
const firstCellIsInHeading = first < headingSectionSize;
|
|
273
|
-
const lastCellIsInHeading = last < headingSectionSize;
|
|
274
|
-
|
|
275
|
-
return firstCellIsInHeading === lastCellIsInHeading;
|
|
276
|
-
}
|