1use crate::prelude::*;
2use gpui::{img, Div, Hsla, ImageData, ImageSource, Img, IntoElement, Styled};
3use std::sync::Arc;
4
5#[derive(Debug, Default, PartialEq, Clone)]
6pub enum Shape {
7 #[default]
8 Circle,
9 RoundedRectangle,
10}
11
12#[derive(IntoElement)]
13pub struct Avatar {
14 image: Img,
15 border_color: Option<Hsla>,
16 is_available: Option<bool>,
17}
18
19impl RenderOnce for Avatar {
20 type Rendered = Div;
21
22 fn render(mut self, cx: &mut WindowContext) -> Self::Rendered {
23 if self.image.style().corner_radii.top_left.is_none() {
24 self = self.shape(Shape::Circle);
25 }
26
27 let size = cx.rem_size();
28
29 div()
30 .size(size + px(2.))
31 .map(|mut div| {
32 div.style().corner_radii = self.image.style().corner_radii.clone();
33 div
34 })
35 .when_some(self.border_color, |this, color| {
36 this.border().border_color(color)
37 })
38 .child(
39 self.image
40 .size(size)
41 // todo!(Pull the avatar fallback background from the theme.)
42 .bg(gpui::red()),
43 )
44 .children(self.is_available.map(|is_free| {
45 // HACK: non-integer sizes result in oval indicators.
46 let indicator_size = (size * 0.4).round();
47
48 div()
49 .absolute()
50 .z_index(1)
51 .bg(if is_free { gpui::green() } else { gpui::red() })
52 .size(indicator_size)
53 .rounded(indicator_size)
54 .bottom_0()
55 .right_0()
56 }))
57 }
58}
59
60impl Avatar {
61 pub fn uri(src: impl Into<SharedString>) -> Self {
62 Self::source(src.into().into())
63 }
64
65 pub fn data(src: Arc<ImageData>) -> Self {
66 Self::source(src.into())
67 }
68
69 pub fn source(src: ImageSource) -> Self {
70 Self {
71 image: img(src),
72 is_available: None,
73 border_color: None,
74 }
75 }
76
77 pub fn shape(mut self, shape: Shape) -> Self {
78 self.image = match shape {
79 Shape::Circle => self.image.rounded_full(),
80 Shape::RoundedRectangle => self.image.rounded_md(),
81 };
82 self
83 }
84
85 pub fn grayscale(mut self, grayscale: bool) -> Self {
86 self.image = self.image.grayscale(grayscale);
87 self
88 }
89
90 pub fn border_color(mut self, color: impl Into<Hsla>) -> Self {
91 self.border_color = Some(color.into());
92 self
93 }
94
95 pub fn availability_indicator(mut self, is_available: impl Into<Option<bool>>) -> Self {
96 self.is_available = is_available.into();
97 self
98 }
99}