Kaynağa Gözat

Other: Refactor downcasttable.

Maciej Gołaszewski 7 yıl önce
ebeveyn
işleme
967b593b93

+ 63 - 72
packages/ckeditor5-table/src/converters/downcasttable.js

@@ -35,20 +35,17 @@ export default function downcastTable() {
 
 		const tableElement = conversionApi.writer.createContainerElement( 'table' );
 
-		const tableIterator = new TableWalker( table );
+		const tableWalker = new TableWalker( table );
 
-		for ( const tableCellInfo of tableIterator ) {
-			const { row, table: { headingRows } } = tableCellInfo;
+		for ( const tableWalkerValue of tableWalker ) {
+			const { row } = tableWalkerValue;
 
-			const isHead = headingRows && row < headingRows;
-
-			const tableSectionElement = getTableSection( isHead ? 'thead' : 'tbody', tableElement, conversionApi, tableSections );
+			const tableSection = getOrCreateTableSection( getSectionName( tableWalkerValue ), tableElement, conversionApi, tableSections );
 			const tableRow = table.getChild( row );
 
 			// Check if row was converted
-			const trElement = getOrCreateTr( tableRow, row, tableSectionElement, conversionApi );
-
-			createViewTableCellElement( tableCellInfo, trElement, conversionApi );
+			const trElement = getOrCreateTr( tableRow, row, tableSection, conversionApi );
+			createViewTableCellElement( tableWalkerValue, ViewPosition.createAt( trElement, 'end' ), conversionApi );
 		}
 
 		const viewPosition = conversionApi.mapper.toViewPosition( data.range.start );
@@ -58,6 +55,13 @@ export default function downcastTable() {
 	}, { priority: 'normal' } );
 }
 
