From 1f3bb2b33cbb7ffb3e1de2e7ae83e2d1a435a943 Mon Sep 17 00:00:00 2001 From: nick-delirium Date: Mon, 6 Jan 2025 17:13:20 +0100 Subject: [PATCH] ui: use unique id for window values --- frontend/app/components/Charts/BarChart.tsx | 25 +++++----- frontend/app/components/Charts/LineChart.tsx | 31 +++++------- frontend/app/components/Charts/init.ts | 20 ++++++++ frontend/app/components/Charts/utils.ts | 50 ++++++++++---------- 4 files changed, 70 insertions(+), 56 deletions(-) diff --git a/frontend/app/components/Charts/BarChart.tsx b/frontend/app/components/Charts/BarChart.tsx index e5b98280a..5f117bdfb 100644 --- a/frontend/app/components/Charts/BarChart.tsx +++ b/frontend/app/components/Charts/BarChart.tsx @@ -5,7 +5,7 @@ import { customTooltipFormatter } from './utils'; import { buildBarDatasetsAndSeries } from './barUtils'; -import { defaultOptions, echarts } from './init'; +import { defaultOptions, echarts, initWindowStorages } from "./init"; import { BarChart } from 'echarts/charts'; echarts.use([BarChart]); @@ -16,6 +16,7 @@ interface BarChartProps extends DataProps { } function ORBarChart(props: BarChartProps) { + const chartUuid = React.useRef(Math.random().toString(36).substring(7)); const chartRef = React.useRef(null); React.useEffect(() => { @@ -24,23 +25,19 @@ function ORBarChart(props: BarChartProps) { const categories = buildCategories(props.data); const { datasets, series } = buildBarDatasetsAndSeries(props, props.horizontal ?? false); - (window as any).__seriesValueMap = {}; - (window as any).__seriesColorMap = {}; - (window as any).__timestampMap = props.data.chart.map((item) => item.timestamp); - (window as any).__categoryMap = categories; - + initWindowStorages(chartUuid.current, categories, props.data.chart); series.forEach((s: any) => { - (window as any).__seriesColorMap[s.name] = s.itemStyle?.color ?? '#999'; + (window as any).__seriesColorMap[chartUuid.current][s.name] = s.itemStyle?.color ?? '#999'; const ds = datasets.find((d) => d.id === s.datasetId); if (!ds) return; const yDim = props.horizontal ? s.encode.x : s.encode.y; const yDimIndex = ds.dimensions.indexOf(yDim); if (yDimIndex < 0) return; - (window as any).__seriesValueMap[s.name] = {}; + (window as any).__seriesValueMap[chartUuid.current][s.name] = {}; ds.source.forEach((row: any[]) => { const rowIdx = row[0]; // 'idx' - (window as any).__seriesValueMap[s.name][rowIdx] = row[yDimIndex]; + (window as any).__seriesValueMap[chartUuid.current][s.name][rowIdx] = row[yDimIndex]; }); }); @@ -65,7 +62,7 @@ function ORBarChart(props: BarChartProps) { }, tooltip: { ...defaultOptions.tooltip, - formatter: customTooltipFormatter, + formatter: customTooltipFormatter(chartUuid.current), }, xAxis, yAxis, @@ -75,10 +72,10 @@ function ORBarChart(props: BarChartProps) { return () => { chart.dispose(); - delete (window as any).__seriesValueMap; - delete (window as any).__seriesColorMap; - delete (window as any).__categoryMap; - delete (window as any).__timestampMap; + delete (window as any).__seriesValueMap[chartUuid.current]; + delete (window as any).__seriesColorMap[chartUuid.current]; + delete (window as any).__categoryMap[chartUuid.current]; + delete (window as any).__timestampMap[chartUuid.current]; }; }, [props.data, props.compData, props.horizontal]); diff --git a/frontend/app/components/Charts/LineChart.tsx b/frontend/app/components/Charts/LineChart.tsx index a90706d14..5aca0bb3c 100644 --- a/frontend/app/components/Charts/LineChart.tsx +++ b/frontend/app/components/Charts/LineChart.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { echarts, defaultOptions } from './init'; +import { echarts, defaultOptions, initWindowStorages } from './init'; import { customTooltipFormatter, buildCategories, buildDatasetsAndSeries } from './utils' import type { DataProps } from './utils' import { LineChart } from 'echarts/charts'; @@ -15,6 +15,7 @@ interface Props extends DataProps { } function ORLineChart(props: Props) { + const chartUuid = React.useRef(Math.random().toString(36).substring(7)); const chartRef = React.useRef(null); React.useEffect(() => { @@ -24,22 +25,16 @@ function ORLineChart(props: Props) { const categories = buildCategories(props.data); const { datasets, series } = buildDatasetsAndSeries(props); - // Create a quick map of name => dataIndex => value, for partner lookups - // and a map for colors. We'll store them on window in this example for brevity. - (window as any).__seriesValueMap = {}; - (window as any).__seriesColorMap = {}; - (window as any).__timestampMap = props.data.chart.map(item => item.timestamp); - (window as any).__categoryMap = categories; + initWindowStorages(chartUuid.current, categories, props.data.chart); series.forEach((s: any) => { if (props.isArea) { s.areaStyle = {}; - s.stack = 'Total' - // s.emphasis = { focus: 'series' }; + s.stack = 'Total'; } else { s.areaStyle = null; } - (window as any).__seriesColorMap[s.name] = s.itemStyle?.color ?? '#999'; + (window as any).__seriesColorMap[chartUuid.current][s.name] = s.itemStyle?.color ?? '#999'; const datasetId = s.datasetId || 'current'; const ds = datasets.find((d) => d.id === datasetId); if (!ds) return; @@ -47,10 +42,10 @@ function ORLineChart(props: Props) { const yDimIndex = ds.dimensions.indexOf(yDim); if (yDimIndex < 0) return; - (window as any).__seriesValueMap[s.name] = {}; + (window as any).__seriesValueMap[chartUuid.current][s.name] = {}; ds.source.forEach((row: any[]) => { const rowIdx = row[0]; - (window as any).__seriesValueMap[s.name][rowIdx] = row[yDimIndex]; + (window as any).__seriesValueMap[chartUuid.current][s.name][rowIdx] = row[yDimIndex]; }); }); @@ -77,23 +72,23 @@ function ORLineChart(props: Props) { }, tooltip: { ...defaultOptions.tooltip, - formatter: customTooltipFormatter, + formatter: customTooltipFormatter(chartUuid.current), }, dataset: datasets, series, }); chart.on('click', (event) => { const index = event.dataIndex; - const timestamp = (window as any).__timestampMap?.[index]; + const timestamp = (window as any).__timestampMap?.[chartUuid.current]?.[index]; props.onClick?.({ activePayload: [{ payload: { timestamp }}]}) }) return () => { chart.dispose(); - delete (window as any).__seriesValueMap; - delete (window as any).__seriesColorMap; - delete (window as any).__categoryMap; - delete (window as any).__timestampMap; + delete (window as any).__seriesValueMap[chartUuid.current]; + delete (window as any).__seriesColorMap[chartUuid.current]; + delete (window as any).__categoryMap[chartUuid.current]; + delete (window as any).__timestampMap[chartUuid.current]; }; }, [props.data, props.compData]); diff --git a/frontend/app/components/Charts/init.ts b/frontend/app/components/Charts/init.ts index 37549c08c..6c2783b0b 100644 --- a/frontend/app/components/Charts/init.ts +++ b/frontend/app/components/Charts/init.ts @@ -66,4 +66,24 @@ const defaultOptions = { }, } +export function initWindowStorages(chartUuid: string, categories: string[] = [], chartArr: any[] = []) { + (window as any).__seriesValueMap = (window as any).__seriesValueMap ?? {}; + (window as any).__seriesColorMap = (window as any).__seriesColorMap ?? {}; + (window as any).__timestampMap = (window as any).__timestampMap ?? {}; + (window as any).__categoryMap = (window as any).__categoryMap ?? {}; + + if (!(window as any).__seriesColorMap[chartUuid]) { + (window as any).__seriesColorMap[chartUuid] = {}; + } + if (!(window as any).__seriesValueMap[chartUuid]) { + (window as any).__seriesValueMap[chartUuid] = {}; + } + if (!(window as any).__categoryMap[chartUuid]) { + (window as any).__categoryMap[chartUuid] = categories; + } + if (!(window as any).__timestampMap[chartUuid]) { + (window as any).__timestampMap[chartUuid] = chartArr.map((item) => item.timestamp); + } +} + export { echarts, defaultOptions }; \ No newline at end of file diff --git a/frontend/app/components/Charts/utils.ts b/frontend/app/components/Charts/utils.ts index e69376335..dfec64f92 100644 --- a/frontend/app/components/Charts/utils.ts +++ b/frontend/app/components/Charts/utils.ts @@ -42,28 +42,29 @@ export function assignColorsByBaseName(series: any[]) { /** * Show the hovered “current” or “previous” line + the matching partner (if it exists). */ -export function customTooltipFormatter(params: any): string { - // With trigger='item', params is a single object describing the hovered point - // { seriesName, dataIndex, data, marker, color, encode, ... } - if (!params) return ''; - const { seriesName, dataIndex } = params; +export function customTooltipFormatter(uuid: string) { + return (params: any): string => { + // With trigger='item', params is a single object describing the hovered point + // { seriesName, dataIndex, data, marker, color, encode, ... } + if (!params) return ''; + const { seriesName, dataIndex } = params; - // 'value' of the hovered point - const yKey = params.encode.y[0]; // "Series 1" - const value = params.data?.[yKey]; + // 'value' of the hovered point + const yKey = params.encode.y[0]; // "Series 1" + const value = params.data?.[yKey]; - const isPrevious = /^Previous\s+/.test(seriesName); - const baseName = seriesName.replace(/^Previous\s+/, ''); - const partnerName = isPrevious ? baseName : `Previous ${baseName}`; + const isPrevious = /^Previous\s+/.test(seriesName); + const baseName = seriesName.replace(/^Previous\s+/, ''); + const partnerName = isPrevious ? baseName : `Previous ${baseName}`; - // Get partner’s value from some global map - const partnerVal = (window as any).__seriesValueMap?.[partnerName]?.[dataIndex]; - const timestamp = (window as any).__timestampMap?.[dataIndex]; - const categoryLabel = (window as any).__categoryMap - ? (window as any).__categoryMap[dataIndex] - : dataIndex; + // Get partner’s value from some global map + const partnerVal = (window as any).__seriesValueMap?.[uuid]?.[partnerName]?.[dataIndex]; + const timestamp = (window as any).__timestampMap?.[uuid]?.[dataIndex]; + const categoryLabel = (window as any).__categoryMap[uuid] + ? (window as any).__categoryMap[uuid][dataIndex] + : dataIndex; - let tooltipContent = ` + let tooltipContent = `