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

Support for default table (and cells) properties #9393

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
31041d1
An initial implementation of the default styles for the table/cell.
pomek Mar 26, 2021
445f60b
Added tests for default table/cell properties.
pomek Mar 29, 2021
24b747a
API Docs: Added default table/cell properties to docs.
pomek Mar 29, 2021
801dd24
InsertColumnCommand: Support for TableCellProperties.
pomek Mar 30, 2021
9aa0ad2
InsertRowCommand: Support for TableCellProperties.
pomek Mar 30, 2021
1ddab6b
SplitCellCommand: Support for TableCellProperties.
pomek Mar 30, 2021
efa3e19
Support for default table properties when splitting a cell.
pomek Mar 31, 2021
149858d
The table layout post-fixer uses the default cell properties.
pomek Mar 31, 2021
3607d42
Restored .travis.yml
pomek Mar 31, 2021
072100d
Updated the table properties manual tests (included default props).
pomek Mar 31, 2021
eddf6af
Docs for the default table/cell properties.
pomek Mar 31, 2021
fa5f455
Removed default values for attributes/options.
pomek Mar 31, 2021
0166366
Merge branch 'master' into i/8502
pomek Mar 31, 2021
912cb06
Fixed tabs in API docs.
pomek Mar 31, 2021
5ad7484
Merge branch 'master' into i/8502
pomek Apr 8, 2021
0a1201d
Added the default table/cell properties to API docs.
pomek Apr 8, 2021
e245caf
Apply suggestions from code review
pomek Apr 8, 2021
c7b23f2
Removed the "default" word from tableProperties and cellProperties. F…
pomek Apr 8, 2021
88046d8
Extended available options/attributes for the modelTable() util.
pomek Apr 8, 2021
bab6ca2
Table and cells default properties decorate proper commands now inste…
pomek Apr 9, 2021
a1bf418
Reverted InsertTableCommand.
pomek Apr 9, 2021
f3292f0
Reverted SplitCellCommand.
pomek Apr 9, 2021
b00eba2
Clean the code.
pomek Apr 9, 2021
7def432
Added the table cell default properties post fixer.
pomek Apr 9, 2021
6ae94d8
Final improvements.
pomek Apr 9, 2021
977b69e
Simplified post-fixer.
niegowski Apr 12, 2021
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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<div id="snippet-default-properties"></div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

/* globals ClassicEditor, CKEditorPlugins, console, window, document */

ClassicEditor
.create( document.querySelector( '#snippet-default-properties' ), {
extraPlugins: [
CKEditorPlugins.TableProperties,
CKEditorPlugins.TableCellProperties
],
table: {
contentToolbar: [ 'tableColumn', 'tableRow', 'mergeTableCells', 'tableProperties', 'tableCellProperties' ],
tableProperties: {
defaultProperties: {
borderStyle: 'dashed',
borderColor: 'hsl(0, 0%, 60%)',
borderWidth: '3px',
alignment: 'left'
}
},
tableCellProperties: {
defaultProperties: {
borderStyle: 'dotted',
borderColor: 'hsl(120, 75%, 60%)',
borderWidth: '2px',
horizontalAlignment: 'right',
verticalAlignment: 'bottom'
}
}
},
image: {
toolbar: [
'imageStyle:full',
'imageStyle:side',
'|',
'imageTextAlternative'
]
},
placeholder: 'Insert the new table with applied the default styles.'
} )
.then( editor => {
window.editorDefaultStyles = editor;

window.attachTourBalloon( {
target: window.findToolbarItem( editor.ui.view.toolbar, 0 ),
text: 'Click to insert the new table.',
editor
} );
} )
.catch( err => {
console.error( err.stack );
} );
39 changes: 39 additions & 0 deletions packages/ckeditor5-table/docs/features/table.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,45 @@ ClassicEditor
.catch( ... );
```

### Default table and cell styles

You can specify the default styles for the table and cells that will be applied while creating a new table. To configure default table styles, pass the `defaultProperties` object to {@link module:table/table~TableConfig#tableProperties} configuration option. Similarly, cell styles can be configured by setting `defaultProperties` on {@link module:table/table~TableConfig#tableCellProperties} option.

```js
const tableConfig = {
table: {
tableProperties: {
// The default styles for new tables.
defaultProperties: {
borderStyle: 'dashed',
borderColor: 'hsl(0, 0%, 60%)',
borderWidth: '3px',
alignment: 'left'
}
},

tableCellProperties: {
// The default styles for each cell inside the created table.
defaultProperties: {
borderStyle: 'dotted',
borderColor: 'hsl(120, 75%, 60%)',
borderWidth: '2px',
horizontalAlignment: 'right',
verticalAlignment: 'bottom'
}
}
}
};
```

Read more about supported values in {@link module:table/table~TableConfig}.

{@snippet features/table-default-properties}

<info-box>
The default table and cell styles **do not** impact the {@link builds/guides/integration/basic-api#setting-the-editor-data data loaded into the editor}. They are used only when creating the new table.
</info-box>

## Block vs inline content in table cells

The table feature allows creating block content (like paragraphs, lists, headings, etc.) in table cells. However, if a table cell contains just one paragraph and this paragraph has no special attributes (like text alignment), the cell content is considered "inline" and the paragraph is not rendered.
Expand Down
6 changes: 5 additions & 1 deletion packages/ckeditor5-table/src/commands/insertcolumncommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ export default class InsertColumnCommand extends Command {
const column = insertBefore ? columnIndexes.first : columnIndexes.last;
const table = affectedTableCells[ 0 ].findAncestor( 'table' );

tableUtils.insertColumns( table, { columns: 1, at: insertBefore ? column : column + 1 } );
// The `TableUtils` plugin fires the `#insertColumns` event. In order to having a single undo step,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// The `TableUtils` plugin fires the `#insertColumns` event. In order to having a single undo step,
// The `TableUtils` plugin fires the `#insertColumns` event. In order to have a single undo step,

