@citolab/qti-components 8.2.0 → 9.1.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.
Files changed (50) hide show
  1. package/cdn/index.global.js +1 -1
  2. package/cdn/index.js +190 -190
  3. package/dist/base.js +1 -1
  4. package/dist/{chunk-OVBG223G.js → chunk-3VQSUWCK.js} +173 -12
  5. package/dist/chunk-3VQSUWCK.js.map +1 -0
  6. package/dist/{chunk-Q6ZK4LRH.js → chunk-5BXJRO5O.js} +1792 -551
  7. package/dist/chunk-5BXJRO5O.js.map +1 -0
  8. package/dist/{chunk-OIUK5BFU.js → chunk-5SU3MP6Y.js} +811 -811
  9. package/dist/chunk-5SU3MP6Y.js.map +1 -0
  10. package/dist/{chunk-LMGJOBJA.js → chunk-DAUU2NJL.js} +5 -5
  11. package/dist/chunk-DAUU2NJL.js.map +1 -0
  12. package/dist/chunk-KCK27QBP.js +11 -0
  13. package/dist/chunk-KCK27QBP.js.map +1 -0
  14. package/dist/{chunk-M7K5C522.js → chunk-LDY4C2OT.js} +2 -2
  15. package/dist/chunk-MKZ374OJ.js +179 -0
  16. package/dist/chunk-MKZ374OJ.js.map +1 -0
  17. package/dist/{chunk-CRLXQZFA.js → chunk-QCPN7E6K.js} +14 -10
  18. package/dist/chunk-QCPN7E6K.js.map +1 -0
  19. package/dist/{chunk-U7YCXDYC.js → chunk-RT6WZ3P7.js} +2 -2
  20. package/dist/{chunk-7D3NKAGZ.js → chunk-WCWJPIUQ.js} +5 -4
  21. package/dist/chunk-WCWJPIUQ.js.map +1 -0
  22. package/dist/{chunk-YLRMOX6C.js → chunk-WKWCOV5P.js} +2 -2
  23. package/dist/chunk-XE7BALDO.js +7 -0
  24. package/dist/chunk-XE7BALDO.js.map +1 -0
  25. package/dist/{chunk-UC3T3NI3.js → chunk-XYQNTF5B.js} +2 -2
  26. package/dist/corrections.js +82 -7
  27. package/dist/corrections.js.map +1 -1
  28. package/dist/elements.js +3 -3
  29. package/dist/index.js +24 -24
  30. package/dist/interactions.js +14 -15
  31. package/dist/item.js +4 -4
  32. package/dist/processing.d.ts +26 -0
  33. package/dist/processing.js +3 -2
  34. package/dist/qti-components-jsx.d.ts +60 -4
  35. package/dist/test.js +5 -5
  36. package/package.json +7 -7
  37. package/dist/chunk-43XBJ47H.js +0 -1420
  38. package/dist/chunk-43XBJ47H.js.map +0 -1
  39. package/dist/chunk-7D3NKAGZ.js.map +0 -1
  40. package/dist/chunk-CRLXQZFA.js.map +0 -1
  41. package/dist/chunk-LMGJOBJA.js.map +0 -1
  42. package/dist/chunk-OIUK5BFU.js.map +0 -1
  43. package/dist/chunk-OVBG223G.js.map +0 -1
  44. package/dist/chunk-Q6ZK4LRH.js.map +0 -1
  45. package/dist/chunk-TLTYXWW7.js +0 -7
  46. package/dist/chunk-TLTYXWW7.js.map +0 -1
  47. /package/dist/{chunk-M7K5C522.js.map → chunk-LDY4C2OT.js.map} +0 -0
  48. /package/dist/{chunk-U7YCXDYC.js.map → chunk-RT6WZ3P7.js.map} +0 -0
  49. /package/dist/{chunk-YLRMOX6C.js.map → chunk-WKWCOV5P.js.map} +0 -0
  50. /package/dist/{chunk-UC3T3NI3.js.map → chunk-XYQNTF5B.js.map} +0 -0
@@ -2,10 +2,10 @@ import {
2
2
  QtiTest,
3
3
  TestCheckItem,
4
4
  TestNavigation
5
- } from "./chunk-CRLXQZFA.js";
5
+ } from "./chunk-QCPN7E6K.js";
6
6
  import {
7
7
  QtiAssessmentItem
8
- } from "./chunk-YLRMOX6C.js";
8
+ } from "./chunk-WKWCOV5P.js";
9
9
  import {
10
10
  QtiAssociateInteraction,
11
11
  QtiChoiceInteraction,
@@ -28,10 +28,10 @@ import {
28
28
  QtiSimpleChoice,
29
29
  QtiSliderInteraction,
30
30
  QtiTextEntryInteraction
31
- } from "./chunk-Q6ZK4LRH.js";
31
+ } from "./chunk-5BXJRO5O.js";
32
32
  import {
33
33
  QtiItem
34
- } from "./chunk-LMGJOBJA.js";
34
+ } from "./chunk-DAUU2NJL.js";
35
35
  import {
36
36
  watch
37
37
  } from "./chunk-4DB4SC5K.js";
@@ -41,907 +41,758 @@ import {
41
41
  computedItemContext,
42
42
  configContext,
43
43
  responseAttributeConverter
44
- } from "./chunk-7D3NKAGZ.js";
44
+ } from "./chunk-WCWJPIUQ.js";
45
45
 
46
- // ../qti-corrections/dist/mixins/correct-response.mixin.js
46
+ // ../qti-corrections/dist/components/item-correct-response-mode/item-correct-response-mode.js
47
+ import { html, LitElement } from "lit";
47
48
  import { property, state } from "lit/decorators.js";
49
+ import { consume } from "@lit/context";
48
50
  var __decorate = function(decorators, target, key, desc) {
49
51
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
50
52
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
51
53
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
52
54
  return c > 3 && r && Object.defineProperty(target, key, r), r;
53
55
  };
