Sfoglia il codice sorgente

Added: Initial mergeDown command implementation.

Maciej Gołaszewski 7 anni fa
parent
commit
d25b3fefad

+ 66 - 10
packages/ckeditor5-table/src/commands/mergecellcommand.js

@@ -10,6 +10,7 @@
 import Command from '@ckeditor/ckeditor5-core/src/command';
 import Command from '@ckeditor/ckeditor5-core/src/command';
 import Position from '@ckeditor/ckeditor5-engine/src/model/position';
 import Position from '@ckeditor/ckeditor5-engine/src/model/position';
 import Range from '@ckeditor/ckeditor5-engine/src/model/range';
 import Range from '@ckeditor/ckeditor5-engine/src/model/range';
+import TableWalker from '../tablewalker';
 
 
 /**
 /**
  * The merge cell command.
  * The merge cell command.
@@ -50,13 +51,17 @@ export default class MergeCellCommand extends Command {
 		const siblingToMerge = this.value;
 		const siblingToMerge = this.value;
 
 
 		model.change( writer => {
 		model.change( writer => {
-			writer.move( Range.createIn( siblingToMerge ), Position.createAt( tableCell, this.direction == 'right' ? 'end' : undefined ) );
+			const isMergeNext = this.direction == 'right' || this.direction == 'down';
+
+			writer.move( Range.createIn( siblingToMerge ), Position.createAt( tableCell, isMergeNext ? 'end' : undefined ) );
 			writer.remove( siblingToMerge );
 			writer.remove( siblingToMerge );
 
 
-			const colspan = parseInt( tableCell.getAttribute( 'colspan' ) || 1 );
-			const nextTableCellColspan = parseInt( siblingToMerge.getAttribute( 'colspan' ) || 1 );
+			const spanAttribute = isHorizontal( this.direction ) ? 'colspan' : 'rowspan';
 
 
-			writer.setAttribute( 'colspan', colspan + nextTableCellColspan, tableCell );
+			const colspan = parseInt( tableCell.getAttribute( spanAttribute ) || 1 );
+			const nextTableCellColspan = parseInt( siblingToMerge.getAttribute( spanAttribute ) || 1 );
+
+			writer.setAttribute( spanAttribute, colspan + nextTableCellColspan, tableCell );
 		} );
 		} );
 	}
 	}
 
 
@@ -71,18 +76,69 @@ export default class MergeCellCommand extends Command {
 		const doc = model.document;
 		const doc = model.document;
 		const element = doc.selection.getFirstPosition().parent;
 		const element = doc.selection.getFirstPosition().parent;
 
 
-		const siblingToMerge = this.direction == 'right' ? element.nextSibling : element.previousSibling;
+		if ( !element.is( 'tableCell' ) ) {
+			return;
+		}
+
+		const cellToMerge = isHorizontal( this.direction ) ?
+			getHorizontal( element, this.direction ) :
+			getVertical( element, this.direction );
 
 
-		if ( !element.is( 'tableCell' ) || !siblingToMerge ) {
+		if ( !cellToMerge ) {
 			return;
 			return;
 		}
 		}
 
 
-		const rowspan = parseInt( element.getAttribute( 'rowspan' ) || 1 );
+		const spanAttribute = isHorizontal( this.direction ) ? 'rowspan' : 'colspan';
+
+		const span = parseInt( element.getAttribute( spanAttribute ) || 1 );
 
 
-		const nextCellRowspan = parseInt( siblingToMerge.getAttribute( 'rowspan' ) || 1 );
+		const cellToMergeSpan = parseInt( cellToMerge.getAttribute( spanAttribute ) || 1 );
 
 
-		if ( nextCellRowspan === rowspan ) {
-			return siblingToMerge;
+		if ( cellToMergeSpan === span ) {
+			return cellToMerge;
+		}
+
+		function getVertical( tableCell, direction ) {
+			const tableRow = tableCell.parent;
+			const table = tableRow.parent;
+
+			const rowIndex = table.getChildIndex( tableRow );
+
+			if ( direction === 'down' && rowIndex === table.childCount - 1 ) {
+				return;
+			}
+
+			const rowspan = parseInt( tableCell.getAttribute( 'rowspan' ) || 1 );
+
+			const targetMergeRow = rowIndex + rowspan;
+
+			const tableWalker = new TableWalker( table, { endRow: targetMergeRow } );
+
+			const tableWalkerValues = [ ...tableWalker ];
+
+			const cellData = tableWalkerValues.find( value => value.cell === tableCell );
+
+			const cellToMerge = tableWalkerValues.find( value => {
+				const row = value.row;
+				const column = value.column;
+
+				return column === cellData.column && ( targetMergeRow === row );
+			} );
+
+			const colspan = parseInt( tableCell.getAttribute( 'colspan' ) || 1 );
+
+			if ( cellToMerge && cellToMerge.colspan === colspan ) {
+				return cellToMerge.cell;
+			}
+		}
+
+		function getHorizontal( tableCell, direction ) {
+			return direction == 'right' ? tableCell.nextSibling : tableCell.previousSibling;
 		}
 		}
 	}
 	}
 }
 }
+
+// @private
+function isHorizontal( direction ) {
+	return direction == 'right' || direction == 'left';
+}

+ 1 - 0
packages/ckeditor5-table/src/tableediting.js

@@ -91,6 +91,7 @@ export default class TablesEditing extends Plugin {
 		editor.commands.add( 'removeColumn', new RemoveColumnCommand( editor ) );
 		editor.commands.add( 'removeColumn', new RemoveColumnCommand( editor ) );
 		editor.commands.add( 'mergeRight', new MergeCellCommand( editor, { direction: 'right' } ) );
 		editor.commands.add( 'mergeRight', new MergeCellCommand( editor, { direction: 'right' } ) );
 		editor.commands.add( 'mergeLeft', new MergeCellCommand( editor, { direction: 'left' } ) );
 		editor.commands.add( 'mergeLeft', new MergeCellCommand( editor, { direction: 'left' } ) );
+		editor.commands.add( 'mergeDown', new MergeCellCommand( editor, { direction: 'down' } ) );
 
 
 		this.listenTo( editor.editing.view.document, 'keydown', ( ...args ) => this._handleTabOnSelectedTable( ...args ) );
 		this.listenTo( editor.editing.view.document, 'keydown', ( ...args ) => this._handleTabOnSelectedTable( ...args ) );
 		this.listenTo( editor.editing.view.document, 'keydown', ( ...args ) => this._handleTabInsideTable( ...args ) );
 		this.listenTo( editor.editing.view.document, 'keydown', ( ...args ) => this._handleTabInsideTable( ...args ) );

+ 111 - 1
packages/ckeditor5-table/tests/commands/mergecellcommand.js

@@ -12,7 +12,7 @@ import { downcastInsertTable } from '../../src/converters/downcast';
 import upcastTable from '../../src/converters/upcasttable';
 import upcastTable from '../../src/converters/upcasttable';
 import { formatTable, formattedModelTable, modelTable } from '../_utils/utils';
 import { formatTable, formattedModelTable, modelTable } from '../_utils/utils';
 
 
-describe.only( 'MergeCellCommand', () => {
+describe( 'MergeCellCommand', () => {
 	let editor, model, command, root;
 	let editor, model, command, root;
 
 
 	beforeEach( () => {
 	beforeEach( () => {
@@ -268,4 +268,114 @@ describe.only( 'MergeCellCommand', () => {
 			} );
 			} );
 		} );
 		} );
 	} );
 	} );
+
+	describe( 'direction=down', () => {
+		beforeEach( () => {
+			command = new MergeCellCommand( editor, { direction: 'down' } );
+		} );
+
+		describe( 'isEnabled', () => {
+			it( 'should be true if in cell that has mergeable cell in next row', () => {
+				setData( model, modelTable( [
+					[ '00', '01[]' ],
+					[ '10', '11' ]
+				] ) );
+
+				expect( command.isEnabled ).to.be.true;
+			} );
+
+			it( 'should be false if in last row', () => {
+				setData( model, modelTable( [
+					[ '00', '01' ],
+					[ '10[]', '11' ]
+				] ) );
+
+				expect( command.isEnabled ).to.be.false;
+			} );
+
+			it( 'should be true if in a cell that has mergeable cell with the same colspan', () => {
+				setData( model, modelTable( [
+					[ { colspan: 2, contents: '00[]' }, '02' ],
+					[ { colspan: 2, contents: '01' }, '12' ]
+				] ) );
+
+				expect( command.isEnabled ).to.be.true;
+			} );
+
+			it( 'should be false if in a cell that potential mergeable cell has different colspan', () => {
+				setData( model, modelTable( [
+					[ { colspan: 2, contents: '00[]' }, '02' ],
+					[ { colspan: 3, contents: '01' } ]
+				] ) );
+
+				expect( command.isEnabled ).to.be.false;
+			} );
+
+			it( 'should be false if not in a cell', () => {
+				setData( model, '<p>11[]</p>' );
+
+				expect( command.isEnabled ).to.be.false;
+			} );
+		} );
+
+		describe( 'value', () => {
+			it( 'should be set to mergeable cell', () => {
+				setData( model, modelTable( [
+					[ '00', '01[]' ],
+					[ '10', '11' ]
+				] ) );
+
+				expect( command.value ).to.equal( root.getNodeByPath( [ 0, 1, 1 ] ) );
+			} );
+
+			it( 'should be undefined if in last row', () => {
+				setData( model, modelTable( [
+					[ '00', '01' ],
+					[ '10[]', '11' ]
+				] ) );
+
+				expect( command.value ).to.be.undefined;
+			} );
+
+			it( 'should be set to mergeable cell with the same rowspan', () => {
+				setData( model, modelTable( [
+					[ { colspan: 2, contents: '00[]' }, '02' ],
+					[ { colspan: 2, contents: '01' }, '12' ]
+				] ) );
+
+				expect( command.value ).to.equal( root.getNodeByPath( [ 0, 1, 0 ] ) );
+			} );
+
+			it( 'should be undefined if in a cell that potential mergeable cell has different rowspan', () => {
+				setData( model, modelTable( [
+					[ { colspan: 2, contents: '00[]' }, '02' ],
+					[ { colspan: 3, contents: '01' } ]
+				] ) );
+
+				expect( command.value ).to.be.undefined;
+			} );
+
+			it( 'should be undefined if not in a cell', () => {
+				setData( model, '<p>11[]</p>' );
+
+				expect( command.value ).to.be.undefined;
+			} );
+		} );
+
+		describe( 'execute()', () => {
+			it( 'should merge table cells ', () => {
+				setData( model, modelTable( [
+					[ '00', '01[]' ],
+					[ '10', '11' ]
+				] ) );
+
+				command.execute();
+
+				expect( formatTable( getData( model ) ) ).to.equal( formattedModelTable( [
+					[ '00', { rowspan: 2, contents: '0111[]' } ],
+					[ '10' ]
+				] ) );
+			} );
+		} );
+	} );
 } );
 } );