gpui_component/avatar/
avatar_group.rs

1use gpui::{
2    div, prelude::FluentBuilder as _, Div, InteractiveElement, Interactivity, IntoElement,
3    ParentElement as _, RenderOnce, StyleRefinement, Styled,
4};
5
6use crate::{avatar::Avatar, ActiveTheme, Sizable, Size, StyledExt as _};
7
8/// A grouped avatars to display in a compact layout.
9#[derive(IntoElement)]
10pub struct AvatarGroup {
11    base: Div,
12    style: StyleRefinement,
13    avatars: Vec<Avatar>,
14    size: Size,
15    limit: usize,
16    ellipsis: bool,
17}
18
19impl AvatarGroup {
20    /// Create a new AvatarGroup.
21    pub fn new() -> Self {
22        Self {
23            base: div(),
24            style: StyleRefinement::default(),
25            avatars: Vec::new(),
26            size: Size::default(),
27            limit: 3,
28            ellipsis: false,
29        }
30    }
31
32    /// Add a child avatar to the group.
33    pub fn child(mut self, avatar: Avatar) -> Self {
34        self.avatars.push(avatar);
35        self
36    }
37
38    /// Add multiple child avatars to the group.
39    pub fn children(mut self, avatars: impl IntoIterator<Item = Avatar>) -> Self {
40        self.avatars.extend(avatars);
41        self
42    }
43
44    /// Set the maximum number of avatars to display before showing a "more" avatar.
45    pub fn limit(mut self, limit: usize) -> Self {
46        self.limit = limit;
47        self
48    }
49
50    /// Set whether to show an ellipsis when the limit is reached, default: false
51    pub fn ellipsis(mut self) -> Self {
52        self.ellipsis = true;
53        self
54    }
55}
56
57impl Sizable for AvatarGroup {
58    fn with_size(mut self, size: impl Into<Size>) -> Self {
59        self.size = size.into();
60        self
61    }
62}
63
64impl Styled for AvatarGroup {
65    fn style(&mut self) -> &mut StyleRefinement {
66        &mut self.style
67    }
68}
69
70impl InteractiveElement for AvatarGroup {
71    fn interactivity(&mut self) -> &mut Interactivity {
72        self.base.interactivity()
73    }
74}
75
76impl RenderOnce for AvatarGroup {
77    fn render(self, _: &mut gpui::Window, cx: &mut gpui::App) -> impl IntoElement {
78        let item_ml = -super::avatar_size(self.size) * 0.3;
79        let avatars_len = self.avatars.len();
80
81        self.base
82            .h_flex()
83            .flex_row_reverse()
84            .refine_style(&self.style)
85            .children(if self.ellipsis && avatars_len > self.limit {
86                Some(
87                    Avatar::new()
88                        .name("⋯")
89                        .bg(cx.theme().secondary)
90                        .text_color(cx.theme().muted_foreground)
91                        .with_size(self.size)
92                        .ml_1(),
93                )
94            } else {
95                None
96            })
97            .children(
98                self.avatars
99                    .into_iter()
100                    .take(self.limit)
101                    .enumerate()
102                    .rev()
103                    .map(|(ix, item)| {
104                        item.with_size(self.size)
105                            .when(ix > 0, |this| this.ml(item_ml))
106                    }),
107            )
108    }
109}