Skip to content
This repository was archived by the owner on Jun 26, 2020. It is now read-only.

Commit cdf718e

Browse files
authored
Merge pull request #97 from ckeditor/t/56
Feature: Support block content inside table. Closes #56. BREAKING CHANGE: Removed `table/commands/utils~getParentTable()` method. Use `table/commands/utils~findAncestor()` instead.
2 parents 34c8ee1 + 22be0ec commit cdf718e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+2016
-1068
lines changed

package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,14 @@
1515
"@ckeditor/ckeditor5-widget": "^10.2.0"
1616
},
1717
"devDependencies": {
18+
"@ckeditor/ckeditor5-alignment": "^10.0.2",
19+
"@ckeditor/ckeditor5-block-quote": "^10.0.2",
20+
"@ckeditor/ckeditor5-clipboard": "^10.0.2",
1821
"@ckeditor/ckeditor5-editor-classic": "^11.0.0",
22+
"@ckeditor/ckeditor5-image": "^10.2.0",
23+
"@ckeditor/ckeditor5-list": "^11.0.1",
1924
"@ckeditor/ckeditor5-paragraph": "^10.0.2",
20-
"@ckeditor/ckeditor5-undo": "^10.0.1",
25+
"@ckeditor/ckeditor5-undo": "^10.0.2",
2126
"@ckeditor/ckeditor5-utils": "^10.2.0",
2227
"eslint": "^4.15.0",
2328
"eslint-config-ckeditor5": "^1.0.7",

src/commands/insertcolumncommand.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*/
99

1010
import Command from '@ckeditor/ckeditor5-core/src/command';
11-
import { getParentTable } from './utils';
11+
import { findAncestor } from './utils';
1212
import TableUtils from '../tableutils';
1313

1414
/**
@@ -54,7 +54,7 @@ export default class InsertColumnCommand extends Command {
5454
refresh() {
5555
const selection = this.editor.model.document.selection;
5656

57-
const tableParent = getParentTable( selection.getFirstPosition() );
57+
const tableParent = findAncestor( 'table', selection.getFirstPosition() );
5858

5959
this.isEnabled = !!tableParent;
6060
}
@@ -72,8 +72,10 @@ export default class InsertColumnCommand extends Command {
7272
const selection = editor.model.document.selection;
7373
const tableUtils = editor.plugins.get( TableUtils );
7474

75-
const table = getParentTable( selection.getFirstPosition() );
76-
const tableCell = selection.getFirstPosition().parent;
75+
const firstPosition = selection.getFirstPosition();
76+
77+
const tableCell = findAncestor( 'tableCell', firstPosition );
78+
const table = tableCell.parent.parent;
7779

7880
const { column } = tableUtils.getCellLocation( tableCell );
7981
const insertAt = this.order === 'after' ? column + 1 : column;

src/commands/insertrowcommand.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*/
99

1010
import Command from '@ckeditor/ckeditor5-core/src/command';
11-
import { getParentTable } from './utils';
11+
import { findAncestor } from './utils';
1212
import TableUtils from '../tableutils';
1313

1414
/**
@@ -54,7 +54,7 @@ export default class InsertRowCommand extends Command {
5454
refresh() {
5555
const selection = this.editor.model.document.selection;
5656

57-
const tableParent = getParentTable( selection.getFirstPosition() );
57+
const tableParent = findAncestor( 'table', selection.getFirstPosition() );
5858

5959
this.isEnabled = !!tableParent;
6060
}
@@ -71,10 +71,11 @@ export default class InsertRowCommand extends Command {
7171
const selection = editor.model.document.selection;
7272
const tableUtils = editor.plugins.get( TableUtils );
7373

74-
const tableCell = selection.getFirstPosition().parent;
75-
const table = getParentTable( selection.getFirstPosition() );
74+
const tableCell = findAncestor( 'tableCell', selection.getFirstPosition() );
75+
const tableRow = tableCell.parent;
76+
const table = tableRow.parent;
7677

77-
const row = table.getChildIndex( tableCell.parent );
78+
const row = table.getChildIndex( tableRow );
7879
const insertAt = this.order === 'below' ? row + 1 : row;
7980

8081
tableUtils.insertRows( table, { rows: 1, at: insertAt } );

src/commands/inserttablecommand.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export default class InsertTableCommand extends Command {
6262
model.change( writer => {
6363
const table = tableUtils.createTable( insertPosition, rows, columns );
6464

65-
writer.setSelection( Position.createAt( table.getChild( 0 ).getChild( 0 ) ) );
65+
writer.setSelection( Position.createAt( table.getChild( 0 ).getChild( 0 ).getChild( 0 ) ) );
6666
} );
6767
}
6868
}

src/commands/mergecellcommand.js

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import Command from '@ckeditor/ckeditor5-core/src/command';
1111
import Position from '@ckeditor/ckeditor5-engine/src/model/position';
1212
import Range from '@ckeditor/ckeditor5-engine/src/model/range';
1313
import TableWalker from '../tablewalker';
14-
import { updateNumericAttribute } from './utils';
14+
import { findAncestor, updateNumericAttribute } from './utils';
1515
import TableUtils from '../tableutils';
1616

1717
/**
@@ -83,7 +83,7 @@ export default class MergeCellCommand extends Command {
8383
execute() {
8484
const model = this.editor.model;
8585
const doc = model.document;
86-
const tableCell = doc.selection.getFirstPosition().parent;
86+
const tableCell = findAncestor( 'tableCell', doc.selection.getFirstPosition() );
8787
const cellToMerge = this.value;
8888
const direction = this.direction;
8989

@@ -97,9 +97,7 @@ export default class MergeCellCommand extends Command {
9797
// Cache the parent of cell to remove for later check.
9898
const removedTableCellRow = cellToRemove.parent;
9999

100-
// Remove table cell and merge it contents with merged cell.
101-
writer.move( Range.createIn( cellToRemove ), Position.createAt( cellToExpand, 'end' ) );
102-
writer.remove( cellToRemove );
100+
mergeTableCells( cellToRemove, cellToExpand, writer );
103101

104102
const spanAttribute = this.isHorizontal ? 'colspan' : 'rowspan';
105103
const cellSpan = parseInt( tableCell.getAttribute( spanAttribute ) || 1 );
@@ -125,26 +123,26 @@ export default class MergeCellCommand extends Command {
125123
_getMergeableCell() {
126124
const model = this.editor.model;
127125
const doc = model.document;
128-
const element = doc.selection.getFirstPosition().parent;
126+
const tableCell = findAncestor( 'tableCell', doc.selection.getFirstPosition() );
129127

130-
if ( !element.is( 'tableCell' ) ) {
128+
if ( !tableCell ) {
131129
return;
132130
}
133131

134132
const tableUtils = this.editor.plugins.get( TableUtils );
135133

136134
// First get the cell on proper direction.
137135
const cellToMerge = this.isHorizontal ?
138-
getHorizontalCell( element, this.direction, tableUtils ) :
139-
getVerticalCell( element, this.direction );
136+
getHorizontalCell( tableCell, this.direction, tableUtils ) :
137+
getVerticalCell( tableCell, this.direction );
140138

141139
if ( !cellToMerge ) {
142140
return;
143141
}
144142

145143
// If found check if the span perpendicular to merge direction is equal on both cells.
146144
const spanAttribute = this.isHorizontal ? 'rowspan' : 'colspan';
147-
const span = parseInt( element.getAttribute( spanAttribute ) || 1 );
145+
const span = parseInt( tableCell.getAttribute( spanAttribute ) || 1 );
148146

149147
const cellToMergeSpan = parseInt( cellToMerge.getAttribute( spanAttribute ) || 1 );
150148

@@ -254,3 +252,31 @@ function removeEmptyRow( removedTableCellRow, writer ) {
254252

255253
writer.remove( removedTableCellRow );
256254
}
255+
256+
// Merges two table cells - will ensure that after merging cells with empty paragraph the result table cell will only have one paragraph.
257+
// If one of the merged table cell is empty the merged table cell will have contents of the non-empty table cell.
258+
// If both are empty the merged table cell will have only one empty paragraph.
259+
//
260+
// @param {module:engine/model/element~Element} cellToRemove
261+
// @param {module:engine/model/element~Element} cellToExpand
262+
// @param {module:engine/model/writer~Writer} writer
263+
function mergeTableCells( cellToRemove, cellToExpand, writer ) {
264+
if ( !isEmpty( cellToRemove ) ) {
265+
if ( isEmpty( cellToExpand ) ) {
266+
writer.remove( Range.createIn( cellToExpand ) );
267+
}
268+
269+
writer.move( Range.createIn( cellToRemove ), Position.createAt( cellToExpand, 'end' ) );
270+
}
271+
272+
// Remove merged table cell.
273+
writer.remove( cellToRemove );
274+
}
275+
276+
// Checks if passed table cell contains empty paragraph.
277+
//
278+
// @param {module:engine/model/element~Element} tableCell
279+
// @returns {Boolean}
280+
function isEmpty( tableCell ) {
281+
return tableCell.childCount == 1 && tableCell.getChild( 0 ).is( 'paragraph' ) && tableCell.getChild( 0 ).isEmpty;
282+
}

src/commands/removecolumncommand.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import Command from '@ckeditor/ckeditor5-core/src/command';
1111

1212
import TableWalker from '../tablewalker';
1313
import TableUtils from '../tableutils';
14-
import { updateNumericAttribute } from './utils';
14+
import { findAncestor, updateNumericAttribute } from './utils';
1515

1616
/**
1717
* The remove column command.
@@ -33,9 +33,9 @@ export default class RemoveColumnCommand extends Command {
3333
const selection = editor.model.document.selection;
3434
const tableUtils = editor.plugins.get( TableUtils );
3535

36-
const selectedElement = selection.getFirstPosition().parent;
36+
const tableCell = findAncestor( 'tableCell', selection.getFirstPosition() );
3737

38-
this.isEnabled = selectedElement.is( 'tableCell' ) && tableUtils.getColumns( selectedElement.parent.parent ) > 1;
38+
this.isEnabled = !!tableCell && tableUtils.getColumns( tableCell.parent.parent ) > 1;
3939
}
4040

4141
/**
@@ -47,7 +47,7 @@ export default class RemoveColumnCommand extends Command {
4747

4848
const firstPosition = selection.getFirstPosition();
4949

50-
const tableCell = firstPosition.parent;
50+
const tableCell = findAncestor( 'tableCell', firstPosition );
5151
const tableRow = tableCell.parent;
5252
const table = tableRow.parent;
5353

src/commands/removerowcommand.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import Position from '@ckeditor/ckeditor5-engine/src/model/position';
1212
import Range from '@ckeditor/ckeditor5-engine/src/model/range';
1313

1414
import TableWalker from '../tablewalker';
15-
import { updateNumericAttribute } from './utils';
15+
import { findAncestor, updateNumericAttribute } from './utils';
1616

1717
/**
1818
* The remove row command.
@@ -33,9 +33,9 @@ export default class RemoveRowCommand extends Command {
3333
const model = this.editor.model;
3434
const doc = model.document;
3535

36-
const element = doc.selection.getFirstPosition().parent;
36+
const tableCell = findAncestor( 'tableCell', doc.selection.getFirstPosition() );
3737

38-
this.isEnabled = element.is( 'tableCell' ) && element.parent.parent.childCount > 1;
38+
this.isEnabled = !!tableCell && tableCell.parent.parent.childCount > 1;
3939
}
4040

4141
/**
@@ -46,7 +46,7 @@ export default class RemoveRowCommand extends Command {
4646
const selection = model.document.selection;
4747

4848
const firstPosition = selection.getFirstPosition();
49-
const tableCell = firstPosition.parent;
49+
const tableCell = findAncestor( 'tableCell', firstPosition );
5050
const tableRow = tableCell.parent;
5151
const table = tableRow.parent;
5252

src/commands/setheadercolumncommand.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import Command from '@ckeditor/ckeditor5-core/src/command';
1111

12-
import { getParentTable, updateNumericAttribute } from './utils';
12+
import { findAncestor, updateNumericAttribute } from './utils';
1313

1414
/**
1515
* The header column command.
@@ -36,9 +36,9 @@ export default class SetHeaderColumnCommand extends Command {
3636
const selection = doc.selection;
3737

3838
const position = selection.getFirstPosition();
39-
const tableParent = getParentTable( position );
39+
const tableCell = findAncestor( 'tableCell', position );
4040

41-
const isInTable = !!tableParent;
41+
const isInTable = !!tableCell;
4242

4343
this.isEnabled = isInTable;
4444

@@ -50,7 +50,7 @@ export default class SetHeaderColumnCommand extends Command {
5050
* @readonly
5151
* @member {Boolean} #value
5252
*/
53-
this.value = isInTable && this._isInHeading( position.parent, tableParent );
53+
this.value = isInTable && this._isInHeading( tableCell, tableCell.parent.parent );
5454
}
5555

