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