@opentiny/vue-renderless
Version:
An enterprise-class UI component library, support both Vue.js 2 and Vue.js 3, as well as PC and mobile.
337 lines (336 loc) • 11.7 kB
JavaScript
import "../chunk-G2ADBYYC.js";
import { extend } from "@opentiny/utils";
import { xss } from "@opentiny/utils";
import { getToolbarTips, defaultOptions } from "./options";
import registerTableModule from "./table-module";
import registerCustomClipboard from "./clipboard";
const registerCustomSize = (Quill, sizeConfig) => {
if (!sizeConfig || !Array.isArray(sizeConfig)) {
return;
}
const hasPixelValues = sizeConfig.some((item) => {
return item && typeof item === "string" && item.includes("px");
});
if (!hasPixelValues) {
return;
}
const SizeStyle = Quill.import("attributors/style/size");
const whitelist = sizeConfig.filter((item) => item !== false);
SizeStyle.whitelist = whitelist;
Quill.register("formats/size", SizeStyle, true);
};
const initContent = ({ state, props, nextTick }) => () => {
if (state.quill) {
const flag = state.quill.selection.hasFocus();
if (state.content && state.content !== state.innerContent) {
state.innerContent = state.content;
state.quill.pasteHTML(xss.filterHtml(state.content));
} else {
if (!state.content) {
state.quill.setText("");
}
}
nextTick(() => {
if (!props.disabled && !props.displayOnly) {
state.quill.enable(true);
}
flag ? state.quill.selection.focus() : state.quill.blur();
});
}
};
const initQuill = ({ api, emit, props, vm, service, state, Quill, ImageDrop, ImageUpload, FileUpload }) => () => {
var _a, _b;
state.innerOptions = extend(true, {}, defaultOptions, props.globalOptions, props.options);
const toolbarConfig = ((_b = (_a = state.innerOptions.modules) == null ? void 0 : _a.toolbar) == null ? void 0 : _b.container) || [];
const findSizeConfig = (config) => {
if (!config)
return null;
if (Array.isArray(config)) {
for (let i = 0; i < config.length; i++) {
const result = findSizeConfig(config[i]);
if (result)
return result;
}
} else if (typeof config === "object" && config !== null) {
if (config.size && Array.isArray(config.size)) {
return config.size;
}
for (const key in config) {
const result = findSizeConfig(config[key]);
if (result)
return result;
}
}
return null;
};
const sizeConfig = findSizeConfig(toolbarConfig);
if (sizeConfig) {
registerCustomSize(Quill, sizeConfig);
}
if (document.caretRangeFromPoint) {
if (props.imageDrop) {
Quill.register("modules/imageDrop", ImageDrop, true);
state.innerOptions.modules.imageDrop = props.imageDrop;
}
if (props.imageUpload) {
Quill.register("modules/imageUpload", ImageUpload, true);
state.innerOptions.modules.imageUpload = props.imageUpload;
}
if (props.fileUpload) {
Quill.register("modules/fileUpload", FileUpload, true);
state.innerOptions.modules.fileUpload = extend(
true,
{},
{ httpRequest: service && service.network.request },
props.fileUpload,
{ url: state.fileUploadUrl }
);
}
}
registerTableModule(Quill);
if (state.innerOptions.modules) {
state.innerOptions.modules.tableModule = props.tableModule === true;
}
registerCustomClipboard(Quill, props.keepClass);
state.quill = Object.freeze(new Quill(vm.$refs.editor, state.innerOptions));
state.quill.enable(false);
state.quill.on("selection-change", api.selectionChange);
state.quill.on("text-change", api.textChange);
state.quill.root.addEventListener("click", api.handleClick);
if (state.content) {
state.quill.pasteHTML(xss.filterHtml(state.content));
api.textChange();
}
if (!props.disabled && !props.displayOnly) {
state.quill.enable(true);
}
emit("ready", state.quill);
api.setToolbarTips();
api.setTooltipI18n();
state.tooltipI18nHandler = () => setTimeout(() => {
api.setTooltipI18n();
api.adjustTooltipPosition();
});
state.tooltipResizeHandler = () => api.adjustTooltipPosition();
vm.$el.addEventListener("click", state.tooltipI18nHandler);
vm.$el.addEventListener("keyup", state.tooltipI18nHandler);
window.addEventListener("resize", state.tooltipResizeHandler);
if (typeof MutationObserver !== "undefined") {
state.tooltipObserver = new MutationObserver(() => {
requestAnimationFrame(() => api.adjustTooltipPosition());
});
state.tooltipObserver.observe(vm.$el, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ["style", "class", "data-mode"]
});
}
};
const handleClick = ({ state, Quill }) => (event) => {
const el = event.target;
if (!(el instanceof HTMLElement) || el.tagName !== "IMG") {
return;
}
event.stopPropagation();
const imgBlot = Quill.find(el);
const index = state.quill.getIndex(imgBlot);
state.quill.setSelection(index + 1);
};
const setToolbarTips = ({ t, vm }) => () => {
let tip;
const item = getToolbarTips(t);
const length = item.length;
const richTextEl = vm.$el;
for (let i = 0; i < length; i++) {
tip = richTextEl.querySelectorAll(".quill-editor " + item[i].Choice);
if (tip.length) {
Array.prototype.slice.call(tip).forEach((ite) => {
ite.setAttribute("title", item[i].title);
});
}
}
tip = richTextEl.querySelectorAll(".quill-editor .ql-file");
if (tip.length) {
const iconEl = vm.$refs.iconFile.$el;
Array.prototype.slice.call(tip).forEach((ite) => {
ite.innerHTML = getOuterHTML(iconEl);
});
}
};
const setTooltipI18n = ({ t, vm }) => () => {
const richTextEl = vm.$el;
const tips = richTextEl.querySelectorAll(".ql-container .ql-tooltip");
const getPlaceholderByMode = (mode) => {
if (mode === "video")
return t("ui.richText.enterVideo");
if (mode === "formula")
return t("ui.richText.enterFormula");
return t("ui.richText.enterLink");
};
if (tips.length) {
Array.prototype.slice.call(tips).forEach((tip) => {
const mode = tip.getAttribute("data-mode") || "link";
const input = tip.querySelector("input[type='text']");
tip.setAttribute("data-visit-url-text", `${t("ui.richText.visitUrl")}:`);
tip.setAttribute("data-edit-text", t("ui.richText.edit"));
tip.setAttribute("data-remove-text", t("ui.richText.remove"));
tip.setAttribute("data-save-text", t("ui.richText.save"));
tip.setAttribute("data-enter-link-text", `${t("ui.richText.enterLink")}:`);
tip.setAttribute("data-enter-formula-text", `${t("ui.richText.enterFormula")}:`);
tip.setAttribute("data-enter-video-text", `${t("ui.richText.enterVideo")}:`);
if (input) {
input.setAttribute("placeholder", getPlaceholderByMode(mode));
}
});
}
};
const adjustTooltipPosition = ({ vm }) => () => {
const container = vm.$el.querySelector(".ql-container");
const tips = vm.$el.querySelectorAll(".ql-container .ql-tooltip");
const minGap = 8;
if (!container) {
return;
}
const containerRect = container.getBoundingClientRect();
if (tips.length) {
Array.prototype.slice.call(tips).forEach((tip) => {
if (!tip.classList.contains("ql-editing")) {
return;
}
const offsetParent = tip.offsetParent || tip.parentElement;
if (!offsetParent) {
return;
}
const parentRect = offsetParent.getBoundingClientRect();
const currentLeft = parseFloat(tip.style.left || "0");
const safeCurrentLeft = Number.isFinite(currentLeft) ? currentLeft : 0;
const minLeft = containerRect.left - parentRect.left + minGap;
const maxLeft = containerRect.right - parentRect.left - tip.offsetWidth - minGap;
if (maxLeft <= minLeft) {
tip.style.left = `${minLeft}px`;
return;
}
const clampedLeft = Math.min(Math.max(safeCurrentLeft, minLeft), maxLeft);
tip.style.left = `${clampedLeft}px`;
});
}
};
const setPlaceholder = ({ state, props }) => () => {
if (state.quill) {
state.quill.root.setAttribute("data-placeholder", props.options.placeholder);
}
};
const getFileUploadUrl = ({ service }) => () => {
return service ? service.common.getFileUploadUrl() : Promise.resolve("");
};
const selectionChange = ({ emit, state }) => (range) => {
if (!range) {
emit("blur", state.quill);
} else {
emit("focus", state.quill);
}
};
const handlePaste = ({ state }) => (event) => {
const rangeLength = state.quill.selection.savedRange.length || 0;
const newLength = event.clipboardData.getData("text").length || 0;
const currentLength = state.quill.getText().length || 0;
const totalLength = currentLength - rangeLength + newLength;
if (totalLength > state.maxLength) {
event.preventDefault();
state.pasteCanceled = true;
} else {
state.pasteCanceled = false;
}
};
const maxLength = ({ props, constants }) => () => {
return props.maxLength > 0 ? props.maxLength : constants.MAX_LENGTH;
};
const isDisplayOnly = ({ state, props, parent, nextTick }) => () => {
let displayOnly = props.displayOnly || (parent.auiForm || {}).displayOnly;
nextTick(() => {
if (state.quill && !props.disabled) {
state.quill.root.setAttribute("contenteditable", !displayOnly);
}
});
return displayOnly;
};
const textChange = ({ emit, vm, state, Modal, t }) => (delta, oldDelta) => {
const quill = state.quill;
if (state.pasteCanceled) {
state.pasteCanceled = false;
quill.setContents(oldDelta);
Modal.message({ message: `${t("ui.richText.maxLength")}${state.maxLength}`, status: "warning" });
return;
}
const text = state.quill.getText();
const currentLength = text.length - 1;
const maxLength2 = state.maxLength;
if (currentLength > maxLength2) {
quill.setContents(oldDelta);
Modal.message({ message: `${t("ui.richText.maxLength")}${state.maxLength}`, status: "warning" });
return;
}
let html = xss.filterHtml(vm.$refs.editor.children[0].innerHTML);
if (html === "<p><br></p>")
html = "";
state.innerContent = html;
emit("update:modelValue", html);
emit("change", { html, text, quill });
};
const getOuterHTML = (el) => {
if (!el.outerHTML) {
const container = document.createElement("div");
container.appendChild(el);
return xss.filterHtml(container.innerHTML);
} else {
return xss.filterHtml(el.outerHTML);
}
};
const mounted = ({ api, props, state, i18n, watch }) => () => {
if (props.fileUpload && !props.fileUpload.url) {
api.getFileUploadUrl().then((url) => {
url = xss.filterUrl(url);
state.fileUploadUrl = url;
api.initQuill();
});
} else {
api.initQuill();
}
if (i18n) {
watch(() => i18n.locale, () => {
api.setToolbarTips();
api.setTooltipI18n();
api.adjustTooltipPosition();
});
}
};
const beforeUnmount = ({ api, state, vm }) => () => {
state.quill.off("selection-change", api.selectionChange);
state.quill.off("text-change", api.textChange);
state.quill.root.removeEventListener("click", api.handleClick);
state.tooltipI18nHandler && vm.$el.removeEventListener("click", state.tooltipI18nHandler);
state.tooltipI18nHandler && vm.$el.removeEventListener("keyup", state.tooltipI18nHandler);
state.tooltipResizeHandler && window.removeEventListener("resize", state.tooltipResizeHandler);
state.tooltipObserver && state.tooltipObserver.disconnect();
state.tooltipObserver = null;
state.quill = null;
delete state.quill;
};
export {
adjustTooltipPosition,
beforeUnmount,
getFileUploadUrl,
handleClick,
handlePaste,
initContent,
initQuill,
isDisplayOnly,
maxLength,
mounted,
selectionChange,
setPlaceholder,
setToolbarTips,
setTooltipI18n,
textChange
};