5656
/**
@@ -69,7 +69,7 @@ export default class SetHeaderColumnCommand extends Command {
6969
const tableUtils = this.editor.plugins.get( 'TableUtils' );
7070

7171
const position = selection.getFirstPosition();
72-
const tableCell = position.parent;
72+
const tableCell = findAncestor( 'tableCell', position.parent );
7373
const tableRow = tableCell.parent;
7474
const table = tableRow.parent;
7575

src/commands/setheaderrowcommand.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import Command from '@ckeditor/ckeditor5-core/src/command';
1111
import Position from '@ckeditor/ckeditor5-engine/src/model/position';
1212

13-
import { getParentTable, updateNumericAttribute } from './utils';
13+
import { createEmptyTableCell, findAncestor, updateNumericAttribute } from './utils';
1414
import TableWalker from '../tablewalker';
1515

1616
/**
@@ -37,9 +37,8 @@ export default class SetHeaderRowCommand extends Command {
3737
const selection = doc.selection;
3838

3939
const position = selection.getFirstPosition();
40-
const tableParent = getParentTable( position );
41-
42-
const isInTable = !!tableParent;
40+
const tableCell = findAncestor( 'tableCell', position );
41+
const isInTable = !!tableCell;
4342

4443
this.isEnabled = isInTable;
4544

@@ -51,7 +50,7 @@ export default class SetHeaderRowCommand extends Command {
5150
* @readonly
5251
* @member {Boolean} #value
5352
*/
54-
this.value = isInTable && this._isInHeading( position.parent, tableParent );
53+
this.value = isInTable && this._isInHeading( tableCell, tableCell.parent.parent );
5554
}
5655

