Add `Avatar` element

Marshall Bowers created

Change summary

crates/storybook2/src/stories/elements.rs        |  1 
crates/storybook2/src/stories/elements/avatar.rs | 33 +++++++++++++
crates/storybook2/src/story_selector.rs          |  2 
crates/storybook2/src/ui/elements.rs             |  2 
crates/storybook2/src/ui/elements/avatar.rs      | 44 ++++++++++++++++++
crates/storybook2/src/ui/prelude.rs              |  9 +++
6 files changed, 91 insertions(+)

Detailed changes

crates/storybook2/src/stories/elements/avatar.rs 🔗

@@ -0,0 +1,33 @@
+use std::marker::PhantomData;
+
+use crate::ui::prelude::*;
+use crate::ui::Avatar;
+
+use crate::story::Story;
+
+#[derive(Element)]
+pub struct AvatarStory<S: 'static + Send + Sync> {
+    state_type: PhantomData<S>,
+}
+
+impl<S: 'static + Send + Sync> AvatarStory<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::<_, Avatar<S>>(cx))
+            .child(Story::label(cx, "Default"))
+            .child(Avatar::new(
+                "https://avatars.githubusercontent.com/u/1714999?v=4",
+            ))
+            .child(Story::label(cx, "Rounded rectangle"))
+            .child(
+                Avatar::new("https://avatars.githubusercontent.com/u/1714999?v=4")
+                    .shape(Shape::RoundedRectangle),
+            )
+    }
+}

crates/storybook2/src/story_selector.rs 🔗

@@ -12,6 +12,7 @@ use crate::ui::prelude::*;
 #[derive(Debug, PartialEq, Eq, Clone, Copy, strum::Display, EnumString, EnumIter)]
 #[strum(serialize_all = "snake_case")]
 pub enum ElementStory {
+    Avatar,
     Icon,
     Label,
 }
@@ -21,6 +22,7 @@ impl ElementStory {
         use crate::stories::elements;
 
         match self {
+            Self::Avatar => elements::avatar::AvatarStory::new().into_any(),
             Self::Icon => elements::icon::IconStory::new().into_any(),
             Self::Label => elements::label::LabelStory::new().into_any(),
         }

crates/storybook2/src/ui/elements/avatar.rs 🔗

@@ -0,0 +1,44 @@
+use std::marker::PhantomData;
+
+use gpui3::{img, ArcCow};
+
+use crate::theme::theme;
+use crate::ui::prelude::*;
+
+#[derive(Element, Clone)]
+pub struct Avatar<S: 'static + Send + Sync> {
+    state_type: PhantomData<S>,
+    src: ArcCow<'static, str>,
+    shape: Shape,
+}
+
+impl<S: 'static + Send + Sync> Avatar<S> {
+    pub fn new(src: impl Into<ArcCow<'static, str>>) -> Self {
+        Self {
+            state_type: PhantomData,
+            src: src.into(),
+            shape: Shape::Circle,
+        }
+    }
+
+    pub fn shape(mut self, shape: Shape) -> Self {
+        self.shape = shape;
+        self
+    }
+
+    fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
+        let theme = theme(cx);
+
+        let mut img = img();
+
+        if self.shape == Shape::Circle {
+            img = img.rounded_full();
+        } else {
+            img = img.rounded_md();
+        }
+
+        img.uri(self.src.clone())
+            .size_4()
+            .fill(theme.middle.warning.default.foreground)
+    }
+}

crates/storybook2/src/ui/prelude.rs 🔗

@@ -3,3 +3,12 @@ pub use gpui3::{
 };
 
 pub use crate::ui::{HackyChildren, HackyChildrenPayload};
+
+use strum::EnumIter;
+
+#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
+pub enum Shape {
+    #[default]
+    Circle,
+    RoundedRectangle,
+}