catchup-library-web 1.20.32 → 1.20.33
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
|
@@ -4903,21 +4903,29 @@ var FillInTheBlanksActivityMaterialContent = ({
|
|
|
4903
4903
|
const [selectedOption, setSelectedOption] = (0, import_react18.useState)(null);
|
|
4904
4904
|
const [draggedOption, setDraggedOption] = (0, import_react18.useState)(null);
|
|
4905
4905
|
const [dropTargetIndex, setDropTargetIndex] = (0, import_react18.useState)(null);
|
|
4906
|
+
const [draggedElement, setDraggedElement] = (0, import_react18.useState)(
|
|
4907
|
+
null
|
|
4908
|
+
);
|
|
4909
|
+
const dragElementRef = (0, import_react18.useRef)(null);
|
|
4910
|
+
const [touchPosition, setTouchPosition] = (0, import_react18.useState)({
|
|
4911
|
+
x: 0,
|
|
4912
|
+
y: 0
|
|
4913
|
+
});
|
|
4906
4914
|
(0, import_react18.useEffect)(() => {
|
|
4907
4915
|
setShuffleOptionList(shuffleArray(optionList));
|
|
4908
|
-
}, []);
|
|
4916
|
+
}, [optionList]);
|
|
4909
4917
|
(0, import_react18.useEffect)(() => {
|
|
4910
4918
|
if (!showCorrectAnswer) return;
|
|
4911
4919
|
const foundAnswer = answer.data.find(
|
|
4912
4920
|
(answerData) => answerData.type === "FILL_IN_THE_BLANKS"
|
|
4913
4921
|
);
|
|
4914
|
-
if (foundAnswer.answerMap.length === 0) return;
|
|
4922
|
+
if (!foundAnswer || foundAnswer.answerMap.length === 0) return;
|
|
4915
4923
|
if (Object.keys(materialMap).length === 0) return;
|
|
4916
4924
|
foundAnswer.answerMap = Object.keys(materialMap).map(
|
|
4917
4925
|
(materialMapKey) => JSON.parse(materialMap[materialMapKey])[0]
|
|
4918
4926
|
);
|
|
4919
4927
|
onChange(answer, 0, JSON.parse(materialMap[0])[0]);
|
|
4920
|
-
}, [showCorrectAnswer]);
|
|
4928
|
+
}, [showCorrectAnswer, answer, materialMap, onChange]);
|
|
4921
4929
|
const retrieveAnswerMap = () => {
|
|
4922
4930
|
const foundIndex = answer.data.findIndex(
|
|
4923
4931
|
(answerData) => answerData.type === "FILL_IN_THE_BLANKS"
|
|
@@ -4937,36 +4945,89 @@ var FillInTheBlanksActivityMaterialContent = ({
|
|
|
4937
4945
|
const checkAnswerProvided = (answerMap2, option) => {
|
|
4938
4946
|
return Object.keys(answerMap2).findIndex((key) => answerMap2[key] === option) !== -1;
|
|
4939
4947
|
};
|
|
4940
|
-
const
|
|
4941
|
-
|
|
4948
|
+
const handleMouseDown = (e, option) => {
|
|
4949
|
+
e.preventDefault();
|
|
4950
|
+
setDraggedOption(option);
|
|
4951
|
+
setSelectedOption(null);
|
|
4942
4952
|
};
|
|
4943
|
-
const
|
|
4953
|
+
const handleMouseUp = () => {
|
|
4954
|
+
if (dropTargetIndex !== null && draggedOption !== null) {
|
|
4955
|
+
onChange(answer, dropTargetIndex, draggedOption);
|
|
4956
|
+
}
|
|
4957
|
+
setDraggedOption(null);
|
|
4958
|
+
setDropTargetIndex(null);
|
|
4959
|
+
};
|
|
4960
|
+
const handleTouchStart = (e, option, element) => {
|
|
4961
|
+
const touch = e.touches[0];
|
|
4944
4962
|
setDraggedOption(option);
|
|
4963
|
+
setDraggedElement(element);
|
|
4964
|
+
setTouchPosition({ x: touch.clientX, y: touch.clientY });
|
|
4965
|
+
setSelectedOption(null);
|
|
4966
|
+
};
|
|
4967
|
+
const handleTouchMove = (e) => {
|
|
4968
|
+
if (!draggedOption) return;
|
|
4969
|
+
const touch = e.touches[0];
|
|
4970
|
+
setTouchPosition({ x: touch.clientX, y: touch.clientY });
|
|
4971
|
+
const elementUnder = document.elementFromPoint(
|
|
4972
|
+
touch.clientX,
|
|
4973
|
+
touch.clientY
|
|
4974
|
+
);
|
|
4975
|
+
const dropZone = elementUnder == null ? void 0 : elementUnder.closest("[data-drop-zone]");
|
|
4976
|
+
if (dropZone) {
|
|
4977
|
+
const dropIndex = dropZone.getAttribute("data-drop-zone");
|
|
4978
|
+
setDropTargetIndex(dropIndex);
|
|
4979
|
+
} else {
|
|
4980
|
+
setDropTargetIndex(null);
|
|
4981
|
+
}
|
|
4945
4982
|
};
|
|
4946
|
-
const
|
|
4983
|
+
const handleTouchEnd = () => {
|
|
4947
4984
|
if (dropTargetIndex !== null && draggedOption !== null) {
|
|
4948
4985
|
onChange(answer, dropTargetIndex, draggedOption);
|
|
4949
4986
|
}
|
|
4950
4987
|
setDraggedOption(null);
|
|
4951
4988
|
setDropTargetIndex(null);
|
|
4989
|
+
setDraggedElement(null);
|
|
4952
4990
|
};
|
|
4953
|
-
const
|
|
4954
|
-
|
|
4991
|
+
const handleSelectOption = (option) => {
|
|
4992
|
+
setSelectedOption(option);
|
|
4993
|
+
setDraggedOption(null);
|
|
4955
4994
|
};
|
|
4956
|
-
const
|
|
4995
|
+
const handleDropZoneClick = (index) => {
|
|
4957
4996
|
if (selectedOption !== null) {
|
|
4958
4997
|
onChange(answer, index, selectedOption);
|
|
4959
4998
|
setSelectedOption(null);
|
|
4960
|
-
} else if (draggedOption !== null) {
|
|
4961
|
-
onChange(answer, index, draggedOption);
|
|
4962
|
-
setDraggedOption(null);
|
|
4963
4999
|
}
|
|
4964
|
-
setDropTargetIndex(null);
|
|
4965
5000
|
};
|
|
4966
5001
|
const answerMap = retrieveAnswerMap();
|
|
4967
5002
|
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-row flex-wrap items-center", children: [
|
|
4968
5003
|
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "hidden md:block", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "font-semibold text-xl opacity-60", children: i18n_default.t("please_select_fill_in_the_blanks_text") }) }),
|
|
4969
5004
|
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "hidden md:contents", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(DividerLine_default, {}) }),
|
|
5005
|
+
draggedOption && touchPosition.x > 0 && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5006
|
+
"div",
|
|
5007
|
+
{
|
|
5008
|
+
className: "fixed pointer-events-none z-50 opacity-80",
|
|
5009
|
+
style: {
|
|
5010
|
+
left: `${touchPosition.x}px`,
|
|
5011
|
+
top: `${touchPosition.y}px`,
|
|
5012
|
+
transform: "translate(-50%, -50%)"
|
|
5013
|
+
},
|
|
5014
|
+
children: contentMap.type === "TEXT" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "border-catchup-blue border-2 px-2 rounded-catchup-xlarge bg-white shadow-lg", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: "italic whitespace-pre-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5015
|
+
InputWithSpecialExpression_default,
|
|
5016
|
+
{
|
|
5017
|
+
value: draggedOption,
|
|
5018
|
+
showSpecialOnly: false
|
|
5019
|
+
}
|
|
5020
|
+
) }) }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "border-catchup-blue border-2 px-2 py-1 rounded-catchup-xlarge bg-white shadow-lg", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5021
|
+
ShowMaterialMediaByContentType_default,
|
|
5022
|
+
{
|
|
5023
|
+
contentType: contentMap.type,
|
|
5024
|
+
src: draggedOption,
|
|
5025
|
+
canFullScreen: false
|
|
5026
|
+
},
|
|
5027
|
+
uniqueValue
|
|
5028
|
+
) })
|
|
5029
|
+
}
|
|
5030
|
+
),
|
|
4970
5031
|
/* @__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(
|
|
4971
5032
|
(option, index) => checkAnswerProvided(answerMap, option) ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "opacity-30", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
4972
5033
|
ShowMaterialMediaByContentType_default,
|
|
@@ -4979,16 +5040,17 @@ var FillInTheBlanksActivityMaterialContent = ({
|
|
|
4979
5040
|
) }, index) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
4980
5041
|
"div",
|
|
4981
5042
|
{
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
5043
|
+
ref: draggedOption === option ? dragElementRef : null,
|
|
5044
|
+
className: `${draggedOption === option ? "opacity-40" : selectedOption === option ? "ring-2 ring-blue-500" : "opacity-100"} transition-all duration-200`,
|
|
5045
|
+
onMouseDown: (e) => handleMouseDown(e, option),
|
|
5046
|
+
onTouchStart: (e) => handleTouchStart(e, option, e.currentTarget),
|
|
5047
|
+
onTouchMove: handleTouchMove,
|
|
5048
|
+
onTouchEnd: handleTouchEnd,
|
|
4986
5049
|
children: contentMap.type === "TEXT" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
4987
5050
|
"div",
|
|
4988
5051
|
{
|
|
4989
|
-
className: "border-catchup-blue border-2 px-2 rounded-catchup-xlarge cursor-pointer select-none
|
|
5052
|
+
className: "border-catchup-blue border-2 px-2 rounded-catchup-xlarge cursor-pointer select-none",
|
|
4990
5053
|
onClick: () => handleSelectOption(option),
|
|
4991
|
-
onTouchEnd: () => handleSelectOption(option),
|
|
4992
5054
|
children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: "italic whitespace-pre-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
4993
5055
|
InputWithSpecialExpression_default,
|
|
4994
5056
|
{
|
|
@@ -5000,9 +5062,8 @@ var FillInTheBlanksActivityMaterialContent = ({
|
|
|
5000
5062
|
) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5001
5063
|
"div",
|
|
5002
5064
|
{
|
|
5003
|
-
className: "border-catchup-blue border-2 px-2 py-1 rounded-catchup-xlarge cursor-pointer select-none
|
|
5065
|
+
className: "border-catchup-blue border-2 px-2 py-1 rounded-catchup-xlarge cursor-pointer select-none",
|
|
5004
5066
|
onClick: () => handleSelectOption(option),
|
|
5005
|
-
onTouchEnd: () => handleSelectOption(option),
|
|
5006
5067
|
children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5007
5068
|
ShowMaterialMediaByContentType_default,
|
|
5008
5069
|
{
|
|
@@ -5018,7 +5079,7 @@ var FillInTheBlanksActivityMaterialContent = ({
|
|
|
5018
5079
|
index
|
|
5019
5080
|
)
|
|
5020
5081
|
) }),
|
|
5021
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "w-full flex flex-row flex-wrap", children: Object.keys(answerMap).map((materialKey, index) => {
|
|
5082
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "w-full flex flex-row flex-wrap", onMouseUp: handleMouseUp, children: Object.keys(answerMap).map((materialKey, index) => {
|
|
5022
5083
|
const learnerAnswerState = checkAnswerState(
|
|
5023
5084
|
JSON.parse(materialMap[materialKey]),
|
|
5024
5085
|
answerMap[materialKey]
|
|
@@ -5026,21 +5087,11 @@ var FillInTheBlanksActivityMaterialContent = ({
|
|
|
5026
5087
|
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "w-full md:w-1/2", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "mx-2", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5027
5088
|
"div",
|
|
5028
5089
|
{
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
onDrop: (e) => {
|
|
5035
|
-
e.preventDefault();
|
|
5036
|
-
handleDropZoneDrop(materialKey);
|
|
5037
|
-
},
|
|
5038
|
-
onClick: () => {
|
|
5039
|
-
if (selectedOption !== null) {
|
|
5040
|
-
handleDropZoneDrop(materialKey);
|
|
5041
|
-
}
|
|
5042
|
-
},
|
|
5043
|
-
className: `${dropTargetIndex === materialKey ? "ring-2 ring-blue-400" : ""}`,
|
|
5090
|
+
"data-drop-zone": materialKey,
|
|
5091
|
+
onMouseEnter: () => draggedOption && setDropTargetIndex(materialKey),
|
|
5092
|
+
onMouseLeave: () => setDropTargetIndex(null),
|
|
5093
|
+
onClick: () => handleDropZoneClick(materialKey),
|
|
5094
|
+
className: `${dropTargetIndex === materialKey ? "ring-2 ring-blue-400 bg-blue-50" : ""} transition-all duration-200 rounded-lg`,
|
|
5044
5095
|
children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "w-full flex flex-row my-2 gap-x-2", children: [
|
|
5045
5096
|
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "my-auto", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("p", { className: "text-xl", children: [
|
|
5046
5097
|
parseFloat(materialKey) + 1,
|
|
@@ -5050,9 +5101,10 @@ var FillInTheBlanksActivityMaterialContent = ({
|
|
|
5050
5101
|
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5051
5102
|
"div",
|
|
5052
5103
|
{
|
|
5053
|
-
className: `w-full min-h-[44px] border rounded-lg ${answerMap[materialKey] ? "border-catchup-blue-400 px-2" : "bg-catchup-gray-50 border-catchup-gray-200 border-dashed py-2 px-4"}`,
|
|
5054
|
-
onClick: () => {
|
|
5104
|
+
className: `w-full min-h-[44px] border rounded-lg ${answerMap[materialKey] ? "border-catchup-blue-400 px-2 cursor-pointer" : "bg-catchup-gray-50 border-catchup-gray-200 border-dashed py-2 px-4"}`,
|
|
5105
|
+
onClick: (e) => {
|
|
5055
5106
|
if (answerMap[materialKey]) {
|
|
5107
|
+
e.stopPropagation();
|
|
5056
5108
|
onChange(answer, materialKey, "");
|
|
5057
5109
|
}
|
|
5058
5110
|
},
|
|
@@ -5062,7 +5114,7 @@ var FillInTheBlanksActivityMaterialContent = ({
|
|
|
5062
5114
|
value: answerMap[materialKey],
|
|
5063
5115
|
showSpecialOnly: false
|
|
5064
5116
|
}
|
|
5065
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: "text-gray-400 italic" })
|
|
5117
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: "text-gray-400 italic", children: i18n_default.t("please_drop_here") })
|
|
5066
5118
|
}
|
|
5067
5119
|
) }),
|
|
5068
5120
|
learnerAnswerState === "CORRECT" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "absolute -top-[10px] right-4 bg-catchup-white", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
@@ -5090,7 +5142,8 @@ var FillInTheBlanksActivityMaterialContent = ({
|
|
|
5090
5142
|
"div",
|
|
5091
5143
|
{
|
|
5092
5144
|
className: "flex-1 cursor-pointer",
|
|
5093
|
-
onClick: () => {
|
|
5145
|
+
onClick: (e) => {
|
|
5146
|
+
e.stopPropagation();
|
|
5094
5147
|
onChange(answer, materialKey, "");
|
|
5095
5148
|
},
|
|
5096
5149
|
children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
package/dist/index.mjs
CHANGED
|
@@ -4644,7 +4644,7 @@ var DropdownActivityContent_default = DropdownActivityContent;
|
|
|
4644
4644
|
|
|
4645
4645
|
// src/components/activities/material-contents/FillInTheBlanksActivityMaterialContent.tsx
|
|
4646
4646
|
import { InlineMath as InlineMath4 } from "react-katex";
|
|
4647
|
-
import { useState as useState17, useEffect as useEffect8 } from "react";
|
|
4647
|
+
import { useState as useState17, useEffect as useEffect8, useRef as useRef4 } from "react";
|
|
4648
4648
|
|
|
4649
4649
|
// src/components/texts/InputWithSpecialExpression.tsx
|
|
4650
4650
|
import { InlineMath as InlineMath3 } from "react-katex";
|
|
@@ -4687,21 +4687,29 @@ var FillInTheBlanksActivityMaterialContent = ({
|
|
|
4687
4687
|
const [selectedOption, setSelectedOption] = useState17(null);
|
|
4688
4688
|
const [draggedOption, setDraggedOption] = useState17(null);
|
|
4689
4689
|
const [dropTargetIndex, setDropTargetIndex] = useState17(null);
|
|
4690
|
+
const [draggedElement, setDraggedElement] = useState17(
|
|
4691
|
+
null
|
|
4692
|
+
);
|
|
4693
|
+
const dragElementRef = useRef4(null);
|
|
4694
|
+
const [touchPosition, setTouchPosition] = useState17({
|
|
4695
|
+
x: 0,
|
|
4696
|
+
y: 0
|
|
4697
|
+
});
|
|
4690
4698
|
useEffect8(() => {
|
|
4691
4699
|
setShuffleOptionList(shuffleArray(optionList));
|
|
4692
|
-
}, []);
|
|
4700
|
+
}, [optionList]);
|
|
4693
4701
|
useEffect8(() => {
|
|
4694
4702
|
if (!showCorrectAnswer) return;
|
|
4695
4703
|
const foundAnswer = answer.data.find(
|
|
4696
4704
|
(answerData) => answerData.type === "FILL_IN_THE_BLANKS"
|
|
4697
4705
|
);
|
|
4698
|
-
if (foundAnswer.answerMap.length === 0) return;
|
|
4706
|
+
if (!foundAnswer || foundAnswer.answerMap.length === 0) return;
|
|
4699
4707
|
if (Object.keys(materialMap).length === 0) return;
|
|
4700
4708
|
foundAnswer.answerMap = Object.keys(materialMap).map(
|
|
4701
4709
|
(materialMapKey) => JSON.parse(materialMap[materialMapKey])[0]
|
|
4702
4710
|
);
|
|
4703
4711
|
onChange(answer, 0, JSON.parse(materialMap[0])[0]);
|
|
4704
|
-
}, [showCorrectAnswer]);
|
|
4712
|
+
}, [showCorrectAnswer, answer, materialMap, onChange]);
|
|
4705
4713
|
const retrieveAnswerMap = () => {
|
|
4706
4714
|
const foundIndex = answer.data.findIndex(
|
|
4707
4715
|
(answerData) => answerData.type === "FILL_IN_THE_BLANKS"
|
|
@@ -4721,36 +4729,89 @@ var FillInTheBlanksActivityMaterialContent = ({
|
|
|
4721
4729
|
const checkAnswerProvided = (answerMap2, option) => {
|
|
4722
4730
|
return Object.keys(answerMap2).findIndex((key) => answerMap2[key] === option) !== -1;
|
|
4723
4731
|
};
|
|
4724
|
-
const
|
|
4725
|
-
|
|
4732
|
+
const handleMouseDown = (e, option) => {
|
|
4733
|
+
e.preventDefault();
|
|
4734
|
+
setDraggedOption(option);
|
|
4735
|
+
setSelectedOption(null);
|
|
4736
|
+
};
|
|
4737
|
+
const handleMouseUp = () => {
|
|
4738
|
+
if (dropTargetIndex !== null && draggedOption !== null) {
|
|
4739
|
+
onChange(answer, dropTargetIndex, draggedOption);
|
|
4740
|
+
}
|
|
4741
|
+
setDraggedOption(null);
|
|
4742
|
+
setDropTargetIndex(null);
|
|
4726
4743
|
};
|
|
4727
|
-
const
|
|
4744
|
+
const handleTouchStart = (e, option, element) => {
|
|
4745
|
+
const touch = e.touches[0];
|
|
4728
4746
|
setDraggedOption(option);
|
|
4747
|
+
setDraggedElement(element);
|
|
4748
|
+
setTouchPosition({ x: touch.clientX, y: touch.clientY });
|
|
4749
|
+
setSelectedOption(null);
|
|
4729
4750
|
};
|
|
4730
|
-
const
|
|
4751
|
+
const handleTouchMove = (e) => {
|
|
4752
|
+
if (!draggedOption) return;
|
|
4753
|
+
const touch = e.touches[0];
|
|
4754
|
+
setTouchPosition({ x: touch.clientX, y: touch.clientY });
|
|
4755
|
+
const elementUnder = document.elementFromPoint(
|
|
4756
|
+
touch.clientX,
|
|
4757
|
+
touch.clientY
|
|
4758
|
+
);
|
|
4759
|
+
const dropZone = elementUnder == null ? void 0 : elementUnder.closest("[data-drop-zone]");
|
|
4760
|
+
if (dropZone) {
|
|
4761
|
+
const dropIndex = dropZone.getAttribute("data-drop-zone");
|
|
4762
|
+
setDropTargetIndex(dropIndex);
|
|
4763
|
+
} else {
|
|
4764
|
+
setDropTargetIndex(null);
|
|
4765
|
+
}
|
|
4766
|
+
};
|
|
4767
|
+
const handleTouchEnd = () => {
|
|
4731
4768
|
if (dropTargetIndex !== null && draggedOption !== null) {
|
|
4732
4769
|
onChange(answer, dropTargetIndex, draggedOption);
|
|
4733
4770
|
}
|
|
4734
4771
|
setDraggedOption(null);
|
|
4735
4772
|
setDropTargetIndex(null);
|
|
4773
|
+
setDraggedElement(null);
|
|
4736
4774
|
};
|
|
4737
|
-
const
|
|
4738
|
-
|
|
4775
|
+
const handleSelectOption = (option) => {
|
|
4776
|
+
setSelectedOption(option);
|
|
4777
|
+
setDraggedOption(null);
|
|
4739
4778
|
};
|
|
4740
|
-
const
|
|
4779
|
+
const handleDropZoneClick = (index) => {
|
|
4741
4780
|
if (selectedOption !== null) {
|
|
4742
4781
|
onChange(answer, index, selectedOption);
|
|
4743
4782
|
setSelectedOption(null);
|
|
4744
|
-
} else if (draggedOption !== null) {
|
|
4745
|
-
onChange(answer, index, draggedOption);
|
|
4746
|
-
setDraggedOption(null);
|
|
4747
4783
|
}
|
|
4748
|
-
setDropTargetIndex(null);
|
|
4749
4784
|
};
|
|
4750
4785
|
const answerMap = retrieveAnswerMap();
|
|
4751
4786
|
return /* @__PURE__ */ jsxs17("div", { className: "flex flex-row flex-wrap items-center", children: [
|
|
4752
4787
|
/* @__PURE__ */ jsx28("div", { className: "hidden md:block", children: /* @__PURE__ */ jsx28("span", { className: "font-semibold text-xl opacity-60", children: i18n_default.t("please_select_fill_in_the_blanks_text") }) }),
|
|
4753
4788
|
/* @__PURE__ */ jsx28("div", { className: "hidden md:contents", children: /* @__PURE__ */ jsx28(DividerLine_default, {}) }),
|
|
4789
|
+
draggedOption && touchPosition.x > 0 && /* @__PURE__ */ jsx28(
|
|
4790
|
+
"div",
|
|
4791
|
+
{
|
|
4792
|
+
className: "fixed pointer-events-none z-50 opacity-80",
|
|
4793
|
+
style: {
|
|
4794
|
+
left: `${touchPosition.x}px`,
|
|
4795
|
+
top: `${touchPosition.y}px`,
|
|
4796
|
+
transform: "translate(-50%, -50%)"
|
|
4797
|
+
},
|
|
4798
|
+
children: contentMap.type === "TEXT" ? /* @__PURE__ */ jsx28("div", { className: "border-catchup-blue border-2 px-2 rounded-catchup-xlarge bg-white shadow-lg", children: /* @__PURE__ */ jsx28("p", { className: "italic whitespace-pre-wrap", children: /* @__PURE__ */ jsx28(
|
|
4799
|
+
InputWithSpecialExpression_default,
|
|
4800
|
+
{
|
|
4801
|
+
value: draggedOption,
|
|
4802
|
+
showSpecialOnly: false
|
|
4803
|
+
}
|
|
4804
|
+
) }) }) : /* @__PURE__ */ jsx28("div", { className: "border-catchup-blue border-2 px-2 py-1 rounded-catchup-xlarge bg-white shadow-lg", children: /* @__PURE__ */ jsx28(
|
|
4805
|
+
ShowMaterialMediaByContentType_default,
|
|
4806
|
+
{
|
|
4807
|
+
contentType: contentMap.type,
|
|
4808
|
+
src: draggedOption,
|
|
4809
|
+
canFullScreen: false
|
|
4810
|
+
},
|
|
4811
|
+
uniqueValue
|
|
4812
|
+
) })
|
|
4813
|
+
}
|
|
4814
|
+
),
|
|
4754
4815
|
/* @__PURE__ */ jsx28("div", { className: "w-full flex flex-row flex-wrap gap-x-2 gap-y-2 my-2", children: shuffleOptionList.map(
|
|
4755
4816
|
(option, index) => checkAnswerProvided(answerMap, option) ? /* @__PURE__ */ jsx28("div", { className: "opacity-30", children: /* @__PURE__ */ jsx28(
|
|
4756
4817
|
ShowMaterialMediaByContentType_default,
|
|
@@ -4763,16 +4824,17 @@ var FillInTheBlanksActivityMaterialContent = ({
|
|
|
4763
4824
|
) }, index) : /* @__PURE__ */ jsx28(
|
|
4764
4825
|
"div",
|
|
4765
4826
|
{
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4827
|
+
ref: draggedOption === option ? dragElementRef : null,
|
|
4828
|
+
className: `${draggedOption === option ? "opacity-40" : selectedOption === option ? "ring-2 ring-blue-500" : "opacity-100"} transition-all duration-200`,
|
|
4829
|
+
onMouseDown: (e) => handleMouseDown(e, option),
|
|
4830
|
+
onTouchStart: (e) => handleTouchStart(e, option, e.currentTarget),
|
|
4831
|
+
onTouchMove: handleTouchMove,
|
|
4832
|
+
onTouchEnd: handleTouchEnd,
|
|
4770
4833
|
children: contentMap.type === "TEXT" ? /* @__PURE__ */ jsx28(
|
|
4771
4834
|
"div",
|
|
4772
4835
|
{
|
|
4773
|
-
className: "border-catchup-blue border-2 px-2 rounded-catchup-xlarge cursor-pointer select-none
|
|
4836
|
+
className: "border-catchup-blue border-2 px-2 rounded-catchup-xlarge cursor-pointer select-none",
|
|
4774
4837
|
onClick: () => handleSelectOption(option),
|
|
4775
|
-
onTouchEnd: () => handleSelectOption(option),
|
|
4776
4838
|
children: /* @__PURE__ */ jsx28("p", { className: "italic whitespace-pre-wrap", children: /* @__PURE__ */ jsx28(
|
|
4777
4839
|
InputWithSpecialExpression_default,
|
|
4778
4840
|
{
|
|
@@ -4784,9 +4846,8 @@ var FillInTheBlanksActivityMaterialContent = ({
|
|
|
4784
4846
|
) : /* @__PURE__ */ jsx28(
|
|
4785
4847
|
"div",
|
|
4786
4848
|
{
|
|
4787
|
-
className: "border-catchup-blue border-2 px-2 py-1 rounded-catchup-xlarge cursor-pointer select-none
|
|
4849
|
+
className: "border-catchup-blue border-2 px-2 py-1 rounded-catchup-xlarge cursor-pointer select-none",
|
|
4788
4850
|
onClick: () => handleSelectOption(option),
|
|
4789
|
-
onTouchEnd: () => handleSelectOption(option),
|
|
4790
4851
|
children: /* @__PURE__ */ jsx28(
|
|
4791
4852
|
ShowMaterialMediaByContentType_default,
|
|
4792
4853
|
{
|
|
@@ -4802,7 +4863,7 @@ var FillInTheBlanksActivityMaterialContent = ({
|
|
|
4802
4863
|
index
|
|
4803
4864
|
)
|
|
4804
4865
|
) }),
|
|
4805
|
-
/* @__PURE__ */ jsx28("div", { className: "w-full flex flex-row flex-wrap", children: Object.keys(answerMap).map((materialKey, index) => {
|
|
4866
|
+
/* @__PURE__ */ jsx28("div", { className: "w-full flex flex-row flex-wrap", onMouseUp: handleMouseUp, children: Object.keys(answerMap).map((materialKey, index) => {
|
|
4806
4867
|
const learnerAnswerState = checkAnswerState(
|
|
4807
4868
|
JSON.parse(materialMap[materialKey]),
|
|
4808
4869
|
answerMap[materialKey]
|
|
@@ -4810,21 +4871,11 @@ var FillInTheBlanksActivityMaterialContent = ({
|
|
|
4810
4871
|
return /* @__PURE__ */ jsx28("div", { className: "w-full md:w-1/2", children: /* @__PURE__ */ jsx28("div", { className: "mx-2", children: /* @__PURE__ */ jsx28(
|
|
4811
4872
|
"div",
|
|
4812
4873
|
{
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
onDrop: (e) => {
|
|
4819
|
-
e.preventDefault();
|
|
4820
|
-
handleDropZoneDrop(materialKey);
|
|
4821
|
-
},
|
|
4822
|
-
onClick: () => {
|
|
4823
|
-
if (selectedOption !== null) {
|
|
4824
|
-
handleDropZoneDrop(materialKey);
|
|
4825
|
-
}
|
|
4826
|
-
},
|
|
4827
|
-
className: `${dropTargetIndex === materialKey ? "ring-2 ring-blue-400" : ""}`,
|
|
4874
|
+
"data-drop-zone": materialKey,
|
|
4875
|
+
onMouseEnter: () => draggedOption && setDropTargetIndex(materialKey),
|
|
4876
|
+
onMouseLeave: () => setDropTargetIndex(null),
|
|
4877
|
+
onClick: () => handleDropZoneClick(materialKey),
|
|
4878
|
+
className: `${dropTargetIndex === materialKey ? "ring-2 ring-blue-400 bg-blue-50" : ""} transition-all duration-200 rounded-lg`,
|
|
4828
4879
|
children: /* @__PURE__ */ jsxs17("div", { className: "w-full flex flex-row my-2 gap-x-2", children: [
|
|
4829
4880
|
/* @__PURE__ */ jsx28("div", { className: "my-auto", children: /* @__PURE__ */ jsxs17("p", { className: "text-xl", children: [
|
|
4830
4881
|
parseFloat(materialKey) + 1,
|
|
@@ -4834,9 +4885,10 @@ var FillInTheBlanksActivityMaterialContent = ({
|
|
|
4834
4885
|
/* @__PURE__ */ jsx28("div", { className: "flex-1", children: /* @__PURE__ */ jsx28(
|
|
4835
4886
|
"div",
|
|
4836
4887
|
{
|
|
4837
|
-
className: `w-full min-h-[44px] border rounded-lg ${answerMap[materialKey] ? "border-catchup-blue-400 px-2" : "bg-catchup-gray-50 border-catchup-gray-200 border-dashed py-2 px-4"}`,
|
|
4838
|
-
onClick: () => {
|
|
4888
|
+
className: `w-full min-h-[44px] border rounded-lg ${answerMap[materialKey] ? "border-catchup-blue-400 px-2 cursor-pointer" : "bg-catchup-gray-50 border-catchup-gray-200 border-dashed py-2 px-4"}`,
|
|
4889
|
+
onClick: (e) => {
|
|
4839
4890
|
if (answerMap[materialKey]) {
|
|
4891
|
+
e.stopPropagation();
|
|
4840
4892
|
onChange(answer, materialKey, "");
|
|
4841
4893
|
}
|
|
4842
4894
|
},
|
|
@@ -4846,7 +4898,7 @@ var FillInTheBlanksActivityMaterialContent = ({
|
|
|
4846
4898
|
value: answerMap[materialKey],
|
|
4847
4899
|
showSpecialOnly: false
|
|
4848
4900
|
}
|
|
4849
|
-
) : /* @__PURE__ */ jsx28("p", { className: "text-gray-400 italic" })
|
|
4901
|
+
) : /* @__PURE__ */ jsx28("p", { className: "text-gray-400 italic", children: i18n_default.t("please_drop_here") })
|
|
4850
4902
|
}
|
|
4851
4903
|
) }),
|
|
4852
4904
|
learnerAnswerState === "CORRECT" ? /* @__PURE__ */ jsx28("div", { className: "absolute -top-[10px] right-4 bg-catchup-white", children: /* @__PURE__ */ jsx28(
|
|
@@ -4874,7 +4926,8 @@ var FillInTheBlanksActivityMaterialContent = ({
|
|
|
4874
4926
|
"div",
|
|
4875
4927
|
{
|
|
4876
4928
|
className: "flex-1 cursor-pointer",
|
|
4877
|
-
onClick: () => {
|
|
4929
|
+
onClick: (e) => {
|
|
4930
|
+
e.stopPropagation();
|
|
4878
4931
|
onChange(answer, materialKey, "");
|
|
4879
4932
|
},
|
|
4880
4933
|
children: /* @__PURE__ */ jsx28(
|
|
@@ -5006,7 +5059,7 @@ var FillInTheBlanksActivityContent = ({
|
|
|
5006
5059
|
var FillInTheBlanksActivityContent_default = FillInTheBlanksActivityContent;
|
|
5007
5060
|
|
|
5008
5061
|
// src/components/activities/material-contents/GroupingActivityMaterialContent.tsx
|
|
5009
|
-
import { useEffect as useEffect10, useRef as
|
|
5062
|
+
import { useEffect as useEffect10, useRef as useRef6, useState as useState19 } from "react";
|
|
5010
5063
|
import { useDrop as useDrop2 } from "react-dnd";
|
|
5011
5064
|
import { InlineMath as InlineMath5 } from "react-katex";
|
|
5012
5065
|
|
|
@@ -5044,7 +5097,7 @@ var DraggableItem = ({
|
|
|
5044
5097
|
var DraggableItem_default = DraggableItem;
|
|
5045
5098
|
|
|
5046
5099
|
// src/components/dnds/DroppableItem.tsx
|
|
5047
|
-
import { useRef as
|
|
5100
|
+
import { useRef as useRef5 } from "react";
|
|
5048
5101
|
import { useDrop } from "react-dnd";
|
|
5049
5102
|
import { jsx as jsx31 } from "react/jsx-runtime";
|
|
5050
5103
|
var DroppableItem = ({
|
|
@@ -5055,7 +5108,7 @@ var DroppableItem = ({
|
|
|
5055
5108
|
target,
|
|
5056
5109
|
setTarget
|
|
5057
5110
|
}) => {
|
|
5058
|
-
const ref =
|
|
5111
|
+
const ref = useRef5(null);
|
|
5059
5112
|
const [, drop] = useDrop({
|
|
5060
5113
|
accept: type,
|
|
5061
5114
|
hover() {
|
|
@@ -5094,7 +5147,7 @@ var GroupingActivityMaterialContent = ({
|
|
|
5094
5147
|
canDrop: monitor.canDrop()
|
|
5095
5148
|
})
|
|
5096
5149
|
});
|
|
5097
|
-
const ref =
|
|
5150
|
+
const ref = useRef6(null);
|
|
5098
5151
|
useEffect10(() => {
|
|
5099
5152
|
const shuffleArray2 = (array) => {
|
|
5100
5153
|
if (!isShuffled) {
|
|
@@ -5374,7 +5427,7 @@ var GroupingActivityContent = ({
|
|
|
5374
5427
|
var GroupingActivityContent_default = GroupingActivityContent;
|
|
5375
5428
|
|
|
5376
5429
|
// src/components/activities/material-contents/MatchingActivityMaterialContent.tsx
|
|
5377
|
-
import { useEffect as useEffect11, useRef as
|
|
5430
|
+
import { useEffect as useEffect11, useRef as useRef7, useState as useState20 } from "react";
|
|
5378
5431
|
import { useDrop as useDrop3 } from "react-dnd";
|
|
5379
5432
|
import { InlineMath as InlineMath6 } from "react-katex";
|
|
5380
5433
|
import { Fragment as Fragment5, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
@@ -5401,7 +5454,7 @@ var MatchingActivityMaterialContent = ({
|
|
|
5401
5454
|
canDrop: monitor.canDrop()
|
|
5402
5455
|
})
|
|
5403
5456
|
});
|
|
5404
|
-
const itemsRef =
|
|
5457
|
+
const itemsRef = useRef7(null);
|
|
5405
5458
|
useEffect11(() => {
|
|
5406
5459
|
const shuffleArray2 = (array) => {
|
|
5407
5460
|
if (!isShuffled) {
|
|
@@ -6196,7 +6249,7 @@ var useScreenSize = () => {
|
|
|
6196
6249
|
var useScreenSize_default = useScreenSize;
|
|
6197
6250
|
|
|
6198
6251
|
// src/components/dnds/DraggableDroppableItem.tsx
|
|
6199
|
-
import { useRef as
|
|
6252
|
+
import { useRef as useRef8 } from "react";
|
|
6200
6253
|
import { useDrag as useDrag2, useDrop as useDrop4 } from "react-dnd";
|
|
6201
6254
|
import { jsx as jsx42 } from "react/jsx-runtime";
|
|
6202
6255
|
var DraggableDroppableItem = ({
|
|
@@ -6208,7 +6261,7 @@ var DraggableDroppableItem = ({
|
|
|
6208
6261
|
target,
|
|
6209
6262
|
setTarget
|
|
6210
6263
|
}) => {
|
|
6211
|
-
const ref =
|
|
6264
|
+
const ref = useRef8(null);
|
|
6212
6265
|
const [, drop] = useDrop4({
|
|
6213
6266
|
accept: type,
|
|
6214
6267
|
hover() {
|
package/package.json
CHANGED
package/src/components/activities/material-contents/FillInTheBlanksActivityMaterialContent.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { InlineMath } from "react-katex";
|
|
2
|
-
import { useState, useEffect } from "react";
|
|
2
|
+
import { useState, useEffect, useRef } from "react";
|
|
3
3
|
import BaseImage from "../../images/BaseImage";
|
|
4
4
|
import { shuffleArray } from "../../../utilization/AppUtilization";
|
|
5
5
|
import ShowMaterialMediaByContentType from "./ShowMaterialMediaByContentType";
|
|
@@ -20,37 +20,48 @@ const FillInTheBlanksActivityMaterialContent = ({
|
|
|
20
20
|
isPreview,
|
|
21
21
|
showCorrectAnswer,
|
|
22
22
|
}: IFillInTheBlanksActivityMaterialProps) => {
|
|
23
|
-
const [shuffleOptionList, setShuffleOptionList] = useState([]);
|
|
24
|
-
const [selectedOption, setSelectedOption] = useState(null);
|
|
25
|
-
const [draggedOption, setDraggedOption] = useState(null);
|
|
26
|
-
const [dropTargetIndex, setDropTargetIndex] = useState(null);
|
|
23
|
+
const [shuffleOptionList, setShuffleOptionList] = useState<any[]>([]);
|
|
24
|
+
const [selectedOption, setSelectedOption] = useState<any>(null);
|
|
25
|
+
const [draggedOption, setDraggedOption] = useState<any>(null);
|
|
26
|
+
const [dropTargetIndex, setDropTargetIndex] = useState<string | null>(null);
|
|
27
|
+
const [draggedElement, setDraggedElement] = useState<HTMLElement | null>(
|
|
28
|
+
null
|
|
29
|
+
);
|
|
30
|
+
const dragElementRef = useRef<HTMLDivElement>(null);
|
|
31
|
+
const [touchPosition, setTouchPosition] = useState<{ x: number; y: number }>({
|
|
32
|
+
x: 0,
|
|
33
|
+
y: 0,
|
|
34
|
+
});
|
|
27
35
|
|
|
28
36
|
useEffect(() => {
|
|
29
37
|
setShuffleOptionList(shuffleArray(optionList));
|
|
30
|
-
}, []);
|
|
38
|
+
}, [optionList]);
|
|
31
39
|
|
|
32
40
|
useEffect(() => {
|
|
33
41
|
if (!showCorrectAnswer) return;
|
|
34
42
|
const foundAnswer = answer.data.find(
|
|
35
43
|
(answerData: any) => answerData.type === "FILL_IN_THE_BLANKS"
|
|
36
44
|
);
|
|
37
|
-
if (foundAnswer.answerMap.length === 0) return;
|
|
45
|
+
if (!foundAnswer || foundAnswer.answerMap.length === 0) return;
|
|
38
46
|
if (Object.keys(materialMap).length === 0) return;
|
|
39
47
|
foundAnswer.answerMap = Object.keys(materialMap).map(
|
|
40
48
|
(materialMapKey) => JSON.parse(materialMap[materialMapKey])[0]
|
|
41
49
|
);
|
|
42
50
|
|
|
43
51
|
onChange(answer, 0, JSON.parse(materialMap[0])[0]);
|
|
44
|
-
}, [showCorrectAnswer]);
|
|
52
|
+
}, [showCorrectAnswer, answer, materialMap, onChange]);
|
|
45
53
|
|
|
46
|
-
const retrieveAnswerMap = () => {
|
|
54
|
+
const retrieveAnswerMap = (): Record<string, any> => {
|
|
47
55
|
const foundIndex = answer.data.findIndex(
|
|
48
56
|
(answerData: any) => answerData.type === "FILL_IN_THE_BLANKS"
|
|
49
57
|
);
|
|
50
58
|
return answer.data[foundIndex].answerMap;
|
|
51
59
|
};
|
|
52
60
|
|
|
53
|
-
const checkAnswerState = (
|
|
61
|
+
const checkAnswerState = (
|
|
62
|
+
correctAnswerList: any[],
|
|
63
|
+
learnerAnswer: string
|
|
64
|
+
): string | null => {
|
|
54
65
|
if (!isPreview) return null;
|
|
55
66
|
const foundIndex = correctAnswerList.findIndex(
|
|
56
67
|
(correctAnswer: string) => correctAnswer === learnerAnswer
|
|
@@ -61,42 +72,85 @@ const FillInTheBlanksActivityMaterialContent = ({
|
|
|
61
72
|
return "INCORRECT";
|
|
62
73
|
};
|
|
63
74
|
|
|
64
|
-
const checkAnswerProvided = (
|
|
75
|
+
const checkAnswerProvided = (
|
|
76
|
+
answerMap: Record<string, any>,
|
|
77
|
+
option: string
|
|
78
|
+
): boolean => {
|
|
65
79
|
return (
|
|
66
80
|
Object.keys(answerMap).findIndex((key) => answerMap[key] === option) !==
|
|
67
81
|
-1
|
|
68
82
|
);
|
|
69
83
|
};
|
|
70
84
|
|
|
71
|
-
|
|
72
|
-
|
|
85
|
+
// Mouse drag handlers
|
|
86
|
+
const handleMouseDown = (e: React.MouseEvent, option: any): void => {
|
|
87
|
+
e.preventDefault();
|
|
88
|
+
setDraggedOption(option);
|
|
89
|
+
setSelectedOption(null);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const handleMouseUp = (): void => {
|
|
93
|
+
if (dropTargetIndex !== null && draggedOption !== null) {
|
|
94
|
+
onChange(answer, dropTargetIndex, draggedOption);
|
|
95
|
+
}
|
|
96
|
+
setDraggedOption(null);
|
|
97
|
+
setDropTargetIndex(null);
|
|
73
98
|
};
|
|
74
99
|
|
|
75
|
-
|
|
100
|
+
// Touch drag handlers
|
|
101
|
+
const handleTouchStart = (
|
|
102
|
+
e: React.TouchEvent,
|
|
103
|
+
option: any,
|
|
104
|
+
element: HTMLElement
|
|
105
|
+
): void => {
|
|
106
|
+
const touch = e.touches[0];
|
|
76
107
|
setDraggedOption(option);
|
|
108
|
+
setDraggedElement(element);
|
|
109
|
+
setTouchPosition({ x: touch.clientX, y: touch.clientY });
|
|
110
|
+
setSelectedOption(null);
|
|
77
111
|
};
|
|
78
112
|
|
|
79
|
-
const
|
|
113
|
+
const handleTouchMove = (e: React.TouchEvent): void => {
|
|
114
|
+
if (!draggedOption) return;
|
|
115
|
+
|
|
116
|
+
const touch = e.touches[0];
|
|
117
|
+
setTouchPosition({ x: touch.clientX, y: touch.clientY });
|
|
118
|
+
|
|
119
|
+
// Find the element under the touch point
|
|
120
|
+
const elementUnder = document.elementFromPoint(
|
|
121
|
+
touch.clientX,
|
|
122
|
+
touch.clientY
|
|
123
|
+
);
|
|
124
|
+
const dropZone = elementUnder?.closest("[data-drop-zone]");
|
|
125
|
+
|
|
126
|
+
if (dropZone) {
|
|
127
|
+
const dropIndex = dropZone.getAttribute("data-drop-zone");
|
|
128
|
+
setDropTargetIndex(dropIndex);
|
|
129
|
+
} else {
|
|
130
|
+
setDropTargetIndex(null);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const handleTouchEnd = (): void => {
|
|
80
135
|
if (dropTargetIndex !== null && draggedOption !== null) {
|
|
81
136
|
onChange(answer, dropTargetIndex, draggedOption);
|
|
82
137
|
}
|
|
83
138
|
setDraggedOption(null);
|
|
84
139
|
setDropTargetIndex(null);
|
|
140
|
+
setDraggedElement(null);
|
|
85
141
|
};
|
|
86
142
|
|
|
87
|
-
|
|
88
|
-
|
|
143
|
+
// Click/tap to select (for easier mobile interaction)
|
|
144
|
+
const handleSelectOption = (option: any): void => {
|
|
145
|
+
setSelectedOption(option);
|
|
146
|
+
setDraggedOption(null);
|
|
89
147
|
};
|
|
90
148
|
|
|
91
|
-
const
|
|
149
|
+
const handleDropZoneClick = (index: string): void => {
|
|
92
150
|
if (selectedOption !== null) {
|
|
93
151
|
onChange(answer, index, selectedOption);
|
|
94
152
|
setSelectedOption(null);
|
|
95
|
-
} else if (draggedOption !== null) {
|
|
96
|
-
onChange(answer, index, draggedOption);
|
|
97
|
-
setDraggedOption(null);
|
|
98
153
|
}
|
|
99
|
-
setDropTargetIndex(null);
|
|
100
154
|
};
|
|
101
155
|
|
|
102
156
|
const answerMap = retrieveAnswerMap();
|
|
@@ -112,6 +166,38 @@ const FillInTheBlanksActivityMaterialContent = ({
|
|
|
112
166
|
<DividerLine />
|
|
113
167
|
</div>
|
|
114
168
|
|
|
169
|
+
{/* Floating drag preview for touch */}
|
|
170
|
+
{draggedOption && touchPosition.x > 0 && (
|
|
171
|
+
<div
|
|
172
|
+
className="fixed pointer-events-none z-50 opacity-80"
|
|
173
|
+
style={{
|
|
174
|
+
left: `${touchPosition.x}px`,
|
|
175
|
+
top: `${touchPosition.y}px`,
|
|
176
|
+
transform: "translate(-50%, -50%)",
|
|
177
|
+
}}
|
|
178
|
+
>
|
|
179
|
+
{contentMap.type === "TEXT" ? (
|
|
180
|
+
<div className="border-catchup-blue border-2 px-2 rounded-catchup-xlarge bg-white shadow-lg">
|
|
181
|
+
<p className="italic whitespace-pre-wrap">
|
|
182
|
+
<InputWithSpecialExpression
|
|
183
|
+
value={draggedOption}
|
|
184
|
+
showSpecialOnly={false}
|
|
185
|
+
/>
|
|
186
|
+
</p>
|
|
187
|
+
</div>
|
|
188
|
+
) : (
|
|
189
|
+
<div className="border-catchup-blue border-2 px-2 py-1 rounded-catchup-xlarge bg-white shadow-lg">
|
|
190
|
+
<ShowMaterialMediaByContentType
|
|
191
|
+
key={uniqueValue}
|
|
192
|
+
contentType={contentMap.type}
|
|
193
|
+
src={draggedOption}
|
|
194
|
+
canFullScreen={false}
|
|
195
|
+
/>
|
|
196
|
+
</div>
|
|
197
|
+
)}
|
|
198
|
+
</div>
|
|
199
|
+
)}
|
|
200
|
+
|
|
115
201
|
<div className="w-full flex flex-row flex-wrap gap-x-2 gap-y-2 my-2">
|
|
116
202
|
{shuffleOptionList.map((option, index) =>
|
|
117
203
|
checkAnswerProvided(answerMap, option) ? (
|
|
@@ -126,18 +212,23 @@ const FillInTheBlanksActivityMaterialContent = ({
|
|
|
126
212
|
) : (
|
|
127
213
|
<div
|
|
128
214
|
key={index}
|
|
129
|
-
|
|
130
|
-
onDragStart={() => handleDragStart(option)}
|
|
131
|
-
onDragEnd={handleDragEnd}
|
|
215
|
+
ref={draggedOption === option ? dragElementRef : null}
|
|
132
216
|
className={`${
|
|
133
|
-
draggedOption === option
|
|
134
|
-
|
|
217
|
+
draggedOption === option
|
|
218
|
+
? "opacity-40"
|
|
219
|
+
: selectedOption === option
|
|
220
|
+
? "ring-2 ring-blue-500"
|
|
221
|
+
: "opacity-100"
|
|
222
|
+
} transition-all duration-200`}
|
|
223
|
+
onMouseDown={(e) => handleMouseDown(e, option)}
|
|
224
|
+
onTouchStart={(e) => handleTouchStart(e, option, e.currentTarget)}
|
|
225
|
+
onTouchMove={handleTouchMove}
|
|
226
|
+
onTouchEnd={handleTouchEnd}
|
|
135
227
|
>
|
|
136
228
|
{contentMap.type === "TEXT" ? (
|
|
137
229
|
<div
|
|
138
|
-
className="border-catchup-blue border-2 px-2 rounded-catchup-xlarge cursor-pointer select-none
|
|
230
|
+
className="border-catchup-blue border-2 px-2 rounded-catchup-xlarge cursor-pointer select-none"
|
|
139
231
|
onClick={() => handleSelectOption(option)}
|
|
140
|
-
onTouchEnd={() => handleSelectOption(option)}
|
|
141
232
|
>
|
|
142
233
|
<p className="italic whitespace-pre-wrap">
|
|
143
234
|
<InputWithSpecialExpression
|
|
@@ -148,9 +239,8 @@ const FillInTheBlanksActivityMaterialContent = ({
|
|
|
148
239
|
</div>
|
|
149
240
|
) : (
|
|
150
241
|
<div
|
|
151
|
-
className="border-catchup-blue border-2 px-2 py-1 rounded-catchup-xlarge cursor-pointer select-none
|
|
242
|
+
className="border-catchup-blue border-2 px-2 py-1 rounded-catchup-xlarge cursor-pointer select-none"
|
|
152
243
|
onClick={() => handleSelectOption(option)}
|
|
153
|
-
onTouchEnd={() => handleSelectOption(option)}
|
|
154
244
|
>
|
|
155
245
|
<ShowMaterialMediaByContentType
|
|
156
246
|
key={`${uniqueValue}-${index}`}
|
|
@@ -164,7 +254,7 @@ const FillInTheBlanksActivityMaterialContent = ({
|
|
|
164
254
|
)
|
|
165
255
|
)}
|
|
166
256
|
</div>
|
|
167
|
-
<div className="w-full flex flex-row flex-wrap">
|
|
257
|
+
<div className="w-full flex flex-row flex-wrap" onMouseUp={handleMouseUp}>
|
|
168
258
|
{Object.keys(answerMap).map((materialKey, index) => {
|
|
169
259
|
const learnerAnswerState = checkAnswerState(
|
|
170
260
|
JSON.parse(materialMap[materialKey]),
|
|
@@ -174,25 +264,17 @@ const FillInTheBlanksActivityMaterialContent = ({
|
|
|
174
264
|
<div key={index} className="w-full md:w-1/2">
|
|
175
265
|
<div className="mx-2">
|
|
176
266
|
<div
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
e.preventDefault();
|
|
184
|
-
handleDropZoneDrop(materialKey);
|
|
185
|
-
}}
|
|
186
|
-
onClick={() => {
|
|
187
|
-
if (selectedOption !== null) {
|
|
188
|
-
handleDropZoneDrop(materialKey);
|
|
189
|
-
}
|
|
190
|
-
}}
|
|
267
|
+
data-drop-zone={materialKey}
|
|
268
|
+
onMouseEnter={() =>
|
|
269
|
+
draggedOption && setDropTargetIndex(materialKey)
|
|
270
|
+
}
|
|
271
|
+
onMouseLeave={() => setDropTargetIndex(null)}
|
|
272
|
+
onClick={() => handleDropZoneClick(materialKey)}
|
|
191
273
|
className={`${
|
|
192
274
|
dropTargetIndex === materialKey
|
|
193
|
-
? "ring-2 ring-blue-400"
|
|
275
|
+
? "ring-2 ring-blue-400 bg-blue-50"
|
|
194
276
|
: ""
|
|
195
|
-
}`}
|
|
277
|
+
} transition-all duration-200 rounded-lg`}
|
|
196
278
|
>
|
|
197
279
|
<div className="w-full flex flex-row my-2 gap-x-2">
|
|
198
280
|
<div className="my-auto">
|
|
@@ -206,11 +288,12 @@ const FillInTheBlanksActivityMaterialContent = ({
|
|
|
206
288
|
<div
|
|
207
289
|
className={`w-full min-h-[44px] border rounded-lg ${
|
|
208
290
|
answerMap[materialKey]
|
|
209
|
-
? "border-catchup-blue-400 px-2"
|
|
291
|
+
? "border-catchup-blue-400 px-2 cursor-pointer"
|
|
210
292
|
: "bg-catchup-gray-50 border-catchup-gray-200 border-dashed py-2 px-4"
|
|
211
293
|
}`}
|
|
212
|
-
onClick={() => {
|
|
294
|
+
onClick={(e) => {
|
|
213
295
|
if (answerMap[materialKey]) {
|
|
296
|
+
e.stopPropagation();
|
|
214
297
|
onChange(answer, materialKey, "");
|
|
215
298
|
}
|
|
216
299
|
}}
|
|
@@ -221,7 +304,9 @@ const FillInTheBlanksActivityMaterialContent = ({
|
|
|
221
304
|
showSpecialOnly={false}
|
|
222
305
|
/>
|
|
223
306
|
) : (
|
|
224
|
-
<p className="text-gray-400 italic"
|
|
307
|
+
<p className="text-gray-400 italic">
|
|
308
|
+
{i18n.t("please_drop_here")}
|
|
309
|
+
</p>
|
|
225
310
|
)}
|
|
226
311
|
</div>
|
|
227
312
|
</div>
|
|
@@ -263,7 +348,8 @@ const FillInTheBlanksActivityMaterialContent = ({
|
|
|
263
348
|
) : (
|
|
264
349
|
<div
|
|
265
350
|
className="flex-1 cursor-pointer"
|
|
266
|
-
onClick={() => {
|
|
351
|
+
onClick={(e) => {
|
|
352
|
+
e.stopPropagation();
|
|
267
353
|
onChange(answer, materialKey, "");
|
|
268
354
|
}}
|
|
269
355
|
>
|