import extend from 'extend'; import Emitter from '../core/emitter'; import BaseTheme, { BaseTooltip } from './base'; import { Range } from '../core/selection'; import icons from '../ui/icons'; const TOOLBAR_CONFIG = [ ['bold', 'italic', 'link'], [{ header: 1 }, { header: 2 }, 'blockquote'] ]; class BubbleTheme extends BaseTheme { constructor(quill, options) { if (options.modules.toolbar != null && options.modules.toolbar.container == null) { options.modules.toolbar.container = TOOLBAR_CONFIG; } super(quill, options); this.quill.container.classList.add('ql-bubble'); } extendToolbar(toolbar) { this.tooltip = new BubbleTooltip(this.quill, this.options.bounds); this.tooltip.root.appendChild(toolbar.container); this.buildButtons([].slice.call(toolbar.container.querySelectorAll('button')), icons); this.buildPickers([].slice.call(toolbar.container.querySelectorAll('select')), icons); } } BubbleTheme.DEFAULTS = extend(true, {}, BaseTheme.DEFAULTS, { modules: { toolbar: { handlers: { link: function(value) { if (!value) { this.quill.format('link', false); } else { this.quill.theme.tooltip.edit(); } } } } } }); class BubbleTooltip extends BaseTooltip { constructor(quill, bounds) { super(quill, bounds); this.quill.on(Emitter.events.EDITOR_CHANGE, (type, range, oldRange, source) => { if (type !== Emitter.events.SELECTION_CHANGE) return; if (range != null && range.length > 0 && source === Emitter.sources.USER) { this.show(); // Lock our width so we will expand beyond our offsetParent boundaries this.root.style.left = '0px'; this.root.style.width = ''; this.root.style.width = this.root.offsetWidth + 'px'; let lines = this.quill.getLines(range.index, range.length); if (lines.length === 1) { this.position(this.quill.getBounds(range)); } else { let lastLine = lines[lines.length - 1]; let index = this.quill.getIndex(lastLine); let length = Math.min(lastLine.length() - 1, range.index + range.length - index); let bounds = this.quill.getBounds(new Range(index, length)); this.position(bounds); } } else if (document.activeElement !== this.textbox && this.quill.hasFocus()) { this.hide(); } }); } listen() { super.listen(); this.root.querySelector('.ql-close').addEventListener('click', () => { this.root.classList.remove('ql-editing'); }); this.quill.on(Emitter.events.SCROLL_OPTIMIZE, () => { // Let selection be restored by toolbar handlers before repositioning setTimeout(() => { if (this.root.classList.contains('ql-hidden')) return; let range = this.quill.getSelection(); if (range != null) { this.position(this.quill.getBounds(range)); } }, 1); }); } cancel() { this.show(); } position(reference) { let shift = super.position(reference); let arrow = this.root.querySelector('.ql-tooltip-arrow'); arrow.style.marginLeft = ''; if (shift === 0) return shift; arrow.style.marginLeft = (-1*shift - arrow.offsetWidth/2) + 'px'; } } BubbleTooltip.TEMPLATE = [ '', '
', '', '', '
' ].join(''); export { BubbleTooltip, BubbleTheme as default };