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