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 4e43749
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 15 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
18 changes: 9 additions & 9 deletions packages/ckeditor5-table/tests/tableclipboard-paste.js
Original file line number Diff line number Diff line change
Expand Up @@ -3082,23 +3082,23 @@ describe( 'table clipboard', () => {
// +----+----+----+ +----+----+
// | 10 | 11 | 12 | | 14 | 15 |
// +----+----+----+----+----+----+
// | aa | ab | aa | ab | aa | 25 |
// | aa | ab | aa | ab | aa | ab |
// +----+----+----+----+----+----+
// | ba | bb | ba | bb | ba | 35 |
// | ba | bb | ba | bb | ba | bb |
// +----+----+----+----+----+----+
// | aa | ab | aa | ab | aa | |
// +----+----+----+----+----+ +
// | 50 | 51 | 52 | | |
// | aa | ab | aa | ab | aa | ab |
// +----+----+----+----+----+----+
// | 50 | 51 | 52 | |
// +----+----+----+----+----+----+
// | 60 | 61 | 62 | 63 | 64 | 65 |
// +----+----+----+----+----+----+
expect( getModelData( model, { withoutSelection: true } ) ).to.equalMarkup( modelTable( [
[ '00', '01', '02', { contents: '03', rowspan: 2 }, '04', '05' ],
[ '10', '11', '12', '14', '15' ],
[ 'aa', 'ab', 'aa', 'ab', 'aa', '25' ],
[ 'ba', 'bb', 'ba', 'bb', 'ba', '35' ],
[ 'aa', 'ab', 'aa', 'ab', 'aa', { contents: '', rowspan: 2 } ],
[ '50', '51', { contents: '52', colspan: 2 }, '' ],
[ 'aa', 'ab', 'aa', 'ab', 'aa', 'ab' ],
[ 'ba', 'bb', 'ba', 'bb', 'ba', 'bb' ],
[ 'aa', 'ab', 'aa', 'ab', 'aa', 'ab' ],
[ '50', '51', { contents: '52', colspan: 2 }, { contents: '', colspan: 2 } ],
[ '60', '61', '62', '63', '64', '65' ]
] ) );
} );
Expand Down

0 comments on commit 4e43749

Please sign in to comment.