catchup-library-web 1.20.35 → 1.21.0
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.js +1056 -823
- package/dist/index.mjs +971 -738
- package/package.json +2 -6
- package/src/components/activities/material-contents/FillInTheBlanksActivityMaterialContent.tsx +62 -2
- package/src/components/activities/material-contents/GroupingActivityMaterialContent.tsx +363 -203
- package/src/components/activities/material-contents/MatchingActivityMaterialContent.tsx +318 -172
- package/src/components/activities/material-contents/OrderingActivityMaterialContent.tsx +76 -2
- package/src/components/activities/material-contents/FillInTheBlanksActivityMaterialContent2.tsx +0 -306
- package/src/components/activities/material-contents/OrderingActivityMaterialContent2.tsx +0 -231
- package/src/components/dnds/DraggableDroppableItem.tsx +0 -60
- package/src/components/dnds/DraggableItem.tsx +0 -39
- package/src/components/dnds/DroppableItem.tsx +0 -33
|
@@ -22,6 +22,10 @@ const OrderingActivityMaterialContent = ({
|
|
|
22
22
|
null
|
|
23
23
|
);
|
|
24
24
|
const dragElementRef = useRef<HTMLDivElement>(null);
|
|
25
|
+
const [mousePosition, setMousePosition] = useState<{ x: number; y: number }>({
|
|
26
|
+
x: 0,
|
|
27
|
+
y: 0,
|
|
28
|
+
});
|
|
25
29
|
const [touchPosition, setTouchPosition] = useState<{ x: number; y: number }>({
|
|
26
30
|
x: 0,
|
|
27
31
|
y: 0,
|
|
@@ -90,6 +94,24 @@ const OrderingActivityMaterialContent = ({
|
|
|
90
94
|
e.preventDefault();
|
|
91
95
|
setDraggedKey(materialKey);
|
|
92
96
|
setSelectedKey(null);
|
|
97
|
+
setMousePosition({ x: e.clientX, y: e.clientY });
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const handleMouseMove = (e: React.MouseEvent): void => {
|
|
101
|
+
if (!draggedKey) return;
|
|
102
|
+
|
|
103
|
+
setMousePosition({ x: e.clientX, y: e.clientY });
|
|
104
|
+
|
|
105
|
+
// Find the element under the mouse point
|
|
106
|
+
const elementUnder = document.elementFromPoint(e.clientX, e.clientY);
|
|
107
|
+
const dropZone = elementUnder?.closest("[data-ordering-drop-zone]");
|
|
108
|
+
|
|
109
|
+
if (dropZone) {
|
|
110
|
+
const dropKey = dropZone.getAttribute("data-ordering-drop-zone");
|
|
111
|
+
setDropTargetKey(dropKey);
|
|
112
|
+
} else {
|
|
113
|
+
setDropTargetKey(null);
|
|
114
|
+
}
|
|
93
115
|
};
|
|
94
116
|
|
|
95
117
|
const handleMouseUp = (): void => {
|
|
@@ -102,6 +124,7 @@ const OrderingActivityMaterialContent = ({
|
|
|
102
124
|
}
|
|
103
125
|
setDraggedKey(null);
|
|
104
126
|
setDropTargetKey(null);
|
|
127
|
+
setMousePosition({ x: 0, y: 0 });
|
|
105
128
|
};
|
|
106
129
|
|
|
107
130
|
// Touch drag handlers
|
|
@@ -150,6 +173,7 @@ const OrderingActivityMaterialContent = ({
|
|
|
150
173
|
setDraggedKey(null);
|
|
151
174
|
setDropTargetKey(null);
|
|
152
175
|
setDraggedElement(null);
|
|
176
|
+
setTouchPosition({ x: 0, y: 0 });
|
|
153
177
|
};
|
|
154
178
|
|
|
155
179
|
// Click/tap to select (for easier mobile interaction)
|
|
@@ -170,7 +194,57 @@ const OrderingActivityMaterialContent = ({
|
|
|
170
194
|
const answerMap = retrieveAnswerMap();
|
|
171
195
|
|
|
172
196
|
return (
|
|
173
|
-
<div
|
|
197
|
+
<div
|
|
198
|
+
className="flex flex-row flex-wrap"
|
|
199
|
+
onMouseMove={handleMouseMove}
|
|
200
|
+
onMouseUp={handleMouseUp}
|
|
201
|
+
>
|
|
202
|
+
{/* Floating drag preview for mouse */}
|
|
203
|
+
{draggedKey && mousePosition.x > 0 && (
|
|
204
|
+
<div
|
|
205
|
+
className="fixed pointer-events-none z-50 opacity-80"
|
|
206
|
+
style={{
|
|
207
|
+
left: `${mousePosition.x}px`,
|
|
208
|
+
top: `${mousePosition.y}px`,
|
|
209
|
+
transform: "translate(-50%, -50%)",
|
|
210
|
+
}}
|
|
211
|
+
>
|
|
212
|
+
{contentMap.type === "TEXT" ? (
|
|
213
|
+
<div className="border-catchup-blue border-2 px-3 py-2 rounded-catchup-xlarge bg-white shadow-lg">
|
|
214
|
+
<p className="text-xl whitespace-pre-wrap">
|
|
215
|
+
{constructInputWithSpecialExpressionList(
|
|
216
|
+
materialMap[answerMap[draggedKey]]
|
|
217
|
+
).map((inputPart, index) => (
|
|
218
|
+
<span
|
|
219
|
+
key={index}
|
|
220
|
+
className={`${inputPart.isBold ? "font-bold" : ""} ${
|
|
221
|
+
inputPart.isUnderline ? "underline" : ""
|
|
222
|
+
}`}
|
|
223
|
+
>
|
|
224
|
+
{inputPart.isEquation ? (
|
|
225
|
+
<span className="text-xl">
|
|
226
|
+
<InlineMath math={inputPart.value} />
|
|
227
|
+
</span>
|
|
228
|
+
) : (
|
|
229
|
+
inputPart.value
|
|
230
|
+
)}
|
|
231
|
+
</span>
|
|
232
|
+
))}
|
|
233
|
+
</p>
|
|
234
|
+
</div>
|
|
235
|
+
) : (
|
|
236
|
+
<div className="border-catchup-blue border-2 px-2 py-1 rounded-catchup-xlarge bg-white shadow-lg">
|
|
237
|
+
<ShowMaterialMediaByContentType
|
|
238
|
+
key={`${uniqueValue}-drag-mouse`}
|
|
239
|
+
contentType={contentMap.type}
|
|
240
|
+
src={materialMap[answerMap[draggedKey]]}
|
|
241
|
+
canFullScreen={false}
|
|
242
|
+
/>
|
|
243
|
+
</div>
|
|
244
|
+
)}
|
|
245
|
+
</div>
|
|
246
|
+
)}
|
|
247
|
+
|
|
174
248
|
{/* Floating drag preview for touch */}
|
|
175
249
|
{draggedKey && touchPosition.x > 0 && (
|
|
176
250
|
<div
|
|
@@ -207,7 +281,7 @@ const OrderingActivityMaterialContent = ({
|
|
|
207
281
|
) : (
|
|
208
282
|
<div className="border-catchup-blue border-2 px-2 py-1 rounded-catchup-xlarge bg-white shadow-lg">
|
|
209
283
|
<ShowMaterialMediaByContentType
|
|
210
|
-
key={`${uniqueValue}-drag`}
|
|
284
|
+
key={`${uniqueValue}-drag-touch`}
|
|
211
285
|
contentType={contentMap.type}
|
|
212
286
|
src={materialMap[answerMap[draggedKey]]}
|
|
213
287
|
canFullScreen={false}
|
package/src/components/activities/material-contents/FillInTheBlanksActivityMaterialContent2.tsx
DELETED
|
@@ -1,306 +0,0 @@
|
|
|
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
|
-
import InputWithSpecialExpression from "../../texts/InputWithSpecialExpression";
|
|
16
|
-
|
|
17
|
-
const FillInTheBlanksActivityMaterialContent = ({
|
|
18
|
-
uniqueValue,
|
|
19
|
-
answer,
|
|
20
|
-
optionList,
|
|
21
|
-
materialMap,
|
|
22
|
-
contentMap,
|
|
23
|
-
checkCanAnswerQuestion,
|
|
24
|
-
onChange,
|
|
25
|
-
isPreview,
|
|
26
|
-
showCorrectAnswer,
|
|
27
|
-
}: IFillInTheBlanksActivityMaterialProps) => {
|
|
28
|
-
const [shuffleOptionList, setShuffleOptionList] = useState([]);
|
|
29
|
-
const [selectedOption, setSelectedOption] = useState(null);
|
|
30
|
-
const [pasteOptionIndex, setPasteOptionIndex] = useState(null);
|
|
31
|
-
const [{ isOver, canDrop }, drop] = useDrop({
|
|
32
|
-
accept: "FILL_IN_THE_BLANKS",
|
|
33
|
-
drop: () => {},
|
|
34
|
-
collect: (monitor) => ({
|
|
35
|
-
isOver: monitor.isOver(),
|
|
36
|
-
canDrop: monitor.canDrop(),
|
|
37
|
-
}),
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
useEffect(() => {
|
|
41
|
-
setShuffleOptionList(shuffleArray(optionList));
|
|
42
|
-
}, []);
|
|
43
|
-
|
|
44
|
-
useEffect(() => {
|
|
45
|
-
if (!showCorrectAnswer) return;
|
|
46
|
-
const foundAnswer = answer.data.find(
|
|
47
|
-
(answerData: any) => answerData.type === "FILL_IN_THE_BLANKS"
|
|
48
|
-
);
|
|
49
|
-
if (foundAnswer.answerMap.length === 0) return;
|
|
50
|
-
if (Object.keys(materialMap).length === 0) return;
|
|
51
|
-
foundAnswer.answerMap = Object.keys(materialMap).map(
|
|
52
|
-
(materialMapKey) => JSON.parse(materialMap[materialMapKey])[0]
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
onChange(answer, 0, JSON.parse(materialMap[0])[0]);
|
|
56
|
-
}, [showCorrectAnswer]);
|
|
57
|
-
|
|
58
|
-
const retrieveAnswerMap = () => {
|
|
59
|
-
const foundIndex = answer.data.findIndex(
|
|
60
|
-
(answerData: any) => answerData.type === "FILL_IN_THE_BLANKS"
|
|
61
|
-
);
|
|
62
|
-
return answer.data[foundIndex].answerMap;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const checkAnswerState = (correctAnswerList: any, learnerAnswer: string) => {
|
|
66
|
-
if (!isPreview) return null;
|
|
67
|
-
const foundIndex = correctAnswerList.findIndex(
|
|
68
|
-
(correctAnswer: string) => correctAnswer === learnerAnswer
|
|
69
|
-
);
|
|
70
|
-
if (foundIndex !== -1) {
|
|
71
|
-
return "CORRECT";
|
|
72
|
-
}
|
|
73
|
-
return "INCORRECT";
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const checkAnswerProvided = (answerMap: any, option: string) => {
|
|
77
|
-
return (
|
|
78
|
-
Object.keys(answerMap).findIndex((key) => answerMap[key] === option) !==
|
|
79
|
-
-1
|
|
80
|
-
);
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const handleSelectOption = (option: any) => {
|
|
84
|
-
setSelectedOption(option);
|
|
85
|
-
setPasteOptionIndex(null);
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
const answerMap = retrieveAnswerMap();
|
|
89
|
-
|
|
90
|
-
return (
|
|
91
|
-
<div
|
|
92
|
-
className="flex flex-row flex-wrap items-center"
|
|
93
|
-
// onMouseUp={() => {}}
|
|
94
|
-
// onTouchStart={() => {}}
|
|
95
|
-
>
|
|
96
|
-
<div className="hidden md:block">
|
|
97
|
-
<span className="font-semibold text-xl opacity-60">
|
|
98
|
-
{i18n.t("please_select_fill_in_the_blanks_text")}
|
|
99
|
-
</span>
|
|
100
|
-
</div>
|
|
101
|
-
<div className="hidden md:contents">
|
|
102
|
-
<DividerLine />
|
|
103
|
-
</div>
|
|
104
|
-
|
|
105
|
-
<div className="w-full flex flex-row flex-wrap gap-x-2 gap-y-2 my-2">
|
|
106
|
-
{shuffleOptionList.map((option, index) =>
|
|
107
|
-
checkAnswerProvided(answerMap, option) ? (
|
|
108
|
-
<div className="opacity-30" key={index}>
|
|
109
|
-
<ShowMaterialMediaByContentType
|
|
110
|
-
key={`${uniqueValue}-${index}`}
|
|
111
|
-
contentType={contentMap.type}
|
|
112
|
-
src={option}
|
|
113
|
-
canFullScreen={true}
|
|
114
|
-
/>
|
|
115
|
-
</div>
|
|
116
|
-
) : (
|
|
117
|
-
<DraggableItem
|
|
118
|
-
key={index}
|
|
119
|
-
item={{ index: option }}
|
|
120
|
-
type={"FILL_IN_THE_BLANKS"}
|
|
121
|
-
component={
|
|
122
|
-
contentMap.type === "TEXT" ? (
|
|
123
|
-
<div
|
|
124
|
-
className="border-catchup-blue border-2 px-2 rounded-catchup-xlarge cursor-pointer select-none"
|
|
125
|
-
onClick={() => handleSelectOption(option)}
|
|
126
|
-
onMouseDown={() => {
|
|
127
|
-
setSelectedOption(option);
|
|
128
|
-
setPasteOptionIndex(null);
|
|
129
|
-
}}
|
|
130
|
-
onTouchEnd={() => {
|
|
131
|
-
setSelectedOption(option);
|
|
132
|
-
setPasteOptionIndex(null);
|
|
133
|
-
}}
|
|
134
|
-
>
|
|
135
|
-
<p className="italic whitespace-pre-wrap">
|
|
136
|
-
<InputWithSpecialExpression
|
|
137
|
-
value={option}
|
|
138
|
-
showSpecialOnly={false}
|
|
139
|
-
/>
|
|
140
|
-
</p>
|
|
141
|
-
</div>
|
|
142
|
-
) : (
|
|
143
|
-
<div
|
|
144
|
-
className="border-catchup-blue border-2 px-2 py-1 rounded-catchup-xlarge cursor-pointer select-none"
|
|
145
|
-
onClick={() => handleSelectOption(option)}
|
|
146
|
-
onMouseDown={() => {
|
|
147
|
-
setSelectedOption(option);
|
|
148
|
-
setPasteOptionIndex(null);
|
|
149
|
-
}}
|
|
150
|
-
onTouchEnd={() => {
|
|
151
|
-
setSelectedOption(option);
|
|
152
|
-
setPasteOptionIndex(null);
|
|
153
|
-
}}
|
|
154
|
-
>
|
|
155
|
-
<ShowMaterialMediaByContentType
|
|
156
|
-
key={`${uniqueValue}-${index}`}
|
|
157
|
-
contentType={contentMap.type}
|
|
158
|
-
src={option}
|
|
159
|
-
canFullScreen={true}
|
|
160
|
-
/>
|
|
161
|
-
</div>
|
|
162
|
-
)
|
|
163
|
-
}
|
|
164
|
-
moveCardHandler={() => {
|
|
165
|
-
onChange(answer, pasteOptionIndex, selectedOption);
|
|
166
|
-
}}
|
|
167
|
-
/>
|
|
168
|
-
)
|
|
169
|
-
)}
|
|
170
|
-
</div>
|
|
171
|
-
<div className="w-full flex flex-row flex-wrap">
|
|
172
|
-
{Object.keys(answerMap).map((materialKey, index) => {
|
|
173
|
-
const learnerAnswerState = checkAnswerState(
|
|
174
|
-
JSON.parse(materialMap[materialKey]),
|
|
175
|
-
answerMap[materialKey]
|
|
176
|
-
);
|
|
177
|
-
return (
|
|
178
|
-
<div key={index} className="w-full md:w-1/2">
|
|
179
|
-
<div className="mx-2">
|
|
180
|
-
<DroppableItem
|
|
181
|
-
key={index}
|
|
182
|
-
item={{ index }}
|
|
183
|
-
type={"FILL_IN_THE_BLANKS"}
|
|
184
|
-
target={pasteOptionIndex}
|
|
185
|
-
setTarget={setPasteOptionIndex}
|
|
186
|
-
dropRef={drop}
|
|
187
|
-
component={
|
|
188
|
-
<div className="w-full flex flex-row my-2 gap-x-2">
|
|
189
|
-
<div className="my-auto">
|
|
190
|
-
<p className="text-xl">
|
|
191
|
-
{parseFloat(materialKey) + 1}.
|
|
192
|
-
</p>
|
|
193
|
-
</div>
|
|
194
|
-
<div className="flex-1">
|
|
195
|
-
{checkCanAnswerQuestion() ? (
|
|
196
|
-
contentMap.type === "TEXT" ? (
|
|
197
|
-
<div className="relative">
|
|
198
|
-
<div className="flex-1">
|
|
199
|
-
<div
|
|
200
|
-
className={`w-full min-h-[44px] border rounded-lg ${
|
|
201
|
-
answerMap[materialKey]
|
|
202
|
-
? "border-catchup-blue-400 px-2"
|
|
203
|
-
: "bg-catchup-gray-50 border-catchup-gray-200 border-dashed py-2 px-4"
|
|
204
|
-
}`}
|
|
205
|
-
onClick={() => {
|
|
206
|
-
if (answerMap[materialKey]) {
|
|
207
|
-
onChange(answer, materialKey, "");
|
|
208
|
-
}
|
|
209
|
-
}}
|
|
210
|
-
>
|
|
211
|
-
{answerMap[materialKey] ? (
|
|
212
|
-
<InputWithSpecialExpression
|
|
213
|
-
value={answerMap[materialKey]}
|
|
214
|
-
showSpecialOnly={false}
|
|
215
|
-
/>
|
|
216
|
-
) : (
|
|
217
|
-
<p className="text-gray-400 italic"></p>
|
|
218
|
-
)}
|
|
219
|
-
</div>
|
|
220
|
-
</div>
|
|
221
|
-
|
|
222
|
-
{learnerAnswerState === "CORRECT" ? (
|
|
223
|
-
<div className="absolute -top-[10px] right-4 bg-catchup-white">
|
|
224
|
-
<BaseImage
|
|
225
|
-
src="/icons/checkbox.webp"
|
|
226
|
-
alt="checkbox"
|
|
227
|
-
size="small"
|
|
228
|
-
/>
|
|
229
|
-
</div>
|
|
230
|
-
) : learnerAnswerState === "INCORRECT" ? (
|
|
231
|
-
<div className="absolute -top-[10px] right-4 bg-catchup-white">
|
|
232
|
-
<BaseImage
|
|
233
|
-
src="/icons/cross-red.webp"
|
|
234
|
-
alt="cross-red"
|
|
235
|
-
size="small"
|
|
236
|
-
/>
|
|
237
|
-
</div>
|
|
238
|
-
) : null}
|
|
239
|
-
</div>
|
|
240
|
-
) : answerMap[materialKey] === "" ? (
|
|
241
|
-
<div
|
|
242
|
-
className={`w-catchup-activity-media-box-item h-catchup-activity-media-box-item border rounded-catchup-xlarge border-dashed ${
|
|
243
|
-
learnerAnswerState === "CORRECT"
|
|
244
|
-
? "border-catchup-green"
|
|
245
|
-
: learnerAnswerState === "INCORRECT"
|
|
246
|
-
? "border-catchup-red"
|
|
247
|
-
: "border-catchup-blue"
|
|
248
|
-
}`}
|
|
249
|
-
>
|
|
250
|
-
<div className="h-full flex flex-col items-center justify-center px-4 py-2">
|
|
251
|
-
<span className="italic">
|
|
252
|
-
{i18n.t("please_drop_here")}
|
|
253
|
-
</span>
|
|
254
|
-
</div>
|
|
255
|
-
</div>
|
|
256
|
-
) : (
|
|
257
|
-
<div
|
|
258
|
-
className="flex-1 cursor-pointer"
|
|
259
|
-
onClick={() => {
|
|
260
|
-
onChange(answer, materialKey, "");
|
|
261
|
-
}}
|
|
262
|
-
>
|
|
263
|
-
<ShowMaterialMediaByContentType
|
|
264
|
-
key={`${uniqueValue}-${index}`}
|
|
265
|
-
contentType={contentMap.type}
|
|
266
|
-
src={answerMap[materialKey]}
|
|
267
|
-
canFullScreen={true}
|
|
268
|
-
/>
|
|
269
|
-
</div>
|
|
270
|
-
)
|
|
271
|
-
) : (
|
|
272
|
-
<p key={materialKey} className="text-xl">
|
|
273
|
-
{constructInputWithSpecialExpressionList(
|
|
274
|
-
answerMap[materialKey]
|
|
275
|
-
).map((inputPart, index) => (
|
|
276
|
-
<span
|
|
277
|
-
key={index}
|
|
278
|
-
className={`${
|
|
279
|
-
inputPart.isBold ? "font-bold" : ""
|
|
280
|
-
} ${inputPart.isUnderline ? "underline" : ""}`}
|
|
281
|
-
>
|
|
282
|
-
{inputPart.isEquation ? (
|
|
283
|
-
<span className="text-xl">
|
|
284
|
-
<InlineMath math={inputPart.value} />
|
|
285
|
-
</span>
|
|
286
|
-
) : (
|
|
287
|
-
inputPart.value
|
|
288
|
-
)}
|
|
289
|
-
</span>
|
|
290
|
-
))}
|
|
291
|
-
</p>
|
|
292
|
-
)}
|
|
293
|
-
</div>
|
|
294
|
-
</div>
|
|
295
|
-
}
|
|
296
|
-
/>
|
|
297
|
-
</div>
|
|
298
|
-
</div>
|
|
299
|
-
);
|
|
300
|
-
})}
|
|
301
|
-
</div>
|
|
302
|
-
</div>
|
|
303
|
-
);
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
export default FillInTheBlanksActivityMaterialContent;
|
|
@@ -1,231 +0,0 @@
|
|
|
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 <= 1024) {
|
|
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 null;
|
|
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
|
-
if (contentMap.type === "TEXT") {
|
|
84
|
-
return 0;
|
|
85
|
-
} else {
|
|
86
|
-
return 0;
|
|
87
|
-
}
|
|
88
|
-
} else if (index === 1) {
|
|
89
|
-
if (contentMap.type === "TEXT") {
|
|
90
|
-
return 65;
|
|
91
|
-
} else {
|
|
92
|
-
return 130;
|
|
93
|
-
}
|
|
94
|
-
} else if (index % 2 === 0) {
|
|
95
|
-
if (contentMap.type === "TEXT") {
|
|
96
|
-
return -65;
|
|
97
|
-
} else {
|
|
98
|
-
return -130;
|
|
99
|
-
}
|
|
100
|
-
} else if (index % 2 === 1) {
|
|
101
|
-
if (contentMap.type === "TEXT") {
|
|
102
|
-
return 0;
|
|
103
|
-
} else {
|
|
104
|
-
return 0;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return 0;
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
const answerMap = retrieveAnswerMap();
|
|
111
|
-
|
|
112
|
-
return (
|
|
113
|
-
<div className="flex flex-row flex-wrap">
|
|
114
|
-
{Object.keys(answerMap).map((materialKey, index) => {
|
|
115
|
-
const learnerAnswerState = checkAnswerState(
|
|
116
|
-
answerMap[materialKey] + "",
|
|
117
|
-
index + ""
|
|
118
|
-
);
|
|
119
|
-
return (
|
|
120
|
-
<div className="w-full lg:w-1/2" key={index}>
|
|
121
|
-
<div
|
|
122
|
-
className={`flex flex-row items-center my-4 mx-2`}
|
|
123
|
-
style={{
|
|
124
|
-
marginTop:
|
|
125
|
-
view === "PC"
|
|
126
|
-
? calculateMarginTop(parseFloat(materialKey))
|
|
127
|
-
: 0,
|
|
128
|
-
}}
|
|
129
|
-
>
|
|
130
|
-
<div className="mr-3">
|
|
131
|
-
<div className="h-catchup-activity-box-item w-catchup-activity-box-item flex flex-col items-center justify-center cursor-pointer transition-all duration-300 overflow-y-auto">
|
|
132
|
-
<div
|
|
133
|
-
className={`${
|
|
134
|
-
selectedKey === materialKey
|
|
135
|
-
? "border-2 border-catchup-light-gray"
|
|
136
|
-
: "border-2 border-catchup-blue"
|
|
137
|
-
} flex flex-col items-center justify-center transition-all duration-300 rounded-catchup-full w-[50px] h-[50px]`}
|
|
138
|
-
>
|
|
139
|
-
<p className="">{parseFloat(materialKey) + 1}</p>
|
|
140
|
-
</div>
|
|
141
|
-
</div>
|
|
142
|
-
</div>
|
|
143
|
-
|
|
144
|
-
<DraggableDroppableItem
|
|
145
|
-
key={index}
|
|
146
|
-
item={{ index: materialKey }}
|
|
147
|
-
type={"ORDERING"}
|
|
148
|
-
dropRef={drop}
|
|
149
|
-
component={
|
|
150
|
-
<div
|
|
151
|
-
className={`${
|
|
152
|
-
canDrop
|
|
153
|
-
? selectedKey !== materialKey
|
|
154
|
-
? selectedTargetKey === materialKey
|
|
155
|
-
? "bg-catchup-light-blue rounded-catchup-xlarge"
|
|
156
|
-
: "bg-catchup-light-blue rounded-catchup-xlarge opacity-40"
|
|
157
|
-
: ""
|
|
158
|
-
: ""
|
|
159
|
-
} ${
|
|
160
|
-
contentMap.type === "TEXT"
|
|
161
|
-
? "h-catchup-activity-text-box-item"
|
|
162
|
-
: "h-catchup-activity-media-box-item"
|
|
163
|
-
} flex flex-col items-center justify-center border-2 rounded-catchup-xlarge cursor-pointer p-3 ${
|
|
164
|
-
learnerAnswerState === "CORRECT"
|
|
165
|
-
? "border-catchup-green"
|
|
166
|
-
: learnerAnswerState === "INCORRECT"
|
|
167
|
-
? "border-catchup-red"
|
|
168
|
-
: "border-catchup-blue"
|
|
169
|
-
}`}
|
|
170
|
-
onMouseDown={() => {
|
|
171
|
-
if (checkCanAnswerQuestion()) {
|
|
172
|
-
setSelectedKey(materialKey);
|
|
173
|
-
}
|
|
174
|
-
}}
|
|
175
|
-
onTouchEnd={() => {
|
|
176
|
-
if (checkCanAnswerQuestion()) {
|
|
177
|
-
setSelectedKey(materialKey);
|
|
178
|
-
}
|
|
179
|
-
}}
|
|
180
|
-
>
|
|
181
|
-
{contentMap.type === "TEXT" ? (
|
|
182
|
-
<p className="text-xl whitespace-pre-wrap">
|
|
183
|
-
{constructInputWithSpecialExpressionList(
|
|
184
|
-
materialMap[answerMap[materialKey]]
|
|
185
|
-
).map((inputPart, index) => (
|
|
186
|
-
<span
|
|
187
|
-
key={index}
|
|
188
|
-
className={`${
|
|
189
|
-
inputPart.isBold ? "font-bold" : ""
|
|
190
|
-
} ${inputPart.isUnderline ? "underline" : ""}`}
|
|
191
|
-
>
|
|
192
|
-
{inputPart.isEquation ? (
|
|
193
|
-
<span className="text-xl">
|
|
194
|
-
<InlineMath math={inputPart.value} />
|
|
195
|
-
</span>
|
|
196
|
-
) : (
|
|
197
|
-
inputPart.value
|
|
198
|
-
)}
|
|
199
|
-
</span>
|
|
200
|
-
))}
|
|
201
|
-
</p>
|
|
202
|
-
) : (
|
|
203
|
-
<ShowMaterialMediaByContentType
|
|
204
|
-
key={`${uniqueValue}-${index}`}
|
|
205
|
-
contentType={contentMap.type}
|
|
206
|
-
src={materialMap[answerMap[materialKey]]}
|
|
207
|
-
canFullScreen={true}
|
|
208
|
-
/>
|
|
209
|
-
)}
|
|
210
|
-
</div>
|
|
211
|
-
}
|
|
212
|
-
target={selectedTargetKey}
|
|
213
|
-
setTarget={setSelectedTargetKey}
|
|
214
|
-
moveCardHandler={() => {
|
|
215
|
-
if (!selectedKey) return;
|
|
216
|
-
if (!selectedTargetKey) return;
|
|
217
|
-
handleOrderingActivityItemChange(
|
|
218
|
-
selectedKey,
|
|
219
|
-
selectedTargetKey
|
|
220
|
-
);
|
|
221
|
-
}}
|
|
222
|
-
/>
|
|
223
|
-
</div>
|
|
224
|
-
</div>
|
|
225
|
-
);
|
|
226
|
-
})}
|
|
227
|
-
</div>
|
|
228
|
-
);
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
export default OrderingActivityMaterialContent;
|