diff --git a/VERSION b/VERSION index 1cb92ea..c638d14 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.3.06-beta +0.3.07-beta diff --git a/public/css/teach.css b/public/css/teach.css index ccd6e36..9dad3fa 100644 --- a/public/css/teach.css +++ b/public/css/teach.css @@ -1310,3 +1310,54 @@ body.hc #penbar .pen-current,body.hc #penbar .pen-popover{border:2px solid #000; .pen-current{width:40px; height:36px;} #penbar .pen-popover{position:fixed; left:50%; bottom:70px; transform:translateX(-50%);} } + + +/* ============================================================ + Nieuwe widgets: verjaardagstaart, getallenlijn en zinsbouwer + ============================================================ */ +/* Verjaardagstaart */ +.bd{height:100%; min-height:0; display:grid; grid-template-columns:minmax(180px,220px) 1fr; background:var(--surface);} +.bd-controls{padding:12px; overflow:auto; display:flex; flex-direction:column; gap:8px; background:var(--surface-2); border-right:1px solid var(--line);} +.bd-controls input[type=text],.bd-controls input[type=number],.bd-controls select{ + width:100%; height:36px; border:1px solid var(--line); border-radius:9px; padding:0 9px; background:var(--surface); color:var(--ink); font:inherit; font-size:13px; +} +.bd-controls label{display:flex; align-items:center; justify-content:space-between; gap:8px; font-size:12px; font-weight:800; color:var(--muted);} +.bd-controls input[type=range]{width:96px;}.bd-color input{width:42px; height:30px; padding:1px; border:0; background:transparent; cursor:pointer;} +.bd-check{justify-content:flex-start !important;}.bd-party{margin-top:auto; min-height:38px;} +.bd-stage{min-width:0; position:relative; overflow:hidden; display:flex; flex-direction:column; align-items:center; justify-content:center; padding:54px 16px 34px; background:radial-gradient(circle at 50% 20%,#fff 0,var(--surface-2) 72%);} +.bd-title{position:absolute; top:16px; left:12px; right:12px; text-align:center; font-size:clamp(20px,4vw,34px); font-weight:900; color:var(--pink); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;} +.bd-cake{width:min(100%,390px); display:flex; flex-direction:column; align-items:center; filter:drop-shadow(0 10px 10px rgba(29,52,97,.18)); z-index:2;} +.bd-layer{height:72px; position:relative; background:var(--cake); border-radius:20px 20px 12px 12px; border-bottom:8px solid color-mix(in srgb,var(--cake),#8b4b2b 18%); box-shadow:inset 0 13px 0 var(--icing); flex:none;} +.bd-layer-1{width:94%;}.bd-layer-2{width:76%;}.bd-layer-3{width:58%;} +.bd-deco{position:absolute; inset:16px 8px auto; text-align:center; color:#fff; font-weight:900; letter-spacing:5px; white-space:nowrap; overflow:hidden; text-shadow:0 1px 2px rgba(0,0,0,.25);} +.bd-deco.sprinkles{color:#fff5a5;}.bd-deco.stars{color:#fff0a8;font-size:18px}.bd-deco.fruit{font-size:17px;letter-spacing:2px;} +.bd-candle-row{position:absolute; left:8%; right:8%; top:-31px; height:34px; display:flex; justify-content:center; align-items:flex-end; gap:3px;} +.bd-candle{display:block; width:5px; height:25px; border-radius:2px; background:repeating-linear-gradient(45deg,#fff 0 3px,var(--blue) 3px 6px); position:relative;} +.bd-candle::before{content:""; position:absolute; width:8px; height:12px; left:-1.5px; top:-12px; border-radius:70% 30% 60% 40%; background:linear-gradient(#fff7a0,var(--orange)); box-shadow:0 0 8px var(--orange);} +.bd-age-badge{position:absolute; right:8%; top:20%; z-index:4; min-width:58px; height:58px; padding:0 10px; border-radius:20px; display:grid; place-items:center; color:#fff; font-size:29px; font-weight:900; transform:rotate(7deg); box-shadow:0 5px 12px rgba(0,0,0,.2); border:3px solid #fff;} +.bd-plate{width:min(94%,420px); height:30px; border-radius:50%; margin-top:-12px; box-shadow:0 8px 12px rgba(29,52,97,.22); z-index:1;} +/* Getallenlijn */ +.nlw{height:100%; min-height:0; display:flex; flex-direction:column; gap:11px; padding:14px; background:var(--surface);} +.nlw-settings{display:flex; align-items:end; gap:8px; flex-wrap:wrap;} +.nlw-settings label{font-size:11px; font-weight:800; color:var(--muted); display:flex; flex-direction:column; gap:3px;} +.nlw-settings input,.nlw-settings select{width:72px; height:34px; border:1px solid var(--line); border-radius:9px; padding:0 8px; background:var(--surface); color:var(--ink); font:inherit;} +.nlw-new{margin-left:auto; min-height:34px;}.nlw-task{text-align:center; font-size:clamp(18px,3vw,27px); font-weight:900; color:var(--blue);} +.nlw-scroll{flex:1; min-height:115px; overflow-x:auto; overflow-y:hidden; border-radius:14px; background:var(--surface-2); padding:20px 18px 8px;} +.nlw-line{position:relative; min-width:max-content; height:88px; display:flex; align-items:center; padding:0 18px;} +.nlw-line::before{content:""; position:absolute; height:4px; left:18px; right:18px; top:38px; border-radius:4px; background:var(--ink);} +.nlw-tick{width:52px; height:76px; flex:none; border:0; background:transparent; color:var(--ink); position:relative; cursor:pointer; font:inherit; font-weight:800; padding:0;} +.nlw-tick i{position:absolute; width:3px; height:18px; background:var(--ink); top:31px; left:calc(50% - 1.5px);} +.nlw-tick span{position:absolute; top:53px; left:0; right:0; text-align:center; font-size:12px;} +.nlw-tick.selected::before{content:""; position:absolute; width:23px; height:23px; border-radius:50%; background:var(--pink); border:3px solid #fff; left:calc(50% - 14px); top:27px; z-index:2; box-shadow:0 2px 6px rgba(0,0,0,.25);} +.nlw-actions{display:flex; align-items:center; justify-content:center; gap:8px;}.nlw-value{min-width:76px; text-align:center; font-size:28px; font-weight:900; color:var(--pink);} +.nlw-feedback{min-height:24px; text-align:center; font-weight:800;}.nlw-feedback.ok{color:var(--green);}.nlw-feedback.bad{color:var(--red);} +/* Zinsbouwer */ +.sb{height:100%; min-height:0; display:flex; flex-direction:column; gap:10px; padding:12px; background:var(--surface);} +.sb-sentence{min-height:72px; border:2px dashed var(--line); border-radius:14px; padding:12px; display:flex; align-items:center; justify-content:center; flex-wrap:wrap; gap:7px; font-size:clamp(17px,3vw,25px); font-weight:900;} +.sb-sentence.empty{color:var(--muted); font-size:14px; font-weight:700;}.sb-part{padding:7px 11px; border-radius:10px;}.sb-part.who{background:#dce9ff;color:#174c9b}.sb-part.does{background:#dcf7e7;color:#176b3a}.sb-part.what{background:#fff0cf;color:#855b00} +.sb-banks{flex:1; min-height:0; overflow:auto; display:grid; grid-template-columns:repeat(3,1fr); gap:8px;}.sb-bank{min-width:0; border-radius:12px; padding:9px; background:var(--surface-2);}.sb-bank h3{margin:0 0 7px; font-size:12px; color:var(--muted); text-transform:uppercase; letter-spacing:.4px;} +.sb-chips{display:flex; flex-wrap:wrap; gap:6px;}.sb-chip-wrap{display:inline-flex;align-items:stretch}.sb-chip{border:0; border-radius:999px; padding:7px 10px; font:inherit; font-size:12.5px; font-weight:800; cursor:pointer; text-align:left;}.sb-bank.who .sb-chip{background:#dce9ff;color:#174c9b}.sb-bank.does .sb-chip{background:#dcf7e7;color:#176b3a}.sb-bank.what .sb-chip{background:#fff0cf;color:#855b00} +.sb-chip-wrap .sb-chip{border-radius:999px 0 0 999px}.sb-chip-remove{border:0;border-radius:0 999px 999px 0;padding:0 7px;background:var(--surface-3);color:var(--muted);font-weight:900;cursor:pointer}.sb-chip-remove:hover{background:var(--red-soft);color:var(--red)} +.sb-add,.sb-actions{display:flex; align-items:center; gap:7px; flex-wrap:wrap;}.sb-add select,.sb-add input{height:36px; border:1px solid var(--line); border-radius:9px; padding:0 9px; background:var(--surface); color:var(--ink); font:inherit;}.sb-add input{flex:1; min-width:150px;}.sb-actions{justify-content:flex-end;}.sb-back{font-size:20px;} +body.dark .bd-stage{background:radial-gradient(circle at 50% 20%,var(--surface-2),var(--surface));} +@media(max-width:600px){.bd{grid-template-columns:1fr}.bd-controls{max-height:155px;border-right:0;border-bottom:1px solid var(--line);display:grid;grid-template-columns:repeat(2,1fr)}.bd-party{margin-top:0}.sb-banks{grid-template-columns:1fr}.nlw-settings{gap:5px}} diff --git a/public/index.html b/public/index.html index ccb0301..9df8cd5 100644 --- a/public/index.html +++ b/public/index.html @@ -239,6 +239,9 @@ + + + diff --git a/public/js/board.js b/public/js/board.js index fb18c83..5f22666 100644 --- a/public/js/board.js +++ b/public/js/board.js @@ -25,17 +25,20 @@ const REGISTRY = [ { id:"hak", cat:"taal", icon:"✂️", w:540, h:440, minW:420, minH:340, mount:mountHak }, { id:"ball", cat:"taal", icon:"🎈", w:620, h:560, minW:480, minH:440, mount:mountBall }, { id:"anchor", cat:"taal", icon:"⚓", w:680, h:500, minW:440, minH:340, mount:mountAnchor }, + { id:"sentence", cat:"taal", icon:"🧩", w:620, h:520, minW:400, minH:360, mount:mountSentence }, { id:"madd", cat:"rekenen", icon:"➕", w:460, h:620, minW:380, minH:520, mount:(r,s)=>mountMath(r,s,"add") }, { id:"msub", cat:"rekenen", icon:"➖", w:460, h:620, minW:380, minH:520, mount:(r,s)=>mountMath(r,s,"sub") }, { id:"mmul", cat:"rekenen", icon:"✖️", w:460, h:620, minW:380, minH:520, mount:(r,s)=>mountMath(r,s,"mul") }, { id:"mdiv", cat:"rekenen", icon:"➗", w:460, h:620, minW:380, minH:520, mount:(r,s)=>mountMath(r,s,"div") }, { id:"mpct", cat:"rekenen", icon:"💯", w:480, h:620, minW:400, minH:520, mount:(r,s)=>mountMath(r,s,"pct") }, + { id:"numberline", cat:"rekenen", icon:"↔️", w:660, h:430, minW:420, minH:320, mount:mountNumberline }, { id:"write", cat:"tools", icon:"✏️", w:560, h:440, minW:360, minH:280, mount:mountWrite }, { id:"mind", cat:"tools", icon:"🕸️", w:640, h:480, minW:420, minH:320, mount:mountMind }, { id:"notes", cat:"tools", icon:"📝", w:340, h:300, minW:240, minH:180, mount:mountNotes }, { id:"timer", cat:"tools", icon:"⏱️", w:350, h:470, minW:300, minH:420, mount:mountTimer }, { id:"dice", cat:"tools", icon:"🎲", w:400, h:340, minW:300, minH:270, mount:mountDice }, { id:"names", cat:"tools", icon:"🎯", w:420, h:430, minW:320, minH:330, mount:mountNames }, + { id:"birthday",cat:"tools", icon:"🎂", w:620, h:560, minW:400, minH:400, mount:mountBirthday }, { id:"media", cat:"media", icon:"🎬", w:560, h:420, minW:320, minH:240, mount:mountMedia } ]; const WIDGET_CATS = [["taal","catTaal"],["rekenen","catRekenen"],["tools","catTools"],["media","catMedia"]]; diff --git a/public/js/core.js b/public/js/core.js index 64e9032..336a215 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.3.06-beta"; +const VERSION = "0.3.07-beta"; (function(){ const tag = document.getElementById("verTag"); tag.textContent = "v"+VERSION; @@ -90,6 +90,16 @@ const I18N = { hakChop:"✂️ Hak", hakGlue:"🔗 Plak", hakSay:"🔊 Lees voor", wg_ball:"Ballonnenwoord", wg_ball_d:"Raad het woord letter voor letter voordat de ballonnen knappen.", wg_anchor:"Ankerwoorden", wg_anchor_d:"Onbeperkt woordraster rond een thema: elk woord hoort bij de woorden eromheen.", + wg_birthday:"Verjaardagstaart", wg_birthday_d:"Maak een persoonlijke taart met naam, leeftijd, kleuren, lagen, versiering en kaarsjes.", + birthdayName:"naam van de jarige", birthdayAge:"Leeftijd", birthdayLayers:"Lagen", birthdayCakeColor:"Taart", birthdayIcing:"Glazuur", birthdayPlate:"Bord", + birthdayCandles:"Kaarsjes", birthdayCelebrate:"🎉 Vieren!", birthdayTitle:"Er is er één jarig!", birthdayFor:"Hoera voor {name}!", + birthdaySprinkles:"Spikkels", birthdayFruit:"Fruit", birthdayStars:"Sterren", birthdayNone:"Geen versiering", + wg_numberline:"Getallenlijn", wg_numberline_d:"Oefen getalbegrip met een instelbare, interactieve getallenlijn.", + numberlineFrom:"Van", numberlineTo:"Tot", numberlineStep:"Stap", numberlineNew:"Nieuwe opdracht", numberlineTask:"Zet de stip op {number}", + numberlineCheck:"Controleren", numberlineGood:"Goed gevonden! 🌟", numberlineTry:"Kijk nog eens naar de getallenlijn.", + wg_sentence:"Zinsbouwer", wg_sentence_d:"Bouw zinnen met kleurgecodeerde zinsdelen en voeg eigen woorden toe.", + sentence_who:"Wie?", sentence_does:"Doet wat?", sentence_what:"Wat of waar?", sentenceHint:"Kies woorden om een zin te maken…", + sentenceAdd:"eigen woord of zinsdeel…", sentenceClear:"Wissen", sentenceSpeak:"🔊 Lees voor", sentenceRemove:"Verwijder woord", anchorPh:"thema…", anchorHint:"Tik op een vak en typ · sleep de achtergrond om te schuiven", anchorHintReadonly:"Sleep de achtergrond om te schuiven", anchorColor:"Kleur: klik om te wisselen (verbind woorden met dezelfde kleur)", @@ -230,6 +240,16 @@ const I18N = { hakChop:"✂️ Chop", hakGlue:"🔗 Blend", hakSay:"🔊 Read aloud", wg_ball:"Balloon word", wg_ball_d:"Guess the word letter by letter before the balloons pop.", wg_anchor:"Anchor words", wg_anchor_d:"Unlimited word grid around a theme: every word relates to its neighbours.", + wg_birthday:"Birthday cake", wg_birthday_d:"Create a personal cake with a name, age, colours, layers, decorations and candles.", + birthdayName:"birthday person's name", birthdayAge:"Age", birthdayLayers:"Layers", birthdayCakeColor:"Cake", birthdayIcing:"Icing", birthdayPlate:"Plate", + birthdayCandles:"Candles", birthdayCelebrate:"🎉 Celebrate!", birthdayTitle:"Someone has a birthday!", birthdayFor:"Hooray for {name}!", + birthdaySprinkles:"Sprinkles", birthdayFruit:"Fruit", birthdayStars:"Stars", birthdayNone:"No decoration", + wg_numberline:"Number line", wg_numberline_d:"Practise number sense with an adjustable, interactive number line.", + numberlineFrom:"From", numberlineTo:"To", numberlineStep:"Step", numberlineNew:"New task", numberlineTask:"Move the marker to {number}", + numberlineCheck:"Check", numberlineGood:"Well done! 🌟", numberlineTry:"Take another look at the number line.", + wg_sentence:"Sentence builder", wg_sentence_d:"Build sentences with colour-coded parts and add your own words.", + sentence_who:"Who?", sentence_does:"Does what?", sentence_what:"What or where?", sentenceHint:"Choose words to build a sentence…", + sentenceAdd:"your own word or phrase…", sentenceClear:"Clear", sentenceSpeak:"🔊 Read aloud", sentenceRemove:"Remove word", anchorPh:"theme…", anchorHint:"Tap a cell and type · drag the background to pan", anchorHintReadonly:"Drag the background to pan", anchorColor:"Colour: click to cycle (connect words with the same colour)", diff --git a/public/js/widgets/birthday.js b/public/js/widgets/birthday.js new file mode 100644 index 0000000..b833d13 --- /dev/null +++ b/public/js/widgets/birthday.js @@ -0,0 +1,74 @@ +/* widget: volledig aanpasbare verjaardagstaart */ +function mountBirthday(root, initState){ + const S = Object.assign({ + name:"", age:8, layers:2, cake:"#f2b880", icing:"#ff78a8", plate:"#57c7d4", + decoration:"sprinkles", candles:true, + }, initState || {}); + root.innerHTML = ` +