2.9.6
Update table/profiles to match the at a glance box Add edit columns toggle for mobile
This commit is contained in:
+45
-2
@@ -420,6 +420,14 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.col-toggles-edit-btn {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.col-toggles-body {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
/* The little pill buttons */
|
||||
.col-toggle {
|
||||
padding: 5px 13px;
|
||||
@@ -1132,9 +1140,44 @@
|
||||
}
|
||||
|
||||
.col-toggles-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.col-toggles-edit-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex: 0 0 100%;
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
padding: 8px 12px;
|
||||
background: #f5edd6;
|
||||
border: 1.5px solid #c9b08a;
|
||||
color: #7a5c2a;
|
||||
font-family: Manrope, sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.col-toggles-edit-btn-icon {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
flex-shrink: 0;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.col-toggles-edit-btn--open .col-toggles-edit-btn-icon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.col-toggles-body {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.col-toggles-body--open {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.col-toggle {
|
||||
|
||||
+12
-2
@@ -131,6 +131,7 @@ export function UTCIForecast() {
|
||||
// location. The input then overlays the location line to save space.
|
||||
const [searchOpen, setSearchOpen] = useState(false);
|
||||
const searchWrapRef = useRef(null);
|
||||
const [colTogglesOpen, setColTogglesOpen] = useState(false);
|
||||
const searchInputRef = useRef(null);
|
||||
|
||||
// Focus the input the moment the search opens.
|
||||
@@ -290,6 +291,7 @@ export function UTCIForecast() {
|
||||
activeProfile,
|
||||
outdoorsVariant,
|
||||
skinType,
|
||||
visibleCols,
|
||||
);
|
||||
|
||||
// Human-readable date for the "Day at a glance" heading, e.g. "Mon 2nd June 2026".
|
||||
@@ -629,6 +631,11 @@ export function UTCIForecast() {
|
||||
<div class="table-main">
|
||||
<div class="col-toggles">
|
||||
<span class="col-toggles-label">Columns:</span>
|
||||
<button class=${'col-toggles-edit-btn' + (colTogglesOpen ? ' col-toggles-edit-btn--open' : '')} onClick=${() => setColTogglesOpen(v => !v)}>
|
||||
<span class="col-toggles-edit-btn-label">${colTogglesOpen ? 'Done' : 'Edit columns'}</span>
|
||||
<svg class="col-toggles-edit-btn-icon" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M2 4l4 4 4-4" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg>
|
||||
</button>
|
||||
<div class=${'col-toggles-body' + (colTogglesOpen ? ' col-toggles-body--open' : '')}>
|
||||
${(isPro ? (activeProfile === 'custom' || activeProfile === 'showall' || activeCols['utciP']) : activeCols['utciP']) && html`
|
||||
<span class=${'col-toggle-group' + (visibleCols.utciP ? ' col-toggle-group--expanded' : '')}>
|
||||
<${CustomSelect}
|
||||
@@ -791,6 +798,7 @@ export function UTCIForecast() {
|
||||
Units
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=${`utci-table-wrap${tableCanScrollLeft ? ' scroll-fade-left' : ''}${tableCanScrollRight ? ' scroll-fade-right' : ''}${showUnits ? '' : ' no-units'}`} ref=${tableWrapRef}>
|
||||
@@ -1013,9 +1021,11 @@ export function UTCIForecast() {
|
||||
${visibleCols.precip && html`
|
||||
<td class=${groupStart('precip')} style=${{ background: r.snow > 0 ? scaleBg(r.snow, 0, 4, '90,140,210') : scaleBg(r.precip, 0, 8, '70,145,200') }}>
|
||||
<span style=${{ display: 'inline-flex', alignItems: 'center', gap: '5px', verticalAlign: 'middle' }}>
|
||||
<${PrecipIcon} precip=${r.precip} snow=${r.snow} size=${28} />
|
||||
<${PrecipIcon} precip=${r.precipProb > 0 ? r.precip : 0} snow=${r.precipProb > 0 ? r.snow : 0} size=${28} />
|
||||
<span style=${{ color: r.snow > 0 ? '#2a5fa8' : r.precip > 0 ? '#2a6a90' : '#7a5c30' }}>
|
||||
${r.snow > 0 ? fmt(r.snow) + u('cm') : r.precip > 0 ? fmt(r.precip) + u('mm') : '—'}
|
||||
${r.precipProb > 0
|
||||
? (r.snow > 0 ? fmt(r.snow) + u('cm') : r.precip > 0 ? fmt(r.precip) + u('mm') : '—')
|
||||
: '—'}
|
||||
</span>
|
||||
</span>
|
||||
</td>`}
|
||||
|
||||
+37
-30
@@ -363,8 +363,9 @@ export function computeWhyFeelsLike(row, env) {
|
||||
// variant - active sub-variant key e.g. "running", "beach" (or null)
|
||||
// skinType - Fitzpatrick skin type key for UV burn time
|
||||
// ------------------------------------------------------------------------
|
||||
export function computeGlanceSummary(todayRows, profile, variant, skinType) {
|
||||
export function computeGlanceSummary(todayRows, profile, variant, skinType, cols = null) {
|
||||
if (!todayRows || todayRows.length === 0) return [];
|
||||
const show = (key) => !cols || !!cols[key];
|
||||
|
||||
const hhmm = (iso) => iso ? iso.slice(11, 16) : null;
|
||||
const dayRows = todayRows.filter(r => r.elev > 0);
|
||||
@@ -437,20 +438,20 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType) {
|
||||
: 'No suitable window',
|
||||
alert: !fieldWindow,
|
||||
},
|
||||
{
|
||||
...(show('soilT') ? [{
|
||||
icon: '🌱',
|
||||
label: 'Soil warms to 10°C by',
|
||||
value: soilWarmRow ? hhmm(soilWarmRow.iso) : 'Not today',
|
||||
alert: !soilWarmRow,
|
||||
},
|
||||
rainItem,
|
||||
{
|
||||
}] : []),
|
||||
...(show('precipProb') ? [rainItem] : []),
|
||||
...(show('soilM') ? [{
|
||||
icon: '💧',
|
||||
label: 'Soil moisture',
|
||||
value: moistureLabel(soilM) ?? '-',
|
||||
alert: soilM != null && (soilM < 0.10 || soilM > 0.50),
|
||||
},
|
||||
...(maxPollen >= 10 ? [{
|
||||
}] : []),
|
||||
...(show('pollen') && maxPollen >= 10 ? [{
|
||||
icon: '🌿',
|
||||
label: 'Grass pollen',
|
||||
value: pollenLabel(maxPollen),
|
||||
@@ -465,19 +466,18 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType) {
|
||||
const dangerFrom = todayRows.find(r => r.vehicleT != null && r.vehicleT >= 35);
|
||||
|
||||
return [
|
||||
{
|
||||
...(show('vehicleT') ? [{
|
||||
icon: '🌡',
|
||||
label: 'Peak cabin temp',
|
||||
value: peakCabin ? `${Math.round(peakCabin.vehicleT)}° at ${hhmm(peakCabin.iso)}` : '-',
|
||||
alert: !!(peakCabin && peakCabin.vehicleT >= 35),
|
||||
},
|
||||
{
|
||||
}, {
|
||||
icon: '🧒',
|
||||
label: 'Children/pets in car',
|
||||
value: dangerFrom ? `Unsafe from ${hhmm(dangerFrom.iso)}` : 'Safe all day',
|
||||
alert: !!dangerFrom,
|
||||
},
|
||||
rainItem,
|
||||
}] : []),
|
||||
...(show('precipProb') ? [rainItem] : []),
|
||||
{
|
||||
icon: '🌤',
|
||||
label: 'Best travel comfort',
|
||||
@@ -502,33 +502,33 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType) {
|
||||
const maxPollen = Math.max(0, ...todayRows.map(r => r.grassPollen ?? 0));
|
||||
|
||||
return [
|
||||
{
|
||||
...(show('indoorT') ? [{
|
||||
icon: '🌡',
|
||||
label: 'Peak indoor (unmanaged)',
|
||||
value: peakIndoor ? `${Math.round(peakIndoor.indoorT)}° at ${hhmm(peakIndoor.iso)}` : '-',
|
||||
alert: !!(peakIndoor && peakIndoor.indoorT >= 28),
|
||||
},
|
||||
{
|
||||
}] : []),
|
||||
...(show('managedT') ? [{
|
||||
icon: '🌡',
|
||||
label: 'Peak indoor (managed)',
|
||||
value: peakManaged ? `${Math.round(peakManaged.managedT)}°` : '-',
|
||||
alert: !!(peakManaged && peakManaged.managedT >= 28),
|
||||
},
|
||||
{
|
||||
}] : []),
|
||||
...(show('indoorT') ? [{
|
||||
icon: '🪟',
|
||||
label: 'Open windows',
|
||||
value: ventWindow
|
||||
? `${hhmm(ventWindow.start.iso)} – ${hhmm(ventWindow.end.iso)}`
|
||||
: 'Keep closed',
|
||||
alert: false,
|
||||
},
|
||||
{
|
||||
}] : []),
|
||||
...(show('aqi') ? [{
|
||||
icon: '💨',
|
||||
label: 'Air quality',
|
||||
value: peakAqi > 0 ? aqiLabel(peakAqi) : '-',
|
||||
alert: peakAqi >= 60,
|
||||
},
|
||||
...(maxPollen >= 10 ? [{
|
||||
}] : []),
|
||||
...(show('pollen') && maxPollen >= 10 ? [{
|
||||
icon: '🌼',
|
||||
label: 'Pollen',
|
||||
value: pollenLabel(maxPollen),
|
||||
@@ -558,20 +558,20 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType) {
|
||||
: 'No cool window today',
|
||||
alert: !coolWindow,
|
||||
},
|
||||
rainItem,
|
||||
...(burnMins ? [{
|
||||
...(show('precipProb') ? [rainItem] : []),
|
||||
...(show('burn') && burnMins ? [{
|
||||
icon: '☀',
|
||||
label: 'UV burn time (peak)',
|
||||
value: burnMins,
|
||||
alert: !!(peakUvRow && peakUvRow.uv >= 6),
|
||||
}] : []),
|
||||
{
|
||||
...(show('aqi') ? [{
|
||||
icon: '💨',
|
||||
label: 'Air quality',
|
||||
value: peakAqi > 0 ? aqiLabel(peakAqi) : '-',
|
||||
alert: peakAqi >= 60,
|
||||
},
|
||||
...(maxPollen >= 10 ? [{
|
||||
}] : []),
|
||||
...(show('pollen') && maxPollen >= 10 ? [{
|
||||
icon: '🌿',
|
||||
label: 'Pollen',
|
||||
value: pollenLabel(maxPollen),
|
||||
@@ -587,6 +587,7 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType) {
|
||||
const peakFelt = peakRow('utciAdj');
|
||||
const peakUvRow = peakRow('uv');
|
||||
const peakAqi = Math.max(0, ...todayRows.map(r => r.aqi ?? 0));
|
||||
const maxPollen = Math.max(0, ...todayRows.map(r => r.grassPollen ?? 0));
|
||||
const burnMins = peakUvRow && peakUvRow.uv > 0
|
||||
? burnLabel(sunburnMinutes(peakUvRow.uv, skinType))
|
||||
: null;
|
||||
@@ -606,18 +607,24 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType) {
|
||||
value: peakFelt ? `${Math.round(peakFelt.utciAdj)}° at ${hhmm(peakFelt.iso)}` : '-',
|
||||
alert: !!(peakFelt && (peakFelt.utciAdj >= 32 || peakFelt.utciAdj < 0)),
|
||||
},
|
||||
...(burnMins ? [{
|
||||
...((show('burn') || show('uvA')) && burnMins ? [{
|
||||
icon: '☀',
|
||||
label: 'UV burn time (peak)',
|
||||
value: burnMins,
|
||||
alert: !!(peakUvRow && peakUvRow.uv >= 6),
|
||||
}] : []),
|
||||
rainItem,
|
||||
{
|
||||
...(show('precipProb') ? [rainItem] : []),
|
||||
...(show('aqi') ? [{
|
||||
icon: '💨',
|
||||
label: 'Air quality',
|
||||
value: peakAqi > 0 ? aqiLabel(peakAqi) : '-',
|
||||
alert: peakAqi >= 60,
|
||||
},
|
||||
}] : []),
|
||||
...(show('pollen') && maxPollen >= 10 ? [{
|
||||
icon: '🌿',
|
||||
label: 'Pollen',
|
||||
value: pollenLabel(maxPollen),
|
||||
alert: maxPollen >= 50,
|
||||
}] : []),
|
||||
];
|
||||
}
|
||||
+1
-1
@@ -29,7 +29,7 @@ export const FILTER_PROFILES = {
|
||||
label: 'Basic',
|
||||
icon: '🌡️',
|
||||
scene: "url('assets/images/profiles/basic.png') center / cover no-repeat, linear-gradient(160deg,#b8d8e8,#daeef8)",
|
||||
cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: false, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false },
|
||||
cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: false, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: false, aqi: false, pollen: true, rh: true },
|
||||
},
|
||||
home: {
|
||||
label: 'Home',
|
||||
|
||||
@@ -83,7 +83,7 @@ function rewriteHTML(html, version) {
|
||||
html = html.replace(/<script\s[^>]*src=["'][^"']*assets\/js\/[^"']*["'][^>]*><\/script>/gi, '');
|
||||
// Rewrite standalone about.css reference to versioned minified file.
|
||||
html = html.replace(
|
||||
/(<link\s[^>]*href=["'][^"']*assets\/)about\.css(["'][^>]*>)/gi,
|
||||
/(<link\s[^>]*href=["'][^"']*assets\/)about\.css[^"']*(["'][^>]*>)/gi,
|
||||
'$1about.min.css?v=' + version + '$2'
|
||||
);
|
||||
const css = ' <link rel="stylesheet" href="./assets/bundle.min.css?v=' + version + '">';
|
||||
|
||||
@@ -4,5 +4,17 @@
|
||||
"profiles": {
|
||||
"showall": 1
|
||||
}
|
||||
},
|
||||
"2026-06-08": {
|
||||
"visits": 1,
|
||||
"profiles": []
|
||||
},
|
||||
"2026-06-10": {
|
||||
"visits": 9,
|
||||
"profiles": {
|
||||
"basic": 5,
|
||||
"home": 2,
|
||||
"vehicle": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user