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;
Marshall Bowers created
crates/storybook2/src/stories/components.rs | 1
crates/storybook2/src/stories/components/collab_panel.rs | 26 +
crates/storybook2/src/story_selector.rs | 2
crates/ui2/src/components.rs | 2
crates/ui2/src/components/collab_panel.rs | 160 ++++++++++
crates/ui2/src/static_data.rs | 83 +++++
6 files changed, 271 insertions(+), 3 deletions(-)
@@ -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;
@@ -0,0 +1,26 @@
+use std::marker::PhantomData;
+
+use ui::prelude::*;
+use ui::CollabPanel;
+
+use crate::story::Story;
+
+#[derive(Element)]
+pub struct CollabPanelStory<S: 'static + Send + Sync + Clone> {
+ state_type: PhantomData<S>,
+}
+
+impl<S: 'static + Send + Sync + Clone> CollabPanelStory<S> {
+ pub fn new() -> Self {
+ Self {
+ state_type: PhantomData,
+ }
+ }
+
+ fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
+ Story::container(cx)
+ .child(Story::title_for::<_, CollabPanel<S>>(cx))
+ .child(Story::label(cx, "Default"))
+ .child(CollabPanel::new(ScrollState::default()))
+ }
+}
@@ -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(),
@@ -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::*;
@@ -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<S: 'static + Send + Sync + Clone> {
+ view_type: PhantomData<S>,
+ scroll_state: ScrollState,
+}
+
+impl<S: 'static + Send + Sync + Clone> CollabPanel<S> {
+ pub fn new(scroll_state: ScrollState) -> Self {
+ Self {
+ view_type: PhantomData,
+ scroll_state,
+ }
+ }
+
+ fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
+ 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<ArcCow<'static, str>>,
+ expanded: bool,
+ theme: &Theme,
+ ) -> impl Element<State = S> {
+ 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<ArcCow<'static, str>>,
+ label: impl Into<ArcCow<'static, str>>,
+ theme: &Theme,
+ ) -> impl Element<State = S> {
+ 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()),
+ )
+ }
+}
@@ -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<S: 'static + Send + Sync + Clone>() -> Vec<Tab<S>> {
@@ -464,6 +464,83 @@ pub fn static_project_panel_single_items<S: 'static + Send + Sync + Clone>() ->
.collect()
}
+pub fn static_collab_panel_current_call<S: 'static + Send + Sync + Clone>() -> Vec<ListItem<S>> {
+ 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<S: 'static + Send + Sync + Clone>() -> Vec<ListItem<S>> {
+ 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<S: 'static + Send + Sync + Clone>() -> Editor<S> {
Editor {
tabs: static_tabs_example(),