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);
29 self
30 }
31
32 pub const 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 const 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_b_1()
147 .border_l_1()
148 .border_color(cx.theme().colors().border)
149 .children(self.end_children),
150 )
151 })
152 }
153}
154
155impl Component for TabBar {
156 fn scope() -> ComponentScope {
157 ComponentScope::Navigation
158 }
159
160 fn name() -> &'static str {
161 "TabBar"
162 }
163
164 fn description() -> Option<&'static str> {
165 Some("A horizontal bar containing tabs for navigation between different views or sections.")
166 }
167
168 fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
169 Some(
170 v_flex()
171 .gap_6()
172 .children(vec![
173 example_group_with_title(
174 "Basic Usage",
175 vec![
176 single_example(
177 "Empty TabBar",
178 TabBar::new("empty_tab_bar").into_any_element(),
179 ),
180 single_example(
181 "With Tabs",
182 TabBar::new("tab_bar_with_tabs")
183 .child(Tab::new("tab1"))
184 .child(Tab::new("tab2"))
185 .child(Tab::new("tab3"))
186 .into_any_element(),
187 ),
188 ],
189 ),
190 example_group_with_title(
191 "With Start and End Children",
192 vec![single_example(
193 "Full TabBar",
194 TabBar::new("full_tab_bar")
195 .start_child(Button::new("start_button", "Start"))
196 .child(Tab::new("tab1"))
197 .child(Tab::new("tab2"))
198 .child(Tab::new("tab3"))
199 .end_child(Button::new("end_button", "End"))
200 .into_any_element(),
201 )],
202 ),
203 ])
204 .into_any_element(),
205 )
206 }
207}