frontend client for gemstone. decentralised workplace app
1import { useFacet } from "@/lib/facet"; 2import type { Facet } from "@/lib/facet/src/facet"; 3import type { FacetPalette } from "@/lib/facet/src/palette"; 4import type { Enumify } from "@/lib/utils/types"; 5import type { Dispatch, ReactNode, SetStateAction } from "react"; 6import { createContext, useContext, useState } from "react"; 7 8export const ColorMode = { 9 DARK: "dark", 10 LIGHT: "light", 11} as const; 12 13// eslint-disable-next-line @typescript-eslint/no-redeclare -- intentional. lets us have enum-like objects without some of the weirdness 14export type ColorMode = Enumify<typeof ColorMode>; 15 16interface ThemeContextValue { 17 colorMode: ColorMode; 18 setColorMode: Dispatch<SetStateAction<ColorMode>>; 19 currentPalette: FacetPalette; 20} 21 22const ThemeContext = createContext<ThemeContextValue | null>(null); 23 24const useThemeProvider = () => { 25 const value = useContext(ThemeContext); 26 if (!value) 27 throw new Error( 28 "Theme provider failed to initialise. Did you access this out of tree somehow? Tried to access theme values before it was initialised.", 29 ); 30 return value; 31}; 32 33export const useColorMode = () => { 34 const { colorMode } = useThemeProvider(); 35 return colorMode; 36}; 37 38export const useSetColorMode = () => { 39 const { setColorMode } = useThemeProvider(); 40 return setColorMode; 41}; 42 43export const useCurrentPalette = () => { 44 const { currentPalette } = useThemeProvider(); 45 return currentPalette; 46}; 47 48export const ThemeProvider = ({ children }: { children: ReactNode }) => { 49 const [colorMode, setColorMode] = useState<ColorMode>(ColorMode.DARK); 50 const facet = useFacet(); 51 const currentPalette = 52 colorMode === "dark" 53 ? facet.variants.obsidian 54 : // TODO: temporary. will remove the null coalesce when pearl is defined. 55 (facet.variants.pearl ?? facet.variants.obsidian); 56 57 // TODO: remove nullability with obsidian and pearl palettes. 58 // instead, these are provided as defaults and if we add more themes 59 // in the future, they can just be tacked on. 60 if (!currentPalette) throw new Error("Don't use light mode for now."); 61 62 const value: ThemeContextValue = { 63 colorMode, 64 setColorMode, 65 currentPalette, 66 }; 67 return <ThemeContext value={value}>{children}</ThemeContext>; 68};