200 lines · 6.0 KB · Rust
RawDownload
#![allow(missing_docs)]use crate::prelude::*;use gpui::{AnyElement, SharedString}; /// Which side of the preview to show labels on#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]pub enum ExampleLabelSide {    /// Left side    Left,    /// Right side    Right,    #[default]    /// Top side    Top,    /// Bottom side    Bottom,} /// Implement this trait to enable rich UI previews with metadata in the Theme Preview tool.pub trait ComponentPreview: IntoElement {    fn title() -> &'static str {        std::any::type_name::<Self>()    }     fn description() -> impl Into<Option<&'static str>> {        None    }     fn example_label_side() -> ExampleLabelSide {        ExampleLabelSide::default()    }     fn examples(_cx: &mut WindowContext) -> Vec<ComponentExampleGroup<Self>>;     fn custom_example(_cx: &WindowContext) -> impl Into<Option<AnyElement>> {        None::<AnyElement>    }     fn component_previews(cx: &mut WindowContext) -> Vec<AnyElement> {        Self::examples(cx)            .into_iter()            .map(|example| Self::render_example_group(example))            .collect()    }     fn render_component_previews(cx: &mut WindowContext) -> AnyElement {        let title = Self::title();        let (source, title) = title            .rsplit_once("::")            .map_or((None, title), |(s, t)| (Some(s), t));        let description = Self::description().into();         v_flex()            .w_full()            .gap_6()            .p_4()            .border_1()            .border_color(cx.theme().colors().border)            .rounded_md()            .child(                v_flex()                    .gap_1()                    .child(                        h_flex()                            .gap_1()                            .child(Headline::new(title).size(HeadlineSize::Small))                            .when_some(source, |this, source| {                                this.child(Label::new(format!("({})", source)).color(Color::Muted))                            }),                    )                    .when_some(description, |this, description| {                        this.child(                            div()                                .text_ui_sm(cx)                                .text_color(cx.theme().colors().text_muted)                                .max_w(px(600.0))                                .child(description),                        )                    }),            )            .when_some(Self::custom_example(cx).into(), |this, custom_example| {                this.child(custom_example)            })            .children(Self::component_previews(cx))            .into_any_element()    }     fn render_example_group(group: ComponentExampleGroup<Self>) -> AnyElement {        v_flex()            .gap_6()            .when(group.grow, |this| this.w_full().flex_1())            .when_some(group.title, |this, title| {                this.child(Label::new(title).size(LabelSize::Small))            })            .child(                h_flex()                    .w_full()                    .gap_6()                    .children(group.examples.into_iter().map(Self::render_example))                    .into_any_element(),            )            .into_any_element()    }     fn render_example(example: ComponentExample<Self>) -> AnyElement {        let base = div().flex();         let base = match Self::example_label_side() {            ExampleLabelSide::Right => base.flex_row(),            ExampleLabelSide::Left => base.flex_row_reverse(),            ExampleLabelSide::Bottom => base.flex_col(),            ExampleLabelSide::Top => base.flex_col_reverse(),        };         base.gap_1()            .when(example.grow, |this| this.flex_1())            .child(example.element)            .child(                Label::new(example.variant_name)                    .size(LabelSize::XSmall)                    .color(Color::Muted),            )            .into_any_element()    }} /// A single example of a component.pub struct ComponentExample<T> {    variant_name: SharedString,    element: T,    grow: bool,} impl<T> ComponentExample<T> {    /// Create a new example with the given variant name and example value.    pub fn new(variant_name: impl Into<SharedString>, example: T) -> Self {        Self {            variant_name: variant_name.into(),            element: example,            grow: false,        }    }     /// Set the example to grow to fill the available horizontal space.    pub fn grow(mut self) -> Self {        self.grow = true;        self    }} /// A group of component examples.pub struct ComponentExampleGroup<T> {    pub title: Option<SharedString>,    pub examples: Vec<ComponentExample<T>>,    pub grow: bool,} impl<T> ComponentExampleGroup<T> {    /// Create a new group of examples with the given title.    pub fn new(examples: Vec<ComponentExample<T>>) -> Self {        Self {            title: None,            examples,            grow: false,        }    }     /// Create a new group of examples with the given title.    pub fn with_title(title: impl Into<SharedString>, examples: Vec<ComponentExample<T>>) -> Self {        Self {            title: Some(title.into()),            examples,            grow: false,        }    }     /// Set the group to grow to fill the available horizontal space.    pub fn grow(mut self) -> Self {        self.grow = true;        self    }} /// Create a single examplepub fn single_example<T>(variant_name: impl Into<SharedString>, example: T) -> ComponentExample<T> {    ComponentExample::new(variant_name, example)} /// Create a group of examples without a titlepub fn example_group<T>(examples: Vec<ComponentExample<T>>) -> ComponentExampleGroup<T> {    ComponentExampleGroup::new(examples)} /// Create a group of examples with a titlepub fn example_group_with_title<T>(    title: impl Into<SharedString>,    examples: Vec<ComponentExample<T>>,) -> ComponentExampleGroup<T> {    ComponentExampleGroup::with_title(title, examples)}