/* bits.jsx — atomic UI bits: icons, sparkline, etc. */ const Icon = ({ name, size=16, color, style, className, fill }) => { const s = { width:size, height:size, color:color||'currentColor', flex:'none', ...style }; const P = (paths) => {paths}; switch(name){ case 'home': return P(<>); case 'bolt': return P(<>); case 'sun': return P(<>); case 'cloudy': return P(<>); case 'cloud': return P(<>); /* fill 0–1 = laddnivå (Lennie 14/9). Utan fill: gamla fasta fyllningen. Inre spannet är 4…18 (14 bred); minst 0,6 så en tom cell ändå syns som en strimma. */ case 'battery': { /* Fyllfärg efter nivå (Lennie 14/9): röd vid tomt, orange runt 40 %, grönt däröver; mjuk övergång via hue 0 → 30 → 140. Konturen behåller nodens färg (currentColor). Utan fill: gamla fasta fyllningen. */ const lv = fill == null ? null : Math.max(0, Math.min(1, fill)); const hue = lv == null ? null : lv <= 0.25 ? 30*(lv/0.25) : 30 + 110*Math.min(1, (lv-0.25)/0.25); /* v607: röd 0, orange 25 %, grön från 50 % (André 26/9) */ const fc = lv == null ? 'currentColor' : `hsl(${hue.toFixed(0)} 85% 55%)`; return P(<>); } case 'car': return P(<>); case 'plug': return P(<>); case 'flash': return P(<>); case 'grid': return P(<>); case 'chart': return P(<>); case 'gear': return P(<>); case 'bell': return P(<>); case 'search': return P(<>); case 'lightning': return P(<>); case 'leaf': return P(<>); case 'wallet': return P(<>); case 'chevron': return P(<>); case 'expand': return P(<>); case 'wifi': return P(<>); case 'arrowup': return P(<>); case 'arrowdown': return P(<>); case 'arrowr': return P(<>); case 'wind': return P(<>); case 'snow': return P(<>); case 'eye': return P(<>); case 'pause': return P(<>); case 'play': return P(<>); case 'thermo': return P(<>); default: return null; } }; /* ---- small sparkline ---- */ const Sparkline = ({ data, width=80, height=24, color="currentColor", area=true, stroke=1.4 }) => { if (!data || data.length<2) return null; const min = Math.min(...data), max = Math.max(...data); const range = (max-min) || 1; const pts = data.map((v,i)=>{ const x = (i/(data.length-1)) * (width-2) + 1; const y = height - 2 - ((v-min)/range) * (height-4); return [x,y]; }); const d = pts.map((p,i)=> (i?'L':'M') + p[0].toFixed(1) + ' ' + p[1].toFixed(1)).join(' '); const dArea = d + ` L ${(width-1).toFixed(1)} ${(height-1).toFixed(1)} L 1 ${(height-1).toFixed(1)} Z`; return ( {area && } ); }; /* ---- diagonal stripe pattern as inline svg url ---- */ const stripePattern = (color, opacity=1) => `url("data:image/svg+xml;utf8,${encodeURIComponent( `` )}")`; Object.assign(window, { Icon, Sparkline, stripePattern });