@capytale/meta-player 0.8.2 → 0.8.3

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.
Files changed (105) hide show
  1. package/.prettierrc.json +4 -4
  2. package/LICENSE +674 -674
  3. package/README.md +12 -12
  4. package/eslint.config.js +28 -28
  5. package/index.html +15 -15
  6. package/package.json +60 -60
  7. package/public/themes/lara-dark-blue/theme.css +7015 -7015
  8. package/public/themes/lara-light-blue/theme.css +7005 -7005
  9. package/src/App.tsx +139 -139
  10. package/src/AppRedux.css +39 -39
  11. package/src/MetaPlayer.tsx +170 -170
  12. package/src/activityJs.ts +7 -7
  13. package/src/app/createAppSlice.ts +6 -6
  14. package/src/app/hooks.ts +12 -12
  15. package/src/app/store.ts +52 -52
  16. package/src/app.module.scss +80 -80
  17. package/src/demo.tsx +87 -87
  18. package/src/external/prime.ts +5 -5
  19. package/src/features/activityData/ExitWarning.ts +28 -28
  20. package/src/features/activityData/IsAntiCheatExitDetectionDisabledSetter.tsx +19 -19
  21. package/src/features/activityData/IsDirtySetter.tsx +17 -17
  22. package/src/features/activityData/MetaPlayerOptionsSetter.tsx +41 -41
  23. package/src/features/activityData/activityDataSlice.ts +256 -256
  24. package/src/features/activityData/activityJsData.ts +82 -82
  25. package/src/features/activityData/hooks.ts +34 -34
  26. package/src/features/activityData/metaPlayerOptions.ts +23 -23
  27. package/src/features/activityData/uiState.ts +20 -20
  28. package/src/features/activityJS/ActivityJSProvider.tsx +339 -339
  29. package/src/features/activityJS/AfterResetAction.tsx +23 -23
  30. package/src/features/activityJS/AfterSaveAction.tsx +23 -23
  31. package/src/features/activityJS/BeforeResetAction.tsx +23 -23
  32. package/src/features/activityJS/BeforeSaveAction.tsx +23 -23
  33. package/src/features/activityJS/Saver.tsx +167 -167
  34. package/src/features/activityJS/hooks.ts +85 -85
  35. package/src/features/activityJS/internal-hooks.ts +96 -96
  36. package/src/features/activityJS/saverSlice.ts +96 -96
  37. package/src/features/activitySettings/ActivitySettingsSetter.tsx +21 -21
  38. package/src/features/activitySettings/hooks.ts +12 -12
  39. package/src/features/activitySettings/index.tsx +43 -43
  40. package/src/features/activitySettings/store.ts +108 -108
  41. package/src/features/activitySettings/style.module.scss +8 -8
  42. package/src/features/activitySettings/types.ts +140 -140
  43. package/src/features/activitySettings/ui.tsx +299 -299
  44. package/src/features/functionalities/AttachedFilesFunctionality.ts +23 -23
  45. package/src/features/functionalities/PreviewDialog.tsx +83 -83
  46. package/src/features/functionalities/functionalitiesSlice.ts +98 -98
  47. package/src/features/functionalities/hooks.ts +70 -70
  48. package/src/features/layout/hooks.ts +7 -7
  49. package/src/features/layout/layoutSlice.ts +90 -90
  50. package/src/features/navbar/activity-info/index.tsx +54 -54
  51. package/src/features/navbar/activity-info/style.module.scss +38 -38
  52. package/src/features/navbar/activity-menu/ActivityQuickActions.tsx +57 -57
  53. package/src/features/navbar/activity-menu/index.tsx +154 -153
  54. package/src/features/navbar/activity-menu/style.module.scss +7 -7
  55. package/src/features/navbar/capytale-menu/CloneDialog.tsx +75 -75
  56. package/src/features/navbar/capytale-menu/Countdown.tsx +312 -312
  57. package/src/features/navbar/capytale-menu/CountdownAndSaveButton.tsx +115 -115
  58. package/src/features/navbar/capytale-menu/index.tsx +260 -260
  59. package/src/features/navbar/capytale-menu/style.module.scss +8 -8
  60. package/src/features/navbar/index.tsx +39 -39
  61. package/src/features/navbar/navbarSlice.ts +79 -79
  62. package/src/features/navbar/review-navbar/GradingNav.tsx +128 -128
  63. package/src/features/navbar/review-navbar/index.tsx +18 -18
  64. package/src/features/navbar/review-navbar/style.module.scss +22 -22
  65. package/src/features/navbar/sidebars/ActivitySidebarActions.tsx +57 -57
  66. package/src/features/navbar/sidebars/AttachedFilesSidebarContent.module.scss +43 -43
  67. package/src/features/navbar/sidebars/AttachedFilesSidebarContent.tsx +181 -181
  68. package/src/features/navbar/sidebars/SettingsSidebarContent.tsx +192 -192
  69. package/src/features/navbar/sidebars/style.module.scss +15 -15
  70. package/src/features/navbar/student-utils.ts +11 -11
  71. package/src/features/navbar/style.module.scss +65 -65
  72. package/src/features/pedago/InstructionsEditor.tsx +102 -102
  73. package/src/features/pedago/PdfEditor.tsx +91 -91
  74. package/src/features/pedago/PedagoCommands.tsx +353 -353
  75. package/src/features/pedago/SharedNotesEditor.tsx +144 -144
  76. package/src/features/pedago/index.tsx +207 -204
  77. package/src/features/pedago/style.module.scss +233 -233
  78. package/src/features/theming/ThemeSwitcher.tsx +51 -51
  79. package/src/features/theming/hooks.ts +6 -6
  80. package/src/features/theming/themingSlice.ts +93 -93
  81. package/src/features/toast.tsx +38 -38
  82. package/src/hooks/index.ts +16 -16
  83. package/src/index.css +132 -132
  84. package/src/index.ts +90 -90
  85. package/src/logo.svg +1 -1
  86. package/src/my_json_data.js +4146 -4146
  87. package/src/settings.ts +6 -6
  88. package/src/types.ts +9 -9
  89. package/src/utils/ButtonDoubleIcon.tsx +35 -35
  90. package/src/utils/CardSelector.tsx +41 -41
  91. package/src/utils/ErrorBoundary.tsx +41 -41
  92. package/src/utils/PopupButton.tsx +134 -134
  93. package/src/utils/activity.ts +8 -8
  94. package/src/utils/breakpoints.ts +4 -4
  95. package/src/utils/capytale.ts +10 -10
  96. package/src/utils/clipboard.ts +11 -11
  97. package/src/utils/download.ts +7 -7
  98. package/src/utils/equality.ts +32 -32
  99. package/src/utils/server-clock.ts +42 -42
  100. package/src/utils/style.module.scss +45 -45
  101. package/src/utils/useFullscreen.ts +65 -65
  102. package/src/vite-env.d.ts +1 -1
  103. package/tsconfig.json +28 -28
  104. package/tsconfig.node.json +9 -9
  105. package/vite.config.ts +11 -11
