Skip to main content

liora_components/
space.rs

1use gpui::{
2    AnyElement, App, Component, DefiniteLength, IntoElement, RenderOnce, Window, prelude::*, px,
3};
4
5pub struct Space {
6    children: Vec<AnyElement>,
7    vertical: bool,
8    wrap: bool,
9    gap: Option<DefiniteLength>,
10    align: Option<SpaceAlign>,
11    grow: bool,
12}
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum SpaceAlign {
16    Start,
17    Center,
18    End,
19}
20
21impl Space {
22    pub fn new() -> Self {
23        Self {
24            children: Vec::new(),
25            vertical: false,
26            wrap: false,
27            gap: None,
28            align: None,
29            grow: false,
30        }
31    }
32
33    pub fn vertical(mut self) -> Self {
34        self.vertical = true;
35        self
36    }
37
38    pub fn gap(mut self, gap: impl Into<DefiniteLength>) -> Self {
39        self.gap = Some(gap.into());
40        self
41    }
42
43    pub fn gap_xs(self) -> Self {
44        self.gap(px(4.0))
45    }
46
47    pub fn gap_sm(self) -> Self {
48        self.gap(px(8.0))
49    }
50
51    pub fn gap_md(self) -> Self {
52        self.gap(px(12.0))
53    }
54
55    pub fn gap_lg(self) -> Self {
56        self.gap(px(16.0))
57    }
58
59    pub fn gap_xl(self) -> Self {
60        self.gap(px(24.0))
61    }
62
63    pub fn wrap(mut self) -> Self {
64        self.wrap = true;
65        self
66    }
67
68    pub fn align(mut self, align: SpaceAlign) -> Self {
69        self.align = Some(align);
70        self
71    }
72
73    pub fn align_start(self) -> Self {
74        self.align(SpaceAlign::Start)
75    }
76
77    pub fn align_center(self) -> Self {
78        self.align(SpaceAlign::Center)
79    }
80
81    pub fn align_end(self) -> Self {
82        self.align(SpaceAlign::End)
83    }
84
85    pub fn grow(mut self) -> Self {
86        self.grow = true;
87        self
88    }
89
90    pub fn child(mut self, child: impl IntoElement) -> Self {
91        self.children.push(child.into_any_element());
92        self
93    }
94
95    pub fn children(mut self, children: impl IntoIterator<Item = impl IntoElement>) -> Self {
96        self.children
97            .extend(children.into_iter().map(|c| c.into_any_element()));
98        self
99    }
100}
101
102impl RenderOnce for Space {
103    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
104        let mut div = gpui::div().flex();
105        if self.grow {
106            div = div.flex_1();
107        }
108
109        if self.vertical {
110            div = div.flex_col();
111        } else {
112            div = div.flex_row().items_center();
113        }
114
115        if self.wrap {
116            div = div.flex_wrap();
117        }
118
119        div = match self.align {
120            Some(SpaceAlign::Start) => div.items_start(),
121            Some(SpaceAlign::Center) => div.items_center(),
122            Some(SpaceAlign::End) => div.items_end(),
123            None => div,
124        };
125
126        if let Some(gap) = self.gap {
127            div = div.gap(gap);
128        } else {
129            div = div.gap_2(); // Default gap
130        }
131
132        div.children(self.children)
133    }
134}
135
136impl IntoElement for Space {
137    type Element = Component<Self>;
138    fn into_element(self) -> Self::Element {
139        Component::new(self)
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146
147    #[test]
148    fn space_wrap_builder_tracks_state() {
149        let space = Space::new().wrap();
150
151        assert!(space.wrap);
152    }
153
154    #[test]
155    fn space_align_center_tracks_cross_axis_alignment() {
156        let space = Space::new().vertical().align_center();
157
158        assert_eq!(space.align, Some(SpaceAlign::Center));
159    }
160
161    #[test]
162    fn space_grow_tracks_flex_growth() {
163        assert!(Space::new().grow().grow);
164    }
165}