feat: week view overhaul (#8)

This commit was merged in pull request #8.
This commit is contained in:
2026-07-25 09:53:40 +02:00
parent bac4759662
commit 19686ee107
14 changed files with 753 additions and 185 deletions
+44 -11
View File
@@ -45,6 +45,10 @@ export interface ChartVariableDef {
extrema?: boolean;
/** Draw weather-code pictograms across the top of the chart */
pictograms?: boolean;
/** Draw wind-direction arrows across the top of the chart */
windArrows?: boolean;
/** Marker-only variable (icons / arrows): contributes no plotted series */
marker?: boolean;
/** Render as a puffy cloud band hanging from the top (0-100 → 0-40px) */
cloudBand?: boolean;
/** Transform raw values before plotting (e.g. m → km) */
@@ -62,13 +66,21 @@ export const CHART_VARIABLES: ChartVariableDef[] = [
type: 'line',
kind: 'temp',
color: '#ef6c00',
width: 4,
fill: true,
fillOpacity: 0.12,
width: 9,
colorScale: true,
outline: true,
extrema: true,
pictograms: true
extrema: true
},
{
key: 'weather_icons',
label: 'Weather icons',
short: 'Icons',
field: 'weather_code',
type: 'line',
kind: 'temp',
color: '#94a3b8',
pictograms: true,
marker: true
},
{
key: 'apparent_temperature',
@@ -189,7 +201,8 @@ export const CHART_VARIABLES: ChartVariableDef[] = [
color: '#26a69a',
width: 2,
fill: true,
fillOpacity: 0.15
fillOpacity: 0.15,
windArrows: true
},
{
key: 'wind_gusts',
@@ -347,7 +360,7 @@ export function isZeroBased(kind: UnitKind): boolean {
return kind !== 'temp' && kind !== 'pressure';
}
/** Sensible decimal places for tooltip / label formatting. */
/** Decimal places for on-chart extrema labels (kept coarse, like the cards). */
export function decimalsForKind(kind: UnitKind): number {
switch (kind) {
case 'precip':
@@ -360,6 +373,20 @@ export function decimalsForKind(kind: UnitKind): number {
}
}
/**
* Decimal places for the hover tooltip — finer than the cards / extrema labels
* so the meteogram reveals more detail. Percentages stay whole numbers.
*/
export function tooltipDecimalsForKind(kind: UnitKind): number {
switch (kind) {
case 'percent':
case 'energy':
return 0;
default:
return 1;
}
}
export interface PanelDef {
series: ChartSeries[];
unit: string;
@@ -370,6 +397,7 @@ export interface PanelDef {
/** Whether the left axis should include zero (false for pressure) */
zeroBaseLeft: boolean;
hasPictograms: boolean;
hasWindArrows: boolean;
}
/**
@@ -382,10 +410,13 @@ export function buildPanelDef(
hourly: WeekHourlyData,
units: WeatherUnits
): PanelDef {
const defs = variableKeys
const allDefs = variableKeys
.map((k) => VARIABLE_BY_KEY.get(k))
.filter((d): d is ChartVariableDef => d != null);
// Marker-only variables (weather icons) render no series, just a top row.
const defs = allDefs.filter((d) => !d.marker);
// Cloud-band variables float above the plot and don't claim an axis.
const axisDefs = defs.filter((d) => !d.cloudBand);
const kinds: UnitKind[] = [];
@@ -400,6 +431,7 @@ export function buildPanelDef(
: raw;
const kindUnit = unitForKind(d.kind, units);
const dec = decimalsForKind(d.kind);
const tipDec = tooltipDecimalsForKind(d.kind);
const axis: 'left' | 'right' = d.cloudBand || d.kind === leftKind ? 'left' : 'right';
return {
@@ -425,9 +457,9 @@ export function buildPanelDef(
? (v: number, i: number) => {
const dir = hourly.winddirection_10m?.[i];
const dl = dir != null && !isNaN(dir) ? ` (${getWindDirectionLabel(dir)})` : '';
return `${v.toFixed(dec)} ${kindUnit}${dl}`;
return `${v.toFixed(tipDec)} ${kindUnit}${dl}`;
}
: (v: number) => `${v.toFixed(dec)}${kindUnit ? ' ' + kindUnit : ''}`
: (v: number) => `${v.toFixed(tipDec)}${kindUnit ? ' ' + kindUnit : ''}`
} satisfies ChartSeries;
});
@@ -441,6 +473,7 @@ export function buildPanelDef(
zeroBaseLeft: leftKind !== 'pressure',
yMinRight: rightKind && rightZero ? 0 : undefined,
yMaxRight: rightKind === 'percent' ? 100 : undefined,
hasPictograms: defs.some((d) => d.pictograms)
hasPictograms: allDefs.some((d) => d.pictograms),
hasWindArrows: allDefs.some((d) => d.windArrows)
};
}