diff options
| author | Craig Jennings <c@cjennings.net> | 2026-07-13 00:15:45 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-07-13 00:15:45 -0500 |
| commit | 08fbe0c730dfeba4fd1d674544a37184a6467277 (patch) | |
| tree | 857a4375a39b195afd2f3ddaf8996658b7a42b58 /docs | |
| parent | d9e6e50b676f15187e341aa156d62f2c7e202d9f (diff) | |
| download | archsetup-08fbe0c730dfeba4fd1d674544a37184a6467277.tar.gz archsetup-08fbe0c730dfeba4fd1d674544a37184a6467277.zip | |
feat(gallery): add a live validation tally with an audit stepper
The index panel gains a Validation section: done / in progress / not done counts with a total, each row carrying a mini lamp dot in the matching state color. Every lamp click recounts, so the tally always mirrors the cards.
Clicking a row starts an audit of that state: the page jumps to the first matching card and a pill docks bottom-right showing the position (2/5 · next). Clicking the pill advances through the rest without scrolling back to the index, the x or Esc dismisses it, and the card list re-reads on every step so lamp changes mid-audit are picked up.
setV fires while its card is still detached from the document, so the running count lags the last card by one during the build. A final recount after all cards land closes the gap.
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/prototypes/panel-widget-gallery.html | 71 |
1 files changed, 68 insertions, 3 deletions
diff --git a/docs/prototypes/panel-widget-gallery.html b/docs/prototypes/panel-widget-gallery.html index 2b33ec5..63695df 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> @@ -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]; @@ -1560,6 +1621,10 @@ BOOST.forEach(no=>{const rd=document.getElementById('rd-'+no); g.appendChild(b);} row.appendChild(g);} cardEl.querySelector('.opts').appendChild(row);})(); + +/* 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> |
