From d8610b010d8a929f81af37b004a6c10f4df78c60 Mon Sep 17 00:00:00 2001 From: Ramon Date: Thu, 30 Jul 2026 16:46:37 +0200 Subject: [PATCH] feat: compacter instellingenmenu voor acht widgets (v0.4.80-beta) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bij veel widgets nam de instellingenstrook te veel hoogte in, vooral bij een smal widgetvenster waar de rij naar meerdere regels uitwaaierde. Nieuw gedeeld patroon: besturing die voortdurend nodig is (kleur, niveau, woordbron) blijft in één compacte rij zichtbaar; besturing die maar zelden wordt aangepast (thema, geluid aan/uit, invoerwijze, bereik) gaat achter een inklapbaar ⚙-paneel. Dit patroon bestond al voor klok/geld/rekengetallen/ wereld (learningShell() in learning.js) - nu als widgetGear()/gearField() in core.js beschikbaar voor de rest van de app, met dezelfde .edu-settings- opmaak zodat het overal hetzelfde oogt en werkt. - kleurplaten: twee volle rijen (thema/plaat + kwast/emmer/gum/kleur/dikte/ hulp/reset/klaar) teruggebracht naar één vaste rij (gereedschap, kleur, dikte, klaar) plus het ingeklapte paneel (thema, plaat, kleurhulp, reset). Losstaand: coloring.css gebruikte @media(max-width:620px) in plaats van de @container widget-conventie die de rest van de app gebruikt, waardoor die regels nooit afgingen bij het verkleinen van het widgetvenster zelf - nu gecorrigeerd. - letterblokken: geluid, invoerwijze en aantal kaarten in het paneel; niveau en woordbron blijven vast. - rekenwidgets (optellen/aftrekken/keer/delen/procenten): geluid en invoerwijze in het paneel; niveau blijft vast. - ballonnenwoord: geluid in het paneel. - flitswoorden: flitstijd in het paneel. - getallenlijn: het hele bereik-instelblok (van/tot/stap/nieuw) in het paneel - dat stond toch al uit voor leerlingen met een beheerd niveau. - zinsbouwer: het rijtje voor eigen woorden toevoegen in het paneel; de speelknoppen (verrassing/ongedaan/wissen/voorlezen) blijven vast. - timer: geluid en weergavevorm in het paneel; starttijden en start/reset blijven vast. - geluidsmeter: de twee drempelschuifjes plus de privacynotitie in het paneel. Widgets die dit patroon al hadden (klok, geld, breuken, plaatswaarde, woordsoorten, bomen/vogels/planten, topografie, stemming, stemming-poll, dagplanning) of waar de rij al compact genoeg was (schrijfblad, hakken en plakken, dobbelstenen, notities, naamtrekker) zijn ongewijzigd gelaten. Geverifieerd door elke aangepaste widget live via de echte makeWidget()/ REGISTRY in een headless browser te openen, het paneel open en dicht te klappen en te controleren op consolefouten - ook op de minimale widgetbreedte (coloring, minW 390). 202 tests groen. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_011YErxhic5w9K57zmt8Q1uS --- VERSION | 2 +- public/css/coloring.css | 30 ++++++++++++++++++------------ public/js/core.js | 29 ++++++++++++++++++++++++++--- public/js/widgets/ball.js | 5 +++-- public/js/widgets/coloring.js | 16 +++++++++------- public/js/widgets/flits.js | 5 +++-- public/js/widgets/letters.js | 15 +++++++++------ public/js/widgets/math.js | 6 +++--- public/js/widgets/noise.js | 14 +++++++++----- public/js/widgets/numberline.js | 6 ++++-- public/js/widgets/sentence.js | 6 ++++-- public/js/widgets/timer.js | 6 ++++-- 12 files changed, 93 insertions(+), 47 deletions(-) diff --git a/VERSION b/VERSION index 565bcc8..da7f3b8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.4.79-beta +0.4.80-beta diff --git a/public/css/coloring.css b/public/css/coloring.css index d7cc68a..d0986bd 100644 --- a/public/css/coloring.css +++ b/public/css/coloring.css @@ -6,11 +6,15 @@ bestand zet nooit een `fill` op een kleurvak - dat zou van de vulling van de verfemmer winnen en de verf onzichtbaar maken. */ .coloring-app{--coloring-ink:#1c293d;--coloring-detail:#2d3e56;height:100%;min-height:0;display:flex;flex-direction:column;background:linear-gradient(180deg,color-mix(in srgb,var(--orange) 4%,var(--surface)),var(--surface));color:var(--ink)} -.coloring-choices,.coloring-tools{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:9px 11px;border-bottom:1px solid color-mix(in srgb,var(--line) 72%,var(--orange));background:linear-gradient(100deg,color-mix(in srgb,var(--orange) 10%,var(--surface)),color-mix(in srgb,var(--purple) 5%,var(--surface)))} +/* de veelgebruikte rij (kwast/emmer/gum, kleur, dikte) blijft altijd + zichtbaar; thema, plaat, kleurhulp en reset - die je zelden aanpast - + zitten achter het ⚙-tandwiel (.edu-settings, zie teach.css) zodat ze niet + permanent ruimte innemen naast wat je continu gebruikt */ +.coloring-tools{display:flex;align-items:center;gap:7px;flex-wrap:wrap;padding:9px 11px;border-bottom:1px solid color-mix(in srgb,var(--line) 72%,var(--orange));background:color-mix(in srgb,var(--surface) 96%,var(--orange))} +.coloring-choices{gap:8px} .coloring-choices label{display:grid;grid-template-columns:auto minmax(120px,1fr);align-items:center;gap:6px;color:var(--muted);font-size:11px;font-weight:850} .coloring-choices select{height:36px;max-width:230px;border:1px solid color-mix(in srgb,var(--line) 72%,var(--orange));border-radius:11px;padding:0 30px 0 10px;background:var(--surface);color:var(--ink);box-shadow:0 2px 8px rgba(30,50,90,.07);font:inherit;font-weight:750} -.coloring-fit{margin-left:auto;padding:6px 10px;border:1px solid color-mix(in srgb,var(--line) 75%,var(--orange));border-radius:999px;background:color-mix(in srgb,var(--surface) 92%,var(--orange));color:var(--muted);box-shadow:0 2px 8px rgba(30,50,90,.06);font-size:11px;font-weight:800;white-space:nowrap} -.coloring-tools{gap:7px;background:color-mix(in srgb,var(--surface) 96%,var(--orange))} +.coloring-fit{padding:6px 10px;border:1px solid color-mix(in srgb,var(--line) 75%,var(--orange));border-radius:999px;background:color-mix(in srgb,var(--surface) 92%,var(--orange));color:var(--muted);box-shadow:0 2px 8px rgba(30,50,90,.06);font-size:11px;font-weight:800;white-space:nowrap} .coloring-tool-buttons,.coloring-palette,.coloring-sizes{display:flex;align-items:center;gap:4px;padding:4px;border:1px solid color-mix(in srgb,var(--line) 78%,transparent);border-radius:13px;background:linear-gradient(180deg,color-mix(in srgb,var(--surface-2) 82%,#fff),var(--surface-2));box-shadow:inset 0 1px 0 rgba(255,255,255,.72),0 2px 7px rgba(30,50,90,.06)} .coloring-tools button{width:36px;height:36px;display:grid;place-items:center;border:0;border-radius:10px;background:transparent;color:var(--ink);font:inherit;font-size:18px;cursor:pointer;transition:transform .14s ease,background .14s ease,box-shadow .14s ease} .coloring-tool-buttons button{width:auto;min-width:72px;grid-auto-flow:column;grid-template-columns:auto auto;gap:5px;padding:0 9px} @@ -24,10 +28,10 @@ .coloring-size.on{outline:2px solid var(--orange);outline-offset:-2px} .coloring-custom{display:flex;align-items:center;gap:5px;color:var(--muted);font-size:10px;font-weight:800} .coloring-custom input{width:36px;height:36px;padding:3px;border:1px solid var(--line);border-radius:10px;background:var(--surface);box-shadow:0 2px 7px rgba(30,50,90,.08);cursor:pointer} -.coloring-tools .coloring-guide-btn{margin-left:auto;background:color-mix(in srgb,var(--teal) 14%,var(--surface));box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--teal) 20%,transparent)} -.coloring-tools .coloring-guide-btn.on{outline:2px solid var(--teal);outline-offset:-2px} -.coloring-tools .coloring-reset{background:var(--red-soft);color:var(--red);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--red) 18%,transparent)} -.coloring-tools .coloring-done{width:auto;min-width:112px;padding:0 14px;color:#fff;box-shadow:0 4px 10px color-mix(in srgb,var(--orange) 24%,transparent);font-size:13px} +.coloring-choices .coloring-guide-btn{background:color-mix(in srgb,var(--teal) 14%,var(--surface));box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--teal) 20%,transparent)} +.coloring-choices .coloring-guide-btn.on{outline:2px solid var(--teal);outline-offset:-2px} +.coloring-choices .coloring-reset{background:var(--red-soft);color:var(--red);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--red) 18%,transparent)} +.coloring-tools .coloring-done{width:auto;min-width:112px;margin-left:auto;padding:0 14px;color:#fff;box-shadow:0 4px 10px color-mix(in srgb,var(--orange) 24%,transparent);font-size:13px} .coloring-stage{flex:1;min-height:0;overflow:auto;display:grid;place-items:center;padding:18px;background:radial-gradient(circle at 12% 18%,color-mix(in srgb,var(--orange) 13%,transparent),transparent 31%),radial-gradient(circle at 88% 82%,color-mix(in srgb,var(--purple) 10%,transparent),transparent 34%),linear-gradient(145deg,var(--surface-2),color-mix(in srgb,var(--purple) 5%,var(--surface)))} /* het papier vangt alle aanwijzing op; binnen de SVG wordt meetkundig aangewezen met isPointInFill/isPointInStroke */ @@ -57,17 +61,19 @@ .coloring-app[data-tool="eraser"] .coloring-paper{cursor:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30'%3E%3Ctext y='24' font-size='23'%3E%F0%9F%A7%BD%3C/text%3E%3C/svg%3E") 6 24,pointer} .widget[data-widget="coloring"]{--widget-accent:var(--orange)} -@media(max-width:620px){ - .coloring-choices,.coloring-tools{padding:6px;gap:4px} +/* @container in plaats van @media: dit moet reageren op de breedte van het + widget-venster (dat kan smal staan op een brede monitor), niet op de + breedte van de hele browser - zie .widget-body{container-name:widget} */ +@container widget (max-width:620px){ + .coloring-tools{padding:6px;gap:4px} .coloring-choices label{grid-template-columns:1fr;gap:2px;flex:1;min-width:125px} .coloring-choices select{width:100%;max-width:none;height:32px} - .coloring-fit{width:100%;margin:0;text-align:center} + .coloring-fit{width:100%;text-align:center} .coloring-tools button{width:32px;height:32px;font-size:16px} .coloring-tool-buttons button{min-width:36px;width:36px;padding:0}.coloring-tool-label{display:none} .coloring-color i{width:18px;height:18px} .coloring-custom span{display:none}.coloring-custom input{width:32px;height:32px} - .coloring-tools .coloring-guide-btn{margin-left:0} - .coloring-tools .coloring-done{min-width:90px;font-size:11px} + .coloring-tools .coloring-done{min-width:90px;margin-left:0;font-size:11px} .coloring-stage{place-items:start center;padding:8px} .coloring-paper{min-width:360px;border-radius:10px} .coloring-hint{font-size:10px} diff --git a/public/js/core.js b/public/js/core.js index 3e0135e..f6de1ad 100644 --- a/public/js/core.js +++ b/public/js/core.js @@ -2,7 +2,7 @@ "use strict"; /* version — shown until /api/version resolves (or if the fetch fails, e.g. offline). Kept in sync by hand with the VERSION file at the repo root on every release. */ -const VERSION = "0.4.79-beta"; +const VERSION = "0.4.80-beta"; /* Safari en oudere mobiele browsers behandelen 100vh/100% soms als de hoogte achter hun adres- en navigatiebalk. visualViewport geeft de werkelijk zichtbare hoogte; de CSS gebruikt deze pixelwaarde als betrouwbare bron en @@ -118,7 +118,7 @@ const I18N = { wFull:"Maximaliseren", wUnfull:"Terugzetten", wZoom:"Inhoud groter of kleiner (voor slechtzienden)", wg_flits:"Flitswoorden", wg_flits_d:"Een woord flitst kort in beeld – lezen voordat het verdwijnt!", - flFlash:"⚡ Flits", flShow:"👁 Toon", flNext:"Volgende ▸", flSec:"sec", flMission:"Lees het woord voordat het verdwijnt.", flQuestion:"Kon je het woord zelf lezen?", flGotIt:"🌟 Gelukt!", flAgain:"Nog oefenen", flScore:"{score} sterren", + flFlash:"⚡ Flits", flShow:"👁 Toon", flNext:"Volgende ▸", flSec:"sec", flDurLabel:"Flitstijd", flMission:"Lees het woord voordat het verdwijnt.", flQuestion:"Kon je het woord zelf lezen?", flGotIt:"🌟 Gelukt!", flAgain:"Nog oefenen", flScore:"{score} sterren", wg_dice:"Dobbelstenen", wg_dice_d:"Gooi 1, 2 of 3 grote dobbelstenen.", diceRoll:"🎲 Gooi!", diceSum:"Samen", diceMission:"Gooi, tel de ogen en controleer de uitkomst.", diceHistory:"Vorige worpen", wg_light:"Klassenstoplicht", wg_light_d:"Maak in één oogopslag duidelijk of kinderen luisteren, fluisteren of rustig mogen werken.", @@ -551,7 +551,7 @@ const I18N = { wFull:"Maximize", wUnfull:"Restore", wZoom:"Content bigger or smaller (low vision)", wg_flits:"Flash words", wg_flits_d:"A word flashes briefly – read it before it disappears!", - flFlash:"⚡ Flash", flShow:"👁 Show", flNext:"Next ▸", flSec:"sec", flMission:"Read the word before it disappears.", flQuestion:"Could you read the word by yourself?", flGotIt:"🌟 Got it!", flAgain:"Practise again", flScore:"{score} stars", + flFlash:"⚡ Flash", flShow:"👁 Show", flNext:"Next ▸", flSec:"sec", flDurLabel:"Flash time", flMission:"Read the word before it disappears.", flQuestion:"Could you read the word by yourself?", flGotIt:"🌟 Got it!", flAgain:"Practise again", flScore:"{score} stars", wg_dice:"Dice", wg_dice_d:"Roll 1, 2 or 3 big dice.", diceRoll:"🎲 Roll!", diceSum:"Total", diceMission:"Roll, count the dots and check the total.", diceHistory:"Previous rolls", wg_light:"Class traffic light", wg_light_d:"Show at a glance whether children listen, whisper or work quietly.", @@ -917,6 +917,29 @@ const T = k => I18N[LANG][k] ?? I18N.nl[k] ?? k; are visual pixels, absolute positions are layout pixels → divide by VZ() */ const VZ = ()=>{ const z = parseFloat(document.body.style.zoom); return z>0 ? z : 1; }; +/* Herbruikbaar instellingen-tandwiel: zeldzaam gebruikte besturing (thema, + niveau-stijl, achtergrond...) gaat ingeklapt achter een ⚙, zodat die niet + in dezelfde rij hoeft te concurreren met knoppen die een leerling + voortdurend gebruikt (kwast, kleur, dikte). Zelfde
-opbouw en + CSS-klassen als learningShell() in widgets/learning.js, maar los + bruikbaar: de aanroeper bepaalt zelf waar het tandwiel in de eigen + layout wordt ingevoegd. */ +function widgetGear(controls,label){ + const details=document.createElement("details");details.className="edu-settings widget-gear"; + const summary=document.createElement("summary"); + summary.append(document.createTextNode("⚙ ")); + const span=document.createElement("span");span.textContent=label||T("widgetSettings"); + summary.appendChild(span); + const box=document.createElement("div");box.className="edu-controls"; + box.append(...controls); + details.append(summary,box); + return details; +} +/* Kleine bouwstenen voor widgetGear()-inhoud, zelfde vorm als de + gelijknamige helpers in widgets/learning.js (bewust hier gedupliceerd in + plaats van gedeeld, want learning.js laadt pas ná de meeste widgets). */ +function gearField(label,el){const w=document.createElement("label");const s=document.createElement("span");s.textContent=label;w.append(s,el);return w} + function applyI18n(){ document.querySelectorAll("[data-i18n]").forEach(el=>{ el.textContent = T(el.dataset.i18n); }); document.querySelectorAll("#langToggle button").forEach(b=>b.classList.toggle("active", b.dataset.lang===LANG)); diff --git a/public/js/widgets/ball.js b/public/js/widgets/ball.js index 1c9d4ae..a5a81b0 100644 --- a/public/js/widgets/ball.js +++ b/public/js/widgets/ball.js @@ -12,7 +12,6 @@ function mountBall(root, initState, opts){ root.innerHTML = `
-
@@ -26,10 +25,12 @@ function mountBall(root, initState, opts){
`; const el = s => root.querySelector(s); + const sndBtn=document.createElement("button");sndBtn.type="button";sndBtn.className="lg-snd"; + el(".mt-top").insertAdjacentElement("beforebegin",widgetGear([gearField(T("sndWidget"),sndBtn)])); const srcSel = el(".bl-src"), balloonsEl = el(".bl-balloons"), picEl=el(".bl-pic"), wordEl = el(".bl-word"), msgEl = el(".bl-msg"), nextBtn = el(".bl-next"), kbdEl = el(".bl-kbd"), - scoreEl = el(".bl-score"), sndBtn = el(".lg-snd"); + scoreEl = el(".bl-score"); function list(){ if(S.assignmentWords?.length)return languageWidgetWords(S.assignmentWords,S.yearGroup,S.level,"ball"); diff --git a/public/js/widgets/coloring.js b/public/js/widgets/coloring.js index 37ce940..dd92647 100644 --- a/public/js/widgets/coloring.js +++ b/public/js/widgets/coloring.js @@ -191,20 +191,21 @@ function mountColoring(root,initState,opts={}){ const clipStrokes=coloringClipsStrokes(yearGroup,level); root.innerHTML=`
-
- - - -
- -
+
+
+ + + + + +
`; @@ -337,6 +338,7 @@ function mountColoring(root,initState,opts={}){ return T(tool==="bucket"?"colorBucketHint":tool==="eraser"?"colorEraserHint":"colorBrushHint")+" "+levelHint(); } function updateTexts(){ + root.querySelector(".coloring-settings-label").textContent=T("widgetSettings"); root.querySelector(".coloring-theme-label").textContent=T("colorTheme"); root.querySelector(".coloring-page-label").textContent=T("colorPage"); root.querySelector(".coloring-fit").textContent=T("colorProfile").replace("{year}",yearGroup).replace("{level}",level); diff --git a/public/js/widgets/flits.js b/public/js/widgets/flits.js index 3432108..12a4631 100644 --- a/public/js/widgets/flits.js +++ b/public/js/widgets/flits.js @@ -13,7 +13,6 @@ function mountFlits(root, initState, opts){
-

@@ -32,7 +31,9 @@ function mountFlits(root, initState, opts){
`; const el = s => root.querySelector(s); - const srcSel = el(".fl-src"), durSel = el(".fl-dur"), + const durSel=document.createElement("select");durSel.className="fl-sel fl-dur"; + el(".mt").insertBefore(widgetGear([gearField(T("flDurLabel"),durSel)]),el(".mt-top").nextSibling); + const srcSel = el(".fl-src"), wordEl = el(".fl-word"), picEl = el(".fl-pic"), cntEl = el(".fl-cnt"), reflect=el(".fl-reflect"); function list(){ diff --git a/public/js/widgets/letters.js b/public/js/widgets/letters.js index f6b222a..3139cf0 100644 --- a/public/js/widgets/letters.js +++ b/public/js/widgets/letters.js @@ -21,13 +21,10 @@ function mountLetterGame(root, initState, opts){ root.innerHTML = `
- -
-
@@ -89,8 +86,16 @@ function mountLetterGame(root, initState, opts){ instrEl = el(".lg-instr"), nextBtn = el(".lg-next"), doneEl = el(".lg-done"), countEl = el(".lg-count"), starsEl = el(".lg-stars"), cardsEl = el(".lg-cards"); + /* zeldzaam gebruikte besturing (geluid, invoerwijze, aantal kaarten) achter + een tandwiel: die concurreerden eerder met de niveauknoppen om ruimte in + één brede rij, terwijl ze maar zelden worden aangepast */ + const numSel=document.createElement("select");numSel.className="lg-num"; + const sndBtn=document.createElement("button");sndBtn.type="button";sndBtn.className="lg-snd"; + const modeBtn=document.createElement("button");modeBtn.type="button";modeBtn.className="lg-mode"; + el(".lg-top").insertAdjacentElement("beforebegin",widgetGear([ + gearField(T("sndWidget"),sndBtn),gearField(T("amode"),modeBtn),gearField(T("numWords"),numSel) + ])); /* card buttons (built dynamically: N cards + star + edit) */ - const numSel = root.querySelector(".lg-num"); function buildNumSel(){ numSel.innerHTML = ""; [4,6,8,10,"all"].forEach(v=>{ @@ -106,7 +111,6 @@ function mountLetterGame(root, initState, opts){ S.count = numSel.value==="all" ? "all" : +numSel.value; newRound(); render(); }); - const sndBtn = root.querySelector(".lg-snd"); sndBtn.addEventListener("click", ()=>{ S.snd = !S.snd; updateSndBtn(); @@ -119,7 +123,6 @@ function mountLetterGame(root, initState, opts){ modeBtn.title = T("amode"); root.querySelector(".lg").classList.toggle("typemode", S.amode==="type"); } - const modeBtn = root.querySelector(".lg-mode"); modeBtn.addEventListener("click", ()=>{ S.amode = S.amode==="blocks" ? "type" : "blocks"; updateSndBtn(); diff --git a/public/js/widgets/math.js b/public/js/widgets/math.js index 3c42b16..adb255d 100644 --- a/public/js/widgets/math.js +++ b/public/js/widgets/math.js @@ -21,8 +21,6 @@ function mountMath(root, initState, mode, opts){ root.innerHTML = `
- -
@@ -56,9 +54,11 @@ function mountMath(root, initState, mode, opts){ fbEl.appendChild(cheer); } } + const sndBtn=document.createElement("button");sndBtn.type="button";sndBtn.className="lg-snd"; + const modeBtn=document.createElement("button");modeBtn.type="button";modeBtn.className="lg-mode"; + el(".mt-top").insertAdjacentElement("beforebegin",widgetGear([gearField(T("sndWidget"),sndBtn),gearField(T("amode"),modeBtn)])); const mtEl = el(".mt"), qEl = el(".mt-q"), aEl = el(".mt-a"), fbEl = el(".mt-fb"), scoreEl = el(".mt-score"), doneEl = el(".mt-done"), - sndBtn = el(".lg-snd"), modeBtn = el(".lg-mode"), padEl = el(".mt-pad"), slotsEl = el(".mt-slots"), trayEl = el(".mt-tray"); /* ---- digit blocks with dot patterns (like the letter blocks) ---- */ diff --git a/public/js/widgets/noise.js b/public/js/widgets/noise.js index aac7794..fb25c95 100644 --- a/public/js/widgets/noise.js +++ b/public/js/widgets/noise.js @@ -21,11 +21,14 @@ function mountNoise(root, initState){
-
- - -
-

+
+
+
+ + +
+

+
`; const gauge = root.querySelector(".nm-gauge"), fill = root.querySelector(".nm-fill"), @@ -112,6 +115,7 @@ function mountNoise(root, initState){ watchWarn(); function labels(){ root.querySelector(".nm-mission").textContent = T("wg_noise_d"); + root.querySelector(".nm-settings-label").textContent = T("widgetSettings"); toggle.textContent = running ? "⏸ " + T("noiseStop") : "🎙️ " + T("noiseStart"); root.querySelectorAll(".nm-zone-lbl")[0].textContent = T("noiseZoneGreen"); root.querySelectorAll(".nm-zone-lbl")[1].textContent = T("noiseZoneYellow"); diff --git a/public/js/widgets/numberline.js b/public/js/widgets/numberline.js index 5107887..341b1fd 100644 --- a/public/js/widgets/numberline.js +++ b/public/js/widgets/numberline.js @@ -5,12 +5,13 @@ function mountNumberline(root, initState){ if(S.managedLearningLevel&&rules){S.min=rules.numberlineMin;S.max=rules.numberlineMax;S.step=rules.numberlineStep;S.value=S.min;S.target=S.min+S.step;} root.innerHTML=`
-
+
+
-
+
@@ -62,6 +63,7 @@ function mountNumberline(root, initState){ if(ok){if(!S.solved){S.score++;S.streak++;S.solved=true;}sndGood();confetti(root);}else{S.streak=0;S.solved=false;sndBad();}render(); }); function labels(){ + q('.nlw-settings-label').textContent=T('widgetSettings'); q('.nlw-from-label').textContent=T('numberlineFrom'); q('.nlw-to-label').textContent=T('numberlineTo'); q('.nlw-step-label').textContent=T('numberlineStep'); q('.nlw-new').textContent=T('numberlineNew'); q('.nlw-check').textContent=T('numberlineCheck'); render(); diff --git a/public/js/widgets/sentence.js b/public/js/widgets/sentence.js index b266e31..50274f8 100644 --- a/public/js/widgets/sentence.js +++ b/public/js/widgets/sentence.js @@ -26,11 +26,12 @@ function mountSentence(root, initState){
-
+
+
-
+
`; const q=s=>root.querySelector(s), sentenceEl=q('.sb-sentence'), banksEl=q('.sb-banks'), kind=q('.sb-kind'), word=q('.sb-word'); @@ -64,6 +65,7 @@ function mountSentence(root, initState){ q('.sb-surprise').addEventListener('click',()=>{S.sentence=['who','does','what'].filter(k=>S.banks[k].length).map(kind=>({kind,word:S.banks[kind][Math.floor(Math.random()*S.banks[kind].length)]}));renderSentence();sndGood();confetti(sentenceEl)}); q('.sb-speak').addEventListener('click',()=>{const text=sentenceText();if(text&&'speechSynthesis'in window){speechSynthesis.cancel();const u=new SpeechSynthesisUtterance(text);u.lang=LANG==='nl'?'nl-NL':'en-GB';speechSynthesis.speak(u);}}); function labels(){ + q('.sb-add-label').textContent=T('widgetSettings'); [...kind.options].forEach(o=>o.textContent=T('sentence_'+o.value)); word.placeholder=T('sentenceAdd'); q('.sb-surprise').textContent=T('sentenceSurprise');q('.sb-clear').textContent=T('sentenceClear'); q('.sb-speak').textContent=T('sentenceSpeak'); renderSentence();renderBanks(); } diff --git a/public/js/widgets/timer.js b/public/js/widgets/timer.js index 78b7ad7..c2d854e 100644 --- a/public/js/widgets/timer.js +++ b/public/js/widgets/timer.js @@ -14,14 +14,15 @@ function mountTimer(root, initState){ } root.innerHTML = `
-
+
+
-
+
@@ -78,6 +79,7 @@ function mountTimer(root, initState){ drawPie(); } function labels(){ + root.querySelector(".topts-label").textContent = T("widgetSettings"); bstart.textContent = (iv ? "⏸ "+T("pause") : "▶ "+T("start")); breset.textContent = "↺ "+T("reset"); presets.querySelectorAll("button").forEach(b=>b.textContent = b.dataset.m+" "+T("min"));