guise/data/
avatargroup.rs1use gpui::prelude::*;
4use gpui::{div, px, App, FontWeight, IntoElement, SharedString, Window};
5
6use crate::theme::{theme, ColorName, Size};
7
8const PALETTE: [ColorName; 6] = [
9 ColorName::Blue,
10 ColorName::Teal,
11 ColorName::Grape,
12 ColorName::Orange,
13 ColorName::Pink,
14 ColorName::Lime,
15];
16
17#[derive(IntoElement)]
19pub struct AvatarGroup {
20 items: Vec<SharedString>,
21 size: Size,
22 limit: Option<usize>,
23}
24
25impl AvatarGroup {
26 pub fn new() -> Self {
27 AvatarGroup {
28 items: Vec::new(),
29 size: Size::Md,
30 limit: None,
31 }
32 }
33
34 pub fn avatar(mut self, initials: impl Into<SharedString>) -> Self {
35 self.items.push(initials.into());
36 self
37 }
38
39 pub fn avatars<I, S>(mut self, items: I) -> Self
40 where
41 I: IntoIterator<Item = S>,
42 S: Into<SharedString>,
43 {
44 self.items.extend(items.into_iter().map(Into::into));
45 self
46 }
47
48 pub fn size(mut self, size: Size) -> Self {
49 self.size = size;
50 self
51 }
52
53 pub fn limit(mut self, limit: usize) -> Self {
55 self.limit = Some(limit);
56 self
57 }
58}
59
60impl Default for AvatarGroup {
61 fn default() -> Self {
62 AvatarGroup::new()
63 }
64}
65
66impl RenderOnce for AvatarGroup {
67 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
68 let t = theme(cx);
69 let dim = super::avatar::avatar_size(self.size);
70 let ring = t.body().hsla();
71 let overflow_bg = t
72 .color(ColorName::Gray, if t.scheme.is_dark() { 6 } else { 3 })
73 .hsla();
74 let overflow_fg = t.text().hsla();
75 let dark = t.scheme.is_dark();
76
77 let total = self.items.len();
78 let shown = self.limit.unwrap_or(total).min(total);
79 let overflow = total - shown;
80
81 let bubble = |bg, fg, content: SharedString, first: bool| {
83 let mut b = div()
84 .w(px(dim))
85 .h(px(dim))
86 .flex()
87 .items_center()
88 .justify_center()
89 .rounded(px(dim))
90 .border_2()
91 .border_color(ring)
92 .bg(bg)
93 .text_color(fg)
94 .text_size(px(dim * 0.38))
95 .font_weight(FontWeight::SEMIBOLD)
96 .child(content);
97 if !first {
98 b = b.ml(px(-(dim * 0.3)));
99 }
100 b
101 };
102
103 let mut row = div().flex().items_center();
104 for (i, initials) in self.items.into_iter().take(shown).enumerate() {
105 let name = PALETTE[i % PALETTE.len()];
106 let bg = t.color(name, if dark { 8 } else { 1 }).hsla();
107 let fg = t.color(name, if dark { 2 } else { 8 }).hsla();
108 row = row.child(bubble(bg, fg, initials, i == 0));
109 }
110 if overflow > 0 {
111 row = row.child(bubble(
112 overflow_bg,
113 overflow_fg,
114 SharedString::from(format!("+{overflow}")),
115 shown == 0,
116 ));
117 }
118 row
119 }
120}