@hirotoshioi/hiraku-base-ui
v0.0.6
Published
Modal state management library for Base UI based components
Readme
Features
- ⚡ Open from anywhere - Call
modal.open()from any file, even outside React components - 🔒 Type-safe - Strongly typed
- 🎯 Base UI native - First-class support for Dialog, Sheet, and AlertDialog
- 🪶 Lightweight - Depends on
@hirotoshioi/hiraku-core(zustand-based)
Installation
npm install @hirotoshioi/hiraku-base-uiBase UI is required as a peer dependency:
npm install @base-ui/reactQuick Start
1. Add the Provider
// app.tsx
import { ModalProvider } from "@hirotoshioi/hiraku-base-ui";
function App() {
return (
<>
<YourApp />
<ModalProvider />
</>
);
}2. Create a modal
// modals/confirm-dialog.tsx
import { Dialog } from "@base-ui/react/dialog";
import { createDialog } from "@hirotoshioi/hiraku-base-ui";
interface ConfirmDialogProps {
title: string;
message: string;
}
function ConfirmDialog({ title, message }: ConfirmDialogProps) {
return (
<Dialog.Portal>
<Dialog.Backdrop />
<Dialog.Popup>
<Dialog.Title>{title}</Dialog.Title>
<Dialog.Description>{message}</Dialog.Description>
<button onClick={() => void confirmDialog.close({ role: "cancel" })}>
Cancel
</button>
<button onClick={() => void confirmDialog.close({ data: true, role: "confirm" })}>
Confirm
</button>
</Dialog.Popup>
</Dialog.Portal>
);
}
export const confirmDialog = createDialog(ConfirmDialog).returns<boolean>();3. Open from anywhere
import { confirmDialog } from "./modals/confirm-dialog";
async function handleDelete() {
await confirmDialog.open({
title: "Delete item?",
message: "This action cannot be undone.",
});
const { data, role } = await confirmDialog.onDidClose();
if (role === "confirm" && data) {
// Perform delete
}
}Examples
See the example app in this repository: examples/base-ui/.
API
Factory Functions
| Function | Description |
| ------------------------------ | ----------------------------------------- |
| createDialog(Component) | Create a modal using Base UI Dialog |
| createSheet(Component) | Create a modal using Base UI (Dialog.Root)|
| createAlertDialog(Component) | Create a modal using Base UI AlertDialog |
Modal Controller
const myModal = createDialog(MyComponent).returns<ResultType>();
myModal.open(props) // Open the modal (returns Promise)
myModal.close({ data, role }) // Close the modal with result
myModal.onDidClose() // Get Promise that resolves when closed
myModal.isOpen() // Check if modal is openuseModal Hook
import { useModal } from "@hirotoshioi/hiraku-base-ui";
function MyComponent() {
const modal = useModal(confirmDialog);
return (
<>
<button onClick={() => modal.open({ title: "Hello", message: "World" })}>
Open
</button>
<p>isOpen: {modal.isOpen}</p>
<p>result: {JSON.stringify(modal.data)}</p>
<p>role: {modal.role}</p>
</>
);
}Global Controller
import { modalController } from "@hirotoshioi/hiraku-base-ui";
modalController.closeAll() // Close all open modals
modalController.getCount() // Get count of open modals
modalController.isOpen() // Check if any modal is open
modalController.getTop() // Get the topmost modal