@@ -486,8 +486,12 @@ impl CollabTitlebarItem {
.child(
Avatar::new(user.avatar_uri.clone())
.grayscale(!is_present)
- .when(is_speaking, |avatar| {
- avatar.border_color(cx.theme().status().info_border)
+ .border_color(if is_speaking {
+ cx.theme().status().info_border
+ } else {
+ // We draw the border in a transparent color rather to avoid
+ // the layout shift that would come with adding/removing the border.
+ gpui::transparent_black()
})
.when(is_muted, |avatar| {
avatar.indicator(
@@ -85,6 +85,18 @@ fn generate_methods() -> Vec<TokenStream2> {
}
for (prefix, fields, prefix_doc_string) in border_prefixes() {
+ methods.push(generate_custom_value_setter(
+ // The plain method names (e.g., `border`, `border_t`, `border_r`, etc.) are special-cased
+ // versions of the 1px variants. This better matches Tailwind, but breaks our existing
+ // convention of the suffix-less variant of the method being the one that accepts a custom value
+ //
+ // To work around this, we're assigning a `_width` suffix here.
+ &format!("{prefix}_width"),
+ quote! { AbsoluteLength },
+ &fields,
+ prefix_doc_string,
+ ));
+
for (suffix, width_tokens, suffix_doc_string) in border_suffixes() {
methods.push(generate_predefined_setter(
prefix,
@@ -141,7 +153,7 @@ fn generate_predefined_setter(
}
fn generate_custom_value_setter(
- prefix: &'static str,
+ prefix: &str,
length_type: TokenStream2,
fields: &[TokenStream2],
doc_string: &str,
@@ -99,20 +99,27 @@ impl RenderOnce for Avatar {
self = self.shape(AvatarShape::Circle);
}
- let size = self.size.unwrap_or_else(|| cx.rem_size());
+ let border_width = if self.border_color.is_some() {
+ px(2.)
+ } else {
+ px(0.)
+ };
+
+ let image_size = self.size.unwrap_or_else(|| cx.rem_size());
+ let container_size = image_size + border_width * 2.;
div()
- .size(size + px(2.))
+ .size(container_size)
.map(|mut div| {
div.style().corner_radii = self.image.style().corner_radii.clone();
div
})
.when_some(self.border_color, |this, color| {
- this.border().border_color(color)
+ this.border_width(border_width).border_color(color)
})
.child(
self.image
- .size(size)
+ .size(image_size)
.bg(cx.theme().colors().ghost_element_background),
)
.children(
@@ -1,5 +1,5 @@
use gpui::Render;
-use story::Story;
+use story::{StoryContainer, StoryItem, StorySection};
use crate::{prelude::*, AudioStatus, Availability, AvatarAvailabilityIndicator};
use crate::{Avatar, AvatarAudioStatusIndicator};
@@ -7,31 +7,57 @@ use crate::{Avatar, AvatarAudioStatusIndicator};
pub struct AvatarStory;
impl Render for AvatarStory {
- fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
- Story::container()
- .child(Story::title_for::<Avatar>())
- .child(Story::label("Default"))
- .child(Avatar::new(
- "https://avatars.githubusercontent.com/u/1714999?v=4",
- ))
- .child(Avatar::new(
- "https://avatars.githubusercontent.com/u/326587?v=4",
- ))
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+ StoryContainer::new("Avatar", "crates/ui/src/components/stories/avatar.rs")
.child(
- Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
- .indicator(AvatarAvailabilityIndicator::new(Availability::Free)),
+ StorySection::new()
+ .child(StoryItem::new(
+ "Default",
+ Avatar::new("https://avatars.githubusercontent.com/u/1714999?v=4"),
+ ))
+ .child(StoryItem::new(
+ "Default",
+ Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4"),
+ )),
)
.child(
- Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
- .indicator(AvatarAvailabilityIndicator::new(Availability::Busy)),
+ StorySection::new()
+ .child(StoryItem::new(
+ "With free availability indicator",
+ Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
+ .indicator(AvatarAvailabilityIndicator::new(Availability::Free)),
+ ))
+ .child(StoryItem::new(
+ "With busy availability indicator",
+ Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
+ .indicator(AvatarAvailabilityIndicator::new(Availability::Busy)),
+ )),
)
.child(
- Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
- .indicator(AvatarAudioStatusIndicator::new(AudioStatus::Muted)),
+ StorySection::new()
+ .child(StoryItem::new(
+ "With info border",
+ Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
+ .border_color(cx.theme().status().info_border),
+ ))
+ .child(StoryItem::new(
+ "With error border",
+ Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
+ .border_color(cx.theme().status().error_border),
+ )),
)
.child(
- Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
- .indicator(AvatarAudioStatusIndicator::new(AudioStatus::Deafened)),
+ StorySection::new()
+ .child(StoryItem::new(
+ "With muted audio indicator",
+ Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
+ .indicator(AvatarAudioStatusIndicator::new(AudioStatus::Muted)),
+ ))
+ .child(StoryItem::new(
+ "With deafened audio indicator",
+ Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
+ .indicator(AvatarAudioStatusIndicator::new(AudioStatus::Deafened)),
+ )),
)
}
}