@intellectif/lk-react 2.0.0 → 3.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.
Files changed (58) hide show
  1. package/dist/WrittenResponse-B50EjVR0.d.cts +87 -0
  2. package/dist/WrittenResponse-BHhP936i.d.ts +87 -0
  3. package/dist/{chunk-32WWQOG3.js → chunk-3X7MYAFT.js} +99 -26
  4. package/dist/chunk-3X7MYAFT.js.map +1 -0
  5. package/dist/chunk-7MW7HGOA.cjs +258 -0
  6. package/dist/chunk-7MW7HGOA.cjs.map +1 -0
  7. package/dist/chunk-AEQQ7VPF.js +256 -0
  8. package/dist/chunk-AEQQ7VPF.js.map +1 -0
  9. package/dist/chunk-BGWXN44S.js +152 -0
  10. package/dist/chunk-BGWXN44S.js.map +1 -0
  11. package/dist/chunk-HO3AC3WQ.cjs +154 -0
  12. package/dist/chunk-HO3AC3WQ.cjs.map +1 -0
  13. package/dist/{chunk-73XCWYKD.cjs → chunk-UXO6KYSZ.cjs} +97 -24
  14. package/dist/chunk-UXO6KYSZ.cjs.map +1 -0
  15. package/dist/{chunk-MKH5THDR.cjs → chunk-W6BH2YMZ.cjs} +119 -36
  16. package/dist/chunk-W6BH2YMZ.cjs.map +1 -0
  17. package/dist/{chunk-G5ZEWJRA.js → chunk-ZVFWJTT4.js} +119 -36
  18. package/dist/chunk-ZVFWJTT4.js.map +1 -0
  19. package/dist/components/ActivitySequence.cjs +5 -4
  20. package/dist/components/ActivitySequence.d.cts +52 -4
  21. package/dist/components/ActivitySequence.d.ts +52 -4
  22. package/dist/components/ActivitySequence.js +4 -3
  23. package/dist/components/FillInTheBlanks.cjs +2 -2
  24. package/dist/components/FillInTheBlanks.d.cts +7 -1
  25. package/dist/components/FillInTheBlanks.d.ts +7 -1
  26. package/dist/components/FillInTheBlanks.js +1 -1
  27. package/dist/components/MultipleChoice.cjs +2 -2
  28. package/dist/components/MultipleChoice.d.cts +14 -3
  29. package/dist/components/MultipleChoice.d.ts +14 -3
  30. package/dist/components/MultipleChoice.js +1 -1
  31. package/dist/components/WrittenResponse.cjs +2 -2
  32. package/dist/components/WrittenResponse.d.cts +5 -38
  33. package/dist/components/WrittenResponse.d.ts +5 -38
  34. package/dist/components/WrittenResponse.js +1 -1
  35. package/dist/index.cjs +13 -9
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +6 -5
  38. package/dist/index.d.ts +6 -5
  39. package/dist/index.js +11 -4
  40. package/dist/index.js.map +1 -1
  41. package/dist/theme/skin.css +75 -0
  42. package/dist/types-CNmjqD6M.d.cts +108 -0
  43. package/dist/types-CNmjqD6M.d.ts +108 -0
  44. package/package.json +70 -28
  45. package/dist/chunk-32WWQOG3.js.map +0 -1
  46. package/dist/chunk-73XCWYKD.cjs.map +0 -1
  47. package/dist/chunk-G5ZEWJRA.js.map +0 -1
  48. package/dist/chunk-KJNQLAB5.cjs +0 -158
  49. package/dist/chunk-KJNQLAB5.cjs.map +0 -1
  50. package/dist/chunk-MKH5THDR.cjs.map +0 -1
  51. package/dist/chunk-OS7MO2OW.js +0 -109
  52. package/dist/chunk-OS7MO2OW.js.map +0 -1
  53. package/dist/chunk-SH7BRP4M.cjs +0 -111
  54. package/dist/chunk-SH7BRP4M.cjs.map +0 -1
  55. package/dist/chunk-V5EN3ESV.js +0 -156
  56. package/dist/chunk-V5EN3ESV.js.map +0 -1
  57. package/dist/types-iknDq5tf.d.cts +0 -17
  58. package/dist/types-iknDq5tf.d.ts +0 -17
package/dist/index.js CHANGED
@@ -1,10 +1,17 @@
1
- export { ActivitySequence } from './chunk-OS7MO2OW.js';
2
- export { MultipleChoice } from './chunk-G5ZEWJRA.js';
3
- export { FillInTheBlanks } from './chunk-32WWQOG3.js';
4
- export { WrittenResponse } from './chunk-V5EN3ESV.js';
1
+ export { ActivitySequence } from './chunk-BGWXN44S.js';
2
+ export { MultipleChoice } from './chunk-ZVFWJTT4.js';
3
+ export { FillInTheBlanks } from './chunk-3X7MYAFT.js';
4
+ export { WrittenResponse } from './chunk-AEQQ7VPF.js';
5
5
  import './chunk-UIGIYYGY.js';
6
6
  export { useActivityState } from './chunk-5HVUKXF3.js';
7
7
  export { useXAPI } from './chunk-E4MNA2IQ.js';
8
8
  export { ThemeProvider, darkTheme, defaultTheme, useTheme } from './chunk-B3FUUKZ2.js';
9
+
10
+ // src/components/types.ts
11
+ function asRenderable(redacted) {
12
+ return redacted;
13
+ }
14
+
15
+ export { asRenderable };
9
16
  //# sourceMappingURL=index.js.map
