feat: rendering tooltips and dataView with slots (#838)

* feat: experimental component rendered tooltip

* revert slot in VChart

* feat: use tooltip composable

* feat: try createApp

* feat: use pie chart as tooltip

* feat: switch to createVNode

The limitation is that the tooltip detached from the current component tree, not provide/inject

will try teleport next

* feat: try component with teleport

* wip

* add xAxis example

* refactor with shallowReactive

* Support dynamic slot

* fix: fill empty elements with object in array

* shallow copy option along the path

* ssr friendly

* vibe docs

* typo

* update according to the review

* add dataView slot

* chore: fix warnings and errors in demo (#839)

* chore: suppress warning in demo

* chore: prevent multiple intializations of esbuild-wasm in demo HMR

* feat: dynamically update the theme (#841)

Co-authored-by: GU Yiling <justice360@gmail.com>

* feat: add dataView slot

* vibe docs

---------

Co-authored-by: GU Yiling <justice360@gmail.com>

* fix docs typo

* update according to the review

* small fix

* remove wrapper around slotProp

* update comments

* remove anys

* add tooltip slot prop type

* target to vue 3.3

* move slot related codes to slot.ts

---------

Co-authored-by: GU Yiling <justice360@gmail.com>
This commit is contained in:
Yue JIN
2025-07-26 01:30:58 +08:00
committed by GU Yiling
parent 4beaa9bce9
commit a6ad4e70a2
13 changed files with 513 additions and 45 deletions

View File

@ -8,6 +8,7 @@ import { track } from "@vercel/analytics";
import LogoChart from "./examples/LogoChart.vue";
import BarChart from "./examples/BarChart.vue";
import LineChart from "./examples/LineChart.vue";
import PieChart from "./examples/PieChart.vue";
import PolarChart from "./examples/PolarChart.vue";
import ScatterChart from "./examples/ScatterChart.vue";
@ -74,6 +75,7 @@ watch(codeOpen, (open) => {
</p>
<bar-chart />
<line-chart />
<pie-chart />
<polar-chart />
<scatter-chart />

56
demo/data/line.js Normal file
View File

@ -0,0 +1,56 @@
export default function getData() {
return {
textStyle: {
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
fontWeight: 300,
},
legend: { top: 20 },
tooltip: {
trigger: "axis",
},
dataset: {
source: [
["product", "2012", "2013", "2014", "2015", "2016", "2017"],
["Milk Tea", 56.5, 82.1, 88.7, 70.1, 53.4, 85.1],
["Matcha Latte", 51.1, 51.4, 55.1, 53.3, 73.8, 68.7],
["Cheese Cocoa", 40.1, 62.2, 69.5, 36.4, 45.2, 32.5],
["Walnut Brownie", 25.2, 37.1, 41.2, 18, 33.9, 49.1],
],
},
xAxis: {
type: "category",
triggerEvent: true,
tooltip: { show: true, formatter: "" },
},
yAxis: {
triggerEvent: true,
tooltip: { show: true, formatter: "" },
},
series: [
{
type: "line",
smooth: true,
seriesLayoutBy: "row",
emphasis: { focus: "series" },
},
{
type: "line",
smooth: true,
seriesLayoutBy: "row",
emphasis: { focus: "series" },
},
{
type: "line",
smooth: true,
seriesLayoutBy: "row",
emphasis: { focus: "series" },
},
{
type: "line",
smooth: true,
seriesLayoutBy: "row",
emphasis: { focus: "series" },
},
],
};
}

View File

@ -43,7 +43,7 @@ defineProps({
width: fit-content;
margin: 2em auto;
.echarts {
> .echarts {
width: calc(60vw + 4em);
height: 360px;
max-width: 720px;

108
demo/examples/LineChart.vue Normal file
View File

@ -0,0 +1,108 @@
<script setup>
import { use } from "echarts/core";
import { LineChart, PieChart } from "echarts/charts";
import {
GridComponent,
DatasetComponent,
LegendComponent,
TooltipComponent,
ToolboxComponent,
} from "echarts/components";
import { shallowRef } from "vue";
import VChart from "../../src/ECharts";
import VExample from "./Example.vue";
import getData from "../data/line";
use([
DatasetComponent,
GridComponent,
LegendComponent,
LineChart,
TooltipComponent,
ToolboxComponent,
PieChart,
]);
const option = shallowRef(getData());
const axis = shallowRef("xAxis");
function getPieOption(params) {
const option = {
dataset: { source: [params[0].dimensionNames, params[0].data] },
series: [
{
type: "pie",
radius: ["60%", "100%"],
seriesLayoutBy: "row",
itemStyle: {
borderRadius: 5,
borderColor: "#fff",
borderWidth: 2,
},
label: {
position: "center",
formatter: params[0].name,
fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
fontWeight: 300,
},
},
],
};
return option;
}
</script>
<template>
<v-example
id="line"
title="Line chart"
desc="(with tooltip and dataView slots)"
>
<v-chart :option="option" autoresize>
<template #tooltip="params">
<v-chart
:style="{ width: '100px', height: '100px' }"
:option="getPieOption(params)"
autoresize
/>
</template>
<template #[`tooltip-${axis}`]="params">
{{ axis === "xAxis" ? "Year" : "Value" }}:
<b>{{ params.name }}</b>
</template>
<template #dataView="option">
<table style="margin: 20px auto">
<thead>
<tr>
<th v-for="(t, i) in option.dataset[0].source[0]" :key="i">
{{ t }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, i) in option.dataset[0].source.slice(1)" :key="i">
<th>{{ row[0] }}</th>
<td v-for="(v, i) in row.slice(1)" :key="i">{{ v }}</td>
</tr>
</tbody>
</table>
</template>
</v-chart>
<template #extra>
<p class="actions">
Custom tooltip on
<select v-model="axis">
<option value="xAxis">X Axis</option>
<option value="yAxis">Y Axis</option>
</select>
</p>
</template>
</v-example>
</template>
<style scoped>
th,
td {
padding: 4px 8px;
}
</style>