@pie-element/mc-populated-blank 0.2.11 → 0.2.12
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/package.json +3 -14
- package/dist/controller/index.test.d.ts +0 -63
- package/dist/controller/index.test.d.ts.map +0 -1
- package/dist/delivery/AudioPlayer.test.d.ts +0 -2
- package/dist/delivery/AudioPlayer.test.d.ts.map +0 -1
- package/dist/delivery/ChoiceRow.test.d.ts +0 -2
- package/dist/delivery/ChoiceRow.test.d.ts.map +0 -1
- package/dist/delivery/ClozeMarker.test.d.ts +0 -2
- package/dist/delivery/ClozeMarker.test.d.ts.map +0 -1
- package/dist/delivery/McPopulatedBlank.transcript.test.d.ts +0 -2
- package/dist/delivery/McPopulatedBlank.transcript.test.d.ts.map +0 -1
- package/dist/delivery/computeAudioMode.test.d.ts +0 -2
- package/dist/delivery/computeAudioMode.test.d.ts.map +0 -1
- package/dist/delivery/computeChoiceCorrectness.test.d.ts +0 -19
- package/dist/delivery/computeChoiceCorrectness.test.d.ts.map +0 -1
- package/dist/delivery/computeDisplayState.test.d.ts +0 -38
- package/dist/delivery/computeDisplayState.test.d.ts.map +0 -1
- package/dist/delivery/computeLayoutProfile.test.d.ts +0 -2
- package/dist/delivery/computeLayoutProfile.test.d.ts.map +0 -1
- package/dist/delivery/computeLayoutStyle.test.d.ts +0 -2
- package/dist/delivery/computeLayoutStyle.test.d.ts.map +0 -1
- package/src/author/Author.svelte +0 -41
- package/src/author/index.ts +0 -28
- package/src/controller/defaults.ts +0 -61
- package/src/controller/index.test.ts +0 -428
- package/src/controller/index.ts +0 -372
- package/src/delivery/AudioPlayer.svelte +0 -246
- package/src/delivery/AudioPlayer.test.ts +0 -149
- package/src/delivery/ChoiceRow.svelte +0 -222
- package/src/delivery/ChoiceRow.test.ts +0 -179
- package/src/delivery/ClozeMarker.svelte +0 -83
- package/src/delivery/ClozeMarker.test.ts +0 -128
- package/src/delivery/McPopulatedBlank.svelte +0 -739
- package/src/delivery/McPopulatedBlank.transcript.test.ts +0 -154
- package/src/delivery/computeAudioMode.test.ts +0 -40
- package/src/delivery/computeAudioMode.ts +0 -11
- package/src/delivery/computeChoiceCorrectness.test.ts +0 -120
- package/src/delivery/computeChoiceCorrectness.ts +0 -42
- package/src/delivery/computeDisplayState.test.ts +0 -274
- package/src/delivery/computeDisplayState.ts +0 -101
- package/src/delivery/computeLayoutProfile.test.ts +0 -163
- package/src/delivery/computeLayoutProfile.ts +0 -72
- package/src/delivery/computeLayoutStyle.test.ts +0 -168
- package/src/delivery/computeLayoutStyle.ts +0 -128
- package/src/delivery/cqt-css/sel-r1-base.css +0 -59
- package/src/delivery/cqt-css/sel-r1-g-stem.css +0 -14
- package/src/delivery/cqt-css/sel-r1-gg-plus.css +0 -12
- package/src/delivery/cqt-css/sel-r1-ggplus.css +0 -17
- package/src/delivery/cqt-css/sel-r1-gplusggg.css +0 -25
- package/src/delivery/cqt-css/sel-r1-plusggg.css +0 -17
- package/src/delivery/cqt-css/sel-r1-s3.css +0 -85
- package/src/delivery/cqt-css/sel-vic.css +0 -99
- package/src/delivery/cqt-css/sr-vic.css +0 -63
- package/src/delivery/index.ts +0 -94
- package/src/delivery/variant-css-map.ts +0 -124
- package/src/index.iife.ts +0 -13
- package/src/index.ts +0 -1
- package/src/print/Print.svelte +0 -95
- package/src/print/index.ts +0 -18
- package/src/shared/layoutLimits.ts +0 -76
- package/src/shared/types.ts +0 -84
- package/src/types/css-raw.d.ts +0 -4
|
@@ -1,739 +0,0 @@
|
|
|
1
|
-
<svelte:options
|
|
2
|
-
customElement={{
|
|
3
|
-
shadow: 'none',
|
|
4
|
-
props: {
|
|
5
|
-
model: { type: 'Object' },
|
|
6
|
-
session: { type: 'Object' },
|
|
7
|
-
options: { type: 'Object' },
|
|
8
|
-
},
|
|
9
|
-
}}
|
|
10
|
-
/>
|
|
11
|
-
|
|
12
|
-
<script lang="ts">
|
|
13
|
-
import { color } from '@pie-lib/styling-svelte';
|
|
14
|
-
import { forwardSessionChange, resolveDeliveryHost } from '@pie-lib/delivery-events-svelte';
|
|
15
|
-
import AudioPlayer from './AudioPlayer.svelte';
|
|
16
|
-
import ClozeMarker from './ClozeMarker.svelte';
|
|
17
|
-
import ChoiceRow from './ChoiceRow.svelte';
|
|
18
|
-
import { computeChoiceCorrectness } from './computeChoiceCorrectness';
|
|
19
|
-
import { computeLayoutProfile } from './computeLayoutProfile';
|
|
20
|
-
import { computeLayoutStyle, DEFAULT_LAYOUT_LIMITS } from './computeLayoutStyle';
|
|
21
|
-
import {
|
|
22
|
-
computeFeatureAudioSkin,
|
|
23
|
-
computeDisplayChoiceId,
|
|
24
|
-
computeResultText,
|
|
25
|
-
computeLegendText,
|
|
26
|
-
} from './computeDisplayState';
|
|
27
|
-
import {
|
|
28
|
-
ensureVariantCssInjected,
|
|
29
|
-
getVariantCssConfig,
|
|
30
|
-
getVariantRootClass,
|
|
31
|
-
} from './variant-css-map';
|
|
32
|
-
|
|
33
|
-
const BLANK_TOKEN = '{{blank}}';
|
|
34
|
-
const DEFAULT_UI_TEXT = {
|
|
35
|
-
answerChoices: 'Answer choices',
|
|
36
|
-
selectedAnswerInSentence: 'Selected answer in sentence',
|
|
37
|
-
showCorrectAnswer: 'Show correct answer',
|
|
38
|
-
hideCorrectAnswer: 'Hide correct answer',
|
|
39
|
-
clickToEnableAutoplay: 'Click to enable audio autoplay',
|
|
40
|
-
audioResourceUnavailable: 'Audio is enabled but no playable audio URL is configured.',
|
|
41
|
-
transcriptLabel: 'Transcript',
|
|
42
|
-
} as const;
|
|
43
|
-
|
|
44
|
-
let { model, session } = $props<{ model?: any; session?: any; options?: any }>();
|
|
45
|
-
let localChoiceId = $state('');
|
|
46
|
-
let toggleCorrectAnswerButtonEl = $state<HTMLButtonElement | null>(null);
|
|
47
|
-
let choicesGroupEl = $state<HTMLDivElement | null>(null);
|
|
48
|
-
let rootEl = $state<HTMLDivElement | null>(null);
|
|
49
|
-
let ancestorHasTranscriptClass = $state(false);
|
|
50
|
-
const instanceId = `mc-populated-blank-${Math.random().toString(36).slice(2, 10)}`;
|
|
51
|
-
|
|
52
|
-
// ---------------------------------------------------------------------------
|
|
53
|
-
// Cluster: modeFlags — delivery mode, correctness, and answer-reveal state
|
|
54
|
-
// Feeds: shouldShowCorrectAnswerToggle, choiceState, a11y
|
|
55
|
-
// ---------------------------------------------------------------------------
|
|
56
|
-
const deliveryMode = $derived((model?.mode || model?.env?.mode || '').toString());
|
|
57
|
-
const isEvaluateMode = $derived(deliveryMode === 'evaluate');
|
|
58
|
-
const correctness = $derived(model?.correctness);
|
|
59
|
-
const isResponseCorrect = $derived(
|
|
60
|
-
typeof model?.responseCorrect === 'boolean' ? model.responseCorrect : correctness === 'correct'
|
|
61
|
-
);
|
|
62
|
-
const isCorrect = $derived(correctness === 'correct');
|
|
63
|
-
const isIncorrect = $derived(correctness === 'incorrect');
|
|
64
|
-
|
|
65
|
-
let showCorrectAnswer = $state(false);
|
|
66
|
-
const shouldShowCorrectAnswerToggle = $derived(
|
|
67
|
-
isEvaluateMode && !isResponseCorrect && !!model?.correctChoiceId
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
// ---------------------------------------------------------------------------
|
|
71
|
-
// Cluster: layoutConfig — profile limits, blank sizing, choice layout, audio mode
|
|
72
|
-
// Feeds: layout (rootStyle, blankWidth, blankBorderWidth, legendMaxChars), audioMode, choiceState
|
|
73
|
-
// ---------------------------------------------------------------------------
|
|
74
|
-
const layoutProfile = $derived(model?.layoutProfile || '');
|
|
75
|
-
const layoutProfileFlags = $derived(
|
|
76
|
-
computeLayoutProfile({
|
|
77
|
-
interactionMode: model?.interactionMode,
|
|
78
|
-
template: model?.template,
|
|
79
|
-
choiceLayout: model?.choiceLayout,
|
|
80
|
-
layoutProfile,
|
|
81
|
-
hasAudio: model?.hasAudio,
|
|
82
|
-
useFeatureButtonAudio: model?.useFeatureButtonAudio,
|
|
83
|
-
})
|
|
84
|
-
);
|
|
85
|
-
const isAudioOnlyMode = $derived(layoutProfileFlags.isAudioOnlyMode);
|
|
86
|
-
const isBlankOnlyTemplate = $derived(layoutProfileFlags.isBlankOnlyTemplate);
|
|
87
|
-
const isHorizontalChoices = $derived(layoutProfileFlags.isHorizontalChoices);
|
|
88
|
-
const hasInlineSentenceAudioLayout = $derived(layoutProfileFlags.hasInlineSentenceAudioLayout);
|
|
89
|
-
const useFeatureButtonAudio = $derived(layoutProfileFlags.useFeatureButtonAudio);
|
|
90
|
-
const showVisibleTranscript = $derived(ancestorHasTranscriptClass);
|
|
91
|
-
const correctAnswerStyleVars = $derived.by(() =>
|
|
92
|
-
[
|
|
93
|
-
`--pie-correct-answer-toggle-label-color:${color.text()}`,
|
|
94
|
-
`--pie-correct-answer-toggle-icon-open-bg:${color.tertiaryLight()}`,
|
|
95
|
-
`--pie-correct-answer-toggle-icon-closed-bg:${color.backgroundDark()}`,
|
|
96
|
-
`--pie-correct-answer-toggle-icon-glyph-color:${color.tertiary()}`,
|
|
97
|
-
`--pie-correct-answer-choice-hover-bg:${color.backgroundDark()}`,
|
|
98
|
-
`--pie-correct-answer-choice-selected-bg:${color.secondaryBackground()}`,
|
|
99
|
-
`--pie-correct-answer-choice-correct-bg:${color.correctSecondary()}`,
|
|
100
|
-
`--pie-correct-answer-choice-incorrect-bg:${color.incorrectSecondary()}`,
|
|
101
|
-
`--pie-correct-answer-choice-correct-border:${color.correctTertiary()}`,
|
|
102
|
-
`--pie-correct-answer-choice-incorrect-border:${color.incorrectWithIcon()}`,
|
|
103
|
-
`--pie-correct-answer-feedback-correct-bg:${color.correctWithIcon()}`,
|
|
104
|
-
`--pie-correct-answer-feedback-incorrect-bg:${color.incorrectWithIcon()}`,
|
|
105
|
-
`--pie-correct-answer-feedback-glyph-color:${color.white()}`,
|
|
106
|
-
].join(';')
|
|
107
|
-
);
|
|
108
|
-
const layout = $derived(
|
|
109
|
-
computeLayoutStyle({
|
|
110
|
-
layoutProfile,
|
|
111
|
-
isBlankOnlyTemplate,
|
|
112
|
-
configuredLimits: model?.layoutLimits,
|
|
113
|
-
customProfilePresets: model?.layoutProfilePresets,
|
|
114
|
-
correctAnswerStyleVars,
|
|
115
|
-
})
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
// ---------------------------------------------------------------------------
|
|
119
|
-
// Cluster: choiceState — selection, display choice, per-choice correctness, result text
|
|
120
|
-
// Feeds: template blank display, choice rendering, fieldset, result sr-only text
|
|
121
|
-
// ---------------------------------------------------------------------------
|
|
122
|
-
const choices = $derived(Array.isArray(model?.choices) ? model.choices : []);
|
|
123
|
-
const choiceMode = $derived(model?.choiceMode || 'text');
|
|
124
|
-
const selectedId = $derived(session?.choiceId || localChoiceId || '');
|
|
125
|
-
const radioGroupName = $derived(`${instanceId}-choice-group-${model?.id || '1'}`);
|
|
126
|
-
const displayChoiceId = $derived(
|
|
127
|
-
computeDisplayChoiceId({
|
|
128
|
-
selectedId,
|
|
129
|
-
isEvaluateMode,
|
|
130
|
-
showCorrectAnswer,
|
|
131
|
-
alwaysShowCorrect: !!model?.alwaysShowCorrect,
|
|
132
|
-
correctChoiceId: String(model?.correctChoiceId || ''),
|
|
133
|
-
})
|
|
134
|
-
);
|
|
135
|
-
const displayChoice = $derived.by(() => choices.find((c: any) => c.id === displayChoiceId));
|
|
136
|
-
const displayChoiceLabelHtml = $derived.by(() => String(displayChoice?.labelHtml || ''));
|
|
137
|
-
const resultText = $derived(
|
|
138
|
-
computeResultText({ isEvaluateMode, showCorrectAnswer, isCorrect, isIncorrect, selectedId })
|
|
139
|
-
);
|
|
140
|
-
const choiceCorrectnessById = $derived(
|
|
141
|
-
computeChoiceCorrectness({
|
|
142
|
-
isEvaluateMode,
|
|
143
|
-
correctChoiceId: String(model?.correctChoiceId || ''),
|
|
144
|
-
selectedId: String(selectedId || ''),
|
|
145
|
-
showCorrectAnswer,
|
|
146
|
-
})
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
// ---------------------------------------------------------------------------
|
|
150
|
-
// Cluster: a11y — stable IDs, aria labelling, described-by relationships
|
|
151
|
-
// Feeds: fieldset legend, radiogroup labelling, template described-by
|
|
152
|
-
// ---------------------------------------------------------------------------
|
|
153
|
-
const uiText = $derived.by(() => ({
|
|
154
|
-
...DEFAULT_UI_TEXT,
|
|
155
|
-
...(model?.uiText || {}),
|
|
156
|
-
}));
|
|
157
|
-
const promptId = $derived(`${instanceId}-prompt`);
|
|
158
|
-
const transcriptId = $derived(`${instanceId}-transcript`);
|
|
159
|
-
const legendId = $derived(`${instanceId}-choices-legend`);
|
|
160
|
-
const resultId = $derived(`${instanceId}-result`);
|
|
161
|
-
const legendText = $derived(
|
|
162
|
-
computeLegendText({
|
|
163
|
-
prompt: model?.prompt || '',
|
|
164
|
-
legendMaxChars: layout.legendMaxChars,
|
|
165
|
-
answerChoicesLabel: uiText.answerChoices,
|
|
166
|
-
})
|
|
167
|
-
);
|
|
168
|
-
const choicesGroupLabelledBy = $derived(model?.prompt ? promptId : undefined);
|
|
169
|
-
const choicesGroupAriaLabel = $derived.by(() => {
|
|
170
|
-
if (model?.prompt) return undefined;
|
|
171
|
-
const explicit = String(model?.choiceGroupLabel || '').trim();
|
|
172
|
-
if (explicit) return explicit;
|
|
173
|
-
return legendText;
|
|
174
|
-
});
|
|
175
|
-
const templateDescribedBy = $derived.by(() => {
|
|
176
|
-
const ids: string[] = [];
|
|
177
|
-
if (model?.prompt) ids.push(promptId);
|
|
178
|
-
if (model?.hasAudio && model?.audioTranscript) ids.push(transcriptId);
|
|
179
|
-
return ids.length ? ids.join(' ') : undefined;
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
// ---------------------------------------------------------------------------
|
|
183
|
-
// Misc — locale, audio error, template parsing, variant CSS, style strings
|
|
184
|
-
// ---------------------------------------------------------------------------
|
|
185
|
-
const lang = $derived.by(() => {
|
|
186
|
-
const locale = model?.locale || '';
|
|
187
|
-
return locale ? locale.slice(0, 2) : 'en';
|
|
188
|
-
});
|
|
189
|
-
const variantCssConfig = $derived(getVariantCssConfig(model?.customType));
|
|
190
|
-
const variantRootClass = $derived(getVariantRootClass(model?.customType));
|
|
191
|
-
const templateParts = $derived.by(() => {
|
|
192
|
-
const t = model?.template || '';
|
|
193
|
-
const idx = t.indexOf(BLANK_TOKEN);
|
|
194
|
-
if (idx < 0) return { before: t, after: '' };
|
|
195
|
-
return {
|
|
196
|
-
before: t.slice(0, idx),
|
|
197
|
-
after: t.slice(idx + BLANK_TOKEN.length),
|
|
198
|
-
};
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
function emitSession(updatedSession: any, sourceEl?: HTMLElement | null) {
|
|
202
|
-
forwardSessionChange({
|
|
203
|
-
sourceEl,
|
|
204
|
-
fallbackSelector: 'mc-populated-blank',
|
|
205
|
-
component: 'mc-populated-blank',
|
|
206
|
-
session: updatedSession,
|
|
207
|
-
complete: !!updatedSession?.choiceId,
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function onRadioChange(e: Event) {
|
|
212
|
-
const input = e.target as HTMLInputElement;
|
|
213
|
-
if (!input.checked) return;
|
|
214
|
-
const choiceId = input.value;
|
|
215
|
-
localChoiceId = choiceId;
|
|
216
|
-
const updatedSession = {
|
|
217
|
-
...session,
|
|
218
|
-
id: session?.id || model?.id || '1',
|
|
219
|
-
element: 'mc-populated-blank',
|
|
220
|
-
choiceId,
|
|
221
|
-
};
|
|
222
|
-
emitSession(updatedSession, input);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function toggleCorrectAnswer() {
|
|
226
|
-
if (!shouldShowCorrectAnswerToggle) return;
|
|
227
|
-
showCorrectAnswer = !showCorrectAnswer;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
$effect(() => {
|
|
231
|
-
if (!isEvaluateMode || isResponseCorrect) {
|
|
232
|
-
showCorrectAnswer = false;
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
function onRadioGroupKeydown(e: KeyboardEvent) {
|
|
237
|
-
if (model?.disabled) return;
|
|
238
|
-
const key = e.key;
|
|
239
|
-
if (!['ArrowDown', 'ArrowRight', 'ArrowUp', 'ArrowLeft'].includes(key)) return;
|
|
240
|
-
e.preventDefault();
|
|
241
|
-
const container = e.currentTarget as HTMLElement;
|
|
242
|
-
const inputs = Array.from(
|
|
243
|
-
container.querySelectorAll('input[type="radio"]:not([disabled])')
|
|
244
|
-
) as HTMLInputElement[];
|
|
245
|
-
if (inputs.length === 0) return;
|
|
246
|
-
let idx = inputs.findIndex((i) => i.checked);
|
|
247
|
-
if (key === 'ArrowDown' || key === 'ArrowRight') {
|
|
248
|
-
idx = idx < 0 ? 0 : (idx + 1) % inputs.length;
|
|
249
|
-
} else {
|
|
250
|
-
idx = idx < 0 ? inputs.length - 1 : (idx - 1 + inputs.length) % inputs.length;
|
|
251
|
-
}
|
|
252
|
-
const next = inputs[idx];
|
|
253
|
-
next.checked = true;
|
|
254
|
-
next.dispatchEvent(new Event('change', { bubbles: true }));
|
|
255
|
-
next.focus();
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const featureAudioSkin = $derived(
|
|
259
|
-
computeFeatureAudioSkin({
|
|
260
|
-
locale: model?.locale,
|
|
261
|
-
audioButtonSkin: model?.audioButtonSkin,
|
|
262
|
-
audioButtonSkinsByLocale: model?.audioButtonSkinsByLocale,
|
|
263
|
-
})
|
|
264
|
-
);
|
|
265
|
-
|
|
266
|
-
function onAudioStarted() {
|
|
267
|
-
resolveDeliveryHost(rootEl, { fallbackSelector: 'mc-populated-blank' })?.onAudioStarted?.();
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
function onAudioEnded() {
|
|
271
|
-
resolveDeliveryHost(rootEl, { fallbackSelector: 'mc-populated-blank' })?.onAudioEnded?.();
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
$effect(() => {
|
|
275
|
-
if (session?.choiceId) {
|
|
276
|
-
localChoiceId = session.choiceId;
|
|
277
|
-
}
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
$effect(() => {
|
|
281
|
-
const btn = toggleCorrectAnswerButtonEl;
|
|
282
|
-
if (!btn) return;
|
|
283
|
-
btn.addEventListener('click', toggleCorrectAnswer);
|
|
284
|
-
return () => btn.removeEventListener('click', toggleCorrectAnswer);
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
// Delegated listeners on the choices group — two events with a radio-guard on change.
|
|
288
|
-
$effect(() => {
|
|
289
|
-
const group = choicesGroupEl;
|
|
290
|
-
if (!group) return;
|
|
291
|
-
const handleChange = (e: Event) => {
|
|
292
|
-
const target = e.target as HTMLInputElement | null;
|
|
293
|
-
if (!target || target.type !== 'radio') return;
|
|
294
|
-
onRadioChange(e);
|
|
295
|
-
};
|
|
296
|
-
const handleKeydown = (e: KeyboardEvent) => onRadioGroupKeydown(e);
|
|
297
|
-
group.addEventListener('change', handleChange);
|
|
298
|
-
group.addEventListener('keydown', handleKeydown);
|
|
299
|
-
return () => {
|
|
300
|
-
group.removeEventListener('change', handleChange);
|
|
301
|
-
group.removeEventListener('keydown', handleKeydown);
|
|
302
|
-
};
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
$effect(() => {
|
|
306
|
-
ensureVariantCssInjected(variantCssConfig);
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
$effect(() => {
|
|
310
|
-
const el = rootEl;
|
|
311
|
-
if (!el) return;
|
|
312
|
-
|
|
313
|
-
const check = () => {
|
|
314
|
-
ancestorHasTranscriptClass = !!el.closest('.rli-with-audio-transcript');
|
|
315
|
-
};
|
|
316
|
-
check();
|
|
317
|
-
|
|
318
|
-
const observer = new MutationObserver(check);
|
|
319
|
-
// Walk up and observe each ancestor for class changes
|
|
320
|
-
let node: Element | null = el.parentElement;
|
|
321
|
-
while (node) {
|
|
322
|
-
observer.observe(node, { attributes: true, attributeFilter: ['class'] });
|
|
323
|
-
node = node.parentElement;
|
|
324
|
-
}
|
|
325
|
-
return () => observer.disconnect();
|
|
326
|
-
});
|
|
327
|
-
</script>
|
|
328
|
-
|
|
329
|
-
<div
|
|
330
|
-
bind:this={rootEl}
|
|
331
|
-
class={`p-4 mc-populated-blank-root pie-element pie-element-mc-populated-blank pie-delivery-root layout-${layoutProfile} ${variantRootClass} ${hasInlineSentenceAudioLayout ? 'has-inline-audio' : ''}`}
|
|
332
|
-
lang={lang}
|
|
333
|
-
style={layout.rootStyle}
|
|
334
|
-
>
|
|
335
|
-
{#if model?.prompt}
|
|
336
|
-
<div class="mb-4 prose pie-prompt" id={promptId}>{@html model.prompt}</div>
|
|
337
|
-
{/if}
|
|
338
|
-
|
|
339
|
-
{#if model?.audioTranscript}
|
|
340
|
-
<p
|
|
341
|
-
class={`text-sm mb-3 text-gray-700 text-center pie-audio-transcript ${showVisibleTranscript ? '' : 'sr-only'}`}
|
|
342
|
-
id={transcriptId}
|
|
343
|
-
>
|
|
344
|
-
{model.audioTranscript}
|
|
345
|
-
</p>
|
|
346
|
-
{/if}
|
|
347
|
-
|
|
348
|
-
{#if shouldShowCorrectAnswerToggle}
|
|
349
|
-
<div class="pie-correct-answer-toggle-row">
|
|
350
|
-
<button
|
|
351
|
-
bind:this={toggleCorrectAnswerButtonEl}
|
|
352
|
-
type="button"
|
|
353
|
-
class="mb-3 pie-toggle-correct-answer"
|
|
354
|
-
style="gap:var(--mpb-toggle-button-gap, 0.5rem);"
|
|
355
|
-
aria-pressed={showCorrectAnswer}
|
|
356
|
-
data-testid="show-correct-answer"
|
|
357
|
-
>
|
|
358
|
-
<span class="pie-correct-answer-toggle-content">
|
|
359
|
-
<span class="pie-correct-answer-toggle-icon-holder" aria-hidden="true">
|
|
360
|
-
{#if showCorrectAnswer}
|
|
361
|
-
<svg
|
|
362
|
-
class="pie-correct-answer-toggle-svg"
|
|
363
|
-
preserveAspectRatio="xMinYMin meet"
|
|
364
|
-
version="1.1"
|
|
365
|
-
viewBox="-283 359 34 35"
|
|
366
|
-
>
|
|
367
|
-
<circle cx="-266" cy="375.9" r="14" fill="var(--pie-correct-answer-toggle-icon-open-bg, #bce2ff)" />
|
|
368
|
-
<path
|
|
369
|
-
d="M-280.5,375.9c0-8,6.5-14.5,14.5-14.5s14.5,6.5,14.5,14.5s-6.5,14.5-14.5,14.5S-280.5,383.9-280.5,375.9z M-279.5,375.9c0,7.4,6.1,13.5,13.5,13.5c7.4,0,13.5-6.1,13.5-13.5s-6.1-13.5-13.5-13.5C-273.4,362.4-279.5,368.5-279.5,375.9z"
|
|
370
|
-
fill="var(--pie-correct-answer-toggle-icon-open-bg, #bce2ff)"
|
|
371
|
-
/>
|
|
372
|
-
<polygon
|
|
373
|
-
points="-265.4,383.1 -258.6,377.2 -261.2,374.2 -264.3,376.9 -268.9,368.7 -272.4,370.6"
|
|
374
|
-
fill="var(--pie-correct-answer-toggle-icon-glyph-color, #1a9cff)"
|
|
375
|
-
/>
|
|
376
|
-
</svg>
|
|
377
|
-
{:else}
|
|
378
|
-
<svg
|
|
379
|
-
class="pie-correct-answer-toggle-svg"
|
|
380
|
-
preserveAspectRatio="xMinYMin meet"
|
|
381
|
-
version="1.1"
|
|
382
|
-
viewBox="-129.5 127 34 35"
|
|
383
|
-
>
|
|
384
|
-
<path
|
|
385
|
-
d="M-112.9,160.4c-8.5,0-15.5-6.9-15.5-15.5c0-8.5,6.9-15.5,15.5-15.5s15.5,6.9,15.5,15.5 C-97.4,153.5-104.3,160.4-112.9,160.4z"
|
|
386
|
-
fill="#D0CAC5"
|
|
387
|
-
stroke="#E6E3E0"
|
|
388
|
-
stroke-width="0.75"
|
|
389
|
-
/>
|
|
390
|
-
<path
|
|
391
|
-
d="M-113.2,159c-8,0-14.5-6.5-14.5-14.5s6.5-14.5,14.5-14.5s14.5,6.5,14.5,14.5S-105.2,159-113.2,159z"
|
|
392
|
-
fill="#B3ABA4"
|
|
393
|
-
stroke="#CDC7C2"
|
|
394
|
-
stroke-width="0.5"
|
|
395
|
-
/>
|
|
396
|
-
<circle cx="-114.2" cy="143.5" r="14" fill="white" />
|
|
397
|
-
<path
|
|
398
|
-
d="M-114.2,158c-8,0-14.5-6.5-14.5-14.5s6.5-14.5,14.5-14.5s14.5,6.5,14.5,14.5S-106.2,158-114.2,158z M-114.2,130c-7.4,0-13.5,6.1-13.5,13.5s6.1,13.5,13.5,13.5s13.5-6.1,13.5-13.5S-106.8,130-114.2,130z"
|
|
399
|
-
fill="var(--pie-correct-answer-toggle-icon-closed-bg, #bce2ff)"
|
|
400
|
-
/>
|
|
401
|
-
<polygon
|
|
402
|
-
points="-114.8,150.7 -121.6,144.8 -119,141.8 -115.9,144.5 -111.3,136.3 -107.8,138.2"
|
|
403
|
-
fill="var(--pie-correct-answer-toggle-icon-glyph-color, #1a9cff)"
|
|
404
|
-
/>
|
|
405
|
-
</svg>
|
|
406
|
-
{/if}
|
|
407
|
-
</span>
|
|
408
|
-
<span class="pie-correct-answer-toggle-label">
|
|
409
|
-
{showCorrectAnswer ? uiText.hideCorrectAnswer : uiText.showCorrectAnswer}
|
|
410
|
-
</span>
|
|
411
|
-
</span>
|
|
412
|
-
</button>
|
|
413
|
-
</div>
|
|
414
|
-
{/if}
|
|
415
|
-
|
|
416
|
-
<AudioPlayer
|
|
417
|
-
hasAudio={!!model?.hasAudio}
|
|
418
|
-
audioUrl={model?.audioUrl}
|
|
419
|
-
{useFeatureButtonAudio}
|
|
420
|
-
autoplayEnabled={!!model?.autoplayAudioEnabled}
|
|
421
|
-
audioTranscript={model?.audioTranscript}
|
|
422
|
-
{showVisibleTranscript}
|
|
423
|
-
{transcriptId}
|
|
424
|
-
{featureAudioSkin}
|
|
425
|
-
{uiText}
|
|
426
|
-
locale={model?.locale}
|
|
427
|
-
onaudiostarted={onAudioStarted}
|
|
428
|
-
onaudioended={onAudioEnded}
|
|
429
|
-
/>
|
|
430
|
-
|
|
431
|
-
{#if model?.sentenceHtml}
|
|
432
|
-
<div class="mb-3 prose prose-p:my-1 sentence-line pie-sentence-line" aria-describedby={templateDescribedBy}>
|
|
433
|
-
{@html model.sentenceHtml}
|
|
434
|
-
</div>
|
|
435
|
-
{/if}
|
|
436
|
-
|
|
437
|
-
{#if !isAudioOnlyMode}
|
|
438
|
-
<div class="mb-4 template-line pie-template-line" aria-describedby={templateDescribedBy}>
|
|
439
|
-
{@html templateParts.before}
|
|
440
|
-
<ClozeMarker
|
|
441
|
-
{choiceMode}
|
|
442
|
-
displayChoice={displayChoice}
|
|
443
|
-
{displayChoiceLabelHtml}
|
|
444
|
-
isStandalone={isBlankOnlyTemplate}
|
|
445
|
-
blankWidth={layout.blankWidth}
|
|
446
|
-
blankBorderWidth={layout.blankBorderWidth}
|
|
447
|
-
ariaLabel={uiText.selectedAnswerInSentence}
|
|
448
|
-
/>
|
|
449
|
-
{@html templateParts.after}
|
|
450
|
-
</div>
|
|
451
|
-
{/if}
|
|
452
|
-
|
|
453
|
-
{#if resultText}
|
|
454
|
-
<p id={resultId} class="sr-only pie-result-feedback" role="status" aria-live="polite">{resultText}</p>
|
|
455
|
-
{/if}
|
|
456
|
-
|
|
457
|
-
<fieldset class="border-0 p-0 m-0 pie-choices-fieldset" disabled={model?.disabled}>
|
|
458
|
-
<legend class="sr-only pie-choices-legend" id={legendId}>{legendText}</legend>
|
|
459
|
-
<div
|
|
460
|
-
bind:this={choicesGroupEl}
|
|
461
|
-
class={`pie-choices ${isHorizontalChoices ? 'flex flex-row flex-wrap items-start justify-center' : 'flex flex-col'}`}
|
|
462
|
-
style="gap:var(--mpb-choice-group-gap, 0.5rem);"
|
|
463
|
-
role="radiogroup"
|
|
464
|
-
tabindex="-1"
|
|
465
|
-
aria-labelledby={choicesGroupLabelledBy}
|
|
466
|
-
aria-label={choicesGroupAriaLabel}
|
|
467
|
-
aria-describedby={resultText ? resultId : undefined}
|
|
468
|
-
>
|
|
469
|
-
{#each choices as c (c.id)}
|
|
470
|
-
<ChoiceRow
|
|
471
|
-
choice={c}
|
|
472
|
-
{choiceMode}
|
|
473
|
-
isHorizontal={isHorizontalChoices}
|
|
474
|
-
isSelected={displayChoiceId === c.id}
|
|
475
|
-
isDisabled={!!model?.disabled}
|
|
476
|
-
correctness={choiceCorrectnessById.get(c.id)}
|
|
477
|
-
{isEvaluateMode}
|
|
478
|
-
{instanceId}
|
|
479
|
-
{radioGroupName}
|
|
480
|
-
/>
|
|
481
|
-
{/each}
|
|
482
|
-
</div>
|
|
483
|
-
</fieldset>
|
|
484
|
-
</div>
|
|
485
|
-
|
|
486
|
-
<style>
|
|
487
|
-
.sr-only {
|
|
488
|
-
position: absolute;
|
|
489
|
-
width: 1px;
|
|
490
|
-
height: 1px;
|
|
491
|
-
padding: 0;
|
|
492
|
-
margin: -1px;
|
|
493
|
-
overflow: hidden;
|
|
494
|
-
clip: rect(0, 0, 0, 0);
|
|
495
|
-
white-space: nowrap;
|
|
496
|
-
border: 0;
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
.mc-populated-blank-root :global(.prose) {
|
|
500
|
-
max-width: none;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
.pie-toggle-correct-answer {
|
|
504
|
-
width: 100%;
|
|
505
|
-
cursor: pointer;
|
|
506
|
-
border: 0;
|
|
507
|
-
background: transparent;
|
|
508
|
-
padding: 0;
|
|
509
|
-
display: flex;
|
|
510
|
-
justify-content: center;
|
|
511
|
-
text-align: center;
|
|
512
|
-
color: var(--pie-correct-answer-toggle-label-color, var(--pie-text, black));
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
.pie-correct-answer-toggle-row {
|
|
516
|
-
width: 100%;
|
|
517
|
-
display: flex;
|
|
518
|
-
justify-content: flex-end;
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
.pie-correct-answer-toggle-content {
|
|
522
|
-
display: flex;
|
|
523
|
-
margin: 0 auto;
|
|
524
|
-
align-items: center;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
.pie-correct-answer-toggle-icon-holder {
|
|
528
|
-
width: 25px;
|
|
529
|
-
margin-right: 5px;
|
|
530
|
-
display: inline-flex;
|
|
531
|
-
align-items: center;
|
|
532
|
-
justify-content: center;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
.pie-correct-answer-toggle-svg {
|
|
536
|
-
width: 25px;
|
|
537
|
-
height: 25px;
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
.pie-correct-answer-toggle-label {
|
|
541
|
-
width: fit-content;
|
|
542
|
-
min-width: 140px;
|
|
543
|
-
align-self: center;
|
|
544
|
-
vertical-align: middle;
|
|
545
|
-
font-weight: 400;
|
|
546
|
-
user-select: none;
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
.pie-toggle-correct-answer:hover .pie-correct-answer-toggle-label,
|
|
550
|
-
.pie-toggle-correct-answer:focus-visible .pie-correct-answer-toggle-label {
|
|
551
|
-
text-decoration: underline;
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
.template-line {
|
|
555
|
-
white-space: pre-wrap;
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
.template-line :global(p) {
|
|
559
|
-
margin: 0;
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
.layout-audio_blank_only :global(.pie-audio-container),
|
|
563
|
-
.layout-stimulus_image_blank :global(.pie-audio-container) {
|
|
564
|
-
display: flex;
|
|
565
|
-
width: min(100%, var(--mpb-audio-instructions-max-width, 875px));
|
|
566
|
-
margin-left: auto;
|
|
567
|
-
margin-right: auto;
|
|
568
|
-
justify-content: flex-end;
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
.layout-audio_blank_only .template-line,
|
|
572
|
-
.layout-stimulus_image_blank .template-line {
|
|
573
|
-
text-align: center;
|
|
574
|
-
margin: var(--mpb-audio-blank-template-margin-top, 0.8rem) 0
|
|
575
|
-
var(--mpb-audio-blank-template-margin-bottom, 1.8rem);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
.layout-audio_blank_only fieldset {
|
|
579
|
-
display: flex;
|
|
580
|
-
justify-content: center;
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
.layout-audio_blank_only :global(.pie-blank-slot),
|
|
584
|
-
.layout-stimulus_image_blank :global(.pie-blank-slot) {
|
|
585
|
-
width: var(--mpb-blank-wide-width, 10rem);
|
|
586
|
-
border-bottom-width: var(--mpb-blank-underline-wide-width, 6px);
|
|
587
|
-
min-height: 160px;
|
|
588
|
-
padding-bottom: 4px;
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
.layout-stimulus_image_blank {
|
|
592
|
-
display: grid;
|
|
593
|
-
grid-template-columns: minmax(var(--mpb-stimulus-min-column, 210px), 1fr) auto;
|
|
594
|
-
grid-template-areas:
|
|
595
|
-
'transcript transcript'
|
|
596
|
-
'sentence audio'
|
|
597
|
-
'. template'
|
|
598
|
-
'choices choices';
|
|
599
|
-
column-gap: var(--mpb-stimulus-grid-column-gap, 2rem);
|
|
600
|
-
row-gap: var(--mpb-stimulus-grid-row-gap, 0.7rem);
|
|
601
|
-
align-items: start;
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
.layout-stimulus_image_blank .pie-audio-transcript {
|
|
605
|
-
grid-area: transcript;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
.layout-stimulus_image_blank :global(.pie-audio-container) {
|
|
609
|
-
grid-area: audio;
|
|
610
|
-
margin: 0;
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
.layout-stimulus_image_blank .sentence-line {
|
|
614
|
-
grid-area: sentence;
|
|
615
|
-
margin: var(--mpb-stimulus-sentence-margin-top, 0.2rem) 0 0;
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
.layout-stimulus_image_blank .template-line {
|
|
619
|
-
grid-area: template;
|
|
620
|
-
margin: 0;
|
|
621
|
-
text-align: center;
|
|
622
|
-
justify-self: center;
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
.layout-stimulus_image_blank fieldset {
|
|
626
|
-
grid-area: choices;
|
|
627
|
-
margin-top: var(--mpb-stimulus-choices-margin-top, 0.9rem);
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
.layout-token_sequence {
|
|
631
|
-
display: grid;
|
|
632
|
-
grid-template-columns: 1fr auto;
|
|
633
|
-
grid-template-areas:
|
|
634
|
-
'transcript audio'
|
|
635
|
-
'template template'
|
|
636
|
-
'choices choices';
|
|
637
|
-
column-gap: var(--mpb-token-grid-column-gap, 1.5rem);
|
|
638
|
-
row-gap: var(--mpb-token-grid-row-gap, 0.8rem);
|
|
639
|
-
align-items: start;
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
.layout-token_sequence .pie-audio-transcript {
|
|
643
|
-
grid-area: transcript;
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
.layout-token_sequence :global(.pie-audio-container) {
|
|
647
|
-
grid-area: audio;
|
|
648
|
-
margin: 0;
|
|
649
|
-
justify-self: end;
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
.layout-token_sequence .template-line {
|
|
653
|
-
grid-area: template;
|
|
654
|
-
margin: var(--mpb-token-template-margin-top, 0.6rem) 0 0;
|
|
655
|
-
justify-self: center;
|
|
656
|
-
text-align: center;
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
.layout-token_sequence .template-line :global(span + span) {
|
|
660
|
-
margin-left: var(--mpb-token-inline-token-gap, 0.35rem);
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
.layout-token_sequence :global(.pie-blank-slot) {
|
|
664
|
-
width: var(--mpb-blank-standalone-width, 7rem);
|
|
665
|
-
border-bottom-width: var(--mpb-blank-underline-wide-width, 4px);
|
|
666
|
-
margin-left: var(--mpb-token-inline-token-gap, 0.35rem);
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
.layout-token_sequence fieldset {
|
|
670
|
-
grid-area: choices;
|
|
671
|
-
margin-top: var(--mpb-token-choices-margin-top, 0.2rem);
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
.layout-inline_sentence.has-inline-audio {
|
|
675
|
-
display: grid;
|
|
676
|
-
grid-template-columns: minmax(var(--mpb-text-min-column, 260px), 1fr) auto;
|
|
677
|
-
grid-template-areas:
|
|
678
|
-
'transcript transcript'
|
|
679
|
-
'template audio'
|
|
680
|
-
'choices choices';
|
|
681
|
-
column-gap: var(--mpb-inline-grid-column-gap, 1.5rem);
|
|
682
|
-
row-gap: var(--mpb-inline-grid-row-gap, 0.65rem);
|
|
683
|
-
align-items: start;
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
.layout-inline_sentence.has-inline-audio .pie-audio-transcript {
|
|
687
|
-
grid-area: transcript;
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
.layout-inline_sentence.has-inline-audio :global(.pie-audio-container) {
|
|
691
|
-
grid-area: audio;
|
|
692
|
-
margin: 0;
|
|
693
|
-
justify-self: end;
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
.layout-inline_sentence.has-inline-audio .template-line {
|
|
697
|
-
grid-area: template;
|
|
698
|
-
margin: var(--mpb-inline-template-margin-top, 0.45rem) 0 0;
|
|
699
|
-
text-align: center;
|
|
700
|
-
justify-self: center;
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
.layout-inline_sentence.has-inline-audio fieldset {
|
|
704
|
-
grid-area: choices;
|
|
705
|
-
margin-top: var(--mpb-inline-choices-margin-top, 0.25rem);
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
/* Match Learnosity responsive behavior for CQT audio-blank layouts:
|
|
709
|
-
at smaller widths, audio control shifts left and answer tiles stack. */
|
|
710
|
-
@media (max-width: 760px) {
|
|
711
|
-
.layout-audio_blank_only :global(.pie-audio-container),
|
|
712
|
-
.layout-stimulus_image_blank :global(.pie-audio-container),
|
|
713
|
-
.layout-token_sequence :global(.pie-audio-container),
|
|
714
|
-
.layout-inline_sentence.has-inline-audio :global(.pie-audio-container) {
|
|
715
|
-
width: 100%;
|
|
716
|
-
margin-left: 0;
|
|
717
|
-
margin-right: 0;
|
|
718
|
-
justify-content: flex-start;
|
|
719
|
-
justify-self: start;
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
.layout-audio_blank_only .pie-choices,
|
|
723
|
-
.layout-token_sequence .pie-choices,
|
|
724
|
-
.layout-stimulus_image_blank .pie-choices,
|
|
725
|
-
.layout-inline_sentence.has-inline-audio .pie-choices {
|
|
726
|
-
flex-direction: column;
|
|
727
|
-
align-items: flex-start;
|
|
728
|
-
justify-content: flex-start;
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
.layout-audio_blank_only .choice-row-horizontal,
|
|
732
|
-
.layout-token_sequence .choice-row-horizontal,
|
|
733
|
-
.layout-stimulus_image_blank .choice-row-horizontal,
|
|
734
|
-
.layout-inline_sentence.has-inline-audio .choice-row-horizontal {
|
|
735
|
-
width: min(100%, var(--mpb-narrow-choice-max-width, 230px));
|
|
736
|
-
align-items: flex-start;
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
</style>
|