2.2
Add new quick summary panels and update layout to make space for them
This commit is contained in:
+110
-56
@@ -33,6 +33,7 @@ import { getCellTagEvents, getUpcomingEvents } from './events.js';
|
||||
import { POLLEN_TYPES, COL_DESCRIPTIONS, UTCI_ENVIRONMENTS } from './config.js';
|
||||
import { useAppState } from './hooks/useAppState.js';
|
||||
import { DayTabs } from './components/DayTabs.js';
|
||||
import { computeWhyFeelsLike, computeGlanceSummary } from './compute.js';
|
||||
|
||||
const html = htm.bind(h);
|
||||
|
||||
@@ -166,6 +167,16 @@ export function UTCIForecast() {
|
||||
return raw.map(c => Math.min(255, Math.round(c + (255 - c) * 0.66)));
|
||||
};
|
||||
|
||||
// ─── 3b. PANEL COMPUTATIONS ──────────────────────────────────────────
|
||||
// Derived from currentRow and the active day - no side effects.
|
||||
const whyFeelsLike = computeWhyFeelsLike(currentRow ?? null, UTCI_ENVIRONMENTS[utciEnv]);
|
||||
const glanceSummary = computeGlanceSummary(
|
||||
days[selectedDay]?.rows ?? [],
|
||||
activeProfile,
|
||||
outdoorsVariant,
|
||||
skinType,
|
||||
);
|
||||
|
||||
// ─── 4. JSX RETURN ───────────────────────────────────────────────────
|
||||
// Everything below is the actual page markup, written as one big HTM
|
||||
// template. Search tips:
|
||||
@@ -272,43 +283,13 @@ export function UTCIForecast() {
|
||||
</div>
|
||||
|
||||
<div class="utci-header">
|
||||
<div>
|
||||
<div class="header-left">
|
||||
<h1 class="utci-title">
|
||||
<span class="title-sun">SUN</span><span class="title-scope">Scope</span>
|
||||
</h1>
|
||||
<div class="utci-tagline">See the sun the way your skin does.</div>
|
||||
</div>
|
||||
|
||||
<div class="scope-col">
|
||||
<${ScopeReticle}
|
||||
value=${currentRow?.utciAdj ?? null}
|
||||
cat=${currentCat}
|
||||
loading=${loading}
|
||||
elev=${currentRow?.elev ?? 0}
|
||||
dt=${currentRow?.dt ?? new Date()}
|
||||
glob=${currentRow?.glob ?? 0}
|
||||
activeEvent=${lensEvent}
|
||||
utciEnvShort=${UTCI_ENVIRONMENTS[utciEnv]?.shortLabel ?? null}
|
||||
/>
|
||||
<div class="scope-env-row" data-env=${utciEnv}>
|
||||
<span class="scope-env-label">Environment</span>
|
||||
<${CustomSelect}
|
||||
value=${utciEnv}
|
||||
isOn=${true}
|
||||
noHide=${true}
|
||||
grpClass="grp-felt"
|
||||
options=${Object.entries(UTCI_ENVIRONMENTS).map(([k, v]) => ({
|
||||
value: k,
|
||||
label: v.label,
|
||||
}))}
|
||||
onChange=${(v) => setUtciEnv(v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="header-right">
|
||||
<div class="utci-search-wrap">
|
||||
<input
|
||||
<input
|
||||
class="utci-search"
|
||||
type="text"
|
||||
placeholder="Search any town or city…"
|
||||
@@ -350,7 +331,63 @@ export function UTCIForecast() {
|
||||
${fetchedAt && !loading && html`
|
||||
<div class="utci-fetch-time">Last update: ${fetchedAt.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}</div>`}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="scope-col">
|
||||
<${ScopeReticle}
|
||||
value=${currentRow?.utciAdj ?? null}
|
||||
cat=${currentCat}
|
||||
loading=${loading}
|
||||
elev=${currentRow?.elev ?? 0}
|
||||
dt=${currentRow?.dt ?? new Date()}
|
||||
glob=${currentRow?.glob ?? 0}
|
||||
activeEvent=${lensEvent}
|
||||
utciEnvShort=${UTCI_ENVIRONMENTS[utciEnv]?.shortLabel ?? null}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="scope-env-bar">
|
||||
<div class="scope-env-row" data-env=${utciEnv}>
|
||||
<span class="scope-env-label">Environment</span>
|
||||
<${CustomSelect}
|
||||
value=${utciEnv}
|
||||
isOn=${true}
|
||||
noHide=${true}
|
||||
grpClass="grp-felt"
|
||||
options=${Object.entries(UTCI_ENVIRONMENTS).map(([k, v]) => ({
|
||||
value: k,
|
||||
label: v.label,
|
||||
}))}
|
||||
onChange=${(v) => setUtciEnv(v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="header-right">
|
||||
${forecast && currentRow && whyFeelsLike && html`
|
||||
<div class="insight-panel insight-panel--why">
|
||||
<div class="insight-panel-title">Why it feels like this</div>
|
||||
${[
|
||||
{ label: 'Sun and sky', icon: '☀', value: whyFeelsLike.sunAndSky },
|
||||
{ label: 'Wind', icon: '🌬', value: whyFeelsLike.wind },
|
||||
{ label: 'Humidity', icon: '💧', value: whyFeelsLike.humidity },
|
||||
...(whyFeelsLike.environment !== 0 ? [
|
||||
{ label: UTCI_ENVIRONMENTS[utciEnv]?.label ?? 'Environment', icon: '🌍', value: whyFeelsLike.environment }
|
||||
] : []),
|
||||
...(whyFeelsLike.precipitation !== 0 ? [
|
||||
{ label: 'Precipitation', icon: '🌧', value: whyFeelsLike.precipitation }
|
||||
] : []),
|
||||
].map(({ label, icon, value }) => html`
|
||||
<div class="insight-row" key=${label}>
|
||||
<span class="insight-icon">${icon}</span>
|
||||
<span class="insight-label">${label}</span>
|
||||
<span class=${`insight-delta ${value >= 0 ? 'delta-pos' : 'delta-neg'}`}>
|
||||
${value >= 0 ? '+' : ''}${value.toFixed(1)}°
|
||||
</span>
|
||||
</div>
|
||||
`)}
|
||||
</div>`}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${error && html`
|
||||
<div class="utci-status" style=${{ borderColor: '#3a1010', color: '#c44a3a' }}>
|
||||
@@ -385,32 +422,34 @@ export function UTCIForecast() {
|
||||
/>`}
|
||||
|
||||
|
||||
<div class="table-with-rail">
|
||||
<div class="table-main">
|
||||
<div class="col-toggles">
|
||||
<span class="col-toggles-label">Columns:</span>
|
||||
${(isPro ? (activeProfile === 'custom' || activeProfile === 'showall' || activeCols['utciP']) : activeCols['utciP']) && html`
|
||||
<span class=${'col-toggle-group' + (visibleCols.utciP ? ' col-toggle-group--expanded' : '')}>
|
||||
<${CustomSelect}
|
||||
value=${utciEnv}
|
||||
isOn=${visibleCols.utciP}
|
||||
grpClass="grp-felt"
|
||||
hideLabel="SunSoak"
|
||||
hidingLabel="Hide SunSoak"
|
||||
groupedLeft=${true}
|
||||
isLastChild=${true}
|
||||
options=${Object.entries(UTCI_ENVIRONMENTS).map(([k, v]) => ({
|
||||
value: k,
|
||||
label: v.label,
|
||||
}))}
|
||||
onChange=${(v) => {
|
||||
if (v === 'off') {
|
||||
setVisibleCols(prev => ({ ...prev, utciP: false }));
|
||||
} else {
|
||||
setUtciEnv(v);
|
||||
setVisibleCols(prev => ({ ...prev, utciP: true }));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</span>`}
|
||||
<span class=${'col-toggle-group' + (visibleCols.utciP ? ' col-toggle-group--expanded' : '')}>
|
||||
<${CustomSelect}
|
||||
value=${utciEnv}
|
||||
isOn=${visibleCols.utciP}
|
||||
grpClass="grp-felt"
|
||||
hideLabel="SunSoak"
|
||||
hidingLabel="Hide SunSoak"
|
||||
groupedLeft=${true}
|
||||
isLastChild=${true}
|
||||
options=${Object.entries(UTCI_ENVIRONMENTS).map(([k, v]) => ({
|
||||
value: k,
|
||||
label: v.label,
|
||||
}))}
|
||||
onChange=${(v) => {
|
||||
if (v === 'off') {
|
||||
setVisibleCols(prev => ({ ...prev, utciP: false }));
|
||||
} else {
|
||||
setUtciEnv(v);
|
||||
setVisibleCols(prev => ({ ...prev, utciP: true }));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</span>`}
|
||||
${(isPro ? (activeProfile === 'custom' || activeProfile === 'showall' || activeCols['vehicleT']) : activeCols['vehicleT']) && html`
|
||||
<span class=${'col-toggle-group' + (visibleCols.vehicleT ? ' col-toggle-group--expanded' : '')}>
|
||||
<${CustomSelect}
|
||||
@@ -862,6 +901,21 @@ export function UTCIForecast() {
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
${forecast && glanceSummary && glanceSummary.length > 0 && html`
|
||||
<aside class="glance-rail">
|
||||
<div class="insight-panel insight-panel--glance">
|
||||
<div class="insight-panel-title">Today at a glance</div>
|
||||
${glanceSummary.map(({ icon, label, value, alert }) => html`
|
||||
<div class=${`insight-row${alert ? ' insight-row--alert' : ''}`} key=${label}>
|
||||
<span class="insight-icon">${icon}</span>
|
||||
<span class="insight-label">${label}</span>
|
||||
<span class="insight-value">${value}</span>
|
||||
</div>
|
||||
`)}
|
||||
</div>
|
||||
</aside>`}
|
||||
</div>
|
||||
|
||||
${colPopup && COL_DESCRIPTIONS[colPopup.key] && html`
|
||||
<div
|
||||
|
||||
Reference in New Issue
Block a user