Explorar el Código

Other: Refactor TableUtils class and update docs.

Maciej Gołaszewski hace 7 años
padre
commit
2c9e410789
Se han modificado 1 ficheros con 81 adiciones y 63 borrados
  1. 81 63
      packages/ckeditor5-table/src/tableutils.js

+ 81 - 63
packages/ckeditor5-table/src/tableutils.js

@@ -59,16 +59,22 @@ export default class TableUtils extends Plugin {
 		} );
 	}
 
+	/**
+	 * Insert rows into a table.
+	 *
+	 * @param {module:engine/model/element~Element} table
+	 * @param {Object} options
+	 * @param {Number} [options.at=0] Row index at which insert rows.
+	 * @param {Number} [options.rows=1] Number of rows to insert.
+	 */
 	insertRows( table, options = {} ) {
 		const model = this.editor.model;
 
-		const rows = parseInt( options.rows ) || 1;
-		const insertAt = parseInt( options.at ) || 0;
+		const insertAt = options.at || 0;
+		const rows = options.rows || 1;
 
 		const headingRows = table.getAttribute( 'headingRows' ) || 0;
 
-		const columns = this.getColumns( table );
-
 		model.change( writer => {
 			if ( headingRows > insertAt ) {
 				writer.setAttribute( 'headingRows', headingRows + rows, table );
@@ -81,31 +87,42 @@ export default class TableUtils extends Plugin {
 			for ( const tableCellInfo of tableIterator ) {
 				const { row, rowspan, colspan, cell } = tableCellInfo;
 
-				if ( row < insertAt ) {
-					if ( rowspan > 1 ) {
-						// check whether rowspan overlaps inserts:
-						if ( row < insertAt && row + rowspan > insertAt ) {
-							writer.setAttribute( 'rowspan', rowspan + rows, cell );
-						}
-					}
-				} else if ( row === insertAt ) {
+				const isBeforeInsertedRow = row < insertAt;
+				const overlapsInsertedRow = row + rowspan > insertAt;
+
+				if ( isBeforeInsertedRow && overlapsInsertedRow ) {
+					writer.setAttribute( 'rowspan', rowspan + rows, cell );
+				}
+
+				// Calculate how many cells to insert based on the width of cells in a row at insert position.
+				// It might be lower then table width as some cells might overlaps inserted row.
+				if ( row === insertAt ) {
 					tableCellToInsert += colspan;
 				}
 			}
 
+			// If insertion occurs on the end of a table use table width.
 			if ( insertAt >= table.childCount ) {
-				tableCellToInsert = columns;
+				tableCellToInsert = this.getColumns( table );
 			}
 
 			createEmptyRows( writer, table, insertAt, rows, tableCellToInsert );
 		} );
 	}
 
+	/**
+	 * Inserts columns into a table.
+	 *
+	 * @param {module:engine/model/element~Element} table
+	 * @param {Object} options
+	 * @param {Number} [options.at=0] Column index at which insert columns.
+	 * @param {Number} [options.columns=1] Number of columns to insert.
+	 */
 	insertColumns( table, options = {} ) {
 		const model = this.editor.model;
 
-		const columns = parseInt( options.columns ) || 1;
-		const insertAt = parseInt( options.at ) || 0;
+		const insertAt = options.at || 0;
+		const columns = options.columns || 1;
 
 		model.change( writer => {
 			const tableColumns = this.getColumns( table );
@@ -144,7 +161,13 @@ export default class TableUtils extends Plugin {
 		} );
 	}
 
-	splitCellHorizontally( tableCell, cellNumber = 2 ) {
+	/**
+	 * Divides table cell horizontally into several ones.
+	 *
+	 * @param {module:engine/model/element~Element} tableCell
+	 * @param {Number} numberOfCells
+	 */
+	splitCellHorizontally( tableCell, numberOfCells = 2 ) {
 		const model = this.editor.model;
 		const table = getParentTable( tableCell );
 
@@ -152,91 +175,88 @@ export default class TableUtils extends Plugin {
 			const tableMap = [ ...new TableWalker( table ) ];
 			const cellData = tableMap.find( value => value.cell === tableCell );
 
-			const cellColumn = cellData.column;
 			const cellColspan = cellData.colspan;
-			const cellRowspan = cellData.rowspan;
 
-			const isOnlySplit = cellColspan >= cellNumber;
+			const cellsToInsert = numberOfCells - 1;
+			const attributes = {};
 
-			const cellsToInsert = cellNumber - 1;
+			if ( cellColspan >= numberOfCells ) {
+				// If the colspan is bigger then requied cells to create we don't need to update colspan on cells from the same column.
+				// The colspan will be equally devided for newly created cells and a current one.
+				const colspanOfInsertedCells = Math.floor( cellColspan / numberOfCells );
+				const newColspan = ( cellColspan - colspanOfInsertedCells * numberOfCells ) + colspanOfInsertedCells;
 
-			if ( isOnlySplit ) {
-				const colspanOfInsertedCells = Math.floor( cellColspan / cellNumber );
-				const newColspan = ( cellColspan - colspanOfInsertedCells * cellNumber ) + colspanOfInsertedCells;
+				if ( colspanOfInsertedCells > 1 ) {
+					attributes.colspan = colspanOfInsertedCells;
+				}
 
 				updateNumericAttribute( 'colspan', newColspan, tableCell, writer );
 
-				const attributes = colspanOfInsertedCells > 1 ? { colspan: colspanOfInsertedCells } : {};
+				const cellRowspan = cellData.rowspan;
 
 				if ( cellRowspan > 1 ) {
 					attributes.rowspan = cellRowspan;
 				}
+			} else {
+				const cellColumn = cellData.column;
 
-				for ( let i = 0; i < cellsToInsert; i++ ) {
-					writer.insertElement( 'tableCell', attributes, Position.createAfter( tableCell ) );
-				}
-
-				return;
-			}
+				const cellsToUpdate = tableMap.filter( ( { cell, colspan, column } ) => {
+					const isOnSameColumn = cell !== tableCell && column === cellColumn;
+					const spansOverColumn = ( column < cellColumn && column + colspan - 1 >= cellColumn );
 
-			const cellsToUpdate = tableMap.filter( value => {
-				const cell = value.cell;
+					return isOnSameColumn || spansOverColumn;
+				} );
 
-				if ( cell === tableCell ) {
-					return false;
+				for ( const { cell, colspan } of cellsToUpdate ) {
+					writer.setAttribute( 'colspan', colspan + numberOfCells - 1, cell );
 				}
-
-				const colspan = value.colspan;
-				const column = value.column;
-
-				return column === cellColumn || ( column < cellColumn && column + colspan - 1 >= cellColumn );
-			} );
-
-			for ( const tableWalkerValue of cellsToUpdate ) {
-				const colspan = tableWalkerValue.colspan;
-				const cell = tableWalkerValue.cell;
-
-				writer.setAttribute( 'colspan', colspan + cellNumber - 1, cell );
 			}
 
-			for ( let i = 0; i < cellsToInsert; i++ ) {
-				writer.insertElement( 'tableCell', Position.createAfter( tableCell ) );
-			}
+			createCells( cellsToInsert, writer, Position.createAfter( tableCell ), attributes );
 		} );
 	}
 
-	splitCellVertically( tableCell, cellNumber = 2 ) {
+	/**
+	 * Divides table cell horizontally into several ones.
+	 *
+	 * @param {module:engine/model/element~Element} tableCell
+	 * @param {Number} numberOfCells
+	 */
+	splitCellVertically( tableCell, numberOfCells = 2 ) {
 		const model = this.editor.model;
 
 		const table = getParentTable( tableCell );
 		const rowIndex = table.getChildIndex( tableCell.parent );
 
 		model.change( writer => {
-			for ( const tableWalkerValue of new TableWalker( table, { startRow: 0, endRow: rowIndex } ) ) {
-				if ( tableWalkerValue.cell !== tableCell && tableWalkerValue.row + tableWalkerValue.rowspan > rowIndex ) {
-					const rowspan = parseInt( tableWalkerValue.cell.getAttribute( 'rowspan' ) || 1 );
+			const tableMap = [ ...new TableWalker( table, { startRow: 0, endRow: rowIndex } ) ];
+
+			for ( const { cell, rowspan, row } of tableMap ) {
+				if ( cell !== tableCell && row + rowspan > rowIndex ) {
+					const rowspan = parseInt( cell.getAttribute( 'rowspan' ) || 1 );
 
-					writer.setAttribute( 'rowspan', rowspan + cellNumber - 1, tableWalkerValue.cell );
+					writer.setAttribute( 'rowspan', rowspan + numberOfCells - 1, cell );
 				}
 			}
 
-			createEmptyRows( writer, table, rowIndex + 1, cellNumber - 1, 1 );
+			createEmptyRows( writer, table, rowIndex + 1, numberOfCells - 1, 1 );
 		} );
 	}
 
 	/**
 	 * Returns number of columns for given table.
 	 *
-	 * @param {module:engine/model/element} table
+	 * @param {module:engine/model/element~Element} table Table to analyze.
 	 * @returns {Number}
 	 */
 	getColumns( table ) {
+		// Analyze first row only as all the rows should have the same width.
 		const row = table.getChild( 0 );
 
 		return [ ...row.getChildren() ].reduce( ( columns, row ) => {
-			const columnWidth = parseInt( row.getAttribute( 'colspan' ) ) || 1;
+			const columnWidth = parseInt( row.getAttribute( 'colspan' ) || 1 );
 
-			return columns + ( columnWidth );
+			return columns + columnWidth;
 		}, 0 );
 	}
 }
@@ -267,10 +287,8 @@ function createEmptyRows( writer, table, insertAt, rows, tableCellToInsert ) {
 // @param {Number} columns Number of columns to create
 // @param {module:engine/model/writer~Writer} writer
 // @param {module:engine/model/position~Position} insertPosition
-function createCells( columns, writer, insertPosition ) {
-	for ( let i = 0; i < columns; i++ ) {
-		const cell = writer.createElement( 'tableCell' );
-
-		writer.insert( cell, insertPosition );
+function createCells( cells, writer, insertPosition, attributes = {} ) {
+	for ( let i = 0; i < cells; i++ ) {
+		writer.insertElement( 'tableCell', attributes, insertPosition );
 	}
 }