Google Page Insights Tweaks

This commit is contained in:
fraxle
2026-05-16 09:21:06 +01:00
parent 8d2bb6e608
commit de1f2f5a37
7 changed files with 114 additions and 91 deletions
+71 -66
View File
@@ -139,12 +139,6 @@ export function UTCIForecast() {
icon: '🌡️',
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, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false },
},
outdoors: {
label: 'Places',
icon: '🌤️',
// Default cols match the first variant (urban). Switching variant updates visibleCols.
cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: true, vehicleT: false, indoorT: false, managedT: false },
},
home: {
label: 'Home',
icon: '🏠',
@@ -160,11 +154,11 @@ export function UTCIForecast() {
icon: '🌾',
cols: { hour: true, air: true, rh: true, dew: true, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: true, uvA: false, uvB: false, burn: false, utciP: false, precip: true, soilT: true, soilT6: true, soilM: true, concreteT: false, vehicleT: false, indoorT: false, managedT: false },
},
sailing: {
label: 'Sailing',
icon: '',
proOnly: true,
cols: { hour: true, air: true, rh: false, dew: true, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false },
outdoors: {
label: 'Places',
icon: '🌤️',
// Default cols match the first variant (urban). Switching variant updates visibleCols.
cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: true, vehicleT: false, indoorT: false, managedT: false },
},
alltemps: {
label: 'Temps',
@@ -189,6 +183,7 @@ export function UTCIForecast() {
festival: { name: 'Festival', proOnly: true, cols: { hour: true, air: true, rh: false, dew: true, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: true, concreteT: false, vehicleT: false, indoorT: false, managedT: false } },
wintersports: { name: 'Winter Sports', proOnly: true, cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: true, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false } },
naturist: { name: 'Naturist', proOnly: true, cols: { hour: true, air: true, rh: false, dew: true, wind: true, dir: false, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: true, uvA: true, uvB: true, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false } },
sailing: { name: 'Sailing', proOnly: true, cols: { hour: true, air: true, rh: false, dew: true, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false } },
};
// Current active filter profile
@@ -692,7 +687,7 @@ export function UTCIForecast() {
<div class="lens-bloom-a"></div>
<div class="lens-bloom-b"></div>
<div class="utci-shell">
<main class="utci-shell">
<div class="utci-header">
<div>
@@ -1025,6 +1020,41 @@ export function UTCIForecast() {
<span class="filter-profiles-label">Profile:</span>
${Object.entries(FILTER_PROFILES).map(([key, profile]) => {
const locked = profile.proOnly && !isPro;
// The 'outdoors' profile becomes a CustomSelect dropdown
if (key === 'outdoors') {
return html`
<${CustomSelect}
key="outdoors"
value=${activeProfile === 'outdoors' ? outdoorsVariant : 'off'}
isOn=${activeProfile === 'outdoors'}
noHide=${true}
hideLabel=${`${profile.icon} ${profile.label}`}
options=${Object.entries(OUTDOORS_VARIANTS).map(([k, v]) => ({
value: k,
label: v.name + (v.proOnly && !isPro ? ' 🔒' : ''),
disabled: false,
}))}
onChange=${(v) => {
if (v === 'off') {
setActiveProfile('basic');
setVisibleCols({ ...FILTER_PROFILES.basic.cols });
setIndoorMode('off');
return;
}
if (OUTDOORS_VARIANTS[v]?.proOnly && !isPro) {
setProPromptSource(`variant:${v}`);
setProPromptDay(0);
return;
}
setActiveProfile('outdoors');
setOutdoorsVariant(v);
setVisibleCols({ ...OUTDOORS_VARIANTS[v].cols });
setIndoorMode('off');
}}
/>`;
}
return html`
<button
key=${key}
@@ -1038,10 +1068,7 @@ export function UTCIForecast() {
return;
}
setActiveProfile(key);
if (key === 'outdoors') {
// Apply the current variant's cols (default: urban on first switch)
setVisibleCols({ ...OUTDOORS_VARIANTS[outdoorsVariant].cols });
} else if (key !== 'custom') {
if (key !== 'custom') {
setVisibleCols({ ...profile.cols });
}
// Auto-enable indoor column for profiles that include it;
@@ -1067,29 +1094,7 @@ export function UTCIForecast() {
<div class="col-toggles">
<span class="col-toggles-label">Columns:</span>
${activeProfile === 'outdoors' && html`
<${CustomSelect}
value=${outdoorsVariant}
isOn=${true}
hideLabel="Outdoors"
hidingLabel="Hide Outdoors"
options=${Object.entries(OUTDOORS_VARIANTS).map(([k, v]) => ({
value: k,
label: v.name + (v.proOnly && !isPro ? ' 🔒' : ''),
disabled: false,
}))}
onChange=${(v) => {
if (OUTDOORS_VARIANTS[v]?.proOnly && !isPro) {
setProPromptSource(`variant:${v}`);
setProPromptDay(0);
return;
}
setOutdoorsVariant(v);
setVisibleCols({ ...OUTDOORS_VARIANTS[v].cols });
}}
/>`}
${isPro && (activeProfile === 'custom' || FILTER_PROFILES[activeProfile].cols['air']) && html`<button class=${`col-toggle${visibleCols.air ? ' on' : ''}`} onClick=${() => toggleCol('air')}>Air</button>`}
${isPro && (activeProfile === 'custom' || FILTER_PROFILES[activeProfile].cols['air']) && html`<button class=${`col-toggle${visibleCols.air ? ' on' : ''}`} onClick=${() => toggleCol('air')}>Air</button>`}
${isPro && (activeProfile === 'custom' || FILTER_PROFILES[activeProfile].cols['rh']) && html`<button class=${`col-toggle${visibleCols.rh ? ' on' : ''}`} onClick=${() => toggleCol('rh')}>RH</button>`}
${isPro && (activeProfile === 'custom' || FILTER_PROFILES[activeProfile].cols['dew']) && html`<button class=${`col-toggle${visibleCols.dew ? ' on' : ''}`} onClick=${() => toggleCol('dew')}>Dew</button>`}
${isPro && (activeProfile === 'custom' || FILTER_PROFILES[activeProfile].cols['soilT']) && html`<button class=${`col-toggle${visibleCols.soilT ? ' on' : ''}`} onClick=${() => toggleCol('soilT')}>Soil °C</button>`}
@@ -1214,31 +1219,31 @@ export function UTCIForecast() {
<table class="utci-table utci-table-head" ref=${headTableRef}>
<thead>
<tr>
<th class="col-info-th" onClick=${(e) => handleThClick('hour', e)}>Hour</th>
${visibleCols.air && html`<th class="utci-tight-head col-info-th" onClick=${(e) => handleThClick('air', e)}>Air <span class="col-unit">°C</span></th>`}
${visibleCols.rh && html`<th class="col-info-th" onClick=${(e) => handleThClick('rh', e)}>RH <span class="col-unit">%</span></th>`}
${visibleCols.dew && html`<th class="col-info-th" onClick=${(e) => handleThClick('dew', e)}>Dew <span class="col-unit">°C</span></th>`}
${visibleCols.soilT && html`<th class="col-info-th" onClick=${(e) => handleThClick('soilT', e)}>Soil °C <span class="col-unit">surface</span></th>`}
${visibleCols.soilT6 && html`<th class="col-info-th" onClick=${(e) => handleThClick('soilT6', e)}>Soil 6cm <span class="col-unit">°C root</span></th>`}
${visibleCols.soilM && html`<th class="col-info-th" onClick=${(e) => handleThClick('soilM', e)}>Soil moist <span class="col-unit">m³/m³</span></th>`}
${visibleCols.concreteT && html`<th class="col-info-th" onClick=${(e) => handleThClick('concreteT', e)}>Concrete <span class="col-unit">°C surface</span></th>`}
${visibleCols.wind && html`<th class="col-info-th" onClick=${(e) => handleThClick('wind', e)}>Wind <span class="col-unit">m/s (gust)</span></th>`}
${visibleCols.dir && html`<th class="utci-dir-cell col-info-th" onClick=${(e) => handleThClick('dir', e)}>Dir <span class="col-unit">-</span></th>`}
${visibleCols.cloud && html`<th class="col-info-th" onClick=${(e) => handleThClick('cloud', e)}>Cloud <span class="col-unit">%</span></th>`}
${visibleCols.sun && html`<th class="col-info-th" onClick=${(e) => handleThClick('sun', e)}>Sun <span class="col-unit">elev°</span></th>`}
${visibleCols.direct && html`<th class="col-info-th" onClick=${(e) => handleThClick('direct', e)}>Direct <span class="col-unit">W/m²</span></th>`}
${visibleCols.diffuse && html`<th class="col-info-th" onClick=${(e) => handleThClick('diffuse', e)}>Diffuse <span class="col-unit">W/m²</span></th>`}
${visibleCols.tmrt && html`<th class="col-info-th" onClick=${(e) => handleThClick('tmrt', e)}>Tmrt <span class="col-unit">°C</span></th>`}
${visibleCols.delta && html`<th class="col-info-th" onClick=${(e) => handleThClick('delta', e)}>Δ <span class="col-unit">UTCIAir</span></th>`}
${visibleCols.utci && html`<th class="col-info-th" onClick=${(e) => handleThClick('utci', e)}>UTCI <span class="col-unit">°C felt</span></th>`}
${visibleCols.uvA && html`<th class="col-info-th" onClick=${(e) => handleThClick('uvA', e)}>UV-A <span class="col-unit">est. idx</span></th>`}
${visibleCols.uvB && html`<th class="col-info-th" onClick=${(e) => handleThClick('uvB', e)}>UV-B <span class="col-unit">est. idx</span></th>`}
${visibleCols.burn && html`<th class="col-info-th" onClick=${(e) => handleThClick('burn', e)}>Burn <span class="col-unit">to MED</span></th>`}
${visibleCols.vehicleT && html`<th class="col-info-th" onClick=${(e) => handleThClick('vehicleT', e)}>Vehicle <span class="col-unit">°C peak</span></th>`}
${indoorMode === 'on' && !indoorManaged && html`<th class="col-info-th" onClick=${(e) => handleThClick('indoorT', e)}>Indoors <span class="col-unit">°C est.</span></th>`}
${indoorMode === 'on' && indoorManaged && html`<th class="col-info-th" onClick=${(e) => handleThClick('managedT', e)}>Managed <span class="col-unit">°C est.</span></th>`}
${visibleCols.precip && html`<th class="utci-tight-head col-info-th" onClick=${(e) => handleThClick('precip', e)}>Pcpt <span class="col-unit">mm/h</span></th>`}
${visibleCols.utciP && html`<th class="col-info-th" onClick=${(e) => handleThClick('utciP', e)}>UTCI+P <span class="col-unit">°C adj.</span></th>`}
<th class="col-info-th" scope="col" onClick=${(e) => handleThClick('hour', e)}>Hour</th>
${visibleCols.air && html`<th class="utci-tight-head col-info-th" scope="col" onClick=${(e) => handleThClick('air', e)}>Air <span class="col-unit">°C</span></th>`}
${visibleCols.rh && html`<th class="col-info-th" scope="col" onClick=${(e) => handleThClick('rh', e)}>RH <span class="col-unit">%</span></th>`}
${visibleCols.dew && html`<th class="col-info-th" scope="col" onClick=${(e) => handleThClick('dew', e)}>Dew <span class="col-unit">°C</span></th>`}
${visibleCols.soilT && html`<th class="col-info-th" scope="col" onClick=${(e) => handleThClick('soilT', e)}>Soil °C <span class="col-unit">surface</span></th>`}
${visibleCols.soilT6 && html`<th class="col-info-th" scope="col" onClick=${(e) => handleThClick('soilT6', e)}>Soil 6cm <span class="col-unit">°C root</span></th>`}
${visibleCols.soilM && html`<th class="col-info-th" scope="col" onClick=${(e) => handleThClick('soilM', e)}>Soil moist <span class="col-unit">m³/m³</span></th>`}
${visibleCols.concreteT && html`<th class="col-info-th" scope="col" onClick=${(e) => handleThClick('concreteT', e)}>Concrete <span class="col-unit">°C surface</span></th>`}
${visibleCols.wind && html`<th class="col-info-th" scope="col" onClick=${(e) => handleThClick('wind', e)}>Wind <span class="col-unit">m/s (gust)</span></th>`}
${visibleCols.dir && html`<th class="utci-dir-cell col-info-th" scope="col" onClick=${(e) => handleThClick('dir', e)}>Dir <span class="col-unit">-</span></th>`}
${visibleCols.cloud && html`<th class="col-info-th" scope="col" onClick=${(e) => handleThClick('cloud', e)}>Cloud <span class="col-unit">%</span></th>`}
${visibleCols.sun && html`<th class="col-info-th" scope="col" onClick=${(e) => handleThClick('sun', e)}>Sun <span class="col-unit">elev°</span></th>`}
${visibleCols.direct && html`<th class="col-info-th" scope="col" onClick=${(e) => handleThClick('direct', e)}>Direct <span class="col-unit">W/m²</span></th>`}
${visibleCols.diffuse && html`<th class="col-info-th" scope="col" onClick=${(e) => handleThClick('diffuse', e)}>Diffuse <span class="col-unit">W/m²</span></th>`}
${visibleCols.tmrt && html`<th class="col-info-th" scope="col" onClick=${(e) => handleThClick('tmrt', e)}>Tmrt <span class="col-unit">°C</span></th>`}
${visibleCols.delta && html`<th class="col-info-th" scope="col" onClick=${(e) => handleThClick('delta', e)}>Δ <span class="col-unit">UTCIAir</span></th>`}
${visibleCols.utci && html`<th class="col-info-th" scope="col" onClick=${(e) => handleThClick('utci', e)}>UTCI <span class="col-unit">°C felt</span></th>`}
${visibleCols.uvA && html`<th class="col-info-th" scope="col" onClick=${(e) => handleThClick('uvA', e)}>UV-A <span class="col-unit">est. idx</span></th>`}
${visibleCols.uvB && html`<th class="col-info-th" scope="col" onClick=${(e) => handleThClick('uvB', e)}>UV-B <span class="col-unit">est. idx</span></th>`}
${visibleCols.burn && html`<th class="col-info-th" scope="col" onClick=${(e) => handleThClick('burn', e)}>Burn <span class="col-unit">to MED</span></th>`}
${visibleCols.vehicleT && html`<th class="col-info-th" scope="col" onClick=${(e) => handleThClick('vehicleT', e)}>Vehicle <span class="col-unit">°C peak</span></th>`}
${indoorMode === 'on' && !indoorManaged && html`<th class="col-info-th" scope="col" onClick=${(e) => handleThClick('indoorT', e)}>Indoors <span class="col-unit">°C est.</span></th>`}
${indoorMode === 'on' && indoorManaged && html`<th class="col-info-th" scope="col" onClick=${(e) => handleThClick('managedT', e)}>Managed <span class="col-unit">°C est.</span></th>`}
${visibleCols.precip && html`<th class="utci-tight-head col-info-th" scope="col" onClick=${(e) => handleThClick('precip', e)}>Pcpt <span class="col-unit">mm/h</span></th>`}
${visibleCols.utciP && html`<th class="col-info-th" scope="col" onClick=${(e) => handleThClick('utciP', e)}>UTCI+P <span class="col-unit">°C adj.</span></th>`}
</tr>
</thead>
</table>
@@ -1420,7 +1425,7 @@ export function UTCIForecast() {
{ l: '-13 to 0 Freezing', bg: '#7eb0e0', fg: '#111' },
{ l: '0 to 9 Cold', bg: '#bcd9ec', fg: '#111' },
{ l: '9 to 18 Chilled', bg: '#c8dcc0', fg: '#111' },
{ l: '18 to 26 Comfortable', bg: '#6ab05a', fg: '#fff' },
{ l: '18 to 26 Comfortable', bg: '#4a8a3a', fg: '#fff' },
{ l: '26 to 32 Mod heat', bg: '#e8c547', fg: '#111' },
{ l: '32 to 38 Strong heat', bg: '#dc8a3a', fg: '#111' },
{ l: '38 to 46 V. strong', bg: '#c44a3a', fg: '#fff' },
@@ -1460,6 +1465,6 @@ export function UTCIForecast() {
On clear sunny days this gap can exceed 10 °C even at modest air temperatures.
</div>
</div>
</main>
</div>`;
}
+3 -3
View File
@@ -36,7 +36,7 @@ const html = htm.bind(h);
// groupedLeft — bool — fuse right side with a VentPill
// isLastChild — bool — restore right border-radius when no sibling follows
// ═══════════════════════════════════════════════════════════════════
export function CustomSelect({ value, options, onChange, hideLabel, hidingLabel, isOn, groupedLeft, isLastChild }) {
export function CustomSelect({ value, options, onChange, hideLabel, hidingLabel, isOn, noHide, groupedLeft, isLastChild }) {
const [open, setOpen] = useState(false);
const wrapRef = useRef(null);
@@ -78,11 +78,11 @@ export function CustomSelect({ value, options, onChange, hideLabel, hidingLabel,
type="button"
>
${btnLabel}
<span class="cs-arrow"></span>
<span class="cs-arrow"></span>
</button>
${open && html`
<div class="cs-panel" role="listbox">
${isOn && html`
${isOn && !noHide && html`
<span
class="cs-option"
role="option"
+1 -1
View File
@@ -33,7 +33,7 @@ export function utciCategory(u) {
if (u < 0) return { label: 'Freezing', bg: '#7eb0e0', fg: '#1a1612' };
if (u < 9) return { label: 'Cold', bg: '#bcd9ec', fg: '#1a1612' };
if (u < 18) return { label: 'Chilled', bg: '#c8dcc0', fg: '#1a1612' };
if (u < 26) return { label: 'Comfortable', bg: '#6ab05a', fg: '#fff' };
if (u < 26) return { label: 'Comfortable', bg: '#4a8a3a', fg: '#fff' };
if (u < 32) return { label: 'Moderate heat', bg: '#e8c547', fg: '#1a1612' };
if (u < 38) return { label: 'Strong heat', bg: '#dc8a3a', fg: '#1a1612' };
if (u < 46) return { label: 'Very strong heat', bg: '#c44a3a', fg: '#fff' };