Replace built-in chart legend with custom legend for reliable hover sync
ApexCharts legendMouseOver event is unreliable for donut charts. Use custom HTML legend items with direct mouseenter/mouseleave handlers that sync highlighting between legend, chart slices, and the category table. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -86,6 +86,19 @@
|
|||||||
:options="spendingChartOptions"
|
:options="spendingChartOptions"
|
||||||
:series="spendingChartSeries"
|
:series="spendingChartSeries"
|
||||||
/>
|
/>
|
||||||
|
<div v-if="spending.categories.length > 0" class="d-flex flex-wrap justify-center ga-2 mt-2">
|
||||||
|
<div
|
||||||
|
v-for="(cat, idx) in spending.categories"
|
||||||
|
:key="cat.categoryName"
|
||||||
|
class="d-flex align-center ga-1 px-2 py-1 rounded cursor-pointer legend-item"
|
||||||
|
:class="{ 'spending-row-highlight': highlightedCategory === cat.categoryName }"
|
||||||
|
@mouseenter="highlightCategory(cat.categoryName)"
|
||||||
|
@mouseleave="unhighlightCategory()"
|
||||||
|
>
|
||||||
|
<span class="legend-dot" :style="{ backgroundColor: chartColors[idx % chartColors.length] }" />
|
||||||
|
<span class="text-caption">{{ cat.categoryName }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div v-else class="text-center pa-8 text-medium-emphasis">No spending data for this period</div>
|
<div v-else class="text-center pa-8 text-medium-emphasis">No spending data for this period</div>
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="12" md="7">
|
<v-col cols="12" md="7">
|
||||||
@@ -347,6 +360,8 @@ function setChartHighlight(categoryName: string | null) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const chartColors = ['#008FFB', '#00E396', '#FEB019', '#FF4560', '#775DD0', '#546E7A', '#26a69a', '#D10CE8', '#2E93fA', '#FF9800']
|
||||||
|
|
||||||
const spendingChartOptions = computed(() => ({
|
const spendingChartOptions = computed(() => ({
|
||||||
chart: {
|
chart: {
|
||||||
type: 'donut' as const,
|
type: 'donut' as const,
|
||||||
@@ -358,17 +373,11 @@ const spendingChartOptions = computed(() => ({
|
|||||||
dataPointMouseLeave: () => {
|
dataPointMouseLeave: () => {
|
||||||
setChartHighlight(null)
|
setChartHighlight(null)
|
||||||
},
|
},
|
||||||
legendMouseOver: (_ctx: any, config: any) => {
|
|
||||||
const labels = spending.value?.categories.map(c => c.categoryName) ?? []
|
|
||||||
setChartHighlight(labels[config.seriesIndex] ?? null)
|
|
||||||
},
|
|
||||||
legendMouseOut: () => {
|
|
||||||
setChartHighlight(null)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
colors: chartColors,
|
||||||
labels: spending.value?.categories.map(c => c.categoryName) ?? [],
|
labels: spending.value?.categories.map(c => c.categoryName) ?? [],
|
||||||
legend: { position: 'bottom' as const, labels: { colors: labelColor.value } },
|
legend: { show: false },
|
||||||
dataLabels: { style: { colors: ['#FFFFFF'] }, dropShadow: { enabled: true, top: 1, left: 1, blur: 2, opacity: 0.5 } },
|
dataLabels: { style: { colors: ['#FFFFFF'] }, dropShadow: { enabled: true, top: 1, left: 1, blur: 2, opacity: 0.5 } },
|
||||||
tooltip: {
|
tooltip: {
|
||||||
y: {
|
y: {
|
||||||
@@ -589,4 +598,13 @@ onMounted(async () => {
|
|||||||
background-color: rgba(var(--v-theme-primary), 0.15);
|
background-color: rgba(var(--v-theme-primary), 0.15);
|
||||||
transition: background-color 0.15s;
|
transition: background-color 0.15s;
|
||||||
}
|
}
|
||||||
|
.legend-dot {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.legend-item {
|
||||||
|
transition: background-color 0.15s;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user