UNPKG

vue-data-ui

Version:

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

528 lines (527 loc) 21.7 kB
import { useCssVars as Ve, computed as y, ref as o, watch as We, onMounted as De, onBeforeUnmount as Xe, createElementBlock as a, openBlock as i, unref as r, normalizeStyle as C, createBlock as F, createCommentVNode as v, createSlots as Ye, withCtx as z, renderSlot as h, normalizeProps as B, guardReactiveProps as R, normalizeClass as D, createVNode as qe, createElementVNode as He, Fragment as P, renderList as L, mergeProps as me, toDisplayString as E } from "vue"; import { u as Je, c as Ke, t as Qe, p as ke, a as Ze, b as et, o as tt, e as pe, g as st, X as lt, G as we, i as X, f as Y, F as q } from "./index-CHWA6Lnw.js"; import { t as it, u as at } from "./useResponsive-vZgZwV-S.js"; import { _ as nt } from "./Title-BwZtefYd.js"; import { u as rt, U as ut } from "./usePrinter-DibtVl2x.js"; import ot from "./vue-ui-skeleton-BSUFPx4a.js"; import { u as xe } from "./useNestedProp-ByBiJC9_.js"; import { _ as ct } from "./PackageVersion-DcMafJMi.js"; import { P as dt } from "./PenAndPaper-ljJaW1FE.js"; import { u as vt } from "./useUserOptionState-BIvW1Kz7.js"; import { u as ft } from "./useChartAccessibility-BWojgys7.js"; import { _ as yt } from "./_plugin-vue_export-helper-CHgC5LLL.js"; const ht = ["id"], gt = ["xmlns", "viewBox"], mt = ["width", "height"], kt = ["cx", "cy", "r", "stroke", "stroke-width"], pt = { key: 1 }, wt = ["stroke", "d", "stroke-width"], xt = { style: { "pointer-events": "none" } }, bt = ["cx", "cy", "fill", "r", "stroke"], $t = ["x", "y", "fill", "font-size"], _t = { key: 2 }, Ct = ["stroke", "stroke-width", "x1", "x2", "y1", "y2"], Pt = { style: { "pointer-events": "none" } }, Lt = ["cx", "cy", "fill", "r", "stroke"], Ot = ["x", "y", "fill", "font-size"], zt = ["text-anchor", "transform", "x", "y", "onClick", "font-weight", "font-size", "fill"], At = ["cx", "cy", "fill", "stroke", "onClick", "r"], It = { key: 5, class: "vue-data-ui-watermark" }, Mt = { __name: "vue-ui-relation-circle", props: { dataset: { type: Array, default() { return []; } }, config: { type: Object, default() { return {}; } } }, setup(be, { expose: $e }) { Ve((e) => ({ "011bfd17": ze.value, "437fa914": Oe.value, "3d1724d8": Ae.value })); const { vue_ui_relation_circle: _e } = Je(), p = be, T = y(() => !!p.dataset && Object.keys(p.dataset).length), O = o(Ke()), H = o(0), A = o(null), J = o(null), K = o(null), Q = o(null), Z = o(0), s = y({ get: () => le(), set: (e) => e }), { userOptionsVisible: N, setUserOptionsVisibility: ee, keepUserOptionState: te } = vt({ config: s.value }), { svgRef: se } = ft({ config: s.value.style.title }); function le() { const e = xe({ userConfig: p.config, defaultConfig: _e }); return e.theme ? { ...xe({ userConfig: Ze.vue_ui_relation_circle[e.theme] || p.config, defaultConfig: e }), customPalette: Qe[e.theme] || ke } : e; } We(() => p.config, (e) => { s.value = le(), N.value = !s.value.userOptions.showOnChartHover, de(), Z.value += 1; }, { deep: !0 }); const { isPrinting: ie, isImaging: ae, generatePdf: ne, generateImage: re } = rt({ elementId: `relation_circle_${O.value}`, fileName: s.value.style.title.text || "vue-ui-relation-circle" }), Ce = y(() => s.value.userOptions.show && !s.value.style.title.text), ue = y(() => et(s.value.customPalette)), x = o([]), g = o([]), c = o({}), m = o([]), Pe = o(0), j = y(() => p.dataset.slice(0, s.value.style.limit)), $ = o(s.value.style.size), k = o(s.value.style.weightLabels.size), oe = o(s.value.style.plot.radius), ce = o(s.value.style.labels.fontSize), u = o({ height: s.value.style.size, width: s.value.style.size }), b = y({ get() { return $.value * s.value.style.circle.radiusProportion; }, set(e) { return e; } }), Le = y(() => s.value.style.links.curved), Oe = y(() => `${s.value.style.animation.speedMs}ms`), ze = y(() => b.value * 2), Ae = y(() => b.value * 4), I = o(null); De(() => { de(), document.getElementById(`relation_circle_${O.value}`).addEventListener("click", ve); }); function de() { if (tt(p.dataset) ? pe({ componentName: "VueUiRelationCircle", type: "dataset" }) : p.dataset.forEach((e, l) => { st({ datasetObject: e, requiredAttributes: ["id", "label", "relations", "weights"] }).forEach((t) => { pe({ componentName: "VueUiRelationCircle", type: "datasetSerieAttribute", property: t, index: l }); }); }), s.value.responsive) { const e = it(() => { const { width: l, height: t } = at({ chart: A.value, title: s.value.style.title.text ? J.value : null, source: K.value, noTitle: Q.value }); requestAnimationFrame(() => { $.value = Math.min(l, t), u.value.width = l, u.value.height = t, b.value = $.value * s.value.style.circle.radiusProportion, x.value = [], g.value = [], fe(), ye(), k.value = q({ relator: $.value, adjuster: s.value.style.size, source: s.value.style.weightLabels.size, threshold: 6, fallback: 6 }), oe.value = q({ relator: $.value, adjuster: s.value.style.size, source: s.value.style.plot.radius, threshold: 1, fallback: 1 }), ce.value = q({ relator: $.value, adjuster: s.value.style.size, source: s.value.style.labels.fontSize, threshold: 6, fallback: 6 }); }); }); I.value = new ResizeObserver(e), I.value.observe(A.value.parentNode); } else x.value = [], g.value = [], fe(), ye(); } Xe(() => { document.getElementById(`relation_circle_${O.value}`).removeEventListener("click", ve), I.value && I.value.disconnect(); }); function ve(e) { const l = e.target; l && Array.from(l.classList).includes("vue-ui-user-options") || l && Array.from(l.classList).includes("vue-ui-user-options-summary") || l && Array.from(l.classList).includes("vue-data-ui-button") || l && Array.from(l.classList).includes("vue-ui-relation-circle-legend") || (c.value = {}, m.value = []); } function fe() { const e = 6.28319 / j.value.length, l = 360 / j.value.length; let t = 0, n = 0; j.value.forEach((f, d) => { const V = f.weights.reduce((Ue, Ge) => Ue + Ge, 0), W = b.value * Math.cos(t) + u.value.width / 2, je = b.value * Math.sin(t) + u.value.height / 2 + s.value.style.circle.offsetY; x.value.push({ x: W, y: je, ...f, color: f.color ? f.color : ue.value[d] ? ue.value[d] : ke[d], regAngle: n, totalWeight: V }), t += e, n += l; }); } function Ie(e, l) { const t = (e.x + l.x) / 2, n = (e.y + l.y) / 2; return { x: t, y: n }; } function ye() { g.value = [], x.value.forEach((e) => { x.value.filter((t) => t.relations.includes(e.id)).forEach((t, n) => { const f = t.relations.indexOf(e.id); g.value.push({ weight: t.weights[f] ? t.weights[f] : 0, relationId: `${e.id}_${t.id}`, x1: e.x, y1: e.y, x2: t.x, y2: t.y, colorSource: e.color, colorTarget: t.color, midPointLine: Ie({ x: e.x, y: e.y }, { x: t.x, y: t.y }), midPointBezier: Me({ x1: e.x, x2: t.x, y1: e.y, y2: t.y }), ...e }); }); }); } function Me(e) { const l = { x: e.x1, y: e.y1 }, t = { x: e.x2, y: e.y2 }, n = { x: e.x1, y: e.y1 }, f = { x: u.value.width / 2, y: u.value.height / 2 + s.value.style.circle.offsetY }, d = 0.5, V = Math.pow(1 - d, 3) * l.x + 3 * Math.pow(1 - d, 2) * d * n.x + 3 * (1 - d) * Math.pow(d, 2) * f.x + Math.pow(d, 3) * t.x, W = Math.pow(1 - d, 3) * l.y + 3 * Math.pow(1 - d, 2) * d * n.y + 3 * (1 - d) * Math.pow(d, 2) * f.y + Math.pow(d, 3) * t.y; return { x: V, y: W }; } const Se = y(() => Math.max(...g.value.map((e) => e.weight))); function Fe(e) { return Object.hasOwn(c.value, "x") ? m.value.includes(e.id) ? "opacity:1" : "opacity:0.1" : "opacity:1"; } function w(e) { return e.colorSource; } function he(e) { return Object.hasOwn(c.value, "x") ? m.value.includes(e.id) && e.relationId === `${e.id}_${c.value.id}` || e.relationId === `${c.value.id}_${e.id}` ? `opacity:1;stroke-width:${U(e)}` : "opacity: 0" : "opacity: 1"; } function _(e) { return Object.hasOwn(c.value, "x") ? !!(m.value.includes(e.id) && e.relationId === `${e.id}_${c.value.id}` || e.relationId === `${c.value.id}_${e.id}`) : !1; } function Be(e) { return e.regAngle > 90 && e.regAngle < 270 ? "end" : "start"; } function Re(e) { return e.regAngle > 90 && e.regAngle < 270 ? e.x - 5 : e.x + 5; } function Ee(e) { return Object.hasOwn(c.value, "x") ? c.value.id === e.id || m.value.includes(e.id) ? "opacity:1" : "opacity:0.2" : "opacity:1"; } function Te(e) { return e.regAngle > 90 && e.regAngle < 270 ? `rotate(${e.regAngle + 180},${e.x},${e.y})` : `rotate(${e.regAngle},${e.x},${e.y})`; } function ge(e) { Pe.value = 360 - e.regAngle, c.value.id && e.id === c.value.id ? (c.value = {}, m.value = []) : (c.value = e, m.value = [...e.relations]); } function U(e) { const l = e.weight / Se.value * s.value.style.links.maxWidth; return Math.max(0.3, l); } const M = o(!1); function Ne(e) { M.value = e, H.value += 1; } const S = o(!1); function G() { S.value = !S.value; } return $e({ generatePdf: ne, generateImage: re, toggleAnnotator: G }), (e, l) => (i(), a("div", { ref_key: "relationCircleChart", ref: A, class: "vue-ui-relation-circle", style: C(`width:100%;background:${s.value.style.backgroundColor};text-align:center;${s.value.responsive ? "height: 100%" : ""}`), id: `relation_circle_${O.value}`, onMouseenter: l[0] || (l[0] = () => r(ee)(!0)), onMouseleave: l[1] || (l[1] = () => r(ee)(!1)) }, [ s.value.userOptions.buttons.annotator ? (i(), F(dt, { key: 0, svgRef: r(se), backgroundColor: s.value.style.backgroundColor, color: s.value.style.color, active: S.value, onClose: G }, null, 8, ["svgRef", "backgroundColor", "color", "active"])) : v("", !0), Ce.value ? (i(), a("div", { key: 1, ref_key: "noTitle", ref: Q, class: "vue-data-ui-no-title-space", style: "height:36px; width: 100%;background:transparent" }, null, 512)) : v("", !0), s.value.style.title.text ? (i(), a("div", { key: 2, ref_key: "chartTitle", ref: J, style: "width:100%;background:transparent" }, [ (i(), F(nt, { key: `title_${Z.value}`, config: { title: { cy: "relation-div-title", ...s.value.style.title }, subtitle: { cy: "relation-div-subtitle", ...s.value.style.title.subtitle } } }, null, 8, ["config"])) ], 512)) : v("", !0), s.value.userOptions.show && T.value && (r(te) || r(N)) ? (i(), F(ut, { ref: "details", key: `user_options_${H.value}`, backgroundColor: s.value.style.backgroundColor, color: s.value.style.color, isPrinting: r(ie), isImaging: r(ae), uid: O.value, hasPdf: s.value.userOptions.buttons.pdf, hasImg: s.value.userOptions.buttons.img, hasFullscreen: s.value.userOptions.buttons.img, hasXls: !1, isFullscreen: M.value, titles: { ...s.value.userOptions.buttonTitles }, chartElement: A.value, position: s.value.userOptions.position, hasAnnotator: s.value.userOptions.buttons.annotator, isAnnotation: S.value, onToggleFullscreen: Ne, onGeneratePdf: r(ne), onGenerateImage: r(re), onToggleAnnotator: G, style: C({ visibility: r(te) ? r(N) ? "visible" : "hidden" : "visible" }) }, Ye({ _: 2 }, [ e.$slots.menuIcon ? { name: "menuIcon", fn: z(({ isOpen: t, color: n }) => [ h(e.$slots, "menuIcon", B(R({ isOpen: t, color: n })), void 0, !0) ]), key: "0" } : void 0, e.$slots.optionPdf ? { name: "optionPdf", fn: z(() => [ h(e.$slots, "optionPdf", {}, void 0, !0) ]), key: "1" } : void 0, e.$slots.optionImg ? { name: "optionImg", fn: z(() => [ h(e.$slots, "optionImg", {}, void 0, !0) ]), key: "2" } : void 0, e.$slots.optionFullscreen ? { name: "optionFullscreen", fn: z(({ toggleFullscreen: t, isFullscreen: n }) => [ h(e.$slots, "optionFullscreen", B(R({ toggleFullscreen: t, isFullscreen: n })), void 0, !0) ]), key: "3" } : void 0, e.$slots.optionAnnotator ? { name: "optionAnnotator", fn: z(({ toggleAnnotator: t, isAnnotator: n }) => [ h(e.$slots, "optionAnnotator", B(R({ toggleAnnotator: t, isAnnotator: n })), void 0, !0) ]), key: "4" } : void 0 ]), 1032, ["backgroundColor", "color", "isPrinting", "isImaging", "uid", "hasPdf", "hasImg", "hasFullscreen", "isFullscreen", "titles", "chartElement", "position", "hasAnnotator", "isAnnotation", "onGeneratePdf", "onGenerateImage", "style"])) : v("", !0), T.value ? (i(), a("svg", { key: 4, ref_key: "svgRef", ref: se, xmlns: r(lt), class: D([{ "vue-data-ui-fullscreen--on": M.value, "vue-data-ui-fulscreen--off": !M.value }, "relation-circle"]), viewBox: `0 0 ${u.value.width <= 0 ? 10 : u.value.width} ${u.value.height <= 0 ? 10 : u.value.height}`, width: "100%", style: "user-select:none; background:transparent" }, [ qe(ct), e.$slots["chart-background"] ? (i(), a("foreignObject", { key: 0, x: 0, y: 0, width: u.value.width <= 0 ? 10 : u.value.width, height: u.value.height <= 0 ? 10 : u.value.height, style: { pointerEvents: "none" } }, [ h(e.$slots, "chart-background", {}, void 0, !0) ], 8, mt)) : v("", !0), He("circle", { cx: (u.value.width <= 0 ? 1e-4 : u.value.width) / 2, cy: (u.value.height <= 0 ? 1e-4 : u.value.height) / 2 + s.value.style.circle.offsetY, r: b.value <= 0 ? 1e-4 : b.value, stroke: s.value.style.circle.stroke, "stroke-width": s.value.style.circle.strokeWidth, fill: "transparent", class: "main-circle" }, null, 8, kt), Le.value ? (i(), a("g", pt, [ (i(!0), a(P, null, L(g.value, (t, n) => (i(), a("path", { key: `relation_${n}`, style: C(he(t)), stroke: w(t), class: D(["relation", { "vue-ui-relation-circle-selected": c.value.hasOwnProperty("id") && m.value.includes(t.id) }]), d: `M${t.x1},${t.y1} C${t.x1},${t.y1} ${u.value.width / 2},${u.value.height / 2 + s.value.style.circle.offsetY} ${t.x2},${t.y2}`, fill: "none", "stroke-width": U(t), "stroke-linecap": "round" }, null, 14, wt))), 128)), (i(!0), a(P, null, L(g.value, (t, n) => (i(), a("g", xt, [ _(t) ? h(e.$slots, "dataLabel", me({ key: 0, ref_for: !0 }, { x: t.midPointBezier.x, y: t.midPointBezier.y, color: w(t), weight: t.weight, fontSize: k.value }), void 0, !0) : v("", !0), _(t) && !e.$slots.dataLabel ? (i(), a("circle", { key: 1, cx: t.midPointBezier.x, cy: t.midPointBezier.y, fill: w(t), r: k.value, stroke: s.value.style.backgroundColor, "stroke-width": "1" }, null, 8, bt)) : v("", !0), _(t) && !e.$slots.dataLabel ? (i(), a("text", { key: 2, x: t.midPointBezier.x, y: t.midPointBezier.y + k.value / 3, fill: r(we)(w(t)), "text-anchor": "middle", "font-size": k.value }, E(r(X)( s.value.style.weightLabels.formatter, t.weight, r(Y)({ p: s.value.style.weightLabels.prefix, v: t.weight, s: s.value.style.weightLabels.suffix, r: s.value.style.weightLabels.rounding }), { ...t } )), 9, $t)) : v("", !0) ]))), 256)) ])) : (i(), a("g", _t, [ (i(!0), a(P, null, L(g.value, (t, n) => (i(), a("line", { key: `relation_${n}`, stroke: w(t), "stroke-width": U(t), style: C(he(t)), x1: t.x1, x2: t.x2, y1: t.y1, y2: t.y2, class: D({ "vue-ui-relation-circle-selected": c.value.hasOwnProperty("id") && m.value.includes(t.id) }), "stroke-linecap": "round" }, null, 14, Ct))), 128)), (i(!0), a(P, null, L(g.value, (t, n) => (i(), a("g", Pt, [ _(t) ? h(e.$slots, "dataLabel", me({ key: 0, ref_for: !0 }, { x: t.midPointLine.x, y: t.midPointLine.y, color: w(t), weight: t.weight, fontSize: k.value }), void 0, !0) : v("", !0), _(t) && !e.$slots.dataLabel && s.value.style.weightLabels.show ? (i(), a("circle", { key: 1, cx: t.midPointLine.x, cy: t.midPointLine.y, fill: w(t), r: k.value, stroke: s.value.style.backgroundColor, "stroke-width": "1" }, null, 8, Lt)) : v("", !0), _(t) && !e.$slots.dataLabel && s.value.style.weightLabels.show ? (i(), a("text", { key: 2, x: t.midPointLine.x, y: t.midPointLine.y + k.value / 3, fill: r(we)(w(t)), "text-anchor": "middle", "font-size": k.value }, E(r(X)( s.value.style.weightLabels.formatter, t.weight, r(Y)({ p: s.value.style.weightLabels.prefix, v: t.weight, s: s.value.style.weightLabels.suffix, r: s.value.style.weightLabels.rounding }), { ...t } )), 9, Ot)) : v("", !0) ]))), 256)) ])), (i(!0), a(P, null, L(x.value, (t, n) => (i(), a("text", { key: `plot_text_${n}`, "text-anchor": Be(t), transform: Te(t), x: Re(t), y: t.y + 5, onClick: (f) => ge(t), class: "vue-ui-relation-circle-legend", "transform-origin": "start", "font-weight": c.value.id === t.id ? "900" : "400", style: C(`font-family:${s.value.style.fontFamily};${Ee(t)}`), "font-size": ce.value, fill: s.value.style.labels.color }, E(t.label) + " (" + E(r(X)( s.value.style.weightLabels.formatter, t.totalWeight, r(Y)({ p: s.value.style.weightLabels.prefix, v: t.totalWeight, s: s.value.style.weightLabels.suffix, r: s.value.style.weightLabels.rounding }), { ...t } )) + ") ", 13, zt))), 128)), (i(!0), a(P, null, L(x.value, (t, n) => (i(), a("circle", { cx: t.x, cy: t.y, key: `plot_${n}`, style: C(Fe(t)), class: "vue-ui-relation-circle-plot", fill: s.value.style.plot.useSerieColor ? t.color : s.value.style.plot.color, stroke: s.value.style.backgroundColor, "stroke-width": "1", onClick: (f) => ge(t), r: oe.value }, null, 12, At))), 128)), h(e.$slots, "svg", { svg: u.value }, void 0, !0) ], 10, gt)) : v("", !0), e.$slots.watermark ? (i(), a("div", It, [ h(e.$slots, "watermark", B(R({ isPrinting: r(ie) || r(ae) })), void 0, !0) ])) : v("", !0), e.$slots.source ? (i(), a("div", { key: 6, ref_key: "source", ref: K, dir: "auto" }, [ h(e.$slots, "source", {}, void 0, !0) ], 512)) : v("", !0), T.value ? v("", !0) : (i(), F(ot, { key: 7, config: { type: "relationCircle", style: { backgroundColor: s.value.style.backgroundColor, relationCircle: { color: "#CCCCCC" } } } }, null, 8, ["config"])) ], 44, ht)); } }, Dt = /* @__PURE__ */ yt(Mt, [["__scopeId", "data-v-4c69f870"]]); export { Dt as default };