UNPKG

vue-data-ui

Version:

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

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