@intellectif/lk-react 16.0.0 → 16.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 (120) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +3 -3
  3. package/dist/{WrittenResponse-C2zw-1jK.d.cts → WrittenResponse-6tSj6U3G.d.cts} +1 -1
  4. package/dist/{WrittenResponse-CTiNpSCC.d.ts → WrittenResponse-xJ7I_5yq.d.ts} +1 -1
  5. package/dist/{chunk-P5FGJMMI.cjs → chunk-2S3MK4VX.cjs} +5 -5
  6. package/dist/{chunk-P5FGJMMI.cjs.map → chunk-2S3MK4VX.cjs.map} +1 -1
  7. package/dist/{chunk-ZA5QUQ2V.cjs → chunk-2TFNDHJ5.cjs} +13 -13
  8. package/dist/{chunk-ZA5QUQ2V.cjs.map → chunk-2TFNDHJ5.cjs.map} +1 -1
  9. package/dist/{chunk-AEUP63CZ.js → chunk-2XNCPLHU.js} +5 -5
  10. package/dist/{chunk-AEUP63CZ.js.map → chunk-2XNCPLHU.js.map} +1 -1
  11. package/dist/{chunk-4X7NFEDB.js → chunk-3UXRX4KU.js} +5 -5
  12. package/dist/{chunk-4X7NFEDB.js.map → chunk-3UXRX4KU.js.map} +1 -1
  13. package/dist/{chunk-UYRQEIB7.js → chunk-5QHVYOME.js} +5 -5
  14. package/dist/{chunk-UYRQEIB7.js.map → chunk-5QHVYOME.js.map} +1 -1
  15. package/dist/{chunk-RJGCT35F.cjs → chunk-B6S5ADHR.cjs} +13 -13
  16. package/dist/{chunk-RJGCT35F.cjs.map → chunk-B6S5ADHR.cjs.map} +1 -1
  17. package/dist/{chunk-HJLVWZOM.js → chunk-BI547RIA.js} +4 -4
  18. package/dist/{chunk-HJLVWZOM.js.map → chunk-BI547RIA.js.map} +1 -1
  19. package/dist/{chunk-BO64SLHL.cjs → chunk-DFMBVA7D.cjs} +8 -8
  20. package/dist/{chunk-BO64SLHL.cjs.map → chunk-DFMBVA7D.cjs.map} +1 -1
  21. package/dist/{chunk-ICAYWVNF.js → chunk-DKZWUIQD.js} +3 -3
  22. package/dist/{chunk-ICAYWVNF.js.map → chunk-DKZWUIQD.js.map} +1 -1
  23. package/dist/{chunk-3IHVDQRC.js → chunk-DVOPHC2Q.js} +275 -74
  24. package/dist/chunk-DVOPHC2Q.js.map +1 -0
  25. package/dist/{chunk-ALDGRDYD.cjs → chunk-FZWWVJE4.cjs} +24 -24
  26. package/dist/{chunk-ALDGRDYD.cjs.map → chunk-FZWWVJE4.cjs.map} +1 -1
  27. package/dist/{chunk-OOYKKO3V.js → chunk-GDMM2WEG.js} +11 -11
  28. package/dist/{chunk-OOYKKO3V.js.map → chunk-GDMM2WEG.js.map} +1 -1
  29. package/dist/{chunk-W32ND3ZR.js → chunk-GUPBIEUA.js} +6 -6
  30. package/dist/{chunk-W32ND3ZR.js.map → chunk-GUPBIEUA.js.map} +1 -1
  31. package/dist/{chunk-T33AJYLW.js → chunk-IQI5I2X5.js} +5 -5
  32. package/dist/{chunk-T33AJYLW.js.map → chunk-IQI5I2X5.js.map} +1 -1
  33. package/dist/{chunk-NOF6HJDV.js → chunk-JPDR6TTA.js} +4 -4
  34. package/dist/{chunk-NOF6HJDV.js.map → chunk-JPDR6TTA.js.map} +1 -1
  35. package/dist/{chunk-QIA5FAAW.js → chunk-KSAINXRG.js} +3 -2
  36. package/dist/chunk-KSAINXRG.js.map +1 -0
  37. package/dist/{chunk-2WM2KSO7.cjs → chunk-ML6L4LJH.cjs} +25 -25
  38. package/dist/{chunk-2WM2KSO7.cjs.map → chunk-ML6L4LJH.cjs.map} +1 -1
  39. package/dist/{chunk-UISZWFI7.cjs → chunk-MQAZJQRE.cjs} +3 -2
  40. package/dist/chunk-MQAZJQRE.cjs.map +1 -0
  41. package/dist/{chunk-QADR2XMQ.cjs → chunk-OO24JGYH.cjs} +18 -18
  42. package/dist/{chunk-QADR2XMQ.cjs.map → chunk-OO24JGYH.cjs.map} +1 -1
  43. package/dist/{chunk-JYC2VV3Y.cjs → chunk-PTJTC6A3.cjs} +12 -12
  44. package/dist/{chunk-JYC2VV3Y.cjs.map → chunk-PTJTC6A3.cjs.map} +1 -1
  45. package/dist/{chunk-WGIMLXRW.cjs → chunk-PXGJHZKJ.cjs} +6 -6
  46. package/dist/{chunk-WGIMLXRW.cjs.map → chunk-PXGJHZKJ.cjs.map} +1 -1
  47. package/dist/{chunk-2YZZL5FV.cjs → chunk-RSQFGF5W.cjs} +13 -13
  48. package/dist/{chunk-2YZZL5FV.cjs.map → chunk-RSQFGF5W.cjs.map} +1 -1
  49. package/dist/{chunk-OXUUE3HX.cjs → chunk-TRP5Z3XW.cjs} +5 -5
  50. package/dist/{chunk-OXUUE3HX.cjs.map → chunk-TRP5Z3XW.cjs.map} +1 -1
  51. package/dist/{chunk-4AQ4SWJ3.js → chunk-VC2MPB2Y.js} +5 -5
  52. package/dist/{chunk-4AQ4SWJ3.js.map → chunk-VC2MPB2Y.js.map} +1 -1
  53. package/dist/{chunk-A2CPCOJA.cjs → chunk-WOYCT5M6.cjs} +284 -83
  54. package/dist/chunk-WOYCT5M6.cjs.map +1 -0
  55. package/dist/{chunk-WBLRUJ77.js → chunk-ZEDMFTKD.js} +10 -10
  56. package/dist/{chunk-WBLRUJ77.js.map → chunk-ZEDMFTKD.js.map} +1 -1
  57. package/dist/{chunk-UIGSH2XB.js → chunk-ZPCO3CEB.js} +3 -3
  58. package/dist/{chunk-UIGSH2XB.js.map → chunk-ZPCO3CEB.js.map} +1 -1
  59. package/dist/{chunk-DMCFARGP.cjs → chunk-ZSY4VANQ.cjs} +13 -13
  60. package/dist/{chunk-DMCFARGP.cjs.map → chunk-ZSY4VANQ.cjs.map} +1 -1
  61. package/dist/components/ActivityPreview.cjs +12 -12
  62. package/dist/components/ActivityPreview.d.cts +2 -2
  63. package/dist/components/ActivityPreview.d.ts +2 -2
  64. package/dist/components/ActivityPreview.js +11 -11
  65. package/dist/components/ActivitySequence.cjs +13 -13
  66. package/dist/components/ActivitySequence.d.cts +2 -2
  67. package/dist/components/ActivitySequence.d.ts +2 -2
  68. package/dist/components/ActivitySequence.js +12 -12
  69. package/dist/components/Dictation.cjs +5 -5
  70. package/dist/components/Dictation.d.cts +1 -1
  71. package/dist/components/Dictation.d.ts +1 -1
  72. package/dist/components/Dictation.js +4 -4
  73. package/dist/components/FillInTheBlanks.cjs +5 -5
  74. package/dist/components/FillInTheBlanks.d.cts +1 -1
  75. package/dist/components/FillInTheBlanks.d.ts +1 -1
  76. package/dist/components/FillInTheBlanks.js +4 -4
  77. package/dist/components/GapSelect.cjs +5 -5
  78. package/dist/components/GapSelect.d.cts +1 -1
  79. package/dist/components/GapSelect.d.ts +1 -1
  80. package/dist/components/GapSelect.js +4 -4
  81. package/dist/components/InteractiveVideo.cjs +12 -12
  82. package/dist/components/InteractiveVideo.d.cts +117 -6
  83. package/dist/components/InteractiveVideo.d.ts +117 -6
  84. package/dist/components/InteractiveVideo.js +10 -10
  85. package/dist/components/MultipleChoice.cjs +5 -5
  86. package/dist/components/MultipleChoice.d.cts +1 -1
  87. package/dist/components/MultipleChoice.d.ts +1 -1
  88. package/dist/components/MultipleChoice.js +4 -4
  89. package/dist/components/PronunciationFeedback.cjs +4 -4
  90. package/dist/components/PronunciationFeedback.d.cts +1 -1
  91. package/dist/components/PronunciationFeedback.d.ts +1 -1
  92. package/dist/components/PronunciationFeedback.js +3 -3
  93. package/dist/components/ReadAloud.cjs +6 -6
  94. package/dist/components/ReadAloud.d.cts +2 -2
  95. package/dist/components/ReadAloud.d.ts +2 -2
  96. package/dist/components/ReadAloud.js +5 -5
  97. package/dist/components/StimulusPanel.cjs +4 -4
  98. package/dist/components/StimulusPanel.d.cts +1 -1
  99. package/dist/components/StimulusPanel.d.ts +1 -1
  100. package/dist/components/StimulusPanel.js +3 -3
  101. package/dist/components/WrittenResponse.cjs +5 -5
  102. package/dist/components/WrittenResponse.d.cts +3 -3
  103. package/dist/components/WrittenResponse.d.ts +3 -3
  104. package/dist/components/WrittenResponse.js +4 -4
  105. package/dist/i18n/LkIntlProvider.cjs +7 -7
  106. package/dist/i18n/LkIntlProvider.d.cts +2 -2
  107. package/dist/i18n/LkIntlProvider.d.ts +2 -2
  108. package/dist/i18n/LkIntlProvider.js +1 -1
  109. package/dist/index.cjs +32 -32
  110. package/dist/index.d.cts +3 -3
  111. package/dist/index.d.ts +3 -3
  112. package/dist/index.js +14 -14
  113. package/dist/{strings-zY2QQYpU.d.cts → strings-C6wM5aUQ.d.cts} +2 -0
  114. package/dist/{strings-Dgc5-DeA.d.ts → strings-Cd_2F-Bw.d.ts} +2 -0
  115. package/dist/theme/skin.css +16 -0
  116. package/package.json +1 -1
  117. package/dist/chunk-3IHVDQRC.js.map +0 -1
  118. package/dist/chunk-A2CPCOJA.cjs.map +0 -1
  119. package/dist/chunk-QIA5FAAW.js.map +0 -1
  120. package/dist/chunk-UISZWFI7.cjs.map +0 -1
