Verbeter anatomie en plaatsing van avataraccessoires (v0.4.46-beta) #1
5 changed files with 270 additions and 9 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
0.3.63-beta
|
0.3.64-beta
|
||||||
|
|
|
||||||
|
|
@ -490,7 +490,7 @@
|
||||||
.wmin:hover{background:var(--surface-3);}
|
.wmin:hover{background:var(--surface-3);}
|
||||||
.wz{font-size:10.5px; width:auto; min-width:30px; padding:0 5px;}
|
.wz{font-size:10.5px; width:auto; min-width:30px; padding:0 5px;}
|
||||||
.wz.on{background:var(--accent-soft); color:var(--accent-ink);}
|
.wz.on{background:var(--accent-soft); color:var(--accent-ink);}
|
||||||
.widget-body{flex:1; overflow:hidden; position:relative; display:flex; flex-direction:column; min-height:0;}
|
.widget-body{flex:1; overflow:hidden; position:relative; display:flex; flex-direction:column; min-height:0; container-type:inline-size; container-name:widget;}
|
||||||
/* ---------- Modals ---------- */
|
/* ---------- Modals ---------- */
|
||||||
.modalwrap{
|
.modalwrap{
|
||||||
position:fixed; inset:0; z-index:6000; background:rgba(15,22,38,.4);
|
position:fixed; inset:0; z-index:6000; background:rgba(15,22,38,.4);
|
||||||
|
|
@ -1591,3 +1591,158 @@ body.dark .bd-stage{background:radial-gradient(circle at 50% 20%,var(--surface-2
|
||||||
.clock-choices{display:flex;gap:10px;justify-content:center;flex-wrap:wrap}.clock-choices.wood .clock-time-blocks{border:1px solid var(--line);background:var(--surface-2)}.clock-word-choice{min-height:48px;border:2px solid var(--wood-d)!important;background:linear-gradient(#fdf3dd,var(--wood))!important;color:var(--ink)!important;box-shadow:0 3px 0 var(--wood-d)}.clock-digital-choice{min-width:118px;min-height:58px;font-size:28px!important;color:var(--blue)!important}.clock-type-answer{width:min(100%,250px);display:flex;flex-direction:column;align-items:center;gap:9px}.clock-type-answer>p{margin:0;color:var(--muted);font-weight:800;text-align:center}.clock-type-display{min-width:180px;padding:9px 14px;border:3px dashed var(--line);border-radius:14px;background:var(--surface-2);font-size:34px;font-weight:950;letter-spacing:3px;color:var(--blue)}.clock-keypad{width:100%;display:grid;grid-template-columns:repeat(4,1fr);gap:7px}.clock-keypad .tbtn{min-width:0;padding:0;font-size:20px}.clock-keypad .ok{background:var(--green);color:#fff}
|
.clock-choices{display:flex;gap:10px;justify-content:center;flex-wrap:wrap}.clock-choices.wood .clock-time-blocks{border:1px solid var(--line);background:var(--surface-2)}.clock-word-choice{min-height:48px;border:2px solid var(--wood-d)!important;background:linear-gradient(#fdf3dd,var(--wood))!important;color:var(--ink)!important;box-shadow:0 3px 0 var(--wood-d)}.clock-digital-choice{min-width:118px;min-height:58px;font-size:28px!important;color:var(--blue)!important}.clock-type-answer{width:min(100%,250px);display:flex;flex-direction:column;align-items:center;gap:9px}.clock-type-answer>p{margin:0;color:var(--muted);font-weight:800;text-align:center}.clock-type-display{min-width:180px;padding:9px 14px;border:3px dashed var(--line);border-radius:14px;background:var(--surface-2);font-size:34px;font-weight:950;letter-spacing:3px;color:var(--blue)}.clock-keypad{width:100%;display:grid;grid-template-columns:repeat(4,1fr);gap:7px}.clock-keypad .tbtn{min-width:0;padding:0;font-size:20px}.clock-keypad .ok{background:var(--green);color:#fff}
|
||||||
.clock-feedback{min-height:24px;margin:0;text-align:center;font-weight:900}.clock-feedback.ok{color:var(--green)}.clock-feedback.bad{color:var(--red)}.clock-actions{display:flex;justify-content:center;gap:8px;flex-wrap:wrap}.clock-actions .tbtn:not(.ghost){background:var(--green);color:#fff}.clock-drag-hint{color:var(--muted);font-weight:800;text-align:center}
|
.clock-feedback{min-height:24px;margin:0;text-align:center;font-weight:900}.clock-feedback.ok{color:var(--green)}.clock-feedback.bad{color:var(--red)}.clock-actions{display:flex;justify-content:center;gap:8px;flex-wrap:wrap}.clock-actions .tbtn:not(.ghost){background:var(--green);color:#fff}.clock-drag-hint{color:var(--muted);font-weight:800;text-align:center}
|
||||||
@media(max-width:480px){.clock-task-card{padding:12px}.clock-level-control>span{display:none}.clock-top{gap:5px}.clock{--clock-size:min(210px,68vw)}.clock-target{min-height:72px}.clock-block-tray.lg-tray{gap:7px;padding:9px}}
|
@media(max-width:480px){.clock-task-card{padding:12px}.clock-level-control>span{display:none}.clock-top{gap:5px}.clock{--clock-size:min(210px,68vw)}.clock-target{min-height:72px}.clock-block-tray.lg-tray{gap:7px;padding:9px}}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
Widgetinhoud op telefoonformaat
|
||||||
|
De widget-body is de container: dit werkt zowel op het bord als
|
||||||
|
in de leerlingweergave en ook wanneer een widget zelf smal is.
|
||||||
|
============================================================ */
|
||||||
|
@media(max-width:600px){
|
||||||
|
#pupilView{padding:8px;gap:12px}
|
||||||
|
#pupilView .pupil-card{width:100%!important;max-width:100%;min-width:0!important}
|
||||||
|
.widget:not(.bare){max-width:calc(100% - 8px)}
|
||||||
|
.widget-head{gap:4px;padding:6px 8px}
|
||||||
|
.widget-head .wicon{font-size:18px}
|
||||||
|
.wmin,.wclose{width:36px;height:36px;border-radius:10px}
|
||||||
|
.wz{min-width:36px;padding:0 4px}
|
||||||
|
}
|
||||||
|
@media(pointer:coarse){
|
||||||
|
.lg-levels button,.lg-cards button,.lg-snd,.lg-mode,.fl-sel,.tseg button,
|
||||||
|
.nlw-settings input,.nlw-settings select,.sb-add input,.sb-add select,
|
||||||
|
.edu-controls input,.edu-controls select{min-height:var(--tap-sm)}
|
||||||
|
.mm-node .mm-add,.mm-node .mm-del,.mm-node .mm-cog{display:block}
|
||||||
|
.an-tools button{width:var(--tap-sm);height:var(--tap-sm)}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container widget (max-width:520px){
|
||||||
|
/* gedeelde taal-, reken- en gereedschapskaarten */
|
||||||
|
.lg,.mt,.dc,.np,.timer,.nlw,.sb{gap:8px;padding:8px}
|
||||||
|
.lg-top,.mt-top,.topts,.np-row,.nlw-settings{flex-wrap:wrap}
|
||||||
|
.lg-top>*,.mt-top>*{max-width:100%}
|
||||||
|
.lg-levels{max-width:100%;overflow-x:auto;scrollbar-width:thin}
|
||||||
|
.lg-levels button{padding:6px 9px}
|
||||||
|
.lg-progress,.mt-score{min-width:0;margin-left:0;flex:1;justify-content:flex-end;text-align:right}
|
||||||
|
.lg-card,.mt-card{gap:8px;padding:8px}
|
||||||
|
.lg-pic{font-size:clamp(42px,16cqi,60px)}
|
||||||
|
.lg-pic img{width:clamp(58px,20cqi,76px);height:clamp(58px,20cqi,76px)}
|
||||||
|
.lg-instr{padding:6px 8px;font-size:13px}
|
||||||
|
.lg-slots{width:100%;max-width:100%;justify-content:flex-start;gap:7px;overflow-x:auto;padding:3px 1px 8px}
|
||||||
|
.lg-tray{gap:7px;min-height:0;max-height:44%;overflow:auto;padding:8px}
|
||||||
|
.lg-kbd{gap:5px;padding:7px}
|
||||||
|
.lg-kbd button{width:36px;height:42px;font-size:18px}
|
||||||
|
.lg-ed-form{gap:7px;padding:9px}
|
||||||
|
.lg-ed-form input.ed-word,.ed-theme-name,.ed-theme-folder{width:min(100%,170px)}
|
||||||
|
.ed-pictorow{flex-wrap:wrap}
|
||||||
|
.fl-sel{min-width:0}
|
||||||
|
.fl-word{min-height:54px;font-size:clamp(30px,14cqi,48px);letter-spacing:2px;text-align:center}
|
||||||
|
.hk-word{min-height:54px}
|
||||||
|
.hk-whole{font-size:clamp(30px,13cqi,44px)}
|
||||||
|
.hk-l{font-size:clamp(27px,12cqi,40px)}
|
||||||
|
.hk-sep{font-size:22px;margin:0 1px}
|
||||||
|
.bl-balloons{font-size:clamp(24px,9cqi,32px);letter-spacing:2px}
|
||||||
|
.bl-word{min-height:45px;font-size:clamp(28px,12cqi,40px);letter-spacing:3px;text-align:center}
|
||||||
|
.mt-q{font-size:clamp(28px,12cqi,38px)}
|
||||||
|
.mt-a{min-width:90px;height:50px;font-size:28px}
|
||||||
|
.mt-pad{gap:6px}
|
||||||
|
.mt-pad button{min-height:44px;padding:8px 0}
|
||||||
|
|
||||||
|
/* schrijfblad, media, mindmap, anker, dobbelsteen, namen en timer */
|
||||||
|
.wp-bar{gap:4px;padding:5px;max-height:45%;overflow:auto}
|
||||||
|
.wp-group{gap:2px;padding:2px}
|
||||||
|
.wp-group button{width:30px;height:30px}
|
||||||
|
.wp-clear{width:34px;height:34px}
|
||||||
|
.media-bar{flex-wrap:wrap;padding:7px}
|
||||||
|
.media-bar input{flex-basis:190px}
|
||||||
|
.mm-top{max-width:calc(100% - 16px);gap:4px;padding:4px 6px}
|
||||||
|
.mm-hint{display:none}
|
||||||
|
.mm-seg button{padding:5px 8px;font-size:12px}
|
||||||
|
.mm-node{max-width:46%;padding:7px 10px}
|
||||||
|
.mm-pop{max-width:calc(100% - 8px)}
|
||||||
|
.an-hint{min-height:42px;padding:6px 92px 6px 8px}
|
||||||
|
.an-tools{top:4px;right:4px}
|
||||||
|
.an-cell{width:112px;height:52px;padding:2px 5px;font-size:13px}
|
||||||
|
.dc-row{gap:8px}
|
||||||
|
.die{width:clamp(64px,25cqi,84px);height:clamp(64px,25cqi,84px);padding:8px}
|
||||||
|
.die i{width:clamp(10px,4cqi,14px);height:clamp(10px,4cqi,14px)}
|
||||||
|
.np-result{min-height:62px;padding:7px;font-size:clamp(23px,9cqi,30px)}
|
||||||
|
.np-row{gap:7px}
|
||||||
|
.np-row .tbtn{flex:1}
|
||||||
|
.np-row label{margin-left:0}
|
||||||
|
.timer{gap:7px;padding:8px}
|
||||||
|
.timer .tpie{max-height:190px}
|
||||||
|
.timer .tdisp{font-size:clamp(36px,15cqi,46px)}
|
||||||
|
.timer .presets button{padding:7px 10px}
|
||||||
|
.timer .main button{padding:10px 16px}
|
||||||
|
.tseg button{padding:6px 9px}
|
||||||
|
|
||||||
|
/* verjaardag, getallenlijn en zinsbouwer */
|
||||||
|
.bd{grid-template-columns:1fr}
|
||||||
|
.bd-controls{max-height:165px;border-right:0;border-bottom:1px solid var(--line);
|
||||||
|
display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px;padding:8px}
|
||||||
|
.bd-controls>*{min-width:0}
|
||||||
|
.bd-controls input[type=range]{width:min(96px,100%)}
|
||||||
|
.bd-party{margin-top:0}
|
||||||
|
.bd-stage{padding:48px 10px 25px}
|
||||||
|
.bd-layer{height:62px}
|
||||||
|
.bd-age-badge{min-width:48px;height:48px;font-size:24px}
|
||||||
|
.nlw-settings{align-items:end;gap:5px}
|
||||||
|
.nlw-settings label{flex:1;min-width:64px}
|
||||||
|
.nlw-settings input,.nlw-settings select{width:100%}
|
||||||
|
.nlw-new{margin-left:0}
|
||||||
|
.nlw-scroll{padding-inline:8px}
|
||||||
|
.nlw-actions{gap:5px;flex-wrap:wrap}
|
||||||
|
.nlw-value{min-width:58px;font-size:24px}
|
||||||
|
.sb-banks{grid-template-columns:1fr}
|
||||||
|
.sb-sentence{min-height:60px;padding:8px}
|
||||||
|
.sb-add{gap:5px}
|
||||||
|
.sb-add input{min-width:120px}
|
||||||
|
.sb-actions{gap:5px}
|
||||||
|
|
||||||
|
/* educatieve widgets: dezelfde compacte opbouw als de oefenklok */
|
||||||
|
.edu-stage{align-items:flex-start;justify-content:flex-start;padding:10px}
|
||||||
|
.edu-controls{gap:6px;padding:0 8px 8px}
|
||||||
|
.fraction-wall,.place-grid,.wordtypes,.schedule,.mood,.poll{align-self:center}
|
||||||
|
.place-grid b{padding:6px 2px;font-size:9px;overflow-wrap:anywhere}
|
||||||
|
.place-grid output{padding:12px 2px;font-size:clamp(22px,8cqi,36px)}
|
||||||
|
.wordtypes,.mood{width:100%;justify-content:center}
|
||||||
|
.schedule>div{gap:7px;padding:8px}
|
||||||
|
.schedule span{min-width:0;overflow-wrap:anywhere}
|
||||||
|
.mood .tbtn{font-size:30px;padding:9px}
|
||||||
|
.poll h3{margin:8px 0}
|
||||||
|
|
||||||
|
.money-task-card,.clock-task-card{gap:9px;padding:10px}
|
||||||
|
.money-top,.clock-top{top:-10px;gap:5px}
|
||||||
|
.money-level-control>span,.clock-level-control>span{display:none}
|
||||||
|
.money-shop-task{grid-template-columns:minmax(0,.8fr) minmax(0,1.2fr);gap:7px}
|
||||||
|
.money-product,.money-paid{min-height:112px;padding:8px 6px;gap:4px}
|
||||||
|
.money-product-icon{font-size:46px}
|
||||||
|
.money-product-name{font-size:14px}
|
||||||
|
.money-product .money-task-amount{font-size:32px}
|
||||||
|
.money-product .money-task-words{font-size:16px}
|
||||||
|
.money-paid>output{font-size:26px}
|
||||||
|
.money-cash-tray{gap:6px;padding:9px}
|
||||||
|
.clock{--clock-size:min(210px,68cqi)}
|
||||||
|
.clock-target{min-height:72px}
|
||||||
|
.clock-read-layout{gap:10px}
|
||||||
|
.clock-read-layout>.clock-assignment{min-width:0;flex-basis:100%}
|
||||||
|
.clock-block-tray.lg-tray{gap:7px;padding:9px}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container widget (max-width:340px){
|
||||||
|
.lg,.mt,.dc,.np,.timer,.nlw,.sb{padding:6px}
|
||||||
|
.lg-cards{gap:3px}
|
||||||
|
.lg-cards button,.lg-snd,.lg-mode{width:32px;height:34px}
|
||||||
|
.lg .slot:not(.clock-digit-slot):not(.money-digit-slot),
|
||||||
|
.lg .blok:not(.clock-digit-block):not(.money-digit-block){width:52px;height:82px}
|
||||||
|
.clock-answer-slots,.money-answer-slots{gap:4px}
|
||||||
|
.clock-digit-slot.slot.sm,.money-digit-slot.slot.sm{width:39px;height:60px}
|
||||||
|
.clock-digit-block.blok,.money-digit-block.blok{width:34px;height:52px;min-height:52px}
|
||||||
|
.clock-block-tray.lg-tray,.money-digit-tray.lg-tray{
|
||||||
|
grid-template-columns:repeat(5,34px);gap:4px;padding:7px}
|
||||||
|
.clock-block-tray .clock-drag-block{width:34px;height:52px;min-height:52px}
|
||||||
|
.clock-type-display,.money-type-display{min-width:0;width:100%;font-size:30px}
|
||||||
|
.schedule>div{flex-wrap:wrap}
|
||||||
|
.schedule span{flex-basis:calc(100% - 70px)}
|
||||||
|
.sb-add input{flex-basis:100%}
|
||||||
|
.bd-controls{grid-template-columns:1fr;max-height:180px}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,11 +88,13 @@ function genWidgetId(){ return "w" + Date.now().toString(36) + Math.random().toS
|
||||||
function makeWidget(def, saved){
|
function makeWidget(def, saved){
|
||||||
const win = document.createElement("div");
|
const win = document.createElement("div");
|
||||||
win.className = "widget";
|
win.className = "widget";
|
||||||
|
win.dataset.widget = def.id;
|
||||||
const size = clampWidgetSize(saved && saved.w ? saved.w : def.w, saved && saved.h ? saved.h : def.h, def);
|
const size = clampWidgetSize(saved && saved.w ? saved.w : def.w, saved && saved.h ? saved.h : def.h, def);
|
||||||
|
const bounds = widgetSizeBounds(def);
|
||||||
win.style.width = size.w+"px";
|
win.style.width = size.w+"px";
|
||||||
win.style.height = size.h+"px";
|
win.style.height = size.h+"px";
|
||||||
if(def.minW) win.style.minWidth = def.minW+"px";
|
win.style.minWidth = bounds.minW+"px";
|
||||||
if(def.minH) win.style.minHeight = def.minH+"px";
|
win.style.minHeight = bounds.minH+"px";
|
||||||
const cx = saved && saved.x!=null ? saved.x : 40 + (widgetCount*36)%240;
|
const cx = saved && saved.x!=null ? saved.x : 40 + (widgetCount*36)%240;
|
||||||
const cy = saved && saved.y!=null ? saved.y : 30 + (widgetCount*30)%160;
|
const cy = saved && saved.y!=null ? saved.y : 30 + (widgetCount*30)%160;
|
||||||
win.style.left = Math.max(0, Math.min(cx, board.clientWidth - size.w - 8)) + "px";
|
win.style.left = Math.max(0, Math.min(cx, board.clientWidth - size.w - 8)) + "px";
|
||||||
|
|
@ -135,11 +137,10 @@ function makeWidget(def, saved){
|
||||||
const zf = VZ();
|
const zf = VZ();
|
||||||
const sw = win.offsetWidth, sh = win.offsetHeight, sx = e.clientX, sy = e.clientY;
|
const sw = win.offsetWidth, sh = win.offsetHeight, sx = e.clientX, sy = e.clientY;
|
||||||
const mv = ev=>{
|
const mv = ev=>{
|
||||||
const maxW = board.clientWidth - win.offsetLeft - 8;
|
const {minW,minH,maxW,maxH} = widgetSizeBounds(def, win.offsetLeft, win.offsetTop);
|
||||||
const maxH = board.clientHeight - win.offsetTop - 8;
|
|
||||||
let nw = sw + (ev.clientX - sx)/zf, nh = sh + (ev.clientY - sy)/zf;
|
let nw = sw + (ev.clientX - sx)/zf, nh = sh + (ev.clientY - sy)/zf;
|
||||||
nw = Math.max(def.minW || 240, Math.min(nw, maxW));
|
nw = Math.max(minW, Math.min(nw, maxW));
|
||||||
nh = Math.max(def.minH || 180, Math.min(nh, maxH));
|
nh = Math.max(minH, Math.min(nh, maxH));
|
||||||
win.style.width = nw+"px"; win.style.height = nh+"px";
|
win.style.width = nw+"px"; win.style.height = nh+"px";
|
||||||
};
|
};
|
||||||
const up = ()=>{ document.removeEventListener("pointermove",mv); document.removeEventListener("pointerup",up); markBoardChange(); };
|
const up = ()=>{ document.removeEventListener("pointermove",mv); document.removeEventListener("pointerup",up); markBoardChange(); };
|
||||||
|
|
@ -175,6 +176,7 @@ function makeWidget(def, saved){
|
||||||
win.dataset.min = "";
|
win.dataset.min = "";
|
||||||
win.style.display = "flex";
|
win.style.display = "flex";
|
||||||
win.style.zIndex = ++zTop;
|
win.style.zIndex = ++zTop;
|
||||||
|
fitWidgetsToBoard();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
minBtn.addEventListener("click", ()=>{ setMin(true); markBoardChange(); });
|
minBtn.addEventListener("click", ()=>{ setMin(true); markBoardChange(); });
|
||||||
|
|
@ -210,6 +212,7 @@ function makeWidget(def, saved){
|
||||||
win.style.width = win.dataset.pmw+"px"; win.style.height = win.dataset.pmh+"px";
|
win.style.width = win.dataset.pmw+"px"; win.style.height = win.dataset.pmh+"px";
|
||||||
maxBtn.textContent = "▢";
|
maxBtn.textContent = "▢";
|
||||||
}
|
}
|
||||||
|
if(!m) fitWidgetsToBoard();
|
||||||
maxBtn.title = T(win.dataset.max==="1" ? "wUnfull" : "wFull");
|
maxBtn.title = T(win.dataset.max==="1" ? "wUnfull" : "wFull");
|
||||||
}
|
}
|
||||||
maxBtn.addEventListener("click", ()=>{ setMax(win.dataset.max!=="1"); markBoardChange(); });
|
maxBtn.addEventListener("click", ()=>{ setMax(win.dataset.max!=="1"); markBoardChange(); });
|
||||||
|
|
@ -265,6 +268,31 @@ function makeWidget(def, saved){
|
||||||
markBoardChange();
|
markBoardChange();
|
||||||
return inst;
|
return inst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Ook reeds geopende widgets blijven passen wanneer een telefoon draait, het
|
||||||
|
browservenster smaller wordt of een zijbalk de bordruimte verandert. */
|
||||||
|
function fitWidgetsToBoard(){
|
||||||
|
instances.forEach(inst=>{
|
||||||
|
const {win,def} = inst;
|
||||||
|
const bounds = widgetSizeBounds(def);
|
||||||
|
win.style.minWidth = bounds.minW+"px";
|
||||||
|
win.style.minHeight = bounds.minH+"px";
|
||||||
|
if(win.dataset.min==="1") return;
|
||||||
|
if(win.dataset.max==="1"){
|
||||||
|
win.style.left = "0px"; win.style.top = "0px";
|
||||||
|
win.style.width = board.clientWidth+"px";
|
||||||
|
win.style.height = board.clientHeight+"px";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const width = Math.max(bounds.minW, Math.min(win.offsetWidth, bounds.maxW));
|
||||||
|
const height = Math.max(bounds.minH, Math.min(win.offsetHeight, bounds.maxH));
|
||||||
|
win.style.width = width+"px"; win.style.height = height+"px";
|
||||||
|
win.style.left = Math.max(0, Math.min(win.offsetLeft, board.clientWidth-width-8))+"px";
|
||||||
|
win.style.top = Math.max(0, Math.min(win.offsetTop, board.clientHeight-height-8))+"px";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
new ResizeObserver(fitWidgetsToBoard).observe(board);
|
||||||
|
|
||||||
function updateEmptyHint(){
|
function updateEmptyHint(){
|
||||||
document.body.classList.toggle("board-empty", !board.querySelector(".widget"));
|
document.body.classList.toggle("board-empty", !board.querySelector(".widget"));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,9 @@ function renderPupilWidgets(widgets, mode){
|
||||||
usable.forEach(({ w, def }, idx)=>{
|
usable.forEach(({ w, def }, idx)=>{
|
||||||
const card = document.createElement("div");
|
const card = document.createElement("div");
|
||||||
card.className = "widget pupil-card" + (watch ? " watch" : "");
|
card.className = "widget pupil-card" + (watch ? " watch" : "");
|
||||||
card.style.width = def.w + "px";
|
card.dataset.widget = def.id;
|
||||||
|
card.style.width = `min(100%, ${def.w}px)`;
|
||||||
|
card.style.minWidth = "0";
|
||||||
card.style.minHeight = def.h + "px";
|
card.style.minHeight = def.h + "px";
|
||||||
const body = document.createElement("div");
|
const body = document.createElement("div");
|
||||||
body.className = "widget-body";
|
body.className = "widget-body";
|
||||||
|
|
|
||||||
|
|
@ -187,3 +187,79 @@ test('elk geldspel gebruikt geldige niveaugrenzen en realistische productcategor
|
||||||
assert.ok(categories.includes(item.category));
|
assert.ok(categories.includes(item.category));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('alle geregistreerde widgets hebben mobiele dekking en passen op een 320px-bord',async()=>{
|
||||||
|
const [board,pupil,css]=await Promise.all([
|
||||||
|
readFile('public/js/board.js','utf8'),
|
||||||
|
readFile('public/js/pupil.js','utf8'),
|
||||||
|
readFile('public/css/teach.css','utf8'),
|
||||||
|
]);
|
||||||
|
const registrySource=board.slice(board.indexOf('const REGISTRY'),board.indexOf('const WIDGET_CATS'));
|
||||||
|
const registered=[...registrySource.matchAll(/id:"([^"]+)"/g)].map(match=>match[1]).sort();
|
||||||
|
const coverage={
|
||||||
|
anchor:'.an-hint',
|
||||||
|
letters:'.lg-slots',
|
||||||
|
flits:'.fl-word',
|
||||||
|
hak:'.hk-word',
|
||||||
|
ball:'.bl-word',
|
||||||
|
sentence:'.sb-banks',
|
||||||
|
madd:'.mt-pad',
|
||||||
|
msub:'.mt-pad',
|
||||||
|
mmul:'.mt-pad',
|
||||||
|
mdiv:'.mt-pad',
|
||||||
|
mpct:'.mt-pad',
|
||||||
|
numberline:'.nlw-settings',
|
||||||
|
fractions:'.fraction-wall',
|
||||||
|
clock:'.clock-task-card',
|
||||||
|
placevalue:'.place-grid',
|
||||||
|
money:'.money-task-card',
|
||||||
|
wordtypes:'.wordtypes',
|
||||||
|
write:'.wp-bar',
|
||||||
|
mind:'.mm-top',
|
||||||
|
notes:'.notes{',
|
||||||
|
timer:'.timer',
|
||||||
|
dice:'.dc-row',
|
||||||
|
names:'.np-result',
|
||||||
|
birthday:'.bd{',
|
||||||
|
schedule:'.schedule>div',
|
||||||
|
mood:'.mood',
|
||||||
|
poll:'.poll',
|
||||||
|
media:'.media-bar',
|
||||||
|
};
|
||||||
|
assert.deepEqual(registered,Object.keys(coverage).sort(),'ieder geregistreerd widgettype staat in de mobiele audit');
|
||||||
|
for(const [id,selector] of Object.entries(coverage)){
|
||||||
|
assert.ok(css.includes(selector),id+' mist responsive/breedte-onafhankelijke opmaak');
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.match(css,/\.widget-body\{[^}]*container-type:inline-size[^}]*container-name:widget/);
|
||||||
|
assert.match(css,/@container widget \(max-width:520px\)/);
|
||||||
|
const compact=css.slice(css.indexOf('Widgetinhoud op telefoonformaat'));
|
||||||
|
for(const selector of [
|
||||||
|
'.lg,.mt,.dc,.np,.timer,.nlw,.sb',
|
||||||
|
'.wp-bar',
|
||||||
|
'.media-bar',
|
||||||
|
'.mm-top',
|
||||||
|
'.an-hint',
|
||||||
|
'.bd{grid-template-columns:1fr}',
|
||||||
|
'.sb-banks{grid-template-columns:1fr}',
|
||||||
|
'.edu-stage{align-items:flex-start',
|
||||||
|
'.money-task-card,.clock-task-card',
|
||||||
|
]){
|
||||||
|
assert.ok(compact.includes(selector),selector+' mist in de compacte containerregels');
|
||||||
|
}
|
||||||
|
|
||||||
|
const sizeCode=board.slice(
|
||||||
|
board.indexOf('function widgetSizeBounds'),
|
||||||
|
board.indexOf('/* stabiel widget-instantie-id'),
|
||||||
|
);
|
||||||
|
const context={board:{clientWidth:320,clientHeight:480}};
|
||||||
|
runInNewContext(sizeCode+';globalThis.mobileSize={bounds:widgetSizeBounds({minW:640,minH:540}),size:clampWidgetSize(760,640,{minW:640,minH:540})}',context);
|
||||||
|
assert.deepEqual(JSON.parse(JSON.stringify(context.mobileSize)),{
|
||||||
|
bounds:{minW:312,minH:472,maxW:312,maxH:472},
|
||||||
|
size:{w:312,h:472},
|
||||||
|
});
|
||||||
|
assert.ok(board.includes('new ResizeObserver(fitWidgetsToBoard).observe(board)'));
|
||||||
|
assert.ok(board.includes('win.dataset.widget = def.id'));
|
||||||
|
assert.match(pupil,/card\.style\.width = .min\(100%, \$\{def\.w\}px\)./);
|
||||||
|
assert.ok(pupil.includes('card.style.minWidth = "0"'));
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue