@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pie-element/mc-populated-blank",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Multiple-choice option populates an inline blank in a sentence template (Svelte 5 PIE element)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -30,9 +30,6 @@
|
|
|
30
30
|
"development": "./src/print/index.ts",
|
|
31
31
|
"types": "./dist/print/index.d.ts",
|
|
32
32
|
"default": "./dist/print/index.js"
|
|
33
|
-
},
|
|
34
|
-
"./iife": {
|
|
35
|
-
"default": "./dist/index.iife.js"
|
|
36
33
|
}
|
|
37
34
|
},
|
|
38
35
|
"files": [
|
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
/** Token authors must include exactly once in `template` (HTML). */
|
|
2
2
|
export const BLANK_TOKEN = '{{blank}}';
|
|
3
|
+
/** Baseline values tuned to match historical CQT visuals; override via model.layoutLimits. */
|
|
4
|
+
export const DEFAULT_LAYOUT_LIMITS = {
|
|
5
|
+
blankStandaloneWidthRem: 7,
|
|
6
|
+
blankWideWidthRem: 10,
|
|
7
|
+
blankUnderlineWidthPx: 2,
|
|
8
|
+
blankUnderlineWideWidthPx: 4,
|
|
9
|
+
horizontalChoiceWidthPx: 170,
|
|
10
|
+
horizontalChoiceWidthVw: 30,
|
|
11
|
+
horizontalChoiceTileMinHeightRem: 11,
|
|
12
|
+
horizontalChoiceContentMinHeightRem: 7.5,
|
|
13
|
+
selectedImageMaxHeightRem: 4,
|
|
14
|
+
choiceImageMaxHeightRem: 5,
|
|
15
|
+
listenButtonSizePx: 128,
|
|
16
|
+
stimulusMinColumnPx: 210,
|
|
17
|
+
textMinColumnPx: 260,
|
|
18
|
+
legendMaxChars: 120,
|
|
19
|
+
choiceGroupGapRem: 0.5,
|
|
20
|
+
choiceRowGapRem: 0.5,
|
|
21
|
+
toggleButtonGapRem: 0.5,
|
|
22
|
+
horizontalChoiceRadioTopMarginRem: 0.5,
|
|
23
|
+
audioBlankTemplateMarginTopRem: 0.8,
|
|
24
|
+
audioBlankTemplateMarginBottomRem: 1.8,
|
|
25
|
+
stimulusGridColumnGapRem: 2,
|
|
26
|
+
stimulusGridRowGapRem: 0.7,
|
|
27
|
+
stimulusSentenceMarginTopRem: 0.2,
|
|
28
|
+
stimulusChoicesMarginTopRem: 0.9,
|
|
29
|
+
tokenGridColumnGapRem: 1.5,
|
|
30
|
+
tokenGridRowGapRem: 0.8,
|
|
31
|
+
tokenTemplateMarginTopRem: 0.6,
|
|
32
|
+
tokenInlineTokenGapRem: 0.35,
|
|
33
|
+
tokenChoicesMarginTopRem: 0.2,
|
|
34
|
+
inlineGridColumnGapRem: 1.5,
|
|
35
|
+
inlineGridRowGapRem: 0.65,
|
|
36
|
+
inlineTemplateMarginTopRem: 0.45,
|
|
37
|
+
inlineChoicesMarginTopRem: 0.25,
|
|
38
|
+
} as const;
|
|
3
39
|
|
|
4
40
|
export default {
|
|
5
41
|
model: {
|
|
@@ -10,6 +46,21 @@ export default {
|
|
|
10
46
|
interactionMode: 'populate_blank' as const,
|
|
11
47
|
layoutProfile: '',
|
|
12
48
|
choiceLayout: '',
|
|
49
|
+
layoutProfilePresets: {},
|
|
50
|
+
layoutLimits: { ...DEFAULT_LAYOUT_LIMITS },
|
|
51
|
+
audioButtonSkin: null,
|
|
52
|
+
audioButtonSkinsByLocale: {},
|
|
53
|
+
uiText: {
|
|
54
|
+
answerChoices: 'Answer choices',
|
|
55
|
+
selectedAnswerInSentence: 'Selected answer in sentence',
|
|
56
|
+
showCorrectAnswer: 'Show correct answer',
|
|
57
|
+
hideCorrectAnswer: 'Hide correct answer',
|
|
58
|
+
clickToEnableAutoplay: 'Click to enable audio autoplay',
|
|
59
|
+
audioResourceUnavailable: 'Audio is enabled but no playable audio URL is configured.',
|
|
60
|
+
transcriptLabel: 'Transcript',
|
|
61
|
+
listenLabelEn: 'Listen',
|
|
62
|
+
listenLabelEs: 'Escuchar',
|
|
63
|
+
},
|
|
13
64
|
sentenceHtml: '',
|
|
14
65
|
template: `<p>The answer is ${BLANK_TOKEN}.</p>`,
|
|
15
66
|
choiceMode: 'text' as const,
|
package/src/controller/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isEmpty } from 'lodash-es';
|
|
2
|
-
import defaults, { BLANK_TOKEN } from './defaults';
|
|
2
|
+
import defaults, { BLANK_TOKEN, DEFAULT_LAYOUT_LIMITS } from './defaults';
|
|
3
3
|
|
|
4
4
|
export function countBlankTokens(template: string): number {
|
|
5
5
|
if (!template) return 0;
|
|
@@ -72,7 +72,14 @@ export const outcome = (question: any, session: any, env: any) =>
|
|
|
72
72
|
resolve({ score, empty: false, traceLog });
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
-
export const createDefaultModel = (model: any = {}) => ({
|
|
75
|
+
export const createDefaultModel = (model: any = {}) => ({
|
|
76
|
+
...defaults.model,
|
|
77
|
+
...model,
|
|
78
|
+
layoutLimits: {
|
|
79
|
+
...DEFAULT_LAYOUT_LIMITS,
|
|
80
|
+
...(model?.layoutLimits || {}),
|
|
81
|
+
},
|
|
82
|
+
});
|
|
76
83
|
|
|
77
84
|
export const normalizeSession = (s: any) => ({ ...s });
|
|
78
85
|
|
|
@@ -151,6 +158,28 @@ export const model = async (question: any, session: any, env: any, updateSession
|
|
|
151
158
|
interactionMode: normalizedQuestion.interactionMode || 'populate_blank',
|
|
152
159
|
layoutProfile: normalizedQuestion.layoutProfile || '',
|
|
153
160
|
choiceLayout: normalizedQuestion.choiceLayout || '',
|
|
161
|
+
layoutProfilePresets:
|
|
162
|
+
normalizedQuestion.layoutProfilePresets &&
|
|
163
|
+
typeof normalizedQuestion.layoutProfilePresets === 'object'
|
|
164
|
+
? normalizedQuestion.layoutProfilePresets
|
|
165
|
+
: {},
|
|
166
|
+
layoutLimits: {
|
|
167
|
+
...DEFAULT_LAYOUT_LIMITS,
|
|
168
|
+
...(normalizedQuestion.layoutLimits || {}),
|
|
169
|
+
},
|
|
170
|
+
audioButtonSkin:
|
|
171
|
+
normalizedQuestion.audioButtonSkin && typeof normalizedQuestion.audioButtonSkin === 'object'
|
|
172
|
+
? normalizedQuestion.audioButtonSkin
|
|
173
|
+
: null,
|
|
174
|
+
audioButtonSkinsByLocale:
|
|
175
|
+
normalizedQuestion.audioButtonSkinsByLocale &&
|
|
176
|
+
typeof normalizedQuestion.audioButtonSkinsByLocale === 'object'
|
|
177
|
+
? normalizedQuestion.audioButtonSkinsByLocale
|
|
178
|
+
: {},
|
|
179
|
+
uiText:
|
|
180
|
+
normalizedQuestion.uiText && typeof normalizedQuestion.uiText === 'object'
|
|
181
|
+
? normalizedQuestion.uiText
|
|
182
|
+
: {},
|
|
154
183
|
sentenceHtml: normalizedQuestion.sentenceHtml || null,
|
|
155
184
|
template: normalizedQuestion.template,
|
|
156
185
|
choiceMode: normalizedQuestion.choiceMode,
|
|
@@ -161,6 +190,10 @@ export const model = async (question: any, session: any, env: any, updateSession
|
|
|
161
190
|
audioUrl: normalizedQuestion.hasAudio ? normalizedQuestion.audioUrl : null,
|
|
162
191
|
audioTranscript: normalizedQuestion.hasAudio ? normalizedQuestion.audioTranscript : null,
|
|
163
192
|
showVisibleTranscript: !!normalizedQuestion.showVisibleTranscript,
|
|
193
|
+
useFeatureButtonAudio:
|
|
194
|
+
typeof normalizedQuestion.useFeatureButtonAudio === 'boolean'
|
|
195
|
+
? normalizedQuestion.useFeatureButtonAudio
|
|
196
|
+
: undefined,
|
|
164
197
|
locale: normalizedQuestion.locale || '',
|
|
165
198
|
disabled: env.mode !== 'gather',
|
|
166
199
|
view: env.mode === 'view',
|
|
@@ -263,9 +296,55 @@ export const validate = (model: any = {}, _config: any = {}) => {
|
|
|
263
296
|
|
|
264
297
|
if (model.hasAudio) {
|
|
265
298
|
const hasAudioUrl = !!model.audioUrl?.trim();
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
299
|
+
if (!hasAudioUrl) {
|
|
300
|
+
errors.audioUrl = 'A playable audio URL is required when audio is enabled';
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const limits = model?.layoutLimits;
|
|
305
|
+
if (limits && typeof limits === 'object') {
|
|
306
|
+
const numericLimitKeys = [
|
|
307
|
+
'blankStandaloneWidthRem',
|
|
308
|
+
'blankWideWidthRem',
|
|
309
|
+
'blankUnderlineWidthPx',
|
|
310
|
+
'blankUnderlineWideWidthPx',
|
|
311
|
+
'horizontalChoiceWidthPx',
|
|
312
|
+
'horizontalChoiceWidthVw',
|
|
313
|
+
'horizontalChoiceTileMinHeightRem',
|
|
314
|
+
'horizontalChoiceContentMinHeightRem',
|
|
315
|
+
'selectedImageMaxHeightRem',
|
|
316
|
+
'choiceImageMaxHeightRem',
|
|
317
|
+
'listenButtonSizePx',
|
|
318
|
+
'stimulusMinColumnPx',
|
|
319
|
+
'textMinColumnPx',
|
|
320
|
+
'legendMaxChars',
|
|
321
|
+
'choiceGroupGapRem',
|
|
322
|
+
'choiceRowGapRem',
|
|
323
|
+
'toggleButtonGapRem',
|
|
324
|
+
'horizontalChoiceRadioTopMarginRem',
|
|
325
|
+
'audioBlankTemplateMarginTopRem',
|
|
326
|
+
'audioBlankTemplateMarginBottomRem',
|
|
327
|
+
'stimulusGridColumnGapRem',
|
|
328
|
+
'stimulusGridRowGapRem',
|
|
329
|
+
'stimulusSentenceMarginTopRem',
|
|
330
|
+
'stimulusChoicesMarginTopRem',
|
|
331
|
+
'tokenGridColumnGapRem',
|
|
332
|
+
'tokenGridRowGapRem',
|
|
333
|
+
'tokenTemplateMarginTopRem',
|
|
334
|
+
'tokenInlineTokenGapRem',
|
|
335
|
+
'tokenChoicesMarginTopRem',
|
|
336
|
+
'inlineGridColumnGapRem',
|
|
337
|
+
'inlineGridRowGapRem',
|
|
338
|
+
'inlineTemplateMarginTopRem',
|
|
339
|
+
'inlineChoicesMarginTopRem',
|
|
340
|
+
];
|
|
341
|
+
for (const key of numericLimitKeys) {
|
|
342
|
+
if (limits[key] === undefined || limits[key] === null || limits[key] === '') continue;
|
|
343
|
+
const value = Number(limits[key]);
|
|
344
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
345
|
+
errors.layoutLimits = `${key} must be a positive number when provided`;
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
269
348
|
}
|
|
270
349
|
}
|
|
271
350
|
|