now improvements
This commit is contained in:
@@ -262,7 +262,7 @@
|
||||
</span>
|
||||
|
||||
<span
|
||||
class="rel-label overflow-hidden leading-[1.25] whitespace-nowrap {isToday
|
||||
class="rel-label leading-[1.25] whitespace-nowrap {isToday
|
||||
? 'font-semibold text-primary/80'
|
||||
: 'text-muted-foreground'}"
|
||||
>
|
||||
|
||||
@@ -328,6 +328,25 @@
|
||||
: null
|
||||
);
|
||||
|
||||
const NOW_LINE_W = 2;
|
||||
|
||||
// The line is painted as a background on the one cell it crosses (see the
|
||||
// .now-cell rule) rather than as an overlay on top of the table, so it sits
|
||||
// above each cell's colour tint but below its value and icon.
|
||||
let nowCell = $derived.by((): { idx: number; offset: number } | null => {
|
||||
if (nowLeftPx == null || cellData.length === 0) return null;
|
||||
const colWidth = (tableWidth - headerColWidth) / cellData.length;
|
||||
if (colWidth <= NOW_LINE_W) return null;
|
||||
const col = Math.floor((nowLeftPx - headerColWidth) / colWidth);
|
||||
if (col < 0 || col >= cellData.length) return null;
|
||||
// keep the full line width inside the cell, which clips its overflow
|
||||
const offset = nowLeftPx - headerColWidth - col * colWidth - NOW_LINE_W / 2;
|
||||
return {
|
||||
idx: cellData[col].idx,
|
||||
offset: Math.max(0, Math.min(colWidth - NOW_LINE_W, offset))
|
||||
};
|
||||
});
|
||||
|
||||
// ─── Auto-scroll the (overflowing) table on day / interval change ────────────
|
||||
// • Day change → today lands on the current-hour cell, other days on 06:00.
|
||||
// • Interval change (3h ↔ 1h) → keep whatever cell is currently on the left.
|
||||
@@ -501,6 +520,7 @@
|
||||
class="relative {is3h
|
||||
? 'min-w-[400px] md:min-w-[560px]'
|
||||
: 'min-w-[780px] md:min-w-[1100px]'}"
|
||||
style="--now-x:{nowCell?.offset ?? 0}px"
|
||||
bind:clientWidth={tableWidth}
|
||||
>
|
||||
<table class="w-full table-fixed border-collapse whitespace-nowrap">
|
||||
@@ -582,6 +602,14 @@
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Current-time line across the time row. Placed before the hour
|
||||
labels so it paints under them, like it does in the rows below. -->
|
||||
{#if nowPercent != null}
|
||||
<div
|
||||
class="pointer-events-none absolute inset-y-0 w-0.5 -translate-x-1/2 bg-red-500/75"
|
||||
style="left:{nowPercent}%"
|
||||
></div>
|
||||
{/if}
|
||||
<!-- "Now" label, aligned with the sunrise/sunset labels along the bottom -->
|
||||
{#if isTodaySelected && nowPercent != null}
|
||||
<span
|
||||
@@ -597,6 +625,7 @@
|
||||
{@const widthPct = 100 / cellData.length}
|
||||
<span
|
||||
class:col-empty={!cell.hasData}
|
||||
class:now-label={cell.idx === nowCell?.idx}
|
||||
class="absolute top-0 flex items-start pl-1 font-bold {is3h
|
||||
? 'pt-2 text-sm'
|
||||
: 'pt-2.5'}
|
||||
@@ -638,6 +667,7 @@
|
||||
{@const wCode = hourly.weather_code[cell.idx]}
|
||||
<td
|
||||
class:col-empty={!cell.hasData}
|
||||
class:now-cell={cell.idx === nowCell?.idx}
|
||||
class="cell h-12 leading-0"
|
||||
class:cell-empty={!finite(wCode)}
|
||||
class:icon-day={cell.isDaytime}
|
||||
@@ -662,8 +692,11 @@
|
||||
{@const style = getTempStyle(temp, String(units.temperature_unit))}
|
||||
<td
|
||||
class:col-empty={!cell.hasData}
|
||||
class:now-cell={cell.idx === nowCell?.idx}
|
||||
class="cell h-12 font-bold {is3h ? 'text-lg' : 'text-[15px]'}"
|
||||
style={finite(temp) ? `background-color:${style.bg};color:${style.fg}` : ''}
|
||||
style={finite(temp)
|
||||
? `background-color:${style.bg};color:${style.fg};--now-halo:${style.bg}`
|
||||
: ''}
|
||||
>
|
||||
{formatTemp(temp)}
|
||||
</td>
|
||||
@@ -677,6 +710,7 @@
|
||||
{@const temp = hourly.apparent_temperature[cell.idx]}
|
||||
<td
|
||||
class:col-empty={!cell.hasData}
|
||||
class:now-cell={cell.idx === nowCell?.idx}
|
||||
class="cell h-12 text-muted-foreground {is3h ? 'text-[13px]' : 'text-[11px]'}"
|
||||
>
|
||||
{formatTemp(temp)}
|
||||
@@ -691,6 +725,7 @@
|
||||
{@const temp = hourly.dew_point_2m?.[cell.idx]}
|
||||
<td
|
||||
class:col-empty={!cell.hasData}
|
||||
class:now-cell={cell.idx === nowCell?.idx}
|
||||
class="cell h-12 text-muted-foreground {is3h ? 'text-[13px]' : 'text-[11px]'}"
|
||||
>
|
||||
{formatTemp(temp)}
|
||||
@@ -706,6 +741,7 @@
|
||||
{@const windDir = hourly.winddirection_10m[cell.idx]}
|
||||
<td
|
||||
class:col-empty={!cell.hasData}
|
||||
class:now-cell={cell.idx === nowCell?.idx}
|
||||
class="relative cell h-12 align-middle leading-none"
|
||||
>
|
||||
{#if windDir != null && !isNaN(windDir)}
|
||||
@@ -730,6 +766,7 @@
|
||||
{@const v = hourly.wind_gusts_10m?.[cell.idx]}
|
||||
<td
|
||||
class:col-empty={!cell.hasData}
|
||||
class:now-cell={cell.idx === nowCell?.idx}
|
||||
class="cell h-12 font-semibold text-foreground/80 {is3h
|
||||
? 'text-sm'
|
||||
: 'text-xs'}"
|
||||
@@ -746,6 +783,7 @@
|
||||
{@const hum = hourly.relative_humidity_2m[cell.idx]}
|
||||
<td
|
||||
class:col-empty={!cell.hasData}
|
||||
class:now-cell={cell.idx === nowCell?.idx}
|
||||
class="cell h-12"
|
||||
style={finite(hum) ? `background:${getHumidityBg(hum)}` : ''}
|
||||
>
|
||||
@@ -761,6 +799,7 @@
|
||||
{@const cloud = hourly.cloud_cover[cell.idx]}
|
||||
<td
|
||||
class:col-empty={!cell.hasData}
|
||||
class:now-cell={cell.idx === nowCell?.idx}
|
||||
class="cell h-12"
|
||||
style={finite(cloud)
|
||||
? `background:rgba(140,160,180,${getCloudOpacity(cloud)})`
|
||||
@@ -778,6 +817,7 @@
|
||||
{@const v = hourly.pressure_msl?.[cell.idx]}
|
||||
<td
|
||||
class:col-empty={!cell.hasData}
|
||||
class:now-cell={cell.idx === nowCell?.idx}
|
||||
class="cell h-12 {is3h ? 'text-sm' : 'text-xs'}"
|
||||
style="background:{getPressureBg(v ?? null)}"
|
||||
>
|
||||
@@ -793,6 +833,7 @@
|
||||
{@const v = hourly.uv_index?.[cell.idx]}
|
||||
<td
|
||||
class:col-empty={!cell.hasData}
|
||||
class:now-cell={cell.idx === nowCell?.idx}
|
||||
class="cell h-12 font-semibold {is3h ? 'text-sm' : 'text-xs'}"
|
||||
style="background:{getUvBg(v ?? null)}"
|
||||
>
|
||||
@@ -808,6 +849,7 @@
|
||||
{@const v = hourly.visibility?.[cell.idx]}
|
||||
<td
|
||||
class:col-empty={!cell.hasData}
|
||||
class:now-cell={cell.idx === nowCell?.idx}
|
||||
class="cell h-12 {is3h ? 'text-sm' : 'text-xs'}"
|
||||
>
|
||||
{v != null && !isNaN(v) ? (v / 1000).toFixed(0) : '-'}
|
||||
@@ -823,6 +865,7 @@
|
||||
{@const prob = hourly.precipitation_probability[cell.idx]}
|
||||
<td
|
||||
class:col-empty={!cell.hasData}
|
||||
class:now-cell={cell.idx === nowCell?.idx}
|
||||
class="cell precip-cell h-12"
|
||||
style="background:{getPrecipProbBg(prob ?? 0)}"
|
||||
title={formatPrecipTooltip(precip, prob)}
|
||||
@@ -844,6 +887,7 @@
|
||||
{@const v = hourly.snowfall?.[cell.idx]}
|
||||
<td
|
||||
class:col-empty={!cell.hasData}
|
||||
class:now-cell={cell.idx === nowCell?.idx}
|
||||
class="cell h-12 {is3h ? 'text-sm' : 'text-xs'}"
|
||||
>
|
||||
{v != null && !isNaN(v) && v > 0 ? v.toFixed(1) : '-'}
|
||||
@@ -864,7 +908,8 @@
|
||||
></div>
|
||||
{/if}
|
||||
|
||||
<!-- "Now" column highlight + exact-time line -->
|
||||
<!-- "Now" column highlight. The current-time line itself is painted per
|
||||
cell (.now-cell) so it stays under the values and icons. -->
|
||||
{#if nowLeftPx != null}
|
||||
{@const colWidth = (tableWidth - headerColWidth) / cellData.length}
|
||||
{@const nowIdx = cellData.findIndex((c) => c.isNow)}
|
||||
@@ -874,11 +919,6 @@
|
||||
style="left:{headerColWidth + nowIdx * colWidth}px;width:{colWidth}px"
|
||||
></div>
|
||||
{/if}
|
||||
<!-- full-height current-time line (its "Now" label lives in the time row) -->
|
||||
<div
|
||||
class="pointer-events-none absolute inset-y-0 z-10 w-0.5 -translate-x-1/2 bg-red-500/75"
|
||||
style="left:{nowLeftPx}px"
|
||||
></div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
@@ -933,6 +973,59 @@
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
/* ── Current-time line ──────────────────────────────────────────
|
||||
Values sit on top of the line, so they get a soft halo in the colour of
|
||||
whatever is behind them: the card by default, or the cell's own colour on
|
||||
rows that paint one (set inline as --now-halo). Just enough to keep the
|
||||
line from cutting through a glyph. */
|
||||
.now-cell,
|
||||
.now-label {
|
||||
--now-halo: var(--color-card);
|
||||
text-shadow:
|
||||
0 0 2px var(--now-halo),
|
||||
0 0 4px var(--now-halo);
|
||||
}
|
||||
|
||||
/* Same idea for the weather and wind-arrow icons, which text-shadow can't
|
||||
reach. */
|
||||
.now-cell svg {
|
||||
filter: drop-shadow(0 0 2px var(--now-halo)) drop-shadow(0 0 3px var(--now-halo));
|
||||
}
|
||||
|
||||
/* The line itself: a background layer on the single cell it crosses (--now-x
|
||||
is its offset within that cell). A background paints above the cell's own
|
||||
colour tint but below its text and icons, which an overlay element on top
|
||||
of the table can't do. !important because several rows set their tint with
|
||||
the `background` shorthand inline, which would otherwise reset this image. */
|
||||
.now-cell {
|
||||
--now-line-color: color-mix(in oklab, var(--color-red-500) 75%, transparent);
|
||||
--now-line: linear-gradient(
|
||||
to right,
|
||||
transparent var(--now-x),
|
||||
var(--now-line-color) var(--now-x),
|
||||
var(--now-line-color) calc(var(--now-x) + 2px),
|
||||
transparent calc(var(--now-x) + 2px)
|
||||
);
|
||||
background-image: var(--now-line) !important;
|
||||
background-repeat: no-repeat !important;
|
||||
}
|
||||
|
||||
/* The row separator is painted over cell backgrounds, so it would chop the
|
||||
line into per-row segments. On these cells it is switched off (a cell
|
||||
border wins the collapse against the row's, same width and style) and
|
||||
redrawn as a background layer below the line instead. */
|
||||
.row + .row .now-cell {
|
||||
border-top: 1px solid transparent;
|
||||
background-image:
|
||||
var(--now-line),
|
||||
linear-gradient(
|
||||
to bottom,
|
||||
color-mix(in oklab, var(--color-border) 70%, transparent) 1px,
|
||||
transparent 1px
|
||||
) !important;
|
||||
background-repeat: no-repeat, no-repeat !important;
|
||||
}
|
||||
|
||||
/* A whole column the model never reached - dimmed end to end, hour label
|
||||
included, so the point where the forecast runs out is obvious. */
|
||||
.col-empty {
|
||||
|
||||
Reference in New Issue
Block a user