1use crate::{Avatar, prelude::*};
2use gpui::{AnyElement, StyleRefinement};
3use smallvec::SmallVec;
4
5/// A facepile is a collection of faces stacked horizontally–
6/// always with the leftmost face on top and descending in z-index
7///
8/// Facepiles are used to display a group of people or things,
9/// such as a list of participants in a collaboration session.
10#[derive(IntoElement, IntoComponent)]
11pub struct Facepile {
12 base: Div,
13 faces: SmallVec<[AnyElement; 2]>,
14}
15
16impl Facepile {
17 /// Creates a new empty facepile.
18 pub fn empty() -> Self {
19 Self::new(SmallVec::new())
20 }
21
22 /// Creates a new facepile with the given faces.
23 pub fn new(faces: SmallVec<[AnyElement; 2]>) -> Self {
24 Self { base: div(), faces }
25 }
26}
27
28impl ParentElement for Facepile {
29 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
30 self.faces.extend(elements);
31 }
32}
33
34// Style methods.
35impl Facepile {
36 fn style(&mut self) -> &mut StyleRefinement {
37 self.base.style()
38 }
39
40 gpui::padding_style_methods!({
41 visibility: pub
42 });
43}
44
45impl RenderOnce for Facepile {
46 fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
47 // Lay the faces out in reverse so they overlap in the desired order (left to right, front to back)
48 self.base
49 .flex()
50 .flex_row_reverse()
51 .items_center()
52 .justify_start()
53 .children(
54 self.faces
55 .into_iter()
56 .enumerate()
57 .rev()
58 .map(|(ix, player)| div().when(ix > 0, |div| div.ml_neg_1()).child(player)),
59 )
60 }
61}
62
63impl ComponentPreview for Facepile {
64 fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
65 let faces: [&'static str; 6] = [
66 "https://avatars.githubusercontent.com/u/326587?s=60&v=4",
67 "https://avatars.githubusercontent.com/u/2280405?s=60&v=4",
68 "https://avatars.githubusercontent.com/u/1789?s=60&v=4",
69 "https://avatars.githubusercontent.com/u/67129314?s=60&v=4",
70 "https://avatars.githubusercontent.com/u/482957?s=60&v=4",
71 "https://avatars.githubusercontent.com/u/1714999?s=60&v=4",
72 ];
73
74 v_flex()
75 .gap_6()
76 .children(vec![
77 example_group_with_title(
78 "Facepile Examples",
79 vec![
80 single_example(
81 "Default",
82 Facepile::new(
83 faces
84 .iter()
85 .map(|&url| Avatar::new(url).into_any_element())
86 .collect(),
87 )
88 .into_any_element(),
89 ),
90 single_example(
91 "Custom Size",
92 Facepile::new(
93 faces
94 .iter()
95 .map(|&url| Avatar::new(url).size(px(24.)).into_any_element())
96 .collect(),
97 )
98 .into_any_element(),
99 ),
100 ],
101 ),
102 example_group_with_title(
103 "Special Cases",
104 vec![
105 single_example("Empty Facepile", Facepile::empty().into_any_element()),
106 single_example(
107 "Single Face",
108 Facepile::new(vec![Avatar::new(faces[0]).into_any_element()].into())
109 .into_any_element(),
110 ),
111 ],
112 ),
113 ])
114 .into_any_element()
115 }
116}