Skip to main content

ui/components/button/
button_group.rs

1use gpui::AnyElement;
2use smallvec::SmallVec;
3
4use crate::prelude::*;
5
6/// A segmented group of connected buttons: no gap between children, a
7/// shared divider border between each pair, and rounding only on the
8/// outer corners (first/last child).
9///
10/// Children keep their own [`ButtonStyle`]/content untouched — rounding is
11/// achieved by clipping the group container (`overflow_hidden` + outer
12/// `rounded_md`), not by mutating each child's own style API.
13///
14/// # Examples
15///
16/// ```
17/// use ui::prelude::*;
18/// use ui::ButtonGroup;
19///
20/// ButtonGroup::new()
21///     .child(Button::new("left", "Left"))
22///     .child(Button::new("right", "Right"));
23/// ```
24#[derive(IntoElement, RegisterComponent)]
25pub struct ButtonGroup {
26    children: SmallVec<[AnyElement; 4]>,
27}
28
29impl ButtonGroup {
30    pub fn new() -> Self {
31        Self {
32            children: SmallVec::new(),
33        }
34    }
35}
36
37impl Default for ButtonGroup {
38    fn default() -> Self {
39        Self::new()
40    }
41}
42
43impl ParentElement for ButtonGroup {
44    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
45        self.children.extend(elements);
46    }
47}
48
49impl RenderOnce for ButtonGroup {
50    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
51        let border_color = semantic::border(cx);
52        let last_index = self.children.len().saturating_sub(1);
53
54        h_flex()
55            .items_stretch()
56            .rounded_md()
57            .overflow_hidden()
58            .border_1()
59            .border_color(border_color)
60            .children(self.children.into_iter().enumerate().map(|(index, child)| {
61                div()
62                    .when(index > 0 && index <= last_index, |this| {
63                        this.border_l_1().border_color(border_color)
64                    })
65                    .child(child)
66            }))
67    }
68}
69
70impl Component for ButtonGroup {
71    fn scope() -> ComponentScope {
72        ComponentScope::Input
73    }
74
75    fn description() -> Option<&'static str> {
76        Some("A segmented group of connected buttons sharing borders and outer rounding.")
77    }
78
79    fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
80        Some(
81            v_flex()
82                .gap_6()
83                .child(example_group_with_title(
84                    "Button Group",
85                    vec![
86                        single_example(
87                            "Default",
88                            ButtonGroup::new()
89                                .child(Button::new("bg_left", "Left"))
90                                .child(Button::new("bg_center", "Center"))
91                                .child(Button::new("bg_right", "Right"))
92                                .into_any_element(),
93                        ),
94                        single_example(
95                            "Primary",
96                            ButtonGroup::new()
97                                .child(Button::new("bg_p_left", "Day").primary())
98                                .child(Button::new("bg_p_center", "Week").primary())
99                                .child(Button::new("bg_p_right", "Month").primary())
100                                .into_any_element(),
101                        ),
102                    ],
103                ))
104                .into_any_element(),
105        )
106    }
107}