Skip to content

Dialogues

marek edited this page Apr 5, 2026 · 1 revision

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.

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.

How to use it?

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;
}
obrazek

DialogueProvider

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.

How to use it?

import { useDialogue } from "src/ui/services/DialogueProvider";
const { showDialogue, closeDialogue } = useDialogue();

showDialogue(options: DialogueProps)

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 to undefined.
  • 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 the Promise directly 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();
    }
};

closeDialogue(id: number)

There are technically two ways dialogues are closed in this system. It is important to understand the distinction:

  1. 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) or close("new-name")) is what resolves the Promise you are awaiting.

  2. 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 DialogueProvider itself (for instance, to wire up the "Escape" key listener or the background backdrop click).

Best practices

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?"
                        />
                    ),
                });

Clone this wiki locally