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}