disclosure.rs

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