feat: added prefers-color-scheme support

+ read media query for theme preference
+ improved typing of useLocalStorage hook
This commit is contained in:
Sagnik Pradhan
2021-10-19 14:46:18 -04:00
committed by Jean-Philippe Sirois
parent 32c12cba77
commit 6b267780b5
2 changed files with 42 additions and 9 deletions
+35 -6
View File
@@ -1,12 +1,13 @@
import React, { createContext, FC, useMemo } from "react"
import React, { createContext, FC, useEffect, useMemo, useState } from "react"
import { ThemeProvider as BaseThemeProvider } from "styled-components"
import { darkTheme, lightTheme } from "./design/themes"
import { useLocalStorage } from "./hooks/useLocalStorage"
type ThemeType = "dark" | "light"
export interface IThemeContext {
theme: "dark" | "light"
setTheme: () => void
theme: ThemeType
toggleTheme: () => void
}
@@ -17,19 +18,47 @@ interface IScopedDownChildren {
export const ThemeContext = createContext<IThemeContext | null>(null)
const ThemeProvider: FC<IScopedDownChildren> = ({ children }) => {
const [theme, setTheme] = useLocalStorage("theme", "dark")
// User's explicitly set theme
const [localTheme, setLocalTheme] = useLocalStorage<ThemeType | "unset">(
"theme",
"unset"
)
// App's current theme
const [theme, setTheme] = useState<ThemeType>(
localTheme === "unset" ? "dark" : localTheme
)
const themeObject = useMemo(
() => (theme === "dark" ? darkTheme : lightTheme),
[theme]
)
useEffect(() => {
if (localTheme === "unset") {
const prefersLightTheme = window.matchMedia(
"(prefers-color-scheme: light)"
)
setTheme(prefersLightTheme.matches ? "light" : "dark")
prefersLightTheme.onchange = ({ matches }) =>
setTheme(matches ? "light" : "dark")
return () => {
prefersLightTheme.onchange = null
}
}
}, [])
const contextValue = useMemo(
() => ({
theme,
setTheme,
toggleTheme: () => {
setTheme(theme === "light" ? "dark" : "light")
const newTheme = theme === "light" ? "dark" : "light"
setTheme(newTheme)
setLocalTheme(newTheme)
},
}),
[theme, setTheme]
+7 -3
View File
@@ -1,9 +1,13 @@
/* globals window */
import { useEffect, useState } from "react"
export const useLocalStorage = (name: string, initialValue: string) => {
export const useLocalStorage = <Value extends unknown = unknown>(
name: string,
initialValue: Value
) => {
const windowGlobal = typeof window !== "undefined" && window
const [value, setValue] = useState(() => {
const [value, setValue] = useState<Value>(() => {
if (windowGlobal) {
const currentValue = windowGlobal.localStorage.getItem(name)
return currentValue ? JSON.parse(currentValue) : initialValue
@@ -17,5 +21,5 @@ export const useLocalStorage = (name: string, initialValue: string) => {
}
}, [name, value, windowGlobal])
return [value, setValue]
return [value, setValue] as const
}