Browse Source

Working corner case fix - last row/column in selection has all spanned cells.

Maciej Gołaszewski 5 years ago
parent
commit
8cfca04918

+ 45 - 12
packages/ckeditor5-table/src/tableclipboard.js

@@ -138,17 +138,53 @@ export default class TableClipboard extends Plugin {
 		const model = this.editor.model;
 
 		model.change( writer => {
-			// Currently not handled. The selected table content should be trimmed to a rectangular selection.
-			// See: https://github.com/ckeditor/ckeditor5/issues/6122.
+			// Content table to which we insert a pasted table.
+			const selectedTable = findAncestor( 'table', selectedTableCells[ 0 ] );
+			const { last: lastColumnOfSelection, first: firstColumnOfSelection } = getColumnIndexes( selectedTableCells );
+			const { first: firstRowOfSelection, last: lastRowOfSelection } = getRowIndexes( selectedTableCells );
+
+			let lastRowOfAffectedSelection = lastRowOfSelection;
+			let lastColumnOfAffectedSelection = lastColumnOfSelection;
+
+			let selectionHeight = lastRowOfAffectedSelection - firstRowOfSelection + 1;
+			let selectionWidth = lastColumnOfAffectedSelection - firstColumnOfSelection + 1;
+
 			if ( !isSelectionRectangular( selectedTableCells, tableUtils ) ) {
 				trimCellsToRectangularSelection( selectedTableCells, writer );
-			}
+			} else {
+				// Corner case...
+				let rs;
+
+				const ahaRow = Array.from( new TableWalker( selectedTable, {
+					startRow: lastRowOfAffectedSelection,
+					endRow: lastRowOfAffectedSelection
+				} ) ).every( ( { rowspan } ) => {
+					rs = rowspan;
+					return rowspan === 1;
+				} );
+
+				if ( !ahaRow ) {
+					selectionHeight += rs - 1;
+					lastRowOfAffectedSelection = firstRowOfSelection + selectionHeight - 1;
+				}
 
-			const { last: lastColumnOfSelection, first: firstColumnOfSelection } = getColumnIndexes( selectedTableCells );
-			const { first: firstRowOfSelection, last: lastRowOfSelection } = getRowIndexes( selectedTableCells );
+				let cs;
+
+				const ahaColumn = Array.from( new TableWalker( selectedTable, {
+					startRow: firstRowOfSelection,
+					endRow: lastRowOfAffectedSelection,
+					column: lastColumnOfAffectedSelection
+				} ) ).every( ( { colspan } ) => {
+					cs = colspan;
 
-			const selectionHeight = lastRowOfSelection - firstRowOfSelection + 1;
-			const selectionWidth = lastColumnOfSelection - firstColumnOfSelection + 1;
+					return colspan === 1;
+				} );
+
+				if ( !ahaColumn ) {
+					selectionWidth += cs - 1;
+					lastColumnOfAffectedSelection = firstColumnOfSelection + selectionWidth - 1;
+				}
+			}
 
 			const pasteHeight = tableUtils.getRows( pastedTable );
 			const pasteWidth = tableUtils.getColumns( pastedTable );
@@ -176,12 +212,9 @@ export default class TableClipboard extends Plugin {
 			// Holds two-dimensional array that is addressed by [ row ][ column ] that stores cells anchored at given location.
 			const pastedTableLocationMap = createLocationMap( pastedTable, selectionWidth, selectionHeight );
 
-			// Content table to which we insert a pasted table.
-			const selectedTable = findAncestor( 'table', selectedTableCells[ 0 ] );
-
 			const selectedTableMap = [ ...new TableWalker( selectedTable, {
 				startRow: firstRowOfSelection,
-				endRow: lastRowOfSelection,
+				endRow: firstRowOfSelection + selectionHeight - 1,
 				includeSpanned: true
 			} ) ];
 
@@ -203,7 +236,7 @@ export default class TableClipboard extends Plugin {
 				}
 
 				// Could use startColumn, endColumn. See: https://github.com/ckeditor/ckeditor5/issues/6785.
-				if ( column < firstColumnOfSelection || column > lastColumnOfSelection ) {
+				if ( column < firstColumnOfSelection || column > lastColumnOfAffectedSelection ) {
 					// Only update the previousCellInRow for non-spanned slots.
 					if ( !isSpanned ) {
 						previousCellInRow = cell;

+ 107 - 10
packages/ckeditor5-table/tests/tableclipboard-paste.js

@@ -853,8 +853,7 @@ describe( 'table clipboard', () => {
 					/* eslint-enable no-multi-spaces */
 				} );
 
-				// TODO: Skipped case - should allow pasting but no tools to compare areas (like in MergeCellsCommand).
-				it.skip( 'handles pasting table that has cell with colspan (last row in selection is spanned)', () => {
+				it( 'handles pasting table that has cell with colspan (last row in selection is spanned)', () => {
 					// +----+----+----+----+
 					// | 00 | 01 | 02 | 03 |
 					// +----+----+----+----+
@@ -871,6 +870,7 @@ describe( 'table clipboard', () => {
 						[ '30', '31', '32', '33' ]
 					] ) );
 
+					// Select 02 -> 10 (selection 3x3)
 					tableSelection.setCellSelection(
 						modelRoot.getNodeByPath( [ 0, 0, 2 ] ),
 						modelRoot.getNodeByPath( [ 0, 1, 0 ] )
@@ -896,6 +896,50 @@ describe( 'table clipboard', () => {
 						[ 0, 0, 0, 0 ]
 					] );
 				} );
+
+				it( 'handles pasting table that has cell with colspan (last column in selection is spanned)', () => {
+					// +----+----+----+----+
+					// | 00 | 01      | 03 |
+					// +----+----+----+----+
+					// | 10 | 11      | 13 |
+					// +----+         +----+
+					// | 20 |         | 23 |
+					// +----+----+----+----+
+					// | 30 | 31 | 32 | 33 |
+					// +----+----+----+----+
+					setModelData( model, modelTable( [
+						[ '00', { contents: '01', colspan: 2 }, '03' ],
+						[ '10', { contents: '11', colspan: 2, rowspan: 2 }, '13' ],
+						[ '20', '23' ],
+						[ '30', '31', '32', '33' ]
+					] ) );
+
+					// Select 20 -> 01 (selection 3x3)
+					tableSelection.setCellSelection(
+						modelRoot.getNodeByPath( [ 0, 2, 0 ] ),
+						modelRoot.getNodeByPath( [ 0, 0, 1 ] )
+					);
+
+					pasteTable( [
+						[ 'aa', 'ab', 'ac' ],
+						[ 'ba', 'bb', 'bc' ],
+						[ 'ca', 'cb', 'cc' ]
+					] );
+
+					assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
+						[ 'aa', 'ab', 'ac', '03' ],
+						[ 'ba', 'bb', 'bc', '13' ],
+						[ 'ca', 'cb', 'cc', '23' ],
+						[ '30', '31', '32', '33' ]
+					] ) );
+
+					assertSelectedCells( model, [
+						[ 1, 1, 1, 0 ],
+						[ 1, 1, 1, 0 ],
+						[ 1, 1, 1, 0 ],
+						[ 0, 0, 0, 0 ]
+					] );
+				} );
 			} );
 
 			describe( 'content and paste tables have spans', () => {
@@ -1047,8 +1091,7 @@ describe( 'table clipboard', () => {
 					/* eslint-enable no-multi-spaces */
 				} );
 
-				// TODO: Skipped case - should allow pasting but no tools to compare areas (like in MergeCellsCommand).
-				it.skip( 'handles pasting table that has cell with colspan (last row in selection is spanned)', () => {
+				it( 'handles pasting table that has cell with colspan (last row in selection is spanned)', () => {
 					// +----+----+----+----+
 					// | 00 | 01 | 02 | 03 |
 					// +----+----+----+----+
@@ -1065,6 +1108,7 @@ describe( 'table clipboard', () => {
 						[ '30', '31', '32', '33' ]
 					] ) );
 
+					// Select 02 -> 10 (selection 3x3)
 					tableSelection.setCellSelection(
 						modelRoot.getNodeByPath( [ 0, 0, 2 ] ),
 						modelRoot.getNodeByPath( [ 0, 1, 0 ] )
@@ -1084,17 +1128,70 @@ describe( 'table clipboard', () => {
 					] );
 
 					assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
-						[ '00', '01', '02', '03' ],
-						[ '10', { colspan: 2, contents: 'aa' }, '13' ],
-						[ '20', 'ba', 'bb', '23' ],
+						[ 'aa', 'ab', 'ac', '03' ],
+						[ { contents: 'ba', colspan: 2, rowspan: 2 }, 'bc', '13' ],
+						[ 'cc', '23' ],
 						[ '30', '31', '32', '33' ]
 					] ) );
 
 					/* eslint-disable no-multi-spaces */
 					assertSelectedCells( model, [
-						[ 0, 0, 0, 0 ],
-						[ 0, 1, 0 ],
-						[ 0, 1, 1, 0 ],
+						[ 1, 1, 1, 0 ],
+						[ 1,    1, 0 ],
+						[       1, 0 ],
+						[ 0, 0, 0, 0 ]
+					] );
+					/* eslint-enable no-multi-spaces */
+				} );
+
+				it( 'handles pasting table that has cell with colspan (last column in selection is spanned)', () => {
+					// +----+----+----+----+
+					// | 00 | 01      | 03 |
+					// +----+----+----+----+
+					// | 10 | 11      | 13 |
+					// +----+         +----+
+					// | 20 |         | 23 |
+					// +----+----+----+----+
+					// | 30 | 31 | 32 | 33 |
+					// +----+----+----+----+
+					setModelData( model, modelTable( [
+						[ '00', { contents: '01', colspan: 2 }, '03' ],
+						[ '10', { contents: '11', colspan: 2, rowspan: 2 }, '13' ],
+						[ '20', '23' ],
+						[ '30', '31', '32', '33' ]
+					] ) );
+
+					// Select 20 -> 01 (selection 3x3)
+					tableSelection.setCellSelection(
+						modelRoot.getNodeByPath( [ 0, 2, 0 ] ),
+						modelRoot.getNodeByPath( [ 0, 0, 1 ] )
+					);
+
+					// +----+----+----+
+					// | aa | ab | ac |
+					// +----+----+----+
+					// | ba      | bc |
+					// +         +----+
+					// |         | cc |
+					// +----+----+----+
+					pasteTable( [
+						[ 'aa', 'ab', 'ac' ],
+						[ { contents: 'ba', colspan: 2, rowspan: 2 }, 'bc' ],
+						[ 'cc' ]
+					] );
+
+					assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
+						[ 'aa', 'ab', 'ac', '03' ],
+						[ { contents: 'ba', colspan: 2, rowspan: 2 }, 'bc', '13' ],
+						[ 'cc', '23' ],
+						[ '30', '31', '32', '33' ]
+					] ) );
+
+					/* eslint-disable no-multi-spaces */
+					assertSelectedCells( model, [
+						[ 1, 1, 1, 0 ],
+						[ 1,    1, 0 ],
+						[       1, 0 ],
 						[ 0, 0, 0, 0 ]
 					] );
 					/* eslint-enable no-multi-spaces */