catchup-library-web 1.0.0 → 1.0.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/dist/index.d.mts +239 -2
- package/dist/index.d.ts +239 -2
- package/dist/index.js +4686 -2
- package/dist/index.mjs +4621 -1
- package/package.json +10 -2
- package/src/components/activities/DropdownActivityContent.tsx +73 -0
- package/src/components/activities/FillInTheBlanksActivityContent.tsx +102 -0
- package/src/components/activities/GroupingActivityContent.tsx +62 -0
- package/src/components/activities/MCMAActivityContent.tsx +65 -0
- package/src/components/activities/MCSAActivityContent.tsx +58 -0
- package/src/components/activities/MatchingActivityContent.tsx +57 -0
- package/src/components/activities/OpenEndedActivityContent.tsx +92 -0
- package/src/components/activities/OrderingActivityContent.tsx +59 -0
- package/src/components/activities/TrueFalseActivityContent.tsx +98 -0
- package/src/components/activities/body-content/ActivityBodyContent.tsx +108 -0
- package/src/components/activities/body-content/ShowBodyMediaByContentType.tsx +404 -0
- package/src/components/activities/empty-content/ActivityEmptyContent.tsx +15 -0
- package/src/components/activities/material-content/DropdownActivityMaterialContent.tsx +227 -0
- package/src/components/activities/material-content/FillInTheBlanksActivityMaterialContent.tsx +270 -0
- package/src/components/activities/material-content/GroupingActivityMaterialContent.tsx +359 -0
- package/src/components/activities/material-content/MCMAActivityMaterialContent.tsx +166 -0
- package/src/components/activities/material-content/MCSAActivityMaterialContent.tsx +165 -0
- package/src/components/activities/material-content/MatchingActivityMaterialContent.tsx +332 -0
- package/src/components/activities/material-content/OpenEndedActivityMaterialContent.tsx +818 -0
- package/src/components/activities/material-content/OrderingActivityMaterialContent.tsx +216 -0
- package/src/components/activities/material-content/ShowMaterialMediaByContentType.tsx +172 -0
- package/src/components/activities/material-content/TrueFalseActivityMaterialContent.tsx +217 -0
- package/src/components/activities/solution-content/ActivitySolutionContent.tsx +86 -0
- package/src/components/dividers/BlueVerticalDividerLine.tsx +13 -0
- package/src/components/dividers/DividerLine.tsx +5 -0
- package/src/components/dividers/VerticalDividerLine.tsx +5 -0
- package/src/components/dnds/DraggableDroppableItem.tsx +62 -0
- package/src/components/dnds/DraggableItem.tsx +41 -0
- package/src/components/dnds/DroppableItem.tsx +38 -0
- package/src/components/dropdowns/MediaDropdown.tsx +51 -0
- package/src/components/groups/InputGroup.tsx +330 -0
- package/src/hooks/useScreenSize.ts +40 -0
- package/src/index.ts +24 -0
- package/src/language/i18n.ts +10 -0
- package/src/properties/ActivityProperties.ts +204 -0
- package/src/properties/ButtonProperties.ts +1 -1
- package/src/properties/CommonProperties.ts +1 -1
- package/src/properties/DividerLineProperties.ts +3 -0
- package/src/properties/DnDProperties.ts +28 -0
- package/src/properties/DropdownProperties.ts +5 -0
- package/src/properties/EnumProperties.ts +11 -0
- package/src/properties/GroupProperties.ts +19 -0
- package/src/utilization/AppUtilization.ts +56 -0
- package/src/utilization/CatchtivityUtilization.ts +1566 -0
- package/src/utilization/StorageUtilization.ts +35 -0
- package/tsconfig.json +2 -1
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { useDrop } from "react-dnd";
|
|
3
|
+
import ShowMaterialMediaByContentType from "./ShowMaterialMediaByContentType";
|
|
4
|
+
import { InlineMath } from "react-katex";
|
|
5
|
+
import { constructInputWithSpecialExpressionList } from "../../../utilization/CatchtivityUtilization";
|
|
6
|
+
import DividerLine from "../../dividers/DividerLine";
|
|
7
|
+
import { IGroupingActivityMaterialProps } from "../../../properties/ActivityProperties";
|
|
8
|
+
import DraggableItem from "../../dnds/DraggableItem";
|
|
9
|
+
import DroppableItem from "../../dnds/DroppableItem";
|
|
10
|
+
import useScreenSize from "../../../hooks/useScreenSize";
|
|
11
|
+
|
|
12
|
+
const GroupingActivityMaterialContent = ({
|
|
13
|
+
uniqueValue,
|
|
14
|
+
answer,
|
|
15
|
+
materialMap,
|
|
16
|
+
contentMap,
|
|
17
|
+
checkCanAnswerQuestion,
|
|
18
|
+
onChange,
|
|
19
|
+
isPreview,
|
|
20
|
+
showCorrectAnswer,
|
|
21
|
+
}: IGroupingActivityMaterialProps) => {
|
|
22
|
+
const [selectedValue, setSelectedValue] = useState(null);
|
|
23
|
+
const [selectedTargetKey, setSelectedTargetKey] = useState(null);
|
|
24
|
+
const [isShuffled, setIsShuffled] = useState(false);
|
|
25
|
+
const [shuffledMaterialList, setShuffledMaterialList] = useState([]);
|
|
26
|
+
const { screenSize, containerSize } = useScreenSize();
|
|
27
|
+
const [{ isOver, canDrop }, drop] = useDrop({
|
|
28
|
+
accept: "GROUPING",
|
|
29
|
+
drop: () => {},
|
|
30
|
+
collect: (monitor: any) => ({
|
|
31
|
+
isOver: monitor.isOver(),
|
|
32
|
+
canDrop: monitor.canDrop(),
|
|
33
|
+
}),
|
|
34
|
+
});
|
|
35
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
36
|
+
const itemsRef = useRef<HTMLDivElement>(null);
|
|
37
|
+
const [maxWidth, setMaxWidth] = useState<number>(0);
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (!ref) return;
|
|
41
|
+
if (!ref.current) return;
|
|
42
|
+
if (!screenSize) return;
|
|
43
|
+
setMaxWidth(ref.current.offsetWidth - 12);
|
|
44
|
+
}, [ref, screenSize]);
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (!itemsRef) return;
|
|
48
|
+
if (!itemsRef.current) return;
|
|
49
|
+
if (!containerSize) return;
|
|
50
|
+
itemsRef.current.style.width = `${containerSize.width - 200}px`;
|
|
51
|
+
}, [itemsRef, containerSize]);
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
const shuffleArray = (array: any) => {
|
|
55
|
+
if (!isShuffled) {
|
|
56
|
+
const copyArray = JSON.parse(JSON.stringify(array));
|
|
57
|
+
for (let i = copyArray.length - 1; i > 0; i--) {
|
|
58
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
59
|
+
[copyArray[i], copyArray[j]] = [copyArray[j], copyArray[i]];
|
|
60
|
+
}
|
|
61
|
+
setIsShuffled(true);
|
|
62
|
+
return copyArray;
|
|
63
|
+
}
|
|
64
|
+
return array;
|
|
65
|
+
};
|
|
66
|
+
const materialList: any = [];
|
|
67
|
+
Object.keys(materialMap).forEach((materialKey) => {
|
|
68
|
+
for (const materialValue of materialMap[materialKey]) {
|
|
69
|
+
materialList.push(materialValue);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
setShuffledMaterialList(shuffleArray(materialList));
|
|
73
|
+
}, []);
|
|
74
|
+
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
if (!showCorrectAnswer) return;
|
|
77
|
+
answer.data.find(
|
|
78
|
+
(answerData: any) => answerData.type === "GROUPING"
|
|
79
|
+
).answerMap = materialMap;
|
|
80
|
+
}, [showCorrectAnswer]);
|
|
81
|
+
|
|
82
|
+
const retrieveAnswerMap = () => {
|
|
83
|
+
const foundIndex = answer.data.findIndex(
|
|
84
|
+
(answerData: any) => answerData.type === "GROUPING"
|
|
85
|
+
);
|
|
86
|
+
const answerMap = answer.data[foundIndex].answerMap;
|
|
87
|
+
return answerMap;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const retrieveFilteredMaterialList = (answerMap: any) => {
|
|
91
|
+
const selectedValueList: any = [];
|
|
92
|
+
Object.keys(answerMap).forEach((key) => {
|
|
93
|
+
answerMap[key].forEach((value: string) => {
|
|
94
|
+
selectedValueList.push(value);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
return shuffledMaterialList.filter(
|
|
98
|
+
(material) =>
|
|
99
|
+
selectedValueList.findIndex((value: string) => material === value) ===
|
|
100
|
+
-1
|
|
101
|
+
);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const checkAnswerState = (correctAnswerList: any, learnerAnswer: string) => {
|
|
105
|
+
if (!isPreview) return "HIDDEN";
|
|
106
|
+
if (!learnerAnswer) return "EMPTY";
|
|
107
|
+
if (!correctAnswerList) return "EMPTY";
|
|
108
|
+
const foundIndex = correctAnswerList.findIndex(
|
|
109
|
+
(correctAnswer: string) => correctAnswer === learnerAnswer
|
|
110
|
+
);
|
|
111
|
+
if (foundIndex !== -1) {
|
|
112
|
+
return "CORRECT";
|
|
113
|
+
}
|
|
114
|
+
return "INCORRECT";
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const handleGroupingActivityItemOnChange = (
|
|
118
|
+
selectedTargetKey: string,
|
|
119
|
+
selectedValue: string
|
|
120
|
+
) => {
|
|
121
|
+
if (checkCanAnswerQuestion()) {
|
|
122
|
+
onChange(answer, selectedTargetKey, selectedValue, null);
|
|
123
|
+
setSelectedValue(null);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const answerMap = retrieveAnswerMap();
|
|
128
|
+
const filteredMaterialList = retrieveFilteredMaterialList(answerMap);
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<>
|
|
132
|
+
<div
|
|
133
|
+
ref={itemsRef}
|
|
134
|
+
className="flex-1 flex flex-row gap-x-4 gap-y-4 overflow-auto py-2"
|
|
135
|
+
>
|
|
136
|
+
{filteredMaterialList.map((materialValue, index) => {
|
|
137
|
+
return (
|
|
138
|
+
<DraggableItem
|
|
139
|
+
key={index}
|
|
140
|
+
item={{ index: materialValue }}
|
|
141
|
+
type={"GROUPING"}
|
|
142
|
+
component={
|
|
143
|
+
<div
|
|
144
|
+
className={`${
|
|
145
|
+
selectedValue === materialValue
|
|
146
|
+
? "border-catchup-blue"
|
|
147
|
+
: "border-catchup-lighter-gray"
|
|
148
|
+
} h-catchup-activity-covering-box-item flex flex-col items-center justify-center border-2 rounded-catchup-xlarge cursor-pointer transition-all duration-300`}
|
|
149
|
+
onMouseDown={() => {
|
|
150
|
+
if (checkCanAnswerQuestion()) {
|
|
151
|
+
setSelectedValue(materialValue);
|
|
152
|
+
}
|
|
153
|
+
}}
|
|
154
|
+
onMouseUp={() => {
|
|
155
|
+
if (checkCanAnswerQuestion()) {
|
|
156
|
+
setSelectedValue(null);
|
|
157
|
+
}
|
|
158
|
+
}}
|
|
159
|
+
>
|
|
160
|
+
{contentMap.type === "TEXT" ? (
|
|
161
|
+
<div
|
|
162
|
+
className={`flex flex-col items-center justify-center m-4 min-h-[64px] min-w-[200px]`}
|
|
163
|
+
>
|
|
164
|
+
<p className="text-xl text-center whitespace-pre-wrap">
|
|
165
|
+
{constructInputWithSpecialExpressionList(
|
|
166
|
+
materialValue
|
|
167
|
+
).map((inputPart, index) => (
|
|
168
|
+
<span
|
|
169
|
+
key={index}
|
|
170
|
+
className={`${
|
|
171
|
+
inputPart.isBold ? "font-bold" : ""
|
|
172
|
+
} ${inputPart.isUnderline ? "underline" : ""}`}
|
|
173
|
+
>
|
|
174
|
+
{inputPart.isEquation ? (
|
|
175
|
+
<span className="text-2xl">
|
|
176
|
+
<InlineMath math={inputPart.value} />
|
|
177
|
+
</span>
|
|
178
|
+
) : (
|
|
179
|
+
inputPart.value
|
|
180
|
+
)}
|
|
181
|
+
</span>
|
|
182
|
+
))}
|
|
183
|
+
</p>
|
|
184
|
+
</div>
|
|
185
|
+
) : (
|
|
186
|
+
<ShowMaterialMediaByContentType
|
|
187
|
+
key={`${uniqueValue}-${index}`}
|
|
188
|
+
contentType={contentMap.type}
|
|
189
|
+
src={materialValue}
|
|
190
|
+
canFullScreen={true}
|
|
191
|
+
/>
|
|
192
|
+
)}
|
|
193
|
+
</div>
|
|
194
|
+
}
|
|
195
|
+
moveCardHandler={() => {
|
|
196
|
+
if (!selectedTargetKey) return;
|
|
197
|
+
if (!selectedValue) return;
|
|
198
|
+
handleGroupingActivityItemOnChange(
|
|
199
|
+
selectedTargetKey,
|
|
200
|
+
selectedValue
|
|
201
|
+
);
|
|
202
|
+
}}
|
|
203
|
+
/>
|
|
204
|
+
);
|
|
205
|
+
})}
|
|
206
|
+
</div>
|
|
207
|
+
{filteredMaterialList.length > 0 ? <DividerLine /> : null}
|
|
208
|
+
{Object.keys(answerMap).map((answerMapKey, index) => (
|
|
209
|
+
<div key={index} className="flex flex-row w-full">
|
|
210
|
+
<div className="w-1/3">
|
|
211
|
+
<div
|
|
212
|
+
className={`border-catchup-blue h-catchup-activity-outer-box-item flex flex-col items-center justify-center border-2 rounded-catchup-xlarge transition-all duration-300 my-3`}
|
|
213
|
+
>
|
|
214
|
+
<div
|
|
215
|
+
className={`flex flex-col items-center justify-center transition-all duration-300 m-4`}
|
|
216
|
+
>
|
|
217
|
+
<p className="text-xl p-5 whitespace-pre-wrap">
|
|
218
|
+
{constructInputWithSpecialExpressionList(answerMapKey).map(
|
|
219
|
+
(inputPart, index) => (
|
|
220
|
+
<span
|
|
221
|
+
key={index}
|
|
222
|
+
className={`${inputPart.isBold ? "font-bold" : ""} ${
|
|
223
|
+
inputPart.isUnderline ? "underline" : ""
|
|
224
|
+
}`}
|
|
225
|
+
>
|
|
226
|
+
{inputPart.isEquation ? (
|
|
227
|
+
<span className="text-2xl">
|
|
228
|
+
<InlineMath math={inputPart.value} />
|
|
229
|
+
</span>
|
|
230
|
+
) : (
|
|
231
|
+
inputPart.value
|
|
232
|
+
)}
|
|
233
|
+
</span>
|
|
234
|
+
)
|
|
235
|
+
)}
|
|
236
|
+
</p>
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
240
|
+
<div className="mx-4 w-[2px] bg-catchup-lighter-gray"></div>
|
|
241
|
+
<div className="flex-1" ref={ref}>
|
|
242
|
+
<div className="h-full py-3">
|
|
243
|
+
<div
|
|
244
|
+
className={`${
|
|
245
|
+
canDrop
|
|
246
|
+
? selectedTargetKey === answerMapKey
|
|
247
|
+
? "bg-catchup-light-blue"
|
|
248
|
+
: "bg-catchup-light-blue opacity-40"
|
|
249
|
+
: ""
|
|
250
|
+
} flex-1 border-catchup-blue rounded-catchup-xlarge border-2 h-full p-1`}
|
|
251
|
+
>
|
|
252
|
+
<DroppableItem
|
|
253
|
+
key={index}
|
|
254
|
+
item={{ index: answerMapKey }}
|
|
255
|
+
type={"GROUPING"}
|
|
256
|
+
target={selectedTargetKey}
|
|
257
|
+
setTarget={setSelectedTargetKey}
|
|
258
|
+
dropRef={drop}
|
|
259
|
+
component={
|
|
260
|
+
<div
|
|
261
|
+
className="h-full flex-1 flex flex-row items-center overflow-x-auto"
|
|
262
|
+
style={{
|
|
263
|
+
maxWidth: maxWidth,
|
|
264
|
+
}}
|
|
265
|
+
>
|
|
266
|
+
{answerMap[answerMapKey].map(
|
|
267
|
+
(answerMapValue: string, answerMapIndex: number) => {
|
|
268
|
+
const learnerAnswerState = checkAnswerState(
|
|
269
|
+
materialMap[answerMapKey],
|
|
270
|
+
answerMapValue
|
|
271
|
+
);
|
|
272
|
+
return (
|
|
273
|
+
<div className="p-1">
|
|
274
|
+
<div className="h-catchup-activity-box-item">
|
|
275
|
+
<div
|
|
276
|
+
className={`${
|
|
277
|
+
learnerAnswerState === "EMPTY"
|
|
278
|
+
? "border-catchup-lighter-gray"
|
|
279
|
+
: learnerAnswerState === "CORRECT"
|
|
280
|
+
? "border-catchup-green"
|
|
281
|
+
: learnerAnswerState === "INCORRECT"
|
|
282
|
+
? "border-catchup-red"
|
|
283
|
+
: "border-catchup-blue"
|
|
284
|
+
} border-2 rounded-catchup-xlarge h-full flex flex-col items-center justify-center transition-all duration-300 cursor-pointer`}
|
|
285
|
+
onClick={(e) => {
|
|
286
|
+
e.preventDefault();
|
|
287
|
+
if (checkCanAnswerQuestion()) {
|
|
288
|
+
onChange(
|
|
289
|
+
answer,
|
|
290
|
+
answerMapKey,
|
|
291
|
+
null,
|
|
292
|
+
answerMapIndex
|
|
293
|
+
);
|
|
294
|
+
setSelectedValue(null);
|
|
295
|
+
}
|
|
296
|
+
}}
|
|
297
|
+
>
|
|
298
|
+
{contentMap.type === "TEXT" ? (
|
|
299
|
+
<div
|
|
300
|
+
className={`flex flex-col items-center justify-center transition-all duration-300 min-h-[64px] min-w-[200px]`}
|
|
301
|
+
>
|
|
302
|
+
<div className="m-2">
|
|
303
|
+
<p className="text-xl text-center whitespace-pre-wrap">
|
|
304
|
+
{constructInputWithSpecialExpressionList(
|
|
305
|
+
answerMapValue
|
|
306
|
+
).map((inputPart, index) => (
|
|
307
|
+
<span
|
|
308
|
+
key={index}
|
|
309
|
+
className={`${
|
|
310
|
+
inputPart.isBold
|
|
311
|
+
? "font-bold"
|
|
312
|
+
: ""
|
|
313
|
+
} ${
|
|
314
|
+
inputPart.isUnderline
|
|
315
|
+
? "underline"
|
|
316
|
+
: ""
|
|
317
|
+
}`}
|
|
318
|
+
>
|
|
319
|
+
{inputPart.isEquation ? (
|
|
320
|
+
<span className="text-2xl">
|
|
321
|
+
<InlineMath
|
|
322
|
+
math={inputPart.value}
|
|
323
|
+
/>
|
|
324
|
+
</span>
|
|
325
|
+
) : (
|
|
326
|
+
inputPart.value
|
|
327
|
+
)}
|
|
328
|
+
</span>
|
|
329
|
+
))}
|
|
330
|
+
</p>
|
|
331
|
+
</div>
|
|
332
|
+
</div>
|
|
333
|
+
) : (
|
|
334
|
+
<ShowMaterialMediaByContentType
|
|
335
|
+
key={`${uniqueValue}-${index}`}
|
|
336
|
+
contentType={contentMap.type}
|
|
337
|
+
src={answerMapValue}
|
|
338
|
+
canFullScreen={false}
|
|
339
|
+
/>
|
|
340
|
+
)}
|
|
341
|
+
</div>
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
)}
|
|
347
|
+
</div>
|
|
348
|
+
}
|
|
349
|
+
/>
|
|
350
|
+
</div>
|
|
351
|
+
</div>
|
|
352
|
+
</div>
|
|
353
|
+
</div>
|
|
354
|
+
))}
|
|
355
|
+
</>
|
|
356
|
+
);
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
export default GroupingActivityMaterialContent;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { InlineMath } from "react-katex";
|
|
2
|
+
import { constructInputWithSpecialExpressionList } from "../../../utilization/CatchtivityUtilization";
|
|
3
|
+
import ShowMaterialMediaByContentType from "./ShowMaterialMediaByContentType";
|
|
4
|
+
import i18n from "../../../language/i18n";
|
|
5
|
+
import BaseImage from "../../images/BaseImage";
|
|
6
|
+
import { IMCMAActivityMaterialProps } from "../../../properties/ActivityProperties";
|
|
7
|
+
import DividerLine from "../../dividers/DividerLine";
|
|
8
|
+
|
|
9
|
+
const MCMAActivityMaterialContent = ({
|
|
10
|
+
uniqueValue,
|
|
11
|
+
answer,
|
|
12
|
+
materialMap,
|
|
13
|
+
contentMap,
|
|
14
|
+
checkCanAnswerQuestion,
|
|
15
|
+
onChange,
|
|
16
|
+
isPreview,
|
|
17
|
+
}: IMCMAActivityMaterialProps) => {
|
|
18
|
+
const retrieveAnswerMap = () => {
|
|
19
|
+
const foundIndex = answer.data.findIndex(
|
|
20
|
+
(answerData: any) => answerData.type === "MCMA"
|
|
21
|
+
);
|
|
22
|
+
return answer.data[foundIndex].answerMap;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const retrieveCorrectAnswerList = () => {
|
|
26
|
+
return JSON.parse(Object.keys(materialMap)[0]);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const checkAnswerState = (
|
|
30
|
+
correctAnswerList: any,
|
|
31
|
+
currentAnswer: string,
|
|
32
|
+
learnerAnswer: string
|
|
33
|
+
) => {
|
|
34
|
+
if (!isPreview) return "HIDDEN";
|
|
35
|
+
if (currentAnswer !== learnerAnswer) return "EMPTY";
|
|
36
|
+
const foundIndex = correctAnswerList.findIndex(
|
|
37
|
+
(correctAnswer: string) => correctAnswer === learnerAnswer
|
|
38
|
+
);
|
|
39
|
+
if (foundIndex !== -1) {
|
|
40
|
+
return "CORRECT";
|
|
41
|
+
}
|
|
42
|
+
return "INCORRECT";
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const answerMap = retrieveAnswerMap();
|
|
46
|
+
const correctAnswerList = retrieveCorrectAnswerList();
|
|
47
|
+
|
|
48
|
+
return Object.keys(materialMap).map((materialKey, index) => {
|
|
49
|
+
return (
|
|
50
|
+
<div key={index} className="flex flex-row items-center my-1">
|
|
51
|
+
<div className="flex-1 flex flex-col justify-center border-catchup-lighter-gray rounded-catchup-xlarge px-5">
|
|
52
|
+
<div className="hidden md:block">
|
|
53
|
+
<span className="font-semibold text-xl opacity-60">
|
|
54
|
+
{i18n.t("please_select_mcma_text")}
|
|
55
|
+
</span>
|
|
56
|
+
</div>
|
|
57
|
+
<div className="hidden md:contents">
|
|
58
|
+
<DividerLine />
|
|
59
|
+
</div>
|
|
60
|
+
{checkCanAnswerQuestion() ? (
|
|
61
|
+
<div className="flex flex-row w-full flex-wrap ">
|
|
62
|
+
{materialMap[materialKey].map(
|
|
63
|
+
(materialSubKey: string, index: number) => {
|
|
64
|
+
const foundAnswer = answerMap[materialKey].find(
|
|
65
|
+
(learnerAnswer: string) => learnerAnswer === materialSubKey
|
|
66
|
+
);
|
|
67
|
+
const learnerAnswerState = checkAnswerState(
|
|
68
|
+
correctAnswerList,
|
|
69
|
+
materialSubKey,
|
|
70
|
+
foundAnswer
|
|
71
|
+
);
|
|
72
|
+
const foundIndex = correctAnswerList.findIndex(
|
|
73
|
+
(correctAnswer: string) => correctAnswer === materialSubKey
|
|
74
|
+
);
|
|
75
|
+
return (
|
|
76
|
+
<div
|
|
77
|
+
key={index}
|
|
78
|
+
className={`w-full flex flex-row items-center justify-center cursor-pointer my-2 gap-x-2 ${
|
|
79
|
+
(learnerAnswerState === "EMPTY" && foundIndex !== -1) ||
|
|
80
|
+
learnerAnswerState === "CORRECT"
|
|
81
|
+
? "border-2 border-catchup-green rounded-catchup-xlarge"
|
|
82
|
+
: learnerAnswerState === "INCORRECT"
|
|
83
|
+
? "border-2 border-catchup-red rounded-catchup-xlarge"
|
|
84
|
+
: ""
|
|
85
|
+
}`}
|
|
86
|
+
onClick={() => {
|
|
87
|
+
onChange(answer, materialKey, materialSubKey);
|
|
88
|
+
}}
|
|
89
|
+
>
|
|
90
|
+
<div className="">
|
|
91
|
+
<BaseImage
|
|
92
|
+
src={
|
|
93
|
+
answerMap[materialKey].includes(materialSubKey)
|
|
94
|
+
? "/icons/checkbox.png"
|
|
95
|
+
: "/icons/empty-checkbox.png"
|
|
96
|
+
}
|
|
97
|
+
alt="checkbox-empty-checkbox"
|
|
98
|
+
size="small"
|
|
99
|
+
onClick={() => {}}
|
|
100
|
+
/>
|
|
101
|
+
</div>
|
|
102
|
+
<div className="flex-1">
|
|
103
|
+
{contentMap.type === "TEXT" ? (
|
|
104
|
+
<p className="text-xl whitespace-pre-wrap">
|
|
105
|
+
{constructInputWithSpecialExpressionList(
|
|
106
|
+
materialSubKey
|
|
107
|
+
).map((inputPart, index) => (
|
|
108
|
+
<span
|
|
109
|
+
key={index}
|
|
110
|
+
className={`${
|
|
111
|
+
inputPart.isBold ? "font-bold" : ""
|
|
112
|
+
} ${inputPart.isUnderline ? "underline" : ""}`}
|
|
113
|
+
>
|
|
114
|
+
{inputPart.isEquation ? (
|
|
115
|
+
<span className="text-2xl">
|
|
116
|
+
<InlineMath math={inputPart.value} />
|
|
117
|
+
</span>
|
|
118
|
+
) : (
|
|
119
|
+
inputPart.value
|
|
120
|
+
)}
|
|
121
|
+
</span>
|
|
122
|
+
))}
|
|
123
|
+
</p>
|
|
124
|
+
) : (
|
|
125
|
+
<ShowMaterialMediaByContentType
|
|
126
|
+
key={`${uniqueValue}-${index}`}
|
|
127
|
+
contentType={contentMap.type}
|
|
128
|
+
src={materialSubKey}
|
|
129
|
+
canFullScreen={true}
|
|
130
|
+
/>
|
|
131
|
+
)}
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
)}
|
|
137
|
+
</div>
|
|
138
|
+
) : (
|
|
139
|
+
<p key={materialKey} className="text-xl">
|
|
140
|
+
{constructInputWithSpecialExpressionList(
|
|
141
|
+
answerMap[materialKey]
|
|
142
|
+
).map((inputPart, index) => (
|
|
143
|
+
<span
|
|
144
|
+
key={index}
|
|
145
|
+
className={`${inputPart.isBold ? "font-bold" : ""} ${
|
|
146
|
+
inputPart.isUnderline ? "underline" : ""
|
|
147
|
+
}`}
|
|
148
|
+
>
|
|
149
|
+
{inputPart.isEquation ? (
|
|
150
|
+
<span className="text-2xl">
|
|
151
|
+
<InlineMath math={inputPart.value} />
|
|
152
|
+
</span>
|
|
153
|
+
) : (
|
|
154
|
+
inputPart.value
|
|
155
|
+
)}
|
|
156
|
+
</span>
|
|
157
|
+
))}
|
|
158
|
+
</p>
|
|
159
|
+
)}
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
);
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export default MCMAActivityMaterialContent;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { InlineMath } from "react-katex";
|
|
2
|
+
import ShowMaterialMediaByContentType from "./ShowMaterialMediaByContentType";
|
|
3
|
+
import i18n from "../../../language/i18n";
|
|
4
|
+
import { constructInputWithSpecialExpressionList } from "../../../utilization/CatchtivityUtilization";
|
|
5
|
+
import BaseImage from "../../images/BaseImage";
|
|
6
|
+
import { IMCSAActivityMaterialProps } from "../../../properties/ActivityProperties";
|
|
7
|
+
import DividerLine from "../../dividers/DividerLine";
|
|
8
|
+
|
|
9
|
+
const MCSAActivityMaterialContent = ({
|
|
10
|
+
uniqueValue,
|
|
11
|
+
answer,
|
|
12
|
+
materialMap,
|
|
13
|
+
contentMap,
|
|
14
|
+
checkCanAnswerQuestion,
|
|
15
|
+
onChange,
|
|
16
|
+
isPreview,
|
|
17
|
+
}: IMCSAActivityMaterialProps) => {
|
|
18
|
+
const retrieveAnswerMap = () => {
|
|
19
|
+
const foundIndex = answer.data.findIndex(
|
|
20
|
+
(answerData: any) => answerData.type === "MCSA"
|
|
21
|
+
);
|
|
22
|
+
return answer.data[foundIndex].answerMap;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const retrieveCorrectAnswer = () => {
|
|
26
|
+
return Object.keys(materialMap)[0];
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const checkAnswerState = (
|
|
30
|
+
correctAnswer: string,
|
|
31
|
+
currentAnswer: string,
|
|
32
|
+
learnerAnswer: string
|
|
33
|
+
) => {
|
|
34
|
+
if (!isPreview) return "HIDDEN";
|
|
35
|
+
if (currentAnswer !== learnerAnswer) return "EMPTY";
|
|
36
|
+
if (correctAnswer === learnerAnswer) {
|
|
37
|
+
return "CORRECT";
|
|
38
|
+
}
|
|
39
|
+
return "INCORRECT";
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const answerMap = retrieveAnswerMap();
|
|
43
|
+
const correctAnswer = retrieveCorrectAnswer();
|
|
44
|
+
|
|
45
|
+
return Object.keys(materialMap).map((materialKey, index) => {
|
|
46
|
+
return (
|
|
47
|
+
<div className="flex flex-row items-center my-1" key={index}>
|
|
48
|
+
<div className="flex-1 flex flex-col justify-center border-catchup-lighter-gray rounded-catchup-xlarge px-5">
|
|
49
|
+
<div className="hidden md:block">
|
|
50
|
+
<span className="font-semibold text-xl opacity-60">
|
|
51
|
+
{i18n.t("please_select_mcsa_text")}
|
|
52
|
+
</span>
|
|
53
|
+
</div>
|
|
54
|
+
<div className="hidden md:contents">
|
|
55
|
+
<DividerLine />
|
|
56
|
+
</div>
|
|
57
|
+
{checkCanAnswerQuestion() ? (
|
|
58
|
+
<div
|
|
59
|
+
className={`flex flex-row w-full ${
|
|
60
|
+
Object.keys(materialMap[materialKey]).length <= 4
|
|
61
|
+
? "justify-center"
|
|
62
|
+
: ""
|
|
63
|
+
} flex-wrap`}
|
|
64
|
+
>
|
|
65
|
+
{materialMap[materialKey].map(
|
|
66
|
+
(materialSubKey: string, index: number) => {
|
|
67
|
+
const learnerAnswerState = checkAnswerState(
|
|
68
|
+
correctAnswer,
|
|
69
|
+
materialSubKey,
|
|
70
|
+
answerMap[materialKey]
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<div
|
|
75
|
+
key={index}
|
|
76
|
+
className={`w-full flex flex-row items-center justify-center cursor-pointer my-2 gap-x-2 ${
|
|
77
|
+
(learnerAnswerState === "EMPTY" &&
|
|
78
|
+
materialSubKey === correctAnswer) ||
|
|
79
|
+
learnerAnswerState === "CORRECT"
|
|
80
|
+
? "border-2 border-catchup-green rounded-catchup-xlarge"
|
|
81
|
+
: learnerAnswerState === "INCORRECT"
|
|
82
|
+
? "border-2 border-catchup-red rounded-catchup-xlarge"
|
|
83
|
+
: ""
|
|
84
|
+
}`}
|
|
85
|
+
onClick={() => {
|
|
86
|
+
onChange(answer, materialKey, materialSubKey);
|
|
87
|
+
}}
|
|
88
|
+
>
|
|
89
|
+
<div className="">
|
|
90
|
+
<BaseImage
|
|
91
|
+
src={
|
|
92
|
+
answerMap[materialKey] === materialSubKey
|
|
93
|
+
? "/icons/item-element.png"
|
|
94
|
+
: "/icons/not-selected-item-element.png"
|
|
95
|
+
}
|
|
96
|
+
alt="circle"
|
|
97
|
+
size="small"
|
|
98
|
+
onClick={() => {}}
|
|
99
|
+
/>
|
|
100
|
+
</div>
|
|
101
|
+
<div className="flex-1">
|
|
102
|
+
{contentMap.type === "TEXT" ? (
|
|
103
|
+
<p className="text-xl whitespace-pre-wrap">
|
|
104
|
+
{constructInputWithSpecialExpressionList(
|
|
105
|
+
materialSubKey
|
|
106
|
+
).map((inputPart, index) => (
|
|
107
|
+
<span
|
|
108
|
+
key={index}
|
|
109
|
+
className={`${
|
|
110
|
+
inputPart.isBold ? "font-bold" : ""
|
|
111
|
+
} ${inputPart.isUnderline ? "underline" : ""}`}
|
|
112
|
+
>
|
|
113
|
+
{inputPart.isEquation ? (
|
|
114
|
+
<span className="text-2xl">
|
|
115
|
+
<InlineMath math={inputPart.value} />
|
|
116
|
+
</span>
|
|
117
|
+
) : (
|
|
118
|
+
inputPart.value
|
|
119
|
+
)}
|
|
120
|
+
</span>
|
|
121
|
+
))}
|
|
122
|
+
</p>
|
|
123
|
+
) : (
|
|
124
|
+
<ShowMaterialMediaByContentType
|
|
125
|
+
key={`${uniqueValue}-${index}`}
|
|
126
|
+
contentType={contentMap.type}
|
|
127
|
+
src={materialSubKey}
|
|
128
|
+
canFullScreen={true}
|
|
129
|
+
/>
|
|
130
|
+
)}
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
)}
|
|
136
|
+
</div>
|
|
137
|
+
) : (
|
|
138
|
+
<p className="text-xl whitespace-pre-wrap">
|
|
139
|
+
{constructInputWithSpecialExpressionList(
|
|
140
|
+
answerMap[materialKey]
|
|
141
|
+
).map((inputPart, index) => (
|
|
142
|
+
<span
|
|
143
|
+
key={index}
|
|
144
|
+
className={`${inputPart.isBold ? "font-bold" : ""} ${
|
|
145
|
+
inputPart.isUnderline ? "underline" : ""
|
|
146
|
+
}`}
|
|
147
|
+
>
|
|
148
|
+
{inputPart.isEquation ? (
|
|
149
|
+
<span className="text-2xl">
|
|
150
|
+
<InlineMath math={inputPart.value} />
|
|
151
|
+
</span>
|
|
152
|
+
) : (
|
|
153
|
+
inputPart.value
|
|
154
|
+
)}
|
|
155
|
+
</span>
|
|
156
|
+
))}
|
|
157
|
+
</p>
|
|
158
|
+
)}
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
);
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
export default MCSAActivityMaterialContent;
|