Skip to content

Commit fee84b8

Browse files
committed
feat(useColorModes): add the new hook to handling color modes
1 parent 7ab2edc commit fee84b8

File tree

6 files changed

+70
-51
lines changed

6 files changed

+70
-51
lines changed

Diff for: packages/coreui-react/src/hooks/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useColorModes } from './useColorModes'
12
import { useForkedRef } from './useForkedRef'
23

3-
export { useForkedRef }
4+
export { useColorModes, useForkedRef }

Diff for: packages/coreui-react/src/hooks/useColorModes.ts

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { useEffect, useState } from 'react'
2+
3+
interface UseColorModesOutput {
4+
getColorMode: () => string
5+
setColorMode: (mode: string) => void
6+
}
7+
8+
const getStoredTheme = (localStorageItemName: string) => localStorage.getItem(localStorageItemName)
9+
const setStoredTheme = (localStorageItemName: string, colorMode: string) =>
10+
localStorage.setItem(localStorageItemName, colorMode)
11+
12+
const getPreferredColorScheme = (localStorageItemName: string) => {
13+
const storedTheme = getStoredTheme(localStorageItemName)
14+
15+
if (storedTheme) {
16+
return storedTheme
17+
}
18+
19+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
20+
}
21+
22+
const setTheme = (colorMode: string) => {
23+
document.documentElement.dataset.coreuiTheme =
24+
colorMode === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches
25+
? 'dark'
26+
: colorMode
27+
28+
const event = new Event('ColorSchemeChange')
29+
document.documentElement.dispatchEvent(event)
30+
}
31+
32+
export const useColorModes = (
33+
localStorageItemName = 'coreui-react-color-scheme',
34+
): UseColorModesOutput => {
35+
const [colorMode, setColorMode] = useState<string>(getPreferredColorScheme(localStorageItemName))
36+
37+
useEffect(() => {
38+
setStoredTheme(localStorageItemName, colorMode)
39+
setTheme(colorMode)
40+
}, [colorMode])
41+
42+
useEffect(() => {
43+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
44+
const storedTheme = getStoredTheme(localStorageItemName)
45+
if (storedTheme !== 'light' && storedTheme !== 'dark') {
46+
setTheme(colorMode)
47+
}
48+
})
49+
}, [])
50+
51+
return {
52+
getColorMode: () => colorMode,
53+
setColorMode: (mode: string) => setColorMode(mode),
54+
}
55+
}

Diff for: packages/coreui-react/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from './components/'
2+
export * from './hooks/'

Diff for: packages/docs/src/AppContext.tsx

-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ export interface AppContextProps {
44
name?: string
55
sidebarVisible?: boolean | undefined
66
setSidebarVisible?: React.Dispatch<React.SetStateAction<boolean | undefined>>
7-
storedTheme?: string | undefined
8-
setStoredTheme?: React.Dispatch<React.SetStateAction<string | undefined>>
97
}
108

