Verbeter grafische kwaliteit van kleurplaten (v0.4.67-beta) #5
5 changed files with 106 additions and 6 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
0.4.39-beta
|
0.4.40-beta
|
||||||
|
|
|
||||||
|
|
@ -1310,6 +1310,15 @@
|
||||||
.role-preview-note{max-width:760px;margin:0 auto 14px;padding:11px 14px;border:1px solid var(--orange);border-radius:12px;background:color-mix(in srgb,var(--orange) 16%,var(--surface));color:var(--ink);font-size:13px;line-height:1.45}
|
.role-preview-note{max-width:760px;margin:0 auto 14px;padding:11px 14px;border:1px solid var(--orange);border-radius:12px;background:color-mix(in srgb,var(--orange) 16%,var(--surface));color:var(--ink);font-size:13px;line-height:1.45}
|
||||||
body.role-preview-active #parentView button:disabled,body.role-preview-active #parentView input:disabled,body.role-preview-active #parentView select:disabled{opacity:.55;cursor:not-allowed}
|
body.role-preview-active #parentView button:disabled,body.role-preview-active #parentView input:disabled,body.role-preview-active #parentView select:disabled{opacity:.55;cursor:not-allowed}
|
||||||
body.role-preview-pupil #pupilView .widget-body{pointer-events:none;user-select:none}
|
body.role-preview-pupil #pupilView .widget-body{pointer-events:none;user-select:none}
|
||||||
|
|
||||||
|
.role-preview-note .role-preview-add-widget{display:flex;margin-top:9px}
|
||||||
|
.role-preview-widget-picker{width:min(760px,94vw);max-height:88vh;overflow:auto}
|
||||||
|
.role-preview-widget-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:9px;margin-top:12px}
|
||||||
|
.role-preview-widget-card{width:100%;display:grid;grid-template-columns:50px minmax(0,1fr);align-items:center;gap:10px;padding:11px;border:1px solid var(--line);border-radius:14px;background:var(--surface);color:var(--ink);text-align:left;cursor:pointer}
|
||||||
|
.role-preview-widget-card:hover,.role-preview-widget-card:focus-visible{border-color:var(--accent);outline:2px solid var(--accent-soft)}
|
||||||
|
.role-preview-widget-card .gi{font-size:30px;text-align:center}.role-preview-widget-card>span:last-child{min-width:0}.role-preview-widget-card .gn,.role-preview-widget-card .gd{display:block}.role-preview-widget-card .gd{margin-top:3px;color:var(--muted);font-size:11px;line-height:1.35}
|
||||||
|
body.role-preview-updating #board{visibility:hidden}
|
||||||
|
@media(max-width:560px){.role-preview-widget-grid{grid-template-columns:1fr}}
|
||||||
body.tb-left #rolePreviewBanner,body.tb-right #rolePreviewBanner{top:8px}
|
body.tb-left #rolePreviewBanner,body.tb-right #rolePreviewBanner{top:8px}
|
||||||
@media(max-width:720px){.vw-role-block .vw-row{align-items:stretch;flex-direction:column}.vw-role-seg{max-width:none;justify-content:flex-start}#rolePreviewBanner{max-width:calc(100vw - 24px);white-space:nowrap}#rolePreviewBanner strong{display:none}}
|
@media(max-width:720px){.vw-role-block .vw-row{align-items:stretch;flex-direction:column}.vw-role-seg{max-width:none;justify-content:flex-start}#rolePreviewBanner{max-width:calc(100vw - 24px);white-space:nowrap}#rolePreviewBanner strong{display:none}}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
/* version — shown until /api/version resolves (or if the fetch fails, e.g. offline).
|
/* 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. */
|
Kept in sync by hand with the VERSION file at the repo root on every release. */
|
||||||
const VERSION = "0.4.39-beta";
|
const VERSION = "0.4.40-beta";
|
||||||
(function(){
|
(function(){
|
||||||
const tag = document.getElementById("verTag");
|
const tag = document.getElementById("verTag");
|
||||||
tag.textContent = "v"+VERSION;
|
tag.textContent = "v"+VERSION;
|
||||||
|
|
@ -289,6 +289,8 @@ const I18N = {
|
||||||
vwPreview:"Voorbeeldweergave", vwPreviewReset:"Terug naar systeem",
|
vwPreview:"Voorbeeldweergave", vwPreviewReset:"Terug naar systeem",
|
||||||
vwParentPreview:"Voorbeeld van het ouderportaal. Echte kindgegevens en acties zijn in deze weergave uitgeschakeld.",
|
vwParentPreview:"Voorbeeld van het ouderportaal. Echte kindgegevens en acties zijn in deze weergave uitgeschakeld.",
|
||||||
vwPupilPreview:"Voorbeeld van de leerlingomgeving met de taal- en rekenwidgets van het huidige bord. Bediening en voortgang zijn uitgeschakeld.",
|
vwPupilPreview:"Voorbeeld van de leerlingomgeving met de taal- en rekenwidgets van het huidige bord. Bediening en voortgang zijn uitgeschakeld.",
|
||||||
|
vwPupilAddWidget:"Widget toevoegen", vwPupilChooseWidget:"Kies een leerlingwidget",
|
||||||
|
vwPupilAddWidgetNote:"Alleen taal- en rekenwidgets die in de leerlingomgeving werken worden getoond.",
|
||||||
vwNote:"Deze voorkeuren gelden voor dit apparaat.",
|
vwNote:"Deze voorkeuren gelden voor dit apparaat.",
|
||||||
pwChanged:"Wachtwoord gewijzigd ✓", pwCurrentPh:"huidig wachtwoord",
|
pwChanged:"Wachtwoord gewijzigd ✓", pwCurrentPh:"huidig wachtwoord",
|
||||||
namePh:"naam", passPh:"wachtwoord",
|
namePh:"naam", passPh:"wachtwoord",
|
||||||
|
|
@ -667,6 +669,8 @@ const I18N = {
|
||||||
vwPreview:"Preview mode", vwPreviewReset:"Return to system",
|
vwPreview:"Preview mode", vwPreviewReset:"Return to system",
|
||||||
vwParentPreview:"Preview of the parent portal. Real child data and actions are disabled in this view.",
|
vwParentPreview:"Preview of the parent portal. Real child data and actions are disabled in this view.",
|
||||||
vwPupilPreview:"Preview of the pupil environment using language and maths widgets from the current board. Controls and progress are disabled.",
|
vwPupilPreview:"Preview of the pupil environment using language and maths widgets from the current board. Controls and progress are disabled.",
|
||||||
|
vwPupilAddWidget:"Add widget", vwPupilChooseWidget:"Choose a pupil widget",
|
||||||
|
vwPupilAddWidgetNote:"Only language and maths widgets supported in the pupil environment are shown.",
|
||||||
vwNote:"These preferences apply to this device.",
|
vwNote:"These preferences apply to this device.",
|
||||||
pwChanged:"Password changed ✓", pwCurrentPh:"current password",
|
pwChanged:"Password changed ✓", pwCurrentPh:"current password",
|
||||||
namePh:"name", passPh:"password",
|
namePh:"name", passPh:"password",
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,78 @@
|
||||||
parent:"vwRoleParent",pupil:"vwRolePupil"
|
parent:"vwRoleParent",pupil:"vwRolePupil"
|
||||||
};
|
};
|
||||||
let renderSequence=0;
|
let renderSequence=0;
|
||||||
|
let pupilWidgetPicker=null;
|
||||||
|
|
||||||
|
function closePupilWidgetPicker(){
|
||||||
|
if(pupilWidgetPicker)pupilWidgetPicker.classList.remove("open");
|
||||||
|
}
|
||||||
|
function openPupilWidgetPicker(){
|
||||||
|
if(!isSystemManager()||activeRole()!=="pupil")return;
|
||||||
|
if(!pupilWidgetPicker){
|
||||||
|
pupilWidgetPicker=document.createElement("div");
|
||||||
|
pupilWidgetPicker.className="modalwrap";
|
||||||
|
pupilWidgetPicker.id="pupilPreviewWidgetPicker";
|
||||||
|
pupilWidgetPicker.setAttribute("role","dialog");
|
||||||
|
pupilWidgetPicker.setAttribute("aria-modal","true");
|
||||||
|
pupilWidgetPicker.addEventListener("click",event=>{
|
||||||
|
if(event.target===pupilWidgetPicker)closePupilWidgetPicker();
|
||||||
|
});
|
||||||
|
document.body.appendChild(pupilWidgetPicker);
|
||||||
|
}
|
||||||
|
const modal=document.createElement("div");
|
||||||
|
modal.className="modal role-preview-widget-picker";
|
||||||
|
const head=document.createElement("div");
|
||||||
|
head.className="gallery-head";
|
||||||
|
const title=document.createElement("h2");
|
||||||
|
title.id="pupilPreviewWidgetTitle";
|
||||||
|
title.textContent=T("vwPupilChooseWidget");
|
||||||
|
const close=document.createElement("button");
|
||||||
|
close.type="button";close.className="wclose";close.textContent="✕";
|
||||||
|
close.title=T("close");close.setAttribute("aria-label",T("close"));
|
||||||
|
close.addEventListener("click",closePupilWidgetPicker);
|
||||||
|
head.append(title,close);modal.appendChild(head);
|
||||||
|
const note=document.createElement("p");
|
||||||
|
note.className="guestnote";note.textContent=T("vwPupilAddWidgetNote");
|
||||||
|
modal.appendChild(note);
|
||||||
|
const host=document.createElement("div");
|
||||||
|
pupilAllowedDefs().forEach(def=>{
|
||||||
|
const card=document.createElement("button");
|
||||||
|
card.type="button";card.className="gcard role-preview-widget-card";
|
||||||
|
const icon=document.createElement("span");
|
||||||
|
icon.className="gi";icon.textContent=def.icon;
|
||||||
|
const text=document.createElement("span");
|
||||||
|
const name=document.createElement("strong");
|
||||||
|
name.className="gn";name.textContent=T("wg_"+def.id);
|
||||||
|
const desc=document.createElement("small");
|
||||||
|
desc.className="gd";desc.textContent=T("wg_"+def.id+"_d");
|
||||||
|
text.append(name,desc);card.append(icon,text);
|
||||||
|
card.addEventListener("click",()=>addPupilPreviewWidget(def));
|
||||||
|
host.appendChild(card);
|
||||||
|
});
|
||||||
|
host.className="role-preview-widget-grid";
|
||||||
|
modal.appendChild(host);
|
||||||
|
pupilWidgetPicker.setAttribute("aria-labelledby",title.id);
|
||||||
|
pupilWidgetPicker.replaceChildren(modal);
|
||||||
|
pupilWidgetPicker.classList.add("open");
|
||||||
|
close.focus();
|
||||||
|
}
|
||||||
|
async function addPupilPreviewWidget(def){
|
||||||
|
if(!isSystemManager()||activeRole()!=="pupil"||!pupilAllowedDefs().includes(def))return;
|
||||||
|
closePupilWidgetPicker();
|
||||||
|
const previewRole=activeRole();
|
||||||
|
document.body.classList.add("role-preview-updating");
|
||||||
|
stopPupilView();
|
||||||
|
setActiveViewRole("super");
|
||||||
|
try{
|
||||||
|
makeWidget(def);
|
||||||
|
await doSave();
|
||||||
|
}finally{
|
||||||
|
setActiveViewRole(previewRole);
|
||||||
|
document.body.classList.remove("role-preview-updating");
|
||||||
|
renderPreviewSurface();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const isSystemManager=()=>!!currentUser&¤tUser.role==="super";
|
const isSystemManager=()=>!!currentUser&¤tUser.role==="super";
|
||||||
const roleLabel=role=>T(ROLE_KEYS[role]||ROLE_KEYS.super);
|
const roleLabel=role=>T(ROLE_KEYS[role]||ROLE_KEYS.super);
|
||||||
|
|
@ -39,6 +111,7 @@
|
||||||
renderSequence++;
|
renderSequence++;
|
||||||
document.body.classList.remove("role-preview-active","role-preview-parent","role-preview-pupil");
|
document.body.classList.remove("role-preview-active","role-preview-parent","role-preview-pupil");
|
||||||
banner.hidden=true;
|
banner.hidden=true;
|
||||||
|
closePupilWidgetPicker();
|
||||||
if(isSystemManager()){
|
if(isSystemManager()){
|
||||||
stopPupilView();
|
stopPupilView();
|
||||||
stopParentView();
|
stopParentView();
|
||||||
|
|
@ -60,6 +133,7 @@
|
||||||
const sequence=++renderSequence;
|
const sequence=++renderSequence;
|
||||||
if(!isSystemManager()){clearPreviewSurface();syncControls();return;}
|
if(!isSystemManager()){clearPreviewSurface();syncControls();return;}
|
||||||
const role=activeRole();
|
const role=activeRole();
|
||||||
|
closePupilWidgetPicker();
|
||||||
document.body.classList.remove("role-preview-active","role-preview-parent","role-preview-pupil");
|
document.body.classList.remove("role-preview-active","role-preview-parent","role-preview-pupil");
|
||||||
stopPupilView();
|
stopPupilView();
|
||||||
stopParentView();
|
stopParentView();
|
||||||
|
|
@ -74,7 +148,13 @@
|
||||||
const snapshot=serializeBoard();
|
const snapshot=serializeBoard();
|
||||||
await renderPupilWidgets(snapshot.widgets||[],"kijken",true);
|
await renderPupilWidgets(snapshot.widgets||[],"kijken",true);
|
||||||
if(sequence!==renderSequence)return;
|
if(sequence!==renderSequence)return;
|
||||||
pupilView.prepend(previewNotice(T("vwPupilPreview")));
|
const note=previewNotice(T("vwPupilPreview"));
|
||||||
|
const add=document.createElement("button");
|
||||||
|
add.type="button";add.className="tbtn role-preview-add-widget";
|
||||||
|
add.textContent="+ "+T("vwPupilAddWidget");
|
||||||
|
add.addEventListener("click",openPupilWidgetPicker);
|
||||||
|
note.appendChild(add);
|
||||||
|
pupilView.prepend(note);
|
||||||
}
|
}
|
||||||
syncControls();
|
syncControls();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,22 +38,27 @@ test('Weergave bevat de vijf rollen en laadt de veilige voorbeeldmodule vóór d
|
||||||
assert.match(preview,/renderParentView\(\[\]\)/);
|
assert.match(preview,/renderParentView\(\[\]\)/);
|
||||||
assert.match(preview,/renderPupilWidgets\(snapshot\.widgets\|\|\[\],"kijken",true\)/);
|
assert.match(preview,/renderPupilWidgets\(snapshot\.widgets\|\|\[\],"kijken",true\)/);
|
||||||
assert.match(preview,/querySelectorAll\("button,input,select,textarea"\)/);
|
assert.match(preview,/querySelectorAll\("button,input,select,textarea"\)/);
|
||||||
|
assert.match(preview,/pupilAllowedDefs\(\)\.forEach/);
|
||||||
|
assert.match(preview,/pupilAllowedDefs\(\)\.includes\(def\)/);
|
||||||
|
assert.match(preview,/setActiveViewRole\("super"\)[\s\S]*?makeWidget\(def\)[\s\S]*?await doSave\(\)/);
|
||||||
|
assert.match(preview,/role-preview-add-widget/);
|
||||||
assert.doesNotMatch(preview,/currentUser\.role\s*=(?!=)/);
|
assert.doesNotMatch(preview,/currentUser\.role\s*=(?!=)/);
|
||||||
assert.doesNotMatch(preview,/api\(/);
|
assert.doesNotMatch(preview,/api\(/);
|
||||||
assert.match(css,/body\.role-preview-pupil #pupilView \.widget-body\{pointer-events:none/);
|
assert.match(css,/body\.role-preview-pupil #pupilView \.widget-body\{pointer-events:none/);
|
||||||
for(const key of ['vwRole','vwRoleSystem','vwRoleAdmin','vwRoleTeacher','vwRoleParent','vwRolePupil','vwRoleNote','vwPreviewReset']){
|
for(const key of ['vwRole','vwRoleSystem','vwRoleAdmin','vwRoleTeacher','vwRoleParent','vwRolePupil','vwRoleNote','vwPreviewReset','vwPupilAddWidget','vwPupilChooseWidget','vwPupilAddWidgetNote']){
|
||||||
assert.equal((core.match(new RegExp(key+':','g'))||[]).length,2,key);
|
assert.equal((core.match(new RegExp(key+':','g'))||[]).length,2,key);
|
||||||
}
|
}
|
||||||
assert.ok(core.includes('const VERSION = "'+version.trim()+'"'));
|
assert.ok(core.includes('const VERSION = "'+version.trim()+'"'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('voorbeeldrol begrenst clientmenu’s maar verandert serverpermissies niet',async()=>{
|
test('voorbeeldrol begrenst clientmenu’s maar verandert serverpermissies niet',async()=>{
|
||||||
const [client,server,settings,admin,explorer]=await Promise.all([
|
const [client,server,settings,admin,explorer,pupil]=await Promise.all([
|
||||||
readFile('public/js/permissions.js','utf8'),
|
readFile('public/js/permissions.js','utf8'),
|
||||||
readFile('src/permissions.js','utf8'),
|
readFile('src/permissions.js','utf8'),
|
||||||
readFile('public/js/settings.js','utf8'),
|
readFile('public/js/settings.js','utf8'),
|
||||||
readFile('public/js/admin.js','utf8'),
|
readFile('public/js/admin.js','utf8'),
|
||||||
readFile('public/js/explorer.js','utf8')
|
readFile('public/js/explorer.js','utf8'),
|
||||||
|
readFile('public/js/pupil.js','utf8')
|
||||||
]);
|
]);
|
||||||
assert.match(client,/function activeRoles\(\)/);
|
assert.match(client,/function activeRoles\(\)/);
|
||||||
assert.doesNotMatch(server,/VIEW_AS_ROLE|activeRole/);
|
assert.doesNotMatch(server,/VIEW_AS_ROLE|activeRole/);
|
||||||
|
|
@ -61,4 +66,6 @@ test('voorbeeldrol begrenst clientmenu’s maar verandert serverpermissies niet'
|
||||||
assert.match(admin,/activeRoles\(\)\.flatMap/);
|
assert.match(admin,/activeRoles\(\)\.flatMap/);
|
||||||
assert.match(admin,/show:\(\)=>activeRole\(\)==="super"/);
|
assert.match(admin,/show:\(\)=>activeRole\(\)==="super"/);
|
||||||
assert.match(explorer,/activeRole\(\) === "pupil"/);
|
assert.match(explorer,/activeRole\(\) === "pupil"/);
|
||||||
|
assert.match(pupil,/async function renderPupilWidgets\(widgets, mode, preview=false\)/);
|
||||||
|
assert.match(pupil,/if\(!preview\)await Promise\.all/);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue