UNPKG

vue-data-ui

Version:

A user-empowering data visualization Vue 3 components library for eloquent data storytelling

374 lines (373 loc) 17.3 kB
import { ref as c, computed as ge, watch as X, nextTick as le, onMounted as pe, onBeforeUnmount as me, createElementBlock as Y, createCommentVNode as q, openBlock as z, normalizeStyle as x, createElementVNode as k, withDirectives as ae, createVNode as R, normalizeClass as G, toDisplayString as Ae, unref as he, vModelText as oe } from "vue"; import T from "./BaseIcon-Ba5t14Aj.js"; import { C as we } from "./ColorPicker-8-sXft1r.js"; import { l as be, f as xe } from "./index-BLtEpj8j.js"; const ye = { class: "vue-ui-pen-and-paper-action", style: { padding: "0 !important" } }, Ce = ["disabled"], Be = { __name: "PenAndPaper", props: { svgRef: { type: [Object, null, void 0], required: !0 }, color: { type: String, default: "#2D353C" }, backgroundColor: { type: String, default: "#FFFFFF" }, active: { type: Boolean, default: !1 }, scale: { type: Number, default: 1 } }, emits: ["close"], setup(d, { emit: se }) { const s = d, ue = se, b = c([]), E = c([]), S = c(s.color), $ = c(2), M = c(!1), P = c(""), o = c(null), A = c(null), F = c(null), h = c("draw"), Q = c(!1), f = c(null), H = c({ x: 0, y: 0 }), N = c([""]), D = c({ row: 0, col: 0 }), L = c(16), W = c("url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAABg2lDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9TpSIVh2YQcchQnSyIijhKFYtgobQVWnUwufQLmjQkKS6OgmvBwY/FqoOLs64OroIg+AHi6OSk6CIl/i8ptIjx4Lgf7+497t4BQrPKNKtnAtB020wn4lIuvyqFXhGGiAhCiMnMMpKZxSx8x9c9Any9i/Es/3N/jgG1YDEgIBHPMcO0iTeIZzZtg/M+scjKskp8Tjxu0gWJH7muePzGueSywDNFM5ueJxaJpVIXK13MyqZGPE0cVTWd8oWcxyrnLc5atc7a9+QvDBf0lQzXaY4ggSUkkYIEBXVUUIWNGK06KRbStB/38Q+7/hS5FHJVwMixgBo0yK4f/A9+d2sVpya9pHAc6H1xnI9RILQLtBqO833sOK0TIPgMXOkdf60JzH6S3uho0SNgcBu4uO5oyh5wuQMMPRmyKbtSkKZQLALvZ/RNeSByC/Sveb2193H6AGSpq+Ub4OAQGCtR9rrPu/u6e/v3TLu/H5C7crM1WjgWAAAABmJLR0QAqwB5AHWF+8OUAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH5gwUExIUagzGcQAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAABfSURBVBjTldAxDoNQDIPhL0+q1L33P1AvAhN7xfK6WAgoLfSfrNiykpQtE+7RLzx2vgF9D3o8lWDmn1QVVMP0LZQGmNtqp1/cmou0XHdG/+sYeGZwFBqPCub8rkcvvAGvsi1VYarR8wAAAABJRU5ErkJggg==') 5 5, auto"); function O(l) { if (!o.value || h.value !== "text" || Q.value) return; const { x: e, y: n } = V(l); H.value = { x: e, y: n }, N.value = [""], D.value = { row: 0, col: 0 }; const t = document.createElementNS("http://www.w3.org/2000/svg", "text"); t.setAttribute("x", e), t.setAttribute("y", n), t.setAttribute("fill", S.value), t.setAttribute("font-size", L.value * s.scale), t.setAttribute("font-family", "sans-serif"), t.setAttribute("class", "vue-data-ui-doodle"), t.setAttribute("dominant-baseline", "hanging"), t.setAttribute("pointer-events", "all"); const a = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); a.setAttribute("x", e), a.setAttribute("dy", "0"), a.textContent = "", t.appendChild(a), t.style.pointerEvents = "none", t.style.userSelect = "none", o.value.appendChild(t), f.value = t, Q.value = !0, window.addEventListener("keydown", j), window.addEventListener("mousedown", _, !0), J(), Z(); } function j(l) { if (!Q.value) return; let { row: e, col: n } = D.value, t = N.value.slice(), a = !1; if (l.key === "Enter") { const u = t[e], r = u.slice(0, n), v = u.slice(n); t.splice(e, 1, r, v), e += 1, n = 0, a = !0, l.preventDefault(); } else if (l.key === "Backspace") { if (n > 0) t[e] = t[e].slice(0, n - 1) + t[e].slice(n), n -= 1, a = !0; else if (e > 0) { const u = t[e - 1].length; t[e - 1] += t[e], t.splice(e, 1), e -= 1, n = u, a = !0; } l.preventDefault(); } else if (l.key === "Delete") n < t[e].length ? (t[e] = t[e].slice(0, n) + t[e].slice(n + 1), a = !0) : e < t.length - 1 && (t[e] += t[e + 1], t.splice(e + 1, 1), a = !0), l.preventDefault(); else if (l.key === "ArrowLeft") n > 0 ? n -= 1 : e > 0 && (e -= 1, n = t[e].length), a = !0, l.preventDefault(); else if (l.key === "ArrowRight") n < t[e].length ? n += 1 : e < t.length - 1 && (e += 1, n = 0), a = !0, l.preventDefault(); else if (l.key === "ArrowUp") e > 0 && (e -= 1, n = Math.min(n, t[e].length), a = !0), l.preventDefault(); else if (l.key === "ArrowDown") e < t.length - 1 && (e += 1, n = Math.min(n, t[e].length), a = !0), l.preventDefault(); else if (l.key.length === 1 && !l.ctrlKey && !l.metaKey && !l.altKey) t[e] = t[e].slice(0, n) + l.key + t[e].slice(n), n += 1, a = !0, l.preventDefault(); else if (l.key === "Escape") { ee(!0); return; } else l.key === "Tab" && l.preventDefault(); a && (N.value = t, D.value = { row: e, col: n }, J(), Z()); } function J() { const l = f.value, { x: e } = H.value; for (; l.firstChild; ) l.removeChild(l.firstChild); N.value.forEach((n, t) => { const a = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); a.setAttribute("x", e), a.setAttribute("dy", t === 0 ? "0" : `${L.value * 1.2 * s.scale}`), a.textContent = n.length ? n : "​", l.appendChild(a); }); } function Z() { const l = o.value.querySelector(".vue-data-ui-svg-caret"); l && o.value.removeChild(l); const e = f.value; if (!e) return; const { x: n, y: t } = H.value, { row: a, col: u } = D.value, r = L.value * s.scale, v = e.childNodes[a]; if (!v) return; let g = v.textContent.slice(0, u); g.endsWith(" ") && (g += " "); const i = document.createElementNS("http://www.w3.org/2000/svg", "text"); i.setAttribute("x", n), i.setAttribute("y", t), i.setAttribute("font-size", r), i.setAttribute("font-family", "sans-serif"), i.textContent = g || "", o.value.appendChild(i); const p = i.getBBox(); o.value.removeChild(i); let m = t + a * r * 1.2, C = n + p.width; const w = document.createElementNS("http://www.w3.org/2000/svg", "rect"); w.setAttribute("x", C), w.setAttribute("y", m), w.setAttribute("width", 2), w.setAttribute("height", r), w.setAttribute("fill", S.value), w.setAttribute("class", "vue-data-ui-svg-caret"), o.value.appendChild(w); } function _(l) { if (f.value && !f.value.contains(l.target)) { const e = f.value.children; e.length === 1 && (e[0].textContent === "" || e[0].textContent === "​") && f.value.remove(), ee(!1); } } function ee(l = !1) { window.removeEventListener("keydown", j), window.removeEventListener("mousedown", _, !0); const e = o.value.querySelector(".vue-data-ui-svg-caret"); e && o.value.removeChild(e); const n = f.value?.children; let t = !1; if (n && n.length === 1) { const a = n[0].textContent; t = !a || a === "​"; } l || t ? f.value && o.value.contains(f.value) && o.value.removeChild(f.value) : f.value && o.value.contains(f.value) && b.value.push(f.value), Q.value = !1, f.value = null, N.value = [""], D.value = { row: 0, col: 0 }; } const y = ge(() => be(s.color, 0.6)); function te() { if (!o.value) return; const l = o.value.querySelector(".vue-data-ui-mask"); if (l && o.value.removeChild(l), s.active) { const e = document.createElementNS("http://www.w3.org/2000/svg", "rect"); e.setAttribute("class", "vue-data-ui-mask"), e.setAttribute("width", "100%"), e.setAttribute("height", "100%"), e.setAttribute("fill", "transparent"), e.setAttribute("pointer-events", "all"), o.value.insertBefore(e, o.value.firstChild); } } function V(l) { const e = s.svgRef; if (!e) return { x: 0, y: 0 }; const n = e.createSVGPoint(); n.x = l.clientX, n.y = l.clientY; const t = e.getScreenCTM()?.inverse(); return t ? n.matrixTransform(t) : { x: 0, y: 0 }; } function re(l) { const e = l.trim().split(/\s+/); if (e.length < 4) return l; const n = e.slice(1).map(Number); if (n.length % 2 !== 0) return l; const t = ie(n), a = [`M ${t[0]} ${t[1]}`]; for (let v = 2; v < t.length - 2; v += 2) { const g = t[v - 2], i = t[v - 1], p = t[v], m = t[v + 1], C = (g + p) / 2, w = (i + m) / 2; a.push(`Q ${g} ${i} ${C} ${w}`); } const u = t[t.length - 2], r = t[t.length - 1]; return a.push(`L ${u} ${r}`), a.join(" "); } function ie(l, e = 1) { const n = [...l]; for (let t = 2; t < l.length - 2; t += 2) { const a = l[t], u = l[t + 1], r = l[t - 2], v = l[t - 1], g = l[t + 2], i = l[t + 3]; n[t] = a + e * ((r + g) / 2 - a), n[t + 1] = u + e * ((v + i) / 2 - u); } return n; } function ve(l) { const e = l.trim().split(/\s+/); let n = "", t = "", a = null, u = null; for (let r = 0; r < e.length; r += 1) { const v = e[r]; if (isNaN(v)) { if (t = v, t === "M" || t === "L") a = parseFloat(e[++r]), u = parseFloat(e[++r]), n += `${t}${a} ${u}`; else if (t === "Q") { const g = parseFloat(e[++r]), i = parseFloat(e[++r]), p = parseFloat(e[++r]), m = parseFloat(e[++r]); g === a && i === u ? n += `t${p - a} ${m - u}` : n += `q${g - a} ${i - u} ${p - a} ${m - u}`, a = p, u = m; } } else { const g = parseFloat(v), i = parseFloat(e[++r]); if (t === "L") { const p = g - a, m = i - u; p === 0 ? n += `v${m}` : m === 0 ? n += `h${p}` : n += `l${p} ${m}`, a = g, u = i; } else if (t === "Q") { const p = g, m = i, C = parseFloat(e[++r]), w = parseFloat(e[++r]); p === a && m === u ? n += `t${C - a} ${w - u}` : n += `q${p - a} ${m - u} ${C - a} ${w - u}`, a = C, u = w; } } } return n; } function I(l) { if (h.value !== "draw" || !s.active || !o.value) return; M.value = !0; const { x: e, y: n } = V(l); F.value = { x: e, y: n }, P.value = `M ${e} ${n}`, A.value = document.createElementNS("http://www.w3.org/2000/svg", "path"), A.value.setAttribute("stroke", S.value), A.value.setAttribute("stroke-width", $.value * s.scale), A.value.setAttribute("fill", "none"), A.value.setAttribute("stroke-linecap", "round"), A.value.setAttribute("stroke-linejoin", "round"), A.value.setAttribute("class", "vue-data-ui-doodle"), o.value.appendChild(A.value); } function K(l) { if (!M.value || !o.value || !A.value) return; const { x: e, y: n } = V(l); P.value += ` ${e} ${n}`, A.value.setAttribute("d", P.value); } function B(l) { if (M.value && o.value && A.value) { const { x: e, y: n } = V(l); if (F.value && F.value.x === e && F.value.y === n) { const t = document.createElementNS("http://www.w3.org/2000/svg", "circle"); t.setAttribute("cx", e), t.setAttribute("cy", n), t.setAttribute("r", $.value * s.scale / 2), t.setAttribute("fill", S.value), t.setAttribute("class", "vue-data-ui-doodle"), o.value.appendChild(t), b.value.push(t); } else { const t = A.value; t.setAttribute("d", ve(re(P.value))), b.value.push(t); } E.value = [], A.value = ""; } M.value = !1; } function ce() { if (b.value.length > 0) { const l = b.value.pop(); E.value.push(l), o.value && o.value.removeChild(l); } } function de() { if (E.value.length > 0) { const l = E.value.pop(); b.value.push(l), o.value && o.value.appendChild(l); } } function fe() { o.value && (o.value.innerHTML = ""), b.value = [], E.value = [], te(); } X(h, () => { s.active && (U(), ne(), h.value === "text" ? o.value.style.cursor = "text" : o.value.style.cursor = W.value); }); function ne() { !s.svgRef || !s.active || (h.value === "draw" ? (s.svgRef.addEventListener("mousedown", I), s.svgRef.addEventListener("mousemove", K), s.svgRef.addEventListener("mouseup", B), s.svgRef.addEventListener("mouseleave", B), s.svgRef.addEventListener("touchstart", I, { passive: !1 }), s.svgRef.addEventListener("touchmove", K, { passive: !1 }), s.svgRef.addEventListener("touchend", B)) : h.value === "text" && s.svgRef.addEventListener("mousedown", O), o.value && (o.value.style.pointerEvents = "auto")); } function U() { s.svgRef && (s.svgRef.removeEventListener("mousedown", I), s.svgRef.removeEventListener("mousemove", K), s.svgRef.removeEventListener("mouseup", B), s.svgRef.removeEventListener("mouseleave", B), s.svgRef.removeEventListener("touchstart", I), s.svgRef.removeEventListener("touchmove", K), s.svgRef.removeEventListener("touchend", B), s.svgRef.removeEventListener("mousedown", O), o.value && (o.value.style.pointerEvents = "none")); } return X(() => s.active, (l) => { l ? ne() : U(); }), X(() => s.active, () => { le(() => { te(); }); }), pe(() => { le(() => { s.svgRef && (o.value = document.createElementNS("http://www.w3.org/2000/svg", "g"), o.value.setAttribute("class", "vue-data-ui-doodles"), o.value.style.cursor = W.value, s.svgRef.appendChild(o.value), U()); }); }), me(() => { o.value && s.svgRef && (s.svgRef.removeChild(o.value), U()); }), (l, e) => d.active ? (z(), Y("div", { key: 0, "data-dom-to-png-ignore": "", class: "vue-ui-pen-and-paper-actions", style: x({ backgroundColor: d.backgroundColor }) }, [ k("button", { class: "vue-ui-pen-and-paper-action", onClick: e[0] || (e[0] = (n) => ue("close")), style: x({ backgroundColor: d.backgroundColor, border: `1px solid ${y.value}` }) }, [ R(T, { name: "close", stroke: d.color }, null, 8, ["stroke"]) ], 4), k("button", ye, [ R(we, { value: S.value, "onUpdate:value": e[1] || (e[1] = (n) => S.value = n), backgroundColor: d.backgroundColor, buttonBorderColor: y.value }, null, 8, ["value", "backgroundColor", "buttonBorderColor"]) ]), k("button", { class: G(["vue-ui-pen-and-paper-action", { "vue-ui-pen-and-paper-action-active": h.value === "text" }]), onClick: e[2] || (e[2] = (n) => h.value = h.value === "text" ? "draw" : "text"), style: x({ backgroundColor: d.backgroundColor, border: `1px solid ${y.value}` }) }, [ R(T, { name: h.value === "draw" ? "annotator" : "text", stroke: d.color }, null, 8, ["name", "stroke"]), k("div", { style: x({ position: "absolute", bottom: "-20px", color: y.value, width: "100%", textAlign: "center", fontSize: "12px", fontVariantNumeric: "tabular-nums" }) }, Ae(he(xe)({ v: h.value === "draw" ? $.value : L.value, s: "px", r: 1 })), 5) ], 6), k("button", { class: G(["vue-ui-pen-and-paper-action", { "vue-ui-pen-and-paper-action-disabled": !b.value.length }]), disabled: !b.value.length, onClick: ce, style: x({ backgroundColor: d.backgroundColor, border: `1px solid ${y.value}`, marginTop: "20px" }) }, [ R(T, { name: "restart", stroke: d.color }, null, 8, ["stroke"]) ], 14, Ce), k("button", { class: G(["vue-ui-pen-and-paper-action", { "vue-ui-pen-and-paper-action-disabled": !E.value.length }]), onClick: de, style: x({ backgroundColor: d.backgroundColor, border: `1px solid ${y.value}` }) }, [ R(T, { name: "restart", stroke: d.color, style: { transform: "scaleX(-1)" } }, null, 8, ["stroke"]) ], 6), k("button", { class: G(["vue-ui-pen-and-paper-action", { "vue-ui-pen-and-paper-action-disabled": !b.value.length }]), onClick: fe, style: x({ backgroundColor: d.backgroundColor, border: `1px solid ${y.value}` }) }, [ R(T, { name: "trash", stroke: d.color }, null, 8, ["stroke"]) ], 6), h.value === "draw" ? ae((z(), Y("input", { key: 0, ref: "range", type: "range", class: "vertical-range", min: 0.5, max: 12, step: 0.1, "onUpdate:modelValue": e[3] || (e[3] = (n) => $.value = n), style: x({ accentColor: d.color }) }, null, 4)), [ [oe, $.value] ]) : q("", !0), h.value === "text" ? ae((z(), Y("input", { key: 1, ref: "range", type: "range", class: "vertical-range", min: 3, max: 48, step: 0.1, "onUpdate:modelValue": e[4] || (e[4] = (n) => L.value = n), style: x({ accentColor: d.color }) }, null, 4)), [ [oe, L.value] ]) : q("", !0) ], 4)) : q("", !0); } }; export { Be as default };