disclosure.rs

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