liora_components/
space.rs1use 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(); }
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}