Skip to content
Merged
63 changes: 63 additions & 0 deletions components/ThemeModeContext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* ThemeModeContext.js
*
* App-wide light/dark mode state, wired in once at pages/_app.js so every
* page shares the same theme instance and the toggle in ResponsiveAppBar can
* flip it everywhere at once. Persisted to localStorage; defaults to light
* on first visit and while the persisted value hasn't loaded yet server-side
* (localStorage isn't available during SSR, so the very first client render
* always starts light too, then switches after mount if "dark" was saved --
* a brief flash for returning dark-mode users, not worth a blocking
* SSR/cookie script for a research tool's admin toggle).
*/

import { CssBaseline, ThemeProvider } from "@mui/material";
import { createContext, useContext, useEffect, useMemo, useState } from "react";
import { createAppTheme } from "./theme";

const STORAGE_KEY = "redux-theme-mode";

const ThemeModeContext = createContext({
mode: "light",
toggleMode: () => {},
});

export function ThemeModeProvider({ children }) {
const [mode, setMode] = useState("light");

useEffect(() => {
// Deliberately read-then-setState here, not moved into a useState lazy initializer:
// localStorage isn't available during SSR, so the server always renders "light". Reading
// it in the initializer would make the client's first (pre-hydration) render read real
// localStorage and could produce "dark" while the server-rendered HTML says "light" --
// a hydration mismatch, which is worse than this rule's generic extra-render concern.
const saved = window.localStorage.getItem(STORAGE_KEY);
if (saved === "light" || saved === "dark") {
// eslint-disable-next-line react-hooks/set-state-in-effect
setMode(saved);
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
}
}, []);

const toggleMode = () => {
setMode((current) => {
const next = current === "light" ? "dark" : "light";
window.localStorage.setItem(STORAGE_KEY, next);
return next;
});
};

const theme = useMemo(() => createAppTheme(mode), [mode]);

return (
<ThemeModeContext.Provider value={{ mode, toggleMode }}>
<ThemeProvider theme={theme}>
<CssBaseline />
{children}
</ThemeProvider>
</ThemeModeContext.Provider>
);
}

export function useThemeMode() {
return useContext(ThemeModeContext);
}
12 changes: 9 additions & 3 deletions components/pageblocks/ProblemRowReact.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import { COMPLEXITY_CLASS_ORDER, complexityClassLabel } from '../hooks/ProblemFi
import ProblemInstanceParser from '../../Tools/ProblemInstanceParser';
import ProblemSection from '../widgets/ProblemSection';
import SearchBarExtensible from '../widgets/SearchBarExtensible';
import { surfaceColors, textColors } from '../theme';
import { useThemeMode } from '../ThemeModeContext';

const ACCORDION_FORM_ONE = { placeHolder: "Select problem" }
const ACCORDION_FORM_TWO = { placeHolder: "default instance" }
Expand Down Expand Up @@ -60,6 +62,10 @@ function complexityClassGroupLabel(complexityClass) {
* Creates an accordion that has a nested autocomplete search bar, as well as an editable problem instance textbox
*/
export default function ProblemRowReact({ url, problemName, setProblemName, problemNameMap, setProblemInstance, dragHandleProps }) {
const { mode } = useThemeMode();
const surface = surfaceColors(mode);
const text = textColors(mode);

const problemInfo = useProblemInfo(url, problemName);
const { problemIndex, reductionGraph } = useProblemIndex(url);
const {
Expand Down Expand Up @@ -248,9 +254,9 @@ export default function ProblemRowReact({ url, problemName, setProblemName, prob
title="Drag to reorder"
sx={{
cursor: 'grab',
color: '#424242',
backgroundColor: '#f5f5f5',
'&:hover': { backgroundColor: '#e0e0e0' },
color: text.body,
backgroundColor: surface.surfaceAlt,
'&:hover': { backgroundColor: surface.surfaceAltHover },
mr: 1,
}}
>
Expand Down
12 changes: 9 additions & 3 deletions components/pageblocks/ReduceToRowReact.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import { useProblemInfo, useReducerInfo } from '../hooks/ProblemProvider'
import PopoverTooltipClick from '../widgets/PopoverTooltipClick';
import ProblemSection from '../widgets/ProblemSection';
import SearchBarExtensible from '../widgets/SearchBarExtensible';
import { surfaceColors, textColors } from '../theme';
import { useThemeMode } from '../ThemeModeContext';
import { complexityClassLabel } from '../hooks/ProblemFilters/complexityClassOrder';
import { reductionTypeLabel } from '../hooks/ProblemFilters/tagLabels';

Expand Down Expand Up @@ -71,6 +73,10 @@ export default function ReduceToRowReact({
setReducedInstance,
dragHandleProps,
}) {
const { mode } = useThemeMode();
const surface = surfaceColors(mode);
const text = textColors(mode);

const reduceToInfo = useProblemInfo(url, chosenReduceTo);
const reducerInfo = useReducerInfo(url, chosenReductionType);

Expand Down Expand Up @@ -201,9 +207,9 @@ export default function ReduceToRowReact({
title="Drag to reorder"
sx={{
cursor: 'grab',
color: '#424242',
backgroundColor: '#f5f5f5',
'&:hover': { backgroundColor: '#e0e0e0' },
color: text.body,
backgroundColor: surface.surfaceAlt,
'&:hover': { backgroundColor: surface.surfaceAltHover },
mr: 1,
}}
>
Expand Down
12 changes: 9 additions & 3 deletions components/pageblocks/SolveRowReact.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import PopoverTooltipClick from "../widgets/PopoverTooltipClick";
import { useSolverInfo } from "../hooks/ProblemProvider";
import ProblemSection from "../widgets/ProblemSection";
import SearchBarExtensible from "../widgets/SearchBarExtensible";
import { surfaceColors, textColors } from "../theme";
import { useThemeMode } from "../ThemeModeContext";
import { solverTypeLabel } from "../hooks/ProblemFilters/tagLabels";

const ACCORDION_FORM_ONE = { placeHolder: "Select Solver" };
Expand Down Expand Up @@ -50,6 +52,10 @@ export default function SolveRowReact({
chosenReduceTo,
dragHandleProps,
}) {
const { mode } = useThemeMode();
const surface = surfaceColors(mode);
const text = textColors(mode);

const solverInfo = useSolverInfo(url, chosenSolver);

async function handleSolve() {
Expand Down Expand Up @@ -126,9 +132,9 @@ export default function SolveRowReact({
title="Drag to reorder"
sx={{
cursor: 'grab',
color: '#424242',
backgroundColor: '#f5f5f5',
'&:hover': { backgroundColor: '#e0e0e0' },
color: text.body,
backgroundColor: surface.surfaceAlt,
'&:hover': { backgroundColor: surface.surfaceAltHover },
mr: 1,
}}
>
Expand Down
17 changes: 14 additions & 3 deletions components/pageblocks/VerifyRowReact.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import PopoverTooltipClick from '../widgets/PopoverTooltipClick';
import { useVerifierInfo } from '../hooks/ProblemProvider';
import ProblemSection from '../widgets/ProblemSection';
import SearchBarExtensible from '../widgets/SearchBarExtensible';
import { surfaceColors, textColors } from '../theme';
import { useThemeMode } from '../ThemeModeContext';

const ACCORDION_FORM_ONE = { placeHolder: "Select verifier" }
const BUTTON = { buttonText: "Verify" }
Expand All @@ -38,6 +40,10 @@ export default function VerifyRowReact({
verifierNameMap,
dragHandleProps,
}) {
const { mode } = useThemeMode();
const surface = surfaceColors(mode);
const text = textColors(mode);

const [certificate, setCertificate] = useState("");
const [verifyResult, setVerifyResult] = useState("");
const verifierInfo = useVerifierInfo(url, chosenVerifier);
Expand Down Expand Up @@ -109,9 +115,9 @@ export default function VerifyRowReact({
title="Drag to reorder"
sx={{
cursor: 'grab',
color: '#424242',
backgroundColor: '#f5f5f5',
'&:hover': { backgroundColor: '#e0e0e0' },
color: text.body,
backgroundColor: surface.surfaceAlt,
'&:hover': { backgroundColor: surface.surfaceAltHover },
mr: 1,
}}
>
Expand All @@ -126,6 +132,11 @@ export default function VerifyRowReact({
as="textarea"
value={certificate}
onChange={(event) => setCertificate(event.target.value)}
style={{
backgroundColor: surface.surface,
color: text.body,
borderColor: surface.border,
}}
></FormControl>{" "}
{/**FORM CONTROL 2 (dropdown) */}
{"Verifier output: " + verifyResult + ""}
Expand Down
23 changes: 15 additions & 8 deletions components/pageblocks/VisualizeRowReact.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ import VisualizationLogic from "../widgets/VisualizationLogic";
import ProblemSection from "../widgets/ProblemSection";
import { useVisualizationInfo } from "../hooks/ProblemProvider";
import { isRenderable } from "../Visualization/svgs/renderability";
import { surfaceColors, textColors } from "../theme";
import { useThemeMode } from "../ThemeModeContext";

const CARD = { cardBodyText: "DEFAULT BODY", cardHeaderText: "Visualize" };
const SWITCHES = {
Expand Down Expand Up @@ -75,6 +77,10 @@ export default function VisualizeRowReact({
visualizationTypeMap,
dragHandleProps,
}) {
const { mode } = useThemeMode();
const surface = surfaceColors(mode);
const text = textColors(mode);

const visualizationInfo = useVisualizationInfo(url, chosenVisualization);

const unrenderableOptions = (VisualizationOptions || []).filter(
Expand Down Expand Up @@ -387,9 +393,9 @@ export default function VisualizeRowReact({
title="Drag to reorder"
sx={{
cursor: 'grab',
color: '#424242',
backgroundColor: '#f5f5f5',
'&:hover': { backgroundColor: '#e0e0e0' },
color: text.body,
backgroundColor: surface.surfaceAlt,
'&:hover': { backgroundColor: surface.surfaceAltHover },
mr: 1,
}}
>
Expand All @@ -403,13 +409,14 @@ export default function VisualizeRowReact({
<div
data-tour-id="viz-controls"
style={{
border: "2px solid #ccc",
border: mode === "dark" ? "2px solid rgba(255,255,255,0.10)" : "2px solid #ccc",
borderRadius: "8px",
padding: "16px",
marginBottom: "20px",
display: "flex",
justifyContent: "space-between",
backgroundColor: "#f9f9f9",
backgroundColor: surface.surfaceAlt,
color: text.body,
flexWrap: "wrap",
}}
>
Expand Down Expand Up @@ -475,9 +482,9 @@ export default function VisualizeRowReact({

{/* Switches */}
<div style={{ display: "flex", alignItems: "center", gap: "16px" }}>
<FormControlLabel disabled={disableReduction} checked={showReduction} control={<Switch />} label={SWITCHES.switch3} onChange={handleSwitch3Change} />
<FormControlLabel disabled={disableGadget} checked={showGadgets} control={<Switch id="highlightGadgets" />} label={SWITCHES.switch2} onChange={handleSwitch2Change} />
<FormControlLabel disabled={disableSolution} checked={showSolution} control={<Switch id="showSolution" />} label={SWITCHES.switch1} onChange={handleSwitch1Change} />
<FormControlLabel sx={{ color: text.body }} disabled={disableReduction} checked={showReduction} control={<Switch />} label={SWITCHES.switch3} onChange={handleSwitch3Change} />
<FormControlLabel sx={{ color: text.body }} disabled={disableGadget} checked={showGadgets} control={<Switch id="highlightGadgets" />} label={SWITCHES.switch2} onChange={handleSwitch2Change} />
<FormControlLabel sx={{ color: text.body }} disabled={disableSolution} checked={showSolution} control={<Switch id="showSolution" />} label={SWITCHES.switch1} onChange={handleSwitch1Change} />
</div>
</div>

Expand Down
Loading
Loading