Overlay / Modals Manager
Preview: Default
Mount once. Lets any code open dialogs imperatively and await their result (confirm, prompt, custom).
Import
import { OverlayProvider } from '@marbleui/ui'
import { useOverlay } from '@marbleui/ui'Props
<OverlayProvider>
| Prop | Type | Default | Description |
|---|---|---|---|
| children *required | ReactNode | — |
Hooks
useOverlay(): { open: <T>(render: (close: (v?: T | undefined) => void) => ReactNode) => Promise<T | undefined>; confirm: (title: string, body?: str…`confirm()` / `prompt()` that return promises, plus raw `open()` for custom dialogs.
Accessibility
Keyboard interaction, focus management and ARIA semantics come from the underlying primitives: React Aria Heading · React Aria Modal · React Aria ModalOverlay · React Aria Dialog. Follow the primitive's docs for the full keyboard map.
Usage notes
Open dialogs from event handlers and await the answer: confirm(), prompt(), and open() for custom content. Mount OverlayProvider once near the app root. For a confirmation tied to a single button, AlertDialog or Popconfirm is simpler.
const { confirm, prompt } = useOverlay()
if (await confirm('Delete 3 files?', 'They move to the trash for 30 days.', { confirmLabel: 'Delete', destructive: true })) deleteFiles()
const name = await prompt('Rename project', 'Project name', project.name)
confirmresolvestrueorfalse; Escape and clicking outside count as cancel. It is analertdialogwith focus on Cancel.promptresolves the entered text, orundefinedwhen cancelled. Enter submits.open((close) => …)renders your content in a bare modal and resolves with whatever you pass toclose. Wrap that content in React Aria’sDialogwith aHeading slot="title", asconfirmandpromptdo, so it’s a labelled dialog.useOverlay()throws outside anOverlayProvider.
Example source · show· hide
import { useState } from 'react'
import { Button } from '../button'
import { OverlayProvider, useOverlay } from './overlay-manager'
function Inner() {
const { confirm, prompt } = useOverlay()
const [log, setLog] = useState<string[]>([])
const add = (s: string) => setLog((l) => [s, ...l].slice(0, 4))
return (
<div className="flex w-80 flex-col gap-3">
<div className="flex gap-2">
<Button variant="outline" onPress={async () => add((await confirm('Delete 3 files?', 'They move to the trash for 30 days.', { confirmLabel: 'Delete', destructive: true })) ? 'Deleted' : 'Kept files')}>confirm()</Button>
<Button variant="outline" onPress={async () => { const name = await prompt('Rename project', 'Project name', 'Website'); add(name ? `Renamed to “${name}”` : 'Rename cancelled') }}>prompt()</Button>
</div>
<ul aria-live="polite" className="text-sm text-fg-muted">{log.map((l, i) => <li key={i}>→ {l}</li>)}</ul>
</div>
)
}
export const styles = { Default: () => <OverlayProvider><Inner /></OverlayProvider> }Source: packages/ui/src/overlay-manager/overlay-manager.tsx
Catalog
- Category
- utilities
- Priority
- Extra
- Primitive
- custom