diff --git a/crates/storybook2/src/stories/components.rs b/crates/storybook2/src/stories/components.rs index 2ab3749c489287df30953854f4f7b6071f3ba74d..1b4797bc8ec88337095098d9e2f90224c900eb6c 100644 --- a/crates/storybook2/src/stories/components.rs +++ b/crates/storybook2/src/stories/components.rs @@ -2,6 +2,7 @@ pub mod assistant_panel; pub mod breadcrumb; pub mod buffer; pub mod chat_panel; +pub mod collab_panel; pub mod facepile; pub mod panel; pub mod project_panel; diff --git a/crates/storybook2/src/stories/components/collab_panel.rs b/crates/storybook2/src/stories/components/collab_panel.rs new file mode 100644 index 0000000000000000000000000000000000000000..d1a3b072d7e14d7d21ea96435b5e7a820a1e52fb --- /dev/null +++ b/crates/storybook2/src/stories/components/collab_panel.rs @@ -0,0 +1,26 @@ +use std::marker::PhantomData; + +use ui::prelude::*; +use ui::CollabPanel; + +use crate::story::Story; + +#[derive(Element)] +pub struct CollabPanelStory { + state_type: PhantomData, +} + +impl CollabPanelStory { + pub fn new() -> Self { + Self { + state_type: PhantomData, + } + } + + fn render(&mut self, cx: &mut ViewContext) -> impl Element { + Story::container(cx) + .child(Story::title_for::<_, CollabPanel>(cx)) + .child(Story::label(cx, "Default")) + .child(CollabPanel::new(ScrollState::default())) + } +} diff --git a/crates/storybook2/src/story_selector.rs b/crates/storybook2/src/story_selector.rs index 646ba0dbd9db84c5071ba6af4fa4f352f288ccfa..b59febd974999ca756fe297d77f230527725d85a 100644 --- a/crates/storybook2/src/story_selector.rs +++ b/crates/storybook2/src/story_selector.rs @@ -40,6 +40,7 @@ pub enum ComponentStory { Breadcrumb, Buffer, ChatPanel, + CollabPanel, Facepile, Panel, ProjectPanel, @@ -63,6 +64,7 @@ impl ComponentStory { Self::Buffer => components::buffer::BufferStory::new().into_any(), Self::Breadcrumb => components::breadcrumb::BreadcrumbStory::new().into_any(), Self::ChatPanel => components::chat_panel::ChatPanelStory::new().into_any(), + Self::CollabPanel => components::collab_panel::CollabPanelStory::new().into_any(), Self::Facepile => components::facepile::FacepileStory::new().into_any(), Self::Panel => components::panel::PanelStory::new().into_any(), Self::ProjectPanel => components::project_panel::ProjectPanelStory::new().into_any(), diff --git a/crates/ui2/src/components.rs b/crates/ui2/src/components.rs index 454b2e571e63f9974b932fd6ca6c1d33ef97aee1..0b9e3cd74d9974192be77179601929bb452c3cb4 100644 --- a/crates/ui2/src/components.rs +++ b/crates/ui2/src/components.rs @@ -2,6 +2,7 @@ mod assistant_panel; mod breadcrumb; mod buffer; mod chat_panel; +mod collab_panel; mod editor_pane; mod facepile; mod icon_button; @@ -23,6 +24,7 @@ pub use assistant_panel::*; pub use breadcrumb::*; pub use buffer::*; pub use chat_panel::*; +pub use collab_panel::*; pub use editor_pane::*; pub use facepile::*; pub use icon_button::*; diff --git a/crates/ui2/src/components/collab_panel.rs b/crates/ui2/src/components/collab_panel.rs new file mode 100644 index 0000000000000000000000000000000000000000..c435f7e9cdfe965ec45c519a3ba99df931370b98 --- /dev/null +++ b/crates/ui2/src/components/collab_panel.rs @@ -0,0 +1,160 @@ +use std::marker::PhantomData; + +use gpui3::{img, svg, ArcCow}; + +use crate::prelude::*; +use crate::theme::{theme, Theme}; +use crate::{ + static_collab_panel_channels, static_collab_panel_current_call, v_stack, Icon, List, + ListHeader, ToggleState, +}; + +#[derive(Element)] +pub struct CollabPanel { + view_type: PhantomData, + scroll_state: ScrollState, +} + +impl CollabPanel { + pub fn new(scroll_state: ScrollState) -> Self { + Self { + view_type: PhantomData, + scroll_state, + } + } + + fn render(&mut self, cx: &mut ViewContext) -> impl Element { + let theme = theme(cx); + + v_stack() + .w_64() + .h_full() + .fill(theme.middle.base.default.background) + .child( + v_stack() + .w_full() + .overflow_y_scroll(self.scroll_state.clone()) + .child( + div() + .fill(theme.lowest.base.default.background) + .pb_1() + .border_color(theme.lowest.base.default.border) + .border_b() + .child( + List::new(static_collab_panel_current_call()) + .header( + ListHeader::new("CRDB") + .left_icon(Icon::Hash.into()) + .set_toggle(ToggleState::Toggled), + ) + .set_toggle(ToggleState::Toggled), + ), + ) + .child( + v_stack().py_1().child( + List::new(static_collab_panel_channels()) + .header( + ListHeader::new("CHANNELS").set_toggle(ToggleState::Toggled), + ) + .empty_message("No channels yet. Add a channel to get started.") + .set_toggle(ToggleState::Toggled), + ), + ) + .child( + v_stack().py_1().child( + List::new(static_collab_panel_current_call()) + .header( + ListHeader::new("CONTACTS – ONLINE") + .set_toggle(ToggleState::Toggled), + ) + .set_toggle(ToggleState::Toggled), + ), + ) + .child( + v_stack().py_1().child( + List::new(static_collab_panel_current_call()) + .header( + ListHeader::new("CONTACTS – OFFLINE") + .set_toggle(ToggleState::NotToggled), + ) + .set_toggle(ToggleState::NotToggled), + ), + ), + ) + .child( + div() + .h_7() + .px_2() + .border_t() + .border_color(theme.middle.variant.default.border) + .flex() + .items_center() + .child( + div() + .text_sm() + .text_color(theme.middle.variant.default.foreground) + .child("Find..."), + ), + ) + } + + fn list_section_header( + &self, + label: impl Into>, + expanded: bool, + theme: &Theme, + ) -> impl Element { + div() + .h_7() + .px_2() + .flex() + .justify_between() + .items_center() + .child(div().flex().gap_1().text_sm().child(label.into())) + .child( + div().flex().h_full().gap_1().items_center().child( + svg() + .path(if expanded { + "icons/caret_down.svg" + } else { + "icons/caret_up.svg" + }) + .w_3p5() + .h_3p5() + .fill(theme.middle.variant.default.foreground), + ), + ) + } + + fn list_item( + &self, + avatar_uri: impl Into>, + label: impl Into>, + theme: &Theme, + ) -> impl Element { + div() + .h_7() + .px_2() + .flex() + .items_center() + // .hover() + // .fill(theme.lowest.variant.hovered.background) + // .active() + // .fill(theme.lowest.variant.pressed.background) + .child( + div() + .flex() + .items_center() + .gap_1() + .text_sm() + .child( + img() + .uri(avatar_uri) + .size_3p5() + .rounded_full() + .fill(theme.middle.positive.default.foreground), + ) + .child(label.into()), + ) + } +} diff --git a/crates/ui2/src/static_data.rs b/crates/ui2/src/static_data.rs index 36b98817697fe58f58a1bf88c2d64f5495370a59..8fddd9c517dfc3d571d8f9391f569c1c6c984a83 100644 --- a/crates/ui2/src/static_data.rs +++ b/crates/ui2/src/static_data.rs @@ -5,9 +5,9 @@ use rand::Rng; use crate::{ Buffer, BufferRow, BufferRows, Editor, FileSystemStatus, GitStatus, HighlightColor, - HighlightedLine, HighlightedText, Icon, Label, LabelColor, ListEntry, ListItem, Livestream, - MicStatus, Player, PlayerCallStatus, PlayerWithCallStatus, ScreenShareStatus, Symbol, Tab, - Theme, ToggleState, VideoStatus, + HighlightedLine, HighlightedText, Icon, Label, LabelColor, ListEntry, ListEntrySize, ListItem, + Livestream, MicStatus, Player, PlayerCallStatus, PlayerWithCallStatus, ScreenShareStatus, + Symbol, Tab, Theme, ToggleState, VideoStatus, }; pub fn static_tabs_example() -> Vec> { @@ -464,6 +464,83 @@ pub fn static_project_panel_single_items() -> .collect() } +pub fn static_collab_panel_current_call() -> Vec> { + vec![ + ListEntry::new(Label::new("as-cii")).left_avatar("http://github.com/as-cii.png?s=50"), + ListEntry::new(Label::new("nathansobo")) + .left_avatar("http://github.com/nathansobo.png?s=50"), + ListEntry::new(Label::new("maxbrunsfeld")) + .left_avatar("http://github.com/maxbrunsfeld.png?s=50"), + ] + .into_iter() + .map(From::from) + .collect() +} + +pub fn static_collab_panel_channels() -> Vec> { + vec![ + ListEntry::new(Label::new("zed")) + .left_icon(Icon::Hash.into()) + .size(ListEntrySize::Medium) + .indent_level(0), + ListEntry::new(Label::new("community")) + .left_icon(Icon::Hash.into()) + .size(ListEntrySize::Medium) + .indent_level(1), + ListEntry::new(Label::new("dashboards")) + .left_icon(Icon::Hash.into()) + .size(ListEntrySize::Medium) + .indent_level(2), + ListEntry::new(Label::new("feedback")) + .left_icon(Icon::Hash.into()) + .size(ListEntrySize::Medium) + .indent_level(2), + ListEntry::new(Label::new("teams-in-channels-alpha")) + .left_icon(Icon::Hash.into()) + .size(ListEntrySize::Medium) + .indent_level(2), + ListEntry::new(Label::new("current-projects")) + .left_icon(Icon::Hash.into()) + .size(ListEntrySize::Medium) + .indent_level(1), + ListEntry::new(Label::new("codegen")) + .left_icon(Icon::Hash.into()) + .size(ListEntrySize::Medium) + .indent_level(2), + ListEntry::new(Label::new("gpui2")) + .left_icon(Icon::Hash.into()) + .size(ListEntrySize::Medium) + .indent_level(2), + ListEntry::new(Label::new("livestreaming")) + .left_icon(Icon::Hash.into()) + .size(ListEntrySize::Medium) + .indent_level(2), + ListEntry::new(Label::new("open-source")) + .left_icon(Icon::Hash.into()) + .size(ListEntrySize::Medium) + .indent_level(2), + ListEntry::new(Label::new("replace")) + .left_icon(Icon::Hash.into()) + .size(ListEntrySize::Medium) + .indent_level(2), + ListEntry::new(Label::new("semantic-index")) + .left_icon(Icon::Hash.into()) + .size(ListEntrySize::Medium) + .indent_level(2), + ListEntry::new(Label::new("vim")) + .left_icon(Icon::Hash.into()) + .size(ListEntrySize::Medium) + .indent_level(2), + ListEntry::new(Label::new("web-tech")) + .left_icon(Icon::Hash.into()) + .size(ListEntrySize::Medium) + .indent_level(2), + ] + .into_iter() + .map(From::from) + .collect() +} + pub fn empty_editor_example() -> Editor { Editor { tabs: static_tabs_example(),