1#![allow(missing_docs)]
2use crate::prelude::*;
3use gpui::{AnyElement, SharedString};
4
5/// Implement this trait to enable rich UI previews with metadata in the Theme Preview tool.
6pub trait ComponentPreview: IntoElement {
7 fn title() -> &'static str {
8 std::any::type_name::<Self>()
9 }
10
11 fn description() -> impl Into<Option<&'static str>> {
12 None
13 }
14
15 fn examples() -> Vec<ComponentExampleGroup<Self>>;
16
17 fn component_previews() -> Vec<AnyElement> {
18 Self::examples()
19 .into_iter()
20 .map(|example| Self::render_example_group(example))
21 .collect()
22 }
23
24 fn render_component_previews(cx: &WindowContext) -> AnyElement {
25 let title = Self::title();
26 let (source, title) = title
27 .rsplit_once("::")
28 .map_or((None, title), |(s, t)| (Some(s), t));
29 let description = Self::description().into();
30
31 v_flex()
32 .gap_3()
33 .p_4()
34 .border_1()
35 .border_color(cx.theme().colors().border)
36 .rounded_md()
37 .child(
38 v_flex()
39 .gap_1()
40 .child(
41 h_flex()
42 .gap_1()
43 .child(Headline::new(title).size(HeadlineSize::Small))
44 .when_some(source, |this, source| {
45 this.child(Label::new(format!("({})", source)).color(Color::Muted))
46 }),
47 )
48 .when_some(description, |this, description| {
49 this.child(
50 div()
51 .text_ui_sm(cx)
52 .text_color(cx.theme().colors().text_muted)
53 .max_w(px(600.0))
54 .child(description),
55 )
56 }),
57 )
58 .children(Self::component_previews())
59 .into_any_element()
60 }
61
62 fn render_example_group(group: ComponentExampleGroup<Self>) -> AnyElement {
63 v_flex()
64 .gap_2()
65 .child(Label::new(group.title).size(LabelSize::Small))
66 .child(
67 h_flex()
68 .gap_6()
69 .children(group.examples.into_iter().map(Self::render_example))
70 .into_any_element(),
71 )
72 .into_any_element()
73 }
74
75 fn render_example(example: ComponentExample<Self>) -> AnyElement {
76 v_flex()
77 .gap_1()
78 .child(example.element)
79 .child(
80 Label::new(example.variant_name)
81 .size(LabelSize::XSmall)
82 .color(Color::Muted),
83 )
84 .into_any_element()
85 }
86}
87
88/// A single example of a component.
89pub struct ComponentExample<T> {
90 variant_name: SharedString,
91 element: T,
92}
93
94impl<T> ComponentExample<T> {
95 /// Create a new example with the given variant name and example value.
96 pub fn new(variant_name: impl Into<SharedString>, example: T) -> Self {
97 Self {
98 variant_name: variant_name.into(),
99 element: example,
100 }
101 }
102}
103
104/// A group of component examples.
105pub struct ComponentExampleGroup<T> {
106 pub title: SharedString,
107 pub examples: Vec<ComponentExample<T>>,
108}
109
110impl<T> ComponentExampleGroup<T> {
111 /// Create a new group of examples with the given title.
112 pub fn new(title: impl Into<SharedString>, examples: Vec<ComponentExample<T>>) -> Self {
113 Self {
114 title: title.into(),
115 examples,
116 }
117 }
118}
119
120/// Create a single example
121pub fn single_example<T>(variant_name: impl Into<SharedString>, example: T) -> ComponentExample<T> {
122 ComponentExample::new(variant_name, example)
123}
124
125/// Create a group of examples
126pub fn example_group<T>(
127 title: impl Into<SharedString>,
128 examples: Vec<ComponentExample<T>>,
129) -> ComponentExampleGroup<T> {
130 ComponentExampleGroup::new(title, examples)
131}