Bluesky app fork with some witchin' additions 馃挮
at linkat-integration 68 lines 1.7 kB view raw
1import React from 'react' 2 3type Component = React.ReactElement 4 5type ContextType = { 6 outlet: Component | null 7 append(id: string, component: Component): void 8 remove(id: string): void 9} 10 11type ComponentMap = { 12 [id: string]: Component 13} 14 15export function createPortalGroup_INTERNAL() { 16 const Context = React.createContext<ContextType>({ 17 outlet: null, 18 append: () => {}, 19 remove: () => {}, 20 }) 21 Context.displayName = 'BottomSheetPortalContext' 22 23 function Provider(props: React.PropsWithChildren<{}>) { 24 const map = React.useRef<ComponentMap>({}) 25 const [outlet, setOutlet] = React.useState<ContextType['outlet']>(null) 26 27 const append = React.useCallback<ContextType['append']>((id, component) => { 28 if (map.current[id]) return 29 map.current[id] = <React.Fragment key={id}>{component}</React.Fragment> 30 setOutlet(<>{Object.values(map.current)}</>) 31 }, []) 32 33 const remove = React.useCallback<ContextType['remove']>(id => { 34 delete map.current[id] 35 setOutlet(<>{Object.values(map.current)}</>) 36 }, []) 37 38 const contextValue = React.useMemo( 39 () => ({ 40 outlet, 41 append, 42 remove, 43 }), 44 [outlet, append, remove], 45 ) 46 47 return ( 48 <Context.Provider value={contextValue}>{props.children}</Context.Provider> 49 ) 50 } 51 52 function Outlet() { 53 const ctx = React.useContext(Context) 54 return ctx.outlet 55 } 56 57 function Portal({children}: React.PropsWithChildren<{}>) { 58 const {append, remove} = React.useContext(Context) 59 const id = React.useId() 60 React.useEffect(() => { 61 append(id, children as Component) 62 return () => remove(id) 63 }, [id, children, append, remove]) 64 return null 65 } 66 67 return {Provider, Outlet, Portal} 68}