github.com/turbot/steampipe@v1.7.0-rc.0.0.20240517123944-7cef272d4458/ui/dashboard/src/hooks/useTheme.tsx (about)

     1  import React, { createContext, useContext, useState } from "react";
     2  import useLocalStorage from "./useLocalStorage";
     3  import useMediaQuery from "./useMediaQuery";
     4  import { classNames } from "../utils/styles";
     5  
     6  export type Theme = {
     7    name: string;
     8    label: string;
     9  };
    10  
    11  type IThemes = {
    12    [key: string]: Theme;
    13  };
    14  
    15  const ThemeNames = {
    16    STEAMPIPE_DEFAULT: "steampipe-default",
    17    STEAMPIPE_DARK: "steampipe-dark",
    18  };
    19  
    20  const Themes: IThemes = {
    21    [ThemeNames.STEAMPIPE_DEFAULT]: {
    22      label: "Light",
    23      name: ThemeNames.STEAMPIPE_DEFAULT,
    24    },
    25    [ThemeNames.STEAMPIPE_DARK]: {
    26      label: "Dark",
    27      name: ThemeNames.STEAMPIPE_DARK,
    28    },
    29  };
    30  
    31  type IThemeContext = {
    32    localStorageTheme: string | null;
    33    theme: Theme;
    34    withFooterPadding: boolean;
    35    wrapperRef: React.Ref<null>;
    36    setTheme(theme: string): void;
    37    setWithFooterPadding(newValue: boolean): void;
    38    setWrapperRef(element: any): void;
    39  };
    40  
    41  const ThemeContext = createContext<IThemeContext | undefined>(undefined);
    42  
    43  const ThemeProvider = ({ children }) => {
    44    const [withFooterPadding, setWithFooterPadding] = useState(true);
    45    const [localStorageTheme, setLocalStorageTheme] =
    46      useLocalStorage("steampipe.ui.theme");
    47    const prefersDarkTheme = useMediaQuery("(prefers-color-scheme: dark)");
    48    const [wrapperRef, setWrapperRef] = useState(null);
    49    const doSetWrapperRef = (element) => setWrapperRef(() => element);
    50  
    51    let theme;
    52  
    53    if (
    54      localStorageTheme &&
    55      (localStorageTheme === ThemeNames.STEAMPIPE_DEFAULT ||
    56        localStorageTheme === ThemeNames.STEAMPIPE_DARK)
    57    ) {
    58      theme = Themes[localStorageTheme];
    59    } else if (prefersDarkTheme) {
    60      theme = Themes[ThemeNames.STEAMPIPE_DARK];
    61    } else {
    62      theme = Themes[ThemeNames.STEAMPIPE_DEFAULT];
    63    }
    64  
    65    return (
    66      <ThemeContext.Provider
    67        value={{
    68          localStorageTheme,
    69          theme,
    70          setTheme: setLocalStorageTheme,
    71          setWrapperRef: doSetWrapperRef,
    72          withFooterPadding,
    73          wrapperRef,
    74          setWithFooterPadding,
    75        }}
    76      >
    77        {children}
    78      </ThemeContext.Provider>
    79    );
    80  };
    81  
    82  const FullHeightThemeWrapper = ({ children }) => {
    83    const { setWrapperRef, theme, withFooterPadding } = useTheme();
    84    return (
    85      <div
    86        ref={setWrapperRef}
    87        className={classNames(
    88          `h-screen flex flex-col theme-${theme.name} bg-dashboard print:bg-white print:theme-steampipe-default text-foreground print:text-black overflow-y-hidden`,
    89          withFooterPadding ? "pb-4" : ""
    90        )}
    91      >
    92        {children}
    93      </div>
    94    );
    95  };
    96  
    97  const ThemeWrapper = ({ children }) => {
    98    const { setWrapperRef, theme } = useTheme();
    99    return (
   100      <div
   101        ref={setWrapperRef}
   102        className={classNames(
   103          `theme-${theme.name} bg-dashboard print:bg-white print:theme-steampipe-default text-foreground print:text-black`
   104        )}
   105      >
   106        {children}
   107      </div>
   108    );
   109  };
   110  
   111  const ModalThemeWrapper = ({ children }) => {
   112    const { setWrapperRef, theme } = useTheme();
   113    return (
   114      <div
   115        ref={setWrapperRef}
   116        className={`theme-${theme.name} print:bg-white print:theme-steampipe-default text-foreground print:text-black`}
   117      >
   118        {children}
   119      </div>
   120    );
   121  };
   122  
   123  const useTheme = () => {
   124    const context = useContext(ThemeContext);
   125    if (context === undefined) {
   126      throw new Error("useTheme must be used within a ThemeContext");
   127    }
   128    return context;
   129  };
   130  
   131  export {
   132    FullHeightThemeWrapper,
   133    ModalThemeWrapper,
   134    Themes,
   135    ThemeNames,
   136    ThemeProvider,
   137    ThemeWrapper,
   138    useTheme,
   139  };