Skip to main content

ui/components/
container.rs

1use gpui::AnyElement;
2use smallvec::SmallVec;
3
4use crate::prelude::*;
5
6/// A fixed max-width centering wrapper, matching Tailwind's `.container`
7/// pattern.
8///
9/// Centering approach: GPUI's `Styled` trait generates `max_w()` and
10/// `mx_auto()` from `margin_style_methods!` (see `gpui_macros::styles`,
11/// `margin_box_style_prefixes` has `auto_allowed: true` for the `mx` prefix),
12/// giving a true CSS-like auto-margin centering primitive — no
13/// `justify_center()` fallback is needed here.
14#[derive(IntoElement, RegisterComponent)]
15pub struct Container {
16    max_width: Pixels,
17    children: SmallVec<[AnyElement; 2]>,
18}
19
20impl Container {
21    pub fn new() -> Self {
22        Self {
23            max_width: px(1024.),
24            children: SmallVec::new(),
25        }
26    }
27
28    /// Sets the max width of the container (defaults to `1024px`).
29    pub fn max_width(mut self, max_width: Pixels) -> Self {
30        self.max_width = max_width;
31        self
32    }
33}
34
35impl Default for Container {
36    fn default() -> Self {
37        Self::new()
38    }
39}
40
41impl ParentElement for Container {
42    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
43        self.children.extend(elements);
44    }
45}
46
47impl RenderOnce for Container {
48    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
49        div()
50            .w_full()
51            .max_w(self.max_width)
52            .mx_auto()
53            .px_6()
54            .children(self.children)
55    }
56}
57
58impl Component for Container {
59    fn scope() -> ComponentScope {
60        ComponentScope::Layout
61    }
62
63    fn description() -> Option<&'static str> {
64        Some("A fixed max-width wrapper that centers its content horizontally.")
65    }
66
67    fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
68        let boxed = |label: &'static str| {
69            div()
70                .bg(semantic::surface(cx))
71                .border_1()
72                .border_color(semantic::border(cx))
73                .rounded_md()
74                .py_2()
75                .child(Label::new(label))
76        };
77        let frame = |content: AnyElement| {
78            div()
79                .border_1()
80                .border_dashed()
81                .border_color(semantic::border_muted(cx))
82                .child(content)
83        };
84
85        Some(
86            v_flex()
87                .gap_4()
88                .w(px(700.))
89                .child(frame(
90                    Container::new()
91                        .max_width(px(400.))
92                        .child(boxed("max_width(400px) — centered"))
93                        .into_any_element(),
94                ))
95                .child(frame(
96                    Container::new()
97                        .max_width(px(240.))
98                        .child(boxed("max_width(240px) — narrower"))
99                        .into_any_element(),
100                ))
101                .into_any_element(),
102        )
103    }
104}