Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

6120: Pasting table into table - base scenarios. #6786

Merged
merged 76 commits into from
May 15, 2020
Merged
Show file tree
Hide file tree
Changes from 75 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
9ac915b
Add tests for pasting scenarios.
jodator Mar 31, 2020
6f7ba10
Add paste handler stub to TableClipboard plugin.
jodator Apr 1, 2020
791649f
Add test for preventing paste in read only mode.
jodator Apr 1, 2020
8b90a88
Add test for pasting when no table cells are selected.
jodator Apr 1, 2020
c7b28a2
Listen to Clipboard plugin's "inputTransformation" event.
jodator Apr 1, 2020
7b0e49c
Handle insertContent overriding in a base paste scenario.
jodator May 6, 2020
6522563
Calculate and compare selection and inserted table areas.
jodator May 6, 2020
149bb66
Handle simple table paste over simple selection of same size.
jodator May 6, 2020
f6e0c2f
Add column/row tests for simple paste scenarios.
jodator May 6, 2020
07a5e42
Handle case of pasted table that has has colspans.
jodator May 6, 2020
06ac3f0
Handle pasting over table with colspans.
jodator May 6, 2020
b9adc75
Handle various rowspan scenarios in tests.
jodator May 6, 2020
a0c877f
Merge branch 'master' into i/6120
jodator May 7, 2020
220e9bf
Add tests for mixed col- and row-spans.
jodator May 7, 2020
9532786
Merge branch 'master' into i/6120
jodator May 7, 2020
682f949
Add option to renumber table cells using letters.
jodator May 7, 2020
1af43b2
Add tests for special selection cases (wrong area calculation).
jodator May 7, 2020
5b7fe35
Align code to the changes in table selection API.
jodator May 7, 2020
b9d1a84
Skip edge cases for handling paste.
jodator May 7, 2020
8bc3650
Disable non-rectangular selection from paste handling.
jodator May 7, 2020
432cc1b
Fix if statement that disables non-rectangular selection.
jodator May 8, 2020
ec300ea
Re-organize code to anticipate table cropping.
jodator May 8, 2020
9d156f8
Add early return when no table is found on paste.
jodator May 8, 2020
7a37015
Refactor table cropping utilities to make them reusable.
jodator May 8, 2020
67761ce
Add tests for simple pasting scenarios when pasted table exceeds sele…
jodator May 8, 2020
f5ef5c9
Update docs.
jodator May 8, 2020
32ed5df
Add stub test for bigger selection than pasted table case.
jodator May 8, 2020
de13de5
Add stub test for non-rectangular selection is pasted.
jodator May 8, 2020
448e61c
Add stub test for non-rectangular selection is pasted.
jodator May 8, 2020
cddb808
Fix cases for "pasted table has spans" and bigger pasted table.
jodator May 8, 2020
04a01ee
Remove special test cases.
jodator May 8, 2020
9b2f0d5
Merge branch 'master' into i/6120
jodator May 8, 2020
5117d98
Split table clipboard tests.
jodator May 11, 2020
be31d72
Handle block content in pasted table cells.
jodator May 11, 2020
f36df6a
Unify not implemented debug console logs.
jodator May 11, 2020
efc0f7f
Add tests cases for block content in pasted tables.
jodator May 11, 2020
ac826a5
Sort imports in tests.
jodator May 11, 2020
1139352
Handle table cell properties in pasted table.
jodator May 11, 2020
b20b4e2
Add test case for table properties in paste scenarios.
jodator May 11, 2020
fb21bb0
Refactor _onInsertContent() logic.
jodator May 11, 2020
58523f4
Organize imports in table clipboard.
jodator May 11, 2020
09c0375
Update table clipboard docs.
jodator May 11, 2020
1912c43
Fix jsdoc comments.
jodator May 11, 2020
b6fe69c
Anticipate temporary console.log in table clipboard paste tests.
jodator May 11, 2020
af0fac6
Add table clipboard tests and update its requires call.
jodator May 12, 2020
3ac58ce
Refactor cropTableToDimensions().
jodator May 12, 2020
85290c7
Update documentation of cropTableToDimensions() util.
jodator May 13, 2020
d01555d
Fix copy table tests.
jodator May 13, 2020
07500b6
Refactor variable names in insert content method.
jodator May 13, 2020
42cc975
Refactor insertionMap to use an array.
jodator May 13, 2020
80022e0
Refactor insertion map to a two dimensional location map.
jodator May 13, 2020
4926e94
Make table paste work only for document selection.
jodator May 13, 2020
70b23ae
Refactor cell replace algorithm to make more clear.
jodator May 13, 2020
afde3cf
Add information about content table replacing goals.
jodator May 13, 2020
1228069
Fix: Pasted table after spanned slots will not break table geometry.
jodator May 13, 2020
098b763
Code style - inline simple arrow function.
jodator May 13, 2020
22ca037
Improve pasted/selected nomenclature.
jodator May 13, 2020
0c09a2e
Use selected table cells in isSelectionRectangular().
jodator May 13, 2020
3e723af
Use getChildren() to iterate over document fragment.
jodator May 13, 2020
7f5451f
Remove confusing comment.
jodator May 13, 2020
c51cd4d
Rename tableMap to selectedTableMap.
jodator May 13, 2020
5d2c1e9
Refactor crop table utilities to use object parameter for crop dimens…
jodator May 13, 2020
9b46958
Fix jsdoc errors.
jodator May 13, 2020
4c0bd10
Minor refactor.
jodator May 13, 2020
92083f8
Restore comment about location map.
jodator May 13, 2020
3b03eb6
Change params order for cropTableToSelection.
jodator May 13, 2020
932cd63
Adjust trimTableCellIfNeeded parameter names and logic to the functio…
jodator May 13, 2020
eaeddf8
Merge branch 'master' into i/6120
jodator May 13, 2020
27330d9
Fixed complex copy-table fragment test (ascii-art should match test, …
niegowski May 13, 2020
ede28f4
Added missing spaces in tests.
niegowski May 13, 2020
7af3c7f
Merge branch 'master' into i/6120
jodator May 14, 2020
507f0de
Remove cropTableToSelection() util method.
jodator May 14, 2020
cc68497
Fix cropTableToDimensions jsdoc.
jodator May 14, 2020
d002dc9
Handle paste only when alone table is pasted.
jodator May 14, 2020
27a4903
Add test for content without table is pasted.
jodator May 14, 2020
3633a36
Fixed getTableIfOnlyTableInContent if content has no children.
niegowski May 14, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 4 additions & 129 deletions packages/ckeditor5-table/src/commands/mergecellscommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
*/

import Command from '@ckeditor/ckeditor5-core/src/command';
import { findAncestor, updateNumericAttribute } from './utils';
import TableUtils from '../tableutils';
import { getColumnIndexes, getRowIndexes, getSelectedTableCells } from '../utils';
import { findAncestor, updateNumericAttribute } from './utils';
import { isSelectionRectangular, getSelectedTableCells } from '../utils';

/**
* The merge cells command.
Expand All @@ -28,7 +28,8 @@ export default class MergeCellsCommand extends Command {
* @inheritDoc
*/
refresh() {
this.isEnabled = canMergeCells( this.editor.model.document.selection, this.editor.plugins.get( TableUtils ) );
const selectedTableCells = getSelectedTableCells( this.editor.model.document.selection );
this.isEnabled = isSelectionRectangular( selectedTableCells, this.editor.plugins.get( TableUtils ) );
}

/**
Expand Down Expand Up @@ -107,132 +108,6 @@ function isEmpty( tableCell ) {
return tableCell.childCount == 1 && tableCell.getChild( 0 ).is( 'paragraph' ) && tableCell.getChild( 0 ).isEmpty;
}

// Checks if the selection contains cells that can be merged.
//
// In a table below:
//
// ┌───┬───┬───┬───┐
// │ a │ b │ c │ d │
// ├───┴───┼───┤ │
// │ e │ f │ │
// ├ ├───┼───┤
// │ │ g │ h │
// └───────┴───┴───┘
//
// Valid selections are these which create a solid rectangle (without gaps), such as:
// - a, b (two horizontal cells)
// - c, f (two vertical cells)
// - a, b, e (cell "e" spans over four cells)
// - c, d, f (cell d spans over a cell in the row below)
//
// While an invalid selection would be:
// - a, c (the unselected cell "b" creates a gap)
// - f, g, h (cell "d" spans over a cell from the row of "f" cell - thus creates a gap)
//
// @param {module:engine/model/selection~Selection} selection
// @param {module:table/tableUtils~TableUtils} tableUtils
// @returns {boolean}
function canMergeCells( selection, tableUtils ) {
const selectedTableCells = getSelectedTableCells( selection );

if ( selectedTableCells.length < 2 || !areCellInTheSameTableSection( selectedTableCells ) ) {
return false;
}

// A valid selection is a fully occupied rectangle composed of table cells.
// Below we will calculate the area of a selected table cells and the area of valid selection.
// The area of a valid selection is defined by top-left and bottom-right cells.
const rows = new Set();
const columns = new Set();

let areaOfSelectedCells = 0;

for ( const tableCell of selectedTableCells ) {
const { row, column } = tableUtils.getCellLocation( tableCell );
const rowspan = parseInt( tableCell.getAttribute( 'rowspan' ) || 1 );
const colspan = parseInt( tableCell.getAttribute( 'colspan' ) || 1 );

// Record row & column indexes of current cell.
rows.add( row );
columns.add( column );

// For cells that spans over multiple rows add also the last row that this cell spans over.
if ( rowspan > 1 ) {
rows.add( row + rowspan - 1 );
}

// For cells that spans over multiple columns add also the last column that this cell spans over.
if ( colspan > 1 ) {
columns.add( column + colspan - 1 );
}

areaOfSelectedCells += ( rowspan * colspan );
}

// We can only merge table cells that are in adjacent rows...
const areaOfValidSelection = getBiggestRectangleArea( rows, columns );

return areaOfValidSelection == areaOfSelectedCells;
}

// Calculates the area of a maximum rectangle that can span over the provided row & column indexes.
//
// @param {Array.<Number>} rows
// @param {Array.<Number>} columns
// @returns {Number}
function getBiggestRectangleArea( rows, columns ) {
const rowsIndexes = Array.from( rows.values() );
const columnIndexes = Array.from( columns.values() );

const lastRow = Math.max( ...rowsIndexes );
const firstRow = Math.min( ...rowsIndexes );
const lastColumn = Math.max( ...columnIndexes );
const firstColumn = Math.min( ...columnIndexes );

return ( lastRow - firstRow + 1 ) * ( lastColumn - firstColumn + 1 );
}

// Checks if the selection does not mix a header (column or row) with other cells.
//
// For instance, in the table below valid selections consist of cells with the same letter only.
// So, a-a (same heading row and column) or d-d (body cells) are valid while c-d or a-b are not.
//
// header columns
// ↓ ↓
// ┌───┬───┬───┬───┐
// │ a │ a │ b │ b │ ← header row
// ├───┼───┼───┼───┤
// │ c │ c │ d │ d │
// ├───┼───┼───┼───┤
// │ c │ c │ d │ d │
// └───┴───┴───┴───┘
//
function areCellInTheSameTableSection( tableCells ) {
const table = findAncestor( 'table', tableCells[ 0 ] );

const rowIndexes = getRowIndexes( tableCells );
const headingRows = parseInt( table.getAttribute( 'headingRows' ) || 0 );

// Calculating row indexes is a bit cheaper so if this check fails we can't merge.
if ( !areIndexesInSameSection( rowIndexes, headingRows ) ) {
return false;
}

const headingColumns = parseInt( table.getAttribute( 'headingColumns' ) || 0 );
const columnIndexes = getColumnIndexes( tableCells );

// Similarly cells must be in same column section.
return areIndexesInSameSection( columnIndexes, headingColumns );
}

// Unified check if table rows/columns indexes are in the same heading/body section.
function areIndexesInSameSection( { first, last }, headingSectionSize ) {
const firstCellIsInHeading = first < headingSectionSize;
const lastCellIsInHeading = last < headingSectionSize;

return firstCellIsInHeading === lastCellIsInHeading;
}

function getMergeDimensions( firstTableCell, selectedTableCells, tableUtils ) {
let maxWidthOffset = 0;
let maxHeightOffset = 0;
Expand Down
Loading