Skip to main content

23_modal/
23_modal.rs

1//! Example 23: Modal Dialogs
2//!
3//! Demonstrates modal overlay dialogs including confirm dialogs,
4//! alert dialogs, and custom modal content.
5//!
6//! Run with: `cargo run -p telex-tui --example 23_modal`
7
8use crossterm::event::KeyCode;
9use telex::prelude::*;
10use telex::Color;
11
12telex::require_api!(0, 2);
13
14fn main() {
15    telex::run_with_theme(App, telex::theme::Theme::nord()).unwrap();
16}
17
18struct App;
19
20impl Component for App {
21    fn render(&self, cx: Scope) -> View {
22        let show_help = state!(cx, || false);
23
24        // F1 toggles help
25        cx.use_command(
26            KeyBinding::key(KeyCode::F(1)),
27            with!(show_help => move || show_help.update(|v| *v = !*v)),
28        );
29
30        // Modal visibility states
31        let show_confirm = state!(cx, || false);
32        let show_alert = state!(cx, || false);
33        let show_custom = state!(cx, || false);
34
35        // App state that modals can modify
36        let deleted_count = state!(cx, || 0);
37        let custom_input = state!(cx, String::new);
38        let last_action = state!(cx, || "No action yet".to_string());
39
40        // Handlers
41        let open_confirm = with!(show_confirm => move || {
42            show_confirm.set(true);
43        });
44
45        let open_alert = with!(show_alert => move || {
46            show_alert.set(true);
47        });
48
49        let open_custom = with!(show_custom => move || {
50            show_custom.set(true);
51        });
52
53        let on_confirm_yes = with!(show_confirm, deleted_count, last_action => move || {
54            deleted_count.set(deleted_count.get() + 1);
55            last_action.set(format!("Deleted item #{}", deleted_count.get()));
56            show_confirm.set(false);
57        });
58
59        let on_confirm_no = with!(show_confirm, last_action => move || {
60            last_action.set("Cancelled delete".to_string());
61            show_confirm.set(false);
62        });
63
64        let on_alert_dismiss = with!(show_alert, last_action => move || {
65            last_action.set("Dismissed alert".to_string());
66            show_alert.set(false);
67        });
68
69        let on_custom_save = with!(show_custom, custom_input, last_action => move || {
70            let value = custom_input.get();
71            if !value.is_empty() {
72                last_action.set(format!("Saved: {}", value));
73                custom_input.set(String::new());
74            }
75            show_custom.set(false);
76        });
77
78        let on_custom_dismiss = with!(show_custom => move || {
79            show_custom.set(false);
80        });
81
82        View::vstack()
83            .spacing(1)
84            .child(
85                // Header
86                View::boxed()
87                    .border(true)
88                    .padding(1)
89                    .child(
90                        View::vstack()
91                            .child(View::styled_text("Modal Dialogs Demo").bold().build())
92                            .child(
93                                View::styled_text("Click buttons to open different modal types")
94                                    .dim()
95                                    .build(),
96                            )
97                            .build(),
98                    )
99                    .build(),
100            )
101            .child(
102                // Main content
103                View::boxed()
104                    .flex(1)
105                    .border(true)
106                    .padding(1)
107                    .child(
108                        View::vstack()
109                            .spacing(1)
110                            .child(View::text("Modal Types:"))
111                            .child(
112                                View::hstack()
113                                    .spacing(2)
114                                    .child(
115                                        View::button()
116                                            .label("Confirm Dialog")
117                                            .on_press(open_confirm)
118                                            .build(),
119                                    )
120                                    .child(
121                                        View::button()
122                                            .label("Alert Dialog")
123                                            .on_press(open_alert)
124                                            .build(),
125                                    )
126                                    .child(
127                                        View::button()
128                                            .label("Custom Modal")
129                                            .on_press(open_custom)
130                                            .build(),
131                                    )
132                                    .build(),
133                            )
134                            .child(View::spacer())
135                            .child(
136                                View::styled_text(format!(
137                                    "Deleted items: {}",
138                                    deleted_count.get()
139                                ))
140                                .color(Color::Yellow)
141                                .build(),
142                            )
143                            .child(
144                                View::styled_text(format!("Last action: {}", last_action.get()))
145                                    .dim()
146                                    .build(),
147                            )
148                            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
149                            .build(),
150                    )
151                    .build(),
152            )
153            // Help modal
154            .child(
155                View::modal()
156                    .visible(show_help.get())
157                    .title("Example 23: Modal")
158                    .on_dismiss(with!(show_help => move || show_help.set(false)))
159                    .child(
160                        View::vstack()
161                            .child(View::styled_text("What you're seeing").bold().build())
162                            .child(View::text("• Confirm, alert, and custom modals"))
163                            .child(View::text("• Modal focus containment"))
164                            .child(View::text("• Escape to dismiss"))
165                            .child(View::gap(1))
166                            .child(View::styled_text("Key concepts").bold().build())
167                            .child(View::text("• View::modal() creates overlay"))
168                            .child(View::text("• .visible() controls show/hide"))
169                            .child(View::text("• .on_dismiss() handles Escape"))
170                            .child(View::text("• Focus trapped in open modal"))
171                            .child(View::gap(1))
172                            .child(View::styled_text("Try this").bold().build())
173                            .child(View::text("• Open confirm, click Yes/No"))
174                            .child(View::text("• Custom modal has text input"))
175                            .child(View::text("• Press Escape to close modals"))
176                            .child(View::gap(1))
177                            .child(View::styled_text("Next up").bold().build())
178                            .child(View::text("→ 24_async_data: async loading"))
179                            .child(View::gap(1))
180                            .child(View::styled_text("Press Escape to close").dim().build())
181                            .build(),
182                    )
183                    .build(),
184            )
185            // Confirm dialog modal
186            .child(
187                View::modal()
188                    .visible(show_confirm.get())
189                    .title("Confirm Delete")
190                    .on_dismiss(on_confirm_no.clone())
191                    .width(40)
192                    .height(30)
193                    .child(
194                        View::vstack()
195                            .spacing(1)
196                            .child(View::text("Are you sure you want to delete this item?"))
197                            .child(View::text("This action cannot be undone."))
198                            .child(View::spacer())
199                            .child(
200                                View::hstack()
201                                    .spacing(2)
202                                    .child(
203                                        View::button()
204                                            .label("Yes, Delete")
205                                            .on_press(on_confirm_yes)
206                                            .build(),
207                                    )
208                                    .child(
209                                        View::button()
210                                            .label("Cancel")
211                                            .on_press(on_confirm_no)
212                                            .build(),
213                                    )
214                                    .build(),
215                            )
216                            .build(),
217                    )
218                    .build(),
219            )
220            // Alert dialog modal
221            .child(
222                View::modal()
223                    .visible(show_alert.get())
224                    .title("Alert")
225                    .on_dismiss(on_alert_dismiss.clone())
226                    .width(50)
227                    .height(25)
228                    .child(
229                        View::vstack()
230                            .spacing(1)
231                            .child(
232                                View::styled_text("Operation completed successfully!")
233                                    .color(Color::Green)
234                                    .build(),
235                            )
236                            .child(View::text("Your changes have been saved."))
237                            .child(View::spacer())
238                            .child(
239                                View::button()
240                                    .label("OK")
241                                    .on_press(on_alert_dismiss)
242                                    .build(),
243                            )
244                            .build(),
245                    )
246                    .build(),
247            )
248            // Custom modal with input
249            .child(
250                View::modal()
251                    .visible(show_custom.get())
252                    .title("Enter Details")
253                    .on_dismiss(on_custom_dismiss.clone())
254                    .width(50)
255                    .height(40)
256                    .child(
257                        View::vstack()
258                            .spacing(1)
259                            .child(View::text("Enter a value:"))
260                            .child(
261                                View::text_input()
262                                    .value(custom_input.get())
263                                    .placeholder("Type something...")
264                                    .on_change(with!(custom_input => move |v: String| {
265                                        custom_input.set(v);
266                                    }))
267                                    .build(),
268                            )
269                            .child(View::spacer())
270                            .child(
271                                View::hstack()
272                                    .spacing(2)
273                                    .child(
274                                        View::button()
275                                            .label("Save")
276                                            .on_press(on_custom_save)
277                                            .build(),
278                                    )
279                                    .child(
280                                        View::button()
281                                            .label("Cancel")
282                                            .on_press(on_custom_dismiss)
283                                            .build(),
284                                    )
285                                    .build(),
286                            )
287                            .build(),
288                    )
289                    .build(),
290            )
291            .build()
292    }
293}