5756
/**
@@ -69,7 +68,7 @@ export default class SetHeaderRowCommand extends Command {
6968
const selection = doc.selection;
7069

7170
const position = selection.getFirstPosition();
72-
const tableCell = position.parent;
71+
const tableCell = findAncestor( 'tableCell', position );
7372
const tableRow = tableCell.parent;
7473
const table = tableRow.parent;
7574

@@ -170,8 +169,9 @@ function splitHorizontally( tableCell, headingRows, writer ) {
170169

171170
if ( columnIndex !== undefined && columnIndex === column && row === endRow ) {
172171
const tableRow = table.getChild( row );
172+
const tableCellPosition = Position.createFromParentAndOffset( tableRow, cellIndex );
173173

174-
writer.insertElement( 'tableCell', attributes, Position.createFromParentAndOffset( tableRow, cellIndex ) );
174+
createEmptyTableCell( writer, tableCellPosition, attributes );
175175
}
176176
}
177177

src/commands/splitcellcommand.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import Command from '@ckeditor/ckeditor5-core/src/command';
1111
import TableUtils from '../tableutils';
12+
import { findAncestor } from './utils';
1213

1314
/**
1415
* The split cell command.
@@ -49,9 +50,9 @@ export default class SplitCellCommand extends Command {
4950
const model = this.editor.model;
5051
const doc = model.document;
5152

52-
const element = doc.selection.getFirstPosition().parent;
53+
const tableCell = findAncestor( 'tableCell', doc.selection.getFirstPosition() );
5354

54-
this.isEnabled = element.is( 'tableCell' );
55+
this.isEnabled = !!tableCell;
5556
}
5657

5758
/**
@@ -63,7 +64,7 @@ export default class SplitCellCommand extends Command {
6364
const selection = document.selection;
6465

6566
const firstPosition = selection.getFirstPosition();
66-
const tableCell = firstPosition.parent;
67+
const tableCell = findAncestor( 'tableCell', firstPosition );
6768

6869
const isHorizontally = this.direction === 'horizontally';
6970

src/commands/utils.js

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,17 @@
88
*/
99

