@intellectif/lk-react 23.1.0 → 23.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +9 -5
- package/dist/ActivityErrorBoundary-B379hvtL.cjs +39 -0
- package/dist/ActivityErrorBoundary-B379hvtL.cjs.map +1 -0
- package/dist/ActivityErrorBoundary-Dw6SbWv3.js +34 -0
- package/dist/ActivityErrorBoundary-Dw6SbWv3.js.map +1 -0
- package/dist/ActivityMedia-CQQTRNl7.cjs +641 -0
- package/dist/ActivityMedia-CQQTRNl7.cjs.map +1 -0
- package/dist/ActivityMedia-vDiP-Psa.js +636 -0
- package/dist/ActivityMedia-vDiP-Psa.js.map +1 -0
- package/dist/ActivityPreview-1hhTkYuj.js +240 -0
- package/dist/ActivityPreview-1hhTkYuj.js.map +1 -0
- package/dist/ActivityPreview-BKr34exz.cjs +245 -0
- package/dist/ActivityPreview-BKr34exz.cjs.map +1 -0
- package/dist/ActivitySequence-CkX87pfB.js +839 -0
- package/dist/ActivitySequence-CkX87pfB.js.map +1 -0
- package/dist/ActivitySequence-CmoSyDnp.cjs +844 -0
- package/dist/ActivitySequence-CmoSyDnp.cjs.map +1 -0
- package/dist/Dictation-BlxRp-3k.js +926 -0
- package/dist/Dictation-BlxRp-3k.js.map +1 -0
- package/dist/Dictation-jECzL88E.cjs +931 -0
- package/dist/Dictation-jECzL88E.cjs.map +1 -0
- package/dist/FeedbackRegion-BNNMtTY-.cjs +108 -0
- package/dist/FeedbackRegion-BNNMtTY-.cjs.map +1 -0
- package/dist/FeedbackRegion-CyPSp8bA.js +85 -0
- package/dist/FeedbackRegion-CyPSp8bA.js.map +1 -0
- package/dist/FillInTheBlanks-B6xzqYXx.js +474 -0
- package/dist/FillInTheBlanks-B6xzqYXx.js.map +1 -0
- package/dist/FillInTheBlanks-BpKhdHg2.cjs +479 -0
- package/dist/FillInTheBlanks-BpKhdHg2.cjs.map +1 -0
- package/dist/GapSelect-DUSiaEQH.js +441 -0
- package/dist/GapSelect-DUSiaEQH.js.map +1 -0
- package/dist/GapSelect-payTq_Zn.cjs +446 -0
- package/dist/GapSelect-payTq_Zn.cjs.map +1 -0
- package/dist/InteractiveVideo-BguGv-HA.cjs +2955 -0
- package/dist/InteractiveVideo-BguGv-HA.cjs.map +1 -0
- package/dist/InteractiveVideo-Cg2jbU3j.js +2944 -0
- package/dist/InteractiveVideo-Cg2jbU3j.js.map +1 -0
- package/dist/LkAiProvider-DAPpA3PQ.d.cts +84 -0
- package/dist/LkAiProvider-DAPpA3PQ.d.cts.map +1 -0
- package/dist/LkAiProvider-QdEcIhoo.d.ts +84 -0
- package/dist/LkAiProvider-QdEcIhoo.d.ts.map +1 -0
- package/dist/LkIntlProvider-D_FdYXjQ.js +375 -0
- package/dist/LkIntlProvider-D_FdYXjQ.js.map +1 -0
- package/dist/LkIntlProvider-oi57lYB0.cjs +410 -0
- package/dist/LkIntlProvider-oi57lYB0.cjs.map +1 -0
- package/dist/MultipleChoice-COn5MtKY.cjs +433 -0
- package/dist/MultipleChoice-COn5MtKY.cjs.map +1 -0
- package/dist/MultipleChoice-Dra0_KYD.js +428 -0
- package/dist/MultipleChoice-Dra0_KYD.js.map +1 -0
- package/dist/PronunciationFeedback-DlMPGjkR.cjs +831 -0
- package/dist/PronunciationFeedback-DlMPGjkR.cjs.map +1 -0
- package/dist/PronunciationFeedback-Dxd6XPkO.js +772 -0
- package/dist/PronunciationFeedback-Dxd6XPkO.js.map +1 -0
- package/dist/ReadAloud-C8RhaUqV.js +1092 -0
- package/dist/{chunk-DC4CQCK5.js.map → ReadAloud-C8RhaUqV.js.map} +1 -1
- package/dist/ReadAloud-akDehitT.cjs +1103 -0
- package/dist/ReadAloud-akDehitT.cjs.map +1 -0
- package/dist/StimulusPanel-BgzByfkb.js +74 -0
- package/dist/StimulusPanel-BgzByfkb.js.map +1 -0
- package/dist/StimulusPanel-b66gaN3i.cjs +79 -0
- package/dist/StimulusPanel-b66gaN3i.cjs.map +1 -0
- package/dist/ThemeProvider-B6DyGqzh.d.cts +62 -0
- package/dist/ThemeProvider-B6DyGqzh.d.cts.map +1 -0
- package/dist/ThemeProvider-BCrxBaY-.d.ts +62 -0
- package/dist/ThemeProvider-BCrxBaY-.d.ts.map +1 -0
- package/dist/ThemeProvider-BRN1tj4n.cjs +187 -0
- package/dist/ThemeProvider-BRN1tj4n.cjs.map +1 -0
- package/dist/ThemeProvider-C7K9iMLX.js +158 -0
- package/dist/ThemeProvider-C7K9iMLX.js.map +1 -0
- package/dist/WrittenResponse-Bj3eFmGl.cjs +491 -0
- package/dist/WrittenResponse-Bj3eFmGl.cjs.map +1 -0
- package/dist/WrittenResponse-Cl8G6nO_.js +486 -0
- package/dist/WrittenResponse-Cl8G6nO_.js.map +1 -0
- package/dist/ai/LkAiProvider.cjs +34 -14
- package/dist/ai/LkAiProvider.cjs.map +1 -1
- package/dist/ai/LkAiProvider.d.cts +2 -83
- package/dist/ai/LkAiProvider.d.ts +2 -83
- package/dist/ai/LkAiProvider.js +33 -2
- package/dist/ai/LkAiProvider.js.map +1 -1
- package/dist/ai/useAiHelp.cjs +7 -25
- package/dist/ai/useAiHelp.d.cts +175 -177
- package/dist/ai/useAiHelp.d.cts.map +1 -0
- package/dist/ai/useAiHelp.d.ts +175 -177
- package/dist/ai/useAiHelp.d.ts.map +1 -0
- package/dist/ai/useAiHelp.js +3 -4
- package/dist/components/ActivityPreview.cjs +3 -29
- package/dist/components/ActivityPreview.d.cts +2 -81
- package/dist/components/ActivityPreview.d.ts +2 -81
- package/dist/components/ActivityPreview.js +2 -20
- package/dist/components/ActivitySequence.cjs +3 -30
- package/dist/components/ActivitySequence.d.cts +2 -432
- package/dist/components/ActivitySequence.d.ts +2 -432
- package/dist/components/ActivitySequence.js +2 -21
- package/dist/components/Dictation.cjs +4 -21
- package/dist/components/Dictation.d.cts +2 -19
- package/dist/components/Dictation.d.ts +2 -19
- package/dist/components/Dictation.js +3 -12
- package/dist/components/FillInTheBlanks.cjs +4 -20
- package/dist/components/FillInTheBlanks.d.cts +2 -30
- package/dist/components/FillInTheBlanks.d.ts +2 -30
- package/dist/components/FillInTheBlanks.js +3 -11
- package/dist/components/GapSelect.cjs +4 -20
- package/dist/components/GapSelect.d.cts +2 -27
- package/dist/components/GapSelect.d.ts +2 -27
- package/dist/components/GapSelect.js +3 -11
- package/dist/components/InteractiveVideo.cjs +5 -32
- package/dist/components/InteractiveVideo.d.cts +3 -291
- package/dist/components/InteractiveVideo.d.ts +3 -291
- package/dist/components/InteractiveVideo.js +3 -19
- package/dist/components/MultipleChoice.cjs +4 -20
- package/dist/components/MultipleChoice.d.cts +2 -26
- package/dist/components/MultipleChoice.d.ts +2 -26
- package/dist/components/MultipleChoice.js +3 -11
- package/dist/components/PronunciationFeedback.cjs +4 -17
- package/dist/components/PronunciationFeedback.d.cts +2 -90
- package/dist/components/PronunciationFeedback.d.ts +2 -90
- package/dist/components/PronunciationFeedback.js +3 -8
- package/dist/components/ReadAloud.cjs +4 -22
- package/dist/components/ReadAloud.d.cts +7 -11
- package/dist/components/ReadAloud.d.cts.map +1 -0
- package/dist/components/ReadAloud.d.ts +7 -11
- package/dist/components/ReadAloud.d.ts.map +1 -0
- package/dist/components/ReadAloud.js +3 -13
- package/dist/components/StimulusPanel.cjs +3 -14
- package/dist/components/StimulusPanel.d.cts +2 -51
- package/dist/components/StimulusPanel.d.ts +2 -51
- package/dist/components/StimulusPanel.js +2 -5
- package/dist/components/WrittenResponse.cjs +4 -19
- package/dist/components/WrittenResponse.d.cts +3 -18
- package/dist/components/WrittenResponse.d.ts +3 -18
- package/dist/components/WrittenResponse.js +3 -10
- package/dist/direction-Dq0_HtA5.js +183 -0
- package/dist/direction-Dq0_HtA5.js.map +1 -0
- package/dist/direction-K1pj0HCA.cjs +200 -0
- package/dist/direction-K1pj0HCA.cjs.map +1 -0
- package/dist/hooks/useActivityState.cjs +28 -10
- package/dist/hooks/useActivityState.cjs.map +1 -1
- package/dist/hooks/useActivityState.d.cts +25 -24
- package/dist/hooks/useActivityState.d.cts.map +1 -0
- package/dist/hooks/useActivityState.d.ts +25 -24
- package/dist/hooks/useActivityState.d.ts.map +1 -0
- package/dist/hooks/useActivityState.js +28 -2
- package/dist/hooks/useActivityState.js.map +1 -1
- package/dist/hooks/useSpeechRecorder.cjs +5 -16
- package/dist/hooks/useSpeechRecorder.d.cts +2 -127
- package/dist/hooks/useSpeechRecorder.d.ts +2 -127
- package/dist/hooks/useSpeechRecorder.js +3 -3
- package/dist/hooks/useXAPI.cjs +126 -10
- package/dist/hooks/useXAPI.cjs.map +1 -1
- package/dist/hooks/useXAPI.d.cts +14 -14
- package/dist/hooks/useXAPI.d.cts.map +1 -0
- package/dist/hooks/useXAPI.d.ts +14 -14
- package/dist/hooks/useXAPI.d.ts.map +1 -0
- package/dist/hooks/useXAPI.js +126 -2
- package/dist/hooks/useXAPI.js.map +1 -1
- package/dist/i18n/LkIntlProvider.cjs +9 -32
- package/dist/i18n/LkIntlProvider.d.cts +34 -38
- package/dist/i18n/LkIntlProvider.d.cts.map +1 -0
- package/dist/i18n/LkIntlProvider.d.ts +34 -38
- package/dist/i18n/LkIntlProvider.d.ts.map +1 -0
- package/dist/i18n/LkIntlProvider.js +3 -3
- package/dist/index-6P4rJHrJ.d.cts +432 -0
- package/dist/index-6P4rJHrJ.d.cts.map +1 -0
- package/dist/index-90CX504k.d.ts +90 -0
- package/dist/index-90CX504k.d.ts.map +1 -0
- package/dist/index-B_SgnjsC.d.cts +291 -0
- package/dist/index-B_SgnjsC.d.cts.map +1 -0
- package/dist/index-BavVW5z5.d.cts +25 -0
- package/dist/index-BavVW5z5.d.cts.map +1 -0
- package/dist/index-BhHv6Fiu.d.cts +28 -0
- package/dist/index-BhHv6Fiu.d.cts.map +1 -0
- package/dist/index-Bsu8VciA.d.ts +432 -0
- package/dist/index-Bsu8VciA.d.ts.map +1 -0
- package/dist/index-C1KGpD1k.d.ts +49 -0
- package/dist/index-C1KGpD1k.d.ts.map +1 -0
- package/dist/index-CKTpYTCC.d.cts +49 -0
- package/dist/index-CKTpYTCC.d.cts.map +1 -0
- package/dist/index-CO_SRFJj.d.ts +16 -0
- package/dist/index-CO_SRFJj.d.ts.map +1 -0
- package/dist/index-CSsuUsP7.d.cts +124 -0
- package/dist/index-CSsuUsP7.d.cts.map +1 -0
- package/dist/index-CunUyeuN.d.cts +78 -0
- package/dist/index-CunUyeuN.d.cts.map +1 -0
- package/dist/index-CzfRzks6.d.cts +90 -0
- package/dist/index-CzfRzks6.d.cts.map +1 -0
- package/dist/index-D14xtpBS.d.cts +24 -0
- package/dist/index-D14xtpBS.d.cts.map +1 -0
- package/dist/index-DHRPAPZf.d.ts +291 -0
- package/dist/index-DHRPAPZf.d.ts.map +1 -0
- package/dist/index-DLV6cK26.d.cts +16 -0
- package/dist/index-DLV6cK26.d.cts.map +1 -0
- package/dist/index-DbCCQ9cP.d.ts +124 -0
- package/dist/index-DbCCQ9cP.d.ts.map +1 -0
- package/dist/index-DeV5B2m5.d.ts +24 -0
- package/dist/index-DeV5B2m5.d.ts.map +1 -0
- package/dist/index-TZCJVSge.d.ts +28 -0
- package/dist/index-TZCJVSge.d.ts.map +1 -0
- package/dist/index-ZVvLHsd3.d.ts +25 -0
- package/dist/index-ZVvLHsd3.d.ts.map +1 -0
- package/dist/index-eQwBgpSw.d.ts +78 -0
- package/dist/index-eQwBgpSw.d.ts.map +1 -0
- package/dist/index.cjs +118 -163
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -24
- package/dist/index.d.ts +20 -24
- package/dist/index.js +84 -29
- package/dist/index.js.map +1 -1
- package/dist/theme/ThemeProvider.cjs +8 -28
- package/dist/theme/ThemeProvider.d.cts +2 -59
- package/dist/theme/ThemeProvider.d.ts +2 -59
- package/dist/theme/ThemeProvider.js +3 -3
- package/dist/tries-CbbtZkdG.cjs +363 -0
- package/dist/tries-CbbtZkdG.cjs.map +1 -0
- package/dist/tries-DTHSrLCm.js +310 -0
- package/dist/tries-DTHSrLCm.js.map +1 -0
- package/dist/types-BOQd93x3.d.cts +994 -0
- package/dist/types-BOQd93x3.d.cts.map +1 -0
- package/dist/types-HIrgF_mS.d.ts +994 -0
- package/dist/types-HIrgF_mS.d.ts.map +1 -0
- package/dist/useAiHelp-B-ccDL5Q.js +906 -0
- package/dist/useAiHelp-B-ccDL5Q.js.map +1 -0
- package/dist/useAiHelp-h9yOLFqt.cjs +995 -0
- package/dist/useAiHelp-h9yOLFqt.cjs.map +1 -0
- package/dist/useSpeechRecorder--O_f-xvt.cjs +720 -0
- package/dist/useSpeechRecorder--O_f-xvt.cjs.map +1 -0
- package/dist/useSpeechRecorder-DtAYvl02.d.cts +154 -0
- package/dist/useSpeechRecorder-DtAYvl02.d.cts.map +1 -0
- package/dist/useSpeechRecorder-DtAYvl02.d.ts +154 -0
- package/dist/useSpeechRecorder-DtAYvl02.d.ts.map +1 -0
- package/dist/useSpeechRecorder-zIi9Dlw8.js +709 -0
- package/dist/useSpeechRecorder-zIi9Dlw8.js.map +1 -0
- package/package.json +10 -11
- package/dist/WrittenResponse-B7A5gHMj.d.ts +0 -115
- package/dist/WrittenResponse-BpL9p2Hn.d.cts +0 -115
- package/dist/ai/useAiHelp.cjs.map +0 -1
- package/dist/ai/useAiHelp.js.map +0 -1
- package/dist/chunk-2PNSVBLQ.cjs +0 -54
- package/dist/chunk-2PNSVBLQ.cjs.map +0 -1
- package/dist/chunk-2TW6WLBC.js +0 -80
- package/dist/chunk-2TW6WLBC.js.map +0 -1
- package/dist/chunk-4ZZWZSWI.js +0 -61
- package/dist/chunk-4ZZWZSWI.js.map +0 -1
- package/dist/chunk-6N43WDVG.js +0 -29
- package/dist/chunk-6N43WDVG.js.map +0 -1
- package/dist/chunk-7ALBSPYR.cjs +0 -436
- package/dist/chunk-7ALBSPYR.cjs.map +0 -1
- package/dist/chunk-7NFY3M3W.js +0 -234
- package/dist/chunk-7NFY3M3W.js.map +0 -1
- package/dist/chunk-7SB2MJA4.js +0 -29
- package/dist/chunk-7SB2MJA4.js.map +0 -1
- package/dist/chunk-ABJJ3LII.js +0 -434
- package/dist/chunk-ABJJ3LII.js.map +0 -1
- package/dist/chunk-BIGGXHAW.cjs +0 -342
- package/dist/chunk-BIGGXHAW.cjs.map +0 -1
- package/dist/chunk-C7QZE5BI.js +0 -430
- package/dist/chunk-C7QZE5BI.js.map +0 -1
- package/dist/chunk-CWTUWNV3.cjs +0 -2937
- package/dist/chunk-CWTUWNV3.cjs.map +0 -1
- package/dist/chunk-DC4CQCK5.js +0 -915
- package/dist/chunk-DS6HN3YR.cjs +0 -496
- package/dist/chunk-DS6HN3YR.cjs.map +0 -1
- package/dist/chunk-FDQR4GAZ.js +0 -240
- package/dist/chunk-FDQR4GAZ.js.map +0 -1
- package/dist/chunk-FKGMSOJF.js +0 -394
- package/dist/chunk-FKGMSOJF.js.map +0 -1
- package/dist/chunk-G6PXJ2SA.js +0 -823
- package/dist/chunk-G6PXJ2SA.js.map +0 -1
- package/dist/chunk-HNLRVRQR.js +0 -335
- package/dist/chunk-HNLRVRQR.js.map +0 -1
- package/dist/chunk-J3C72RPK.cjs +0 -31
- package/dist/chunk-J3C72RPK.cjs.map +0 -1
- package/dist/chunk-K63VPFOV.cjs +0 -134
- package/dist/chunk-K63VPFOV.cjs.map +0 -1
- package/dist/chunk-KFLIDTDU.cjs +0 -825
- package/dist/chunk-KFLIDTDU.cjs.map +0 -1
- package/dist/chunk-KHKH5LCT.cjs +0 -250
- package/dist/chunk-KHKH5LCT.cjs.map +0 -1
- package/dist/chunk-LBEKRN6Y.cjs +0 -664
- package/dist/chunk-LBEKRN6Y.cjs.map +0 -1
- package/dist/chunk-LPUWXIPR.cjs +0 -578
- package/dist/chunk-LPUWXIPR.cjs.map +0 -1
- package/dist/chunk-MCO52PV5.js +0 -520
- package/dist/chunk-MCO52PV5.js.map +0 -1
- package/dist/chunk-MKXEM2C5.cjs +0 -396
- package/dist/chunk-MKXEM2C5.cjs.map +0 -1
- package/dist/chunk-MXNC5UNO.js +0 -563
- package/dist/chunk-MXNC5UNO.js.map +0 -1
- package/dist/chunk-NAK55BYU.cjs +0 -859
- package/dist/chunk-NAK55BYU.cjs.map +0 -1
- package/dist/chunk-NDBMKGQ5.cjs +0 -599
- package/dist/chunk-NDBMKGQ5.cjs.map +0 -1
- package/dist/chunk-OD65OZIW.cjs +0 -159
- package/dist/chunk-OD65OZIW.cjs.map +0 -1
- package/dist/chunk-ORT3KLGI.js +0 -50
- package/dist/chunk-ORT3KLGI.js.map +0 -1
- package/dist/chunk-PNNUPUH4.cjs +0 -82
- package/dist/chunk-PNNUPUH4.cjs.map +0 -1
- package/dist/chunk-PWHECW6N.js +0 -2934
- package/dist/chunk-PWHECW6N.js.map +0 -1
- package/dist/chunk-RBS5A2DF.js +0 -857
- package/dist/chunk-RBS5A2DF.js.map +0 -1
- package/dist/chunk-RPWUDXG5.cjs +0 -63
- package/dist/chunk-RPWUDXG5.cjs.map +0 -1
- package/dist/chunk-TCDJY3H6.js +0 -494
- package/dist/chunk-TCDJY3H6.js.map +0 -1
- package/dist/chunk-TP5UMRAU.cjs +0 -918
- package/dist/chunk-TP5UMRAU.cjs.map +0 -1
- package/dist/chunk-TS7J6DTY.js +0 -597
- package/dist/chunk-TS7J6DTY.js.map +0 -1
- package/dist/chunk-TUVENMXB.cjs +0 -20
- package/dist/chunk-TUVENMXB.cjs.map +0 -1
- package/dist/chunk-UDCQTOVU.cjs +0 -523
- package/dist/chunk-UDCQTOVU.cjs.map +0 -1
- package/dist/chunk-UHKZXOXD.js +0 -128
- package/dist/chunk-UHKZXOXD.js.map +0 -1
- package/dist/chunk-V6BA44DS.cjs +0 -236
- package/dist/chunk-V6BA44DS.cjs.map +0 -1
- package/dist/chunk-XO5R73Y4.js +0 -653
- package/dist/chunk-XO5R73Y4.js.map +0 -1
- package/dist/chunk-XOR4MPEN.cjs +0 -31
- package/dist/chunk-XOR4MPEN.cjs.map +0 -1
- package/dist/chunk-YORQQXGO.js +0 -155
- package/dist/chunk-YORQQXGO.js.map +0 -1
- package/dist/chunk-YY4PFR3Z.cjs +0 -432
- package/dist/chunk-YY4PFR3Z.cjs.map +0 -1
- package/dist/chunk-ZPI6Q42R.js +0 -17
- package/dist/chunk-ZPI6Q42R.js.map +0 -1
- package/dist/components/ActivityPreview.cjs.map +0 -1
- package/dist/components/ActivityPreview.js.map +0 -1
- package/dist/components/ActivitySequence.cjs.map +0 -1
- package/dist/components/ActivitySequence.js.map +0 -1
- package/dist/components/Dictation.cjs.map +0 -1
- package/dist/components/Dictation.js.map +0 -1
- package/dist/components/FillInTheBlanks.cjs.map +0 -1
- package/dist/components/FillInTheBlanks.js.map +0 -1
- package/dist/components/GapSelect.cjs.map +0 -1
- package/dist/components/GapSelect.js.map +0 -1
- package/dist/components/InteractiveVideo.cjs.map +0 -1
- package/dist/components/InteractiveVideo.js.map +0 -1
- package/dist/components/MultipleChoice.cjs.map +0 -1
- package/dist/components/MultipleChoice.js.map +0 -1
- package/dist/components/PronunciationFeedback.cjs.map +0 -1
- package/dist/components/PronunciationFeedback.js.map +0 -1
- package/dist/components/ReadAloud.cjs.map +0 -1
- package/dist/components/ReadAloud.js.map +0 -1
- package/dist/components/StimulusPanel.cjs.map +0 -1
- package/dist/components/StimulusPanel.js.map +0 -1
- package/dist/components/WrittenResponse.cjs.map +0 -1
- package/dist/components/WrittenResponse.js.map +0 -1
- package/dist/hooks/useSpeechRecorder.cjs.map +0 -1
- package/dist/hooks/useSpeechRecorder.js.map +0 -1
- package/dist/i18n/LkIntlProvider.cjs.map +0 -1
- package/dist/i18n/LkIntlProvider.js.map +0 -1
- package/dist/machine-CuwKqWXC.d.cts +0 -26
- package/dist/machine-CuwKqWXC.d.ts +0 -26
- package/dist/theme/ThemeProvider.cjs.map +0 -1
- package/dist/theme/ThemeProvider.js.map +0 -1
- package/dist/types-C4IsUQsK.d.ts +0 -989
- package/dist/types-CJgZWtl4.d.cts +0 -989
|
@@ -0,0 +1,839 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { d as randomSessionId, f as SequenceSlotContext, h as takeOf, p as mintTake } from "./useAiHelp-B-ccDL5Q.js";
|
|
3
|
+
import { i as useLkStrings } from "./LkIntlProvider-D_FdYXjQ.js";
|
|
4
|
+
import { t as Dictation } from "./Dictation-BlxRp-3k.js";
|
|
5
|
+
import { t as FillInTheBlanks } from "./FillInTheBlanks-B6xzqYXx.js";
|
|
6
|
+
import { t as GapSelect } from "./GapSelect-DUSiaEQH.js";
|
|
7
|
+
import { t as MultipleChoice } from "./MultipleChoice-Dra0_KYD.js";
|
|
8
|
+
import { n as stopCaptureGroup, t as ReadAloud } from "./ReadAloud-C8RhaUqV.js";
|
|
9
|
+
import { t as WrittenResponse } from "./WrittenResponse-Cl8G6nO_.js";
|
|
10
|
+
import { t as StimulusPanel } from "./StimulusPanel-BgzByfkb.js";
|
|
11
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
12
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
13
|
+
import { flattenSequence, isItemGroup, resolveDeliveryPolicy, resolveItemScoringPolicy, resolvePlaybackPolicy, slotMediaKey, stimulusMediaKey } from "@intellectif/lk-core";
|
|
14
|
+
//#region src/components/ActivitySequence/ActivitySequence.tsx
|
|
15
|
+
/**
|
|
16
|
+
* One slot's key for the life of a set: its pane's React key, and the name its
|
|
17
|
+
* portal node is held under.
|
|
18
|
+
*/
|
|
19
|
+
function slotKeyOf(setKey, slot) {
|
|
20
|
+
return `${setKey}::slot-${slot.slotId}-${slot.activity.id}`;
|
|
21
|
+
}
|
|
22
|
+
/** The authored-entry prefix of a slot id: `"3"` for `"3"` and for `"3.1"`. */
|
|
23
|
+
function entryKeyOf(slot) {
|
|
24
|
+
return slot.slotId.split(".")[0] ?? slot.slotId;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Names the capture groups of one pager, so a slot's microphone belongs to that
|
|
28
|
+
* pager alone. Per mount, for the reason the playback groups are: slot ids are
|
|
29
|
+
* short and repeat ("0", "1.0"), so two pagers on one page — a review beside a
|
|
30
|
+
* live attempt — would share a group, and hiding one's first question would
|
|
31
|
+
* stop the other's recorder. The name never reaches the DOM, so a server and
|
|
32
|
+
* the client that hydrates it may mint different ones.
|
|
33
|
+
*/
|
|
34
|
+
let captureScopeCount = 0;
|
|
35
|
+
/**
|
|
36
|
+
* A pane that is kept MOUNTED but shown only when it is the current one, and
|
|
37
|
+
* that stops any media it contains on the way out.
|
|
38
|
+
*
|
|
39
|
+
* `hidden` alone is not enough. It resolves to `display: none`, and CSS does
|
|
40
|
+
* not touch playback: an `<audio>` or `<video>` inside a hidden subtree keeps
|
|
41
|
+
* playing to the end. (The HTML spec pauses a media element when it is
|
|
42
|
+
* REMOVED from the document — a different thing, and the thing this pager
|
|
43
|
+
* deliberately does not do, because unmounting is what used to destroy the
|
|
44
|
+
* learner's answers.) So a recording started for one question would carry on
|
|
45
|
+
* underneath the next, which for a listening paper means the passage plays
|
|
46
|
+
* while the learner is somewhere else entirely.
|
|
47
|
+
*
|
|
48
|
+
* Pausing preserves `currentTime`: coming back to a group resumes where the
|
|
49
|
+
* learner left off. Nothing auto-plays on the way in — starting audio the
|
|
50
|
+
* learner did not ask for is its own defect.
|
|
51
|
+
*
|
|
52
|
+
* `embed` media (a provider iframe) cannot be paused this way: controlling a
|
|
53
|
+
* third-party player needs its own JS API, which the SDK has no reliable
|
|
54
|
+
* access to because the author supplies the embed URL. Use `audio`/`video`
|
|
55
|
+
* media for anything that must stop when the learner navigates.
|
|
56
|
+
*
|
|
57
|
+
* A microphone is the other way round: it is not an element in this subtree, so
|
|
58
|
+
* no query can find it. A slot pane hands its content a `channel` through
|
|
59
|
+
* `SequenceSlotContext` — never a prop, so nothing on a public component's
|
|
60
|
+
* signature can name it — whose `captureGroup` the pane's recorders join, and
|
|
61
|
+
* hiding the pane stops them: a learner who navigates away mid-take must not
|
|
62
|
+
* leave the microphone live under the next question.
|
|
63
|
+
*/
|
|
64
|
+
function SequencePane({ className, hidden, channel, children }) {
|
|
65
|
+
const ref = useRef(null);
|
|
66
|
+
const captureGroup = channel?.captureGroup;
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (!hidden) return;
|
|
69
|
+
if (captureGroup !== void 0) stopCaptureGroup(captureGroup);
|
|
70
|
+
const node = ref.current;
|
|
71
|
+
if (node === null) return;
|
|
72
|
+
for (const media of node.querySelectorAll("audio, video")) {
|
|
73
|
+
const element = media;
|
|
74
|
+
if (!element.paused) element.pause();
|
|
75
|
+
}
|
|
76
|
+
}, [hidden, captureGroup]);
|
|
77
|
+
return /* @__PURE__ */ jsx("div", {
|
|
78
|
+
className,
|
|
79
|
+
hidden,
|
|
80
|
+
ref,
|
|
81
|
+
children: channel === void 0 ? children : /* @__PURE__ */ jsx(SequenceSlotContext.Provider, {
|
|
82
|
+
value: channel,
|
|
83
|
+
children
|
|
84
|
+
})
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Presents a set of activities as an in-place pager: one question visible at a
|
|
89
|
+
* time, navigated with Prev/Next (no page scrolling). Linear free navigation —
|
|
90
|
+
* the learner may move back and forth; activities keep their own submit and
|
|
91
|
+
* scoring. On navigation, focus moves to the question region and the position
|
|
92
|
+
* is announced via an aria-live region.
|
|
93
|
+
*
|
|
94
|
+
* Item groups are flattened into consecutive questions; the group's stimulus
|
|
95
|
+
* is mounted ONCE, beside the question region rather than inside it, and shown
|
|
96
|
+
* alongside every question in the group — so a passage stays put, and a
|
|
97
|
+
* recording keeps its position, as the learner moves between its questions.
|
|
98
|
+
*/
|
|
99
|
+
/**
|
|
100
|
+
* Whether an activity shuffles its own options, and therefore needs a seed of
|
|
101
|
+
* its own. Read structurally: `shuffle` is a per-type content field, not part
|
|
102
|
+
* of the sequence entry contract.
|
|
103
|
+
*/
|
|
104
|
+
function shufflesItsOwnOptions(entry) {
|
|
105
|
+
const item = entry;
|
|
106
|
+
return item?.shuffle === true || item?.shuffleChoices === true;
|
|
107
|
+
}
|
|
108
|
+
function ActivitySequence({ activities, renderers, onActivityComplete, onComplete, onFinished, onSubmit, onInteraction, renderMode = "practice", shuffle, shuffleSeed, defaultIndex, onIndexChange, responses, submittedSlotIds, outcomes, sanitizeHtml, theme, locale, disabled, mediaBudget, recordingBinding, assessments, workletUrl, strings, ai, delivery, scoring }) {
|
|
109
|
+
const sessionIdRef = useRef(null);
|
|
110
|
+
const s = useLkStrings(strings);
|
|
111
|
+
const mediaPlays = mediaBudget?.plays;
|
|
112
|
+
const [captureScope] = useState(() => {
|
|
113
|
+
captureScopeCount += 1;
|
|
114
|
+
return `lk-seq-capture-${captureScopeCount}`;
|
|
115
|
+
});
|
|
116
|
+
const shuffleEntries = shuffle === "entries";
|
|
117
|
+
const needsSeed = shuffleEntries || activities.some((entry) => isItemGroup(entry) ? entry.shuffle === "within-group" || entry.items.some(shufflesItsOwnOptions) : shufflesItsOwnOptions(entry));
|
|
118
|
+
if (needsSeed && shuffleSeed === void 0 && renderMode !== "practice") throw new Error(`ActivitySequence: renderMode "${renderMode}" requires a \`shuffleSeed\` when anything in the sequence shuffles: shuffle="entries", a group with shuffle: "within-group", or an activity's own \`data.shuffle\` (multiple choice) or \`data.shuffleChoices\` (gap select), including an activity inside a group. Pass the attempt id, so the server can rebuild every order the learner saw from that one seed.`);
|
|
119
|
+
const seed = needsSeed ? shuffleSeed ?? (sessionIdRef.current ??= randomSessionId()) : void 0;
|
|
120
|
+
const slots = useMemo(() => flattenSequence(activities, {
|
|
121
|
+
shuffleEntries,
|
|
122
|
+
...seed !== void 0 ? { seed } : {}
|
|
123
|
+
}), [
|
|
124
|
+
activities,
|
|
125
|
+
shuffleEntries,
|
|
126
|
+
seed
|
|
127
|
+
]);
|
|
128
|
+
const [index, setIndex] = useState(() => {
|
|
129
|
+
const requested = Math.trunc(defaultIndex ?? 0);
|
|
130
|
+
if (!Number.isFinite(requested)) return 0;
|
|
131
|
+
return Math.min(Math.max(0, requested), Math.max(0, slots.length - 1));
|
|
132
|
+
});
|
|
133
|
+
const outcomesRef = useRef(null);
|
|
134
|
+
const outcomeSlotsRef = useRef(null);
|
|
135
|
+
const takesRef = useRef(/* @__PURE__ */ new Map());
|
|
136
|
+
const setGenerationRef = useRef(0);
|
|
137
|
+
const indexRef = useRef(index);
|
|
138
|
+
const reportedRef = useRef(false);
|
|
139
|
+
const channelsRef = useRef(/* @__PURE__ */ new Map());
|
|
140
|
+
const callsRef = useRef(/* @__PURE__ */ new Map());
|
|
141
|
+
const hostPendingRef = useRef(/* @__PURE__ */ new Set());
|
|
142
|
+
const [portals, setPortals] = useState(() => /* @__PURE__ */ new Map());
|
|
143
|
+
const portalRefsRef = useRef(/* @__PURE__ */ new Map());
|
|
144
|
+
const modeRef = useRef(renderMode);
|
|
145
|
+
modeRef.current = renderMode;
|
|
146
|
+
const deliveryRef = useRef(resolveDeliveryPolicy(delivery));
|
|
147
|
+
deliveryRef.current = resolveDeliveryPolicy(delivery);
|
|
148
|
+
const scoringKey = JSON.stringify(scoring ?? null);
|
|
149
|
+
const scoringInForce = useMemo(() => scoring === void 0 || scoring === null ? void 0 : resolveItemScoringPolicy(scoring), [scoringKey]);
|
|
150
|
+
const scoringRef = useRef(scoringInForce);
|
|
151
|
+
scoringRef.current = scoringInForce;
|
|
152
|
+
const [, setReported] = useState(false);
|
|
153
|
+
const triesRef = useRef(/* @__PURE__ */ new Map());
|
|
154
|
+
const triedRef = useRef(/* @__PURE__ */ new Map());
|
|
155
|
+
const latestRef = useRef(null);
|
|
156
|
+
const aliveRef = useRef(true);
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
aliveRef.current = true;
|
|
159
|
+
return () => {
|
|
160
|
+
aliveRef.current = false;
|
|
161
|
+
};
|
|
162
|
+
}, []);
|
|
163
|
+
if (outcomesRef.current === null) {
|
|
164
|
+
const submittedAtMount = new Set(submittedSlotIds ?? []);
|
|
165
|
+
outcomesRef.current = slots.map((slot) => submittedAtMount.has(slot.slotId) ? {
|
|
166
|
+
kind: "restored",
|
|
167
|
+
index: slot.index,
|
|
168
|
+
slotId: slot.slotId,
|
|
169
|
+
activityId: slot.activity.id,
|
|
170
|
+
...responses !== void 0 && Object.hasOwn(responses, slot.slotId) ? { response: responses[slot.slotId] } : {}
|
|
171
|
+
} : null);
|
|
172
|
+
outcomeSlotsRef.current = slots;
|
|
173
|
+
}
|
|
174
|
+
const regionRef = useRef(null);
|
|
175
|
+
const mountedRef = useRef(false);
|
|
176
|
+
const setKey = JSON.stringify(slots.map((slot) => [slot.slotId, slot.activity.id]));
|
|
177
|
+
const [prevSetKey, setPrevSetKey] = useState(setKey);
|
|
178
|
+
const seedKey = `${setKey}::${mediaBudget?.resumeKey ?? ""}`;
|
|
179
|
+
const mountSetKeyRef = useRef(setKey);
|
|
180
|
+
const mountSeedKeyRef = useRef(seedKey);
|
|
181
|
+
const seedsApply = mountSetKeyRef.current === setKey;
|
|
182
|
+
const mediaSeedsApply = mountSeedKeyRef.current === seedKey || seedsApply;
|
|
183
|
+
if (mountSeedKeyRef.current !== seedKey) mountSeedKeyRef.current = seedKey;
|
|
184
|
+
const submitted = useMemo(() => new Set(submittedSlotIds ?? []), [submittedSlotIds]);
|
|
185
|
+
if (prevSetKey !== setKey) {
|
|
186
|
+
setPrevSetKey(setKey);
|
|
187
|
+
setIndex(0);
|
|
188
|
+
indexRef.current = 0;
|
|
189
|
+
outcomesRef.current = slots.map(() => null);
|
|
190
|
+
outcomeSlotsRef.current = slots;
|
|
191
|
+
takesRef.current = /* @__PURE__ */ new Map();
|
|
192
|
+
setGenerationRef.current += 1;
|
|
193
|
+
channelsRef.current = /* @__PURE__ */ new Map();
|
|
194
|
+
callsRef.current = /* @__PURE__ */ new Map();
|
|
195
|
+
hostPendingRef.current = /* @__PURE__ */ new Set();
|
|
196
|
+
portalRefsRef.current = /* @__PURE__ */ new Map();
|
|
197
|
+
reportedRef.current = false;
|
|
198
|
+
triesRef.current = /* @__PURE__ */ new Map();
|
|
199
|
+
triedRef.current = /* @__PURE__ */ new Map();
|
|
200
|
+
setReported(false);
|
|
201
|
+
}
|
|
202
|
+
const setGeneration = setGenerationRef.current;
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
if (mountedRef.current) regionRef.current?.focus();
|
|
205
|
+
else mountedRef.current = true;
|
|
206
|
+
}, [index]);
|
|
207
|
+
const reportedIndexRef = useRef(null);
|
|
208
|
+
useEffect(() => {
|
|
209
|
+
if (reportedIndexRef.current === index) return;
|
|
210
|
+
const first = reportedIndexRef.current === null;
|
|
211
|
+
reportedIndexRef.current = index;
|
|
212
|
+
if (first && index === Math.trunc(defaultIndex ?? 0)) return;
|
|
213
|
+
onIndexChange?.(index);
|
|
214
|
+
}, [
|
|
215
|
+
index,
|
|
216
|
+
defaultIndex,
|
|
217
|
+
onIndexChange
|
|
218
|
+
]);
|
|
219
|
+
useEffect(() => {
|
|
220
|
+
if (indexRef.current === index) return;
|
|
221
|
+
indexRef.current = index;
|
|
222
|
+
latestRef.current?.reportIfFinished();
|
|
223
|
+
}, [index]);
|
|
224
|
+
const stimulusMounts = useMemo(() => {
|
|
225
|
+
const mounts = /* @__PURE__ */ new Map();
|
|
226
|
+
for (const slot of slots) {
|
|
227
|
+
if (slot.group === void 0) continue;
|
|
228
|
+
const entryKey = entryKeyOf(slot);
|
|
229
|
+
const existing = mounts.get(entryKey);
|
|
230
|
+
if (existing === void 0) mounts.set(entryKey, {
|
|
231
|
+
entryKey,
|
|
232
|
+
group: slot.group,
|
|
233
|
+
first: slot.index,
|
|
234
|
+
last: slot.index
|
|
235
|
+
});
|
|
236
|
+
else existing.last = slot.index;
|
|
237
|
+
}
|
|
238
|
+
return [...mounts.values()];
|
|
239
|
+
}, [slots]);
|
|
240
|
+
const total = slots.length;
|
|
241
|
+
const current = slots[index];
|
|
242
|
+
const go = (next) => {
|
|
243
|
+
setIndex(next);
|
|
244
|
+
};
|
|
245
|
+
/**
|
|
246
|
+
* Fires the whole-set callbacks — once, together — when every slot is done
|
|
247
|
+
* and no take is still on its way.
|
|
248
|
+
*
|
|
249
|
+
* **A take on its way holds the report.** A practice read-aloud records its
|
|
250
|
+
* `responded` outcome the moment its take is stored, and its grade can land
|
|
251
|
+
* after the last slot was filled; reporting then handed `onFinished` the raw
|
|
252
|
+
* outcome, and the grade a moment later could never reach it. So a slot
|
|
253
|
+
* whose latest take is being stored or judged is not done. The take states
|
|
254
|
+
* come from the component itself, through the slot's channel, and every way a
|
|
255
|
+
* take can end reports an end — a grade, a take with no grade, a failure
|
|
256
|
+
* nobody can retry, an unmount — so a take that settles never holds a set.
|
|
257
|
+
*
|
|
258
|
+
* **So does a failure the learner can still retry, on the question in front
|
|
259
|
+
* of them.** Reporting there reported a set whose answer "Try again" was about
|
|
260
|
+
* to change: the retry stored and graded the take, and `onComplete` then
|
|
261
|
+
* described a different set from the `onFinished` before it. Once the learner
|
|
262
|
+
* moves to another question the failure no longer holds the report: the slot
|
|
263
|
+
* completes as whatever it last stored, or as `unsubmitted` when that is
|
|
264
|
+
* nothing — never as a `recording` of `null`, which is the blank a learner
|
|
265
|
+
* chooses and a grader marks 0. `unsubmitted` is a `practice` outcome only:
|
|
266
|
+
* an exam question with no stored take is unanswered, and the learner has a
|
|
267
|
+
* blank to hand in if that is their answer.
|
|
268
|
+
*/
|
|
269
|
+
const reportIfFinished = () => {
|
|
270
|
+
const held = outcomesRef.current;
|
|
271
|
+
const presented = outcomeSlotsRef.current;
|
|
272
|
+
if (held === null || presented === null || reportedRef.current) return;
|
|
273
|
+
if (hostPendingRef.current.size > 0) return;
|
|
274
|
+
for (const [at, state] of triesRef.current) if (state === "retrying" || at === indexRef.current) return;
|
|
275
|
+
const items = [];
|
|
276
|
+
for (const [at, outcome] of held.entries()) {
|
|
277
|
+
const take = takesRef.current.get(at);
|
|
278
|
+
if (take?.state === "in-flight" || take?.state === "retryable" && at === indexRef.current) return;
|
|
279
|
+
const slot = presented[at];
|
|
280
|
+
if (outcome !== null) items.push(outcome);
|
|
281
|
+
else if (take?.state === "retryable" && renderMode === "practice" && slot !== void 0) items.push({
|
|
282
|
+
kind: "unsubmitted",
|
|
283
|
+
index: at,
|
|
284
|
+
slotId: slot.slotId,
|
|
285
|
+
activityId: slot.activity.id
|
|
286
|
+
});
|
|
287
|
+
else return;
|
|
288
|
+
}
|
|
289
|
+
reportedRef.current = true;
|
|
290
|
+
setReported(true);
|
|
291
|
+
onFinished?.(items);
|
|
292
|
+
const scored = [];
|
|
293
|
+
for (const item of items) {
|
|
294
|
+
if (item.kind !== "scored") return;
|
|
295
|
+
scored.push(item.result);
|
|
296
|
+
}
|
|
297
|
+
onComplete?.(scored);
|
|
298
|
+
};
|
|
299
|
+
/**
|
|
300
|
+
* Records a finished slot and reports the set when it is done — the one place
|
|
301
|
+
* a slot's outcome is decided.
|
|
302
|
+
*
|
|
303
|
+
* **An outcome must come from the set on screen.** A grade can settle after
|
|
304
|
+
* the paper changed — an assessment held across a swap of `activities`, or a
|
|
305
|
+
* consumer's own renderer grading asynchronously — and it arrives through a
|
|
306
|
+
* callback from the render that started it. Accepted, it landed in whatever
|
|
307
|
+
* slot of the NEW paper had its index, and could report that paper fully
|
|
308
|
+
* scored though nobody had answered it. Ids cannot tell the papers apart: a
|
|
309
|
+
* new paper can reuse both the slot id and the activity id, and a loading
|
|
310
|
+
* blip reuses every id there is. `generation` can: it is the set the callback
|
|
311
|
+
* was handed out for, and it is refused unless that set is still current.
|
|
312
|
+
*
|
|
313
|
+
* **A slot that already holds an outcome is FINAL**: the first outcome wins,
|
|
314
|
+
* so a renderer that reports twice keeps what it reported first. There is one
|
|
315
|
+
* exception.
|
|
316
|
+
*
|
|
317
|
+
* **The exception is a `practice` read-aloud, and it is decided by `take`.**
|
|
318
|
+
* That slot records `responded` when a take is stored, because a take that
|
|
319
|
+
* comes back unscorable produces no grade at all and the slot would otherwise
|
|
320
|
+
* stay `null` for ever. Each take has a number (`mintTake`), unique on the page
|
|
321
|
+
* and larger for a later take, carried by everything the component reports
|
|
322
|
+
* about it. The invariant:
|
|
323
|
+
*
|
|
324
|
+
* **The score and the response a slot holds always describe the same take.**
|
|
325
|
+
*
|
|
326
|
+
* - A `responded` for a newer take replaces whatever the slot holds, a grade
|
|
327
|
+
* included: `onSubmit` has already told the host about that take, so a
|
|
328
|
+
* grade kept for the older one would sit beside a response it does not
|
|
329
|
+
* grade.
|
|
330
|
+
* - A `scored` lands only for the slot's latest take, once, and replaces that
|
|
331
|
+
* take's `responded`. A grade for any other take — one the learner has
|
|
332
|
+
* since replaced, even one still in flight when they did — is dropped
|
|
333
|
+
* rather than filed against a take it did not grade.
|
|
334
|
+
* - A `restored` slot is never replaced: it was submitted before this mount,
|
|
335
|
+
* and a resumed attempt must not be re-reported as if it had just been sat.
|
|
336
|
+
*
|
|
337
|
+
* A grade therefore lands once per take, and `onActivityComplete` — the
|
|
338
|
+
* per-item hook, keyed by `slotId` — only ever grades the answer `onSubmit`
|
|
339
|
+
* last reported for that slot. The whole-set callbacks stay once per set
|
|
340
|
+
* whatever is replaced — see `reportedRef`.
|
|
341
|
+
*/
|
|
342
|
+
const record = (outcome, generation, take, tried) => {
|
|
343
|
+
const held = outcomesRef.current;
|
|
344
|
+
if (held === null || generation !== setGenerationRef.current) return;
|
|
345
|
+
const at = outcome.index;
|
|
346
|
+
const holding = held[at];
|
|
347
|
+
let accepted = outcome;
|
|
348
|
+
if (tried !== void 0) {
|
|
349
|
+
const latest = triedRef.current.get(at);
|
|
350
|
+
if (holding?.kind === "restored") return;
|
|
351
|
+
if (holding != null && (reportedRef.current || latest === void 0 || tried <= latest)) return;
|
|
352
|
+
triedRef.current.set(at, tried);
|
|
353
|
+
} else if (take === void 0) {
|
|
354
|
+
if (holding != null) return;
|
|
355
|
+
} else {
|
|
356
|
+
if (holding?.kind === "restored") return;
|
|
357
|
+
const latest = takesRef.current.get(at);
|
|
358
|
+
if (outcome.kind === "responded") {
|
|
359
|
+
if (latest !== void 0 && (take < latest.take || take === latest.take && latest.response !== void 0)) return;
|
|
360
|
+
takesRef.current.set(at, {
|
|
361
|
+
take,
|
|
362
|
+
state: latest?.take === take ? latest.state : "settled",
|
|
363
|
+
response: outcome.response
|
|
364
|
+
});
|
|
365
|
+
} else if (outcome.kind === "scored") {
|
|
366
|
+
if (latest?.take !== take || latest.response === void 0 || holding?.kind === "scored") return;
|
|
367
|
+
accepted = {
|
|
368
|
+
...outcome,
|
|
369
|
+
response: latest.response
|
|
370
|
+
};
|
|
371
|
+
} else return;
|
|
372
|
+
}
|
|
373
|
+
const next = held.slice();
|
|
374
|
+
next[at] = accepted;
|
|
375
|
+
outcomesRef.current = next;
|
|
376
|
+
if (accepted.kind === "scored") onActivityComplete?.(accepted.result, accepted.index, accepted.slotId);
|
|
377
|
+
reportIfFinished();
|
|
378
|
+
};
|
|
379
|
+
/**
|
|
380
|
+
* The learner committed an answer to a slot: the body of the `onSubmit` every
|
|
381
|
+
* question is handed, the SDK's own components and a host's renderer alike.
|
|
382
|
+
*/
|
|
383
|
+
const submitSlot = (generation, at, response) => {
|
|
384
|
+
if (generation !== setGenerationRef.current) return;
|
|
385
|
+
onSubmit?.(response, {
|
|
386
|
+
slotId: at.slotId,
|
|
387
|
+
index: at.index,
|
|
388
|
+
activityId: at.activityId
|
|
389
|
+
});
|
|
390
|
+
if (at.type === "written-response") return;
|
|
391
|
+
if (renderMode !== "practice") {
|
|
392
|
+
record({
|
|
393
|
+
kind: "responded",
|
|
394
|
+
index: at.index,
|
|
395
|
+
slotId: at.slotId,
|
|
396
|
+
activityId: at.activityId,
|
|
397
|
+
response
|
|
398
|
+
}, generation);
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
if (at.type === "read-aloud") record({
|
|
402
|
+
kind: "responded",
|
|
403
|
+
index: at.index,
|
|
404
|
+
slotId: at.slotId,
|
|
405
|
+
activityId: at.activityId,
|
|
406
|
+
response
|
|
407
|
+
}, generation, takeOf(response) ?? mintTake());
|
|
408
|
+
};
|
|
409
|
+
/** A slot produced a grade: the body of the `onComplete` every question is handed. */
|
|
410
|
+
const completeSlot = (generation, at, result) => {
|
|
411
|
+
const takesAnswers = renderMode === "practice" && at.type === "read-aloud";
|
|
412
|
+
const outcome = {
|
|
413
|
+
kind: "scored",
|
|
414
|
+
index: at.index,
|
|
415
|
+
slotId: at.slotId,
|
|
416
|
+
activityId: at.activityId,
|
|
417
|
+
result
|
|
418
|
+
};
|
|
419
|
+
const tried = takesAnswers ? void 0 : takeOf(result);
|
|
420
|
+
if (tried !== void 0 && renderMode === "practice") {
|
|
421
|
+
record(outcome, generation, void 0, tried);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
record(outcome, generation, takesAnswers ? takeOf(result) ?? takesRef.current.get(at.index)?.take : void 0);
|
|
425
|
+
};
|
|
426
|
+
/**
|
|
427
|
+
* A host's question withdrew its answer. The slot holds nothing again — its
|
|
428
|
+
* outcome and, with it, the take any grade would be filed against — so the
|
|
429
|
+
* set is unfinished until it is answered again.
|
|
430
|
+
*
|
|
431
|
+
* A set already reported stays reported: the whole-set callbacks fire once,
|
|
432
|
+
* and a consumer who persists on them must not be handed one attempt twice.
|
|
433
|
+
*/
|
|
434
|
+
const clearSlot = (generation, at) => {
|
|
435
|
+
const held = outcomesRef.current;
|
|
436
|
+
if (!aliveRef.current || held === null || generation !== setGenerationRef.current) return;
|
|
437
|
+
takesRef.current.delete(at.index);
|
|
438
|
+
if (held[at.index] == null) return;
|
|
439
|
+
const next = held.slice();
|
|
440
|
+
next[at.index] = null;
|
|
441
|
+
outcomesRef.current = next;
|
|
442
|
+
};
|
|
443
|
+
/** A host's question is waiting on work of its own, or has stopped waiting. */
|
|
444
|
+
const hostPending = (generation, at, pending) => {
|
|
445
|
+
if (!aliveRef.current || generation !== setGenerationRef.current) return;
|
|
446
|
+
const waiting = hostPendingRef.current;
|
|
447
|
+
if (pending) {
|
|
448
|
+
waiting.add(at.index);
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
if (!waiting.delete(at.index)) return;
|
|
452
|
+
reportIfFinished();
|
|
453
|
+
};
|
|
454
|
+
/** A host's question reported an interaction: its activity and the moment, filled in. */
|
|
455
|
+
const emitFor = (generation, at, type, payload) => {
|
|
456
|
+
if (!aliveRef.current || generation !== setGenerationRef.current) return;
|
|
457
|
+
onInteraction?.({
|
|
458
|
+
type,
|
|
459
|
+
activityId: at.activityId,
|
|
460
|
+
timestamp: Date.now(),
|
|
461
|
+
payload: payload ?? {}
|
|
462
|
+
});
|
|
463
|
+
};
|
|
464
|
+
/**
|
|
465
|
+
* What a slot's channel reports: the state of one of its takes. A take older
|
|
466
|
+
* than the slot's latest has been replaced, and a report from a set that is
|
|
467
|
+
* no longer current, or reaching a pager that is unmounting, describes
|
|
468
|
+
* nothing on screen.
|
|
469
|
+
*/
|
|
470
|
+
const updateTake = (generation, at, take, state) => {
|
|
471
|
+
if (!aliveRef.current || generation !== setGenerationRef.current) return;
|
|
472
|
+
const latest = takesRef.current.get(at);
|
|
473
|
+
if (latest !== void 0 && take < latest.take) return;
|
|
474
|
+
takesRef.current.set(at, latest?.take === take ? {
|
|
475
|
+
...latest,
|
|
476
|
+
state
|
|
477
|
+
} : {
|
|
478
|
+
take,
|
|
479
|
+
state
|
|
480
|
+
});
|
|
481
|
+
reportIfFinished();
|
|
482
|
+
};
|
|
483
|
+
/**
|
|
484
|
+
* What a slot's channel reports: where its question is with its tries. One
|
|
485
|
+
* that stops offering or making a try may leave the set with nothing to wait
|
|
486
|
+
* for.
|
|
487
|
+
*/
|
|
488
|
+
const updateTries = (generation, at, state) => {
|
|
489
|
+
if (!aliveRef.current || generation !== setGenerationRef.current) return;
|
|
490
|
+
const was = triesRef.current.get(at);
|
|
491
|
+
if (state === "none") triesRef.current.delete(at);
|
|
492
|
+
else triesRef.current.set(at, state);
|
|
493
|
+
if (was !== void 0 && was !== state) reportIfFinished();
|
|
494
|
+
};
|
|
495
|
+
latestRef.current = {
|
|
496
|
+
updateTake,
|
|
497
|
+
updateTries,
|
|
498
|
+
reportIfFinished,
|
|
499
|
+
submitSlot,
|
|
500
|
+
completeSlot,
|
|
501
|
+
clearSlot,
|
|
502
|
+
hostPending,
|
|
503
|
+
emit: emitFor
|
|
504
|
+
};
|
|
505
|
+
/**
|
|
506
|
+
* The calls one slot makes, made once per slot per set. They reach the latest
|
|
507
|
+
* render through `latestRef`, so they can keep one identity while the props
|
|
508
|
+
* they report to change — what a host renderer needs to hold them in an
|
|
509
|
+
* effect, and what the pager needs so a host that re-renders on every
|
|
510
|
+
* keystroke does not re-run one.
|
|
511
|
+
*/
|
|
512
|
+
const callsFor = (slot) => {
|
|
513
|
+
const key = `${slot.index}::${slot.slotId}`;
|
|
514
|
+
let calls = callsRef.current.get(key);
|
|
515
|
+
if (calls === void 0) {
|
|
516
|
+
const generation = setGeneration;
|
|
517
|
+
const at = {
|
|
518
|
+
slotId: slot.slotId,
|
|
519
|
+
index: slot.index,
|
|
520
|
+
activityId: slot.activity.id,
|
|
521
|
+
type: slot.activity.type
|
|
522
|
+
};
|
|
523
|
+
calls = {
|
|
524
|
+
slot: {
|
|
525
|
+
slotId: at.slotId,
|
|
526
|
+
index: at.index,
|
|
527
|
+
activityId: at.activityId
|
|
528
|
+
},
|
|
529
|
+
onSubmit: (response) => latestRef.current?.submitSlot(generation, at, response),
|
|
530
|
+
onComplete: (result) => latestRef.current?.completeSlot(generation, at, result),
|
|
531
|
+
clear: () => latestRef.current?.clearSlot(generation, at),
|
|
532
|
+
setPending: (pending) => latestRef.current?.hostPending(generation, at, pending === true),
|
|
533
|
+
emit: (type, payload) => latestRef.current?.emit(generation, at, type, payload)
|
|
534
|
+
};
|
|
535
|
+
callsRef.current.set(key, calls);
|
|
536
|
+
}
|
|
537
|
+
return calls;
|
|
538
|
+
};
|
|
539
|
+
/**
|
|
540
|
+
* Where a host-drawn question portals: a node inside its own pane, so it is
|
|
541
|
+
* hidden with the question it belongs to and painted with it in fullscreen.
|
|
542
|
+
* One ref callback per slot, kept, because a new one each render detaches and
|
|
543
|
+
* re-attaches the node on every commit.
|
|
544
|
+
*/
|
|
545
|
+
const portalRefFor = (key) => {
|
|
546
|
+
let callback = portalRefsRef.current.get(key);
|
|
547
|
+
if (callback === void 0) {
|
|
548
|
+
callback = (node) => {
|
|
549
|
+
setPortals((held) => {
|
|
550
|
+
if (node === null) {
|
|
551
|
+
if (!held.has(key)) return held;
|
|
552
|
+
const next = new Map(held);
|
|
553
|
+
next.delete(key);
|
|
554
|
+
return next;
|
|
555
|
+
}
|
|
556
|
+
if (held.get(key) === node) return held;
|
|
557
|
+
const next = new Map(held);
|
|
558
|
+
next.set(key, node);
|
|
559
|
+
return next;
|
|
560
|
+
});
|
|
561
|
+
};
|
|
562
|
+
portalRefsRef.current.set(key, callback);
|
|
563
|
+
}
|
|
564
|
+
return callback;
|
|
565
|
+
};
|
|
566
|
+
/** The channel a slot's pane hands its content: its capture group and its takes. */
|
|
567
|
+
const channelFor = (slot) => {
|
|
568
|
+
const key = `${slot.index}::${slot.slotId}`;
|
|
569
|
+
let channel = channelsRef.current.get(key);
|
|
570
|
+
if (channel === void 0) {
|
|
571
|
+
const generation = setGeneration;
|
|
572
|
+
const at = slot.index;
|
|
573
|
+
channel = {
|
|
574
|
+
captureGroup: `${captureScope}::${slot.slotId}`,
|
|
575
|
+
get renderMode() {
|
|
576
|
+
return modeRef.current;
|
|
577
|
+
},
|
|
578
|
+
get delivery() {
|
|
579
|
+
return deliveryRef.current;
|
|
580
|
+
},
|
|
581
|
+
get scoring() {
|
|
582
|
+
return scoringRef.current;
|
|
583
|
+
},
|
|
584
|
+
get triesClosed() {
|
|
585
|
+
return reportedRef.current;
|
|
586
|
+
},
|
|
587
|
+
triesState: (state) => {
|
|
588
|
+
latestRef.current?.updateTries(generation, at, state);
|
|
589
|
+
},
|
|
590
|
+
takeState: (take, state) => {
|
|
591
|
+
latestRef.current?.updateTake(generation, at, take, state);
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
channelsRef.current.set(key, channel);
|
|
595
|
+
}
|
|
596
|
+
return channel;
|
|
597
|
+
};
|
|
598
|
+
if (!current) return /* @__PURE__ */ jsx("div", { className: "lk-seq" });
|
|
599
|
+
const budgetedMedia = slots.flatMap((slot) => {
|
|
600
|
+
const found = [];
|
|
601
|
+
const own = slot.activity.media;
|
|
602
|
+
if (own !== void 0 && resolvePlaybackPolicy(own).maxPlays !== null) found.push({
|
|
603
|
+
key: slotMediaKey(slot.slotId),
|
|
604
|
+
url: own.url
|
|
605
|
+
});
|
|
606
|
+
const stimulus = slot.group?.stimulus.media;
|
|
607
|
+
if (stimulus !== void 0 && resolvePlaybackPolicy(stimulus).maxPlays !== null) found.push({
|
|
608
|
+
key: stimulusMediaKey(slot.slotId),
|
|
609
|
+
url: stimulus.url
|
|
610
|
+
});
|
|
611
|
+
return found;
|
|
612
|
+
});
|
|
613
|
+
const budgetEnforced = mediaBudget?.enforced ?? renderMode !== "review";
|
|
614
|
+
if (budgetedMedia.length > 0 && renderMode === "exam" && budgetEnforced) {
|
|
615
|
+
if (mediaBudget?.onPlayConsumed === void 0) throw new Error(`ActivitySequence: renderMode "exam" with media that declares \`maxPlays\` requires \`mediaBudget.onPlayConsumed\`. The SDK persists nothing, so without it the count lives only in this mount: a refresh silently restores the full budget, and a paper that grants unlimited plays while showing "2 plays remaining" is indistinguishable — to the learner and to an appeal — from one that works. Budgeted media: ${[...new Set(budgetedMedia.map((m) => m.key))].join(", ")}.`);
|
|
616
|
+
if ((responses !== void 0 && Object.keys(responses).length > 0 || submittedSlotIds !== void 0 && submittedSlotIds.length > 0) && mediaBudget.plays === void 0) throw new Error("ActivitySequence: this is a resumed attempt (`responses`/`submittedSlotIds` were supplied) but `mediaBudget.plays` is absent. A resume that omits the ledger hands the learner a fresh budget at exactly the moment it matters. Pass `restoreMediaPlayLedger(plan, stored).entries`, or `{}` to state explicitly that nothing was spent.");
|
|
617
|
+
}
|
|
618
|
+
const keysByUrl = /* @__PURE__ */ new Map();
|
|
619
|
+
for (const { key, url } of budgetedMedia) {
|
|
620
|
+
const keys = keysByUrl.get(url) ?? /* @__PURE__ */ new Set();
|
|
621
|
+
keys.add(key);
|
|
622
|
+
keysByUrl.set(url, keys);
|
|
623
|
+
}
|
|
624
|
+
for (const [url, keys] of keysByUrl) if (keys.size > 1) throw new Error(`ActivitySequence: media ${JSON.stringify(url)} is budgeted under ${keys.size} separate keys (${[...keys].join(", ")}), so one recording grants ${keys.size} × maxPlays. Put the questions that share a recording in an item group — a group's stimulus is one recording with one budget.`);
|
|
625
|
+
/** The per-media binding handed to a component or to the stimulus panel. */
|
|
626
|
+
const bindingFor = (key, slot) => {
|
|
627
|
+
if (mediaBudget === void 0) return;
|
|
628
|
+
const entry = mediaSeedsApply && mediaPlays !== void 0 && Object.hasOwn(mediaPlays, key) ? mediaPlays[key] : void 0;
|
|
629
|
+
return {
|
|
630
|
+
key,
|
|
631
|
+
slotId: slot.slotId,
|
|
632
|
+
index: slot.index,
|
|
633
|
+
activityId: slot.activity.id,
|
|
634
|
+
...entry !== void 0 ? { entry } : {},
|
|
635
|
+
...mediaBudget.enforced !== void 0 ? { enforced: mediaBudget.enforced } : {},
|
|
636
|
+
...mediaBudget.onPlayConsumed !== void 0 ? { onPlayConsumed: mediaBudget.onPlayConsumed } : {},
|
|
637
|
+
...mediaBudget.onPlayRefunded !== void 0 ? { onPlayRefunded: mediaBudget.onPlayRefunded } : {},
|
|
638
|
+
...mediaBudget.onPosition !== void 0 ? { onPosition: mediaBudget.onPosition } : {},
|
|
639
|
+
...mediaBudget.strings !== void 0 ? { strings: mediaBudget.strings } : {}
|
|
640
|
+
};
|
|
641
|
+
};
|
|
642
|
+
/**
|
|
643
|
+
* The per-slot recording binding handed to a read-aloud, modelled on
|
|
644
|
+
* `bindingFor`: the sequence-level methods are called with the slot they
|
|
645
|
+
* belong to, and `assess` / `playbackUrl` are copied only when the consumer
|
|
646
|
+
* supplied them — a binding carrying `assess: undefined` is a binding the
|
|
647
|
+
* component would try to call.
|
|
648
|
+
*
|
|
649
|
+
* It watches nothing. The pager used to watch the promises it handed out to
|
|
650
|
+
* decide when a set was finished, and a promise knows nothing of the take
|
|
651
|
+
* that asked for it: a late failure from a paper no longer on screen landed
|
|
652
|
+
* in a new paper that reused the slot's ids, as a blank. What a take came to
|
|
653
|
+
* is reported by the component, numbered by take, through the slot's channel
|
|
654
|
+
* — see `record` and `reportIfFinished`.
|
|
655
|
+
*
|
|
656
|
+
* A consumer's own renderer is handed no binding.
|
|
657
|
+
*/
|
|
658
|
+
const recordingBindingFor = (slot) => {
|
|
659
|
+
if (recordingBinding === void 0) return;
|
|
660
|
+
const at = {
|
|
661
|
+
slotId: slot.slotId,
|
|
662
|
+
index: slot.index,
|
|
663
|
+
activityId: slot.activity.id
|
|
664
|
+
};
|
|
665
|
+
const { assess, playbackUrl } = recordingBinding;
|
|
666
|
+
return {
|
|
667
|
+
upload: (take) => recordingBinding.upload(take, at),
|
|
668
|
+
...assess !== void 0 ? { assess: (ref) => assess.call(recordingBinding, ref, at) } : {},
|
|
669
|
+
...playbackUrl !== void 0 ? { playbackUrl: (ref) => playbackUrl.call(recordingBinding, ref, at) } : {}
|
|
670
|
+
};
|
|
671
|
+
};
|
|
672
|
+
const forwarded = {
|
|
673
|
+
...onInteraction ? { onInteraction } : {},
|
|
674
|
+
renderMode,
|
|
675
|
+
...sanitizeHtml ? { sanitizeHtml } : {},
|
|
676
|
+
...strings !== void 0 ? { strings } : {},
|
|
677
|
+
...ai !== void 0 && renderMode !== "exam" ? { ai } : {},
|
|
678
|
+
...delivery !== void 0 && delivery !== null ? { delivery } : {},
|
|
679
|
+
...scoring !== void 0 && scoring !== null ? { scoring } : {},
|
|
680
|
+
...theme ? { theme } : {},
|
|
681
|
+
...locale ? { locale } : {},
|
|
682
|
+
...disabled ? { disabled } : {}
|
|
683
|
+
};
|
|
684
|
+
const renderSlot = (slot) => {
|
|
685
|
+
const activity = slot.activity;
|
|
686
|
+
const { slotId, index: slotIndex } = slot;
|
|
687
|
+
const activityId = activity.id;
|
|
688
|
+
const restored = seedsApply && responses !== void 0 && Object.hasOwn(responses, slotId) ? responses[slotId] : void 0;
|
|
689
|
+
const slotOutcome = outcomes !== void 0 && Object.hasOwn(outcomes, slotId) ? outcomes[slotId] : void 0;
|
|
690
|
+
const slotBinding = bindingFor(slotMediaKey(slotId), slot);
|
|
691
|
+
const calls = callsFor(slot);
|
|
692
|
+
const childProps = {
|
|
693
|
+
...restored !== void 0 ? { defaultValue: restored } : {},
|
|
694
|
+
...seedsApply && submitted.has(slotId) ? { defaultSubmitted: true } : {},
|
|
695
|
+
...slotOutcome !== void 0 ? { outcome: slotOutcome } : {},
|
|
696
|
+
onComplete: calls.onComplete,
|
|
697
|
+
onSubmit: calls.onSubmit,
|
|
698
|
+
...forwarded,
|
|
699
|
+
...slotBinding !== void 0 ? { mediaBudget: slotBinding } : {}
|
|
700
|
+
};
|
|
701
|
+
const CustomRenderer = renderers?.[activity.type];
|
|
702
|
+
if (CustomRenderer) {
|
|
703
|
+
const portalKey = slotKeyOf(setKey, slot);
|
|
704
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(CustomRenderer, {
|
|
705
|
+
data: activity,
|
|
706
|
+
...childProps,
|
|
707
|
+
question: {
|
|
708
|
+
slot: calls.slot,
|
|
709
|
+
active: slotIndex === index,
|
|
710
|
+
portalContainer: portals.get(portalKey) ?? null,
|
|
711
|
+
clear: calls.clear,
|
|
712
|
+
setPending: calls.setPending,
|
|
713
|
+
emit: calls.emit
|
|
714
|
+
}
|
|
715
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
716
|
+
className: "lk-seq-portal",
|
|
717
|
+
ref: portalRefFor(portalKey)
|
|
718
|
+
})] });
|
|
719
|
+
}
|
|
720
|
+
if (activity.type === "multiple-choice") return /* @__PURE__ */ jsx(MultipleChoice, {
|
|
721
|
+
data: activity,
|
|
722
|
+
...shuffleSeed !== void 0 ? { shuffleSeed } : {},
|
|
723
|
+
...childProps
|
|
724
|
+
});
|
|
725
|
+
if (activity.type === "fill-in-the-blanks") return /* @__PURE__ */ jsx(FillInTheBlanks, {
|
|
726
|
+
data: activity,
|
|
727
|
+
...childProps
|
|
728
|
+
});
|
|
729
|
+
if (activity.type === "gap-select") return /* @__PURE__ */ jsx(GapSelect, {
|
|
730
|
+
data: activity,
|
|
731
|
+
...shuffleSeed !== void 0 ? { shuffleSeed } : {},
|
|
732
|
+
...childProps
|
|
733
|
+
});
|
|
734
|
+
if (activity.type === "dictation") return /* @__PURE__ */ jsx(Dictation, {
|
|
735
|
+
data: activity,
|
|
736
|
+
...childProps
|
|
737
|
+
});
|
|
738
|
+
if (activity.type === "read-aloud") {
|
|
739
|
+
const slotRecordingBinding = recordingBindingFor(slot);
|
|
740
|
+
const slotAssessment = assessments !== void 0 && Object.hasOwn(assessments, slotId) ? assessments[slotId] : void 0;
|
|
741
|
+
return /* @__PURE__ */ jsx(ReadAloud, {
|
|
742
|
+
data: activity,
|
|
743
|
+
...childProps,
|
|
744
|
+
...slotRecordingBinding !== void 0 ? { recordingBinding: slotRecordingBinding } : {},
|
|
745
|
+
...slotAssessment !== void 0 ? { assessment: slotAssessment } : {},
|
|
746
|
+
...workletUrl !== void 0 ? { workletUrl } : {}
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
if (activity.type === "written-response") {
|
|
750
|
+
const { onComplete: _neverGrades, ...writtenResponseProps } = childProps;
|
|
751
|
+
return /* @__PURE__ */ jsx(WrittenResponse, {
|
|
752
|
+
data: activity,
|
|
753
|
+
...writtenResponseProps,
|
|
754
|
+
onSubmitted: (submission) => record({
|
|
755
|
+
kind: "submitted",
|
|
756
|
+
index: slotIndex,
|
|
757
|
+
slotId,
|
|
758
|
+
activityId,
|
|
759
|
+
submission
|
|
760
|
+
}, setGeneration)
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
return /* @__PURE__ */ jsx("div", {
|
|
764
|
+
className: "lk-seq-unsupported",
|
|
765
|
+
role: "note",
|
|
766
|
+
children: s.unsupportedActivity
|
|
767
|
+
});
|
|
768
|
+
};
|
|
769
|
+
const currentEntryKey = entryKeyOf(current);
|
|
770
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
771
|
+
className: "lk-seq",
|
|
772
|
+
lang: locale,
|
|
773
|
+
children: [
|
|
774
|
+
/* @__PURE__ */ jsx("div", {
|
|
775
|
+
className: "lk-seq-progress",
|
|
776
|
+
"aria-live": "polite",
|
|
777
|
+
role: "status",
|
|
778
|
+
children: s.questionProgress(index + 1, total)
|
|
779
|
+
}),
|
|
780
|
+
stimulusMounts.map(({ entryKey, group, first, last }) => {
|
|
781
|
+
const firstSlot = slots[first];
|
|
782
|
+
const stimulusBinding = firstSlot === void 0 ? void 0 : bindingFor(stimulusMediaKey(firstSlot.slotId), firstSlot);
|
|
783
|
+
return /* @__PURE__ */ jsx(SequencePane, {
|
|
784
|
+
className: "lk-seq-stimulus",
|
|
785
|
+
hidden: entryKey !== currentEntryKey,
|
|
786
|
+
children: /* @__PURE__ */ jsx(StimulusPanel, {
|
|
787
|
+
stimulus: group.stimulus,
|
|
788
|
+
range: {
|
|
789
|
+
first: first + 1,
|
|
790
|
+
last: last + 1
|
|
791
|
+
},
|
|
792
|
+
renderMode,
|
|
793
|
+
...sanitizeHtml ? { sanitizeHtml } : {},
|
|
794
|
+
...locale ? { locale } : {},
|
|
795
|
+
...onInteraction ? { onInteraction } : {},
|
|
796
|
+
...disabled ? { disabled } : {},
|
|
797
|
+
...strings !== void 0 ? { strings } : {},
|
|
798
|
+
...stimulusBinding !== void 0 ? { mediaBudget: stimulusBinding } : {}
|
|
799
|
+
})
|
|
800
|
+
}, `${setKey}::group-${entryKey}`);
|
|
801
|
+
}),
|
|
802
|
+
/* @__PURE__ */ jsx("section", {
|
|
803
|
+
className: "lk-seq-question",
|
|
804
|
+
ref: regionRef,
|
|
805
|
+
tabIndex: -1,
|
|
806
|
+
"aria-label": s.questionProgress(index + 1, total),
|
|
807
|
+
children: slots.map((slot) => {
|
|
808
|
+
const slotKey = slotKeyOf(setKey, slot);
|
|
809
|
+
return /* @__PURE__ */ jsx(SequencePane, {
|
|
810
|
+
className: "lk-seq-slot",
|
|
811
|
+
hidden: slot.index !== index,
|
|
812
|
+
channel: channelFor(slot),
|
|
813
|
+
children: renderSlot(slot)
|
|
814
|
+
}, slotKey);
|
|
815
|
+
})
|
|
816
|
+
}),
|
|
817
|
+
/* @__PURE__ */ jsxs("div", {
|
|
818
|
+
className: "lk-seq-nav",
|
|
819
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
820
|
+
type: "button",
|
|
821
|
+
className: "lk-seq-prev",
|
|
822
|
+
onClick: () => go(index - 1),
|
|
823
|
+
disabled: index === 0,
|
|
824
|
+
children: s.previous
|
|
825
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
826
|
+
type: "button",
|
|
827
|
+
className: "lk-seq-next",
|
|
828
|
+
onClick: () => go(index + 1),
|
|
829
|
+
disabled: index === total - 1,
|
|
830
|
+
children: s.next
|
|
831
|
+
})]
|
|
832
|
+
})
|
|
833
|
+
]
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
//#endregion
|
|
837
|
+
export { ActivitySequence as t };
|
|
838
|
+
|
|
839
|
+
//# sourceMappingURL=ActivitySequence-CkX87pfB.js.map
|