@ckeditor/ckeditor5-table
Version:
Table feature for CKEditor 5.
277 lines (276 loc) • 12.8 kB
JavaScript
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
/**
* @module table/tablecellproperties/tablecellpropertiesediting
*/
import { Plugin } from 'ckeditor5/src/core.js';
import { addBorderStylesRules, addPaddingStylesRules, addBackgroundStylesRules } from 'ckeditor5/src/engine.js';
import { downcastAttributeToStyle, getDefaultValueAdjusted, upcastBorderStyles } from '../converters/tableproperties.js';
import { TableEditing } from './../tableediting.js';
import { TableCellWidthEditing } from '../tablecellwidth/tablecellwidthediting.js';
import { TableCellPaddingCommand } from './commands/tablecellpaddingcommand.js';
import { TableCellHeightCommand } from './commands/tablecellheightcommand.js';
import { TableCellBackgroundColorCommand } from './commands/tablecellbackgroundcolorcommand.js';
import { TableCellVerticalAlignmentCommand } from './commands/tablecellverticalalignmentcommand.js';
import { TableCellHorizontalAlignmentCommand } from './commands/tablecellhorizontalalignmentcommand.js';
import { TableCellBorderStyleCommand } from './commands/tablecellborderstylecommand.js';
import { TableCellBorderColorCommand } from './commands/tablecellbordercolorcommand.js';
import { TableCellBorderWidthCommand } from './commands/tablecellborderwidthcommand.js';
import { getNormalizedDefaultCellProperties } from '../utils/table-properties.js';
import { enableProperty } from '../utils/common.js';
const VALIGN_VALUES_REG_EXP = /^(top|middle|bottom)$/;
const ALIGN_VALUES_REG_EXP = /^(left|center|right|justify)$/;
/**
* The table cell properties editing feature.
*
* Introduces table cell model attributes and their conversion:
*
* - border: `tableCellBorderStyle`, `tableCellBorderColor` and `tableCellBorderWidth`
* - background color: `tableCellBackgroundColor`
* - cell padding: `tableCellPadding`
* - horizontal and vertical alignment: `tableCellHorizontalAlignment`, `tableCellVerticalAlignment`
* - cell width and height: `tableCellWidth`, `tableCellHeight`
*
* It also registers commands used to manipulate the above attributes:
*
* - border: the `'tableCellBorderStyle'`, `'tableCellBorderColor'` and `'tableCellBorderWidth'` commands
* - background color: the `'tableCellBackgroundColor'` command
* - cell padding: the `'tableCellPadding'` command
* - horizontal and vertical alignment: the `'tableCellHorizontalAlignment'` and `'tableCellVerticalAlignment'` commands
* - width and height: the `'tableCellWidth'` and `'tableCellHeight'` commands
*/
export class TableCellPropertiesEditing extends Plugin {
/**
* @inheritDoc
*/
static get pluginName() {
return 'TableCellPropertiesEditing';
}
/**
* @inheritDoc
*/
static get isOfficialPlugin() {
return true;
}
/**
* @inheritDoc
*/
static get requires() {
return [TableEditing, TableCellWidthEditing];
}
/**
* @inheritDoc
*/
init() {
const editor = this.editor;
const schema = editor.model.schema;
const conversion = editor.conversion;
editor.config.define('table.tableCellProperties.defaultProperties', {});
const defaultTableCellProperties = getNormalizedDefaultCellProperties(editor.config.get('table.tableCellProperties.defaultProperties'), {
includeVerticalAlignmentProperty: true,
includeHorizontalAlignmentProperty: true,
includePaddingProperty: true,
isRightToLeftContent: editor.locale.contentLanguageDirection === 'rtl'
});
editor.data.addStyleProcessorRules(addBorderStylesRules);
enableBorderProperties(schema, conversion, {
color: defaultTableCellProperties.borderColor,
style: defaultTableCellProperties.borderStyle,
width: defaultTableCellProperties.borderWidth
});
editor.commands.add('tableCellBorderStyle', new TableCellBorderStyleCommand(editor, defaultTableCellProperties.borderStyle));
editor.commands.add('tableCellBorderColor', new TableCellBorderColorCommand(editor, defaultTableCellProperties.borderColor));
editor.commands.add('tableCellBorderWidth', new TableCellBorderWidthCommand(editor, defaultTableCellProperties.borderWidth));
enableProperty(schema, conversion, {
modelAttribute: 'tableCellHeight',
styleName: 'height',
attributeName: 'height',
attributeType: 'length',
defaultValue: defaultTableCellProperties.height
});
editor.commands.add('tableCellHeight', new TableCellHeightCommand(editor, defaultTableCellProperties.height));
editor.data.addStyleProcessorRules(addPaddingStylesRules);
enableProperty(schema, conversion, {
modelAttribute: 'tableCellPadding',
styleName: 'padding',
reduceBoxSides: true,
defaultValue: defaultTableCellProperties.padding
});
editor.commands.add('tableCellPadding', new TableCellPaddingCommand(editor, defaultTableCellProperties.padding));
editor.data.addStyleProcessorRules(addBackgroundStylesRules);
enableProperty(schema, conversion, {
modelAttribute: 'tableCellBackgroundColor',
styleName: 'background-color',
attributeName: 'bgcolor',
attributeType: 'color',
defaultValue: defaultTableCellProperties.backgroundColor
});
editor.commands.add('tableCellBackgroundColor', new TableCellBackgroundColorCommand(editor, defaultTableCellProperties.backgroundColor));
enableHorizontalAlignmentProperty(schema, conversion, defaultTableCellProperties.horizontalAlignment);
editor.commands.add('tableCellHorizontalAlignment', new TableCellHorizontalAlignmentCommand(editor, defaultTableCellProperties.horizontalAlignment));
enableVerticalAlignmentProperty(schema, conversion, defaultTableCellProperties.verticalAlignment);
editor.commands.add('tableCellVerticalAlignment', new TableCellVerticalAlignmentCommand(editor, defaultTableCellProperties.verticalAlignment));
}
}
/**
* Enables the `'tableCellBorderStyle'`, `'tableCellBorderColor'` and `'tableCellBorderWidth'` attributes for table cells.
*
* @param defaultBorder The default border values.
* @param defaultBorder.color The default `tableCellBorderColor` value.
* @param defaultBorder.style The default `tableCellBorderStyle` value.
* @param defaultBorder.width The default `tableCellBorderWidth` value.
*/
function enableBorderProperties(schema, conversion, defaultBorder) {
const modelAttributes = {
width: 'tableCellBorderWidth',
color: 'tableCellBorderColor',
style: 'tableCellBorderStyle'
};
schema.extend('tableCell', {
allowAttributes: Object.values(modelAttributes)
});
for (const modelAttribute of Object.values(modelAttributes)) {
schema.setAttributeProperties(modelAttribute, { isFormatting: true });
}
upcastBorderStyles(conversion, 'td', modelAttributes, defaultBorder);
upcastBorderStyles(conversion, 'th', modelAttributes, defaultBorder);
downcastAttributeToStyle(conversion, { modelElement: 'tableCell', modelAttribute: modelAttributes.style, styleName: 'border-style' });
downcastAttributeToStyle(conversion, { modelElement: 'tableCell', modelAttribute: modelAttributes.color, styleName: 'border-color' });
downcastAttributeToStyle(conversion, { modelElement: 'tableCell', modelAttribute: modelAttributes.width, styleName: 'border-width' });
}
/**
* Enables the `'tableCellHorizontalAlignment'` attribute for table cells.
*
* @param defaultValue The default horizontal alignment value.
*/
function enableHorizontalAlignmentProperty(schema, conversion, defaultValue) {
schema.extend('tableCell', {
allowAttributes: ['tableCellHorizontalAlignment']
});
schema.setAttributeProperties('tableCellHorizontalAlignment', { isFormatting: true });
conversion.for('downcast')
.attributeToAttribute({
model: {
name: 'tableCell',
key: 'tableCellHorizontalAlignment'
},
view: alignment => ({
key: 'style',
value: {
'text-align': alignment
}
})
});
conversion.for('upcast')
// Support for the `text-align:*;` CSS definition for the table cell alignment.
.attributeToAttribute({
view: {
name: /^(td|th)$/,
styles: {
'text-align': ALIGN_VALUES_REG_EXP
}
},
model: {
key: 'tableCellHorizontalAlignment',
value: (viewElement, conversionApi, data) => {
const localDefaultValue = getDefaultValueAdjusted(defaultValue, 'left', data);
const align = viewElement.getStyle('text-align');
if (align !== localDefaultValue) {
return align;
}
// Consume the style even if not applied to the element so it won't be processed by other converters.
conversionApi.consumable.consume(viewElement, { styles: 'text-align' });
}
}
})
// Support for the `align` attribute as the backward compatibility while pasting from other sources.
.attributeToAttribute({
view: {
name: /^(td|th)$/,
attributes: {
align: ALIGN_VALUES_REG_EXP
}
},
model: {
key: 'tableCellHorizontalAlignment',
value: (viewElement, conversionApi, data) => {
const localDefaultValue = getDefaultValueAdjusted(defaultValue, 'left', data);
const align = viewElement.getAttribute('align');
if (align !== localDefaultValue) {
return align;
}
// Consume the style even if not applied to the element so it won't be processed by other converters.
conversionApi.consumable.consume(viewElement, { attributes: 'align' });
}
}
});
}
/**
* Enables the `'verticalAlignment'` attribute for table cells.
*
* @param defaultValue The default vertical alignment value.
*/
function enableVerticalAlignmentProperty(schema, conversion, defaultValue) {
schema.extend('tableCell', {
allowAttributes: ['tableCellVerticalAlignment']
});
schema.setAttributeProperties('tableCellVerticalAlignment', { isFormatting: true });
conversion.for('downcast')
.attributeToAttribute({
model: {
name: 'tableCell',
key: 'tableCellVerticalAlignment'
},
view: alignment => ({
key: 'style',
value: {
'vertical-align': alignment
}
})
});
conversion.for('upcast')
// Support for the `vertical-align:*;` CSS definition for the table cell alignment.
.attributeToAttribute({
view: {
name: /^(td|th)$/,
styles: {
'vertical-align': VALIGN_VALUES_REG_EXP
}
},
model: {
key: 'tableCellVerticalAlignment',
value: (viewElement, conversionApi, data) => {
const localDefaultValue = getDefaultValueAdjusted(defaultValue, 'middle', data);
const align = viewElement.getStyle('vertical-align');
if (align !== localDefaultValue) {
return align;
}
// Consume the style even if not applied to the element so it won't be processed by other converters.
conversionApi.consumable.consume(viewElement, { styles: 'vertical-align' });
}
}
})
// Support for the `align` attribute as the backward compatibility while pasting from other sources.
.attributeToAttribute({
view: {
name: /^(td|th)$/,
attributes: {
valign: VALIGN_VALUES_REG_EXP
}
},
model: {
key: 'tableCellVerticalAlignment',
value: (viewElement, conversionApi, data) => {
const localDefaultValue = getDefaultValueAdjusted(defaultValue, 'middle', data);
const valign = viewElement.getAttribute('valign');
if (valign !== localDefaultValue) {
return valign;
}
// Consume the attribute even if not applied to the element so it won't be processed by other converters.
conversionApi.consumable.consume(viewElement, { attributes: 'valign' });
}
}
});
}