Ver Fonte

Added: Add split cell command.

Maciej Gołaszewski há 7 anos atrás
pai
commit
1ef8e0fe97

+ 100 - 0
packages/ckeditor5-table/src/commands/splitcellcommand.js

@@ -0,0 +1,100 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module table/commands/splitcell
+ */
+
+import Command from '@ckeditor/ckeditor5-core/src/command';
+import Position from '@ckeditor/ckeditor5-engine/src/model/position';
+import TableWalker from '../tablewalker';
+
+/**
+ * The split cell command.
+ *
+ * @extends module:core/command~Command
+ */
+export default class InsertTableCommand extends Command {
+	/**
+	 * @inheritDoc
+	 */
+	refresh() {
+		const model = this.editor.model;
+		const doc = model.document;
+
+		const element = doc.selection.getFirstPosition().parent;
+
+		this.isEnabled = element.is( 'tableCell' ) && ( element.hasAttribute( 'colspan' ) || element.hasAttribute( 'rowspan' ) );
+	}
+
+	/**
+	 * Executes the command.
+	 *
+	 * @fires execute
+	 */
+	execute() {
+		const model = this.editor.model;
+		const document = model.document;
+		const selection = document.selection;
+
+		const firstPosition = selection.getFirstPosition();
+		const tableCell = firstPosition.parent;
+
+		const colspan = parseInt( tableCell.getAttribute( 'colspan' ) || 1 );
+		const rowspan = parseInt( tableCell.getAttribute( 'rowspan' ) || 1 );
+
+		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 ) );
+							}
+						}
+					}
+				}
+			}
+
+			if ( colspan > 1 ) {
+				for ( let i = colspan - 1; i > 0; i-- ) {
+					writer.insertElement( 'tableCell', Position.createAfter( tableCell ) );
+				}
+			}
+
+			writer.removeAttribute( 'colspan', tableCell );
+			writer.removeAttribute( 'rowspan', tableCell );
+		} );
+	}
+}

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

@@ -17,6 +17,7 @@ import { downcastInsertCell, downcastInsertRow, downcastInsertTable, downcastRem
 import InsertTableCommand from './commands/inserttablecommand';
 import InsertTableCommand from './commands/inserttablecommand';
 import InsertRowCommand from './commands/insertrowcommand';
 import InsertRowCommand from './commands/insertrowcommand';
 import InsertColumnCommand from './commands/insertcolumncommand';
 import InsertColumnCommand from './commands/insertcolumncommand';
+import SplitCellCommand from './commands/splitcellcommand';
 import { getParentTable } from './commands/utils';
 import { getParentTable } from './commands/utils';
 
 
 import './../theme/table.css';
 import './../theme/table.css';
@@ -82,6 +83,7 @@ export default class TablesEditing extends Plugin {
 		editor.commands.add( 'insertTable', new InsertTableCommand( editor ) );
 		editor.commands.add( 'insertTable', new InsertTableCommand( editor ) );
 		editor.commands.add( 'insertRow', new InsertRowCommand( editor ) );
 		editor.commands.add( 'insertRow', new InsertRowCommand( editor ) );
 		editor.commands.add( 'insertColumn', new InsertColumnCommand( editor ) );
 		editor.commands.add( 'insertColumn', new InsertColumnCommand( editor ) );
+		editor.commands.add( 'splitCell', new SplitCellCommand( editor ) );
 
 
 		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 ) );

+ 161 - 0
packages/ckeditor5-table/tests/commands/splitcellcommand.js

