Skip to content

Commit

Permalink
feat: shadow DOM에서 다크모드 대응되도록 설정
Browse files Browse the repository at this point in the history
  • Loading branch information
kangju2000 committed Apr 13, 2024
1 parent c27bb85 commit af92d3c
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 139 deletions.
8 changes: 2 additions & 6 deletions src/components/ChakraMotion.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import { type ChakraProps, chakra, shouldForwardProp, forwardRef } from '@chakra-ui/react'
import { chakra, shouldForwardProp, forwardRef, type ChakraProps } from '@chakra-ui/react'
import { isValidMotionProp, motion, type MotionProps } from 'framer-motion'

import type { ComponentPropsWithoutRef } from 'react'

const ChakraMotion = <C extends React.ElementType>(
{
as,
children,
...props
}: ComponentPropsWithoutRef<C> & { as?: C } & MotionProps & Omit<ChakraProps, 'transition'>, /// <reference path=
{ as, children, ...props }: ComponentPropsWithoutRef<C> & { as?: C } & MotionProps & Omit<ChakraProps, 'transition'>,
ref: React.ComponentPropsWithRef<C>['ref'],
) => {
const Component = as || 'div'
Expand Down
22 changes: 4 additions & 18 deletions src/components/ContentModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,16 @@ import CourseList from './CourseList'
import { RefreshIcon, SettingIcon } from './Icons'
import PopoverOptions from './PopoverOptions'
import TabContent from './TabContent'
import { useRootRefContext } from '@/components/ShadowChakraProvider'
import useGetContents from '@/hooks/useGetContents'
import { useShadowContext } from '@/pages/content/App'

type Props = {
isOpen: boolean
onClose: () => void
}

const ContentModal = ({ isOpen, onClose }: Props) => {
const shadowRef = useShadowContext()
const rootRef = useRootRefContext()
const [selectedCourseId, setSelectedCourseId] = useState('-1')
const {
data: { courseList, activityList, updateAt },
Expand All @@ -42,23 +42,9 @@ const ContentModal = ({ isOpen, onClose }: Props) => {
const updateAtDate = new Date(updateAt)

return (
<Modal
isCentered
isOpen={isOpen}
onClose={onClose}
portalProps={{
containerRef: shadowRef,
}}
>
<Modal isCentered isOpen={isOpen} onClose={onClose} portalProps={{ containerRef: rootRef }}>
<ModalOverlay />
<ModalContent
minW={{
base: '90%',
md: '750px',
}}
h="500px"
borderRadius="8px"
>
<ModalContent minW={{ base: '90%', md: '750px' }} h="500px" borderRadius="8px">
<ModalHeader display="flex" alignItems="center" minH="60px" px="24px">
<Text fontSize="18px" fontWeight="700">
Gachon Tools
Expand Down
21 changes: 3 additions & 18 deletions src/components/PopoverOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,7 @@ const PopoverOptions = ({ triggerElement }: Props) => {
<PopoverTrigger>{triggerElement}</PopoverTrigger>
<PopoverContent w="auto">
<PopoverArrow />
<PopoverHeader
p="8px"
fontWeight="700"
_light={{ color: 'gray.700' }}
_dark={{ color: 'gray.200' }}
>
<PopoverHeader p="8px" fontWeight="700" _light={{ color: 'gray.700' }} _dark={{ color: 'gray.200' }}>
설정
</PopoverHeader>
<PopoverCloseButton
Expand All @@ -52,20 +47,10 @@ const PopoverOptions = ({ triggerElement }: Props) => {
<PopoverBody p="12px">
<Stack spacing={5} direction="row" fontSize="12px" mb="8px">
<Text>테마 :</Text>
<Checkbox
m="0"
size="lg"
isChecked={colorMode === 'light'}
onChange={() => setColorMode('light')}
>
<Checkbox m="0" size="lg" isChecked={colorMode === 'light'} onChange={() => setColorMode('light')}>
<Text>Light</Text>
</Checkbox>
<Checkbox
m="0"
size="lg"
isChecked={colorMode === 'dark'}
onChange={() => setColorMode('dark')}
>
<Checkbox m="0" size="lg" isChecked={colorMode === 'dark'} onChange={() => setColorMode('dark')}>
<Text>Dark</Text>
</Checkbox>
</Stack>
Expand Down
29 changes: 29 additions & 0 deletions src/components/ShadowChakraProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { ChakraProvider } from '@chakra-ui/react'
import { createContext, useContext, useRef } from 'react'

import { ShadowRootWrapper } from './ShadowRootWrapper'
import { customTheme } from '@/constants/customTheme'

type Props = {
children: React.ReactNode
}

export function ShadowChakraProvider({ children }: Props) {
const rootRef = useRef<HTMLDivElement>(null)

return (
<ShadowRootWrapper>
<ChakraProvider cssVarsRoot=":host,:root" theme={customTheme}>
<div ref={rootRef}>
<RootRefContext.Provider value={rootRef}>{children}</RootRefContext.Provider>
</div>
</ChakraProvider>
</ShadowRootWrapper>
)
}

const RootRefContext = createContext<React.RefObject<HTMLElement>>(null)

export const useRootRefContext = () => {
return useContext(RootRefContext)
}
31 changes: 31 additions & 0 deletions src/components/ShadowRootWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import createCache from '@emotion/cache'
import { CacheProvider, EmotionCache } from '@emotion/react'
import { useEffect, useRef, useState } from 'react'
import root from 'react-shadow/emotion'

type Props = {
children: React.ReactNode
}

export function ShadowRootWrapper({ children }: Props) {
const shadowRef = useRef<HTMLElement>(null)

const [emotionCache, setEmotionCache] = useState<EmotionCache | null>(null)

useEffect(() => {
if (!shadowRef.current?.shadowRoot) return

const cache = createCache({
key: 'shadow',
container: shadowRef.current.shadowRoot,
})

setEmotionCache(cache)
}, [])

return (
<root.div ref={shadowRef}>
<CacheProvider value={emotionCache}>{emotionCache ? children : null}</CacheProvider>
</root.div>
)
}
22 changes: 6 additions & 16 deletions src/components/TabContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,7 @@ type Props = {

const TabContent = ({ activityList, selectedCourseId, pos, isLoading }: Props) => {
const [tabIndex, setTabIndex] = useState(0)
const filteredActivities = useFilteredActivityList(
activityList,
selectedCourseId,
tabIndex,
false,
)
const filteredActivities = useFilteredActivityList(activityList, selectedCourseId, tabIndex, false)

return (
<Tabs isLazy={true} index={tabIndex} onChange={index => setTabIndex(index)}>
Expand All @@ -33,6 +28,9 @@ const TabContent = ({ activityList, selectedCourseId, pos, isLoading }: Props) =
_light={{ bg: 'white' }}
_dark={{ bg: 'gray.700' }}
pt="16px"
bg="white"
borderBottom="2px solid"
borderColor="gray.200"
>
{TAB_LIST.map(tab => (
<Tab
Expand All @@ -58,18 +56,10 @@ const TabContent = ({ activityList, selectedCourseId, pos, isLoading }: Props) =

<TabPanels as={AnimatePresence}>
<TabPanel>
{isLoading ? (
<LoadingProgress pos={pos} />
) : (
<ActivityList contentData={filteredActivities} />
)}
{isLoading ? <LoadingProgress pos={pos} /> : <ActivityList contentData={filteredActivities} />}
</TabPanel>
<TabPanel>
{isLoading ? (
<LoadingProgress pos={pos} />
) : (
<ActivityList contentData={filteredActivities} />
)}
{isLoading ? <LoadingProgress pos={pos} /> : <ActivityList contentData={filteredActivities} />}
</TabPanel>
</TabPanels>
</Tabs>
Expand Down
6 changes: 5 additions & 1 deletion src/components/Trigger.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,17 @@ export default function Trigger() {
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0 }}
transition={{ duration: 0.3, ease: 'easeOut' }}
whileHover={{ width: '100px', height: '50px' }}
whileHover={{ width: '100px', height: '50px', x: -30 }}
position="fixed"
bottom="25px"
left="50%"
w="40px"
h="40px"
bg="#2F6EA2"
boxShadow="dark-lg"
rounded="full"
cursor="pointer"
zIndex={9999}
onClick={onOpen}
/>
)}
Expand Down
25 changes: 25 additions & 0 deletions src/constants/customTheme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ThemeConfig, extendTheme } from '@chakra-ui/react'
import { mode, type StyleFunctionProps } from '@chakra-ui/theme-tools'

const config: ThemeConfig = {
initialColorMode: 'light',
useSystemColorMode: true,
}

export const customTheme = extendTheme({
config,
components: {
Text: {
baseStyle: (props: StyleFunctionProps) => ({
color: mode('gray.700', 'gray.200')(props),
margin: 0,
padding: 0,
}),
},
Divider: {
baseStyle: (props: StyleFunctionProps) => ({
borderColor: mode('gray.200', 'gray.700')(props),
}),
},
},
})
64 changes: 18 additions & 46 deletions src/pages/content/App.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,28 @@
import { ThemeProvider, theme, CSSReset, Box } from '@chakra-ui/react'
import createCache from '@emotion/cache'
import { CacheProvider, EmotionCache } from '@emotion/react'
import { createContext, useContext, useEffect, useRef, useState } from 'react'
import root from 'react-shadow/emotion'
import { useColorMode } from '@chakra-ui/react'
import { useEffect } from 'react'

import { ShadowChakraProvider, useRootRefContext } from '@/components/ShadowChakraProvider'
import Trigger from '@/components/Trigger'

const ShadowContext = createContext<React.RefObject<HTMLElement>>(null)

export const useShadowContext = () => {
return useContext(ShadowContext)
}

export default function App() {
const shadowRef = useRef<HTMLElement>(null)
const rootRef = useRef<HTMLDivElement>(null)
const [shadowRoot, setShadowRoot] = useState<ShadowRoot | null>(null)
const [emotionCache, setEmotionCache] = useState<EmotionCache | null>(null)

useEffect(() => {
if (!shadowRoot) return

const cache = createCache({
key: 'shadow',
container: shadowRoot,
})
return (
<ShadowChakraProvider>
<Trigger />
<ColorSetting />
</ShadowChakraProvider>
)
}

setEmotionCache(cache)
}, [shadowRoot])
const ColorSetting = () => {
const { colorMode } = useColorMode()
const rootRef = useRootRefContext()

useEffect(() => {
if (!shadowRef.current.shadowRoot) return
const root = rootRef.current

setShadowRoot(shadowRef.current.shadowRoot)
}, [shadowRef.current?.shadowRoot])
root.dataset.theme = colorMode
root.style.colorScheme = colorMode
}, [colorMode])

return (
<root.div ref={shadowRef}>
<CacheProvider value={emotionCache}>
<div id="shadow-root" ref={rootRef}>
{emotionCache && (
<ThemeProvider cssVarsRoot=":host,:root" theme={theme}>
<CSSReset />
<ShadowContext.Provider value={rootRef}>
<Box pos="fixed" w="100vw" display="flex" bottom="25px" justifyContent="center" zIndex="9999">
<Trigger />
</Box>
</ShadowContext.Provider>
</ThemeProvider>
)}
</div>
</CacheProvider>
</root.div>
)
return null
}
34 changes: 0 additions & 34 deletions src/pages/content/main.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,9 @@
import { ChakraProvider, extendTheme, type ThemeConfig } from '@chakra-ui/react'
import { mode, type StyleFunctionProps } from '@chakra-ui/theme-tools'
import { createRoot } from 'react-dom/client'
import root from 'react-shadow'

import App from '@/pages/content/App'

const crxRoot = document.createElement('div')
crxRoot.id = 'crx-root'
document.body.append(crxRoot)

const config: ThemeConfig = {
initialColorMode: 'light',
useSystemColorMode: true,
}

const theme = extendTheme({
config,
styles: {
global: {
body: {
// 사이버캠퍼스 기본 설정 적용
fontSize: '14px',
lineHeight: '1.42857143',
color: '#333',
},
'#back-top': {
opacity: 0,
},
},
},
components: {
Text: {
baseStyle: (props: StyleFunctionProps) => ({
color: mode('gray.700', 'gray.200')(props),
margin: 0,
padding: 0,
}),
},
},
})

createRoot(crxRoot).render(<App />)

0 comments on commit af92d3c

Please sign in to comment.