@lucid-softworks/dialog-manager
v0.2.0
Published
Manage typed application dialogs from React.
Downloads
414
Maintainers
Readme
@lucid-softworks/dialog-manager
Manage typed application dialogs from React without promise-based result
handling. Dialog content renders in a native modal <dialog>, so the browser
provides the top layer, focus management, inert page content, and a backdrop.
Usage
import { useState, type ReactElement } from "react";
import {
DialogManagerProvider,
useDialog,
useManager,
type DialogCloseReason,
} from "@lucid-softworks/dialog-manager";
interface Account {
readonly id: string;
readonly name: string;
}
function AddAccountDialog(): ReactElement {
const dialog = useDialog<Account>();
return (
<section aria-labelledby="add-account-title">
<h2 id="add-account-title">Add account</h2>
<button onClick={dialog.close} type="button">
Cancel
</button>
<button
onClick={() => {
dialog.success({ id: "account-1", name: "Primary" });
}}
type="button"
>
Save
</button>
</section>
);
}
function AccountsPage(): ReactElement {
const manager = useManager();
const [account, setAccount] = useState<Account | null>(null);
const [closeReason, setCloseReason] = useState<DialogCloseReason | null>(
null,
);
function openAddAccount(): void {
manager.open<Account>({
id: "add-account",
ariaLabel: "Add an account",
content: <AddAccountDialog />,
onClose(reason) {
setCloseReason(reason);
},
onSuccess(createdAccount) {
setAccount(createdAccount);
},
});
}
return (
<main>
<button onClick={openAddAccount} type="button">
Add account
</button>
<button
onClick={() => {
manager.close({ id: "add-account" });
}}
type="button"
>
Close add-account externally
</button>
<p>Account: {account?.name ?? "None"}</p>
<p>Last close reason: {closeReason ?? "None"}</p>
</main>
);
}
export interface AppProps {
readonly navigationKey: string;
}
export function App({ navigationKey }: AppProps): ReactElement {
return (
<DialogManagerProvider navigationKey={navigationKey}>
<AccountsPage />
</DialogManagerProvider>
);
}Pass a router location key or pathname as navigationKey. Every open dialog
closes with the navigation reason when that value changes.
Controllers
manager.open<TResult>() returns a controller immediately. Calling its
close() method invokes onClose("cancel"); calling success(result) invokes
onSuccess(result). Dialog content gets the same typed controller through
useDialog<TResult>().
Calling manager.close({ id }) from elsewhere invokes onClose("external").
Opening the same non-empty ID twice throws until the first dialog settles.
DialogCloseReason is one of backdrop, cancel, escape, external, or
navigation. A successful dialog only calls onSuccess; it does not also call
onClose.
