112 lines
3.5 KiB
JavaScript
112 lines
3.5 KiB
JavaScript
|
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 = [
|
||
|
'<span class="ql-tooltip-arrow"></span>',
|
||
|
'<div class="ql-tooltip-editor">',
|
||
|
'<input type="text" data-formula="e=mc^2" data-link="https://quilljs.com" data-video="Embed URL">',
|
||
|
'<a class="ql-close"></a>',
|
||
|
'</div>'
|
||
|
].join('');
|
||
|
|
||
|
|
||
|
export { BubbleTooltip, BubbleTheme as default };
|