1use component::{example_group_with_title, single_example};
2use gpui::StatefulInteractiveElement as _;
3use gpui::{AnyElement, App, ClickEvent, IntoElement, RenderOnce, Window};
4use ui::prelude::*;
5
6#[derive(IntoElement, RegisterComponent)]
7pub struct CheckboxRow {
8 label: SharedString,
9 description: Option<SharedString>,
10 checked: bool,
11 on_click: Option<Box<dyn Fn(&mut Window, &mut App) + 'static>>,
12}
13
14impl CheckboxRow {
15 pub fn new(label: impl Into<SharedString>) -> Self {
16 Self {
17 label: label.into(),
18 description: None,
19 checked: false,
20 on_click: None,
21 }
22 }
23
24 pub fn description(mut self, description: impl Into<SharedString>) -> Self {
25 self.description = Some(description.into());
26 self
27 }
28
29 pub fn checked(mut self, checked: bool) -> Self {
30 self.checked = checked;
31 self
32 }
33
34 pub fn on_click(mut self, handler: impl Fn(&mut Window, &mut App) + 'static) -> Self {
35 self.on_click = Some(Box::new(handler));
36 self
37 }
38}
39
40impl RenderOnce for CheckboxRow {
41 fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
42 let checked = self.checked;
43 let on_click = self.on_click;
44
45 let checkbox = gpui::div()
46 .w_4()
47 .h_4()
48 .rounded_sm()
49 .border_1()
50 .border_color(cx.theme().colors().border)
51 .when(checked, |this| {
52 this.bg(cx.theme().colors().element_selected)
53 .border_color(cx.theme().colors().border_selected)
54 })
55 .hover(|this| this.bg(cx.theme().colors().element_hover))
56 .child(gpui::div().when(checked, |this| {
57 this.size_full()
58 .flex()
59 .items_center()
60 .justify_center()
61 .child(Icon::new(IconName::Check))
62 }));
63
64 let main_row = if let Some(on_click) = on_click {
65 gpui::div()
66 .id("checkbox-row")
67 .h_flex()
68 .gap_2()
69 .items_center()
70 .child(checkbox)
71 .child(Label::new(self.label))
72 .cursor_pointer()
73 .on_click(move |_event, window, cx| on_click(window, cx))
74 } else {
75 gpui::div()
76 .id("checkbox-row")
77 .h_flex()
78 .gap_2()
79 .items_center()
80 .child(checkbox)
81 .child(Label::new(self.label))
82 };
83
84 v_flex()
85 .px_5()
86 .py_1()
87 .gap_1()
88 .child(main_row)
89 .when_some(self.description, |this, desc| {
90 this.child(
91 gpui::div()
92 .ml_6()
93 .child(Label::new(desc).size(LabelSize::Small).color(Color::Muted)),
94 )
95 })
96 }
97}
98
99impl Component for CheckboxRow {
100 fn scope() -> ComponentScope {
101 ComponentScope::Layout
102 }
103
104 fn sort_name() -> &'static str {
105 "RowCheckbox"
106 }
107
108 fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
109 let examples = example_group_with_title(
110 "CheckboxRow Examples",
111 vec![
112 single_example(
113 "Unchecked",
114 CheckboxRow::new("Enable Vim Mode").into_any_element(),
115 ),
116 single_example(
117 "Checked",
118 CheckboxRow::new("Send Crash Reports")
119 .checked(true)
120 .into_any_element(),
121 ),
122 single_example(
123 "With Description",
124 CheckboxRow::new("Send Telemetry")
125 .description("Help improve Zed by sending anonymous usage data")
126 .checked(true)
127 .into_any_element(),
128 ),
129 ],
130 );
131
132 Some(v_flex().p_4().gap_4().child(examples).into_any_element())
133 }
134}