UNPKG

@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
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 };