catchup-library-web 1.21.0 → 1.21.1
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
CHANGED
|
@@ -5080,7 +5080,7 @@ var FillInTheBlanksActivityMaterialContent = ({
|
|
|
5080
5080
|
}
|
|
5081
5081
|
),
|
|
5082
5082
|
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "w-full flex flex-row flex-wrap gap-x-2 gap-y-2 my-2", children: shuffleOptionList.map(
|
|
5083
|
-
(option, index) => checkAnswerProvided(answerMap, option) ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5083
|
+
(option, index) => checkAnswerProvided(answerMap, option) ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5084
5084
|
ShowMaterialMediaByContentType_default,
|
|
5085
5085
|
{
|
|
5086
5086
|
contentType: contentMap.type,
|
|
@@ -5088,7 +5088,7 @@ var FillInTheBlanksActivityMaterialContent = ({
|
|
|
5088
5088
|
canFullScreen: true
|
|
5089
5089
|
},
|
|
5090
5090
|
`${uniqueValue}-${index}`
|
|
5091
|
-
)
|
|
5091
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5092
5092
|
"div",
|
|
5093
5093
|
{
|
|
5094
5094
|
ref: draggedOption === option ? dragElementRef : null,
|
|
@@ -5747,21 +5747,16 @@ var MatchingActivityMaterialContent = ({
|
|
|
5747
5747
|
const [selectedValue, setSelectedValue] = (0, import_react21.useState)(null);
|
|
5748
5748
|
const [draggedValue, setDraggedValue] = (0, import_react21.useState)(null);
|
|
5749
5749
|
const [dropTargetKey, setDropTargetKey] = (0, import_react21.useState)(null);
|
|
5750
|
-
const [draggedElement, setDraggedElement] = (0, import_react21.useState)(
|
|
5751
|
-
null
|
|
5752
|
-
);
|
|
5750
|
+
const [draggedElement, setDraggedElement] = (0, import_react21.useState)(null);
|
|
5753
5751
|
const [isShuffled, setIsShuffled] = (0, import_react21.useState)(false);
|
|
5754
5752
|
const [shuffledMaterialList, setShuffledMaterialList] = (0, import_react21.useState)([]);
|
|
5755
5753
|
const dragElementRef = (0, import_react21.useRef)(null);
|
|
5756
|
-
const [mousePosition, setMousePosition] = (0, import_react21.useState)({
|
|
5757
|
-
|
|
5758
|
-
y: 0
|
|
5759
|
-
});
|
|
5760
|
-
const [touchPosition, setTouchPosition] = (0, import_react21.useState)({
|
|
5761
|
-
x: 0,
|
|
5762
|
-
y: 0
|
|
5763
|
-
});
|
|
5754
|
+
const [mousePosition, setMousePosition] = (0, import_react21.useState)({ x: 0, y: 0 });
|
|
5755
|
+
const [touchPosition, setTouchPosition] = (0, import_react21.useState)({ x: 0, y: 0 });
|
|
5764
5756
|
const itemsRef = (0, import_react21.useRef)(null);
|
|
5757
|
+
const scrollIntervalRef = (0, import_react21.useRef)(null);
|
|
5758
|
+
const SCROLL_THRESHOLD = 100;
|
|
5759
|
+
const SCROLL_SPEED = 10;
|
|
5765
5760
|
(0, import_react21.useEffect)(() => {
|
|
5766
5761
|
const shuffleArray2 = (array) => {
|
|
5767
5762
|
if (!isShuffled) {
|
|
@@ -5783,10 +5778,44 @@ var MatchingActivityMaterialContent = ({
|
|
|
5783
5778
|
}, [materialMap, isShuffled]);
|
|
5784
5779
|
(0, import_react21.useEffect)(() => {
|
|
5785
5780
|
if (!showCorrectAnswer) return;
|
|
5786
|
-
answer.data.find(
|
|
5787
|
-
(answerData) => answerData.type === "MATCHING"
|
|
5788
|
-
).answerMap = materialMap;
|
|
5781
|
+
answer.data.find((answerData) => answerData.type === "MATCHING").answerMap = materialMap;
|
|
5789
5782
|
}, [showCorrectAnswer, answer, materialMap]);
|
|
5783
|
+
(0, import_react21.useEffect)(() => {
|
|
5784
|
+
if (!draggedValue || mousePosition.y === 0) return;
|
|
5785
|
+
const handleAutoScroll = () => {
|
|
5786
|
+
const viewportHeight = window.innerHeight;
|
|
5787
|
+
const scrollY = window.scrollY;
|
|
5788
|
+
if (mousePosition.y < SCROLL_THRESHOLD + scrollY) {
|
|
5789
|
+
window.scrollBy(0, -SCROLL_SPEED);
|
|
5790
|
+
} else if (mousePosition.y > viewportHeight + scrollY - SCROLL_THRESHOLD) {
|
|
5791
|
+
window.scrollBy(0, SCROLL_SPEED);
|
|
5792
|
+
}
|
|
5793
|
+
};
|
|
5794
|
+
scrollIntervalRef.current = setInterval(handleAutoScroll, 16);
|
|
5795
|
+
return () => {
|
|
5796
|
+
if (scrollIntervalRef.current) {
|
|
5797
|
+
clearInterval(scrollIntervalRef.current);
|
|
5798
|
+
}
|
|
5799
|
+
};
|
|
5800
|
+
}, [draggedValue, mousePosition.y]);
|
|
5801
|
+
(0, import_react21.useEffect)(() => {
|
|
5802
|
+
if (!draggedValue || touchPosition.y === 0) return;
|
|
5803
|
+
const handleAutoScroll = () => {
|
|
5804
|
+
const viewportHeight = window.innerHeight;
|
|
5805
|
+
const scrollY = window.scrollY;
|
|
5806
|
+
if (touchPosition.y < SCROLL_THRESHOLD + scrollY) {
|
|
5807
|
+
window.scrollBy(0, -SCROLL_SPEED);
|
|
5808
|
+
} else if (touchPosition.y > viewportHeight + scrollY - SCROLL_THRESHOLD) {
|
|
5809
|
+
window.scrollBy(0, SCROLL_SPEED);
|
|
5810
|
+
}
|
|
5811
|
+
};
|
|
5812
|
+
scrollIntervalRef.current = setInterval(handleAutoScroll, 16);
|
|
5813
|
+
return () => {
|
|
5814
|
+
if (scrollIntervalRef.current) {
|
|
5815
|
+
clearInterval(scrollIntervalRef.current);
|
|
5816
|
+
}
|
|
5817
|
+
};
|
|
5818
|
+
}, [draggedValue, touchPosition.y]);
|
|
5790
5819
|
const retrieveAnswerMap = () => {
|
|
5791
5820
|
const foundIndex = answer.data.findIndex(
|
|
5792
5821
|
(answerData) => answerData.type === "MATCHING"
|
|
@@ -5989,7 +6018,7 @@ var MatchingActivityMaterialContent = ({
|
|
|
5989
6018
|
),
|
|
5990
6019
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "flex-shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(DividerLine_default, {}) })
|
|
5991
6020
|
] }) : null,
|
|
5992
|
-
|
|
6021
|
+
Object.keys(answerMap).map((answerMapKey, index) => {
|
|
5993
6022
|
const learnerAnswerState = checkAnswerState(
|
|
5994
6023
|
materialMap[answerMapKey],
|
|
5995
6024
|
answerMap[answerMapKey]
|
|
@@ -5999,16 +6028,16 @@ var MatchingActivityMaterialContent = ({
|
|
|
5999
6028
|
"div",
|
|
6000
6029
|
{
|
|
6001
6030
|
className: `${contentMap.type === "TEXT" ? "h-catchup-activity-text-box-item" : "h-catchup-activity-media-box-item"} flex flex-col items-center justify-center border-2 rounded-catchup-xlarge transition-all duration-300 my-3 ${learnerAnswerState === "EMPTY" ? "border-catchup-blue" : learnerAnswerState === "CORRECT" ? "border-catchup-green" : learnerAnswerState === "INCORRECT" ? "border-catchup-red" : "border-catchup-blue"}`,
|
|
6002
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "flex flex-col items-center justify-center transition-all duration-300 px-4 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { className: "text-lg whitespace-pre-wrap", children: constructInputWithSpecialExpressionList(
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
|
|
6010
|
-
|
|
6011
|
-
)
|
|
6031
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "flex flex-col items-center justify-center transition-all duration-300 px-4 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { className: "text-lg whitespace-pre-wrap", children: constructInputWithSpecialExpressionList(answerMapKey).map(
|
|
6032
|
+
(inputPart, index2) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
6033
|
+
"span",
|
|
6034
|
+
{
|
|
6035
|
+
className: `${inputPart.isBold ? "font-bold" : ""} ${inputPart.isUnderline ? "underline" : ""}`,
|
|
6036
|
+
children: inputPart.isEquation ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-xl", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_react_katex6.InlineMath, { math: inputPart.value }) }) : inputPart.value
|
|
6037
|
+
},
|
|
6038
|
+
index2
|
|
6039
|
+
)
|
|
6040
|
+
) }) })
|
|
6012
6041
|
}
|
|
6013
6042
|
) }),
|
|
6014
6043
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "mx-4 w-[2px] bg-catchup-lighter-gray" }),
|
|
@@ -6054,7 +6083,7 @@ var MatchingActivityMaterialContent = ({
|
|
|
6054
6083
|
}
|
|
6055
6084
|
) })
|
|
6056
6085
|
] }, index);
|
|
6057
|
-
})
|
|
6086
|
+
})
|
|
6058
6087
|
] });
|
|
6059
6088
|
};
|
|
6060
6089
|
var MatchingActivityMaterialContent_default = MatchingActivityMaterialContent;
|
package/dist/index.mjs
CHANGED
|
@@ -4864,7 +4864,7 @@ var FillInTheBlanksActivityMaterialContent = ({
|
|
|
4864
4864
|
}
|
|
4865
4865
|
),
|
|
4866
4866
|
/* @__PURE__ */ jsx28("div", { className: "w-full flex flex-row flex-wrap gap-x-2 gap-y-2 my-2", children: shuffleOptionList.map(
|
|
4867
|
-
(option, index) => checkAnswerProvided(answerMap, option) ? /* @__PURE__ */ jsx28(
|
|
4867
|
+
(option, index) => checkAnswerProvided(answerMap, option) ? /* @__PURE__ */ jsx28(
|
|
4868
4868
|
ShowMaterialMediaByContentType_default,
|
|
4869
4869
|
{
|
|
4870
4870
|
contentType: contentMap.type,
|
|
@@ -4872,7 +4872,7 @@ var FillInTheBlanksActivityMaterialContent = ({
|
|
|
4872
4872
|
canFullScreen: true
|
|
4873
4873
|
},
|
|
4874
4874
|
`${uniqueValue}-${index}`
|
|
4875
|
-
)
|
|
4875
|
+
) : /* @__PURE__ */ jsx28(
|
|
4876
4876
|
"div",
|
|
4877
4877
|
{
|
|
4878
4878
|
ref: draggedOption === option ? dragElementRef : null,
|
|
@@ -5531,21 +5531,16 @@ var MatchingActivityMaterialContent = ({
|
|
|
5531
5531
|
const [selectedValue, setSelectedValue] = useState20(null);
|
|
5532
5532
|
const [draggedValue, setDraggedValue] = useState20(null);
|
|
5533
5533
|
const [dropTargetKey, setDropTargetKey] = useState20(null);
|
|
5534
|
-
const [draggedElement, setDraggedElement] = useState20(
|
|
5535
|
-
null
|
|
5536
|
-
);
|
|
5534
|
+
const [draggedElement, setDraggedElement] = useState20(null);
|
|
5537
5535
|
const [isShuffled, setIsShuffled] = useState20(false);
|
|
5538
5536
|
const [shuffledMaterialList, setShuffledMaterialList] = useState20([]);
|
|
5539
5537
|
const dragElementRef = useRef6(null);
|
|
5540
|
-
const [mousePosition, setMousePosition] = useState20({
|
|
5541
|
-
|
|
5542
|
-
y: 0
|
|
5543
|
-
});
|
|
5544
|
-
const [touchPosition, setTouchPosition] = useState20({
|
|
5545
|
-
x: 0,
|
|
5546
|
-
y: 0
|
|
5547
|
-
});
|
|
5538
|
+
const [mousePosition, setMousePosition] = useState20({ x: 0, y: 0 });
|
|
5539
|
+
const [touchPosition, setTouchPosition] = useState20({ x: 0, y: 0 });
|
|
5548
5540
|
const itemsRef = useRef6(null);
|
|
5541
|
+
const scrollIntervalRef = useRef6(null);
|
|
5542
|
+
const SCROLL_THRESHOLD = 100;
|
|
5543
|
+
const SCROLL_SPEED = 10;
|
|
5549
5544
|
useEffect11(() => {
|
|
5550
5545
|
const shuffleArray2 = (array) => {
|
|
5551
5546
|
if (!isShuffled) {
|
|
@@ -5567,10 +5562,44 @@ var MatchingActivityMaterialContent = ({
|
|
|
5567
5562
|
}, [materialMap, isShuffled]);
|
|
5568
5563
|
useEffect11(() => {
|
|
5569
5564
|
if (!showCorrectAnswer) return;
|
|
5570
|
-
answer.data.find(
|
|
5571
|
-
(answerData) => answerData.type === "MATCHING"
|
|
5572
|
-
).answerMap = materialMap;
|
|
5565
|
+
answer.data.find((answerData) => answerData.type === "MATCHING").answerMap = materialMap;
|
|
5573
5566
|
}, [showCorrectAnswer, answer, materialMap]);
|
|
5567
|
+
useEffect11(() => {
|
|
5568
|
+
if (!draggedValue || mousePosition.y === 0) return;
|
|
5569
|
+
const handleAutoScroll = () => {
|
|
5570
|
+
const viewportHeight = window.innerHeight;
|
|
5571
|
+
const scrollY = window.scrollY;
|
|
5572
|
+
if (mousePosition.y < SCROLL_THRESHOLD + scrollY) {
|
|
5573
|
+
window.scrollBy(0, -SCROLL_SPEED);
|
|
5574
|
+
} else if (mousePosition.y > viewportHeight + scrollY - SCROLL_THRESHOLD) {
|
|
5575
|
+
window.scrollBy(0, SCROLL_SPEED);
|
|
5576
|
+
}
|
|
5577
|
+
};
|
|
5578
|
+
scrollIntervalRef.current = setInterval(handleAutoScroll, 16);
|
|
5579
|
+
return () => {
|
|
5580
|
+
if (scrollIntervalRef.current) {
|
|
5581
|
+
clearInterval(scrollIntervalRef.current);
|
|
5582
|
+
}
|
|
5583
|
+
};
|
|
5584
|
+
}, [draggedValue, mousePosition.y]);
|
|
5585
|
+
useEffect11(() => {
|
|
5586
|
+
if (!draggedValue || touchPosition.y === 0) return;
|
|
5587
|
+
const handleAutoScroll = () => {
|
|
5588
|
+
const viewportHeight = window.innerHeight;
|
|
5589
|
+
const scrollY = window.scrollY;
|
|
5590
|
+
if (touchPosition.y < SCROLL_THRESHOLD + scrollY) {
|
|
5591
|
+
window.scrollBy(0, -SCROLL_SPEED);
|
|
5592
|
+
} else if (touchPosition.y > viewportHeight + scrollY - SCROLL_THRESHOLD) {
|
|
5593
|
+
window.scrollBy(0, SCROLL_SPEED);
|
|
5594
|
+
}
|
|
5595
|
+
};
|
|
5596
|
+
scrollIntervalRef.current = setInterval(handleAutoScroll, 16);
|
|
5597
|
+
return () => {
|
|
5598
|
+
if (scrollIntervalRef.current) {
|
|
5599
|
+
clearInterval(scrollIntervalRef.current);
|
|
5600
|
+
}
|
|
5601
|
+
};
|
|
5602
|
+
}, [draggedValue, touchPosition.y]);
|
|
5574
5603
|
const retrieveAnswerMap = () => {
|
|
5575
5604
|
const foundIndex = answer.data.findIndex(
|
|
5576
5605
|
(answerData) => answerData.type === "MATCHING"
|
|
@@ -5773,7 +5802,7 @@ var MatchingActivityMaterialContent = ({
|
|
|
5773
5802
|
),
|
|
5774
5803
|
/* @__PURE__ */ jsx32("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx32(DividerLine_default, {}) })
|
|
5775
5804
|
] }) : null,
|
|
5776
|
-
|
|
5805
|
+
Object.keys(answerMap).map((answerMapKey, index) => {
|
|
5777
5806
|
const learnerAnswerState = checkAnswerState(
|
|
5778
5807
|
materialMap[answerMapKey],
|
|
5779
5808
|
answerMap[answerMapKey]
|
|
@@ -5783,16 +5812,16 @@ var MatchingActivityMaterialContent = ({
|
|
|
5783
5812
|
"div",
|
|
5784
5813
|
{
|
|
5785
5814
|
className: `${contentMap.type === "TEXT" ? "h-catchup-activity-text-box-item" : "h-catchup-activity-media-box-item"} flex flex-col items-center justify-center border-2 rounded-catchup-xlarge transition-all duration-300 my-3 ${learnerAnswerState === "EMPTY" ? "border-catchup-blue" : learnerAnswerState === "CORRECT" ? "border-catchup-green" : learnerAnswerState === "INCORRECT" ? "border-catchup-red" : "border-catchup-blue"}`,
|
|
5786
|
-
children: /* @__PURE__ */ jsx32("div", { className: "flex flex-col items-center justify-center transition-all duration-300 px-4 text-center", children: /* @__PURE__ */ jsx32("p", { className: "text-lg whitespace-pre-wrap", children: constructInputWithSpecialExpressionList(
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
)
|
|
5815
|
+
children: /* @__PURE__ */ jsx32("div", { className: "flex flex-col items-center justify-center transition-all duration-300 px-4 text-center", children: /* @__PURE__ */ jsx32("p", { className: "text-lg whitespace-pre-wrap", children: constructInputWithSpecialExpressionList(answerMapKey).map(
|
|
5816
|
+
(inputPart, index2) => /* @__PURE__ */ jsx32(
|
|
5817
|
+
"span",
|
|
5818
|
+
{
|
|
5819
|
+
className: `${inputPart.isBold ? "font-bold" : ""} ${inputPart.isUnderline ? "underline" : ""}`,
|
|
5820
|
+
children: inputPart.isEquation ? /* @__PURE__ */ jsx32("span", { className: "text-xl", children: /* @__PURE__ */ jsx32(InlineMath6, { math: inputPart.value }) }) : inputPart.value
|
|
5821
|
+
},
|
|
5822
|
+
index2
|
|
5823
|
+
)
|
|
5824
|
+
) }) })
|
|
5796
5825
|
}
|
|
5797
5826
|
) }),
|
|
5798
5827
|
/* @__PURE__ */ jsx32("div", { className: "mx-4 w-[2px] bg-catchup-lighter-gray" }),
|
|
@@ -5838,7 +5867,7 @@ var MatchingActivityMaterialContent = ({
|
|
|
5838
5867
|
}
|
|
5839
5868
|
) })
|
|
5840
5869
|
] }, index);
|
|
5841
|
-
})
|
|
5870
|
+
})
|
|
5842
5871
|
] });
|
|
5843
5872
|
};
|
|
5844
5873
|
var MatchingActivityMaterialContent_default = MatchingActivityMaterialContent;
|
package/package.json
CHANGED
package/src/components/activities/material-contents/FillInTheBlanksActivityMaterialContent.tsx
CHANGED
|
@@ -261,14 +261,12 @@ const FillInTheBlanksActivityMaterialContent = ({
|
|
|
261
261
|
<div className="w-full flex flex-row flex-wrap gap-x-2 gap-y-2 my-2">
|
|
262
262
|
{shuffleOptionList.map((option, index) =>
|
|
263
263
|
checkAnswerProvided(answerMap, option) ? (
|
|
264
|
-
<
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
/>
|
|
271
|
-
</div>
|
|
264
|
+
<ShowMaterialMediaByContentType
|
|
265
|
+
key={`${uniqueValue}-${index}`}
|
|
266
|
+
contentType={contentMap.type}
|
|
267
|
+
src={option}
|
|
268
|
+
canFullScreen={true}
|
|
269
|
+
/>
|
|
272
270
|
) : (
|
|
273
271
|
<div
|
|
274
272
|
key={index}
|
|
@@ -2,7 +2,6 @@ import { useEffect, useRef, useState } from "react";
|
|
|
2
2
|
import ShowMaterialMediaByContentType from "./ShowMaterialMediaByContentType";
|
|
3
3
|
import { InlineMath } from "react-katex";
|
|
4
4
|
import { constructInputWithSpecialExpressionList } from "../../../utilization/CatchtivityUtilization";
|
|
5
|
-
import { IMatchingActivityMaterialProps } from "../../../properties/ActivityProperties";
|
|
6
5
|
import DividerLine from "../../dividers/DividerLine";
|
|
7
6
|
|
|
8
7
|
const MatchingActivityMaterialContent = ({
|
|
@@ -14,28 +13,25 @@ const MatchingActivityMaterialContent = ({
|
|
|
14
13
|
onChange,
|
|
15
14
|
isPreview,
|
|
16
15
|
showCorrectAnswer,
|
|
17
|
-
}
|
|
18
|
-
const [selectedValue, setSelectedValue] = useState
|
|
19
|
-
const [draggedValue, setDraggedValue] = useState
|
|
20
|
-
const [dropTargetKey, setDropTargetKey] = useState
|
|
21
|
-
const [draggedElement, setDraggedElement] = useState
|
|
22
|
-
null
|
|
23
|
-
);
|
|
16
|
+
}) => {
|
|
17
|
+
const [selectedValue, setSelectedValue] = useState(null);
|
|
18
|
+
const [draggedValue, setDraggedValue] = useState(null);
|
|
19
|
+
const [dropTargetKey, setDropTargetKey] = useState(null);
|
|
20
|
+
const [draggedElement, setDraggedElement] = useState(null);
|
|
24
21
|
const [isShuffled, setIsShuffled] = useState(false);
|
|
25
|
-
const [shuffledMaterialList, setShuffledMaterialList] = useState
|
|
26
|
-
const dragElementRef = useRef
|
|
27
|
-
const [mousePosition, setMousePosition] = useState
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const itemsRef = useRef<HTMLDivElement>(null);
|
|
22
|
+
const [shuffledMaterialList, setShuffledMaterialList] = useState([]);
|
|
23
|
+
const dragElementRef = useRef(null);
|
|
24
|
+
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
|
|
25
|
+
const [touchPosition, setTouchPosition] = useState({ x: 0, y: 0 });
|
|
26
|
+
const itemsRef = useRef(null);
|
|
27
|
+
const scrollIntervalRef = useRef(null);
|
|
28
|
+
|
|
29
|
+
// Auto-scroll configuration
|
|
30
|
+
const SCROLL_THRESHOLD = 100; // Distance from edge to trigger scroll
|
|
31
|
+
const SCROLL_SPEED = 10; // Pixels to scroll per interval
|
|
36
32
|
|
|
37
33
|
useEffect(() => {
|
|
38
|
-
const shuffleArray = (array
|
|
34
|
+
const shuffleArray = (array) => {
|
|
39
35
|
if (!isShuffled) {
|
|
40
36
|
const copyArray = JSON.parse(JSON.stringify(array));
|
|
41
37
|
for (let i = copyArray.length - 1; i > 0; i--) {
|
|
@@ -47,7 +43,7 @@ const MatchingActivityMaterialContent = ({
|
|
|
47
43
|
}
|
|
48
44
|
return array;
|
|
49
45
|
};
|
|
50
|
-
const materialList
|
|
46
|
+
const materialList = [];
|
|
51
47
|
Object.keys(materialMap).forEach((materialKey) => {
|
|
52
48
|
materialList.push(materialMap[materialKey]);
|
|
53
49
|
});
|
|
@@ -56,32 +52,84 @@ const MatchingActivityMaterialContent = ({
|
|
|
56
52
|
|
|
57
53
|
useEffect(() => {
|
|
58
54
|
if (!showCorrectAnswer) return;
|
|
59
|
-
answer.data.find(
|
|
60
|
-
|
|
61
|
-
).answerMap = materialMap;
|
|
55
|
+
answer.data.find((answerData) => answerData.type === "MATCHING").answerMap =
|
|
56
|
+
materialMap;
|
|
62
57
|
}, [showCorrectAnswer, answer, materialMap]);
|
|
63
58
|
|
|
59
|
+
// Auto-scroll effect for mouse drag
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (!draggedValue || mousePosition.y === 0) return;
|
|
62
|
+
|
|
63
|
+
const handleAutoScroll = () => {
|
|
64
|
+
const viewportHeight = window.innerHeight;
|
|
65
|
+
const scrollY = window.scrollY;
|
|
66
|
+
|
|
67
|
+
// Check if near top edge
|
|
68
|
+
if (mousePosition.y < SCROLL_THRESHOLD + scrollY) {
|
|
69
|
+
window.scrollBy(0, -SCROLL_SPEED);
|
|
70
|
+
}
|
|
71
|
+
// Check if near bottom edge
|
|
72
|
+
else if (mousePosition.y > viewportHeight + scrollY - SCROLL_THRESHOLD) {
|
|
73
|
+
window.scrollBy(0, SCROLL_SPEED);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
scrollIntervalRef.current = setInterval(handleAutoScroll, 16); // ~60fps
|
|
78
|
+
|
|
79
|
+
return () => {
|
|
80
|
+
if (scrollIntervalRef.current) {
|
|
81
|
+
clearInterval(scrollIntervalRef.current);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}, [draggedValue, mousePosition.y]);
|
|
85
|
+
|
|
86
|
+
// Auto-scroll effect for touch drag
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
if (!draggedValue || touchPosition.y === 0) return;
|
|
89
|
+
|
|
90
|
+
const handleAutoScroll = () => {
|
|
91
|
+
const viewportHeight = window.innerHeight;
|
|
92
|
+
const scrollY = window.scrollY;
|
|
93
|
+
|
|
94
|
+
// Check if near top edge
|
|
95
|
+
if (touchPosition.y < SCROLL_THRESHOLD + scrollY) {
|
|
96
|
+
window.scrollBy(0, -SCROLL_SPEED);
|
|
97
|
+
}
|
|
98
|
+
// Check if near bottom edge
|
|
99
|
+
else if (touchPosition.y > viewportHeight + scrollY - SCROLL_THRESHOLD) {
|
|
100
|
+
window.scrollBy(0, SCROLL_SPEED);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
scrollIntervalRef.current = setInterval(handleAutoScroll, 16); // ~60fps
|
|
105
|
+
|
|
106
|
+
return () => {
|
|
107
|
+
if (scrollIntervalRef.current) {
|
|
108
|
+
clearInterval(scrollIntervalRef.current);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}, [draggedValue, touchPosition.y]);
|
|
112
|
+
|
|
64
113
|
const retrieveAnswerMap = () => {
|
|
65
114
|
const foundIndex = answer.data.findIndex(
|
|
66
|
-
(answerData
|
|
115
|
+
(answerData) => answerData.type === "MATCHING"
|
|
67
116
|
);
|
|
68
117
|
return answer.data[foundIndex].answerMap;
|
|
69
118
|
};
|
|
70
119
|
|
|
71
|
-
const retrieveFilteredMaterialList = (answerMap
|
|
72
|
-
const selectedValueList
|
|
120
|
+
const retrieveFilteredMaterialList = (answerMap) => {
|
|
121
|
+
const selectedValueList = [];
|
|
73
122
|
Object.keys(answerMap).forEach((key) => {
|
|
74
123
|
selectedValueList.push(answerMap[key]);
|
|
75
124
|
});
|
|
76
125
|
|
|
77
126
|
return shuffledMaterialList.filter(
|
|
78
127
|
(material) =>
|
|
79
|
-
selectedValueList.findIndex((value
|
|
80
|
-
-1
|
|
128
|
+
selectedValueList.findIndex((value) => material === value) === -1
|
|
81
129
|
);
|
|
82
130
|
};
|
|
83
131
|
|
|
84
|
-
const checkAnswerState = (correctAnswer
|
|
132
|
+
const checkAnswerState = (correctAnswer, learnerAnswer) => {
|
|
85
133
|
if (!isPreview) return null;
|
|
86
134
|
if (!learnerAnswer) return "EMPTY";
|
|
87
135
|
if (correctAnswer === learnerAnswer) {
|
|
@@ -91,10 +139,7 @@ const MatchingActivityMaterialContent = ({
|
|
|
91
139
|
};
|
|
92
140
|
|
|
93
141
|
// Mouse drag handlers
|
|
94
|
-
const handleMouseDown = (
|
|
95
|
-
e: React.MouseEvent,
|
|
96
|
-
materialValue: string
|
|
97
|
-
): void => {
|
|
142
|
+
const handleMouseDown = (e, materialValue) => {
|
|
98
143
|
if (!checkCanAnswerQuestion()) return;
|
|
99
144
|
e.preventDefault();
|
|
100
145
|
setDraggedValue(materialValue);
|
|
@@ -102,7 +147,7 @@ const MatchingActivityMaterialContent = ({
|
|
|
102
147
|
setMousePosition({ x: e.clientX, y: e.clientY });
|
|
103
148
|
};
|
|
104
149
|
|
|
105
|
-
const handleMouseMove = (e
|
|
150
|
+
const handleMouseMove = (e) => {
|
|
106
151
|
if (!draggedValue) return;
|
|
107
152
|
|
|
108
153
|
setMousePosition({ x: e.clientX, y: e.clientY });
|
|
@@ -119,7 +164,7 @@ const MatchingActivityMaterialContent = ({
|
|
|
119
164
|
}
|
|
120
165
|
};
|
|
121
166
|
|
|
122
|
-
const handleMouseUp = ()
|
|
167
|
+
const handleMouseUp = () => {
|
|
123
168
|
if (dropTargetKey !== null && draggedValue !== null) {
|
|
124
169
|
onChange(answer, dropTargetKey, draggedValue);
|
|
125
170
|
}
|
|
@@ -129,11 +174,7 @@ const MatchingActivityMaterialContent = ({
|
|
|
129
174
|
};
|
|
130
175
|
|
|
131
176
|
// Touch drag handlers
|
|
132
|
-
const handleTouchStart = (
|
|
133
|
-
e: React.TouchEvent,
|
|
134
|
-
materialValue: string,
|
|
135
|
-
element: HTMLElement
|
|
136
|
-
): void => {
|
|
177
|
+
const handleTouchStart = (e, materialValue, element) => {
|
|
137
178
|
if (!checkCanAnswerQuestion()) return;
|
|
138
179
|
const touch = e.touches[0];
|
|
139
180
|
setDraggedValue(materialValue);
|
|
@@ -142,7 +183,7 @@ const MatchingActivityMaterialContent = ({
|
|
|
142
183
|
setSelectedValue(null);
|
|
143
184
|
};
|
|
144
185
|
|
|
145
|
-
const handleTouchMove = (e
|
|
186
|
+
const handleTouchMove = (e) => {
|
|
146
187
|
if (!draggedValue) return;
|
|
147
188
|
|
|
148
189
|
const touch = e.touches[0];
|
|
@@ -163,7 +204,7 @@ const MatchingActivityMaterialContent = ({
|
|
|
163
204
|
}
|
|
164
205
|
};
|
|
165
206
|
|
|
166
|
-
const handleTouchEnd = ()
|
|
207
|
+
const handleTouchEnd = () => {
|
|
167
208
|
if (dropTargetKey !== null && draggedValue !== null) {
|
|
168
209
|
onChange(answer, dropTargetKey, draggedValue);
|
|
169
210
|
}
|
|
@@ -174,13 +215,13 @@ const MatchingActivityMaterialContent = ({
|
|
|
174
215
|
};
|
|
175
216
|
|
|
176
217
|
// Click/tap to select (for easier mobile interaction)
|
|
177
|
-
const handleSelectItem = (materialValue
|
|
218
|
+
const handleSelectItem = (materialValue) => {
|
|
178
219
|
if (!checkCanAnswerQuestion()) return;
|
|
179
220
|
setSelectedValue(materialValue);
|
|
180
221
|
setDraggedValue(null);
|
|
181
222
|
};
|
|
182
223
|
|
|
183
|
-
const handleDropZoneClick = (answerMapKey
|
|
224
|
+
const handleDropZoneClick = (answerMapKey) => {
|
|
184
225
|
if (selectedValue !== null) {
|
|
185
226
|
onChange(answer, answerMapKey, selectedValue);
|
|
186
227
|
setSelectedValue(null);
|
|
@@ -365,36 +406,34 @@ const MatchingActivityMaterialContent = ({
|
|
|
365
406
|
</>
|
|
366
407
|
) : null}
|
|
367
408
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
);
|
|
409
|
+
{Object.keys(answerMap).map((answerMapKey, index) => {
|
|
410
|
+
const learnerAnswerState = checkAnswerState(
|
|
411
|
+
materialMap[answerMapKey],
|
|
412
|
+
answerMap[answerMapKey]
|
|
413
|
+
);
|
|
374
414
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
).map((inputPart, index) => (
|
|
415
|
+
return (
|
|
416
|
+
<div key={index} className="flex flex-row w-full">
|
|
417
|
+
<div className="w-1/3">
|
|
418
|
+
<div
|
|
419
|
+
className={`${
|
|
420
|
+
contentMap.type === "TEXT"
|
|
421
|
+
? "h-catchup-activity-text-box-item"
|
|
422
|
+
: "h-catchup-activity-media-box-item"
|
|
423
|
+
} flex flex-col items-center justify-center border-2 rounded-catchup-xlarge transition-all duration-300 my-3 ${
|
|
424
|
+
learnerAnswerState === "EMPTY"
|
|
425
|
+
? "border-catchup-blue"
|
|
426
|
+
: learnerAnswerState === "CORRECT"
|
|
427
|
+
? "border-catchup-green"
|
|
428
|
+
: learnerAnswerState === "INCORRECT"
|
|
429
|
+
? "border-catchup-red"
|
|
430
|
+
: "border-catchup-blue"
|
|
431
|
+
}`}
|
|
432
|
+
>
|
|
433
|
+
<div className="flex flex-col items-center justify-center transition-all duration-300 px-4 text-center">
|
|
434
|
+
<p className="text-lg whitespace-pre-wrap">
|
|
435
|
+
{constructInputWithSpecialExpressionList(answerMapKey).map(
|
|
436
|
+
(inputPart, index) => (
|
|
398
437
|
<span
|
|
399
438
|
key={index}
|
|
400
439
|
className={`${inputPart.isBold ? "font-bold" : ""} ${
|
|
@@ -409,86 +448,86 @@ const MatchingActivityMaterialContent = ({
|
|
|
409
448
|
inputPart.value
|
|
410
449
|
)}
|
|
411
450
|
</span>
|
|
412
|
-
)
|
|
413
|
-
|
|
414
|
-
</
|
|
451
|
+
)
|
|
452
|
+
)}
|
|
453
|
+
</p>
|
|
415
454
|
</div>
|
|
416
455
|
</div>
|
|
417
|
-
|
|
418
|
-
|
|
456
|
+
</div>
|
|
457
|
+
<div className="mx-4 w-[2px] bg-catchup-lighter-gray"></div>
|
|
458
|
+
<div className="flex-1">
|
|
459
|
+
<div
|
|
460
|
+
data-matching-drop-zone={answerMapKey}
|
|
461
|
+
onMouseEnter={() =>
|
|
462
|
+
draggedValue && setDropTargetKey(answerMapKey)
|
|
463
|
+
}
|
|
464
|
+
onMouseLeave={() => setDropTargetKey(null)}
|
|
465
|
+
onClick={() => handleDropZoneClick(answerMapKey)}
|
|
466
|
+
className={`${
|
|
467
|
+
dropTargetKey === answerMapKey
|
|
468
|
+
? "bg-catchup-light-blue ring-2 ring-blue-400"
|
|
469
|
+
: ""
|
|
470
|
+
} ${
|
|
471
|
+
contentMap.type === "TEXT"
|
|
472
|
+
? "h-catchup-activity-text-box-item"
|
|
473
|
+
: "h-catchup-activity-media-box-item"
|
|
474
|
+
} flex flex-col items-center justify-center border-2 rounded-catchup-xlarge cursor-pointer transition-all duration-300 my-3 ${
|
|
475
|
+
learnerAnswerState === "EMPTY"
|
|
476
|
+
? "border-catchup-blue"
|
|
477
|
+
: learnerAnswerState === "CORRECT"
|
|
478
|
+
? "border-catchup-green"
|
|
479
|
+
: learnerAnswerState === "INCORRECT"
|
|
480
|
+
? "border-catchup-red"
|
|
481
|
+
: "border-catchup-blue"
|
|
482
|
+
}`}
|
|
483
|
+
>
|
|
419
484
|
<div
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
? "bg-catchup-light-blue ring-2 ring-blue-400"
|
|
429
|
-
: ""
|
|
430
|
-
} ${
|
|
431
|
-
contentMap.type === "TEXT"
|
|
432
|
-
? "h-catchup-activity-text-box-item"
|
|
433
|
-
: "h-catchup-activity-media-box-item"
|
|
434
|
-
} flex flex-col items-center justify-center border-2 rounded-catchup-xlarge cursor-pointer transition-all duration-300 my-3 ${
|
|
435
|
-
learnerAnswerState === "EMPTY"
|
|
436
|
-
? "border-catchup-blue"
|
|
437
|
-
: learnerAnswerState === "CORRECT"
|
|
438
|
-
? "border-catchup-green"
|
|
439
|
-
: learnerAnswerState === "INCORRECT"
|
|
440
|
-
? "border-catchup-red"
|
|
441
|
-
: "border-catchup-blue"
|
|
442
|
-
}`}
|
|
485
|
+
className="h-full flex-1 flex flex-row items-center justify-center px-4"
|
|
486
|
+
onClick={(e) => {
|
|
487
|
+
e.stopPropagation();
|
|
488
|
+
if (checkCanAnswerQuestion() && answerMap[answerMapKey]) {
|
|
489
|
+
onChange(answer, answerMapKey, null);
|
|
490
|
+
setSelectedValue(null);
|
|
491
|
+
}
|
|
492
|
+
}}
|
|
443
493
|
>
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
</p>
|
|
476
|
-
) : (
|
|
477
|
-
<ShowMaterialMediaByContentType
|
|
478
|
-
key={`${uniqueValue}-${index}`}
|
|
479
|
-
contentType={contentMap.type}
|
|
480
|
-
src={answerMap[answerMapKey]}
|
|
481
|
-
canFullScreen={false}
|
|
482
|
-
/>
|
|
483
|
-
)
|
|
484
|
-
) : null}
|
|
485
|
-
</div>
|
|
494
|
+
{answerMap[answerMapKey] ? (
|
|
495
|
+
contentMap.type === "TEXT" ? (
|
|
496
|
+
<p className="text-lg whitespace-pre-wrap">
|
|
497
|
+
{constructInputWithSpecialExpressionList(
|
|
498
|
+
answerMap[answerMapKey]
|
|
499
|
+
).map((inputPart, index) => (
|
|
500
|
+
<span
|
|
501
|
+
key={index}
|
|
502
|
+
className={`${
|
|
503
|
+
inputPart.isBold ? "font-bold" : ""
|
|
504
|
+
} ${inputPart.isUnderline ? "underline" : ""}`}
|
|
505
|
+
>
|
|
506
|
+
{inputPart.isEquation ? (
|
|
507
|
+
<span className="text-xl">
|
|
508
|
+
<InlineMath math={inputPart.value} />
|
|
509
|
+
</span>
|
|
510
|
+
) : (
|
|
511
|
+
inputPart.value
|
|
512
|
+
)}
|
|
513
|
+
</span>
|
|
514
|
+
))}
|
|
515
|
+
</p>
|
|
516
|
+
) : (
|
|
517
|
+
<ShowMaterialMediaByContentType
|
|
518
|
+
key={`${uniqueValue}-${index}`}
|
|
519
|
+
contentType={contentMap.type}
|
|
520
|
+
src={answerMap[answerMapKey]}
|
|
521
|
+
canFullScreen={false}
|
|
522
|
+
/>
|
|
523
|
+
)
|
|
524
|
+
) : null}
|
|
486
525
|
</div>
|
|
487
526
|
</div>
|
|
488
527
|
</div>
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
528
|
+
</div>
|
|
529
|
+
);
|
|
530
|
+
})}
|
|
492
531
|
</div>
|
|
493
532
|
);
|
|
494
533
|
};
|