catchup-library-web 1.20.36 → 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 +448 -243
- package/dist/index.mjs +448 -243
- package/package.json +2 -6
- package/src/components/activities/material-contents/FillInTheBlanksActivityMaterialContent.tsx +62 -2
- package/src/components/activities/material-contents/GroupingActivityMaterialContent.tsx +74 -2
- package/src/components/activities/material-contents/MatchingActivityMaterialContent.tsx +74 -2
- 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/GroupingActivityMaterialContent2.tsx +0 -362
- package/src/components/activities/material-contents/MatchingActivityMaterialContent2.tsx +0 -350
- 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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "catchup-library-web",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.21.00",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -13,14 +13,11 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@types/lodash": "^4.17.20",
|
|
15
15
|
"lodash": "^4.17.21",
|
|
16
|
-
"mathlive": "^0.105.3"
|
|
17
|
-
"react-dnd-html5-backend": "^16.0.1",
|
|
18
|
-
"react-dnd-touch-backend": "^16.0.1"
|
|
16
|
+
"mathlive": "^0.105.3"
|
|
19
17
|
},
|
|
20
18
|
"peerDependencies": {
|
|
21
19
|
"i18next": ">=22.0.0",
|
|
22
20
|
"react": "^18.0.0",
|
|
23
|
-
"react-dnd": "^16.0.0",
|
|
24
21
|
"react-dom": "^18.0.0",
|
|
25
22
|
"react-i18next": ">=12.0.0",
|
|
26
23
|
"react-katex": "^3.0.0",
|
|
@@ -36,7 +33,6 @@
|
|
|
36
33
|
"@types/react-modal": "^3.16.3",
|
|
37
34
|
"i18next": "^24.2.2",
|
|
38
35
|
"react": "^18.3.0",
|
|
39
|
-
"react-dnd": "^16.0.1",
|
|
40
36
|
"react-dom": "^18.3.0",
|
|
41
37
|
"react-i18next": "^15.4.0",
|
|
42
38
|
"react-katex": "^3.0.1",
|
package/src/components/activities/material-contents/FillInTheBlanksActivityMaterialContent.tsx
CHANGED
|
@@ -28,6 +28,10 @@ const FillInTheBlanksActivityMaterialContent = ({
|
|
|
28
28
|
null
|
|
29
29
|
);
|
|
30
30
|
const dragElementRef = useRef<HTMLDivElement>(null);
|
|
31
|
+
const [mousePosition, setMousePosition] = useState<{ x: number; y: number }>({
|
|
32
|
+
x: 0,
|
|
33
|
+
y: 0,
|
|
34
|
+
});
|
|
31
35
|
const [touchPosition, setTouchPosition] = useState<{ x: number; y: number }>({
|
|
32
36
|
x: 0,
|
|
33
37
|
y: 0,
|
|
@@ -87,6 +91,24 @@ const FillInTheBlanksActivityMaterialContent = ({
|
|
|
87
91
|
e.preventDefault();
|
|
88
92
|
setDraggedOption(option);
|
|
89
93
|
setSelectedOption(null);
|
|
94
|
+
setMousePosition({ x: e.clientX, y: e.clientY });
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const handleMouseMove = (e: React.MouseEvent): void => {
|
|
98
|
+
if (!draggedOption) return;
|
|
99
|
+
|
|
100
|
+
setMousePosition({ x: e.clientX, y: e.clientY });
|
|
101
|
+
|
|
102
|
+
// Find the element under the mouse point
|
|
103
|
+
const elementUnder = document.elementFromPoint(e.clientX, e.clientY);
|
|
104
|
+
const dropZone = elementUnder?.closest("[data-drop-zone]");
|
|
105
|
+
|
|
106
|
+
if (dropZone) {
|
|
107
|
+
const dropIndex = dropZone.getAttribute("data-drop-zone");
|
|
108
|
+
setDropTargetIndex(dropIndex);
|
|
109
|
+
} else {
|
|
110
|
+
setDropTargetIndex(null);
|
|
111
|
+
}
|
|
90
112
|
};
|
|
91
113
|
|
|
92
114
|
const handleMouseUp = (): void => {
|
|
@@ -95,6 +117,7 @@ const FillInTheBlanksActivityMaterialContent = ({
|
|
|
95
117
|
}
|
|
96
118
|
setDraggedOption(null);
|
|
97
119
|
setDropTargetIndex(null);
|
|
120
|
+
setMousePosition({ x: 0, y: 0 });
|
|
98
121
|
};
|
|
99
122
|
|
|
100
123
|
// Touch drag handlers
|
|
@@ -138,6 +161,7 @@ const FillInTheBlanksActivityMaterialContent = ({
|
|
|
138
161
|
setDraggedOption(null);
|
|
139
162
|
setDropTargetIndex(null);
|
|
140
163
|
setDraggedElement(null);
|
|
164
|
+
setTouchPosition({ x: 0, y: 0 });
|
|
141
165
|
};
|
|
142
166
|
|
|
143
167
|
// Click/tap to select (for easier mobile interaction)
|
|
@@ -156,7 +180,11 @@ const FillInTheBlanksActivityMaterialContent = ({
|
|
|
156
180
|
const answerMap = retrieveAnswerMap();
|
|
157
181
|
|
|
158
182
|
return (
|
|
159
|
-
<div
|
|
183
|
+
<div
|
|
184
|
+
className="flex flex-row flex-wrap items-center"
|
|
185
|
+
onMouseMove={handleMouseMove}
|
|
186
|
+
onMouseUp={handleMouseUp}
|
|
187
|
+
>
|
|
160
188
|
<div className="hidden md:block">
|
|
161
189
|
<span className="font-semibold text-xl opacity-60">
|
|
162
190
|
{i18n.t("please_select_fill_in_the_blanks_text")}
|
|
@@ -166,6 +194,38 @@ const FillInTheBlanksActivityMaterialContent = ({
|
|
|
166
194
|
<DividerLine />
|
|
167
195
|
</div>
|
|
168
196
|
|
|
197
|
+
{/* Floating drag preview for mouse */}
|
|
198
|
+
{draggedOption && mousePosition.x > 0 && (
|
|
199
|
+
<div
|
|
200
|
+
className="fixed pointer-events-none z-50 opacity-80"
|
|
201
|
+
style={{
|
|
202
|
+
left: `${mousePosition.x}px`,
|
|
203
|
+
top: `${mousePosition.y}px`,
|
|
204
|
+
transform: "translate(-50%, -50%)",
|
|
205
|
+
}}
|
|
206
|
+
>
|
|
207
|
+
{contentMap.type === "TEXT" ? (
|
|
208
|
+
<div className="border-catchup-blue border-2 px-2 rounded-catchup-xlarge bg-white shadow-lg">
|
|
209
|
+
<p className="italic whitespace-pre-wrap">
|
|
210
|
+
<InputWithSpecialExpression
|
|
211
|
+
value={draggedOption}
|
|
212
|
+
showSpecialOnly={false}
|
|
213
|
+
/>
|
|
214
|
+
</p>
|
|
215
|
+
</div>
|
|
216
|
+
) : (
|
|
217
|
+
<div className="border-catchup-blue border-2 px-2 py-1 rounded-catchup-xlarge bg-white shadow-lg">
|
|
218
|
+
<ShowMaterialMediaByContentType
|
|
219
|
+
key={uniqueValue}
|
|
220
|
+
contentType={contentMap.type}
|
|
221
|
+
src={draggedOption}
|
|
222
|
+
canFullScreen={false}
|
|
223
|
+
/>
|
|
224
|
+
</div>
|
|
225
|
+
)}
|
|
226
|
+
</div>
|
|
227
|
+
)}
|
|
228
|
+
|
|
169
229
|
{/* Floating drag preview for touch */}
|
|
170
230
|
{draggedOption && touchPosition.x > 0 && (
|
|
171
231
|
<div
|
|
@@ -254,7 +314,7 @@ const FillInTheBlanksActivityMaterialContent = ({
|
|
|
254
314
|
)
|
|
255
315
|
)}
|
|
256
316
|
</div>
|
|
257
|
-
<div className="w-full flex flex-row flex-wrap"
|
|
317
|
+
<div className="w-full flex flex-row flex-wrap">
|
|
258
318
|
{Object.keys(answerMap).map((materialKey, index) => {
|
|
259
319
|
const learnerAnswerState = checkAnswerState(
|
|
260
320
|
JSON.parse(materialMap[materialKey]),
|
|
@@ -24,6 +24,10 @@ const GroupingActivityMaterialContent = ({
|
|
|
24
24
|
const [isShuffled, setIsShuffled] = useState(false);
|
|
25
25
|
const [shuffledMaterialList, setShuffledMaterialList] = useState<any[]>([]);
|
|
26
26
|
const dragElementRef = useRef<HTMLDivElement>(null);
|
|
27
|
+
const [mousePosition, setMousePosition] = useState<{ x: number; y: number }>({
|
|
28
|
+
x: 0,
|
|
29
|
+
y: 0,
|
|
30
|
+
});
|
|
27
31
|
const [touchPosition, setTouchPosition] = useState<{ x: number; y: number }>({
|
|
28
32
|
x: 0,
|
|
29
33
|
y: 0,
|
|
@@ -103,6 +107,24 @@ const GroupingActivityMaterialContent = ({
|
|
|
103
107
|
e.preventDefault();
|
|
104
108
|
setDraggedValue(materialValue);
|
|
105
109
|
setSelectedValue(null);
|
|
110
|
+
setMousePosition({ x: e.clientX, y: e.clientY });
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const handleMouseMove = (e: React.MouseEvent): void => {
|
|
114
|
+
if (!draggedValue) return;
|
|
115
|
+
|
|
116
|
+
setMousePosition({ x: e.clientX, y: e.clientY });
|
|
117
|
+
|
|
118
|
+
// Find the element under the mouse point
|
|
119
|
+
const elementUnder = document.elementFromPoint(e.clientX, e.clientY);
|
|
120
|
+
const dropZone = elementUnder?.closest("[data-grouping-drop-zone]");
|
|
121
|
+
|
|
122
|
+
if (dropZone) {
|
|
123
|
+
const dropKey = dropZone.getAttribute("data-grouping-drop-zone");
|
|
124
|
+
setDropTargetKey(dropKey);
|
|
125
|
+
} else {
|
|
126
|
+
setDropTargetKey(null);
|
|
127
|
+
}
|
|
106
128
|
};
|
|
107
129
|
|
|
108
130
|
const handleMouseUp = (): void => {
|
|
@@ -111,6 +133,7 @@ const GroupingActivityMaterialContent = ({
|
|
|
111
133
|
}
|
|
112
134
|
setDraggedValue(null);
|
|
113
135
|
setDropTargetKey(null);
|
|
136
|
+
setMousePosition({ x: 0, y: 0 });
|
|
114
137
|
};
|
|
115
138
|
|
|
116
139
|
// Touch drag handlers
|
|
@@ -155,6 +178,7 @@ const GroupingActivityMaterialContent = ({
|
|
|
155
178
|
setDraggedValue(null);
|
|
156
179
|
setDropTargetKey(null);
|
|
157
180
|
setDraggedElement(null);
|
|
181
|
+
setTouchPosition({ x: 0, y: 0 });
|
|
158
182
|
};
|
|
159
183
|
|
|
160
184
|
// Click/tap to select (for easier mobile interaction)
|
|
@@ -175,7 +199,55 @@ const GroupingActivityMaterialContent = ({
|
|
|
175
199
|
const filteredMaterialList = retrieveFilteredMaterialList(answerMap);
|
|
176
200
|
|
|
177
201
|
return (
|
|
178
|
-
<div onMouseUp={handleMouseUp}>
|
|
202
|
+
<div onMouseMove={handleMouseMove} onMouseUp={handleMouseUp}>
|
|
203
|
+
{/* Floating drag preview for mouse */}
|
|
204
|
+
{draggedValue && mousePosition.x > 0 && (
|
|
205
|
+
<div
|
|
206
|
+
className="fixed pointer-events-none z-50 opacity-80"
|
|
207
|
+
style={{
|
|
208
|
+
left: `${mousePosition.x}px`,
|
|
209
|
+
top: `${mousePosition.y}px`,
|
|
210
|
+
transform: "translate(-50%, -50%)",
|
|
211
|
+
}}
|
|
212
|
+
>
|
|
213
|
+
{contentMap.type === "TEXT" ? (
|
|
214
|
+
<div className="border-catchup-blue border-2 rounded-catchup-xlarge bg-white shadow-lg">
|
|
215
|
+
<div className="flex flex-col items-center justify-center m-2 min-w-[200px]">
|
|
216
|
+
<p className="text-xl text-center whitespace-pre-wrap">
|
|
217
|
+
{constructInputWithSpecialExpressionList(draggedValue).map(
|
|
218
|
+
(inputPart, index) => (
|
|
219
|
+
<span
|
|
220
|
+
key={index}
|
|
221
|
+
className={`${inputPart.isBold ? "font-bold" : ""} ${
|
|
222
|
+
inputPart.isUnderline ? "underline" : ""
|
|
223
|
+
}`}
|
|
224
|
+
>
|
|
225
|
+
{inputPart.isEquation ? (
|
|
226
|
+
<span className="text-2xl">
|
|
227
|
+
<InlineMath math={inputPart.value} />
|
|
228
|
+
</span>
|
|
229
|
+
) : (
|
|
230
|
+
inputPart.value
|
|
231
|
+
)}
|
|
232
|
+
</span>
|
|
233
|
+
)
|
|
234
|
+
)}
|
|
235
|
+
</p>
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
) : (
|
|
239
|
+
<div className="border-catchup-blue border-2 rounded-catchup-xlarge bg-white shadow-lg">
|
|
240
|
+
<ShowMaterialMediaByContentType
|
|
241
|
+
key={`${uniqueValue}-drag-mouse`}
|
|
242
|
+
contentType={contentMap.type}
|
|
243
|
+
src={draggedValue}
|
|
244
|
+
canFullScreen={false}
|
|
245
|
+
/>
|
|
246
|
+
</div>
|
|
247
|
+
)}
|
|
248
|
+
</div>
|
|
249
|
+
)}
|
|
250
|
+
|
|
179
251
|
{/* Floating drag preview for touch */}
|
|
180
252
|
{draggedValue && touchPosition.x > 0 && (
|
|
181
253
|
<div
|
|
@@ -214,7 +286,7 @@ const GroupingActivityMaterialContent = ({
|
|
|
214
286
|
) : (
|
|
215
287
|
<div className="border-catchup-blue border-2 rounded-catchup-xlarge bg-white shadow-lg">
|
|
216
288
|
<ShowMaterialMediaByContentType
|
|
217
|
-
key={`${uniqueValue}-drag`}
|
|
289
|
+
key={`${uniqueValue}-drag-touch`}
|
|
218
290
|
contentType={contentMap.type}
|
|
219
291
|
src={draggedValue}
|
|
220
292
|
canFullScreen={false}
|
|
@@ -24,6 +24,10 @@ const MatchingActivityMaterialContent = ({
|
|
|
24
24
|
const [isShuffled, setIsShuffled] = useState(false);
|
|
25
25
|
const [shuffledMaterialList, setShuffledMaterialList] = useState<any[]>([]);
|
|
26
26
|
const dragElementRef = useRef<HTMLDivElement>(null);
|
|
27
|
+
const [mousePosition, setMousePosition] = useState<{ x: number; y: number }>({
|
|
28
|
+
x: 0,
|
|
29
|
+
y: 0,
|
|
30
|
+
});
|
|
27
31
|
const [touchPosition, setTouchPosition] = useState<{ x: number; y: number }>({
|
|
28
32
|
x: 0,
|
|
29
33
|
y: 0,
|
|
@@ -95,6 +99,24 @@ const MatchingActivityMaterialContent = ({
|
|
|
95
99
|
e.preventDefault();
|
|
96
100
|
setDraggedValue(materialValue);
|
|
97
101
|
setSelectedValue(null);
|
|
102
|
+
setMousePosition({ x: e.clientX, y: e.clientY });
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const handleMouseMove = (e: React.MouseEvent): void => {
|
|
106
|
+
if (!draggedValue) return;
|
|
107
|
+
|
|
108
|
+
setMousePosition({ x: e.clientX, y: e.clientY });
|
|
109
|
+
|
|
110
|
+
// Find the element under the mouse point
|
|
111
|
+
const elementUnder = document.elementFromPoint(e.clientX, e.clientY);
|
|
112
|
+
const dropZone = elementUnder?.closest("[data-matching-drop-zone]");
|
|
113
|
+
|
|
114
|
+
if (dropZone) {
|
|
115
|
+
const dropKey = dropZone.getAttribute("data-matching-drop-zone");
|
|
116
|
+
setDropTargetKey(dropKey);
|
|
117
|
+
} else {
|
|
118
|
+
setDropTargetKey(null);
|
|
119
|
+
}
|
|
98
120
|
};
|
|
99
121
|
|
|
100
122
|
const handleMouseUp = (): void => {
|
|
@@ -103,6 +125,7 @@ const MatchingActivityMaterialContent = ({
|
|
|
103
125
|
}
|
|
104
126
|
setDraggedValue(null);
|
|
105
127
|
setDropTargetKey(null);
|
|
128
|
+
setMousePosition({ x: 0, y: 0 });
|
|
106
129
|
};
|
|
107
130
|
|
|
108
131
|
// Touch drag handlers
|
|
@@ -147,6 +170,7 @@ const MatchingActivityMaterialContent = ({
|
|
|
147
170
|
setDraggedValue(null);
|
|
148
171
|
setDropTargetKey(null);
|
|
149
172
|
setDraggedElement(null);
|
|
173
|
+
setTouchPosition({ x: 0, y: 0 });
|
|
150
174
|
};
|
|
151
175
|
|
|
152
176
|
// Click/tap to select (for easier mobile interaction)
|
|
@@ -167,7 +191,55 @@ const MatchingActivityMaterialContent = ({
|
|
|
167
191
|
const filteredMaterialList = retrieveFilteredMaterialList(answerMap);
|
|
168
192
|
|
|
169
193
|
return (
|
|
170
|
-
<div onMouseUp={handleMouseUp}>
|
|
194
|
+
<div onMouseMove={handleMouseMove} onMouseUp={handleMouseUp}>
|
|
195
|
+
{/* Floating drag preview for mouse */}
|
|
196
|
+
{draggedValue && mousePosition.x > 0 && (
|
|
197
|
+
<div
|
|
198
|
+
className="fixed pointer-events-none z-50 opacity-80"
|
|
199
|
+
style={{
|
|
200
|
+
left: `${mousePosition.x}px`,
|
|
201
|
+
top: `${mousePosition.y}px`,
|
|
202
|
+
transform: "translate(-50%, -50%)",
|
|
203
|
+
}}
|
|
204
|
+
>
|
|
205
|
+
{contentMap.type === "TEXT" ? (
|
|
206
|
+
<div className="border-catchup-blue border-2 rounded-catchup-xlarge bg-white shadow-lg">
|
|
207
|
+
<div className="flex flex-col items-center justify-center m-2 min-w-[200px] px-4">
|
|
208
|
+
<p className="text-lg whitespace-pre-wrap">
|
|
209
|
+
{constructInputWithSpecialExpressionList(draggedValue).map(
|
|
210
|
+
(inputPart, index) => (
|
|
211
|
+
<span
|
|
212
|
+
key={index}
|
|
213
|
+
className={`${inputPart.isBold ? "font-bold" : ""} ${
|
|
214
|
+
inputPart.isUnderline ? "underline" : ""
|
|
215
|
+
}`}
|
|
216
|
+
>
|
|
217
|
+
{inputPart.isEquation ? (
|
|
218
|
+
<span className="text-xl">
|
|
219
|
+
<InlineMath math={inputPart.value} />
|
|
220
|
+
</span>
|
|
221
|
+
) : (
|
|
222
|
+
inputPart.value
|
|
223
|
+
)}
|
|
224
|
+
</span>
|
|
225
|
+
)
|
|
226
|
+
)}
|
|
227
|
+
</p>
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
) : (
|
|
231
|
+
<div className="border-catchup-blue border-2 rounded-catchup-xlarge bg-white shadow-lg">
|
|
232
|
+
<ShowMaterialMediaByContentType
|
|
233
|
+
key={`${uniqueValue}-drag-mouse`}
|
|
234
|
+
contentType={contentMap.type}
|
|
235
|
+
src={draggedValue}
|
|
236
|
+
canFullScreen={false}
|
|
237
|
+
/>
|
|
238
|
+
</div>
|
|
239
|
+
)}
|
|
240
|
+
</div>
|
|
241
|
+
)}
|
|
242
|
+
|
|
171
243
|
{/* Floating drag preview for touch */}
|
|
172
244
|
{draggedValue && touchPosition.x > 0 && (
|
|
173
245
|
<div
|
|
@@ -206,7 +278,7 @@ const MatchingActivityMaterialContent = ({
|
|
|
206
278
|
) : (
|
|
207
279
|
<div className="border-catchup-blue border-2 rounded-catchup-xlarge bg-white shadow-lg">
|
|
208
280
|
<ShowMaterialMediaByContentType
|
|
209
|
-
key={`${uniqueValue}-drag`}
|
|
281
|
+
key={`${uniqueValue}-drag-touch`}
|
|
210
282
|
contentType={contentMap.type}
|
|
211
283
|
src={draggedValue}
|
|
212
284
|
canFullScreen={false}
|
|
@@ -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}
|