storybook/stories/
containers.rs1use 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}