UNPKG

@rickx/ckeditor5-line-height

Version:
345 lines (336 loc) 13.3 kB
import { Command, first, Plugin, createDropdown, addListToDropdown, MenuBarMenuView, MenuBarMenuListView, MenuBarMenuListItemView, MenuBarMenuListItemButtonView, Collection, UIModel } from 'ckeditor5'; var lineHeightIcon = "<?xml version=\"1.0\" standalone=\"no\"?><!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\"><svg t=\"1545631834210\" class=\"icon\" style=\"\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"1840\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"20\" height=\"20\"><defs><style type=\"text/css\"></style></defs><path d=\"M648 160H104c-4.4 0-8 3.6-8 8v128c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-64h168v560h-92c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h264c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8h-92V232h168v64c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V168c0-4.4-3.6-8-8-8z m272.8 546H856V318h64.8c6 0 9.4-7 5.7-11.7L825.7 178.7c-2.9-3.7-8.5-3.7-11.3 0L713.6 306.3c-3.7 4.7-0.4 11.7 5.7 11.7H784v388h-64.8c-6 0-9.4 7-5.7 11.7l100.8 127.5c2.9 3.7 8.5 3.7 11.3 0l100.8-127.5c3.8-4.7 0.4-11.7-5.6-11.7z\" fill=\"#000000\" p-id=\"1841\"></path></svg>"; /** * The name of the lineHeight plugin. */ const LINE_HEIGHT = 'lineHeight'; const CSS_VARIABLE_NAME = '--ck-content-line-height'; function getDefaultLineHeight() { return getComputedStyle(document.body).getPropertyValue(CSS_VARIABLE_NAME); } function getOptionDefinition(option) { // Check whether passed option is a full item definition provided by user in configuration. if (typeof option === 'object' && isFullItemDefinition(option)) return option; // 'Default' lineHeight. It will be used to remove the lineHeight attribute. if (option === 'default' || option === 'Default') { return { title: 'Default', model: undefined }; } return generatePreset(option); } function generatePreset(definition) { if (typeof definition !== 'object') { definition = { title: String(definition), model: String(definition) }; } return { title: definition.title, model: definition.model }; } function normalizeOptions(configuredOptions) { return configuredOptions.map((item)=>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