10
17
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
1
+ {"version":3,"sources":["../src/components/types.ts"],"names":[],"mappings":";;;;;;;;;;AAqHO,SAAS,aACd,QAAA,EACmB;AACnB,EAAA,OAAO,QAAA;AACT","file":"index.js","sourcesContent":["import type {\n ActivityData,\n ActivityResult,\n InteractionEvent,\n ItemOutcome,\n LearnerResponse,\n RedactedActivityData,\n ThemeTokens,\n} from '@intellectif/lk-core';\n\n/**\n * How an activity is being presented. This is the single switch that decides\n * whether the component may grade, reveal, or submit — the three things a\n * summative exam must take away from the client.\n *\n * - `practice` (default, v1 behaviour): the component owns the attempt. It\n * scores locally on submit, reveals correctness and feedback, and calls\n * `onComplete` with a full {@link ActivityResult}.\n * - `exam`: the component NEVER scores and NEVER reveals correctness. Submit\n * emits the raw learner response through `onSubmit`; the server grades it.\n * Safe to render redacted data, because nothing here needs the answer key.\n * - `review`: read-only. Renders the learner's submitted answer, and marks\n * correctness only from an `outcome` the caller supplies (which the server\n * computed). Nothing is submittable.\n */\nexport type RenderMode = 'practice' | 'exam' | 'review';\n\n/**\n * Makes the answer-key-bearing parts of an activity optional, so a component\n * can render either full activity data OR a `redact()` projection with the\n * same props. Fields the SDK classifies `answer-key` become optional here;\n * a component in `exam` mode must not read them at all.\n */\nexport type Renderable<TData> = Omit<TData, 'scoringStrategy'> & {\n scoringStrategy?: unknown;\n /** Present on a `redact()` projection. */\n redacted?: true;\n};\n\n/**\n * Sanitiser for author-supplied rich text (`questionHtml`, `passageHtml`,\n * `promptHtml`). The SDK deliberately ships NO sanitiser — that would add a\n * dependency and, worse, a false promise. Rich text is rendered only when you\n * supply this function; without it the component falls back to the plain-text\n * field, which is always escaped. Fail-safe by construction: the SDK never\n * injects HTML it was not explicitly given a sanitiser for.\n */\nexport type HtmlSanitizer = (html: string) => string;\n\n/**\n * The prop contract shared by every activity component (Req 3.1). Defined\n * here (React-specific) rather than in lk-core, which is React-free.\n *\n * Controlled / uncontrolled follows the React convention: pass `value` +\n * `onChange` to own the learner's answer (restore an in-progress attempt,\n * autosave a delta, drive a review); pass `defaultValue` to seed an\n * uncontrolled component; pass neither for the v1 behaviour.\n */\nexport interface ActivityProps<TData extends ActivityData = ActivityData> {\n /** Activity content. Accepts a `redact()` projection in `exam` mode. */\n data: Renderable<TData>;\n /**\n * Called when the component scored the attempt itself. Only ever fires in\n * `practice` mode — in `exam` mode the client does not grade, so there is\n * no `ActivityResult` to give you; use `onSubmit`.\n */\n onComplete?: (result: ActivityResult) => void;\n /**\n * Called on submit with the raw learner response and no grade. Fires in\n * every mode, before `onComplete`, so an exam runner can persist the\n * response and let the server score it.\n */\n onSubmit?: (response: LearnerResponse) => void;\n /** Controlled value: the learner's current response. */\n value?: LearnerResponse;\n /** Initial response for an uncontrolled component (ignored when `value` is set). */\n defaultValue?: LearnerResponse;\n /** Fires on every change to the learner's response. Required for a controlled component. */\n onChange?: (response: LearnerResponse) => void;\n /** Presentation mode. Defaults to `practice`. */\n renderMode?: RenderMode;\n /**\n * Server-computed outcome, used by `review` mode to mark correctness\n * without the client ever scoring. Ignored in other modes.\n */\n outcome?: ItemOutcome;\n /** Renders author-supplied rich text when provided. See {@link HtmlSanitizer}. */\n sanitizeHtml?: HtmlSanitizer;\n onInteraction?: (event: InteractionEvent) => void;\n /** Per-instance token overrides, applied as inline CSS vars on the root. */\n theme?: Partial<ThemeTokens>;\n locale?: string;\n disabled?: boolean;\n}\n\n/**\n * Bridges a server-produced `redact()` projection into the `data` prop.\n *\n * `RedactedActivityData` is deliberately index-signature typed in lk-core — it\n * proves a payload is learner-safe, not what shape it has — so TypeScript\n * cannot know it still carries the public fields a renderer needs. This is the\n * SDK's single, documented crossing of that gap, so an exam runner does not\n * have to write `as unknown as` at every call site:\n *\n * ```tsx\n * <MultipleChoice\n * data={asRenderable<MultipleChoiceData>(redactedFromServer)}\n * renderMode=\"exam\"\n * onSubmit={persist}\n * />\n * ```\n *\n * Safe because `exam` mode reads only public fields; the answer-key fields the\n * type claims are exactly the ones the component is forbidden to touch there.\n * A future release will derive per-type redacted interfaces so this becomes\n * unnecessary.\n */\nexport function asRenderable<TData extends ActivityData>(\n redacted: RedactedActivityData,\n): Renderable<TData> {\n return redacted as unknown as Renderable<TData>;\n}\n"]}
@@ -435,3 +435,78 @@
435
435
  }
436
436
  }
437
437
  }
