Skip to content

Commit 9793ab1

Browse files
meganrmShrimpCryptidCopilot
authored
Feature/navigation (#202)
* make indicator clickable * switch module and save which have been completed * add pop confirm * Update src/components/PageIndicator.tsx Co-authored-by: Peyton Lee <[email protected]> * type index as number * add key * make sure index is a number * use a set for completed modules * show cursor * fix merge conflict * fix merge conflict * Update src/components/PageIndicator.tsx Co-authored-by: Copilot <[email protected]> * clean up css --------- Co-authored-by: Peyton Lee <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent 8f1ef09 commit 9793ab1

File tree

6 files changed

+100
-37
lines changed

6 files changed

+100
-37
lines changed

src/App.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ function App() {
9292
const [timeFactor, setTimeFactor] = useState(
9393
LiveSimulationData.INITIAL_TIME_FACTOR
9494
);
95+
96+
const [completedModules, setCompletedModules] = useState<Set<Module>>(
97+
new Set()
98+
);
9599
const [viewportSize, setViewportSize] = useState(DEFAULT_VIEWPORT_SIZE);
96100
const adjustableAgentName =
97101
LiveSimulationData.ADJUSTABLE_AGENT_MAP[currentModule];
@@ -478,8 +482,13 @@ function App() {
478482

479483
// User input handlers
480484

485+
const addCompletedModule = (module: Module) => {
486+
setCompletedModules((prev: Set<Module>) => new Set(prev).add(module));
487+
};
488+
481489
const setModule = (module: Module) => {
482490
setPage(FIRST_PAGE[module]);
491+
clearAllAnalysisState();
483492
setCurrentModule(module);
484493
setIsPlaying(false);
485494
};
@@ -683,6 +692,8 @@ function App() {
683692
timeUnit: simulationData.timeUnit,
684693
trajectoryName,
685694
viewportSize,
695+
addCompletedModule,
696+
completedModules,
686697
}}
687698
>
688699
<MainLayout

src/components/PageIndicator.tsx

Lines changed: 63 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import React from "react";
2-
import { Progress, Flex } from "antd";
1+
import React, { useMemo } from "react";
2+
import { Progress, Flex, Popconfirm } from "antd";
33
import { map } from "lodash";
44
import classNames from "classnames";
55

66
import { moduleNames } from "../content";
77
import styles from "./page-indicator.module.css";
8+
import { SimulariumContext } from "../simulation/context";
89

910
interface PageIndicatorProps {
1011
title: string;
@@ -17,45 +18,80 @@ const PageIndicator: React.FC<PageIndicatorProps> = ({
1718
page,
1819
total,
1920
}) => {
20-
let indexOfActiveModule = -1;
21+
const { module, setModule, completedModules } =
22+
React.useContext(SimulariumContext);
23+
const indexOfActiveModule: number = useMemo(() => {
24+
let toReturn = -1;
25+
map(moduleNames, (name, index) => {
26+
if (name === title) {
27+
toReturn = Number(index);
28+
}
29+
});
30+
return toReturn;
31+
}, [title]);
2132

2233
const getModulePercent = (isActiveModule: boolean, moduleIndex: number) => {
2334
if (isActiveModule) {
2435
return (page / total) * 100;
25-
} else if (moduleIndex < indexOfActiveModule) {
36+
} else if (completedModules.has(moduleIndex)) {
2637
return 100;
2738
} else {
2839
return 0;
2940
}
3041
};
42+
43+
const getTitle = (moduleIndex: number) => {
44+
if (module === moduleIndex) {
45+
return "Restart module";
46+
} else {
47+
return "Leave module";
48+
}
49+
};
50+
51+
const getMessage = (moduleIndex: number) => {
52+
if (module === moduleIndex) {
53+
return "Are you sure you want to restart this module? You will lose all progress.";
54+
} else {
55+
return "Are you sure you want to leave this module? You will lose all progress.";
56+
}
57+
};
58+
3159
return (
3260
<Flex align="center" justify="flex-end" className={styles.container}>
33-
{map(moduleNames, (name, index) => {
61+
{map(moduleNames, (name, index: string) => {
3462
const moduleIndex = Number(index);
35-
const isActiveModule = name === title;
36-
if (isActiveModule) {
37-
indexOfActiveModule = moduleIndex;
38-
}
63+
const isActiveModule = moduleIndex === indexOfActiveModule;
3964
return (
40-
<div
41-
key={index}
42-
className={classNames(styles.progressBarWrapper, {
43-
[styles.previous]:
44-
moduleIndex <= indexOfActiveModule,
45-
[styles.current]: isActiveModule,
46-
})}
65+
<Popconfirm
66+
key={moduleIndex}
67+
title={getTitle(moduleIndex)}
68+
description={getMessage(moduleIndex)}
69+
onConfirm={() => {
70+
setModule(moduleIndex);
71+
}}
72+
onCancel={() => {}}
73+
okText="Yes"
74+
cancelText="No"
4775
>
48-
<div className={styles.title}>{name}</div>
49-
<Progress
50-
className={styles.progressBar}
51-
size={["100%", 4]}
52-
percent={getModulePercent(
53-
isActiveModule,
54-
moduleIndex
55-
)}
56-
showInfo={false}
57-
/>
58-
</div>
76+
<div
77+
className={classNames(styles.progressBarWrapper, {
78+
[styles.previous]:
79+
moduleIndex <= indexOfActiveModule,
80+
[styles.current]: isActiveModule,
81+
})}
82+
>
83+
<div className={styles.title}>{name}</div>
84+
<Progress
85+
className={styles.progressBar}
86+
size={["100%", 4]}
87+
percent={getModulePercent(
88+
isActiveModule,
89+
moduleIndex
90+
)}
91+
showInfo={false}
92+
/>
93+
</div>
94+
</Popconfirm>
5995
);
6096
})}
6197
</Flex>

src/components/page-indicator.module.css

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
display: flex;
99
flex-direction: column;
1010
position: relative;
11+
cursor: pointer;
1112
}
1213

1314
.text-format {
@@ -27,12 +28,23 @@
2728
position: absolute;
2829
width: 10px;
2930
height: 10px;
30-
top: 23px; /* aligned by eye after rest of layout created */
31-
left: -2px;
31+
top: 24px; /* aligned by eye after rest of layout created */
32+
left: 0px;
3233
border-radius: 50%;
3334
background-color: var(--dark-gray);
3435
}
3536

37+
/* click target */
38+
.progress-bar-wrapper::after {
39+
content: "";
40+
position: absolute;
41+
width: 20px;
42+
height: 20px;
43+
top: 20px;
44+
left: -5px;
45+
border-radius: 50%;
46+
}
47+
3648
.progress-bar {
3749
margin-top: 8px;
3850
line-height: 7.5px;

src/components/quiz-questions/KdQuestion.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const KdQuestion: React.FC<KdQuestionProps> = ({ kd, canAnswer }) => {
1919
const [selectedAnswer, setSelectedAnswer] = useState<number | null>(null);
2020
const [formState, setFormState] = useState(FormState.Clear);
2121

22-
const { module } = useContext(SimulariumContext);
22+
const { module, addCompletedModule } = useContext(SimulariumContext);
2323

2424
useEffect(() => {
2525
setSelectedAnswer(null);
@@ -82,6 +82,7 @@ const KdQuestion: React.FC<KdQuestionProps> = ({ kd, canAnswer }) => {
8282
Math.abs(selectedAnswer - correctAnswer) / correctAnswer;
8383
if (closeness <= tolerance) {
8484
setFormState(FormState.Correct);
85+
addCompletedModule(module);
8586
} else {
8687
setFormState(FormState.Incorrect);
8788
}

src/simulation/LiveSimulationData.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,12 @@ const agentC: InputAgent = {
4848
color: AGENT_C_COLOR,
4949
};
5050

51-
const kds = {
52-
[Module.A_B_AB]: 0.75,
53-
[Module.A_C_AC]: 74,
54-
[Module.A_B_D_AB]: 5,
55-
};
56-
5751
export default class LiveSimulation implements ISimulationData {
52+
static ESTIMATED_SOLUTIONS = {
53+
[Module.A_B_AB]: 0.75,
54+
[Module.A_C_AC]: 74,
55+
[Module.A_B_D_AB]: 5,
56+
};
5857
static NAME_TO_FUNCTION_MAP = {
5958
[AgentName.A]: AgentFunction.Fixed,
6059
[AgentName.B]: AgentFunction.Adjustable,
@@ -168,6 +167,6 @@ export default class LiveSimulation implements ISimulationData {
168167
}, {});
169168
};
170169
getKd = (module: Module): number => {
171-
return kds[module];
170+
return LiveSimulation.ESTIMATED_SOLUTIONS[module];
172171
};
173172
}

src/simulation/context.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ interface SimulariumContextType {
3939
timeUnit: string;
4040
trajectoryName: string;
4141
viewportSize: { width: number; height: number };
42+
addCompletedModule: (value: Module) => void;
43+
completedModules: Set<Module>;
4244
}
4345

4446
export const SimulariumContext = createContext({
@@ -68,4 +70,6 @@ export const SimulariumContext = createContext({
6870
timeUnit: NANO,
6971
trajectoryName: LIVE_SIMULATION_NAME,
7072
viewportSize: DEFAULT_VIEWPORT_SIZE,
73+
addCompletedModule: () => {},
74+
completedModules: new Set(),
7175
} as SimulariumContextType);

0 commit comments

Comments
 (0)