1.44 - New Filters, profiles & Mobile fixes
This commit is contained in:
+41
-22
@@ -1,16 +1,18 @@
|
||||
// ════════════════════════════════════════════════════════════════════════
|
||||
// components.js — Preact UI components (SVG widgets).
|
||||
//
|
||||
// Exports:
|
||||
// SkyScope({ elev, dt, size }) little porthole circle per hour
|
||||
// WindVane({ bearing, size }) compass arrow
|
||||
// CloudIcon({ category, size, elev, dt }) cloud/sun/moon icon
|
||||
// ScopeReticle({ value, cat, loading, elev, dt }) big UTCI dial
|
||||
// PrecipIcon({ precip, snow, size }) rain/snow icon for precip cell
|
||||
// components.js — Preact UI components (SVG widgets and controls).
|
||||
//
|
||||
// All components use the html`` tagged template from htm + Preact.
|
||||
// They depend on utils.js (skyFillForElev, grassFillForElev,
|
||||
// moonPhaseFraction) but have no other external state.
|
||||
// No external state; they depend only on utils.js and events.js.
|
||||
//
|
||||
// Exports (in order of appearance):
|
||||
// CustomSelect({ value, options, onChange, ... }) styled dropdown pill
|
||||
// VentPill({ checked, onChange, label, title }) checkbox fused to pill
|
||||
// SkyScope({ elev, dt, glob, size }) small porthole per hour
|
||||
// WindVane({ bearing, size }) compass-arrow widget
|
||||
// CloudIcon({ category, size, elev, dt }) cloud/sun glyph
|
||||
// ScopeReticle({ value, cat, loading, elev, dt, glob, activeEvent })
|
||||
// large UTCI dial
|
||||
// PrecipIcon({ precip, snow, size }) rain/snow icon
|
||||
// ════════════════════════════════════════════════════════════════════════
|
||||
|
||||
import { h, Fragment } from '../vendor/preact.js';
|
||||
@@ -91,8 +93,9 @@ export function CustomSelect({ value, options, onChange, hideLabel, hidingLabel,
|
||||
>${hidingLabel || ('Hide ' + hideLabel)}</span>
|
||||
<div class="cs-divider"></div>
|
||||
`}
|
||||
${options.map(opt => html`
|
||||
<span
|
||||
${options.map(opt => opt.divider
|
||||
? html`<div key=${opt.value} class="cs-divider"></div>`
|
||||
: html`<span
|
||||
key=${opt.value}
|
||||
class=${`cs-option${opt.value === value && isOn ? ' selected' : ''}${opt.disabled ? ' disabled' : ''}`}
|
||||
role="option"
|
||||
@@ -101,8 +104,8 @@ export function CustomSelect({ value, options, onChange, hideLabel, hidingLabel,
|
||||
onChange(opt.value);
|
||||
setOpen(false);
|
||||
}}
|
||||
>${opt.label}</span>
|
||||
`)}
|
||||
>${opt.label}</span>`
|
||||
)}
|
||||
</div>
|
||||
`}
|
||||
</span>`;
|
||||
@@ -342,23 +345,39 @@ export function CloudIcon({ category, size = 30, elev = 90, dt = new Date() }) {
|
||||
${line(size * 0.30, y0 + size * 0.18, size * 0.92, y0 + size * 0.18, ink, sw * 0.82, 0.72)}
|
||||
</g>`;
|
||||
|
||||
// Sun shape — circle + 8 rays, centred at (cx, cy)
|
||||
const sun = (cx, cy, cr) => {
|
||||
const rays = Array.from({ length: 8 }, (_, k) => {
|
||||
const angle = (k * Math.PI) / 4;
|
||||
const r1 = cr * 1.55;
|
||||
const r2 = cr * 2.1;
|
||||
return html`<line key=${k}
|
||||
x1=${cx + Math.cos(angle) * r1} y1=${cy + Math.sin(angle) * r1}
|
||||
x2=${cx + Math.cos(angle) * r2} y2=${cy + Math.sin(angle) * r2}
|
||||
stroke=${brass} stroke-width=${sw * 0.85} stroke-linecap="round" opacity="0.9" />`;
|
||||
});
|
||||
return html`<g>
|
||||
${rays}
|
||||
<circle cx=${cx} cy=${cy} r=${cr} fill="none" stroke=${brass} stroke-width=${sw} opacity="0.95" />
|
||||
</g>`;
|
||||
};
|
||||
|
||||
return html`
|
||||
<svg width=${size} height=${size} viewBox=${`0 0 ${size} ${size}`}
|
||||
style=${{ flexShrink: 0, display: 'inline-block', verticalAlign: 'middle', overflow: 'visible' }}>
|
||||
${category === 'clear' && html`
|
||||
${line(size * 0.26, size * 0.50, size * 0.74, size * 0.50, brass, sw, 0.9)}
|
||||
${line(size * 0.38, size * 0.64, size * 0.62, size * 0.64, muted, sw * 0.75, 0.7)}`}
|
||||
${sun(size * 0.50, size * 0.38, size * 0.16)}`}
|
||||
${category === 'wispy' && html`
|
||||
${mist(size * 0.38, brass)}
|
||||
${line(size * 0.18, size * 0.78, size * 0.60, size * 0.78, muted, sw * 0.7, 0.6)}`}
|
||||
${mist(size * 0.18, brass)}
|
||||
${line(size * 0.18, size * 0.56, size * 0.60, size * 0.56, muted, sw * 0.7, 0.6)}`}
|
||||
${category === 'scattered' && html`
|
||||
<path d=${cloudPath(0, 0, 0.82)} fill="none" stroke=${ink}
|
||||
<path d=${cloudPath(0, -size * 0.2425, 0.82)} fill="none" stroke=${ink}
|
||||
stroke-width=${sw} stroke-linecap=${cap} stroke-linejoin=${join} />
|
||||
${line(size * 0.20, size * 0.78, size * 0.48, size * 0.78, brass, sw * 0.75, 0.78)}`}
|
||||
${line(size * 0.20, size * 0.58, size * 0.48, size * 0.58, brass, sw * 0.75, 0.78)}`}
|
||||
${category === 'overcast' && html`
|
||||
<path d=${cloudPath(-size * 0.02, -size * 0.10, 0.70)} fill="none" stroke=${muted}
|
||||
<path d=${cloudPath(-size * 0.02, -size * 0.2275, 0.70)} fill="none" stroke=${muted}
|
||||
stroke-width=${sw * 0.9} stroke-linecap=${cap} stroke-linejoin=${join} opacity="0.82" />
|
||||
<path d=${cloudPath(0, size * 0.14, 0.88)} fill="none" stroke=${ink}
|
||||
<path d=${cloudPath(0, -size * 0.25, 0.88)} fill="none" stroke=${ink}
|
||||
stroke-width=${sw} stroke-linecap=${cap} stroke-linejoin=${join} />`}
|
||||
</svg>`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user