438
+
439
+ @layer lk-skin {
440
+ /* ── Written Response: a returned grade ──────────────────────────────── */
441
+ .lk-wr-outcome {
442
+ display: flex;
443
+ flex-direction: column;
444
+ gap: var(--lk-spacing-sm);
445
+ padding: var(--lk-spacing-md);
446
+ border: 1px solid var(--lk-color-border);
447
+ border-radius: var(--lk-border-radius-md);
448
+ background: var(--lk-color-surface);
449
+ }
450
+
451
+ .lk-wr-outcome[data-passed="true"] {
452
+ border-color: var(--lk-color-success);
453
+ }
454
+ .lk-wr-outcome[data-status="deferred"] {
455
+ border-style: dashed;
456
+ }
457
+
458
+ .lk-wr-grade {
459
+ margin: 0;
460
+ font-weight: 600;
461
+ color: var(--lk-color-text);
462
+ }
463
+ .lk-wr-grade-feedback {
464
+ margin: 0;
465
+ color: var(--lk-color-text-muted);
466
+ }
467
+
468
+ .lk-wr-criteria,
469
+ .lk-wr-corrections {
470
+ display: flex;
471
+ flex-direction: column;
472
+ gap: var(--lk-spacing-xs);
473
+ margin: 0;
474
+ padding: 0;
475
+ list-style: none;
476
+ }
477
+
478
+ .lk-wr-criterion {
479
+ display: flex;
480
+ flex-wrap: wrap;
481
+ gap: var(--lk-spacing-xs);
482
+ font-size: var(--lk-font-size-sm);
483
+ }
484
+
485
+ .lk-wr-criterion-name {
486
+ font-weight: 600;
487
+ }
488
+ .lk-wr-criterion-comment,
489
+ .lk-wr-correction-explanation {
490
+ color: var(--lk-color-text-muted);
491
+ }
492
+ .lk-wr-criterion[data-na="true"] {
493
+ opacity: 0.6;
494
+ }
495
+
496
+ .lk-wr-correction {
497
+ font-size: var(--lk-font-size-sm);
498
+ }
499
+ .lk-wr-correction-original {
500
+ color: var(--lk-color-error);
501
+ }
502
+ .lk-wr-correction-corrected {
503
+ color: var(--lk-color-success);
504
+ text-decoration: none;
505
+ }
506
+
507
+ .lk-wr-review-flag {
508
+ margin: 0;
509
+ font-size: var(--lk-font-size-sm);
510
+ color: var(--lk-color-warning);
511
+ }
512
+ }
@@ -0,0 +1,108 @@
1
+ import { ActivityData, ActivityResult, LearnerResponse, ItemOutcome, InteractionEvent, ThemeTokens, RedactedActivityData } from '@intellectif/lk-core';
2
+
3
+ /**
4
+ * How an activity is being presented. This is the single switch that decides
5
+ * whether the component may grade, reveal, or submit — the three things a
6
+ * summative exam must take away from the client.
7
+ *
8
+ * - `practice` (default, v1 behaviour): the component owns the attempt. It
9
+ * scores locally on submit, reveals correctness and feedback, and calls
10
+ * `onComplete` with a full {@link ActivityResult}.
11
+ * - `exam`: the component NEVER scores and NEVER reveals correctness. Submit
12
+ * emits the raw learner response through `onSubmit`; the server grades it.
13
+ * Safe to render redacted data, because nothing here needs the answer key.
14
+ * - `review`: read-only. Renders the learner's submitted answer, and marks
15
+ * correctness only from an `outcome` the caller supplies (which the server
16
+ * computed). Nothing is submittable.
17
+ */
18
+ type RenderMode = 'practice' | 'exam' | 'review';
19
+ /**
20
+ * Makes the answer-key-bearing parts of an activity optional, so a component
21
+ * can render either full activity data OR a `redact()` projection with the
22
+ * same props. Fields the SDK classifies `answer-key` become optional here;
23
+ * a component in `exam` mode must not read them at all.
24
+ */
25
+ type Renderable<TData> = Omit<TData, 'scoringStrategy'> & {
26
+ scoringStrategy?: unknown;
27
+ /** Present on a `redact()` projection. */
28
+ redacted?: true;
29
+ };
30
+ /**
31
+ * Sanitiser for author-supplied rich text (`questionHtml`, `passageHtml`,
32
+ * `promptHtml`). The SDK deliberately ships NO sanitiser — that would add a
33
+ * dependency and, worse, a false promise. Rich text is rendered only when you
34
+ * supply this function; without it the component falls back to the plain-text
35
+ * field, which is always escaped. Fail-safe by construction: the SDK never
36
+ * injects HTML it was not explicitly given a sanitiser for.
37
+ */
38
+ type HtmlSanitizer = (html: string) => string;
39
+ /**
40
+ * The prop contract shared by every activity component (Req 3.1). Defined
41
+ * here (React-specific) rather than in lk-core, which is React-free.
42
+ *
43
+ * Controlled / uncontrolled follows the React convention: pass `value` +
44
+ * `onChange` to own the learner's answer (restore an in-progress attempt,
45
+ * autosave a delta, drive a review); pass `defaultValue` to seed an
46
+ * uncontrolled component; pass neither for the v1 behaviour.
47
+ */
48
+ interface ActivityProps<TData extends ActivityData = ActivityData> {
49
+ /** Activity content. Accepts a `redact()` projection in `exam` mode. */
50
+ data: Renderable<TData>;
51
+ /**
52
+ * Called when the component scored the attempt itself. Only ever fires in
53
+ * `practice` mode — in `exam` mode the client does not grade, so there is
54
+ * no `ActivityResult` to give you; use `onSubmit`.
55
+ */
56
+ onComplete?: (result: ActivityResult) => void;
57
+ /**
58
+ * Called on submit with the raw learner response and no grade. Fires in
59
+ * every mode, before `onComplete`, so an exam runner can persist the
60
+ * response and let the server score it.
61
+ */
62
+ onSubmit?: (response: LearnerResponse) => void;
63
+ /** Controlled value: the learner's current response. */
64
+ value?: LearnerResponse;
65
+ /** Initial response for an uncontrolled component (ignored when `value` is set). */
66
+ defaultValue?: LearnerResponse;
67
+ /** Fires on every change to the learner's response. Required for a controlled component. */
68
+ onChange?: (response: LearnerResponse) => void;
69
+ /** Presentation mode. Defaults to `practice`. */
70
+ renderMode?: RenderMode;
71
+ /**
72
+ * Server-computed outcome, used by `review` mode to mark correctness
73
+ * without the client ever scoring. Ignored in other modes.
74
+ */
75
+ outcome?: ItemOutcome;
76
+ /** Renders author-supplied rich text when provided. See {@link HtmlSanitizer}. */
77
+ sanitizeHtml?: HtmlSanitizer;
78
+ onInteraction?: (event: InteractionEvent) => void;
79
+ /** Per-instance token overrides, applied as inline CSS vars on the root. */
80
+ theme?: Partial<ThemeTokens>;
81
+ locale?: string;
82
+ disabled?: boolean;
83
+ }
84
+ /**
85
+ * Bridges a server-produced `redact()` projection into the `data` prop.
86
+ *
87
+ * `RedactedActivityData` is deliberately index-signature typed in lk-core — it
88
+ * proves a payload is learner-safe, not what shape it has — so TypeScript
89
+ * cannot know it still carries the public fields a renderer needs. This is the
90
+ * SDK's single, documented crossing of that gap, so an exam runner does not
91
+ * have to write `as unknown as` at every call site:
92
+ *
93
+ * ```tsx
94
+ * <MultipleChoice
95
+ * data={asRenderable<MultipleChoiceData>(redactedFromServer)}
96
+ * renderMode="exam"
97
+ * onSubmit={persist}
98
+ * />
99
+ * ```
100
+ *
101
+ * Safe because `exam` mode reads only public fields; the answer-key fields the
102
+ * type claims are exactly the ones the component is forbidden to touch there.
103
+ * A future release will derive per-type redacted interfaces so this becomes
104
+ * unnecessary.
105
+ */
106
+ declare function asRenderable<TData extends ActivityData>(redacted: RedactedActivityData): Renderable<TData>;
107
+
108
+ export { type ActivityProps as A, type HtmlSanitizer as H, type RenderMode as R, type Renderable as a, asRenderable as b };
@@ -0,0 +1,108 @@
1
+ import { ActivityData, ActivityResult, LearnerResponse, ItemOutcome, InteractionEvent, ThemeTokens, RedactedActivityData } from '@intellectif/lk-core';
2
+
3
+ /**
4
+ * How an activity is being presented. This is the single switch that decides
5
+ * whether the component may grade, reveal, or submit — the three things a
6
+ * summative exam must take away from the client.
7
+ *
8
+ * - `practice` (default, v1 behaviour): the component owns the attempt. It
9
+ * scores locally on submit, reveals correctness and feedback, and calls
10
+ * `onComplete` with a full {@link ActivityResult}.
11
+ * - `exam`: the component NEVER scores and NEVER reveals correctness. Submit
12
+ * emits the raw learner response through `onSubmit`; the server grades it.
13
+ * Safe to render redacted data, because nothing here needs the answer key.
14
+ * - `review`: read-only. Renders the learner's submitted answer, and marks
15
+ * correctness only from an `outcome` the caller supplies (which the server
16
+ * computed). Nothing is submittable.
17
+ */
18
+ type RenderMode = 'practice' | 'exam' | 'review';
19
+ /**
20
+ * Makes the answer-key-bearing parts of an activity optional, so a component
21
+ * can render either full activity data OR a `redact()` projection with the
22
+ * same props. Fields the SDK classifies `answer-key` become optional here;
23
+ * a component in `exam` mode must not read them at all.
24
+ */
25
+ type Renderable<TData> = Omit<TData, 'scoringStrategy'> & {
26
+ scoringStrategy?: unknown;
27
+ /** Present on a `redact()` projection. */
28
+ redacted?: true;
29
+ };
30
+ /**
31
+ * Sanitiser for author-supplied rich text (`questionHtml`, `passageHtml`,
32
+ * `promptHtml`). The SDK deliberately ships NO sanitiser — that would add a
33
+ * dependency and, worse, a false promise. Rich text is rendered only when you
34
+ * supply this function; without it the component falls back to the plain-text
35
+ * field, which is always escaped. Fail-safe by construction: the SDK never
36
+ * injects HTML it was not explicitly given a sanitiser for.
37
+ */
38
+ type HtmlSanitizer = (html: string) => string;
39
+ /**
40
+ * The prop contract shared by every activity component (Req 3.1). Defined
41
+ * here (React-specific) rather than in lk-core, which is React-free.
42
+ *
43
+ * Controlled / uncontrolled follows the React convention: pass `value` +
44
+ * `onChange` to own the learner's answer (restore an in-progress attempt,
45
+ * autosave a delta, drive a review); pass `defaultValue` to seed an
46
+ * uncontrolled component; pass neither for the v1 behaviour.
47
+ */
48
+ interface ActivityProps<TData extends ActivityData = ActivityData> {
49
+ /** Activity content. Accepts a `redact()` projection in `exam` mode. */
50
+ data: Renderable<TData>;
51
+ /**
52
+ * Called when the component scored the attempt itself. Only ever fires in
53
+ * `practice` mode — in `exam` mode the client does not grade, so there is
54
+ * no `ActivityResult` to give you; use `onSubmit`.
55
+ */
56
+ onComplete?: (result: ActivityResult) => void;
57
+ /**
58
+ * Called on submit with the raw learner response and no grade. Fires in
59
+ * every mode, before `onComplete`, so an exam runner can persist the
60
+ * response and let the server score it.
61
+ */
62
+ onSubmit?: (response: LearnerResponse) => void;
63
+ /** Controlled value: the learner's current response. */
64
+ value?: LearnerResponse;
65
+ /** Initial response for an uncontrolled component (ignored when `value` is set). */
66
+ defaultValue?: LearnerResponse;
67
+ /** Fires on every change to the learner's response. Required for a controlled component. */
68
+ onChange?: (response: LearnerResponse) => void;
69
+ /** Presentation mode. Defaults to `practice`. */
70
+ renderMode?: RenderMode;
71
+ /**
72
+ * Server-computed outcome, used by `review` mode to mark correctness
73
+ * without the client ever scoring. Ignored in other modes.
74
+ */
75
+ outcome?: ItemOutcome;
76
+ /** Renders author-supplied rich text when provided. See {@link HtmlSanitizer}. */
77
+ sanitizeHtml?: HtmlSanitizer;
78
+ onInteraction?: (event: InteractionEvent) => void;
79
+ /** Per-instance token overrides, applied as inline CSS vars on the root. */
80
+ theme?: Partial<ThemeTokens>;
81
+ locale?: string;
82
+ disabled?: boolean;
83
+ }
84
+ /**
85
+ * Bridges a server-produced `redact()` projection into the `data` prop.
86
+ *
87
+ * `RedactedActivityData` is deliberately index-signature typed in lk-core — it
88
+ * proves a payload is learner-safe, not what shape it has — so TypeScript
89
+ * cannot know it still carries the public fields a renderer needs. This is the
90
+ * SDK's single, documented crossing of that gap, so an exam runner does not
91
+ * have to write `as unknown as` at every call site:
92
+ *
93
+ * ```tsx
94
+ * <MultipleChoice
95
+ * data={asRenderable<MultipleChoiceData>(redactedFromServer)}
96
+ * renderMode="exam"
97
+ * onSubmit={persist}
98
+ * />
99
+ * ```
100
+ *
101
+ * Safe because `exam` mode reads only public fields; the answer-key fields the
102
+ * type claims are exactly the ones the component is forbidden to touch there.
103
+ * A future release will derive per-type redacted interfaces so this becomes
104
+ * unnecessary.
105
+ */
106
+ declare function asRenderable<TData extends ActivityData>(redacted: RedactedActivityData): Renderable<TData>;
107
+
108
+ export { type ActivityProps as A, type HtmlSanitizer as H, type RenderMode as R, type Renderable as a, asRenderable as b };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intellectif/lk-react",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "React 19 activity components, hooks, and theming for learning-kit",
5
5
  "repository": {
6
6
  "type": "git",
@@ -13,44 +13,84 @@
13
13
  ],
