Skip to main content

boltz_component/
component_layout.rs

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}