feat(react): allow theme customization
This commit is contained in:
parent
582b58dc4e
commit
3b8c393be8
|
@ -0,0 +1,59 @@
|
||||||
|
import React, {
|
||||||
|
createContext,
|
||||||
|
useContext,
|
||||||
|
useLayoutEffect,
|
||||||
|
useMemo,
|
||||||
|
} from 'react'
|
||||||
|
|
||||||
|
import { createTheme, darkTheme, theme as defaultTheme } from '../styles/config'
|
||||||
|
|
||||||
|
import type { Theme } from '../styles/config'
|
||||||
|
import type { Config } from '../types/config'
|
||||||
|
|
||||||
|
const ThemeContext = createContext<Theme | undefined>(undefined)
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
theme: Config['theme']
|
||||||
|
children: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ThemeProvider = (props: Props) => {
|
||||||
|
const { children, theme = 'light' } = props
|
||||||
|
|
||||||
|
const appTheme = useMemo(() => {
|
||||||
|
if (!theme || theme === 'light') {
|
||||||
|
return defaultTheme
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theme === 'dark') {
|
||||||
|
return darkTheme
|
||||||
|
}
|
||||||
|
|
||||||
|
return createTheme({
|
||||||
|
colors: theme.colors,
|
||||||
|
fonts: theme.fonts,
|
||||||
|
})
|
||||||
|
}, [theme])
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
document.body.classList.add(appTheme)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.body.classList.remove(appTheme)
|
||||||
|
}
|
||||||
|
}, [appTheme])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeContext.Provider value={appTheme}>{children}</ThemeContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useTheme = () => {
|
||||||
|
const context = useContext(ThemeContext)
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('useTheme must be used within a ThemeProvider')
|
||||||
|
}
|
||||||
|
|
||||||
|
return context
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ import { MessengerProvider } from '~/src/contexts/messengerProvider'
|
||||||
import { ModalProvider } from '~/src/contexts/modalProvider'
|
import { ModalProvider } from '~/src/contexts/modalProvider'
|
||||||
import { NarrowProvider } from '~/src/contexts/narrowProvider'
|
import { NarrowProvider } from '~/src/contexts/narrowProvider'
|
||||||
import { ScrollProvider } from '~/src/contexts/scrollProvider'
|
import { ScrollProvider } from '~/src/contexts/scrollProvider'
|
||||||
|
import { ThemeProvider } from '~/src/contexts/theme-context'
|
||||||
import { ToastProvider } from '~/src/contexts/toastProvider'
|
import { ToastProvider } from '~/src/contexts/toastProvider'
|
||||||
import { styled } from '~/src/styles/config'
|
import { styled } from '~/src/styles/config'
|
||||||
import { GlobalStyle } from '~/src/styles/GlobalStyle'
|
import { GlobalStyle } from '~/src/styles/GlobalStyle'
|
||||||
|
@ -33,11 +34,12 @@ export const Community = (props: Props) => {
|
||||||
return (
|
return (
|
||||||
<Router>
|
<Router>
|
||||||
<AppProvider config={props}>
|
<AppProvider config={props}>
|
||||||
|
<ThemeProvider theme={theme}>
|
||||||
<DialogProvider>
|
<DialogProvider>
|
||||||
<NarrowProvider myRef={ref}>
|
<NarrowProvider myRef={ref}>
|
||||||
<ModalProvider>
|
<ModalProvider>
|
||||||
<ToastProvider>
|
<ToastProvider>
|
||||||
<Wrapper ref={ref} className={theme}>
|
<Wrapper ref={ref}>
|
||||||
<GlobalStyle />
|
<GlobalStyle />
|
||||||
<IdentityProvider>
|
<IdentityProvider>
|
||||||
<MessengerProvider
|
<MessengerProvider
|
||||||
|
@ -57,6 +59,7 @@ export const Community = (props: Props) => {
|
||||||
</ModalProvider>
|
</ModalProvider>
|
||||||
</NarrowProvider>
|
</NarrowProvider>
|
||||||
</DialogProvider>
|
</DialogProvider>
|
||||||
|
</ThemeProvider>
|
||||||
</AppProvider>
|
</AppProvider>
|
||||||
</Router>
|
</Router>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,75 +0,0 @@
|
||||||
import type { Theme } from '../types/theme'
|
|
||||||
|
|
||||||
export const lightTheme: Theme = {
|
|
||||||
primary: '#000',
|
|
||||||
secondary: '#939BA1',
|
|
||||||
tertiary: '#4360DF',
|
|
||||||
bodyBackgroundColor: '#fff',
|
|
||||||
sectionBackgroundColor: '#F6F8FA',
|
|
||||||
bodyBackgroundGradient:
|
|
||||||
'linear-gradient(0deg, #FFFFFF 50%, rgba(255, 255, 255, 0) 102.57%)',
|
|
||||||
guestNameColor: '#887AF9',
|
|
||||||
iconColor: '#D37EF4',
|
|
||||||
iconUserColor: '#717199',
|
|
||||||
iconTextColor: 'rgba(255, 255, 255, 0.7)',
|
|
||||||
logoColor: '#51D0F0',
|
|
||||||
activeChannelBackground: '#E9EDF1',
|
|
||||||
notificationColor: '#4360DF',
|
|
||||||
inputColor: '#EEF2F5',
|
|
||||||
border: '#EEF2F5',
|
|
||||||
buttonBg: 'rgba(67, 96, 223, 0.1)',
|
|
||||||
buttonBgHover: 'rgba(67, 96, 223, 0.2)',
|
|
||||||
buttonNoBg: 'rgba(255, 45, 85, 0.1)',
|
|
||||||
buttonNoBgHover: 'rgba(255, 45, 85, 0.2)',
|
|
||||||
skeletonLight: '#F6F8FA',
|
|
||||||
skeletonDark: '#E9EDF1',
|
|
||||||
redColor: '#FF2D55',
|
|
||||||
greenColor: '#4EBC60',
|
|
||||||
greenBg: 'rgba(78, 188, 96, 0.1)',
|
|
||||||
mentionColor: '#0DA4C9',
|
|
||||||
mentionHover: '#BDE7F2',
|
|
||||||
mentionBg: '#E5F8FD',
|
|
||||||
mentionBgHover: '#D4F3FA',
|
|
||||||
shadow:
|
|
||||||
'0px 2px 4px rgba(0, 34, 51, 0.16), 0px 4px 12px rgba(0, 34, 51, 0.08)',
|
|
||||||
reactionBg: '#eceffc',
|
|
||||||
blueBg: 'rgba(67, 96, 223, 0.1)',
|
|
||||||
}
|
|
||||||
|
|
||||||
export const darkTheme: Theme = {
|
|
||||||
primary: '#fff',
|
|
||||||
secondary: '#909090',
|
|
||||||
tertiary: '#88B0FF',
|
|
||||||
bodyBackgroundColor: '#000',
|
|
||||||
sectionBackgroundColor: '#252525',
|
|
||||||
bodyBackgroundGradient:
|
|
||||||
'linear-gradient(0deg, #000 50%, rgba(255, 255, 255, 0) 102.57%)',
|
|
||||||
guestNameColor: '#887AF9',
|
|
||||||
iconColor: '#D37EF4',
|
|
||||||
iconUserColor: '#717199',
|
|
||||||
logoColor: '#51D0F0',
|
|
||||||
iconTextColor: 'rgba(0, 0, 0, 0.7)',
|
|
||||||
activeChannelBackground: '#2C2C2C',
|
|
||||||
notificationColor: '#887AF9',
|
|
||||||
inputColor: '#373737',
|
|
||||||
border: '#373737',
|
|
||||||
buttonBg: 'rgba(134, 158, 255, 0.2)',
|
|
||||||
buttonBgHover: 'rgba(67, 96, 223, 0.3)',
|
|
||||||
buttonNoBg: 'rgba(255, 92, 123, 0.2)',
|
|
||||||
buttonNoBgHover: 'rgba(255, 45, 85, 0.3)',
|
|
||||||
skeletonLight: '#2E2F31',
|
|
||||||
skeletonDark: '#141414',
|
|
||||||
redColor: '#FF5C7B',
|
|
||||||
greenColor: '#60C370',
|
|
||||||
greenBg: 'rgba(96, 195, 112, 0.2)',
|
|
||||||
mentionColor: '#51D0F0',
|
|
||||||
mentionHover: '#004E60',
|
|
||||||
mentionBg: '#004050',
|
|
||||||
mentionBgHover: '#002D39',
|
|
||||||
shadow:
|
|
||||||
'0px 2px 4px rgba(0, 34, 51, 0.16), 0px 4px 12px rgba(0, 34, 51, 0.08)',
|
|
||||||
reactionBg: '#373737',
|
|
||||||
blueBg: 'rgba(134, 158, 255, 0.3)',
|
|
||||||
}
|
|
||||||
|
|
||||||
export default { lightTheme, darkTheme }
|
|
|
@ -3,10 +3,19 @@ import type { BrowserRouter, HashRouter, MemoryRouter } from 'react-router-dom'
|
||||||
|
|
||||||
export type Environment = 'production' | 'test'
|
export type Environment = 'production' | 'test'
|
||||||
|
|
||||||
|
type CustomTheme = {
|
||||||
|
colors: {
|
||||||
|
[key in keyof Theme['colors']]: string
|
||||||
|
}
|
||||||
|
fonts: {
|
||||||
|
[key in keyof Theme['colors']]: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface Config {
|
export interface Config {
|
||||||
publicKey: string
|
publicKey: string
|
||||||
environment?: Environment
|
environment?: Environment
|
||||||
theme?: Theme
|
theme?: 'light' | 'dark' | CustomTheme
|
||||||
router?: typeof BrowserRouter | typeof MemoryRouter | typeof HashRouter
|
router?: typeof BrowserRouter | typeof MemoryRouter | typeof HashRouter
|
||||||
options?: {
|
options?: {
|
||||||
enableSidebar: boolean
|
enableSidebar: boolean
|
||||||
|
|
Loading…
Reference in New Issue