catchup-library-web 1.15.3 → 1.15.4
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.d.mts +9 -2
- package/dist/index.d.ts +9 -2
- package/dist/index.js +649 -563
- package/dist/index.mjs +608 -523
- package/package.json +2 -1
- package/src/components/activities/ActivityPreviewByData.tsx +1 -0
- package/src/components/activities/OpenEndedActivityContent.tsx +2 -31
- package/src/components/activities/body-content/ShowBodyMediaByContentType.tsx +4 -24
- package/src/components/activities/material-content/OpenEndedActivityMaterialContent.tsx +52 -2
- package/src/components/pdfs/BasePDF.tsx +69 -0
- package/src/index.ts +2 -0
- package/src/properties/ActivityProperties.ts +2 -0
- package/src/properties/PDFProperties.ts +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "catchup-library-web",
|
|
3
|
-
"version": "1.15.
|
|
3
|
+
"version": "1.15.4",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"react-katex": "^3.0.1",
|
|
23
23
|
"react-loader-spinner": "^6.1.6",
|
|
24
24
|
"react-modal": "^3.16.3",
|
|
25
|
+
"react-pdf": "^10.0.1",
|
|
25
26
|
"react-select": "^5.10.0"
|
|
26
27
|
},
|
|
27
28
|
"devDependencies": {
|
|
@@ -12,27 +12,12 @@ const OpenEndedActivityContent = ({
|
|
|
12
12
|
answer,
|
|
13
13
|
data,
|
|
14
14
|
changeAnswer,
|
|
15
|
+
canAnswerQuestion,
|
|
15
16
|
showMaterialContent,
|
|
16
17
|
isFullScreen,
|
|
17
18
|
}: IOpenEndedActivityProps) => {
|
|
18
|
-
// const {
|
|
19
|
-
// answer,
|
|
20
|
-
// data,
|
|
21
|
-
// canAnswerQuestion,
|
|
22
|
-
// changeAnswer,
|
|
23
|
-
// userId,
|
|
24
|
-
// userProfileId,
|
|
25
|
-
// catchtivityApplicationId,
|
|
26
|
-
// catchxamApplicationId,
|
|
27
|
-
// etudeId,
|
|
28
|
-
// activityId,
|
|
29
|
-
// activityTemplateId,
|
|
30
|
-
// showMaterialContent,
|
|
31
|
-
// storageStompClient,
|
|
32
|
-
// } = props;
|
|
33
19
|
const contentMap = parseContentMapFromData(data);
|
|
34
20
|
const openEndedBodyMap = parseBodyMapFromData(data, "OPEN_ENDED");
|
|
35
|
-
// const openEndedMaterialMap = JSON.parse(data.openEndedMaterialMap);
|
|
36
21
|
|
|
37
22
|
const handleOpenEndedAnswerOnChange = (answer: any, value: string) => {
|
|
38
23
|
let foundIndex = answer.data.findIndex(
|
|
@@ -74,23 +59,9 @@ const OpenEndedActivityContent = ({
|
|
|
74
59
|
<OpenEndedActivityMaterialContent
|
|
75
60
|
answer={answer}
|
|
76
61
|
contentMap={contentMap}
|
|
77
|
-
onChange={handleOpenEndedAnswerOnChange}
|
|
78
|
-
/>
|
|
79
|
-
{/* <OpenEndedActivityMaterialContent
|
|
80
|
-
answer={answer}
|
|
81
|
-
bodyMap={openEndedBodyMap}
|
|
82
|
-
contentMap={contentMap}
|
|
83
62
|
checkCanAnswerQuestion={canAnswerQuestion}
|
|
84
63
|
onChange={handleOpenEndedAnswerOnChange}
|
|
85
|
-
|
|
86
|
-
userProfileId={userProfileId}
|
|
87
|
-
catchtivityApplicationId={catchtivityApplicationId}
|
|
88
|
-
catchxamApplicationId={catchxamApplicationId}
|
|
89
|
-
etudeId={etudeId}
|
|
90
|
-
activityId={activityId}
|
|
91
|
-
activityTemplateId={activityTemplateId}
|
|
92
|
-
storageStompClient={storageStompClient}
|
|
93
|
-
/> */}
|
|
64
|
+
/>
|
|
94
65
|
</div>
|
|
95
66
|
</>
|
|
96
67
|
) : null}
|
|
@@ -109,7 +109,6 @@ const ShowBodyMediaByContentType = ({
|
|
|
109
109
|
const firstIndex = copyValue.indexOf(checkText);
|
|
110
110
|
const textBeforeTag = copyValue.substring(0, firstIndex);
|
|
111
111
|
|
|
112
|
-
// Handle text before the tag if it's not empty
|
|
113
112
|
if (textBeforeTag.trim() !== "") {
|
|
114
113
|
const balancedText = balanceSpecialChars(textBeforeTag);
|
|
115
114
|
currentIndex++;
|
|
@@ -117,10 +116,9 @@ const ShowBodyMediaByContentType = ({
|
|
|
117
116
|
valuePartList.push(renderTextContent(balancedText, itemKey));
|
|
118
117
|
}
|
|
119
118
|
|
|
120
|
-
// Process the text inside the tags
|
|
121
119
|
const subValue = copyValue.substring(firstIndex + checkText.length);
|
|
122
120
|
const secondIndex = subValue.indexOf(checkText);
|
|
123
|
-
if (secondIndex === -1) break;
|
|
121
|
+
if (secondIndex === -1) break;
|
|
124
122
|
|
|
125
123
|
const textInsideTag = copyValue.substring(
|
|
126
124
|
firstIndex + checkText.length,
|
|
@@ -138,26 +136,22 @@ const ShowBodyMediaByContentType = ({
|
|
|
138
136
|
</span>
|
|
139
137
|
);
|
|
140
138
|
|
|
141
|
-
// Update copyValue to process the next occurrence
|
|
142
139
|
copyValue = copyValue.substring(
|
|
143
140
|
firstIndex + checkText.length + secondIndex + checkText.length
|
|
144
141
|
);
|
|
145
142
|
|
|
146
|
-
// Balance backticks in remaining text if needed
|
|
147
143
|
if ((copyValue.split("`").length - 1) % 2 === 1) {
|
|
148
144
|
copyValue = "`" + copyValue;
|
|
149
145
|
}
|
|
150
146
|
}
|
|
151
147
|
};
|
|
152
148
|
|
|
153
|
-
// Process IMAGE tags
|
|
154
149
|
const processImageTags = () => {
|
|
155
150
|
const checkText = "--IMAGE--";
|
|
156
151
|
while (copyValue.includes(checkText)) {
|
|
157
152
|
const firstIndex = copyValue.indexOf(checkText);
|
|
158
153
|
const textBeforeTag = copyValue.substring(0, firstIndex);
|
|
159
154
|
|
|
160
|
-
// Handle text before the tag if it's not empty
|
|
161
155
|
if (textBeforeTag.trim() !== "") {
|
|
162
156
|
currentIndex++;
|
|
163
157
|
valuePartList.push(
|
|
@@ -170,10 +164,9 @@ const ShowBodyMediaByContentType = ({
|
|
|
170
164
|
);
|
|
171
165
|
}
|
|
172
166
|
|
|
173
|
-
// Process the image source inside the tags
|
|
174
167
|
const subValue = copyValue.substring(firstIndex + checkText.length);
|
|
175
168
|
const secondIndex = subValue.indexOf(checkText);
|
|
176
|
-
if (secondIndex === -1) break;
|
|
169
|
+
if (secondIndex === -1) break;
|
|
177
170
|
|
|
178
171
|
const imageSource = copyValue.substring(
|
|
179
172
|
firstIndex + checkText.length,
|
|
@@ -206,21 +199,18 @@ const ShowBodyMediaByContentType = ({
|
|
|
206
199
|
</div>
|
|
207
200
|
);
|
|
208
201
|
|
|
209
|
-
// Update copyValue to process the next occurrence
|
|
210
202
|
copyValue = copyValue.substring(
|
|
211
203
|
firstIndex + checkText.length + secondIndex + checkText.length
|
|
212
204
|
);
|
|
213
205
|
}
|
|
214
206
|
};
|
|
215
207
|
|
|
216
|
-
// Process VIDEO tags
|
|
217
208
|
const processVideoTags = () => {
|
|
218
209
|
const checkText = "--VIDEO--";
|
|
219
210
|
while (copyValue.includes(checkText)) {
|
|
220
211
|
const firstIndex = copyValue.indexOf(checkText);
|
|
221
212
|
const textBeforeTag = copyValue.substring(0, firstIndex);
|
|
222
213
|
|
|
223
|
-
// Handle text before the tag if it's not empty
|
|
224
214
|
if (textBeforeTag.trim() !== "") {
|
|
225
215
|
currentIndex++;
|
|
226
216
|
valuePartList.push(
|
|
@@ -233,10 +223,9 @@ const ShowBodyMediaByContentType = ({
|
|
|
233
223
|
);
|
|
234
224
|
}
|
|
235
225
|
|
|
236
|
-
// Process the video source inside the tags
|
|
237
226
|
const subValue = copyValue.substring(firstIndex + checkText.length);
|
|
238
227
|
const secondIndex = subValue.indexOf(checkText);
|
|
239
|
-
if (secondIndex === -1) break;
|
|
228
|
+
if (secondIndex === -1) break;
|
|
240
229
|
|
|
241
230
|
const videoSource = copyValue.substring(
|
|
242
231
|
firstIndex + checkText.length,
|
|
@@ -253,21 +242,18 @@ const ShowBodyMediaByContentType = ({
|
|
|
253
242
|
/>
|
|
254
243
|
);
|
|
255
244
|
|
|
256
|
-
// Update copyValue to process the next occurrence
|
|
257
245
|
copyValue = copyValue.substring(
|
|
258
246
|
firstIndex + checkText.length + secondIndex + checkText.length
|
|
259
247
|
);
|
|
260
248
|
}
|
|
261
249
|
};
|
|
262
250
|
|
|
263
|
-
// Process AUDIO tags
|
|
264
251
|
const processAudioTags = () => {
|
|
265
252
|
const checkText = "--AUDIO--";
|
|
266
253
|
while (copyValue.includes(checkText)) {
|
|
267
254
|
const firstIndex = copyValue.indexOf(checkText);
|
|
268
255
|
const textBeforeTag = copyValue.substring(0, firstIndex);
|
|
269
256
|
|
|
270
|
-
// Handle text before the tag if it's not empty
|
|
271
257
|
if (textBeforeTag.trim() !== "") {
|
|
272
258
|
currentIndex++;
|
|
273
259
|
valuePartList.push(
|
|
@@ -280,10 +266,9 @@ const ShowBodyMediaByContentType = ({
|
|
|
280
266
|
);
|
|
281
267
|
}
|
|
282
268
|
|
|
283
|
-
// Process the audio source inside the tags
|
|
284
269
|
const subValue = copyValue.substring(firstIndex + checkText.length);
|
|
285
270
|
const secondIndex = subValue.indexOf(checkText);
|
|
286
|
-
if (secondIndex === -1) break;
|
|
271
|
+
if (secondIndex === -1) break;
|
|
287
272
|
|
|
288
273
|
const audioSource = copyValue.substring(
|
|
289
274
|
firstIndex + checkText.length,
|
|
@@ -300,25 +285,21 @@ const ShowBodyMediaByContentType = ({
|
|
|
300
285
|
/>
|
|
301
286
|
);
|
|
302
287
|
|
|
303
|
-
// Update copyValue to process the next occurrence
|
|
304
288
|
copyValue = copyValue.substring(
|
|
305
289
|
firstIndex + checkText.length + secondIndex + checkText.length
|
|
306
290
|
);
|
|
307
291
|
}
|
|
308
292
|
};
|
|
309
293
|
|
|
310
|
-
// Process all types of tags
|
|
311
294
|
processTextTags();
|
|
312
295
|
processImageTags();
|
|
313
296
|
processVideoTags();
|
|
314
297
|
processAudioTags();
|
|
315
298
|
|
|
316
|
-
// Handle any remaining text
|
|
317
299
|
if (copyValue.trim() !== "") {
|
|
318
300
|
currentIndex++;
|
|
319
301
|
copyValue = balanceSpecialChars(copyValue);
|
|
320
302
|
|
|
321
|
-
// Check for image description markup
|
|
322
303
|
const regexMatchImageText = copyValue.match(/<image>([\s\S]*?)<\/image>/);
|
|
323
304
|
if (regexMatchImageText) {
|
|
324
305
|
const imageText = regexMatchImageText[1];
|
|
@@ -334,7 +315,6 @@ const ShowBodyMediaByContentType = ({
|
|
|
334
315
|
</div>
|
|
335
316
|
);
|
|
336
317
|
} else {
|
|
337
|
-
// Handle normal remaining text
|
|
338
318
|
const itemKey = `remaining-${index}-${currentIndex}`;
|
|
339
319
|
valuePartList.push(renderTextContent(copyValue, itemKey));
|
|
340
320
|
}
|
|
@@ -2,10 +2,14 @@ import InputGroup from "../../groups/InputGroup";
|
|
|
2
2
|
import i18n from "../../../language/i18n";
|
|
3
3
|
import DividerLine from "../../dividers/DividerLine";
|
|
4
4
|
import { IOpenEndedActivityMaterialProps } from "../../../properties/ActivityProperties";
|
|
5
|
+
import { retrieveDocumentTypeFromExtension } from "../../../utilization/StorageUtilization";
|
|
6
|
+
import BaseImage from "../../images/BaseImage";
|
|
7
|
+
import BasePDF from "../../pdfs/BasePDF";
|
|
5
8
|
|
|
6
9
|
const OpenEndedActivityMaterialContent = ({
|
|
7
10
|
answer,
|
|
8
11
|
contentMap,
|
|
12
|
+
checkCanAnswerQuestion,
|
|
9
13
|
onChange,
|
|
10
14
|
}: IOpenEndedActivityMaterialProps) => {
|
|
11
15
|
const retrieveAnswer = () => {
|
|
@@ -33,12 +37,52 @@ const OpenEndedActivityMaterialContent = ({
|
|
|
33
37
|
value={answerMapAnswer}
|
|
34
38
|
useMinHeight={true}
|
|
35
39
|
onChange={(e) => {
|
|
36
|
-
|
|
40
|
+
if (checkCanAnswerQuestion()) {
|
|
41
|
+
onChange(answer, e.target.value);
|
|
42
|
+
}
|
|
37
43
|
}}
|
|
38
44
|
/>
|
|
39
45
|
);
|
|
40
46
|
};
|
|
41
47
|
|
|
48
|
+
const RenderImageContent = (answerMap: any) => {
|
|
49
|
+
const answerMapAnswer = answerMap["ANSWER"];
|
|
50
|
+
const extension = answerMapAnswer.split(".").pop();
|
|
51
|
+
const documentType = retrieveDocumentTypeFromExtension(extension);
|
|
52
|
+
return (
|
|
53
|
+
<>
|
|
54
|
+
{documentType === "IMAGE" ? (
|
|
55
|
+
<div className="flex flex-col justify-center items-center my-5">
|
|
56
|
+
<BaseImage
|
|
57
|
+
src={answerMapAnswer}
|
|
58
|
+
alt="document"
|
|
59
|
+
size="custom"
|
|
60
|
+
className="w-[80%] rounded-catchup-xlarge"
|
|
61
|
+
/>
|
|
62
|
+
</div>
|
|
63
|
+
) : documentType === "PDF" ? (
|
|
64
|
+
<div className="flex flex-col justify-center items-center my-5">
|
|
65
|
+
<BasePDF file={answerMapAnswer} />
|
|
66
|
+
</div>
|
|
67
|
+
) : null}
|
|
68
|
+
</>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const RenderAudioContent = (answerMap: any) => {
|
|
73
|
+
const answerMapAnswer = answerMap["ANSWER"];
|
|
74
|
+
return (
|
|
75
|
+
<div className="h-[56px]">
|
|
76
|
+
<audio
|
|
77
|
+
className="h-full w-full rounded-catchup-xlarge"
|
|
78
|
+
src={answerMapAnswer}
|
|
79
|
+
controls
|
|
80
|
+
onClick={() => {}}
|
|
81
|
+
/>
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
85
|
+
|
|
42
86
|
const answerMap = retrieveAnswerMap();
|
|
43
87
|
|
|
44
88
|
return (
|
|
@@ -52,7 +96,13 @@ const OpenEndedActivityMaterialContent = ({
|
|
|
52
96
|
<div className="hidden md:contents">
|
|
53
97
|
<DividerLine />
|
|
54
98
|
</div>
|
|
55
|
-
{contentMap.type === "TEXT"
|
|
99
|
+
{contentMap.type === "TEXT"
|
|
100
|
+
? RenderTextContent(answerMap)
|
|
101
|
+
: contentMap.type === "IMAGE"
|
|
102
|
+
? RenderImageContent(answerMap)
|
|
103
|
+
: contentMap.type === "AUDIO"
|
|
104
|
+
? RenderAudioContent(answerMap)
|
|
105
|
+
: null}
|
|
56
106
|
</div>
|
|
57
107
|
</>
|
|
58
108
|
);
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { Document, Page } from "react-pdf";
|
|
3
|
+
import BaseImage from "../images/BaseImage";
|
|
4
|
+
import { IBasePDFProps } from "../../properties/PDFProperties";
|
|
5
|
+
|
|
6
|
+
const BasePDF = ({ file }: IBasePDFProps) => {
|
|
7
|
+
const [pageNumber, setPageNumber] = useState(1);
|
|
8
|
+
const [numberOfPages, setNumberOfPages] = useState(0);
|
|
9
|
+
|
|
10
|
+
const handleOnDocumentLoadSuccess = ({ numPages }: any) => {
|
|
11
|
+
setPageNumber(1);
|
|
12
|
+
setNumberOfPages(numPages);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<Document file={file} onLoadSuccess={handleOnDocumentLoadSuccess}>
|
|
17
|
+
<Page pageNumber={pageNumber} />
|
|
18
|
+
<div className="flex flex-row items-center justify-center">
|
|
19
|
+
{pageNumber === 1 ? null : (
|
|
20
|
+
<div className="px-2">
|
|
21
|
+
<div className="cursor-pointer">
|
|
22
|
+
<BaseImage
|
|
23
|
+
alt="arrow-left"
|
|
24
|
+
src="/icons/arrow-left.webp"
|
|
25
|
+
size="small"
|
|
26
|
+
onClick={() => {
|
|
27
|
+
setPageNumber(pageNumber - 1);
|
|
28
|
+
}}
|
|
29
|
+
/>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
)}
|
|
33
|
+
{Array.from(Array(numberOfPages).keys())
|
|
34
|
+
.filter((index) => index < pageNumber + 5)
|
|
35
|
+
.filter((index) => index > pageNumber - 5)
|
|
36
|
+
.map((index) => (
|
|
37
|
+
<div key={index} className="px-2">
|
|
38
|
+
<p
|
|
39
|
+
className={`${
|
|
40
|
+
pageNumber === index + 1 ? "text-2xl" : "text-md"
|
|
41
|
+
} cursor-pointer`}
|
|
42
|
+
onClick={() => {
|
|
43
|
+
setPageNumber(index + 1);
|
|
44
|
+
}}
|
|
45
|
+
>
|
|
46
|
+
{index + 1}
|
|
47
|
+
</p>
|
|
48
|
+
</div>
|
|
49
|
+
))}
|
|
50
|
+
{numberOfPages === 0 || pageNumber === numberOfPages ? null : (
|
|
51
|
+
<div className="px-2">
|
|
52
|
+
<div className="cursor-pointer">
|
|
53
|
+
<BaseImage
|
|
54
|
+
src="/icons/arrow-right.webp"
|
|
55
|
+
alt="arrow-right"
|
|
56
|
+
size="small"
|
|
57
|
+
onClick={() => {
|
|
58
|
+
setPageNumber(pageNumber + 1);
|
|
59
|
+
}}
|
|
60
|
+
/>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
)}
|
|
64
|
+
</div>
|
|
65
|
+
</Document>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export default BasePDF;
|
package/src/index.ts
CHANGED
|
@@ -18,6 +18,8 @@ export { default as BaseLoadingWithText } from "./components/loading/BaseLoading
|
|
|
18
18
|
|
|
19
19
|
export { default as BaseModal } from "./components/modals/BaseModal";
|
|
20
20
|
|
|
21
|
+
export { default as BasePDF } from "./components/pdfs/BasePDF";
|
|
22
|
+
|
|
21
23
|
export { default as ActivityEmptyContent } from "./components/activities/empty-content/ActivityEmptyContent";
|
|
22
24
|
export { default as ActivityBodyContent } from "./components/activities/body-content/ActivityBodyContent";
|
|
23
25
|
export { default as DropdownActivityContent } from "./components/activities/DropdownActivityContent";
|
|
@@ -169,6 +169,7 @@ export interface IOpenEndedActivityProps {
|
|
|
169
169
|
answer: any;
|
|
170
170
|
data: any;
|
|
171
171
|
changeAnswer: (e: any) => void;
|
|
172
|
+
canAnswerQuestion: () => boolean;
|
|
172
173
|
showMaterialContent: boolean;
|
|
173
174
|
isFullScreen: boolean;
|
|
174
175
|
}
|
|
@@ -176,6 +177,7 @@ export interface IOpenEndedActivityProps {
|
|
|
176
177
|
export interface IOpenEndedActivityMaterialProps {
|
|
177
178
|
answer: any;
|
|
178
179
|
contentMap: any;
|
|
180
|
+
checkCanAnswerQuestion: () => boolean;
|
|
179
181
|
onChange: (e1: any, e2: any) => void;
|
|
180
182
|
}
|
|
181
183
|
|