UNPKG

mofeiyu-editorjs-summary

Version:

Editor.js tool for adding colorful summarys

355 lines (311 loc) 8.97 kB
/** * Summary block for the Editor.js. * * @author Vishal Telangre * @license MIT */ /** * Build styles */ require('./index.css').toString(); /** * Import Tool's icon */ import ToolboxIcon from '../assets/icon.svg'; /** * @class Summary * @classdesc Summary Tool for Editor.js * @property {SummaryData} data - Summary Tool`s input and output data * @property {object} api - Editor.js API instance * * @typedef {object} SummaryData * @description Summary Tool`s input and output data * @property {string} type - Summary type * @property {string} message - Summary message * * @typedef {object} SummaryConfig * @description Summary Tool`s initial configuration * @property {string} defaultType - default Summary type * @property {string} messagePlaceholder - placeholder to show in Summary`s message input */ export default class Summary { /** * Get Toolbox settings * * @public * @returns {string} */ static get toolbox() { return { icon: ToolboxIcon, title: '高亮文本', }; } /** * Allow to press Enter inside the Summary block * @public * @returns {boolean} */ static get enableLineBreaks() { return true; } /** * 微信公众号复制支持, 内联的css */ static get INLINE_STYLE() { return {}; } /** * Default Summary type * * @public * @returns {string} */ static get DEFAULT_TYPE() { return 'info'; } /** * Default placeholder for Summary message * * @public * @returns {string} */ static get DEFAULT_MESSAGE_PLACEHOLDER() { return 'Type here...'; } /** * Supported Summary types * * @public * @returns {array} */ static get ALERT_TYPES() { return [ 'primary', 'secondary', 'info', 'success', 'warning', 'danger', 'light', 'dark', ]; } /** * Summary Tool`s styles * * @returns {Object} */ get CSS() { return { settingsButton: this.api.styles.settingsButton, settingsButtonActive: this.api.styles.settingsButtonActive, wrapper: 'cdx-summary', wrapperForType: (type) => `cdx-summary-${type}`, message: 'cdx-summary__message', inlineStyle: (type) => { let styles = { primary: 'background-color: #ebf8ff;border: 1px solid #4299e1;color: #2b6cb0;', secondary: 'background-color: #f7fafc;border: 1px solid #cbd5e0;color: #616161;', info: 'background-color: #e6fdff;border: 1px solid #4cd4ce;color: #00727c;', success: 'background-color: #f0fff4;border: 1px solid #68d391;color: #2f855a;', warning: 'background-color: #fffaf0;border: 1px solid #ed8936;color: #c05621;', danger: 'background-color: #fff5f5;border: 1px solid #fc8181;color: #c53030;', light: 'background-color: #fff;border: 1px solid #edf2f7;color: #1a202c;', dark: 'background-color: #2d3748;border: 1px solid #1a202c;color: #d3d3d3;', }; return styles[type] + 'border:0px;margin-top:10px; margin-bottom:10px;'; }, }; } /** * Render plugin`s main Element and fill it with saved data * * @param {SummaryData} data — previously saved data * @param {SummaryConfig} config — user config for Tool * @param {Object} api - Editor.js API * @param {boolean} readOnly - read only mode flag */ constructor({ data, config, api, readOnly }) { this.api = api; this.defaultType = config.defaultType || Summary.DEFAULT_TYPE; this.messagePlaceholder = config.messagePlaceholder || Summary.DEFAULT_MESSAGE_PLACEHOLDER; this.data = { type: Summary.ALERT_TYPES.includes(data.type) ? data.type : this.defaultType, message: data.message || '', }; this.container = undefined; this.readOnly = readOnly; } /** * Returns true to notify the core that read-only mode is supported * * @return {boolean} */ static get isReadOnlySupported() { return true; } /** * Create Summary Tool container * * @returns {Element} */ render() { const containerClasses = [ this.CSS.wrapper, this.CSS.wrapperForType(this.data.type), ]; this.container = this._make('div', containerClasses, { style: this.CSS.inlineStyle(this.data.type), }); const messageEl = this._make('p', [this.CSS.message], { contentEditable: !this.readOnly, innerHTML: this.data.message, style: this.CSS.inlineStyle(this.data.type), }); messageEl.dataset.placeholder = this.messagePlaceholder; this.container.appendChild(messageEl); return this.container; } /** * Create Block's settings block * * @returns {HTMLElement} */ renderSettings() { const settingsContainer = this._make('div'); Summary.ALERT_TYPES.forEach((type) => { const settingsButton = this._make( 'div', [ this.CSS.settingsButton, this.CSS.wrapper, this.CSS.wrapperForType(type), ], { innerHTML: 'A', } ); if (this.data.type === type) { // Highlight current type button settingsButton.classList.add(this.CSS.settingsButtonActive); } // Set up click handler settingsButton.addEventListener('click', () => { this._changeSummaryType(type); // Un-highlight previous type button settingsContainer .querySelectorAll(`.${this.CSS.settingsButton}`) .forEach((button) => button.classList.remove(this.CSS.settingsButtonActive) ); // and highlight the clicked type button settingsButton.classList.add(this.CSS.settingsButtonActive); }); settingsContainer.appendChild(settingsButton); }); return settingsContainer; } /** * Helper for changing style of Summary block with the selected Summary type * * @param {string} newType - new Summary type to be applied to the block * @private */ _changeSummaryType(newType) { // Save new type this.data.type = newType; Summary.ALERT_TYPES.forEach((type) => { const summaryClass = this.CSS.wrapperForType(type); // Remove the old Summary type class this.container.classList.remove(summaryClass); if (newType === type) { // Add an Summary class for the selected Summary type this.container.classList.add(summaryClass); let inlineStyle = this.CSS.inlineStyle(this.data.type); this.container.style.cssText = inlineStyle; this.container.childNodes[0].style.cssText = inlineStyle; } }); } /** * Extract Summary data from Summary Tool element * * @param {HTMLDivElement} summaryElement - element to save * @returns {SummaryData} */ save(summaryElement) { const messageEl = summaryElement.querySelector(`.${this.CSS.message}`); return { ...this.data, message: messageEl.innerHTML }; } /** * Helper for making Elements with attributes * * @param {string} tagName - new Element tag name * @param {array|string} classNames - list or name of CSS classname(s) * @param {Object} attributes - any attributes * @returns {Element} * @private */ _make(tagName, classNames = null, attributes = {}) { let el = document.createElement(tagName); if (Array.isArray(classNames)) { el.classList.add(...classNames); } else if (classNames) { el.classList.add(classNames); } for (let attrName in attributes) { el[attrName] = attributes[attrName]; } return el; } /** * Fill Summary's message with the pasted content * * @param {PasteEvent} event - event with pasted content */ onPaste(event) { const { data } = event.detail; this.data = { type: this.defaultType, message: data.innerHTML || '', }; } /** * Allow Summary to be converted to/from other blocks */ static get conversionConfig() { return { // export Summary's message for other blocks export: (data) => data.message, // fill Summary's message from other block's export string import: (string) => { return { message: string, type: this.DEFAULT_TYPE, }; }, }; } /** * Sanitizer config for Summary Tool saved data * @returns {Object} */ static get sanitize() { return { type: false, message: true, }; } }