54
- var Correctness;
55
- (function(Correctness2) {
56
- Correctness2["Correct"] = "correct";
57
- Correctness2["PartiallyCorrect"] = "partially-correct";
58
- Correctness2["Incorrect"] = "incorrect";
59
- })(Correctness || (Correctness = {}));
60
- var CorrectResponseMixin = (superClass) => {
61
- class CorrectResponseMixinClass extends superClass {
62
- constructor() {
63
- super(...arguments);
64
- this.showCorrectResponse = false;
65
- this._handleShowCorrectResponseChange = (_, show) => {
66
- this.toggleInternalCorrectResponse(show);
67
- };
68
- this.showFullCorrectResponse = false;
69
- this._handleShowFullCorrectResponseChange = (_, show) => {
70
- this.toggleFullCorrectResponse(show);
71
- };
72
- this.showCandidateCorrection = false;
73
- this._handleShowCandidateCorrectionChange = (_, show) => {
74
- this.toggleCandidateCorrection(show);
75
- };
76
- this._correctResponse = null;
77
- }
78
- /**
79
- * The correct response for this interaction. Can be set as a comma-separated
80
- * string via attribute or as string/string[] via property.
81
- *
82
- * @example
83
- * ```html
84
- * <qti-choice-interaction correct-response="A,B">
85
- * ```
86
- */
87
- set correctResponseAttr(val) {
88
- this._correctResponse = val;
89
- }
90
- get correctResponseAttr() {
91
- return this._correctResponse;
92
- }
93
- get isFullCorrectResponse() {
94
- return this._isFullCorrectResponse;
95
- }
96
- get registersWithItem() {
97
- return !this.isFullCorrectResponse && super.registersWithItem;
98
- }
99
- set isFullCorrectResponse(val) {
100
- this._isFullCorrectResponse = val;
101
- if (val) {
102
- this.setAttribute("inert", "");
103
- this.setAttribute("response-identifier", this.responseIdentifier + "_cr");
104
- this.setAttribute("answer-key", "");
105
- }
106
- }
107
- get correctResponse() {
108
- const responseVariable = this.responseVariable;
109
- if (responseVariable?.correctResponse) {
110
- return responseVariable.correctResponse;
111
- }
112
- return this._correctResponse;
113
- }
114
- set correctResponse(val) {
115
- const oldVal = this._correctResponse;
116
- this._correctResponse = val;
117
- if (val === null || val === void 0) {
118
- this.removeAttribute("correct-response");
119
- } else {
120
- this.setAttribute("correct-response", Array.isArray(val) ? val.join(",") : val);
121
- }
122
- this.requestUpdate("_correctResponse", oldVal);
123
- }
124
- // ─── correctness getter ───────────────────────────────────────────────────
125
- get correctness() {
126
- const correctResponse = this.correctResponse;
127
- if (!correctResponse)
128
- return null;
129
- const currentResponse = this.response;
130
- if (currentResponse === null || currentResponse === void 0 || currentResponse === "" || Array.isArray(currentResponse) && currentResponse.length === 0) {
131
- return null;
132
- }
133
- const correctArr = Array.isArray(correctResponse) ? correctResponse : [correctResponse];
134
- const responseArr = Array.isArray(currentResponse) ? currentResponse : [currentResponse];
135
- const filteredResponseArr = responseArr.filter((r) => r !== "");
136
- if (filteredResponseArr.length === 0)
137
- return null;
138
- const correctCount = filteredResponseArr.filter((r) => correctArr.includes(r)).length;
139
- const incorrectCount = filteredResponseArr.filter((r) => !correctArr.includes(r)).length;
140
- if (correctCount === correctArr.length && incorrectCount === 0 && filteredResponseArr.length === correctArr.length) {
141
- return Correctness.Correct;
142
- }
143
- if (correctCount > 0 && (correctCount < correctArr.length || incorrectCount > 0)) {
144
- return Correctness.PartiallyCorrect;
145
- }
146
- return Correctness.Incorrect;
147
- }
148
- // ─── correctionPart ───────────────────────────────────────────────────────
149
- get correctionPart() {
150
- return "correction";
151
- }
152
- // ─── toggle methods ───────────────────────────────────────────────────────
153
- toggleCorrectResponse(show) {
154
- const correctionConfig = this.configContext;
155
- const correctResponseMode = correctionConfig?.correctResponseMode || "internal";
156
- if (correctResponseMode === "full") {
157
- this.toggleFullCorrectResponse(show);
158
- } else {
159
- this.toggleInternalCorrectResponse(show);
160
- }
161
- }
162
- /**
163
- * Whether the full correct response is withheld from a candidate who is already correct.
164
- *
165
- * Withheld by default, unless the item configures `fullCorrectResponseOnlyWhenIncorrect: false`
166
- * — the behaviour `show-full-correct-response` has always had, on the reasoning that a correct
167
- * answer already *is* the key.
168
- *
169
- * An interaction that routes its internal mode here overrides this to false: there, the caller
170
- * asked to show the correct response, and silently showing nothing is worse than showing the
171
- * candidate the answer they already gave.
172
- */
173
- get withholdsFullCorrectResponseWhenCorrect() {
174
- return this.configContext?.fullCorrectResponseOnlyWhenIncorrect !== false;
175
- }
176
- async toggleFullCorrectResponse(show) {
177
- const nextSibling = this.nextSibling;
178
- const nextSiblingIsFullCorrectResponse = nextSibling instanceof HTMLDivElement && nextSibling?.classList.contains("full-correct-response");
179
- const correctResponse = this.correctResponse;
180
- if (!correctResponse) {
181
- return;
182
- }
183
- const showFullCorrectResponse = show && (!this.withholdsFullCorrectResponseWhenCorrect || this.correctness !== Correctness.Correct);
184
- if (!showFullCorrectResponse) {
185
- if (!nextSiblingIsFullCorrectResponse) {
186
- return;
187
- }
188
- this.parentElement?.removeChild(nextSibling);
189
- return;
190
- }
191
- if (nextSiblingIsFullCorrectResponse) {
192
- return;
193
- }
194
- const clone = this.cloneNode(true);
195
- clone.removeAttribute("show-full-correct-response");
196
- clone.removeAttribute("show-correct-response");
197
- clone.removeAttribute("show-candidate-correction");
198
- const containerDiv = document.createElement("div");
199
- containerDiv.classList.add("full-correct-response");
200
- if (this.isInline) {
201
- containerDiv.classList.add("full-correct-response-inline");
202
- } else {
203
- containerDiv.classList.add("full-correct-response-block");
204
- }
205
- containerDiv.role = "full-correct-response";
206
- containerDiv.appendChild(clone);
207
- clone.isFullCorrectResponse = true;
208
- this.parentElement?.insertBefore(containerDiv, this.nextSibling);
209
- await clone.updateComplete;
210
- clone.response = Array.isArray(correctResponse) ? [...correctResponse] : correctResponse;
211
- }
212
- toggleInternalCorrectResponse(show) {
213
- this._internals.states.delete("show-correct-response");
214
- if (show && this.correctResponse) {
215
- this._internals.states.add("show-correct-response");
216
- }
217
- }
218
- toggleCandidateCorrection(show) {
219
- if (!show) {
220
- return;
221
- }
222
- }
223
- // ─── Lifecycle ────────────────────────────────────────────────────────────
224
- firstUpdated() {
225
- super.firstUpdated();
226
- if (this.isFullCorrectResponse) {
227
- return;
228
- }
229
- if (this.showCorrectResponse) {
230
- this.toggleInternalCorrectResponse(true);
231
- }
232
- if (this.showFullCorrectResponse) {
233
- this.toggleFullCorrectResponse(true);
234
- }
235
- }
56
+ var ItemCorrectResponseMode = class extends LitElement {
57
+ constructor() {
58
+ super(...arguments);
59
+ this.label = "Correct response mode";
60
+ this._options = {
61
+ internal: "Internal",
62
+ full: "Full (copy)"
63
+ };
236
64
  }
237
- __decorate([
238
- property({
239
- attribute: "correct-response",
240
- reflect: true,
241
- converter: responseAttributeConverter({ emptyAs: null })
242
- })
243
- ], CorrectResponseMixinClass.prototype, "correctResponseAttr", null);
244
- __decorate([
245
- property({ type: Boolean, attribute: "show-correct-response", reflect: true })
246
- ], CorrectResponseMixinClass.prototype, "showCorrectResponse", void 0);
247
- __decorate([
248
- watch("showCorrectResponse", { waitUntilFirstUpdate: true })
249
- ], CorrectResponseMixinClass.prototype, "_handleShowCorrectResponseChange", void 0);
250
- __decorate([
251
- property({ type: Boolean, attribute: "show-full-correct-response", reflect: true })
252
- ], CorrectResponseMixinClass.prototype, "showFullCorrectResponse", void 0);
253
- __decorate([
254
- watch("showFullCorrectResponse", { waitUntilFirstUpdate: true })
255
- ], CorrectResponseMixinClass.prototype, "_handleShowFullCorrectResponseChange", void 0);
256
- __decorate([
257
- property({ type: Boolean, attribute: "show-candidate-correction", reflect: true })
258
- ], CorrectResponseMixinClass.prototype, "showCandidateCorrection", void 0);
259
- __decorate([
260
- watch("showCandidateCorrection", { waitUntilFirstUpdate: true })
261
- ], CorrectResponseMixinClass.prototype, "_handleShowCandidateCorrectionChange", void 0);
262
- __decorate([
263
- state()
264
- ], CorrectResponseMixinClass.prototype, "_isFullCorrectResponse", void 0);
265
- __decorate([
266
- state()
267
- ], CorrectResponseMixinClass.prototype, "_correctResponse", void 0);
268
- return CorrectResponseMixinClass;
269
- };
270
-
271
- // ../qti-corrections/dist/styles/correction.styles.js
272
- import { css } from "lit";
273
- var correctionPart = css`
274
- /* Nothing at all until the item judges this element. */
275
- [part~='correction'] {
276
- display: none;
65
+ #switchMode(view) {
66
+ this.dispatchEvent(new CustomEvent("item-switch-correct-response-mode", {
67
+ detail: view,
68
+ bubbles: true
69
+ }));
277
70
  }
71
+ render() {
72
+ return html`
73
+ <label part="label" for="modeSelect">${this.label}</label>
74
+ <select
75
+ part="select"
76
+ id="modeSelect"
77
+ @change=${(e) => {
78
+ const el = e.target;
79
+ this.#switchMode(el.value);
80
+ }}
81
+ >
82
+ ${Object.keys(this._options).map((value) => html`<option value="${value}" ?selected=${value === this.itemContext?.correctResponseMode}>
83
+ ${this._options[value]}
84
+ </option>`)}
85
+ </select>
86
+ `;
87
+ }
88
+ };
89
+ __decorate([
90
+ consume({ context: configContext, subscribe: true })
91
+ ], ItemCorrectResponseMode.prototype, "itemContext", void 0);
92
+ __decorate([
93
+ property({ type: String })
94
+ ], ItemCorrectResponseMode.prototype, "label", void 0);
95
+ __decorate([
96
+ state()
97
+ ], ItemCorrectResponseMode.prototype, "_options", void 0);
278
98
 
279
- [part~='correction-correct'],
280
- [part~='correction-incorrect'],
281
- [part~='correction-partially-correct'] {
282
- display: inline-flex;
283
- flex: 0 0 auto;
284
-
285
- /*
286
- * The badge aligns ITSELF, and that is the whole of the vertical story.
287
- *
288
- * It is a flex item with a definite height, so left alone its cross-axis position is decided by
289
- * whatever its host says — and the hosts disagree: qti-simple-associable-choice defaults to
290
- * align-items: normal, and qti-gap-text gates center behind :not([part~='drag']), so the
291
- * same chip centred its badge in the bank and top-aligned it once placed. align-self settles it
292
- * from the badge's side, in one line, without touching a single container.
293
- *
294
- * This replaces an align-items/justify-content: center pair that had never done anything: the
295
- * badge is empty — a mask painted over a background — so centring its CONTENTS centres nothing.
296
- */
297
- align-self: center;
298
-
299
- box-sizing: border-box;
300
- width: var(--qti-correction-size, 1em);
301
- height: var(--qti-correction-size, 1em);
302
- margin-left: var(--qti-glyph-gap, 0.5em);
99
+ // ../qti-corrections/dist/components/item-show-candidate-correction/item-show-candidate-correction.js
100
+ import { css as css2, html as html2, LitElement as LitElement2 } from "lit";
101
+ import { property as property2 } from "lit/decorators.js";
102
+ import { consume as consume2 } from "@lit/context";
303
103
 
304
- pointer-events: none;
305
- }
104
+ // ../qti-corrections/dist/components/styles.js
105
+ import { css } from "lit";
106
+ var form = css`
107
+ display: inline-flex;
108
+ align-items: center;
109
+ cursor: pointer;
110
+ padding: 0.5rem 1rem;
111
+ border-radius: 0.25rem;
112
+ user-select: none;
113
+ `;
114
+ var btn = css`
115
+ background-color: lightgray;
116
+ ${form};
117
+ `;
118
+ var dis = css`
119
+ cursor: not-allowed;
120
+ opacity: 0.8;
121
+ `;
122
+ var ind = css`
123
+ ${form};
124
+ border: 1px solid gray;
306
125
  `;
307
126
 
308
- // ../qti-corrections/dist/mixins/candidate-correction.mixin.js
309
- import { state as state2 } from "lit/decorators.js";
127
+ // ../qti-corrections/dist/components/item-show-candidate-correction/item-show-candidate-correction.js
310
128
  var __decorate2 = function(decorators, target, key, desc) {
311
129
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
312
130
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
313
131
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
314
132
  return c > 3 && r && Object.defineProperty(target, key, r), r;
315
133
  };
316
- var CandidateCorrectionMixin = (superClass) => {
317
- class CandidateCorrectionMixinClass extends CorrectResponseMixin(superClass) {
318
- constructor() {
319
- super(...arguments);
320
- this._candidateCorrection = null;
321
- }
322
- get correctionPart() {
323
- return this._candidateCorrection ? `correction correction-${this._candidateCorrection}` : "correction";
324
- }
325
- toggleCandidateCorrection(show) {
326
- if (this.showCandidateCorrection !== show) {
327
- this.showCandidateCorrection = show;
328
- }
329
- super.toggleCandidateCorrection(show);
330
- const internals = this._internals;
331
- internals.states.delete("candidate-correct");
332
- internals.states.delete("candidate-partially-correct");
333
- internals.states.delete("candidate-incorrect");
334
- let verdict = null;
335
- if (show) {
336
- if (this.correctness === Correctness.Correct) {
337
- internals.states.add("candidate-correct");
338
- verdict = "correct";
339
- }
340
- if (this.correctness === Correctness.PartiallyCorrect) {
341
- internals.states.add("candidate-partially-correct");
342
- verdict = "partially-correct";
343
- }
344
- if (this.correctness === Correctness.Incorrect) {
345
- internals.states.add("candidate-incorrect");
346
- verdict = "incorrect";
347
- }
348
- }
349
- if (this._candidateCorrection !== verdict) {
350
- this._candidateCorrection = verdict;
351
- }
134
+ var ItemShowCandidateCorrection = class extends LitElement2 {
135
+ static {
136
+ this.styles = css2`
137
+ :host {
138
+ ${btn};
352
139
  }
353
- connectedCallback() {
354
- super.connectedCallback?.();
355
- queueMicrotask(() => {
356
- if (!this.isConnected || this.isFullCorrectResponse) {
357
- return;
358
- }
359
- if (this.showCandidateCorrection) {
360
- this.toggleCandidateCorrection(true);
361
- }
362
- });
140
+ :host([disabled]) {
141
+ ${dis};
363
142
  }
143
+ `;
364
144
  }
365
- __decorate2([
366
- state2()
367
- ], CandidateCorrectionMixinClass.prototype, "_candidateCorrection", void 0);
368
- return CandidateCorrectionMixinClass;
369
- };
370
-
371
- // ../qti-corrections/dist/mixins/choice-correction.mixin.js
372
- var ChoiceCorrectionMixin = (superClass) => {
373
- class ChoiceCorrectionMixinClass extends CandidateCorrectionMixin(superClass) {
374
- toggleInternalCorrectResponse(show) {
375
- super.toggleInternalCorrectResponse(show);
376
- const correctResponse = this.correctResponse;
377
- const correctResponses = correctResponse ? Array.isArray(correctResponse) ? correctResponse : [correctResponse] : [];
378
- for (const choice of this._choiceElements) {
379
- choice.internals.states.delete("correct-response");
380
- choice.internals.states.delete("incorrect-response");
381
- if (show && correctResponses.length > 0) {
382
- choice.internals.states.add(correctResponses.includes(choice.identifier) ? "correct-response" : "incorrect-response");
383
- }
384
- }
385
- }
386
- toggleCandidateCorrection(show) {
387
- super.toggleCandidateCorrection(show);
388
- for (const choice of this._choiceElements) {
389
- choice.candidateCorrection = null;
390
- }
391
- if (!show || !this.correctResponse) {
392
- return;
393
- }
394
- const correctResponses = Array.isArray(this.correctResponse) ? this.correctResponse : [this.correctResponse];
395
- const candidateResponses = Array.isArray(this.response) ? this.response : this.response ? [this.response] : [];
396
- for (const choice of this._choiceElements) {
397
- if (!candidateResponses.includes(choice.identifier)) {
398
- continue;
399
- }
400
- choice.candidateCorrection = correctResponses.includes(choice.identifier) ? "correct" : "incorrect";
401
- }
402
- }
145
+ #hasCorrectResponse;
146
+ // correct response is removed on certain point
147
+ #previousActiveItem;
148
+ // Store previous active item reference
149
+ constructor() {
150
+ super();
151
+ this.shown = false;
152
+ this.disabled = false;
153
+ this.showCandidateCorrectionText = "Show candidate correction";
154
+ this.hideCandidateCorrectionText = "Hide candidate correction";
155
+ this.noCorrectResponseText = "No correct response specified";
156
+ this.#hasCorrectResponse = false;
157
+ this.#previousActiveItem = "";
403
158
  }
404
- return ChoiceCorrectionMixinClass;
405
- };
406
-
407
- // ../qti-corrections/dist/elements/qti-gap-text-correction.js
408
- import { html } from "lit";
409
-
410
- // ../qti-corrections/dist/mixins/active-element-correction.mixin.js
411
- var ActiveElementCorrectionMixin = (superClass) => {
412
- class ActiveElementCorrectionMixinClass extends superClass {
413
- #candidateCorrection = null;
414
- get candidateCorrection() {
415
- return this.#candidateCorrection;
416
- }
417
- set candidateCorrection(value) {
418
- if (this.#candidateCorrection === value)
419
- return;
420
- const previous = this.#candidateCorrection;
421
- this.#candidateCorrection = value;
422
- const states = this.internals.states;
423
- states.delete("candidate-correct");
424
- states.delete("candidate-incorrect");
425
- states.delete("candidate-partially-correct");
426
- if (value)
427
- states.add(`candidate-${value}`);
428
- this.requestUpdate("candidateCorrection", previous);
429
- }
430
- get correctionPart() {
431
- return this.#candidateCorrection ? `correction correction-${this.#candidateCorrection}` : "correction";
159
+ updated() {
160
+ if (!this.#hasCorrectResponse || this.#previousActiveItem !== this.computedContext?.identifier) {
161
+ this.#previousActiveItem = this.computedContext?.identifier;
162
+ const containsCorrectResponse = !!this.computedContext?.variables.some((v) => v["correctResponse"]);
163
+ const containsMapping = !!this.computedContext?.variables.some((v) => {
164
+ return v["mapping"]?.mapEntries?.length > 0 || v["areaMapping"]?.areaMapEntries?.length > 0;
165
+ });
166
+ this.#hasCorrectResponse = containsCorrectResponse || containsMapping;
432
167
  }
168
+ this.disabled = !this.#hasCorrectResponse;
433
169
  }
434
- return ActiveElementCorrectionMixinClass;
435
- };
436
-
437
- // ../qti-corrections/dist/elements/qti-gap-text-correction.js
438
- var QtiGapTextCorrection = class extends ActiveElementCorrectionMixin(QtiGapText) {
439
- static {
440
- this.styles = [QtiGapText.styles, correctionPart];
170
+ #toggleState() {
171
+ if (this.disabled)
172
+ return;
173
+ this.dispatchEvent(new CustomEvent("item-show-candidate-correction", {
174
+ detail: !this.shown,
175
+ bubbles: true
176
+ }));
177
+ }
178
+ #getDisplayedText() {
179
+ return this.disabled ? this.noCorrectResponseText : this.shown ? this.hideCandidateCorrectionText : this.showCandidateCorrectionText;
441
180
  }
442
181
  render() {
443
- return html`<div part="control"></div>
444
- <slot part="label"></slot>
445
- <span part=${this.correctionPart} aria-hidden="true"></span>`;
182
+ return html2` <div @click="${this.#toggleState}">${this.#getDisplayedText()}</div> `;
446
183
  }
447
184
  };
185
+ __decorate2([
186
+ consume2({ context: computedItemContext, subscribe: true })
187
+ ], ItemShowCandidateCorrection.prototype, "computedContext", void 0);
188
+ __decorate2([
189
+ property2({ type: Boolean, reflect: true })
190
+ ], ItemShowCandidateCorrection.prototype, "shown", void 0);
191
+ __decorate2([
192
+ property2({ type: Boolean, reflect: true })
193
+ ], ItemShowCandidateCorrection.prototype, "disabled", void 0);
194
+ __decorate2([
195
+ property2({ type: String })
196
+ ], ItemShowCandidateCorrection.prototype, "showCandidateCorrectionText", void 0);
197
+ __decorate2([
198
+ property2({ type: String })
199
+ ], ItemShowCandidateCorrection.prototype, "hideCandidateCorrectionText", void 0);
200
+ __decorate2([
201
+ property2({ type: String })
202
+ ], ItemShowCandidateCorrection.prototype, "noCorrectResponseText", void 0);
448
203
 
449
- // ../qti-corrections/dist/elements/qti-hotspot-choice-correction.js
450
- import { property as property2 } from "lit/decorators.js";
204
+ // ../qti-corrections/dist/components/item-show-correct-response/item-show-correct-response.js
205
+ import { css as css3, html as html3, LitElement as LitElement3 } from "lit";
206
+ import { property as property3 } from "lit/decorators.js";
207
+ import { consume as consume3 } from "@lit/context";
451
208
  var __decorate3 = function(decorators, target, key, desc) {
452
209
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
453
210
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
454
211
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
455
212
  return c > 3 && r && Object.defineProperty(target, key, r), r;
456
213
  };
457
- var QtiHotspotChoiceCorrection = class extends ActiveElementCorrectionMixin(QtiHotspotChoice) {
214
+ var ItemShowCorrectResponse = class extends LitElement3 {
458
215
  constructor() {
459
216
  super(...arguments);
460
- this.orderCorrect = null;
217
+ this.shown = false;
218
+ this.disabled = false;
219
+ this.showCorrectText = "Show correct response";
220
+ this.hideCorrectText = "Hide correct response";
221
+ this.noCorrectResponseText = "No correct response specified";
222
+ this.#hasCorrectResponse = false;
223
+ this.#previousActiveItem = "";
461
224
  }
462
- };
463
- __decorate3([
464
- property2({ attribute: "aria-ordercorrectvalue", type: Number, reflect: true })
465
- ], QtiHotspotChoiceCorrection.prototype, "orderCorrect", void 0);
466
-
467
- // ../qti-corrections/dist/elements/qti-hottext-correction.js
468
- import { html as html2 } from "lit";
469
- var QtiHottextCorrection = class extends ActiveElementCorrectionMixin(QtiHottext) {
470
225
  static {
471
- this.styles = [QtiHottext.styles, correctionPart];
472
- }
473
- render() {
474
- return html2`<div part="control"><div part="control-mark"></div></div>
475
- <slot part="label"></slot>
476
- <span part=${this.correctionPart} aria-hidden="true"></span>`;
226
+ this.styles = css3`
227
+ :host {
228
+ ${btn};
229
+ }
230
+ :host([disabled]) {
231
+ ${dis};
232
+ }
233
+ `;
477
234
  }
478
- };
479
-
480
- // ../qti-corrections/dist/elements/qti-simple-associable-choice-correction.js
481
- import { html as html3 } from "lit";
482
- import { repeat } from "lit/directives/repeat.js";
483
- var QtiSimpleAssociableChoiceCorrection = class extends ActiveElementCorrectionMixin(QtiSimpleAssociableChoice) {
484
- static {
485
- this.styles = [QtiSimpleAssociableChoice.styles, correctionPart];
235
+ #hasCorrectResponse;
236
+ // correct response is removed on certain point
237
+ #previousActiveItem;
238
+ // Store previous active item reference
239
+ updated() {
240
+ if (!this.#hasCorrectResponse || this.#previousActiveItem !== this.computedContext?.identifier) {
241
+ this.#previousActiveItem = this.computedContext?.identifier;
242
+ const containsCorrectResponse = !!this.computedContext?.variables.some((v) => v["correctResponse"]);
243
+ const containsMapping = !!this.computedContext?.variables.some((v) => {
244
+ return v["mapping"]?.mapEntries?.length > 0 || v["areaMapping"]?.areaMapEntries?.length > 0;
245
+ });
246
+ this.#hasCorrectResponse = containsCorrectResponse || containsMapping;
247
+ }
248
+ this.disabled = !this.#hasCorrectResponse;
486
249
  }
487
- render() {
488
- return html3`
489
- <div part="control"></div>
490
- <slot part="label"></slot>
491
- <span part=${this.correctionPart} aria-hidden="true"></span>
492
- <div part="drop">
493
- ${repeat(this.drags, (node) => node.getAttribute("identifier"), (node) => node)}
494
- </div>
495
- `;
250
+ #toggleState() {
251
+ if (this.disabled)
252
+ return;
253
+ this.dispatchEvent(new CustomEvent("item-show-correct-response", {
254
+ detail: !this.shown,
255
+ bubbles: true
256
+ }));
496
257
  }
497
- };
498
-
499
- // ../qti-corrections/dist/elements/qti-simple-choice-correction.js
500
- import { html as html4, nothing } from "lit";
501
- var QtiSimpleChoiceCorrection = class extends ActiveElementCorrectionMixin(QtiSimpleChoice) {
502
- static {
503
- this.styles = [QtiSimpleChoice.styles, correctionPart];
258
+ #getDisplayedText() {
259
+ return this.disabled ? this.noCorrectResponseText : this.shown ? this.hideCorrectText : this.showCorrectText;
504
260
  }
505
261
  render() {
506
- return html4`<div part="control" tabindex="0"><div part="control-mark"></div></div>
507
- ${this.marker ? html4`<div id="label" part="marker">${this.marker}</div>` : nothing}
508
- <slot part="label"></slot>
509
- <span part=${this.correctionPart} aria-hidden="true"></span>`;
262
+ return html3` <div @click="${this.#toggleState}">${this.#getDisplayedText()}</div> `;
510
263
  }
511
264
  };
265
+ __decorate3([
266
+ consume3({ context: computedItemContext, subscribe: true })
267
+ ], ItemShowCorrectResponse.prototype, "computedContext", void 0);
268
+ __decorate3([
269
+ property3({ type: Boolean, reflect: true })
270
+ ], ItemShowCorrectResponse.prototype, "shown", void 0);
271
+ __decorate3([
272
+ property3({ type: Boolean, reflect: true })
273
+ ], ItemShowCorrectResponse.prototype, "disabled", void 0);
274
+ __decorate3([
275
+ property3({ type: String })
276
+ ], ItemShowCorrectResponse.prototype, "showCorrectText", void 0);
277
+ __decorate3([
278
+ property3({ type: String })
279
+ ], ItemShowCorrectResponse.prototype, "hideCorrectText", void 0);
280
+ __decorate3([
281
+ property3({ type: String })
282
+ ], ItemShowCorrectResponse.prototype, "noCorrectResponseText", void 0);
512
283
 
513
- // ../qti-corrections/dist/components/qti-assessment-item-correction.js
514
- var QtiAssessmentItemCorrection = class extends QtiAssessmentItem {
515
- showCorrectResponse(show) {
516
- for (const interaction of this.interactionElements) {
517
- interaction.toggleCorrectResponse?.(show);
518
- }
519
- this.updateControlState("item-show-correct-response", show);
284
+ // ../qti-corrections/dist/components/test-show-correct-response/test-show-correct-response.js
285
+ import { css as css4, html as html4, LitElement as LitElement4 } from "lit";
286
+ import { property as property4 } from "lit/decorators.js";
287
+ import { consume as consume4 } from "@lit/context";
288
+ var __decorate4 = function(decorators, target, key, desc) {
289
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
290
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
291
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
292
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
293
+ };
294
+ var TestShowCorrectResponse = class extends LitElement4 {
295
+ constructor() {
296
+ super(...arguments);
297
+ this.shown = false;
298
+ this.disabled = false;
299
+ this.showCorrectText = "Show correct response";
300
+ this.hideCorrectText = "Hide correct response";
301
+ this.noCorrectResponseText = "No correct response specified";
520
302
  }
521
- showCandidateCorrection(show) {
522
- for (const interaction of this.interactionElements) {
523
- interaction.toggleCandidateCorrection?.(show);
303
+ static {
304
+ this.styles = css4`
305
+ :host {
306
+ ${btn};
524
307
  }
525
- this.updateControlState("item-show-candidate-correction", show);
526
- }
527
- afterResponseVariableUpdated() {
528
- this.showCandidateCorrection(false);
308
+ :host([disabled]) {
309
+ ${dis};
310
+ }
311
+ `;
529
312
  }
530
- updateControlState(selector, shown) {
531
- let root = this.getRootNode();
532
- while (true) {
533
- root.querySelectorAll(selector).forEach((control) => control.shown = shown);
534
- if (!(root instanceof ShadowRoot))
535
- return;
536
- root = root.host.getRootNode();
313
+ #previousActiveItem;
314
+ // Store previous active item reference
315
+ willUpdate(_changedProperties) {
316
+ const activeItem = this.computedContext?.testParts.flatMap((testPart) => testPart.sections.flatMap((section) => section.items)).find((item) => item.active);
317
+ if (this.#previousActiveItem !== activeItem) {
318
+ this.shown = false;
319
+ this.#previousActiveItem = activeItem;
320
+ }
321
+ if (activeItem) {
322
+ const containsCorrectResponse = !!activeItem?.variables?.some((v) => v["correctResponse"]);
323
+ const containsMapping = !!activeItem?.variables?.some((v) => {
324
+ return v["mapping"]?.mapEntries?.length > 0 || v["areaMapping"]?.areaMapEntries?.length > 0;
325
+ });
326
+ this.disabled = !containsCorrectResponse && !containsMapping;
327
+ } else {
328
+ this.disabled = true;
537
329
  }
538
330
  }
539
- };
540
-
541
- // ../qti-corrections/dist/components/qti-item-correction.js
542
- var QtiItemCorrection = class extends QtiItem {
543
- #onShowCorrectResponse = (event) => {
544
- const show = event.detail;
545
- this.assessmentItem?.showCorrectResponse?.(show);
546
- this.updateControlState("item-show-correct-response", show);
547
- };
548
- #onShowCandidateCorrection = (event) => {
549
- const show = event.detail;
550
- this.assessmentItem?.showCandidateCorrection?.(show);
551
- this.updateControlState("item-show-candidate-correction", show);
552
- };
553
- #onSwitchCorrectResponseMode = (event) => {
554
- this.assessmentItem?.showCorrectResponse?.(false);
555
- this.configContext = {
556
- ...this.configContext,
557
- correctResponseMode: event.detail
558
- };
559
- };
560
- constructor() {
561
- super();
562
- this.addEventListener("item-show-correct-response", this.#onShowCorrectResponse);
563
- this.addEventListener("item-show-candidate-correction", this.#onShowCandidateCorrection);
564
- this.addEventListener("item-switch-correct-response-mode", this.#onSwitchCorrectResponseMode);
331
+ #toggleState() {
332
+ if (this.disabled)
333
+ return;
334
+ this.shown = !this.shown;
335
+ this.dispatchEvent(new CustomEvent("test-show-correct-response", {
336
+ detail: this.shown,
337
+ bubbles: true
338
+ }));
565
339
  }
566
- updateControlState(selector, shown) {
567
- this.querySelectorAll(selector).forEach((control) => control.shown = shown);
340
+ #getDisplayedText() {
341
+ return this.disabled ? this.noCorrectResponseText : this.shown ? this.hideCorrectText : this.showCorrectText;
568
342
  }
569
- disconnectedCallback() {
570
- this.removeEventListener("item-show-correct-response", this.#onShowCorrectResponse);
571
- this.removeEventListener("item-show-candidate-correction", this.#onShowCandidateCorrection);
572
- this.removeEventListener("item-switch-correct-response-mode", this.#onSwitchCorrectResponseMode);
573
- super.disconnectedCallback();
343
+ render() {
344
+ return html4` <div @click="${this.#toggleState}">${this.#getDisplayedText()}</div> `;
574
345
  }
575
346
  };
347
+ __decorate4([
348
+ consume4({ context: computedContext, subscribe: true })
349
+ ], TestShowCorrectResponse.prototype, "computedContext", void 0);
350
+ __decorate4([
351
+ property4({ type: Boolean, reflect: true })
352
+ ], TestShowCorrectResponse.prototype, "shown", void 0);
353
+ __decorate4([
354
+ property4({ type: Boolean, reflect: true })
355
+ ], TestShowCorrectResponse.prototype, "disabled", void 0);
356
+ __decorate4([
357
+ property4({ type: String })
358
+ ], TestShowCorrectResponse.prototype, "showCorrectText", void 0);
359
+ __decorate4([
360
+ property4({ type: String })
361
+ ], TestShowCorrectResponse.prototype, "hideCorrectText", void 0);
362
+ __decorate4([
363
+ property4({ type: String })
364
+ ], TestShowCorrectResponse.prototype, "noCorrectResponseText", void 0);
576
365
 
577
- // ../qti-corrections/dist/components/test-correction-elements.js
578
- var TestNavigationCorrection = class extends TestNavigation {
579
- #onShowCorrectResponse = (event) => {
580
- this.activeAssessmentItem?.showCorrectResponse?.(event.detail);
581
- };
582
- #onShowCandidateCorrection = (event) => {
583
- this.activeAssessmentItem?.showCandidateCorrection?.(event.detail);
584
- };
585
- constructor() {
586
- super();
587
- this.addEventListener("test-show-correct-response", this.#onShowCorrectResponse);
588
- this.addEventListener("test-show-candidate-correction", this.#onShowCandidateCorrection);
366
+ // ../qti-corrections/dist/styles/correction.styles.js
367
+ import { css as css5 } from "lit";
368
+ var correctionPart = css5`
369
+ /* Nothing at all until the item judges this element. */
370
+ [part~='correction'] {
371
+ display: none;
589
372
  }
590
- disconnectedCallback() {
591
- this.removeEventListener("test-show-correct-response", this.#onShowCorrectResponse);
592
- this.removeEventListener("test-show-candidate-correction", this.#onShowCandidateCorrection);
593
- super.disconnectedCallback();
373
+
374
+ [part~='correction-correct'],
375
+ [part~='correction-incorrect'],
376
+ [part~='correction-partially-correct'] {
377
+ display: inline-flex;
378
+ flex: 0 0 auto;
379
+
380
+ /*
381
+ * The badge aligns ITSELF, and that is the whole of the vertical story.
382
+ *
383
+ * It is a flex item with a definite height, so left alone its cross-axis position is decided by
384
+ * whatever its host says — and the hosts disagree: qti-simple-associable-choice defaults to
385
+ * align-items: normal, and qti-gap-text gates center behind :not([part~='drag']), so the
386
+ * same chip centred its badge in the bank and top-aligned it once placed. align-self settles it
387
+ * from the badge's side, in one line, without touching a single container.
388
+ *
389
+ * This replaces an align-items/justify-content: center pair that had never done anything: the
390
+ * badge is empty — a mask painted over a background — so centring its CONTENTS centres nothing.
391
+ */
392
+ align-self: center;
393
+
394
+ box-sizing: border-box;
395
+ width: var(--qti-correction-size, 1em);
396
+ height: var(--qti-correction-size, 1em);
397
+ margin-left: var(--qti-glyph-gap, 0.5em);
398
+
399
+ pointer-events: none;
594
400
  }
595
- /**
596
- * Reflect correctness back to the candidate after an ended attempt, for items
597
- * the assessment opted in via the standard `qti-item-session-control
598
- * show-solution` (QTI 3.0 ItemSessionControl.show-solution) the player takes
599
- * no opinion of its own:
600
- * - candidate correction (a mark on the learner's selection) after every attempt;
601
- * - the correct response (a mark on the right answer) once the item is done —
602
- * the candidate reached the optimal outcome, or ran out of attempts.
603
- */
604
- afterAttemptEnded(assessmentItem, computedItem, done) {
605
- if (!assessmentItem || !computedItem?.showSolution)
606
- return;
607
- const correctable = assessmentItem;
608
- correctable.showCandidateCorrection?.(true);
609
- correctable.showCorrectResponse?.(done);
401
+ `;
402
+
403
+ // ../qti-corrections/dist/elements/qti-gap-text-correction.js
404
+ import { html as html5 } from "lit";
405
+
406
+ // ../qti-corrections/dist/mixins/active-element-correction.mixin.js
407
+ var ActiveElementCorrectionMixin = (superClass) => {
408
+ class ActiveElementCorrectionMixinClass extends superClass {
409
+ #candidateCorrection = null;
410
+ get candidateCorrection() {
411
+ return this.#candidateCorrection;
412
+ }
413
+ set candidateCorrection(value) {
414
+ if (this.#candidateCorrection === value)
415
+ return;
416
+ const previous = this.#candidateCorrection;
417
+ this.#candidateCorrection = value;
418
+ const states = this.internals.states;
419
+ states.delete("candidate-correct");
420
+ states.delete("candidate-incorrect");
421
+ states.delete("candidate-partially-correct");
422
+ if (value)
423
+ states.add(`candidate-${value}`);
424
+ this.requestUpdate("candidateCorrection", previous);
425
+ }
426
+ get correctionPart() {
427
+ return this.#candidateCorrection ? `correction correction-${this.#candidateCorrection}` : "correction";
428
+ }
610
429
  }
430
+ return ActiveElementCorrectionMixinClass;
611
431
  };
612
- var QtiTestCorrection = class extends QtiTest {
613
- updateAssessmentItemView(assessmentItem, view) {
614
- assessmentItem.showCorrectResponse?.(view === "scorer");
432
+
433
+ // ../qti-corrections/dist/elements/qti-gap-text-correction.js
434
+ var QtiGapTextCorrection = class extends ActiveElementCorrectionMixin(QtiGapText) {
435
+ static {
436
+ this.styles = [QtiGapText.styles, correctionPart];
615
437
  }
616
- };
617
- var TestCheckItemCorrection = class extends TestCheckItem {
618
- constructor() {
619
- super();
620
- this.addEventListener("click", () => {
621
- this.dispatchEvent(new CustomEvent("test-show-correct-response", { detail: true, bubbles: true }));
622
- });
438
+ render() {
439
+ return html5`<div part="control"></div>
440
+ <slot part="label"></slot>
441
+ <span part=${this.correctionPart} aria-hidden="true"></span>`;
623
442
  }
624
443
  };
625
444
 
626
- // ../qti-corrections/dist/components/item-correct-response-mode/item-correct-response-mode.js
627
- import { html as html5, LitElement } from "lit";
628
- import { property as property3, state as state3 } from "lit/decorators.js";
629
- import { consume } from "@lit/context";
630
- var __decorate4 = function(decorators, target, key, desc) {
445
+ // ../qti-corrections/dist/elements/qti-hotspot-choice-correction.js
446
+ import { property as property5 } from "lit/decorators.js";
447
+ var __decorate5 = function(decorators, target, key, desc) {
631
448
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
632
449
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
633
450
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
634
451
  return c > 3 && r && Object.defineProperty(target, key, r), r;
635
452
  };
636
- var ItemCorrectResponseMode = class extends LitElement {
453
+ var QtiHotspotChoiceCorrection = class extends ActiveElementCorrectionMixin(QtiHotspotChoice) {
637
454
  constructor() {
638
455
  super(...arguments);
639
- this.label = "Correct response mode";
640
- this._options = {
641
- internal: "Internal",
642
- full: "Full (copy)"
643
- };
456
+ this.orderCorrect = null;
644
457
  }
645
- #switchMode(view) {
646
- this.dispatchEvent(new CustomEvent("item-switch-correct-response-mode", {
647
- detail: view,
648
- bubbles: true
649
- }));
458
+ };
459
+ __decorate5([
460
+ property5({ attribute: "aria-ordercorrectvalue", type: Number, reflect: true })
461
+ ], QtiHotspotChoiceCorrection.prototype, "orderCorrect", void 0);
462
+
463
+ // ../qti-corrections/dist/elements/qti-hottext-correction.js
464
+ import { html as html6 } from "lit";
465
+ var QtiHottextCorrection = class extends ActiveElementCorrectionMixin(QtiHottext) {
466
+ static {
467
+ this.styles = [QtiHottext.styles, correctionPart];
650
468
  }
651
469
  render() {
652
- return html5`
653
- <label part="label" for="modeSelect">${this.label}</label>
654
- <select
655
- part="select"
656
- id="modeSelect"
657
- @change=${(e) => {
658
- const el = e.target;
659
- this.#switchMode(el.value);
660
- }}
661
- >
662
- ${Object.keys(this._options).map((value) => html5`<option value="${value}" ?selected=${value === this.itemContext?.correctResponseMode}>
663
- ${this._options[value]}
664
- </option>`)}
665
- </select>
666
- `;
470
+ return html6`<div part="control"><div part="control-mark"></div></div>
471
+ <slot part="label"></slot>
472
+ <span part=${this.correctionPart} aria-hidden="true"></span>`;
667
473
  }
668
474
  };
669
- __decorate4([
670
- consume({ context: configContext, subscribe: true })
671
- ], ItemCorrectResponseMode.prototype, "itemContext", void 0);
672
- __decorate4([
673
- property3({ type: String })
674
- ], ItemCorrectResponseMode.prototype, "label", void 0);
675
- __decorate4([
676
- state3()
677
- ], ItemCorrectResponseMode.prototype, "_options", void 0);
678
475
 
679
- // ../qti-corrections/dist/components/item-show-candidate-correction/item-show-candidate-correction.js
680
- import { css as css3, html as html6, LitElement as LitElement2 } from "lit";
681
- import { property as property4 } from "lit/decorators.js";
682
- import { consume as consume2 } from "@lit/context";
476
+ // ../qti-corrections/dist/elements/qti-simple-associable-choice-correction.js
477
+ import { html as html7 } from "lit";
478
+ import { repeat } from "lit/directives/repeat.js";
479
+ var QtiSimpleAssociableChoiceCorrection = class extends ActiveElementCorrectionMixin(QtiSimpleAssociableChoice) {
480
+ static {
481
+ this.styles = [QtiSimpleAssociableChoice.styles, correctionPart];
482
+ }
483
+ render() {
484
+ return html7`
485
+ <div part="control"></div>
486
+ <slot part="label"></slot>
487
+ <span part=${this.correctionPart} aria-hidden="true"></span>
488
+ <div part="drop">
489
+ ${repeat(this.drags, (node) => node.getAttribute("identifier"), (node) => node)}
490
+ </div>
491
+ `;
492
+ }
493
+ };
683
494
 
684
- // ../qti-corrections/dist/components/styles.js
685
- import { css as css2 } from "lit";
686
- var form = css2`
687
- display: inline-flex;
688
- align-items: center;
689
- cursor: pointer;
690
- padding: 0.5rem 1rem;
691
- border-radius: 0.25rem;
692
- user-select: none;
693
- `;
694
- var btn = css2`
695
- background-color: lightgray;
696
- ${form};
697
- `;
698
- var dis = css2`
699
- cursor: not-allowed;
700
- opacity: 0.8;
701
- `;
702
- var ind = css2`
703
- ${form};
704
- border: 1px solid gray;
705
- `;
495
+ // ../qti-corrections/dist/elements/qti-simple-choice-correction.js
496
+ import { html as html8, nothing } from "lit";
497
+ var QtiSimpleChoiceCorrection = class extends ActiveElementCorrectionMixin(QtiSimpleChoice) {
498
+ static {
499
+ this.styles = [QtiSimpleChoice.styles, correctionPart];
500
+ }
501
+ render() {
502
+ return html8`<div part="control" tabindex="0"><div part="control-mark"></div></div>
503
+ ${this.marker ? html8`<div id="label" part="marker">${this.marker}</div>` : nothing}
504
+ <slot part="label"></slot>
505
+ <span part=${this.correctionPart} aria-hidden="true"></span>`;
506
+ }
507
+ };
706
508
 
707
- // ../qti-corrections/dist/components/item-show-candidate-correction/item-show-candidate-correction.js
708
- var __decorate5 = function(decorators, target, key, desc) {
509
+ // ../qti-corrections/dist/mixins/correct-response.mixin.js
510
+ import { property as property6, state as state2 } from "lit/decorators.js";
511
+ var __decorate6 = function(decorators, target, key, desc) {
709
512
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
710
513
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
711
514
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
712
515
  return c > 3 && r && Object.defineProperty(target, key, r), r;
713
516
  };
714
- var ItemShowCandidateCorrection = class extends LitElement2 {
715
- static {
716
- this.styles = css3`
717
- :host {
718
- ${btn};
517
+ var Correctness;
518
+ (function(Correctness2) {
519
+ Correctness2["Correct"] = "correct";
520
+ Correctness2["PartiallyCorrect"] = "partially-correct";
521
+ Correctness2["Incorrect"] = "incorrect";
522
+ })(Correctness || (Correctness = {}));
523
+ var CorrectResponseMixin = (superClass) => {
524
+ class CorrectResponseMixinClass extends superClass {
525
+ constructor() {
526
+ super(...arguments);
527
+ this.showCorrectResponse = false;
528
+ this._handleShowCorrectResponseChange = (_, show) => {
529
+ this.toggleInternalCorrectResponse(show);
530
+ };
531
+ this.showFullCorrectResponse = false;
532
+ this._handleShowFullCorrectResponseChange = (_, show) => {
533
+ this.toggleFullCorrectResponse(show);
534
+ };
535
+ this.showCandidateCorrection = false;
536
+ this._handleShowCandidateCorrectionChange = (_, show) => {
537
+ this.toggleCandidateCorrection(show);
538
+ };
539
+ this._correctResponse = null;
540
+ }
541
+ /**
542
+ * The correct response for this interaction. Can be set as a comma-separated
543
+ * string via attribute or as string/string[] via property.
544
+ *
545
+ * @example
546
+ * ```html
547
+ * <qti-choice-interaction correct-response="A,B">
548
+ * ```
549
+ */
550
+ set correctResponseAttr(val) {
551
+ this._correctResponse = val;
552
+ }
553
+ get correctResponseAttr() {
554
+ return this._correctResponse;
555
+ }
556
+ get isFullCorrectResponse() {
557
+ return this._isFullCorrectResponse;
558
+ }
559
+ get registersWithItem() {
560
+ return !this.isFullCorrectResponse && super.registersWithItem;
561
+ }
562
+ set isFullCorrectResponse(val) {
563
+ this._isFullCorrectResponse = val;
564
+ if (val) {
565
+ this.setAttribute("inert", "");
566
+ this.setAttribute("response-identifier", this.responseIdentifier + "_cr");
567
+ this.setAttribute("answer-key", "");
568
+ }
569
+ }
570
+ get correctResponse() {
571
+ const responseVariable = this.responseVariable;
572
+ if (responseVariable?.correctResponse) {
573
+ return responseVariable.correctResponse;
574
+ }
575
+ return this._correctResponse;
576
+ }
577
+ set correctResponse(val) {
578
+ const oldVal = this._correctResponse;
579
+ this._correctResponse = val;
580
+ if (val === null || val === void 0) {
581
+ this.removeAttribute("correct-response");
582
+ } else {
583
+ this.setAttribute("correct-response", Array.isArray(val) ? val.join(",") : val);
584
+ }
585
+ this.requestUpdate("_correctResponse", oldVal);
586
+ }
587
+ // ─── correctness getter ───────────────────────────────────────────────────
588
+ get correctness() {
589
+ const correctResponse = this.correctResponse;
590
+ if (!correctResponse)
591
+ return null;
592
+ const currentResponse = this.response;
593
+ if (currentResponse === null || currentResponse === void 0 || currentResponse === "" || Array.isArray(currentResponse) && currentResponse.length === 0) {
594
+ return null;
595
+ }
596
+ const correctArr = Array.isArray(correctResponse) ? correctResponse : [correctResponse];
597
+ const responseArr = Array.isArray(currentResponse) ? currentResponse : [currentResponse];
598
+ const filteredResponseArr = responseArr.filter((r) => r !== "");
599
+ if (filteredResponseArr.length === 0)
600
+ return null;
601
+ const correctCount = filteredResponseArr.filter((r) => correctArr.includes(r)).length;
602
+ const incorrectCount = filteredResponseArr.filter((r) => !correctArr.includes(r)).length;
603
+ if (correctCount === correctArr.length && incorrectCount === 0 && filteredResponseArr.length === correctArr.length) {
604
+ return Correctness.Correct;
605
+ }
606
+ if (correctCount > 0 && (correctCount < correctArr.length || incorrectCount > 0)) {
607
+ return Correctness.PartiallyCorrect;
608
+ }
609
+ return Correctness.Incorrect;
719
610
  }
720
- :host([disabled]) {
721
- ${dis};
611
+ // ─── correctionPart ───────────────────────────────────────────────────────
612
+ get correctionPart() {
613
+ return "correction";
722
614
  }
723
- `;
724
- }
725
- #hasCorrectResponse;
726
- // correct response is removed on certain point
727
- #previousActiveItem;
728
- // Store previous active item reference
729
- constructor() {
730
- super();
731
- this.shown = false;
732
- this.disabled = false;
733
- this.showCandidateCorrectionText = "Show candidate correction";
734
- this.hideCandidateCorrectionText = "Hide candidate correction";
735
- this.noCorrectResponseText = "No correct response specified";
736
- this.#hasCorrectResponse = false;
737
- this.#previousActiveItem = "";
738
- }
739
- updated() {
740
- if (!this.#hasCorrectResponse || this.#previousActiveItem !== this.computedContext?.identifier) {
741
- this.#previousActiveItem = this.computedContext?.identifier;
742
- const containsCorrectResponse = !!this.computedContext?.variables.some((v) => v["correctResponse"]);
743
- const containsMapping = !!this.computedContext?.variables.some((v) => {
744
- return v["mapping"]?.mapEntries?.length > 0 || v["areaMapping"]?.areaMapEntries?.length > 0;
745
- });
746
- this.#hasCorrectResponse = containsCorrectResponse || containsMapping;
615
+ // ─── toggle methods ───────────────────────────────────────────────────────
616
+ toggleCorrectResponse(show) {
617
+ const correctionConfig = this.configContext;
618
+ const correctResponseMode = correctionConfig?.correctResponseMode || "internal";
619
+ if (correctResponseMode === "full") {
620
+ this.toggleFullCorrectResponse(show);
621
+ } else {
622
+ this.toggleInternalCorrectResponse(show);
623
+ }
747
624
  }
748
- this.disabled = !this.#hasCorrectResponse;
749
- }
750
- #toggleState() {
751
- if (this.disabled)
752
- return;
753
- this.dispatchEvent(new CustomEvent("item-show-candidate-correction", {
754
- detail: !this.shown,
755
- bubbles: true
756
- }));
757
- }
758
- #getDisplayedText() {
759
- return this.disabled ? this.noCorrectResponseText : this.shown ? this.hideCandidateCorrectionText : this.showCandidateCorrectionText;
760
- }
761
- render() {
762
- return html6` <div @click="${this.#toggleState}">${this.#getDisplayedText()}</div> `;
763
- }
764
- };
765
- __decorate5([
766
- consume2({ context: computedItemContext, subscribe: true })
767
- ], ItemShowCandidateCorrection.prototype, "computedContext", void 0);
768
- __decorate5([
769
- property4({ type: Boolean, reflect: true })
770
- ], ItemShowCandidateCorrection.prototype, "shown", void 0);
771
- __decorate5([
772
- property4({ type: Boolean, reflect: true })
773
- ], ItemShowCandidateCorrection.prototype, "disabled", void 0);
774
- __decorate5([
775
- property4({ type: String })
776
- ], ItemShowCandidateCorrection.prototype, "showCandidateCorrectionText", void 0);
777
- __decorate5([
778
- property4({ type: String })
779
- ], ItemShowCandidateCorrection.prototype, "hideCandidateCorrectionText", void 0);
780
- __decorate5([
781
- property4({ type: String })
782
- ], ItemShowCandidateCorrection.prototype, "noCorrectResponseText", void 0);
783
-
784
- // ../qti-corrections/dist/components/item-show-correct-response/item-show-correct-response.js
785
- import { css as css4, html as html7, LitElement as LitElement3 } from "lit";
786
- import { property as property5 } from "lit/decorators.js";
787
- import { consume as consume3 } from "@lit/context";
788
- var __decorate6 = function(decorators, target, key, desc) {
789
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
790
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
791
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
792
- return c > 3 && r && Object.defineProperty(target, key, r), r;
793
- };
794
- var ItemShowCorrectResponse = class extends LitElement3 {
795
- constructor() {
796
- super(...arguments);
797
- this.shown = false;
798
- this.disabled = false;
799
- this.showCorrectText = "Show correct response";
800
- this.hideCorrectText = "Hide correct response";
801
- this.noCorrectResponseText = "No correct response specified";
802
- this.#hasCorrectResponse = false;
803
- this.#previousActiveItem = "";
804
- }
805
- static {
806
- this.styles = css4`
807
- :host {
808
- ${btn};
625
+ /**
626
+ * Whether the full correct response is withheld from a candidate who is already correct.
627
+ *
628
+ * Withheld by default, unless the item configures `fullCorrectResponseOnlyWhenIncorrect: false`
629
+ * — the behaviour `show-full-correct-response` has always had, on the reasoning that a correct
630
+ * answer already *is* the key.
631
+ *
632
+ * An interaction that routes its internal mode here overrides this to false: there, the caller
633
+ * asked to show the correct response, and silently showing nothing is worse than showing the
634
+ * candidate the answer they already gave.
635
+ */
636
+ get withholdsFullCorrectResponseWhenCorrect() {
637
+ return this.configContext?.fullCorrectResponseOnlyWhenIncorrect !== false;
809
638
  }
810
- :host([disabled]) {
811
- ${dis};
639
+ async toggleFullCorrectResponse(show) {
640
+ const nextSibling = this.nextSibling;
641
+ const nextSiblingIsFullCorrectResponse = nextSibling instanceof HTMLDivElement && nextSibling?.classList.contains("full-correct-response");
642
+ const correctResponse = this.correctResponse;
643
+ if (!correctResponse) {
644
+ return;
645
+ }
646
+ const showFullCorrectResponse = show && (!this.withholdsFullCorrectResponseWhenCorrect || this.correctness !== Correctness.Correct);
647
+ if (!showFullCorrectResponse) {
648
+ if (!nextSiblingIsFullCorrectResponse) {
649
+ return;
650
+ }
651
+ this.parentElement?.removeChild(nextSibling);
652
+ return;
653
+ }
654
+ if (nextSiblingIsFullCorrectResponse) {
655
+ return;
656
+ }
657
+ const clone = this.cloneNode(true);
658
+ clone.removeAttribute("show-full-correct-response");
659
+ clone.removeAttribute("show-correct-response");
660
+ clone.removeAttribute("show-candidate-correction");
661
+ const containerDiv = document.createElement("div");
662
+ containerDiv.classList.add("full-correct-response");
663
+ if (this.isInline) {
664
+ containerDiv.classList.add("full-correct-response-inline");
665
+ } else {
666
+ containerDiv.classList.add("full-correct-response-block");
667
+ }
668
+ containerDiv.role = "full-correct-response";
669
+ containerDiv.appendChild(clone);
670
+ clone.isFullCorrectResponse = true;
671
+ this.parentElement?.insertBefore(containerDiv, this.nextSibling);
672
+ await clone.updateComplete;
673
+ clone.response = Array.isArray(correctResponse) ? [...correctResponse] : correctResponse;
812
674
  }
813
- `;
814
- }
815
- #hasCorrectResponse;
816
- // correct response is removed on certain point
817
- #previousActiveItem;
818
- // Store previous active item reference
819
- updated() {
820
- if (!this.#hasCorrectResponse || this.#previousActiveItem !== this.computedContext?.identifier) {
821
- this.#previousActiveItem = this.computedContext?.identifier;
822
- const containsCorrectResponse = !!this.computedContext?.variables.some((v) => v["correctResponse"]);
823
- const containsMapping = !!this.computedContext?.variables.some((v) => {
824
- return v["mapping"]?.mapEntries?.length > 0 || v["areaMapping"]?.areaMapEntries?.length > 0;
825
- });
826
- this.#hasCorrectResponse = containsCorrectResponse || containsMapping;
675
+ toggleInternalCorrectResponse(show) {
676
+ this._internals.states.delete("show-correct-response");
677
+ if (show && this.correctResponse) {
678
+ this._internals.states.add("show-correct-response");
679
+ }
680
+ }
681
+ toggleCandidateCorrection(show) {
682
+ if (!show) {
683
+ return;
684
+ }
685
+ }
686
+ // ─── Lifecycle ────────────────────────────────────────────────────────────
687
+ firstUpdated() {
688
+ super.firstUpdated();
689
+ if (this.isFullCorrectResponse) {
690
+ return;
691
+ }
692
+ if (this.showCorrectResponse) {
693
+ this.toggleInternalCorrectResponse(true);
694
+ }
695
+ if (this.showFullCorrectResponse) {
696
+ this.toggleFullCorrectResponse(true);
697
+ }
827
698
  }
828
- this.disabled = !this.#hasCorrectResponse;
829
- }
830
- #toggleState() {
831
- if (this.disabled)
832
- return;
833
- this.dispatchEvent(new CustomEvent("item-show-correct-response", {
834
- detail: !this.shown,
835
- bubbles: true
836
- }));
837
- }
838
- #getDisplayedText() {
839
- return this.disabled ? this.noCorrectResponseText : this.shown ? this.hideCorrectText : this.showCorrectText;
840
- }
841
- render() {
842
- return html7` <div @click="${this.#toggleState}">${this.#getDisplayedText()}</div> `;
843
699
  }
700
+ __decorate6([
701
+ property6({
702
+ attribute: "correct-response",
703
+ reflect: true,
704
+ converter: responseAttributeConverter({ emptyAs: null })
705
+ })
706
+ ], CorrectResponseMixinClass.prototype, "correctResponseAttr", null);
707
+ __decorate6([
708
+ property6({ type: Boolean, attribute: "show-correct-response", reflect: true })
709
+ ], CorrectResponseMixinClass.prototype, "showCorrectResponse", void 0);
710
+ __decorate6([
711
+ watch("showCorrectResponse", { waitUntilFirstUpdate: true })
712
+ ], CorrectResponseMixinClass.prototype, "_handleShowCorrectResponseChange", void 0);
713
+ __decorate6([
714
+ property6({ type: Boolean, attribute: "show-full-correct-response", reflect: true })
715
+ ], CorrectResponseMixinClass.prototype, "showFullCorrectResponse", void 0);
716
+ __decorate6([
717
+ watch("showFullCorrectResponse", { waitUntilFirstUpdate: true })
718
+ ], CorrectResponseMixinClass.prototype, "_handleShowFullCorrectResponseChange", void 0);
719
+ __decorate6([
720
+ property6({ type: Boolean, attribute: "show-candidate-correction", reflect: true })
721
+ ], CorrectResponseMixinClass.prototype, "showCandidateCorrection", void 0);
722
+ __decorate6([
723
+ watch("showCandidateCorrection", { waitUntilFirstUpdate: true })
724
+ ], CorrectResponseMixinClass.prototype, "_handleShowCandidateCorrectionChange", void 0);
725
+ __decorate6([
726
+ state2()
727
+ ], CorrectResponseMixinClass.prototype, "_isFullCorrectResponse", void 0);
728
+ __decorate6([
729
+ state2()
730
+ ], CorrectResponseMixinClass.prototype, "_correctResponse", void 0);
731
+ return CorrectResponseMixinClass;
844
732
  };
845
- __decorate6([
846
- consume3({ context: computedItemContext, subscribe: true })
847
- ], ItemShowCorrectResponse.prototype, "computedContext", void 0);
848
- __decorate6([
849
- property5({ type: Boolean, reflect: true })
850
- ], ItemShowCorrectResponse.prototype, "shown", void 0);
851
- __decorate6([
852
- property5({ type: Boolean, reflect: true })
853
- ], ItemShowCorrectResponse.prototype, "disabled", void 0);
854
- __decorate6([
855
- property5({ type: String })
856
- ], ItemShowCorrectResponse.prototype, "showCorrectText", void 0);
857
- __decorate6([
858
- property5({ type: String })
859
- ], ItemShowCorrectResponse.prototype, "hideCorrectText", void 0);
860
- __decorate6([
861
- property5({ type: String })
862
- ], ItemShowCorrectResponse.prototype, "noCorrectResponseText", void 0);
863
733
 
864
- // ../qti-corrections/dist/components/test-show-correct-response/test-show-correct-response.js
865
- import { css as css5, html as html8, LitElement as LitElement4 } from "lit";
866
- import { property as property6 } from "lit/decorators.js";
867
- import { consume as consume4 } from "@lit/context";
734
+ // ../qti-corrections/dist/mixins/candidate-correction.mixin.js
735
+ import { state as state3 } from "lit/decorators.js";
868
736
  var __decorate7 = function(decorators, target, key, desc) {
869
737
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
870
738
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
871
739
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
872
740
  return c > 3 && r && Object.defineProperty(target, key, r), r;
873
741
  };
874
- var TestShowCorrectResponse = class extends LitElement4 {
875
- constructor() {
876
- super(...arguments);
877
- this.shown = false;
878
- this.disabled = false;
879
- this.showCorrectText = "Show correct response";
880
- this.hideCorrectText = "Hide correct response";
881
- this.noCorrectResponseText = "No correct response specified";
882
- }
883
- static {
884
- this.styles = css5`
885
- :host {
886
- ${btn};
742
+ var CandidateCorrectionMixin = (superClass) => {
743
+ class CandidateCorrectionMixinClass extends CorrectResponseMixin(superClass) {
744
+ constructor() {
745
+ super(...arguments);
746
+ this._candidateCorrection = null;
887
747
  }
888
- :host([disabled]) {
889
- ${dis};
748
+ get correctionPart() {
749
+ return this._candidateCorrection ? `correction correction-${this._candidateCorrection}` : "correction";
890
750
  }
891
- `;
892
- }
893
- #previousActiveItem;
894
- // Store previous active item reference
895
- willUpdate(_changedProperties) {
896
- const activeItem = this.computedContext?.testParts.flatMap((testPart) => testPart.sections.flatMap((section) => section.items)).find((item) => item.active);
897
- if (this.#previousActiveItem !== activeItem) {
898
- this.shown = false;
899
- this.#previousActiveItem = activeItem;
751
+ toggleCandidateCorrection(show) {
752
+ if (this.showCandidateCorrection !== show) {
753
+ this.showCandidateCorrection = show;
754
+ }
755
+ super.toggleCandidateCorrection(show);
756
+ const internals = this._internals;
757
+ internals.states.delete("candidate-correct");
758
+ internals.states.delete("candidate-partially-correct");
759
+ internals.states.delete("candidate-incorrect");
760
+ let verdict = null;
761
+ if (show) {
762
+ if (this.correctness === Correctness.Correct) {
763
+ internals.states.add("candidate-correct");
764
+ verdict = "correct";
765
+ }
766
+ if (this.correctness === Correctness.PartiallyCorrect) {
767
+ internals.states.add("candidate-partially-correct");
768
+ verdict = "partially-correct";
769
+ }
770
+ if (this.correctness === Correctness.Incorrect) {
771
+ internals.states.add("candidate-incorrect");
772
+ verdict = "incorrect";
773
+ }
774
+ }
775
+ if (this._candidateCorrection !== verdict) {
776
+ this._candidateCorrection = verdict;
777
+ }
900
778
  }
901
- if (activeItem) {
902
- const containsCorrectResponse = !!activeItem?.variables?.some((v) => v["correctResponse"]);
903
- const containsMapping = !!activeItem?.variables?.some((v) => {
904
- return v["mapping"]?.mapEntries?.length > 0 || v["areaMapping"]?.areaMapEntries?.length > 0;
779
+ connectedCallback() {
780
+ super.connectedCallback?.();
781
+ queueMicrotask(() => {
782
+ if (!this.isConnected || this.isFullCorrectResponse) {
783
+ return;
784
+ }
785
+ if (this.showCandidateCorrection) {
786
+ this.toggleCandidateCorrection(true);
787
+ }
905
788
  });
906
- this.disabled = !containsCorrectResponse && !containsMapping;
907
- } else {
908
- this.disabled = true;
909
789
  }
910
790
  }
911
- #toggleState() {
912
- if (this.disabled)
913
- return;
914
- this.shown = !this.shown;
915
- this.dispatchEvent(new CustomEvent("test-show-correct-response", {
916
- detail: this.shown,
917
- bubbles: true
918
- }));
919
- }
920
- #getDisplayedText() {
921
- return this.disabled ? this.noCorrectResponseText : this.shown ? this.hideCorrectText : this.showCorrectText;
922
- }
923
- render() {
924
- return html8` <div @click="${this.#toggleState}">${this.#getDisplayedText()}</div> `;
925
- }
791
+ __decorate7([
792
+ state3()
793
+ ], CandidateCorrectionMixinClass.prototype, "_candidateCorrection", void 0);
794
+ return CandidateCorrectionMixinClass;
926
795
  };
927
- __decorate7([
928
- consume4({ context: computedContext, subscribe: true })
929
- ], TestShowCorrectResponse.prototype, "computedContext", void 0);
930
- __decorate7([
931
- property6({ type: Boolean, reflect: true })
932
- ], TestShowCorrectResponse.prototype, "shown", void 0);
933
- __decorate7([
934
- property6({ type: Boolean, reflect: true })
935
- ], TestShowCorrectResponse.prototype, "disabled", void 0);
936
- __decorate7([
937
- property6({ type: String })
938
- ], TestShowCorrectResponse.prototype, "showCorrectText", void 0);
939
- __decorate7([
940
- property6({ type: String })
941
- ], TestShowCorrectResponse.prototype, "hideCorrectText", void 0);
942
- __decorate7([
943
- property6({ type: String })
944
- ], TestShowCorrectResponse.prototype, "noCorrectResponseText", void 0);
945
796
 
946
797
  // ../qti-corrections/dist/mixins/drag-drop-correction.mixin.js
947
798
  var DragDropCorrectionMixin = (superClass) => {
@@ -968,6 +819,42 @@ var DragDropCorrectionMixin = (superClass) => {
968
819
  var QtiAssociateInteractionCorrection = class extends DragDropCorrectionMixin(QtiAssociateInteraction) {
969
820
  };
970
821
 
822
+ // ../qti-corrections/dist/mixins/choice-correction.mixin.js
823
+ var ChoiceCorrectionMixin = (superClass) => {
824
+ class ChoiceCorrectionMixinClass extends CandidateCorrectionMixin(superClass) {
825
+ toggleInternalCorrectResponse(show) {
826
+ super.toggleInternalCorrectResponse(show);
827
+ const correctResponse = this.correctResponse;
828
+ const correctResponses = correctResponse ? Array.isArray(correctResponse) ? correctResponse : [correctResponse] : [];
829
+ for (const choice of this._choiceElements) {
830
+ choice.internals.states.delete("correct-response");
831
+ choice.internals.states.delete("incorrect-response");
832
+ if (show && correctResponses.length > 0) {
833
+ choice.internals.states.add(correctResponses.includes(choice.identifier) ? "correct-response" : "incorrect-response");
834
+ }
835
+ }
836
+ }
837
+ toggleCandidateCorrection(show) {
838
+ super.toggleCandidateCorrection(show);
839
+ for (const choice of this._choiceElements) {
840
+ choice.candidateCorrection = null;
841
+ }
842
+ if (!show || !this.correctResponse) {
843
+ return;
844
+ }
845
+ const correctResponses = Array.isArray(this.correctResponse) ? this.correctResponse : [this.correctResponse];
846
+ const candidateResponses = Array.isArray(this.response) ? this.response : this.response ? [this.response] : [];
847
+ for (const choice of this._choiceElements) {
848
+ if (!candidateResponses.includes(choice.identifier)) {
849
+ continue;
850
+ }
851
+ choice.candidateCorrection = correctResponses.includes(choice.identifier) ? "correct" : "incorrect";
852
+ }
853
+ }
854
+ }
855
+ return ChoiceCorrectionMixinClass;
856
+ };
857
+
971
858
  // ../qti-corrections/dist/interactions/qti-choice-interaction-correction.js
972
859
  var QtiChoiceInteractionCorrection = class extends ChoiceCorrectionMixin(QtiChoiceInteraction) {
973
860
  };
@@ -1610,27 +1497,135 @@ var QtiTextEntryInteractionCorrection = class extends CandidateCorrectionMixin(Q
1610
1497
  }
1611
1498
  };
1612
1499
 
1500
+ // ../qti-corrections/dist/components/qti-assessment-item-correction.js
1501
+ var QtiAssessmentItemCorrection = class extends QtiAssessmentItem {
1502
+ showCorrectResponse(show) {
1503
+ for (const interaction of this.interactionElements) {
1504
+ interaction.toggleCorrectResponse?.(show);
1505
+ }
1506
+ this.updateControlState("item-show-correct-response", show);
1507
+ }
1508
+ showCandidateCorrection(show) {
1509
+ for (const interaction of this.interactionElements) {
1510
+ interaction.toggleCandidateCorrection?.(show);
1511
+ }
1512
+ this.updateControlState("item-show-candidate-correction", show);
1513
+ }
1514
+ afterResponseVariableUpdated() {
1515
+ this.showCandidateCorrection(false);
1516
+ }
1517
+ updateControlState(selector, shown) {
1518
+ let root = this.getRootNode();
1519
+ while (true) {
1520
+ root.querySelectorAll(selector).forEach((control) => control.shown = shown);
1521
+ if (!(root instanceof ShadowRoot))
1522
+ return;
1523
+ root = root.host.getRootNode();
1524
+ }
1525
+ }
1526
+ };
1527
+
1528
+ // ../qti-corrections/dist/components/qti-item-correction.js
1529
+ var QtiItemCorrection = class extends QtiItem {
1530
+ #onShowCorrectResponse = (event) => {
1531
+ const show = event.detail;
1532
+ this.assessmentItem?.showCorrectResponse?.(show);
1533
+ this.updateControlState("item-show-correct-response", show);
1534
+ };
1535
+ #onShowCandidateCorrection = (event) => {
1536
+ const show = event.detail;
1537
+ this.assessmentItem?.showCandidateCorrection?.(show);
1538
+ this.updateControlState("item-show-candidate-correction", show);
1539
+ };
1540
+ #onSwitchCorrectResponseMode = (event) => {
1541
+ this.assessmentItem?.showCorrectResponse?.(false);
1542
+ this.configContext = {
1543
+ ...this.configContext,
1544
+ correctResponseMode: event.detail
1545
+ };
1546
+ };
1547
+ constructor() {
1548
+ super();
1549
+ this.addEventListener("item-show-correct-response", this.#onShowCorrectResponse);
1550
+ this.addEventListener("item-show-candidate-correction", this.#onShowCandidateCorrection);
1551
+ this.addEventListener("item-switch-correct-response-mode", this.#onSwitchCorrectResponseMode);
1552
+ }
1553
+ updateControlState(selector, shown) {
1554
+ this.querySelectorAll(selector).forEach((control) => control.shown = shown);
1555
+ }
1556
+ disconnectedCallback() {
1557
+ this.removeEventListener("item-show-correct-response", this.#onShowCorrectResponse);
1558
+ this.removeEventListener("item-show-candidate-correction", this.#onShowCandidateCorrection);
1559
+ this.removeEventListener("item-switch-correct-response-mode", this.#onSwitchCorrectResponseMode);
1560
+ super.disconnectedCallback();
1561
+ }
1562
+ };
1563
+
1564
+ // ../qti-corrections/dist/components/test-correction-elements.js
1565
+ var TestNavigationCorrection = class extends TestNavigation {
1566
+ #onShowCorrectResponse = (event) => {
1567
+ this.activeAssessmentItem?.showCorrectResponse?.(event.detail);
1568
+ };
1569
+ #onShowCandidateCorrection = (event) => {
1570
+ this.activeAssessmentItem?.showCandidateCorrection?.(event.detail);
1571
+ };
1572
+ constructor() {
1573
+ super();
1574
+ this.addEventListener("test-show-correct-response", this.#onShowCorrectResponse);
1575
+ this.addEventListener("test-show-candidate-correction", this.#onShowCandidateCorrection);
1576
+ }
1577
+ disconnectedCallback() {
1578
+ this.removeEventListener("test-show-correct-response", this.#onShowCorrectResponse);
1579
+ this.removeEventListener("test-show-candidate-correction", this.#onShowCandidateCorrection);
1580
+ super.disconnectedCallback();
1581
+ }
1582
+ /**
1583
+ * Reflect correctness back to the candidate after an ended attempt, for items
1584
+ * the assessment opted in via the standard `qti-item-session-control
1585
+ * show-solution` (QTI 3.0 ItemSessionControl.show-solution) — the player takes
1586
+ * no opinion of its own:
1587
+ * - candidate correction (a mark on the learner's selection) after every attempt;
1588
+ * - the correct response (a mark on the right answer) once the item is done —
1589
+ * the candidate reached the optimal outcome, or ran out of attempts.
1590
+ */
1591
+ afterAttemptEnded(assessmentItem, computedItem, done) {
1592
+ if (!assessmentItem || !computedItem?.showSolution)
1593
+ return;
1594
+ const correctable = assessmentItem;
1595
+ correctable.showCandidateCorrection?.(true);
1596
+ correctable.showCorrectResponse?.(done);
1597
+ }
1598
+ };
1599
+ var QtiTestCorrection = class extends QtiTest {
1600
+ updateAssessmentItemView(assessmentItem, view) {
1601
+ assessmentItem.showCorrectResponse?.(view === "scorer");
1602
+ }
1603
+ };
1604
+ var TestCheckItemCorrection = class extends TestCheckItem {
1605
+ constructor() {
1606
+ super();
1607
+ this.addEventListener("click", () => {
1608
+ this.dispatchEvent(new CustomEvent("test-show-correct-response", { detail: true, bubbles: true }));
1609
+ });
1610
+ }
1611
+ };
1612
+
1613
1613
  export {
1614
- Correctness,
1615
- CorrectResponseMixin,
1614
+ ItemCorrectResponseMode,
1615
+ ItemShowCandidateCorrection,
1616
+ ItemShowCorrectResponse,
1617
+ TestShowCorrectResponse,
1616
1618
  correctionPart,
1617
- CandidateCorrectionMixin,
1618
- ChoiceCorrectionMixin,
1619
1619
  QtiGapTextCorrection,
1620
1620
  QtiHotspotChoiceCorrection,
1621
1621
  QtiHottextCorrection,
1622
1622
  QtiSimpleAssociableChoiceCorrection,
1623
1623
  QtiSimpleChoiceCorrection,
1624
- QtiAssessmentItemCorrection,
1625
- QtiItemCorrection,
1626
- TestNavigationCorrection,
1627
- QtiTestCorrection,
1628
- TestCheckItemCorrection,
1629
- ItemCorrectResponseMode,
1630
- ItemShowCandidateCorrection,
1631
- ItemShowCorrectResponse,
1632
- TestShowCorrectResponse,
1624
+ Correctness,
1625
+ CorrectResponseMixin,
1626
+ CandidateCorrectionMixin,
1633
1627
  QtiAssociateInteractionCorrection,
1628
+ ChoiceCorrectionMixin,
1634
1629
  QtiChoiceInteractionCorrection,
1635
1630
  QtiExtendedTextInteractionCorrection,
1636
1631
  QtiGapMatchInteractionCorrection,
@@ -1645,6 +1640,11 @@ export {
1645
1640
  QtiPortableCustomInteractionCorrection,
1646
1641
  QtiSelectPointInteractionCorrection,
1647
1642
  QtiSliderInteractionCorrection,
1648
- QtiTextEntryInteractionCorrection
1643
+ QtiTextEntryInteractionCorrection,
1644
+ QtiAssessmentItemCorrection,
1645
+ QtiItemCorrection,
1646
+ TestNavigationCorrection,
1647
+ QtiTestCorrection,
1648
+ TestCheckItemCorrection
1649
1649
  };
1650
- //# sourceMappingURL=chunk-OIUK5BFU.js.map
1650
+ //# sourceMappingURL=chunk-5SU3MP6Y.js.map