@@ -1,11 +1,11 @@
1
1
  'use client';
2
- import { MultipleChoice } from './chunk-4X7NFEDB.js';
3
- import { FillInTheBlanks } from './chunk-4AQ4SWJ3.js';
4
- import { GapSelect } from './chunk-UYRQEIB7.js';
5
- import { Dictation } from './chunk-T33AJYLW.js';
6
- import { stopCaptureGroup, SequenceSlotContext, ReadAloud } from './chunk-W32ND3ZR.js';
7
- import { ActivityErrorBoundary, isDevelopment } from './chunk-ICAYWVNF.js';
8
- import { useLkStrings } from './chunk-QIA5FAAW.js';
2
+ import { MultipleChoice } from './chunk-3UXRX4KU.js';
3
+ import { FillInTheBlanks } from './chunk-VC2MPB2Y.js';
4
+ import { GapSelect } from './chunk-5QHVYOME.js';
5
+ import { Dictation } from './chunk-IQI5I2X5.js';
6
+ import { stopCaptureGroup, SequenceSlotContext, ReadAloud } from './chunk-GUPBIEUA.js';
7
+ import { ActivityErrorBoundary, isDevelopment } from './chunk-DKZWUIQD.js';
8
+ import { useLkStrings } from './chunk-KSAINXRG.js';
9
9
  import { assertRedactedItemGroup, validateItemGroup, ActivitySchemaError, flattenSequence, readMediaProgress } from '@intellectif/lk-core';
