@import url("@uxf/ui/dialog/dialog.css");
import {AppProps} from "next/app";
import {getModalStackRef, ModalProvider} from "@uxf/ui/modal";
function App(props: AppProps) {
return (
<UiContextProvider value={...}>
{props.children}
<ModalProvider ref={getModalStackRef()}/>
</UiContextProvider>
);
}
function Component(props: AppProps) {
return (
<div>
<Button
onClick={() =>
openModal({
children: (
<DialogPanel width="xs">Whatever content inside Modal</DialogPanel>
),
onClose: () => console.log("modal closed"),
})
}
>
Click to open modal - default
</Button>
</div>
);
}
function Component(props: AppProps) {
const [isOpen, setIsOpen] = useState(false);
return (
<div>
<Button onClick={() => setIsOpen(true)}>Click to open modal</Button>
<Modal onClose={() => setIsOpen(false)} isOpen={isOpen}>
<DialogPanel width="xs">Whatever content inside Modal</DialogPanel>
</Modal>
</div>
);
}
The modal system supports opening multiple modals simultaneously in different layers. Each layer has its own z-index and stacking order.
By default, two layers are available:
{
layers: {
main: { name: "main", zIndex: 100 },
confirm: { name: "confirm", zIndex: 1000 },
},
defaultLayer: "main"
}
import { openModal, closeModal, closeModalLayer, closeAllModals } from "@uxf/ui/modal";
// Open modal in default "main" layer
openModal({
children: <DialogPanel>Main content</DialogPanel>
});
// Open modal in specific layer (fully type-safe!)
openModal({
children: <DialogPanel>Confirmation dialog</DialogPanel>
}, {
layer: "confirm" // TypeScript autocompletes: "main" | "confirm"
});
// Close topmost modal (highest z-index)
closeModal();
// Close all modals in a specific layer
closeModalLayer("confirm");
// Close all modals across all layers
closeAllModals();
shouldReplace: false option to allow multiple)openModal)| Function | Description |
|---|---|
| closeModal() | Closes the topmost modal (highest z-index across all layers) |
| closeModalLayer(layer) | Closes all modals in the specified layer |
| closeAllModals() | Closes all modals across all layers |
import { closeModal, closeModalLayer, closeAllModals } from "@uxf/ui/modal";
closeModal(); // closes topmost
closeModalLayer("confirm"); // closes all in "confirm" layer
closeAllModals(); // closes everything
onClose callbackWhen opening a modal via openModal, you can pass an onClose callback that fires whenever the modal is closed (by backdrop click, ESC key, or programmatic close):
openModal({
children: <DialogPanel>Content</DialogPanel>,
onClose: () => console.log("modal closed"),
});
You can prevent the modal from being closed by backdrop click or ESC key:
openModal({
children: <DialogPanel>Content</DialogPanel>,
isBackdropCloseDisabled: true, // backdrop click won't close
isEscapeKeyCloseDisabled: true, // ESC key won't close
});
Modal component (controlled)When using the Modal component directly, pass onClose to handle close events:
<Modal onClose={() => setIsOpen(false)} isOpen={isOpen} isBackdropCloseDisabled={false}>
<DialogPanel>Content</DialogPanel>
</Modal>
You can configure custom layers for your project using ModalLayerConfigProvider:
import {
ModalProvider,
ModalLayerConfigProvider,
getModalStackRef,
ModalLayersConfiguration
} from "@uxf/ui/modal";
// Define custom layers
const customModalLayers: ModalLayersConfiguration = {
layers: {
base: { name: "base", zIndex: 1000 },
form: { name: "form", zIndex: 1100 },
tooltip: { name: "tooltip", zIndex: 1200 },
notification: { name: "notification", zIndex: 9000 },
},
defaultLayer: "base"
};
// Wrap your app with the config provider
function App({ children }: { children: React.ReactNode }) {
return (
<ModalLayerConfigProvider config={customModalLayers}>
<ModalProvider ref={getModalStackRef()} />
{children}
</ModalLayerConfigProvider>
);
}
// Use your custom layers
function MyComponent() {
return (
<Button onClick={() =>
openModal({
children: <DialogPanel>Form modal</DialogPanel>
}, {
layer: "form"
})
}>
Open Form
</Button>
);
}
You can extend the available layer names using TypeScript declaration merging:
Step 1: Extend the interface
// types/modal.d.ts (in your project)
import "@uxf/ui/modal";
declare module "@uxf/ui/modal/theme" {
interface ModalLayers {
// Add your custom layer names
form: true;
notification: true;
tooltip: true;
}
}
Step 2: Configure layers at runtime
// app/layout.tsx or _app.tsx
import { ModalLayerConfigProvider, ModalProvider, getModalStackRef } from "@uxf/ui/modal";
const customModalLayers = {
layers: {
// Default layers (keep or override)
main: { name: "main", zIndex: 1000 },
confirm: { name: "confirm", zIndex: 1010 },
// Your custom layers
form: { name: "form", zIndex: 1100 },
notification: { name: "notification", zIndex: 9000 },
tooltip: { name: "tooltip", zIndex: 9999 },
},
defaultLayer: "main"
};
<ModalLayerConfigProvider config={customModalLayers}>
<ModalProvider ref={getModalStackRef()} />
<App />
</ModalLayerConfigProvider>
Step 3: Use with full type safety
import { openModal } from "@uxf/ui/modal";
openModal({
children: <DialogPanel>Form content</DialogPanel>
}, {
layer: "form" // ✅ TypeScript autocompletes and validates!
// Available: "main" | "confirm" | "form" | "notification" | "tooltip"
});
The layered modal system is fully backwards compatible:
openModal() without options continues to workgetModalRef() is aliased to getModalStackRef()