diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..baa31f10 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +optuna_dashboard/static/components/PlotlyDarkMode.ts \ No newline at end of file diff --git a/optuna_dashboard/static/components/App.tsx b/optuna_dashboard/static/components/App.tsx index e0c3ae5d..ca3c5327 100644 --- a/optuna_dashboard/static/components/App.tsx +++ b/optuna_dashboard/static/components/App.tsx @@ -1,25 +1,58 @@ -import React, { FC } from "react" +import React, { FC, useMemo, useState, useEffect } from "react" import { RecoilRoot } from "recoil" import { BrowserRouter as Router, Switch, Route } from "react-router-dom" import { SnackbarProvider } from "notistack" +import { createTheme, useMediaQuery, ThemeProvider, Box } from "@mui/material" import { StudyDetail } from "./StudyDetail" import { StudyList } from "./StudyList" export const App: FC = () => { + const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)") + const [colorMode, setColorMode] = useState<"light" | "dark">("light") + useEffect(() => { + setColorMode(prefersDarkMode ? "dark" : "light") + }, [prefersDarkMode]) + const theme = useMemo( + () => + createTheme({ + palette: { + mode: colorMode, + }, + }), + [colorMode] + ) + const toggleColorMode = () => { + setColorMode(colorMode === "dark" ? "light" : "dark") + } + return ( - - - - } - /> - } /> - - - + + + + + + } + /> + } + /> + + + + + ) } diff --git a/optuna_dashboard/static/components/GraphEdf.tsx b/optuna_dashboard/static/components/GraphEdf.tsx index 57cb81fc..f799e04a 100644 --- a/optuna_dashboard/static/components/GraphEdf.tsx +++ b/optuna_dashboard/static/components/GraphEdf.tsx @@ -10,6 +10,7 @@ import { SelectChangeEvent, useTheme, } from "@mui/material" +import { plotlyDarkTemplate } from "./PlotlyDarkMode" const plotDomId = "graph-edf" @@ -25,9 +26,9 @@ export const Edf: FC<{ useEffect(() => { if (study != null) { - plotEdf(study, objectiveId) + plotEdf(study, objectiveId, theme.palette.mode) } - }, [study, objectiveId]) + }, [study, objectiveId, theme.palette.mode]) return ( @@ -63,7 +64,7 @@ export const Edf: FC<{ ) } -const plotEdf = (study: StudyDetail, objectiveId: number) => { +const plotEdf = (study: StudyDetail, objectiveId: number, mode: string) => { if (document.getElementById(plotDomId) === null) { return } @@ -95,6 +96,7 @@ const plotEdf = (study: StudyDetail, objectiveId: number) => { r: 50, b: 50, }, + template: mode === "dark" ? plotlyDarkTemplate : {}, } const values = completedTrials.map((t) => target(t)) diff --git a/optuna_dashboard/static/components/GraphHistory.tsx b/optuna_dashboard/static/components/GraphHistory.tsx index 686dd75b..9607c63f 100644 --- a/optuna_dashboard/static/components/GraphHistory.tsx +++ b/optuna_dashboard/static/components/GraphHistory.tsx @@ -15,6 +15,7 @@ import { SelectChangeEvent, useTheme, } from "@mui/material" +import { plotlyDarkTemplate } from "./PlotlyDarkMode" const plotDomId = "graph-history" @@ -59,7 +60,8 @@ export const GraphHistory: FC<{ xAxis, logScale, filterCompleteTrial, - filterPrunedTrial + filterPrunedTrial, + theme.palette.mode ) } }, [ @@ -69,6 +71,7 @@ export const GraphHistory: FC<{ xAxis, filterPrunedTrial, filterCompleteTrial, + theme.palette.mode, ]) return ( @@ -171,7 +174,8 @@ const plotHistory = ( xAxis: string, logScale: boolean, filterCompleteTrial: boolean, - filterPrunedTrial: boolean + filterPrunedTrial: boolean, + mode: string ) => { if (document.getElementById(plotDomId) === null) { return @@ -191,6 +195,7 @@ const plotHistory = ( type: xAxis === "number" ? "linear" : "date", }, showlegend: false, + template: mode === "dark" ? plotlyDarkTemplate : {}, } let filteredTrials = study.trials.filter( diff --git a/optuna_dashboard/static/components/GraphHyperparameterImportances.tsx b/optuna_dashboard/static/components/GraphHyperparameterImportances.tsx index 296f71be..f2a8454c 100644 --- a/optuna_dashboard/static/components/GraphHyperparameterImportances.tsx +++ b/optuna_dashboard/static/components/GraphHyperparameterImportances.tsx @@ -12,6 +12,7 @@ import { } from "@mui/material" import { getParamImportances } from "../apiClient" +import { plotlyDarkTemplate } from "./PlotlyDarkMode" const plotDomId = "graph-hyperparameter-importances" // To match colors used by plot_param_importances in optuna. @@ -59,13 +60,13 @@ export const GraphHyperparameterImportances: FC<{ studyId, objectiveId ) - plotParamImportances(paramsImportanceData) + plotParamImportances(paramsImportanceData, theme.palette.mode) } if (numOfTrials > 0) { fetchAndPlotParamImportances(studyId, objectiveId) } - }, [numOfTrials, objectiveId]) + }, [numOfTrials, objectiveId, theme.palette.mode]) return ( @@ -102,7 +103,10 @@ export const GraphHyperparameterImportances: FC<{ ) } -const plotParamImportances = (paramsImportanceData: ParamImportances) => { +const plotParamImportances = ( + paramsImportanceData: ParamImportances, + mode: string +) => { if (document.getElementById(plotDomId) === null) { return } @@ -131,6 +135,7 @@ const plotParamImportances = (paramsImportanceData: ParamImportances) => { b: 50, }, showlegend: false, + template: mode === "dark" ? plotlyDarkTemplate : {}, } const plotData: Partial[] = [ diff --git a/optuna_dashboard/static/components/GraphIntermediateValues.tsx b/optuna_dashboard/static/components/GraphIntermediateValues.tsx index 28bc3b0f..09f4c146 100644 --- a/optuna_dashboard/static/components/GraphIntermediateValues.tsx +++ b/optuna_dashboard/static/components/GraphIntermediateValues.tsx @@ -1,15 +1,17 @@ import * as plotly from "plotly.js-dist" import React, { FC, useEffect } from "react" -import { Grid, Typography } from "@mui/material" +import { Grid, Typography, useTheme } from "@mui/material" +import { plotlyDarkTemplate } from "./PlotlyDarkMode" const plotDomId = "graph-intermediate-values" export const GraphIntermediateValues: FC<{ trials: Trial[] }> = ({ trials = [] }) => { + const theme = useTheme() useEffect(() => { - plotIntermediateValue(trials) - }, [trials]) + plotIntermediateValue(trials, theme.palette.mode) + }, [trials, theme.palette.mode]) return ( @@ -27,7 +29,7 @@ export const GraphIntermediateValues: FC<{ ) } -const plotIntermediateValue = (trials: Trial[]) => { +const plotIntermediateValue = (trials: Trial[], mode: string) => { if (document.getElementById(plotDomId) === null) { return } @@ -39,6 +41,7 @@ const plotIntermediateValue = (trials: Trial[]) => { r: 50, b: 0, }, + template: mode === "dark" ? plotlyDarkTemplate : {}, } if (trials.length === 0) { plotly.react(plotDomId, [], layout) diff --git a/optuna_dashboard/static/components/GraphParallelCoordinate.tsx b/optuna_dashboard/static/components/GraphParallelCoordinate.tsx index 5c43b524..2b1f6ef4 100644 --- a/optuna_dashboard/static/components/GraphParallelCoordinate.tsx +++ b/optuna_dashboard/static/components/GraphParallelCoordinate.tsx @@ -10,6 +10,7 @@ import { SelectChangeEvent, useTheme, } from "@mui/material" +import { plotlyDarkTemplate } from "./PlotlyDarkMode" const plotDomId = "graph-parallel-coordinate" @@ -25,16 +26,16 @@ export const GraphParallelCoordinate: FC<{ useEffect(() => { if (study !== null) { - plotCoordinate(study, objectiveId) + plotCoordinate(study, objectiveId, theme.palette.mode) } - }, [study, objectiveId]) + }, [study, objectiveId, theme.palette.mode]) return ( - Parallel coordinate + Parallel Coordinate {study !== null && study.directions.length !== 1 ? ( { +const plotCoordinate = ( + study: StudyDetail, + objectiveId: number, + mode: string +) => { if (document.getElementById(plotDomId) === null) { return } @@ -76,6 +81,7 @@ const plotCoordinate = (study: StudyDetail, objectiveId: number) => { r: 50, b: 0, }, + template: mode === "dark" ? plotlyDarkTemplate : {}, } if (study.trials.length === 0) { diff --git a/optuna_dashboard/static/components/GraphParetoFront.tsx b/optuna_dashboard/static/components/GraphParetoFront.tsx index c60f450a..eeae1c6c 100644 --- a/optuna_dashboard/static/components/GraphParetoFront.tsx +++ b/optuna_dashboard/static/components/GraphParetoFront.tsx @@ -10,6 +10,7 @@ import { SelectChangeEvent, useTheme, } from "@mui/material" +import { plotlyDarkTemplate } from "./PlotlyDarkMode" const plotDomId = "graph-pareto-front" @@ -30,9 +31,9 @@ export const GraphParetoFront: FC<{ useEffect(() => { if (study != null) { - plotParetoFront(study, objectiveXId, objectiveYId) + plotParetoFront(study, objectiveXId, objectiveYId, theme.palette.mode) } - }, [study, objectiveXId, objectiveYId]) + }, [study, objectiveXId, objectiveYId, theme.palette.mode]) return ( @@ -87,7 +88,8 @@ export const GraphParetoFront: FC<{ const plotParetoFront = ( study: StudyDetail, objectiveXId: number, - objectiveYId: number + objectiveYId: number, + mode: string ) => { if (document.getElementById(plotDomId) === null) { return @@ -100,6 +102,7 @@ const plotParetoFront = ( r: 50, b: 0, }, + template: mode === "dark" ? plotlyDarkTemplate : {}, } const trials: Trial[] = study ? study.trials : [] diff --git a/optuna_dashboard/static/components/GraphSlice.tsx b/optuna_dashboard/static/components/GraphSlice.tsx index 98d99050..b4a10198 100644 --- a/optuna_dashboard/static/components/GraphSlice.tsx +++ b/optuna_dashboard/static/components/GraphSlice.tsx @@ -12,6 +12,7 @@ import { SelectChangeEvent, useTheme, } from "@mui/material" +import { plotlyDarkTemplate } from "./PlotlyDarkMode" const plotDomId = "graph-slice" @@ -38,8 +39,15 @@ export const GraphSlice: FC<{ } useEffect(() => { - plotSlice(trials, objectiveId, selected, logXScale, logYScale) - }, [trials, objectiveId, selected, logXScale, logYScale]) + plotSlice( + trials, + objectiveId, + selected, + logXScale, + logYScale, + theme.palette.mode + ) + }, [trials, objectiveId, selected, logXScale, logYScale, theme.palette.mode]) const handleObjectiveChange = (event: SelectChangeEvent) => { setObjectiveId(event.target.value as number) @@ -126,7 +134,8 @@ const plotSlice = ( objectiveId: number, selected: string | null, logXScale: boolean, - logYScale: boolean + logYScale: boolean, + mode: string ) => { if (document.getElementById(plotDomId) === null) { return @@ -142,27 +151,17 @@ const plotSlice = ( xaxis: { title: selected || "", type: logXScale ? "log" : "linear", - zerolinecolor: "#f2f5fa", - zerolinewidth: 1.5, - linecolor: "#f2f5fa", - linewidth: 5, - gridcolor: "#f2f5fa", gridwidth: 1, automargin: true, }, yaxis: { title: "Objective Values", type: logYScale ? "log" : "linear", - zerolinecolor: "#f2f5fa", - zerolinewidth: 2, - linecolor: "#f2f5fa", - linewidth: 5, - gridcolor: "#f2f5fa", gridwidth: 1, automargin: true, }, - plot_bgcolor: "#E5ecf6", showlegend: false, + template: mode === "dark" ? plotlyDarkTemplate : {}, } const filteredTrials = trials.filter( @@ -195,23 +194,12 @@ const plotSlice = ( x: valuesNum, y: objectiveValues, mode: "markers", - marker: { - color: "#185799", - }, }, ] layout["xaxis"] = { title: selected, type: logXScale ? "log" : "linear", - zerolinecolor: "#f2f5fa", - zerolinewidth: 1.5, - linecolor: "#f2f5fa", - linewidth: 5, - gridcolor: "#f2f5fa", gridwidth: 1, - tickfont: { - color: "#000000", - }, automargin: true, // Otherwise the label is outside of the plot } plotly.react(plotDomId, trace, layout) @@ -228,23 +216,12 @@ const plotSlice = ( x: valuesCategorical, y: objectiveValues, mode: "markers", - marker: { - color: "#185799", - }, }, ] layout["xaxis"] = { title: selected, type: logXScale ? "log" : "linear", - zerolinecolor: "#f2f5fa", - zerolinewidth: 1.5, - linecolor: "#f2f5fa", - linewidth: 5, - gridcolor: "#f2f5fa", gridwidth: 1, - tickfont: { - color: "#000000", - }, tickvals: tickvals, ticktext: vocabArr, automargin: true, // Otherwise the label is outside of the plot diff --git a/optuna_dashboard/static/components/PlotlyDarkMode.ts b/optuna_dashboard/static/components/PlotlyDarkMode.ts new file mode 100644 index 00000000..f7d4d966 --- /dev/null +++ b/optuna_dashboard/static/components/PlotlyDarkMode.ts @@ -0,0 +1,8 @@ +import * as plotly from "plotly.js-dist" + +// Following template is extracted from the sdist of plotly Python library. +// See https://github.com/plotly/plotly.py/blob/v5.6.0/packages/python/plotly/templategen/__init__.py and +// https://github.com/plotly/plotly.py/blob/v5.6.0/packages/python/plotly/templategen/definitions.py + +//@ts-ignore +export const plotlyDarkTemplate: Partial = {"data":{"bar":[{"error_x":{"color":"#f2f5fa"},"error_y":{"color":"#f2f5fa"},"marker":{"line":{"color":"rgb(17,17,17)","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"bar"}],"barpolar":[{"marker":{"line":{"color":"rgb(17,17,17)","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"barpolar"}],"carpet":[{"aaxis":{"endlinecolor":"#A2B1C6","gridcolor":"#506784","linecolor":"#506784","minorgridcolor":"#506784","startlinecolor":"#A2B1C6"},"baxis":{"endlinecolor":"#A2B1C6","gridcolor":"#506784","linecolor":"#506784","minorgridcolor":"#506784","startlinecolor":"#A2B1C6"},"type":"carpet"}],"choropleth":[{"colorbar":{"outlinewidth":0,"ticks":""},"type":"choropleth"}],"contour":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"contour"}],"contourcarpet":[{"colorbar":{"outlinewidth":0,"ticks":""},"type":"contourcarpet"}],"heatmap":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"heatmap"}],"heatmapgl":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"heatmapgl"}],"histogram":[{"marker":{"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"histogram"}],"histogram2d":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"histogram2d"}],"histogram2dcontour":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"histogram2dcontour"}],"mesh3d":[{"colorbar":{"outlinewidth":0,"ticks":""},"type":"mesh3d"}],"parcoords":[{"line":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"parcoords"}],"pie":[{"automargin":true,"type":"pie"}],"scatter":[{"marker":{"line":{"color":"#283442"}},"type":"scatter"}],"scatter3d":[{"line":{"colorbar":{"outlinewidth":0,"ticks":""}},"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatter3d"}],"scattercarpet":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattercarpet"}],"scattergeo":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattergeo"}],"scattergl":[{"marker":{"line":{"color":"#283442"}},"type":"scattergl"}],"scattermapbox":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattermapbox"}],"scatterpolar":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatterpolar"}],"scatterpolargl":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatterpolargl"}],"scatterternary":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatterternary"}],"surface":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"surface"}],"table":[{"cells":{"fill":{"color":"#506784"},"line":{"color":"rgb(17,17,17)"}},"header":{"fill":{"color":"#2a3f5f"},"line":{"color":"rgb(17,17,17)"}},"type":"table"}]},"layout":{"annotationdefaults":{"arrowcolor":"#f2f5fa","arrowhead":0,"arrowwidth":1},"autotypenumbers":"strict","coloraxis":{"colorbar":{"outlinewidth":0,"ticks":""}},"colorscale":{"diverging":[[0,"#8e0152"],[0.1,"#c51b7d"],[0.2,"#de77ae"],[0.3,"#f1b6da"],[0.4,"#fde0ef"],[0.5,"#f7f7f7"],[0.6,"#e6f5d0"],[0.7,"#b8e186"],[0.8,"#7fbc41"],[0.9,"#4d9221"],[1,"#276419"]],"sequential":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"sequentialminus":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]},"colorway":["#636efa","#EF553B","#00cc96","#ab63fa","#FFA15A","#19d3f3","#FF6692","#B6E880","#FF97FF","#FECB52"],"font":{"color":"#f2f5fa"},"geo":{"bgcolor":"rgb(17,17,17)","lakecolor":"rgb(17,17,17)","landcolor":"rgb(17,17,17)","showlakes":true,"showland":true,"subunitcolor":"#506784"},"hoverlabel":{"align":"left"},"hovermode":"closest","mapbox":{"style":"dark"},"paper_bgcolor":"rgb(17,17,17)","plot_bgcolor":"rgb(17,17,17)","polar":{"angularaxis":{"gridcolor":"#506784","linecolor":"#506784","ticks":""},"bgcolor":"rgb(17,17,17)","radialaxis":{"gridcolor":"#506784","linecolor":"#506784","ticks":""}},"scene":{"xaxis":{"backgroundcolor":"rgb(17,17,17)","gridcolor":"#506784","gridwidth":2,"linecolor":"#506784","showbackground":true,"ticks":"","zerolinecolor":"#C8D4E3"},"yaxis":{"backgroundcolor":"rgb(17,17,17)","gridcolor":"#506784","gridwidth":2,"linecolor":"#506784","showbackground":true,"ticks":"","zerolinecolor":"#C8D4E3"},"zaxis":{"backgroundcolor":"rgb(17,17,17)","gridcolor":"#506784","gridwidth":2,"linecolor":"#506784","showbackground":true,"ticks":"","zerolinecolor":"#C8D4E3"}},"shapedefaults":{"line":{"color":"#f2f5fa"}},"sliderdefaults":{"bgcolor":"#C8D4E3","bordercolor":"rgb(17,17,17)","borderwidth":1,"tickwidth":0},"ternary":{"aaxis":{"gridcolor":"#506784","linecolor":"#506784","ticks":""},"baxis":{"gridcolor":"#506784","linecolor":"#506784","ticks":""},"bgcolor":"rgb(17,17,17)","caxis":{"gridcolor":"#506784","linecolor":"#506784","ticks":""}},"title":{"x":0.05},"updatemenudefaults":{"bgcolor":"#506784","borderwidth":0},"xaxis":{"automargin":true,"gridcolor":"#283442","linecolor":"#506784","ticks":"","title":{"standoff":15},"zerolinecolor":"#283442","zerolinewidth":2},"yaxis":{"automargin":true,"gridcolor":"#283442","linecolor":"#506784","ticks":"","title":{"standoff":15},"zerolinecolor":"#283442","zerolinewidth":2}}} diff --git a/optuna_dashboard/static/components/StudyDetail.tsx b/optuna_dashboard/static/components/StudyDetail.tsx index 2ee51108..9f7862d5 100644 --- a/optuna_dashboard/static/components/StudyDetail.tsx +++ b/optuna_dashboard/static/components/StudyDetail.tsx @@ -26,6 +26,8 @@ import FormControlLabel from "@mui/material/FormControlLabel" import MuiDialogTitle from "@mui/material/DialogTitle" import MuiDialogContent from "@mui/material/DialogContent" import CloseIcon from "@mui/icons-material/Close" +import Brightness4Icon from "@mui/icons-material/Brightness4" +import Brightness7Icon from "@mui/icons-material/Brightness7" import { DataGridColumn, DataGrid } from "./DataGrid" import { GraphParallelCoordinate } from "./GraphParallelCoordinate" @@ -51,7 +53,9 @@ export const useStudyDetailValue = (studyId: number): StudyDetail | null => { return studyDetails[studyId] || null } -export const StudyDetail: FC = () => { +export const StudyDetail: FC<{ + toggleColorMode: () => void +}> = ({ toggleColorMode }) => { const theme = useTheme() const action = actionCreator() const { studyId } = useParams() @@ -251,6 +255,18 @@ export const StudyDetail: FC = () => { {APP_BAR_TITLE} + { + toggleColorMode() + }} + color="inherit" + > + {theme.palette.mode === "dark" ? ( + + ) : ( + + )} + diff --git a/optuna_dashboard/static/components/StudyList.tsx b/optuna_dashboard/static/components/StudyList.tsx index 8f428450..ae31eb24 100644 --- a/optuna_dashboard/static/components/StudyList.tsx +++ b/optuna_dashboard/static/components/StudyList.tsx @@ -1,4 +1,4 @@ -import React, { FC, useEffect } from "react" +import React, { FC, useEffect, useMemo } from "react" import { useRecoilValue } from "recoil" import { Link } from "react-router-dom" import { @@ -31,8 +31,12 @@ import { Add, AddBox, Delete, Refresh, Remove } from "@mui/icons-material" import { actionCreator } from "../action" import { DataGrid, DataGridColumn } from "./DataGrid" import { studySummariesState } from "../state" +import Brightness7Icon from "@mui/icons-material/Brightness7" +import Brightness4Icon from "@mui/icons-material/Brightness4" -export const StudyList: FC = () => { +export const StudyList: FC<{ + toggleColorMode: () => void +}> = ({ toggleColorMode }) => { const theme = useTheme() const [newStudySelectionAnchorEl, setNewStudySelectionAnchorEl] = @@ -55,6 +59,13 @@ export const StudyList: FC = () => { const [directions, setDirections] = React.useState([ "minimize", ]) + const linkColor = useMemo( + () => + theme.palette.mode === "dark" + ? theme.palette.primary.light + : theme.palette.primary.dark, + [theme.palette.mode] + ) const action = actionCreator() const studies = useRecoilValue(studySummariesState) @@ -78,7 +89,10 @@ export const StudyList: FC = () => { label: "Name", sortable: true, toCellValue: (i) => ( - + {studies[i].study_name} ), @@ -212,6 +226,18 @@ export const StudyList: FC = () => { {APP_BAR_TITLE} + { + toggleColorMode() + }} + color="inherit" + > + {theme.palette.mode === "dark" ? ( + + ) : ( + + )} +