storybook/stories/
containers.rs

1use gpui::{div, prelude::*, px, rgb, Context, SharedString};
2use allui::prelude::*;
3
4use crate::Storybook;
5
6pub fn render_scrollview_story() -> impl IntoElement {
7    VStack::new()
8        .spacing(16.0)
9        .alignment(HorizontalAlignment::Leading)
10        .child(Text::new("ScrollView - Scrollable container for content:"))
11        .child(
12            VStack::new()
13                .spacing(16.0)
14                .child(Text::new("Vertical scroll (default):"))
15                .child(
16                    ScrollView::new("vertical-scroll")
17                        .axes(ScrollAxes::vertical())
18                        .child(VStack::new().spacing(8.0).children((1..=20).map(|i| {
19                            Text::new(format!("Item {}", i))
20                                .padding(12.0)
21                                .background(Color::tertiary_system_background())
22                                .corner_radius(4.0)
23                        })))
24                        .frame(Frame::size(300.0, 200.0))
25                        .background(Color::tertiary_system_background())
26                        .corner_radius(8.0),
27                )
28                .child(Text::new("Horizontal scroll:"))
29                .child(
30                    ScrollView::new("horizontal-scroll")
31                        .axes(ScrollAxes::horizontal())
32                        .child(HStack::new().spacing(8.0).children((1..=15).map(|i| {
33                            VStack::new()
34                                .child(div().size(px(60.0)).bg(rgb(0x007AFF)).rounded(px(8.0)))
35                                .child(Text::new(format!("{}", i)))
36                                .spacing(4.0)
37                                .padding(8.0)
38                        })))
39                        .frame(Frame::size(400.0, 120.0))
40                        .background(Color::tertiary_system_background())
41                        .corner_radius(8.0),
42                ),
43        )
44}
45
46pub fn render_list_story() -> impl IntoElement {
47    VStack::new()
48        .spacing(16.0)
49        .alignment(HorizontalAlignment::Leading)
50        .child(Text::new("List & Section - iOS-style grouped lists:"))
51        .child(
52            HStack::new()
53                .spacing(24.0)
54                .alignment(VerticalAlignment::Top)
55                .child(
56                    VStack::new()
57                        .spacing(8.0)
58                        .child(Text::new("Inset Grouped (Dark):").foreground_color(Color::gray()))
59                        .child(
60                            List::new("settings-list")
61                                .list_style(ListStyle::inset_grouped())
62                                .child(
63                                    Section::new()
64                                        .header("Account")
65                                        .child(Text::new("Profile"))
66                                        .child(Text::new("Privacy"))
67                                        .child(Text::new("Security")),
68                                )
69                                .child(
70                                    Section::new()
71                                        .header("Preferences")
72                                        .footer("Customize your experience")
73                                        .child(Text::new("Notifications"))
74                                        .child(Text::new("Appearance"))
75                                        .child(Text::new("Language")),
76                                )
77                                .frame(Frame::size(280.0, 380.0))
78                                .background(Color::system_background())
79                                .corner_radius(12.0),
80                        ),
81                )
82                .child(
83                    VStack::new()
84                        .spacing(8.0)
85                        .child(Text::new("Plain (Dark):").foreground_color(Color::gray()))
86                        .child(
87                            List::new("plain-list")
88                                .list_style(ListStyle::plain())
89                                .child(
90                                    Section::new()
91                                        .child(Text::new("First Item"))
92                                        .child(Text::new("Second Item"))
93                                        .child(Text::new("Third Item")),
94                                )
95                                .frame(Frame::size(200.0, 200.0))
96                                .background(Color::system_background())
97                                .corner_radius(8.0),
98                        ),
99                ),
100        )
101}
102
103pub fn render_foreach_story() -> impl IntoElement {
104    let fruits = vec!["Apple", "Banana", "Cherry", "Date", "Elderberry"];
105    let numbers = vec![1, 2, 3, 4, 5];
106
107    VStack::new()
108        .spacing(16.0)
109        .alignment(HorizontalAlignment::Leading)
110        .child(Text::new("ForEach - Iterate over collections:"))
111        .child(
112            HStack::new()
113                .spacing(32.0)
114                .alignment(VerticalAlignment::Top)
115                .child(
116                    VStack::new()
117                        .spacing(8.0)
118                        .child(Text::new("String list:").foreground_color(Color::gray()))
119                        .child(
120                            VStack::new()
121                                .spacing(4.0)
122                                .children(ForEach::new(fruits, |fruit| {
123                                    HStack::new()
124                                        .spacing(8.0)
125                                        .child(div().size(px(8.0)).bg(rgb(0x34C759)).rounded_full())
126                                        .child(Text::new(*fruit))
127                                }))
128                                .padding(16.0)
129                                .background(Color::tertiary_system_background())
130                                .corner_radius(8.0),
131                        ),
132                )
133                .child(
134                    VStack::new()
135                        .spacing(8.0)
136                        .child(Text::new("Number grid:").foreground_color(Color::gray()))
137                        .child(
138                            HStack::new()
139                                .spacing(8.0)
140                                .children(ForEach::new(numbers, |num| {
141                                    Text::new(format!("{}", num))
142                                        .padding(16.0)
143                                        .background(Color::blue())
144                                        .corner_radius(8.0)
145                                }))
146                                .padding(16.0)
147                                .background(Color::tertiary_system_background())
148                                .corner_radius(8.0),
149                        ),
150                ),
151        )
152        .child(
153            Text::new("ForEach works with VStack.children(), HStack.children(), etc.")
154                .foreground_color(Color::gray()),
155        )
156}
157
158pub fn render_conditional_story(
159    storybook: &Storybook,
160    cx: &mut Context<Storybook>,
161) -> impl IntoElement {
162    let show_content = storybook.show_content;
163    let selected_fruit = storybook.selected_fruit;
164    let entity = cx.entity().clone();
165    let entity2 = cx.entity().clone();
166
167    let fruits = ["Apple", "Banana", "Cherry"];
168
169    VStack::new()
170        .spacing(16.0)
171        .alignment(HorizontalAlignment::Leading)
172        .child(Text::new("If - Conditional rendering:"))
173        .child(
174            VStack::new()
175                .spacing(12.0)
176                .child(HStack::new().spacing(12.0).child(Toggle::new_with_handler(
177                    "Show Content",
178                    show_content,
179                    cx.listener(|this: &mut Storybook, checked: &bool, _window, cx| {
180                        this.show_content = *checked;
181                        cx.notify();
182                    }),
183                )))
184                .child(
185                    If::new(show_content)
186                        .then(|| {
187                            Text::new("Content is visible!")
188                                .padding(16.0)
189                                .background(Color::green())
190                                .corner_radius(8.0)
191                        })
192                        .otherwise(|| {
193                            Text::new("Content is hidden")
194                                .padding(16.0)
195                                .background(Color::red())
196                                .corner_radius(8.0)
197                        }),
198                )
199                .padding(16.0)
200                .background(Color::tertiary_system_background())
201                .corner_radius(8.0),
202        )
203        .child(Text::new("IfLet - Render when Option is Some:"))
204        .child(
205            VStack::new()
206                .spacing(12.0)
207                .child(
208                    HStack::new()
209                        .spacing(8.0)
210                        .children(fruits.iter().enumerate().map(|(idx, fruit)| {
211                            let is_selected = selected_fruit == Some(idx);
212                            let entity = entity.clone();
213                            Text::new(*fruit)
214                                .padding(8.0)
215                                .background(if is_selected {
216                                    Color::blue()
217                                } else {
218                                    Color::secondary()
219                                })
220                                .corner_radius(4.0)
221                                .on_tap_gesture_with(
222                                    SharedString::from(format!("fruit-{}", idx)),
223                                    move |_, _, cx| {
224                                        entity.update(cx, |this, cx| {
225                                            this.selected_fruit = Some(idx);
226                                            cx.notify();
227                                        });
228                                    },
229                                )
230                        }))
231                        .child(
232                            Text::new("None")
233                                .padding(8.0)
234                                .background(if selected_fruit.is_none() {
235                                    Color::blue()
236                                } else {
237                                    Color::secondary()
238                                })
239                                .corner_radius(4.0)
240                                .on_tap_gesture_with("fruit-none", move |_, _, cx| {
241                                    entity2.update(cx, |this, cx| {
242                                        this.selected_fruit = None;
243                                        cx.notify();
244                                    });
245                                }),
246                        ),
247                )
248                .child(IfLet::new(selected_fruit, move |idx| {
249                    let fruit_names = ["Apple", "Banana", "Cherry"];
250                    Text::new(format!("Selected: {} (index {})", fruit_names[*idx], idx))
251                        .padding(12.0)
252                        .background(Color::green())
253                        .corner_radius(8.0)
254                }))
255                .child(
256                    If::new(selected_fruit.is_none())
257                        .then(|| {
258                            Text::new("No fruit selected")
259                                .padding(12.0)
260                                .foreground_color(Color::gray())
261                        })
262                        .otherwise(EmptyView::new),
263                )
264                .padding(16.0)
265                .background(Color::tertiary_system_background())
266                .corner_radius(8.0),
267        )
268}