Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ An educational web app for exploring molecular binding simulations, built for th
```
binding-sim-edu/
├── CLAUDE.md <- this file
├── CONTEXT_ORGANIZATION.md <- context/state architecture docs
├── docs/CONTEXT_ORGANIZATION.md <- context/state architecture docs
├── src/
Comment thread
meganrm marked this conversation as resolved.
│ ├── components/ <- UI components
│ ├── hooks/ <- React hooks (incl. useSimulationContext.ts)
Expand All @@ -25,8 +25,7 @@ binding-sim-edu/

## State Architecture

Context is split into three providers — see `CONTEXT_ORGANIZATION.md` for full details:

Context is split into three providers — see `docs/CONTEXT_ORGANIZATION.md` for full details:
- **SimulariumUiContext** — page, module, section, viewport type, quiz, progression, completed modules
- **SimulariumSimulationContext** — playback, controller, trajectory, concentrations, agents, handlers
- **SimulariumAnalysisContext** — recorded concentrations, analysis reset
Expand Down
Binary file modified bun.lockb
Binary file not shown.
19 changes: 12 additions & 7 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ function App() {
1,
[halfFilled, uniqMeasuredConcentrations],
);
const canDetermineKd = useMemo(() => {
const canDetermineConstant = useMemo(() => {
return (
hasAValueAboveKd &&
hasAValueBelowKd &&
Expand Down Expand Up @@ -833,8 +833,12 @@ function App() {
<MainLayout
centerPanel={
<CenterPanel
kd={simulationData.getKd(currentModule)}
canDetermineEquilibrium={canDetermineKd}
eqConstant={simulationData.getEquilibriumConstant(
currentModule,
)}
canDetermineConstant={
canDetermineConstant
}
overlay={pageContent.visualContent}
/>
}
Expand All @@ -843,7 +847,7 @@ function App() {
pageContent={{
...pageContent,
nextButton:
(canDetermineKd &&
(canDetermineConstant &&
pageContent.section ===
Section.Experiment) ||
pageContent.nextButton,
Expand Down Expand Up @@ -920,9 +924,10 @@ function App() {
timeToEquilibrium:
timeToReachEquilibrium,
colors: dataColors,
kd: simulationData.getKd(
currentModule,
),
eqConstant:
simulationData.getEquilibriumConstant(
currentModule,
),
}}
equilibriumFeedback={
equilibriumFeedback
Expand Down
9 changes: 6 additions & 3 deletions src/components/HelpPopup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@ import { Popover } from "antd";
interface HelpPopupProps {
children: React.ReactNode;
content: React.ReactNode;
initialOpen: boolean;
open?: boolean;
trigger?: "hover" | "click" | "focus";
}

const HelpPopup: React.FC<HelpPopupProps> = ({
children,
content,
initialOpen,
open,
trigger,
}) => {
return (
<Popover
open={initialOpen}
open={open}
trigger={trigger}
content={content}
placement="left"
arrow={true}
Expand Down
16 changes: 12 additions & 4 deletions src/components/concentration-display/Concentration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import {
AgentName,
CurrentConcentration,
InputConcentration,
Module,
Section,
UiElement,
} from "../../types";
import {
useSimulariumAnalysis,
useSimulariumSimulation,
useSimulariumUi,
} from "../../hooks/useSimulationContext";
Expand Down Expand Up @@ -47,7 +49,12 @@ const Concentration: React.FC<AgentProps> = ({
}) => {
const { isPlaying, maxConcentration, getAgentColor } =
useSimulariumSimulation();
const { section, progressionElement } = useSimulariumUi();
const { module, section, progressionElement } = useSimulariumUi();
const { recordedConcentrations } = useSimulariumAnalysis();
const isSliderDisabled =
module === Module.A_B_D_AB &&
!recordedConcentrations.includes(0) &&
section === Section.Experiment;
const [width, setWidth] = useState<number>(0);

const MARGINS = 64.2;
Expand Down Expand Up @@ -79,13 +86,14 @@ const Concentration: React.FC<AgentProps> = ({
if (adjustableAgent === agent && !isPlaying) {
return (
<ConcentrationSlider
min={0}
disabled={isSliderDisabled}
initialValue={concentration[agent] || 0}
key={agent}
max={maxConcentration}
min={0}
name={agent}
initialValue={concentration[agent] || 0}
onChange={handleChange}
onChangeComplete={onChangeComplete}
key={agent}
/>
);
} else {
Expand Down
56 changes: 36 additions & 20 deletions src/components/concentration-display/ConcentrationSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ import Slider from "../shared/Slider";
import { useSimulariumAnalysis } from "../../hooks/useSimulationContext";
import styles from "./concentration-slider.module.css";
import classNames from "classnames";
import HelpPopup from "../HelpPopup";

interface SliderProps {
min: number;
max: number;
disabled?: boolean;
initialValue: number;
max: number;
min: number;
name: string;
onChange: (name: string, value: number) => void;
onChangeComplete?: (name: string, value: number) => void;
name: string;
}

const Mark: React.FC<{
Expand Down Expand Up @@ -53,12 +55,13 @@ const Mark: React.FC<{
};

const ConcentrationSlider: React.FC<SliderProps> = ({
min,
max,
disabled,
initialValue,
max,
min,
name,
onChange,
onChangeComplete,
name,
}) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
const disabledNumbers = [0];
Expand All @@ -75,26 +78,39 @@ const ConcentrationSlider: React.FC<SliderProps> = ({
: index
}
disabledNumbers={disabledNumbers}
onMouseUp={() => onChangeComplete?.(name, index)}
onMouseUp={
disabled
? () => {}
: () => onChangeComplete?.(name, index)
}
/>
),
};
}
return marks;
}, [min, max, disabledNumbers, onChangeComplete, name, stepSize]);
}, [min, max, disabledNumbers, disabled, onChangeComplete, name, stepSize]);
return (
<Slider
initialValue={initialValue}
className={styles.container}
name={name}
min={min}
max={max}
step={stepSize}
onChange={onChange}
onChangeComplete={onChangeComplete}
marks={marks}
disabledNumbers={disabledNumbers}
/>
<HelpPopup
content="You need to measure a baseline value before you can add any of the inhibitor"
trigger="hover"
open={disabled ? undefined : false}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does it mean for open to be undefined? Is it ever true?

>
<div style={{ width: "100%" }}>
<Slider
disabled={disabled}
initialValue={initialValue}
className={styles.container}
name={name}
min={min}
max={max}
step={stepSize}
onChange={onChange}
onChangeComplete={onChangeComplete}
marks={marks}
disabledNumbers={disabledNumbers}
/>
</div>
</HelpPopup>
);
};

Expand Down
24 changes: 19 additions & 5 deletions src/components/main-layout/CenterPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ import React from "react";
import ViewSwitch from "../ViewSwitch";
import EquilibriumQuestion from "../quiz-questions/EquilibriumQuestion";
import KdQuestion from "../quiz-questions/KdQuestion";
import KiQuestion from "../quiz-questions/KiQuestion";
import styles from "./layout.module.css";
import { useSimulariumUi } from "../../hooks/useSimulationContext";
import { Module } from "../../types";

interface CenterPanelProps {
kd: number;
canDetermineEquilibrium: boolean;
eqConstant: number;
canDetermineConstant: boolean;
overlay?: JSX.Element;
}

Expand All @@ -19,18 +22,29 @@ export const CenterPanelContext = React.createContext<{
});

const CenterPanel: React.FC<CenterPanelProps> = ({
kd,
canDetermineEquilibrium,
eqConstant,
canDetermineConstant,
overlay,
}) => {
const [lastOpened, setLastOpened] = React.useState<string | null>(null);
const { module } = useSimulariumUi();
return (
<>
<ViewSwitch />
<CenterPanelContext.Provider value={{ lastOpened, setLastOpened }}>
<div className={styles.questionContainer}>
<EquilibriumQuestion />
<KdQuestion kd={kd} canAnswer={canDetermineEquilibrium} />
{module === Module.A_B_D_AB ? (
<KiQuestion
ki={eqConstant}
canAnswer={canDetermineConstant}
/>
) : (
<KdQuestion
kd={eqConstant}
canAnswer={canDetermineConstant}
/>
)}
</div>
</CenterPanelContext.Provider>
{overlay && overlay}
Expand Down
6 changes: 3 additions & 3 deletions src/components/main-layout/RightPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ interface RightPanelProps {
productConcentrations: number[];
timeToEquilibrium: number[];
colors: string[];
kd: number;
eqConstant: number;
};
equilibriumFeedback: ReactNode | string;
showHelpPanel: boolean;
Expand Down Expand Up @@ -71,7 +71,7 @@ const RightPanel: React.FC<RightPanelProps> = ({
content={
"Use this plot to help determine when the reaction reaches equilibrium."
}
initialOpen={showHelpPanel}
open={showHelpPanel}
>
<ProductConcentrationPlot
data={data}
Expand All @@ -94,7 +94,7 @@ const RightPanel: React.FC<RightPanelProps> = ({
x={equilibriumData.reactantConcentrations}
y={equilibriumData.productConcentrations}
colors={equilibriumData.colors}
kd={equilibriumData.kd}
eqConstant={equilibriumData.eqConstant}
/>
<div className={styles.recordButton}>
<RecordEquilibriumButton
Expand Down
6 changes: 3 additions & 3 deletions src/components/plots/EquilibriumPlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ interface PlotProps {
height: number;
width: number;
colors: string[];
kd: number;
eqConstant: number;
}

const EquilibriumPlot: React.FC<PlotProps> = ({
Expand All @@ -34,7 +34,7 @@ const EquilibriumPlot: React.FC<PlotProps> = ({
height,
width,
colors,
kd,
eqConstant,
}) => {
const {
fixedAgentStartingConcentration,
Expand All @@ -44,7 +44,7 @@ const EquilibriumPlot: React.FC<PlotProps> = ({
} = useSimulariumSimulation();
const { module } = useSimulariumUi();
const xMax = Math.max(...x);
const xAxisMax = Math.max(kd * 2, xMax * 1.1);
const xAxisMax = Math.max(eqConstant * 2, xMax * 1.1);

// Calculate the best fit line for the data points
const bestFit = useMemo(() => {
Expand Down
Loading
Loading