@intellectif/lk-react 20.0.0 → 21.0.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/CHANGELOG.md +21 -0
- package/README.md +30 -2
- package/dist/{WrittenResponse-obIDDYDH.d.cts → WrittenResponse-BVPmrmQL.d.cts} +1 -1
- package/dist/{WrittenResponse-D2YI8xOK.d.ts → WrittenResponse-IAJvvL-g.d.ts} +1 -1
- package/dist/ai/LkAiProvider.cjs +3 -3
- package/dist/ai/LkAiProvider.d.cts +5 -3
- package/dist/ai/LkAiProvider.d.ts +5 -3
- package/dist/ai/LkAiProvider.js +1 -1
- package/dist/ai/useAiHelp.cjs +5 -5
- package/dist/ai/useAiHelp.d.cts +1 -1
- package/dist/ai/useAiHelp.d.ts +1 -1
- package/dist/ai/useAiHelp.js +3 -3
- package/dist/{chunk-W6DMHN33.js → chunk-5MO7ACSB.js} +10 -10
- package/dist/{chunk-W6DMHN33.js.map → chunk-5MO7ACSB.js.map} +1 -1
- package/dist/{chunk-7K4B2EMU.js → chunk-5Q2KFIZQ.js} +4 -4
- package/dist/{chunk-7K4B2EMU.js.map → chunk-5Q2KFIZQ.js.map} +1 -1
- package/dist/{chunk-APZ6JFN6.cjs → chunk-6SNNMVBV.cjs} +5 -5
- package/dist/{chunk-APZ6JFN6.cjs.map → chunk-6SNNMVBV.cjs.map} +1 -1
- package/dist/{chunk-L7WY5YFQ.cjs → chunk-7JBXDQTD.cjs} +5 -5
- package/dist/{chunk-L7WY5YFQ.cjs.map → chunk-7JBXDQTD.cjs.map} +1 -1
- package/dist/{chunk-NWNQ2PHT.js → chunk-AY3WN4FB.js} +8 -8
- package/dist/{chunk-NWNQ2PHT.js.map → chunk-AY3WN4FB.js.map} +1 -1
- package/dist/{chunk-CEUEXISZ.cjs → chunk-CB2A56Q7.cjs} +131 -50
- package/dist/chunk-CB2A56Q7.cjs.map +1 -0
- package/dist/chunk-CQ37UD4T.cjs +251 -0
- package/dist/chunk-CQ37UD4T.cjs.map +1 -0
- package/dist/{chunk-YRNEOBXA.js → chunk-EYLBSS4Y.js} +11 -2
- package/dist/chunk-EYLBSS4Y.js.map +1 -0
- package/dist/{chunk-AHGB5HH6.cjs → chunk-GHKMX5UT.cjs} +11 -2
- package/dist/chunk-GHKMX5UT.cjs.map +1 -0
- package/dist/chunk-GND3OBVE.js +241 -0
- package/dist/chunk-GND3OBVE.js.map +1 -0
- package/dist/{chunk-DZVF3IGB.cjs → chunk-GPGCBDBN.cjs} +59 -25
- package/dist/chunk-GPGCBDBN.cjs.map +1 -0
- package/dist/{chunk-LK5VXNMR.cjs → chunk-GW3YIPDE.cjs} +18 -18
- package/dist/{chunk-LK5VXNMR.cjs.map → chunk-GW3YIPDE.cjs.map} +1 -1
- package/dist/{chunk-BJLQKJZX.cjs → chunk-J2QIHZ3H.cjs} +2 -2
- package/dist/chunk-J2QIHZ3H.cjs.map +1 -0
- package/dist/{chunk-3XP6VO4G.js → chunk-KRM6DX47.js} +139 -45
- package/dist/chunk-KRM6DX47.js.map +1 -0
- package/dist/{chunk-PWLJDP3X.cjs → chunk-LHFC2BCM.cjs} +95 -27
- package/dist/chunk-LHFC2BCM.cjs.map +1 -0
- package/dist/{chunk-G7AGM2Y7.js → chunk-M3K6BF5M.js} +4 -4
- package/dist/{chunk-G7AGM2Y7.js.map → chunk-M3K6BF5M.js.map} +1 -1
- package/dist/{chunk-IXMT7X3E.cjs → chunk-M4UJBLVR.cjs} +17 -6
- package/dist/chunk-M4UJBLVR.cjs.map +1 -0
- package/dist/{chunk-YNDKAYW3.js → chunk-MCTNLUKO.js} +2 -2
- package/dist/{chunk-YNDKAYW3.js.map → chunk-MCTNLUKO.js.map} +1 -1
- package/dist/{chunk-ZDYNAWDZ.cjs → chunk-MPHX3Y42.cjs} +2 -2
- package/dist/{chunk-ZDYNAWDZ.cjs.map → chunk-MPHX3Y42.cjs.map} +1 -1
- package/dist/{chunk-AQIAQHJC.js → chunk-NDIKHMLJ.js} +4 -4
- package/dist/{chunk-AQIAQHJC.js.map → chunk-NDIKHMLJ.js.map} +1 -1
- package/dist/{chunk-LX2X4KC7.cjs → chunk-NILHU555.cjs} +7 -7
- package/dist/{chunk-LX2X4KC7.cjs.map → chunk-NILHU555.cjs.map} +1 -1
- package/dist/{chunk-JLDSCHY5.cjs → chunk-OKEN6NJL.cjs} +150 -61
- package/dist/chunk-OKEN6NJL.cjs.map +1 -0
- package/dist/{chunk-ORBU3XKH.js → chunk-PJMXHDY5.js} +16 -5
- package/dist/chunk-PJMXHDY5.js.map +1 -0
- package/dist/{chunk-QKW2GGTV.js → chunk-U2BX2RWY.js} +84 -16
- package/dist/chunk-U2BX2RWY.js.map +1 -0
- package/dist/{chunk-WBLYD5QA.js → chunk-U4KTOH7C.js} +2 -2
- package/dist/chunk-U4KTOH7C.js.map +1 -0
- package/dist/{chunk-FEOFX4QW.js → chunk-U6SEII5V.js} +3 -3
- package/dist/{chunk-FEOFX4QW.js.map → chunk-U6SEII5V.js.map} +1 -1
- package/dist/{chunk-6S2CKZ4H.cjs → chunk-W2P5RILE.cjs} +145 -51
- package/dist/chunk-W2P5RILE.cjs.map +1 -0
- package/dist/{chunk-AG7MWZYZ.js → chunk-W6TWQY6S.js} +144 -55
- package/dist/chunk-W6TWQY6S.js.map +1 -0
- package/dist/{chunk-HREA5SMB.cjs → chunk-WGHMWBEJ.cjs} +116 -47
- package/dist/chunk-WGHMWBEJ.cjs.map +1 -0
- package/dist/{chunk-5GI53C6T.js → chunk-WNDNQ6NR.js} +109 -40
- package/dist/chunk-WNDNQ6NR.js.map +1 -0
- package/dist/{chunk-RT2PP4SO.cjs → chunk-WXESKL75.cjs} +30 -30
- package/dist/{chunk-RT2PP4SO.cjs.map → chunk-WXESKL75.cjs.map} +1 -1
- package/dist/{chunk-BY4UQH2Y.js → chunk-X4YWXZ4J.js} +3 -3
- package/dist/{chunk-BY4UQH2Y.js.map → chunk-X4YWXZ4J.js.map} +1 -1
- package/dist/{chunk-HI6BUUIJ.js → chunk-XB2AHEU5.js} +48 -14
- package/dist/chunk-XB2AHEU5.js.map +1 -0
- package/dist/{chunk-IQ55QNBI.js → chunk-XM5Q2ZCH.js} +125 -44
- package/dist/chunk-XM5Q2ZCH.js.map +1 -0
- package/dist/{chunk-UHIOVOFK.cjs → chunk-XZCMUDFP.cjs} +12 -12
- package/dist/{chunk-UHIOVOFK.cjs.map → chunk-XZCMUDFP.cjs.map} +1 -1
- package/dist/{chunk-TRMWBJWT.cjs → chunk-Y4IDF5UO.cjs} +8 -8
- package/dist/{chunk-TRMWBJWT.cjs.map → chunk-Y4IDF5UO.cjs.map} +1 -1
- package/dist/{chunk-3FBBP5QR.cjs → chunk-YWJ5XJDU.cjs} +6 -6
- package/dist/{chunk-3FBBP5QR.cjs.map → chunk-YWJ5XJDU.cjs.map} +1 -1
- package/dist/{chunk-BGA2IVQY.js → chunk-ZDDBWUUA.js} +6 -6
- package/dist/{chunk-BGA2IVQY.js.map → chunk-ZDDBWUUA.js.map} +1 -1
- package/dist/components/ActivityPreview.cjs +17 -17
- package/dist/components/ActivityPreview.d.cts +2 -2
- package/dist/components/ActivityPreview.d.ts +2 -2
- package/dist/components/ActivityPreview.js +16 -16
- package/dist/components/ActivitySequence.cjs +18 -18
- package/dist/components/ActivitySequence.d.cts +22 -4
- package/dist/components/ActivitySequence.d.ts +22 -4
- package/dist/components/ActivitySequence.js +17 -17
- package/dist/components/Dictation.cjs +10 -10
- package/dist/components/Dictation.d.cts +1 -1
- package/dist/components/Dictation.d.ts +1 -1
- package/dist/components/Dictation.js +9 -9
- package/dist/components/FillInTheBlanks.cjs +10 -10
- package/dist/components/FillInTheBlanks.d.cts +1 -1
- package/dist/components/FillInTheBlanks.d.ts +1 -1
- package/dist/components/FillInTheBlanks.js +9 -9
- package/dist/components/GapSelect.cjs +10 -10
- package/dist/components/GapSelect.d.cts +1 -1
- package/dist/components/GapSelect.d.ts +1 -1
- package/dist/components/GapSelect.js +9 -9
- package/dist/components/InteractiveVideo.cjs +17 -17
- package/dist/components/InteractiveVideo.d.cts +19 -3
- package/dist/components/InteractiveVideo.d.ts +19 -3
- package/dist/components/InteractiveVideo.js +15 -15
- package/dist/components/MultipleChoice.cjs +10 -10
- package/dist/components/MultipleChoice.d.cts +1 -1
- package/dist/components/MultipleChoice.d.ts +1 -1
- package/dist/components/MultipleChoice.js +9 -9
- package/dist/components/PronunciationFeedback.cjs +4 -4
- package/dist/components/PronunciationFeedback.d.cts +1 -1
- package/dist/components/PronunciationFeedback.d.ts +1 -1
- package/dist/components/PronunciationFeedback.js +3 -3
- package/dist/components/ReadAloud.cjs +8 -8
- package/dist/components/ReadAloud.d.cts +2 -2
- package/dist/components/ReadAloud.d.ts +2 -2
- package/dist/components/ReadAloud.js +7 -7
- package/dist/components/StimulusPanel.cjs +4 -4
- package/dist/components/StimulusPanel.d.cts +1 -1
- package/dist/components/StimulusPanel.d.ts +1 -1
- package/dist/components/StimulusPanel.js +3 -3
- package/dist/components/WrittenResponse.cjs +7 -7
- package/dist/components/WrittenResponse.d.cts +3 -3
- package/dist/components/WrittenResponse.d.ts +3 -3
- package/dist/components/WrittenResponse.js +6 -6
- package/dist/i18n/LkIntlProvider.cjs +7 -7
- package/dist/i18n/LkIntlProvider.d.cts +2 -2
- package/dist/i18n/LkIntlProvider.d.ts +2 -2
- package/dist/i18n/LkIntlProvider.js +1 -1
- package/dist/index.cjs +41 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +19 -19
- package/dist/index.js.map +1 -1
- package/dist/theme/skin.css +55 -0
- package/dist/{types-C-GMCJ0g.d.ts → types-7lGlo0zE.d.ts} +48 -1
- package/dist/{types-B_PCcqO-.d.cts → types-Botf4Wqj.d.cts} +48 -1
- package/package.json +3 -3
- package/dist/chunk-3XP6VO4G.js.map +0 -1
- package/dist/chunk-5GI53C6T.js.map +0 -1
- package/dist/chunk-6S2CKZ4H.cjs.map +0 -1
- package/dist/chunk-AG7MWZYZ.js.map +0 -1
- package/dist/chunk-AHGB5HH6.cjs.map +0 -1
- package/dist/chunk-BJLQKJZX.cjs.map +0 -1
- package/dist/chunk-CEUEXISZ.cjs.map +0 -1
- package/dist/chunk-DZVF3IGB.cjs.map +0 -1
- package/dist/chunk-HI6BUUIJ.js.map +0 -1
- package/dist/chunk-HREA5SMB.cjs.map +0 -1
- package/dist/chunk-IQ55QNBI.js.map +0 -1
- package/dist/chunk-IXMT7X3E.cjs.map +0 -1
- package/dist/chunk-JLDSCHY5.cjs.map +0 -1
- package/dist/chunk-NOC2M6LU.cjs +0 -118
- package/dist/chunk-NOC2M6LU.cjs.map +0 -1
- package/dist/chunk-ORBU3XKH.js.map +0 -1
- package/dist/chunk-PWLJDP3X.cjs.map +0 -1
- package/dist/chunk-QKW2GGTV.js.map +0 -1
- package/dist/chunk-RUS42NMN.js +0 -115
- package/dist/chunk-RUS42NMN.js.map +0 -1
- package/dist/chunk-WBLYD5QA.js.map +0 -1
- package/dist/chunk-YRNEOBXA.js.map +0 -1
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
var
|
|
4
|
+
var chunkCQ37UD4T_cjs = require('./chunk-CQ37UD4T.cjs');
|
|
5
|
+
var chunkMPHX3Y42_cjs = require('./chunk-MPHX3Y42.cjs');
|
|
6
|
+
var chunkM4UJBLVR_cjs = require('./chunk-M4UJBLVR.cjs');
|
|
7
|
+
var chunkJ2QIHZ3H_cjs = require('./chunk-J2QIHZ3H.cjs');
|
|
8
|
+
var chunk6SNNMVBV_cjs = require('./chunk-6SNNMVBV.cjs');
|
|
8
9
|
var chunkEANGKAKG_cjs = require('./chunk-EANGKAKG.cjs');
|
|
9
|
-
var
|
|
10
|
+
var chunk7JBXDQTD_cjs = require('./chunk-7JBXDQTD.cjs');
|
|
10
11
|
var chunkXOR4MPEN_cjs = require('./chunk-XOR4MPEN.cjs');
|
|
11
|
-
var
|
|
12
|
+
var chunkGHKMX5UT_cjs = require('./chunk-GHKMX5UT.cjs');
|
|
12
13
|
var lkCore = require('@intellectif/lk-core');
|
|
13
14
|
var react = require('react');
|
|
14
15
|
var jsxRuntime = require('react/jsx-runtime');
|
|
@@ -73,13 +74,15 @@ function GapSelect({
|
|
|
73
74
|
disabled,
|
|
74
75
|
shuffleSeed,
|
|
75
76
|
ai: aiProp,
|
|
76
|
-
delivery
|
|
77
|
+
delivery,
|
|
78
|
+
scoring
|
|
77
79
|
}) {
|
|
78
80
|
const isExam = renderMode === "exam";
|
|
79
81
|
const isReview = renderMode === "review";
|
|
80
|
-
const s =
|
|
81
|
-
const ai =
|
|
82
|
-
const policy =
|
|
82
|
+
const s = chunkGHKMX5UT_cjs.useLkStrings(strings);
|
|
83
|
+
const ai = chunkMPHX3Y42_cjs.useLearnerAi(aiProp);
|
|
84
|
+
const policy = chunkM4UJBLVR_cjs.useDeliveryPolicy(delivery);
|
|
85
|
+
const scoringPolicy = chunkCQ37UD4T_cjs.useItemScoringPolicy(scoring);
|
|
83
86
|
const devError = react.useMemo(() => {
|
|
84
87
|
if (!chunkEANGKAKG_cjs.isDevelopment()) {
|
|
85
88
|
return null;
|
|
@@ -98,13 +101,25 @@ function GapSelect({
|
|
|
98
101
|
const { state, start, complete, getTimeSpent, reset } = chunkXOR4MPEN_cjs.useActivityState(
|
|
99
102
|
defaultSubmitted === true ? "completed" : "idle"
|
|
100
103
|
);
|
|
104
|
+
const tries = chunkCQ37UD4T_cjs.useTries({
|
|
105
|
+
policy: scoringPolicy,
|
|
106
|
+
graded: renderMode === "practice" && policy.feedback,
|
|
107
|
+
submitted: state === "completed",
|
|
108
|
+
disabled: disabled === true
|
|
109
|
+
});
|
|
110
|
+
const resetTries = tries.reset;
|
|
101
111
|
const isControlled = value !== void 0;
|
|
102
112
|
const [internalSelections, setInternalSelections] = react.useState(
|
|
103
113
|
() => selectionsOf(defaultValue)
|
|
104
114
|
);
|
|
105
115
|
const [result, setResult] = react.useState(null);
|
|
106
116
|
const [summary, setSummary] = react.useState(null);
|
|
117
|
+
const [overall, setOverall] = react.useState(null);
|
|
107
118
|
const [feedbackHidden, setFeedbackHidden] = react.useState(false);
|
|
119
|
+
const seedHintsRef = react.useRef(chunkCQ37UD4T_cjs.hintsOf(value ?? defaultValue));
|
|
120
|
+
const formRef = react.useRef(null);
|
|
121
|
+
const feedbackRef = react.useRef(null);
|
|
122
|
+
const focusAfterRef = react.useRef(null);
|
|
108
123
|
const selections = isControlled ? selectionsOf(value) : internalSelections;
|
|
109
124
|
const defaultValueRef = react.useRef(defaultValue);
|
|
110
125
|
react.useEffect(() => {
|
|
@@ -123,9 +138,12 @@ function GapSelect({
|
|
|
123
138
|
setInternalSelections(selectionsOf(defaultValueRef.current));
|
|
124
139
|
setResult(null);
|
|
125
140
|
setSummary(null);
|
|
141
|
+
setOverall(null);
|
|
126
142
|
setFeedbackHidden(false);
|
|
143
|
+
seedHintsRef.current = chunkCQ37UD4T_cjs.hintsOf(defaultValueRef.current);
|
|
144
|
+
resetTries();
|
|
127
145
|
reset(defaultSubmittedRef.current === true ? "completed" : "idle");
|
|
128
|
-
}, [data, reset]);
|
|
146
|
+
}, [data, reset, resetTries]);
|
|
129
147
|
const segments = react.useMemo(() => parsePassage(data.passage), [data.passage]);
|
|
130
148
|
const gapById = react.useMemo(() => new Map(data.gaps.map((gap) => [gap.id, gap])), [data.gaps]);
|
|
131
149
|
const sessionIdRef = react.useRef(null);
|
|
@@ -141,6 +159,47 @@ function GapSelect({
|
|
|
141
159
|
}
|
|
142
160
|
return map;
|
|
143
161
|
}, [data, shuffleSeed]);
|
|
162
|
+
const answered = state === "completed";
|
|
163
|
+
const aiHints = chunkCQ37UD4T_cjs.useComponentAiHints({
|
|
164
|
+
ai,
|
|
165
|
+
data,
|
|
166
|
+
renderMode,
|
|
167
|
+
submitted: answered,
|
|
168
|
+
disabled: disabled === true,
|
|
169
|
+
response: { type: "gap-select", selections },
|
|
170
|
+
locale,
|
|
171
|
+
onInteraction,
|
|
172
|
+
strings: s,
|
|
173
|
+
delivery: policy
|
|
174
|
+
});
|
|
175
|
+
const hintsRevealed = renderMode === "practice" ? seedHintsRef.current + aiHints.used : 0;
|
|
176
|
+
const responseOf = (next) => ({
|
|
177
|
+
type: "gap-select",
|
|
178
|
+
selections: next,
|
|
179
|
+
...hintsRevealed > 0 ? { hintsRevealed } : {}
|
|
180
|
+
});
|
|
181
|
+
const hintsSeenRef = react.useRef(aiHints.used);
|
|
182
|
+
react.useEffect(() => {
|
|
183
|
+
if (hintsSeenRef.current === aiHints.used) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
hintsSeenRef.current = aiHints.used;
|
|
187
|
+
if (aiHints.used > 0) {
|
|
188
|
+
onChange?.(responseOf(selections));
|
|
189
|
+
}
|
|
190
|
+
}, [aiHints.used]);
|
|
191
|
+
react.useEffect(() => {
|
|
192
|
+
const target = focusAfterRef.current;
|
|
193
|
+
if (target === null) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
focusAfterRef.current = null;
|
|
197
|
+
if (target === "answer") {
|
|
198
|
+
formRef.current?.querySelector("select:not(:disabled)")?.focus();
|
|
199
|
+
} else {
|
|
200
|
+
feedbackRef.current?.focus();
|
|
201
|
+
}
|
|
202
|
+
});
|
|
144
203
|
if (devError) {
|
|
145
204
|
throw devError;
|
|
146
205
|
}
|
|
@@ -167,7 +226,7 @@ function GapSelect({
|
|
|
167
226
|
if (!isControlled) {
|
|
168
227
|
setInternalSelections(next);
|
|
169
228
|
}
|
|
170
|
-
onChange?.(
|
|
229
|
+
onChange?.(responseOf(next));
|
|
171
230
|
fireInteraction("gap-selected", { gapId, choiceId });
|
|
172
231
|
};
|
|
173
232
|
const handleSubmit = (event) => {
|
|
@@ -175,7 +234,7 @@ function GapSelect({
|
|
|
175
234
|
if (inactive) {
|
|
176
235
|
return;
|
|
177
236
|
}
|
|
178
|
-
const response =
|
|
237
|
+
const response = responseOf(selections);
|
|
179
238
|
onSubmit?.(response);
|
|
180
239
|
if (isExam) {
|
|
181
240
|
complete();
|
|
@@ -183,9 +242,18 @@ function GapSelect({
|
|
|
183
242
|
fireInteraction("submitted", { selections });
|
|
184
243
|
return;
|
|
185
244
|
}
|
|
186
|
-
const
|
|
245
|
+
const answer = lkCore.score("gap-select", data, response);
|
|
187
246
|
complete();
|
|
188
247
|
const timeSpent = getTimeSpent();
|
|
248
|
+
const counted = tries.record({
|
|
249
|
+
score: answer.score,
|
|
250
|
+
maxScore: answer.maxScore,
|
|
251
|
+
hintsRevealed
|
|
252
|
+
});
|
|
253
|
+
const mine = counted.tries[counted.tries.length - 1];
|
|
254
|
+
const costed = mine === void 0 ? answer.score : mine.scored;
|
|
255
|
+
const scoringResult = costed === answer.score ? answer : { ...answer, score: costed, passed: lkCore.computePassThreshold(data, costed) };
|
|
256
|
+
const passed = counted.score === scoringResult.score ? scoringResult.passed : lkCore.computePassThreshold(data, counted.score);
|
|
189
257
|
const xapiStatement = lkCore.xAPIBuilder.buildAnsweredStatement({
|
|
190
258
|
actor: chunkEANGKAKG_cjs.ANONYMOUS_ACTOR,
|
|
191
259
|
object: {
|
|
@@ -197,23 +265,29 @@ function GapSelect({
|
|
|
197
265
|
timeSpentMs: timeSpent,
|
|
198
266
|
response: JSON.stringify(selections)
|
|
199
267
|
});
|
|
200
|
-
setResult(
|
|
201
|
-
onComplete?.(
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const overall = scoringResult.feedback;
|
|
268
|
+
setResult(answer);
|
|
269
|
+
onComplete?.(
|
|
270
|
+
chunkJ2QIHZ3H_cjs.stampTake(
|
|
271
|
+
{ score: counted.score, maxScore: counted.maxScore, passed, timeSpent, xapiStatement },
|
|
272
|
+
chunkJ2QIHZ3H_cjs.mintTake()
|
|
273
|
+
)
|
|
274
|
+
);
|
|
275
|
+
setOverall(answer.feedback);
|
|
209
276
|
setSummary(
|
|
210
|
-
`${s.answerSubmitted} ${s.scoreAnnouncement(
|
|
211
|
-
Math.round(scoringResult.score * 100),
|
|
212
|
-
scoringResult.passed
|
|
213
|
-
)}${overall ? ` ${overall}` : ""}`
|
|
277
|
+
`${s.answerSubmitted} ${s.scoreAnnouncement(Math.round(counted.score * 100), passed)}`
|
|
214
278
|
);
|
|
215
279
|
fireInteraction("submitted", { selections, score: scoringResult.score });
|
|
216
280
|
};
|
|
281
|
+
const retry = () => {
|
|
282
|
+
reset("idle");
|
|
283
|
+
setSummary(null);
|
|
284
|
+
setOverall(null);
|
|
285
|
+
focusAfterRef.current = "answer";
|
|
286
|
+
};
|
|
287
|
+
const closeTries = () => {
|
|
288
|
+
tries.close();
|
|
289
|
+
focusAfterRef.current = "feedback";
|
|
290
|
+
};
|
|
217
291
|
const correctByGap = react.useMemo(() => {
|
|
218
292
|
if (isExam) {
|
|
219
293
|
return /* @__PURE__ */ new Map();
|
|
@@ -240,9 +314,12 @@ function GapSelect({
|
|
|
240
314
|
return s.noGradeAvailable;
|
|
241
315
|
}, [isReview, outcome, s]);
|
|
242
316
|
const anyFeedback = data.gaps.some((gap) => gap.feedback !== void 0);
|
|
317
|
+
const extra = !isReview && policy.feedback && submitted && tries.counted !== null ? chunkCQ37UD4T_cjs.triesSummary(s, scoringPolicy, tries.counted, tries.open) : "";
|
|
318
|
+
const announced = summary === null ? null : `${summary}${extra ? ` ${extra}` : ""}${overall ? ` ${overall}` : ""}`;
|
|
243
319
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
244
320
|
"form",
|
|
245
321
|
{
|
|
322
|
+
ref: formRef,
|
|
246
323
|
className: "lk-gs",
|
|
247
324
|
"aria-label": data.title,
|
|
248
325
|
lang: locale,
|
|
@@ -251,7 +328,7 @@ function GapSelect({
|
|
|
251
328
|
onSubmit: handleSubmit,
|
|
252
329
|
children: [
|
|
253
330
|
data.media ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
254
|
-
|
|
331
|
+
chunk7JBXDQTD_cjs.ActivityMedia,
|
|
255
332
|
{
|
|
256
333
|
media: data.media,
|
|
257
334
|
renderMode,
|
|
@@ -302,21 +379,8 @@ function GapSelect({
|
|
|
302
379
|
) : null
|
|
303
380
|
] }, seg.id);
|
|
304
381
|
}) }),
|
|
305
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
306
|
-
|
|
307
|
-
{
|
|
308
|
-
ai,
|
|
309
|
-
data,
|
|
310
|
-
renderMode,
|
|
311
|
-
submitted,
|
|
312
|
-
disabled: disabled === true,
|
|
313
|
-
response: { type: "gap-select", selections },
|
|
314
|
-
locale,
|
|
315
|
-
onInteraction,
|
|
316
|
-
strings: s,
|
|
317
|
-
delivery: policy
|
|
318
|
-
}
|
|
319
|
-
),
|
|
382
|
+
aiHints.offered && renderMode === "practice" ? /* @__PURE__ */ jsxRuntime.jsx(chunkCQ37UD4T_cjs.HintCost, { policy: scoringPolicy, strings: s }) : null,
|
|
383
|
+
/* @__PURE__ */ jsxRuntime.jsx(chunkCQ37UD4T_cjs.AiHints, { help: aiHints, ai, strings: s }),
|
|
320
384
|
isReview ? null : /* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", disabled: inactive, children: isExam ? s.submitAnswers : s.checkAnswers })
|
|
321
385
|
] }),
|
|
322
386
|
marking && anyFeedback ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -329,9 +393,26 @@ function GapSelect({
|
|
|
329
393
|
children: feedbackHidden ? s.showFeedback : s.hideFeedback
|
|
330
394
|
}
|
|
331
395
|
) : null,
|
|
332
|
-
/* @__PURE__ */ jsxRuntime.jsx(chunkIXMT7X3E_cjs.FeedbackRegion, { id: `${data.id}-feedback`, children: isReview ? policy.feedback || !chunkIXMT7X3E_cjs.outcomeShowsMarks(outcome) ? reviewSummary : null : policy.feedback || summary === null ? summary : s.answerSubmitted }),
|
|
333
396
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
334
|
-
|
|
397
|
+
chunkM4UJBLVR_cjs.FeedbackRegion,
|
|
398
|
+
{
|
|
399
|
+
id: `${data.id}-feedback`,
|
|
400
|
+
...scoringPolicy.retries > 0 ? { ref: feedbackRef } : {},
|
|
401
|
+
children: isReview ? policy.feedback || !chunkM4UJBLVR_cjs.outcomeShowsMarks(outcome) ? reviewSummary : null : policy.feedback || summary === null ? announced : s.answerSubmitted
|
|
402
|
+
}
|
|
403
|
+
),
|
|
404
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
405
|
+
chunkCQ37UD4T_cjs.TryActions,
|
|
406
|
+
{
|
|
407
|
+
tries,
|
|
408
|
+
solutions: false,
|
|
409
|
+
strings: s,
|
|
410
|
+
onRetry: retry,
|
|
411
|
+
onClose: closeTries
|
|
412
|
+
}
|
|
413
|
+
),
|
|
414
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
415
|
+
chunkCQ37UD4T_cjs.AiExplanation,
|
|
335
416
|
{
|
|
336
417
|
ai,
|
|
337
418
|
data,
|
|
@@ -342,7 +423,7 @@ function GapSelect({
|
|
|
342
423
|
locale,
|
|
343
424
|
onInteraction,
|
|
344
425
|
strings: s,
|
|
345
|
-
delivery: policy
|
|
426
|
+
delivery: tries.open ? { ...policy, solutions: false } : policy
|
|
346
427
|
}
|
|
347
428
|
)
|
|
348
429
|
]
|
|
@@ -350,10 +431,10 @@ function GapSelect({
|
|
|
350
431
|
);
|
|
351
432
|
}
|
|
352
433
|
function GapSelect2(props) {
|
|
353
|
-
const strings =
|
|
354
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
434
|
+
const strings = chunkGHKMX5UT_cjs.useLkStrings(props.strings);
|
|
435
|
+
return /* @__PURE__ */ jsxRuntime.jsx(chunk6SNNMVBV_cjs.ActivityErrorBoundary, { activityTitle: props.data?.title, strings, children: /* @__PURE__ */ jsxRuntime.jsx(GapSelect, { ...props }) });
|
|
355
436
|
}
|
|
356
437
|
|
|
357
438
|
exports.GapSelect = GapSelect2;
|
|
358
|
-
//# sourceMappingURL=chunk-
|
|
359
|
-
//# sourceMappingURL=chunk-
|
|
439
|
+
//# sourceMappingURL=chunk-CB2A56Q7.cjs.map
|
|
440
|
+
//# sourceMappingURL=chunk-CB2A56Q7.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/GapSelect/GapSelect.tsx","../src/components/GapSelect/index.tsx"],"names":["useLkStrings","useLearnerAi","useDeliveryPolicy","useItemScoringPolicy","useMemo","isDevelopment","assertRedacted","result","validateActivity","ActivitySchemaError","useActivityState","useTries","useState","useRef","hintsOf","useEffect","randomSessionId","seededShuffle","useComponentAiHints","score","computePassThreshold","xAPIBuilder","ANONYMOUS_ACTOR","objectIdFor","xapiDefinitionFor","stampTake","mintTake","triesSummary","jsxs","jsx","ActivityMedia","HintCost","AiHints","FeedbackRegion","outcomeShowsMarks","TryActions","AiExplanation","GapSelect","ActivityErrorBoundary"],"mappings":"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","file":"chunk-CB2A56Q7.cjs","sourcesContent":["'use client';\n\nimport {\n ActivitySchemaError,\n assertRedacted,\n computePassThreshold,\n type GapSelectBank,\n type GapSelectChoice,\n type GapSelectData,\n type GapSelectGap,\n type GapSelectLearnerResponse,\n type LearnerResponse,\n type ScoringDetail,\n type ScoringResult,\n score,\n seededShuffle,\n validateActivity,\n xAPIBuilder,\n xapiDefinitionFor,\n} from '@intellectif/lk-core';\nimport { type CSSProperties, useEffect, useMemo, useRef, useState } from 'react';\nimport { useLearnerAi } from '../../ai/LkAiProvider.js';\nimport { useActivityState } from '../../hooks/useActivityState.js';\nimport { useLkStrings } from '../../i18n/LkIntlProvider.js';\nimport { ANONYMOUS_ACTOR, isDevelopment, objectIdFor, randomSessionId } from '../_internal.js';\nimport { ActivityMedia } from '../shared/ActivityMedia.js';\nimport { AiExplanation, AiHints, useComponentAiHints } from '../shared/AiHelp.js';\nimport { outcomeShowsMarks, useDeliveryPolicy } from '../shared/delivery.js';\nimport { FeedbackRegion } from '../shared/FeedbackRegion.js';\nimport { mintTake, stampTake } from '../shared/sequence-slot.js';\nimport {\n HintCost,\n hintsOf,\n TryActions,\n triesSummary,\n useItemScoringPolicy,\n useTries,\n} from '../shared/tries.js';\nimport type { ActivityProps } from '../types.js';\n\nexport interface GapSelectProps extends ActivityProps<GapSelectData> {\n /**\n * Seeds the per-gap choice shuffle when `data.shuffleChoices` is set. Pass\n * the attempt id, exactly as on `<MultipleChoice>`: an order nobody can\n * reproduce cannot be reconciled with the attempt a server recorded. Without\n * one, a random per-mount seed is used — stable within the mount, and\n * deliberately not reproducible.\n */\n shuffleSeed?: string;\n}\n\nconst PLACEHOLDER = /\\{\\{\\s*([^{}]+?)\\s*\\}\\}/g;\n\ntype Segment = { kind: 'text'; value: string } | { kind: 'gap'; id: string; ordinal: number };\n\n/** Splits a passage into literal text and its `{{id}}` gaps, in order. */\nfunction parsePassage(passage: string): Segment[] {\n const segments: Segment[] = [];\n const re = new RegExp(PLACEHOLDER);\n let last = 0;\n let ordinal = 0;\n let match = re.exec(passage);\n while (match !== null) {\n if (match.index > last) {\n segments.push({ kind: 'text', value: passage.slice(last, match.index) });\n }\n ordinal += 1;\n segments.push({ kind: 'gap', id: match[1] as string, ordinal });\n last = match.index + match[0].length;\n match = re.exec(passage);\n }\n if (last < passage.length) {\n segments.push({ kind: 'text', value: passage.slice(last) });\n }\n return segments;\n}\n\nconst NO_SELECTIONS: Record<string, string> = {};\n\n/**\n * The selections out of a `LearnerResponse`. A response of another activity\n * type reads as \"nothing selected\" rather than throwing — a controlled host\n * that has not yet swapped its state on an activity change would otherwise\n * crash the attempt.\n */\nfunction selectionsOf(response: LearnerResponse | undefined): Record<string, string> {\n return response !== undefined && response.type === 'gap-select'\n ? response.selections\n : NO_SELECTIONS;\n}\n\n/** Per-gap correctness keyed by gap id, preferring the unambiguous `outcome`. */\nfunction correctnessByItem(details: readonly ScoringDetail[]): Map<string, boolean> {\n const map = new Map<string, boolean>();\n for (const detail of details) {\n map.set(\n detail.itemId,\n detail.outcome !== undefined ? detail.outcome === 'correct' : detail.correct,\n );\n }\n return map;\n}\n\n/**\n * The choices a gap offers: its own list, or the bank it names.\n *\n * Both survive redaction — they are what the learner picks from — so this is\n * the one resolution that works identically on full and redacted data.\n */\nfunction choicesFor(\n banks: readonly GapSelectBank[] | undefined,\n gap: GapSelectGap,\n): readonly GapSelectChoice[] {\n if (gap.choices !== undefined) {\n return gap.choices;\n }\n return banks?.find((bank) => bank.id === gap.bankId)?.choices ?? [];\n}\n\nexport function GapSelect({\n data,\n onComplete,\n onSubmit,\n onChange,\n onInteraction,\n value,\n defaultValue,\n defaultSubmitted,\n renderMode = 'practice',\n outcome,\n mediaBudget,\n mediaStrings,\n strings,\n theme,\n locale,\n disabled,\n shuffleSeed,\n ai: aiProp,\n delivery,\n scoring,\n}: GapSelectProps) {\n const isExam = renderMode === 'exam';\n const isReview = renderMode === 'review';\n const s = useLkStrings(strings);\n const ai = useLearnerAi(aiProp);\n const policy = useDeliveryPolicy(delivery);\n const scoringPolicy = useItemScoringPolicy(scoring);\n\n const devError = useMemo(() => {\n if (!isDevelopment()) {\n return null;\n }\n if (data.redacted === true) {\n try {\n assertRedacted(data);\n return null;\n } catch (error) {\n return error instanceof Error ? error : new Error(String(error));\n }\n }\n const result = validateActivity('gap-select', data);\n return result.success ? null : new ActivitySchemaError('gap-select', result.errors);\n }, [data]);\n\n const { state, start, complete, getTimeSpent, reset } = useActivityState(\n defaultSubmitted === true ? 'completed' : 'idle',\n );\n // Before anything that resets them: a new question starts with no tries.\n const tries = useTries({\n policy: scoringPolicy,\n graded: renderMode === 'practice' && policy.feedback,\n submitted: state === 'completed',\n disabled: disabled === true,\n });\n const resetTries = tries.reset;\n const isControlled = value !== undefined;\n const [internalSelections, setInternalSelections] = useState<Record<string, string>>(() =>\n selectionsOf(defaultValue),\n );\n const [result, setResult] = useState<ScoringResult | null>(null);\n const [summary, setSummary] = useState<string | null>(null);\n const [overall, setOverall] = useState<string | null>(null);\n const [feedbackHidden, setFeedbackHidden] = useState(false);\n // Hints the learner had been shown before this mount: a restored answer's\n // count stands, and the hints shown here add to it.\n const seedHintsRef = useRef(hintsOf(value ?? defaultValue));\n const formRef = useRef<HTMLFormElement>(null);\n const feedbackRef = useRef<HTMLDivElement>(null);\n const focusAfterRef = useRef<'answer' | 'feedback' | null>(null);\n\n const selections = isControlled ? selectionsOf(value) : internalSelections;\n\n const defaultValueRef = useRef(defaultValue);\n useEffect(() => {\n defaultValueRef.current = defaultValue;\n }, [defaultValue]);\n\n const defaultSubmittedRef = useRef(defaultSubmitted);\n useEffect(() => {\n defaultSubmittedRef.current = defaultSubmitted;\n }, [defaultSubmitted]);\n\n // Identity-guarded so the mount run is a no-op: without it this fires after\n // the first paint and undoes every seed it was just given (Req 3.7).\n const lastDataRef = useRef(data);\n useEffect(() => {\n if (lastDataRef.current === data) {\n return;\n }\n lastDataRef.current = data;\n setInternalSelections(selectionsOf(defaultValueRef.current));\n setResult(null);\n setSummary(null);\n setOverall(null);\n setFeedbackHidden(false);\n seedHintsRef.current = hintsOf(defaultValueRef.current);\n resetTries();\n reset(defaultSubmittedRef.current === true ? 'completed' : 'idle');\n }, [data, reset, resetTries]);\n\n const segments = useMemo(() => parsePassage(data.passage), [data.passage]);\n const gapById = useMemo(() => new Map(data.gaps.map((gap) => [gap.id, gap])), [data.gaps]);\n\n const sessionIdRef = useRef<string | null>(null);\n /**\n * The choices each gap shows, in the order it shows them.\n *\n * Seeded PER GAP, not once for the activity: two gaps drawing on the same\n * word bank must not be dealt the same order, or the bank's distractors line\n * up column-wise and the second gap becomes easier than the first.\n */\n const choicesByGap = useMemo(() => {\n const map = new Map<string, readonly GapSelectChoice[]>();\n // biome-ignore lint/suspicious/noAssignInExpressions: sanctioned lazy ref initialization\n const seedSource = shuffleSeed ?? (sessionIdRef.current ??= randomSessionId());\n for (const gap of data.gaps) {\n const choices = choicesFor(data.banks, gap);\n map.set(\n gap.id,\n data.shuffleChoices === true\n ? seededShuffle(choices, `${seedSource}:${data.id}:${gap.id}`)\n : choices,\n );\n }\n return map;\n }, [data, shuffleSeed]);\n\n // Hints are a hook, so they are read here, before the throws below.\n const answered = state === 'completed';\n const aiHints = useComponentAiHints({\n ai,\n data,\n renderMode,\n submitted: answered,\n disabled: disabled === true,\n response: { type: 'gap-select', selections },\n locale,\n onInteraction,\n strings: s,\n delivery: policy,\n });\n // Every hint the learner has been shown on this question, counted from its\n // start: what a scoring policy charges for. Hints exist only in practice.\n const hintsRevealed = renderMode === 'practice' ? seedHintsRef.current + aiHints.used : 0;\n const responseOf = (next: Record<string, string>): GapSelectLearnerResponse => ({\n type: 'gap-select',\n selections: next,\n ...(hintsRevealed > 0 ? { hintsRevealed } : {}),\n });\n // A hint shown is part of the answer's record: say so as it happens.\n const hintsSeenRef = useRef(aiHints.used);\n // biome-ignore lint/correctness/useExhaustiveDependencies: a new hint is the trigger, and the response it reports is this render's\n useEffect(() => {\n if (hintsSeenRef.current === aiHints.used) {\n return;\n }\n hintsSeenRef.current = aiHints.used;\n if (aiHints.used > 0) {\n onChange?.(responseOf(selections));\n }\n }, [aiHints.used]);\n useEffect(() => {\n const target = focusAfterRef.current;\n if (target === null) {\n return;\n }\n focusAfterRef.current = null;\n if (target === 'answer') {\n formRef.current?.querySelector<HTMLElement>('select:not(:disabled)')?.focus();\n } else {\n feedbackRef.current?.focus();\n }\n });\n\n // All hooks are called before these throws, so hook order stays stable.\n if (devError) {\n throw devError;\n }\n // Practice grades locally, and a redacted projection has no answer key to\n // grade against — `score()` would throw RedactedScoringError from the submit\n // handler, where no error boundary can reach it and after the learner has\n // answered. Fail at render instead, in production too, exactly as every other\n // built-in does.\n if (data.redacted === true && renderMode === 'practice') {\n throw new Error(\n `Gap Select \"${data.id}\" received redacted activity data in renderMode \"practice\", ` +\n 'which grades locally and has no answer key to grade against. ' +\n 'Render redacted data with renderMode=\"exam\" (server grades) or \"review\" (pass `outcome`).',\n );\n }\n\n const submitted = state === 'completed';\n const inactive = disabled === true || submitted || isReview;\n\n /**\n * Whether answer-key-derived information may be shown. Never in `exam`; in\n * `practice` only once this component has scored; in `review` only once the\n * caller supplies a SCORED outcome.\n */\n const revealing = isReview ? outcome?.status === 'scored' : !isExam && submitted;\n // What the delivery policy lets that reveal show: right and wrong, and the\n // author's feedback. A gap select never writes in the right choice, so\n // `solutions` has nothing here to hide.\n const marking = revealing && policy.feedback;\n\n const fireInteraction = (\n type: 'gap-selected' | 'submitted',\n payload: Record<string, unknown>,\n ): void => {\n onInteraction?.({ type, activityId: data.id, timestamp: Date.now(), payload });\n };\n\n const handleSelect = (gapId: string, choiceId: string): void => {\n if (inactive) {\n return;\n }\n if (state === 'idle') {\n start();\n }\n const next = { ...selections, [gapId]: choiceId };\n if (!isControlled) {\n setInternalSelections(next);\n }\n onChange?.(responseOf(next));\n fireInteraction('gap-selected', { gapId, choiceId });\n };\n\n const handleSubmit = (event: React.FormEvent): void => {\n event.preventDefault();\n if (inactive) {\n return;\n }\n const response = responseOf(selections);\n onSubmit?.(response);\n\n if (isExam) {\n // The client neither grades nor reveals: no score(), no read of any\n // answer-key field, no onComplete, and no xAPI statement — its\n // correctResponsesPattern IS the answer key.\n complete();\n setSummary(s.answerSubmitted);\n fireInteraction('submitted', { selections });\n return;\n }\n\n const answer = score('gap-select', data as GapSelectData, response);\n complete();\n const timeSpent = getTimeSpent();\n // The question's score under the policy — see MultipleChoice.\n const counted = tries.record({\n score: answer.score,\n maxScore: answer.maxScore,\n hintsRevealed,\n });\n const mine = counted.tries[counted.tries.length - 1];\n const costed = mine === undefined ? answer.score : mine.scored;\n const scoringResult =\n costed === answer.score\n ? answer\n : { ...answer, score: costed, passed: computePassThreshold(data as GapSelectData, costed) };\n const passed =\n counted.score === scoringResult.score\n ? scoringResult.passed\n : computePassThreshold(data as GapSelectData, counted.score);\n const xapiStatement = xAPIBuilder.buildAnsweredStatement({\n actor: ANONYMOUS_ACTOR,\n object: {\n id: objectIdFor(data.id),\n name: { [data.locale ?? 'en-US']: data.title },\n ...xapiDefinitionFor(data),\n },\n scoringResult,\n timeSpentMs: timeSpent,\n response: JSON.stringify(selections),\n });\n setResult(answer);\n onComplete?.(\n stampTake(\n { score: counted.score, maxScore: counted.maxScore, passed, timeSpent, xapiStatement },\n mintTake(),\n ),\n );\n setOverall(answer.feedback);\n setSummary(\n `${s.answerSubmitted} ${s.scoreAnnouncement(Math.round(counted.score * 100), passed)}`,\n );\n fireInteraction('submitted', { selections, score: scoringResult.score });\n };\n\n /** \"Try again\": the choices stay, to be changed; the marks go. */\n const retry = (): void => {\n reset('idle');\n setSummary(null);\n setOverall(null);\n focusAfterRef.current = 'answer';\n };\n\n /** \"Keep this answer\": no more tries. A gap select never writes in the right choice. */\n const closeTries = (): void => {\n tries.close();\n focusAfterRef.current = 'feedback';\n };\n\n const correctByGap = useMemo(() => {\n if (isExam) {\n return new Map<string, boolean>();\n }\n if (isReview) {\n return outcome?.status === 'scored'\n ? correctnessByItem(outcome.details)\n : new Map<string, boolean>();\n }\n return correctnessByItem(result?.details ?? []);\n }, [isExam, isReview, outcome, result]);\n\n const reviewSummary = useMemo(() => {\n if (!isReview || outcome === undefined) {\n return null;\n }\n if (outcome.status === 'scored') {\n return `${s.scoreAnnouncement(Math.round(outcome.score * 100), outcome.passed)}${\n outcome.feedback ? ` ${outcome.feedback}` : ''\n }`;\n }\n if (outcome.status === 'deferred') {\n return s.notGradedYet;\n }\n if (outcome.status === 'graded') {\n const percent =\n outcome.maxScore > 0\n ? Math.round((outcome.score / outcome.maxScore) * 100)\n : Math.round(outcome.score * 100);\n return `${s.scoreAnnouncement(percent, outcome.passed)}${\n outcome.feedback ? ` ${outcome.feedback}` : ''\n }`;\n }\n return s.noGradeAvailable;\n }, [isReview, outcome, s]);\n\n const anyFeedback = data.gaps.some((gap) => gap.feedback !== undefined);\n const extra =\n !isReview && policy.feedback && submitted && tries.counted !== null\n ? triesSummary(s, scoringPolicy, tries.counted, tries.open)\n : '';\n const announced =\n summary === null\n ? null\n : `${summary}${extra ? ` ${extra}` : ''}${overall ? ` ${overall}` : ''}`;\n\n return (\n <form\n ref={formRef}\n className=\"lk-gs\"\n aria-label={data.title}\n lang={locale}\n data-render-mode={renderMode}\n style={theme as CSSProperties | undefined}\n onSubmit={handleSubmit}\n >\n {data.media ? (\n <ActivityMedia\n media={data.media}\n renderMode={renderMode}\n {...(mediaBudget !== undefined ? { mediaBudget } : {})}\n {...(mediaStrings !== undefined ? { mediaStrings } : {})}\n {...(strings !== undefined ? { strings } : {})}\n {...(onInteraction !== undefined ? { onInteraction } : {})}\n {...(locale !== undefined ? { locale } : {})}\n />\n ) : null}\n <fieldset disabled={inactive}>\n <p className=\"lk-gs-passage\">\n {segments.map((seg, i) => {\n if (seg.kind === 'text') {\n // biome-ignore lint/suspicious/noArrayIndexKey: passage segments are positional and static\n return <span key={`t${i}`}>{seg.value}</span>;\n }\n const gap = gapById.get(seg.id);\n if (!gap) {\n return null;\n }\n const choices = choicesByGap.get(seg.id) ?? [];\n const correctness = correctByGap.get(seg.id);\n // Read defensively and never in exam: a redact() projection omits\n // both at runtime while the type still says they are there.\n const gapFeedback = isExam ? undefined : gap.feedback;\n return (\n <span key={seg.id} className=\"lk-gs-gap\">\n <select\n className=\"lk-gs-select\"\n aria-label={s.gapLabel(seg.ordinal)}\n value={selections[seg.id] ?? ''}\n disabled={inactive}\n aria-disabled={inactive || undefined}\n data-correct={\n marking && correctness !== undefined ? String(correctness) : undefined\n }\n onChange={(e) => handleSelect(seg.id, e.target.value)}\n >\n {/*\n The empty entry is part of the contract, not a decoration.\n It is how a learner leaves a gap alone — and how they take\n an answer back — so \"not answered\" stays distinguishable\n from \"answered wrongly\" all the way into ItemOutcome, where\n the scorer reports it as `incorrect-omission`.\n */}\n <option value=\"\">{s.gapPlaceholder}</option>\n {choices.map((choice) => (\n <option key={choice.id} value={choice.id}>\n {choice.text}\n </option>\n ))}\n </select>\n {marking && !feedbackHidden && gapFeedback ? (\n <span\n className=\"lk-gs-gap-feedback\"\n role=\"note\"\n data-correct={correctness !== undefined ? String(correctness) : undefined}\n >\n {gapFeedback}\n </span>\n ) : null}\n </span>\n );\n })}\n </p>\n {aiHints.offered && renderMode === 'practice' ? (\n <HintCost policy={scoringPolicy} strings={s} />\n ) : null}\n <AiHints help={aiHints} ai={ai} strings={s} />\n {isReview ? null : (\n <button type=\"submit\" disabled={inactive}>\n {isExam ? s.submitAnswers : s.checkAnswers}\n </button>\n )}\n </fieldset>\n {marking && anyFeedback ? (\n <button\n type=\"button\"\n className=\"lk-gs-feedback-toggle\"\n aria-expanded={!feedbackHidden}\n onClick={() => setFeedbackHidden((h) => !h)}\n >\n {feedbackHidden ? s.showFeedback : s.hideFeedback}\n </button>\n ) : null}\n <FeedbackRegion\n id={`${data.id}-feedback`}\n {...(scoringPolicy.retries > 0 ? { ref: feedbackRef } : {})}\n >\n {isReview\n ? policy.feedback || !outcomeShowsMarks(outcome)\n ? reviewSummary\n : null\n : policy.feedback || summary === null\n ? announced\n : s.answerSubmitted}\n </FeedbackRegion>\n <TryActions\n tries={tries}\n solutions={false}\n strings={s}\n onRetry={retry}\n onClose={closeTries}\n />\n <AiExplanation\n ai={ai}\n data={data}\n renderMode={renderMode}\n submitted={submitted}\n response={{ type: 'gap-select', selections }}\n outcome={outcome}\n locale={locale}\n onInteraction={onInteraction}\n strings={s}\n // An explanation all but always names the answer: not while another\n // try is on offer.\n delivery={tries.open ? { ...policy, solutions: false } : policy}\n />\n </form>\n );\n}\n","'use client';\n\nimport { useLkStrings } from '../../i18n/LkIntlProvider.js';\nimport { ActivityErrorBoundary } from '../ActivityErrorBoundary.js';\nimport { GapSelect as GapSelectCore, type GapSelectProps } from './GapSelect.js';\n\nexport type { GapSelectProps } from './GapSelect.js';\n\n/**\n * Public Gap Select activity: the core wrapped in `ActivityErrorBoundary` so a\n * render failure (including the dev schema-validation throw and the\n * redacted-in-`practice` guard) degrades to an accessible fallback instead of\n * crashing the host.\n */\nexport function GapSelect(props: GapSelectProps) {\n const strings = useLkStrings(props.strings);\n return (\n <ActivityErrorBoundary activityTitle={props.data?.title} strings={strings}>\n <GapSelectCore {...props} />\n </ActivityErrorBoundary>\n );\n}\n"]}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var chunkY4IDF5UO_cjs = require('./chunk-Y4IDF5UO.cjs');
|
|
5
|
+
var chunkJ2QIHZ3H_cjs = require('./chunk-J2QIHZ3H.cjs');
|
|
6
|
+
var react = require('react');
|
|
7
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
8
|
+
var lkCore = require('@intellectif/lk-core');
|
|
9
|
+
|
|
10
|
+
function situation(input) {
|
|
11
|
+
const { data, response, submitted, renderMode, ai, locale, onInteraction, delivery } = input;
|
|
12
|
+
return {
|
|
13
|
+
data,
|
|
14
|
+
response,
|
|
15
|
+
submitted,
|
|
16
|
+
renderMode,
|
|
17
|
+
delivery,
|
|
18
|
+
...ai !== void 0 ? { ai } : {},
|
|
19
|
+
...locale !== void 0 ? { locale } : {},
|
|
20
|
+
...onInteraction !== void 0 ? { onInteraction } : {}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function AiExplanation({
|
|
24
|
+
outcome,
|
|
25
|
+
...rest
|
|
26
|
+
}) {
|
|
27
|
+
const s = rest.strings;
|
|
28
|
+
const help = chunkY4IDF5UO_cjs.useAiExplanation({
|
|
29
|
+
...situation(rest),
|
|
30
|
+
...outcome !== void 0 ? { outcome } : {}
|
|
31
|
+
});
|
|
32
|
+
const buttonRef = react.useRef(null);
|
|
33
|
+
const panelRef = react.useRef(null);
|
|
34
|
+
const refocus = react.useRef(false);
|
|
35
|
+
react.useEffect(() => {
|
|
36
|
+
if (help.status === "shown" && refocus.current) {
|
|
37
|
+
refocus.current = false;
|
|
38
|
+
panelRef.current?.focus();
|
|
39
|
+
}
|
|
40
|
+
if (help.status === "unavailable") {
|
|
41
|
+
refocus.current = false;
|
|
42
|
+
}
|
|
43
|
+
}, [help.status]);
|
|
44
|
+
if (!help.offered) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const ask = () => {
|
|
48
|
+
refocus.current = typeof document !== "undefined" && document.activeElement === buttonRef.current;
|
|
49
|
+
help.ask();
|
|
50
|
+
};
|
|
51
|
+
const loading = help.status === "loading";
|
|
52
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lk-ai", "data-state": help.status, children: [
|
|
53
|
+
help.explanation === null ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
54
|
+
"button",
|
|
55
|
+
{
|
|
56
|
+
ref: buttonRef,
|
|
57
|
+
type: "button",
|
|
58
|
+
className: "lk-ai-button",
|
|
59
|
+
"aria-busy": loading || void 0,
|
|
60
|
+
"aria-disabled": loading || void 0,
|
|
61
|
+
onClick: ask,
|
|
62
|
+
children: loading ? s.aiExplaining : s.aiExplain
|
|
63
|
+
}
|
|
64
|
+
) : null,
|
|
65
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { "aria-live": "polite", children: help.explanation !== null ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
66
|
+
"section",
|
|
67
|
+
{
|
|
68
|
+
ref: panelRef,
|
|
69
|
+
className: "lk-ai-panel",
|
|
70
|
+
tabIndex: -1,
|
|
71
|
+
"aria-label": s.aiExplanation,
|
|
72
|
+
children: [
|
|
73
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "lk-ai-heading", children: s.aiExplanation }),
|
|
74
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "lk-ai-text", children: help.explanation.text }),
|
|
75
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "lk-ai-notice", children: s.aiNotice })
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
) : help.status === "unavailable" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "lk-ai-unavailable", children: s.aiExplanationUnavailable }) : null })
|
|
79
|
+
] });
|
|
80
|
+
}
|
|
81
|
+
function useComponentAiHints({
|
|
82
|
+
disabled,
|
|
83
|
+
...rest
|
|
84
|
+
}) {
|
|
85
|
+
return chunkY4IDF5UO_cjs.useAiHints({ ...situation(rest), disabled });
|
|
86
|
+
}
|
|
87
|
+
function AiHints({
|
|
88
|
+
help,
|
|
89
|
+
ai,
|
|
90
|
+
strings: s
|
|
91
|
+
}) {
|
|
92
|
+
const used = help.used;
|
|
93
|
+
if (ai?.hint === void 0 || !help.offered && used === 0) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
const loading = help.status === "loading";
|
|
97
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lk-ai lk-ai-hint-area", "data-state": help.status, children: [
|
|
98
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { "aria-live": "polite", children: [
|
|
99
|
+
used > 0 ? /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "lk-ai-hints", "aria-label": s.aiHints, children: help.hints.map((hint, index) => (
|
|
100
|
+
// Hints are only ever appended, so a position is a stable key.
|
|
101
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: an append-only list
|
|
102
|
+
/* @__PURE__ */ jsxRuntime.jsxs("li", { className: "lk-ai-hint", children: [
|
|
103
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "lk-ai-hint-label", children: s.aiHintNumber(index + 1) }),
|
|
104
|
+
" ",
|
|
105
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "lk-ai-text", children: hint.text })
|
|
106
|
+
] }, index)
|
|
107
|
+
)) }) : null,
|
|
108
|
+
help.status === "unavailable" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "lk-ai-unavailable", children: s.aiHintUnavailable }) : null
|
|
109
|
+
] }),
|
|
110
|
+
help.offered ? used < help.limit ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
111
|
+
"button",
|
|
112
|
+
{
|
|
113
|
+
type: "button",
|
|
114
|
+
className: "lk-ai-button",
|
|
115
|
+
"aria-busy": loading || void 0,
|
|
116
|
+
"aria-disabled": loading || void 0,
|
|
117
|
+
onClick: help.ask,
|
|
118
|
+
children: loading ? s.aiHintLoading : s.aiHint
|
|
119
|
+
}
|
|
120
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "lk-ai-note", children: s.aiNoMoreHints }) : null,
|
|
121
|
+
used > 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "lk-ai-notice", children: s.aiNotice }) : null
|
|
122
|
+
] });
|
|
123
|
+
}
|
|
124
|
+
function useItemScoringPolicy(given) {
|
|
125
|
+
const around = react.useContext(chunkJ2QIHZ3H_cjs.SequenceSlotContext)?.scoring;
|
|
126
|
+
const key = policyKey(given);
|
|
127
|
+
const own = react.useMemo(
|
|
128
|
+
() => around === void 0 ? lkCore.resolveItemScoringPolicy(given) : null,
|
|
129
|
+
[around, key]
|
|
130
|
+
);
|
|
131
|
+
return around ?? own;
|
|
132
|
+
}
|
|
133
|
+
function policyKey(given) {
|
|
134
|
+
try {
|
|
135
|
+
return JSON.stringify(given) ?? "";
|
|
136
|
+
} catch {
|
|
137
|
+
return String(given);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function hintsOf(response) {
|
|
141
|
+
const count = response?.hintsRevealed;
|
|
142
|
+
return typeof count === "number" && Number.isSafeInteger(count) && count > 0 ? count : 0;
|
|
143
|
+
}
|
|
144
|
+
var fullMarks = (one) => one.score / one.maxScore >= 1 - 1e-9;
|
|
145
|
+
function offersAfter(now, made, shut) {
|
|
146
|
+
const last = made[made.length - 1];
|
|
147
|
+
return now.graded && !now.disabled && !shut && now.channel?.triesClosed !== true && last !== void 0 && made.length < 1 + now.policy.retries && !fullMarks(last);
|
|
148
|
+
}
|
|
149
|
+
function useTries({ policy, graded, submitted, disabled }) {
|
|
150
|
+
const channel = react.useContext(chunkJ2QIHZ3H_cjs.SequenceSlotContext);
|
|
151
|
+
const [tries, setTries] = react.useState([]);
|
|
152
|
+
const [closed, setClosed] = react.useState(false);
|
|
153
|
+
const triesRef = react.useRef([]);
|
|
154
|
+
const closedRef = react.useRef(false);
|
|
155
|
+
const latest = react.useRef({ policy, graded, disabled, channel });
|
|
156
|
+
latest.current = { policy, graded, disabled, channel };
|
|
157
|
+
const record = react.useCallback((one) => {
|
|
158
|
+
const next = [...triesRef.current, one];
|
|
159
|
+
triesRef.current = next;
|
|
160
|
+
setTries(next);
|
|
161
|
+
if (offersAfter(latest.current, next, closedRef.current)) {
|
|
162
|
+
latest.current.channel?.triesState("offered");
|
|
163
|
+
}
|
|
164
|
+
return lkCore.scoreTries(next, latest.current.policy);
|
|
165
|
+
}, []);
|
|
166
|
+
const close = react.useCallback(() => {
|
|
167
|
+
closedRef.current = true;
|
|
168
|
+
setClosed(true);
|
|
169
|
+
}, []);
|
|
170
|
+
const reset = react.useCallback(() => {
|
|
171
|
+
triesRef.current = [];
|
|
172
|
+
closedRef.current = false;
|
|
173
|
+
setTries([]);
|
|
174
|
+
setClosed(false);
|
|
175
|
+
}, []);
|
|
176
|
+
const allowed = 1 + policy.retries;
|
|
177
|
+
const left = Math.max(0, allowed - tries.length);
|
|
178
|
+
const open = submitted && offersAfter(latest.current, tries, closed);
|
|
179
|
+
const retrying = !submitted && tries.length > 0 && !closed && graded && !disabled && channel?.triesClosed !== true;
|
|
180
|
+
const state = open ? "offered" : retrying ? "retrying" : "none";
|
|
181
|
+
const report = channel?.triesState;
|
|
182
|
+
react.useEffect(() => {
|
|
183
|
+
report?.(state);
|
|
184
|
+
}, [report, state]);
|
|
185
|
+
react.useEffect(() => () => report?.("none"), [report]);
|
|
186
|
+
const counted = react.useMemo(
|
|
187
|
+
() => tries.length === 0 ? null : lkCore.scoreTries(tries, policy),
|
|
188
|
+
[tries, policy]
|
|
189
|
+
);
|
|
190
|
+
return { tries, counted, open, left, record, close, reset };
|
|
191
|
+
}
|
|
192
|
+
var percentOf = (fraction) => Math.round(fraction * 100);
|
|
193
|
+
function triesSummary(s, policy, counted, offered) {
|
|
194
|
+
const parts = [];
|
|
195
|
+
const latest = counted.tries.length - 1;
|
|
196
|
+
const mine = counted.tries[latest];
|
|
197
|
+
if (mine !== void 0) {
|
|
198
|
+
if (counted.counted !== latest) {
|
|
199
|
+
parts.push(
|
|
200
|
+
s.countedTry(
|
|
201
|
+
policy.counts === "best" ? "best" : "first",
|
|
202
|
+
percentOf(mine.scored / mine.maxScore)
|
|
203
|
+
)
|
|
204
|
+
);
|
|
205
|
+
} else if (mine.scored !== mine.score) {
|
|
206
|
+
parts.push(s.scoreBeforeCosts(percentOf(mine.score / mine.maxScore)));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (offered) {
|
|
210
|
+
parts.push(
|
|
211
|
+
s.triesLeft(policy.retries + 1 - counted.tries.length, percentOf(policy.retryPenalty))
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
return parts.join(" ");
|
|
215
|
+
}
|
|
216
|
+
function TryActions({
|
|
217
|
+
tries,
|
|
218
|
+
solutions,
|
|
219
|
+
strings: s,
|
|
220
|
+
onRetry,
|
|
221
|
+
onClose
|
|
222
|
+
}) {
|
|
223
|
+
if (!tries.open) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lk-tries", children: [
|
|
227
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "lk-tries-again", onClick: onRetry, children: s.tryAgain }),
|
|
228
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "lk-tries-close", onClick: onClose, children: solutions ? s.showAnswer : s.keepAnswer })
|
|
229
|
+
] });
|
|
230
|
+
}
|
|
231
|
+
function HintCost({
|
|
232
|
+
policy,
|
|
233
|
+
strings: s
|
|
234
|
+
}) {
|
|
235
|
+
if (policy.hintPenalty <= 0) {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "lk-hint-cost", children: s.hintCost(percentOf(policy.hintPenalty)) });
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
exports.AiExplanation = AiExplanation;
|
|
242
|
+
exports.AiHints = AiHints;
|
|
243
|
+
exports.HintCost = HintCost;
|
|
244
|
+
exports.TryActions = TryActions;
|
|
245
|
+
exports.hintsOf = hintsOf;
|
|
246
|
+
exports.triesSummary = triesSummary;
|
|
247
|
+
exports.useComponentAiHints = useComponentAiHints;
|
|
248
|
+
exports.useItemScoringPolicy = useItemScoringPolicy;
|
|
249
|
+
exports.useTries = useTries;
|
|
250
|
+
//# sourceMappingURL=chunk-CQ37UD4T.cjs.map
|
|
251
|
+
//# sourceMappingURL=chunk-CQ37UD4T.cjs.map
|