mirror of https://github.com/acid-info/lsd.git
feat: implement root portal provider
This commit is contained in:
parent
36df81df77
commit
225a32f43f
|
@ -0,0 +1,23 @@
|
|||
import React from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { useCanUsePortal } from './PortalContext'
|
||||
import { usePortal } from './usePortal'
|
||||
|
||||
export type PortalProps = React.PropsWithChildren & {
|
||||
id: string
|
||||
}
|
||||
|
||||
export const Portal: React.FC<PortalProps> = ({ id, children }) => {
|
||||
const canUse = useCanUsePortal()
|
||||
if (!canUse) return <></>
|
||||
|
||||
return <PortalContent id={id}>{children}</PortalContent>
|
||||
}
|
||||
|
||||
const PortalContent: React.FC<PortalProps> = ({ id, children }) => {
|
||||
const element = usePortal({ parentId: 'lsd-presentation' })
|
||||
|
||||
if (!element) return <></>
|
||||
|
||||
return createPortal(children, element, id)
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import React, { useContext } from 'react'
|
||||
|
||||
export type PortalContextType = {
|
||||
initialized?: boolean
|
||||
}
|
||||
|
||||
export const PortalContext = React.createContext<PortalContextType>({
|
||||
initialized: false,
|
||||
})
|
||||
|
||||
export const useCanUsePortal = (): boolean =>
|
||||
useContext(PortalContext)?.initialized ?? false
|
|
@ -0,0 +1,29 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { PortalContext } from './PortalContext'
|
||||
|
||||
export type PortalProviderProps = React.PropsWithChildren
|
||||
|
||||
export const PortalProvider: React.FC<PortalProviderProps> = ({ children }) => {
|
||||
const [initialized, setInitialized] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') return
|
||||
|
||||
const body = document.querySelector('body')!
|
||||
const container = document.createElement('div')
|
||||
container.id = 'lsd-presentation'
|
||||
body.appendChild(container)
|
||||
|
||||
setInitialized(true)
|
||||
|
||||
return () => {
|
||||
body.removeChild(container)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<PortalContext.Provider value={{ initialized }}>
|
||||
{children}
|
||||
</PortalContext.Provider>
|
||||
)
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export * from './PortalProvider'
|
|
@ -0,0 +1,28 @@
|
|||
import { useEffect, useRef } from 'react'
|
||||
|
||||
interface Props {
|
||||
parentId: string
|
||||
}
|
||||
|
||||
export const usePortal = ({ parentId }: Props) => {
|
||||
const elementRef = useRef<HTMLElement>()
|
||||
|
||||
if (typeof window !== 'undefined' && !elementRef.current) {
|
||||
elementRef.current = document.createElement('div')
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined' || !elementRef.current) return
|
||||
document.getElementById(parentId)?.appendChild(elementRef.current)
|
||||
|
||||
return () => {
|
||||
try {
|
||||
document
|
||||
.getElementById(parentId)
|
||||
?.removeChild(elementRef.current as Node)
|
||||
} catch (error) {}
|
||||
}
|
||||
}, [parentId, elementRef.current])
|
||||
|
||||
return elementRef.current
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import { Global, ThemeProvider as EmotionThemeProvider } from '@emotion/react'
|
||||
import React from 'react'
|
||||
import { CSSBaseline } from '../CSSBaseline'
|
||||
import { PortalProvider } from '../PortalProvider'
|
||||
import { Theme } from './types'
|
||||
|
||||
export type ThemeProviderProps = React.PropsWithChildren<{
|
||||
|
@ -13,9 +14,11 @@ export const ThemeProvider: React.FC<ThemeProviderProps> = ({
|
|||
}) => {
|
||||
return (
|
||||
<ThemeContext.Provider value={{ theme }}>
|
||||
<EmotionThemeProvider theme={theme}>{children}</EmotionThemeProvider>
|
||||
<CSSBaseline theme={theme} />
|
||||
<Global styles={theme.globalStyles} />
|
||||
<PortalProvider>
|
||||
<EmotionThemeProvider theme={theme}>{children}</EmotionThemeProvider>
|
||||
<CSSBaseline theme={theme} />
|
||||
<Global styles={theme.globalStyles} />
|
||||
</PortalProvider>
|
||||
</ThemeContext.Provider>
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue