diff --git a/crates/storybook/src/prelude.rs b/crates/storybook/src/prelude.rs index 50ab042cfe..e53514a5a8 100644 --- a/crates/storybook/src/prelude.rs +++ b/crates/storybook/src/prelude.rs @@ -5,7 +5,7 @@ pub enum ButtonVariant { Filled, } -#[derive(Default, PartialEq)] +#[derive(Default, PartialEq, Clone, Copy)] pub enum Shape { #[default] Circle, diff --git a/crates/storybook/src/ui.rs b/crates/storybook/src/ui.rs index fecb36412e..9597db2cde 100644 --- a/crates/storybook/src/ui.rs +++ b/crates/storybook/src/ui.rs @@ -2,20 +2,17 @@ mod component; mod element; mod module; -pub use component::tab::tab; -pub use component::tab::Tab; +pub use component::facepile::*; +pub use component::follow_group::*; +pub use component::tab::*; -pub use module::chat_panel::chat_panel; -pub use module::status_bar::status_bar; -pub use module::status_bar::StatusBar; -pub use module::tab_bar::tab_bar; -pub use module::title_bar::title_bar; +pub use module::chat_panel::*; +pub use module::status_bar::*; +pub use module::tab_bar::*; +pub use module::title_bar::*; -pub use element::avatar::avatar; -pub use element::avatar::Avatar; -pub use element::icon_button::icon_button; -pub use element::icon_button::IconButton; -pub use element::text_button::text_button; -pub use element::text_button::TextButton; -pub use element::tool_divider::tool_divider; -pub use element::tool_divider::ToolDivider; +pub use element::avatar::*; +pub use element::icon_button::*; +pub use element::indicator::*; +pub use element::text_button::*; +pub use element::tool_divider::*; diff --git a/crates/storybook/src/ui/component.rs b/crates/storybook/src/ui/component.rs index c87bc82d9b..8e99b60483 100644 --- a/crates/storybook/src/ui/component.rs +++ b/crates/storybook/src/ui/component.rs @@ -1 +1,3 @@ +pub(crate) mod facepile; +pub(crate) mod follow_group; pub(crate) mod tab; diff --git a/crates/storybook/src/ui/component/facepile.rs b/crates/storybook/src/ui/component/facepile.rs new file mode 100644 index 0000000000..1a2b2a98f6 --- /dev/null +++ b/crates/storybook/src/ui/component/facepile.rs @@ -0,0 +1,31 @@ +use crate::theme::theme; +use crate::ui::Avatar; +use gpui2::style::StyleHelpers; +use gpui2::{elements::div, IntoElement}; +use gpui2::{Element, ParentElement, ViewContext}; + +#[derive(Element)] +pub struct Facepile { + players: Vec, +} + +pub fn facepile(players: Vec) -> Facepile { + Facepile { players } +} + +impl Facepile { + fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { + let theme = theme(cx); + let player_list = self + .players + .iter() + .map(|player| div().right_1().child(player.clone())); + + div() + .relative() + .p_1() + .flex() + .items_center() + .children(player_list) + } +} diff --git a/crates/storybook/src/ui/component/follow_group.rs b/crates/storybook/src/ui/component/follow_group.rs new file mode 100644 index 0000000000..5d79b8fc06 --- /dev/null +++ b/crates/storybook/src/ui/component/follow_group.rs @@ -0,0 +1,52 @@ +use crate::theme::theme; +use crate::ui::{facepile, indicator, Avatar}; +use gpui2::style::StyleHelpers; +use gpui2::{elements::div, IntoElement}; +use gpui2::{Element, ParentElement, ViewContext}; + +#[derive(Element)] +pub struct FollowGroup { + player: usize, + players: Vec, +} + +pub fn follow_group(players: Vec) -> FollowGroup { + FollowGroup { player: 0, players } +} + +impl FollowGroup { + pub fn player(mut self, player: usize) -> Self { + self.player = player; + self + } + + fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { + let theme = theme(cx); + let player_bg = theme.players[self.player].selection; + + div() + .h_full() + .flex() + .flex_col() + .gap_px() + .justify_center() + .child( + div() + .flex() + .justify_center() + .w_full() + .child(indicator().player(self.player)), + ) + .child( + div() + .flex() + .items_center() + .justify_center() + .h_6() + .px_1() + .rounded_lg() + .fill(player_bg) + .child(facepile(self.players.clone())), + ) + } +} diff --git a/crates/storybook/src/ui/element.rs b/crates/storybook/src/ui/element.rs index 382da0f731..dde718c40b 100644 --- a/crates/storybook/src/ui/element.rs +++ b/crates/storybook/src/ui/element.rs @@ -1,4 +1,5 @@ pub(crate) mod avatar; pub(crate) mod icon_button; +pub(crate) mod indicator; pub(crate) mod text_button; pub(crate) mod tool_divider; diff --git a/crates/storybook/src/ui/element/avatar.rs b/crates/storybook/src/ui/element/avatar.rs index bfa4ed0175..cc25587c8d 100644 --- a/crates/storybook/src/ui/element/avatar.rs +++ b/crates/storybook/src/ui/element/avatar.rs @@ -7,7 +7,7 @@ use gpui2::{Element, ViewContext}; pub type UnknownString = ArcCow<'static, str>; -#[derive(Element)] +#[derive(Element, Clone)] pub struct Avatar { src: ArcCow<'static, str>, shape: Shape, diff --git a/crates/storybook/src/ui/element/indicator.rs b/crates/storybook/src/ui/element/indicator.rs new file mode 100644 index 0000000000..b905892f1a --- /dev/null +++ b/crates/storybook/src/ui/element/indicator.rs @@ -0,0 +1,32 @@ +use crate::theme::theme; +use gpui2::style::StyleHelpers; +use gpui2::{elements::div, IntoElement}; +use gpui2::{Element, ViewContext}; + +#[derive(Element)] +pub struct Indicator { + player: usize, +} + +pub fn indicator() -> Indicator { + Indicator { player: 0 } +} + +impl Indicator { + pub fn player(mut self, player: usize) -> Self { + self.player = player; + self + } + + fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { + let theme = theme(cx); + let player_color = theme.players[self.player].cursor; + + div() + .w_4() + .h_1() + .rounded_bl_sm() + .rounded_br_sm() + .fill(player_color) + } +} diff --git a/crates/storybook/src/ui/module/title_bar.rs b/crates/storybook/src/ui/module/title_bar.rs index 9aab5a2d5e..a41d56476f 100644 --- a/crates/storybook/src/ui/module/title_bar.rs +++ b/crates/storybook/src/ui/module/title_bar.rs @@ -2,7 +2,7 @@ use std::marker::PhantomData; use crate::prelude::Shape; use crate::theme::theme; -use crate::ui::{avatar, icon_button, text_button, tool_divider}; +use crate::ui::{avatar, follow_group, icon_button, text_button, tool_divider}; use gpui2::style::StyleHelpers; use gpui2::{elements::div, IntoElement}; use gpui2::{Element, ParentElement, ViewContext}; @@ -21,6 +21,10 @@ pub fn title_bar() -> TitleBar { impl TitleBar { fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { let theme = theme(cx); + let player_list = vec![ + avatar("https://avatars.githubusercontent.com/u/1714999?v=4"), + avatar("https://avatars.githubusercontent.com/u/1714999?v=4"), + ]; div() .flex() @@ -70,9 +74,13 @@ impl TitleBar { .flex() .items_center() .gap_1() + .child(text_button("maxbrunsfeld")) .child(text_button("zed")) .child(text_button("nate/gpui2-ui-components")), - ), + ) + .child(follow_group(player_list.clone()).player(0)) + .child(follow_group(player_list.clone()).player(1)) + .child(follow_group(player_list.clone()).player(2)), ) .child( div()