gpui_kit/navigation/
collapsible.rs1use std::rc::Rc;
16
17use gpui::{
18 AnyElement, App, IntoElement, RenderOnce, SharedString, Window, prelude::FluentBuilder,
19};
20use gpui_kit_theme::ControlSize;
21
22use crate::foundation::{Ident, Sizable};
23use crate::navigation::accordion::{Accordion, AccordionSection};
24
25const SECTION: &str = "header";
31
32type ToggleHandler = Rc<dyn Fn(bool, &mut Window, &mut App)>;
33
34#[derive(IntoElement)]
36pub struct Collapsible {
37 ident: Ident,
38 title: SharedString,
39 description: Option<SharedString>,
40 open: bool,
41 disabled: bool,
42 size: ControlSize,
43 body: Option<AnyElement>,
44 on_toggle: Option<ToggleHandler>,
45}
46
47impl std::fmt::Debug for Collapsible {
48 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 formatter
50 .debug_struct("Collapsible")
51 .field("ident", &self.ident)
52 .field("title", &self.title)
53 .field("open", &self.open)
54 .field("disabled", &self.disabled)
55 .field("has_body", &self.body.is_some())
56 .field("has_handler", &self.on_toggle.is_some())
57 .finish()
58 }
59}
60
61impl Collapsible {
62 pub fn new(ident: impl Into<Ident>, title: impl Into<SharedString>) -> Self {
63 Self {
64 ident: ident.into(),
65 title: title.into(),
66 description: None,
67 open: false,
68 disabled: false,
69 size: ControlSize::Md,
70 body: None,
71 on_toggle: None,
72 }
73 }
74
75 pub fn description(mut self, description: impl Into<SharedString>) -> Self {
77 self.description = Some(description.into());
78 self
79 }
80
81 pub fn open(mut self, open: bool) -> Self {
83 self.open = open;
84 self
85 }
86
87 pub fn disabled(mut self, disabled: bool) -> Self {
89 self.disabled = disabled;
90 self
91 }
92
93 pub fn body(mut self, body: impl IntoElement) -> Self {
94 self.body = Some(body.into_any_element());
95 self
96 }
97
98 pub fn on_toggle(mut self, handler: impl Fn(bool, &mut Window, &mut App) + 'static) -> Self {
100 self.on_toggle = Some(Rc::new(handler));
101 self
102 }
103
104 pub fn header_id(ident: &Ident) -> SharedString {
106 ident.child(SECTION).semantic_id()
107 }
108}
109
110impl Sizable for Collapsible {
111 fn control_size(mut self, size: ControlSize) -> Self {
112 self.size = size;
113 self
114 }
115}
116
117impl RenderOnce for Collapsible {
118 fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
119 let mut section =
120 AccordionSection::new(SECTION, self.title.clone()).disabled(self.disabled);
121 if let Some(description) = self.description.clone() {
122 section = section.description(description);
123 }
124 if let Some(body) = self.body {
125 section = section.body(body);
126 }
127
128 Accordion::new(self.ident.clone())
129 .control_size(self.size)
130 .section(section)
131 .when(self.open, |accordion| accordion.expanded_ids(&[SECTION]))
132 .when_some(self.on_toggle, |accordion, handler| {
133 accordion.on_toggle(move |_, next, window, cx| handler(next, window, cx))
134 })
135 }
136}