1use crate::prelude::*;
2use component::{ComponentPreview, example_group, single_example};
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, IntoComponent)]
27#[component(scope = "Layout")]
28pub struct ContentGroup {
29 base: Div,
30 border: bool,
31 fill: bool,
32 children: SmallVec<[AnyElement; 2]>,
33}
34
35impl ContentGroup {
36 /// Creates a new [`ContentGroup`].
37 pub fn new() -> Self {
38 Self {
39 base: div(),
40 border: true,
41 fill: true,
42 children: SmallVec::new(),
43 }
44 }
45
46 /// Removes the border from the [`ContentGroup`].
47 pub fn borderless(mut self) -> Self {
48 self.border = false;
49 self
50 }
51
52 /// Removes the background fill from the [`ContentGroup`].
53 pub fn unfilled(mut self) -> Self {
54 self.fill = false;
55 self
56 }
57}
58
59impl ParentElement for ContentGroup {
60 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
61 self.children.extend(elements)
62 }
63}
64
65impl Styled for ContentGroup {
66 fn style(&mut self) -> &mut StyleRefinement {
67 self.base.style()
68 }
69}
70
71impl RenderOnce for ContentGroup {
72 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
73 // TODO:
74 // Baked in padding will make scrollable views inside of content boxes awkward.
75 //
76 // Do we make the padding optional, or do we push to use a different component?
77
78 self.base
79 .when(self.fill, |this| {
80 this.bg(cx.theme().colors().text.opacity(0.05))
81 })
82 .when(self.border, |this| {
83 this.border_1().border_color(cx.theme().colors().border)
84 })
85 .rounded_sm()
86 .p_2()
87 .children(self.children)
88 }
89}
90
91// View this component preview using `workspace: open component-preview`
92impl ComponentPreview for ContentGroup {
93 fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
94 example_group(vec![
95 single_example(
96 "Default",
97 ContentGroup::new()
98 .flex_1()
99 .items_center()
100 .justify_center()
101 .h_48()
102 .child(Label::new("Default ContentBox"))
103 .into_any_element(),
104 )
105 .grow(),
106 single_example(
107 "Without Border",
108 ContentGroup::new()
109 .flex_1()
110 .items_center()
111 .justify_center()
112 .h_48()
113 .borderless()
114 .child(Label::new("Borderless ContentBox"))
115 .into_any_element(),
116 )
117 .grow(),
118 single_example(
119 "Without Fill",
120 ContentGroup::new()
121 .flex_1()
122 .items_center()
123 .justify_center()
124 .h_48()
125 .unfilled()
126 .child(Label::new("Unfilled ContentBox"))
127 .into_any_element(),
128 )
129 .grow(),
130 ])
131 .into_any_element()
132 }
133}