1use crate::component_prelude::*;
2use crate::prelude::*;
3use gpui::{AnyElement, IntoElement, ParentElement, StyleRefinement, Styled};
4use smallvec::SmallVec;
5
6/// Creates a new [ContentGroup].
7pub fn content_group() -> ContentGroup {
8 ContentGroup::new()
9}
10
11/// A [ContentGroup] that vertically stacks its children.
12///
13/// This is a convenience function that simply combines [`ContentGroup`] and [`v_flex`](crate::v_flex).
14pub fn v_container() -> ContentGroup {
15 content_group().v_flex()
16}
17
18/// Creates a new horizontal [ContentGroup].
19///
20/// This is a convenience function that simply combines [`ContentGroup`] and [`h_flex`](crate::h_flex).
21pub fn h_container() -> ContentGroup {
22 content_group().h_flex()
23}
24
25/// A flexible container component that can hold other elements.
26#[derive(IntoElement, Documented, RegisterComponent)]
27pub struct ContentGroup {
28 base: Div,
29 border: bool,
30 fill: bool,
31 children: SmallVec<[AnyElement; 2]>,
32}
33
34impl ContentGroup {
35 /// Creates a new [`ContentGroup`].
36 pub fn new() -> Self {
37 Self {
38 base: div(),
39 border: true,
40 fill: true,
41 children: SmallVec::new(),
42 }
43 }
44
45 /// Removes the border from the [`ContentGroup`].
46 pub const fn borderless(mut self) -> Self {
47 self.border = false;
48 self
49 }
50
51 /// Removes the background fill from the [`ContentGroup`].
52 pub const fn unfilled(mut self) -> Self {
53 self.fill = false;
54 self
55 }
56}
57
58impl ParentElement for ContentGroup {
59 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
60 self.children.extend(elements)
61 }
62}
63
64impl Styled for ContentGroup {
65 fn style(&mut self) -> &mut StyleRefinement {
66 self.base.style()
67 }
68}
69
70impl RenderOnce for ContentGroup {
71 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
72 // TODO:
73 // Baked in padding will make scrollable views inside of content boxes awkward.
74 //
75 // Do we make the padding optional, or do we push to use a different component?
76
77 self.base
78 .when(self.fill, |this| {
79 this.bg(cx.theme().colors().text.opacity(0.05))
80 })
81 .when(self.border, |this| {
82 this.border_1().border_color(cx.theme().colors().border)
83 })
84 .rounded_sm()
85 .children(self.children)
86 }
87}
88
89impl Component for ContentGroup {
90 fn scope() -> ComponentScope {
91 ComponentScope::Layout
92 }
93
94 fn description() -> Option<&'static str> {
95 Some(ContentGroup::DOCS)
96 }
97
98 fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
99 Some(
100 example_group(vec![
101 single_example(
102 "Default",
103 ContentGroup::new()
104 .flex_1()
105 .items_center()
106 .justify_center()
107 .h_48()
108 .child(Label::new("Default ContentGroup"))
109 .into_any_element(),
110 ).description("A contained style for laying out groups of content. Has a default background and border color."),
111 single_example(
112 "Without Border",
113 ContentGroup::new()
114 .flex_1()
115 .items_center()
116 .justify_center()
117 .h_48()
118 .borderless()
119 .child(Label::new("Borderless ContentGroup"))
120 .into_any_element(),
121 ),
122 single_example(
123 "Without Fill",
124 ContentGroup::new()
125 .flex_1()
126 .items_center()
127 .justify_center()
128 .h_48()
129 .unfilled()
130 .child(Label::new("Unfilled ContentGroup"))
131 .into_any_element(),
132 ),
133 ])
134 .into_any_element(),
135 )
136 }
137}