Modals
Every modals export of @appfunnel-dev/sdk, generated from the SDK source. Signatures and
descriptions are exact; for how these fit together, see the guides.
useModal
hook · modals/registry.tsx
useModal(): ModalHandler
useModal(modal: string, args?: Record<string, unknown>): ModalHandler
useModal<P extends object>(modal: ComponentType<P>, args?: Partial<P>): ModalHandler<P>Modal
component · modals/sheet.tsx
Modal(props: ModalProps): ReactNodeA centered dialog. Auto-binds to its defineModal — <Modal>….
Sheet
component · modals/sheet.tsx
Sheet(props: SheetProps): ReactNodeA drawer, or (with variant="floating") a centered card. Inside a defineModal it
auto-binds — <Sheet>…; outside one, control it with open/onClose.
defineModal
function · modals/registry.tsx
defineModal<P extends object>(Comp: ComponentType<P>): ComponentType<P & ModalHocProps>Wrap a component into an id-keyed modal. Inside it, useModal() (no args)
gives the handle. Render a controlled <Sheet>/<Modal> with
open={m.visible} onClose={m.hide}.
dismissAllModals
function · modals/registry.tsx
dismissAllModals(): voidClose every open modal at once and settle anything awaiting them.
The registry is mounted ABOVE the flow (FunnelProvider renders ModalRoot, FunnelView lives
inside it), so a modal a page opened outlives that page — it would otherwise sit on top of the next
screen. FunnelView calls this on every page change; authors rarely need it directly.
Pending showModal() promises resolve with undefined rather than hanging forever, so await showModal(...) in a page handler unblocks and can tell “dismissed” from a real result. No-ops when
no runtime is mounted (SSR, or a tree rendered without FunnelProvider).
hideModal
function · modals/registry.tsx
hideModal<T = unknown>(modal: string | ComponentType<any>): Promise<T>Hide a modal. Returns a promise resolved by resolveHide() (post-transition).
registerModal
function · modals/registry.tsx
registerModal(id: string, comp: ComponentType<any>, props?: Record<string, unknown>): voidRegister a component as an id-keyed modal so the placeholder can render it.
removeModal
function · modals/registry.tsx
removeModal(modal: string | ComponentType<any>): voidRemove a modal from the tree (frees its state; better than just hiding).
showModal
function · modals/registry.tsx
showModal<T = unknown>(modal: string | ComponentType<any>, args?: Record<string, unknown>): Promise<T>Show a modal (by component or id). Returns a promise resolved by resolve().
unregisterModal
function · modals/registry.tsx
unregisterModal(id: string): voidModalHandler
interface · modals/registry.tsx
export interface ModalHandler<Props = Record<string, unknown>> {
id: string
args?: Record<string, unknown>
visible: boolean
keepMounted: boolean
show: (args?: Props) => Promise<unknown>
hide: () => Promise<unknown>
remove: () => void
resolve: (value?: unknown) => void
reject: (value?: unknown) => void
resolveHide: (value?: unknown) => void
}The handle useModal() returns to control one modal.
ModalHocProps
interface · modals/registry.tsx
export interface ModalHocProps {
id: string
defaultVisible?: boolean
keepMounted?: boolean
}ModalState
interface · modals/registry.tsx
export interface ModalState {
id: string
args?: Record<string, unknown>
visible?: boolean
delayVisible?: boolean
keepMounted?: boolean
}ModalProps
type · modals/sheet.tsx
export type ModalProps = OverlayCommonSheetProps
type · modals/sheet.tsx
export type SheetProps = OverlayCommon & {
side?: SheetSide
variant?: SheetVariant
}SheetSide
type · modals/sheet.tsx
export type SheetSide = 'bottom' | 'top' | 'left' | 'right'SheetVariant
type · modals/sheet.tsx
export type SheetVariant = 'edge' | 'floating'How a <Sheet> presents itself.
edge(default) — the classic drawer, flush against thesideedge. Every existing consumer is this, unchanged in DOM, geometry and (lack of) motion.floating— a rounded card insetFLOATING_INSETfrom the viewport, presented two ways by width (seefloatingGeometry): bottom-anchored on a phone, centered atFLOATING_WIDTHon a desktop. Both RESERVE their height. Springs in.sidedoes not apply.