vue3-rich-editor
Version:
A Vue 3 rich text editor component
194 lines (193 loc) • 6.18 kB
JavaScript
import { defineComponent as T, createElementBlock as S, openBlock as v, createElementVNode as o, createTextVNode as g, withModifiers as q, Fragment as F, renderList as I, toDisplayString as V } from "vue";
const W = { class: "rich-text-editor" }, _ = { class: "toolbar" }, $ = ["value"], H = ["innerHTML"], j = /* @__PURE__ */ T({
__name: "Vue3RichEditor",
props: {
value: {}
},
emits: ["update-html"],
setup(u, { emit: f }) {
const m = f;
let l = null;
const p = [8, 9, 10, 11, 12, 14, 16, 18, 20, 24, 28, 32, 36, 48, 72];
function x(t) {
m("update-html", t.target.innerHTML);
}
function r() {
const t = window.getSelection();
t && t.rangeCount > 0 && (l = t.getRangeAt(0));
}
function i() {
if (!l) return;
const t = window.getSelection();
t && (t.removeAllRanges(), t.addRange(l));
}
function b() {
if (!l) return;
const t = l.cloneContents();
if (!t || !t.firstChild) return;
const e = document.createElement("span");
e.style.fontSize = "0", e.appendChild(t), l.deleteContents(), l.insertNode(e), l.selectNode(e);
}
function w(t) {
const e = t.target;
if (!e.value) return;
const n = parseFloat(e.value);
isNaN(n) || n <= 0 || k(n);
}
function k(t) {
i(), b(), i(), document.queryCommandSupported("styleWithCSS") && document.execCommand("styleWithCSS", !1, "true"), document.execCommand("fontSize", !1, "7");
const e = document.querySelector(".editor");
e && (e.querySelectorAll('font[size="7"]').forEach((n) => {
n.removeAttribute("size"), n.style.fontSize = t + "px";
}), e.querySelectorAll('span[style*="font-size"]').forEach((n) => {
const s = n.style.fontSize.toLowerCase().trim();
(s.includes("large") || s.includes("small")) && (n.style.fontSize = t + "px");
}));
}
function h(t, e) {
i();
const n = t.target;
n.value && (document.queryCommandSupported("styleWithCSS") && document.execCommand("styleWithCSS", !1, "true"), e === "color" ? document.execCommand("foreColor", !1, n.value) : document.queryCommandSupported("hiliteColor") ? document.execCommand("hiliteColor", !1, n.value) : document.execCommand("backColor", !1, n.value));
}
function M() {
i(), document.execCommand("bold", !1);
}
function z() {
i(), document.execCommand("italic", !1);
}
function E() {
i(), document.execCommand("underline", !1);
}
function A() {
i(), document.execCommand("strikeThrough", !1);
}
function C(t) {
switch (i(), t) {
case "left":
document.execCommand("justifyLeft", !1);
break;
case "center":
document.execCommand("justifyCenter", !1);
break;
case "right":
document.execCommand("justifyRight", !1);
break;
}
}
const N = [
"color",
"background-color",
"font-size",
"font-style",
"font-weight",
"text-decoration"
];
function R(t) {
const e = t.querySelectorAll("*");
Array.from(e).forEach((n) => {
const s = n, a = document.createElement("div");
for (N.forEach((c) => {
const d = s.style[c];
d && (a.style.cssText += `${c}: ${d};`);
}); s.firstChild; )
a.appendChild(s.firstChild);
s.replaceWith(a);
});
}
function D(t) {
t.preventDefault();
const e = t.clipboardData;
if (!e) return;
const n = e.getData("text/html") || e.getData("text/plain"), a = new DOMParser().parseFromString(n, "text/html");
R(a.body);
const c = window.getSelection();
if (c && c.rangeCount > 0) {
const d = c.getRangeAt(0);
d.deleteContents();
const y = document.createDocumentFragment();
Array.from(a.body.childNodes).forEach((L) => y.appendChild(L)), d.insertNode(y);
}
}
return (t, e) => (v(), S("div", W, [
o("div", _, [
o("label", null, [
e[5] || (e[5] = g(" 文字顏色: ")),
o("input", {
type: "color",
onMousedown: r,
onInput: e[0] || (e[0] = (n) => h(n, "color"))
}, null, 32)
]),
o("label", null, [
e[6] || (e[6] = g(" 背景顏色: ")),
o("input", {
type: "color",
onMousedown: r,
onInput: e[1] || (e[1] = (n) => h(n, "background-color"))
}, null, 32)
]),
o("label", null, [
e[7] || (e[7] = g(" 字體大小: ")),
o("select", {
onMousedown: q(r, ["stop"]),
onChange: w
}, [
(v(), S(F, null, I(p, (n) => o("option", {
key: n,
value: n
}, V(n) + "px", 9, $)), 64))
], 32)
]),
o("button", {
onMousedown: r,
onClick: M
}, "粗體", 32),
o("button", {
onMousedown: r,
onClick: z
}, "斜體", 32),
o("button", {
onMousedown: r,
onClick: E
}, "下劃線", 32),
o("button", {
onMousedown: r,
onClick: A
}, "刪除線", 32),
o("button", {
onMousedown: r,
onClick: e[2] || (e[2] = (n) => C("left"))
}, "靠左", 32),
o("button", {
onMousedown: r,
onClick: e[3] || (e[3] = (n) => C("center"))
}, "置中", 32),
o("button", {
onMousedown: r,
onClick: e[4] || (e[4] = (n) => C("right"))
}, "靠右", 32)
]),
o("div", {
class: "editor",
contenteditable: "true",
innerHTML: t.value,
onInput: x,
onPaste: D,
style: { "min-height": "120px" }
}, null, 40, H)
]));
}
}), B = (u, f) => {
const m = u.__vccOpts || u;
for (const [l, p] of f)
m[l] = p;
return m;
}, P = /* @__PURE__ */ B(j, [["__scopeId", "data-v-b6f7478c"]]), U = {
install(u) {
u.component("Vue3RichEditor", P);
}
};
export {
P as Vue3RichEditor,
U as default
};