ThemeToggle.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. /*
  2. * Copyright 2021-2024 Avaiga Private Limited
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  5. * the License. You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  10. * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
  11. * specific language governing permissions and limitations under the License.
  12. */
  13. import React, { MouseEvent, useCallback, useContext, useEffect, useMemo } from "react";
  14. import Box from "@mui/material/Box";
  15. import Typography from "@mui/material/Typography";
  16. import { PaletteMode, SxProps } from "@mui/material";
  17. import ToggleButton from "@mui/material/ToggleButton";
  18. import ToggleButtonGroup from "@mui/material/ToggleButtonGroup";
  19. import WbSunny from "@mui/icons-material/WbSunny";
  20. import Brightness3 from "@mui/icons-material/Brightness3";
  21. import { TaipyActiveProps, getCssSize } from "./utils";
  22. import { TaipyContext } from "../../context/taipyContext";
  23. import { createThemeAction } from "../../context/taipyReducers";
  24. import { useClassNames } from "../../utils/hooks";
  25. import { getLocalStorageValue } from "../../context/utils";
  26. interface ThemeToggleProps extends TaipyActiveProps {
  27. style?: SxProps;
  28. label?: string;
  29. width?: string | number;
  30. }
  31. const boxSx = {
  32. display: "flex",
  33. flexDirection: "column",
  34. alignItems: "center",
  35. position: "fixed",
  36. top: "1rem",
  37. right: "1rem",
  38. "& > *": {
  39. m: 1,
  40. },
  41. } as SxProps;
  42. export const emptyStyle = {} as SxProps;
  43. const groupSx = { verticalAlign: "middle" };
  44. const ThemeToggle = (props: ThemeToggleProps) => {
  45. const { id, label = "Mode", style = emptyStyle, active = true } = props;
  46. const { state, dispatch } = useContext(TaipyContext);
  47. const className = useClassNames(props.libClassName, props.dynamicClassName, props.className);
  48. const changeMode = useCallback(
  49. (evt: MouseEvent, mode: PaletteMode) => mode !== null && dispatch(createThemeAction(mode === "dark")),
  50. [dispatch]
  51. );
  52. useEffect(() => {
  53. const localMode = getLocalStorageValue("theme", state.theme.palette.mode, ["light", "dark"]);
  54. if (state.theme.palette.mode !== localMode) {
  55. dispatch(createThemeAction(localMode === "dark"));
  56. }
  57. }, [state.theme.palette.mode, dispatch]);
  58. const mainSx = useMemo(
  59. () =>
  60. props.width
  61. ? ({ ...boxSx, ...style, width: getCssSize(props.width) } as SxProps)
  62. : ({ ...boxSx, ...style } as SxProps),
  63. [style, props.width]
  64. );
  65. return (
  66. <Box id={id} sx={mainSx} className={className}>
  67. <Typography>{label}</Typography>
  68. <ToggleButtonGroup
  69. value={state.theme.palette.mode}
  70. exclusive
  71. onChange={changeMode}
  72. aria-label="Theme mode"
  73. disabled={!active}
  74. sx={groupSx}
  75. fullWidth={!!props.width}
  76. >
  77. <ToggleButton value="light" aria-label="light" title="Light">
  78. <WbSunny />
  79. </ToggleButton>
  80. <ToggleButton value="dark" aria-label="dark" title="Dark">
  81. <Brightness3 />
  82. </ToggleButton>
  83. </ToggleButtonGroup>
  84. </Box>
  85. );
  86. };
  87. export default ThemeToggle;