From 933a5f8658498bbcaa9bf47006a94c8b260e80a5 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sun, 12 Jul 2026 21:22:56 -0500 Subject: refactor(gallery): extract controls 01-09, 24, 25 into GW builders Eleven CSS-form controls (slide toggle, console keys, both faders, knob, segmented, chip, arm-to-fire, lamp row, rotary selector, slide-rule dial) move into widgets.js as parameterized builders returning handles; card() now accepts a builder function in place of stage HTML, so these cards are declarative records wiring GW.* onChange into their readouts. Probes green plus a per-widget behavioral pass over all eleven. --- docs/prototypes/panel-widget-gallery.html | 99 ++++++++----------------------- 1 file changed, 24 insertions(+), 75 deletions(-) (limited to 'docs/prototypes/panel-widget-gallery.html') diff --git a/docs/prototypes/panel-widget-gallery.html b/docs/prototypes/panel-widget-gallery.html index d6e847b..a6d40c9 100644 --- a/docs/prototypes/panel-widget-gallery.html +++ b/docs/prototypes/panel-widget-gallery.html @@ -1515,10 +1515,14 @@ const INFO={ const INFO_FIELDS=[['input','input'],['solves','solves'],['use','use'],['limits','limits'], ['origin','origin'],['difficulty','difficulty'],['prefer','prefer when'],['period','period']]; const VLAMP_TITLES={off:'validation: not done',amber:'validation: in progress',green:'validation: done'}; +/* card(host, no, name, htmlOrBuild, note) — the declarative card record. + htmlOrBuild: a legacy stage-HTML string, or a builder function (stage, rd) => handle + that instantiates a GW.* widget into the stage; rd(txt) writes the card readout. */ function card(host, no, name, html, note){ + const isBuild=typeof html==='function'; const c=document.createElement('div'); c.className='card'; c.id='card-'+no; c.innerHTML=`
${no}${name}
`+ - `
${html}
`+ + `
${isBuild?'':html}
`+ `
${note}
`; const lamp=c.querySelector('.vlamp'), VKEY='gv-'+no, VSTATES=['off','amber','green']; const setV=v=>{lamp.dataset.v=v;lamp.title=VLAMP_TITLES[v];}; @@ -1533,7 +1537,9 @@ function card(host, no, name, html, note){ if(inf.ref) rows+=`
reference
photo
`; d.innerHTML=`spec sheet
${rows}
`; c.appendChild(d);} - host.appendChild(c); return c; + host.appendChild(c); + if(isBuild)c.gw=html(c.querySelector('.stagew'),txt=>setRd(no,txt)); + return c; } function setRd(no,txt){const e=$('rd-'+no);if(e)e.textContent=txt;} /* drag helpers, seg7, buildBars live in widgets.js (GW) */ @@ -1541,45 +1547,41 @@ function setRd(no,txt){const e=$('rd-'+no);if(e)e.textContent=txt;} /* ============ CONTROLS ============ */ const C=$('controls'); card(C,'01','Slide toggle', - ``, + (st,rd)=>GW.slideToggle(st,{on:true,onChange:(v,t)=>rd(t)}), 'on / off, the touchscreen way. The one born-digital control in the kit — the smartphone pill toggle the live net/bt/audio panels use. Click to flip; readout shows the state. Physical cousins: the bat-handle (R03) and the rocker (N01).'); card(C,'02','Console key', - ``, + (st,rd)=>GW.consoleKeys(st,{onChange:(i,t)=>rd(t)}), 'physical push button. Mutually exclusive — click one to engage. Gold = live, terracotta = muted.'); card(C,'03','Horizontal fader', - `
`, + (st,rd)=>GW.faderH(st,{value:68,onChange:(v,t)=>rd(t)}), 'continuous 0-100. Per-device volume, brightness. Drag the gold cap; readout tracks.'); card(C,'04','Vertical fader', - `
-
`, + (st,rd)=>{let ready=false; + const upd=()=>{if(ready)rd('A '+Math.round(a.get())+' · B '+Math.round(b.get()));}; + const a=GW.faderV(st,{value:60,onChange:upd}); + const b=GW.faderV(st,{value:35,onChange:upd}); + ready=true;upd();return {a,b};}, 'channel-strip style. A mixer column per device. Drag each fader up or down.'); card(C,'05','Rotary knob', - ``, + (st,rd)=>GW.knob(st,{value:53,onChange:v=>rd('gain '+Math.round(v))}), 'dial in a value. Volume/gain the analog way. Drag up/down to turn; readout shows the level.'); card(C,'06','Segmented selector', - `
`, + (st,rd)=>GW.segmented(st,{onChange:(i,t)=>rd(t)}), 'pick one of a few. Timer type, layout mode. Click a segment; readout names the choice.'); card(C,'07','Chip toggle', - `discoverable on`, + (st,rd)=>GW.chipToggle(st,{on:true,onChange:(v,t)=>rd(t)}), 'inline binary. A soft toggle inside a line of text. Click to flip; gold when on.'); card(C,'08','Arm-to-fire', - ``, + (st,rd)=>GW.armButton(st,{onChange:(v,t)=>rd(t)}), 'two-stage confirm. Destructive actions. First click arms (red), second fires. Readout shows the stage.'); card(C,'09','Lamp row', - `
WH-1000XM4tap to connect
`, + (st,rd)=>GW.lampRow(st,{onChange:(i,t)=>rd(t)}), 'actionable list item. Lamp + name + status. Click to cycle idle → connecting → connected.'); card(C,'24','Rotary selector', - ` - 4681012 - - `, + (st,rd)=>GW.rotarySelector(st,{onChange:(v,t)=>rd(t)}), 'pick one of N by position. Printed detents, the pointer names the value. Click to turn; readout shows it.'); card(C,'25','Slide-rule dial', - ` - - 4681012 - - `, + (st,rd)=>GW.slideRule(st,{onChange:(v,t)=>rd(t)}), 'value on a printed scale. A lit pointer glides a warm-backlit strip. Click a mark to jump, or focus it and press ←/→ (↑/↓); readout shows it.'); card(C,'N01','Rocker power switch', `ONOFF`, @@ -1926,60 +1928,7 @@ card(I,'R52','Blinkenlights front panel', /* ============ CONTROL WIRING ============ */ /* 01 toggle */ -(function(){const sw=$('toggle01').querySelector('.switch'); - function upd(){setRd('01',sw.classList.contains('on')?'ON':'OFF');} - sw.onclick=()=>{sw.classList.toggle('on');upd();};upd();})(); -/* 02 console key */ -$('keys02').querySelectorAll('.key').forEach(k=>k.onclick=()=>{ - $('keys02').querySelectorAll('.key').forEach(x=>x.classList.remove('on','red')); - k.classList.add(k.textContent==='MUTED'?'red':'on');setRd('02',k.textContent);}); -setRd('02','LIVE'); -/* 03 horizontal fader */ -function setF1(p){const f=$('f1');f.querySelector('.fill').style.width=p+'%';f.querySelector('.cap').style.left=p+'%';setRd('03','level '+Math.round(p));} -dragX($('f1'),setF1);setF1(68); -/* 04 vertical faders */ -let vfA=60,vfB=35; -function vfUpd(){setRd('04','A '+Math.round(vfA)+' · B '+Math.round(vfB));} -function setVf(el,p){el.querySelector('.fill').style.height=p+'%';el.querySelector('.cap').style.bottom=p+'%';} -dragY($('v1'),p=>{vfA=p;setVf($('v1'),p);vfUpd();}); -dragY($('v2'),p=>{vfB=p;setVf($('v2'),p);vfUpd();}); -setVf($('v1'),60);setVf($('v2'),35);vfUpd(); -/* 05 knob */ -let knobV=53; -function setKnob(v){knobV=v;$('kind').style.transform=`rotate(${-150+v/100*300}deg)`;setRd('05','gain '+Math.round(v));} -dragDelta($('knob'),()=>knobV,setKnob,{min:0,max:100});setKnob(53); -/* 06 segmented */ -$('seg06').querySelectorAll('button').forEach(b=>b.onclick=()=>{ - $('seg06').querySelectorAll('button').forEach(x=>x.classList.remove('on'));b.classList.add('on');setRd('06',b.textContent);}); -setRd('06','TIMER'); -/* 07 chip */ -$('chip07').onclick=()=>{$('chip07').classList.toggle('on');setRd('07',$('chip07').classList.contains('on')?'ON':'OFF');}; -setRd('07','ON'); -/* 08 arm */ -let armed=false; -$('arm').onclick=()=>{const a=$('arm');if(!armed){armed=true;a.classList.add('armed');a.textContent='forget? again';setRd('08','ARMED');} - else{armed=false;a.classList.remove('armed');a.textContent='forget';setRd('08','FIRED · reset');}}; -setRd('08','SAFE'); -/* 09 lamp row */ -const lrStates=[['gold','tap to connect'],['busy','connecting…'],['','connected']];let lrI=0; -$('lrow9').onclick=()=>{lrI=(lrI+1)%3;const s=lrStates[lrI];$('lrow9').querySelector('.lamp').className='lamp '+s[0]; - $('lrow9').querySelector('.what').textContent=s[1];setRd('09',s[1]);}; -setRd('09','tap to connect'); -/* 24 rotary selector */ -const rotVals=[4,6,8,10,12];let rotIdx=2;const rotAng=[-70,-35,0,35,70]; -$('rotsel24').querySelector('.knob').onclick=()=>{rotIdx=(rotIdx+1)%5; - $('rotsel24').querySelector('.ind').style.transform=`rotate(${rotAng[rotIdx]}deg)`; - $('rotsel24').querySelectorAll('.pos').forEach((p,i)=>p.classList.toggle('on',i===rotIdx));setRd('24','size '+rotVals[rotIdx]);}; -setRd('24','size 8'); -/* 25 slide-rule */ -const tunVals=[4,6,8,10,12];let tunIdx=2;const tunX=[12,51,90,129,168]; -function setTun(i){tunIdx=Math.max(0,Math.min(tunVals.length-1,i));$('tundl').style.left=tunX[tunIdx]+'px'; - $('tuner').querySelectorAll('.mk').forEach((m,j)=>m.classList.toggle('on',j===tunIdx));setRd('25','pos '+tunVals[tunIdx]);} -$('tuner').onclick=(e)=>{const r=$('tuner').getBoundingClientRect(),x=e.clientX-r.left; - let best=0,bd=1e9;tunX.forEach((tx,j)=>{const d=Math.abs(tx-x);if(d{if(e.key==='ArrowLeft'||e.key==='ArrowDown'){e.preventDefault();setTun(tunIdx-1);} - else if(e.key==='ArrowRight'||e.key==='ArrowUp'){e.preventDefault();setTun(tunIdx+1);}}; -setRd('25','pos 8'); +/* 01-09, 24, 25 extracted to widgets.js (GW.*) — wired at their card records */ /* N01 rocker */ $('rocker').onclick=()=>{$('rocker').classList.toggle('on');setRd('N01',$('rocker').classList.contains('on')?'ON':'OFF');}; setRd('N01','ON'); -- cgit v1.2.3