component: Add component and component_preview crates to power UI components (#24456)

This PR formalizes design components with the Component and
ComponentPreview traits.

You can open the preview UI with `workspace: open component preview`.

Component previews no longer need to return `Self` allowing for more
complex previews, and previews of components like `ui::Tooltip` that
supplement other components rather than are rendered by default.

`cargo-machete` incorrectly identifies `linkme` as an unused dep on
crates that have components deriving `IntoComponent`, so you may need to
add this to that crate's `Cargo.toml`:

```toml
# cargo-machete doesn't understand that linkme is used in the component macro
[package.metadata.cargo-machete]
ignored = ["linkme"]
```

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
This commit is contained in:
Nate Butler 2025-02-09 13:25:03 -05:00 committed by GitHub
parent 56cfc60875
commit 8f1ff189cc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 1582 additions and 976 deletions

View file

@ -1,4 +1,4 @@
use crate::{prelude::*, Avatar};
use crate::prelude::*;
use gpui::{AnyElement, StyleRefinement};
use smallvec::SmallVec;
@ -60,60 +60,60 @@ impl RenderOnce for Facepile {
}
}
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(_window: &mut Window, _: &mut App) -> 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",
];
// 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(_window: &mut Window, _: &mut App) -> 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",
];
// 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(),
),
),
],
)]
}
}
// 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(),
// ),
// ),
// ],
// )]
// }
// }