10
10
  import { useMemo, useRef, useState, useEffect, useCallback } from 'react';
11
11
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -700,8 +700,10 @@ function EndScreen({
700
700
  strings,
701
701
  onOpenQuiz,
702
702
  onWatchAgain,
703
+ pending,
703
704
  onFinish
704
705
  }) {
706
+ const pendingId = `${idPrefix}-end-pending`;
705
707
  const headingRef = useRef(null);
706
708
  useEffect(() => {
707
709
  headingRef.current?.focus();
@@ -726,12 +728,23 @@ function EndScreen({
726
728
  ]
727
729
  }
728
730
  ) }, quiz.id)) }) : null,
731
+ onFinish !== void 0 && pending ? /* @__PURE__ */ jsx("p", { id: pendingId, className: "lk-iv-end-pending", role: "status", children: strings.videoAnswerPending }) : null,
729
732
  /* @__PURE__ */ jsxs("div", { className: "lk-iv-end-actions", children: [
730
733
  /* @__PURE__ */ jsxs("button", { type: "button", className: "lk-iv-action", onClick: onWatchAgain, children: [
731
734
  /* @__PURE__ */ jsx(ReplayIcon, {}),
732
735
  strings.videoWatchAgain
733
736
  ] }),
734
- onFinish !== void 0 ? /* @__PURE__ */ jsx("button", { type: "button", className: "lk-iv-action lk-iv-action-primary", onClick: onFinish, children: strings.videoFinish }) : null
737
+ onFinish !== void 0 ? /* @__PURE__ */ jsx(
738
+ "button",
739
+ {
740
+ type: "button",
741
+ className: "lk-iv-action lk-iv-action-primary",
742
+ "aria-disabled": pending || void 0,
743
+ "aria-describedby": pending ? pendingId : void 0,
744
+ onClick: onFinish,
745
+ children: strings.videoFinish
746
+ }
747
+ ) : null
735
748
  ] })
