use gpui::{ App, AppContext, Context, Entity, FocusHandle, Focusable, InteractiveElement as _, IntoElement, ParentElement, Render, SharedString, Styled, Window, div, px, }; use gpui_component::{ ActiveTheme, Icon, IconName, WindowExt as _, button::{Button, ButtonVariant, ButtonVariants as _}, checkbox::Checkbox, date_picker::{DatePicker, DatePickerState}, dialog::DialogButtonProps, h_flex, input::{Input, InputState}, select::{Select, SelectState}, text::TextView, v_flex, }; use crate::{TestAction, section}; pub struct DialogStory { focus_handle: FocusHandle, selected_value: Option, input1: Entity, input2: Entity, date: Entity, select: Entity>>, dialog_overlay: bool, close_button: bool, keyboard: bool, overlay_closable: bool, } impl super::Story for DialogStory { fn title() -> &'static str { "Dialog" } fn description() -> &'static str { "A dialog dialog" } fn new_view(window: &mut Window, cx: &mut App) -> Entity { Self::view(window, cx) } } impl DialogStory { pub fn view(window: &mut Window, cx: &mut App) -> Entity { cx.new(|cx| Self::new(window, cx)) } fn new(window: &mut Window, cx: &mut Context) -> Self { let input1 = cx.new(|cx| InputState::new(window, cx).placeholder("Your Name")); let input2 = cx.new(|cx| { InputState::new(window, cx).placeholder("For test focus back on dialog close.") }); let date = cx.new(|cx| DatePickerState::new(window, cx)); let select = cx.new(|cx| { SelectState::new( vec![ "Option 1".to_string(), "Option 2".to_string(), "Option 3".to_string(), ], None, window, cx, ) }); Self { focus_handle: cx.focus_handle(), selected_value: None, input1, input2, date, select, dialog_overlay: true, close_button: true, keyboard: true, overlay_closable: true, } } fn show_dialog(&mut self, window: &mut Window, cx: &mut Context) { let overlay = self.dialog_overlay; let dialog_close_button = self.close_button; let overlay_closable = self.overlay_closable; let input1 = self.input1.clone(); let date = self.date.clone(); let select = self.select.clone(); let view = cx.entity().clone(); let keyboard = self.keyboard; window.open_dialog(cx, move |dialog, _, _| { dialog .title("Form Dialog") .overlay(overlay) .keyboard(keyboard) .close_button(dialog_close_button) .overlay_closable(overlay_closable) .child( v_flex() .gap_3() .child("This is a dialog dialog.") .child("You can put anything here.") .child(Input::new(&input1)) .child(Select::new(&select)) .child(DatePicker::new(&date).placeholder("Date of Birth")), ) .footer({ let view = view.clone(); let input1 = input1.clone(); let date = date.clone(); move |_, _, _, _cx| { vec![ Button::new("confirm").primary().label("Confirm").on_click({ let view = view.clone(); let input1 = input1.clone(); let date = date.clone(); move |_, window, cx| { window.close_dialog(cx); view.update(cx, |view, cx| { view.selected_value = Some( format!( "Hello, {}, date: {}", input1.read(cx).value(), date.read(cx).date() ) .into(), ) }); } }), Button::new("new-dialog") .label("Open Other Dialog") .on_click(move |_, window, cx| { window.open_dialog(cx, move |dialog, _, _| { dialog .title("Other Dialog") .child("This is another dialog.") .min_h(px(100.)) .overlay(overlay) .keyboard(keyboard) .close_button(dialog_close_button) .overlay_closable(overlay_closable) }); }), Button::new("cancel") .label("Cancel") .on_click(move |_, window, cx| { window.close_dialog(cx); }), ] } }) }); self.input1.focus_handle(cx).focus(window); } fn on_action_test_action( &mut self, _: &TestAction, window: &mut Window, cx: &mut Context, ) { window.push_notification("You have clicked the TestAction.", cx); } } impl Focusable for DialogStory { fn focus_handle(&self, _cx: &gpui::App) -> FocusHandle { self.focus_handle.clone() } } impl Render for DialogStory { fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { let dialog_overlay = self.dialog_overlay; let overlay_closable = self.overlay_closable; div() .id("dialog-story") .track_focus(&self.focus_handle) .on_action(cx.listener(Self::on_action_test_action)) .size_full() .child( v_flex() .gap_6() .child( h_flex() .items_center() .gap_3() .child( Checkbox::new("dialog-overlay") .label("Dialog Overlay") .checked(self.dialog_overlay) .on_click(cx.listener(|view, _, _, cx| { view.dialog_overlay = !view.dialog_overlay; cx.notify(); })), ) .child( Checkbox::new("overlay-closable") .label("Overlay Closable") .checked(self.overlay_closable) .on_click(cx.listener(|view, _, _, cx| { view.overlay_closable = !view.overlay_closable; cx.notify(); })), ) .child( Checkbox::new("dialog-show-close") .label("Model Close Button") .checked(self.close_button) .on_click(cx.listener(|view, _, _, cx| { view.close_button = !view.close_button; cx.notify(); })), ) .child( Checkbox::new("dialog-keyboard") .label("Keyboard") .checked(self.keyboard) .on_click(cx.listener(|view, _, _, cx| { view.keyboard = !view.keyboard; cx.notify(); })), ), ) .child( section("Normal Dialog").child( Button::new("show-dialog") .outline() .label("Open Dialog") .on_click( cx.listener(|this, _, window, cx| this.show_dialog(window, cx)), ), ), ) .child( section("Focus back test") .max_w_md() .child(Input::new(&self.input2)) .child( Button::new("test-action") .outline() .label("Test Action") .flex_shrink_0() .on_click(|_, window, cx| { window.dispatch_action(Box::new(TestAction), cx); }) .tooltip( "This button for test dispatch action, \ to make sure when Dialog close,\ \nthis still can handle the action.", ), ), ) .child( section("Confirm Dialog").child( Button::new("confirm-dialog0") .outline() .label("Open Confirm Dialog") .on_click(cx.listener(move |_, _, window, cx| { window.open_dialog(cx, move |dialog, _, _| { dialog .confirm() .overlay(dialog_overlay) .overlay_closable(overlay_closable) .child("Are you sure to submit?") .on_ok(|_, window, cx| { window .push_notification("You have pressed ok.", cx); true }) .on_cancel(|_, window, cx| { window.push_notification( "You have pressed cancel.", cx, ); true }) }); })), ), ) .child( section("Confirm Dialog with custom buttons").child( Button::new("confirm-dialog1") .outline() .label("Custom Buttons") .on_click(cx.listener(move |_, _, window, cx| { window.open_dialog(cx, move |dialog, _, cx| { dialog .rounded_lg() .confirm() .overlay(dialog_overlay) .overlay_closable(overlay_closable) .child( h_flex().gap_3() .child(Icon::new(IconName::TriangleAlert).size_6().text_color(cx.theme().warning)) .child("Update successful, we need to restart the application.") ) .button_props( DialogButtonProps::default() .cancel_text("Later") .cancel_variant(ButtonVariant::Secondary) .ok_text("Restart Now") .ok_variant(ButtonVariant::Danger), ) .on_ok(|_, window, cx| { window.push_notification( "You have pressed restart.", cx, ); true }) .on_cancel(|_, window, cx| { window.push_notification( "You have pressed later.", cx, ); true }) }); })), ), ) .child( section("Alert Dialog").child( Button::new("alert-dialog") .outline() .label("Alert") .on_click(cx.listener(move |_, _, window, cx| { window.open_dialog(cx, move |dialog, _, _| { dialog .confirm() .overlay(dialog_overlay) .overlay_closable(overlay_closable) .child("You are successfully logged in.") .alert() .on_close(|_, window, cx| { window .push_notification("You have pressed Ok.", cx); }) }); })), ), ) .child( section("Scrollable Dialog").child( Button::new("scrollable-dialog") .outline() .label("Scrollable Dialog") .on_click(cx.listener(move |_, _, window, cx| { window.open_dialog(cx, move |dialog, window, cx| { dialog .w(px(720.)) .h(px(600.)) .overlay(dialog_overlay) .overlay_closable(overlay_closable) .title("Dialog with scrollbar") .child(TextView::markdown( "markdown1", include_str!("../../../README.md"), window, cx )) }); })), ), ) .child( section("Custom Paddings").child( Button::new("custom-dialog-paddings") .outline() .label("Custom Paddings") .on_click(cx.listener(move |_, _, window, cx| { window.open_dialog(cx, move |dialog, _, _| { dialog .p_3() .title("Custom Dialog Title") .child("This is a custom dialog content, we can use paddings to control the layout and spacing within the dialog.") }); })), ), ) .child( section("Custom Style").child( Button::new("custom-dialog-style") .outline() .label("Custom Dialog Style") .on_click(cx.listener(move |_, _, window, cx| { window.open_dialog(cx, move |dialog, _, cx| { dialog .rounded_lg() .bg(cx.theme().cyan) .text_color(cx.theme().info_foreground) .title("Custom Dialog Title") .child("This is a custom dialog content.") }); })), ), ), ) } }