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}