@capytale/meta-player 0.0.3 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,10 @@
1
1
  import { Button } from "primereact/button";
2
2
  import { SplitButton } from "primereact/splitbutton";
3
3
  import { ConfirmDialog, confirmDialog } from "primereact/confirmdialog";
4
+ import { Toast } from "primereact/toast";
4
5
 
5
6
  import styles from "./style.module.scss";
6
- import { useMemo } from "react";
7
+ import { useMemo, useRef } from "react";
7
8
  import { useWindowSize } from "@uidotdev/usehooks";
8
9
  import { ML, XL } from "../../utils/breakpoints";
9
10
  import { useAppDispatch, useAppSelector } from "../../app/hooks";
@@ -38,6 +39,7 @@ const CapytaleMenu: React.FC = () => {
38
39
  () => windowsSize.width && windowsSize.width < ML,
39
40
  [windowsSize.width],
40
41
  );
42
+ const toast = useRef<Toast>(null);
41
43
  const changeWorkflow = (value: wf) => {
42
44
  dispatch(setWorkflow(value));
43
45
  dispatch(setSaveState("should-save"));
@@ -55,135 +57,151 @@ const CapytaleMenu: React.FC = () => {
55
57
  });
56
58
  };
57
59
  return (
58
- <div className={styles.capytaleMenu}>
59
- {showSaveButton && (
60
- <Button
61
- label={isQuiteSmall ? undefined : "Enregistrer"}
62
- disabled={!isDirty}
63
- severity={"warning"}
64
- size="small"
65
- icon="pi pi-save"
66
- loading={saveState !== "idle"}
67
- tooltip={isDirty ? "Enregistrer l'activité" : "Rien à enregistrer"}
68
- tooltipOptions={{
69
- position: "bottom",
70
- showDelay: settings.TOOLTIP_SHOW_DELAY,
71
- showOnDisabled: true,
72
- }}
73
- onClick={() => {
74
- dispatch(setSaveState("should-save"));
75
- }}
76
- />
77
- )}
78
-
79
- {mode === "create" && (
80
- <SplitButton
81
- label={isLarge ? sharingInfo.code : undefined}
82
- severity="secondary"
83
- size="small"
84
- icon="pi pi-share-alt"
85
- outlined
86
- buttonProps={{
87
- tooltip: "Copier le code de partage",
88
- tooltipOptions: {
60
+ <>
61
+ <Toast ref={toast} position="bottom-right" />
62
+ <div className={styles.capytaleMenu}>
63
+ {showSaveButton && (
64
+ <Button
65
+ label={isQuiteSmall ? undefined : "Enregistrer"}
66
+ disabled={!isDirty}
67
+ severity={"warning"}
68
+ size="small"
69
+ icon="pi pi-save"
70
+ loading={saveState !== "idle"}
71
+ tooltip={isDirty ? "Enregistrer l'activité" : "Rien à enregistrer"}
72
+ tooltipOptions={{
89
73
  position: "bottom",
90
74
  showDelay: settings.TOOLTIP_SHOW_DELAY,
91
- },
92
- }}
93
- onClick={() => {
94
- copyToClipboard(sharingInfo.code);
95
- }}
96
- model={[
97
- {
98
- label: "Copier l'URL de partage",
99
- icon: "pi pi-link",
100
- command: () => {
101
- copyToClipboard(sharingInfo.codeLink);
75
+ showOnDisabled: true,
76
+ }}
77
+ onClick={() => {
78
+ dispatch(setSaveState("should-save"));
79
+ }}
80
+ />
81
+ )}
82
+
83
+ {mode === "create" && (
84
+ <SplitButton
85
+ label={isLarge ? sharingInfo.code : undefined}
86
+ severity="secondary"
87
+ size="small"
88
+ icon="pi pi-share-alt"
89
+ outlined
90
+ buttonProps={{
91
+ tooltip: "Copier le code de partage",
92
+ tooltipOptions: {
93
+ position: "bottom",
94
+ showDelay: settings.TOOLTIP_SHOW_DELAY,
95
+ },
96
+ }}
97
+ onClick={async () => {
98
+ await copyToClipboard(sharingInfo.code);
99
+ toast.current!.show({
100
+ summary: "Code copié",
101
+ detail: "Le code de partage a été copié dans le presse-papier.",
102
+ severity: "info",
103
+ life: 2000,
104
+ });
105
+ }}
106
+ model={[
107
+ {
108
+ label: "Copier l'URL de partage",
109
+ icon: "pi pi-link",
110
+ command: () => {
111
+ copyToClipboard(sharingInfo.codeLink).then(() => {
112
+ toast.current!.show({
113
+ summary: "URL copiée",
114
+ detail:
115
+ "L'URL de partage a été copiée dans le presse-papier.",
116
+ severity: "info",
117
+ life: 2000,
118
+ });
119
+ });
120
+ },
102
121
  },
103
- },
104
- ]}
105
- />
106
- )}
107
- {mode === "assignment" && workflow === "current" && (
108
- <Button
109
- outlined
110
- label="Rendre"
111
- icon="pi pi-envelope"
112
- onClick={() => {
113
- console.log("Hey");
114
- confirmFinishAssignment();
115
- }}
116
- />
117
- )}
118
- {mode === "assignment" && workflow !== "current" && (
119
- <Button
120
- outlined
121
- label={workflow === "finished" ? "Rendue" : "Corrigée"}
122
- disabled
123
- icon={
124
- workflow === "finished" ? "pi pi-envelope" : "pi pi-check-square"
125
- }
126
- tooltip="Copie déjà rendue"
127
- />
128
- )}
129
- {mode === "review" && (
130
- <SplitButton
131
- label={
132
- workflow === "current"
133
- ? "En cours"
134
- : workflow === "finished"
135
- ? "Rendue"
136
- : "Corrigée"
137
- }
138
- severity="secondary"
139
- size="small"
140
- icon={
141
- workflow === "current"
142
- ? "pi pi-pencil"
143
- : workflow === "finished"
144
- ? "pi pi-envelope"
145
- : "pi pi-check-square"
146
- }
147
- outlined
148
- model={[
149
- ...(workflow === "current"
150
- ? []
151
- : [
152
- {
153
- label: "Réautoriser la modification",
154
- icon: "pi pi-pencil",
155
- command: () => {
156
- changeWorkflow("current");
122
+ ]}
123
+ />
124
+ )}
125
+ {mode === "assignment" && workflow === "current" && (
126
+ <Button
127
+ outlined
128
+ label="Rendre"
129
+ icon="pi pi-envelope"
130
+ onClick={() => {
131
+ confirmFinishAssignment();
132
+ }}
133
+ />
134
+ )}
135
+ {mode === "assignment" && workflow !== "current" && (
136
+ <Button
137
+ outlined
138
+ label={workflow === "finished" ? "Rendue" : "Corrigée"}
139
+ disabled
140
+ icon={
141
+ workflow === "finished" ? "pi pi-envelope" : "pi pi-check-square"
142
+ }
143
+ tooltip="Copie déjà rendue"
144
+ />
145
+ )}
146
+ {mode === "review" && (
147
+ <SplitButton
148
+ label={
149
+ workflow === "current"
150
+ ? "En cours"
151
+ : workflow === "finished"
152
+ ? "Rendue"
153
+ : "Corrigée"
154
+ }
155
+ severity="secondary"
156
+ size="small"
157
+ icon={
158
+ workflow === "current"
159
+ ? "pi pi-pencil"
160
+ : workflow === "finished"
161
+ ? "pi pi-envelope"
162
+ : "pi pi-check-square"
163
+ }
164
+ outlined
165
+ model={[
166
+ ...(workflow === "current"
167
+ ? []
168
+ : [
169
+ {
170
+ label: "Réautoriser la modification",
171
+ icon: "pi pi-pencil",
172
+ command: () => {
173
+ changeWorkflow("current");
174
+ },
157
175
  },
158
- },
159
- ]),
160
- ...(workflow === "finished"
161
- ? []
162
- : [
163
- {
164
- label: "Marquer comme rendue",
165
- icon: "pi pi-envelope",
166
- command: () => {
167
- changeWorkflow("finished");
176
+ ]),
177
+ ...(workflow === "finished"
178
+ ? []
179
+ : [
180
+ {
181
+ label: "Marquer comme rendue",
182
+ icon: "pi pi-envelope",
183
+ command: () => {
184
+ changeWorkflow("finished");
185
+ },
168
186
  },
169
- },
170
- ]),
171
- ...(workflow === "corrected"
172
- ? []
173
- : [
174
- {
175
- label: "Marquer comme corrigée",
176
- icon: "pi pi-check-square",
177
- command: () => {
178
- changeWorkflow("corrected");
187
+ ]),
188
+ ...(workflow === "corrected"
189
+ ? []
190
+ : [
191
+ {
192
+ label: "Marquer comme corrigée",
193
+ icon: "pi pi-check-square",
194
+ command: () => {
195
+ changeWorkflow("corrected");
196
+ },
179
197
  },
180
- },
181
- ]),
182
- ]}
183
- />
184
- )}
185
- <ConfirmDialog />
186
- </div>
198
+ ]),
199
+ ]}
200
+ />
201
+ )}
202
+ <ConfirmDialog />
203
+ </div>
204
+ </>
187
205
  );
188
206
  };
189
207
 
@@ -19,7 +19,6 @@ const GradingNav: React.FC = () => {
19
19
  const nid = useAppSelector(selectNid) as number;
20
20
  const mode = useAppSelector(selectMode);
21
21
  const activityNid = useAppSelector(selectActivityNid) as number;
22
- console.log("Le nid est : ", nid);
23
22
  useEffect(() => {
24
23
  evalApi.listSa(activityNid).then((j) => {
25
24
  setStudentList(j);
@@ -19,8 +19,15 @@ import {
19
19
  selectCanChoosePedagoLayout,
20
20
  selectCanChooseTheme,
21
21
  } from "../activityData/activityDataSlice";
22
+ import { ActivitySettingsDisplay } from "../activitySettings";
23
+ import { Button } from "primereact/button";
24
+ import settings from "../../settings";
22
25
 
23
- const SidebarContent: FC = () => {
26
+ type SidebarContentProps = {
27
+ showHelp?: () => void;
28
+ };
29
+
30
+ const SidebarContent: FC<SidebarContentProps> = (props) => {
24
31
  const canChooseOrientation = useAppSelector(selectCanChoosePedagoLayout);
25
32
  const canChooseTheme = useAppSelector(selectCanChooseTheme);
26
33
  const orientation = useAppSelector(selectOrientation);
@@ -29,11 +36,30 @@ const SidebarContent: FC = () => {
29
36
  const dispatch = useAppDispatch();
30
37
  return (
31
38
  <>
39
+ {props.showHelp && (
40
+ <div className={styles.sidebarCapytaleActions}>
41
+ <Button
42
+ severity="secondary"
43
+ size="small"
44
+ icon={"pi pi-question-circle"}
45
+ onClick={() => {
46
+ props.showHelp!();
47
+ }}
48
+ outlined
49
+ label="Documentation"
50
+ tooltip="Voir la documentation"
51
+ tooltipOptions={{
52
+ position: "left",
53
+ showDelay: settings.TOOLTIP_SHOW_DELAY,
54
+ }}
55
+ />
56
+ </div>
57
+ )}
32
58
  {sidebarActions.length > 0 && (
33
59
  <Fieldset
34
60
  legend="Actions"
35
61
  className={classNames(
36
- styles.sidebarFieldset,
62
+ "sidebarFieldset",
37
63
  styles.sidebarFieldsetButtons,
38
64
  )}
39
65
  >
@@ -41,9 +67,9 @@ const SidebarContent: FC = () => {
41
67
  </Fieldset>
42
68
  )}
43
69
  {canChooseOrientation && (
44
- <Fieldset legend="Disposition" className={styles.sidebarFieldset}>
45
- <div className={styles.sidebarRadioButtons}>
46
- <div className={styles.sidebarRadioGroup}>
70
+ <Fieldset legend="Disposition" className="sidebarFieldset">
71
+ <div className="sidebarRadioButtons">
72
+ <div className="sidebarRadioGroup">
47
73
  <RadioButton
48
74
  inputId="rb-horizontal"
49
75
  name="horizontal"
@@ -55,7 +81,7 @@ const SidebarContent: FC = () => {
55
81
  Horizontale
56
82
  </label>
57
83
  </div>
58
- <div className={styles.sidebarRadioGroup}>
84
+ <div className="sidebarRadioGroup">
59
85
  <RadioButton
60
86
  inputId="rb-vertical"
61
87
  name="vertical"
@@ -71,9 +97,9 @@ const SidebarContent: FC = () => {
71
97
  </Fieldset>
72
98
  )}
73
99
  {canChooseTheme && (
74
- <Fieldset legend="Thème" className={styles.sidebarFieldset}>
75
- <div className={styles.sidebarRadioButtons}>
76
- <div className={styles.sidebarRadioGroup}>
100
+ <Fieldset legend="Thème" className="sidebarFieldset">
101
+ <div className="sidebarRadioButtons">
102
+ <div className="sidebarRadioGroup">
77
103
  <RadioButton
78
104
  inputId="rb-light"
79
105
  name="light"
@@ -88,7 +114,7 @@ const SidebarContent: FC = () => {
88
114
  Clair
89
115
  </label>
90
116
  </div>
91
- <div className={styles.sidebarRadioGroup}>
117
+ <div className="sidebarRadioGroup">
92
118
  <RadioButton
93
119
  inputId="rb-dark"
94
120
  name="dark"
@@ -103,7 +129,7 @@ const SidebarContent: FC = () => {
103
129
  Sombre
104
130
  </label>
105
131
  </div>
106
- <div className={styles.sidebarRadioGroup}>
132
+ <div className="sidebarRadioGroup">
107
133
  <RadioButton
108
134
  inputId="rb-auto"
109
135
  name="auto"
@@ -118,6 +144,7 @@ const SidebarContent: FC = () => {
118
144
  </div>
119
145
  </Fieldset>
120
146
  )}
147
+ <ActivitySettingsDisplay />
121
148
  </>
122
149
  );
123
150
  };
@@ -139,24 +139,15 @@
139
139
  align-items: center;
140
140
  }
141
141
 
142
- .sidebarFieldset {
143
- margin-bottom: 8px;
144
- }
145
-
146
142
  .sidebarFieldsetButtons :global(.p-fieldset-content) {
147
143
  display: flex;
148
144
  flex-direction: column;
149
145
  gap: 6px;
150
146
  }
151
147
 
152
- .sidebarRadioButtons {
153
- display: flex;
154
- flex-direction: column;
155
- gap: 4px;
156
- }
157
-
158
- .sidebarRadioGroup {
159
- display: flex;
160
- gap: 8px;
161
- align-items: center;
148
+ .sidebarCapytaleActions {
149
+ margin-bottom: 8px;
150
+ & > button {
151
+ width: 100%;
152
+ }
162
153
  }
@@ -48,7 +48,6 @@ const Pedago: React.FC<DivProps> = ({ className, ...props }) => {
48
48
  ) => {
49
49
  dispatch(setComments(event.target.value));
50
50
  dispatch(setIsMPDirty(true));
51
- console.log("51");
52
51
  };
53
52
 
54
53
  const handleGradingChange: ChangeEventHandler<HTMLTextAreaElement> = (
@@ -56,7 +55,6 @@ const Pedago: React.FC<DivProps> = ({ className, ...props }) => {
56
55
  ) => {
57
56
  dispatch(setGrading(event.target.value));
58
57
  dispatch(setIsMPDirty(true));
59
- console.log("59");
60
58
  };
61
59
 
62
60
  return (
package/src/index.css CHANGED
@@ -33,8 +33,57 @@ body,
33
33
  color-scheme: light;
34
34
  }
35
35
 
36
+ .p-splitter-panel:has(> #meta-player-content-cover) {
37
+ position: relative;
38
+ }
39
+
40
+ #meta-player-content-cover {
41
+ display: none;
42
+ width: 100%;
43
+ height: 100%;
44
+ position: absolute;
45
+ top: 0;
46
+ left: 0;
47
+ z-index: 1000;
48
+ background-color: rgba(0, 0, 0, 0);
49
+ .p-splitter-resizing & {
50
+ display: block;
51
+ }
52
+ }
53
+
36
54
  #meta-player-content,
37
55
  :has(> #meta-player-content) {
38
56
  height: 100%;
39
57
  background-color: var(--surface-a);
40
58
  }
59
+
60
+ .sidebarFieldset {
61
+ margin-bottom: 8px;
62
+ }
63
+
64
+ .sidebarRadioButtons {
65
+ display: flex;
66
+ flex-direction: column;
67
+ gap: 4px;
68
+ }
69
+
70
+ .sidebarRadioGroup {
71
+ display: flex;
72
+ gap: 8px;
73
+ align-items: center;
74
+ }
75
+
76
+ #metaPlayerHelpDialog {
77
+ width: 80vw;
78
+ height: 80vh;
79
+ }
80
+
81
+ #metaPlayerHelpDialog_content {
82
+ padding: 0;
83
+ & iframe {
84
+ width: 100%;
85
+ height: 100%;
86
+ border: 0;
87
+ display: block;
88
+ }
89
+ }
@@ -0,0 +1,32 @@
1
+ // https://medium.com/@stheodorejohn/javascript-object-deep-equality-comparison-in-javascript-7aa227e889d4
2
+
3
+ export function deepEqual(obj1: any, obj2: any): boolean {
4
+ // Base case: If both objects are identical, return true.
5
+ if (obj1 === obj2) {
6
+ return true;
7
+ }
8
+ // Check if both objects are objects and not null.
9
+ if (
10
+ typeof obj1 !== "object" ||
11
+ typeof obj2 !== "object" ||
12
+ obj1 === null ||
13
+ obj2 === null
14
+ ) {
15
+ return false;
16
+ }
17
+ // Get the keys of both objects.
18
+ const keys1 = Object.keys(obj1);
19
+ const keys2 = Object.keys(obj2);
20
+ // Check if the number of keys is the same.
21
+ if (keys1.length !== keys2.length) {
22
+ return false;
23
+ }
24
+ // Iterate through the keys and compare their values recursively.
25
+ for (const key of keys1) {
26
+ if (!keys2.includes(key) || !deepEqual(obj1[key], obj2[key])) {
27
+ return false;
28
+ }
29
+ }
30
+ // If all checks pass, the objects are deep equal.
31
+ return true;
32
+ }
@@ -1,35 +0,0 @@
1
- import { FC } from "react";
2
- import { useAppDispatch } from "../../app/hooks";
3
- import { setPlayerSettings } from "./activityDataSlice";
4
- import { setLayout } from "../layout/layoutSlice";
5
- import { setAutoSwitch, switchToTheme } from "../theming/themingSlice";
6
- import { MetaPlayerOptions } from "./metaPlayerOptions";
7
-
8
- type OptionSetterProps = {
9
- options: MetaPlayerOptions;
10
- };
11
-
12
- const OptionSetter: FC<OptionSetterProps> = ({ options }) => {
13
- const dispatch = useAppDispatch();
14
- if (!options.supportsLightTheme && !options.supportsDarkTheme) {
15
- throw new Error("At least one theme must be supported");
16
- }
17
- dispatch(setPlayerSettings(options));
18
- dispatch(
19
- setLayout(
20
- options.pedagoLayout === "horizontal" ||
21
- options.pedagoLayout === "default-horizontal"
22
- ? "horizontal"
23
- : "vertical",
24
- ),
25
- );
26
- if (options.supportsDarkTheme && options.supportsLightTheme) {
27
- dispatch(setAutoSwitch(true));
28
- } else {
29
- dispatch(setAutoSwitch(false));
30
- dispatch(switchToTheme(options.supportsLightTheme ? "light" : "dark"));
31
- }
32
- return null;
33
- };
34
-
35
- export default OptionSetter;