Bladeren bron

Fixed: SetTableHeadersCommand should split rowspanned cells that overlaps thead section after changing heading rows.

Maciej Gołaszewski 7 jaren geleden
bovenliggende
commit
535598eab7

+ 24 - 1
packages/ckeditor5-table/src/commands/settableheaderscommand.js

@@ -8,7 +8,8 @@
  */
 
 import Command from '@ckeditor/ckeditor5-core/src/command';
-import { getParentTable } from './utils';
+import { getParentTable, unsplitVertically } from './utils';
+import TableWalker from '../tablewalker';
 
 /**
  * The set table headers command.
@@ -49,6 +50,28 @@ export default class SetTableHeadersCommand extends Command {
 		const table = getParentTable( selection.getFirstPosition() );
 
 		model.change( writer => {
+			const oldValue = parseInt( table.getAttribute( 'headingRows' ) || 0 );
+
+			if ( oldValue !== rows && rows > 0 ) {
+				const cellsToSplit = [];
+
+				const startAnalysisRow = rows > oldValue ? oldValue : 0;
+
+				for ( const tableWalkerValue of new TableWalker( table, { startRow: startAnalysisRow, endRow: rows } ) ) {
+					const rowspan = tableWalkerValue.rowspan;
+					const row = tableWalkerValue.row;
+
+					if ( rowspan > 1 && row + rowspan > rows ) {
+						cellsToSplit.push( tableWalkerValue.cell );
+					}
+				}
+
+				for ( const tableCell of cellsToSplit ) {
+					unsplitVertically( tableCell, writer );
+					writer.removeAttribute( 'rowspan', tableCell );
+				}
+			}
+
 			updateTableAttribute( table, 'headingRows', rows, writer );
 			updateTableAttribute( table, 'headingColumns', columns, writer );
 		} );

+ 2 - 39
packages/ckeditor5-table/src/commands/splitcellcommand.js

@@ -9,7 +9,7 @@
 
 import Command from '@ckeditor/ckeditor5-core/src/command';
 import Position from '@ckeditor/ckeditor5-engine/src/model/position';
-import TableWalker from '../tablewalker';
+import { unsplitVertically } from './utils';
 
 /**
  * The split cell command.
@@ -47,44 +47,7 @@ export default class SplitCellCommand extends Command {
 
 		model.change( writer => {
 			if ( rowspan > 1 ) {
-				const tableRow = tableCell.parent;
-				const table = tableRow.parent;
-
-				const startRow = table.getChildIndex( tableRow );
-				const endRow = startRow + rowspan - 1;
-
-				const options = { startRow, endRow, includeSpanned: true };
-
-				const tableWalker = new TableWalker( table, options );
-
-				let columnIndex;
-				let previousCell;
-				let cellsToInsert;
-
-				for ( const tableWalkerInfo of tableWalker ) {
-					if ( tableWalkerInfo.cell ) {
-						previousCell = tableWalkerInfo.cell;
-					}
-
-					if ( tableWalkerInfo.cell === tableCell ) {
-						columnIndex = tableWalkerInfo.column;
-						cellsToInsert = tableWalkerInfo.colspan;
-					}
-
-					if ( columnIndex !== undefined && columnIndex === tableWalkerInfo.column && tableWalkerInfo.row > startRow ) {
-						const insertRow = table.getChild( tableWalkerInfo.row );
-
-						if ( previousCell.parent === insertRow ) {
-							for ( let i = 0; i < cellsToInsert; i++ ) {
-								writer.insertElement( 'tableCell', Position.createAfter( previousCell ) );
-							}
-						} else {
-							for ( let i = 0; i < cellsToInsert; i++ ) {
-								writer.insertElement( 'tableCell', Position.createAt( insertRow ) );
-							}
-						}
-					}
-				}
+				unsplitVertically( tableCell, writer, { breakHorizontally: true } );
 			}
 
 			if ( colspan > 1 ) {

+ 59 - 0
packages/ckeditor5-table/src/commands/utils.js

@@ -7,6 +7,9 @@
  * @module table/commands/utils
  */
 
