@pie-element/mc-populated-blank 0.1.0 → 0.2.1
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 +116 -2
- package/dist/controller/defaults.d.ts +89 -0
- package/dist/controller/defaults.d.ts.map +1 -1
- package/dist/controller/index.d.ts.map +1 -1
- package/dist/controller/index.js +110 -4
- package/dist/controller/index.js.map +1 -1
- package/dist/delivery/index.js +589 -477
- package/dist/delivery/index.js.map +1 -1
- package/dist/index.iife.d.ts +2 -1
- package/dist/index.iife.d.ts.map +1 -1
- package/dist/index.iife.js +25 -19
- package/dist/index.js +589 -477
- package/dist/index.js.map +1 -1
- package/package.json +1 -4
- package/src/controller/defaults.ts +53 -0
- package/src/controller/index.ts +83 -5
- package/src/delivery/McPopulatedBlank.svelte +464 -457
- package/src/index.iife.ts +2 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pie-element/mc-populated-blank",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
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,43 @@
|
|
|
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
|
+
audioInstructionsMaxWidthPx: 875,
|
|
26
|
+
narrowHorizontalChoiceMaxWidthPx: 230,
|
|
27
|
+
stimulusGridColumnGapRem: 2,
|
|
28
|
+
stimulusGridRowGapRem: 0.7,
|
|
29
|
+
stimulusSentenceMarginTopRem: 0.2,
|
|
30
|
+
stimulusChoicesMarginTopRem: 0.9,
|
|
31
|
+
tokenGridColumnGapRem: 1.5,
|
|
32
|
+
tokenGridRowGapRem: 0.8,
|
|
33
|
+
tokenTemplateMarginTopRem: 0.6,
|
|
34
|
+
tokenInlineTokenGapRem: 0.35,
|
|
35
|
+
tokenChoicesMarginTopRem: 0.2,
|
|
36
|
+
inlineGridColumnGapRem: 1.5,
|
|
37
|
+
inlineGridRowGapRem: 0.65,
|
|
38
|
+
inlineTemplateMarginTopRem: 0.45,
|
|
39
|
+
inlineChoicesMarginTopRem: 0.25,
|
|
40
|
+
} as const;
|
|
3
41
|
|
|
4
42
|
export default {
|
|
5
43
|
model: {
|
|
@@ -10,6 +48,21 @@ export default {
|
|
|
10
48
|
interactionMode: 'populate_blank' as const,
|
|
11
49
|
layoutProfile: '',
|
|
12
50
|
choiceLayout: '',
|
|
51
|
+
layoutProfilePresets: {},
|
|
52
|
+
layoutLimits: { ...DEFAULT_LAYOUT_LIMITS },
|
|
53
|
+
audioButtonSkin: null,
|
|
54
|
+
audioButtonSkinsByLocale: {},
|
|
55
|
+
uiText: {
|
|
56
|
+
answerChoices: 'Answer choices',
|
|
57
|
+
selectedAnswerInSentence: 'Selected answer in sentence',
|
|
58
|
+
showCorrectAnswer: 'Show correct answer',
|
|
59
|
+
hideCorrectAnswer: 'Hide correct answer',
|
|
60
|
+
clickToEnableAutoplay: 'Click to enable audio autoplay',
|
|
61
|
+
audioResourceUnavailable: 'Audio is enabled but no playable audio URL is configured.',
|
|
62
|
+
transcriptLabel: 'Transcript',
|
|
63
|
+
listenLabelEn: 'Listen',
|
|
64
|
+
listenLabelEs: 'Escuchar',
|
|
65
|
+
},
|
|
13
66
|
sentenceHtml: '',
|
|
14
67
|
template: `<p>The answer is ${BLANK_TOKEN}.</p>`,
|
|
15
68
|
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,25 @@ 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: normalizedQuestion.layoutLimits || { ...DEFAULT_LAYOUT_LIMITS },
|
|
167
|
+
audioButtonSkin:
|
|
168
|
+
normalizedQuestion.audioButtonSkin && typeof normalizedQuestion.audioButtonSkin === 'object'
|
|
169
|
+
? normalizedQuestion.audioButtonSkin
|
|
170
|
+
: null,
|
|
171
|
+
audioButtonSkinsByLocale:
|
|
172
|
+
normalizedQuestion.audioButtonSkinsByLocale &&
|
|
173
|
+
typeof normalizedQuestion.audioButtonSkinsByLocale === 'object'
|
|
174
|
+
? normalizedQuestion.audioButtonSkinsByLocale
|
|
175
|
+
: {},
|
|
176
|
+
uiText:
|
|
177
|
+
normalizedQuestion.uiText && typeof normalizedQuestion.uiText === 'object'
|
|
178
|
+
? normalizedQuestion.uiText
|
|
179
|
+
: {},
|
|
154
180
|
sentenceHtml: normalizedQuestion.sentenceHtml || null,
|
|
155
181
|
template: normalizedQuestion.template,
|
|
156
182
|
choiceMode: normalizedQuestion.choiceMode,
|
|
@@ -161,6 +187,10 @@ export const model = async (question: any, session: any, env: any, updateSession
|
|
|
161
187
|
audioUrl: normalizedQuestion.hasAudio ? normalizedQuestion.audioUrl : null,
|
|
162
188
|
audioTranscript: normalizedQuestion.hasAudio ? normalizedQuestion.audioTranscript : null,
|
|
163
189
|
showVisibleTranscript: !!normalizedQuestion.showVisibleTranscript,
|
|
190
|
+
useFeatureButtonAudio:
|
|
191
|
+
typeof normalizedQuestion.useFeatureButtonAudio === 'boolean'
|
|
192
|
+
? normalizedQuestion.useFeatureButtonAudio
|
|
193
|
+
: undefined,
|
|
164
194
|
locale: normalizedQuestion.locale || '',
|
|
165
195
|
disabled: env.mode !== 'gather',
|
|
166
196
|
view: env.mode === 'view',
|
|
@@ -263,9 +293,57 @@ export const validate = (model: any = {}, _config: any = {}) => {
|
|
|
263
293
|
|
|
264
294
|
if (model.hasAudio) {
|
|
265
295
|
const hasAudioUrl = !!model.audioUrl?.trim();
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
296
|
+
if (!hasAudioUrl) {
|
|
297
|
+
errors.audioUrl = 'A playable audio URL is required when audio is enabled';
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const limits = model?.layoutLimits;
|
|
302
|
+
if (limits && typeof limits === 'object') {
|
|
303
|
+
const numericLimitKeys = [
|
|
304
|
+
'blankStandaloneWidthRem',
|
|
305
|
+
'blankWideWidthRem',
|
|
306
|
+
'blankUnderlineWidthPx',
|
|
307
|
+
'blankUnderlineWideWidthPx',
|
|
308
|
+
'horizontalChoiceWidthPx',
|
|
309
|
+
'horizontalChoiceWidthVw',
|
|
310
|
+
'horizontalChoiceTileMinHeightRem',
|
|
311
|
+
'horizontalChoiceContentMinHeightRem',
|
|
312
|
+
'selectedImageMaxHeightRem',
|
|
313
|
+
'choiceImageMaxHeightRem',
|
|
314
|
+
'listenButtonSizePx',
|
|
315
|
+
'stimulusMinColumnPx',
|
|
316
|
+
'textMinColumnPx',
|
|
317
|
+
'legendMaxChars',
|
|
318
|
+
'choiceGroupGapRem',
|
|
319
|
+
'choiceRowGapRem',
|
|
320
|
+
'toggleButtonGapRem',
|
|
321
|
+
'horizontalChoiceRadioTopMarginRem',
|
|
322
|
+
'audioBlankTemplateMarginTopRem',
|
|
323
|
+
'audioBlankTemplateMarginBottomRem',
|
|
324
|
+
'audioInstructionsMaxWidthPx',
|
|
325
|
+
'narrowHorizontalChoiceMaxWidthPx',
|
|
326
|
+
'stimulusGridColumnGapRem',
|
|
327
|
+
'stimulusGridRowGapRem',
|
|
328
|
+
'stimulusSentenceMarginTopRem',
|
|
329
|
+
'stimulusChoicesMarginTopRem',
|
|
330
|
+
'tokenGridColumnGapRem',
|
|
331
|
+
'tokenGridRowGapRem',
|
|
332
|
+
'tokenTemplateMarginTopRem',
|
|
333
|
+
'tokenInlineTokenGapRem',
|
|
334
|
+
'tokenChoicesMarginTopRem',
|
|
335
|
+
'inlineGridColumnGapRem',
|
|
336
|
+
'inlineGridRowGapRem',
|
|
337
|
+
'inlineTemplateMarginTopRem',
|
|
338
|
+
'inlineChoicesMarginTopRem',
|
|
339
|
+
];
|
|
340
|
+
for (const key of numericLimitKeys) {
|
|
341
|
+
if (limits[key] === undefined || limits[key] === null || limits[key] === '') continue;
|
|
342
|
+
const value = Number(limits[key]);
|
|
343
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
344
|
+
errors.layoutLimits = `${key} must be a positive number when provided`;
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
269
347
|
}
|
|
270
348
|
}
|
|
271
349
|
|