component_preview.rs

  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}