All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 10s
- ball.js/flits.js/hak.js: S.card bewaart de geselecteerde bronwaarde nu altijd als string i.p.v. te casten naar number, zodat de bronselectie correct blijft werken - AGENTS.md en CLAUDE.md toegevoegd met projectafspraken Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
90 lines
3 KiB
JavaScript
90 lines
3 KiB
JavaScript
/* widget: Flitswoorden */
|
|
/* =========================================================
|
|
Flash words widget
|
|
==========================================================*/
|
|
function mountFlits(root, initState){
|
|
const S = { card:0, dur:2, idx:0, order:[] };
|
|
if(initState){ S.card = initState.card ?? 0; S.dur = initState.dur || 2; }
|
|
let flashTimer = null;
|
|
|
|
root.innerHTML = `
|
|
<div class="mt">
|
|
<div class="mt-top">
|
|
<select class="fl-sel fl-src"></select>
|
|
<select class="fl-sel fl-dur"></select>
|
|
<div class="mt-score fl-cnt"></div>
|
|
</div>
|
|
<div class="mt-card">
|
|
<div class="fl-word dim">• • •</div>
|
|
<div class="fl-pic"></div>
|
|
</div>
|
|
<div class="trow" style="display:flex;gap:10px;justify-content:center;flex:none;">
|
|
<button class="tbtn fl-flash"></button>
|
|
<button class="tbtn ghost fl-show"></button>
|
|
<button class="tbtn ghost fl-next"></button>
|
|
</div>
|
|
</div>`;
|
|
const el = s => root.querySelector(s);
|
|
const srcSel = el(".fl-src"), durSel = el(".fl-dur"),
|
|
wordEl = el(".fl-word"), picEl = el(".fl-pic"), cntEl = el(".fl-cnt");
|
|
|
|
function list(){ S.card = normWordSource(S.card); return wordSourceList(S.card); }
|
|
function buildSrc(){
|
|
fillWordSourceSel(srcSel, S.card);
|
|
durSel.innerHTML = "";
|
|
[0.5,1,2,3,5].forEach(d=>{
|
|
const o = document.createElement("option");
|
|
o.value = d; o.textContent = d+" "+T("flSec");
|
|
durSel.appendChild(o);
|
|
});
|
|
durSel.value = S.dur;
|
|
}
|
|
function newRound(){
|
|
S.order = shuffle(list().map((_,i)=>i));
|
|
S.idx = 0;
|
|
}
|
|
function cur(){ return list()[S.order[S.idx]]; }
|
|
function hide(){
|
|
wordEl.textContent = "• • •";
|
|
wordEl.classList.add("dim");
|
|
picEl.textContent = "";
|
|
}
|
|
function flash(){
|
|
clearTimeout(flashTimer);
|
|
wordEl.textContent = cur()[0];
|
|
wordEl.classList.remove("dim");
|
|
picEl.textContent = "";
|
|
cntEl.textContent = `${T("word")} ${S.idx+1}/${S.order.length}`;
|
|
flashTimer = setTimeout(hide, S.dur*1000);
|
|
}
|
|
function show(){
|
|
clearTimeout(flashTimer);
|
|
wordEl.textContent = cur()[0];
|
|
wordEl.classList.remove("dim");
|
|
setPic(picEl, cur()[1]);
|
|
}
|
|
el(".fl-flash").addEventListener("click", flash);
|
|
el(".fl-show").addEventListener("click", show);
|
|
el(".fl-next").addEventListener("click", ()=>{
|
|
S.idx++;
|
|
if(S.idx >= S.order.length) newRound();
|
|
flash();
|
|
});
|
|
srcSel.addEventListener("change", ()=>{
|
|
S.card = srcSel.value;
|
|
newRound(); hide();
|
|
cntEl.textContent = "";
|
|
});
|
|
durSel.addEventListener("change", ()=>{ S.dur = +durSel.value; });
|
|
function labels(){
|
|
el(".fl-flash").textContent = T("flFlash");
|
|
el(".fl-show").textContent = T("flShow");
|
|
el(".fl-next").textContent = T("flNext");
|
|
buildSrc();
|
|
}
|
|
document.addEventListener("langchange", ()=>{ S.card=0; labels(); newRound(); hide(); });
|
|
document.addEventListener("wordschange", ()=>{ buildSrc(); newRound(); hide(); });
|
|
labels(); newRound();
|
|
return { getState: ()=>({card:S.card, dur:S.dur}) };
|
|
}
|
|
|