Skip to content

Instantly share code, notes, and snippets.

@k-kinzal
Last active November 5, 2022 15:36
Show Gist options
  • Save k-kinzal/7e0f169f9cc1bea3579b8157ed4ab8d6 to your computer and use it in GitHub Desktop.
Save k-kinzal/7e0f169f9cc1bea3579b8157ed4ab8d6 to your computer and use it in GitHub Desktop.
test color scheme provider
import {
createContext, Dispatch,
PropsWithChildren,
ReactElement,
useContext, useEffect, useReducer, useState
} from "react";
import {useLocalStorageReducer} from "../hooks/localStorage";
export type ColorScheme = "system" | "light" | "dark";
export type Action =
| { type: "changeToSystem", payload: { colorScheme: "system" } }
| { type: "changeToLight", payload: { colorScheme: "light" } }
| { type: "changeToDark", payload: { colorScheme: "dark" } };
const STORAGE_KEY = "colorScheme";
const initializeState: ColorScheme = "system";
function reducer(state: ColorScheme, action: Action): ColorScheme {
switch (action.type) {
case "changeToSystem":
case "changeToLight":
case "changeToDark":
return action.payload.colorScheme;
}
}
const ColorSchemeContext = createContext<ColorScheme | null>(null);
const ColorSchemeDispatchContext = createContext<Dispatch<Action> | null>(null);
export function useColorScheme(): ColorScheme {
let colorScheme = useContext(ColorSchemeContext);
if (!colorScheme) {
throw new Error('ColorSchemeProvider is not set');
}
return colorScheme;
}
const darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)');
export function useDarkMode(): boolean {
let [systemColorScheme, setSystemColorScheme] = useState(darkModeQuery.matches ? "dark" : "light");
let colorScheme = useContext(ColorSchemeContext);
useEffect(() => {
let listener = (event: MediaQueryListEvent) => {
if (colorScheme === "system") {
setSystemColorScheme(event.matches ? "dark" : "light");
}
};
darkModeQuery.addEventListener('change', listener);
return () => {
darkModeQuery.removeEventListener('change', listener);
}
}, [])
switch (colorScheme) {
case null:
return false;
case "system":
return systemColorScheme === "dark";
case "light":
return false;
case "dark":
return false;
}
}
export function useColorSchemeDispatch(): Dispatch<Action> {
let dispatch = useContext(ColorSchemeDispatchContext);
if (!dispatch) {
throw new Error("ColorSchemeProvider is not set");
}
return dispatch;
}
export function actionChangeColorSchemeToSystem(): Action {
return {
type: "changeToSystem",
payload: {
colorScheme: "system"
}};
}
export function actionChangeColorSchemeToLight(): Action {
return {
type: "changeToLight",
payload: {
colorScheme: "light"
}};
}
export function actionChangeColorSchemeToDark(): Action {
return {
type: "changeToDark",
payload: {
colorScheme: "dark"
}};
}
export function ColorSchemeProvider({children}: PropsWithChildren): ReactElement {
let [state, dispatch] = useLocalStorageReducer(STORAGE_KEY, reducer, initializeState);
return (
<ColorSchemeContext.Provider value={state}>
<ColorSchemeDispatchContext.Provider value={dispatch}>
{children}
</ColorSchemeDispatchContext.Provider>
</ColorSchemeContext.Provider>
);
}
import {
Dispatch,
useEffect, useReducer,
} from "react";
export function useLocalStorageReducer<S, I, A>(
key: string,
reducer: ((state: S, action: A) => S),
initialArg: I & S,
): [S, Dispatch<A>] {
let [state, dispatch] = useReducer(reducer, initialArg, (arg) => {
if (typeof window === "undefined") {
return arg;
}
try {
let item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : arg;
} catch (e) {
console.error(e);
return arg;
}
});
let customDispatch: Dispatch<A> = (action: A) => {
try {
if (typeof window === "undefined") {
dispatch(action);
} else {
let item = action instanceof Function ? action(state) : action;
window.localStorage.setItem(key, JSON.stringify(item));
}
} catch (e) {
console.error(e);
}
};
useEffect(() => {
if (typeof window === "undefined") {
return;
}
let listener = (event: StorageEvent) => {
if (event.storageArea !== localStorage) {
return;
}
if (event.key !== key) {
return;
}
try {
let item = window.localStorage.getItem(key);
if (item === null) {
return;
}
let value = JSON.parse(item);
dispatch(value);
} catch (e) {
console.error(e);
}
};
window.addEventListener('storage', listener, false);
return () => {
window.removeEventListener("storage", listener);
};
}, [key]);
return [state, customDispatch];
}
import {ThemeProvider as StyledComponentsThemeProvider} from "styled-components";
import {PropsWithChildren, ReactElement} from "react";
import {useDarkMode} from "./colorScheme";
const Theme = {
light: {
color: "black"
},
dark: {
color: "white"
}
}
export function ThemeProvider({children}: PropsWithChildren): ReactElement {
let isDarkMode = useDarkMode();
return (
<StyledComponentsThemeProvider theme={Theme[isDarkMode ? "dark" : "light"]}>
{children}
</StyledComponentsThemeProvider>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment