Skip to content

Instantly share code, notes, and snippets.

@luooooob
Last active February 8, 2023 17:26
Show Gist options
  • Save luooooob/902b4bb4962c9affb1e2cce5084c48ee to your computer and use it in GitHub Desktop.
Save luooooob/902b4bb4962c9affb1e2cce5084c48ee to your computer and use it in GitHub Desktop.

Revisions

  1. luooooob revised this gist Feb 8, 2023. 1 changed file with 100 additions and 0 deletions.
    100 changes: 100 additions & 0 deletions DianDianDianColorScheme.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,100 @@
    /* eslint-disable react-hooks/rules-of-hooks */

    import { createContext, ReactNode, useContext, useEffect, useMemo, useState } from "react"

    import type { Dispatch, FC } from "react"

    const LOCALSTORAGE_KEY = "diandiandian-user-perfers-color-scheme"

    type ColorScheme = string | null

    type ColorSchemeDispatch = Dispatch<ColorScheme>

    const useSysColorScheme = () => {
    if (typeof window === 'undefined') {
    return { sysColorScheme: null }
    }

    const avaliableSysColorScheme = ["dark", "light"] as const

    const mediasMap = {
    ["dark"]: window.matchMedia(`(prefers-color-scheme: dark`),
    ["light"]: window.matchMedia(`(prefers-color-scheme: light`)
    } satisfies Record<typeof avaliableSysColorScheme[number], MediaQueryList>

    const findSysColorScheme = (): ColorScheme => {
    return avaliableSysColorScheme.find(key =>
    mediasMap[key].matches
    ) || null
    }

    const [sysColorScheme, setSysColorScheme] = useState(findSysColorScheme())


    useEffect(() => {
    const onMediaChange = () => {
    const newColorScheme = findSysColorScheme()
    if (sysColorScheme !== newColorScheme) {
    setSysColorScheme(newColorScheme)
    }
    }
    const medias = avaliableSysColorScheme.map(key => mediasMap[key])
    medias.forEach(media => media.addEventListener('change', onMediaChange))
    return () => medias.forEach(media => media.removeEventListener('change', onMediaChange))
    })

    return { sysColorScheme }
    }

    const useUserColorScheme = () => {
    if (typeof window === 'undefined') {
    return {
    userColorScheme: null,
    setUserColorSchemeAndSave: () => { },
    }
    }
    const [userColorScheme, setUserColorScheme] = useState<ColorScheme>(
    window.localStorage.getItem(LOCALSTORAGE_KEY)
    )

    const setUserColorSchemeAndSave: ColorSchemeDispatch = (colorScheme: ColorScheme) => {
    setUserColorScheme(colorScheme)
    if(colorScheme) {
    window.localStorage.setItem(LOCALSTORAGE_KEY, colorScheme)
    }
    window.localStorage.removeItem(LOCALSTORAGE_KEY)
    }

    return { userColorScheme, setUserColorSchemeAndSave }
    }

    const ColorSchemeContext = createContext<ColorScheme>(null)
    const ColorSchemeDispatchContext = createContext<ColorSchemeDispatch | null>(null)
    type ColorSchemeProps = {
    children: ReactNode
    }

    export const ColorScheme:FC<ColorSchemeProps> = ({ children }) => {
    const { sysColorScheme } = useSysColorScheme()
    const { userColorScheme, setUserColorSchemeAndSave } = useUserColorScheme()

    const colorScheme = userColorScheme ?? sysColorScheme

    return (
    <ColorSchemeContext.Provider value={colorScheme}>
    <ColorSchemeDispatchContext.Provider value={setUserColorSchemeAndSave}>
    {children}
    </ColorSchemeDispatchContext.Provider>
    </ColorSchemeContext.Provider>
    )
    }

    export const useColorScheme = () => {
    const colorScheme = useContext(ColorSchemeContext)
    const setColorScheme = useContext(ColorSchemeDispatchContext)
    if (!setColorScheme) {
    throw new Error("")
    }
    return { colorScheme, setColorScheme }
    }

  2. luooooob created this gist Aug 15, 2021.
    25 changes: 25 additions & 0 deletions colorSchemeTools.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,25 @@


    const systemColorSchemeRange = ['light', 'dark'] as const

    const getMediaByColorScheme = (colorScheme: string): MediaQueryList => {
    return matchMedia(`(prefers-color-scheme: ${colorScheme})`)
    }

    export const getSystemColorScheme = (): (typeof systemColorSchemeRange)[number] | null => {
    return systemColorSchemeRange.find(colorScheme => getMediaByColorScheme(colorScheme).matches) || null
    }

    export const getSystemColorSchemeMedias = (): MediaQueryList[] => {
    return systemColorSchemeRange.map(colorScheme => getMediaByColorScheme(colorScheme))
    }

    export const getColorSchemeFromLocalStorage = (localStorageKey: string): string | null => {
    return localStorage.getItem(localStorageKey)
    }

    export const setColorSchemeToLocalStorage = (localStorageKey: string, colorScheme: string): void => {
    colorScheme
    ? localStorage.setItem(localStorageKey, colorScheme)
    : localStorage.removeItem(localStorageKey)
    }
    39 changes: 39 additions & 0 deletions hooks.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,39 @@
    import { useEffect, useState } from "react"
    import {
    getSystemColorScheme,
    getSystemColorSchemeMedias,
    getColorSchemeFromLocalStorage,
    setColorSchemeToLocalStorage
    } from "../tools/colorScheme"

    export const useSystemPrefersColorScheme = () => {
    const initialColorScheme = getSystemColorScheme()
    const [systemPrefersColorScheme, setSystemPrefersColorScheme] = useState(initialColorScheme)
    const onMediaChange = () => {
    const newColorScheme = getSystemColorScheme()
    if (systemPrefersColorScheme !== newColorScheme) {
    setSystemPrefersColorScheme(newColorScheme)
    }
    }
    useEffect(() => {
    const medias = getSystemColorSchemeMedias()
    medias.forEach(media => media.addEventListener('change', onMediaChange))
    return () => medias.forEach(media => media.removeEventListener('change', onMediaChange))
    })

    return [systemPrefersColorScheme]
    }

    export const useUserPrefersColorScheme = (localStorageKey?: string) => {
    const initialColorScheme = localStorageKey
    ? getColorSchemeFromLocalStorage(localStorageKey)
    : null
    const [userPrefersColorScheme, _setUserPrefersColorScheme] = useState(initialColorScheme)

    const setUserPrefersColorScheme = (coloeScheme: string) => {
    _setUserPrefersColorScheme(coloeScheme)
    localStorageKey && setColorSchemeToLocalStorage(localStorageKey, coloeScheme)
    }
    return [userPrefersColorScheme, setUserPrefersColorScheme]
    }

    13 changes: 13 additions & 0 deletions mountColorScheme.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,13 @@
    import { getSystemColorScheme, getColorSchemeFromLocalStorage } from "./tools/colorScheme"

    const backgroundColorScheme: string =
    getSystemColorScheme()
    || getColorSchemeFromLocalStorage('user-prefers-background-color-scheme')
    || 'light'

    const themeColorScheme: string =
    getColorSchemeFromLocalStorage('user-prefers-theme-color-scheme')
    || 'jiaran'

    document.documentElement.setAttribute('data-background-color-scheme', backgroundColorScheme)
    document.documentElement.setAttribute('data-theme-color-scheme', themeColorScheme)