diff --git a/src/rich_text_editor/index.js b/src/rich_text_editor/index.js index 6803c09ee..f98c71d55 100644 --- a/src/rich_text_editor/index.js +++ b/src/rich_text_editor/index.js @@ -2,7 +2,6 @@ * * [add](#add) * * [get](#get) * * [getAll](#getall) - * * [remove](#remove) * * [getToolbarEl](#gettoolbarel) * * This module allows to customize the toolbar of the Rich Text Editor and use commands from the HTML Editing APIs. @@ -28,7 +27,7 @@ import {on, off} from 'utils/mixins' module.exports = () => { let config = {}; const defaults = require('./config/config'); - let toolbar, actions, lastEl; + let toolbar, actions, lastEl, globalRte; return { @@ -65,6 +64,7 @@ module.exports = () => { actions = config.actions || []; toolbar = document.createElement('div'); toolbar.className = `${ppfx}rte-toolbar`; + globalRte = this.initRte(document.createElement('div')); //Avoid closing on toolbar clicking on(toolbar, 'mousedown', e => e.stopPropagation()); @@ -77,48 +77,89 @@ module.exports = () => { canvas.getToolsEl().appendChild(toolbar); }, + /** - * Add a new action to the RTE toolbar + * Init the built-in RTE + * @param {HTMLElement} el + * @return {RichTextEditor} + * @private + */ + initRte(el) { + const pfx = this.pfx; + const actionbarContainer = toolbar; + const actionbar = this.actionbar; + const actions = this.actions || config.actions; + const classes = { + actionbar: `${pfx}actionbar`, + button: `${pfx}action`, + active: `${pfx}active`, + }; + const rte = new RichTextEditor({ + el, + classes, + actions, + actionbar, + actionbarContainer, + }); + + if (rte.actionbar) { + this.actionbar = rte.actionbar; + } + + if (rte.actions) { + this.actions = rte.actions; + } + + return rte; + }, + + /** + * Add a new action to the built-in RTE toolbar * @param {string} name Action name - * @param {Object} opts Action options + * @param {Object} action Action options * @example * rte.add('bold', { * icon: 'B', - * title: 'Bold', + * attributes: {title: 'Bold',} * result: rte => rte.exec('bold') * }); * rte.add('link', { * icon: 'L', - * title: 'Link', - * result: rte => { - * const url = window.prompt('Enter the link URL') - * if (url) rte.exec('createLink', url) - * } + * attributes: {title: 'Link',} + * result: rte => + * rte.insertHTML(`${rte.selection()}`) * }); */ - add(name, opts = {}) { - opts.name = name; - actions.push(opts); - //gloabl.addAction(); + add(name, action = {}) { + action.name = name; + globalRte.getActions().push(action); + globalRte.addAction(action, {sync: 1}); }, /** - * Get the command by its name - * @param {string} command Command name - * @return {Model} + * Get the action by its name + * @param {string} name Action name + * @return {Object} * @example - * var cm = rte.get('fontSize'); + * const action = rte.get('bold'); + * // {name: 'bold', ...} */ - get(command) { - return commands.where({command})[0]; + get(name) { + let result; + globalRte.getActions().forEach(action => { + if (action.name == name) { + result = action; + } + }); + return result; }, /** - * Returns the collection of commands - * @return {Collection} + * Get all actions + * @return {Array} */ getAll() { - return commands; + return globalRte.getActions(); }, /** @@ -153,24 +194,11 @@ module.exports = () => { enable(view, rte) { lastEl = view.el; const em = config.em; - const pfx = this.pfx; const el = view.getChildrenContainer(); const customRte = this.customRte; - const actionbar = this.actionbar; - const actionbarContainer = toolbar; - const classes = { - actionbar: `${pfx}actionbar`, - button: `${pfx}action`, - active: `${pfx}active`, - }; toolbar.style.display = ''; - rte = customRte ? customRte.enable(el, rte) : - new RichTextEditor({el, actionbarContainer, classes, actionbar}).enable(); - - if (rte.actionbar) { - this.actionbar = rte.actionbar; - } + rte = customRte ? customRte.enable(el, rte) : this.initRte(el).enable(); if (em) { setTimeout(this.udpatePosition.bind(this), 0); @@ -190,6 +218,7 @@ module.exports = () => { * */ disable(view, rte) { const customRte = this.customRte; + const style = toolbar.style; var el = view.getChildrenContainer(); if (customRte) { @@ -198,13 +227,14 @@ module.exports = () => { rte.disable(); } - toolbar.style.display = 'none'; + style.display = 'none'; + style.top = 0; + style.left = 0; }, /** - * Return the toolbar element + * Get the toolbar element * @return {HTMLElement} - * @private */ getToolbarEl() { return toolbar; diff --git a/src/rich_text_editor/model/RichTextEditor.js b/src/rich_text_editor/model/RichTextEditor.js index deed0b457..faf8478c0 100644 --- a/src/rich_text_editor/model/RichTextEditor.js +++ b/src/rich_text_editor/model/RichTextEditor.js @@ -5,7 +5,7 @@ import {on, off} from 'utils/mixins' const RTE_KEY = '_rte'; -const actions = { +const defActions = { bold: { name: 'bold', icon: 'B', @@ -38,6 +38,12 @@ const actions = { }, result: (rte) => rte.insertHTML(`${rte.selection()}`) }, + avoid: { + name: 'avoid', + icon: 'avoid', + attributes: {title: 'avoid'}, + result: (rte) => rte.insertHTML(`Avoid`) + }, } export default class RichTextEditor { @@ -49,22 +55,25 @@ export default class RichTextEditor { return el[RTE_KEY]; } - //el.oninput = e => settings.onChange && settings.onChange(e.target.innerHTML); - //el.onkeydown = e => (e.which === 9 && e.preventDefault()); el[RTE_KEY] = this; this.el = el; this.doc = el.ownerDocument; + //el.oninput = e => settings.onChange && settings.onChange(e.target.innerHTML); + //el.onkeydown = e => (e.which === 9 && e.preventDefault()); + this.updateActiveActions = this.updateActiveActions.bind(this); - settings.actions = settings.actions - ? settings.actions.map(action => { + const settAct = settings.actions || []; + settAct.forEach((action, i) => { if (typeof action === 'string') { - return actions[action]; - } else if (actions[action.name]) { - return {...actions[action.name], ...action}; + action = defActions[action]; + } else if (defActions[action.name]) { + action = {...defActions[action.name], ...action}; } - return action; - }) : Object.keys(actions).map(action => actions[action]) + settAct[i] = action; + }); + const actions = settAct.length ? settAct : + Object.keys(defActions).map(action => defActions[action]) settings.classes = { ...{ actionbar: 'actionbar', @@ -77,6 +86,7 @@ export default class RichTextEditor { this.actionbar = actionbar; this.settings = settings; this.classes = classes; + this.actions = actions; if (!actionbar) { const actionbarCont = settings.actionbarContainer; @@ -84,7 +94,7 @@ export default class RichTextEditor { actionbar.className = classes.actionbar; actionbarCont.appendChild(actionbar); this.actionbar = actionbar; - settings.actions.forEach(action => this.addAction(action)) + actions.forEach(action => this.addAction(action)) } settings.styleWithCSS && this.exec('styleWithCSS'); @@ -94,7 +104,7 @@ export default class RichTextEditor { } updateActiveActions() { - this.actions().forEach(action => { + this.getActions().forEach(action => { const btn = action.btn; const active = this.classes.active; btn.className = btn.className.replace(active, '').trim(); @@ -110,6 +120,7 @@ export default class RichTextEditor { this.el.contentEditable = true; on(this.el, 'mouseup keyup', this.updateActiveActions) this.syncActions(); + this.updateActiveActions(); this.el.focus(); return this; } @@ -125,7 +136,7 @@ export default class RichTextEditor { * Sync actions with the current RTE */ syncActions() { - this.actions().forEach(action => { + this.getActions().forEach(action => { const event = action.event || 'click'; action.btn[`on${event}`] = e => { action.result(this); @@ -137,8 +148,10 @@ export default class RichTextEditor { /** * Add new action to the actionbar * @param {Object} action + * @param {Object} [opts={}] */ - addAction(action) { + addAction(action, opts = {}) { + const sync = opts.sync; const btn = document.createElement('span'); const icon = action.icon; const attr = action.attributes || {}; @@ -156,14 +169,18 @@ export default class RichTextEditor { } this.actionbarEl().appendChild(btn); + + if (sync) { + this.syncActions(); + } } /** * Get the array of current actions * @return {Array} */ - actions() { - return this.settings.actions; + getActions() { + return this.actions; } /**