@pie-element/mc-populated-blank 0.1.0 → 0.2.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 +110 -2
- package/dist/controller/defaults.d.ts +85 -0
- package/dist/controller/defaults.d.ts.map +1 -1
- package/dist/controller/index.d.ts.map +1 -1
- package/dist/controller/index.js +109 -4
- package/dist/controller/index.js.map +1 -1
- package/dist/delivery/index.js +334 -398
- package/dist/delivery/index.js.map +1 -1
- package/dist/index.iife.js +10 -18
- package/dist/index.js +334 -398
- package/dist/index.js.map +1 -1
- package/package.json +1 -4
- package/src/controller/defaults.ts +51 -0
- package/src/controller/index.ts +84 -5
- package/src/delivery/McPopulatedBlank.svelte +317 -425
|
@@ -1,79 +1,102 @@
|
|
|
1
1
|
<svelte:options
|
|
2
2
|
customElement={{
|
|
3
|
+
tag: 'mc-populated-blank-element',
|
|
3
4
|
shadow: 'none',
|
|
4
5
|
props: {
|
|
5
6
|
model: { type: 'Object' },
|
|
6
7
|
session: { type: 'Object' },
|
|
7
8
|
options: { type: 'Object' },
|
|
8
|
-
onSessionChange: { type: 'Object' },
|
|
9
|
-
onAudioStarted: { type: 'Object' },
|
|
10
|
-
onAudioEnded: { type: 'Object' },
|
|
11
9
|
},
|
|
12
10
|
}}
|
|
13
11
|
/>
|
|
14
12
|
|
|
15
13
|
<script lang="ts">
|
|
16
|
-
import { onDestroy } from 'svelte';
|
|
17
|
-
|
|
18
14
|
/** Must match controller `BLANK_TOKEN` (kept local to avoid pulling controller into delivery). */
|
|
19
15
|
const BLANK_TOKEN = '{{blank}}';
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
16
|
+
const DEFAULT_LAYOUT_LIMITS = {
|
|
17
|
+
blankStandaloneWidthRem: 7,
|
|
18
|
+
blankWideWidthRem: 10,
|
|
19
|
+
blankUnderlineWidthPx: 2,
|
|
20
|
+
blankUnderlineWideWidthPx: 4,
|
|
21
|
+
horizontalChoiceWidthPx: 170,
|
|
22
|
+
horizontalChoiceWidthVw: 30,
|
|
23
|
+
horizontalChoiceTileMinHeightRem: 11,
|
|
24
|
+
horizontalChoiceContentMinHeightRem: 7.5,
|
|
25
|
+
selectedImageMaxHeightRem: 4,
|
|
26
|
+
choiceImageMaxHeightRem: 5,
|
|
27
|
+
listenButtonSizePx: 128,
|
|
28
|
+
stimulusMinColumnPx: 210,
|
|
29
|
+
textMinColumnPx: 260,
|
|
30
|
+
legendMaxChars: 120,
|
|
31
|
+
choiceGroupGapRem: 0.5,
|
|
32
|
+
choiceRowGapRem: 0.5,
|
|
33
|
+
toggleButtonGapRem: 0.5,
|
|
34
|
+
horizontalChoiceRadioTopMarginRem: 0.5,
|
|
35
|
+
audioBlankTemplateMarginTopRem: 0.8,
|
|
36
|
+
audioBlankTemplateMarginBottomRem: 1.8,
|
|
37
|
+
stimulusGridColumnGapRem: 2,
|
|
38
|
+
stimulusGridRowGapRem: 0.7,
|
|
39
|
+
stimulusSentenceMarginTopRem: 0.2,
|
|
40
|
+
stimulusChoicesMarginTopRem: 0.9,
|
|
41
|
+
tokenGridColumnGapRem: 1.5,
|
|
42
|
+
tokenGridRowGapRem: 0.8,
|
|
43
|
+
tokenTemplateMarginTopRem: 0.6,
|
|
44
|
+
tokenInlineTokenGapRem: 0.35,
|
|
45
|
+
tokenChoicesMarginTopRem: 0.2,
|
|
46
|
+
inlineGridColumnGapRem: 1.5,
|
|
47
|
+
inlineGridRowGapRem: 0.65,
|
|
48
|
+
inlineTemplateMarginTopRem: 0.45,
|
|
49
|
+
inlineChoicesMarginTopRem: 0.25,
|
|
50
|
+
} as const;
|
|
51
|
+
const DEFAULT_LAYOUT_PROFILE_PRESETS: Record<string, Record<string, number>> = {
|
|
52
|
+
audio_blank_only: {
|
|
53
|
+
blankWideWidthRem: 10,
|
|
54
|
+
blankUnderlineWideWidthPx: 4,
|
|
55
|
+
},
|
|
56
|
+
stimulus_image_blank: {
|
|
57
|
+
blankWideWidthRem: 10,
|
|
58
|
+
blankUnderlineWideWidthPx: 4,
|
|
59
|
+
},
|
|
60
|
+
token_sequence: {
|
|
61
|
+
blankStandaloneWidthRem: 7,
|
|
62
|
+
blankUnderlineWideWidthPx: 4,
|
|
63
|
+
},
|
|
57
64
|
};
|
|
65
|
+
const DEFAULT_AUDIO_BUTTON_SKINS = {
|
|
66
|
+
default: {
|
|
67
|
+
silentUrl:
|
|
68
|
+
'https://assets.learnosity.com/organisations/844/0c9f2aa3-3cd5-4de7-93ef-541c24ca35da.svg',
|
|
69
|
+
playingUrl:
|
|
70
|
+
'https://assets.learnosity.com/organisations/844/231dfdc2-c113-4be5-91fb-e75a0ca5994b.svg',
|
|
71
|
+
},
|
|
72
|
+
es: {
|
|
73
|
+
silentUrl:
|
|
74
|
+
'https://assets.learnosity.com/organisations/844/27a9d5b5-d873-4bd5-b9ba-22748782d8ba.svg',
|
|
75
|
+
playingUrl:
|
|
76
|
+
'https://assets.learnosity.com/organisations/844/120f216d-96b7-4560-94b8-1d90710216b7.svg',
|
|
77
|
+
},
|
|
78
|
+
} as const;
|
|
79
|
+
const DEFAULT_UI_TEXT = {
|
|
80
|
+
answerChoices: 'Answer choices',
|
|
81
|
+
selectedAnswerInSentence: 'Selected answer in sentence',
|
|
82
|
+
showCorrectAnswer: 'Show correct answer',
|
|
83
|
+
hideCorrectAnswer: 'Hide correct answer',
|
|
84
|
+
clickToEnableAutoplay: 'Click to enable audio autoplay',
|
|
85
|
+
audioResourceUnavailable: 'Audio is enabled but no playable audio URL is configured.',
|
|
86
|
+
transcriptLabel: 'Transcript',
|
|
87
|
+
listenLabelEn: 'Listen',
|
|
88
|
+
listenLabelEs: 'Escuchar',
|
|
89
|
+
} as const;
|
|
58
90
|
|
|
59
|
-
let props = $props<{
|
|
60
|
-
model?: McModel;
|
|
61
|
-
session?: McSession;
|
|
62
|
-
options?: Record<string, unknown>;
|
|
63
|
-
onSessionChange?: (session: McSession) => void;
|
|
64
|
-
onAudioStarted?: () => void;
|
|
65
|
-
onAudioEnded?: () => void;
|
|
66
|
-
}>();
|
|
91
|
+
let props = $props<{ model?: any; session?: any; options?: any }>();
|
|
67
92
|
let audioEl = $state<HTMLAudioElement | null>(null);
|
|
68
|
-
let activeUtterance = $state<SpeechSynthesisUtterance | null>(null);
|
|
69
|
-
let isSynthSpeaking = $state(false);
|
|
70
93
|
let isMediaPlaying = $state(false);
|
|
71
94
|
let localChoiceId = $state('');
|
|
72
95
|
let autoPlayPromptOpen = $state(false);
|
|
73
96
|
let autoplayAttempted = $state(false);
|
|
74
97
|
const instanceId = `mc-populated-blank-${Math.random().toString(36).slice(2, 10)}`;
|
|
75
98
|
|
|
76
|
-
const isEvaluateMode = $derived(props?.model?.mode === 'evaluate');
|
|
99
|
+
const isEvaluateMode = $derived(props?.model?.env?.mode === 'evaluate');
|
|
77
100
|
const correctness = $derived(props?.model?.correctness);
|
|
78
101
|
const isCorrect = $derived(correctness === 'correct');
|
|
79
102
|
const isIncorrect = $derived(correctness === 'incorrect');
|
|
@@ -92,17 +115,83 @@ const choiceLayout = $derived(
|
|
|
92
115
|
props?.model?.choiceLayout || (isAudioOnlyMode || isBlankOnlyTemplate ? 'horizontal' : 'vertical')
|
|
93
116
|
);
|
|
94
117
|
const isHorizontalChoices = $derived(choiceLayout === 'horizontal');
|
|
95
|
-
const isVicFamilyLayout = $derived(layoutProfile === 'inline_sentence' && !isHorizontalChoices);
|
|
96
|
-
const isSelVicFamilyLayout = $derived(isVicFamilyLayout && !!props?.model?.hasAudio);
|
|
97
118
|
const showVisibleTranscript = $derived(!!props?.model?.showVisibleTranscript);
|
|
98
119
|
const hasPlayableAudio = $derived(!!props?.model?.hasAudio && !!props?.model?.audioUrl);
|
|
99
|
-
const
|
|
100
|
-
!!props?.model?.hasAudio && !props?.model?.audioUrl && !!props?.model?.audioTranscript
|
|
101
|
-
);
|
|
120
|
+
const hasAudioButMissingResource = $derived(!!props?.model?.hasAudio && !props?.model?.audioUrl);
|
|
102
121
|
const hasInlineSentenceAudioLayout = $derived(
|
|
103
122
|
layoutProfile === 'inline_sentence' && !!props?.model?.hasAudio
|
|
104
123
|
);
|
|
124
|
+
const uiText = $derived.by(() => ({
|
|
125
|
+
...DEFAULT_UI_TEXT,
|
|
126
|
+
...(props?.model?.uiText || {}),
|
|
127
|
+
}));
|
|
128
|
+
const profilePresetLimits = $derived.by(() => {
|
|
129
|
+
const profile = String(layoutProfile || '');
|
|
130
|
+
const defaults = DEFAULT_LAYOUT_PROFILE_PRESETS[profile] || {};
|
|
131
|
+
const customPresets =
|
|
132
|
+
props?.model?.layoutProfilePresets && typeof props.model.layoutProfilePresets === 'object'
|
|
133
|
+
? props.model.layoutProfilePresets
|
|
134
|
+
: {};
|
|
135
|
+
const custom =
|
|
136
|
+
customPresets[profile] && typeof customPresets[profile] === 'object'
|
|
137
|
+
? customPresets[profile]
|
|
138
|
+
: {};
|
|
139
|
+
return {
|
|
140
|
+
...defaults,
|
|
141
|
+
...custom,
|
|
142
|
+
};
|
|
143
|
+
});
|
|
144
|
+
const layoutLimits = $derived.by(() => {
|
|
145
|
+
const configured =
|
|
146
|
+
props?.model?.layoutLimits && typeof props.model.layoutLimits === 'object'
|
|
147
|
+
? props.model.layoutLimits
|
|
148
|
+
: {};
|
|
149
|
+
return {
|
|
150
|
+
...DEFAULT_LAYOUT_LIMITS,
|
|
151
|
+
...profilePresetLimits,
|
|
152
|
+
...configured,
|
|
153
|
+
};
|
|
154
|
+
});
|
|
155
|
+
const rootStyle = $derived.by(() =>
|
|
156
|
+
[
|
|
157
|
+
`--mpb-listen-button-size:${layoutLimits.listenButtonSizePx}px`,
|
|
158
|
+
`--mpb-blank-standalone-width:${layoutLimits.blankStandaloneWidthRem}rem`,
|
|
159
|
+
`--mpb-blank-wide-width:${layoutLimits.blankWideWidthRem}rem`,
|
|
160
|
+
`--mpb-blank-underline-width:${layoutLimits.blankUnderlineWidthPx}px`,
|
|
161
|
+
`--mpb-blank-underline-wide-width:${layoutLimits.blankUnderlineWideWidthPx}px`,
|
|
162
|
+
`--mpb-selected-image-max-height:${layoutLimits.selectedImageMaxHeightRem}rem`,
|
|
163
|
+
`--mpb-choice-image-max-height:${layoutLimits.choiceImageMaxHeightRem}rem`,
|
|
164
|
+
`--mpb-choice-width-px:${layoutLimits.horizontalChoiceWidthPx}px`,
|
|
165
|
+
`--mpb-choice-width-vw:${layoutLimits.horizontalChoiceWidthVw}vw`,
|
|
166
|
+
`--mpb-choice-tile-min-height:${layoutLimits.horizontalChoiceTileMinHeightRem}rem`,
|
|
167
|
+
`--mpb-choice-content-min-height:${layoutLimits.horizontalChoiceContentMinHeightRem}rem`,
|
|
168
|
+
`--mpb-stimulus-min-column:${layoutLimits.stimulusMinColumnPx}px`,
|
|
169
|
+
`--mpb-text-min-column:${layoutLimits.textMinColumnPx}px`,
|
|
170
|
+
`--mpb-choice-group-gap:${layoutLimits.choiceGroupGapRem}rem`,
|
|
171
|
+
`--mpb-choice-row-gap:${layoutLimits.choiceRowGapRem}rem`,
|
|
172
|
+
`--mpb-toggle-button-gap:${layoutLimits.toggleButtonGapRem}rem`,
|
|
173
|
+
`--mpb-horizontal-choice-radio-top-margin:${layoutLimits.horizontalChoiceRadioTopMarginRem}rem`,
|
|
174
|
+
`--mpb-audio-blank-template-margin-top:${layoutLimits.audioBlankTemplateMarginTopRem}rem`,
|
|
175
|
+
`--mpb-audio-blank-template-margin-bottom:${layoutLimits.audioBlankTemplateMarginBottomRem}rem`,
|
|
176
|
+
`--mpb-stimulus-grid-column-gap:${layoutLimits.stimulusGridColumnGapRem}rem`,
|
|
177
|
+
`--mpb-stimulus-grid-row-gap:${layoutLimits.stimulusGridRowGapRem}rem`,
|
|
178
|
+
`--mpb-stimulus-sentence-margin-top:${layoutLimits.stimulusSentenceMarginTopRem}rem`,
|
|
179
|
+
`--mpb-stimulus-choices-margin-top:${layoutLimits.stimulusChoicesMarginTopRem}rem`,
|
|
180
|
+
`--mpb-token-grid-column-gap:${layoutLimits.tokenGridColumnGapRem}rem`,
|
|
181
|
+
`--mpb-token-grid-row-gap:${layoutLimits.tokenGridRowGapRem}rem`,
|
|
182
|
+
`--mpb-token-template-margin-top:${layoutLimits.tokenTemplateMarginTopRem}rem`,
|
|
183
|
+
`--mpb-token-inline-token-gap:${layoutLimits.tokenInlineTokenGapRem}rem`,
|
|
184
|
+
`--mpb-token-choices-margin-top:${layoutLimits.tokenChoicesMarginTopRem}rem`,
|
|
185
|
+
`--mpb-inline-grid-column-gap:${layoutLimits.inlineGridColumnGapRem}rem`,
|
|
186
|
+
`--mpb-inline-grid-row-gap:${layoutLimits.inlineGridRowGapRem}rem`,
|
|
187
|
+
`--mpb-inline-template-margin-top:${layoutLimits.inlineTemplateMarginTopRem}rem`,
|
|
188
|
+
`--mpb-inline-choices-margin-top:${layoutLimits.inlineChoicesMarginTopRem}rem`,
|
|
189
|
+
].join(';')
|
|
190
|
+
);
|
|
105
191
|
const useFeatureButtonAudio = $derived.by(() => {
|
|
192
|
+
if (typeof props?.model?.useFeatureButtonAudio === 'boolean') {
|
|
193
|
+
return !!props.model.useFeatureButtonAudio;
|
|
194
|
+
}
|
|
106
195
|
const profile = layoutProfile || '';
|
|
107
196
|
return (
|
|
108
197
|
!!props?.model?.hasAudio &&
|
|
@@ -123,73 +212,23 @@ const templateParts = $derived.by(() => {
|
|
|
123
212
|
after: t.slice(idx + BLANK_TOKEN.length),
|
|
124
213
|
};
|
|
125
214
|
});
|
|
126
|
-
|
|
127
|
-
function plainTextFromHtml(value = ''): string {
|
|
128
|
-
return value
|
|
129
|
-
.replace(/<[^>]+>/g, ' ')
|
|
130
|
-
.replace(/ /g, ' ')
|
|
131
|
-
.replace(/\s+/g, ' ')
|
|
132
|
-
.trim();
|
|
133
|
-
}
|
|
134
|
-
|
|
135
215
|
const blankWidth = $derived.by(() => {
|
|
136
216
|
if (layoutProfile === 'audio_blank_only' || layoutProfile === 'stimulus_image_blank') {
|
|
137
|
-
return
|
|
217
|
+
return `${layoutLimits.blankWideWidthRem}rem`;
|
|
138
218
|
}
|
|
139
219
|
if (isBlankOnlyTemplate) {
|
|
140
|
-
return
|
|
141
|
-
}
|
|
142
|
-
if (choiceMode === 'text') {
|
|
143
|
-
const modelChoices = Array.isArray(props?.model?.choices) ? props.model.choices : [];
|
|
144
|
-
const longestChoiceChars = modelChoices.reduce((max: number, choice: McChoice) => {
|
|
145
|
-
const textLength = plainTextFromHtml(choice?.labelHtml || '').length;
|
|
146
|
-
return Math.max(max, textLength);
|
|
147
|
-
}, 0);
|
|
148
|
-
if (longestChoiceChars > 0) {
|
|
149
|
-
const widthInCh = Math.min(24, Math.max(7, longestChoiceChars + 1));
|
|
150
|
-
return `${widthInCh}ch`;
|
|
151
|
-
}
|
|
220
|
+
return `${layoutLimits.blankStandaloneWidthRem}rem`;
|
|
152
221
|
}
|
|
153
222
|
return 'auto';
|
|
154
223
|
});
|
|
155
224
|
const blankBorderWidth = $derived.by(() => {
|
|
156
225
|
if (layoutProfile === 'audio_blank_only' || layoutProfile === 'stimulus_image_blank') {
|
|
157
|
-
return
|
|
226
|
+
return `${layoutLimits.blankUnderlineWideWidthPx}px`;
|
|
158
227
|
}
|
|
159
|
-
return
|
|
228
|
+
return `${layoutLimits.blankUnderlineWidthPx}px`;
|
|
160
229
|
});
|
|
161
230
|
|
|
162
231
|
const choices = $derived(Array.isArray(props?.model?.choices) ? props.model.choices : []);
|
|
163
|
-
const shouldShuffleChoices = $derived.by(() => {
|
|
164
|
-
const modelShuffle = !!props?.model?.shuffle;
|
|
165
|
-
const optionsShuffle = !!props?.options?.shuffle;
|
|
166
|
-
const lockChoiceOrder = !!props?.model?.lockChoiceOrder || !!props?.options?.lockChoiceOrder;
|
|
167
|
-
return (modelShuffle || optionsShuffle) && !lockChoiceOrder;
|
|
168
|
-
});
|
|
169
|
-
const shuffleSeed = $derived.by(
|
|
170
|
-
() =>
|
|
171
|
-
`${props?.session?.id || props?.model?.id || instanceId}:${choices.map((c: McChoice) => c.id).join('|')}`
|
|
172
|
-
);
|
|
173
|
-
function hashString(value: string): number {
|
|
174
|
-
let hash = 2166136261;
|
|
175
|
-
for (let i = 0; i < value.length; i++) {
|
|
176
|
-
hash ^= value.charCodeAt(i);
|
|
177
|
-
hash = Math.imul(hash, 16777619);
|
|
178
|
-
}
|
|
179
|
-
return hash >>> 0;
|
|
180
|
-
}
|
|
181
|
-
function deterministicShuffle<T extends { id?: string }>(items: T[], seed: string): T[] {
|
|
182
|
-
return [...items]
|
|
183
|
-
.map((item, index) => ({
|
|
184
|
-
item,
|
|
185
|
-
key: hashString(`${seed}:${item?.id || index}:${index}`),
|
|
186
|
-
}))
|
|
187
|
-
.sort((a, b) => a.key - b.key)
|
|
188
|
-
.map((entry) => entry.item);
|
|
189
|
-
}
|
|
190
|
-
const renderedChoices = $derived.by(() =>
|
|
191
|
-
shouldShuffleChoices ? deterministicShuffle(choices, shuffleSeed) : choices
|
|
192
|
-
);
|
|
193
232
|
const choiceMode = $derived(props?.model?.choiceMode || 'text');
|
|
194
233
|
const selectedId = $derived(props?.session?.choiceId || localChoiceId || '');
|
|
195
234
|
const radioGroupName = $derived(`${instanceId}-choice-group-${props?.model?.id || '1'}`);
|
|
@@ -203,13 +242,20 @@ const displayChoiceId = $derived.by(() => {
|
|
|
203
242
|
return selectedId;
|
|
204
243
|
});
|
|
205
244
|
|
|
206
|
-
const displayChoice = $derived.by(() =>
|
|
207
|
-
renderedChoices.find((c: McChoice) => c.id === displayChoiceId)
|
|
208
|
-
);
|
|
245
|
+
const displayChoice = $derived.by(() => choices.find((c: any) => c.id === displayChoiceId));
|
|
209
246
|
|
|
210
247
|
const legendText = $derived.by(() => {
|
|
211
|
-
const plain =
|
|
212
|
-
|
|
248
|
+
const plain = (props?.model?.prompt || '')
|
|
249
|
+
.replace(/<[^>]+>/g, ' ')
|
|
250
|
+
.replace(/\s+/g, ' ')
|
|
251
|
+
.trim();
|
|
252
|
+
const maxChars = Math.max(
|
|
253
|
+
8,
|
|
254
|
+
Number(layoutLimits.legendMaxChars) || DEFAULT_LAYOUT_LIMITS.legendMaxChars
|
|
255
|
+
);
|
|
256
|
+
return plain.length > maxChars
|
|
257
|
+
? `${plain.slice(0, Math.max(1, maxChars - 1))}…`
|
|
258
|
+
: plain || uiText.answerChoices;
|
|
213
259
|
});
|
|
214
260
|
|
|
215
261
|
const promptId = $derived(`${instanceId}-prompt`);
|
|
@@ -232,47 +278,32 @@ const lang = $derived.by(() => {
|
|
|
232
278
|
const locale = props?.model?.locale || '';
|
|
233
279
|
return locale ? locale.slice(0, 2) : 'en';
|
|
234
280
|
});
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
answerChoices: 'Answer choices',
|
|
239
|
-
correctSelected: 'Correct answer selected',
|
|
240
|
-
incorrectSelected: 'Incorrect answer selected',
|
|
241
|
-
selectedAnswerInSentence: 'Selected answer in sentence',
|
|
242
|
-
selectedAnswerImage: 'Selected answer image',
|
|
243
|
-
showCorrect: 'Show correct answer',
|
|
244
|
-
hideCorrect: 'Hide correct answer',
|
|
245
|
-
enableAutoplay: 'Click to enable audio autoplay',
|
|
246
|
-
listen: 'Listen',
|
|
247
|
-
teacherInstructions: 'Teacher instructions',
|
|
248
|
-
},
|
|
249
|
-
es: {
|
|
250
|
-
answerChoices: 'Opciones de respuesta',
|
|
251
|
-
correctSelected: 'Respuesta correcta seleccionada',
|
|
252
|
-
incorrectSelected: 'Respuesta incorrecta seleccionada',
|
|
253
|
-
selectedAnswerInSentence: 'Respuesta seleccionada en la oración',
|
|
254
|
-
selectedAnswerImage: 'Imagen de la respuesta seleccionada',
|
|
255
|
-
showCorrect: 'Mostrar respuesta correcta',
|
|
256
|
-
hideCorrect: 'Ocultar respuesta correcta',
|
|
257
|
-
enableAutoplay: 'Haga clic para habilitar la reproducción automática de audio',
|
|
258
|
-
listen: 'Escuchar',
|
|
259
|
-
teacherInstructions: 'Instrucciones para el docente',
|
|
260
|
-
},
|
|
261
|
-
} as const;
|
|
262
|
-
type UiKey = keyof (typeof uiStrings)['en'];
|
|
263
|
-
function t(key: UiKey): string {
|
|
264
|
-
return (lang === 'es' ? uiStrings.es[key] : uiStrings.en[key]) || uiStrings.en[key];
|
|
265
|
-
}
|
|
281
|
+
const audioErrorMessage = $derived.by(() =>
|
|
282
|
+
hasAudioButMissingResource ? uiText.audioResourceUnavailable : ''
|
|
283
|
+
);
|
|
266
284
|
|
|
267
285
|
const resultText = $derived.by(() => {
|
|
268
286
|
if (!isEvaluateMode || showCorrectAnswer) return '';
|
|
269
|
-
if (isCorrect) return
|
|
270
|
-
if (isIncorrect && selectedId) return
|
|
287
|
+
if (isCorrect) return 'Correct answer selected';
|
|
288
|
+
if (isIncorrect && selectedId) return 'Incorrect answer selected';
|
|
271
289
|
return '';
|
|
272
290
|
});
|
|
273
291
|
|
|
274
|
-
function emitSession(updatedSession:
|
|
275
|
-
|
|
292
|
+
function emitSession(updatedSession: any, sourceEl?: HTMLElement | null) {
|
|
293
|
+
const host =
|
|
294
|
+
(sourceEl?.closest?.('mc-populated-blank-element') as any) ||
|
|
295
|
+
(document.querySelector('mc-populated-blank-element') as any);
|
|
296
|
+
if (host?.onSessionChange) {
|
|
297
|
+
host.onSessionChange(updatedSession);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
host?.dispatchEvent(
|
|
301
|
+
new CustomEvent('session-changed', {
|
|
302
|
+
bubbles: true,
|
|
303
|
+
composed: true,
|
|
304
|
+
detail: { complete: !!updatedSession?.choiceId, component: 'mc-populated-blank' },
|
|
305
|
+
})
|
|
306
|
+
);
|
|
276
307
|
}
|
|
277
308
|
|
|
278
309
|
function onRadioChange(e: Event) {
|
|
@@ -286,7 +317,7 @@ function onRadioChange(e: Event) {
|
|
|
286
317
|
element: 'mc-populated-blank',
|
|
287
318
|
choiceId,
|
|
288
319
|
};
|
|
289
|
-
emitSession(updatedSession);
|
|
320
|
+
emitSession(updatedSession, input);
|
|
290
321
|
}
|
|
291
322
|
|
|
292
323
|
function toggleCorrectAnswer() {
|
|
@@ -320,45 +351,42 @@ function handleEnableAutoplayClick() {
|
|
|
320
351
|
audioEl.play().finally(() => {
|
|
321
352
|
autoPlayPromptOpen = false;
|
|
322
353
|
});
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
if (hasTranscriptOnlyAudio) {
|
|
326
|
-
speakTranscript();
|
|
327
|
-
autoPlayPromptOpen = false;
|
|
328
354
|
}
|
|
329
355
|
}
|
|
330
356
|
|
|
331
|
-
function onAudioPlaying() {
|
|
357
|
+
function onAudioPlaying(e: Event) {
|
|
358
|
+
const host = (e.currentTarget as HTMLElement)?.closest('mc-populated-blank-element') as any;
|
|
332
359
|
isMediaPlaying = true;
|
|
333
|
-
|
|
360
|
+
host?.onAudioStarted?.();
|
|
334
361
|
autoPlayPromptOpen = false;
|
|
335
362
|
}
|
|
336
363
|
|
|
337
|
-
function onAudioEnded() {
|
|
364
|
+
function onAudioEnded(e: Event) {
|
|
365
|
+
const host = (e.currentTarget as HTMLElement)?.closest('mc-populated-blank-element') as any;
|
|
338
366
|
isMediaPlaying = false;
|
|
339
|
-
|
|
367
|
+
host?.onAudioEnded?.();
|
|
340
368
|
}
|
|
341
369
|
|
|
342
|
-
function speechButtonLabel() {
|
|
343
|
-
return
|
|
370
|
+
function speechButtonLabel(locale = '') {
|
|
371
|
+
return locale.toLowerCase().startsWith('es') ? uiText.listenLabelEs : uiText.listenLabelEn;
|
|
344
372
|
}
|
|
345
373
|
|
|
346
374
|
const featureAudioSkin = $derived.by(() => {
|
|
347
375
|
const locale = String(props?.model?.locale || '').toLowerCase();
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
376
|
+
const lang = locale.slice(0, 2);
|
|
377
|
+
const byLocale =
|
|
378
|
+
props?.model?.audioButtonSkinsByLocale &&
|
|
379
|
+
typeof props.model.audioButtonSkinsByLocale === 'object'
|
|
380
|
+
? props.model.audioButtonSkinsByLocale
|
|
381
|
+
: {};
|
|
382
|
+
const customSingle =
|
|
383
|
+
props?.model?.audioButtonSkin && typeof props.model.audioButtonSkin === 'object'
|
|
384
|
+
? props.model.audioButtonSkin
|
|
385
|
+
: null;
|
|
386
|
+
const defaultSkin = locale.startsWith('es')
|
|
387
|
+
? DEFAULT_AUDIO_BUTTON_SKINS.es
|
|
388
|
+
: DEFAULT_AUDIO_BUTTON_SKINS.default;
|
|
389
|
+
return byLocale[locale] || byLocale[lang] || byLocale.default || customSingle || defaultSkin;
|
|
362
390
|
});
|
|
363
391
|
|
|
364
392
|
function playFeatureAudio() {
|
|
@@ -366,45 +394,9 @@ function playFeatureAudio() {
|
|
|
366
394
|
audioEl.play().catch(() => {
|
|
367
395
|
// Keep behavior resilient: if playback is blocked, user can retry.
|
|
368
396
|
});
|
|
369
|
-
return;
|
|
370
397
|
}
|
|
371
|
-
speakTranscript();
|
|
372
398
|
}
|
|
373
399
|
|
|
374
|
-
function speakTranscript() {
|
|
375
|
-
const transcript = (props?.model?.audioTranscript || '').trim();
|
|
376
|
-
if (!transcript || !window?.speechSynthesis) return;
|
|
377
|
-
|
|
378
|
-
window.speechSynthesis.cancel();
|
|
379
|
-
const utterance = new SpeechSynthesisUtterance(transcript);
|
|
380
|
-
utterance.lang = props?.model?.locale || 'en-US';
|
|
381
|
-
|
|
382
|
-
utterance.onstart = () => {
|
|
383
|
-
isSynthSpeaking = true;
|
|
384
|
-
props?.onAudioStarted?.();
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
utterance.onend = () => {
|
|
388
|
-
isSynthSpeaking = false;
|
|
389
|
-
activeUtterance = null;
|
|
390
|
-
props?.onAudioEnded?.();
|
|
391
|
-
};
|
|
392
|
-
|
|
393
|
-
utterance.onerror = () => {
|
|
394
|
-
isSynthSpeaking = false;
|
|
395
|
-
activeUtterance = null;
|
|
396
|
-
};
|
|
397
|
-
|
|
398
|
-
activeUtterance = utterance;
|
|
399
|
-
window.speechSynthesis.speak(utterance);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
onDestroy(() => {
|
|
403
|
-
if (activeUtterance && window?.speechSynthesis) {
|
|
404
|
-
window.speechSynthesis.cancel();
|
|
405
|
-
}
|
|
406
|
-
});
|
|
407
|
-
|
|
408
400
|
$effect(() => {
|
|
409
401
|
if (props?.session?.choiceId) {
|
|
410
402
|
localChoiceId = props.session.choiceId;
|
|
@@ -421,50 +413,46 @@ $effect(() => {
|
|
|
421
413
|
audioEl.play().catch(() => {
|
|
422
414
|
autoPlayPromptOpen = true;
|
|
423
415
|
});
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
if (props?.model?.audioTranscript) {
|
|
428
|
-
autoPlayPromptOpen = true;
|
|
429
416
|
}
|
|
430
417
|
});
|
|
431
418
|
</script>
|
|
432
419
|
|
|
433
420
|
<div
|
|
434
|
-
class={`p-4 mc-populated-blank-root layout-${layoutProfile} ${hasInlineSentenceAudioLayout ? 'has-inline-audio' : ''}
|
|
421
|
+
class={`p-4 mc-populated-blank-root pie-element pie-element-mc-populated-blank pie-delivery-root layout-${layoutProfile} ${hasInlineSentenceAudioLayout ? 'has-inline-audio' : ''}`}
|
|
435
422
|
lang={lang}
|
|
423
|
+
style={rootStyle}
|
|
436
424
|
>
|
|
437
425
|
{#if props?.model?.prompt}
|
|
438
|
-
<div class="mb-4 prose" id={promptId}>{@html props.model.prompt}</div>
|
|
426
|
+
<div class="mb-4 prose pie-prompt" id={promptId}>{@html props.model.prompt}</div>
|
|
439
427
|
{/if}
|
|
440
428
|
|
|
441
429
|
{#if props?.model?.hasAudio}
|
|
442
|
-
<div class="mb-4 audio-container">
|
|
430
|
+
<div class="mb-4 audio-container pie-audio-container">
|
|
443
431
|
{#if hasPlayableAudio && useFeatureButtonAudio}
|
|
444
432
|
<audio
|
|
445
433
|
bind:this={audioEl}
|
|
446
|
-
class="sr-only"
|
|
434
|
+
class="sr-only pie-audio-player"
|
|
447
435
|
preload="metadata"
|
|
448
436
|
src={props.model.audioUrl}
|
|
449
437
|
aria-hidden="true"
|
|
450
438
|
tabindex="-1"
|
|
451
|
-
|
|
452
|
-
|
|
439
|
+
on:playing={onAudioPlaying}
|
|
440
|
+
on:ended={onAudioEnded}
|
|
453
441
|
></audio>
|
|
454
442
|
<button
|
|
455
|
-
class="listen-button rli-feature-audio"
|
|
443
|
+
class="listen-button pie-listen-button rli-feature-audio"
|
|
456
444
|
type="button"
|
|
457
|
-
aria-label={speechButtonLabel()}
|
|
458
|
-
|
|
445
|
+
aria-label={speechButtonLabel(props?.model?.locale)}
|
|
446
|
+
on:click={playFeatureAudio}
|
|
459
447
|
>
|
|
460
448
|
<img
|
|
461
|
-
class={`listen-feature-icon rli-feature-listen ${isMediaPlaying
|
|
449
|
+
class={`listen-feature-icon pie-listen-icon rli-feature-listen ${isMediaPlaying ? '' : 'listen-active'}`}
|
|
462
450
|
src={featureAudioSkin.silentUrl}
|
|
463
451
|
alt=""
|
|
464
452
|
aria-hidden="true"
|
|
465
453
|
/>
|
|
466
454
|
<img
|
|
467
|
-
class={`listen-feature-icon rli-feature-listen ${isMediaPlaying
|
|
455
|
+
class={`listen-feature-icon pie-listen-icon rli-feature-listen ${isMediaPlaying ? 'listen-active' : ''}`}
|
|
468
456
|
src={featureAudioSkin.playingUrl}
|
|
469
457
|
alt=""
|
|
470
458
|
aria-hidden="true"
|
|
@@ -474,84 +462,64 @@ $effect(() => {
|
|
|
474
462
|
<audio
|
|
475
463
|
bind:this={audioEl}
|
|
476
464
|
controls
|
|
477
|
-
class="w-full max-w-md"
|
|
465
|
+
class="w-full max-w-md pie-audio-player"
|
|
478
466
|
preload="metadata"
|
|
479
467
|
src={props.model.audioUrl}
|
|
480
468
|
aria-describedby={props?.model?.audioTranscript ? transcriptId : undefined}
|
|
481
|
-
|
|
482
|
-
|
|
469
|
+
on:playing={onAudioPlaying}
|
|
470
|
+
on:ended={onAudioEnded}
|
|
483
471
|
>
|
|
484
472
|
<track kind="captions" />
|
|
485
473
|
</audio>
|
|
486
474
|
{#if autoPlayPromptOpen}
|
|
487
|
-
<button
|
|
488
|
-
|
|
475
|
+
<button
|
|
476
|
+
class="mt-2 text-sm underline pie-audio-autoplay-enable"
|
|
477
|
+
type="button"
|
|
478
|
+
on:click={handleEnableAutoplayClick}
|
|
479
|
+
>
|
|
480
|
+
{uiText.clickToEnableAutoplay}
|
|
489
481
|
</button>
|
|
490
482
|
{/if}
|
|
491
|
-
{:else if
|
|
492
|
-
<
|
|
493
|
-
class="listen-button rli-feature-audio"
|
|
494
|
-
type="button"
|
|
495
|
-
aria-label={speechButtonLabel()}
|
|
496
|
-
onclick={playFeatureAudio}
|
|
497
|
-
>
|
|
498
|
-
<img
|
|
499
|
-
class={`listen-feature-icon rli-feature-listen ${isMediaPlaying || isSynthSpeaking ? '' : 'listen-active'}`}
|
|
500
|
-
src={featureAudioSkin.silentUrl}
|
|
501
|
-
alt=""
|
|
502
|
-
aria-hidden="true"
|
|
503
|
-
/>
|
|
504
|
-
<img
|
|
505
|
-
class={`listen-feature-icon rli-feature-listen ${isMediaPlaying || isSynthSpeaking ? 'listen-active' : ''}`}
|
|
506
|
-
src={featureAudioSkin.playingUrl}
|
|
507
|
-
alt=""
|
|
508
|
-
aria-hidden="true"
|
|
509
|
-
/>
|
|
510
|
-
</button>
|
|
483
|
+
{:else if hasAudioButMissingResource}
|
|
484
|
+
<p class="text-sm text-red-700 pie-audio-error" role="alert">{audioErrorMessage}</p>
|
|
511
485
|
{/if}
|
|
512
486
|
{#if props?.model?.audioTranscript}
|
|
513
487
|
<p
|
|
514
|
-
class={`text-sm mt-2 text-gray-700 ${showVisibleTranscript ? '' : 'sr-only'}`}
|
|
488
|
+
class={`text-sm mt-2 text-gray-700 pie-audio-transcript ${showVisibleTranscript ? '' : 'sr-only'}`}
|
|
515
489
|
id={transcriptId}
|
|
516
490
|
>
|
|
517
|
-
{props.model.audioTranscript}
|
|
491
|
+
<strong>{uiText.transcriptLabel}:</strong> {props.model.audioTranscript}
|
|
518
492
|
</p>
|
|
519
493
|
{/if}
|
|
520
494
|
</div>
|
|
521
495
|
{/if}
|
|
522
496
|
|
|
523
|
-
{#if props?.model?.teacherInstructions}
|
|
524
|
-
<div class="mb-4 teacher-instructions">
|
|
525
|
-
<h4 class="teacher-instructions-title">{t('teacherInstructions')}</h4>
|
|
526
|
-
<div class="prose teacher-instructions-content">{@html props.model.teacherInstructions}</div>
|
|
527
|
-
</div>
|
|
528
|
-
{/if}
|
|
529
|
-
|
|
530
497
|
{#if props?.model?.sentenceHtml}
|
|
531
|
-
<div class="mb-3 prose prose-p:my-1 sentence-line" aria-describedby={templateDescribedBy}>
|
|
498
|
+
<div class="mb-3 prose prose-p:my-1 sentence-line pie-sentence-line" aria-describedby={templateDescribedBy}>
|
|
532
499
|
{@html props.model.sentenceHtml}
|
|
533
500
|
</div>
|
|
534
501
|
{/if}
|
|
535
502
|
|
|
536
503
|
{#if !isAudioOnlyMode}
|
|
537
|
-
<div class="mb-4 template-line" aria-describedby={templateDescribedBy}>
|
|
504
|
+
<div class="mb-4 template-line pie-template-line" aria-describedby={templateDescribedBy}>
|
|
538
505
|
{@html templateParts.before}
|
|
539
506
|
<span
|
|
540
|
-
class={`inline-flex items-center min-h-[1.5em] px-2 mx-1 border-b-2 border-gray-500 align-baseline blank-slot ${isBlankOnlyTemplate ? 'blank-slot-standalone' : ''}`}
|
|
507
|
+
class={`inline-flex items-center min-h-[1.5em] px-2 mx-1 border-b-2 border-gray-500 align-baseline blank-slot pie-blank-slot ${isBlankOnlyTemplate ? 'blank-slot-standalone pie-blank-slot-standalone' : ''}`}
|
|
541
508
|
style={`width:${blankWidth};border-bottom-width:${blankBorderWidth};`}
|
|
542
509
|
role="status"
|
|
543
510
|
aria-live="polite"
|
|
544
511
|
aria-atomic="true"
|
|
545
|
-
aria-label={
|
|
512
|
+
aria-label={uiText.selectedAnswerInSentence}
|
|
546
513
|
>
|
|
547
514
|
{#if choiceMode === 'image' && displayChoice?.imageUrl}
|
|
548
515
|
<img
|
|
549
516
|
src={displayChoice.imageUrl}
|
|
550
|
-
alt={displayChoice.imageAlt ||
|
|
551
|
-
class="
|
|
517
|
+
alt={displayChoice.imageAlt || 'Selected answer image'}
|
|
518
|
+
class="w-auto object-contain pie-blank-image"
|
|
519
|
+
style="max-height:var(--mpb-selected-image-max-height, 4rem);"
|
|
552
520
|
/>
|
|
553
521
|
{:else if displayChoice?.labelHtml}
|
|
554
|
-
<span class="blank-inner choice-label">{@html displayChoice.labelHtml}</span>
|
|
522
|
+
<span class="blank-inner choice-label pie-blank-value">{@html displayChoice.labelHtml}</span>
|
|
555
523
|
{:else}
|
|
556
524
|
<span class="blank-inner-empty" aria-hidden="true"> </span>
|
|
557
525
|
{/if}
|
|
@@ -563,44 +531,51 @@ $effect(() => {
|
|
|
563
531
|
{#if isEvaluateMode && isIncorrect}
|
|
564
532
|
<button
|
|
565
533
|
type="button"
|
|
566
|
-
class="mb-3 flex items-center
|
|
567
|
-
|
|
534
|
+
class="mb-3 flex items-center cursor-pointer select-none pie-toggle-correct-answer"
|
|
535
|
+
style="gap:var(--mpb-toggle-button-gap, 0.5rem);"
|
|
536
|
+
on:click={toggleCorrectAnswer}
|
|
568
537
|
aria-pressed={showCorrectAnswer}
|
|
569
538
|
>
|
|
570
539
|
<span class="text-sm hover:underline">
|
|
571
|
-
{showCorrectAnswer ?
|
|
540
|
+
{showCorrectAnswer ? uiText.hideCorrectAnswer : uiText.showCorrectAnswer}
|
|
572
541
|
</span>
|
|
573
542
|
</button>
|
|
574
543
|
{/if}
|
|
575
544
|
{#if resultText}
|
|
576
|
-
<p id={resultId} class="sr-only" role="status" aria-live="polite">{resultText}</p>
|
|
545
|
+
<p id={resultId} class="sr-only pie-result-feedback" role="status" aria-live="polite">{resultText}</p>
|
|
577
546
|
{/if}
|
|
578
547
|
|
|
579
|
-
<fieldset class="border-0 p-0 m-0" disabled={props?.model?.disabled}>
|
|
580
|
-
<legend class="sr-only" id={legendId}>{legendText}</legend>
|
|
548
|
+
<fieldset class="border-0 p-0 m-0 pie-choices-fieldset" disabled={props?.model?.disabled}>
|
|
549
|
+
<legend class="sr-only pie-choices-legend" id={legendId}>{legendText}</legend>
|
|
581
550
|
<div
|
|
582
|
-
class={`
|
|
551
|
+
class={`pie-choices ${isHorizontalChoices ? 'flex flex-row flex-wrap items-start justify-center' : 'flex flex-col'}`}
|
|
552
|
+
style="gap:var(--mpb-choice-group-gap, 0.5rem);"
|
|
583
553
|
role="radiogroup"
|
|
584
554
|
tabindex="-1"
|
|
585
555
|
aria-labelledby={choicesGroupLabelledBy}
|
|
586
556
|
aria-describedby={resultText ? resultId : undefined}
|
|
587
|
-
|
|
557
|
+
on:keydown={onRadioGroupKeydown}
|
|
588
558
|
>
|
|
589
|
-
{#each
|
|
559
|
+
{#each choices as c (c.id)}
|
|
590
560
|
<div
|
|
591
|
-
class={`flex items-start
|
|
561
|
+
class={`flex items-start choice-row pie-choice ${isHorizontalChoices ? 'choice-row-horizontal pie-choice-horizontal' : ''} ${((showCorrectAnswer && isEvaluateMode ? props?.model?.correctChoiceId : selectedId) === c.id) ? 'is-selected pie-choice-selected' : ''}`}
|
|
562
|
+
style="gap:var(--mpb-choice-row-gap, 0.5rem);"
|
|
592
563
|
>
|
|
593
564
|
{#if isHorizontalChoices}
|
|
594
|
-
<label
|
|
595
|
-
|
|
565
|
+
<label
|
|
566
|
+
for={`${instanceId}-opt-${c.id}`}
|
|
567
|
+
class="cursor-pointer choice-tile text-center pie-choice-tile"
|
|
568
|
+
>
|
|
569
|
+
<span class="choice-tile-content pie-choice-tile-content">
|
|
596
570
|
{#if choiceMode === 'image' && c.imageUrl}
|
|
597
571
|
<img
|
|
598
572
|
src={c.imageUrl}
|
|
599
573
|
alt={c.imageAlt || `Choice ${c.id}`}
|
|
600
|
-
class="
|
|
574
|
+
class="object-contain mx-auto pie-choice-image"
|
|
575
|
+
style="max-height:var(--mpb-choice-image-max-height, 5rem);"
|
|
601
576
|
/>
|
|
602
577
|
{:else}
|
|
603
|
-
<span class="choice-label">{@html c.labelHtml || ''}</span>
|
|
578
|
+
<span class="choice-label pie-choice-label">{@html c.labelHtml || ''}</span>
|
|
604
579
|
{/if}
|
|
605
580
|
</span>
|
|
606
581
|
<input
|
|
@@ -612,8 +587,8 @@ $effect(() => {
|
|
|
612
587
|
(showCorrectAnswer && isEvaluateMode ? props?.model?.correctChoiceId : selectedId) === c.id
|
|
613
588
|
}
|
|
614
589
|
disabled={props?.model?.disabled}
|
|
615
|
-
|
|
616
|
-
class="choice-radio-bottom"
|
|
590
|
+
on:change={onRadioChange}
|
|
591
|
+
class="choice-radio-bottom pie-choice-radio pie-choice-radio-bottom"
|
|
617
592
|
/>
|
|
618
593
|
</label>
|
|
619
594
|
{:else}
|
|
@@ -626,22 +601,31 @@ $effect(() => {
|
|
|
626
601
|
(showCorrectAnswer && isEvaluateMode ? props?.model?.correctChoiceId : selectedId) === c.id
|
|
627
602
|
}
|
|
628
603
|
disabled={props?.model?.disabled}
|
|
629
|
-
|
|
630
|
-
class="
|
|
604
|
+
on:change={onRadioChange}
|
|
605
|
+
class="choice-radio-inline pie-choice-radio pie-choice-radio-inline"
|
|
631
606
|
/>
|
|
632
|
-
<label for={`${instanceId}-opt-${c.id}`} class="cursor-pointer flex-1">
|
|
607
|
+
<label for={`${instanceId}-opt-${c.id}`} class="cursor-pointer flex-1 pie-choice-label-wrap">
|
|
633
608
|
{#if choiceMode === 'image' && c.imageUrl}
|
|
634
|
-
<img
|
|
609
|
+
<img
|
|
610
|
+
src={c.imageUrl}
|
|
611
|
+
alt={c.imageAlt || `Choice ${c.id}`}
|
|
612
|
+
class="object-contain pie-choice-image"
|
|
613
|
+
style="max-height:var(--mpb-choice-image-max-height, 5rem);"
|
|
614
|
+
/>
|
|
635
615
|
{:else}
|
|
636
|
-
<span class="choice-label">{@html c.labelHtml || ''}</span>
|
|
616
|
+
<span class="choice-label pie-choice-label">{@html c.labelHtml || ''}</span>
|
|
637
617
|
{/if}
|
|
638
618
|
</label>
|
|
639
619
|
{/if}
|
|
640
620
|
{#if isEvaluateMode && !showCorrectAnswer}
|
|
641
621
|
{#if selectedId === c.id && isCorrect}
|
|
642
|
-
<span class="text-green-600 text-sm font-medium" aria-hidden="true"
|
|
622
|
+
<span class="text-green-600 text-sm font-medium pie-choice-feedback-correct" aria-hidden="true">
|
|
623
|
+
✓
|
|
624
|
+
</span>
|
|
643
625
|
{:else if selectedId === c.id && isIncorrect}
|
|
644
|
-
<span class="text-red-600 text-sm font-medium" aria-hidden="true"
|
|
626
|
+
<span class="text-red-600 text-sm font-medium pie-choice-feedback-incorrect" aria-hidden="true">
|
|
627
|
+
✗
|
|
628
|
+
</span>
|
|
645
629
|
{/if}
|
|
646
630
|
{/if}
|
|
647
631
|
</div>
|
|
@@ -694,12 +678,12 @@ $effect(() => {
|
|
|
694
678
|
}
|
|
695
679
|
|
|
696
680
|
.blank-slot-standalone {
|
|
697
|
-
width: 7rem;
|
|
681
|
+
width: var(--mpb-blank-standalone-width, 7rem);
|
|
698
682
|
}
|
|
699
683
|
|
|
700
684
|
.listen-button {
|
|
701
|
-
width: 128px;
|
|
702
|
-
height: 128px;
|
|
685
|
+
width: var(--mpb-listen-button-size, 128px);
|
|
686
|
+
height: var(--mpb-listen-button-size, 128px);
|
|
703
687
|
border: 0;
|
|
704
688
|
padding: 0;
|
|
705
689
|
background-color: transparent;
|
|
@@ -712,8 +696,8 @@ $effect(() => {
|
|
|
712
696
|
}
|
|
713
697
|
|
|
714
698
|
.listen-feature-icon {
|
|
715
|
-
width: 128px;
|
|
716
|
-
height: 128px;
|
|
699
|
+
width: var(--mpb-listen-button-size, 128px);
|
|
700
|
+
height: var(--mpb-listen-button-size, 128px);
|
|
717
701
|
object-fit: contain;
|
|
718
702
|
display: none;
|
|
719
703
|
}
|
|
@@ -725,7 +709,7 @@ $effect(() => {
|
|
|
725
709
|
.choice-row-horizontal {
|
|
726
710
|
flex-direction: column;
|
|
727
711
|
align-items: center;
|
|
728
|
-
width: min(170px, 30vw);
|
|
712
|
+
width: min(var(--mpb-choice-width-px, 170px), var(--mpb-choice-width-vw, 30vw));
|
|
729
713
|
gap: 0;
|
|
730
714
|
}
|
|
731
715
|
|
|
@@ -735,7 +719,7 @@ $effect(() => {
|
|
|
735
719
|
justify-content: space-between;
|
|
736
720
|
align-items: center;
|
|
737
721
|
width: 100%;
|
|
738
|
-
min-height: 11rem;
|
|
722
|
+
min-height: var(--mpb-choice-tile-min-height, 11rem);
|
|
739
723
|
padding: 0.8rem 0.65rem 0.5rem;
|
|
740
724
|
border-radius: 8px;
|
|
741
725
|
background: transparent;
|
|
@@ -747,88 +731,19 @@ $effect(() => {
|
|
|
747
731
|
display: flex;
|
|
748
732
|
align-items: center;
|
|
749
733
|
justify-content: center;
|
|
750
|
-
min-height: 7.5rem;
|
|
734
|
+
min-height: var(--mpb-choice-content-min-height, 7.5rem);
|
|
751
735
|
}
|
|
752
736
|
|
|
753
737
|
.choice-row-horizontal:hover .choice-tile {
|
|
754
|
-
background: #
|
|
738
|
+
background: #ececec;
|
|
755
739
|
}
|
|
756
740
|
|
|
757
741
|
.choice-row-horizontal.is-selected .choice-tile {
|
|
758
|
-
background: #
|
|
742
|
+
background: #eceabf;
|
|
759
743
|
}
|
|
760
744
|
|
|
761
745
|
.choice-row-horizontal.is-selected:hover .choice-tile {
|
|
762
|
-
background: #
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
.choice-row:not(.choice-row-horizontal):hover {
|
|
766
|
-
background: #f2f2f2;
|
|
767
|
-
border-radius: 6px;
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
.choice-row.is-selected:not(.choice-row-horizontal) {
|
|
771
|
-
background: #fcfcd3;
|
|
772
|
-
border-radius: 6px;
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
:global(.access-contrast-3) .choice-row-horizontal,
|
|
776
|
-
:global(.access-contrast-6) .choice-row-horizontal {
|
|
777
|
-
border-bottom-width: 4px;
|
|
778
|
-
border-bottom-style: solid;
|
|
779
|
-
padding-bottom: 10px;
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
:global(.access-contrast-3) .choice-row:not(.choice-row-horizontal),
|
|
783
|
-
:global(.access-contrast-6) .choice-row:not(.choice-row-horizontal) {
|
|
784
|
-
border-left-width: 4px;
|
|
785
|
-
border-left-style: solid;
|
|
786
|
-
padding-left: 0.35rem;
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
:global(.access-contrast-3) .choice-row-horizontal,
|
|
790
|
-
:global(.access-contrast-3) .choice-row:not(.choice-row-horizontal) {
|
|
791
|
-
border-color: #2d4e84;
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
:global(.access-contrast-6) .choice-row-horizontal,
|
|
795
|
-
:global(.access-contrast-6) .choice-row:not(.choice-row-horizontal) {
|
|
796
|
-
border-color: #000;
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
:global(.access-contrast-3) .choice-row:hover,
|
|
800
|
-
:global(.access-contrast-6) .choice-row:hover {
|
|
801
|
-
border-radius: 0;
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
:global(.access-contrast-3) .choice-row:hover {
|
|
805
|
-
border-color: #fff25d;
|
|
806
|
-
color: #fff25d;
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
:global(.access-contrast-6) .choice-row:hover {
|
|
810
|
-
border-color: #fff;
|
|
811
|
-
color: #fff;
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
:global(.access-contrast-3) .choice-row:hover:not(.is-selected):not(.choice-row-horizontal),
|
|
815
|
-
:global(.access-contrast-6) .choice-row:hover:not(.is-selected):not(.choice-row-horizontal) {
|
|
816
|
-
background: inherit;
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
:global(.access-contrast-3) .choice-row-horizontal:hover:not(.is-selected) .choice-tile,
|
|
820
|
-
:global(.access-contrast-6) .choice-row-horizontal:hover:not(.is-selected) .choice-tile {
|
|
821
|
-
background: inherit;
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
:global(.access-contrast-3) .choice-row.is-selected:hover,
|
|
825
|
-
:global(.access-contrast-6) .choice-row.is-selected:hover {
|
|
826
|
-
color: #000;
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
:global(.access-contrast-3) .choice-row-horizontal.is-selected:hover .choice-tile,
|
|
830
|
-
:global(.access-contrast-6) .choice-row-horizontal.is-selected:hover .choice-tile {
|
|
831
|
-
background: #fcfcd3;
|
|
746
|
+
background: #eceabf;
|
|
832
747
|
}
|
|
833
748
|
|
|
834
749
|
.choice-row-horizontal :global(p) {
|
|
@@ -841,34 +756,6 @@ $effect(() => {
|
|
|
841
756
|
margin: 0;
|
|
842
757
|
}
|
|
843
758
|
|
|
844
|
-
.teacher-instructions {
|
|
845
|
-
border-left: 4px solid #60a5fa;
|
|
846
|
-
background: #eff6ff;
|
|
847
|
-
padding: 0.5rem 0.75rem;
|
|
848
|
-
border-radius: 4px;
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
.teacher-instructions-title {
|
|
852
|
-
margin: 0 0 0.3rem;
|
|
853
|
-
font-size: 0.9rem;
|
|
854
|
-
font-weight: 600;
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
.vic-family-layout {
|
|
858
|
-
margin-left: auto;
|
|
859
|
-
margin-right: auto;
|
|
860
|
-
max-width: 800px;
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
.vic-family-layout .choice-row {
|
|
864
|
-
user-select: none;
|
|
865
|
-
margin-right: 10px;
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
.sel-vic-family-layout {
|
|
869
|
-
font-size: 1.5em;
|
|
870
|
-
}
|
|
871
|
-
|
|
872
759
|
.blank-inner {
|
|
873
760
|
width: 100%;
|
|
874
761
|
display: inline-flex;
|
|
@@ -878,7 +765,11 @@ $effect(() => {
|
|
|
878
765
|
}
|
|
879
766
|
|
|
880
767
|
.choice-radio-bottom {
|
|
881
|
-
margin-top: 0.5rem;
|
|
768
|
+
margin-top: var(--mpb-horizontal-choice-radio-top-margin, 0.5rem);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
.choice-radio-inline {
|
|
772
|
+
margin-top: var(--mpb-horizontal-choice-radio-top-margin, 0.5rem);
|
|
882
773
|
}
|
|
883
774
|
|
|
884
775
|
.layout-audio_blank_only .audio-container,
|
|
@@ -890,24 +781,25 @@ $effect(() => {
|
|
|
890
781
|
.layout-audio_blank_only .template-line,
|
|
891
782
|
.layout-stimulus_image_blank .template-line {
|
|
892
783
|
text-align: center;
|
|
893
|
-
margin: 0.8rem 0
|
|
784
|
+
margin: var(--mpb-audio-blank-template-margin-top, 0.8rem) 0
|
|
785
|
+
var(--mpb-audio-blank-template-margin-bottom, 1.8rem);
|
|
894
786
|
}
|
|
895
787
|
|
|
896
788
|
.layout-audio_blank_only .blank-slot,
|
|
897
789
|
.layout-stimulus_image_blank .blank-slot {
|
|
898
|
-
width: 10rem;
|
|
899
|
-
border-bottom-width: 4px;
|
|
790
|
+
width: var(--mpb-blank-wide-width, 10rem);
|
|
791
|
+
border-bottom-width: var(--mpb-blank-underline-wide-width, 4px);
|
|
900
792
|
}
|
|
901
793
|
|
|
902
794
|
.layout-stimulus_image_blank {
|
|
903
795
|
display: grid;
|
|
904
|
-
grid-template-columns: minmax(210px, 1fr) auto;
|
|
796
|
+
grid-template-columns: minmax(var(--mpb-stimulus-min-column, 210px), 1fr) auto;
|
|
905
797
|
grid-template-areas:
|
|
906
798
|
'sentence audio'
|
|
907
799
|
'. template'
|
|
908
800
|
'choices choices';
|
|
909
|
-
column-gap: 2rem;
|
|
910
|
-
row-gap: 0.7rem;
|
|
801
|
+
column-gap: var(--mpb-stimulus-grid-column-gap, 2rem);
|
|
802
|
+
row-gap: var(--mpb-stimulus-grid-row-gap, 0.7rem);
|
|
911
803
|
align-items: start;
|
|
912
804
|
}
|
|
913
805
|
|
|
@@ -918,7 +810,7 @@ $effect(() => {
|
|
|
918
810
|
|
|
919
811
|
.layout-stimulus_image_blank .sentence-line {
|
|
920
812
|
grid-area: sentence;
|
|
921
|
-
margin: 0.2rem 0 0;
|
|
813
|
+
margin: var(--mpb-stimulus-sentence-margin-top, 0.2rem) 0 0;
|
|
922
814
|
}
|
|
923
815
|
|
|
924
816
|
.layout-stimulus_image_blank .template-line {
|
|
@@ -930,17 +822,17 @@ $effect(() => {
|
|
|
930
822
|
|
|
931
823
|
.layout-stimulus_image_blank fieldset {
|
|
932
824
|
grid-area: choices;
|
|
933
|
-
margin-top: 0.9rem;
|
|
825
|
+
margin-top: var(--mpb-stimulus-choices-margin-top, 0.9rem);
|
|
934
826
|
}
|
|
935
827
|
|
|
936
828
|
.layout-token_sequence {
|
|
937
829
|
display: grid;
|
|
938
|
-
grid-template-columns: minmax(260px, 1fr) auto;
|
|
830
|
+
grid-template-columns: minmax(var(--mpb-text-min-column, 260px), 1fr) auto;
|
|
939
831
|
grid-template-areas:
|
|
940
832
|
'template audio'
|
|
941
833
|
'choices choices';
|
|
942
|
-
column-gap: 1.5rem;
|
|
943
|
-
row-gap: 0.8rem;
|
|
834
|
+
column-gap: var(--mpb-token-grid-column-gap, 1.5rem);
|
|
835
|
+
row-gap: var(--mpb-token-grid-row-gap, 0.8rem);
|
|
944
836
|
align-items: start;
|
|
945
837
|
}
|
|
946
838
|
|
|
@@ -952,34 +844,34 @@ $effect(() => {
|
|
|
952
844
|
|
|
953
845
|
.layout-token_sequence .template-line {
|
|
954
846
|
grid-area: template;
|
|
955
|
-
margin: 0.6rem 0 0;
|
|
847
|
+
margin: var(--mpb-token-template-margin-top, 0.6rem) 0 0;
|
|
956
848
|
justify-self: center;
|
|
957
849
|
text-align: center;
|
|
958
850
|
}
|
|
959
851
|
|
|
960
852
|
.layout-token_sequence .template-line :global(span + span) {
|
|
961
|
-
margin-left: 0.35rem;
|
|
853
|
+
margin-left: var(--mpb-token-inline-token-gap, 0.35rem);
|
|
962
854
|
}
|
|
963
855
|
|
|
964
856
|
.layout-token_sequence .blank-slot {
|
|
965
|
-
width: 7rem;
|
|
966
|
-
border-bottom-width: 4px;
|
|
967
|
-
margin-left: 0.35rem;
|
|
857
|
+
width: var(--mpb-blank-standalone-width, 7rem);
|
|
858
|
+
border-bottom-width: var(--mpb-blank-underline-wide-width, 4px);
|
|
859
|
+
margin-left: var(--mpb-token-inline-token-gap, 0.35rem);
|
|
968
860
|
}
|
|
969
861
|
|
|
970
862
|
.layout-token_sequence fieldset {
|
|
971
863
|
grid-area: choices;
|
|
972
|
-
margin-top: 0.2rem;
|
|
864
|
+
margin-top: var(--mpb-token-choices-margin-top, 0.2rem);
|
|
973
865
|
}
|
|
974
866
|
|
|
975
867
|
.layout-inline_sentence.has-inline-audio {
|
|
976
868
|
display: grid;
|
|
977
|
-
grid-template-columns: minmax(260px, 1fr) auto;
|
|
869
|
+
grid-template-columns: minmax(var(--mpb-text-min-column, 260px), 1fr) auto;
|
|
978
870
|
grid-template-areas:
|
|
979
871
|
'template audio'
|
|
980
872
|
'choices choices';
|
|
981
|
-
column-gap: 1.5rem;
|
|
982
|
-
row-gap: 0.65rem;
|
|
873
|
+
column-gap: var(--mpb-inline-grid-column-gap, 1.5rem);
|
|
874
|
+
row-gap: var(--mpb-inline-grid-row-gap, 0.65rem);
|
|
983
875
|
align-items: start;
|
|
984
876
|
}
|
|
985
877
|
|
|
@@ -991,13 +883,13 @@ $effect(() => {
|
|
|
991
883
|
|
|
992
884
|
.layout-inline_sentence.has-inline-audio .template-line {
|
|
993
885
|
grid-area: template;
|
|
994
|
-
margin: 0.45rem 0 0;
|
|
886
|
+
margin: var(--mpb-inline-template-margin-top, 0.45rem) 0 0;
|
|
995
887
|
text-align: center;
|
|
996
888
|
justify-self: center;
|
|
997
889
|
}
|
|
998
890
|
|
|
999
891
|
.layout-inline_sentence.has-inline-audio fieldset {
|
|
1000
892
|
grid-area: choices;
|
|
1001
|
-
margin-top: 0.25rem;
|
|
893
|
+
margin-top: var(--mpb-inline-choices-margin-top, 0.25rem);
|
|
1002
894
|
}
|
|
1003
895
|
</style>
|