ui/components/
action_panel.rs1use gpui::{AnyElement, FontWeight, IntoElement, ParentElement, Styled};
2use std::rc::Rc;
3
4use crate::prelude::*;
5
6#[derive(IntoElement, RegisterComponent)]
10pub struct ActionPanel {
11 title: Option<SharedString>,
12 description: Option<SharedString>,
13 fields: Vec<AnyElement>,
14 save_label: SharedString,
15 cancel_label: SharedString,
16 on_save: Option<Rc<dyn Fn(&mut Window, &mut App) + 'static>>,
17 on_cancel: Option<Rc<dyn Fn(&mut Window, &mut App) + 'static>>,
18}
19
20impl ActionPanel {
21 pub fn new() -> Self {
22 Self {
23 title: None,
24 description: None,
25 fields: Vec::new(),
26 save_label: "Save".into(),
27 cancel_label: "Cancel".into(),
28 on_save: None,
29 on_cancel: None,
30 }
31 }
32
33 pub fn title(mut self, title: impl Into<SharedString>) -> Self {
34 self.title = Some(title.into());
35 self
36 }
37
38 pub fn description(mut self, description: impl Into<SharedString>) -> Self {
39 self.description = Some(description.into());
40 self
41 }
42
43 pub fn field(mut self, field: impl IntoElement) -> Self {
45 self.fields.push(field.into_any_element());
46 self
47 }
48
49 pub fn save_label(mut self, label: impl Into<SharedString>) -> Self {
50 self.save_label = label.into();
51 self
52 }
53
54 pub fn cancel_label(mut self, label: impl Into<SharedString>) -> Self {
55 self.cancel_label = label.into();
56 self
57 }
58
59 pub fn on_save(mut self, handler: impl Fn(&mut Window, &mut App) + 'static) -> Self {
60 self.on_save = Some(Rc::new(handler));
61 self
62 }
63
64 pub fn on_cancel(mut self, handler: impl Fn(&mut Window, &mut App) + 'static) -> Self {
65 self.on_cancel = Some(Rc::new(handler));
66 self
67 }
68}
69
70impl Default for ActionPanel {
71 fn default() -> Self {
72 Self::new()
73 }
74}
75
76impl RenderOnce for ActionPanel {
77 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
78 let title = self.title;
79 let description = self.description;
80 let fields = self.fields;
81 let save_label = self.save_label;
82 let cancel_label = self.cancel_label;
83 let on_save = self.on_save;
84 let on_cancel = self.on_cancel;
85
86 let mut fieldset = v_flex().gap_4().p_4();
87 for field in fields {
88 fieldset = fieldset.child(field);
89 }
90
91 v_flex()
92 .w_full()
93 .rounded_lg()
94 .border_1()
95 .border_color(semantic::border(cx))
96 .bg(semantic::elevated_surface(cx))
97 .overflow_hidden()
98 .when(title.is_some() || description.is_some(), |this| {
99 this.child(
100 v_flex()
101 .gap_1()
102 .px_4()
103 .pt_4()
104 .when_some(title, |this, title| {
105 this.child(
106 Label::new(title)
107 .size(LabelSize::Default)
108 .weight(FontWeight::MEDIUM),
109 )
110 })
111 .when_some(description, |this, description| {
112 this.child(
113 Label::new(description)
114 .size(LabelSize::Small)
115 .color(Color::Muted),
116 )
117 }),
118 )
119 })
120 .child(fieldset)
121 .child(
122 h_flex()
123 .justify_end()
124 .gap_2()
125 .px_4()
126 .py_3()
127 .border_t_1()
128 .border_color(semantic::border(cx))
129 .when_some(on_cancel, |this, handler| {
130 this.child(
131 div()
140 .id("action-panel-cancel-wrap")
141 .debug_selector(|| "ACTION_PANEL-cancel".into())
142 .child(
143 Button::new("action-panel-cancel", cancel_label)
144 .on_click(move |_, window, cx| handler(window, cx)),
145 ),
146 )
147 })
148 .when_some(on_save, |this, handler| {
149 this.child(
150 div()
152 .id("action-panel-save-wrap")
153 .debug_selector(|| "ACTION_PANEL-save".into())
154 .child(
155 Button::new("action-panel-save", save_label)
156 .primary()
157 .on_click(move |_, window, cx| handler(window, cx)),
158 ),
159 )
160 }),
161 )
162 }
163}
164
165impl Component for ActionPanel {
166 fn scope() -> ComponentScope {
167 ComponentScope::Input
168 }
169
170 fn description() -> Option<&'static str> {
171 Some("A fieldset section with a footer of right-aligned Save/Cancel buttons.")
172 }
173
174 fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
175 Some(
176 ActionPanel::new()
177 .title("Profile")
178 .description("Update your personal details.")
179 .field(
180 FormField::new(Label::new("Ada Lovelace").color(Color::Default)).label("Name"),
181 )
182 .field(
183 FormField::new(Label::new("ada@example.com").color(Color::Default))
184 .label("Email"),
185 )
186 .on_save(|_, _| {})
187 .on_cancel(|_, _| {})
188 .into_any_element(),
189 )
190 }
191}