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 fn borderless(mut self) -> Self {
 47        self.border = false;
 48        self
 49    }
 50
 51    /// Removes the background fill from the [`ContentGroup`].
 52    pub 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}