+function getSectionName( treeWalkerValue ) {
+	const { row, table: { headingRows } } = treeWalkerValue;
+	const isHead = row < headingRows;
+
+	return isHead ? 'thead' : 'tbody';
+}
+
 /**
  * Model row element to view <tr> element conversion helper.
  *
@@ -77,19 +81,15 @@ export function downcastInsertRow() {
 
 		const tableElement = conversionApi.mapper.toViewElement( table );
 
-		const headingRows = table.getAttribute( 'headingRows' ) || 0;
-
 		const row = table.getChildIndex( tableRow );
-		const isHeadingRow = row < headingRows;
-
-		const tableSection = getOrCreateTableSection( isHeadingRow ? 'thead' : 'tbody', tableElement, conversionApi );
 
-		const tableIterator = new TableWalker( table, { startRow: row, endRow: row } );
+		const tableWalker = new TableWalker( table, { startRow: row, endRow: row } );
 
-		for ( const tableCellInfo of tableIterator ) {
+		for ( const tableWalkerValue of tableWalker ) {
+			const tableSection = getOrCreateTableSection( getSectionName( tableWalkerValue ), tableElement, conversionApi );
 			const trElement = getOrCreateTr( tableRow, row, tableSection, conversionApi );
 
-			createViewTableCellElement( tableCellInfo, trElement, conversionApi );
+			createViewTableCellElement( tableWalkerValue, ViewPosition.createAt( trElement, 'end' ), conversionApi );
 		}
 	}, { priority: 'normal' } );
 }
@@ -112,14 +112,17 @@ export function downcastInsertCell() {
 		const tableRow = tableCell.parent;
 		const table = tableRow.parent;
 
-		const tableIterator = new TableWalker( table );
+		const tableWalker = new TableWalker( table );
 
-		for ( const tableCellInfo of tableIterator ) {
-			if ( tableCellInfo.cell === tableCell ) {
+		// We need to iterate over a table in order to get proper row & column values from a walker
+		for ( const tableWalkerValue of tableWalker ) {
+			if ( tableWalkerValue.cell === tableCell ) {
 				const trElement = conversionApi.mapper.toViewElement( tableRow );
+				const insertPosition = ViewPosition.createAt( trElement, tableRow.getChildIndex( tableCell ) );
 
-				createViewTableCellElement( tableCellInfo, trElement, conversionApi, tableRow.getChildIndex( tableCell ) );
+				createViewTableCellElement( tableWalkerValue, insertPosition, conversionApi );
 
+				// No need to iterate further.
 				return;
 			}
 		}
@@ -144,44 +147,41 @@ export function downcastAttributeChange( attribute ) {
 			return;
 		}
 
-		const headingRows = table.getAttribute( 'headingRows' ) || 0;
 		const tableElement = conversionApi.mapper.toViewElement( table );
 
 		const cachedTableSections = {};
 
-		const tableIterator = new TableWalker( table );
+		const tableWalker = new TableWalker( table );
 
-		for ( const tableCellInfo of tableIterator ) {
-			const { row, cell } = tableCellInfo;
+		for ( const tableWalkerValue of tableWalker ) {
+			const { row, cell } = tableWalkerValue;
 			const tableRow = table.getChild( row );
 
-			const tr = conversionApi.mapper.toViewElement( tableRow );
-
-			const desiredParentName = row < headingRows ? 'thead' : 'tbody';
+			const trElement = conversionApi.mapper.toViewElement( tableRow );
 
-			if ( desiredParentName !== tr.parent.name ) {
-				const tableSection = getTableSection( desiredParentName, tableElement, conversionApi, cachedTableSections );
+			const desiredParentName = getSectionName( tableWalkerValue );
 
+			if ( desiredParentName !== trElement.parent.name ) {
 				let targetPosition;
 
-				if ( desiredParentName === 'tbody' &&
-					row === data.attributeNewValue &&
-					data.attributeNewValue < data.attributeOldValue
+				if (
+					( desiredParentName == 'tbody' && row === data.attributeNewValue && data.attributeNewValue < data.attributeOldValue ) ||
+					row === 0
 				) {
+					const tableSection = getOrCreateTableSection( desiredParentName, tableElement, conversionApi, cachedTableSections );
+
 					targetPosition = ViewPosition.createAt( tableSection, 'start' );
-				} else if ( row > 0 ) {
+				} else {
 					const previousTr = conversionApi.mapper.toViewElement( table.getChild( row - 1 ) );
 
 					targetPosition = ViewPosition.createAfter( previousTr );
-				} else {
-					targetPosition = ViewPosition.createAt( tableSection, 'start' );
 				}
 
-				conversionApi.writer.move( ViewRange.createOn( tr ), targetPosition );
+				conversionApi.writer.move( ViewRange.createOn( trElement ), targetPosition );
 			}
 
 			// Check whether current columnIndex is overlapped by table cells from previous rows.
-			const cellElementName = getCellElementName( tableCellInfo );
+			const cellElementName = getCellElementName( tableWalkerValue );
 
 			const viewCell = conversionApi.mapper.toViewElement( cell );
 
@@ -192,25 +192,20 @@ export function downcastAttributeChange( attribute ) {
 			}
 		}
 
-		// TODO: maybe a postfixer?
-		if ( headingRows === 0 ) {
-			removeTableSectionIfEmpty( 'thead', tableElement, conversionApi );
-		} else if ( headingRows === table.childCount ) {
-			removeTableSectionIfEmpty( 'tbody', tableElement, conversionApi );
-		}
+		removeTableSectionIfEmpty( 'thead', tableElement, conversionApi );
+		removeTableSectionIfEmpty( 'tbody', tableElement, conversionApi );
 	}, { priority: 'normal' } );
 }
 
-// Downcast converter for tableRow model element. Converts tableCells as well.
+// Creates a table cell element in a view.
 //
-// @param {module:engine/model/element~Element} tableRow
-// @param {Number} rowIndex
-// @param {module:table/cellspans~CellSpans} cellSpans
-// @param {module:engine/view/containerelement~ContainerElement} tableSection
-function createViewTableCellElement( tableCellInfo, trElement, conversionApi, offset = 'end' ) {
-	const tableCell = tableCellInfo.cell;
+// @param {module:table/tablewalker~TableWalkerValue} tableWalkerValue
+// @param {module:engine/view/position~Position} insertPosition
+// @param conversionApi
+function createViewTableCellElement( tableWalkerValue, insertPosition, conversionApi ) {
+	const tableCell = tableWalkerValue.cell;
 
-	const cellElementName = getCellElementName( tableCellInfo );
+	const cellElementName = getCellElementName( tableWalkerValue );
 
 	// Will always consume since we're converting <tableRow> element from a parent <table>.
 	conversionApi.consumable.consume( tableCell, 'insert' );
@@ -218,9 +213,10 @@ function createViewTableCellElement( tableCellInfo, trElement, conversionApi, of
 	const cellElement = conversionApi.writer.createContainerElement( cellElementName );
 
 	conversionApi.mapper.bindElements( tableCell, cellElement );
-	conversionApi.writer.insert( ViewPosition.createAt( trElement, offset ), cellElement );
+	conversionApi.writer.insert( insertPosition, cellElement );
 }
 
+// Creates or returns an existing tr element from a view.
 function getOrCreateTr( tableRow, rowIndex, tableSection, conversionApi ) {
 	let trElement = conversionApi.mapper.toViewElement( tableRow );
 
@@ -249,21 +245,21 @@ function getOrCreateTr( tableRow, rowIndex, tableSection, conversionApi ) {
 // @param {Number} headingRows
 // @param {Number} headingColumns
 // @returns {String}
-function getCellElementName( tableCellInfo ) {
-	const headingRows = tableCellInfo.table.headingRows;
+function getCellElementName( tableWalkerValue ) {
+	const headingRows = tableWalkerValue.table.headingRows;
 
 	// Column heading are all tableCells in the first `columnHeading` rows.
-	const isHeadingForAColumn = headingRows && headingRows > tableCellInfo.row;
+	const isHeadingForAColumn = headingRows && headingRows > tableWalkerValue.row;
 
 	// So a whole row gets <th> element.
 	if ( isHeadingForAColumn ) {
 		return 'th';
 	}
 
-	const headingColumns = tableCellInfo.table.headingColumns;
+	const headingColumns = tableWalkerValue.table.headingColumns;
 
 	// Row heading are tableCells which columnIndex is lower then headingColumns.
-	const isHeadingForARow = headingColumns && headingColumns > tableCellInfo.column;
+	const isHeadingForARow = headingColumns && headingColumns > tableWalkerValue.column;
 
 	return isHeadingForARow ? 'th' : 'td';
 }
@@ -275,23 +271,18 @@ function getCellElementName( tableCellInfo ) {
 // @param conversionApi
 // @param {Object} cachedTableSection An object on which store cached elements.
 // @return {module:engine/view/containerelement~ContainerElement}
-function getTableSection( sectionName, tableElement, conversionApi, cachedTableSections ) {
+function getOrCreateTableSection( sectionName, tableElement, conversionApi, cachedTableSections = {} ) {
 	if ( cachedTableSections[ sectionName ] ) {
 		return cachedTableSections[ sectionName ];
 	}
 
-	cachedTableSections[ sectionName ] = getOrCreateTableSection( sectionName, tableElement, conversionApi );
+	cachedTableSections[ sectionName ] = getExistingTableSectionElement( sectionName, tableElement );
 
-	return cachedTableSections[ sectionName ];
-}
+	if ( !cachedTableSections[ sectionName ] ) {
+		cachedTableSections[ sectionName ] = createTableSection( sectionName, tableElement, conversionApi );
+	}
 
-// Creates or returns an existing <tbody> or <thead> element.
-//
-// @param {String} sectionName
-// @param {module:engine/view/element~Element} tableElement
-// @param conversionApi
-function getOrCreateTableSection( sectionName, tableElement, conversionApi ) {
-	return getExistingTableSectionElement( sectionName, tableElement ) || createTableSection( sectionName, tableElement, conversionApi );
+	return cachedTableSections[ sectionName ];
 }
 
 // Finds an existing <tbody> or <thead> element or returns undefined.
@@ -327,9 +318,9 @@ function createTableSection( sectionName, tableElement, conversionApi ) {
 // @param {module:engine/view/element~Element} tableElement
 // @param conversionApi
 function removeTableSectionIfEmpty( sectionName, tableElement, conversionApi ) {
-	const tHead = getExistingTableSectionElement( sectionName, tableElement );
+	const tableSection = getExistingTableSectionElement( sectionName, tableElement );
 
-	if ( tHead && tHead.childCount === 0 ) {
-		conversionApi.writer.remove( ViewRange.createOn( tHead ) );
+	if ( tableSection && tableSection.childCount === 0 ) {
+		conversionApi.writer.remove( ViewRange.createOn( tableSection ) );
 	}
 }