@rickx/ckeditor5-line-height
Version:
LineHeight plugin for CKEditor5
345 lines (336 loc) • 13.3 kB
JavaScript
import { Command, first, Plugin, createDropdown, addListToDropdown, MenuBarMenuView, MenuBarMenuListView, MenuBarMenuListItemView, MenuBarMenuListItemButtonView, Collection, UIModel } from 'ckeditor5';
var lineHeightIcon = "xml version=\"1.0\" standalone=\"no\"?>getOptionDefinition(item)).filter((option)=>!!option);
}
function buildDefinition(options) {
const definition = {
model: {
key: LINE_HEIGHT,
values: []
},
view: {}
};
for (const option of options){
if (option.view) {
definition.model.values.push(option.model);
definition.view[option.model] = option.view;
} else {
definition.model.values.push(option.model);
definition.view[option.model] = {
key: 'style',
value: `line-height:${option.model};`
};
}
}
return definition;
}
/**
* We treat `definition` as completed if it is an object that contains `title`, `model` and `view` values.
*/ function isFullItemDefinition(definition) {
return definition.title && definition.model && definition.view;
}
/**
* The lineHeight command plugin.
*/ class LineHeightCommand extends Command {
static get pluginName() {
return 'LineHeightEditing';
}
constructor(editor){
super(editor);
}
refresh() {
const model = this.editor.model;
const selection = model.document.selection;
const firstBlock = first(selection.getSelectedBlocks());
// As first check whether to enable or disable the command as the value will always be false if the command cannot be enabled.
this.isEnabled = !!firstBlock && this._canSetLineHeight(firstBlock);
this.value = this.isEnabled && firstBlock.hasAttribute(LINE_HEIGHT) ? firstBlock.getAttribute(LINE_HEIGHT) : 'default';
}
execute(options = {}) {
const model = this.editor.model;
const selection = model.document.selection;
const value = options.value;
model.change((writer)=>{
const blocks = Array.from(selection.getSelectedBlocks()).filter((block)=>this._canSetLineHeight(block));
const currentLineHeight = blocks[0].getAttribute(LINE_HEIGHT);
const removeLineHeight = currentLineHeight === value || !value;
if (removeLineHeight) removeLineHeightFromSelection(blocks, writer);
else setLineHeightOnSelection(blocks, writer, value);
});
}
_canSetLineHeight(block) {
return this.editor.model.schema.checkAttribute(block, LINE_HEIGHT);
}
}
function removeLineHeightFromSelection(blocks, writer) {
for (const block of blocks)writer.removeAttribute(LINE_HEIGHT, block);
}
function setLineHeightOnSelection(blocks, writer, lineHeight) {
for (const block of blocks)writer.setAttribute(LINE_HEIGHT, lineHeight, block);
}
class LineHeightEditing extends Plugin {
static get pluginName() {
return 'LineHeightEditing';
}
constructor(editor){
super(editor);
// Define default configuration using named presets.
editor.config.define(LINE_HEIGHT, {
options: [
'default',
1,
1.1,
1.2,
1.3,
1.4,
1.5,
1.6,
2,
2.5
],
supportAllValues: false
});
}
init() {
const editor = this.editor;
const schema = editor.model.schema;
// Add LineHeight Command.
editor.commands.add(LINE_HEIGHT, new LineHeightCommand(editor));
// Allow LineHeight attribute on all blocks.
schema.extend('$block', {
allowAttributes: LINE_HEIGHT
});
editor.model.schema.setAttributeProperties(LINE_HEIGHT, {
isFormatting: true
});
const supportAllValues = editor.config.get('lineHeight.supportAllValues');
if (supportAllValues) {
this._prepareAnyValueConverters();
} else {
this._preparePredefinedConverters();
}
}
/**
* These converters enable keeping any value found as `style="line-height: *"` as a value of an attribute on a text even
* if it is not defined in the plugin configuration.
*/ _prepareAnyValueConverters() {
const editor = this.editor;
editor.conversion.for('downcast').attributeToAttribute({
model: {
key: LINE_HEIGHT
},
view: (attributeValue)=>{
return {
key: 'style',
value: {
'line-height': String(attributeValue)
}
};
}
});
editor.conversion.for('upcast').attributeToAttribute({
model: {
key: LINE_HEIGHT,
value: (viewElement)=>viewElement.getStyle('line-height')
},
view: {
styles: {
'line-height': /.*/
}
}
});
}
_preparePredefinedConverters() {
const editor = this.editor;
const options = normalizeOptions(editor.config.get('lineHeight.options')).filter((option)=>option.model);
// Define view to model conversion.
const definition = buildDefinition(options);
editor.conversion.attributeToAttribute(definition);
editor.conversion.for('upcast').attributeToAttribute({
view: {
styles: {
'line-height': /[\s\S]+/
}
},
model: {
key: 'lineHeight',
value: (viewElement)=>{
const value = viewElement.getStyle('line-height');
return value && definition.model.values.includes(value) ? value : null;
}
}
});
}
}
class LineHeightUI extends Plugin {
static get pluginName() {
return 'LineHeightUI';
}
init() {
const editor = this.editor;
const componentFactory = editor.ui.componentFactory;
const t = editor.t;
const command = editor.commands.get(LINE_HEIGHT);
const accessibleLabel = t('Line Height');
const listOptions = this._prepareListOptions();
// Register UI component.
componentFactory.add(LINE_HEIGHT, (locale)=>{
const dropdownView = createDropdown(locale);
addListToDropdown(dropdownView, listOptions, {
role: 'menu',
ariaLabel: accessibleLabel
});
// Create dropdown model.
dropdownView.buttonView.set({
label: accessibleLabel,
icon: lineHeightIcon,
tooltip: true
});
dropdownView.extendTemplate({
attributes: {
class: [
'ck-line-height-dropdown'
]
}
});
dropdownView.bind('isEnabled').to(command);
// Execute command when an item from the dropdown is selected.
this.listenTo(dropdownView, 'execute', (evt)=>{
const { commandName, commandParam } = evt.source;
editor.execute(commandName, {
value: commandParam
});
editor.editing.view.focus();
});
return dropdownView;
});
componentFactory.add(`menuBar:${LINE_HEIGHT}`, (locale)=>{
const menuView = new MenuBarMenuView(locale);
menuView.buttonView.set({
role: 'menuitem',
label: accessibleLabel,
icon: lineHeightIcon
});
menuView.bind('isEnabled').to(command);
const listView = new MenuBarMenuListView(locale);
for (const option of listOptions){
const listItemView = new MenuBarMenuListItemView(locale, menuView);
const buttonView = new MenuBarMenuListItemButtonView(locale);
buttonView.set({
role: 'menuitemradio',
isToggleable: true
});
buttonView.bind(...Object.keys(option.model)).to(option.model);
buttonView.delegate('execute').to(menuView);
buttonView.on('execute', ()=>{
editor.execute(option.model.commandName, {
value: option.model.commandParam
});
editor.editing.view.focus();
});
listItemView.children.add(buttonView);
listView.items.add(listItemView);
}
menuView.panelView.children.add(listView);
return menuView;
});
}
// Prepares LineHeight dropdown items.
_prepareListOptions() {
const command = this.editor.commands.get(LINE_HEIGHT);
const defaultValue = getDefaultLineHeight();
const options = this._getLocalizedOptions();
const itemDefinitions = new Collection();
const isDefault = (value)=>value === 'Default' || value === String(defaultValue);
for (const option of options){
const def = {
type: 'button',
model: new UIModel({
commandName: LINE_HEIGHT,
// commandParam: option.model,
label: option.title,
role: 'menuitemradio',
class: 'ck-line-height-option',
withText: true
})
};
if (isDefault(option.model)) {
def.model.bind('isOn').to(command, 'value', (value)=>!value);
def.model.commandParam = undefined;
} else {
def.model.bind('isOn').to(command, 'value', (value)=>value === option.model);
def.model.commandParam = option.model;
}
// Add the option to the collection.
itemDefinitions.add(def);
}
return itemDefinitions;
}
_getLocalizedOptions() {
const editor = this.editor;
const t = editor.t;
const config = editor.config.get(LINE_HEIGHT);
const localizedTitles = {
Default: t('Default')
};
const options = normalizeOptions(config.options);
return options.map((option)=>{
const title = localizedTitles[option.title];
if (title && title !== option.title) {
// Clone the option to avoid altering the original `namedPresets` from `./utils.js`.
option = Object.assign({}, option, {
title
});
}
return option;
});
}
}
class LineHeight extends Plugin {
static get requires() {
return [
LineHeightEditing,
LineHeightUI
];
}
static get pluginName() {
return 'LineHeight';
}
}
const icons = {
lineHeight: lineHeightIcon
};
export { LINE_HEIGHT, LineHeight, LineHeightEditing, LineHeightUI, icons };
//# sourceMappingURL=index.js.map