736
749
  ] }) });
737
750
  }
@@ -1286,18 +1299,61 @@ function Player(props) {
1286
1299
  });
1287
1300
  const answeredRef = useRef(answered);
1288
1301
  answeredRef.current = answered;
1289
- const scores = useRef(/* @__PURE__ */ new Map());
1290
- const markAnswered = useCallback((slotId) => {
1302
+ const setSlotAnswered = useCallback((slotId, on) => {
1291
1303
  setAnswered((previous) => {
1292
- if (previous.has(slotId)) {
1304
+ if (previous.has(slotId) === on) {
1293
1305
  return previous;
1294
1306
  }
1295
1307
  const next = new Set(previous);
1296
- next.add(slotId);
1308
+ if (on) {
1309
+ next.add(slotId);
1310
+ } else {
1311
+ next.delete(slotId);
1312
+ }
1297
1313
  answeredRef.current = next;
1298
1314
  return next;
1299
1315
  });
1300
1316
  }, []);
1317
+ const [scores, setScores] = useState(() => /* @__PURE__ */ new Map());
1318
+ const setSlotScore = useCallback((slotId, score) => {
1319
+ setScores((previous) => {
1320
+ if (previous.get(slotId) === score) {
1321
+ return previous;
1322
+ }
1323
+ const next = new Map(previous);
1324
+ if (score === void 0) {
1325
+ next.delete(slotId);
1326
+ } else {
1327
+ next.set(slotId, score);
1328
+ }
1329
+ return next;
1330
+ });
1331
+ }, []);
1332
+ const [pending, setPendingState] = useState(() => /* @__PURE__ */ new Set());
1333
+ const pendingRef = useRef(pending);
1334
+ const setPendingKey = useCallback((key, on) => {
1335
+ setPendingState((previous) => {
1336
+ if (previous.has(key) === on) {
1337
+ return previous;
1338
+ }
1339
+ const next = new Set(previous);
1340
+ if (on) {
1341
+ next.add(key);
1342
+ } else {
1343
+ next.delete(key);
1344
+ }
1345
+ pendingRef.current = next;
1346
+ return next;
1347
+ });
1348
+ }, []);
1349
+ const latestTakes = useRef(/* @__PURE__ */ new Map());
1350
+ const alive = useRef(true);
1351
+ useEffect(() => {
1352
+ alive.current = true;
1353
+ return () => {
1354
+ alive.current = false;
1355
+ };
1356
+ }, []);
1301
1357
  const finished = useCallback(
1302
1358
  (cueId) => (slotsByCue.get(cueId) ?? []).every((slot) => answeredRef.current.has(slot.slotId)),
1303
1359
  [slotsByCue]
@@ -1306,6 +1362,7 @@ function Player(props) {
1306
1362
  const videoRef = useRef(null);
1307
1363
  const playButtonRef = useRef(null);
1308
1364
  const quizHeadingRef = useRef(null);
1365
+ const [portalContainer, setPortalContainer] = useState(null);
1309
1366
  const [playing, setPlaying] = useState(false);
1310
1367
  const [waiting, setWaiting] = useState(false);
1311
1368
  const [errorCode, setErrorCode] = useState(null);
@@ -1555,9 +1612,9 @@ function Player(props) {
1555
1612
  const loaded = useRef(/* @__PURE__ */ new Map());
1556
1613
  const loadCues = useCallback((track) => {
1557
1614
  const key = trackKey(track);
1558
- const pending = loading.current.get(key);
1559
- if (pending !== void 0) {
1560
- return pending;
1615
+ const pending2 = loading.current.get(key);
1616
+ if (pending2 !== void 0) {
1617
+ return pending2;
1561
1618
  }
1562
1619
  const request = Promise.resolve().then(
1563
1620
  () => loaderRef.current ? loaderRef.current(track) : fetch(track.src, { credentials: "same-origin" }).then((response) => {
@@ -1886,7 +1943,7 @@ function Player(props) {
1886
1943
  }));
1887
1944
  const totalQuestions = quizzes.reduce((sum, quiz) => sum + quiz.total, 0);
1888
1945
  const answeredCount = quizzes.reduce((sum, quiz) => sum + quiz.answered, 0);
1889
- const graded = [...scores.current.values()];
1946
+ const graded = [...scores.values()];
1890
1947
  const percent = graded.length === 0 ? null : Math.round(graded.reduce((a, b) => a + b, 0) / graded.length * 100);
1891
1948
  const summary = useCallback(() => {
1892
1949
  return {
@@ -1908,11 +1965,12 @@ function Player(props) {
1908
1965
  finishedOnce.current = true;
1909
1966
  onFinished?.(summary());
1910
1967
  }, [onFinished, summary]);
1968
+ const answerPending = pending.size > 0;
1911
1969
  useEffect(() => {
1912
- if (ended && totalQuestions > 0 && answeredCount === totalQuestions && renderMode !== "review") {
1970
+ if (ended && !answerPending && totalQuestions > 0 && answeredCount === totalQuestions && renderMode !== "review") {
1913
1971
  finish();
1914
1972
  }
1915
- }, [ended, totalQuestions, answeredCount, renderMode, finish]);
1973
+ }, [ended, answerPending, totalQuestions, answeredCount, renderMode, finish]);
1916
1974
  const slotBindings = useMemo(() => {
1917
1975
  const bindings = /* @__PURE__ */ new Map();
1918
1976
  if (recordingBinding === void 0) {
@@ -1933,41 +1991,147 @@ function Player(props) {
1933
1991
  }
1934
1992
  return bindings;
1935
1993
  }, [slots, recordingBinding]);
1936
- const renderQuestion = (slot, cueId) => {
1937
- const activity = slot.activity;
1938
- const place = {
1939
- slotId: slot.slotId,
1940
- index: slot.index,
1941
- activityId: activity.id,
1942
- cueId
1994
+ const places = useMemo(() => {
1995
+ const byId = /* @__PURE__ */ new Map();
1996
+ for (const [cueId, cueSlots] of slotsByCue) {
1997
+ for (const slot of cueSlots) {
1998
+ byId.set(slot.slotId, {
1999
+ slotId: slot.slotId,
2000
+ index: slot.index,
2001
+ activityId: slot.activity.id,
2002
+ cueId
2003
+ });
2004
+ }
2005
+ }
2006
+ return byId;
2007
+ }, [slotsByCue]);
2008
+ const reportTo = useRef({ onSubmit, onActivityComplete, onInteraction, renderMode });
2009
+ reportTo.current = { onSubmit, onActivityComplete, onInteraction, renderMode };
2010
+ const calls = useRef(/* @__PURE__ */ new Map());
2011
+ const callsFor = (place) => {
2012
+ const made = calls.current.get(place.slotId);
2013
+ if (made !== void 0) {
2014
+ return made;
2015
+ }
2016
+ const { slotId } = place;
2017
+ const warned = /* @__PURE__ */ new Set();
2018
+ const refused = (call) => {
2019
+ if (reportTo.current.renderMode !== "review") {
2020
+ return false;
2021
+ }
2022
+ if (isDevelopment() && !warned.has(call)) {
2023
+ warned.add(call);
2024
+ console.warn(
2025
+ `InteractiveVideo: \`${call}()\` was called for question "${slotId}" in renderMode "review", where nothing is submittable. It was ignored.`
2026
+ );
2027
+ }
2028
+ return true;
2029
+ };
2030
+ const next = {
2031
+ submit: (response) => {
2032
+ if (!alive.current || refused("submit")) {
2033
+ return;
2034
+ }
2035
+ setSlotAnswered(slotId, true);
2036
+ reportTo.current.onSubmit?.(response, place);
2037
+ },
2038
+ complete: (result) => {
2039
+ if (!alive.current || refused("complete")) {
2040
+ return;
2041
+ }
2042
+ setSlotAnswered(slotId, true);
2043
+ setSlotScore(slotId, result.maxScore > 0 ? result.score / result.maxScore : void 0);
2044
+ reportTo.current.onActivityComplete?.(result, place);
2045
+ },
2046
+ clear: () => {
2047
+ if (!alive.current || refused("clear")) {
2048
+ return;
2049
+ }
2050
+ setSlotAnswered(slotId, false);
2051
+ setSlotScore(slotId, void 0);
2052
+ },
2053
+ setPending: (on) => {
2054
+ if (!alive.current) {
2055
+ return;
2056
+ }
2057
+ setPendingKey(`host:${slotId}`, on === true);
2058
+ },
2059
+ emit: (type, payload) => {
2060
+ if (!alive.current) {
2061
+ return;
2062
+ }
2063
+ reportTo.current.onInteraction?.({
2064
+ type,
2065
+ activityId: place.activityId,
2066
+ timestamp: Date.now(),
2067
+ ...payload !== void 0 ? { payload } : {}
2068
+ });
2069
+ }
2070
+ };
2071
+ calls.current.set(slotId, next);
2072
+ return next;
2073
+ };
2074
+ const reportTake = (slotId, take, state) => {
2075
+ if (!alive.current) {
2076
+ return;
2077
+ }
2078
+ const latest = latestTakes.current.get(slotId);
2079
+ if (latest !== void 0 && take < latest) {
2080
+ return;
2081
+ }
2082
+ latestTakes.current.set(slotId, take);
2083
+ setPendingKey(`take:${slotId}`, state === "in-flight");
2084
+ };
2085
+ const channels = useRef(/* @__PURE__ */ new Map());
2086
+ const channelFor = (slotId) => {
2087
+ let channel = channels.current.get(slotId);
2088
+ if (channel === void 0) {
2089
+ channel = {
2090
+ captureGroup: `${playerId}::${slotId}`,
2091
+ takeState: (take, state) => reportTake(slotId, take, state)
2092
+ };
2093
+ channels.current.set(slotId, channel);
2094
+ }
2095
+ return channel;
2096
+ };
2097
+ const seedsFor = (slotId) => ({
2098
+ restored: props.responses !== void 0 && Object.hasOwn(props.responses, slotId) ? props.responses[slotId] : void 0,
2099
+ outcome: props.outcomes !== void 0 && Object.hasOwn(props.outcomes, slotId) ? props.outcomes[slotId] : void 0
2100
+ });
2101
+ const questionFor = (slot, place, active) => {
2102
+ const { restored, outcome } = seedsFor(slot.slotId);
2103
+ return {
2104
+ activity: slot.activity,
2105
+ slot: place,
2106
+ renderMode,
2107
+ active,
2108
+ ...locale !== void 0 ? { locale } : {},
2109
+ ...restored !== void 0 ? { defaultValue: restored } : {},
2110
+ defaultSubmitted: submitted.has(slot.slotId),
2111
+ ...outcome !== void 0 ? { outcome } : {},
2112
+ portalContainer,
2113
+ ...callsFor(place)
1943
2114
  };
1944
- const restored = props.responses !== void 0 && Object.hasOwn(props.responses, slot.slotId) ? props.responses[slot.slotId] : void 0;
1945
- const outcome = props.outcomes !== void 0 && Object.hasOwn(props.outcomes, slot.slotId) ? props.outcomes[slot.slotId] : void 0;
2115
+ };
2116
+ const sdkQuestion = (slot, place) => {
2117
+ const activity = slot.activity;
2118
+ const { restored, outcome } = seedsFor(slot.slotId);
2119
+ const reported = callsFor(place);
1946
2120
  const common = {
1947
2121
  renderMode,
1948
2122
  ...restored !== void 0 ? { defaultValue: restored } : {},
1949
2123
  ...submitted.has(slot.slotId) ? { defaultSubmitted: true } : {},
1950
2124
  ...outcome !== void 0 ? { outcome } : {},
1951
- onSubmit: (response) => {
1952
- markAnswered(slot.slotId);
1953
- onSubmit?.(response, place);
1954
- },
1955
- onComplete: (result) => {
1956
- markAnswered(slot.slotId);
1957
- if (result.maxScore > 0) {
1958
- scores.current.set(slot.slotId, result.score / result.maxScore);
1959
- }
1960
- onActivityComplete?.(result, place);
1961
- },
2125
+ onSubmit: reported.submit,
2126
+ onComplete: reported.complete,
1962
2127
  ...onInteraction !== void 0 ? { onInteraction } : {},
1963
2128
  ...sanitizeHtml !== void 0 ? { sanitizeHtml } : {},
1964
2129
  ...props.strings !== void 0 ? { strings: props.strings } : {},
1965
2130
  ...locale !== void 0 ? { locale } : {}
1966
2131
  };
1967
- let question;
1968
2132
  switch (activity.type) {
1969
2133
  case "multiple-choice":
1970
- question = /* @__PURE__ */ jsx(
2134
+ return /* @__PURE__ */ jsx(
1971
2135
  MultipleChoice,
1972
2136
  {
1973
2137
  data: activity,
@@ -1975,12 +2139,10 @@ function Player(props) {
1975
2139
  ...shuffleSeed !== void 0 ? { shuffleSeed } : {}
1976
2140
  }
1977
2141
  );
1978
- break;
1979
2142
  case "fill-in-the-blanks":
1980
- question = /* @__PURE__ */ jsx(FillInTheBlanks, { data: activity, ...common });
1981
- break;
2143
+ return /* @__PURE__ */ jsx(FillInTheBlanks, { data: activity, ...common });
1982
2144
  case "gap-select":
1983
- question = /* @__PURE__ */ jsx(
2145
+ return /* @__PURE__ */ jsx(
1984
2146
  GapSelect,
1985
2147
  {
1986
2148
  data: activity,
@@ -1988,14 +2150,12 @@ function Player(props) {
1988
2150
  ...shuffleSeed !== void 0 ? { shuffleSeed } : {}
1989
2151
  }
1990
2152
  );
1991
- break;
1992
2153
  case "dictation":
1993
- question = /* @__PURE__ */ jsx(Dictation, { data: activity, ...common });
1994
- break;
2154
+ return /* @__PURE__ */ jsx(Dictation, { data: activity, ...common });
1995
2155
  case "read-aloud": {
1996
2156
  const binding = slotBindings.get(slot.slotId);
1997
2157
  const assessment = props.assessments !== void 0 && Object.hasOwn(props.assessments, slot.slotId) ? props.assessments[slot.slotId] : void 0;
1998
- question = /* @__PURE__ */ jsx(
2158
+ return /* @__PURE__ */ jsx(
1999
2159
  ReadAloud,
2000
2160
  {
2001
2161
  data: activity,
@@ -2005,21 +2165,10 @@ function Player(props) {
2005
2165
  ...workletUrl !== void 0 ? { workletUrl } : {}
2006
2166
  }
2007
2167
  );
2008
- break;
2009
2168
  }
2010
2169
  default:
2011
- question = null;
2012
- }
2013
- return question;
2014
- };
2015
- const channels = useRef(/* @__PURE__ */ new Map());
2016
- const channelFor = (slotId) => {
2017
- let channel = channels.current.get(slotId);
2018
- if (channel === void 0) {
2019
- channel = { captureGroup: `${playerId}::${slotId}`, takeState: () => void 0 };
2020
- channels.current.set(slotId, channel);
2170
+ return null;
2021
2171
  }
2022
- return channel;
2023
2172
  };
2024
2173
  if (devError !== null) {
2025
2174
  throw devError;
@@ -2440,7 +2589,7 @@ function Player(props) {
2440
2589
  "aria-labelledby": `${playerId}-quiz-title`,
2441
2590
  hidden: openQuiz === null,
2442
2591
  onKeyDown: (event) => {
2443
- if (event.key === "Escape" && !required && openQuiz !== null) {
2592
+ if (event.key === "Escape" && !event.defaultPrevented && !required && openQuiz !== null) {
2444
2593
  event.preventDefault();
2445
2594
  closeQuiz("skip");
2446
2595
  }
@@ -2487,15 +2636,29 @@ function Player(props) {
2487
2636
  ] }) : null
2488
2637
  ] }) : null,
2489
2638
  /* @__PURE__ */ jsx("div", { className: "lk-iv-quiz-body", children: cues.filter((cue) => mounted.has(cue.id)).map(
2490
- (cue) => (slotsByCue.get(cue.id) ?? []).map((slot, index) => /* @__PURE__ */ jsx(
2491
- QuestionPane,
2492
- {
2493
- hidden: !(openQuiz?.cueId === cue.id && step === index),
2494
- channel: channelFor(slot.slotId),
2495
- children: renderQuestion(slot, cue.id)
2496
- },
2497
- slot.slotId
2498
- ))
2639
+ (cue) => (slotsByCue.get(cue.id) ?? []).map((slot, index) => {
2640
+ const shown = openQuiz?.cueId === cue.id && step === index;
2641
+ const place = places.get(slot.slotId);
2642
+ return /* @__PURE__ */ jsx(
2643
+ QuestionPane,
2644
+ {
2645
+ hidden: !shown,
2646
+ channel: channelFor(slot.slotId),
2647
+ children: /* @__PURE__ */ jsx(
2648
+ SlotQuestion,
2649
+ {
2650
+ title: slot.activity.title,
2651
+ strings,
2652
+ render: props.renderQuestion,
2653
+ question: props.renderQuestion === void 0 ? void 0 : questionFor(slot, place, shown),
2654
+ sdk: () => sdkQuestion(slot, place),
2655
+ setHostPending: callsFor(place).setPending
2656
+ }
2657
+ )
2658
+ },
2659
+ slot.slotId
2660
+ );
2661
+ })
2499
2662
  ) }),
2500
2663
  openCue !== void 0 ? /* @__PURE__ */ jsxs("div", { className: "lk-iv-quiz-foot", children: [
2501
2664
  /* @__PURE__ */ jsxs("div", { className: "lk-iv-quiz-foot-start", children: [
@@ -2551,7 +2714,14 @@ function Player(props) {
2551
2714
  }
2552
2715
  },
2553
2716
  onWatchAgain: togglePlay,
2554
- onFinish: renderMode === "review" ? void 0 : finish
2717
+ pending: answerPending,
2718
+ onFinish: renderMode === "review" ? void 0 : () => {
2719
+ if (pendingRef.current.size > 0) {
2720
+ announce(strings.videoAnswerPending);
2721
+ return;
2722
+ }
2723
+ finish();
2724
+ }
2555
2725
  }
2556
2726
  ) : null,
2557
2727
  sheet ? /* @__PURE__ */ jsx(
@@ -2602,12 +2772,43 @@ function Player(props) {
2602
2772
  }
2603
2773
  }
2604
2774
  ) : null,
2605
- /* @__PURE__ */ jsx("div", { className: "lk-iv-live", "aria-live": "polite", "aria-atomic": "true", children: announcement })
2775
+ /* @__PURE__ */ jsx("div", { className: "lk-iv-live", "aria-live": "polite", "aria-atomic": "true", children: announcement }),
2776
+ /* @__PURE__ */ jsx("div", { ref: setPortalContainer, className: "lk-iv-portal" })
2606
2777
  ]
2607
2778
  }
2608
2779
  )
2609
2780
  );
2610
2781
  }
2782
+ function SlotQuestion({
2783
+ title,
2784
+ strings,
2785
+ ...content
2786
+ }) {
2787
+ return /* @__PURE__ */ jsx(
2788
+ ActivityErrorBoundary,
2789
+ {
2790
+ ...title !== void 0 ? { activityTitle: title } : {},
2791
+ strings,
2792
+ children: /* @__PURE__ */ jsx(SlotContent, { ...content })
2793
+ }
2794
+ );
2795
+ }
2796
+ function SlotContent({
2797
+ render,
2798
+ question,
2799
+ sdk,
2800
+ setHostPending
2801
+ }) {
2802
+ const hosted = render !== void 0 && question !== void 0 ? render(question) : void 0;
2803
+ const hosting = hosted !== void 0;
2804
+ useEffect(() => {
2805
+ if (!hosting) {
2806
+ return;
2807
+ }
2808
+ return () => setHostPending(false);
2809
+ }, [hosting, setHostPending]);
2810
+ return hosting ? hosted : sdk();
2811
+ }
2611
2812
  function QuestionPane({
2612
2813
  hidden,
2613
2814
  channel,
@@ -2679,5 +2880,5 @@ function InteractiveVideo2(props) {
2679
2880
  }
2680
2881
 
2681
2882
  export { InteractiveVideo2 as InteractiveVideo, resolveCaptionTracks };
2682
- //# sourceMappingURL=chunk-3IHVDQRC.js.map
2683
- //# sourceMappingURL=chunk-3IHVDQRC.js.map
2883
+ //# sourceMappingURL=chunk-DVOPHC2Q.js.map
2884
+ //# sourceMappingURL=chunk-DVOPHC2Q.js.map