119
export const AppContextInitialState: AppContextProps = {

Diff for: packages/docs/src/components/Header.tsx

+12-9
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,13 @@ import {
2323
CHeaderNav,
2424
CHeaderToggler,
2525
CNavItem,
26+
useColorModes,
2627
} from '@coreui/react/src'
2728
import { AppContext } from './../AppContext'
2829

2930
const Header: FC = () => {
31+
const { getColorMode, setColorMode } = useColorModes('coreui-react-docs-theme')
32+
const colorMode = getColorMode()
3033
return (
3134
<>
3235
<AppContext.Consumer>
@@ -63,39 +66,39 @@ const Header: FC = () => {
6366
</li>
6467
<CDropdown variant="nav-item" placement="bottom-end">
6568
<CDropdownToggle caret={false}>
66-
{context.storedTheme === 'dark' ? (
69+
{colorMode === 'dark' ? (
6770
<CIcon icon={cilMoon} size="xl" />
68-
) : (context.storedTheme === 'auto' ? (
71+
) : colorMode === 'auto' ? (
6972
<CIcon icon={cilContrast} size="xl" />
7073
) : (
7174
<CIcon icon={cilSun} size="xl" />
72-
))}
75+
)}
7376
</CDropdownToggle>
7477
<CDropdownMenu>
7578
<CDropdownItem
76-
active={context.storedTheme === 'light'}
79+
active={colorMode === 'light'}
7780
className="d-flex align-items-center"
7881
component="button"
7982
type="button"
80-
onClick={() => context.setStoredTheme && context.setStoredTheme('light')}
83+
onClick={() => setColorMode('light')}
8184
>
8285
<CIcon className="me-2" icon={cilSun} size="lg" /> Light
8386
</CDropdownItem>
8487
<CDropdownItem
85-
active={context.storedTheme === 'dark'}
88+
active={colorMode === 'dark'}
8689
className="d-flex align-items-center"
8790
component="button"
8891
type="button"
89-
onClick={() => context.setStoredTheme && context.setStoredTheme('dark')}
92+
onClick={() => setColorMode('dark')}
9093
>
9194
<CIcon className="me-2" icon={cilMoon} size="lg" /> Dark
9295
</CDropdownItem>
9396
<CDropdownItem
94-
active={context.storedTheme === 'auto'}
97+
active={colorMode === 'auto'}
9598
className="d-flex align-items-center"
9699
component="button"
97100
type="button"
98-
onClick={() => context.setStoredTheme && context.setStoredTheme('auto')}
101+
onClick={() => setColorMode('auto')}
99102
>
100103
<CIcon className="me-2" icon={cilContrast} size="lg" /> Auto
101104
</CDropdownItem>

Diff for: packages/docs/src/templates/DefaultLayout.tsx

-39
Original file line numberDiff line numberDiff line change
@@ -13,58 +13,19 @@ interface DefaultLayoutProps {
1313
path: any // eslint-disable-line @typescript-eslint/no-explicit-any
1414
}
1515

16-
const getPreferredTheme = (storedTheme: string | undefined) => {
17-
if (storedTheme) {
18-
return storedTheme
19-
}
20-
21-
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
22-
}
23-
24-
const setTheme = (theme: string) => {
25-
document.documentElement.dataset.coreuiTheme =
26-
theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : theme
27-
28-
const event = new Event('ColorSchemeChange')
29-
document.documentElement.dispatchEvent(event)
30-
}
31-
3216
const DefaultLayout: FC<DefaultLayoutProps> = ({ children, data, pageContext, path }) => {
33-
const theme = 'coreui-react-docs-theme'
3417
const [sidebarVisible, setSidebarVisible] = useState()
35-
const [storedTheme, setStoredTheme] = useState()
36-
37-
useEffect(() => {
38-
if (typeof localStorage.getItem(theme) === 'string') {
39-
setStoredTheme(localStorage.getItem(theme))
40-
}
41-
42-
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
43-
if (storedTheme !== 'light' || storedTheme !== 'dark') {
44-
setTheme(getPreferredTheme(storedTheme))
45-
}
46-
})
47-
}, [])
4818

4919
const title = pageContext.frontmatter ? pageContext.frontmatter.title : ''
5020
const description = pageContext.frontmatter ? pageContext.frontmatter.description : ''
5121
const name = pageContext.frontmatter ? pageContext.frontmatter.name : ''
5222
const route = pageContext.frontmatter ? pageContext.frontmatter.route : ''
5323

54-
useEffect(() => {
55-
if (storedTheme) {
56-
localStorage.setItem(theme, storedTheme)
57-
setTheme(storedTheme)
58-
}
59-
}, [storedTheme])
60-
6124
return (
6225
<AppContext.Provider
6326
value={{
6427
sidebarVisible,
6528
setSidebarVisible,
66-
storedTheme,
67-
setStoredTheme,
6829
}}
6930
>
7031
<Seo title={title} description={description} name={name} />

0 commit comments

Comments
 (0)