@nualang/nualang-ui-components 0.1.1419 → 0.1.1421

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.
@@ -62,8 +62,10 @@ const sxInfoColorWithMargin = theme => ({
62
62
  marginRight: theme.spacing(2),
63
63
  marginBottom: theme.spacing(2)
64
64
  });
65
- const sxDescriptionWrapper = () => ({
66
- maxWidth: "70%"
65
+ const sxTitleDescriptionWrapper = () => ({
66
+ maxWidth: "70%",
67
+ overflowWrap: "anywhere",
68
+ wordBreak: "break-word"
67
69
  });
68
70
 
69
71
  // Open an add-menu at the mouse cursor for pointer clicks, but fall back to
@@ -378,7 +380,16 @@ function PhraseListRow({
378
380
  alt: ""
379
381
  })
380
382
  }), /*#__PURE__*/_jsx(ListItemText, {
381
- primary: isLargeScreen ? phraseList.phraseListName || "" : /*#__PURE__*/_jsxs(_Fragment, {
383
+ primaryTypographyProps: {
384
+ component: "div"
385
+ },
386
+ secondaryTypographyProps: {
387
+ component: "div"
388
+ },
389
+ primary: isLargeScreen ? /*#__PURE__*/_jsx(Box, {
390
+ sx: sxTitleDescriptionWrapper,
391
+ children: phraseList.phraseListName || ""
392
+ }) : /*#__PURE__*/_jsxs(_Fragment, {
382
393
  children: [/*#__PURE__*/_jsx(Box, {
383
394
  sx: {
384
395
  mb: 1
@@ -393,7 +404,7 @@ function PhraseListRow({
393
404
  })]
394
405
  }),
395
406
  secondary: isLargeScreen ? /*#__PURE__*/_jsx(Box, {
396
- sx: isCreator ? sxDescriptionWrapper : null,
407
+ sx: sxTitleDescriptionWrapper,
397
408
  children: /*#__PURE__*/_jsx(Typography, {
398
409
  variant: "body2",
399
410
  component: "p",
@@ -637,7 +648,16 @@ function RoleplayListItem({
637
648
  alt: ""
638
649
  })
639
650
  }), /*#__PURE__*/_jsx(ListItemText, {
640
- primary: isLargeScreen ? roleplay.roleplayName || "" : /*#__PURE__*/_jsxs(_Fragment, {
651
+ primaryTypographyProps: {
652
+ component: "div"
653
+ },
654
+ secondaryTypographyProps: {
655
+ component: "div"
656
+ },
657
+ primary: isLargeScreen ? /*#__PURE__*/_jsx(Box, {
658
+ sx: sxTitleDescriptionWrapper,
659
+ children: roleplay.roleplayName || ""
660
+ }) : /*#__PURE__*/_jsxs(_Fragment, {
641
661
  children: [/*#__PURE__*/_jsx(Box, {
642
662
  sx: {
643
663
  mb: 1
@@ -652,7 +672,7 @@ function RoleplayListItem({
652
672
  })]
653
673
  }),
654
674
  secondary: isLargeScreen ? /*#__PURE__*/_jsx(Box, {
655
- sx: isCreator ? sxDescriptionWrapper : null,
675
+ sx: sxTitleDescriptionWrapper,
656
676
  children: /*#__PURE__*/_jsx(Typography, {
657
677
  variant: "body2",
658
678
  component: "p",
@@ -906,28 +926,39 @@ function BotListItem({
906
926
  alt: ""
907
927
  })
908
928
  }), /*#__PURE__*/_jsx(ListItemText, {
909
- primary: isLargeScreen ? !isBotChallenge ? bot.botName || "" : /*#__PURE__*/_jsxs(Box, {
910
- sx: {
911
- display: "flex",
912
- alignItems: "center",
913
- gap: "8px"
914
- },
915
- children: [bot.botName || "", isCreator || parentClassroom?.isCreator ? /*#__PURE__*/_jsx(HtmlTooltipComponent, {
916
- titleBold: t("challenge_mode"),
917
- titleDesc: t("challenge_bot_desc"),
918
- placement: "right",
919
- children: /*#__PURE__*/_jsx(Chip, {
929
+ primaryTypographyProps: {
930
+ component: "div"
931
+ },
932
+ secondaryTypographyProps: {
933
+ component: "div"
934
+ },
935
+ primary: isLargeScreen ? /*#__PURE__*/_jsx(Box, {
936
+ sx: sxTitleDescriptionWrapper,
937
+ children: !isBotChallenge ? bot.botName || "" : /*#__PURE__*/_jsxs(Box, {
938
+ sx: {
939
+ display: "flex",
940
+ alignItems: "center",
941
+ gap: "8px",
942
+ flexWrap: "wrap",
943
+ minWidth: 0
944
+ },
945
+ children: [bot.botName || "", isCreator || parentClassroom?.isCreator ? /*#__PURE__*/_jsx(HtmlTooltipComponent, {
946
+ titleBold: t("challenge_mode"),
947
+ titleDesc: t("challenge_bot_desc"),
948
+ placement: "right",
949
+ children: /*#__PURE__*/_jsx(Chip, {
950
+ label: t("challenge_mode"),
951
+ size: "small",
952
+ variant: "outlined",
953
+ color: "primary"
954
+ })
955
+ }) : /*#__PURE__*/_jsx(Chip, {
920
956
  label: t("challenge_mode"),
921
957
  size: "small",
922
958
  variant: "outlined",
923
959
  color: "primary"
924
- })
925
- }) : /*#__PURE__*/_jsx(Chip, {
926
- label: t("challenge_mode"),
927
- size: "small",
928
- variant: "outlined",
929
- color: "primary"
930
- })]
960
+ })]
961
+ })
931
962
  }) : /*#__PURE__*/_jsxs(_Fragment, {
932
963
  children: [/*#__PURE__*/_jsx(Box, {
933
964
  sx: {
@@ -958,7 +989,7 @@ function BotListItem({
958
989
  }))]
959
990
  }),
960
991
  secondary: isLargeScreen ? /*#__PURE__*/_jsx(Box, {
961
- sx: isCreator ? sxDescriptionWrapper : null,
992
+ sx: sxTitleDescriptionWrapper,
962
993
  children: /*#__PURE__*/_jsx(Typography, {
963
994
  variant: "body2",
964
995
  component: "p",
@@ -180,8 +180,8 @@ function Member(props) {
180
180
  primary: member?.username?.length > 18 ? member.username.substring(0, 18) + "..." : member.username,
181
181
  slotProps: {
182
182
  primary: {
183
- fontWeight: 800,
184
- fontSize: 22
183
+ fontWeight: 900,
184
+ fontSize: 28
185
185
  }
186
186
  }
187
187
  }), isIconsVisible ? renderStreakIcon() : null, isIconsVisible ? renderIcon() : null, /*#__PURE__*/_jsx("div", {
@@ -193,8 +193,8 @@ function Member(props) {
193
193
  primary: member.score,
194
194
  slotProps: {
195
195
  primary: {
196
- fontWeight: 800,
197
- fontSize: 26
196
+ fontWeight: 900,
197
+ fontSize: 32
198
198
  }
199
199
  }
200
200
  })
@@ -1,154 +1,89 @@
1
- import { useState, useEffect } from "react";
2
- import { Box, Button, ListItemText, ListItemButton, Avatar, ListItemIcon, Collapse, CircularProgress, Typography } from "@mui/material";
3
- import PlayCircleOutlinedIcon from "@mui/icons-material/PlayCircleOutlined";
4
- import HearingIcon from "@mui/icons-material/Hearing";
5
- import SwapHorizIcon from "@mui/icons-material/SwapHoriz";
6
- import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
7
- import ExpandLessIcon from "@mui/icons-material/ExpandLess";
1
+ import { useEffect, useMemo } from "react";
2
+ import { Box, Button, ListItemText, ListItemButton, Avatar, Checkbox, CircularProgress, Typography } from "@mui/material";
8
3
  import ImageSearchIcon from "@mui/icons-material/ImageSearch";
9
- import ExtensionIcon from "@mui/icons-material/Extension";
4
+ import { selectedRowBg } from "../RoleplaySelection/RoleplaySelection";
10
5
  import { courses as courseQuerys } from "@nualang/nualang-api-and-queries/Queries";
11
- import EditNoteIcon from '@mui/icons-material/EditNote';
12
6
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
+ export const getPhraseCount = phraseList => Array.isArray(phraseList?.phrases) ? phraseList.phrases.length : phraseList?.phraseCount ?? 0;
8
+ export const getHasDefinitions = phraseList => Array.isArray(phraseList?.phrases) ? phraseList.phrases.some(p => p?.definitions?.length > 0) : !!phraseList?.hasDefinitions;
9
+
10
+ // Enrich a raw phrase list with the derived fields the dialog needs to build a
11
+ // merged, capped pool and to decide game-type availability, plus the ids needed
12
+ // to identify the source topic once several lists across topics are combined.
13
+ export const enrichPhraseList = (phraseList, {
14
+ courseId,
15
+ courseSectionTopicId,
16
+ topicName,
17
+ topicVideoUrl
18
+ }) => ({
19
+ ...phraseList,
20
+ courseId,
21
+ courseSectionTopicId,
22
+ topicName: topicName ?? "",
23
+ topicVideoUrl: topicVideoUrl ?? null,
24
+ phraseCount: getPhraseCount(phraseList),
25
+ hasDefinitions: getHasDefinitions(phraseList)
26
+ });
13
27
  function PhraseList({
14
28
  phraseList,
15
- handleCreateGame,
16
- courseSectionTopicId,
17
- isPhraseListSelected,
18
- setIsPhraseListSelected,
19
- t,
20
- isExerciseSelected,
21
- setIsExerciseSelected,
22
- topicName
29
+ selected,
30
+ onToggle,
31
+ t
23
32
  }) {
24
- const [open, setOpen] = useState(false);
25
- const isTranslationHidden = phraseList?.hiddenPhraseGroupGames?.translation;
26
- const isListeningHidden = phraseList?.hiddenPhraseGroupGames?.listening;
27
- const isMeaningHidden = phraseList?.hiddenPhraseGroupGames?.meaning;
28
- const isReadAndWriteHidden = phraseList?.hiddenPhraseGroupGames?.["meaning-read-and-write"];
29
- const hasDefinitions = phraseList?.phrases?.some(p => p.definitions?.length > 0);
30
- const hasSelectableGame = !isTranslationHidden || !isListeningHidden || !isMeaningHidden;
31
- if (!hasSelectableGame) return null;
32
- const handleGameSelected = exercise => () => {
33
- setIsPhraseListSelected(true);
34
- setIsExerciseSelected(true);
35
- handleCreateGame({
36
- courseSectionTopicId,
37
- exercise,
38
- timer: 30,
39
- answerOption: 0,
40
- phraseListId: phraseList.phraseListId,
41
- phrases: phraseList.phrases || [],
42
- topicName: topicName ?? ""
43
- });
44
- };
45
- return /*#__PURE__*/_jsxs("div", {
46
- children: [/*#__PURE__*/_jsxs(ListItemButton, {
47
- onClick: () => setOpen(!open),
48
- disabled: isPhraseListSelected || isExerciseSelected,
33
+ return /*#__PURE__*/_jsxs(ListItemButton, {
34
+ onClick: onToggle,
35
+ role: "checkbox",
36
+ "aria-checked": selected,
37
+ sx: {
38
+ display: "flex",
39
+ alignItems: "center",
40
+ px: 2,
41
+ py: 1,
42
+ borderRadius: 1,
43
+ ...(selected ? {
44
+ backgroundColor: selectedRowBg
45
+ } : {})
46
+ },
47
+ children: [phraseList.picture ? /*#__PURE__*/_jsx(Avatar, {
48
+ src: phraseList.picture,
49
+ alt: phraseList.phraseListName,
49
50
  sx: {
51
+ flex: "none"
52
+ }
53
+ }) : /*#__PURE__*/_jsx(Avatar, {
54
+ alt: phraseList.phraseListName,
55
+ sx: {
56
+ flex: "none"
57
+ },
58
+ children: /*#__PURE__*/_jsx(ImageSearchIcon, {})
59
+ }), /*#__PURE__*/_jsx(ListItemText, {
60
+ primary: phraseList.phraseListName,
61
+ secondary: `${t("n_phrases", {
62
+ count: getPhraseCount(phraseList)
63
+ })}` + (getHasDefinitions(phraseList) ? ` · ${t("has_definitions")}` : ""),
64
+ sx: {
65
+ ml: 2,
66
+ flex: 1,
67
+ minWidth: 0
68
+ }
69
+ }), /*#__PURE__*/_jsx(Box, {
70
+ sx: {
71
+ minWidth: 56,
50
72
  display: "flex",
51
- alignItems: "center",
52
- justifyContent: "space-between",
53
- flexDirection: {
54
- xs: "column",
55
- sm: "row"
56
- }
73
+ justifyContent: "flex-end",
74
+ flex: "none"
57
75
  },
58
- children: [/*#__PURE__*/_jsxs(Box, {
59
- sx: {
60
- display: "flex",
61
- alignItems: "center",
62
- width: {
63
- xs: "100%",
64
- sm: "85%"
65
- }
66
- },
67
- children: [phraseList.picture ? /*#__PURE__*/_jsx(Avatar, {
68
- src: phraseList.picture,
69
- alt: phraseList.phraseListName
70
- }) : /*#__PURE__*/_jsx(Avatar, {
71
- alt: phraseList.phraseListName,
72
- children: /*#__PURE__*/_jsx(ImageSearchIcon, {})
73
- }), /*#__PURE__*/_jsx(ListItemText, {
74
- primary: phraseList.phraseListName,
75
- secondary: phraseList.description,
76
- sx: {
77
- marginLeft: 2
78
- }
79
- })]
80
- }), /*#__PURE__*/_jsx(ListItemIcon, {
81
- children: open ? /*#__PURE__*/_jsx(ExpandLessIcon, {
82
- sx: {
83
- color: "#757575"
84
- }
85
- }) : /*#__PURE__*/_jsx(ExpandMoreIcon, {
86
- sx: {
87
- color: "#757575"
88
- }
89
- })
90
- })]
91
- }), /*#__PURE__*/_jsx(Collapse, {
92
- in: open,
93
- timeout: "auto",
94
- unmountOnExit: true,
95
- children: /*#__PURE__*/_jsxs(Box, {
76
+ children: /*#__PURE__*/_jsx(Checkbox, {
77
+ checked: selected,
78
+ tabIndex: -1,
79
+ disableRipple: true,
80
+ color: "primary",
96
81
  sx: {
97
- ml: 3.5
82
+ p: 0
98
83
  },
99
- children: [!isTranslationHidden && /*#__PURE__*/_jsxs(ListItemButton, {
100
- onClick: handleGameSelected("translation"),
101
- disabled: isPhraseListSelected || isExerciseSelected,
102
- children: [/*#__PURE__*/_jsx(ListItemIcon, {
103
- children: /*#__PURE__*/_jsx(SwapHorizIcon, {})
104
- }), /*#__PURE__*/_jsx(ListItemText, {
105
- primary: t("translation"),
106
- secondary: t("translation_exercise_desc")
107
- }), /*#__PURE__*/_jsx(ListItemIcon, {
108
- children: /*#__PURE__*/_jsx(PlayCircleOutlinedIcon, {
109
- color: "primary"
110
- })
111
- })]
112
- }), !isListeningHidden && /*#__PURE__*/_jsxs(ListItemButton, {
113
- onClick: handleGameSelected("listening"),
114
- disabled: isPhraseListSelected || isExerciseSelected,
115
- children: [/*#__PURE__*/_jsx(ListItemIcon, {
116
- children: /*#__PURE__*/_jsx(HearingIcon, {})
117
- }), /*#__PURE__*/_jsx(ListItemText, {
118
- primary: t("listening"),
119
- secondary: t("listening_exercise_desc")
120
- }), /*#__PURE__*/_jsx(ListItemIcon, {
121
- children: /*#__PURE__*/_jsx(PlayCircleOutlinedIcon, {
122
- color: "primary"
123
- })
124
- })]
125
- }), !isMeaningHidden && /*#__PURE__*/_jsxs(ListItemButton, {
126
- onClick: handleGameSelected("meaning"),
127
- disabled: isPhraseListSelected || isExerciseSelected || !hasDefinitions,
128
- children: [/*#__PURE__*/_jsx(ListItemIcon, {
129
- children: /*#__PURE__*/_jsx(ExtensionIcon, {})
130
- }), /*#__PURE__*/_jsx(ListItemText, {
131
- primary: t("meaning"),
132
- secondary: t("meaning_exercise_desc")
133
- }), /*#__PURE__*/_jsx(ListItemIcon, {
134
- children: /*#__PURE__*/_jsx(PlayCircleOutlinedIcon, {
135
- color: "primary"
136
- })
137
- })]
138
- }), !isReadAndWriteHidden && /*#__PURE__*/_jsxs(ListItemButton, {
139
- onClick: handleGameSelected("meaning-read-and-write"),
140
- disabled: isPhraseListSelected || isExerciseSelected || !hasDefinitions,
141
- children: [/*#__PURE__*/_jsx(ListItemIcon, {
142
- children: /*#__PURE__*/_jsx(EditNoteIcon, {})
143
- }), /*#__PURE__*/_jsx(ListItemText, {
144
- primary: t("meaning_read_write"),
145
- secondary: t("meaning_read_write_exercise_desc")
146
- }), /*#__PURE__*/_jsx(ListItemIcon, {
147
- children: /*#__PURE__*/_jsx(PlayCircleOutlinedIcon, {
148
- color: "primary"
149
- })
150
- })]
151
- })]
84
+ inputProps: {
85
+ "aria-label": phraseList.phraseListName
86
+ }
152
87
  })
153
88
  })]
154
89
  });
@@ -158,16 +93,15 @@ function PhraseListSelection({
158
93
  courseId,
159
94
  sectionId,
160
95
  topicId,
161
- handleCreateGame,
162
96
  courseSectionTopicId,
163
97
  t,
164
- isExerciseSelected,
165
98
  isExpanded,
166
- setIsExerciseSelected,
99
+ selectedListIds,
100
+ onTogglePhraseList,
167
101
  onPhraseListsLoaded,
168
- topicName
102
+ topicName,
103
+ topicVideoUrl
169
104
  }) {
170
- const [isPhraseListSelected, setIsPhraseListSelected] = useState(false);
171
105
  const phraseListsQuery = courseQuerys.useCourseSectionTopicPhraseLists(async (cId, sId, tId, filters) => getPhraseLists(cId, sId, tId, filters), {
172
106
  courseId,
173
107
  sectionId,
@@ -180,11 +114,20 @@ function PhraseListSelection({
180
114
  enabled: !!courseId && !!sectionId && !!topicId && isExpanded
181
115
  });
182
116
  const phraseLists = phraseListsQuery.isSuccess && Array.isArray(phraseListsQuery.data?.Items) ? phraseListsQuery.data.Items : [];
117
+ const enrichedLists = useMemo(() => phraseLists.map(pl => enrichPhraseList(pl, {
118
+ courseId,
119
+ courseSectionTopicId,
120
+ topicName,
121
+ topicVideoUrl
122
+ })), [phraseLists, courseId, courseSectionTopicId, topicName, topicVideoUrl]);
123
+
124
+ // Report the loaded lists up so the topic row can render its
125
+ // "n of m lists selected" subtitle and drive Select all / Clear all.
183
126
  useEffect(() => {
184
127
  if (phraseListsQuery.isSuccess && onPhraseListsLoaded) {
185
- onPhraseListsLoaded(phraseLists.length);
128
+ onPhraseListsLoaded(enrichedLists);
186
129
  }
187
- }, [phraseListsQuery.isSuccess, phraseLists.length]);
130
+ }, [phraseListsQuery.isSuccess, enrichedLists]);
188
131
  if (phraseListsQuery.isError) {
189
132
  return /*#__PURE__*/_jsxs(Box, {
190
133
  sx: {
@@ -214,7 +157,7 @@ function PhraseListSelection({
214
157
  })
215
158
  });
216
159
  }
217
- if (phraseListsQuery.isSuccess && phraseLists.length === 0) {
160
+ if (phraseListsQuery.isSuccess && enrichedLists.length === 0) {
218
161
  return /*#__PURE__*/_jsx(Box, {
219
162
  sx: {
220
163
  px: 2,
@@ -227,16 +170,11 @@ function PhraseListSelection({
227
170
  })
228
171
  });
229
172
  }
230
- return phraseLists.map((phraseList, index) => /*#__PURE__*/_jsx(PhraseList, {
173
+ return enrichedLists.map((phraseList, index) => /*#__PURE__*/_jsx(PhraseList, {
231
174
  phraseList: phraseList,
232
- handleCreateGame: handleCreateGame,
233
- courseSectionTopicId: courseSectionTopicId,
234
- isPhraseListSelected: isPhraseListSelected,
235
- setIsPhraseListSelected: setIsPhraseListSelected,
236
- t: t,
237
- isExerciseSelected: isExerciseSelected,
238
- setIsExerciseSelected: setIsExerciseSelected,
239
- topicName: topicName
175
+ selected: !!selectedListIds?.has(phraseList.phraseListId),
176
+ onToggle: () => onTogglePhraseList(phraseList),
177
+ t: t
240
178
  }, phraseList.phraseListId || index));
241
179
  }
242
180
  export default PhraseListSelection;