Combine and relabel table view switch.
Remove and re-arrange some table columns
Group Pets together
This commit is contained in:
fraxle
2026-08-16 09:13:15 +01:00
parent dd0ce5b44c
commit df3ecdae35
11 changed files with 151 additions and 187 deletions
+31 -31
View File
@@ -233,7 +233,7 @@ export function UTCIForecast() {
indoorMode, setIndoorMode,
pollenType, setPollenTypeAndSave,
utciEnv, setUtciEnv,
tableRotated, toggleTableRotated,
tableRotated, setTableRotated,
headStickyRef, headTrackRef, headTableRef,
bodyScrollRef, bodyTableRef, tableWrapRef,
colPopup, colPopupRef,
@@ -430,10 +430,11 @@ export function UTCIForecast() {
// Columns that mark the start of a logical group - used to draw a faint
// vertical border separating groups in the forecast table.
const GROUP_ORDER = {
felt: ['utciP', 'shadeT', 'vehicleT', 'indoorT', 'managedT', 'burn', 'utci', 'delta', 'tmrt'],
surface: ['concreteT', 'soilT', 'soilT6', 'soilM', 'furSurfaceT', 'pawT'],
ambient: ['petShadeT', 'petHomeT', 'air', 'rh', 'dew'],
precip: ['precip', 'precipProb', 'lightning'],
felt: ['utciP', 'shadeT', 'vehicleT', 'indoorT', 'managedT', 'utci', 'burn'],
surface: ['concreteT', 'soilT', 'soilT6', 'soilM'],
pets: ['furSurfaceT', 'pawT', 'petShadeT', 'petHomeT'],
ambient: ['air', 'rh', 'dew'],
precip: ['precip', 'precipProb'],
sky: ['cloud', 'vis'],
wind: ['wind', 'dir'],
airqual: ['aqi', 'pollen'],
@@ -464,7 +465,7 @@ export function UTCIForecast() {
};
// Display names for the column groups — used for the spanning labels
// above the columns normally, and for the divider rows when rotated.
const GROUP_LABELS = { felt: 'Felt', surface: 'Surface', ambient: 'Ambient', precip: 'Precip', sky: 'Sky', wind: 'Wind', airqual: 'Air quality', solar: 'Solar' };
const GROUP_LABELS = { felt: 'Felt', surface: 'Surface', pets: 'Pets', ambient: 'Ambient', precip: 'Precip', sky: 'Sky', wind: 'Wind', airqual: 'Air quality', solar: 'Solar' };
// Builds the group label row above the column headers.
// Each visible group gets one spanning cell; groups with no visible
// columns are skipped entirely. Hour always gets a blank lead cell.
@@ -1168,30 +1169,32 @@ export function UTCIForecast() {
<div class="table-toolbar-row">
<span class="fvt-toolbar-left">
<span class="forecast-view-toggle-label">View:</span>
${/* One three-way control. Quick is the card layout; the other
two are the same hourly table on opposite axes — Detailed
runs hours along the top, Table runs them down the side.
Rotation used to be a separate toggle that only appeared
once you were already in the table, which hid the layout
people wanted behind a mode they had to find first. */''}
<span class="forecast-view-toggle">
<button type="button" title="Quick view visual card layout" class=${'fvt-btn' + (forecastView === 'simple' ? ' on' : '')} onClick=${() => setForecastView('simple')}>
<button type="button" title="Quick view visual card layout" class=${'fvt-btn' + (forecastView === 'simple' ? ' on' : '')}
aria-pressed=${forecastView === 'simple' ? 'true' : 'false'}
onClick=${() => setForecastView('simple')}>
<svg class="fvt-btn-icon" viewBox="0 0 12 12" width="12" height="12"><rect x=".5" y=".5" width="4.5" height="4.5" rx=".8" fill="currentColor"/><rect x="7" y=".5" width="4.5" height="4.5" rx=".8" fill="currentColor"/><rect x=".5" y="7" width="4.5" height="4.5" rx=".8" fill="currentColor"/><rect x="7" y="7" width="4.5" height="4.5" rx=".8" fill="currentColor"/></svg>
Quick
</button>
<button type="button" title="Detailed view full hourly table" class=${'fvt-btn' + (forecastView === 'table' ? ' on' : '')} onClick=${() => setForecastView('table')}>
<svg class="fvt-btn-icon" viewBox="0 0 12 12" width="12" height="12"><rect x=".5" y="1.5" width="11" height="3" rx=".6" fill="currentColor"/><rect x=".5" y="5.5" width="11" height="2.5" rx=".5" fill="currentColor" opacity=".35"/><rect x=".5" y="8.8" width="11" height="2.5" rx=".5" fill="currentColor" opacity=".25"/></svg>
<button type="button" title="Detailed view hours along the top, metrics down the side" class=${'fvt-btn' + (forecastView === 'table' && tableRotated ? ' on' : '')}
aria-pressed=${forecastView === 'table' && tableRotated ? 'true' : 'false'}
onClick=${() => { setForecastView('table'); setTableRotated(true); }}>
<svg class="fvt-btn-icon" viewBox="0 0 12 12" width="12" height="12" aria-hidden="true"><rect x=".5" y="1.5" width="3" height="10" rx=".6" fill="currentColor"/><rect x="4.5" y="1.5" width="2.5" height="10" rx=".5" fill="currentColor" opacity=".35"/><rect x="8" y="1.5" width="2.5" height="10" rx=".5" fill="currentColor" opacity=".25"/></svg>
Detailed
</button>
<button type="button" title="Table view hours down the side, metrics along the top" class=${'fvt-btn' + (forecastView === 'table' && !tableRotated ? ' on' : '')}
aria-pressed=${forecastView === 'table' && !tableRotated ? 'true' : 'false'}
onClick=${() => { setForecastView('table'); setTableRotated(false); }}>
<svg class="fvt-btn-icon" viewBox="0 0 12 12" width="12" height="12"><rect x=".5" y="1.5" width="11" height="3" rx=".6" fill="currentColor"/><rect x=".5" y="5.5" width="11" height="2.5" rx=".5" fill="currentColor" opacity=".35"/><rect x=".5" y="8.8" width="11" height="2.5" rx=".5" fill="currentColor" opacity=".25"/></svg>
Table
</button>
</span>
${forecastView === 'table' && html`
<button type="button"
class=${'table-rotate-btn' + (tableRotated ? ' on' : '')}
title=${tableRotated ? 'Hours run along the top — click for hours down the side' : 'Hours run down the side — click for hours along the top'}
aria-pressed=${tableRotated ? 'true' : 'false'}
onClick=${toggleTableRotated}>
<svg class="fvt-btn-icon" viewBox="0 0 12 12" width="12" height="12" aria-hidden="true">
<path d="M2.4 6.6a3.7 3.7 0 0 1 6-3.2" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M9.6 5.4a3.7 3.7 0 0 1-6 3.2" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M6.3 1.1l2.4 2.2-2.4 1.8" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.7 10.9L3.3 8.7l2.4-1.8" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
Rotate
</button>`}
<button
class=${'col-toggles-edit-btn col-toggles-edit-btn--toolbar' + (colTogglesOpen ? ' col-toggles-edit-btn--open' : '') + (isPro ? '' : ' col-toggles-edit-btn--locked')}
title=${isPro ? '' : 'Custom columns are part of SunScope Extra'}
@@ -1242,25 +1245,22 @@ export function UTCIForecast() {
${colOffered('utciP') && html`<button class=${`col-toggle grp-felt${visibleCols.utciP ? ' on' : ''}`} onClick=${() => toggleCol('utciP')}>SunSoak</button>`}
${colOffered('vehicleT') && html`<button class=${`col-toggle grp-felt${visibleCols.vehicleT ? ' on' : ''}`} onClick=${() => { if (visibleCols.vehicleT) setVehicleVent(false); toggleCol('vehicleT'); }}>Vehicle</button>`}
${(colOffered('indoorT') || colOffered('managedT')) && html`<button class=${`col-toggle grp-felt${indoorMode === 'on' ? ' on' : ''}`} onClick=${() => { if (indoorMode === 'on') { setIndoorMode('off'); setIndoorManaged(false); } else { setIndoorMode('on'); } }}>Indoors</button>`}
${colOffered('furSurfaceT') && html`<button class=${`col-toggle grp-surface${visibleCols.furSurfaceT ? ' on' : ''}`} onClick=${() => toggleCol('furSurfaceT')}>Fur</button>`}
${colOffered('pawT') && html`<button class=${`col-toggle grp-surface${visibleCols.pawT ? ' on' : ''}`} onClick=${() => toggleCol('pawT')}>Paw</button>`}
${colOffered('petShadeT') && html`<button class=${`col-toggle grp-ambient${visibleCols.petShadeT ? ' on' : ''}`} onClick=${() => toggleCol('petShadeT')}>Pet Shade</button>`}
${colOffered('petHomeT') && html`<button class=${`col-toggle grp-ambient${visibleCols.petHomeT ? ' on' : ''}`} onClick=${() => toggleCol('petHomeT')}>Pet Home</button>`}
${colOffered('burn') && html`<button class=${`col-toggle grp-felt${visibleCols.burn ? ' on' : ''}`} onClick=${() => toggleCol('burn')}>Burn</button>`}
${colOffered('utci') && html`<button class=${`col-toggle grp-felt${visibleCols.utci ? ' on' : ''}`} onClick=${() => toggleCol('utci')}>UTCI</button>`}
${colOffered('delta') && html`<button class=${`col-toggle grp-felt${visibleCols.delta ? ' on' : ''}`} onClick=${() => toggleCol('delta')}>Δ</button>`}
${colOffered('tmrt') && html`<button class=${`col-toggle grp-felt${visibleCols.tmrt ? ' on' : ''}`} onClick=${() => toggleCol('tmrt')}>Tmrt</button>`}
${colOffered('burn') && html`<button class=${`col-toggle grp-felt${visibleCols.burn ? ' on' : ''}`} onClick=${() => toggleCol('burn')}>Burn</button>`}
${colOffered('concreteT') && html`<button class=${`col-toggle grp-surface${visibleCols.concreteT ? ' on' : ''}`} onClick=${() => toggleCol('concreteT')}>Concrete</button>`}
${colOffered('soilT') && html`<button class=${`col-toggle grp-surface${visibleCols.soilT ? ' on' : ''}`} onClick=${() => toggleCol('soilT')}>Soil °C</button>`}
${colOffered('soilT6') && html`<button class=${`col-toggle grp-surface${visibleCols.soilT6 ? ' on' : ''}`} onClick=${() => toggleCol('soilT6')}>Soil 6cm</button>`}
${colOffered('soilM') && html`<button class=${`col-toggle grp-surface${visibleCols.soilM ? ' on' : ''}`} onClick=${() => toggleCol('soilM')}>Soil moist</button>`}
${colOffered('furSurfaceT') && html`<button class=${`col-toggle grp-pets${visibleCols.furSurfaceT ? ' on' : ''}`} onClick=${() => toggleCol('furSurfaceT')}>Fur</button>`}
${colOffered('pawT') && html`<button class=${`col-toggle grp-pets${visibleCols.pawT ? ' on' : ''}`} onClick=${() => toggleCol('pawT')}>Paw</button>`}
${colOffered('petShadeT') && html`<button class=${`col-toggle grp-pets${visibleCols.petShadeT ? ' on' : ''}`} onClick=${() => toggleCol('petShadeT')}>Pet Shade</button>`}
${colOffered('petHomeT') && html`<button class=${`col-toggle grp-pets${visibleCols.petHomeT ? ' on' : ''}`} onClick=${() => toggleCol('petHomeT')}>Pet Home</button>`}
${colOffered('shadeT') && html`<button class=${`col-toggle grp-ambient${visibleCols.shadeT ? ' on' : ''}`} onClick=${() => toggleCol('shadeT')}>Shade</button>`}
${colOffered('air') && html`<button class=${`col-toggle grp-ambient${visibleCols.air ? ' on' : ''}`} onClick=${() => toggleCol('air')}>Air</button>`}
${colOffered('rh') && html`<button class=${`col-toggle grp-ambient${visibleCols.rh ? ' on' : ''}`} onClick=${() => toggleCol('rh')}>RH</button>`}
${colOffered('dew') && html`<button class=${`col-toggle grp-ambient${visibleCols.dew ? ' on' : ''}`} onClick=${() => toggleCol('dew')}>Dew</button>`}
${colOffered('precip') && html`<button class=${`col-toggle grp-precip${visibleCols.precip ? ' on' : ''}`} onClick=${() => toggleCol('precip')}>Precip</button>`}
${colOffered('precipProb') && html`<button class=${`col-toggle grp-precip${visibleCols.precipProb ? ' on' : ''}`} onClick=${() => toggleCol('precipProb')}>Rain%</button>`}
${colOffered('lightning') && html`<button class=${`col-toggle grp-precip${visibleCols.lightning ? ' on' : ''}`} onClick=${() => toggleCol('lightning')}>Lightning</button>`}
${colOffered('cloud') && html`<button class=${`col-toggle grp-sky${visibleCols.cloud ? ' on' : ''}`} onClick=${() => toggleCol('cloud')}>Cloud</button>`}
${colOffered('vis') && html`<button class=${`col-toggle grp-sky${visibleCols.vis ? ' on' : ''}`} onClick=${() => toggleCol('vis')}>Visibility</button>`}
${colOffered('wind') && html`<button class=${`col-toggle grp-wind${visibleCols.wind ? ' on' : ''}`} onClick=${() => toggleCol('wind')}>Wind</button>`}