aboutsummaryrefslogtreecommitdiff
path: root/docs/prototypes/panel-widget-gallery.html
diff options
context:
space:
mode:
Diffstat (limited to 'docs/prototypes/panel-widget-gallery.html')
-rw-r--r--docs/prototypes/panel-widget-gallery.html71
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 &amp; gauges</a>
<a href="#sec-indicators">Indicators &amp; 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>