disclosure.rs

  1use std::sync::Arc;
  2
  3use gpui::{ClickEvent, CursorStyle, SharedString};
  4
  5use crate::{Color, IconButton, IconName, IconSize, 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        .disabled(self.disabled)
 96        .toggle_state(self.selected)
 97        .when_some(self.visible_on_hover.clone(), |this, group_name| {
 98            this.visible_on_hover(group_name)
 99        })
100        .when_some(self.on_toggle_expanded, move |this, on_toggle| {
101            this.on_click(move |event, window, cx| on_toggle(event, window, cx))
102        })
103    }
104}
105
106impl Component for Disclosure {
107    fn scope() -> ComponentScope {
108        ComponentScope::Input
109    }
110
111    fn description() -> Option<&'static str> {
112        Some(
113            "An interactive element used to show or hide content, typically used in expandable sections or tree-like structures.",
114        )
115    }
116
117    fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
118        Some(
119            v_flex()
120                .gap_6()
121                .children(vec![
122                    example_group_with_title(
123                        "Disclosure States",
124                        vec![
125                            single_example(
126                                "Closed",
127                                Disclosure::new("closed", false).into_any_element(),
128                            ),
129                            single_example(
130                                "Open",
131                                Disclosure::new("open", true).into_any_element(),
132                            ),
133                        ],
134                    ),
135                    example_group_with_title(
136                        "Interactive Example",
137                        vec![single_example(
138                            "Toggleable",
139                            v_flex()
140                                .gap_2()
141                                .child(Disclosure::new("interactive", false).into_any_element())
142                                .child(Label::new("Click to toggle"))
143                                .into_any_element(),
144                        )],
145                    ),
146                ])
147                .into_any_element(),
148        )
149    }
150}