// we need to wrap the code in the `editor.model.change()` block.
editor.model.change( () => {
tableUtils.insertColumns( table, { columns: 1, at: insertBefore ? column : column + 1 } );
} );
}
}
6 changes: 5 additions & 1 deletion packages/ckeditor5-table/src/commands/insertrowcommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ export default class InsertRowCommand extends Command {
const row = insertAbove ? rowIndexes.first : rowIndexes.last;
const table = affectedTableCells[ 0 ].findAncestor( 'table' );

tableUtils.insertRows( table, { at: insertAbove ? row : row + 1, copyStructureFromAbove: !insertAbove } );
// The `TableUtils` plugin fires the `#insertRows` event. In order to having a single undo step,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// The `TableUtils` plugin fires the `#insertRows` event. In order to having a single undo step,
// The `TableUtils` plugin fires the `#insertRows` event. In order to have a single undo step,

// we need to wrap the code in the `editor.model.change()` block.
editor.model.change( () => {
tableUtils.insertRows( table, { at: insertAbove ? row : row + 1, copyStructureFromAbove: !insertAbove } );
} );
}
}
2 changes: 1 addition & 1 deletion packages/ckeditor5-table/src/converters/downcast.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @module table/converters/downcast
*/

import TableWalker from './../tablewalker';
import TableWalker from '../tablewalker';
import { setHighlightHandling, toWidget, toWidgetEditable } from 'ckeditor5/src/widget';
import { toArray } from 'ckeditor5/src/utils';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

/**
* @module table/converters/table-layout-post-fixer
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @module table/converters/table-layout-post-fixer
* @module table/converters/table-cell-default-properties-post-fixer

*/

const TABLE_CELL_PROPERTIES = [
'borderStyle',
'borderColor',
'borderWidth',
'backgroundColor',
'padding',
'horizontalAlignment',
'verticalAlignment',
'width',
'height'
];

/**
* Injects a table cell default properties post-fixer into the model.
*
* A table cell should have specified the default properties when a new cell was added into the table.
*
* @param {module:core/editor/editor~Editor} editor
*/
export default function injectTableCellDefaultPropertiesPostFixer( editor ) {
editor.model.document.registerPostFixer( writer => tableCellDefaultPropertiesPostFixer( writer, editor ) );
}