14
14
  "exports": {
15
15
  ".": {
16
- "types": "./dist/index.d.ts",
17
- "import": "./dist/index.js",
18
- "require": "./dist/index.cjs"
16
+ "import": {
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/index.js"
19
+ },
20
+ "require": {
21
+ "types": "./dist/index.d.cts",
22
+ "default": "./dist/index.cjs"
23
+ }
19
24
  },
20
25
  "./components/ActivitySequence": {
21
- "types": "./dist/components/ActivitySequence.d.ts",
22
- "import": "./dist/components/ActivitySequence.js",
23
- "require": "./dist/components/ActivitySequence.cjs"
26
+ "import": {
27
+ "types": "./dist/components/ActivitySequence.d.ts",
28
+ "default": "./dist/components/ActivitySequence.js"
29
+ },
30
+ "require": {
31
+ "types": "./dist/components/ActivitySequence.d.cts",
32
+ "default": "./dist/components/ActivitySequence.cjs"
33
+ }
24
34
  },
25
35
  "./components/MultipleChoice": {
26
- "types": "./dist/components/MultipleChoice.d.ts",
27
- "import": "./dist/components/MultipleChoice.js",
28
- "require": "./dist/components/MultipleChoice.cjs"
36
+ "import": {
37
+ "types": "./dist/components/MultipleChoice.d.ts",
38
+ "default": "./dist/components/MultipleChoice.js"
39
+ },
40
+ "require": {
41
+ "types": "./dist/components/MultipleChoice.d.cts",
42
+ "default": "./dist/components/MultipleChoice.cjs"
43
+ }
29
44
  },
30
45
  "./components/FillInTheBlanks": {
31
- "types": "./dist/components/FillInTheBlanks.d.ts",
32
- "import": "./dist/components/FillInTheBlanks.js",
33
- "require": "./dist/components/FillInTheBlanks.cjs"
46
+ "import": {
47
+ "types": "./dist/components/FillInTheBlanks.d.ts",
48
+ "default": "./dist/components/FillInTheBlanks.js"
49
+ },
50
+ "require": {
51
+ "types": "./dist/components/FillInTheBlanks.d.cts",
52
+ "default": "./dist/components/FillInTheBlanks.cjs"
53
+ }
34
54
  },
35
55
  "./components/WrittenResponse": {
36
- "types": "./dist/components/WrittenResponse.d.ts",
37
- "import": "./dist/components/WrittenResponse.js",
38
- "require": "./dist/components/WrittenResponse.cjs"
56
+ "import": {
57
+ "types": "./dist/components/WrittenResponse.d.ts",
58
+ "default": "./dist/components/WrittenResponse.js"
59
+ },
60
+ "require": {
61
+ "types": "./dist/components/WrittenResponse.d.cts",
62
+ "default": "./dist/components/WrittenResponse.cjs"
63
+ }
39
64
  },
40
65
  "./hooks/useActivityState": {
41
- "types": "./dist/hooks/useActivityState.d.ts",
42
- "import": "./dist/hooks/useActivityState.js",
43
- "require": "./dist/hooks/useActivityState.cjs"
66
+ "import": {
67
+ "types": "./dist/hooks/useActivityState.d.ts",
68
+ "default": "./dist/hooks/useActivityState.js"
69
+ },
70
+ "require": {
71
+ "types": "./dist/hooks/useActivityState.d.cts",
72
+ "default": "./dist/hooks/useActivityState.cjs"
73
+ }
44
74
  },
45
75
  "./hooks/useXAPI": {
46
- "types": "./dist/hooks/useXAPI.d.ts",
47
- "import": "./dist/hooks/useXAPI.js",
48
- "require": "./dist/hooks/useXAPI.cjs"
76
+ "import": {
77
+ "types": "./dist/hooks/useXAPI.d.ts",
78
+ "default": "./dist/hooks/useXAPI.js"
79
+ },
80
+ "require": {
81
+ "types": "./dist/hooks/useXAPI.d.cts",
82
+ "default": "./dist/hooks/useXAPI.cjs"
83
+ }
49
84
  },
50
85
  "./theme/ThemeProvider": {
51
- "types": "./dist/theme/ThemeProvider.d.ts",
52
- "import": "./dist/theme/ThemeProvider.js",
53
- "require": "./dist/theme/ThemeProvider.cjs"
86
+ "import": {
87
+ "types": "./dist/theme/ThemeProvider.d.ts",
88
+ "default": "./dist/theme/ThemeProvider.js"
89
+ },
90
+ "require": {
91
+ "types": "./dist/theme/ThemeProvider.d.cts",
92
+ "default": "./dist/theme/ThemeProvider.cjs"
93
+ }
54
94
  },
55
95
  "./theme/defaults.css": "./dist/theme/defaults.css",
56
96
  "./theme/skin.css": "./dist/theme/skin.css"
@@ -65,7 +105,7 @@
65
105
  "peerDependencies": {
66
106
  "react": "^19",
67
107
  "react-dom": "^19",
68
- "@intellectif/lk-core": "^0.3.0"
108
+ "@intellectif/lk-core": "^0.4.0"
69
109
  },
70
110
  "devDependencies": {
71
111
  "@axe-core/react": "^4.10.0",
@@ -84,7 +124,7 @@
84
124
  "tsup": "^8.3.0",
85
125
  "vitest": "^4.1.6",
86
126
  "vitest-axe": "^0.1.0",
87
- "@intellectif/lk-core": "0.3.0"
127
+ "@intellectif/lk-core": "0.4.0"
88
128
  },
89
129
  "license": "MIT",
90
130
  "engines": {
@@ -95,6 +135,8 @@
95
135
  "test": "vitest run",
96
136
  "coverage": "vitest run --coverage",
97
137
  "lint": "biome check .",
98
- "e2e": "playwright test -c ../../playwright.config.ts"
138
+ "e2e": "playwright test -c ../../playwright.config.ts",
139
+ "publint": "publint",
140
+ "attw": "attw --pack . --ignore-rules no-resolution --exclude-entrypoints \"theme/defaults.css\" \"theme/skin.css\""
99
141
  }
100
142
  }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/FillInTheBlanks/FillInTheBlanks.tsx","../src/components/FillInTheBlanks/index.tsx"],"names":["result","FillInTheBlanks","jsx"],"mappings":"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","file":"chunk-32WWQOG3.js","sourcesContent":["'use client';\n\nimport {\n ActivitySchemaError,\n type FillInTheBlanksData,\n type ScoringResult,\n score,\n validateActivity,\n xAPIBuilder,\n} from '@intellectif/lk-core';\nimport { type CSSProperties, useEffect, useMemo, useState } from 'react';\nimport { useActivityState } from '../../hooks/useActivityState.js';\nimport { ANONYMOUS_ACTOR, isDevelopment, objectIdFor } from '../_internal.js';\nimport { ActivityMedia } from '../shared/ActivityMedia.js';\nimport { FeedbackRegion } from '../shared/FeedbackRegion.js';\nimport type { ActivityProps } from '../types.js';\n\nexport interface FillInTheBlanksProps extends ActivityProps<FillInTheBlanksData> {\n /**\n * After submission, replace each incorrectly-answered blank with its first\n * accepted answer, styled distinctly (Req 5.7). Component-specific prop: it\n * is presentation, not activity data, and is outside the fixed Req-3.1 set.\n */\n showCorrectAnswers?: boolean;\n}\n\nconst PLACEHOLDER = /\\{\\{\\s*([^{}]+?)\\s*\\}\\}/g;\n\ntype Segment = { kind: 'text'; value: string } | { kind: 'blank'; id: string; ordinal: number };\n\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: 'blank', 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\nexport function FillInTheBlanks({\n data,\n onComplete,\n onInteraction,\n theme,\n locale,\n disabled,\n showCorrectAnswers,\n}: FillInTheBlanksProps) {\n // Dev-only boundary validation (Req 2.3); throws in render so the wrapping\n // ActivityErrorBoundary catches it. Re-runs only when data changes.\n const devError = useMemo(() => {\n if (!isDevelopment()) {\n return null;\n }\n const result = validateActivity('fill-in-the-blanks', data);\n return result.success ? null : new ActivitySchemaError('fill-in-the-blanks', result.errors);\n }, [data]);\n\n const { state, start, complete, getTimeSpent, reset } = useActivityState();\n const [answers, setAnswers] = useState<Record<string, string>>({});\n const [revealed, setRevealed] = useState<Set<string>>(() => new Set());\n const [result, setResult] = useState<ScoringResult | null>(null);\n const [summary, setSummary] = useState<string | null>(null);\n const [feedbackHidden, setFeedbackHidden] = useState(false);\n\n // `data` is an intentional reset trigger (Req 3.7), not read in the body.\n // biome-ignore lint/correctness/useExhaustiveDependencies: data is the reset trigger (Req 3.7)\n useEffect(() => {\n setAnswers({});\n setRevealed(new Set());\n setResult(null);\n setSummary(null);\n setFeedbackHidden(false);\n reset();\n }, [data, reset]);\n\n const segments = useMemo(() => parsePassage(data.passage), [data.passage]);\n const blankById = useMemo(() => new Map(data.blanks.map((b) => [b.id, b])), [data.blanks]);\n\n if (devError) {\n throw devError;\n }\n\n const submitted = state === 'completed';\n const inactive = disabled === true || submitted;\n\n const fireInteraction = (\n type: 'blank-filled' | 'hint-requested' | 'submitted',\n payload: Record<string, unknown>,\n ): void => {\n onInteraction?.({ type, activityId: data.id, timestamp: Date.now(), payload });\n };\n\n const handleChange = (blankId: string, value: string): void => {\n if (inactive) {\n return;\n }\n if (state === 'idle') {\n start();\n }\n setAnswers((prev) => ({ ...prev, [blankId]: value }));\n fireInteraction('blank-filled', { blankId, value });\n };\n\n const toggleHint = (blankId: string): void => {\n if (inactive) {\n return;\n }\n if (state === 'idle') {\n start();\n }\n setRevealed((prev) => {\n const next = new Set(prev);\n if (next.has(blankId)) {\n next.delete(blankId);\n return next;\n }\n next.add(blankId);\n return next;\n });\n // Only a reveal is a \"hint requested\"; hiding is not a new request.\n if (!revealed.has(blankId)) {\n fireInteraction('hint-requested', { blankId });\n }\n };\n\n const handleSubmit = (event: React.FormEvent): void => {\n event.preventDefault();\n if (inactive) {\n return;\n }\n const response = { type: 'fill-in-the-blanks', answers } as const;\n const scoringResult = score('fill-in-the-blanks', data, response);\n complete();\n const timeSpent = getTimeSpent();\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 type: 'http://adlnet.gov/expapi/activities/cmi.interaction',\n interactionType: 'fill-in',\n correctResponsesPattern: [\n data.blanks.map((blank) => blank.acceptedAnswers[0] ?? '').join('[,]'),\n ],\n },\n scoringResult,\n timeSpentMs: timeSpent,\n response: JSON.stringify(answers),\n });\n setResult(scoringResult);\n onComplete({\n score: scoringResult.score,\n maxScore: scoringResult.maxScore,\n passed: scoringResult.passed,\n timeSpent,\n xapiStatement,\n });\n // Core selects the authored overall feedback on `passed` (B3 fix).\n const overall = scoringResult.feedback;\n setSummary(\n `Answer submitted. Score ${Math.round(scoringResult.score * 100)}%. ${\n scoringResult.passed ? 'Passed.' : 'Not passed.'\n }${overall ? ` ${overall}` : ''}`,\n );\n fireInteraction('submitted', { answers, score: scoringResult.score });\n };\n\n const correctByBlank = useMemo(() => {\n const map = new Map<string, boolean>();\n for (const detail of result?.details ?? []) {\n map.set(detail.itemId, detail.correct);\n }\n return map;\n }, [result]);\n\n return (\n <form\n className=\"lk-fib\"\n aria-label={data.title}\n lang={locale}\n style={theme as CSSProperties | undefined}\n onSubmit={handleSubmit}\n >\n {data.media ? <ActivityMedia media={data.media} /> : null}\n <fieldset disabled={inactive}>\n <p className=\"lk-fib-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 blank = blankById.get(seg.id);\n if (!blank) {\n return null;\n }\n const isCorrect = correctByBlank.get(seg.id) === true;\n const hintId = `${data.id}-hint-${seg.id}`;\n if (submitted && showCorrectAnswers && !isCorrect) {\n return (\n <span key={seg.id} className=\"lk-fib-answer\" data-correct=\"false\">\n {blank.acceptedAnswers[0]}\n </span>\n );\n }\n return (\n <span key={seg.id} className=\"lk-fib-blank\">\n <input\n type=\"text\"\n aria-label={`Fill in blank ${seg.ordinal}`}\n aria-describedby={blank.hint ? hintId : undefined}\n value={answers[seg.id] ?? ''}\n disabled={inactive}\n aria-disabled={inactive || undefined}\n data-correct={submitted ? String(isCorrect) : undefined}\n onChange={(e) => handleChange(seg.id, e.target.value)}\n />\n {blank.hint ? (\n <>\n {/*\n Default affordance is an icon; the accessible NAME stays\n text via aria-label (screen readers + tests rely on it).\n The glyph is restylable/replaceable via .lk-fib-hint-btn.\n */}\n <button\n type=\"button\"\n className=\"lk-fib-hint-btn\"\n aria-controls={hintId}\n aria-expanded={revealed.has(seg.id)}\n aria-label={revealed.has(seg.id) ? 'Hide hint' : 'Show hint'}\n disabled={inactive}\n onClick={() => toggleHint(seg.id)}\n >\n <svg\n className=\"lk-fib-hint-icon\"\n viewBox=\"0 0 24 24\"\n width=\"16\"\n height=\"16\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <path\n fill=\"currentColor\"\n d=\"M9 21h6v-1H9v1Zm3-19a7 7 0 0 0-4 12.74V17h8v-2.26A7 7 0 0 0 12 2Z\"\n />\n </svg>\n </button>\n {/*\n Not role=\"tooltip\": a real ARIA tooltip is a named\n hover/focus popup with strict WCAG 1.4.13 constraints.\n This is an accessible click-to-toggle disclosure that is\n the input's aria-describedby target and is announced via\n aria-live; the skin gives it a tooltip-like *visual*\n without the tooltip *semantics*. (Refines Task 15.3.)\n */}\n <span id={hintId} aria-live=\"polite\">\n {revealed.has(seg.id) ? blank.hint : ''}\n </span>\n </>\n ) : null}\n {submitted && !feedbackHidden && blank.feedback ? (\n <span\n className=\"lk-fib-blank-feedback\"\n role=\"note\"\n data-correct={String(isCorrect)}\n >\n {blank.feedback}\n </span>\n ) : null}\n </span>\n );\n })}\n </p>\n <button type=\"submit\" disabled={inactive}>\n Check answers\n </button>\n </fieldset>\n {submitted && data.blanks.some((b) => b.feedback) ? (\n <button\n type=\"button\"\n className=\"lk-fib-feedback-toggle\"\n aria-expanded={!feedbackHidden}\n onClick={() => setFeedbackHidden((h) => !h)}\n >\n {feedbackHidden ? 'Show feedback' : 'Hide feedback'}\n </button>\n ) : null}\n <FeedbackRegion id={`${data.id}-feedback`}>{summary}</FeedbackRegion>\n </form>\n );\n}\n","'use client';\n\nimport { ActivityErrorBoundary } from '../ActivityErrorBoundary.js';\nimport {\n FillInTheBlanks as FillInTheBlanksCore,\n type FillInTheBlanksProps,\n} from './FillInTheBlanks.js';\n\nexport type { FillInTheBlanksProps } from './FillInTheBlanks.js';\n\n/**\n * Public Fill-in-the-Blanks activity: the core wrapped in\n * `ActivityErrorBoundary` so a render failure (incl. the dev schema-validation\n * throw) degrades to an accessible fallback instead of crashing the host.\n */\nexport function FillInTheBlanks(props: FillInTheBlanksProps) {\n return (\n <ActivityErrorBoundary activityTitle={props.data?.title}>\n <FillInTheBlanksCore {...props} />\n </ActivityErrorBoundary>\n );\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/FillInTheBlanks/FillInTheBlanks.tsx","../src/components/FillInTheBlanks/index.tsx"],"names":["useMemo","isDevelopment","result","validateActivity","ActivitySchemaError","useActivityState","useState","useEffect","score","xAPIBuilder","ANONYMOUS_ACTOR","objectIdFor","jsxs","jsx","ActivityMedia","Fragment","FeedbackRegion","FillInTheBlanks","ActivityErrorBoundary"],"mappings":"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","file":"chunk-73XCWYKD.cjs","sourcesContent":["'use client';\n\nimport {\n ActivitySchemaError,\n type FillInTheBlanksData,\n type ScoringResult,\n score,\n validateActivity,\n xAPIBuilder,\n} from '@intellectif/lk-core';\nimport { type CSSProperties, useEffect, useMemo, useState } from 'react';\nimport { useActivityState } from '../../hooks/useActivityState.js';\nimport { ANONYMOUS_ACTOR, isDevelopment, objectIdFor } from '../_internal.js';\nimport { ActivityMedia } from '../shared/ActivityMedia.js';\nimport { FeedbackRegion } from '../shared/FeedbackRegion.js';\nimport type { ActivityProps } from '../types.js';\n\nexport interface FillInTheBlanksProps extends ActivityProps<FillInTheBlanksData> {\n /**\n * After submission, replace each incorrectly-answered blank with its first\n * accepted answer, styled distinctly (Req 5.7). Component-specific prop: it\n * is presentation, not activity data, and is outside the fixed Req-3.1 set.\n */\n showCorrectAnswers?: boolean;\n}\n\nconst PLACEHOLDER = /\\{\\{\\s*([^{}]+?)\\s*\\}\\}/g;\n\ntype Segment = { kind: 'text'; value: string } | { kind: 'blank'; id: string; ordinal: number };\n\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: 'blank', 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\nexport function FillInTheBlanks({\n data,\n onComplete,\n onInteraction,\n theme,\n locale,\n disabled,\n showCorrectAnswers,\n}: FillInTheBlanksProps) {\n // Dev-only boundary validation (Req 2.3); throws in render so the wrapping\n // ActivityErrorBoundary catches it. Re-runs only when data changes.\n const devError = useMemo(() => {\n if (!isDevelopment()) {\n return null;\n }\n const result = validateActivity('fill-in-the-blanks', data);\n return result.success ? null : new ActivitySchemaError('fill-in-the-blanks', result.errors);\n }, [data]);\n\n const { state, start, complete, getTimeSpent, reset } = useActivityState();\n const [answers, setAnswers] = useState<Record<string, string>>({});\n const [revealed, setRevealed] = useState<Set<string>>(() => new Set());\n const [result, setResult] = useState<ScoringResult | null>(null);\n const [summary, setSummary] = useState<string | null>(null);\n const [feedbackHidden, setFeedbackHidden] = useState(false);\n\n // `data` is an intentional reset trigger (Req 3.7), not read in the body.\n // biome-ignore lint/correctness/useExhaustiveDependencies: data is the reset trigger (Req 3.7)\n useEffect(() => {\n setAnswers({});\n setRevealed(new Set());\n setResult(null);\n setSummary(null);\n setFeedbackHidden(false);\n reset();\n }, [data, reset]);\n\n const segments = useMemo(() => parsePassage(data.passage), [data.passage]);\n const blankById = useMemo(() => new Map(data.blanks.map((b) => [b.id, b])), [data.blanks]);\n\n if (devError) {\n throw devError;\n }\n\n const submitted = state === 'completed';\n const inactive = disabled === true || submitted;\n\n const fireInteraction = (\n type: 'blank-filled' | 'hint-requested' | 'submitted',\n payload: Record<string, unknown>,\n ): void => {\n onInteraction?.({ type, activityId: data.id, timestamp: Date.now(), payload });\n };\n\n const handleChange = (blankId: string, value: string): void => {\n if (inactive) {\n return;\n }\n if (state === 'idle') {\n start();\n }\n setAnswers((prev) => ({ ...prev, [blankId]: value }));\n fireInteraction('blank-filled', { blankId, value });\n };\n\n const toggleHint = (blankId: string): void => {\n if (inactive) {\n return;\n }\n if (state === 'idle') {\n start();\n }\n setRevealed((prev) => {\n const next = new Set(prev);\n if (next.has(blankId)) {\n next.delete(blankId);\n return next;\n }\n next.add(blankId);\n return next;\n });\n // Only a reveal is a \"hint requested\"; hiding is not a new request.\n if (!revealed.has(blankId)) {\n fireInteraction('hint-requested', { blankId });\n }\n };\n\n const handleSubmit = (event: React.FormEvent): void => {\n event.preventDefault();\n if (inactive) {\n return;\n }\n const response = { type: 'fill-in-the-blanks', answers } as const;\n const scoringResult = score('fill-in-the-blanks', data, response);\n complete();\n const timeSpent = getTimeSpent();\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 type: 'http://adlnet.gov/expapi/activities/cmi.interaction',\n interactionType: 'fill-in',\n correctResponsesPattern: [\n data.blanks.map((blank) => blank.acceptedAnswers[0] ?? '').join('[,]'),\n ],\n },\n scoringResult,\n timeSpentMs: timeSpent,\n response: JSON.stringify(answers),\n });\n setResult(scoringResult);\n onComplete({\n score: scoringResult.score,\n maxScore: scoringResult.maxScore,\n passed: scoringResult.passed,\n timeSpent,\n xapiStatement,\n });\n // Core selects the authored overall feedback on `passed` (B3 fix).\n const overall = scoringResult.feedback;\n setSummary(\n `Answer submitted. Score ${Math.round(scoringResult.score * 100)}%. ${\n scoringResult.passed ? 'Passed.' : 'Not passed.'\n }${overall ? ` ${overall}` : ''}`,\n );\n fireInteraction('submitted', { answers, score: scoringResult.score });\n };\n\n const correctByBlank = useMemo(() => {\n const map = new Map<string, boolean>();\n for (const detail of result?.details ?? []) {\n map.set(detail.itemId, detail.correct);\n }\n return map;\n }, [result]);\n\n return (\n <form\n className=\"lk-fib\"\n aria-label={data.title}\n lang={locale}\n style={theme as CSSProperties | undefined}\n onSubmit={handleSubmit}\n >\n {data.media ? <ActivityMedia media={data.media} /> : null}\n <fieldset disabled={inactive}>\n <p className=\"lk-fib-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 blank = blankById.get(seg.id);\n if (!blank) {\n return null;\n }\n const isCorrect = correctByBlank.get(seg.id) === true;\n const hintId = `${data.id}-hint-${seg.id}`;\n if (submitted && showCorrectAnswers && !isCorrect) {\n return (\n <span key={seg.id} className=\"lk-fib-answer\" data-correct=\"false\">\n {blank.acceptedAnswers[0]}\n </span>\n );\n }\n return (\n <span key={seg.id} className=\"lk-fib-blank\">\n <input\n type=\"text\"\n aria-label={`Fill in blank ${seg.ordinal}`}\n aria-describedby={blank.hint ? hintId : undefined}\n value={answers[seg.id] ?? ''}\n disabled={inactive}\n aria-disabled={inactive || undefined}\n data-correct={submitted ? String(isCorrect) : undefined}\n onChange={(e) => handleChange(seg.id, e.target.value)}\n />\n {blank.hint ? (\n <>\n {/*\n Default affordance is an icon; the accessible NAME stays\n text via aria-label (screen readers + tests rely on it).\n The glyph is restylable/replaceable via .lk-fib-hint-btn.\n */}\n <button\n type=\"button\"\n className=\"lk-fib-hint-btn\"\n aria-controls={hintId}\n aria-expanded={revealed.has(seg.id)}\n aria-label={revealed.has(seg.id) ? 'Hide hint' : 'Show hint'}\n disabled={inactive}\n onClick={() => toggleHint(seg.id)}\n >\n <svg\n className=\"lk-fib-hint-icon\"\n viewBox=\"0 0 24 24\"\n width=\"16\"\n height=\"16\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <path\n fill=\"currentColor\"\n d=\"M9 21h6v-1H9v1Zm3-19a7 7 0 0 0-4 12.74V17h8v-2.26A7 7 0 0 0 12 2Z\"\n />\n </svg>\n </button>\n {/*\n Not role=\"tooltip\": a real ARIA tooltip is a named\n hover/focus popup with strict WCAG 1.4.13 constraints.\n This is an accessible click-to-toggle disclosure that is\n the input's aria-describedby target and is announced via\n aria-live; the skin gives it a tooltip-like *visual*\n without the tooltip *semantics*. (Refines Task 15.3.)\n */}\n <span id={hintId} aria-live=\"polite\">\n {revealed.has(seg.id) ? blank.hint : ''}\n </span>\n </>\n ) : null}\n {submitted && !feedbackHidden && blank.feedback ? (\n <span\n className=\"lk-fib-blank-feedback\"\n role=\"note\"\n data-correct={String(isCorrect)}\n >\n {blank.feedback}\n </span>\n ) : null}\n </span>\n );\n })}\n </p>\n <button type=\"submit\" disabled={inactive}>\n Check answers\n </button>\n </fieldset>\n {submitted && data.blanks.some((b) => b.feedback) ? (\n <button\n type=\"button\"\n className=\"lk-fib-feedback-toggle\"\n aria-expanded={!feedbackHidden}\n onClick={() => setFeedbackHidden((h) => !h)}\n >\n {feedbackHidden ? 'Show feedback' : 'Hide feedback'}\n </button>\n ) : null}\n <FeedbackRegion id={`${data.id}-feedback`}>{summary}</FeedbackRegion>\n </form>\n );\n}\n","'use client';\n\nimport { ActivityErrorBoundary } from '../ActivityErrorBoundary.js';\nimport {\n FillInTheBlanks as FillInTheBlanksCore,\n type FillInTheBlanksProps,\n} from './FillInTheBlanks.js';\n\nexport type { FillInTheBlanksProps } from './FillInTheBlanks.js';\n\n/**\n * Public Fill-in-the-Blanks activity: the core wrapped in\n * `ActivityErrorBoundary` so a render failure (incl. the dev schema-validation\n * throw) degrades to an accessible fallback instead of crashing the host.\n */\nexport function FillInTheBlanks(props: FillInTheBlanksProps) {\n return (\n <ActivityErrorBoundary activityTitle={props.data?.title}>\n <FillInTheBlanksCore {...props} />\n </ActivityErrorBoundary>\n );\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/MultipleChoice/MultipleChoice.tsx","../src/components/MultipleChoice/index.tsx"],"names":["MultipleChoice","jsx"],"mappings":"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","file":"chunk-G5ZEWJRA.js","sourcesContent":["'use client';\n\nimport {\n ActivitySchemaError,\n type MultipleChoiceData,\n type MultipleChoiceOption,\n score,\n validateActivity,\n xAPIBuilder,\n} from '@intellectif/lk-core';\nimport { type CSSProperties, useEffect, useMemo, useRef, useState } from 'react';\nimport { useActivityState } from '../../hooks/useActivityState.js';\nimport { ANONYMOUS_ACTOR, isDevelopment, objectIdFor, randomSessionId } from '../_internal.js';\nimport { ActivityMedia } from '../shared/ActivityMedia.js';\nimport { FeedbackRegion } from '../shared/FeedbackRegion.js';\nimport type { ActivityProps } from '../types.js';\n\n/** FNV-1a hash → 32-bit seed. */\nfunction hashSeed(input: string): number {\n let h = 2166136261 >>> 0;\n for (let i = 0; i < input.length; i += 1) {\n h ^= input.charCodeAt(i);\n h = Math.imul(h, 16777619) >>> 0;\n }\n return h;\n}\n\n/** Deterministic LCG Fisher–Yates; returns a permutation (no loss/dupe). */\nfunction seededShuffle<T>(items: readonly T[], seed: number): T[] {\n const out = [...items];\n let s = seed >>> 0 || 1;\n for (let i = out.length - 1; i > 0; i -= 1) {\n s = (Math.imul(s, 1664525) + 1013904223) >>> 0;\n const j = s % (i + 1);\n [out[i], out[j]] = [out[j] as T, out[i] as T];\n }\n return out;\n}\n\nexport interface MultipleChoiceProps extends ActivityProps<MultipleChoiceData> {\n /**\n * Seed for the deterministic option shuffle. Supply one (e.g. the attempt\n * id) to make the shuffled order reproducible server-side, stable across\n * page reloads, and identical between SSR and hydration. When absent, a\n * random per-mount session seed is used (order stable within the mount\n * only — the v1 behaviour).\n */\n shuffleSeed?: string;\n}\n\nexport function MultipleChoice({\n data,\n onComplete,\n onInteraction,\n theme,\n locale,\n disabled,\n shuffleSeed,\n}: MultipleChoiceProps) {\n // Dev-only boundary validation (Req 2.3). Throwing during render lets\n // ActivityErrorBoundary catch it. Memoised so it only re-runs on data change.\n const devError = useMemo(() => {\n if (!isDevelopment()) {\n return null;\n }\n const result = validateActivity('multiple-choice', data);\n return result.success ? null : new ActivitySchemaError('multiple-choice', result.errors);\n }, [data]);\n\n // Lazily created only when shuffling without a caller seed: avoids calling\n // crypto.randomUUID (absent on non-secure http origins) unless needed.\n const sessionIdRef = useRef<string | null>(null);\n const { state, start, complete, getTimeSpent, reset } = useActivityState();\n const [selected, setSelected] = useState<string[]>([]);\n const [summary, setSummary] = useState<string | null>(null);\n\n // Reset on data-prop change (Req 3.7). `data` is an intentional change\n // trigger (not read in the body); dropping it would break the reset.\n // biome-ignore lint/correctness/useExhaustiveDependencies: data is the reset trigger (Req 3.7)\n useEffect(() => {\n setSelected([]);\n setSummary(null);\n reset();\n }, [data, reset]);\n\n const displayedOptions = useMemo<MultipleChoiceOption[]>(() => {\n if (!data.shuffle) {\n return [...data.options];\n }\n // biome-ignore lint/suspicious/noAssignInExpressions: sanctioned lazy ref initialization\n const seedSource = shuffleSeed ?? (sessionIdRef.current ??= randomSessionId());\n return seededShuffle(data.options, hashSeed(`${seedSource}:${data.id}`));\n }, [data, shuffleSeed]);\n\n // All hooks are called before this throw, so hook order stays stable.\n if (devError) {\n throw devError;\n }\n\n const isSingle = data.mode === 'single';\n const submitted = state === 'completed';\n const inactive = disabled === true || submitted;\n\n const fireInteraction = (\n type: 'option-selected' | 'option-deselected' | 'submitted',\n payload: Record<string, unknown>,\n ): void => {\n onInteraction?.({ type, activityId: data.id, timestamp: Date.now(), payload });\n };\n\n const selectSingle = (optionId: string): void => {\n if (inactive) {\n return;\n }\n if (state === 'idle') {\n start();\n }\n setSelected([optionId]);\n fireInteraction('option-selected', { optionId });\n };\n\n const toggleMulti = (optionId: string, checked: boolean): void => {\n if (inactive) {\n return;\n }\n if (state === 'idle') {\n start();\n }\n setSelected((prev) => (checked ? [...prev, optionId] : prev.filter((id) => id !== optionId)));\n fireInteraction(checked ? 'option-selected' : 'option-deselected', { optionId });\n };\n\n const handleSubmit = (event: React.FormEvent): void => {\n event.preventDefault();\n if (inactive) {\n return;\n }\n const response = { type: 'multiple-choice', selectedOptionIds: selected } as const;\n const scoringResult = score('multiple-choice', data, response);\n complete();\n const timeSpent = getTimeSpent();\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 type: 'http://adlnet.gov/expapi/activities/cmi.interaction',\n interactionType: 'choice',\n correctResponsesPattern: [\n data.options\n .filter((option) => option.isCorrect)\n .map((option) => option.id)\n .join('[,]'),\n ],\n choices: data.options.map((option) => ({\n id: option.id,\n description: { [data.locale ?? 'en-US']: option.text },\n })),\n },\n scoringResult,\n timeSpentMs: timeSpent,\n response: selected.join(','),\n });\n onComplete({\n score: scoringResult.score,\n maxScore: scoringResult.maxScore,\n passed: scoringResult.passed,\n timeSpent,\n xapiStatement,\n });\n // Core selects the authored overall feedback on `passed` (B3 fix).\n const overall = scoringResult.feedback;\n setSummary(\n `Answer submitted. Score ${Math.round(scoringResult.score * 100)}%. ${\n scoringResult.passed ? 'Passed.' : 'Not passed.'\n }${overall ? ` ${overall}` : ''}`,\n );\n fireInteraction('submitted', {\n selectedOptionIds: selected,\n score: scoringResult.score,\n });\n };\n\n const questionId = `${data.id}-question`;\n\n const optionList = displayedOptions.map((option) => {\n const checked = selected.includes(option.id);\n return (\n <label\n key={option.id}\n className=\"lk-mc-option\"\n data-correct={submitted ? String(option.isCorrect) : undefined}\n >\n <input\n type={isSingle ? 'radio' : 'checkbox'}\n name={isSingle ? `${data.id}-options` : undefined}\n value={option.id}\n checked={checked}\n disabled={inactive}\n aria-disabled={inactive || undefined}\n onChange={(e) =>\n isSingle ? selectSingle(option.id) : toggleMulti(option.id, e.target.checked)\n }\n />\n <span>{option.text}</span>\n {submitted && option.feedback ? (\n <span className=\"lk-mc-option-feedback\" role=\"note\">\n {option.feedback}\n </span>\n ) : null}\n </label>\n );\n });\n\n return (\n <div className=\"lk-mc\" lang={locale} style={theme as CSSProperties | undefined}>\n {data.media ? <ActivityMedia media={data.media} /> : null}\n <form onSubmit={handleSubmit}>\n <fieldset disabled={inactive}>\n <legend id={questionId}>{data.question}</legend>\n {/*\n single: an explicit radiogroup is meaningful (fieldset's implicit\n role is `group`, not `radiogroup`). multi: the fieldset + legend\n already provide a named group — an explicit role=\"group\" here would\n be a redundant, duplicate same-named group in the a11y tree, so we\n use a plain layout div. (Refines the design ARIA sketch.)\n */}\n {isSingle ? (\n <div role=\"radiogroup\" aria-labelledby={questionId}>\n {optionList}\n </div>\n ) : (\n <div>{optionList}</div>\n )}\n <button type=\"submit\" disabled={inactive}>\n Submit\n </button>\n </fieldset>\n </form>\n <FeedbackRegion id={`${data.id}-feedback`}>{summary}</FeedbackRegion>\n </div>\n );\n}\n","'use client';\n\nimport type { MultipleChoiceData } from '@intellectif/lk-core';\nimport { ActivityErrorBoundary } from '../ActivityErrorBoundary.js';\nimport type { ActivityProps } from '../types.js';\nimport { MultipleChoice as MultipleChoiceCore } from './MultipleChoice.js';\n\nexport type { ActivityProps } from '../types.js';\n\n/**\n * Public Multiple Choice activity: the core component wrapped in\n * `ActivityErrorBoundary` so a render failure (incl. the dev schema-validation\n * throw) degrades to an accessible fallback instead of crashing the host.\n */\nexport function MultipleChoice(props: ActivityProps<MultipleChoiceData>) {\n return (\n <ActivityErrorBoundary activityTitle={props.data?.title}>\n <MultipleChoiceCore {...props} />\n </ActivityErrorBoundary>\n );\n}\n"]}