Teren-app/resources/js/Components/ui/chart/ChartCrosshair.vue

55 lines
1.8 KiB
Vue

<script setup>
import { omit } from "@unovis/ts";
import { VisCrosshair, VisTooltip } from "@unovis/vue";
import { createApp } from "vue";
import { ChartTooltip } from ".";
const props = defineProps({
colors: { type: Array, required: false, default: () => [] },
index: { type: String, required: true },
// items now optional when using external template factory
items: { type: Array, required: false, default: () => [] },
customTooltip: { type: null, required: false },
labelFormatter: { type: Function, required: false },
// template override (e.g., componentToString(...))
template: { type: Function, required: false },
});
// Use weakmap to store reference to each datapoint for Tooltip
const wm = new WeakMap();
function internalTemplate(d) {
// If we have cached markup and no custom formatter altering title, reuse.
if (wm.has(d) && !props.labelFormatter && !props.template) {
return wm.get(d);
}
// If external template provided, delegate directly
if (props.template) {
const html = props.template(d, d[props.index]);
wm.set(d, html);
return html;
}
const componentDiv = document.createElement("div");
const omittedData = Object.entries(omit(d, [props.index])).map(([key, value]) => {
const legendReference = props.items.find((i) => i.name === key);
return { ...legendReference, value };
});
const TooltipComponent = props.customTooltip ?? ChartTooltip;
createApp(TooltipComponent, {
title: d[props.index],
data: omittedData,
labelFormatter: props.labelFormatter,
}).mount(componentDiv);
wm.set(d, componentDiv.innerHTML);
return componentDiv.innerHTML;
}
function color(d, i) {
return props.colors[i] ?? "transparent";
}
</script>
<template>
<VisTooltip :horizontal-shift="20" :vertical-shift="20" />
<VisCrosshair :template="internalTemplate" :color="color" />
</template>