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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "catchup-library-web",
3
- "version": "1.20.36",
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",
@@ -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 className="flex flex-row flex-wrap items-center">
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" onMouseUp={handleMouseUp}>
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 className="flex flex-row flex-wrap" onMouseUp={handleMouseUp}>
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}