@isi-ui7/modal-manager
v0.2.1
Published
Modal stack manager for multi-level modal flows in banking applications.
Readme
@isi-ui7/modal-manager
Stack-based modal manager untuk React — buka/tutup modal berlapis tanpa boilerplate state per komponen.
Fitur
- Modal stack: beberapa modal bisa terbuka sekaligus (bertumpuk), penutupan LIFO
showPage(): bukaPageComponentsebagai modal danawaithasilnya (status + data)dismissible: modal bisa ditutup dengan Escape / klik backdrop (opsional)showFull: mode layar penuh tanpa max-width/heightinitialFocusSelector: tentukan elemen yang difokus saat modal terbuka- Tanpa dependensi Carbon — bebas dipakai di semua aplikasi React
Instalasi
pnpm add @isi-ui7/modal-manager react react-domPenggunaan
1. Setup Provider
// app/layout.tsx
import { ModalProvider } from "@isi-ui7/modal-manager";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<ModalProvider>{children}</ModalProvider>
</body>
</html>
);
}2. Buka modal dari komponen
"use client";
import { useModal } from "@isi-ui7/modal-manager";
export function MyPage() {
const { open, closeTop } = useModal();
const handleOpen = () => {
open(
<div>
<h2>Konfirmasi</h2>
<p>Yakin ingin menghapus?</p>
<button onClick={closeTop}>Tutup</button>
</div>,
{ dismissible: true }
);
};
return <button onClick={handleOpen}>Buka Modal</button>;
}3. Gunakan showPage (await hasil modal)
"use client";
import { showPage } from "@isi-ui7/modal-manager";
import { useModal } from "@isi-ui7/modal-manager";
import type { PageComponent } from "@isi-ui7/modal-manager";
const FormPage: PageComponent = ({ dataParam, onClose }) => (
<div>
<h2>Edit {(dataParam?.name as string) ?? ""}</h2>
<button onClick={() => onClose?.("ok", { saved: true })}>Simpan</button>
<button onClick={() => onClose?.("cancel")}>Batal</button>
</div>
);
export function MyList() {
const modal = useModal();
const handleEdit = async (row: { name: string }) => {
const result = await showPage<{ saved: boolean }>(modal, FormPage, {
dataParam: { name: row.name },
});
if (result.status === "ok" && result.data?.saved) {
console.log("tersimpan");
}
};
return <button onClick={() => handleEdit({ name: "Rekening A" })}>Edit</button>;
}Props
ModalProvider
| Nama | Tipe | Wajib | Deskripsi |
| --- | --- | --- | --- |
| children | ReactNode | ya | Konten aplikasi |
useModal() — returns ModalContextType
| Method | Tipe | Deskripsi |
| --- | --- | --- |
| open | (content, options?) => string | Buka modal baru, kembalikan ID |
| closeTop | () => void | Tutup modal paling atas |
| closeById | (id: string) => void | Tutup modal berdasarkan ID |
| stack | ModalItem[] | Stack modal saat ini (bawah → atas) |
ModalOptions
| Nama | Tipe | Default | Deskripsi |
| --- | --- | --- | --- |
| id | string | auto | ID eksplisit (opsional) |
| dismissible | boolean | true | Tutup dengan Escape / klik backdrop |
| showFull | boolean | false | Mode layar penuh |
| initialFocusSelector | string | — | CSS selector elemen yang difokus saat buka |
| onClose | () => void | — | Callback dipanggil saat modal dihapus dari stack |
showPage<RT>(modal, Component, options?)
| Parameter | Tipe | Deskripsi |
| --- | --- | --- |
| modal | ModalContextType | Dari useModal() |
| Component | PageComponent | Komponen halaman yang dibuka sebagai modal |
| options.dataParam | Record<string, unknown> | Data yang diteruskan ke Component |
| options.modalOptions | ModalOptions | Opsi modal |
Returns: Promise<T_showPageResult<RT>> — { status, data? }
A11y
- Focus dikembalikan ke elemen pemicu saat modal ditutup
- Escape menutup modal jika
dismissible: true initialFocusSelectoruntuk kontrol focus trap eksplisit
Scripts
pnpm build # Build ke dist/
pnpm lint # ESLint
pnpm test # Vitest
pnpm typecheck # TypeScript checkCatatan
- Peer deps: React/ReactDOM
>=18 - Tanpa dependensi Carbon — bisa dipakai di aplikasi non-Carbon
- Komponen yang dibuka sebagai
PageComponentharus menggunakan"use client"di Next.js App Router
