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,216 @@
|
|
|
1
|
+
import { useEffect, 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 { IOrderingActivityMaterialProps } from "../../../properties/ActivityProperties";
|
|
8
|
+
import DraggableDroppableItem from "../../dnds/DraggableDroppableItem";
|
|
9
|
+
|
|
10
|
+
const OrderingActivityMaterialContent = ({
|
|
11
|
+
uniqueValue,
|
|
12
|
+
answer,
|
|
13
|
+
materialMap,
|
|
14
|
+
contentMap,
|
|
15
|
+
checkCanAnswerQuestion,
|
|
16
|
+
onChange,
|
|
17
|
+
isPreview,
|
|
18
|
+
showCorrectAnswer,
|
|
19
|
+
}: IOrderingActivityMaterialProps) => {
|
|
20
|
+
const [selectedTargetKey, setSelectedTargetKey] = useState(null);
|
|
21
|
+
const [selectedKey, setSelectedKey] = useState<string | null>(null);
|
|
22
|
+
const { screenSize } = useScreenSize();
|
|
23
|
+
const [view, setView] = useState("PC");
|
|
24
|
+
const [{ isOver, canDrop }, drop] = useDrop({
|
|
25
|
+
accept: "ORDERING",
|
|
26
|
+
drop: () => {},
|
|
27
|
+
collect: (monitor) => ({
|
|
28
|
+
isOver: monitor.isOver(),
|
|
29
|
+
canDrop: monitor.canDrop(),
|
|
30
|
+
}),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (!screenSize) return;
|
|
35
|
+
if (screenSize.width <= 768) {
|
|
36
|
+
setView("TABLET");
|
|
37
|
+
} else {
|
|
38
|
+
setView("PC");
|
|
39
|
+
}
|
|
40
|
+
}, [screenSize]);
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (!showCorrectAnswer) return;
|
|
44
|
+
const answerMap = answer.data.find(
|
|
45
|
+
(answerData: any) => answerData.type === "ORDERING"
|
|
46
|
+
).answerMap;
|
|
47
|
+
Object.keys(answerMap).forEach((answerKey, index) => {
|
|
48
|
+
answerMap[answerKey] = index;
|
|
49
|
+
});
|
|
50
|
+
}, [showCorrectAnswer]);
|
|
51
|
+
|
|
52
|
+
const retrieveAnswerMap = () => {
|
|
53
|
+
const foundIndex = answer.data.findIndex(
|
|
54
|
+
(answerData: any) => answerData.type === "ORDERING"
|
|
55
|
+
);
|
|
56
|
+
return answer.data[foundIndex].answerMap;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const checkAnswerState = (correctAnswer: string, learnerAnswer: string) => {
|
|
60
|
+
if (!isPreview) return "HIDDEN";
|
|
61
|
+
if (correctAnswer === learnerAnswer) {
|
|
62
|
+
return "CORRECT";
|
|
63
|
+
}
|
|
64
|
+
return "INCORRECT";
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const handleOrderingActivityItemChange = (
|
|
68
|
+
selectedKey: string,
|
|
69
|
+
materialKey: string
|
|
70
|
+
) => {
|
|
71
|
+
if (checkCanAnswerQuestion()) {
|
|
72
|
+
if (selectedKey) {
|
|
73
|
+
onChange(answer, selectedKey, materialKey);
|
|
74
|
+
setSelectedKey(null);
|
|
75
|
+
} else {
|
|
76
|
+
setSelectedKey(materialKey);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const calculateMarginTop = (index: number) => {
|
|
82
|
+
if (index === 0) {
|
|
83
|
+
return 0;
|
|
84
|
+
} else if (index === 1) {
|
|
85
|
+
return 150;
|
|
86
|
+
} else if (index % 2 === 0) {
|
|
87
|
+
return -150;
|
|
88
|
+
} else if (index % 2 === 1) {
|
|
89
|
+
return 0;
|
|
90
|
+
}
|
|
91
|
+
return 0;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const answerMap = retrieveAnswerMap();
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<div className="flex flex-row flex-wrap">
|
|
98
|
+
{Object.keys(answerMap).map((materialKey, index) => {
|
|
99
|
+
const learnerAnswerState = checkAnswerState(
|
|
100
|
+
answerMap[materialKey] + "",
|
|
101
|
+
index + ""
|
|
102
|
+
);
|
|
103
|
+
return (
|
|
104
|
+
<div className="w-full lg:w-1/2" key={index}>
|
|
105
|
+
<div
|
|
106
|
+
className={`flex flex-row items-center my-4 mx-2`}
|
|
107
|
+
style={{
|
|
108
|
+
marginTop:
|
|
109
|
+
view === "PC"
|
|
110
|
+
? calculateMarginTop(parseFloat(materialKey))
|
|
111
|
+
: 0,
|
|
112
|
+
}}
|
|
113
|
+
>
|
|
114
|
+
<div className="mr-3">
|
|
115
|
+
<div
|
|
116
|
+
className={`${
|
|
117
|
+
learnerAnswerState === "CORRECT"
|
|
118
|
+
? "border-catchup-green"
|
|
119
|
+
: learnerAnswerState === "INCORRECT"
|
|
120
|
+
? "border-catchup-red"
|
|
121
|
+
: selectedKey === materialKey
|
|
122
|
+
? "border-catchup-blue"
|
|
123
|
+
: "border-catchup-lighter-gray"
|
|
124
|
+
} min-h-catchup-activity-outer-box-item w-catchup-activity-box-item flex flex-col items-center justify-center border-2 rounded-catchup-xlarge cursor-pointer transition-all duration-300`}
|
|
125
|
+
>
|
|
126
|
+
<div
|
|
127
|
+
className={`${
|
|
128
|
+
selectedKey === materialKey
|
|
129
|
+
? "border-2 border-catchup-light-gray"
|
|
130
|
+
: "border-2 border-catchup-blue"
|
|
131
|
+
} flex flex-col items-center justify-center transition-all duration-300 rounded-catchup-full w-[50px] h-[50px]`}
|
|
132
|
+
>
|
|
133
|
+
<p className="">{parseFloat(materialKey) + 1}</p>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
<DraggableDroppableItem
|
|
139
|
+
key={index}
|
|
140
|
+
item={{ index: materialKey }}
|
|
141
|
+
type={"ORDERING"}
|
|
142
|
+
dropRef={drop}
|
|
143
|
+
component={
|
|
144
|
+
<div
|
|
145
|
+
className={`${
|
|
146
|
+
canDrop
|
|
147
|
+
? selectedKey !== materialKey
|
|
148
|
+
? selectedTargetKey === materialKey
|
|
149
|
+
? "bg-catchup-light-blue rounded-catchup-xlarge"
|
|
150
|
+
: "bg-catchup-light-blue rounded-catchup-xlarge opacity-40"
|
|
151
|
+
: ""
|
|
152
|
+
: ""
|
|
153
|
+
} flex-1 min-h-catchup-activity-outer-box-item flex flex-col items-center justify-center border-2 rounded-catchup-xlarge cursor-pointer p-3 ${
|
|
154
|
+
learnerAnswerState === "CORRECT"
|
|
155
|
+
? "border-catchup-green"
|
|
156
|
+
: learnerAnswerState === "INCORRECT"
|
|
157
|
+
? "border-catchup-red"
|
|
158
|
+
: "border-catchup-blue"
|
|
159
|
+
}`}
|
|
160
|
+
onMouseDown={() => {
|
|
161
|
+
if (checkCanAnswerQuestion()) {
|
|
162
|
+
setSelectedKey(materialKey);
|
|
163
|
+
}
|
|
164
|
+
}}
|
|
165
|
+
>
|
|
166
|
+
{contentMap.type === "TEXT" ? (
|
|
167
|
+
<p className="text-xl whitespace-pre-wrap">
|
|
168
|
+
{constructInputWithSpecialExpressionList(
|
|
169
|
+
materialMap[answerMap[materialKey]]
|
|
170
|
+
).map((inputPart, index) => (
|
|
171
|
+
<span
|
|
172
|
+
key={index}
|
|
173
|
+
className={`${
|
|
174
|
+
inputPart.isBold ? "font-bold" : ""
|
|
175
|
+
} ${inputPart.isUnderline ? "underline" : ""}`}
|
|
176
|
+
>
|
|
177
|
+
{inputPart.isEquation ? (
|
|
178
|
+
<span className="text-2xl">
|
|
179
|
+
<InlineMath math={inputPart.value} />
|
|
180
|
+
</span>
|
|
181
|
+
) : (
|
|
182
|
+
inputPart.value
|
|
183
|
+
)}
|
|
184
|
+
</span>
|
|
185
|
+
))}
|
|
186
|
+
</p>
|
|
187
|
+
) : (
|
|
188
|
+
<ShowMaterialMediaByContentType
|
|
189
|
+
key={`${uniqueValue}-${index}`}
|
|
190
|
+
contentType={contentMap.type}
|
|
191
|
+
src={materialMap[answerMap[materialKey]]}
|
|
192
|
+
canFullScreen={true}
|
|
193
|
+
/>
|
|
194
|
+
)}
|
|
195
|
+
</div>
|
|
196
|
+
}
|
|
197
|
+
target={selectedTargetKey}
|
|
198
|
+
setTarget={setSelectedTargetKey}
|
|
199
|
+
moveCardHandler={() => {
|
|
200
|
+
if (!selectedKey) return;
|
|
201
|
+
if (!selectedTargetKey) return;
|
|
202
|
+
handleOrderingActivityItemChange(
|
|
203
|
+
selectedKey,
|
|
204
|
+
selectedTargetKey
|
|
205
|
+
);
|
|
206
|
+
}}
|
|
207
|
+
/>
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
);
|
|
211
|
+
})}
|
|
212
|
+
</div>
|
|
213
|
+
);
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
export default OrderingActivityMaterialContent;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import Modal from "react-modal";
|
|
3
|
+
import BaseImage from "../../images/BaseImage";
|
|
4
|
+
import { IShowMaterialMediaByContentTypeProps } from "../../../properties/ActivityProperties";
|
|
5
|
+
|
|
6
|
+
const ShowMaterialMediaByContentType = ({
|
|
7
|
+
key,
|
|
8
|
+
contentType,
|
|
9
|
+
src,
|
|
10
|
+
canFullScreen,
|
|
11
|
+
}: IShowMaterialMediaByContentTypeProps) => {
|
|
12
|
+
const [showFullScreen, setShowFullScreen] = useState<boolean>(false);
|
|
13
|
+
const [selectedFullScreenItem, setSelectedFullScreenItem] = useState<
|
|
14
|
+
any | null
|
|
15
|
+
>(null);
|
|
16
|
+
const [isLoaded, setIsLoaded] = useState<boolean>(false);
|
|
17
|
+
const [isFullHeight, setIsFullHeight] = useState<boolean>(false);
|
|
18
|
+
const imageRef = useRef<HTMLImageElement>(null);
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
setIsLoaded(false);
|
|
22
|
+
}, []);
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (!isLoaded) return;
|
|
26
|
+
if (!imageRef) return;
|
|
27
|
+
if (!imageRef.current) return;
|
|
28
|
+
const w = imageRef.current.naturalWidth;
|
|
29
|
+
const h = imageRef.current.naturalHeight;
|
|
30
|
+
if (w >= h) {
|
|
31
|
+
setIsFullHeight(false);
|
|
32
|
+
} else {
|
|
33
|
+
setIsFullHeight(true);
|
|
34
|
+
}
|
|
35
|
+
}, [isLoaded, key]);
|
|
36
|
+
|
|
37
|
+
const RenderShowFullScreenItem = () => {
|
|
38
|
+
return (
|
|
39
|
+
<Modal
|
|
40
|
+
isOpen={showFullScreen}
|
|
41
|
+
onAfterOpen={() => {}}
|
|
42
|
+
onRequestClose={() => {
|
|
43
|
+
setShowFullScreen(false);
|
|
44
|
+
setSelectedFullScreenItem(null);
|
|
45
|
+
}}
|
|
46
|
+
style={{
|
|
47
|
+
content: {
|
|
48
|
+
top: "50%",
|
|
49
|
+
left: "50%",
|
|
50
|
+
right: "auto",
|
|
51
|
+
bottom: "auto",
|
|
52
|
+
marginRight: "-50%",
|
|
53
|
+
transform: "translate(-50%, -50%)",
|
|
54
|
+
padding: 0,
|
|
55
|
+
borderRadius: 20,
|
|
56
|
+
background: "",
|
|
57
|
+
border: "",
|
|
58
|
+
width: "900px",
|
|
59
|
+
maxHeight: "70%",
|
|
60
|
+
overflow: "auto",
|
|
61
|
+
},
|
|
62
|
+
overlay: {
|
|
63
|
+
background: "rgba(0, 0, 0, 0.6)",
|
|
64
|
+
},
|
|
65
|
+
}}
|
|
66
|
+
contentLabel=""
|
|
67
|
+
>
|
|
68
|
+
<div className="flex-1 flex flex-col">
|
|
69
|
+
{/* <div className="m-4"> */}
|
|
70
|
+
<div className="ml-auto px-5 py-3">
|
|
71
|
+
<BaseImage
|
|
72
|
+
src="/icons/cross-red.png"
|
|
73
|
+
alt="cross-red"
|
|
74
|
+
size="medium"
|
|
75
|
+
onClick={() => {
|
|
76
|
+
setShowFullScreen(false);
|
|
77
|
+
setSelectedFullScreenItem(null);
|
|
78
|
+
}}
|
|
79
|
+
/>
|
|
80
|
+
</div>
|
|
81
|
+
<div className="flex items-center justify-center h-[500px]">
|
|
82
|
+
<BaseImage
|
|
83
|
+
src={selectedFullScreenItem}
|
|
84
|
+
alt="selected-fullscreen-item"
|
|
85
|
+
size="custom"
|
|
86
|
+
className="w-full"
|
|
87
|
+
/>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
</Modal>
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
return contentType === "IMAGE" ? (
|
|
95
|
+
<div key={key}>
|
|
96
|
+
{RenderShowFullScreenItem()}
|
|
97
|
+
<div className="my-2">
|
|
98
|
+
<div className="h-full flex flex-row flex-wrap items-center">
|
|
99
|
+
<div
|
|
100
|
+
className={`${
|
|
101
|
+
isFullHeight
|
|
102
|
+
? "h-catchup-activity-box-item"
|
|
103
|
+
: "max-w-catchup-activity-box-item"
|
|
104
|
+
} flex flex-col justify-center items-center relative`}
|
|
105
|
+
>
|
|
106
|
+
<BaseImage
|
|
107
|
+
src={src}
|
|
108
|
+
alt="image"
|
|
109
|
+
ref={imageRef}
|
|
110
|
+
size="custom"
|
|
111
|
+
className="h-full rounded-catchup-xlarge"
|
|
112
|
+
onLoad={() => {
|
|
113
|
+
setIsLoaded(true);
|
|
114
|
+
}}
|
|
115
|
+
/>
|
|
116
|
+
{src !== null &&
|
|
117
|
+
src !== "" &&
|
|
118
|
+
src !== "DEFAULT_OPTION" &&
|
|
119
|
+
canFullScreen ? (
|
|
120
|
+
<div
|
|
121
|
+
className="absolute flex items-center justify-center top-2 right-2 h-6 w-6 cursor-pointer border rounded-catchup-xlarge border-catchup-blue p-1"
|
|
122
|
+
onMouseEnter={(e) => {
|
|
123
|
+
e.preventDefault();
|
|
124
|
+
}}
|
|
125
|
+
onClick={(e) => {
|
|
126
|
+
e.preventDefault();
|
|
127
|
+
setShowFullScreen(true);
|
|
128
|
+
setSelectedFullScreenItem(src);
|
|
129
|
+
}}
|
|
130
|
+
>
|
|
131
|
+
<BaseImage
|
|
132
|
+
src="/icons/arrow-up.png"
|
|
133
|
+
alt="arrow-up"
|
|
134
|
+
size="custom"
|
|
135
|
+
className="w-full"
|
|
136
|
+
/>
|
|
137
|
+
</div>
|
|
138
|
+
) : null}
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
) : contentType === "VIDEO" ? (
|
|
144
|
+
<div className="my-2">
|
|
145
|
+
<div className="h-full flex flex-row flex-wrap items-center">
|
|
146
|
+
<div className="h-full flex flex-col justify-center items-center">
|
|
147
|
+
<video
|
|
148
|
+
className="h-catchup-activity-box-item rounded-catchup-xlarge"
|
|
149
|
+
src={src}
|
|
150
|
+
controls
|
|
151
|
+
onClick={() => {}}
|
|
152
|
+
/>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
) : contentType === "AUDIO" ? (
|
|
157
|
+
<div className="my-2">
|
|
158
|
+
<div className="h-full flex flex-row flex-wrap items-center">
|
|
159
|
+
<div className="h-full flex flex-col justify-center items-center">
|
|
160
|
+
<audio
|
|
161
|
+
className="h-catchup-activity-box-item rounded-catchup-xlarge"
|
|
162
|
+
src={src}
|
|
163
|
+
controls
|
|
164
|
+
onClick={() => {}}
|
|
165
|
+
/>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
) : null;
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
export default ShowMaterialMediaByContentType;
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import useScreenSize from "../../../hooks/useScreenSize";
|
|
3
|
+
import i18n from "../../../language/i18n";
|
|
4
|
+
import ShowMaterialMediaByContentType from "./ShowMaterialMediaByContentType";
|
|
5
|
+
import { InlineMath } from "react-katex";
|
|
6
|
+
import { constructInputWithSpecialExpressionList } from "../../../utilization/CatchtivityUtilization";
|
|
7
|
+
import BaseImage from "../../images/BaseImage";
|
|
8
|
+
import { ITrueFalseActivityMaterialProps } from "../../../properties/ActivityProperties";
|
|
9
|
+
import DividerLine from "../../dividers/DividerLine";
|
|
10
|
+
import { shuffleArray } from "../../../utilization/AppUtilization";
|
|
11
|
+
|
|
12
|
+
const TrueFalseActivityMaterialContent = ({
|
|
13
|
+
uniqueValue,
|
|
14
|
+
answer,
|
|
15
|
+
materialMap,
|
|
16
|
+
contentMap,
|
|
17
|
+
checkCanAnswerQuestion,
|
|
18
|
+
onChange,
|
|
19
|
+
isPreview,
|
|
20
|
+
showCorrectAnswer,
|
|
21
|
+
}: ITrueFalseActivityMaterialProps) => {
|
|
22
|
+
const { screenSize } = useScreenSize();
|
|
23
|
+
const [shuffleOptionList, setShuffleOptionList] = useState<any[]>([]);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
const optionList = [];
|
|
27
|
+
optionList.push(...materialMap.trueList);
|
|
28
|
+
optionList.push(...materialMap.falseList);
|
|
29
|
+
if (isPreview) {
|
|
30
|
+
setShuffleOptionList(optionList);
|
|
31
|
+
} else {
|
|
32
|
+
setShuffleOptionList(shuffleArray(optionList));
|
|
33
|
+
}
|
|
34
|
+
}, []);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (!showCorrectAnswer) return;
|
|
38
|
+
answer.data.find(
|
|
39
|
+
(answerData: any) => answerData.type === "TRUE_FALSE"
|
|
40
|
+
).answerMap = materialMap;
|
|
41
|
+
}, [showCorrectAnswer]);
|
|
42
|
+
|
|
43
|
+
const retrieveAnswer = () => {
|
|
44
|
+
if (!answer)
|
|
45
|
+
return {
|
|
46
|
+
answerMap: {
|
|
47
|
+
trueList: [],
|
|
48
|
+
falseList: [],
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
return answer.data.find(
|
|
52
|
+
(answerData: any) => answerData.type === "TRUE_FALSE"
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const retrieveAnswerMap = () => {
|
|
57
|
+
const { answerMap } = retrieveAnswer();
|
|
58
|
+
return answerMap;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const checkAnswerState = (correctAnswer: string, learnerAnswer: string) => {
|
|
62
|
+
if (!isPreview) return "HIDDEN";
|
|
63
|
+
if (correctAnswer === learnerAnswer) {
|
|
64
|
+
return "CORRECT";
|
|
65
|
+
}
|
|
66
|
+
return "INCORRECT";
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const answerMap = retrieveAnswerMap();
|
|
70
|
+
return (
|
|
71
|
+
<div className="">
|
|
72
|
+
<div className="hidden md:block">
|
|
73
|
+
<span className="font-semibold text-xl opacity-60">
|
|
74
|
+
{i18n.t("please_select_true_false_text")}
|
|
75
|
+
</span>
|
|
76
|
+
</div>
|
|
77
|
+
<div className="hidden md:contents">
|
|
78
|
+
<DividerLine />
|
|
79
|
+
</div>
|
|
80
|
+
<div className="flex flex-row justify-end items-center gap-x-2">
|
|
81
|
+
<div className="w-[50px]">
|
|
82
|
+
<p className="font-bold text-lg">{i18n.t("true")}</p>
|
|
83
|
+
</div>
|
|
84
|
+
<div className="w-[50px]">
|
|
85
|
+
<p className="font-bold text-lg">{i18n.t("false")}</p>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
{checkCanAnswerQuestion() ? (
|
|
89
|
+
<div className={`flex flex-row w-full justify-center flex-wrap`}>
|
|
90
|
+
{shuffleOptionList.map((shuffleOption, index) => {
|
|
91
|
+
const correctAnswer =
|
|
92
|
+
materialMap.trueList.find(
|
|
93
|
+
(trueItem: string) => trueItem === shuffleOption
|
|
94
|
+
) !== undefined
|
|
95
|
+
? "TRUE"
|
|
96
|
+
: "FALSE";
|
|
97
|
+
const learnerAnswer =
|
|
98
|
+
answerMap.trueList.find(
|
|
99
|
+
(trueItem: string) => trueItem === shuffleOption
|
|
100
|
+
) !== undefined
|
|
101
|
+
? "TRUE"
|
|
102
|
+
: "FALSE";
|
|
103
|
+
|
|
104
|
+
const learnerAnswerState = checkAnswerState(
|
|
105
|
+
correctAnswer,
|
|
106
|
+
learnerAnswer
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<div
|
|
111
|
+
key={index}
|
|
112
|
+
className={`w-full flex flex-row items-center justify-center cursor-pointer my-2 ${
|
|
113
|
+
learnerAnswerState === "CORRECT"
|
|
114
|
+
? "border-2 border-catchup-green rounded-catchup-xlarge"
|
|
115
|
+
: learnerAnswerState === "INCORRECT"
|
|
116
|
+
? "border-2 border-catchup-red rounded-catchup-xlarge"
|
|
117
|
+
: ""
|
|
118
|
+
}`}
|
|
119
|
+
>
|
|
120
|
+
<div className="flex-1">
|
|
121
|
+
{contentMap.type === "TEXT" ? (
|
|
122
|
+
<p className="text-xl p-2 whitespace-pre-wrap">
|
|
123
|
+
{constructInputWithSpecialExpressionList(
|
|
124
|
+
shuffleOption
|
|
125
|
+
).map((inputPart, index) => (
|
|
126
|
+
<span
|
|
127
|
+
key={index}
|
|
128
|
+
className={`${inputPart.isBold ? "font-bold" : ""} ${
|
|
129
|
+
inputPart.isUnderline ? "underline" : ""
|
|
130
|
+
}`}
|
|
131
|
+
>
|
|
132
|
+
{inputPart.isEquation ? (
|
|
133
|
+
<span className="text-2xl">
|
|
134
|
+
<InlineMath math={inputPart.value} />
|
|
135
|
+
</span>
|
|
136
|
+
) : (
|
|
137
|
+
inputPart.value
|
|
138
|
+
)}
|
|
139
|
+
</span>
|
|
140
|
+
))}
|
|
141
|
+
</p>
|
|
142
|
+
) : (
|
|
143
|
+
<ShowMaterialMediaByContentType
|
|
144
|
+
key={`${uniqueValue}-${index}`}
|
|
145
|
+
contentType={contentMap.type}
|
|
146
|
+
src={shuffleOption}
|
|
147
|
+
canFullScreen={true}
|
|
148
|
+
/>
|
|
149
|
+
)}
|
|
150
|
+
</div>
|
|
151
|
+
<div className="flex flex-row items-center gap-x-2">
|
|
152
|
+
<div className="w-[50px]">
|
|
153
|
+
<div className="flex flex-col items-center justify-center">
|
|
154
|
+
<BaseImage
|
|
155
|
+
src={
|
|
156
|
+
answerMap.trueList.includes(shuffleOption)
|
|
157
|
+
? "/icons/checkbox.png"
|
|
158
|
+
: "/icons/empty-checkbox.png"
|
|
159
|
+
}
|
|
160
|
+
alt="checkbox-empty-checkbox"
|
|
161
|
+
size="small"
|
|
162
|
+
onClick={() => {
|
|
163
|
+
onChange(answer, "TRUE", shuffleOption);
|
|
164
|
+
}}
|
|
165
|
+
/>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
<div className="w-[50px]">
|
|
169
|
+
<div className="flex flex-col items-center justify-center">
|
|
170
|
+
<BaseImage
|
|
171
|
+
src={
|
|
172
|
+
answerMap.falseList.includes(shuffleOption)
|
|
173
|
+
? "/icons/checkbox.png"
|
|
174
|
+
: "/icons/empty-checkbox.png"
|
|
175
|
+
}
|
|
176
|
+
alt="checkbox-empty-checkbox"
|
|
177
|
+
size="small"
|
|
178
|
+
onClick={() => {
|
|
179
|
+
onChange(answer, "FALSE", shuffleOption);
|
|
180
|
+
}}
|
|
181
|
+
/>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
);
|
|
187
|
+
})}
|
|
188
|
+
</div>
|
|
189
|
+
) : (
|
|
190
|
+
<>
|
|
191
|
+
{answerMap.trueList.map((item: string) => (
|
|
192
|
+
<div className="flex flex-row items-center gap-x-2">
|
|
193
|
+
<div className="flex-1">
|
|
194
|
+
<p>{item}</p>
|
|
195
|
+
</div>
|
|
196
|
+
<div className="w-[50px]">
|
|
197
|
+
<p className="underline">{i18n.t("true")}</p>
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
))}
|
|
201
|
+
{answerMap.falseList.map((item: string) => (
|
|
202
|
+
<div className="flex flex-row items-center gap-x-2">
|
|
203
|
+
<div className="flex-1">
|
|
204
|
+
<p>{item}</p>
|
|
205
|
+
</div>
|
|
206
|
+
<div className="w-[50px]">
|
|
207
|
+
<p className="underline">{i18n.t("false")}</p>
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
))}
|
|
211
|
+
</>
|
|
212
|
+
)}
|
|
213
|
+
</div>
|
|
214
|
+
);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
export default TrueFalseActivityMaterialContent;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { InlineMath } from "react-katex";
|
|
2
|
+
import i18n from "../../../language/i18n";
|
|
3
|
+
import { constructInputWithSpecialExpressionList } from "../../../utilization/CatchtivityUtilization";
|
|
4
|
+
import { IActivitySolutionProps } from "../../../properties/ActivityProperties";
|
|
5
|
+
|
|
6
|
+
const ActivitySolutionContent = ({
|
|
7
|
+
activityTemplateType,
|
|
8
|
+
data,
|
|
9
|
+
}: IActivitySolutionProps) => {
|
|
10
|
+
if (!activityTemplateType) return;
|
|
11
|
+
|
|
12
|
+
let solutionMapString;
|
|
13
|
+
if (activityTemplateType === "ORDERING") {
|
|
14
|
+
if (data["orderingSolutionMap"])
|
|
15
|
+
solutionMapString = data["orderingSolutionMap"];
|
|
16
|
+
} else if (activityTemplateType === "DROPDOWN") {
|
|
17
|
+
if (data["dropdownSolutionMap"])
|
|
18
|
+
solutionMapString = data["dropdownSolutionMap"];
|
|
19
|
+
} else if (activityTemplateType === "MCSA") {
|
|
20
|
+
if (data["MCSASolutionMap"]) solutionMapString = data["MCSASolutionMap"];
|
|
21
|
+
} else if (activityTemplateType === "MCMA") {
|
|
22
|
+
if (data["MCMASolutionMap"]) solutionMapString = data["MCMASolutionMap"];
|
|
23
|
+
} else if (activityTemplateType === "MATCHING") {
|
|
24
|
+
if (data["matchingSolutionMap"])
|
|
25
|
+
solutionMapString = data["matchingSolutionMap"];
|
|
26
|
+
} else if (activityTemplateType === "GROUPING") {
|
|
27
|
+
if (data["groupingSolutionMap"])
|
|
28
|
+
solutionMapString = data["groupingSolutionMap"];
|
|
29
|
+
} else if (activityTemplateType === "FILL_IN_THE_BLANKS") {
|
|
30
|
+
if (data["fillInTheBlanksSolutionMap"])
|
|
31
|
+
solutionMapString = data["fillInTheBlanksSolutionMap"];
|
|
32
|
+
} else if (activityTemplateType === "OPEN_ENDED") {
|
|
33
|
+
if (data["openEndedSolutionMap"])
|
|
34
|
+
solutionMapString = data["openEndedSolutionMap"];
|
|
35
|
+
} else if (activityTemplateType === "TRUE_FALSE") {
|
|
36
|
+
if (data["trueFalseSolutionMap"])
|
|
37
|
+
solutionMapString = data["trueFalseSolutionMap"];
|
|
38
|
+
}
|
|
39
|
+
if (!solutionMapString.includes("[ONAYLI CEVAP]")) return;
|
|
40
|
+
const solutionMap = JSON.parse(solutionMapString);
|
|
41
|
+
if (!solutionMap) return;
|
|
42
|
+
|
|
43
|
+
if (Object.keys(solutionMap).length === 0) return;
|
|
44
|
+
return (
|
|
45
|
+
<div className="mx-2">
|
|
46
|
+
<div className="p-4 border-catchup-blue border-2 rounded-catchup-xlarge">
|
|
47
|
+
<p className="text-xl font-bold text-center mb-3">
|
|
48
|
+
{i18n.t("solution")}
|
|
49
|
+
</p>
|
|
50
|
+
{Object.keys(solutionMap).map((key, index) => {
|
|
51
|
+
const currentItem = JSON.parse(solutionMap[key]);
|
|
52
|
+
const { value } = currentItem;
|
|
53
|
+
return (
|
|
54
|
+
<p className="my-3">
|
|
55
|
+
<span
|
|
56
|
+
key={`${key}_${index}`}
|
|
57
|
+
className="text-xl whitespace-pre-wrap"
|
|
58
|
+
>
|
|
59
|
+
{constructInputWithSpecialExpressionList(value).map(
|
|
60
|
+
(inputPart, index) => (
|
|
61
|
+
<span
|
|
62
|
+
key={index}
|
|
63
|
+
className={`${inputPart.isBold ? "font-bold" : ""} ${
|
|
64
|
+
inputPart.isUnderline ? "underline" : ""
|
|
65
|
+
}`}
|
|
66
|
+
>
|
|
67
|
+
{inputPart.isEquation ? (
|
|
68
|
+
<span className="text-2xl">
|
|
69
|
+
<InlineMath math={inputPart.value} />
|
|
70
|
+
</span>
|
|
71
|
+
) : (
|
|
72
|
+
inputPart.value
|
|
73
|
+
)}
|
|
74
|
+
</span>
|
|
75
|
+
)
|
|
76
|
+
)}
|
|
77
|
+
</span>
|
|
78
|
+
</p>
|
|
79
|
+
);
|
|
80
|
+
})}
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export default ActivitySolutionContent;
|