vue-data-ui
Version:
A user-empowering data visualization Vue 3 components library for eloquent data storytelling
374 lines (373 loc) • 17.3 kB
JavaScript
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
};