#![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)}
#![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 example
pub 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 title
pub fn example_group<T>(examples: Vec<ComponentExample<T>>) -> ComponentExampleGroup<T> {
ComponentExampleGroup::new(examples)
}
/// Create a group of examples with a title
pub fn example_group_with_title<T>(
title: impl Into<SharedString>,
examples: Vec<ComponentExample<T>>,
) -> ComponentExampleGroup<T> {
ComponentExampleGroup::with_title(title, examples)
}