1use std::marker::PhantomData;
2
3use crate::prelude::*;
4use crate::{Icon, IconButton, Tab};
5
6#[derive(Element)]
7pub struct TabBar<S: 'static + Send + Sync + Clone> {
8 id: ElementId,
9 state_type: PhantomData<S>,
10 /// Backwards, Forwards
11 can_navigate: (bool, bool),
12 tabs: Vec<Tab<S>>,
13}
14
15impl<S: 'static + Send + Sync + Clone> TabBar<S> {
16 pub fn new(id: impl Into<ElementId>, tabs: Vec<Tab<S>>) -> Self {
17 Self {
18 id: id.into(),
19 state_type: PhantomData,
20 can_navigate: (false, false),
21 tabs,
22 }
23 }
24
25 pub fn can_navigate(mut self, can_navigate: (bool, bool)) -> Self {
26 self.can_navigate = can_navigate;
27 self
28 }
29
30 fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
31 let theme = theme(cx);
32
33 let (can_navigate_back, can_navigate_forward) = self.can_navigate;
34
35 div()
36 .id(self.id.clone())
37 .w_full()
38 .flex()
39 .bg(theme.tab_bar)
40 // Left Side
41 .child(
42 div()
43 .px_1()
44 .flex()
45 .flex_none()
46 .gap_2()
47 // Nav Buttons
48 .child(
49 div()
50 .flex()
51 .items_center()
52 .gap_px()
53 .child(
54 IconButton::new("arrow_left", Icon::ArrowLeft)
55 .state(InteractionState::Enabled.if_enabled(can_navigate_back)),
56 )
57 .child(
58 IconButton::new("arrow_right", Icon::ArrowRight).state(
59 InteractionState::Enabled.if_enabled(can_navigate_forward),
60 ),
61 ),
62 ),
63 )
64 .child(
65 div().w_0().flex_1().h_full().child(
66 div()
67 .id("tabs")
68 .flex()
69 .overflow_x_scroll()
70 .children(self.tabs.clone()),
71 ),
72 )
73 // Right Side
74 .child(
75 div()
76 .px_1()
77 .flex()
78 .flex_none()
79 .gap_2()
80 // Nav Buttons
81 .child(
82 div()
83 .flex()
84 .items_center()
85 .gap_px()
86 .child(IconButton::new("plus", Icon::Plus))
87 .child(IconButton::new("split", Icon::Split)),
88 ),
89 )
90 }
91}
92
93use gpui2::ElementId;
94#[cfg(feature = "stories")]
95pub use stories::*;
96
97#[cfg(feature = "stories")]
98mod stories {
99 use crate::Story;
100
101 use super::*;
102
103 #[derive(Element)]
104 pub struct TabBarStory<S: 'static + Send + Sync + Clone> {
105 state_type: PhantomData<S>,
106 }
107
108 impl<S: 'static + Send + Sync + Clone> TabBarStory<S> {
109 pub fn new() -> Self {
110 Self {
111 state_type: PhantomData,
112 }
113 }
114
115 fn render(
116 &mut self,
117 _view: &mut S,
118 cx: &mut ViewContext<S>,
119 ) -> impl Element<ViewState = S> {
120 Story::container(cx)
121 .child(Story::title_for::<_, TabBar<S>>(cx))
122 .child(Story::label(cx, "Default"))
123 .child(TabBar::new(
124 "tab-bar",
125 vec![
126 Tab::new(1)
127 .title("Cargo.toml".to_string())
128 .current(false)
129 .git_status(GitStatus::Modified),
130 Tab::new(2)
131 .title("Channels Panel".to_string())
132 .current(false),
133 Tab::new(3)
134 .title("channels_panel.rs".to_string())
135 .current(true)
136 .git_status(GitStatus::Modified),
137 Tab::new(4)
138 .title("workspace.rs".to_string())
139 .current(false)
140 .git_status(GitStatus::Modified),
141 Tab::new(5)
142 .title("icon_button.rs".to_string())
143 .current(false),
144 Tab::new(6)
145 .title("storybook.rs".to_string())
146 .current(false)
147 .git_status(GitStatus::Created),
148 Tab::new(7).title("theme.rs".to_string()).current(false),
149 Tab::new(8)
150 .title("theme_registry.rs".to_string())
151 .current(false),
152 Tab::new(9)
153 .title("styleable_helpers.rs".to_string())
154 .current(false),
155 ],
156 ))
157 }
158 }
159}