浏览代码

Added: Initial mergeUp command implementation.

Maciej Gołaszewski 7 年之前
父节点
当前提交
3cdb451cae

+ 13 - 13
packages/ckeditor5-table/src/commands/mergecellcommand.js

@@ -47,21 +47,24 @@ export default class MergeCellCommand extends Command {
 		const model = this.editor.model;
 		const doc = model.document;
 		const tableCell = doc.selection.getFirstPosition().parent;
-
-		const siblingToMerge = this.value;
+		const cellToMerge = this.value;
 
 		model.change( writer => {
 			const isMergeNext = this.direction == 'right' || this.direction == 'down';
 
-			writer.move( Range.createIn( siblingToMerge ), Position.createAt( tableCell, isMergeNext ? 'end' : undefined ) );
-			writer.remove( siblingToMerge );
+			const mergeInto = isMergeNext ? tableCell : cellToMerge;
+			const removeCell = isMergeNext ? cellToMerge : tableCell;
+
+			writer.move( Range.createIn( removeCell ), Position.createAt( mergeInto, 'end' ) );
+			writer.remove( removeCell );
 
 			const spanAttribute = isHorizontal( this.direction ) ? 'colspan' : 'rowspan';
+			const cellSpan = parseInt( tableCell.getAttribute( spanAttribute ) || 1 );
+			const cellToMergeSpan = parseInt( cellToMerge.getAttribute( spanAttribute ) || 1 );
 
-			const colspan = parseInt( tableCell.getAttribute( spanAttribute ) || 1 );
-			const nextTableCellColspan = parseInt( siblingToMerge.getAttribute( spanAttribute ) || 1 );
+			writer.setAttribute( spanAttribute, cellSpan + cellToMergeSpan, mergeInto );
 
-			writer.setAttribute( spanAttribute, colspan + nextTableCellColspan, tableCell );
+			writer.setSelection( Range.createIn( mergeInto ) );
 		} );
 	}
 
@@ -89,7 +92,6 @@ export default class MergeCellCommand extends Command {
 		}
 
 		const spanAttribute = isHorizontal( this.direction ) ? 'rowspan' : 'colspan';
-
 		const span = parseInt( element.getAttribute( spanAttribute ) || 1 );
 
 		const cellToMergeSpan = parseInt( cellToMerge.getAttribute( spanAttribute ) || 1 );
@@ -104,16 +106,14 @@ export default class MergeCellCommand extends Command {
 
 			const rowIndex = table.getChildIndex( tableRow );
 
-			if ( direction === 'down' && rowIndex === table.childCount - 1 ) {
+			if ( direction === 'down' && rowIndex === table.childCount - 1 || direction === 'up' && rowIndex === 0 ) {
 				return;
 			}
 
 			const rowspan = parseInt( tableCell.getAttribute( 'rowspan' ) || 1 );
-
-			const targetMergeRow = rowIndex + rowspan;
+			const targetMergeRow = direction === 'up' ? rowIndex : rowIndex + rowspan;
 
 			const tableWalker = new TableWalker( table, { endRow: targetMergeRow } );
-
 			const tableWalkerValues = [ ...tableWalker ];
 
 			const cellData = tableWalkerValues.find( value => value.cell === tableCell );
@@ -122,7 +122,7 @@ export default class MergeCellCommand extends Command {
 				const row = value.row;
 				const column = value.column;
 
-				return column === cellData.column && ( targetMergeRow === row );
+				return column === cellData.column && ( direction === 'down' ? targetMergeRow === row : rowspan + row === rowIndex );
 			} );
 
 			const colspan = parseInt( tableCell.getAttribute( 'colspan' ) || 1 );

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

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

+ 113 - 3
packages/ckeditor5-table/tests/commands/mergecellcommand.js

@@ -163,7 +163,7 @@ describe( 'MergeCellCommand', () => {
 				command.execute();
 
 				expect( formatTable( getData( model ) ) ).to.equal( formattedModelTable( [
-					[ { colspan: 2, contents: '[]0001' } ]
+					[ { colspan: 2, contents: '[0001]' } ]
 				] ) );
 			} );
 		} );
@@ -263,7 +263,7 @@ describe( 'MergeCellCommand', () => {
 				command.execute();
 
 				expect( formatTable( getData( model ) ) ).to.equal( formattedModelTable( [
-					[ { colspan: 2, contents: '00[]01' } ]
+					[ { colspan: 2, contents: '[0001]' } ]
 				] ) );
 			} );
 		} );
@@ -372,7 +372,117 @@ describe( 'MergeCellCommand', () => {
 				command.execute();
 
 				expect( formatTable( getData( model ) ) ).to.equal( formattedModelTable( [
-					[ '00', { rowspan: 2, contents: '0111[]' } ],
+					[ '00', { rowspan: 2, contents: '[0111]' } ],
+					[ '10' ]
+				] ) );
+			} );
+		} );
+	} );
+
+	describe( 'direction=up', () => {
+		beforeEach( () => {
+			command = new MergeCellCommand( editor, { direction: 'up' } );
+		} );
+
+		describe( 'isEnabled', () => {
+			it( 'should be true if in cell that has mergeable cell in previous row', () => {
+				setData( model, modelTable( [
+					[ '00', '01' ],
+					[ '10', '11[]' ]
+				] ) );
+
+				expect( command.isEnabled ).to.be.true;
+			} );
+
+			it( 'should be false if in first 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, 0, 1 ] ) );
+			} );
+
+			it( 'should be undefined if in first 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, 0, 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' ]
 				] ) );
 			} );