mirror of
https://github.com/Stone-Red-Code/website.git
synced 2026-09-07 23:43:57 +02:00
refact: rework theme code
fix: lint errors fix: lint error fix: lint error
This commit is contained in:
committed by
Jean-Philippe Sirois
parent
975ccaa7b6
commit
9b357a98fb
+6
-30
@@ -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
@@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user