1use gpui::{
  2    AnyElement, App, IntoElement, Pixels, RenderOnce, SharedString, Window, div, pattern_slash,
  3    prelude::*, px, rems,
  4};
  5use theme::ActiveTheme;
  6
  7/// A single example of a component.
  8#[derive(IntoElement)]
  9pub struct ComponentExample {
 10    pub variant_name: SharedString,
 11    pub description: Option<SharedString>,
 12    pub element: AnyElement,
 13    pub width: Option<Pixels>,
 14}
 15
 16impl RenderOnce for ComponentExample {
 17    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
 18        div()
 19            .pt_2()
 20            .map(|this| {
 21                if let Some(width) = self.width {
 22                    this.w(width)
 23                } else {
 24                    this.w_full()
 25                }
 26            })
 27            .flex()
 28            .flex_col()
 29            .gap_3()
 30            .child(
 31                div()
 32                    .flex()
 33                    .flex_col()
 34                    .child(
 35                        div()
 36                            .child(self.variant_name.clone())
 37                            .text_size(rems(1.0))
 38                            .text_color(cx.theme().colors().text),
 39                    )
 40                    .when_some(self.description, |this, description| {
 41                        this.child(
 42                            div()
 43                                .text_size(rems(0.875))
 44                                .text_color(cx.theme().colors().text_muted)
 45                                .child(description),
 46                        )
 47                    }),
 48            )
 49            .child(
 50                div()
 51                    .min_h(px(100.))
 52                    .w_full()
 53                    .p_8()
 54                    .flex()
 55                    .items_center()
 56                    .justify_center()
 57                    .rounded_xl()
 58                    .border_1()
 59                    .border_color(cx.theme().colors().border.opacity(0.5))
 60                    .bg(pattern_slash(
 61                        cx.theme().colors().surface_background.opacity(0.25),
 62                        12.0,
 63                        12.0,
 64                    ))
 65                    .child(self.element),
 66            )
 67            .into_any_element()
 68    }
 69}
 70
 71impl ComponentExample {
 72    pub fn new(variant_name: impl Into<SharedString>, element: AnyElement) -> Self {
 73        Self {
 74            variant_name: variant_name.into(),
 75            element,
 76            description: None,
 77            width: None,
 78        }
 79    }
 80
 81    pub fn description(mut self, description: impl Into<SharedString>) -> Self {
 82        self.description = Some(description.into());
 83        self
 84    }
 85
 86    pub fn width(mut self, width: Pixels) -> Self {
 87        self.width = Some(width);
 88        self
 89    }
 90}
 91
 92/// A group of component examples.
 93#[derive(IntoElement)]
 94pub struct ComponentExampleGroup {
 95    pub title: Option<SharedString>,
 96    pub examples: Vec<ComponentExample>,
 97    pub width: Option<Pixels>,
 98    pub grow: bool,
 99    pub vertical: bool,
100}
101
102impl RenderOnce for ComponentExampleGroup {
103    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
104        div()
105            .flex_col()
106            .text_sm()
107            .text_color(cx.theme().colors().text_muted)
108            .map(|this| {
109                if let Some(width) = self.width {
110                    this.w(width)
111                } else {
112                    this.w_full()
113                }
114            })
115            .when_some(self.title, |this, title| {
116                this.gap_4().child(
117                    div()
118                        .flex()
119                        .items_center()
120                        .gap_3()
121                        .mt_4()
122                        .mb_1()
123                        .child(
124                            div()
125                                .flex_none()
126                                .text_size(px(10.))
127                                .child(title.to_uppercase()),
128                        )
129                        .child(
130                            div()
131                                .h_px()
132                                .w_full()
133                                .flex_1()
134                                .bg(cx.theme().colors().border),
135                        ),
136                )
137            })
138            .child(
139                div()
140                    .flex()
141                    .flex_col()
142                    .items_start()
143                    .w_full()
144                    .gap_6()
145                    .children(self.examples)
146                    .into_any_element(),
147            )
148            .into_any_element()
149    }
150}
151
152impl ComponentExampleGroup {
153    pub fn new(examples: Vec<ComponentExample>) -> Self {
154        Self {
155            title: None,
156            examples,
157            width: None,
158            grow: false,
159            vertical: false,
160        }
161    }
162    pub fn with_title(title: impl Into<SharedString>, examples: Vec<ComponentExample>) -> Self {
163        Self {
164            title: Some(title.into()),
165            examples,
166            width: None,
167            grow: false,
168            vertical: false,
169        }
170    }
171    pub fn width(mut self, width: Pixels) -> Self {
172        self.width = Some(width);
173        self
174    }
175    pub fn grow(mut self) -> Self {
176        self.grow = true;
177        self
178    }
179    pub fn vertical(mut self) -> Self {
180        self.vertical = true;
181        self
182    }
183}
184
185pub fn single_example(
186    variant_name: impl Into<SharedString>,
187    example: AnyElement,
188) -> ComponentExample {
189    ComponentExample::new(variant_name, example)
190}
191
192pub fn empty_example(variant_name: impl Into<SharedString>) -> ComponentExample {
193    ComponentExample::new(variant_name, div().w_full().text_center().items_center().text_xs().opacity(0.4).child("This space is intentionally left blank. It indicates a case that should render nothing.").into_any_element())
194}
195
196pub fn example_group(examples: Vec<ComponentExample>) -> ComponentExampleGroup {
197    ComponentExampleGroup::new(examples)
198}
199
200pub fn example_group_with_title(
201    title: impl Into<SharedString>,
202    examples: Vec<ComponentExample>,
203) -> ComponentExampleGroup {
204    ComponentExampleGroup::with_title(title, examples)
205}