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