+import Position from '@ckeditor/ckeditor5-engine/src/model/position';
+import TableWalker from '../tablewalker';
+
 /**
  * Returns parent table.
  *
@@ -40,3 +43,59 @@ export function getColumns( table ) {
 		return columns + ( columnWidth );
 	}, 0 );
 }
+
+/**
+ * Splits table cell vertically.
+ *
+ * @param {module:engine/model/element} tableCell
+ * @param writer
+ * @param {Object} [options]
+ * @param {Boolean} [options.breakHorizontally=false]
+ */
+export function unsplitVertically( tableCell, writer, options = {} ) {
+	const tableRow = tableCell.parent;
+	const table = tableRow.parent;
+
+	const rowspan = parseInt( tableCell.getAttribute( 'rowspan' ) );
+
+	const startRow = table.getChildIndex( tableRow );
+	const endRow = startRow + rowspan - 1;
+
+	const tableWalker = new TableWalker( table, { startRow, endRow, includeSpanned: true } );
+
+	let columnIndex;
+	let previousCell;
+	let cellsToInsert;
+
+	const breakHorizontally = !!options.breakHorizontally;
+	const attributes = {};
+
+	for ( const tableWalkerInfo of tableWalker ) {
+		if ( tableWalkerInfo.cell ) {
+			previousCell = tableWalkerInfo.cell;
+		}
+
+		if ( tableWalkerInfo.cell === tableCell ) {
+			columnIndex = tableWalkerInfo.column;
+			cellsToInsert = breakHorizontally ? tableWalkerInfo.colspan : 1;
+
+			if ( !breakHorizontally && tableWalkerInfo.colspan > 1 ) {
+				attributes.colspan = tableWalkerInfo.colspan;
+			}
+		}
+
+		if ( columnIndex !== undefined && columnIndex === tableWalkerInfo.column && tableWalkerInfo.row > startRow ) {
+			const insertRow = table.getChild( tableWalkerInfo.row );
+
+			if ( previousCell.parent === insertRow ) {
+				for ( let i = 0; i < cellsToInsert; i++ ) {
+					writer.insertElement( 'tableCell', attributes, Position.createAfter( previousCell ) );
+				}
+			} else {
+				for ( let i = 0; i < cellsToInsert; i++ ) {
+					writer.insertElement( 'tableCell', attributes, Position.createAt( insertRow ) );
+				}
+			}
+		}
+	}
+}

+ 30 - 0
packages/ckeditor5-table/tests/commands/settableheaderscommand.js

@@ -161,5 +161,35 @@ describe( 'SetTableHeadersCommand', () => {
 				[ '20', '21' ]
 			] ) );
 		} );
+
+		it( 'should fix rowspaned cells on the edge of an table head section', () => {
+			setData( model, modelTable( [
+				[ '00', '01', '02' ],
+				[ { colspan: 2, rowspan: 2, contents: '10[]' }, '12' ],
+				[ '22' ]
+			], { headingColumns: 2, headingRows: 1 } ) );
+
+			command.execute( { rows: 2, columns: 2 } );
+
+			expect( formatTable( getData( model ) ) ).to.equal( formattedModelTable( [
+				[ '00', '01', '02' ],
+				[ { colspan: 2, contents: '10[]' }, '12' ],
+				[ { colspan: 2, contents: '' }, '22' ]
+			], { headingColumns: 2, headingRows: 2 } ) );
+		} );
+
+		it( 'should fix rowspaned cells on the edge of an table head section when creating section', () => {
+			setData( model, modelTable( [
+				[ { rowspan: 2, contents: '[]00' }, '01' ],
+				[ '11' ]
+			], { headingRows: 2 } ) );
+
+			command.execute( { rows: 1 } );
+
+			expect( formatTable( getData( model ) ) ).to.equal( formattedModelTable( [
+				[ '[]00', '01' ],
+				[ '', '11' ],
+			], { headingRows: 1 } ) );
+		} );
 	} );
 } );

+ 4 - 4
packages/ckeditor5-table/tests/commands/splitcellcommand.js

@@ -131,7 +131,7 @@ describe( 'SplitCellCommand', () => {
 		it( 'should split table cell with rowspan in the middle of a table', () => {
 			setData( model, modelTable( [
 				[ '11', { rowspan: 3, contents: '[]12' }, '13' ],
-				[ { rowspan: 2, contents: '[]21' }, '23' ],
+				[ { rowspan: 2, contents: '21' }, '23' ],
 				[ '33' ]
 			] ) );
 
@@ -139,7 +139,7 @@ describe( 'SplitCellCommand', () => {
 
 			expect( formatTable( getData( model ) ) ).to.equal( formattedModelTable( [
 				[ '11', '[]12', '13' ],
-				[ { rowspan: 2, contents: '[]21' }, '', '23' ],
+				[ { rowspan: 2, contents: '21' }, '', '23' ],
 				[ '', '33' ]
 			] ) );
 		} );
@@ -147,7 +147,7 @@ describe( 'SplitCellCommand', () => {
 		it( 'should split table cell with rowspan and colspan in the middle of a table', () => {
 			setData( model, modelTable( [
 				[ '11', { rowspan: 3, colspan: 2, contents: '[]12' }, '14' ],
-				[ { rowspan: 2, contents: '[]21' }, '24' ],
+				[ { rowspan: 2, contents: '21' }, '24' ],
 				[ '34' ]
 			] ) );
 
@@ -155,7 +155,7 @@ describe( 'SplitCellCommand', () => {
 
 			expect( formatTable( getData( model ) ) ).to.equal( formattedModelTable( [
 				[ '11', '[]12', '', '14' ],
-				[ { rowspan: 2, contents: '[]21' }, '', '', '24' ],
+				[ { rowspan: 2, contents: '21' }, '', '', '24' ],
 				[ '', '', '34' ]
 			] ) );
 		} );

+ 1 - 1
packages/ckeditor5-table/tests/converters/downcast.js

@@ -1024,7 +1024,7 @@ describe( 'downcast converters', () => {
 					} );
 			} );
 
-			it( 'should create renamed cell inside as a widget', () => {
+			it( 'should create renamed cell as a widget', () => {
 				setModelData( model,
 					'<table>' +
 					'<tableRow><tableCell>foo</tableCell></tableRow>' +