gpui-component/docs/docs/components/modal.md
Jason Lee 34ef0ff6d0
chore: Rename Dropdown to Select, TextInput to Input. (#1449)
## Break Change

- The `Dropdown` has been renamed to `Select`.
- The `TextInput` has been renamed to `Input`.

| Before | After |
| --- | --- |
| TextInput | Input |
| Dropdown | Select |
| DropdownItem | SelectItem |
| DropdownItemGroup | SelectGroup |
| DropdownState | SelectState |
| DropdownEvent | SelectEvent |
| DropdownDelegate | SelectDelegate |
| dropdown::ListEvent | ~~Removed~~ |

- Renamed `PopupMenuExt` to `DropdownMenu`.
- Removed pub mod `menu::popup_menu`, `menu::context_menu`, they are
moved into `menu` mod.
- Renamed `popup_menu` method to `dropdown_menu` for `DropdownMenu`,
`Panel`, `PanelView` and `DropdownButton`.

| Before | After |
| --- | --- |
| popup_menu::PopupMenuExt | menu::DropdownMenu |
| popup_menu | dropdown_menu |
| popup_menu_with_anchor | dropdown_menu_with_anchor |
2025-10-28 21:22:31 +08:00

8.7 KiB

title description
Modal A modal dialog for displaying content in a layer above the app.

Modal

Modal component for creating dialogs, confirmations, and alerts. Supports overlay, keyboard shortcuts, and various customizations.

Import

use gpui_component::modal::ModalButtonProps;
use gpui_component::ContextModal;

Usage

Setup application root view for display of modals

You need to set up your application's root view to render the modal layer. This is typically done in your main application struct's render method.

The Root::render_modal_layer function handles rendering any active modals on top of your app content.

use gpui_component::TitleBar;

struct MyApp {
    view: AnyView,
}

impl Render for MyApp {
    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        let modal_layer = Root::render_modal_layer(window, cx);

        div()
            .size_full()
            .child(
                v_flex()
                    .size_full()
                    .child(TitleBar::new())
                    .child(div().flex_1().overflow_hidden().child(self.view.clone())),
            )
            // Render the modal layer on top of the app content
            .children(modal_layer)
    }
}

Basic Modal

window.open_modal(cx, |modal, _, _| {
    modal
        .title("Welcome")
        .child("This is a modal dialog.")
})

Form Modal

let input = cx.new(|cx| InputState::new(window, cx));

window.open_modal(cx, |modal, _, _| {
    modal
        .title("User Information")
        .child(
            v_flex()
                .gap_3()
                .child("Please enter your details:")
                .child(Input::new(&input))
        )
        .footer(|_, _, _, _| {
            vec![
                Button::new("ok")
                    .primary()
                    .label("Submit")
                    .on_click(|_, window, cx| {
                        window.close_modal(cx);
                    }),
                Button::new("cancel")
                    .label("Cancel")
                    .on_click(|_, window, cx| {
                        window.close_modal(cx);
                    }),
            ]
        })
})

Confirm Modal

window.open_modal(cx, |modal, _, _| {
    modal
        .confirm()
        .child("Are you sure you want to delete this item?")
        .on_ok(|_, window, cx| {
            window.push_notification("Item deleted", cx);
            true // Return true to close modal
        })
        .on_cancel(|_, window, cx| {
            window.push_notification("Cancelled", cx);
            true
        })
})

Alert Modal

window.open_modal(cx, |modal, _, _| {
    modal
        .confirm()
        .alert()
        .child("Operation completed successfully!")
        .on_close(|_, window, cx| {
            window.push_notification("Alert closed", cx);
        })
})

Custom Button Labels

use gpui_component::button::ButtonVariant;

window.open_modal(cx, |modal, _, _| {
    modal
        .confirm()
        .child("Update available. Restart now?")
        .button_props(
            ModalButtonProps::default()
                .cancel_text("Later")
                .cancel_variant(ButtonVariant::Secondary)
                .ok_text("Restart Now")
                .ok_variant(ButtonVariant::Danger)
        )
        .on_ok(|_, window, cx| {
            window.push_notification("Restarting...", cx);
            true
        })
})

