UNPKG

gdy-quill-editor

Version:

基于Quill的vue编辑器组件 npm@0.1.6

183 lines (161 loc) 6.15 kB
import Quill from 'quill' import Fuse from 'fuse.js' import emojiList from '../config/emoji-list.js' const Delta = Quill.import('delta') const Module = Quill.import('core/module') class TextAreaEmoji extends Module { constructor(quill, options) { super(quill, options) this.quill = quill this.container = document.createElement('div') this.container.classList.add('textarea-emoji-control') this.container.style.position = 'absolute' this.container.innerHTML = options.buttonIcon this.quill.container.appendChild(this.container) this.container.addEventListener( 'click', this.checkEmojiBoxExist.bind(this), false ) } checkEmojiBoxExist() { const elementExists = document.getElementById('textarea-emoji') if (elementExists) { elementExists.remove() } else { const ele_emoji_area = document.createElement('div') ele_emoji_area.id = 'textarea-emoji' this.quill.container.appendChild(ele_emoji_area) const tabToolbar = document.createElement('div') tabToolbar.id = 'tab-toolbar' ele_emoji_area.appendChild(tabToolbar) const emojiType = [ { type: 'p', name: 'people', content: '<div class="i-people"></div>' }, { type: 'n', name: 'nature', content: '<div class="i-nature"></div>' }, { type: 'd', name: 'food', content: '<div class="i-food"></div>' }, { type: 's', name: 'symbols', content: '<div class="i-symbols"></div>', }, { type: 'a', name: 'activity', content: '<div class="i-activity"></div>', }, { type: 't', name: 'travel', content: '<div class="i-travel"></div>' }, { type: 'o', name: 'objects', content: '<div class="i-objects"></div>', }, { type: 'f', name: 'flags', content: '<div class="i-flags"></div>' }, ] const tabElementHolder = document.createElement('ul') tabToolbar.appendChild(tabElementHolder) if (document.getElementById('emoji-close-div') === null) { const closeDiv = document.createElement('div') closeDiv.id = 'emoji-close-div' closeDiv.addEventListener('click', fn_close, false) document.getElementsByTagName('body')[0].appendChild(closeDiv) } else { document.getElementById('emoji-close-div').style.display = 'block' } const panel = document.createElement('div') panel.id = 'tab-panel' ele_emoji_area.appendChild(panel) const innerQuill = this.quill emojiType.map(function (emojiType) { const tabElement = document.createElement('li') tabElement.classList.add('emoji-tab') tabElement.classList.add('filter-' + emojiType.name) const tabValue = emojiType.content tabElement.innerHTML = tabValue tabElement.dataset.filter = emojiType.type tabElementHolder.appendChild(tabElement) const emojiFilter = document.querySelector('.filter-' + emojiType.name) emojiFilter.addEventListener('click', function () { const emojiContainer = document.getElementById('textarea-emoji') const tab = emojiContainer && emojiContainer.querySelector('.active') if (tab) { tab.classList.remove('active') } emojiFilter.classList.toggle('active') while (panel.firstChild) { panel.removeChild(panel.firstChild) } const type = emojiFilter.dataset.filter fn_emojiElementsToPanel(type, panel, innerQuill) }) }) const windowHeight = window.innerHeight const editorPos = this.quill.container.getBoundingClientRect().top if (editorPos > windowHeight / 2) { ele_emoji_area.style.top = '-250px' } fn_emojiPanelInit(panel, this.quill) } } } TextAreaEmoji.DEFAULTS = { buttonIcon: '<svg viewbox="0 0 18 18"><circle class="ql-fill" cx="7" cy="7" r="1"></circle><circle class="ql-fill" cx="11" cy="7" r="1"></circle><path class="ql-stroke" d="M7,10a2,2,0,0,0,4,0H7Z"></path><circle class="ql-stroke" cx="9" cy="9" r="6"></circle></svg>', } function fn_close() { const ele_emoji_plate = document.getElementById('textarea-emoji') document.getElementById('emoji-close-div').style.display = 'none' if (ele_emoji_plate) { ele_emoji_plate.remove() } } function fn_updateRange(quill) { const range = quill.getSelection() return range } function fn_emojiPanelInit(panel, quill) { fn_emojiElementsToPanel('p', panel, quill) document.querySelector('.filter-people').classList.add('active') } function fn_emojiElementsToPanel(type, panel, quill) { const fuseOptions = { shouldSort: true, matchAllTokens: true, threshold: 0.3, location: 0, distance: 100, maxPatternLength: 32, minMatchCharLength: 3, keys: ['category'], } const fuse = new Fuse(emojiList, fuseOptions) const result = fuse.search(type) result.sort(function (a, b) { return a.emoji_order - b.emoji_order }) quill.focus() const range = fn_updateRange(quill) result.map(function (emoji) { const span = document.createElement('span') const t = document.createTextNode(emoji.shortname) span.appendChild(t) span.classList.add('bem') span.classList.add('bem-' + emoji.item.name) span.classList.add('ap') span.classList.add('ap-' + emoji.item.name) const output = '' + emoji.item.code_decimal + '' span.innerHTML = output + ' ' panel.appendChild(span) const customButton = document.querySelector('.bem-' + emoji.item.name) if (customButton) { customButton.addEventListener('click', function () { // quill.insertText(range.index, customButton.innerHTML); // quill.setSelection(range.index + customButton.innerHTML.length, 0); // range.index = range.index + customButton.innerHTML.length; quill.insertEmbed(range.index, 'emoji', emoji, Quill.sources.USER) setTimeout(() => quill.setSelection(range.index + 1), 0) fn_close() }) } }) } export default TextAreaEmoji