// The table cell default properties post-fixer.
//
// @param {module:engine/model/writer~Writer} writer
// @param {module:core/editor/editor~Editor} editor
function tableCellDefaultPropertiesPostFixer( writer, editor ) {
const model = editor.model;
const changes = model.document.differ.getChanges();
const cellProperties = editor.config.get( 'table.tableCellProperties.defaultProperties' );

// Do not check anything if the default cell properties are not specified.
if ( Object.keys( cellProperties ).length === 0 ) {
return false;
}
Comment on lines +42 to +45
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be checked before registering the post-fixer.


let wasFixed = false;

for ( const entry of changes ) {
if ( entry.type != 'insert' ) {
continue;
}

// Fix table on adding/removing table cells and rows.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Fix table on adding/removing table cells and rows.
// Fix table cells on adding table rows.

if ( entry.name == 'tableRow' ) {
const tableRow = entry.position.nodeAfter;

// For each cell in the table row...
for ( const tableCell of tableRow.getChildren() ) {
// ...check its cell properties...
if ( shouldApplyDefaultCellProperties( tableCell ) ) {
// ...and if the cell has no properties, apply the default.
writer.setAttributes( cellProperties, tableCell );

wasFixed = true;
}
}
}

// Fix table cell on adding/removing table cells and rows.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Fix table cell on adding/removing table cells and rows.
// Fix table cell after adding it.

if ( entry.name == 'tableCell' ) {
const tableCell = entry.position.nodeAfter;

if ( shouldApplyDefaultCellProperties( tableCell ) ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if ( shouldApplyDefaultCellProperties( tableCell ) ) {
// Check cell's properties...
if ( shouldApplyDefaultCellProperties( tableCell ) ) {

// ...and if the cell has no properties, apply the default.
writer.setAttributes( cellProperties, tableCell );

wasFixed = true;
}
}
}

return wasFixed;
}

// Checks whether the default properties should be applied for the specified cell.
//
// The default properties will be applied only if the cell does not contain any "visual" properties.
//
// @param {module:engine/model/element~Element} tableCell
// @returns {Boolean}
function shouldApplyDefaultCellProperties( tableCell ) {
const attrs = [ ...tableCell.getAttributeKeys() ];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const attrs = [ ...tableCell.getAttributeKeys() ];
const attrs = Array.from( tableCell.getAttributeKeys() );

To be more explicit.


return attrs.some( attributeName => TABLE_CELL_PROPERTIES.includes( attributeName ) ) === false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this line would be easier to read if we flip this helper logic to hasCellProperties ?

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @module table/converters/table-layout-post-fixer
*/

import TableWalker from './../tablewalker';
import TableWalker from '../tablewalker';
import { createEmptyTableCell, updateNumericAttribute } from '../utils/common';

/**
Expand Down
26 changes: 26 additions & 0 deletions packages/ckeditor5-table/src/tablecellproperties.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,32 @@ export default class TableCellProperties extends Plugin {
* }
* };
*
* * The default styles for the cell while creating a new table (`tableCellProperties.defaultProperties`):
*
* const tableConfig = {
* tableCellProperties: {
* defaultProperties: {
* borderStyle: 'dotted',
* borderColor: 'hsl(120, 75%, 60%)',
* borderWidth: '2px',
* horizontalAlignment: 'right',
* verticalAlignment: 'bottom'
pomek marked this conversation as resolved.
Show resolved Hide resolved
* }
* }
* }
*
* The following properties are supported:
*
* * `backgroundColor` – sets the cell background color
* * `borderColor` – sets the border color
* * `borderStyle` – sets the border style
* * `borderWidth` – sets the border width
* * `height` – sets the cell height
* * `horizontalAlignment` – sets the cell horizontal alignment
* * `padding` – sets the cell padding
* * `verticalAlignment` – sets the cell vertical alignment
* * `width` – sets the cell width
*
* **Note**: The configurations do not impact the data loaded into the editor,
* i.e. they do not limit or filter the colors in the data. They are used only in the user interface
* allowing users to pick colors in a more convenient way.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import TableCellHorizontalAlignmentCommand from './commands/tablecellhorizontala
import TableCellBorderStyleCommand from './commands/tablecellborderstylecommand';
import TableCellBorderColorCommand from './commands/tablecellbordercolorcommand';
import TableCellBorderWidthCommand from './commands/tablecellborderwidthcommand';
import TableWalker from '../tablewalker';
import TableUtils from '../tableutils';
import injectTableCellDefaultPropertiesPostFixer from '../converters/table-cell-default-properties-post-fixer';

const VALIGN_VALUES_REG_EXP = /^(top|bottom)$/;

Expand Down Expand Up @@ -57,7 +60,7 @@ export default class TableCellPropertiesEditing extends Plugin {
* @inheritDoc
*/
static get requires() {
return [ TableEditing ];
return [ TableEditing, TableUtils ];
}

/**
Expand All @@ -69,6 +72,8 @@ export default class TableCellPropertiesEditing extends Plugin {
const conversion = editor.conversion;
const locale = editor.locale;

editor.config.define( 'table.tableCellProperties.defaultProperties', {} );

editor.data.addStyleProcessorRules( addBorderRules );
enableBorderProperties( schema, conversion );
editor.commands.add( 'tableCellBorderStyle', new TableCellBorderStyleCommand( editor ) );
Expand All @@ -94,6 +99,36 @@ export default class TableCellPropertiesEditing extends Plugin {

enableVerticalAlignmentProperty( schema, conversion );
editor.commands.add( 'tableCellVerticalAlignment', new TableCellVerticalAlignmentCommand( editor ) );

this._enableDefaultCellProperties();

injectTableCellDefaultPropertiesPostFixer( editor );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not pass the whole editor, we should pass only needed parameters, in this case, I would pass the model instance and the default cell attributes object.

}

/**
* Enables applying the default cell properties.
*
* @private
*/
_enableDefaultCellProperties() {
const editor = this.editor;
const tableCellProperties = editor.config.get( 'table.tableCellProperties.defaultProperties' );
const tableUtils = editor.plugins.get( TableUtils );

// Apply default cell properties while creating a new table.
this.listenTo( tableUtils, 'createTable', ( evt, [ writer ] ) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not use the writer from the event's data. We should have here a change block.

const tableElement = evt.return;

for ( const item of new TableWalker( tableElement ) ) {
writer.setAttributes( tableCellProperties, item.cell );
}
}, { priority: 'low' } );

// Apply default cell properties while inserting new rows into the table.
this.listenTo( tableUtils, 'insertRows', applyDefaultCellProperties( editor, tableCellProperties ), { priority: 'low' } );

// Apply default cell properties while inserting new rows into the table.
this.listenTo( tableUtils, 'insertColumns', applyDefaultCellProperties( editor, tableCellProperties ), { priority: 'low' } );
}
}

Expand Down Expand Up @@ -202,3 +237,20 @@ function enableProperty( schema, conversion, modelAttribute, styleName ) {
upcastStyleToAttribute( conversion, 'tableCell', modelAttribute, styleName );
downcastAttributeToStyle( conversion, 'tableCell', modelAttribute, styleName );
}

// A factory function that returns a handler which applies default cell properties for created cells.
//
// @param {module:core/editor/editor~Editor} editor
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not pass the whole editor instance if not needed, please replace it with the model.

// @param {module:table/table~TableConfig#tableCellProperties} tableCellProperties
// @return {Function}
function applyDefaultCellProperties( editor, tableCellProperties ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rename this helper to indicate that it is a factory (it's not applying anything itself).

return evt => {
const createdCells = evt.return;

editor.model.change( writer => {
for ( const tableCell of createdCells ) {
writer.setAttributes( tableCellProperties, tableCell );
}
} );
};
}
23 changes: 23 additions & 0 deletions packages/ckeditor5-table/src/tableproperties.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,29 @@ export default class TableProperties extends Plugin {
* }
* };
*
* * The default styles for the new created table (`tableProperties.defaultProperties`):
*
* const tableConfig = {
* tableProperties: {
* defaultProperties: {
* borderStyle: 'dashed',
* borderColor: 'hsl(0, 0%, 90%)',
* borderWidth: '3px',
* alignment: 'left'
* }
* }
* }
pomek marked this conversation as resolved.
Show resolved Hide resolved
*
* The following properties are supported:
*
* * `alignment` – sets the table alignment
* * `backgroundColor` – sets the table background color
* * `borderColor` – sets the border color
* * `borderStyle` – sets the border style
* * `borderWidth` – sets the border width
* * `height` – sets the table height
* * `width` – sets the table width
*
* **Note**: The configurations do not impact the data loaded into the editor,
* i.e. they do not limit or filter the colors in the data. They are used only in the user interface
* allowing users to pick colors in a more convenient way.
Expand Down
Loading