diff options
Diffstat (limited to 'apps/browser-extension/src/utils')
| -rw-r--r-- | apps/browser-extension/src/utils/ThemeProvider.tsx | 73 | ||||
| -rw-r--r-- | apps/browser-extension/src/utils/css.ts | 7 | ||||
| -rw-r--r-- | apps/browser-extension/src/utils/providers.tsx | 9 |
3 files changed, 88 insertions, 1 deletions
diff --git a/apps/browser-extension/src/utils/ThemeProvider.tsx b/apps/browser-extension/src/utils/ThemeProvider.tsx new file mode 100644 index 00000000..79a0b32f --- /dev/null +++ b/apps/browser-extension/src/utils/ThemeProvider.tsx @@ -0,0 +1,73 @@ +import { createContext, useContext, useEffect, useState } from "react"; + +type Theme = "dark" | "light" | "system"; + +interface ThemeProviderProps { + children: React.ReactNode; + defaultTheme?: Theme; + storageKey?: string; +} + +interface ThemeProviderState { + theme: Theme; + setTheme: (theme: Theme) => void; +} + +const initialState: ThemeProviderState = { + theme: "system", + setTheme: () => null, +}; + +const ThemeProviderContext = createContext<ThemeProviderState>(initialState); + +export function ThemeProvider({ + children, + defaultTheme = "system", + storageKey = "vite-ui-theme", + ...props +}: ThemeProviderProps) { + const [theme, setTheme] = useState<Theme>( + () => (localStorage.getItem(storageKey) as Theme) || defaultTheme, + ); + + useEffect(() => { + const root = window.document.documentElement; + + root.classList.remove("light", "dark"); + + if (theme === "system") { + const systemTheme = window.matchMedia("(prefers-color-scheme: dark)") + .matches + ? "dark" + : "light"; + + root.classList.add(systemTheme); + return; + } + + root.classList.add(theme); + }, [theme]); + + const value = { + theme, + setTheme: (theme: Theme) => { + localStorage.setItem(storageKey, theme); + setTheme(theme); + }, + }; + + return ( + <ThemeProviderContext.Provider {...props} value={value}> + {children} + </ThemeProviderContext.Provider> + ); +} + +export const useTheme = () => { + const context = useContext(ThemeProviderContext); + + if (context === undefined) + throw new Error("useTheme must be used within a ThemeProvider"); + + return context; +}; diff --git a/apps/browser-extension/src/utils/css.ts b/apps/browser-extension/src/utils/css.ts new file mode 100644 index 00000000..88283f01 --- /dev/null +++ b/apps/browser-extension/src/utils/css.ts @@ -0,0 +1,7 @@ +import type { ClassValue } from "clsx"; +import { clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/apps/browser-extension/src/utils/providers.tsx b/apps/browser-extension/src/utils/providers.tsx index 4ca17016..4b571254 100644 --- a/apps/browser-extension/src/utils/providers.tsx +++ b/apps/browser-extension/src/utils/providers.tsx @@ -1,9 +1,16 @@ import { TRPCProvider } from "@hoarder/shared-react/providers/trpc-provider"; import usePluginSettings from "./settings"; +import { ThemeProvider } from "./ThemeProvider"; export function Providers({ children }: { children: React.ReactNode }) { const { settings } = usePluginSettings(); - return <TRPCProvider settings={settings}>{children}</TRPCProvider>; + return ( + <TRPCProvider settings={settings}> + <ThemeProvider defaultTheme="system" storageKey="vite-ui-theme"> + {children} + </ThemeProvider> + </TRPCProvider> + ); } |
