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