refact: rework theme code

fix: lint errors

fix: lint error

fix: lint error
This commit is contained in:
Sagnik Pradhan
2021-10-19 14:46:18 -04:00
committed by Jean-Philippe Sirois
parent 975ccaa7b6
commit 9b357a98fb
4 changed files with 81 additions and 64 deletions
+6 -30
View File
@@ -1,31 +1,7 @@
import React from "react" /**
* Implement Gatsby's SSR (Server Side Rendering) APIs in this file.
*
* See: https://www.gatsbyjs.org/docs/ssr-apis/
*/
const ThemeScript = () => { // You can delete this file if you're not using it
const themeScript = `
(function() {
function getInitialTheme() {
const userSetTheme = window.localStorage.getItem("theme")
const prefersLightTheme = window.matchMedia(
"(prefers-color-scheme: light)"
)
if (userSetTheme && userSetTheme !== '"unset"') return userSetTheme
else if (prefersLightTheme.matches === true) return "light"
else return "dark"
}
if (typeof document === "undefined") return;
document.documentElement.style.setProperty(
"--initial-theme",
getInitialTheme()
)
})()
`
return <script dangerouslySetInnerHTML={{ __html: themeScript }} />
}
export const onRenderBody = ({ setPreBodyComponents }) => {
setPreBodyComponents(<ThemeScript></ThemeScript>)
}
+29 -34
View File
@@ -1,8 +1,10 @@
import React, { createContext, FC, useEffect, useMemo, useState } from "react" import React, { createContext, FC, useMemo } from "react"
import { ThemeProvider as BaseThemeProvider } from "styled-components" import { ThemeProvider as BaseThemeProvider } from "styled-components"
import { darkTheme, lightTheme } from "./design/themes" import { darkTheme, lightTheme } from "./design/themes"
import { useIsBrowser } from "./hooks/useIsBrowser"
import { useLocalStorage } from "./hooks/useLocalStorage" import { useLocalStorage } from "./hooks/useLocalStorage"
import { useMedia } from "./hooks/useMedia"
type ThemeType = "dark" | "light" type ThemeType = "dark" | "light"
@@ -17,20 +19,20 @@ interface IScopedDownChildren {
export const ThemeContext = createContext<IThemeContext | null>(null) export const ThemeContext = createContext<IThemeContext | null>(null)
const ThemeProvider: FC<IScopedDownChildren> = ({ children }) => { function useTheme() {
// User's explicitly set theme const preferredTheme = useMedia<ThemeType>(
const [localTheme, setLocalTheme] = useLocalStorage<ThemeType | "unset">( ["(prefers-color-scheme: light)", "(prefers-color-scheme: dark)"],
"theme", ["light", "dark"],
"unset" "dark"
) )
// App's current theme const [userSelectedTheme, setUserSelectedTheme] = useLocalStorage<
const [theme, setTheme] = useState<ThemeType>( ThemeType | "unset"
typeof document !== "undefined" >("user-selected-theme", "unset")
? (document.documentElement.style.getPropertyValue(
"--initial-theme" const theme = useMemo(
) as ThemeType) () => (userSelectedTheme !== "unset" ? userSelectedTheme : preferredTheme),
: "dark" [preferredTheme, userSelectedTheme]
) )
const themeObject = useMemo( const themeObject = useMemo(
@@ -38,37 +40,30 @@ const ThemeProvider: FC<IScopedDownChildren> = ({ children }) => {
[theme] [theme]
) )
useEffect(() => { return {
if (localTheme === "unset") { theme,
const prefersLightTheme = window.matchMedia( themeObject,
"(prefers-color-scheme: light)" setTheme: setUserSelectedTheme,
) }
}
prefersLightTheme.onchange = ({ matches }) => const ThemeProvider: FC<IScopedDownChildren> = ({ children }) => {
setTheme(matches ? "light" : "dark") const isBrowser = useIsBrowser()
const { theme, themeObject, setTheme } = useTheme()
return () => {
prefersLightTheme.onchange = null
}
}
}, [localTheme])
const contextValue = useMemo( const contextValue = useMemo(
() => ({ () => ({
theme, theme,
toggleTheme: () => setTheme(theme === "light" ? "dark" : "light"),
toggleTheme: () => {
const newTheme = theme === "light" ? "dark" : "light"
setTheme(newTheme)
setLocalTheme(newTheme)
},
}), }),
[theme, setTheme, setLocalTheme] [theme, setTheme]
) )
return ( return (
<ThemeContext.Provider value={contextValue}> <ThemeContext.Provider value={contextValue}>
<BaseThemeProvider theme={themeObject}>{children}</BaseThemeProvider> <BaseThemeProvider theme={themeObject}>
{isBrowser ? children : undefined}
</BaseThemeProvider>
</ThemeContext.Provider> </ThemeContext.Provider>
) )
} }
+11
View File
@@ -0,0 +1,11 @@
import { useEffect, useState } from "react"
export function useIsBrowser() {
const isBrowserCheck = () => typeof window !== "undefined"
const [isBrowser, setIsBrowser] = useState(isBrowserCheck)
useEffect(() => setIsBrowser(isBrowserCheck), [])
return isBrowser
}
+35
View File
@@ -0,0 +1,35 @@
import { useEffect, useState } from "react"
export function useMedia<Value extends unknown>(
queries: string[],
values: Value[],
defaultValue: Value
) {
const [currentValue, setCurrentValue] = useState(defaultValue)
useEffect(() => {
const windowGlobal = typeof window !== "undefined" && window
if (!windowGlobal) return
const mediaQueries = queries.map((query) => windowGlobal.matchMedia(query))
const getCurrentValue = () => {
const index = mediaQueries.findIndex((value) => value.matches)
return values[index] || defaultValue
}
setCurrentValue(getCurrentValue)
const handler = () => setCurrentValue(getCurrentValue)
mediaQueries.forEach((query) => query.addEventListener("change", handler))
return () =>
mediaQueries.forEach((query) =>
query.removeEventListener("change", handler)
)
}, [queries, values, defaultValue])
return currentValue
}