@cairn214/fluent-editor
Version:
A rich text editor based on Quill 2.0, which extends rich modules and formats on the basis of Quill. It's powerful and out-of-the-box.
308 lines (307 loc) • 10.3 kB
JavaScript
"use strict";
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
const Quill = require("quill");
const Emitter = require("quill/core/emitter");
const base = require("quill/themes/base");
const debounce = require("../../utils/debounce.cjs.js");
require("../../config/index.cjs.js");
const editor_utils = require("../../config/editor.utils.cjs.js");
const link = require("../formats/link.cjs.js");
const editor_config = require("../../config/editor.config.cjs.js");
const _Tooltip = class _Tooltip extends base.BaseTooltip {
constructor(quill, bounds) {
super(quill, bounds);
this.options = {
autoProtocol: "https"
};
this.setTemplate();
this.isInputFocus = false;
this.isHover = false;
this.resolveOptions();
link.default.autoProtocol = this.options.autoProtocol;
this.debouncedHideToolTip = debounce.debounce(this.hideToolTip, 300);
this.debouncedShowToolTip = debounce.debounce(this.showToolTip, 300);
this.quill.on(editor_config.CHANGE_LANGUAGE_EVENT, () => {
this.setTemplate();
});
}
setTemplate() {
var _a;
this.root.innerHTML = [
`<input type="text" data-formula="e=mc^2" data-link="${(_a = this.quill.options.langText) == null ? void 0 : _a.linkplaceholder}" data-video="Embed URL" style="width: 225px;">`,
'<span class="ql-split"></span>',
'<a class="ql-preview"><i class="icon-share"></i></a>',
'<a class="ql-remove"><i class="icon-delete"></i></a>'
].join("");
this.textbox = this.root.querySelector('input[type="text"]');
this.listen();
}
resolveOptions() {
this.options = {
autoProtocol: "https"
};
const value = this.quill.options.autoProtocol;
if (value && typeof value === "string") {
this.options.autoProtocol = value;
} else if (typeof value === "boolean" && !value) {
this.options.autoProtocol = "";
}
}
shouldHide() {
return !this.isHover && !this.isInputFocus;
}
hideToolTip() {
if (this.shouldHide()) {
this.hide();
}
}
showToolTip(name, value, range) {
if (!this.shouldHide()) {
this.edit(name, value, range);
}
}
handleMouseLeave() {
this.isHover = false;
this.debouncedHideToolTip();
}
handleMouseEnter(event) {
const isTooltipShow = !this.root.classList.contains("ql-hidden");
if (isTooltipShow) {
return;
}
if (this.isInputFocus) {
this.save();
}
this.isHover = true;
const linkNode = event.target;
const preview = link.default.formats(linkNode);
if (preview.startsWith("#")) {
return;
}
const linkBlot = Quill.find(linkNode);
const index = this.quill.getIndex(linkBlot);
const [link$1, offset] = this.quill.scroll.descendant(
link.default,
index
);
const length = link$1 && link$1.length();
this.linkRange = new Quill.Range(index - offset, length);
this.debouncedShowToolTip("link", preview, this.linkRange);
}
listen() {
super.listen();
this.root.querySelector("a.ql-remove").addEventListener("click", (event) => {
if (!editor_utils.isNullOrUndefined(this.linkRange)) {
const range = this.linkRange;
this.restoreFocus();
this.quill.formatText(range, "link", false, Emitter.sources.API);
delete this.linkRange;
}
event.preventDefault();
this.hide();
});
this.quill.root.addEventListener(
"mouseover",
(event) => {
if ((event.target.tagName.toUpperCase() !== "A" || !event.target.classList.contains(link.default.className)) && !event.target.closest(`a.${link.default.className}`)) {
return;
}
this.handleMouseEnter(event);
},
false
);
this.quill.root.addEventListener(
"mouseout",
(event) => {
if (event.target.tagName.toUpperCase() !== "A" && !event.target.closest(`a.${link.default.className}`)) {
return;
}
this.handleMouseLeave();
},
false
);
this.root.addEventListener(
"mouseenter",
() => {
this.isHover = true;
},
false
);
this.root.addEventListener("mouseleave", this.handleMouseLeave.bind(this), false);
this.root.querySelector("a.ql-preview").addEventListener("click", (event) => {
const link$1 = link.default.sanitize(this.textbox.value);
window.open(link$1, "_blank");
event.preventDefault();
});
this.root.querySelector('input[type="text"]').addEventListener("focus", () => {
this.isInputFocus = true;
});
this.root.querySelector('input[type="text"]').addEventListener("blur", () => {
this.isInputFocus = false;
this.save();
});
this.quill.on(
Emitter.events.SELECTION_CHANGE,
(range, _oldRange, source) => {
if (editor_utils.isNullOrUndefined(range)) return;
if (source === Emitter.sources.USER) {
const [link$1, offset] = this.quill.scroll.descendant(
link.default,
range.index
);
if (!editor_utils.isNullOrUndefined(link$1)) {
this.linkRange = new Quill.Range(range.index - offset, link$1.length());
const preview = link.default.formats(link$1.domNode);
if (!preview.startsWith("#")) {
this.edit("link", preview, this.linkRange);
}
return;
}
}
if (this.shouldHide()) {
this.hide();
}
}
);
this.quill.on(
Emitter.events.TEXT_CHANGE,
() => {
const selection = this.quill.getSelection();
const index = selection && selection.index;
setTimeout(() => {
const link$1 = this.quill.scroll.descendant(
link.default,
index
)[0];
if (!link$1) {
this.handleMouseLeave();
}
});
}
);
}
save() {
let value = this.textbox.value;
if (!value) return;
this.textbox.value = "";
switch (this.root.getAttribute("data-mode")) {
case "link": {
const { scrollTop } = this.quill.root;
if (this.options.autoProtocol) {
value = this.addHttpProtocol(value);
}
if (this.linkRange) {
this.quill.formatText(
this.linkRange,
"link",
value,
Emitter.sources.USER
);
this.restoreFocus();
} else {
this.restoreFocus();
this.quill.format("link", value, Emitter.sources.USER);
}
this.quill.root.scrollTop = scrollTop;
break;
}
case "formula": {
const range = this.quill.getSelection(true);
if (!editor_utils.isNullOrUndefined(range)) {
const index = range.index + range.length;
this.quill.insertEmbed(
index,
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;
}
case "video": {
const range = this.quill.getSelection(true);
this.quill.insertText(range.index, "\n", Emitter.sources.USER);
this.quill.insertEmbed(range.index + 1, "video", { src: value }, Emitter.sources.USER);
this.quill.insertText(range.index + 2, "\n", Emitter.sources.USER);
this.quill.setSelection(range.index + 3, Emitter.sources.SILENT);
this.textbox.value = "";
this.hide();
break;
}
default:
}
}
position(reference) {
const left = reference.left;
const top = reference.bottom + this.quill.root.scrollTop;
this.root.style.left = `${left}px`;
this.root.style.top = `${top}px`;
this.root.classList.remove("ql-flip");
const containerBounds = this.boundsContainer.getBoundingClientRect();
const rootBounds = this.root.getBoundingClientRect();
let shift = 0;
if (rootBounds.right > containerBounds.right) {
shift = containerBounds.right - rootBounds.right;
this.root.style.left = `${left + shift}px`;
}
if (rootBounds.left < containerBounds.left) {
shift = containerBounds.left - rootBounds.left;
this.root.style.left = `${left + shift}px`;
}
if (rootBounds.bottom > containerBounds.bottom) {
const height = rootBounds.bottom - rootBounds.top;
const verticalShift = reference.bottom - reference.top + height;
const fixedTop = top - verticalShift;
this.root.style.top = `${fixedTop < 0 ? this.quill.root.scrollTop + reference.top : fixedTop}px`;
this.root.classList.add("ql-flip");
}
return shift;
}
// @ts-expect-error
edit(mode = "link", preview = null, range) {
this.linkRange = range || this.quill.selection.savedRange;
this.root.classList.remove("ql-hidden");
this.root.classList.add("ql-editing");
if (!editor_utils.isNullOrUndefined(preview)) {
this.textbox.value = preview;
} else if (mode !== this.root.getAttribute("data-mode")) {
this.textbox.value = "";
}
this.position(this.quill.getBounds(range || this.quill.selection.savedRange));
if (this.textbox.value === "") {
this.textbox.focus();
}
this.textbox.setAttribute(
"placeholder",
this.textbox.getAttribute(`data-${mode}`) || ""
);
this.root.setAttribute("data-mode", mode);
}
show() {
super.show();
this.root.removeAttribute("data-mode");
}
addHttpProtocol(url) {
let result = url;
if (!url) {
return "";
}
if (!editor_utils.hadProtocol(url)) {
result = `${this.options.autoProtocol}://${url}`;
}
return result;
}
};
_Tooltip.TEMPLATE = [
`<input type="text" data-formula="e=mc^2" data-link="${editor_config.LANG_CONF["en-US"].linkplaceholder}" data-video="Embed URL" style="width: 225px;">`,
'<span class="ql-split"></span>',
'<a class="ql-preview"><i class="icon-share"></i></a>',
'<a class="ql-remove"><i class="icon-delete"></i></a>'
].join("");
let Tooltip = _Tooltip;
exports.default = Tooltip;
//# sourceMappingURL=tooltip.cjs.js.map