1use gpui::{AnyElement, ScrollHandle};
2use smallvec::SmallVec;
3
4use crate::Tab;
5use crate::prelude::*;
6
7#[derive(IntoElement, RegisterComponent)]
8pub struct TabBar {
9 id: ElementId,
10 start_children: SmallVec<[AnyElement; 2]>,
11 children: SmallVec<[AnyElement; 2]>,
12 end_children: SmallVec<[AnyElement; 2]>,
13 scroll_handle: Option<ScrollHandle>,
14}
15
16impl TabBar {
17 pub fn new(id: impl Into<ElementId>) -> Self {
18 Self {
19 id: id.into(),
20 start_children: SmallVec::new(),
21 children: SmallVec::new(),
22 end_children: SmallVec::new(),
23 scroll_handle: None,
24 }
25 }
26
27 pub fn track_scroll(mut self, scroll_handle: &ScrollHandle) -> Self {
28 self.scroll_handle = Some(scroll_handle.clone());
29 self
30 }
31
32 pub fn start_children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
33 &mut self.start_children
34 }
35
36 pub fn start_child(mut self, start_child: impl IntoElement) -> Self
37 where
38 Self: Sized,
39 {
40 self.start_children_mut()
41 .push(start_child.into_element().into_any());
42 self
43 }
44
45 pub fn start_children(
46 mut self,
47 start_children: impl IntoIterator<Item = impl IntoElement>,
48 ) -> Self
49 where
50 Self: Sized,
51 {
52 self.start_children_mut().extend(
53 start_children
54 .into_iter()
55 .map(|child| child.into_any_element()),
56 );
57 self
58 }
59
60 pub fn end_children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
61 &mut self.end_children
62 }
63
64 pub fn end_child(mut self, end_child: impl IntoElement) -> Self
65 where
66 Self: Sized,
67 {
68 self.end_children_mut()
69 .push(end_child.into_element().into_any());
70 self
71 }
72
73 pub fn end_children(mut self, end_children: impl IntoIterator<Item = impl IntoElement>) -> Self
74 where
75 Self: Sized,
76 {
77 self.end_children_mut().extend(
78 end_children
79 .into_iter()
80 .map(|child| child.into_any_element()),
81 );
82 self
83 }
84}
85
86impl ParentElement for TabBar {
87 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
88 self.children.extend(elements)
89 }
90}
91
92impl RenderOnce for TabBar {
93 fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
94 div()
95 .id(self.id)
96 .group("tab_bar")
97 .flex()
98 .flex_none()
99 .w_full()
100 .h(Tab::container_height(cx))
101 .bg(cx.theme().colors().tab_bar_background)
102 .when(!self.start_children.is_empty(), |this| {
103 this.child(
104 h_flex()
105 .flex_none()
106 .gap(DynamicSpacing::Base04.rems(cx))
107 .px(DynamicSpacing::Base06.rems(cx))
108 .border_b_1()
109 .border_r_1()
110 .border_color(cx.theme().colors().border)
111 .children(self.start_children),
112 )
113 })
114 .child(
115 div()
116 .relative()
117 .flex_1()
118 .h_full()
119 .overflow_x_hidden()
120 .child(
121 div()
122 .absolute()
123 .top_0()
124 .left_0()
125 .size_full()
126 .border_b_1()
127 .border_color(cx.theme().colors().border),
128 )
129 .child(
130 h_flex()
131 .id("tabs")
132 .flex_grow()
133 .overflow_x_scroll()
134 .when_some(self.scroll_handle, |cx, scroll_handle| {
135 cx.track_scroll(&scroll_handle)
136 })
137 .children(self.children),
138 ),
139 )
140 .when(!self.end_children.is_empty(), |this| {
141 this.child(
142 h_flex()
143 .flex_none()
144 .gap(DynamicSpacing::Base04.rems(cx))
145 .px(DynamicSpacing::Base06.rems(cx))
146 .border_color(cx.theme().colors().border)
147 .border_b_1()
148 .children(self.end_children),
149 )
150 })
151 }
152}
153
154impl Component for TabBar {
155 fn scope() -> ComponentScope {
156 ComponentScope::Navigation
157 }
158
159 fn name() -> &'static str {
160 "TabBar"
161 }
162
163 fn description() -> Option<&'static str> {
164 Some("A horizontal bar containing tabs for navigation between different views or sections.")
165 }
166
167 fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
168 Some(
169 v_flex()
170 .gap_6()
171 .children(vec![
172 example_group_with_title(
173 "Basic Usage",
174 vec![
175 single_example(
176 "Empty TabBar",
177 TabBar::new("empty_tab_bar").into_any_element(),
178 ),
179 single_example(
180 "With Tabs",
181 TabBar::new("tab_bar_with_tabs")
182 .child(Tab::new("tab1"))
183 .child(Tab::new("tab2"))
184 .child(Tab::new("tab3"))
185 .into_any_element(),
186 ),
187 ],
188 ),
189 example_group_with_title(
190 "With Start and End Children",
191 vec![single_example(
192 "Full TabBar",
193 TabBar::new("full_tab_bar")
194 .start_child(Button::new("start_button", "Start"))
195 .child(Tab::new("tab1"))
196 .child(Tab::new("tab2"))
197 .child(Tab::new("tab3"))
198 .end_child(Button::new("end_button", "End"))
199 .into_any_element(),
200 )],
201 ),
202 ])
203 .into_any_element(),
204 )
205 }
206}