1010
/**
11-
* Returns the parent table.
11+
* Returns the parent element of given name. Returns undefined if position is not inside desired parent.
1212
*
13-
* @param {module:engine/model/position~Position} position
13+
* @param {String} parentName Name of parent element to find.
14+
* @param {module:engine/model/position~Position|module:engine/model/position~Position} position Position to start searching.
1415
* @returns {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment}
1516
*/
16-
export function getParentTable( position ) {
17+
export function findAncestor( parentName, position ) {
1718
let parent = position.parent;
1819

1920
while ( parent ) {
20-
if ( parent.name === 'table' ) {
21+
if ( parent.name === parentName ) {
2122
return parent;
2223
}
2324

@@ -41,3 +42,16 @@ export function updateNumericAttribute( key, value, item, writer, defaultValue =
4142
writer.removeAttribute( key, item );
4243
}
4344
}
45+
46+
/**
47+
* Common method to create empty table cell - it will create proper model structure as table cell must have at least one block inside.
48+
*
49+
* @param {module:engine/model/writer~Writer} writer Model writer.
50+
* @param {module:engine/model/position~Position} insertPosition Position at which table cell should be inserted.
51+
* @param {Object} attributes Element's attributes.
52+
*/
53+
export function createEmptyTableCell( writer, insertPosition, attributes = {} ) {
54+
const tableCell = writer.createElement( 'tableCell', attributes );
55+
writer.insertElement( 'paragraph', tableCell );
56+
writer.insert( tableCell, insertPosition );
57+
}

0 commit comments

Comments
 (0)