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