Browse Source

Added: Custom remove table row downcast converter.

Maciej Gołaszewski 7 years ago
parent
commit
f21416059c

+ 37 - 0
packages/ckeditor5-table/src/converters/downcast.js

@@ -191,6 +191,43 @@ export function downcastAttributeChange( attribute ) {
 	}, { priority: 'normal' } );
 }
 
+/**
+ * Conversion helper that acts on removed row.
+ *
+ * @returns {Function} Conversion helper.
+ */
+export function downcastRemoveRow() {
+	return dispatcher => dispatcher.on( 'remove:tableRow', ( evt, data, conversionApi ) => {
+		// Prevent default remove converter.
+		evt.stop();
+
+		let viewStart = conversionApi.mapper.toViewPosition( data.position );
+
+		const modelEnd = data.position.getShiftedBy( data.length );
+		let viewEnd = conversionApi.mapper.toViewPosition( modelEnd, { isPhantom: true } );
+
+		// Make sure that start and end positions are inside the same parent as default remove converter doesn't work well with
+		// wrapped elements: https://github.com/ckeditor/ckeditor5-engine/issues/1414
+		if ( viewStart.parent !== viewEnd.parent ) {
+			if ( viewStart.parent.name == 'table' ) {
+				viewStart = ViewPosition.createAt( viewEnd.parent );
+			}
+
+			if ( viewEnd.parent.name == 'table' ) {
+				viewEnd = ViewPosition.createAt( viewStart.parent, 'end' );
+			}
+		}
+
+		const viewRange = new ViewRange( viewStart, viewEnd );
+
+		const removed = conversionApi.writer.remove( viewRange.getTrimmed() );
+
+		for ( const child of ViewRange.createIn( removed ).getItems() ) {
+			conversionApi.mapper.unbindViewElement( child );
+		}
+	}, { priority: 'higher' } );
+}
+
 // Creates a table cell element in a view.
 //
 // @param {module:table/tablewalker~TableWalkerValue} tableWalkerValue

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

@@ -10,7 +10,7 @@
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 import { upcastElementToElement } from '@ckeditor/ckeditor5-engine/src/conversion/upcast-converters';
 import upcastTable from './converters/upcasttable';
-import { downcastInsertCell, downcastInsertRow, downcastInsertTable } from './converters/downcast';
+import { downcastInsertCell, downcastInsertRow, downcastInsertTable, downcastRemoveRow } from './converters/downcast';
 import InsertTableCommand from './commands/inserttablecommand';
 import InsertRowCommand from './commands/insertrowcommand';
 import InsertColumnCommand from './commands/insertcolumncommand';
@@ -59,6 +59,9 @@ export default class TablesEditing extends Plugin {
 		conversion.for( 'downcast' ).add( downcastInsertRow() );
 		conversion.for( 'downcast' ).add( downcastInsertCell() );
 
+		// Remove row conversion.
+		conversion.for( 'downcast' ).add( downcastRemoveRow() );
+
 		// Table cell conversion.
 		conversion.for( 'upcast' ).add( upcastElementToElement( { model: 'tableCell', view: 'td' } ) );
 		conversion.for( 'upcast' ).add( upcastElementToElement( { model: 'tableCell', view: 'th' } ) );

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

@@ -11,7 +11,8 @@ import {
 	downcastAttributeChange,
 	downcastInsertCell,
 	downcastInsertRow,
-	downcastInsertTable
+	downcastInsertTable,
+	downcastRemoveRow
 } from '../../src/converters/downcast';
 import { formatModelTable, formattedViewTable, modelTable, viewTable } from '../_utils/utils';
 
@@ -60,6 +61,8 @@ describe( 'downcast converters', () => {
 				conversion.for( 'downcast' ).add( downcastInsertRow() );
 				conversion.for( 'downcast' ).add( downcastInsertCell() );
 
+				conversion.for( 'downcast' ).add( downcastRemoveRow() );
+
 				conversion.for( 'downcast' ).add( downcastAttributeChange( 'headingRows' ) );
 				conversion.for( 'downcast' ).add( downcastAttributeChange( 'headingColumns' ) );
 			} );
@@ -715,4 +718,109 @@ describe( 'downcast converters', () => {
 			] ) );
 		} );
 	} );
+
+	describe( 'downcastRemoveRow()', () => {
+		it( 'should react to removed row from the beginning of a tbody', () => {
+			setModelData( model, modelTable( [
+				[ '00[]', '01' ],
+				[ '10', '11' ]
+			] ) );
+
+			const table = root.getChild( 0 );
+
+			model.change( writer => {
+				writer.remove( table.getChild( 1 ) );
+			} );
+
+			expect( formatModelTable( getViewData( viewDocument, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
+				[ '00', '01' ]
+			] ) );
+		} );
+
+		it( 'should react to removed row form the end of a tbody', () => {
+			setModelData( model, modelTable( [
+				[ '00[]', '01' ],
+				[ '10', '11' ]
+			] ) );
+
+			const table = root.getChild( 0 );
+
+			model.change( writer => {
+				writer.remove( table.getChild( 0 ) );
+			} );
+
+			expect( formatModelTable( getViewData( viewDocument, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
+				[ '10', '11' ]
+			] ) );
+		} );
+
+		it( 'should react to removed row from the beginning of a thead', () => {
+			setModelData( model, modelTable( [
+				[ '00[]', '01' ],
+				[ '10', '11' ]
+			], { headingRows: 2 } ) );
+
+			const table = root.getChild( 0 );
+
+			model.change( writer => {
+				writer.remove( table.getChild( 1 ) );
+			} );
+
+			expect( formatModelTable( getViewData( viewDocument, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
+				[ '00', '01' ]
+			], { headingRows: 2 } ) );
+		} );
+
+		it( 'should react to removed row form the end of a thead', () => {
+			setModelData( model, modelTable( [
+				[ '00[]', '01' ],
+				[ '10', '11' ]
+			], { headingRows: 2 } ) );
+
+			const table = root.getChild( 0 );
+
+			model.change( writer => {
+				writer.remove( table.getChild( 0 ) );
+			} );
+
+			expect( formatModelTable( getViewData( viewDocument, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
+				[ '10', '11' ]
+			], { headingRows: 2 } ) );
+		} );
+
+		it( 'should remove empty thead section if a last row was removed from thead', () => {
+			setModelData( model, modelTable( [
+				[ '00[]', '01' ],
+				[ '10', '11' ]
+			], { headingRows: 1 } ) );
+
+			const table = root.getChild( 0 );
+
+			model.change( writer => {
+				writer.setAttribute( 'headingRows', 0, table );
+				writer.remove( table.getChild( 0 ) );
+			} );
+
+			expect( formatModelTable( getViewData( viewDocument, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
+				[ '10', '11' ]
+			] ) );
+		} );
+
+		it( 'should remove empty tbody section if a last row was removed from tbody', () => {
+			setModelData( model, modelTable( [
+				[ '00[]', '01' ],
+				[ '10', '11' ]
+			], { headingRows: 1 } ) );
+
+			const table = root.getChild( 0 );
+
+			model.change( writer => {
+				writer.remove( table.getChild( 1 ) );
+			} );
+
+			expect( formatModelTable( getViewData( viewDocument, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
+				[ '00', '01' ]
+			], { headingRows: 1 } ) );
+		} );
+	} );
 } );