diff options
Diffstat (limited to 'docs/prototypes/panel-widget-gallery.html')
| -rw-r--r-- | docs/prototypes/panel-widget-gallery.html | 96 |
1 files changed, 83 insertions, 13 deletions
diff --git a/docs/prototypes/panel-widget-gallery.html b/docs/prototypes/panel-widget-gallery.html index 30bf086..9740c46 100644 --- a/docs/prototypes/panel-widget-gallery.html +++ b/docs/prototypes/panel-widget-gallery.html @@ -105,8 +105,27 @@ h2::after{content:"";height:1px;background:var(--wash);flex:1} /* validation lamp: off = not done, amber = in progress, green = done; click cycles, state persists */ .vlamp{margin-left:auto;flex:0 0 auto;width:10px;height:10px;border-radius:50%;cursor:pointer; background:#221f1b;border:1px solid #33302b} -.vlamp[data-v="amber"]{background:var(--gold);border-color:#7d5c16;box-shadow:0 0 6px 1px rgba(var(--glow-lo),.6)} -.vlamp[data-v="green"]{background:var(--pass);border-color:#4a5c22;box-shadow:0 0 6px 1px rgba(116,147,47,.6)} +.vlamp[data-v="amber"],.vtally .vdot[data-v="amber"],#vaudit .vdot[data-v="amber"]{background:var(--gold);border-color:#7d5c16;box-shadow:0 0 6px 1px rgba(var(--glow-lo),.6)} +.vlamp[data-v="green"],.vtally .vdot[data-v="green"],#vaudit .vdot[data-v="green"]{background:var(--pass);border-color:#4a5c22;box-shadow:0 0 6px 1px rgba(116,147,47,.6)} +/* audit stepper pill: docks bottom-right while stepping through one state's cards */ +#vaudit{position:fixed;right:18px;bottom:16px;z-index:60;display:flex;align-items:center;gap:7px; + padding:8px 13px;border-radius:20px;background:var(--raise);border:1px solid var(--wash); + color:var(--cream);font-size:.78rem;letter-spacing:.05em;cursor:pointer;user-select:none; + box-shadow:0 4px 14px rgba(0,0,0,.5)} +#vaudit .vdot{flex:0 0 auto;width:8px;height:8px;border-radius:50%;background:#221f1b;border:1px solid #33302b} +#vaudit .vx{margin-left:6px;color:var(--steel);padding:0 2px} +#vaudit .vx:hover{color:var(--fail)} +/* validation tally in the index: .vdot mirrors the lamp look but must NOT match + .vlamp — the counter counts .vlamp nodes */ +.vtally{margin-top:2px} +.vtally .vrow{display:flex;align-items:center;gap:7px;color:var(--dim);font-size:.75rem; + letter-spacing:.04em;padding:2.5px 0} +.vtally .vrow[data-v]{cursor:pointer} +.vtally .vrow[data-v]:hover{color:var(--gold-hi)} +.vtally .vdot{flex:0 0 auto;width:8px;height:8px;border-radius:50%; + background:#221f1b;border:1px solid #33302b} +.vtally .vn{margin-left:auto;color:var(--cream);font-variant-numeric:tabular-nums} +.vtally .vtot{border-top:1px solid var(--wash);margin-top:3px;padding-top:4.5px;color:var(--steel)} .stagew{background:var(--well);border:1px solid #201d17;border-radius:9px;padding:14px 12px; min-height:78px;display:flex;align-items:center;justify-content:center;gap:12px;flex-wrap:wrap} .wrd{color:var(--gold-hi);font-size:.8rem;letter-spacing:.06em;font-variant-numeric:tabular-nums; @@ -176,6 +195,8 @@ h2::after{content:"";height:1px;background:var(--wash);flex:1} <a href="#sec-meters">Meters & gauges</a> <a href="#sec-indicators">Indicators & readouts</a> <a href="#sec-palette">Palette</a> + <div class="tt" style="margin-top:11px">Validation</div> + <div class="vtally" id="vtally"></div> </nav> </div> </header> @@ -366,7 +387,7 @@ const INFO={ limits:'Stepping is sequential — jumping to a far position takes several clicks.', origin:'Rotary switches on radios and instruments.',difficulty:'Intuitive.', prefer:'The selection should read like hardware, not a menu.'}, -'25':{input:'Drag to slide the cursor. Drag-only — needs a key idiom for Emacs.', +'25':{input:'Click a numeral, a mark, or between marks for the units; focused, arrows step one unit. Click + keys.', solves:'Reading a value against a long calibrated scale.', use:'Specialty. Shines where the scale itself carries meaning.', limits:'Scale literacy required; poor for quick setting.', @@ -1018,6 +1039,46 @@ 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'}; +/* index tally: recounts every card lamp; setV calls it on every state change. + Clicking a row jumps to the next card in that state (cycles), so a count + that disagrees with a visual scan can be audited card by card. */ +const VJUMP={green:0,amber:0,off:0}; +function updateVTally(){ + const el=$('vtally'); if(!el)return; + const lamps=[...document.querySelectorAll('.vlamp')]; + const n={off:0,amber:0,green:0}; + lamps.forEach(l=>{n[l.dataset.v]=(n[l.dataset.v]||0)+1;}); + el.innerHTML= + `<div class="vrow" data-v="green"><span class="vdot" data-v="green"></span>done<span class="vn">${n.green}</span></div>`+ + `<div class="vrow" data-v="amber"><span class="vdot" data-v="amber"></span>in progress<span class="vn">${n.amber}</span></div>`+ + `<div class="vrow" data-v="off"><span class="vdot"></span>not done<span class="vn">${n.off}</span></div>`+ + `<div class="vrow vtot">total<span class="vn">${lamps.length}</span></div>`; + el.querySelectorAll('.vrow[data-v]').forEach(row=>row.addEventListener('click',()=>{ + VJUMP[row.dataset.v]=0; auditNext(row.dataset.v); + })); +} +/* audit stepper: a row click jumps to the first card in that state and docks a + floating pill; clicking the pill advances through the rest without scrolling + back to the index. The card list is re-read on every step, so lamp changes + mid-audit are picked up. Esc or the x dismisses. */ +const VAUDIT_LBL={green:'done',amber:'in progress',off:'not done'}; +function auditNext(v){ + const cards=[...document.querySelectorAll('.vlamp')].filter(l=>l.dataset.v===v) + .map(l=>l.closest('.card')); + if(!cards.length){auditStop();return;} + const i=VJUMP[v]%cards.length; VJUMP[v]++; + location.hash='';location.hash=cards[i].id; /* re-fire :target ring on repeat visits */ + let pill=$('vaudit'); + if(!pill){pill=document.createElement('div');pill.id='vaudit';document.body.appendChild(pill); + pill.addEventListener('click',e=>{ + if(e.target.classList.contains('vx')){auditStop();return;} + auditNext(pill.dataset.v);});} + pill.dataset.v=v; + pill.innerHTML=`<span class="vdot" data-v="${v==='off'?'':v}"></span>`+ + `${VAUDIT_LBL[v]} ${i+1}/${cards.length} · next ▸<span class="vx" title="stop auditing">✕</span>`; +} +function auditStop(){const p=$('vaudit');if(p)p.remove();} +addEventListener('keydown',e=>{if(e.key==='Escape')auditStop();}); /* 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. */ @@ -1028,7 +1089,7 @@ function card(host, no, name, html, note){ `<div class="stagew">${isBuild?'':html}</div><div class="wrd" id="rd-${no}">—</div>`+ `<div class="opts"></div><div class="wnote">${note}</div>`; const lamp=c.querySelector('.vlamp'), VKEY='gv-'+no, VSTATES=['off','amber','green']; - const setV=v=>{lamp.dataset.v=v;lamp.title=VLAMP_TITLES[v];}; + const setV=v=>{lamp.dataset.v=v;lamp.title=VLAMP_TITLES[v];updateVTally();}; setV(localStorage.getItem(VKEY)||'off'); lamp.addEventListener('click',e=>{e.stopPropagation(); const v=VSTATES[(VSTATES.indexOf(lamp.dataset.v)+1)%3]; @@ -1070,7 +1131,7 @@ card(C,'05','Rotary knob', '<b>dial in a value.</b> 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)}), - '<b>pick one of a few.</b> Timer type, layout mode. Click a segment; readout names the choice.'); + '<b>pick one of a few.</b> Timer type, layout mode. Click a segment; readout names the choice. The lit segment ships in amber, green, or red — the accent chips below switch it.'); card(C,'07','Chip toggle', (st,rd)=>GW.chipToggle(st,{on:true,onChange:(v,t)=>rd(t)}), '<b>inline binary.</b> A soft toggle inside a line of text. Click to flip; gold when on.'); @@ -1085,7 +1146,7 @@ card(C,'24','Rotary selector', '<b>pick one of N by position.</b> Printed detents, the pointer names the value. Click to turn; readout shows it.'); card(C,'25','Slide-rule dial', (st,rd)=>GW.slideRule(st,{onChange:(v,t)=>rd(t)}), - '<b>value on a printed scale.</b> A lit pointer glides a warm-backlit strip. Click a mark to jump, or focus it and press ←/→ (↑/↓); readout shows it.'); + '<b>value on a printed scale.</b> A lit pointer glides a warm-backlit strip. Printed numerals are the majors; the units between them carry minor ticks. Click any of them to jump, or focus and press ←/→ (↑/↓) to step a unit; readout shows it. Four faces — warm backlit, chrome, 80s black glass, marantz blue — on the chips below.'); card(C,'N01','Rocker power switch', (st,rd)=>GW.rocker(st,{on:true,onChange:(v,t)=>rd(t)}), '<b>hard on / off, lit legend.</b> A master power paddle — the pressed half glows. Click to rock.'); @@ -1127,7 +1188,7 @@ card(C,'R04','Bakelite fluted knob', '<b>the skirted console knob.</b> Scalloped bakelite skirt, glossy dome, amber index over a printed 0-10 scale. Drag up/down to turn. After a vintage console mixer knob.'); card(C,'R05','Filter slider bank', (st,rd)=>GW.filterBank(st,{onChange:(v,t)=>rd(t)}), - '<b>a wall of band faders.</b> One slider per band, arrow-head caps on black tracks, dB rail at the side. Drag any cap; readout names band and level. After a variable multi-band filter.'); + '<b>a wall of band faders.</b> Twelve bands on a screwed faceplate, dB rails both sides. Drag any cap; readout names band and level. Skins split three independent axes — panel (silver hi-fi after the Pioneer SG-9500 / studio black after the Technics SH-8065), cap shape (tall block fader after the Zaxcom Oasis / short ribbed / chrome T), and cap color (black white-index / red / green / blue / amber stripes / chrome / cream). The chips below mix them freely.'); card(C,'R06','Chicken-head selector', (st,rd)=>GW.chickenHead(st,{index:2,onChange:(i,t)=>rd(t)}), '<b>the pointer-lever switch.</b> The tapered bakelite lever IS the indicator — it aims at the engraved position. Click to step through. After a modulator mode switch.'); @@ -1523,24 +1584,33 @@ BOOST.forEach(no=>{const rd=document.getElementById('rd-'+no); el.innerHTML=el.innerHTML.replace(/\b(R\d{2}|N\d{2})\b/g, m=>ids.has(m)?`<a class="xref" href="#card-${m}">${m}</a>`:m);});})(); -/* slide-toggle style chips: demo rig for GW.slideToggle's constructor opts — - the named styles live in GW.slideToggle.STYLES; chips drive setStyle on the live instance */ -(function(){const h=$('card-01')?.gw;if(!h)return; +/* style chips: shared demo rig for builders with constructor style opts — + reads a builder's STYLES table and drives setStyle on the live card instance */ +function styleChips(no,STYLES,AXES){ + const h=$('card-'+no)?.gw; if(!h)return; const cardEl=h.el.closest('.card'); const row=document.createElement('div'); row.className='famchips'; - const AXES=[['on','on','amber'],['off','off','dark'],['off text','offText','white'],['thumb','thumb','light']]; for(const [label,axis,def] of AXES){ const g=document.createElement('span'); g.className='fgroup'; const lab=document.createElement('span'); lab.className='lab'; lab.textContent=label; g.appendChild(lab); const chips=[]; - for(const [name,o] of Object.entries(GW.slideToggle.STYLES[axis])){ + for(const [name,o] of Object.entries(STYLES[axis])){ const b=document.createElement('span'); b.className='fc'+(name===def?' on':''); b.style.background=o.dot; b.title=name; chips.push(b); b.addEventListener('click',()=>{h.setStyle(axis,name); chips.forEach(x=>x.classList.toggle('on',x===b));}); g.appendChild(b);} row.appendChild(g);} - cardEl.querySelector('.opts').appendChild(row);})(); + cardEl.querySelector('.opts').appendChild(row); +} +styleChips('01',GW.slideToggle.STYLES,[['on','on','amber'],['off','off','dark'],['off text','offText','white'],['thumb','thumb','light']]); +styleChips('R05',GW.filterBank.STYLES,[['panel','panel','silver'],['shape','caps','block'],['color','capColor','black']]); +styleChips('06',GW.segmented.STYLES,[['accent','accent','amber']]); +styleChips('25',GW.slideRule.STYLES,[['face','skin','warm']]); + +/* final tally pass: setV fires per card during build, but each card is still + detached at that moment, so the running counts lag by one — recount now */ +updateVTally(); </script> </body> </html> |
