gpui_component/
collapsible.rs

1use gpui::{
2    AnyElement, App, IntoElement, ParentElement, RenderOnce, StyleRefinement, Styled, Window,
3};
4
5use crate::{v_flex, StyledExt};
6
7enum CollapsibleChild {
8    Element(AnyElement),
9    Content(AnyElement),
10}
11
12impl CollapsibleChild {
13    fn is_content(&self) -> bool {
14        matches!(self, CollapsibleChild::Content(_))
15    }
16}
17
18/// An interactive element which expands/collapses.
19#[derive(IntoElement)]
20pub struct Collapsible {
21    style: StyleRefinement,
22    children: Vec<CollapsibleChild>,
23    open: bool,
24}
25
26impl Collapsible {
27    /// Creates a new `Collapsible` instance.
28    pub fn new() -> Self {
29        Self {
30            style: StyleRefinement::default(),
31            open: false,
32            children: vec![],
33        }
34    }
35
36    /// Sets whether the collapsible is open. default is false.
37    pub fn open(mut self, open: bool) -> Self {
38        self.open = open;
39        self
40    }
41
42    /// Sets the content of the collapsible.
43    ///
44    /// If `open` is false, content will be hidden.
45    pub fn content(mut self, content: impl IntoElement) -> Self {
46        self.children
47            .push(CollapsibleChild::Content(content.into_any_element()));
48        self
49    }
50}
51
52impl Styled for Collapsible {
53    fn style(&mut self) -> &mut StyleRefinement {
54        &mut self.style
55    }
56}
57
58impl ParentElement for Collapsible {
59    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
60        self.children
61            .extend(elements.into_iter().map(|el| CollapsibleChild::Element(el)));
62    }
63}
64
65impl RenderOnce for Collapsible {
66    fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement {
67        v_flex()
68            .refine_style(&self.style)
69            .children(self.children.into_iter().filter_map(|child| {
70                if child.is_content() && !self.open {
71                    None
72                } else {
73                    match child {
74                        CollapsibleChild::Element(el) => Some(el),
75                        CollapsibleChild::Content(el) => Some(el),
76                    }
77                }
78            }))
79    }
80}