@@ -0,0 +1,161 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor';
+import { setData, getData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
+import { upcastElementToElement } from '@ckeditor/ckeditor5-engine/src/conversion/upcast-converters';
+
+import SplitCellCommand from '../../src/commands/splitcellcommand';
+import { downcastInsertTable } from '../../src/converters/downcast';
+import upcastTable from '../../src/converters/upcasttable';
+import { formatModelTable, formattedModelTable, modelTable } from '../_utils/utils';
+
+describe( 'SplitCellCommand', () => {
+	let editor, model, command;
+
+	beforeEach( () => {
+		return ModelTestEditor.create()
+			.then( newEditor => {
+				editor = newEditor;
+				model = editor.model;
+				command = new SplitCellCommand( editor );
+
+				const conversion = editor.conversion;
+				const schema = model.schema;
+
+				schema.register( 'table', {
+					allowWhere: '$block',
+					allowAttributes: [ 'headingRows' ],
+					isBlock: true,
+					isObject: true
+				} );
+
+				schema.register( 'tableRow', {
+					allowIn: 'table',
+					allowAttributes: [],
+					isBlock: true,
+					isLimit: true
+				} );
+
+				schema.register( 'tableCell', {
+					allowIn: 'tableRow',
+					allowContentOf: '$block',
+					allowAttributes: [ 'colspan', 'rowspan' ],
+					isBlock: true,
+					isLimit: true
+				} );
+
+				model.schema.register( 'p', { inheritAllFrom: '$block' } );
+
+				// Table conversion.
+				conversion.for( 'upcast' ).add( upcastTable() );
+				conversion.for( 'downcast' ).add( downcastInsertTable() );
+
+				// Table row upcast only since downcast conversion is done in `downcastTable()`.
+				conversion.for( 'upcast' ).add( upcastElementToElement( { model: 'tableRow', view: 'tr' } ) );
+
+				// Table cell conversion.
+				conversion.for( 'upcast' ).add( upcastElementToElement( { model: 'tableCell', view: 'td' } ) );
+				conversion.for( 'upcast' ).add( upcastElementToElement( { model: 'tableCell', view: 'th' } ) );
+
+				conversion.attributeToAttribute( { model: 'colspan', view: 'colspan' } );
+				conversion.attributeToAttribute( { model: 'rowspan', view: 'rowspan' } );
+			} );
+	} );
+
+	afterEach( () => {
+		return editor.destroy();
+	} );
+
+	describe( 'isEnabled', () => {
+		it( 'should be true if in cell with colspan attribute set', () => {
+			setData( model, modelTable( [
+				[ { colspan: 2, contents: '11[]' } ]
+			] ) );
+
+			expect( command.isEnabled ).to.be.true;
+		} );
+
+		it( 'should be true if in cell with rowspan attribute set', () => {
+			setData( model, modelTable( [
+				[ { rowspan: 2, contents: '11[]' } ]
+			] ) );
+		} );
+
+		it( 'should be false in cell without rowspan or colspan attribute', () => {
+			setData( model, modelTable( [
+				[ '11[]' ]
+			] ) );
+
+			expect( command.isEnabled ).to.be.false;
+		} );
+
+		it( 'should be false if not in cell', () => {
+			setData( model, '<p>11[]</p>' );
+
+			expect( command.isEnabled ).to.be.false;
+		} );
+	} );
+
+	describe( 'execute()', () => {
+		it( 'should split table cell with colspan', () => {
+			setData( model, modelTable( [
+				[ { colspan: 2, contents: '[]11' } ]
+			] ) );
+
+			command.execute();
+
+			expect( formatModelTable( getData( model ) ) ).to.equal( formattedModelTable( [
+				[ '[]11', '' ]
+			] ) );
+		} );
+
+		it( 'should split table cell with rowspan', () => {
+			setData( model, modelTable( [
+				[ { rowspan: 2, contents: '[]11' }, '12' ],
+				[ '22' ]
+			] ) );
+
+			command.execute();
+
+			expect( formatModelTable( getData( model ) ) ).to.equal( formattedModelTable( [
+				[ '[]11', '12' ],
+				[ '', '22' ]
+			] ) );
+		} );
+
+		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' ],
+				[ '33' ]
+			] ) );
+
+			command.execute();
+
+			expect( formatModelTable( getData( model ) ) ).to.equal( formattedModelTable( [
+				[ '11', '[]12', '13' ],
+				[ { rowspan: 2, contents: '[]21' }, '', '23' ],
+				[ '', '33' ]
+			] ) );
+		} );
+
+		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' ],
+				[ '34' ]
+			] ) );
+
+			command.execute();
+
+			expect( formatModelTable( getData( model ) ) ).to.equal( formattedModelTable( [
+				[ '11', '[]12', '', '14' ],
+				[ { rowspan: 2, contents: '[]21' }, '', '', '24' ],
+				[ '', '', '34' ]
+			] ) );
+		} );
+	} );
+} );