import React from 'react' import * as Dialog from '#/components/Dialog' /** * Type util for global dialog components. Wrap individual dialog component * types with this to get types for the additional properties applied by the * the global dialon controller. */ export type GlobalDialogProps = { params: T cleanup(): void options?: Dialog.DialogControlOpenOptions } type ActiveDialog> = { component: T props: React.ComponentProps options?: Dialog.DialogControlOpenOptions } type ContextProps = { activeDialogs: ActiveDialog[] open>( component: T, props: React.ComponentProps['params'], options?: Dialog.DialogControlOpenOptions, ): void popTopDialog(): void } export const Context = React.createContext({ activeDialogs: [], open() {}, popTopDialog() {}, }) /** * Hook to open a "global" dialog. * * @example * ```tsx * const openGlobalDialog = useOpenGlobalDialog() * openGlobalDialog( * MyDialog, * // component props * {foo: 'bar'}, * // base Dialog options * { index: 1 } * ) * ``` */ export function useOpenGlobalDialog() { return React.useContext(Context).open } /** * Provider for "global" dialogs _only_. ALL dialogs are still registered by * `#/state/dialogs` as well. */ export function Provider({children}: React.PropsWithChildren<{}>) { const [activeDialogs, setActiveDialogs] = React.useState[]>( [], ) const ctx = React.useMemo( () => ({ activeDialogs, open(component, props, options) { setActiveDialogs(s => [...s, {component, props, options}]) }, popTopDialog() { setActiveDialogs(s => s.slice(0, -1)) }, }), [activeDialogs, setActiveDialogs], ) return {children} } /** * Outlet for any active "global" dialogs. Since these are rendered into a * portal in the root, this is technically not their final resting place. */ export function GlobalDialog() { const {activeDialogs, popTopDialog} = React.useContext(Context) const cleanup = React.useCallback(() => { popTopDialog() }, [popTopDialog]) return activeDialogs.map(({component: Comp, props, options}, i) => { return }) }