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,227 @@
1
+ import { InlineMath } from "react-katex";
2
+ import InputGroup from "../../groups/InputGroup";
3
+ import { constructInputWithSpecialExpressionList } from "../../../utilization/CatchtivityUtilization";
4
+ import i18n from "../../../language/i18n";
5
+ import { useEffect } from "react";
6
+ import { useState } from "react";
7
+ import BaseImage from "../../images/BaseImage";
8
+ import { shuffleArray } from "../../../utilization/AppUtilization";
9
+ import DividerLine from "../../dividers/DividerLine";
10
+ import MediaDropdown from "../../dropdowns/MediaDropdown";
11
+ import { IDropdownActivityMaterialProps } from "../../../properties/ActivityProperties";
12
+ import ShowMaterialMediaByContentType from "./ShowMaterialMediaByContentType";
13
+
14
+ const DropdownActivityMaterialContent = ({
15
+ uniqueValue,
16
+ answer,
17
+ materialMap,
18
+ contentMap,
19
+ checkCanAnswerQuestion,
20
+ onChange,
21
+ isPreview,
22
+ showCorrectAnswer,
23
+ }: IDropdownActivityMaterialProps) => {
24
+ const [updated, setUpdated] = useState(false);
25
+
26
+ useEffect(() => {
27
+ if (!showCorrectAnswer) return;
28
+ const foundAnswer = answer.data.find(
29
+ (answerData: any) => answerData.type === "DROPDOWN"
30
+ );
31
+ if (foundAnswer.answerMap.length === 0) return;
32
+ foundAnswer.answerMap = Object.keys(materialMap).map(
33
+ (materialMapKey, index) => Object.keys(materialMap[materialMapKey])[0]
34
+ );
35
+ onChange(answer, 0, Object.keys(materialMap[0])[0]);
36
+ setUpdated(true);
37
+ }, [showCorrectAnswer]);
38
+
39
+ useEffect(() => {
40
+ if (!updated) return;
41
+ setUpdated(false);
42
+ }, [updated]);
43
+
44
+ const retrieveAnswerMap = () => {
45
+ const foundIndex = answer.data.findIndex(
46
+ (answerData: any) => answerData.type === "DROPDOWN"
47
+ );
48
+ return answer.data[foundIndex].answerMap;
49
+ };
50
+
51
+ const checkAnswerState = (correctAnswer: string, learnerAnswer: string) => {
52
+ if (!isPreview) return "HIDDEN";
53
+ if (correctAnswer === learnerAnswer) {
54
+ return "CORRECT";
55
+ }
56
+ return "INCORRECT";
57
+ };
58
+
59
+ const answerMap = retrieveAnswerMap();
60
+
61
+ return (
62
+ <div className="flex flex-row flex-wrap items-center">
63
+ <div className="hidden md:block">
64
+ <span className="font-semibold text-xl opacity-60">
65
+ {i18n.t("please_select_dropdown_text")}
66
+ </span>
67
+ </div>
68
+ <div className="hidden md:contents">
69
+ <DividerLine />
70
+ </div>
71
+ <div className="w-full flex flex-row flex-wrap">
72
+ {Object.keys(answerMap).map((materialKey: string, index: number) => {
73
+ const answerKey = Object.keys(materialMap[materialKey])[0];
74
+ const learnerAnswerState = checkAnswerState(
75
+ answerKey,
76
+ answerMap[materialKey]
77
+ );
78
+ return (
79
+ <div key={index} className="w-full md:w-1/2">
80
+ <div className="mx-2">
81
+ <div className="w-full flex flex-row my-2 gap-x-2">
82
+ <div className="my-auto">
83
+ <p className="text-xl">{parseFloat(materialKey) + 1}.</p>
84
+ </div>
85
+ <div className="w-full relative">
86
+ <div className="flex-1">
87
+ {checkCanAnswerQuestion() ? (
88
+ contentMap.type === "TEXT" ? (
89
+ <div className="flex-1">
90
+ <InputGroup
91
+ type="select"
92
+ value={answerMap[materialKey]}
93
+ optionList={shuffleArray(
94
+ materialMap[materialKey][answerKey]
95
+ ).map((materialOption: any) => ({
96
+ text: (
97
+ <span className="text-xl whitespace-pre-wrap">
98
+ {constructInputWithSpecialExpressionList(
99
+ materialOption
100
+ ).map((inputPart, index) => (
101
+ <span
102
+ key={index}
103
+ className={`${
104
+ inputPart.isBold ? "font-bold" : ""
105
+ } ${
106
+ inputPart.isUnderline
107
+ ? "underline"
108
+ : ""
109
+ }`}
110
+ >
111
+ {inputPart.isEquation ? (
112
+ <span className="text-2xl">
113
+ <InlineMath
114
+ math={inputPart.value}
115
+ />
116
+ </span>
117
+ ) : (
118
+ inputPart.value
119
+ )}
120
+ </span>
121
+ ))}
122
+ </span>
123
+ ),
124
+ value: materialOption,
125
+ }))}
126
+ onChange={(e) => {
127
+ onChange(answer, materialKey, e.target.value);
128
+ }}
129
+ />
130
+ </div>
131
+ ) : (
132
+ <MediaDropdown
133
+ id={materialKey}
134
+ answer={
135
+ answerMap[materialKey] === "DEFAULT_OPTION" ? (
136
+ <div className="w-catchup-activity-box-item border h-catchup-activity-box-item rounded-catchup-xlarge border-dashed border-catchup-blue">
137
+ <div className="h-full flex flex-col items-center justify-center px-4 py-2">
138
+ <span className="italic">
139
+ {i18n.t("please_select")}
140
+ </span>
141
+ </div>
142
+ </div>
143
+ ) : (
144
+ <ShowMaterialMediaByContentType
145
+ key={`${uniqueValue}-${index}`}
146
+ contentType={contentMap.type}
147
+ src={answerMap[materialKey]}
148
+ canFullScreen={false}
149
+ />
150
+ )
151
+ }
152
+ optionList={materialMap[materialKey][answerKey].map(
153
+ (materialOption: any, index: number) => ({
154
+ id: index,
155
+ media: (
156
+ <div key={index}>
157
+ <ShowMaterialMediaByContentType
158
+ key={`${uniqueValue}-${index}`}
159
+ contentType={contentMap.type}
160
+ src={materialOption}
161
+ canFullScreen={false}
162
+ />
163
+ </div>
164
+ ),
165
+ onClick: (e: any) => {
166
+ onChange(
167
+ answer,
168
+ materialKey,
169
+ e.target.currentSrc
170
+ );
171
+ },
172
+ })
173
+ )}
174
+ />
175
+ )
176
+ ) : (
177
+ <p className="text-xl whitespace-pre-wrap">
178
+ {constructInputWithSpecialExpressionList(
179
+ answerMap[materialKey]
180
+ ).map((inputPart, index) => (
181
+ <span
182
+ key={index}
183
+ className={`${
184
+ inputPart.isBold ? "font-bold" : ""
185
+ } ${inputPart.isUnderline ? "underline" : ""}`}
186
+ >
187
+ {inputPart.isEquation ? (
188
+ <span className="text-2xl">
189
+ <InlineMath math={inputPart.value} />
190
+ </span>
191
+ ) : (
192
+ inputPart.value
193
+ )}
194
+ </span>
195
+ ))}
196
+ </p>
197
+ )}
198
+ </div>
199
+ {learnerAnswerState === "CORRECT" ? (
200
+ <div className="absolute top-[0px] right-4 bg-catchup-white">
201
+ <BaseImage
202
+ src="/icons/checkbox.png"
203
+ alt="chekbbox"
204
+ size="small"
205
+ />
206
+ </div>
207
+ ) : learnerAnswerState === "INCORRECT" ? (
208
+ <div className="absolute top-[0px] right-4 bg-catchup-white">
209
+ <BaseImage
210
+ src="/icons/cross-red.png"
211
+ alt="cross-red"
212
+ size="small"
213
+ />
214
+ </div>
215
+ ) : null}
216
+ </div>
217
+ </div>
218
+ </div>
219
+ </div>
220
+ );
221
+ })}
222
+ </div>
223
+ </div>
224
+ );
225
+ };
226
+
227
+ export default DropdownActivityMaterialContent;
@@ -0,0 +1,270 @@
1
+ import { InlineMath } from "react-katex";
2
+ import InputGroup from "../../groups/InputGroup";
3
+ import { constructInputWithSpecialExpressionList } from "../../../utilization/CatchtivityUtilization";
4
+ import i18n from "../../../language/i18n";
5
+ import { useState } from "react";
6
+ import { useEffect } from "react";
7
+ import { useDrop } from "react-dnd";
8
+ import BaseImage from "../../images/BaseImage";
9
+ import { shuffleArray } from "../../../utilization/AppUtilization";
10
+ import DraggableItem from "../../dnds/DraggableItem";
11
+ import DroppableItem from "../../dnds/DroppableItem";
12
+ import ShowMaterialMediaByContentType from "./ShowMaterialMediaByContentType";
13
+ import DividerLine from "../../dividers/DividerLine";
14
+ import { IFillInTheBlanksActivityMaterialProps } from "../../../properties/ActivityProperties";
15
+
16
+ const FillInTheBlanksActivityMaterialContent = ({
17
+ uniqueValue,
18
+ answer,
19
+ optionList,
20
+ materialMap,
21
+ contentMap,
22
+ checkCanAnswerQuestion,
23
+ onChange,
24
+ isPreview,
25
+ showCorrectAnswer,
26
+ }: IFillInTheBlanksActivityMaterialProps) => {
27
+ const [shuffleOptionList, setShuffleOptionList] = useState([]);
28
+ const [selectedOption, setSelectedOption] = useState(null);
29
+ const [pasteOptionIndex, setPasteOptionIndex] = useState(null);
30
+ const [{ isOver, canDrop }, drop] = useDrop({
31
+ accept: "FILL_IN_THE_BLANKS",
32
+ drop: () => {},
33
+ collect: (monitor) => ({
34
+ isOver: monitor.isOver(),
35
+ canDrop: monitor.canDrop(),
36
+ }),
37
+ });
38
+
39
+ useEffect(() => {
40
+ setShuffleOptionList(shuffleArray(optionList));
41
+ }, []);
42
+
43
+ useEffect(() => {
44
+ if (!showCorrectAnswer) return;
45
+ const foundAnswer = answer.data.find(
46
+ (answerData: any) => answerData.type === "FILL_IN_THE_BLANKS"
47
+ );
48
+ if (foundAnswer.answerMap.length === 0) return;
49
+ foundAnswer.answerMap = Object.keys(materialMap).map(
50
+ (materialMapKey, index) => JSON.parse(materialMap[materialMapKey])[0]
51
+ );
52
+
53
+ onChange(answer, 0, JSON.parse(materialMap[0])[0]);
54
+ }, [showCorrectAnswer]);
55
+
56
+ const retrieveAnswerMap = () => {
57
+ const foundIndex = answer.data.findIndex(
58
+ (answerData: any) => answerData.type === "FILL_IN_THE_BLANKS"
59
+ );
60
+ return answer.data[foundIndex].answerMap;
61
+ };
62
+
63
+ const checkAnswerState = (correctAnswerList: any, learnerAnswer: string) => {
64
+ if (!isPreview) return "HIDDEN";
65
+ const foundIndex = correctAnswerList.findIndex(
66
+ (correctAnswer: string) => correctAnswer === learnerAnswer
67
+ );
68
+ if (foundIndex !== -1) {
69
+ return "CORRECT";
70
+ }
71
+ return "INCORRECT";
72
+ };
73
+
74
+ const checkAnswerProvided = (answerMap: any, option: string) => {
75
+ return (
76
+ Object.keys(answerMap).findIndex((key) => answerMap[key] === option) !==
77
+ -1
78
+ );
79
+ };
80
+
81
+ const answerMap = retrieveAnswerMap();
82
+
83
+ return (
84
+ <div className="flex flex-row flex-wrap items-center" onMouseUp={() => {}}>
85
+ <div className="hidden md:block">
86
+ <span className="font-semibold text-xl opacity-60">
87
+ {i18n.t("please_select_fill_in_the_blanks_text")}
88
+ </span>
89
+ </div>
90
+ <div className="hidden md:contents">
91
+ <DividerLine />
92
+ </div>
93
+
94
+ <div className="w-full flex flex-row flex-wrap gap-x-2 gap-y-2 my-2">
95
+ {shuffleOptionList.map((option, index) =>
96
+ checkAnswerProvided(answerMap, option) ? (
97
+ <div className="opacity-30">
98
+ <ShowMaterialMediaByContentType
99
+ key={`${uniqueValue}-${index}`}
100
+ contentType={contentMap.type}
101
+ src={option}
102
+ canFullScreen={true}
103
+ />
104
+ </div>
105
+ ) : (
106
+ <DraggableItem
107
+ key={index}
108
+ item={{ index: option }}
109
+ type={"FILL_IN_THE_BLANKS"}
110
+ component={
111
+ contentMap.type === "TEXT" ? (
112
+ <div
113
+ className="border-catchup-blue border-2 px-2 py-1 rounded-catchup-xlarge cursor-pointer"
114
+ onMouseDown={() => {
115
+ setSelectedOption(option);
116
+ setPasteOptionIndex(null);
117
+ }}
118
+ >
119
+ <p className="italic whitespace-pre-wrap">{option}</p>
120
+ </div>
121
+ ) : (
122
+ <div
123
+ className="border-catchup-blue border-2 px-2 py-1 rounded-catchup-xlarge cursor-pointer"
124
+ onMouseDown={() => {
125
+ setSelectedOption(option);
126
+ setPasteOptionIndex(null);
127
+ }}
128
+ >
129
+ <ShowMaterialMediaByContentType
130
+ key={`${uniqueValue}-${index}`}
131
+ contentType={contentMap.type}
132
+ src={option}
133
+ canFullScreen={true}
134
+ />
135
+ </div>
136
+ )
137
+ }
138
+ moveCardHandler={() => {
139
+ onChange(answer, pasteOptionIndex, selectedOption);
140
+ }}
141
+ />
142
+ )
143
+ )}
144
+ </div>
145
+ <div className="flex flex-row flex-wrap">
146
+ {Object.keys(answerMap).map((materialKey, index) => {
147
+ const learnerAnswerState = checkAnswerState(
148
+ JSON.parse(materialMap[materialKey]),
149
+ answerMap[materialKey]
150
+ );
151
+ return (
152
+ <div key={index} className="w-full md:w-1/2">
153
+ <div className="mx-2">
154
+ <DroppableItem
155
+ key={index}
156
+ item={{ index }}
157
+ type={"FILL_IN_THE_BLANKS"}
158
+ target={pasteOptionIndex}
159
+ setTarget={setPasteOptionIndex}
160
+ dropRef={drop}
161
+ component={
162
+ <div className="w-full flex flex-row my-2 gap-x-2">
163
+ <div className="my-auto">
164
+ <p className="text-xl">
165
+ {parseFloat(materialKey) + 1}.
166
+ </p>
167
+ </div>
168
+ <div className="flex-1">
169
+ {checkCanAnswerQuestion() ? (
170
+ contentMap.type === "TEXT" ? (
171
+ <div className="relative">
172
+ <div className="flex-1">
173
+ <InputGroup
174
+ type="textarea"
175
+ value={answerMap[materialKey]}
176
+ useMinHeight={false}
177
+ onChange={(e) => {
178
+ onChange(
179
+ answer,
180
+ materialKey,
181
+ e.target.value
182
+ );
183
+ }}
184
+ />
185
+ </div>
186
+ {learnerAnswerState === "CORRECT" ? (
187
+ <div className="absolute -top-[10px] right-4 bg-catchup-white">
188
+ <BaseImage
189
+ src="/icons/checkbox.png"
190
+ alt="checkbox"
191
+ size="small"
192
+ />
193
+ </div>
194
+ ) : learnerAnswerState === "INCORRECT" ? (
195
+ <div className="absolute -top-[10px] right-4 bg-catchup-white">
196
+ <BaseImage
197
+ src="/icons/cross-red.png"
198
+ alt="cross-red"
199
+ size="small"
200
+ />
201
+ </div>
202
+ ) : null}
203
+ </div>
204
+ ) : answerMap[materialKey] === "" ? (
205
+ <div
206
+ className={`w-catchup-activity-box-item border h-catchup-activity-box-item rounded-catchup-xlarge border-dashed ${
207
+ learnerAnswerState === "CORRECT"
208
+ ? "border-catchup-green"
209
+ : learnerAnswerState === "INCORRECT"
210
+ ? "border-catchup-red"
211
+ : "border-catchup-blue"
212
+ }`}
213
+ >
214
+ <div className="h-full flex flex-col items-center justify-center px-4 py-2">
215
+ <span className="italic">
216
+ {i18n.t("please_drop_here")}
217
+ </span>
218
+ </div>
219
+ </div>
220
+ ) : (
221
+ <div
222
+ className="flex-1 cursor-pointer"
223
+ onClick={() => {
224
+ onChange(answer, materialKey, "");
225
+ }}
226
+ >
227
+ <ShowMaterialMediaByContentType
228
+ key={`${uniqueValue}-${index}`}
229
+ contentType={contentMap.type}
230
+ src={answerMap[materialKey]}
231
+ canFullScreen={true}
232
+ />
233
+ </div>
234
+ )
235
+ ) : (
236
+ <p key={materialKey} className="text-xl">
237
+ {constructInputWithSpecialExpressionList(
238
+ answerMap[materialKey]
239
+ ).map((inputPart, index) => (
240
+ <span
241
+ key={index}
242
+ className={`${
243
+ inputPart.isBold ? "font-bold" : ""
244
+ } ${inputPart.isUnderline ? "underline" : ""}`}
245
+ >
246
+ {inputPart.isEquation ? (
247
+ <span className="text-2xl">
248
+ <InlineMath math={inputPart.value} />
249
+ </span>
250
+ ) : (
251
+ inputPart.value
252
+ )}
253
+ </span>
254
+ ))}
255
+ </p>
256
+ )}
257
+ </div>
258
+ </div>
259
+ }
260
+ />
261
+ </div>
262
+ </div>
263
+ );
264
+ })}
265
+ </div>
266
+ </div>
267
+ );
268
+ };
269
+
270
+ export default FillInTheBlanksActivityMaterialContent;