Skip to content

Commit

Permalink
Select all columns in previous table rows when the last row is fully …
Browse files Browse the repository at this point in the history
…selected.
  • Loading branch information
Mati365 committed Dec 30, 2024
1 parent c5143d0 commit 20d2fd4
Show file tree
Hide file tree
Showing 4 changed files with 402 additions and 236 deletions.
77 changes: 74 additions & 3 deletions packages/ckeditor5-table/src/tableselection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -368,9 +368,10 @@ export default class TableSelection extends Plugin {

const startColumn = Math.min( startLocation.column, endLocation.column );
const endColumn = Math.max( startLocation.column, endLocation.column );
const table = anchorCell.findAncestor( 'table' )!;

// 2-dimensional array of the selected cells to ease flipping the order of cells for backward selections.
const selectionMap: Array<Array<Element>> = new Array( endRow - startRow + 1 ).fill( null ).map( () => [] );
// First collect cells based on initial selection
let selectionMap: Array<Array<Element>> = createSelectionMap( endRow - startRow + 1 );

const walkerOptions = {
startRow,
Expand All @@ -379,10 +380,41 @@ export default class TableSelection extends Plugin {
endColumn
};

for ( const { row, cell } of new TableWalker( anchorCell.findAncestor( 'table' )!, walkerOptions ) ) {
for ( const { row, cell } of new TableWalker( table, walkerOptions ) ) {
selectionMap[ row - startRow ].push( cell );
}

// If the entire last row is selected, extend the selection to include all columns in the rows above for better UX.
// This prevents a scenario where selecting the entire last row (which may contain colspans) results in only one column
// being selected in the row above, instead of all columns (when colspan is equal to the total amount of columns in the table).
// This adjustment is only active for top-left to bottom-right selections.
// See: https://github.com/ckeditor/ckeditor5/issues/17538
if ( !startColumn && startLocation.row <= endLocation.row ) {
// Pick total width of the last selection row. It includes colspan values and not-fully selected cells.
const totalRowWidth = getTotalColumnsInRow( table, endRow );

// Pick width of all selected cells in the last selection row.
const selectedCellsWidth = getTotalCellsWidth( selectionMap[ selectionMap.length - 1 ] );

// If last row is fully selected, adjust selection to include all columns in all rows above
if ( selectedCellsWidth === totalRowWidth ) {
const totalTableColumns = tableUtils.getColumns( table );
const fullSelectorWalker = new TableWalker( table, {
startRow,
endRow,
startColumn: 0,
endColumn: totalTableColumns
} );

// Let's reset the selection map and fill it with the full row selection
selectionMap = createSelectionMap( endRow - startRow + 1 );

for ( const { row, cell } of fullSelectorWalker ) {
selectionMap[ row - startRow ].push( cell );
}
}
}

const flipVertically = endLocation.row < startLocation.row;
const flipHorizontally = endLocation.column < startLocation.column;

Expand All @@ -400,3 +432,42 @@ export default class TableSelection extends Plugin {
};
}
}

/**
* Creates a 2D array of selections based on the given size.
*
* @param size The size of the map
* @returns An array of arrays of elements
*/
function createSelectionMap( size: number ): Array<Array<Element>> {
return new Array( size ).fill( null ).map( () => [] );
}

/**
* Calculates the total width of columns in a table row by getting the sum of colspan values
* of all cells in that row.
*
* @param table The table element to calculate the row width for
* @param rowIndex The index of the row to process
* @returns The total width of the row (sum of colspan values)
*/
function getTotalColumnsInRow( table: Element, rowIndex: number ): number {
const cells = Array
.from( new TableWalker( table, { row: rowIndex } ) )
.map( ( { cell } ) => cell );

return getTotalCellsWidth( cells );
}

/**
* Calculates the total width of the given cells by summing up their colspan values.
*
* @param cells An array of table cell elements to process
* @returns The total width (sum of colspan values) of all provided cells
*/
function getTotalCellsWidth( cells: Array<Element> ): number {
return cells.reduce(
( width, cell ) => width + ( parseInt( cell.getAttribute( 'colspan' ) as string ) || 1 ),
0
);
}
5 changes: 2 additions & 3 deletions packages/ckeditor5-table/tests/commands/mergecellscommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -745,10 +745,9 @@ describe( 'MergeCellsCommand', () => {

expect( getData( model ) ).to.equalMarkup( modelTable( [
[
'<paragraph>[00</paragraph><paragraph>01</paragraph>' +
'<paragraph>[00</paragraph><paragraph>01</paragraph><paragraph>02</paragraph>' +
'<paragraph>10</paragraph><paragraph>11</paragraph>' +
'<paragraph>20</paragraph><paragraph>21]</paragraph>',
'02'
'<paragraph>20</paragraph><paragraph>21]</paragraph>'
]
] ) );
} );
Expand Down
Loading

0 comments on commit 20d2fd4

Please sign in to comment.