feat(react): allow theme customization

This commit is contained in:
Pavel Prichodko 2022-04-18 15:48:34 +02:00
parent 582b58dc4e
commit 3b8c393be8
No known key found for this signature in database
GPG Key ID: 0EB8D75C775AB6F1
4 changed files with 96 additions and 100 deletions

View File

@ -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
}

View File

@ -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,30 +34,32 @@ export const Community = (props: Props) => {
return ( return (
<Router> <Router>
<AppProvider config={props}> <AppProvider config={props}>
<DialogProvider> <ThemeProvider theme={theme}>
<NarrowProvider myRef={ref}> <DialogProvider>
<ModalProvider> <NarrowProvider myRef={ref}>
<ToastProvider> <ModalProvider>
<Wrapper ref={ref} className={theme}> <ToastProvider>
<GlobalStyle /> <Wrapper ref={ref}>
<IdentityProvider> <GlobalStyle />
<MessengerProvider <IdentityProvider>
publicKey={publicKey} <MessengerProvider
environment={environment} publicKey={publicKey}
> environment={environment}
<ChatStateProvider> >
<ScrollProvider> <ChatStateProvider>
<Messenger /> <ScrollProvider>
<div id="portal" data-radix-portal /> <Messenger />
</ScrollProvider> <div id="portal" data-radix-portal />
</ChatStateProvider> </ScrollProvider>
</MessengerProvider> </ChatStateProvider>
</IdentityProvider> </MessengerProvider>
</Wrapper> </IdentityProvider>
</ToastProvider> </Wrapper>
</ModalProvider> </ToastProvider>
</NarrowProvider> </ModalProvider>
</DialogProvider> </NarrowProvider>
</DialogProvider>
</ThemeProvider>
</AppProvider> </AppProvider>
</Router> </Router>
) )

View File

@ -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 }

View File

@ -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