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