Modal with Icon

window.open_modal(cx, |modal, _, cx| {
    modal
        .confirm()
        .child(
            h_flex()
                .gap_3()
                .child(Icon::new(IconName::TriangleAlert)
                    .size_6()
                    .text_color(cx.theme().warning))
                .child("This action cannot be undone.")
        )
})

Scrollable Modal

window.open_modal(cx, |modal, window, cx| {
    modal
        .h(px(450.))
        .title("Long Content")
        .child(TextView::markdown("content", long_markdown_text, window, cx))
})

Modal Options

window.open_modal(cx, |modal, _, _| {
    modal
        .title("Custom Modal")
        .overlay(true)              // Show overlay (default: true)
        .overlay_closable(true)     // Click overlay to close (default: true)
        .keyboard(true)             // ESC to close (default: true)
        .show_close(true)           // Show close button (default: true)
        .child("Modal content")
})

Nested Modals

window.open_modal(cx, |modal, _, _| {
    modal
        .title("First Modal")
        .child("This is the first modal")
        .footer(|_, _, _, _| {
            vec![
                Button::new("open-another")
                    .label("Open Another Modal")
                    .on_click(|_, window, cx| {
                        window.open_modal(cx, |modal, _, _| {
                            modal
                                .title("Second Modal")
                                .child("This is nested")
                        });
                    }),
            ]
        })
})

Custom Styling

window.open_modal(cx, |modal, _, cx| {
    modal
        .rounded_lg()
        .bg(cx.theme().cyan)
        .text_color(cx.theme().info_foreground)
        .title("Custom Style")
        .child("Styled modal content")
})

Custom Padding

window.open_modal(cx, |modal, _, _| {
    modal
        .p_3()                      // Custom padding
        .title("Custom Padding")
        .child("Modal with custom spacing")
})

Close Modal Programmatically

// From inside modal
window.close_modal(cx);

// Close and perform action
Button::new("submit")
    .primary()
    .label("Submit")
    .on_click(|_, window, cx| {
        // Do something
        window.close_modal(cx);
    })

API Reference

Modal Builder

Method Description
title(str) Set modal title
child(el) Add content to modal body
footer(fn) Set footer with custom buttons
overlay(bool) Show/hide overlay (default: true)
overlay_closable(bool) Allow closing by clicking overlay
keyboard(bool) Allow closing with ESC key
show_close(bool) Show close button in header
confirm() Use confirm modal style
alert() Use alert modal style (single OK button)
button_props(props) Customize confirm/alert buttons
on_ok(fn) OK button callback (confirm/alert)
on_cancel(fn) Cancel button callback (confirm)
on_close(fn) Close callback (alert)
min_h(px) Set minimum height
h(px) Set fixed height
rounded_lg() Apply large border radius
p_*() Custom padding
bg() Custom background

ModalButtonProps

Method Description
ok_text(str) Text for OK button
ok_variant(variant) Style for OK button
cancel_text(str) Text for Cancel button
cancel_variant(variant) Style for Cancel button

Window Extensions

Method Description
open_modal(cx, fn) Open a modal dialog
close_modal(cx) Close current modal

Examples

Delete Confirmation

Button::new("delete")
    .danger()
    .label("Delete")
    .on_click(|_, window, cx| {
        window.open_modal(cx, |modal, _, _| {
            modal
                .confirm()
                .child("Are you sure you want to delete this item?")
                .on_ok(|_, window, cx| {
                    // Perform delete
                    window.push_notification("Deleted", cx);
                    true
                })
        });
    })

Success Alert

window.open_modal(cx, |modal, _, _| {
    modal
        .confirm()
        .alert()
        .child("Your changes have been saved successfully!")
        .on_close(|_, _, _| {
            // Optional close handler
        })
})