Skip to content

Commit 34b199d

Browse files
committed
Faster algorithm.
1 parent 6d58de0 commit 34b199d

File tree

3 files changed

+35
-109
lines changed

3 files changed

+35
-109
lines changed

packages/ckeditor5-table/src/tableselection.ts

+9-84
Original file line numberDiff line numberDiff line change
@@ -360,18 +360,24 @@ export default class TableSelection extends Plugin {
360360
*/
361361
private _getCellsToSelect( anchorCell: Element, targetCell: Element ) {
362362
const tableUtils: TableUtils = this.editor.plugins.get( 'TableUtils' );
363+
const table = anchorCell.findAncestor( 'table' )!;
364+
363365
const startLocation = tableUtils.getCellLocation( anchorCell );
364366
const endLocation = tableUtils.getCellLocation( targetCell );
365367

366368
const startRow = Math.min( startLocation.row, endLocation.row );
367369
const endRow = Math.max( startLocation.row, endLocation.row );
368370

371+
// If users selects the colspan cell, and the previous row contains selection, the selection should be
372+
// expanded in the previous row to accommodate the size of the colspan cell.
373+
// See: https://github.com/ckeditor/ckeditor5/issues/17538
374+
const targetColumnExtraColspan = ( parseInt( targetCell.getAttribute( 'colspan' ) as string || '1' ) - 1 );
375+
369376
const startColumn = Math.min( startLocation.column, endLocation.column );
370-
const endColumn = Math.max( startLocation.column, endLocation.column );
371-
const table = anchorCell.findAncestor( 'table' )!;
377+
const endColumn = Math.max( startLocation.column, endLocation.column + targetColumnExtraColspan );
372378

373379
// First collect cells based on initial selection
374-
let selectionMap: Array<Array<Element>> = createSelectionMap( endRow - startRow + 1 );
380+
const selectionMap: Array<Array<Element>> = new Array( endRow - startRow + 1 ).fill( null ).map( () => [] );
375381

376382
const walkerOptions = {
377383
startRow,
@@ -384,48 +390,6 @@ export default class TableSelection extends Plugin {
384390
selectionMap[ row - startRow ].push( cell );
385391
}
386392

387-
// If the entire last row is selected, extend the selection to include all columns in the rows above for better UX.
388-
// This prevents a scenario where selecting the entire last row (which may contain colspans) results in only one column
389-
// being selected in the row above, instead of all columns (when colspan is equal to the total amount of columns in the table).
390-
// This adjustment is only active for top-left to bottom-right selections, as it mimics the behavior of colspan in tables.
391-
//
392-
// Example:
393-
// +---+---+---+---+
394-
// | A | B | C | D |
395-
// +---+---+---+---+
396-
// | E |
397-
// +---+---+---+---+
398-
//
399-
// The selection begins at `A` and ends at `E`. The entire last row is selected, without this condition the selection would
400-
// include only `A` and `E` cells, but with this condition, the selection will include `A`, `B`, `C`, `D` and `E` cells.
401-
//
402-
// See: https://github.com/ckeditor/ckeditor5/issues/17538
403-
if ( !startColumn && startLocation.row <= endLocation.row ) {
404-
// Pick total width of the last selection row. It includes colspan values and not-fully selected cells.
405-
const totalRowWidth = getTotalColumnsInRow( table, endRow );
406-
407-
// Pick width of all selected cells in the last selection row.
408-
const selectedCellsWidth = getTotalCellsWidth( selectionMap[ selectionMap.length - 1 ] );
409-
410-
// If last row is fully selected, adjust selection to include all columns in all rows above
411-
if ( selectedCellsWidth === totalRowWidth ) {
412-
const totalTableColumns = tableUtils.getColumns( table );
413-
const fullSelectorWalker = new TableWalker( table, {
414-
startRow,
415-
endRow,
416-
startColumn: 0,
417-
endColumn: totalTableColumns
418-
} );
419-
420-
// Let's reset the selection map and fill it with the full row selection
421-
selectionMap = createSelectionMap( endRow - startRow + 1 );
422-
423-
for ( const { row, cell } of fullSelectorWalker ) {
424-
selectionMap[ row - startRow ].push( cell );
425-
}
426-
}
427-
}
428-
429393
const flipVertically = endLocation.row < startLocation.row;
430394
const flipHorizontally = endLocation.column < startLocation.column;
431395

@@ -443,42 +407,3 @@ export default class TableSelection extends Plugin {
443407
};
444408
}
445409
}
446-
447-
/**
448-
* Creates a 2D array of selections based on the given size.
449-
*
450-
* @param size The size of the map
451-
* @returns An array of arrays of elements
452-
*/
453-
function createSelectionMap( size: number ): Array<Array<Element>> {
454-
return new Array( size ).fill( null ).map( () => [] );
455-
}
456-
457-
/**
458-
* Calculates the total width of columns in a table row by getting the sum of colspan values
459-
* of all cells in that row.
460-
*
461-
* @param table The table element to calculate the row width for
462-
* @param rowIndex The index of the row to process
463-
* @returns The total width of the row (sum of colspan values)
464-
*/
465-
function getTotalColumnsInRow( table: Element, rowIndex: number ): number {
466-
const cells = Array
467-
.from( new TableWalker( table, { row: rowIndex } ) )
468-
.map( ( { cell } ) => cell );
469-
470-
return getTotalCellsWidth( cells );
471-
}
472-
473-
/**
474-
* Calculates the total width of the given cells by summing up their colspan values.
475-
*
476-
* @param cells An array of table cell elements to process
477-
* @returns The total width (sum of colspan values) of all provided cells
478-
*/
479-
function getTotalCellsWidth( cells: Array<Element> ): number {
480-
return cells.reduce(
481-
( width, cell ) => width + ( parseInt( cell.getAttribute( 'colspan' ) as string ) || 1 ),
482-
0
483-
);
484-
}

packages/ckeditor5-table/tests/commands/mergecellscommand.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -745,9 +745,10 @@ describe( 'MergeCellsCommand', () => {
745745

746746
expect( getData( model ) ).to.equalMarkup( modelTable( [
747747
[
748-
'<paragraph>[00</paragraph><paragraph>01</paragraph><paragraph>02</paragraph>' +
748+
'<paragraph>[00</paragraph><paragraph>01</paragraph>' +
749749
'<paragraph>10</paragraph><paragraph>11</paragraph>' +
750-
'<paragraph>20</paragraph><paragraph>21]</paragraph>'
750+
'<paragraph>20</paragraph><paragraph>21]</paragraph>',
751+
'02'
751752
]
752753
] ) );
753754
} );

packages/ckeditor5-table/tests/tableclipboard-paste.js

+23-23
Original file line numberDiff line numberDiff line change
@@ -2527,15 +2527,15 @@ describe( 'table clipboard', () => {
25272527
// + + + +----+----+----+
25282528
// | | | | 14 | 15 | 16 |
25292529
// + +----+----+----+----+----+
2530-
// | | aa | ab | ac | |
2530+
// | | aa | ab | ac | aa | ab |
25312531
// +----+----+----+----+----+----+----+
2532-
// | 30 | 31 | ba | bb | bc | 35 | 36 |
2532+
// | 30 | 31 | ba | bb | bc | ba | bb |
2533+
// + +----+----+----+----+----+----+
2534+
// | | 41 | ca | cb | cc | ca | cb |
25332535
// + +----+----+----+----+----+----+
2534-
// | | 41 | ca | cb | cc | 45 |
2535-
// + +----+----+----+----+ +
25362536
// | | 51 | 52 | | 54 | |
2537-
// +----+----+----+ +----+ +
2538-
// | 60 | 61 | 62 | | 64 | |
2537+
// + +----+----+ +----+ +
2538+
// | | 61 | 62 | | 64 | |
25392539
// +----+----+----+----+----+----+----+
25402540
expect( getModelData( model, { withoutSelection: true } ) ).to.equalMarkup( modelTable( [
25412541
[
@@ -2545,10 +2545,10 @@ describe( 'table clipboard', () => {
25452545
{ contents: '04', colspan: 3 }
25462546
],
25472547
[ '14', '15', '16' ],
2548-
[ 'aa', 'ab', 'ac', { contents: '', colspan: 2 } ],
2549-
[ { contents: '30', rowspan: 3 }, '31', 'ba', 'bb', 'bc', '35', '36' ],
2550-
[ '41', 'ca', 'cb', 'cc', { contents: '45', colspan: 2, rowspan: 3 } ],
2551-
[ '51', '52', { contents: '', rowspan: 2 }, '54' ],
2548+
[ 'aa', 'ab', 'ac', 'aa', 'ab' ],
2549+
[ { contents: '30', rowspan: 3 }, '31', 'ba', 'bb', 'bc', 'ba', 'bb' ],
2550+
[ '41', 'ca', 'cb', 'cc', 'ca', 'cb' ],
2551+
[ '51', '52', { contents: '', rowspan: 2 }, '54', { contents: '', colspan: 2, rowspan: 2 } ],
25522552
[ '60', '61', '62', '64' ]
25532553
] ) );
25542554
} );
@@ -3115,27 +3115,27 @@ describe( 'table clipboard', () => {
31153115
] );
31163116

31173117
// +----+----+----+----+----+----+
3118-
// | 00 | 01 | aa | ab | aa | 05 |
3118+
// | 00 | 01 | aa | ab | aa | ab |
31193119
// +----+----+----+----+----+----+
3120-
// | 10 | 11 | ba | bb | ba | 15 |
3120+
// | 10 | 11 | ba | bb | ba | bb |
31213121
// +----+----+----+----+----+----+
3122-
// | 20 | 21 | aa | ab | aa | 25 |
3122+
// | 20 | 21 | aa | ab | aa | ab |
31233123
// +----+----+----+----+----+----+
3124-
// | 30 | 31 | ba | bb | ba | 35 |
3124+
// | 30 | 31 | ba | bb | ba | bb |
31253125
// +----+----+----+----+----+----+
3126-
// | 40 | aa | ab | aa | |
3127-
// +----+----+----+----+----+ +
3128-
// | 50 | 51 | 52 | | |
3126+
// | 40 | aa | ab | aa | ab |
3127+
// +----+----+----+----+----+----+
3128+
// | 50 | 51 | 52 | |
31293129
// +----+----+----+----+----+----+
31303130
// | 60 | 61 | 62 | 63 | 64 | 65 |
31313131
// +----+----+----+----+----+----+
31323132
expect( getModelData( model, { withoutSelection: true } ) ).to.equalMarkup( modelTable( [
3133-
[ '00', '01', 'aa', 'ab', 'aa', '05' ],
3134-
[ '10', '11', 'ba', 'bb', 'ba', '15' ],
3135-
[ '20', '21', 'aa', 'ab', 'aa', '25' ],
3136-
[ '30', '31', 'ba', 'bb', 'ba', '35' ],
3137-
[ { contents: '40', colspan: 2 }, 'aa', 'ab', 'aa', { contents: '', rowspan: 2 } ],
3138-
[ '50', '51', { contents: '52', colspan: 2 }, '' ],
3133+
[ '00', '01', 'aa', 'ab', 'aa', 'ab' ],
3134+
[ '10', '11', 'ba', 'bb', 'ba', 'bb' ],
3135+
[ '20', '21', 'aa', 'ab', 'aa', 'ab' ],
3136+
[ '30', '31', 'ba', 'bb', 'ba', 'bb' ],
3137+
[ { contents: '40', colspan: 2 }, 'aa', 'ab', 'aa', 'ab' ],
3138+
[ '50', '51', { contents: '52', colspan: 2 }, { contents: '', colspan: 2 } ],
31393139
[ '60', '61', '62', '63', '64', '65' ]
31403140
] ) );
31413141
} );

0 commit comments

Comments
 (0)