8
0
Pārlūkot izejas kodu

Added: Consider `colspan` attribute in `headingColumns` support in downcasting.

Maciej Gołaszewski 7 gadi atpakaļ
vecāks
revīzija
666abcfcb2

+ 32 - 27
packages/ckeditor5-table/src/converters.js

@@ -65,23 +65,6 @@ export function upcastTable() {
 	};
 }
 
-export function downcastTableCell() {
-	return dispatcher => dispatcher.on( 'insert:tableCell', ( evt, data, conversionApi ) => {
-		const tableCell = data.item;
-
-		if ( !conversionApi.consumable.consume( tableCell, 'insert' ) ) {
-			return;
-		}
-
-		const tableCellElement = conversionApi.writer.createContainerElement( isHead( tableCell ) ? 'th' : 'td' );
-
-		const viewPosition = conversionApi.mapper.toViewPosition( data.range.start );
-
-		conversionApi.mapper.bindElements( tableCell, tableCellElement );
-		conversionApi.writer.insert( viewPosition, tableCellElement );
-	}, { priority: 'normal' } );
-}
-
 export function downcastTable() {
 	return dispatcher => dispatcher.on( 'insert:table', ( evt, data, conversionApi ) => {
 		const table = data.item;
@@ -113,16 +96,16 @@ export function downcastTable() {
 	}, { priority: 'normal' } );
 }
 
-function isHead( tableCell ) {
+function isHead( tableCell, headingColumnsLeft ) {
 	const row = tableCell.parent;
 	const table = row.parent;
 	const rowIndex = table.getChildIndex( row );
-	const headingRows = table.getAttribute( 'headingRows' );
-	const headingColumns = table.getAttribute( 'headingColumns' );
+	const headingRows = table.getAttribute( 'headingRows' ) || 0;
+	const headingColumns = table.getAttribute( 'headingColumns' ) || 0;
 
 	const cellIndex = row.getChildIndex( tableCell );
 
-	return ( headingRows && headingRows > rowIndex ) || ( headingColumns && headingColumns > cellIndex );
+	return ( !!headingRows && headingRows > rowIndex ) || ( !!headingColumnsLeft && headingColumns > cellIndex );
 }
 
 function _downcastTableSection( elementName, tableElement, rows, conversionApi ) {
@@ -136,10 +119,29 @@ function _downcastTableRow( tableRow, conversionApi, parent ) {
 	// Will always consume since we're converting <tableRow> element from a parent <table>.
 	conversionApi.consumable.consume( tableRow, 'insert' );
 
-	const tableRowElement = conversionApi.writer.createContainerElement( 'tr' );
+	const trElement = conversionApi.writer.createContainerElement( 'tr' );
 
-	conversionApi.mapper.bindElements( tableRow, tableRowElement );
-	conversionApi.writer.insert( Position.createAt( parent, 'end' ), tableRowElement );
+	conversionApi.mapper.bindElements( tableRow, trElement );
+	conversionApi.writer.insert( Position.createAt( parent, 'end' ), trElement );
+
+	let headingColumnsLeft = tableRow.parent.getAttribute( 'headingColumns' ) || 0;
+
+	for ( const tableCell of Array.from( tableRow.getChildren() ) ) {
+		// Will always consume since we're converting <tableRow> element from a parent <table>.
+		conversionApi.consumable.consume( tableCell, 'insert' );
+
+		const is = isHead( tableCell, headingColumnsLeft );
+
+		if ( headingColumnsLeft ) {
+			headingColumnsLeft -= tableCell.hasAttribute( 'colspan' ) ? tableCell.getAttribute( 'colspan' ) : 1;
+		}
+
+		const tableCellElement = conversionApi.writer.createContainerElement( is ? 'th' : 'td' );
+		const viewPosition = Position.createAt( trElement, 'end' );
+
+		conversionApi.mapper.bindElements( tableCell, tableCellElement );
+		conversionApi.writer.insert( viewPosition, tableCellElement );
+	}
 }
 
 function _upcastTableRows( viewTable, modelTable, conversionApi ) {
@@ -228,11 +230,14 @@ function _scanRow( tr, tableMeta, firstThead ) {
 
 	let headingCols = 0;
 	let index = 0;
-	const childCount = tr.childCount;
+
+	// Filter out empty text nodes from tr children.
+	const children = Array.from( tr.getChildren() )
+		.filter( child => child.name === 'th' || child.name === 'td' );
 
 	// Count starting adjacent <th> elements of a <tr>.
-	while ( index < childCount && tr.getChild( index ).name === 'th' ) {
-		const td = tr.getChild( index );
+	while ( index < children.length && children[ index ].name === 'th' ) {
+		const td = children[ index ];
 
 		// Adjust columns calculation by the number of extended columns.
 		const hasAttribute = td.hasAttribute( 'colspan' );

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

@@ -9,7 +9,7 @@
 
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 import { upcastElementToElement } from '@ckeditor/ckeditor5-engine/src/conversion/upcast-converters';
-import { downcastTableCell, downcastTable, upcastTable } from './converters';
+import { downcastTable, upcastTable } from './converters';
 import InsertTableCommand from './inserttablecommand';
 
 /**
@@ -55,7 +55,6 @@ export default class TablesEditing extends Plugin {
 		// Table cell conversion.
 		conversion.for( 'upcast' ).add( upcastElementToElement( { model: 'tableCell', view: 'td' } ) );
 		conversion.for( 'upcast' ).add( upcastElementToElement( { model: 'tableCell', view: 'th' } ) );
-		conversion.for( 'downcast' ).add( downcastTableCell() );
 
 		conversion.attributeToAttribute( { model: 'colspan', view: 'colspan' } );
 		conversion.attributeToAttribute( { model: 'rowspan', view: 'rowspan' } );

+ 43 - 50
packages/ckeditor5-table/tests/converters.js

@@ -4,12 +4,11 @@
  */
 
 import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
-
 import { upcastElementToElement } from '@ckeditor/ckeditor5-engine/src/conversion/upcast-converters';
-
 import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
+
 import { setData as setModelData, getData as getModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
-import { downcastTable, downcastTableCell, upcastTable } from '../src/converters';
+import { downcastTable, upcastTable } from '../src/converters';
 
 describe( 'Table converters', () => {
 	let editor, model, viewDocument;
@@ -55,7 +54,6 @@ describe( 'Table converters', () => {
 				// Table cell conversion.
 				conversion.for( 'upcast' ).add( upcastElementToElement( { model: 'tableCell', view: 'td' } ) );
 				conversion.for( 'upcast' ).add( upcastElementToElement( { model: 'tableCell', view: 'th' } ) );
-				conversion.for( 'downcast' ).add( downcastTableCell() );
 
 				conversion.attributeToAttribute( { model: 'colspan', view: 'colspan' } );
 				conversion.attributeToAttribute( { model: 'rowspan', view: 'rowspan' } );
@@ -297,10 +295,8 @@ describe( 'Table converters', () => {
 					'<table>' +
 					'<tbody>' +
 					// This row has colspan of 3 so it should be the whole table should have 3 heading columns.
-					'<tr><th colspan="3">21</th><td>24</td></tr>' +
-					'<tr><th>31</th><th>32</th><td>33</td><td>34</td></tr>' +
-					'<tr><th colspan="2">41</th><th>43</th><td>44</td></tr>' +
-					'<tr><td>51</td><th>52</th><th>53</th><th>54</th></tr>' +
+					'<tr><th>21</th><th>22</th><td>23</td><td>24</td></tr>' +
+					'<tr><th colspan="2">31</th><th>33</th><td>34</td></tr>' +
 					'</tbody>' +
 					'<thead>' +
 					// This row has 4 ths but it is a thead.
@@ -315,16 +311,10 @@ describe( 'Table converters', () => {
 					'<tableCell>11</tableCell><tableCell>12</tableCell><tableCell>13</tableCell><tableCell>14</tableCell>' +
 					'</tableRow>' +
 					'<tableRow>' +
-					'<tableCell colspan="3">21</tableCell><tableCell>24</tableCell>' +
-					'</tableRow>' +
-					'<tableRow>' +
-					'<tableCell>31</tableCell><tableCell>32</tableCell><tableCell>33</tableCell><tableCell>34</tableCell>' +
+					'<tableCell>21</tableCell><tableCell>22</tableCell><tableCell>23</tableCell><tableCell>24</tableCell>' +
 					'</tableRow>' +
 					'<tableRow>' +
-					'<tableCell colspan="2">41</tableCell><tableCell>43</tableCell><tableCell>44</tableCell>' +
-					'</tableRow>' +
-					'<tableRow>' +
-					'<tableCell>51</tableCell><tableCell>52</tableCell><tableCell>53</tableCell><tableCell>54</tableCell>' +
+					'<tableCell colspan="2">31</tableCell><tableCell>33</tableCell><tableCell>34</tableCell>' +
 					'</tableRow>' +
 					'</table>'
 				);
@@ -387,24 +377,6 @@ describe( 'Table converters', () => {
 			);
 		} );
 
-		it( 'should create table with headingColumns', () => {
-			setModelData( model,
-				'<table headingColumns="2">' +
-				'<tableRow><tableCell>11</tableCell><tableCell>12</tableCell><tableCell>13</tableCell></tableRow>' +
-				'<tableRow><tableCell>21</tableCell><tableCell>22</tableCell><tableCell>23</tableCell></tableRow>' +
-				'</table>'
-			);
-
-			expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal(
-				'<table>' +
-				'<tbody>' +
-				'<tr><th>11</th><th>12</th><td>13</td></tr>' +
-				'<tr><th>21</th><th>22</th><td>23</td></tr>' +
-				'</tbody>' +
-				'</table>'
-			);
-		} );
-
 		it( 'should create table with heading columns and rows', () => {
 			setModelData( model,
 				'<table headingColumns="3" headingRows="1">' +
@@ -431,6 +403,7 @@ describe( 'Table converters', () => {
 
 		it( 'should be possible to overwrite', () => {
 			editor.conversion.elementToElement( { model: 'tableRow', view: 'tr' } );
+			editor.conversion.elementToElement( { model: 'tableCell', view: 'td' } );
 			editor.conversion.for( 'downcast' ).add( dispatcher => {
 				dispatcher.on( 'insert:table', ( evt, data, conversionApi ) => {
 					conversionApi.consumable.consume( data.item, 'insert' );
@@ -455,25 +428,45 @@ describe( 'Table converters', () => {
 				'</table>'
 			);
 		} );
-	} );
 
-	describe( 'downcastTableCell()', () => {
-		it( 'should be possible to overwrite row conversion', () => {
-			editor.conversion.elementToElement( { model: 'tableCell', view: { name: 'td', class: 'foo' }, priority: 'high' } );
+		describe( 'headingColumns attribute', () => {
+			it( 'should mark heading columns table cells', () => {
+				setModelData( model,
+					'<table headingColumns="2">' +
+					'<tableRow><tableCell>11</tableCell><tableCell>12</tableCell><tableCell>13</tableCell></tableRow>' +
+					'<tableRow><tableCell>21</tableCell><tableCell>22</tableCell><tableCell>23</tableCell></tableRow>' +
+					'</table>'
+				);
 
-			setModelData( model,
-				'<table>' +
-				'<tableRow><tableCell></tableCell></tableRow>' +
-				'</table>'
-			);
+				expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal(
+					'<table>' +
+					'<tbody>' +
+					'<tr><th>11</th><th>12</th><td>13</td></tr>' +
+					'<tr><th>21</th><th>22</th><td>23</td></tr>' +
+					'</tbody>' +
+					'</table>'
+				);
+			} );
 
-			expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal(
-				'<table>' +
-				'<tbody>' +
-				'<tr><td class="foo"></td></tr>' +
-				'</tbody>' +
-				'</table>'
-			);
+			it( 'should mark heading columns table cells when one has colspan attribute', () => {
+				setModelData( model,
+					'<table headingColumns="3">' +
+					'<tableRow>' +
+					'<tableCell>11</tableCell><tableCell>12</tableCell><tableCell>13</tableCell><tableCell>14</tableCell>' +
+					'</tableRow>' +
+					'<tableRow><tableCell colspan="2">21</tableCell><tableCell>23</tableCell><tableCell>24</tableCell></tableRow>' +
+					'</table>'
+				);
+
+				expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal(
+					'<table>' +
+					'<tbody>' +
+					'<tr><th>11</th><th>12</th><th>13</th><td>14</td></tr>' +
+					'<tr><th colspan="2">21</th><th>23</th><td>24</td></tr>' +
+					'</tbody>' +
+					'</table>'
+				);
+			} );
 		} );
 	} );
 } );

+ 2 - 2
packages/ckeditor5-table/tests/inserttablecommand.js

@@ -6,8 +6,9 @@
 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 InsertTableCommand from '../src/inserttablecommand';
-import { upcastTable, downcastTable, downcastTableCell } from '../src/converters';
+import { upcastTable, downcastTable } from '../src/converters';
 
 describe( 'InsertTableCommand', () => {
 	let editor, model, command;
@@ -56,7 +57,6 @@ describe( 'InsertTableCommand', () => {
 				// Table cell conversion.
 				conversion.for( 'upcast' ).add( upcastElementToElement( { model: 'tableCell', view: 'td' } ) );
 				conversion.for( 'upcast' ).add( upcastElementToElement( { model: 'tableCell', view: 'th' } ) );
-				conversion.for( 'downcast' ).add( downcastTableCell );
 
 				conversion.attributeToAttribute( { model: 'colspan', view: 'colspan' } );
 				conversion.attributeToAttribute( { model: 'rowspan', view: 'rowspan' } );

+ 3 - 7
packages/ckeditor5-table/tests/manual/table.html

@@ -7,7 +7,7 @@
 
 	table, th, td {
 		padding: 5px;
-		border: 1px solid black;
+		border: 1px solid #000000;
 	}
 
 	table th,
@@ -16,17 +16,13 @@
 	}
 
 	table th {
-		background: #f0f0f0;
+		background: #dadada;
 		font-weight: normal;
 	}
 
 	table thead th {
-		background: #dadada;
-	}
-
-	table tfoot th {
 		color: #ffffff;
-		background: #7a7a7a;
+		background: #666666;
 	}
 </style>
 

+ 2 - 3
packages/ckeditor5-table/tests/tableediting.js

@@ -3,13 +3,12 @@
  * For licensing, see LICENSE.md.
  */
 
-import TableEditing from '../src/tableediting';
-
 import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
-
 import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
 import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
 
+import TableEditing from '../src/tableediting';
+
 describe( 'TableEditing', () => {
 	let editor, model;
 

+ 4 - 4
packages/ckeditor5-table/tests/tableui.js

@@ -5,13 +5,13 @@
 
 /* global document */
 
-import TableEditing from '../src/tableediting';
-import TableUI from '../src/tableui';
-
 import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
-import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 import { _clear as clearTranslations, add as addTranslations } from '@ckeditor/ckeditor5-utils/src/translation-service';
 import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
+import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
+
+import TableEditing from '../src/tableediting';
+import TableUI from '../src/tableui';
 
 testUtils.createSinonSandbox();