387 lines
14 KiB
JavaScript
387 lines
14 KiB
JavaScript
/*!
|
|
* VueQuill @vueup/vue-quill v1.2.0
|
|
* https://vueup.github.io/vue-quill/
|
|
*
|
|
* Includes quill v1.3.7
|
|
* https://quilljs.com/
|
|
*
|
|
* Copyright (c) 2023 Ahmad Luthfi Masruri
|
|
* Released under the MIT license
|
|
* Date: 2023-05-12T08:44:03.742Z
|
|
*/
|
|
'use strict';
|
|
|
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
|
var Quill = require('quill');
|
|
var Delta = require('quill-delta');
|
|
var vue = require('vue');
|
|
|
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e["default"] : e; }
|
|
|
|
var Quill__default = /*#__PURE__*/_interopDefaultLegacy(Quill);
|
|
var Delta__default = /*#__PURE__*/_interopDefaultLegacy(Delta);
|
|
|
|
const toolbarOptions = {
|
|
essential: [
|
|
[{ header: [1, 2, 3, 4, 5, 6, false] }],
|
|
['bold', 'italic', 'underline'],
|
|
[{ list: 'ordered' }, { list: 'bullet' }, { align: [] }],
|
|
['blockquote', 'code-block', 'link'],
|
|
[{ color: [] }, 'clean'],
|
|
],
|
|
minimal: [
|
|
[{ header: 1 }, { header: 2 }],
|
|
['bold', 'italic', 'underline'],
|
|
[{ list: 'ordered' }, { list: 'bullet' }, { align: [] }],
|
|
],
|
|
full: [
|
|
['bold', 'italic', 'underline', 'strike'],
|
|
['blockquote', 'code-block'],
|
|
[{ header: 1 }, { header: 2 }],
|
|
[{ list: 'ordered' }, { list: 'bullet' }],
|
|
[{ script: 'sub' }, { script: 'super' }],
|
|
[{ indent: '-1' }, { indent: '+1' }],
|
|
[{ direction: 'rtl' }],
|
|
[{ size: ['small', false, 'large', 'huge'] }],
|
|
[{ header: [1, 2, 3, 4, 5, 6, false] }],
|
|
[{ color: [] }, { background: [] }],
|
|
[{ font: [] }],
|
|
[{ align: [] }],
|
|
['link', 'video', 'image'],
|
|
['clean'], // remove formatting button
|
|
],
|
|
};
|
|
|
|
const QuillEditor = vue.defineComponent({
|
|
name: 'QuillEditor',
|
|
inheritAttrs: false,
|
|
props: {
|
|
content: {
|
|
type: [String, Object],
|
|
},
|
|
contentType: {
|
|
type: String,
|
|
default: 'delta',
|
|
validator: (value) => {
|
|
return ['delta', 'html', 'text'].includes(value);
|
|
},
|
|
},
|
|
enable: {
|
|
type: Boolean,
|
|
default: true,
|
|
},
|
|
readOnly: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
placeholder: {
|
|
type: String,
|
|
required: false,
|
|
},
|
|
theme: {
|
|
type: String,
|
|
default: 'snow',
|
|
validator: (value) => {
|
|
return ['snow', 'bubble', ''].includes(value);
|
|
},
|
|
},
|
|
toolbar: {
|
|
type: [String, Array, Object],
|
|
required: false,
|
|
validator: (value) => {
|
|
if (typeof value === 'string' && value !== '') {
|
|
return value.charAt(0) === '#'
|
|
? true
|
|
: Object.keys(toolbarOptions).indexOf(value) !== -1;
|
|
}
|
|
return true;
|
|
},
|
|
},
|
|
modules: {
|
|
type: Object,
|
|
required: false,
|
|
},
|
|
options: {
|
|
type: Object,
|
|
required: false,
|
|
},
|
|
globalOptions: {
|
|
type: Object,
|
|
required: false,
|
|
},
|
|
},
|
|
emits: [
|
|
'textChange',
|
|
'selectionChange',
|
|
'editorChange',
|
|
'update:content',
|
|
'focus',
|
|
'blur',
|
|
'ready',
|
|
],
|
|
setup: (props, ctx) => {
|
|
vue.onMounted(() => {
|
|
initialize();
|
|
});
|
|
vue.onBeforeUnmount(() => {
|
|
quill = null;
|
|
});
|
|
let quill;
|
|
let options;
|
|
const editor = vue.ref();
|
|
// Initialize Quill
|
|
const initialize = () => {
|
|
var _a;
|
|
if (!editor.value)
|
|
return;
|
|
options = composeOptions();
|
|
// Register modules
|
|
if (props.modules) {
|
|
if (Array.isArray(props.modules)) {
|
|
for (const module of props.modules) {
|
|
Quill__default.register(`modules/${module.name}`, module.module);
|
|
}
|
|
}
|
|
else {
|
|
Quill__default.register(`modules/${props.modules.name}`, props.modules.module);
|
|
}
|
|
}
|
|
// Create new Quill instance
|
|
quill = new Quill__default(editor.value, options);
|
|
// Set editor content
|
|
setContents(props.content);
|
|
// Set event handlers
|
|
quill.on('text-change', handleTextChange);
|
|
quill.on('selection-change', handleSelectionChange);
|
|
quill.on('editor-change', handleEditorChange);
|
|
// Remove editor class when theme changes
|
|
if (props.theme !== 'bubble')
|
|
editor.value.classList.remove('ql-bubble');
|
|
if (props.theme !== 'snow')
|
|
editor.value.classList.remove('ql-snow');
|
|
// Fix clicking the quill toolbar is detected as blur event
|
|
(_a = quill
|
|
.getModule('toolbar')) === null || _a === void 0 ? void 0 : _a.container.addEventListener('mousedown', (e) => {
|
|
e.preventDefault();
|
|
});
|
|
// Emit ready event
|
|
ctx.emit('ready', quill);
|
|
};
|
|
// Compose Options
|
|
const composeOptions = () => {
|
|
const clientOptions = {};
|
|
if (props.theme !== '')
|
|
clientOptions.theme = props.theme;
|
|
if (props.readOnly)
|
|
clientOptions.readOnly = props.readOnly;
|
|
if (props.placeholder)
|
|
clientOptions.placeholder = props.placeholder;
|
|
if (props.toolbar && props.toolbar !== '') {
|
|
clientOptions.modules = {
|
|
toolbar: (() => {
|
|
if (typeof props.toolbar === 'object') {
|
|
return props.toolbar;
|
|
}
|
|
else if (typeof props.toolbar === 'string') {
|
|
const str = props.toolbar;
|
|
return str.charAt(0) === '#'
|
|
? props.toolbar
|
|
: toolbarOptions[props.toolbar];
|
|
}
|
|
return;
|
|
})(),
|
|
};
|
|
}
|
|
if (props.modules) {
|
|
const modules = (() => {
|
|
var _a, _b;
|
|
const modulesOption = {};
|
|
if (Array.isArray(props.modules)) {
|
|
for (const module of props.modules) {
|
|
modulesOption[module.name] = (_a = module.options) !== null && _a !== void 0 ? _a : {};
|
|
}
|
|
}
|
|
else {
|
|
modulesOption[props.modules.name] = (_b = props.modules.options) !== null && _b !== void 0 ? _b : {};
|
|
}
|
|
return modulesOption;
|
|
})();
|
|
clientOptions.modules = Object.assign({}, clientOptions.modules, modules);
|
|
}
|
|
return Object.assign({}, props.globalOptions, props.options, clientOptions);
|
|
};
|
|
const maybeClone = (delta) => {
|
|
return typeof delta === 'object' && delta ? delta.slice() : delta;
|
|
};
|
|
const deltaHasValuesOtherThanRetain = (delta) => {
|
|
return Object.values(delta.ops).some((v) => !v.retain || Object.keys(v).length !== 1);
|
|
};
|
|
// Doesn't need reactivity, but does need to be cloned to avoid deep mutations always registering as equal
|
|
let internalModel;
|
|
const internalModelEquals = (against) => {
|
|
if (typeof internalModel === typeof against) {
|
|
if (against === internalModel) {
|
|
return true;
|
|
}
|
|
// Ref/Proxy does not support instanceof, so do a loose check
|
|
if (typeof against === 'object' &&
|
|
against &&
|
|
typeof internalModel === 'object' &&
|
|
internalModel) {
|
|
return !deltaHasValuesOtherThanRetain(internalModel.diff(against));
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
const handleTextChange = (delta, oldContents, source) => {
|
|
internalModel = maybeClone(getContents());
|
|
// Update v-model:content when text changes
|
|
if (!internalModelEquals(props.content)) {
|
|
ctx.emit('update:content', internalModel);
|
|
}
|
|
ctx.emit('textChange', { delta, oldContents, source });
|
|
};
|
|
const isEditorFocus = vue.ref();
|
|
const handleSelectionChange = (range, oldRange, source) => {
|
|
// Set isEditorFocus if quill.hasFocus()
|
|
isEditorFocus.value = !!(quill === null || quill === void 0 ? void 0 : quill.hasFocus());
|
|
ctx.emit('selectionChange', { range, oldRange, source });
|
|
};
|
|
vue.watch(isEditorFocus, (focus) => {
|
|
if (focus)
|
|
ctx.emit('focus', editor);
|
|
else
|
|
ctx.emit('blur', editor);
|
|
});
|
|
const handleEditorChange = (...args) => {
|
|
if (args[0] === 'text-change')
|
|
ctx.emit('editorChange', {
|
|
name: args[0],
|
|
delta: args[1],
|
|
oldContents: args[2],
|
|
source: args[3],
|
|
});
|
|
if (args[0] === 'selection-change')
|
|
ctx.emit('editorChange', {
|
|
name: args[0],
|
|
range: args[1],
|
|
oldRange: args[2],
|
|
source: args[3],
|
|
});
|
|
};
|
|
const getEditor = () => {
|
|
return editor.value;
|
|
};
|
|
const getToolbar = () => {
|
|
var _a;
|
|
return (_a = quill === null || quill === void 0 ? void 0 : quill.getModule('toolbar')) === null || _a === void 0 ? void 0 : _a.container;
|
|
};
|
|
const getQuill = () => {
|
|
if (quill)
|
|
return quill;
|
|
else
|
|
throw `The quill editor hasn't been instantiated yet,
|
|
make sure to call this method when the editor ready
|
|
or use v-on:ready="onReady(quill)" event instead.`;
|
|
};
|
|
const getContents = (index, length) => {
|
|
if (props.contentType === 'html') {
|
|
return getHTML();
|
|
}
|
|
else if (props.contentType === 'text') {
|
|
return getText(index, length);
|
|
}
|
|
return quill === null || quill === void 0 ? void 0 : quill.getContents(index, length);
|
|
};
|
|
const setContents = (content, source = 'api') => {
|
|
const normalizedContent = !content
|
|
? props.contentType === 'delta'
|
|
? new Delta__default()
|
|
: ''
|
|
: content;
|
|
if (props.contentType === 'html') {
|
|
setHTML(normalizedContent);
|
|
}
|
|
else if (props.contentType === 'text') {
|
|
setText(normalizedContent, source);
|
|
}
|
|
else {
|
|
quill === null || quill === void 0 ? void 0 : quill.setContents(normalizedContent, source);
|
|
}
|
|
internalModel = maybeClone(normalizedContent);
|
|
};
|
|
const getText = (index, length) => {
|
|
var _a;
|
|
return (_a = quill === null || quill === void 0 ? void 0 : quill.getText(index, length)) !== null && _a !== void 0 ? _a : '';
|
|
};
|
|
const setText = (text, source = 'api') => {
|
|
quill === null || quill === void 0 ? void 0 : quill.setText(text, source);
|
|
};
|
|
const getHTML = () => {
|
|
var _a;
|
|
return (_a = quill === null || quill === void 0 ? void 0 : quill.root.innerHTML) !== null && _a !== void 0 ? _a : '';
|
|
};
|
|
const setHTML = (html) => {
|
|
if (quill)
|
|
quill.root.innerHTML = html;
|
|
};
|
|
const pasteHTML = (html, source = 'api') => {
|
|
const delta = quill === null || quill === void 0 ? void 0 : quill.clipboard.convert(html);
|
|
if (delta)
|
|
quill === null || quill === void 0 ? void 0 : quill.setContents(delta, source);
|
|
};
|
|
const focus = () => {
|
|
quill === null || quill === void 0 ? void 0 : quill.focus();
|
|
};
|
|
const reinit = () => {
|
|
vue.nextTick(() => {
|
|
var _a;
|
|
if (!ctx.slots.toolbar && quill)
|
|
(_a = quill.getModule('toolbar')) === null || _a === void 0 ? void 0 : _a.container.remove();
|
|
initialize();
|
|
});
|
|
};
|
|
vue.watch(() => props.content, (newContent) => {
|
|
if (!quill || !newContent || internalModelEquals(newContent))
|
|
return;
|
|
// Restore the selection and cursor position after updating the content
|
|
const selection = quill.getSelection();
|
|
if (selection) {
|
|
vue.nextTick(() => quill === null || quill === void 0 ? void 0 : quill.setSelection(selection));
|
|
}
|
|
setContents(newContent);
|
|
}, { deep: true });
|
|
vue.watch(() => props.enable, (newValue) => {
|
|
if (quill)
|
|
quill.enable(newValue);
|
|
});
|
|
return {
|
|
editor,
|
|
getEditor,
|
|
getToolbar,
|
|
getQuill,
|
|
getContents,
|
|
setContents,
|
|
getHTML,
|
|
setHTML,
|
|
pasteHTML,
|
|
focus,
|
|
getText,
|
|
setText,
|
|
reinit,
|
|
};
|
|
},
|
|
render() {
|
|
var _a, _b;
|
|
return [
|
|
(_b = (_a = this.$slots).toolbar) === null || _b === void 0 ? void 0 : _b.call(_a),
|
|
vue.h('div', { ref: 'editor', ...this.$attrs }),
|
|
];
|
|
},
|
|
});
|
|
|
|
exports.Quill = Quill__default;
|
|
exports.Delta = Delta__default;
|
|
exports.QuillEditor = QuillEditor;
|