diff options
author | Samuel Newman <mozzius@protonmail.com> | 2024-10-09 21:30:42 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-10-09 11:30:42 -0700 |
commit | cca344a3d1cdca3d4e63806a9bd5f7867f8961d4 (patch) | |
tree | 999d7dffe5d53989b7e217db13f451c6d019ff57 /modules/bottom-sheet/src/lib | |
parent | b3ade19bbe3da3caf07bf9561cebb11dac4b6afc (diff) | |
download | voidsky-cca344a3d1cdca3d4e63806a9bd5f7867f8961d4.tar.zst |
Allow nested sheets without boilerplate (#5660)
Co-authored-by: Hailey <me@haileyok.com>
Diffstat (limited to 'modules/bottom-sheet/src/lib')
-rw-r--r-- | modules/bottom-sheet/src/lib/Portal.tsx | 67 |
1 files changed, 67 insertions, 0 deletions
diff --git a/modules/bottom-sheet/src/lib/Portal.tsx b/modules/bottom-sheet/src/lib/Portal.tsx new file mode 100644 index 000000000..dd1bc4c13 --- /dev/null +++ b/modules/bottom-sheet/src/lib/Portal.tsx @@ -0,0 +1,67 @@ +import React from 'react' + +type Component = React.ReactElement + +type ContextType = { + outlet: Component | null + append(id: string, component: Component): void + remove(id: string): void +} + +type ComponentMap = { + [id: string]: Component +} + +export function createPortalGroup_INTERNAL() { + const Context = React.createContext<ContextType>({ + outlet: null, + append: () => {}, + remove: () => {}, + }) + + function Provider(props: React.PropsWithChildren<{}>) { + const map = React.useRef<ComponentMap>({}) + const [outlet, setOutlet] = React.useState<ContextType['outlet']>(null) + + const append = React.useCallback<ContextType['append']>((id, component) => { + if (map.current[id]) return + map.current[id] = <React.Fragment key={id}>{component}</React.Fragment> + setOutlet(<>{Object.values(map.current)}</>) + }, []) + + const remove = React.useCallback<ContextType['remove']>(id => { + delete map.current[id] + setOutlet(<>{Object.values(map.current)}</>) + }, []) + + const contextValue = React.useMemo( + () => ({ + outlet, + append, + remove, + }), + [outlet, append, remove], + ) + + return ( + <Context.Provider value={contextValue}>{props.children}</Context.Provider> + ) + } + + function Outlet() { + const ctx = React.useContext(Context) + return ctx.outlet + } + + function Portal({children}: React.PropsWithChildren<{}>) { + const {append, remove} = React.useContext(Context) + const id = React.useId() + React.useEffect(() => { + append(id, children as Component) + return () => remove(id) + }, [id, children, append, remove]) + return null + } + + return {Provider, Outlet, Portal} +} |