ZIm/crates/ui/src/components/facepile.rs
Danilo Leal 82427e1ffb
Add new DecoratedIcon component (#20516)
This PR creates a new, revamped `DecoratedIcon` component that enables
using different SVGs, one for the knockout background and another for
the actual icon. That's different than what we were doing before—copying
the SVG and using slightly different positioning—because we wanted to
unlock an aligned knockout effect, which was particularly hard to do
with non-simple shapes such as an X.

Release Notes:

- N/A

---------

Co-authored-by: Nate Butler <1714999+iamnbutler@users.noreply.github.com>
2024-11-11 19:09:02 -03:00

119 lines
3.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use crate::{prelude::*, Avatar};
use gpui::{AnyElement, StyleRefinement};
use smallvec::SmallVec;
/// A facepile is a collection of faces stacked horizontally
/// always with the leftmost face on top and descending in z-index
///
/// Facepiles are used to display a group of people or things,
/// such as a list of participants in a collaboration session.
#[derive(IntoElement)]
pub struct Facepile {
base: Div,
faces: SmallVec<[AnyElement; 2]>,
}
impl Facepile {
/// Creates a new empty facepile.
pub fn empty() -> Self {
Self::new(SmallVec::new())
}
/// Creates a new facepile with the given faces.
pub fn new(faces: SmallVec<[AnyElement; 2]>) -> Self {
Self { base: div(), faces }
}
}
impl ParentElement for Facepile {
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
self.faces.extend(elements);
}
}
// Style methods.
impl Facepile {
fn style(&mut self) -> &mut StyleRefinement {
self.base.style()
}
gpui::padding_style_methods!({
visibility: pub
});
}
impl RenderOnce for Facepile {
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
// Lay the faces out in reverse so they overlap in the desired order (left to right, front to back)
self.base
.flex()
.flex_row_reverse()
.items_center()
.justify_start()
.children(
self.faces
.into_iter()
.enumerate()
.rev()
.map(|(ix, player)| div().when(ix > 0, |div| div.ml_neg_1()).child(player)),
)
}
}
impl ComponentPreview for Facepile {
fn description() -> impl Into<Option<&'static str>> {
"A facepile is a collection of faces stacked horizontally\
always with the leftmost face on top and descending in z-index.\
\n\nFacepiles are used to display a group of people or things,\
such as a list of participants in a collaboration session."
}
fn examples(_: &WindowContext) -> Vec<ComponentExampleGroup<Self>> {
let few_faces: [&'static str; 3] = [
"https://avatars.githubusercontent.com/u/1714999?s=60&v=4",
"https://avatars.githubusercontent.com/u/67129314?s=60&v=4",
"https://avatars.githubusercontent.com/u/482957?s=60&v=4",
];
let many_faces: [&'static str; 6] = [
"https://avatars.githubusercontent.com/u/326587?s=60&v=4",
"https://avatars.githubusercontent.com/u/2280405?s=60&v=4",
"https://avatars.githubusercontent.com/u/1789?s=60&v=4",
"https://avatars.githubusercontent.com/u/67129314?s=60&v=4",
"https://avatars.githubusercontent.com/u/482957?s=60&v=4",
"https://avatars.githubusercontent.com/u/1714999?s=60&v=4",
];
vec![example_group_with_title(
"Examples",
vec![
single_example(
"Few Faces",
Facepile::new(
few_faces
.iter()
.map(|&url| Avatar::new(url).into_any_element())
.collect(),
),
),
single_example(
"Many Faces",
Facepile::new(
many_faces
.iter()
.map(|&url| Avatar::new(url).into_any_element())
.collect(),
),
),
single_example(
"Custom Size",
Facepile::new(
few_faces
.iter()
.map(|&url| Avatar::new(url).size(px(24.)).into_any_element())
.collect(),
),
),
],
)]
}
}