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