forked from github/docs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathuseTheme.ts
129 lines (113 loc) · 3.89 KB
/
useTheme.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import { useState, useEffect } from 'react'
import Cookies from '../lib/cookies'
enum CssColorMode {
auto = 'auto',
light = 'light',
dark = 'dark',
}
enum ComponentColorMode {
auto = 'auto',
day = 'day',
night = 'night',
}
enum SupportedTheme {
light = 'light',
dark = 'dark',
dark_dimmed = 'dark_dimmed',
dark_high_contrast = 'dark_high_contrast',
}
type CssColorTheme = {
colorMode: CssColorMode
lightTheme: SupportedTheme
darkTheme: SupportedTheme
}
type ComponentColorTheme = {
colorMode: ComponentColorMode
dayScheme: SupportedTheme
nightScheme: SupportedTheme
}
type ColorModeThemes = {
css: CssColorTheme
component: ComponentColorTheme
}
export const defaultCSSTheme: CssColorTheme = {
colorMode: CssColorMode.auto,
lightTheme: SupportedTheme.light,
darkTheme: SupportedTheme.dark,
}
export const defaultComponentTheme: ComponentColorTheme = {
colorMode: ComponentColorMode.auto,
dayScheme: SupportedTheme.light,
nightScheme: SupportedTheme.dark,
}
const cssColorModeToComponentColorMode: Record<CssColorMode, ComponentColorMode> = {
[CssColorMode.auto]: ComponentColorMode.auto,
[CssColorMode.light]: ComponentColorMode.day,
[CssColorMode.dark]: ComponentColorMode.night,
}
function filterMode(mode = ''): CssColorMode | undefined {
if (Object.values<string>(CssColorMode).includes(mode)) {
return mode as CssColorMode
}
}
function filterTheme({ name = '', color_mode = '' } = {}): SupportedTheme | undefined {
if (Object.values<string>(SupportedTheme).includes(name)) {
return name as SupportedTheme
}
if (Object.values<string>(SupportedTheme).includes(color_mode)) {
return color_mode as SupportedTheme
}
}
export function getCssTheme(cookieValue = ''): CssColorTheme {
if (!cookieValue) return defaultCSSTheme
try {
const parsed = JSON.parse(cookieValue)
const { color_mode, light_theme, dark_theme } = parsed
return {
colorMode: filterMode(color_mode) || defaultCSSTheme.colorMode,
lightTheme: filterTheme(light_theme) || defaultCSSTheme.lightTheme,
darkTheme: filterTheme(dark_theme) || defaultCSSTheme.darkTheme,
}
} catch (err) {
if (process.env.NODE_ENV === 'development')
console.warn("Unable to parse 'color_mode' cookie", err)
return defaultCSSTheme
}
}
export function getComponentTheme(cookieValue = ''): ComponentColorTheme {
const { colorMode, lightTheme, darkTheme } = getCssTheme(cookieValue)
return {
// The cookie value is a primer/css color_mode.
// We need to convert that to a primer/react compatible version.
colorMode: cssColorModeToComponentColorMode[colorMode],
dayScheme: lightTheme,
nightScheme: darkTheme,
}
}
export function useTheme() {
const [theme, setTheme] = useState<ColorModeThemes>({
css: defaultCSSTheme,
component: defaultComponentTheme,
})
useEffect(() => {
// Using setTimeout with a default delay value of 0 interjects one
// additional event cycle, which works around a bug that is the
// result of a timing issue. Without the setTimeout function
// the page loads, then the docs site switches the color mode to
// match the user's GitHub color mode. Primer React has a useEffect
// call that overrides this change, causing the site to ignore the
// user's GitHub color mode and revert to auto.
// As a temporary workaround, this code that fetches the user's GitHub
// color mode will be called after Primer React's useEffect call.
// The long term solution to this theming issue is to migrate to CSS variables
// under the hood, which Primer is planning to do in the next couple quarters.
// Reference: https://github.com/primer/react/issues/2229
setTimeout(() => {
const cookieValue = Cookies.get('color_mode')
const css = getCssTheme(cookieValue)
const component = getComponentTheme(cookieValue)
setTheme({ css, component })
})
}, [])
return { theme }
}