Skip to content
This repository was archived by the owner on Sep 20, 2024. It is now read-only.

Commit 6d47759

Browse files
committed
fix: color mode utilities and semantic tokens
1 parent 57bef48 commit 6d47759

File tree

16 files changed

+502
-243
lines changed

16 files changed

+502
-243
lines changed

packages/c-accordion/tests/c-accordion.cy.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ import { CAccordion } from "../src"
22
import { h } from "vue"
33

44
it("should render properly", () => {
5-
cy.mount(<CAccordion></CAccordion>)
5+
cy.mount(<CAccordion />)
66
})
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
<template>
22
<c-alert> Base Alert </c-alert>
33
</template>
4+
5+
<script setup lang="ts">
6+
import { DarkMode } from "@chakra-ui/vue-next"
7+
</script>

packages/c-color-mode/src/color-mode-provider.ts

Lines changed: 0 additions & 60 deletions
This file was deleted.
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
import { __DEV__ } from "@chakra-ui/utils"
2+
import {
3+
computed,
4+
defineComponent,
5+
inject,
6+
h,
7+
Fragment,
8+
provide,
9+
readonly,
10+
Ref,
11+
ref,
12+
watch,
13+
watchEffect,
14+
getCurrentInstance,
15+
} from "vue"
16+
import { ColorMode, ColorModeRef, getColorModeUtils } from "./color-mode.utils"
17+
import { StorageManager } from "./storage-manager"
18+
import { createContext } from "@chakra-ui/vue-utils"
19+
import { mountColorModeScript } from './color-mode-script'
20+
21+
export type { ColorModeRef }
22+
23+
export interface ColorModeOptions {
24+
initialColorMode?: ColorModeRef
25+
useSystemColorMode?: boolean
26+
}
27+
28+
export type ColorModeContext = {
29+
colorMode: ColorModeRef
30+
toggleColorMode: () => void
31+
}
32+
33+
export interface InternalColorModeContext {
34+
colorModeManager: StorageManager
35+
toggleColorMode(): void
36+
colorMode: Ref<Exclude<ColorMode, "system">>
37+
useSystemColorMode?: boolean
38+
disableTransitionOnChange?: boolean
39+
initialColorMode?: ColorMode
40+
}
41+
42+
function getTheme(manager: StorageManager, fallback?: ColorMode) {
43+
return manager.type === "cookie" && manager.ssr
44+
? manager.get(fallback)
45+
: fallback
46+
}
47+
48+
export interface IColorModeContext {
49+
colorMode: ColorModeRef
50+
toggleColorMode: () => void
51+
forced?: boolean
52+
}
53+
54+
const [ColorModeProvider, useColorModeContext] =
55+
createContext<IColorModeContext>({
56+
name: "ColorModeContext",
57+
})
58+
59+
interface SetupColorModeContext {
60+
_colorMode: ColorModeRef
61+
colorModeManager: StorageManager
62+
useSystemColorMode?: boolean
63+
disableTransitionOnChange?: boolean
64+
initialColorMode?: ColorMode
65+
}
66+
67+
export const AppColorModeContextSymbol = Symbol("AppColorModeContextSymbol")
68+
69+
export function setupColorModeContext(
70+
app: any,
71+
{
72+
_colorMode,
73+
colorModeManager,
74+
useSystemColorMode,
75+
disableTransitionOnChange,
76+
initialColorMode,
77+
}: SetupColorModeContext
78+
) {
79+
80+
mountColorModeScript({
81+
initialColorMode: _colorMode.value,
82+
type: colorModeManager.type,
83+
})
84+
85+
const colorMode = computed({
86+
get: () =>
87+
(getTheme(colorModeManager, _colorMode.value) as Exclude<
88+
ColorMode,
89+
"system"
90+
>) || (_colorMode.value as Exclude<ColorMode, "system">),
91+
set: (value: Exclude<ColorMode, "system">) => {
92+
_colorMode.value = value
93+
},
94+
})
95+
96+
const utils = computed(() =>
97+
getColorModeUtils({ preventTransition: disableTransitionOnChange })
98+
)
99+
100+
const managerValue = colorModeManager.get()
101+
102+
if (managerValue) {
103+
setColorMode(managerValue)
104+
} else if (initialColorMode === "system") {
105+
setColorMode("system")
106+
} else {
107+
setColorMode(colorMode.value)
108+
}
109+
110+
const resolvedColorMode = computed(() => getTheme(colorModeManager))
111+
112+
113+
function setColorMode(value: ColorMode | "system") {
114+
const { setClassName, setDataset, getSystemTheme } = utils.value
115+
const resolved = value === "system" ? getSystemTheme() : value
116+
colorMode.value = resolved
117+
118+
setClassName(resolved === "dark")
119+
setDataset(resolved)
120+
121+
colorModeManager.set(resolved)
122+
}
123+
124+
const toggleColorMode = () => {
125+
if (colorMode.value === "light") {
126+
setColorMode("dark")
127+
} else {
128+
setColorMode("light")
129+
}
130+
}
131+
132+
watch(
133+
() => useSystemColorMode,
134+
(nextUseSystemColorMode) => {
135+
if (typeof document !== "undefined") {
136+
if (!nextUseSystemColorMode) return
137+
return utils.value.addListener(setColorMode)
138+
}
139+
},
140+
{
141+
immediate: true,
142+
}
143+
)
144+
145+
app.provide(AppColorModeContextSymbol, {
146+
colorMode: computed(() => colorMode.value),
147+
toggleColorMode,
148+
})
149+
}
150+
151+
/** Injects color mode into component instance */
152+
export const useColorMode = () => {
153+
const context = inject<IColorModeContext>(AppColorModeContextSymbol, {
154+
colorMode: computed(() => "light" as Exclude<ColorMode, "system">),
155+
toggleColorMode: () => { },
156+
})
157+
return context
158+
}
159+
160+
/**
161+
* Change value based on color mode.
162+
*
163+
* @param lightValue the light mode value
164+
* @param darkValue the dark mode value
165+
*
166+
* @example
167+
*
168+
* ```js
169+
* const Icon = useColorModeValue(MoonIcon, SunIcon)
170+
* ```
171+
*/
172+
export function useColorModeValue<TLight = unknown, TDark = unknown>(
173+
lightValue: TLight,
174+
darkValue: TDark
175+
) {
176+
const { colorMode } = useColorMode()
177+
const modeValue = ref()
178+
179+
watchEffect(() => {
180+
modeValue.value = colorMode.value === "dark" ? darkValue : lightValue
181+
})
182+
183+
return modeValue
184+
}
185+
186+
187+
export const DarkMode = defineComponent((_, { slots, attrs }) => {
188+
provide(AppColorModeContextSymbol, {
189+
colorMode: computed(() => "dark" as Exclude<ColorMode, "system">),
190+
toggleColorMode: () => { },
191+
forced: true
192+
})
193+
194+
return () => (
195+
<>
196+
{slots.default?.()}
197+
</>
198+
)
199+
})
200+
201+
export const LightMode = defineComponent((_, { slots, attrs }) => {
202+
provide(AppColorModeContextSymbol, {
203+
colorMode: computed(() => "light" as Exclude<ColorMode, "system">),
204+
toggleColorMode: () => { },
205+
forced: true
206+
})
207+
208+
return () => (
209+
<>
210+
{slots.default?.()}
211+
</>
212+
)
213+
})
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
export type ColorModeScriptProps = {
2+
type?: "localStorage" | "cookie"
3+
initialColorMode?: "light" | "dark" | "system"
4+
storageKey?: string
5+
nonce?: string
6+
}
7+
8+
const VALID_VALUES = new Set(["dark", "light", "system"])
9+
10+
/**
11+
* runtime safe-guard against invalid color mode values
12+
*/
13+
function normalize(initialColorMode: "light" | "dark" | "system") {
14+
let value = initialColorMode
15+
if (!VALID_VALUES.has(value)) value = "light"
16+
return value
17+
}
18+
19+
export function getScriptSrc(props: ColorModeScriptProps = {}) {
20+
const {
21+
initialColorMode = "light",
22+
type = "localStorage",
23+
storageKey: key = "chakra-ui-color-mode",
24+
} = props
25+
26+
// runtime safe-guard against invalid color mode values
27+
const init = normalize(initialColorMode)
28+
29+
const isCookie = type === "cookie"
30+
31+
const cookieScript = `(function(){try{var a=function(o){var l="(prefers-color-scheme: dark)",v=window.matchMedia(l).matches?"dark":"light",e=o==="system"?v:o,d=document.documentElement,m=document.body,i="chakra-ui-light",n="chakra-ui-dark",s=e==="dark";return m.classList.add(s?n:i),m.classList.remove(s?i:n),d.style.colorScheme=e,d.dataset.theme=e,e},u=a,h="${init}",r="${key}",t=document.cookie.match(new RegExp("(^| )".concat(r,"=([^;]+)"))),c=t?t[2]:null;c?a(c):document.cookie="".concat(r,"=").concat(a(h),"; max-age=31536000; path=/")}catch(a){}})();
32+
`
33+
34+
const localStorageScript = `(function(){try{var a=function(c){var v="(prefers-color-scheme: dark)",h=window.matchMedia(v).matches?"dark":"light",r=c==="system"?h:c,o=document.documentElement,s=document.body,l="chakra-ui-light",d="chakra-ui-dark",i=r==="dark";return s.classList.add(i?d:l),s.classList.remove(i?l:d),o.style.colorScheme=r,o.dataset.theme=r,r},n=a,m="${init}",e="${key}",t=localStorage.getItem(e);t?a(t):localStorage.setItem(e,a(m))}catch(a){}})();
35+
`
36+
37+
const fn = isCookie ? cookieScript : localStorageScript
38+
return `!${fn}`.trim()
39+
}
40+
41+
export function mountColorModeScript(props: ColorModeScriptProps = {}) {
42+
const { nonce } = props
43+
44+
const script = document.createElement("script")
45+
script.id = "chakra-script"
46+
script.nonce = nonce
47+
script.innerHTML = getScriptSrc(props)
48+
document.head.appendChild(script)
49+
}

0 commit comments

Comments
 (0)