From 00e8531898467df8b840e103ccfaf286a3ecf292 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sat, 7 Oct 2023 11:13:54 -0400 Subject: [PATCH] Add `TabBar` component --- crates/storybook2/src/stories/components.rs | 1 + .../src/stories/components/tab_bar.rs | 56 ++++++++++++ crates/storybook2/src/story_selector.rs | 2 + crates/ui2/src/components.rs | 2 + crates/ui2/src/components/tab_bar.rs | 85 +++++++++++++++++++ 5 files changed, 146 insertions(+) create mode 100644 crates/storybook2/src/stories/components/tab_bar.rs create mode 100644 crates/ui2/src/components/tab_bar.rs diff --git a/crates/storybook2/src/stories/components.rs b/crates/storybook2/src/stories/components.rs index 4a78644b713d371d1b6bdb2a2874409e9d960b60..056896b0f96581829c75d6a44cb3756d43b5b8d4 100644 --- a/crates/storybook2/src/stories/components.rs +++ b/crates/storybook2/src/stories/components.rs @@ -3,5 +3,6 @@ pub mod buffer; pub mod panel; pub mod project_panel; pub mod tab; +pub mod tab_bar; pub mod terminal; pub mod workspace; diff --git a/crates/storybook2/src/stories/components/tab_bar.rs b/crates/storybook2/src/stories/components/tab_bar.rs new file mode 100644 index 0000000000000000000000000000000000000000..d3461418b3edc21d53eccef32cee367ef0d73283 --- /dev/null +++ b/crates/storybook2/src/stories/components/tab_bar.rs @@ -0,0 +1,56 @@ +use std::marker::PhantomData; + +use ui::prelude::*; +use ui::{Tab, TabBar}; + +use crate::story::Story; + +#[derive(Element)] +pub struct TabBarStory { + state_type: PhantomData, +} + +impl TabBarStory { + pub fn new() -> Self { + Self { + state_type: PhantomData, + } + } + + fn render(&mut self, cx: &mut ViewContext) -> impl Element { + Story::container(cx) + .child(Story::title_for::<_, TabBar>(cx)) + .child(Story::label(cx, "Default")) + .child(TabBar::new(vec![ + Tab::new() + .title("Cargo.toml".to_string()) + .current(false) + .git_status(GitStatus::Modified), + Tab::new() + .title("Channels Panel".to_string()) + .current(false), + Tab::new() + .title("channels_panel.rs".to_string()) + .current(true) + .git_status(GitStatus::Modified), + Tab::new() + .title("workspace.rs".to_string()) + .current(false) + .git_status(GitStatus::Modified), + Tab::new() + .title("icon_button.rs".to_string()) + .current(false), + Tab::new() + .title("storybook.rs".to_string()) + .current(false) + .git_status(GitStatus::Created), + Tab::new().title("theme.rs".to_string()).current(false), + Tab::new() + .title("theme_registry.rs".to_string()) + .current(false), + Tab::new() + .title("styleable_helpers.rs".to_string()) + .current(false), + ])) + } +} diff --git a/crates/storybook2/src/story_selector.rs b/crates/storybook2/src/story_selector.rs index c66cdc20794dcc8956aa7deb31e216923cb630e3..8fcb1e66bf2666bb790d15a87430d315d20a4150 100644 --- a/crates/storybook2/src/story_selector.rs +++ b/crates/storybook2/src/story_selector.rs @@ -39,6 +39,7 @@ pub enum ComponentStory { Panel, ProjectPanel, Tab, + TabBar, Terminal, Workspace, } @@ -55,6 +56,7 @@ impl ComponentStory { Self::Panel => components::panel::PanelStory::new().into_any(), Self::ProjectPanel => components::project_panel::ProjectPanelStory::new().into_any(), Self::Tab => components::tab::TabStory::new().into_any(), + Self::TabBar => components::tab_bar::TabBarStory::new().into_any(), Self::Terminal => components::terminal::TerminalStory::new().into_any(), Self::Workspace => components::workspace::WorkspaceStory::new().into_any(), } diff --git a/crates/ui2/src/components.rs b/crates/ui2/src/components.rs index 468d4dca99c5f89b4c8c269b24c14b116c6bf143..15cb304b18e6ac97508c1ca5c139b4ad0f0cb7c6 100644 --- a/crates/ui2/src/components.rs +++ b/crates/ui2/src/components.rs @@ -7,6 +7,7 @@ mod panes; mod project_panel; mod status_bar; mod tab; +mod tab_bar; mod terminal; mod workspace; @@ -19,5 +20,6 @@ pub use panes::*; pub use project_panel::*; pub use status_bar::*; pub use tab::*; +pub use tab_bar::*; pub use terminal::*; pub use workspace::*; diff --git a/crates/ui2/src/components/tab_bar.rs b/crates/ui2/src/components/tab_bar.rs new file mode 100644 index 0000000000000000000000000000000000000000..b9393d830bfb22f31b9611bff1f7f6a5df9a0587 --- /dev/null +++ b/crates/ui2/src/components/tab_bar.rs @@ -0,0 +1,85 @@ +use std::marker::PhantomData; + +use crate::prelude::*; +use crate::{theme, Icon, IconButton, Tab}; + +#[derive(Element)] +pub struct TabBar { + state_type: PhantomData, + scroll_state: ScrollState, + tabs: Vec>, +} + +impl TabBar { + pub fn new(tabs: Vec>) -> Self { + Self { + state_type: PhantomData, + scroll_state: ScrollState::default(), + tabs, + } + } + + pub fn bind_scroll_state(&mut self, scroll_state: ScrollState) { + self.scroll_state = scroll_state; + } + + fn render(&mut self, cx: &mut ViewContext) -> impl Element { + let theme = theme(cx); + let can_navigate_back = true; + let can_navigate_forward = false; + + div() + .w_full() + .flex() + .fill(theme.middle.base.default.background) + // Left Side + .child( + div() + .px_1() + .flex() + .flex_none() + .gap_2() + // Nav Buttons + .child( + div() + .flex() + .items_center() + .gap_px() + .child( + IconButton::new(Icon::ArrowLeft) + .state(InteractionState::Enabled.if_enabled(can_navigate_back)), + ) + .child( + IconButton::new(Icon::ArrowRight).state( + InteractionState::Enabled.if_enabled(can_navigate_forward), + ), + ), + ), + ) + .child( + div().w_0().flex_1().h_full().child( + div() + .flex() + .overflow_x_scroll(self.scroll_state.clone()) + .children(self.tabs.clone()), + ), + ) + // Right Side + .child( + div() + .px_1() + .flex() + .flex_none() + .gap_2() + // Nav Buttons + .child( + div() + .flex() + .items_center() + .gap_px() + .child(IconButton::new(Icon::Plus)) + .child(IconButton::new(Icon::Split)), + ), + ) + } +}