-
Notifications
You must be signed in to change notification settings - Fork 0
Dialogues
Dialogue is a specialised user interface component—typically a modal window or overlay—that interrupts the user flow to request immediate input, convey critical information, or require confirmation before proceeding. In the app, we can use a more low-level approach by leveraging the BaseDialgoue component itself, or better, and this is a recommended path, to use DialogueProvider. Firstly, about BaseDialogue.
The base component is located in src/ui/components/common/dialogue/BaseDialogue.tsx.
It can be used to implement any type of modal/non-modal dialogue window.
When you want to use a dialogue window on your page, initialize it as any other component:
<BaseDialogue
isOpen={isDialogueOpen}
onClose={() => setIsDialogueOpen(false)}
title="Info"
showCloseButton={true}
>
<div>
<p>This is your custom content inside the dialogue.</p>
<p>You can put any React components here!</p>
<p>This is your custom content inside the dialogue.</p>
<p>You can put any React components here!</p>
<p>This is your custom content inside the dialogue.</p>
<p>You can put any React components here!</p>
<p>This is your custom content inside the dialogue.</p>
<p>You can put any React components here!</p>
<p>This is your custom content inside the dialogue.</p>
<p>You can put any React components here!</p>
<p>This is your custom content inside the dialogue.</p>
<p>You can put any React components here!</p>
<p>This is your custom content inside the dialogue.</p>
<p>You can put any React components here!</p>
<div style={{ marginTop: "20px" }}>
<button onClick={() => setIsDialogueOpen(false)}>
Close
</button>
</div>
</div>
</BaseDialogue>
The difference is that if the dialogue is shown depends on isDialogueOpen state:
const [isDialogueOpen, setIsDialogueOpen] = useState(false);
How you set up the dialogue depends on setting up its props:
interface BaseDialogueProps {
isOpen: boolean;
onClose: () => void;
children: ReactNode;
title?: string;
showCloseButton?: boolean;
closeOnBackdropClick?: boolean;
closeOnEscapeEntered?: boolean;
width?: string;
maxWidth?: string;
}
This service is located in src/ui/services/DialogueProvider.tsx. It allows developers to trigger dialogues from anywhere in the component tree, wait for user input asynchronously, and handle complex typed returns without muddying component state. It supports dialogue stacking, meaning multiple dialogues can be opened on top of one another safely.
import { useDialogue } from "src/ui/services/DialogueProvider";
const { showDialogue, closeDialogue } = useDialogue();
The primary method for launching a dialogue. It returns a Promise<T | undefined> that resolves when the dialogue is closed.
- Generics (
<T>): You define what type of data you expect the dialogue to return (e.g., boolean, string, or a custom object). If the user closes the dialogue via the background overlay or the Escape key, it resolves toundefined. - Render Prop (
content): Instead of a static React node, the content property is a function. This function automatically injects the specific close method for that exact dialogue instance, allowing you to resolve thePromisedirectly from your buttons.
Example usage:
const handleDelete = async () => {
// Await the dialogue and expect a boolean return.
const confirmed = await showDialogue<boolean>({
title: "Confirm Deletion",
showCloseButton: false, // Hides the top-right 'X'.
// The 'close' function is injected here.
content: (close) => (
<div>
<p>Are you sure you want to delete this item?</p>
<div className="button-group">
<button onClick={() => close(true)}>Yes, Delete</button>
<button onClick={() => close(false)}>Cancel</button>
</div>
</div>
)
});
// Handle the result synchronously.
if (confirmed === true) {
performDeletion();
}
};
There are technically two ways dialogues are closed in this system. It is important to understand the distinction:
-
close(value?: T)This is the function provided directly inside the content:(close) => ...render prop. How it works: It is pre-bound to the specific ID of the dialogue it lives inside. When to use it: Always use this method inside your dialogue content. Passing a value into this function (e.g.,close(true)orclose("new-name")) is what resolves thePromiseyou are awaiting. -
closeDialogue(id: number, value?: any)This method is exposed globally on the Context object alongside showDialogue, but you will rarely use it directly in your components. How it works: It searches the internal state stack for a dialogue matching the provided id, resolves its associated Promise, and removes it from the array. When to use it: This is primarily used internally by the
DialogueProvideritself (for instance, to wire up the "Escape" key listener or the background backdrop click).
There are some common dialogue types you often need, such as confirmation dialogue. To make your job easier, you will find these templated dialogue contents in src/ui/components/common/dialogue. See for example ConfirmationDialogueContent.tsx, the usage is simple:
const confirmed = await showDialogue<boolean>({
title: "Confirmation",
showCloseButton: false,
content: (close) => (
<ConfirmationDialogueContent
close={close}
doYouReallyWantToQuestion="Do you really want to clear the
viewer?"
/>
),
});