@@ -1,353 +1,353 @@
1
- import { useAppDispatch, useAppSelector } from "../../app/hooks";
2
- import {
3
- selectHasGradingOrComments,
4
- selectInstructionsType,
5
- selectMode,
6
- selectPdfInstructions,
7
- selectSharedNotesType,
8
- setInstructionsType,
9
- setSharedNotesType,
10
- } from "../activityData/activityDataSlice";
11
- import {
12
- PedagoTab,
13
- selectIsGradingVisible,
14
- selectOrientation,
15
- selectPedagoTab,
16
- setPedagoTab,
17
- toggleIsGradingVisible,
18
- toggleIsPedagoVisible,
19
- } from "../layout/layoutSlice";
20
- import styles from "./style.module.scss";
21
- import { Button } from "primereact/button";
22
- import settings from "../../settings";
23
- import { SelectButton } from "primereact/selectbutton";
24
- import { FC, useCallback, useMemo } from "react";
25
- import { TabMenu } from "primereact/tabmenu";
26
- import { classNames } from "primereact/utils";
27
-
28
- export const PedagoCommands = () => {
29
- const isHorizontal = useAppSelector(selectOrientation) === "horizontal";
30
- return isHorizontal ? (
31
- <HorizontalPedagoCommands />
32
- ) : (
33
- <VerticalPedagoCommands />
34
- );
35
- };
36
-
37
- export const CloseOnlyPedagoCommands: FC = () => {
38
- const dispatch = useAppDispatch();
39
- return (
40
- <div className={styles.closeOnlyPedagoCommands}>
41
- <Button
42
- severity="secondary"
43
- icon="pi pi-times"
44
- rounded
45
- text
46
- aria-label="Masquer consignes et évaluation"
47
- tooltip="Masquer l'évaluation"
48
- tooltipOptions={{
49
- position: "left",
50
- showDelay: settings.TOOLTIP_SHOW_DELAY,
51
- }}
52
- onClick={() => dispatch(toggleIsPedagoVisible())}
53
- style={{ flexShrink: 0 }}
54
- />
55
- </div>
56
- );
57
- };
58
-
59
- type DocumentSelectorHzItem = {
60
- name: string;
61
- value: PedagoTab;
62
- tooltip: string;
63
- };
64
-
65
- type DocumentSelectorItem = DocumentSelectorHzItem & {
66
- template: (item: any) => JSX.Element;
67
- };
68
-
69
- const HorizontalPedagoCommands = () => {
70
- const mode = useAppSelector(selectMode);
71
- const dispatch = useAppDispatch();
72
- return (
73
- <div className={styles.pedagoCommands}>
74
- <div
75
- style={{
76
- display: "flex",
77
- flexDirection: "row",
78
- justifyContent: "space-between",
79
- alignItems: "center",
80
- width: "100%",
81
- paddingLeft: "4px",
82
- }}
83
- >
84
- {(mode === "assignment" || mode === "review") && (
85
- <ShowHideGradingButton />
86
- )}
87
- <div></div>
88
- <Button
89
- severity="secondary"
90
- icon="pi pi-times"
91
- rounded
92
- text
93
- aria-label="Masquer consignes et évaluation"
94
- tooltip="Masquer les consignes"
95
- tooltipOptions={{
96
- position: "left",
97
- showDelay: settings.TOOLTIP_SHOW_DELAY,
98
- }}
99
- onClick={() => dispatch(toggleIsPedagoVisible())}
100
- style={{ flexShrink: 0 }}
101
- />
102
- </div>
103
- <HorizontalDocumentSelector />
104
- </div>
105
- );
106
- };
107
-
108
- const DocumentSelectorItemActions: FC<{
109
- item: DocumentSelectorHzItem;
110
- }> = ({ item }) => {
111
- const mode = useAppSelector(selectMode);
112
- const itemTemplate = useCallback((option: any) => {
113
- return <i className={option.icon}></i>;
114
- }, []);
115
-
116
- const instructionsType = useAppSelector(selectInstructionsType);
117
- const sharedNotesType = useAppSelector(selectSharedNotesType);
118
- const pdfInstructions = useAppSelector(selectPdfInstructions);
119
- const dispatch = useAppDispatch();
120
-
121
- return (
122
- <>
123
- {mode === "create" && (
124
- <>
125
- <div className={styles.pdfActions}>
126
- {item.value === "pdf" && !pdfInstructions && (
127
- <i className="pi pi-eye-slash"></i>
128
- )}
129
- {item.value === "pdf" && pdfInstructions && (
130
- <i className="pi pi-file-pdf"></i>
131
- )}
132
- </div>
133
- {item.value !== "pdf" && (
134
- <SelectButton
135
- aria-label={`Sélectionner le type de ${item.name}`}
136
- className={styles.smallSelectButton}
137
- value={
138
- item.value === "instructions"
139
- ? instructionsType
140
- : sharedNotesType
141
- }
142
- onChange={(e) => {
143
- if (item.value === "instructions") {
144
- dispatch(setInstructionsType(e.value));
145
- } else {
146
- dispatch(setSharedNotesType(e.value));
147
- }
148
- }}
149
- itemTemplate={itemTemplate}
150
- optionLabel="value"
151
- allowEmpty={false}
152
- options={[
153
- {
154
- label: "Éditeur riche",
155
- value: "rich",
156
- icon: "pi pi-pen-to-square",
157
- },
158
- {
159
- label: "Pas de consigne",
160
- value: "none",
161
- icon: "pi pi-eye-slash",
162
- },
163
- ]}
164
- tooltip={item.tooltip}
165
- tooltipOptions={{
166
- position: "left",
167
- showDelay: settings.TOOLTIP_SHOW_DELAY,
168
- }}
169
- />
170
- )}
171
- </>
172
- )}
173
- </>
174
- );
175
- };
176
-
177
- const HorizontalDocumentSelector = () => {
178
- const pedagoTab = useAppSelector(selectPedagoTab);
179
- const instructionsType = useAppSelector(selectInstructionsType);
180
- const mode = useAppSelector(selectMode);
181
- // const pdfInstructions = useAppSelector(selectPdfInstructions);
182
- const sharedNotesType = useAppSelector(selectSharedNotesType);
183
- const dispatch = useAppDispatch();
184
-
185
- const items: DocumentSelectorHzItem[] = useMemo(() => {
186
- const allItems: DocumentSelectorHzItem[] = [];
187
- if (mode === "create" || instructionsType !== "none") {
188
- allItems.push({
189
- name: "Consignes",
190
- value: "instructions",
191
- tooltip: "Activer/désactiver les consignes",
192
- });
193
- }
194
- /* // Removed PDF for now
195
- if (mode === "create" || pdfInstructions) {
196
- allItems.push({
197
- name: "PDF",
198
- value: "pdf",
199
- tooltip: "Activer/désactiver le PDF",
200
- });
201
- }
202
- */
203
- if (mode === "create" || sharedNotesType !== "none") {
204
- allItems.push({
205
- name: "Notes partagées",
206
- value: "sharedNotes",
207
- tooltip: "Activer/désactiver les notes partagées",
208
- });
209
- }
210
- return allItems;
211
- }, []);
212
-
213
- return (
214
- <div className={styles.pedagoHorizontalTabMenu}>
215
- {items.map((item) => (
216
- <div
217
- className={styles.pedagoHorizontalTab}
218
- role="button"
219
- aria-label={`Basculer vers l'onglet ${item.name}`}
220
- onClick={() => dispatch(setPedagoTab(item.value))}
221
- data-selected={item.value === pedagoTab}
222
- key={item.value}
223
- >
224
- <span>{item.name}</span>
225
- <DocumentSelectorItemActions item={item} />
226
- </div>
227
- ))}
228
- </div>
229
- );
230
- };
231
-
232
- const VerticalPedagoCommands = () => {
233
- const mode = useAppSelector(selectMode);
234
- const dispatch = useAppDispatch();
235
- return (
236
- <div className={styles.pedagoCommands}>
237
- <VerticalDocumentSelector />
238
- <div className={styles.pedagoSeparator}></div>
239
- {(mode === "assignment" || mode === "review") && (
240
- <ShowHideGradingButton />
241
- )}
242
- <Button
243
- severity="secondary"
244
- icon="pi pi-times"
245
- rounded
246
- text
247
- aria-label="Masquer consignes et évaluation"
248
- tooltip="Masquer les consignes"
249
- tooltipOptions={{
250
- position: "left",
251
- showDelay: settings.TOOLTIP_SHOW_DELAY,
252
- }}
253
- onClick={() => dispatch(toggleIsPedagoVisible())}
254
- style={{ flexShrink: 0 }}
255
- />
256
- </div>
257
- );
258
- };
259
-
260
- const VerticalDocumentSelector = () => {
261
- const pedagoTab = useAppSelector(selectPedagoTab);
262
- const instructionsType = useAppSelector(selectInstructionsType);
263
- const mode = useAppSelector(selectMode);
264
- // const pdfInstructions = useAppSelector(selectPdfInstructions);
265
- const sharedNotesType = useAppSelector(selectSharedNotesType);
266
- const dispatch = useAppDispatch();
267
-
268
- const itemRenderer = useCallback(
269
- (item: DocumentSelectorItem) => (
270
- <a
271
- className={classNames("p-menuitem-link", styles.pedagoTab)}
272
- onClick={() => dispatch(setPedagoTab(item.value))}
273
- >
274
- <span>{item.name}</span>
275
- <DocumentSelectorItemActions item={item} />
276
- </a>
277
- ),
278
- [dispatch],
279
- );
280
-
281
- const items: DocumentSelectorItem[] = useMemo(() => {
282
- const allItems: DocumentSelectorItem[] = [];
283
- if (mode === "create" || instructionsType !== "none") {
284
- allItems.push({
285
- name: "Consignes",
286
- value: "instructions",
287
- tooltip: "Activer/désactiver les consignes",
288
- template: (item: any) => itemRenderer(item),
289
- });
290
- }
291
- /* // Removed PDF for now
292
- if (mode === "create" || pdfInstructions) {
293
- allItems.push({
294
- name: "PDF",
295
- value: "pdf",
296
- tooltip: "Activer/désactiver le PDF",
297
- template: (item: any) => itemRenderer(item),
298
- });
299
- }
300
- */
301
- if (mode === "create" || sharedNotesType !== "none") {
302
- allItems.push({
303
- name: "Notes partagées",
304
- value: "sharedNotes",
305
- tooltip: "Activer/désactiver les notes partagées",
306
- template: (item: any) => itemRenderer(item),
307
- });
308
- }
309
- return allItems;
310
- }, []);
311
- const activeIndex = useMemo(() => {
312
- const index = items.findIndex((item) => item.value === pedagoTab);
313
- return index;
314
- }, [items, pedagoTab]);
315
-
316
- return (
317
- <TabMenu
318
- className={styles.pedagoTabMenu}
319
- model={items}
320
- activeIndex={activeIndex}
321
- onTabChange={(e) => {
322
- dispatch(setPedagoTab(items[e.index].value));
323
- }}
324
- />
325
- );
326
- };
327
-
328
- const ShowHideGradingButton: React.FC = () => {
329
- const dispatch = useAppDispatch();
330
- const mode = useAppSelector(selectMode);
331
- const hasGradingOrComments = useAppSelector(selectHasGradingOrComments);
332
- const isGradingVisible = useAppSelector(selectIsGradingVisible);
333
- const canShowGrading = hasGradingOrComments || mode === "review";
334
-
335
- return (
336
- <Button
337
- size="small"
338
- icon="pi pi-graduation-cap"
339
- disabled={!canShowGrading}
340
- severity={canShowGrading ? "info" : "secondary"}
341
- aria-label="Afficher/Masquer la notation"
342
- tooltip={canShowGrading ? "Afficher/Masquer la notation" : "Pas de note"}
343
- tooltipOptions={{
344
- position: "left",
345
- showDelay: settings.TOOLTIP_SHOW_DELAY,
346
- showOnDisabled: true,
347
- }}
348
- onClick={() => dispatch(toggleIsGradingVisible())}
349
- style={{ flexShrink: 0 }}
350
- outlined={!isGradingVisible || !canShowGrading}
351
- />
352
- );
353
- };
1
+ import { useAppDispatch, useAppSelector } from "../../app/hooks";
2
+ import {
3
+ selectHasGradingOrComments,
4
+ selectInstructionsType,
5
+ selectMode,
6
+ selectPdfInstructions,
7
+ selectSharedNotesType,
8
+ setInstructionsType,
9
+ setSharedNotesType,
10
+ } from "../activityData/activityDataSlice";
11
+ import {
12
+ PedagoTab,
13
+ selectIsGradingVisible,
14
+ selectOrientation,
15
+ selectPedagoTab,
16
+ setPedagoTab,
17
+ toggleIsGradingVisible,
18
+ toggleIsPedagoVisible,
19
+ } from "../layout/layoutSlice";
20
+ import styles from "./style.module.scss";
21
+ import { Button } from "primereact/button";
22
+ import settings from "../../settings";
23
+ import { SelectButton } from "primereact/selectbutton";
24
+ import { FC, useCallback, useMemo } from "react";
25
+ import { TabMenu } from "primereact/tabmenu";
26
+ import { classNames } from "primereact/utils";
27
+
28
+ export const PedagoCommands = () => {
29
+ const isHorizontal = useAppSelector(selectOrientation) === "horizontal";
30
+ return isHorizontal ? (
31
+ <HorizontalPedagoCommands />
32
+ ) : (
33
+ <VerticalPedagoCommands />
34
+ );
35
+ };
36
+
37
+ export const CloseOnlyPedagoCommands: FC = () => {
38
+ const dispatch = useAppDispatch();
39
+ return (
40
+ <div className={styles.closeOnlyPedagoCommands}>
41
+ <Button
42
+ severity="secondary"
43
+ icon="pi pi-times"
44
+ rounded
45
+ text
46
+ aria-label="Masquer consignes et évaluation"
47
+ tooltip="Masquer l'évaluation"
48
+ tooltipOptions={{
49
+ position: "left",
50
+ showDelay: settings.TOOLTIP_SHOW_DELAY,
51
+ }}
52
+ onClick={() => dispatch(toggleIsPedagoVisible())}
53
+ style={{ flexShrink: 0 }}
54
+ />
55
+ </div>
56
+ );
57
+ };
58
+
59
+ type DocumentSelectorHzItem = {
60
+ name: string;
61
+ value: PedagoTab;
62
+ tooltip: string;
63
+ };
64
+
65
+ type DocumentSelectorItem = DocumentSelectorHzItem & {
66
+ template: (item: any) => JSX.Element;
67
+ };
68
+
69
+ const HorizontalPedagoCommands = () => {
70
+ const mode = useAppSelector(selectMode);
71
+ const dispatch = useAppDispatch();
72
+ return (
73
+ <div className={styles.pedagoCommands}>
74
+ <div
75
+ style={{
76
+ display: "flex",
77
+ flexDirection: "row",
78
+ justifyContent: "space-between",
79
+ alignItems: "center",
80
+ width: "100%",
81
+ paddingLeft: "4px",
82
+ }}
83
+ >
84
+ {(mode === "assignment" || mode === "review") && (
85
+ <ShowHideGradingButton />
86
+ )}
87
+ <div></div>
88
+ <Button
89
+ severity="secondary"
90
+ icon="pi pi-times"
91
+ rounded
92
+ text
93
+ aria-label="Masquer consignes et évaluation"
94
+ tooltip="Masquer les consignes"
95
+ tooltipOptions={{
96
+ position: "left",
97
+ showDelay: settings.TOOLTIP_SHOW_DELAY,
98
+ }}
99
+ onClick={() => dispatch(toggleIsPedagoVisible())}
100
+ style={{ flexShrink: 0 }}
101
+ />
102
+ </div>
103
+ <HorizontalDocumentSelector />
104
+ </div>
105
+ );
106
+ };
107
+
108
+ const DocumentSelectorItemActions: FC<{
109
+ item: DocumentSelectorHzItem;
110
+ }> = ({ item }) => {
111
+ const mode = useAppSelector(selectMode);
112
+ const itemTemplate = useCallback((option: any) => {
113
+ return <i className={option.icon}></i>;
114
+ }, []);
115
+
116
+ const instructionsType = useAppSelector(selectInstructionsType);
117
+ const sharedNotesType = useAppSelector(selectSharedNotesType);
118
+ const pdfInstructions = useAppSelector(selectPdfInstructions);
119
+ const dispatch = useAppDispatch();
120
+
121
+ return (
122
+ <>
123
+ {mode === "create" && (
124
+ <>
125
+ <div className={styles.pdfActions}>
126
+ {item.value === "pdf" && !pdfInstructions && (
127
+ <i className="pi pi-eye-slash"></i>
128
+ )}
129
+ {item.value === "pdf" && pdfInstructions && (
130
+ <i className="pi pi-file-pdf"></i>
131
+ )}
132
+ </div>
133
+ {item.value !== "pdf" && (
134
+ <SelectButton
135
+ aria-label={`Sélectionner le type de ${item.name}`}
136
+ className={styles.smallSelectButton}
137
+ value={
138
+ item.value === "instructions"
139
+ ? instructionsType
140
+ : sharedNotesType
141
+ }
142
+ onChange={(e) => {
143
+ if (item.value === "instructions") {
144
+ dispatch(setInstructionsType(e.value));
145
+ } else {
146
+ dispatch(setSharedNotesType(e.value));
147
+ }
148
+ }}
149
+ itemTemplate={itemTemplate}
150
+ optionLabel="value"
151
+ allowEmpty={false}
152
+ options={[
153
+ {
154
+ label: "Éditeur riche",
155
+ value: "rich",
156
+ icon: "pi pi-pen-to-square",
157
+ },
158
+ {
159
+ label: "Pas de consigne",
160
+ value: "none",
161
+ icon: "pi pi-eye-slash",
162
+ },
163
+ ]}
164
+ tooltip={item.tooltip}
165
+ tooltipOptions={{
166
+ position: "left",
167
+ showDelay: settings.TOOLTIP_SHOW_DELAY,
168
+ }}
169
+ />
170
+ )}
171
+ </>
172
+ )}
173
+ </>
174
+ );
175
+ };
176
+
177
+ const HorizontalDocumentSelector = () => {
178
+ const pedagoTab = useAppSelector(selectPedagoTab);
179
+ const instructionsType = useAppSelector(selectInstructionsType);
180
+ const mode = useAppSelector(selectMode);
181
+ // const pdfInstructions = useAppSelector(selectPdfInstructions);
182
+ const sharedNotesType = useAppSelector(selectSharedNotesType);
183
+ const dispatch = useAppDispatch();
184
+
185
+ const items: DocumentSelectorHzItem[] = useMemo(() => {
186
+ const allItems: DocumentSelectorHzItem[] = [];
187
+ if (mode === "create" || instructionsType !== "none") {
188
+ allItems.push({
189
+ name: "Consignes",
190
+ value: "instructions",
191
+ tooltip: "Activer/désactiver les consignes",
192
+ });
193
+ }
194
+ /* // Removed PDF for now
195
+ if (mode === "create" || pdfInstructions) {
196
+ allItems.push({
197
+ name: "PDF",
198
+ value: "pdf",
199
+ tooltip: "Activer/désactiver le PDF",
200
+ });
201
+ }
202
+ */
203
+ if (mode === "create" || sharedNotesType !== "none") {
204
+ allItems.push({
205
+ name: "Notes partagées",
206
+ value: "sharedNotes",
207
+ tooltip: "Activer/désactiver les notes partagées",
208
+ });
209
+ }
210
+ return allItems;
211
+ }, []);
212
+
213
+ return (
214
+ <div className={styles.pedagoHorizontalTabMenu}>
215
+ {items.map((item) => (
216
+ <div
217
+ className={styles.pedagoHorizontalTab}
218
+ role="button"
219
+ aria-label={`Basculer vers l'onglet ${item.name}`}
220
+ onClick={() => dispatch(setPedagoTab(item.value))}
221
+ data-selected={item.value === pedagoTab}
222
+ key={item.value}
223
+ >
224
+ <span>{item.name}</span>
225
+ <DocumentSelectorItemActions item={item} />
226
+ </div>
227
+ ))}
228
+ </div>
229
+ );
230
+ };
231
+
232
+ const VerticalPedagoCommands = () => {
233
+ const mode = useAppSelector(selectMode);
234
+ const dispatch = useAppDispatch();
235
+ return (
236
+ <div className={styles.pedagoCommands}>
237
+ <VerticalDocumentSelector />
238
+ <div className={styles.pedagoSeparator}></div>
239
+ {(mode === "assignment" || mode === "review") && (
240
+ <ShowHideGradingButton />
241
+ )}
242
+ <Button
243
+ severity="secondary"
244
+ icon="pi pi-times"
245
+ rounded
246
+ text
247
+ aria-label="Masquer consignes et évaluation"
248
+ tooltip="Masquer les consignes"
249
+ tooltipOptions={{
250
+ position: "left",
251
+ showDelay: settings.TOOLTIP_SHOW_DELAY,
252
+ }}
253
+ onClick={() => dispatch(toggleIsPedagoVisible())}
254
+ style={{ flexShrink: 0 }}
255
+ />
256
+ </div>
257
+ );
258
+ };
259
+
260
+ const VerticalDocumentSelector = () => {
261
+ const pedagoTab = useAppSelector(selectPedagoTab);
262
+ const instructionsType = useAppSelector(selectInstructionsType);
263
+ const mode = useAppSelector(selectMode);
264
+ // const pdfInstructions = useAppSelector(selectPdfInstructions);
265
+ const sharedNotesType = useAppSelector(selectSharedNotesType);
266
+ const dispatch = useAppDispatch();
267
+
268
+ const itemRenderer = useCallback(
269
+ (item: DocumentSelectorItem) => (
270
+ <a
271
+ className={classNames("p-menuitem-link", styles.pedagoTab)}
272
+ onClick={() => dispatch(setPedagoTab(item.value))}
273
+ >
274
+ <span>{item.name}</span>
275
+ <DocumentSelectorItemActions item={item} />
276
+ </a>
277
+ ),
278
+ [dispatch],
279
+ );
280
+
281
+ const items: DocumentSelectorItem[] = useMemo(() => {
282
+ const allItems: DocumentSelectorItem[] = [];
283
+ if (mode === "create" || instructionsType !== "none") {
284
+ allItems.push({
285
+ name: "Consignes",
286
+ value: "instructions",
287
+ tooltip: "Activer/désactiver les consignes",
288
+ template: (item: any) => itemRenderer(item),
289
+ });
290
+ }
291
+ /* // Removed PDF for now
292
+ if (mode === "create" || pdfInstructions) {
293
+ allItems.push({
294
+ name: "PDF",
295
+ value: "pdf",
296
+ tooltip: "Activer/désactiver le PDF",
297
+ template: (item: any) => itemRenderer(item),
298
+ });
299
+ }
300
+ */
301
+ if (mode === "create" || sharedNotesType !== "none") {
302
+ allItems.push({
303
+ name: "Notes partagées",
304
+ value: "sharedNotes",
305
+ tooltip: "Activer/désactiver les notes partagées",
306
+ template: (item: any) => itemRenderer(item),
307
+ });
308
+ }
309
+ return allItems;
310
+ }, []);
311
+ const activeIndex = useMemo(() => {
312
+ const index = items.findIndex((item) => item.value === pedagoTab);
313
+ return index;
314
+ }, [items, pedagoTab]);
315
+
316
+ return (
317
+ <TabMenu
318
+ className={styles.pedagoTabMenu}
319
+ model={items}
320
+ activeIndex={activeIndex}
321
+ onTabChange={(e) => {
322
+ dispatch(setPedagoTab(items[e.index].value));
323
+ }}
324
+ />
325
+ );
326
+ };
327
+
328
+ const ShowHideGradingButton: React.FC = () => {
329
+ const dispatch = useAppDispatch();
330
+ const mode = useAppSelector(selectMode);
331
+ const hasGradingOrComments = useAppSelector(selectHasGradingOrComments);
332
+ const isGradingVisible = useAppSelector(selectIsGradingVisible);
333
+ const canShowGrading = hasGradingOrComments || mode === "review";
334
+
335
+ return (
336
+ <Button
337
+ size="small"
338
+ icon="pi pi-graduation-cap"
339
+ disabled={!canShowGrading}
340
+ severity={canShowGrading ? "info" : "secondary"}
341
+ aria-label="Afficher/Masquer la notation"
342
+ tooltip={canShowGrading ? "Afficher/Masquer la notation" : "Pas de note"}
343
+ tooltipOptions={{
344
+ position: "left",
345
+ showDelay: settings.TOOLTIP_SHOW_DELAY,
346
+ showOnDisabled: true,
347
+ }}
348
+ onClick={() => dispatch(toggleIsGradingVisible())}
349
+ style={{ flexShrink: 0 }}
350
+ outlined={!isGradingVisible || !canShowGrading}
351
+ />
352
+ );
353
+ };