storybook/stories/
components.rs

1use gpui::{prelude::*, Context, Entity};
2use allui::prelude::*;
3
4use crate::Storybook;
5
6pub fn render_text_story() -> impl IntoElement {
7    VStack::new()
8        .spacing(16.0)
9        .alignment(HorizontalAlignment::Leading)
10        .child(Text::new("Text Styles").font(Font::headline()))
11        .child(
12            VStack::new()
13                .spacing(8.0)
14                .alignment(HorizontalAlignment::Leading)
15                .child(Text::new("Large Title").font(Font::large_title()))
16                .child(Text::new("Title").font(Font::title()))
17                .child(Text::new("Headline").font(Font::headline()))
18                .child(Text::new("Body text").font(Font::body()))
19                .child(
20                    Text::new("Caption")
21                        .font(Font::caption())
22                        .foreground_color(Color::gray()),
23                )
24                .padding(16.0)
25                .background(Color::tertiary_system_background())
26                .corner_radius(8.0),
27        )
28        .child(Text::new("Font Variations").font(Font::headline()))
29        .child(
30            VStack::new()
31                .spacing(8.0)
32                .alignment(HorizontalAlignment::Leading)
33                .child(Text::new("Bold text").bold())
34                .child(Text::new("Italic text").italic())
35                .child(Text::new("Bold + Italic").bold().italic())
36                .child(Text::new("Colored text").foreground_color(Color::blue()))
37                .padding(16.0)
38                .background(Color::tertiary_system_background())
39                .corner_radius(8.0),
40        )
41        .child(Text::new("Font Design").font(Font::headline()))
42        .child(
43            VStack::new()
44                .spacing(8.0)
45                .alignment(HorizontalAlignment::Leading)
46                .child(Text::new("Default: The quick brown fox"))
47                .child(
48                    Text::new("Monospaced: let x = 42;")
49                        .font(Font::body().monospaced()),
50                )
51                .child(
52                    Text::new("Serif: Classical typography")
53                        .font(Font::body().design(FontDesign::Serif)),
54                )
55                .child(
56                    Text::new("Rounded: Friendly appearance")
57                        .font(Font::body().design(FontDesign::Rounded)),
58                )
59                .padding(16.0)
60                .background(Color::tertiary_system_background())
61                .corner_radius(8.0),
62        )
63        .child(Text::new("Text Decorations").font(Font::headline()))
64        .child(
65            VStack::new()
66                .spacing(8.0)
67                .alignment(HorizontalAlignment::Leading)
68                .child(Text::new("Normal text"))
69                .child(Text::new("Strikethrough text").strikethrough(true))
70                .child(
71                    Text::new("Strikethrough + Red")
72                        .strikethrough(true)
73                        .foreground_color(Color::red()),
74                )
75                .padding(16.0)
76                .background(Color::tertiary_system_background())
77                .corner_radius(8.0),
78        )
79        .child(Text::new("Line Limit").font(Font::headline()))
80        .child(
81            VStack::new()
82                .spacing(12.0)
83                .alignment(HorizontalAlignment::Leading)
84                .child(
85                    VStack::new()
86                        .spacing(4.0)
87                        .alignment(HorizontalAlignment::Leading)
88                        .child(Text::new(".line_limit(1):").foreground_color(Color::gray()))
89                        .child(
90                            Text::new("This is a very long text that should be truncated to a single line when line_limit is set to 1.")
91                                .line_limit(1)
92                                .frame_width(300.0)
93                        ),
94                )
95                .child(
96                    VStack::new()
97                        .spacing(4.0)
98                        .alignment(HorizontalAlignment::Leading)
99                        .child(Text::new(".line_limit(2):").foreground_color(Color::gray()))
100                        .child(
101                            Text::new("This is a very long text that demonstrates the line_limit modifier. It should wrap to two lines maximum and then truncate any remaining content.")
102                                .line_limit(2)
103                                .frame_width(300.0)
104                        ),
105                )
106                .padding(16.0)
107                .background(Color::tertiary_system_background())
108                .corner_radius(8.0),
109        )
110}
111
112pub fn render_button_story() -> impl IntoElement {
113    VStack::new()
114        .spacing(12.0)
115        .alignment(HorizontalAlignment::Leading)
116        .child(
117            Button::new("Bordered Prominent", || {
118                println!("Bordered Prominent clicked!");
119            })
120            .button_style(ButtonStyle::BorderedProminent),
121        )
122        .child(
123            Button::new("Bordered", || {
124                println!("Bordered clicked!");
125            })
126            .button_style(ButtonStyle::Bordered),
127        )
128        .child(
129            Button::new("Plain", || {
130                println!("Plain clicked!");
131            })
132            .button_style(ButtonStyle::Plain),
133        )
134        .child(
135            Button::new("Disabled", || {})
136                .button_style(ButtonStyle::BorderedProminent)
137                .disabled(true),
138        )
139}
140
141pub fn render_modifiers_story() -> impl IntoElement {
142    VStack::new()
143        .spacing(16.0)
144        .alignment(HorizontalAlignment::Leading)
145        .child(Text::new("Modifier order matters:"))
146        .child(
147            HStack::new()
148                .spacing(32.0)
149                .child(
150                    VStack::new()
151                        .spacing(8.0)
152                        .child(Text::new(".padding().background()"))
153                        .child(Text::new("Hello").padding(16.0).background(Color::red())),
154                )
155                .child(
156                    VStack::new()
157                        .spacing(8.0)
158                        .child(Text::new(".background().padding()"))
159                        .child(Text::new("Hello").background(Color::red()).padding(16.0)),
160                ),
161        )
162        .child(Text::new("Chained modifiers:"))
163        .child(
164            Text::new("Styled Text")
165                .padding(12.0)
166                .background(Color::blue())
167                .corner_radius(8.0)
168                .padding(4.0)
169                .background(Color::green())
170                .corner_radius(12.0),
171        )
172}
173
174pub fn render_toggle_story(storybook: &Storybook, cx: &mut Context<Storybook>) -> impl IntoElement {
175    let toggle_value = storybook.toggle_value;
176
177    VStack::new()
178        .spacing(16.0)
179        .alignment(HorizontalAlignment::Leading)
180        .child(Text::new("Toggle - Boolean switch component:"))
181        .child(
182            VStack::new()
183                .spacing(12.0)
184                .child(
185                    HStack::new()
186                        .spacing(12.0)
187                        .child(Toggle::new_with_handler(
188                            "Dark Mode",
189                            toggle_value,
190                            cx.listener(|this: &mut Storybook, checked: &bool, _window, cx| {
191                                this.toggle_value = *checked;
192                                cx.notify();
193                            }),
194                        ))
195                        .child(
196                            Text::new(if toggle_value { "ON" } else { "OFF" }).foreground_color(
197                                if toggle_value {
198                                    Color::green()
199                                } else {
200                                    Color::gray()
201                                },
202                            ),
203                        ),
204                )
205                .child(Toggle::new("Disabled Toggle", true, |_| {}).disabled(true))
206                .padding(16.0)
207                .background(Color::tertiary_system_background())
208                .corner_radius(8.0),
209        )
210        .child(
211            Text::new("Note: Toggle wraps gpui-component's Switch.")
212                .foreground_color(Color::gray()),
213        )
214}
215
216pub fn render_tap_gesture_story(
217    storybook: &Storybook,
218    cx: &mut Context<Storybook>,
219) -> impl IntoElement {
220    let tap_count = storybook.tap_count;
221    let entity = cx.entity().clone();
222
223    VStack::new()
224        .spacing(16.0)
225        .alignment(HorizontalAlignment::Leading)
226        .child(Text::new("on_tap_gesture - Add tap handlers to any view:"))
227        .child(
228            VStack::new()
229                .spacing(12.0)
230                .child(
231                    Text::new("Tap me!")
232                        .padding(16.0)
233                        .background(Color::blue())
234                        .corner_radius(8.0)
235                        .on_tap_gesture_with("tap-me-button", move |_event, _window, cx| {
236                            entity.update(cx, |this, cx| {
237                                this.tap_count += 1;
238                                cx.notify();
239                            });
240                        }),
241                )
242                .child(
243                    Text::new(format!("Tap count: {}", tap_count)).foreground_color(Color::green()),
244                )
245                .padding(16.0)
246                .background(Color::tertiary_system_background())
247                .corner_radius(8.0),
248        )
249        .child(
250            Text::new("Any view can be made tappable with .on_tap_gesture()")
251                .foreground_color(Color::gray()),
252        )
253}
254
255pub fn render_textfields_story(
256    text_input: &Entity<InputState>,
257    text_input_cleanable: &Entity<InputState>,
258    password_input: &Entity<InputState>,
259) -> impl IntoElement {
260    VStack::new()
261        .spacing(16.0)
262        .alignment(HorizontalAlignment::Leading)
263        .child(Text::new("TextField - Single-line text input:"))
264        .child(
265            VStack::new()
266                .spacing(12.0)
267                .alignment(HorizontalAlignment::Leading)
268                .child(Text::new("Basic TextField:").foreground_color(Color::gray()))
269                .child(TextField::new(text_input).frame_width(300.0))
270                .child(
271                    Text::new("TextField with cleanable (x button):")
272                        .foreground_color(Color::gray()),
273                )
274                .child(
275                    TextField::new(text_input_cleanable)
276                        .cleanable(true)
277                        .frame_width(300.0),
278                )
279                .padding(16.0)
280                .background(Color::tertiary_system_background())
281                .corner_radius(8.0),
282        )
283        .child(Text::new("SecureField - Password input (masked):"))
284        .child(
285            VStack::new()
286                .spacing(12.0)
287                .alignment(HorizontalAlignment::Leading)
288                .child(
289                    SecureField::new(password_input)
290                        .show_toggle(true)
291                        .frame_width(300.0),
292                )
293                .padding(16.0)
294                .background(Color::tertiary_system_background())
295                .corner_radius(8.0),
296        )
297        .child(
298            Text::new("Note: State is managed via Entity<InputState>")
299                .foreground_color(Color::gray()),
300        )
301}
302
303pub fn render_sliders_story(
304    slider_state: &Entity<SliderState>,
305    slider_value: f32,
306) -> impl IntoElement {
307    VStack::new()
308        .spacing(16.0)
309        .alignment(HorizontalAlignment::Leading)
310        .child(Text::new("Slider - Range value selection:"))
311        .child(
312            VStack::new()
313                .spacing(12.0)
314                .alignment(HorizontalAlignment::Leading)
315                .child(Text::new("Horizontal slider:").foreground_color(Color::gray()))
316                .child(
317                    HStack::new()
318                        .spacing(16.0)
319                        .child(Slider::new(slider_state).frame_width(200.0))
320                        .child(
321                            Text::new(format!("{:.0}", slider_value))
322                                .foreground_color(Color::green()),
323                        ),
324                )
325                .child(
326                    Text::new(format!("Current value: {:.1}", slider_value))
327                        .foreground_color(Color::gray()),
328                )
329                .padding(16.0)
330                .background(Color::tertiary_system_background())
331                .corner_radius(8.0),
332        )
333        .child(
334            Text::new("Note: Subscribe to SliderEvent for value changes")
335                .foreground_color(Color::gray()),
336        )
337}
338
339pub fn render_more_inputs_story(
340    text_editor_input: &Entity<InputState>,
341    stepper_input: &Entity<InputState>,
342    stepper_value: i32,
343) -> impl IntoElement {
344    VStack::new()
345        .spacing(16.0)
346        .alignment(HorizontalAlignment::Leading)
347        .child(Text::new("TextEditor - Multi-line text input:"))
348        .child(
349            VStack::new()
350                .spacing(8.0)
351                .alignment(HorizontalAlignment::Leading)
352                .child(
353                    TextEditor::new(text_editor_input)
354                        .height(150.0)
355                        .frame_width(300.0),
356                )
357                .child(
358                    Text::new("Note: Create InputState with .multi_line(true)")
359                        .foreground_color(Color::gray()),
360                )
361                .padding(16.0)
362                .background(Color::tertiary_system_background())
363                .corner_radius(8.0),
364        )
365        .child(Text::new("Stepper - Increment/decrement control:"))
366        .child(
367            VStack::new()
368                .spacing(12.0)
369                .alignment(HorizontalAlignment::Leading)
370                .child(
371                    HStack::new()
372                        .spacing(16.0)
373                        .child(Text::new(format!("Value: {}", stepper_value)))
374                        .child(Stepper::new(stepper_input)),
375                )
376                .child(
377                    Text::new("Stepper triggers increment/decrement callbacks")
378                        .foreground_color(Color::gray()),
379                )
380                .padding(16.0)
381                .background(Color::tertiary_system_background())
382                .corner_radius(8.0),
383        )
384        .child(Text::new("Picker - Selection from options:"))
385        .child(
386            VStack::new()
387                .spacing(8.0)
388                .alignment(HorizontalAlignment::Leading)
389                .child(
390                    Text::new(
391                        "Picker requires PickerState + PickerDelegate. See picker.rs for usage.",
392                    )
393                    .foreground_color(Color::gray()),
394                )
395                .child(
396                    VStack::new()
397                        .spacing(4.0)
398                        .child(Text::new("Usage:").bold())
399                        .child(
400                            Text::new("let state = PickerState::new(...);")
401                                .font(Font::caption())
402                                .foreground_color(Color::tertiary_label()),
403                        )
404                        .child(
405                            Text::new("Picker::new(\"id\", &state).build(window, cx)")
406                                .font(Font::caption())
407                                .foreground_color(Color::tertiary_label()),
408                        )
409                        .padding(12.0)
410                        .background(Color::secondary_system_background())
411                        .corner_radius(8.0),
412                )
413                .padding(16.0)
414                .background(Color::tertiary_system_background())
415                .corner_radius(8.0),
416        )
417}
418
419pub fn render_display_components_story() -> impl IntoElement {
420    VStack::new()
421        .spacing(16.0)
422        .alignment(HorizontalAlignment::Leading)
423        .child(Text::new("Divider").font(Font::headline()))
424        .child(
425            VStack::new()
426                .spacing(8.0)
427                .alignment(HorizontalAlignment::Leading)
428                .child(Text::new("Item above"))
429                .child(Divider::new())
430                .child(Text::new("Item below"))
431                .padding(16.0)
432                .background(Color::tertiary_system_background())
433                .corner_radius(8.0)
434                .frame_width(300.0),
435        )
436        .child(Text::new("Label").font(Font::headline()))
437        .child(
438            VStack::new()
439                .spacing(8.0)
440                .alignment(HorizontalAlignment::Leading)
441                .child(Label::new("star.fill", "Favorites"))
442                .child(Label::new("folder", "Documents"))
443                .child(Label::new("gear", "Settings"))
444                .padding(16.0)
445                .background(Color::tertiary_system_background())
446                .corner_radius(8.0),
447        )
448        .child(Text::new("Link").font(Font::headline()))
449        .child(
450            VStack::new()
451                .spacing(8.0)
452                .alignment(HorizontalAlignment::Leading)
453                .child(Link::new("Visit Allui", || {
454                    println!("Link clicked: Visit Allui");
455                }))
456                .child(Link::new("Documentation", || {
457                    println!("Link clicked: Documentation");
458                }))
459                .padding(16.0)
460                .background(Color::tertiary_system_background())
461                .corner_radius(8.0),
462        )
463        .child(Text::new("ProgressView").font(Font::headline()))
464        .child(
465            VStack::new()
466                .spacing(12.0)
467                .alignment(HorizontalAlignment::Leading)
468                .child(Text::new("Linear (determinate):").foreground_color(Color::gray()))
469                .child(
470                    ProgressView::new()
471                        .progress_view_style(ProgressViewStyle::Linear)
472                        .value(0.65)
473                        .frame_width(200.0),
474                )
475                .child(Text::new("Circular (indeterminate):").foreground_color(Color::gray()))
476                .child(ProgressView::new().progress_view_style(ProgressViewStyle::Circular))
477                .padding(16.0)
478                .background(Color::tertiary_system_background())
479                .corner_radius(8.0),
480        )
481        .child(Text::new("Image").font(Font::headline()))
482        .child(
483            VStack::new()
484                .spacing(8.0)
485                .alignment(HorizontalAlignment::Leading)
486                .child(
487                    Text::new("Note: Image currently renders placeholder text.")
488                        .foreground_color(Color::gray()),
489                )
490                .child(Image::new("photo.jpg").frame_size(100.0, 100.0))
491                .padding(16.0)
492                .background(Color::tertiary_system_background())
493                .corner_radius(8.0),
494        )
495}