Skip to main content

guise/layout/
container.rs

1//! `Container` — a max-width centered column, matching Mantine's container
2//! size scale.
3//!
4//! Not to be confused with [`crate::flex::Container`], the Flutter-style
5//! pixel box (which is why `flex` is not glob-exported).
6//!
7//! ```ignore
8//! use guise::prelude::*;
9//!
10//! Container::new()
11//!     .size(Size::Sm)
12//!     .padding(Size::Md)
13//!     .child(Title::new("Article").order(2))
14//!     .child(Text::new("Readable line lengths on any window width."))
15//! ```
16
17use gpui::prelude::*;
18use gpui::{div, px, AnyElement, App, IntoElement, Window};
19
20use crate::theme::{theme, Size};
21
22/// Max content width (px) for each [`Size`] — Mantine's container scale.
23fn max_width(size: Size) -> f32 {
24    match size {
25        Size::Xs => 540.0,
26        Size::Sm => 720.0,
27        Size::Md => 960.0,
28        Size::Lg => 1140.0,
29        Size::Xl => 1320.0,
30    }
31}
32
33/// A centered column with a capped width. The Mantine `Container`.
34#[derive(IntoElement)]
35pub struct Container {
36    size: Size,
37    padding: Size,
38    children: Vec<AnyElement>,
39}
40
41impl Container {
42    pub fn new() -> Self {
43        Container {
44            size: Size::Md,
45            padding: Size::Md,
46            children: Vec::new(),
47        }
48    }
49
50    /// Max content width: `Xs..Xl` map to 540 / 720 / 960 / 1140 / 1320 px.
51    pub fn size(mut self, size: Size) -> Self {
52        self.size = size;
53        self
54    }
55
56    /// Horizontal padding inside the capped column (theme spacing scale).
57    pub fn padding(mut self, padding: Size) -> Self {
58        self.padding = padding;
59        self
60    }
61}
62
63impl Default for Container {
64    fn default() -> Self {
65        Container::new()
66    }
67}
68
69impl ParentElement for Container {
70    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
71        self.children.extend(elements);
72    }
73}
74
75impl RenderOnce for Container {
76    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
77        let pad = theme(cx).spacing(self.padding);
78        div().w_full().flex().flex_col().items_center().child(
79            div()
80                .w_full()
81                .max_w(px(max_width(self.size)))
82                .px(px(pad))
83                .flex()
84                .flex_col()
85                .children(self.children),
86        )
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn widths_match_mantine_scale() {
96        assert_eq!(max_width(Size::Xs), 540.0);
97        assert_eq!(max_width(Size::Sm), 720.0);
98        assert_eq!(max_width(Size::Md), 960.0);
99        assert_eq!(max_width(Size::Lg), 1140.0);
100        assert_eq!(max_width(Size::Xl), 1320.0);
101    }
102}