Skip to main content

ui/components/
disclosure.rs

1use std::sync::Arc;
2
3use gpui::{ClickEvent, CursorStyle, SharedString};
4
5use crate::prelude::*;
6
7#[derive(IntoElement, RegisterComponent)]
8pub struct Disclosure {
9    id: ElementId,
10    is_open: bool,
11    selected: bool,
12    disabled: bool,
13    on_toggle_expanded: Option<Arc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
14    cursor_style: CursorStyle,
15    opened_icon: IconName,
16    closed_icon: IconName,
17    visible_on_hover: Option<SharedString>,
18}
19
20impl Disclosure {
21    pub fn new(id: impl Into<ElementId>, is_open: bool) -> Self {
22        Self {
23            id: id.into(),
24            is_open,
25            selected: false,
26            disabled: false,
27            on_toggle_expanded: None,
28            cursor_style: CursorStyle::PointingHand,
29            opened_icon: IconName::ChevronDown,
30            closed_icon: IconName::ChevronRight,
31            visible_on_hover: None,
32        }
33    }
34
35    pub fn on_toggle_expanded(
36        mut self,
37        handler: impl Into<Option<Arc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>>,
38    ) -> Self {
39        self.on_toggle_expanded = handler.into();
40        self
41    }
42
43    pub fn opened_icon(mut self, icon: IconName) -> Self {
44        self.opened_icon = icon;
45        self
46    }
47
48    pub fn closed_icon(mut self, icon: IconName) -> Self {
49        self.closed_icon = icon;
50        self
51    }
52
53    pub fn disabled(mut self, disabled: bool) -> Self {
54        self.disabled = disabled;
55        self
56    }
57}
58
59impl Toggleable for Disclosure {
60    fn toggle_state(mut self, selected: bool) -> Self {
61        self.selected = selected;
62        self
63    }
64}
65
66impl Clickable for Disclosure {
67    fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self {
68        self.on_toggle_expanded = Some(Arc::new(handler));
69        self
70    }
71
72    fn cursor_style(mut self, cursor_style: gpui::CursorStyle) -> Self {
73        self.cursor_style = cursor_style;
74        self
75    }
76}
77
78impl VisibleOnHover for Disclosure {
79    fn visible_on_hover(mut self, group_name: impl Into<SharedString>) -> Self {
80        self.visible_on_hover = Some(group_name.into());
81        self
82    }
83}
84
85impl RenderOnce for Disclosure {
86    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
87        IconButton::new(
88            self.id,
89            match self.is_open {
90                true => self.opened_icon,
91                false => self.closed_icon,
92            },
93        )
94        .icon_color(Color::Muted)
95        .icon_size(IconSize::Small)
96        .disabled(self.disabled)
97        .toggle_state(self.selected)
98        .when_some(self.visible_on_hover.clone(), |this, group_name| {
99            this.visible_on_hover(group_name)
100        })
101        .when_some(self.on_toggle_expanded, move |this, on_toggle| {
102            this.on_click(move |event, window, cx| on_toggle(event, window, cx))
103        })
104    }
105}
106
107impl Component for Disclosure {
108    fn scope() -> ComponentScope {
109        ComponentScope::Input
110    }
111
112    fn description() -> Option<&'static str> {
113        Some(
114            "An interactive element used to show or hide content, typically used in expandable sections or tree-like structures.",
115        )
116    }
117
118    fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
119        Some(
120            v_flex()
121                .gap_6()
122                .children(vec![
123                    example_group_with_title(
124                        "Disclosure States",
125                        vec![
126                            single_example(
127                                "Closed",
128                                Disclosure::new("closed", false).into_any_element(),
129                            ),
130                            single_example(
131                                "Open",
132                                Disclosure::new("open", true).into_any_element(),
133                            ),
134                        ],
135                    ),
136                    example_group_with_title(
137                        "Interactive Example",
138                        vec![single_example(
139                            "Toggleable",
140                            v_flex()
141                                .gap_2()
142                                .child(Disclosure::new("interactive", false).into_any_element())
143                                .child(Label::new("Click to toggle"))
144                                .into_any_element(),
145                        )],
146                    ),
147                ])
148                .into_any_element(),
149        )
150    }
151}