forked from zicloud/bigscreen_admin
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
257 lines
8.8 KiB
JavaScript
257 lines
8.8 KiB
JavaScript
import { merge } from 'lodash-es';
|
|
import Emitter from '../core/emitter.js';
|
|
import Theme from '../core/theme.js';
|
|
import ColorPicker from '../ui/color-picker.js';
|
|
import IconPicker from '../ui/icon-picker.js';
|
|
import Picker from '../ui/picker.js';
|
|
import Tooltip from '../ui/tooltip.js';
|
|
const ALIGNS = [false, 'center', 'right', 'justify'];
|
|
const COLORS = ['#000000', '#e60000', '#ff9900', '#ffff00', '#008a00', '#0066cc', '#9933ff', '#ffffff', '#facccc', '#ffebcc', '#ffffcc', '#cce8cc', '#cce0f5', '#ebd6ff', '#bbbbbb', '#f06666', '#ffc266', '#ffff66', '#66b966', '#66a3e0', '#c285ff', '#888888', '#a10000', '#b26b00', '#b2b200', '#006100', '#0047b2', '#6b24b2', '#444444', '#5c0000', '#663d00', '#666600', '#003700', '#002966', '#3d1466'];
|
|
const FONTS = [false, 'serif', 'monospace'];
|
|
const HEADERS = ['1', '2', '3', false];
|
|
const SIZES = ['small', false, 'large', 'huge'];
|
|
class BaseTheme extends Theme {
|
|
constructor(quill, options) {
|
|
super(quill, options);
|
|
const listener = e => {
|
|
if (!document.body.contains(quill.root)) {
|
|
document.body.removeEventListener('click', listener);
|
|
return;
|
|
}
|
|
if (this.tooltip != null &&
|
|
// @ts-expect-error
|
|
!this.tooltip.root.contains(e.target) &&
|
|
// @ts-expect-error
|
|
document.activeElement !== this.tooltip.textbox && !this.quill.hasFocus()) {
|
|
this.tooltip.hide();
|
|
}
|
|
if (this.pickers != null) {
|
|
this.pickers.forEach(picker => {
|
|
// @ts-expect-error
|
|
if (!picker.container.contains(e.target)) {
|
|
picker.close();
|
|
}
|
|
});
|
|
}
|
|
};
|
|
quill.emitter.listenDOM('click', document.body, listener);
|
|
}
|
|
addModule(name) {
|
|
const module = super.addModule(name);
|
|
if (name === 'toolbar') {
|
|
// @ts-expect-error
|
|
this.extendToolbar(module);
|
|
}
|
|
return module;
|
|
}
|
|
buildButtons(buttons, icons) {
|
|
Array.from(buttons).forEach(button => {
|
|
const className = button.getAttribute('class') || '';
|
|
className.split(/\s+/).forEach(name => {
|
|
if (!name.startsWith('ql-')) return;
|
|
name = name.slice('ql-'.length);
|
|
if (icons[name] == null) return;
|
|
if (name === 'direction') {
|
|
// @ts-expect-error
|
|
button.innerHTML = icons[name][''] + icons[name].rtl;
|
|
} else if (typeof icons[name] === 'string') {
|
|
// @ts-expect-error
|
|
button.innerHTML = icons[name];
|
|
} else {
|
|
// @ts-expect-error
|
|
const value = button.value || '';
|
|
// @ts-expect-error
|
|
if (value != null && icons[name][value]) {
|
|
// @ts-expect-error
|
|
button.innerHTML = icons[name][value];
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
buildPickers(selects, icons) {
|
|
this.pickers = Array.from(selects).map(select => {
|
|
if (select.classList.contains('ql-align')) {
|
|
if (select.querySelector('option') == null) {
|
|
fillSelect(select, ALIGNS);
|
|
}
|
|
if (typeof icons.align === 'object') {
|
|
return new IconPicker(select, icons.align);
|
|
}
|
|
}
|
|
if (select.classList.contains('ql-background') || select.classList.contains('ql-color')) {
|
|
const format = select.classList.contains('ql-background') ? 'background' : 'color';
|
|
if (select.querySelector('option') == null) {
|
|
fillSelect(select, COLORS, format === 'background' ? '#ffffff' : '#000000');
|
|
}
|
|
return new ColorPicker(select, icons[format]);
|
|
}
|
|
if (select.querySelector('option') == null) {
|
|
if (select.classList.contains('ql-font')) {
|
|
fillSelect(select, FONTS);
|
|
} else if (select.classList.contains('ql-header')) {
|
|
fillSelect(select, HEADERS);
|
|
} else if (select.classList.contains('ql-size')) {
|
|
fillSelect(select, SIZES);
|
|
}
|
|
}
|
|
return new Picker(select);
|
|
});
|
|
const update = () => {
|
|
this.pickers.forEach(picker => {
|
|
picker.update();
|
|
});
|
|
};
|
|
this.quill.on(Emitter.events.EDITOR_CHANGE, update);
|
|
}
|
|
}
|
|
BaseTheme.DEFAULTS = merge({}, Theme.DEFAULTS, {
|
|
modules: {
|
|
toolbar: {
|
|
handlers: {
|
|
formula() {
|
|
this.quill.theme.tooltip.edit('formula');
|
|
},
|
|
image() {
|
|
let fileInput = this.container.querySelector('input.ql-image[type=file]');
|
|
if (fileInput == null) {
|
|
fileInput = document.createElement('input');
|
|
fileInput.setAttribute('type', 'file');
|
|
fileInput.setAttribute('accept', this.quill.uploader.options.mimetypes.join(', '));
|
|
fileInput.classList.add('ql-image');
|
|
fileInput.addEventListener('change', () => {
|
|
const range = this.quill.getSelection(true);
|
|
this.quill.uploader.upload(range, fileInput.files);
|
|
fileInput.value = '';
|
|
});
|
|
this.container.appendChild(fileInput);
|
|
}
|
|
fileInput.click();
|
|
},
|
|
video() {
|
|
this.quill.theme.tooltip.edit('video');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
class BaseTooltip extends Tooltip {
|
|
constructor(quill, boundsContainer) {
|
|
super(quill, boundsContainer);
|
|
this.textbox = this.root.querySelector('input[type="text"]');
|
|
this.listen();
|
|
}
|
|
listen() {
|
|
// @ts-expect-error Fix me later
|
|
this.textbox.addEventListener('keydown', event => {
|
|
if (event.key === 'Enter') {
|
|
this.save();
|
|
event.preventDefault();
|
|
} else if (event.key === 'Escape') {
|
|
this.cancel();
|
|
event.preventDefault();
|
|
}
|
|
});
|
|
}
|
|
cancel() {
|
|
this.hide();
|
|
this.restoreFocus();
|
|
}
|
|
edit() {
|
|
let mode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'link';
|
|
let preview = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
|
this.root.classList.remove('ql-hidden');
|
|
this.root.classList.add('ql-editing');
|
|
if (this.textbox == null) return;
|
|
if (preview != null) {
|
|
this.textbox.value = preview;
|
|
} else if (mode !== this.root.getAttribute('data-mode')) {
|
|
this.textbox.value = '';
|
|
}
|
|
const bounds = this.quill.getBounds(this.quill.selection.savedRange);
|
|
if (bounds != null) {
|
|
this.position(bounds);
|
|
}
|
|
this.textbox.select();
|
|
this.textbox.setAttribute('placeholder', this.textbox.getAttribute(`data-${mode}`) || '');
|
|
this.root.setAttribute('data-mode', mode);
|
|
}
|
|
restoreFocus() {
|
|
this.quill.focus({
|
|
preventScroll: true
|
|
});
|
|
}
|
|
save() {
|
|
// @ts-expect-error Fix me later
|
|
let {
|
|
value
|
|
} = this.textbox;
|
|
switch (this.root.getAttribute('data-mode')) {
|
|
case 'link':
|
|
{
|
|
const {
|
|
scrollTop
|
|
} = this.quill.root;
|
|
if (this.linkRange) {
|
|
this.quill.formatText(this.linkRange, 'link', value, Emitter.sources.USER);
|
|
delete this.linkRange;
|
|
} else {
|
|
this.restoreFocus();
|
|
this.quill.format('link', value, Emitter.sources.USER);
|
|
}
|
|
this.quill.root.scrollTop = scrollTop;
|
|
break;
|
|
}
|
|
case 'video':
|
|
{
|
|
value = extractVideoUrl(value);
|
|
}
|
|
// eslint-disable-next-line no-fallthrough
|
|
case 'formula':
|
|
{
|
|
if (!value) break;
|
|
const range = this.quill.getSelection(true);
|
|
if (range != null) {
|
|
const index = range.index + range.length;
|
|
this.quill.insertEmbed(index,
|
|
// @ts-expect-error Fix me later
|
|
this.root.getAttribute('data-mode'), value, Emitter.sources.USER);
|
|
if (this.root.getAttribute('data-mode') === 'formula') {
|
|
this.quill.insertText(index + 1, ' ', Emitter.sources.USER);
|
|
}
|
|
this.quill.setSelection(index + 2, Emitter.sources.USER);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
}
|
|
// @ts-expect-error Fix me later
|
|
this.textbox.value = '';
|
|
this.hide();
|
|
}
|
|
}
|
|
function extractVideoUrl(url) {
|
|
let match = url.match(/^(?:(https?):\/\/)?(?:(?:www|m)\.)?youtube\.com\/watch.*v=([a-zA-Z0-9_-]+)/) || url.match(/^(?:(https?):\/\/)?(?:(?:www|m)\.)?youtu\.be\/([a-zA-Z0-9_-]+)/);
|
|
if (match) {
|
|
return `${match[1] || 'https'}://www.youtube.com/embed/${match[2]}?showinfo=0`;
|
|
}
|
|
// eslint-disable-next-line no-cond-assign
|
|
if (match = url.match(/^(?:(https?):\/\/)?(?:www\.)?vimeo\.com\/(\d+)/)) {
|
|
return `${match[1] || 'https'}://player.vimeo.com/video/${match[2]}/`;
|
|
}
|
|
return url;
|
|
}
|
|
function fillSelect(select, values) {
|
|
let defaultValue = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
values.forEach(value => {
|
|
const option = document.createElement('option');
|
|
if (value === defaultValue) {
|
|
option.setAttribute('selected', 'selected');
|
|
} else {
|
|
option.setAttribute('value', String(value));
|
|
}
|
|
select.appendChild(option);
|
|
});
|
|
}
|
|
export { BaseTooltip, BaseTheme as default };
|
|
//# sourceMappingURL=base.js.map
|