@curio-sd/e-module-builder 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -15
- package/bin/cli.js +2 -0
- package/build-pdf.mjs +452 -0
- package/build.mjs +126 -73
- package/package.json +3 -1
- package/src/css/main.css +124 -50
- package/src/js/checklist.js +1 -1
- package/src/js/exercises/exercise-shared.js +1 -1
- package/src/js/exercises/external-exercise.js +3 -4
- package/src/js/exercises/hub.js +5 -1
- package/src/js/exercises/load-exercise.js +3 -0
- package/src/js/exercises/theory-panel.js +95 -0
- package/src/js/home.js +1 -1
- package/src/js/nav.js +63 -8
- package/templates/index.html +17 -9
- package/templates/pages/inleveropdracht.html +32 -0
- package/templates/pages/meetmoment-praktijk.html +35 -0
- package/templates/pages/meetmoment-theorie.html +29 -0
- package/templates/pages/{toets.html → meetmoment.html} +11 -4
- package/templates/pages/oefening.html +29 -0
- package/templates/pages/oefeningen.html +46 -38
- package/templates/pages/theorie.html +6 -1
- package/templates/pages/toets-praktijk.html +0 -25
- package/templates/pages/toets-theorie.html +0 -25
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { renderExerciseMeta, markExerciseSolved } from './exercise-shared.js'
|
|
2
2
|
import { sitePath } from '../site-path.js'
|
|
3
3
|
import { initExternalExercise } from './external-exercise.js'
|
|
4
|
+
import { initTheoryPanel } from './theory-panel.js'
|
|
4
5
|
|
|
5
6
|
async function loadWeekData(weekNum) {
|
|
6
7
|
return import(`../../data/exercises/week${weekNum}.json`).then((m) => m.default)
|
|
@@ -52,6 +53,8 @@ export async function initExercisePage(weekNum) {
|
|
|
52
53
|
return
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
initTheoryPanel(exercise.linked_theory)
|
|
57
|
+
|
|
55
58
|
if (!exercise.type || exercise.type === 'text') {
|
|
56
59
|
const panel = document.querySelector('[data-exercise-content]')
|
|
57
60
|
panel?.querySelector('[data-exercise-interactive]')?.classList.add('hidden')
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { sitePath } from '../site-path.js'
|
|
2
|
+
|
|
3
|
+
const WEEK_RE = /^([a-zA-Z]+)(\d+)$/
|
|
4
|
+
|
|
5
|
+
const BOOK_SVG = `<svg class="h-4 w-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6.042A8.967 8.967 0 0 0 6 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 0 1 6 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 0 1 6-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0 0 18 18a8.966 8.966 0 0 0-6 2.292m0-14.25v14.25" /></svg>`
|
|
6
|
+
|
|
7
|
+
export function parseLinkedTheory(linkedTheory) {
|
|
8
|
+
if (!Array.isArray(linkedTheory) || linkedTheory.length === 0) return []
|
|
9
|
+
|
|
10
|
+
return linkedTheory
|
|
11
|
+
.map((entry) => {
|
|
12
|
+
if (typeof entry !== 'string') return null
|
|
13
|
+
const m = entry.trim().match(WEEK_RE)
|
|
14
|
+
if (!m) return null
|
|
15
|
+
const prefix = m[1]
|
|
16
|
+
const num = parseInt(m[2], 10)
|
|
17
|
+
const label = `${prefix.charAt(0).toUpperCase()}${prefix.slice(1)} ${num}`
|
|
18
|
+
return { key: entry.trim(), label, num, dirName: entry.trim() }
|
|
19
|
+
})
|
|
20
|
+
.filter(Boolean)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function theoryIframeSrc(dirName) {
|
|
24
|
+
return sitePath(`/pages/${dirName}-theorie.html`) + '?embedded=1'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function initTheoryPanel(linkedTheory) {
|
|
28
|
+
const toggle = document.querySelector('[data-theory-toggle]')
|
|
29
|
+
const panel = document.querySelector('[data-theory-panel]')
|
|
30
|
+
|
|
31
|
+
const tabs = parseLinkedTheory(linkedTheory)
|
|
32
|
+
|
|
33
|
+
if (!tabs.length) {
|
|
34
|
+
toggle?.classList.add('hidden')
|
|
35
|
+
panel?.classList.add('panel-hidden')
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
toggle?.classList.remove('hidden')
|
|
40
|
+
|
|
41
|
+
const tabsEl = panel?.querySelector('[data-theory-tabs]')
|
|
42
|
+
const iframe = panel?.querySelector('[data-theory-iframe]')
|
|
43
|
+
const loader = panel?.querySelector('[data-theory-loader]')
|
|
44
|
+
|
|
45
|
+
if (!tabsEl || !iframe || !panel) return
|
|
46
|
+
|
|
47
|
+
function showLoader() { loader?.classList.remove('theory-panel-loader-hidden') }
|
|
48
|
+
function hideLoader() { loader?.classList.add('theory-panel-loader-hidden') }
|
|
49
|
+
|
|
50
|
+
iframe.addEventListener('load', hideLoader)
|
|
51
|
+
|
|
52
|
+
tabsEl.innerHTML = tabs
|
|
53
|
+
.map(
|
|
54
|
+
(t, i) =>
|
|
55
|
+
`<button class="theory-tab${i === 0 ? ' theory-tab-active' : ''}" data-tab="${t.key}" type="button">${t.label}</button>`
|
|
56
|
+
)
|
|
57
|
+
.join('')
|
|
58
|
+
|
|
59
|
+
function activateTab(key) {
|
|
60
|
+
tabsEl.querySelectorAll('[data-tab]').forEach((btn) => {
|
|
61
|
+
btn.classList.toggle('theory-tab-active', btn.dataset.tab === key)
|
|
62
|
+
})
|
|
63
|
+
const tab = tabs.find((t) => t.key === key)
|
|
64
|
+
if (tab) {
|
|
65
|
+
showLoader()
|
|
66
|
+
iframe.src = theoryIframeSrc(tab.dirName)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
tabsEl.addEventListener('click', (e) => {
|
|
71
|
+
const btn = e.target.closest('[data-tab]')
|
|
72
|
+
if (btn) activateTab(btn.dataset.tab)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
activateTab(tabs[0].key)
|
|
76
|
+
|
|
77
|
+
const closeBtn = panel.querySelector('[data-theory-panel-close]')
|
|
78
|
+
closeBtn?.addEventListener('click', () => {
|
|
79
|
+
panel.classList.add('panel-hidden')
|
|
80
|
+
toggle?.setAttribute('aria-expanded', 'false')
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
toggle?.addEventListener('click', () => {
|
|
84
|
+
const isHidden = panel.classList.contains('panel-hidden')
|
|
85
|
+
panel.classList.toggle('panel-hidden', !isHidden)
|
|
86
|
+
toggle.setAttribute('aria-expanded', String(isHidden))
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
toggle?.setAttribute('aria-expanded', 'false')
|
|
90
|
+
|
|
91
|
+
// Enable the slide transition after the first frame has been committed so
|
|
92
|
+
// the panel doesn't animate from its off-screen position on page load.
|
|
93
|
+
// Double-rAF ensures this works even when initTheoryPanel is called synchronously.
|
|
94
|
+
requestAnimationFrame(() => requestAnimationFrame(() => panel.classList.add('panel-ready')))
|
|
95
|
+
}
|
package/src/js/home.js
CHANGED
package/src/js/nav.js
CHANGED
|
@@ -13,9 +13,7 @@ function buildNavItems() {
|
|
|
13
13
|
})
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
items.push(page)
|
|
18
|
-
}
|
|
16
|
+
items.push(manifest.nav.assessmentSection)
|
|
19
17
|
|
|
20
18
|
return items
|
|
21
19
|
}
|
|
@@ -69,16 +67,68 @@ function renderNavGroup(group) {
|
|
|
69
67
|
? `<p class="mt-0.5 text-[10px] font-normal leading-snug text-zinc-500">${group.title}</p>`
|
|
70
68
|
: ''
|
|
71
69
|
return `
|
|
72
|
-
<div class="space-y-0.5">
|
|
73
|
-
<
|
|
74
|
-
<
|
|
75
|
-
|
|
70
|
+
<div class="space-y-0.5" data-nav-group="${group.label}">
|
|
71
|
+
<button class="nav-group-toggle" data-group-toggle="${group.label}">
|
|
72
|
+
<div>
|
|
73
|
+
<p class="text-[10px] font-semibold uppercase tracking-[0.14em] text-zinc-400">${group.label}</p>
|
|
74
|
+
${title}
|
|
75
|
+
</div>
|
|
76
|
+
<svg class="nav-group-chevron" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
|
77
|
+
<path fill-rule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z" clip-rule="evenodd" />
|
|
78
|
+
</svg>
|
|
79
|
+
</button>
|
|
80
|
+
<div class="nav-group-children" data-group-children="${group.label}">
|
|
81
|
+
<div class="nav-group-inner">${childLinks}</div>
|
|
76
82
|
</div>
|
|
77
|
-
${childLinks}
|
|
78
83
|
</div>
|
|
79
84
|
`
|
|
80
85
|
}
|
|
81
86
|
|
|
87
|
+
function initCollapsible(navEl, defaultExpandedKeys) {
|
|
88
|
+
const STORAGE_KEY = 'nav-collapsed-groups'
|
|
89
|
+
|
|
90
|
+
function getCollapsed() {
|
|
91
|
+
try {
|
|
92
|
+
return new Set(JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]'))
|
|
93
|
+
} catch {
|
|
94
|
+
return new Set()
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function saveCollapsed(collapsed) {
|
|
99
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify([...collapsed]))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (localStorage.getItem(STORAGE_KEY) === null) {
|
|
103
|
+
const allKeys = [...navEl.querySelectorAll('[data-group-toggle]')].map((b) => b.dataset.groupToggle)
|
|
104
|
+
saveCollapsed(new Set(allKeys.filter((k) => !defaultExpandedKeys.has(k))))
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const collapsed = getCollapsed()
|
|
108
|
+
|
|
109
|
+
navEl.querySelectorAll('[data-group-toggle]').forEach((btn) => {
|
|
110
|
+
const key = btn.dataset.groupToggle
|
|
111
|
+
const children = navEl.querySelector(`[data-group-children="${key}"]`)
|
|
112
|
+
const chevron = btn.querySelector('.nav-group-chevron')
|
|
113
|
+
const hasActive = !!children?.querySelector('.text-white')
|
|
114
|
+
|
|
115
|
+
if (!hasActive && collapsed.has(key)) {
|
|
116
|
+
children?.classList.add('collapsed')
|
|
117
|
+
chevron?.classList.add('-rotate-90')
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
btn.addEventListener('click', () => {
|
|
121
|
+
const nowCollapsed = children?.classList.toggle('collapsed')
|
|
122
|
+
chevron?.classList.toggle('-rotate-90', nowCollapsed)
|
|
123
|
+
|
|
124
|
+
const saved = getCollapsed()
|
|
125
|
+
if (nowCollapsed) saved.add(key)
|
|
126
|
+
else saved.delete(key)
|
|
127
|
+
saveCollapsed(saved)
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
|
|
82
132
|
export function getManifest() {
|
|
83
133
|
return manifest
|
|
84
134
|
}
|
|
@@ -108,6 +158,11 @@ export function initNav() {
|
|
|
108
158
|
<div class="sidebar-scroll space-y-1">${links}</div>
|
|
109
159
|
</div>
|
|
110
160
|
`
|
|
161
|
+
const groups = NAV_ITEMS.filter((i) => i.children)
|
|
162
|
+
const defaultExpandedKeys = new Set()
|
|
163
|
+
if (groups[0]) defaultExpandedKeys.add(groups[0].label)
|
|
164
|
+
if (manifest.nav.assessmentSection?.label) defaultExpandedKeys.add(manifest.nav.assessmentSection.label)
|
|
165
|
+
initCollapsible(navEl, defaultExpandedKeys)
|
|
111
166
|
}
|
|
112
167
|
|
|
113
168
|
export function initMobileNav() {
|
package/templates/index.html
CHANGED
|
@@ -70,11 +70,11 @@
|
|
|
70
70
|
<span class="flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-zinc-900 font-mono text-xs text-white">3</span>
|
|
71
71
|
|
|
72
72
|
<div>
|
|
73
|
-
<p class="font-medium text-zinc-900">
|
|
73
|
+
<p class="font-medium text-zinc-900">Meetmomenten toevoegen (optioneel)</p>
|
|
74
74
|
|
|
75
75
|
<p class="mt-1 text-sm text-zinc-500">
|
|
76
|
-
Plaats <code class="font-mono text-xs">content/
|
|
77
|
-
<code class="font-mono text-xs">practical-
|
|
76
|
+
Plaats <code class="font-mono text-xs">content/assessments/theory-assessment.md</code> en
|
|
77
|
+
<code class="font-mono text-xs">practical-assessment.md</code> met vragen in YAML frontmatter.
|
|
78
78
|
</p>
|
|
79
79
|
</div>
|
|
80
80
|
</li>
|
|
@@ -120,12 +120,20 @@ npm run preview # preview van dist/</code></pre>
|
|
|
120
120
|
</p>
|
|
121
121
|
</div>
|
|
122
122
|
|
|
123
|
-
<
|
|
123
|
+
<div class="mb-6 flex items-center justify-between">
|
|
124
|
+
<p class="text-[11px] font-semibold uppercase tracking-[0.18em] text-zinc-400">Curriculum</p>
|
|
125
|
+
|
|
126
|
+
<a href="e-module.pdf"
|
|
127
|
+
download
|
|
128
|
+
class="inline-flex items-center gap-1.5 rounded border border-zinc-200 bg-white px-3 py-1.5 text-xs font-medium text-zinc-600 hover:bg-zinc-50">
|
|
129
|
+
Download als PDF
|
|
130
|
+
</a>
|
|
131
|
+
</div>
|
|
124
132
|
|
|
125
133
|
<div class="mb-14 grid gap-px bg-zinc-200 sm:grid-cols-2"
|
|
126
134
|
data-home-curriculum></div>
|
|
127
135
|
|
|
128
|
-
<p class="mb-6 text-[11px] font-semibold uppercase tracking-[0.18em] text-zinc-400">
|
|
136
|
+
<p class="mb-6 text-[11px] font-semibold uppercase tracking-[0.18em] text-zinc-400">Meetmoment</p>
|
|
129
137
|
|
|
130
138
|
<div class="grid gap-px bg-zinc-200 sm:grid-cols-3">
|
|
131
139
|
<a href="pages/checklist.html"
|
|
@@ -136,16 +144,16 @@ npm run preview # preview van dist/</code></pre>
|
|
|
136
144
|
<p class="mt-1 text-sm text-zinc-500">Houd je voortgang bij</p>
|
|
137
145
|
</a>
|
|
138
146
|
|
|
139
|
-
<a href="pages/
|
|
147
|
+
<a href="pages/meetmoment-theorie.html"
|
|
140
148
|
class="card-interactive block bg-white">
|
|
141
|
-
<h3 class="font-medium text-zinc-900">
|
|
149
|
+
<h3 class="font-medium text-zinc-900">Meetmoment theorie</h3>
|
|
142
150
|
|
|
143
151
|
<p class="mt-1 text-sm text-zinc-500">Meerkeuzevragen</p>
|
|
144
152
|
</a>
|
|
145
153
|
|
|
146
|
-
<a href="pages/
|
|
154
|
+
<a href="pages/meetmoment-praktijk.html"
|
|
147
155
|
class="card-interactive block bg-white">
|
|
148
|
-
<h3 class="font-medium text-zinc-900">
|
|
156
|
+
<h3 class="font-medium text-zinc-900">Meetmoment praktijk</h3>
|
|
149
157
|
|
|
150
158
|
<p class="mt-1 text-sm text-zinc-500">Praktijkvragen</p>
|
|
151
159
|
</a>
|
|
@@ -19,14 +19,46 @@
|
|
|
19
19
|
</div>
|
|
20
20
|
</main>
|
|
21
21
|
</div>
|
|
22
|
+
|
|
23
|
+
<div data-theory-panel class="theory-panel panel-hidden">
|
|
24
|
+
<div class="theory-panel-header">
|
|
25
|
+
<div data-theory-tabs class="theory-panel-tabs"></div>
|
|
26
|
+
<button data-theory-panel-close
|
|
27
|
+
type="button"
|
|
28
|
+
class="theory-panel-close"
|
|
29
|
+
aria-label="Sluit theorievenster">
|
|
30
|
+
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" /></svg>
|
|
31
|
+
</button>
|
|
32
|
+
</div>
|
|
33
|
+
<div class="theory-panel-body">
|
|
34
|
+
<div data-theory-loader class="theory-panel-loader">
|
|
35
|
+
<div class="theory-panel-spinner"></div>
|
|
36
|
+
</div>
|
|
37
|
+
<iframe data-theory-iframe
|
|
38
|
+
class="theory-panel-iframe"
|
|
39
|
+
title="Theorie"></iframe>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<button data-theory-toggle
|
|
44
|
+
type="button"
|
|
45
|
+
class="theory-panel-toggle hidden"
|
|
46
|
+
aria-label="Toon theorie"
|
|
47
|
+
aria-expanded="false">
|
|
48
|
+
<svg class="h-4 w-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6.042A8.967 8.967 0 0 0 6 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 0 1 6 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 0 1 6-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0 0 18 18a8.966 8.966 0 0 0-6 2.292m0-14.25v14.25" /></svg>
|
|
49
|
+
Theorie
|
|
50
|
+
</button>
|
|
51
|
+
|
|
22
52
|
<script type="module">
|
|
23
53
|
import { initPage } from '/src/js/nav.js'
|
|
24
54
|
import { initInleveropdracht } from '/src/js/inleveropdracht.js'
|
|
55
|
+
import { initTheoryPanel } from '/src/js/exercises/theory-panel.js'
|
|
25
56
|
import inleveropdrachtData from '/src/data/inleveropdracht-week{{week}}.json'
|
|
26
57
|
initPage({ breadcrumbs: [{ label: 'Home', href: '/index.html' }, { label: 'Week {{week}}', href: '/pages/week{{week}}-theorie.html' }, { label: 'Inleveropdracht' }] })
|
|
27
58
|
const titleEl = document.querySelector('[data-inleveropdracht-title]')
|
|
28
59
|
if (titleEl) titleEl.textContent = inleveropdrachtData.title
|
|
29
60
|
initInleveropdracht(inleveropdrachtData)
|
|
61
|
+
initTheoryPanel(inleveropdrachtData.linked_theory)
|
|
30
62
|
</script>
|
|
31
63
|
</body>
|
|
32
64
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="nl">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<!-- include:head -->
|
|
6
|
+
<title>{{pageTitle}}</title>
|
|
7
|
+
</head>
|
|
8
|
+
|
|
9
|
+
<body class="font-sans antialiased">
|
|
10
|
+
<div data-page-content>
|
|
11
|
+
<main class="px-4 py-10 md:px-10">
|
|
12
|
+
<div class="mx-auto max-w-3xl">
|
|
13
|
+
<h1 class="text-3xl font-semibold tracking-tight text-zinc-900"
|
|
14
|
+
data-praktijk-title></h1>
|
|
15
|
+
<p class="mt-2 text-zinc-600"
|
|
16
|
+
data-praktijk-description></p>
|
|
17
|
+
<div data-praktijk-content
|
|
18
|
+
class="prose mt-8"></div>
|
|
19
|
+
</div>
|
|
20
|
+
</main>
|
|
21
|
+
</div>
|
|
22
|
+
<script type="module">
|
|
23
|
+
import { initPage } from '/src/js/nav.js'
|
|
24
|
+
import data from '/src/data/meetmoment-praktijk.json'
|
|
25
|
+
initPage({ breadcrumbs: [{ label: 'Home', href: '/index.html' }, { label: data.navLabel ?? '{{assessmentTitle}}' }] })
|
|
26
|
+
const titleEl = document.querySelector('[data-praktijk-title]')
|
|
27
|
+
if (titleEl) titleEl.textContent = data.title
|
|
28
|
+
const descEl = document.querySelector('[data-praktijk-description]')
|
|
29
|
+
if (descEl) descEl.innerHTML = data.description
|
|
30
|
+
const contentEl = document.querySelector('[data-praktijk-content]')
|
|
31
|
+
if (contentEl) contentEl.innerHTML = data.html
|
|
32
|
+
</script>
|
|
33
|
+
</body>
|
|
34
|
+
|
|
35
|
+
</html>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="nl">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<!-- include:head -->
|
|
6
|
+
<title>{{pageTitle}}</title>
|
|
7
|
+
</head>
|
|
8
|
+
|
|
9
|
+
<body class="font-sans antialiased">
|
|
10
|
+
<div data-page-content>
|
|
11
|
+
<main class="px-4 py-10 md:px-10">
|
|
12
|
+
<div class="mx-auto max-w-3xl">
|
|
13
|
+
<h1 class="text-3xl font-semibold tracking-tight text-zinc-900">{{assessmentTitle}}</h1>
|
|
14
|
+
<p class="mt-2 text-zinc-600">{{assessmentDescription}}</p>
|
|
15
|
+
<div data-quiz
|
|
16
|
+
class="mt-8"></div>
|
|
17
|
+
</div>
|
|
18
|
+
</main>
|
|
19
|
+
</div>
|
|
20
|
+
<script type="module">
|
|
21
|
+
import { initPage } from '/src/js/nav.js'
|
|
22
|
+
import { initQuiz } from '/src/js/quiz.js'
|
|
23
|
+
import quizData from '/src/data/meetmoment-theorie.json'
|
|
24
|
+
initPage({ breadcrumbs: [{ label: 'Home', href: '/index.html' }, { label: '{{assessmentTitle}}' }] })
|
|
25
|
+
initQuiz(quizData)
|
|
26
|
+
</script>
|
|
27
|
+
</body>
|
|
28
|
+
|
|
29
|
+
</html>
|
|
@@ -10,14 +10,14 @@
|
|
|
10
10
|
<div data-page-content>
|
|
11
11
|
<main class="px-4 py-10 md:px-10">
|
|
12
12
|
<div class="mx-auto max-w-3xl">
|
|
13
|
-
<span class="week-label">Week {{weekPadded}} —
|
|
13
|
+
<span class="week-label">Week {{weekPadded}} — Meetmoment Quiz</span>
|
|
14
14
|
<h1 class="text-3xl font-semibold tracking-tight text-zinc-900">{{weekTitle}}</h1>
|
|
15
15
|
<p class="mt-2 text-zinc-600">Test je kennis van week {{week}}. Minimaal 70% om te slagen.</p>
|
|
16
16
|
<div data-quiz
|
|
17
17
|
class="mt-8"></div>
|
|
18
18
|
<div class="card mt-10 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
|
19
19
|
<div>
|
|
20
|
-
<p class="font-medium text-zinc-900">
|
|
20
|
+
<p class="font-medium text-zinc-900">Meetmoment afgerond?</p>
|
|
21
21
|
<p class="mt-1 text-sm text-zinc-500">Werk de inleveropdracht uit.</p>
|
|
22
22
|
</div>
|
|
23
23
|
<a href="week{{week}}-inleveropdracht.html"
|
|
@@ -29,8 +29,15 @@
|
|
|
29
29
|
<script type="module">
|
|
30
30
|
import { initPage } from '/src/js/nav.js'
|
|
31
31
|
import { initQuiz } from '/src/js/quiz.js'
|
|
32
|
-
import quizData from '/src/data/
|
|
33
|
-
|
|
32
|
+
import quizData from '/src/data/meetmoment-quiz-week{{week}}.json'
|
|
33
|
+
|
|
34
|
+
initPage({
|
|
35
|
+
breadcrumbs: [
|
|
36
|
+
{ label: 'Home', href: '/index.html' },
|
|
37
|
+
{ label: 'Week {{week}}', href: '/pages/week{{week}}-theorie.html' },
|
|
38
|
+
{ label: 'Meetmoment Quiz' }
|
|
39
|
+
]
|
|
40
|
+
})
|
|
34
41
|
initQuiz(quizData)
|
|
35
42
|
</script>
|
|
36
43
|
</body>
|
|
@@ -143,6 +143,35 @@
|
|
|
143
143
|
</main>
|
|
144
144
|
</div>
|
|
145
145
|
|
|
146
|
+
<div data-theory-panel class="theory-panel panel-hidden">
|
|
147
|
+
<div class="theory-panel-header">
|
|
148
|
+
<div data-theory-tabs class="theory-panel-tabs"></div>
|
|
149
|
+
<button data-theory-panel-close
|
|
150
|
+
type="button"
|
|
151
|
+
class="theory-panel-close"
|
|
152
|
+
aria-label="Sluit theorievenster">
|
|
153
|
+
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" /></svg>
|
|
154
|
+
</button>
|
|
155
|
+
</div>
|
|
156
|
+
<div class="theory-panel-body">
|
|
157
|
+
<div data-theory-loader class="theory-panel-loader">
|
|
158
|
+
<div class="theory-panel-spinner"></div>
|
|
159
|
+
</div>
|
|
160
|
+
<iframe data-theory-iframe
|
|
161
|
+
class="theory-panel-iframe"
|
|
162
|
+
title="Theorie"></iframe>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
<button data-theory-toggle
|
|
167
|
+
type="button"
|
|
168
|
+
class="theory-panel-toggle hidden"
|
|
169
|
+
aria-label="Toon theorie"
|
|
170
|
+
aria-expanded="false">
|
|
171
|
+
<svg class="h-4 w-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6.042A8.967 8.967 0 0 0 6 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 0 1 6 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 0 1 6-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0 0 18 18a8.966 8.966 0 0 0-6 2.292m0-14.25v14.25" /></svg>
|
|
172
|
+
Theorie
|
|
173
|
+
</button>
|
|
174
|
+
|
|
146
175
|
<script type="module">
|
|
147
176
|
import { initPage } from '/src/js/nav.js'
|
|
148
177
|
import { initExercisePage } from '/src/js/exercises/load-exercise.js'
|
|
@@ -1,44 +1,52 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<html lang="nl">
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
</
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
</div>
|
|
20
|
-
</div>
|
|
21
|
-
<div class="progress-track">
|
|
22
|
-
<div data-hub-progress-bar class="progress-fill" style="width:0%"></div>
|
|
23
|
-
</div>
|
|
24
|
-
</div>
|
|
25
|
-
<div data-exercise-list class="mt-8 space-y-4"></div>
|
|
26
|
-
<div class="card mt-10 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<!-- include:head -->
|
|
6
|
+
<title>{{pageTitle}}</title>
|
|
7
|
+
</head>
|
|
8
|
+
|
|
9
|
+
<body class="font-sans antialiased">
|
|
10
|
+
<div data-page-content>
|
|
11
|
+
<main class="px-4 py-10 md:px-10">
|
|
12
|
+
<div class="mx-auto max-w-3xl">
|
|
13
|
+
<span class="week-label">Week {{week}}</span>
|
|
14
|
+
<h1 class="text-3xl font-semibold tracking-tight text-zinc-900">Oefeningen</h1>
|
|
15
|
+
<p class="mt-2 text-zinc-600"
|
|
16
|
+
data-hub-subtitle>{{weekTitle}} — 8 oefeningen.</p>
|
|
17
|
+
<div class="card mt-8">
|
|
18
|
+
<div class="mb-4 flex items-end justify-between">
|
|
27
19
|
<div>
|
|
28
|
-
<p class="font-
|
|
29
|
-
<p class="mt-1 text-sm text-zinc-500">
|
|
20
|
+
<p class="text-[11px] font-semibold uppercase tracking-[0.18em] text-zinc-400">Voortgang</p>
|
|
21
|
+
<p class="mt-1 text-sm text-zinc-500"><span data-hub-progress-text>0 / 8</span> afgerond</p>
|
|
30
22
|
</div>
|
|
31
|
-
<a href="week{{week}}-toets.html" class="btn-primary shrink-0">Naar de toets</a>
|
|
32
23
|
</div>
|
|
24
|
+
<div class="progress-track">
|
|
25
|
+
<div data-hub-progress-bar
|
|
26
|
+
class="progress-fill"
|
|
27
|
+
style="width:0%"></div>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
<div data-exercise-list
|
|
31
|
+
class="mt-8 space-y-4"></div>
|
|
32
|
+
<div class="card mt-10 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
|
33
|
+
<div>
|
|
34
|
+
<p class="font-medium text-zinc-900">Klaar met de oefeningen?</p>
|
|
35
|
+
<p class="mt-1 text-sm text-zinc-500">Test je kennis met de meetmoment.</p>
|
|
36
|
+
</div>
|
|
37
|
+
<a href="week{{week}}-meetmoment.html"
|
|
38
|
+
class="btn-primary shrink-0">Naar het meetmoment</a>
|
|
33
39
|
</div>
|
|
34
|
-
</
|
|
35
|
-
</
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
</
|
|
44
|
-
</
|
|
40
|
+
</div>
|
|
41
|
+
</main>
|
|
42
|
+
</div>
|
|
43
|
+
<script type="module">
|
|
44
|
+
import { initPage } from '/src/js/nav.js'
|
|
45
|
+
import { initExerciseHub } from '/src/js/exercises/hub.js'
|
|
46
|
+
import weekData from '/src/data/exercises/week{{week}}.json'
|
|
47
|
+
initPage({ breadcrumbs: [{ label: 'Home', href: '/index.html' }, { label: 'Week {{week}}' }, { label: 'Oefeningen' }] })
|
|
48
|
+
initExerciseHub(weekData, {{ week }})
|
|
49
|
+
</script>
|
|
50
|
+
</body>
|
|
51
|
+
|
|
52
|
+
</html>
|
|
@@ -15,7 +15,12 @@
|
|
|
15
15
|
<script type="module">
|
|
16
16
|
import { initPage } from '/src/js/nav.js'
|
|
17
17
|
import { initTheory } from '/src/js/theory.js'
|
|
18
|
-
|
|
18
|
+
const embedded = new URLSearchParams(location.search).has('embedded')
|
|
19
|
+
if (!embedded) {
|
|
20
|
+
initPage({ breadcrumbs: [{ label: 'Home', href: '/index.html' }, { label: 'Week {{week}}', href: '/pages/week{{week}}-theorie.html' }, { label: 'Theorie' }] })
|
|
21
|
+
} else {
|
|
22
|
+
document.body.setAttribute('data-embedded', '')
|
|
23
|
+
}
|
|
19
24
|
initTheory({{week}})
|
|
20
25
|
</script>
|
|
21
26
|
</body>
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="nl">
|
|
3
|
-
<head>
|
|
4
|
-
<!-- include:head -->
|
|
5
|
-
<title>{{pageTitle}}</title>
|
|
6
|
-
</head>
|
|
7
|
-
<body class="font-sans antialiased">
|
|
8
|
-
<div data-page-content>
|
|
9
|
-
<main class="px-4 py-10 md:px-10">
|
|
10
|
-
<div class="mx-auto max-w-3xl">
|
|
11
|
-
<h1 class="text-3xl font-semibold tracking-tight text-zinc-900">Eindtoets praktijk</h1>
|
|
12
|
-
<p class="mt-2 text-zinc-600">Praktijkvragen over de module. Minimaal 70% om te slagen.</p>
|
|
13
|
-
<div data-quiz class="mt-8"></div>
|
|
14
|
-
</div>
|
|
15
|
-
</main>
|
|
16
|
-
</div>
|
|
17
|
-
<script type="module">
|
|
18
|
-
import { initPage } from '/src/js/nav.js'
|
|
19
|
-
import { initQuiz } from '/src/js/quiz.js'
|
|
20
|
-
import quizData from '/src/data/toets-praktijk.json'
|
|
21
|
-
initPage({ breadcrumbs: [{ label: 'Home', href: '/index.html' }, { label: 'Eindtoets praktijk' }] })
|
|
22
|
-
initQuiz(quizData)
|
|
23
|
-
</script>
|
|
24
|
-
</body>
|
|
25
|
-
</html>
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="nl">
|
|
3
|
-
<head>
|
|
4
|
-
<!-- include:head -->
|
|
5
|
-
<title>{{pageTitle}}</title>
|
|
6
|
-
</head>
|
|
7
|
-
<body class="font-sans antialiased">
|
|
8
|
-
<div data-page-content>
|
|
9
|
-
<main class="px-4 py-10 md:px-10">
|
|
10
|
-
<div class="mx-auto max-w-3xl">
|
|
11
|
-
<h1 class="text-3xl font-semibold tracking-tight text-zinc-900">Eindtoets theorie</h1>
|
|
12
|
-
<p class="mt-2 text-zinc-600">Meerkeuzevragen over de module. Minimaal 70% om te slagen.</p>
|
|
13
|
-
<div data-quiz class="mt-8"></div>
|
|
14
|
-
</div>
|
|
15
|
-
</main>
|
|
16
|
-
</div>
|
|
17
|
-
<script type="module">
|
|
18
|
-
import { initPage } from '/src/js/nav.js'
|
|
19
|
-
import { initQuiz } from '/src/js/quiz.js'
|
|
20
|
-
import quizData from '/src/data/toets-theorie.json'
|
|
21
|
-
initPage({ breadcrumbs: [{ label: 'Home', href: '/index.html' }, { label: 'Eindtoets theorie' }] })
|
|
22
|
-
initQuiz(quizData)
|
|
23
|
-
</script>
|
|
24
|
-
</body>
|
|
25
|
-
</html>
|