11<script setup lang="ts">
22import { onClickOutside } from " @vueuse/core" ;
33import type { EChartsOption } from " echarts" ;
4+ import type { ECharts } from " echarts/core" ;
45import { computed , nextTick , onBeforeUnmount , ref , watch } from " vue" ;
56import VChart from " vue-echarts" ;
67
78import type { DataVarId , HourlySeries } from " @/composables/hourlySeries" ;
89import { useUnits } from " @/composables/useUnits" ;
910import { MODELS , type ModelDef } from " @/domain/models" ;
1011
11- import { CHART_VIEWS , convertVar , type ChartViewId , type UnitPrefs } from " ./chartHelpers" ;
12+ import { CHART_VIEWS , convertVar , isVarActive as isVarActiveFor , nextCombinableView , type ChartViewId , type UnitPrefs } from " ./chartHelpers" ;
1213import { AGG_COLOR , BAND_SWATCH , buildHourlyChartOption , paletteFor , TRUTH_COLOR , visibilityPatches } from " ./chartOption" ;
1314
1415const props = withDefaults (
@@ -64,8 +65,8 @@ const showTruth = ref(true);
6465const enabledModels = ref <Set <string >>(new Set ());
6566
6667// Direct handle to the ECharts instance for no-redraw merge patches.
67- // eslint-disable-next-line @typescript-eslint/no-explicit-any
68- const chartRef = ref <any >(null );
68+ // vue-echarts exposes the underlying instance as `.chart` on its component ref.
69+ const chartRef = ref <{ chart ? : ECharts } | null >(null );
6970
7071const hasTruth = computed (() => !! props .data .truth );
7172
@@ -94,36 +95,15 @@ onClickOutside(varRoot, () => (varOpen.value = false));
9495/** Whether a picker entry reads as "active". Temperature and precipitation are
9596 * a combinable pair on the forecast page: either is active in the composite. */
9697function isVarActive(vid : ChartViewId ): boolean {
97- if (canCombineTempPrecip ) {
98- if (vid === " temperature_2m" ) return view .value === " temp_precip" || view .value === " temperature_2m" ;
99- if (vid === " precipitation" ) return view .value === " temp_precip" || view .value === " precipitation" ;
100- }
101- return view .value === vid ;
98+ return isVarActiveFor (view .value , vid , canCombineTempPrecip );
10299}
103100
104101function selectVariable(vid : ChartViewId ): void {
105- // Temperature & precipitation toggle independently (dual-axis), so the two
106- // can be shown together — that combination *is* the composite view. The
107- // remaining variables are exclusive single-axis views .
102+ // Temperature & precipitation toggle independently (dual-axis) into the
103+ // composite; every other variable is an exclusive single-axis view. The set
104+ // arithmetic for the combinable pair lives in chartHelpers.nextCombinableView .
108105 if (canCombineTempPrecip && (vid === " temperature_2m" || vid === " precipitation" )) {
109- const inPair = view .value === " temp_precip" || view .value === " temperature_2m" || view .value === " precipitation" ;
110- let tempOn = view .value === " temp_precip" || view .value === " temperature_2m" ;
111- let precipOn = view .value === " temp_precip" || view .value === " precipitation" ;
112- if (! inPair ) {
113- // Coming from an exclusive view (wind / cloud / prob) → focus the click.
114- tempOn = vid === " temperature_2m" ;
115- precipOn = vid === " precipitation" ;
116- } else if (vid === " temperature_2m" ) {
117- tempOn = ! tempOn ;
118- } else {
119- precipOn = ! precipOn ;
120- }
121- // Never leave the pair empty: toggling off the last one is a no-op.
122- if (! tempOn && ! precipOn ) {
123- tempOn = vid === " temperature_2m" ;
124- precipOn = vid === " precipitation" ;
125- }
126- selectView (tempOn && precipOn ? " temp_precip" : tempOn ? " temperature_2m" : " precipitation" );
106+ selectView (nextCombinableView (view .value , vid ));
127107 } else {
128108 selectView (vid );
129109 }
@@ -180,7 +160,6 @@ watch(
180160// area-fill) live in the builder's `toggles`; visibilityPatches() maps those +
181161// the current toggle state to the patches.
182162function applyVisibility(): void {
183- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
184163 const chart = chartRef .value ?.chart ;
185164 if (! chart ) return ;
186165 const patches = visibilityPatches (toggles .value , {
@@ -189,7 +168,6 @@ function applyVisibility(): void {
189168 showTruth: showTruth .value ,
190169 enabledModels: enabledModels .value ,
191170 });
192- // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
193171 if (patches .length ) chart .setOption ({ series: patches }, false );
194172}
195173
@@ -244,35 +222,27 @@ const spaghettiAxis = computed(() => (activeVar.value === "precipitation" ? 1 :
244222
245223let detachCursor: (() => void ) | null = null ;
246224watch (
247- // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
248225 () => chartRef .value ?.chart ,
249226 (chart ) => {
250227 detachCursor ?.();
251228 detachCursor = null ;
252229 if (! chart ) return ;
253- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
254230 const zr = chart .getZr ();
255231 const onMove = (e : { offsetX: number ; offsetY: number }): void => {
256- // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
257232 if (! chart .containPixel (" grid" , [e .offsetX , e .offsetY ])) {
258233 cursorValue .value = null ;
259234 return ;
260235 }
261- // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
262- const v = chart .convertFromPixel ({ yAxisIndex: spaghettiAxis .value }, e .offsetY ) as unknown ;
236+ const v = chart .convertFromPixel ({ yAxisIndex: spaghettiAxis .value }, e .offsetY );
263237 cursorValue .value = typeof v === " number" ? v : null ;
264238 };
265239 const onOut = (): void => {
266240 cursorValue .value = null ;
267241 };
268- // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
269242 zr .on (" mousemove" , onMove );
270- // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
271243 zr .on (" globalout" , onOut );
272244 detachCursor = (): void => {
273- // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
274245 zr .off (" mousemove" , onMove );
275- // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
276246 zr .off (" globalout" , onOut );
277247 };
278248 },
0 commit comments