Detailed changes
@@ -21,6 +21,7 @@ pub enum ComponentStory {
IconButton,
Keybinding,
Label,
+ List,
ListItem,
Scroll,
Text,
@@ -40,6 +41,7 @@ impl ComponentStory {
Self::IconButton => cx.build_view(|_| ui::IconButtonStory).into(),
Self::Keybinding => cx.build_view(|_| ui::KeybindingStory).into(),
Self::Label => cx.build_view(|_| ui::LabelStory).into(),
+ Self::List => cx.build_view(|_| ui::ListStory).into(),
Self::ListItem => cx.build_view(|_| ui::ListItemStory).into(),
Self::Scroll => ScrollStory::view(cx).into(),
Self::Text => TextStory::view(cx).into(),
@@ -171,7 +171,7 @@ impl Render for ContextMenu {
.child(
List::new().children(self.items.iter().enumerate().map(
|(ix, item)| match item {
- ContextMenuItem::Separator => ListSeparator::new().into_any_element(),
+ ContextMenuItem::Separator => ListSeparator.into_any_element(),
ContextMenuItem::Header(header) => {
ListSubHeader::new(header.clone()).into_any_element()
}
@@ -1,249 +1,46 @@
-use std::rc::Rc;
+mod list_header;
+mod list_item;
+mod list_separator;
+mod list_sub_header;
-use gpui::{
- div, px, AnyElement, ClickEvent, Div, ImageSource, IntoElement, MouseButton, MouseDownEvent,
- Pixels, Stateful, StatefulInteractiveElement,
-};
+use gpui::{AnyElement, Div};
use smallvec::SmallVec;
-use crate::{
- disclosure_control, h_stack, v_stack, Avatar, Icon, IconButton, IconElement, IconSize, Label,
- Toggle,
-};
-use crate::{prelude::*, GraphicSlot};
+use crate::prelude::*;
+use crate::{v_stack, Label, Toggle};
-pub enum ListHeaderMeta {
- Tools(Vec<IconButton>),
- // TODO: This should be a button
- Button(Label),
- Text(Label),
-}
-
-#[derive(IntoElement)]
-pub struct ListHeader {
- label: SharedString,
- left_icon: Option<Icon>,
- meta: Option<ListHeaderMeta>,
- toggle: Toggle,
- on_toggle: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
- inset: bool,
- selected: bool,
-}
-
-impl ListHeader {
- pub fn new(label: impl Into<SharedString>) -> Self {
- Self {
- label: label.into(),
- left_icon: None,
- meta: None,
- inset: false,
- toggle: Toggle::NotToggleable,
- on_toggle: None,
- selected: false,
- }
- }
-
- pub fn toggle(mut self, toggle: Toggle) -> Self {
- self.toggle = toggle;
- self
- }
-
- pub fn on_toggle(
- mut self,
- on_toggle: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
- ) -> Self {
- self.on_toggle = Some(Rc::new(on_toggle));
- self
- }
-
- pub fn left_icon(mut self, left_icon: Option<Icon>) -> Self {
- self.left_icon = left_icon;
- self
- }
-
- pub fn right_button(self, button: IconButton) -> Self {
- self.meta(Some(ListHeaderMeta::Tools(vec![button])))
- }
-
- pub fn meta(mut self, meta: Option<ListHeaderMeta>) -> Self {
- self.meta = meta;
- self
- }
-
- pub fn selected(mut self, selected: bool) -> Self {
- self.selected = selected;
- self
- }
-}
-
-impl RenderOnce for ListHeader {
- type Rendered = Div;
-
- fn render(self, cx: &mut WindowContext) -> Self::Rendered {
- let disclosure_control = disclosure_control(self.toggle, self.on_toggle);
-
- let meta = match self.meta {
- Some(ListHeaderMeta::Tools(icons)) => div().child(
- h_stack()
- .gap_2()
- .items_center()
- .children(icons.into_iter().map(|i| i.color(Color::Muted))),
- ),
- Some(ListHeaderMeta::Button(label)) => div().child(label),
- Some(ListHeaderMeta::Text(label)) => div().child(label),
- None => div(),
- };
-
- h_stack()
- .w_full()
- .bg(cx.theme().colors().surface_background)
- .relative()
- .child(
- div()
- .h_5()
- .when(self.inset, |this| this.px_2())
- .when(self.selected, |this| {
- this.bg(cx.theme().colors().ghost_element_selected)
- })
- .flex()
- .flex_1()
- .items_center()
- .justify_between()
- .w_full()
- .gap_1()
- .child(
- h_stack()
- .gap_1()
- .child(
- div()
- .flex()
- .gap_1()
- .items_center()
- .children(self.left_icon.map(|i| {
- IconElement::new(i)
- .color(Color::Muted)
- .size(IconSize::Small)
- }))
- .child(Label::new(self.label.clone()).color(Color::Muted)),
- )
- .child(disclosure_control),
- )
- .child(meta),
- )
- }
-}
-
-#[derive(IntoElement, Clone)]
-pub struct ListSubHeader {
- label: SharedString,
- left_icon: Option<Icon>,
- inset: bool,
-}
-
-impl ListSubHeader {
- pub fn new(label: impl Into<SharedString>) -> Self {
- Self {
- label: label.into(),
- left_icon: None,
- inset: false,
- }
- }
-
- pub fn left_icon(mut self, left_icon: Option<Icon>) -> Self {
- self.left_icon = left_icon;
- self
- }
-}
-
-impl RenderOnce for ListSubHeader {
- type Rendered = Div;
-
- fn render(self, _cx: &mut WindowContext) -> Self::Rendered {
- h_stack().flex_1().w_full().relative().py_1().child(
- div()
- .h_6()
- .when(self.inset, |this| this.px_2())
- .flex()
- .flex_1()
- .w_full()
- .gap_1()
- .items_center()
- .justify_between()
- .child(
- div()
- .flex()
- .gap_1()
- .items_center()
- .children(self.left_icon.map(|i| {
- IconElement::new(i)
- .color(Color::Muted)
- .size(IconSize::Small)
- }))
- .child(Label::new(self.label.clone()).color(Color::Muted)),
- ),
- )
- }
-}
+pub use list_header::*;
+pub use list_item::*;
+pub use list_separator::*;
+pub use list_sub_header::*;
#[derive(IntoElement)]
-pub struct ListItem {
- id: ElementId,
- selected: bool,
- // TODO: Reintroduce this
- // disclosure_control_style: DisclosureControlVisibility,
- indent_level: usize,
- indent_step_size: Pixels,
- left_slot: Option<GraphicSlot>,
+pub struct List {
+ /// Message to display when the list is empty
+ /// Defaults to "No items"
+ empty_message: SharedString,
+ header: Option<ListHeader>,
toggle: Toggle,
- inset: bool,
- on_click: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
- on_toggle: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
- on_secondary_mouse_down: Option<Rc<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>,
children: SmallVec<[AnyElement; 2]>,
}
-impl ListItem {
- pub fn new(id: impl Into<ElementId>) -> Self {
+impl List {
+ pub fn new() -> Self {
Self {
- id: id.into(),
- selected: false,
- indent_level: 0,
- indent_step_size: px(12.),
- left_slot: None,
+ empty_message: "No items".into(),
+ header: None,
toggle: Toggle::NotToggleable,
- inset: false,
- on_click: None,
- on_secondary_mouse_down: None,
- on_toggle: None,
children: SmallVec::new(),
}
}
- pub fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
- self.on_click = Some(Rc::new(handler));
- self
- }
-
- pub fn on_secondary_mouse_down(
- mut self,
- handler: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
- ) -> Self {
- self.on_secondary_mouse_down = Some(Rc::new(handler));
- self
- }
-
- pub fn inset(mut self, inset: bool) -> Self {
- self.inset = inset;
- self
- }
-
- pub fn indent_level(mut self, indent_level: usize) -> Self {
- self.indent_level = indent_level;
+ pub fn empty_message(mut self, empty_message: impl Into<SharedString>) -> Self {
+ self.empty_message = empty_message.into();
self
}
- pub fn indent_step_size(mut self, indent_step_size: Pixels) -> Self {
- self.indent_step_size = indent_step_size;
+ pub fn header(mut self, header: ListHeader) -> Self {
+ self.header = Some(header);
self
}
@@ -251,125 +48,14 @@ impl ListItem {
self.toggle = toggle;
self
}
-
- pub fn on_toggle(
- mut self,
- on_toggle: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
- ) -> Self {
- self.on_toggle = Some(Rc::new(on_toggle));
- self
- }
-
- pub fn selected(mut self, selected: bool) -> Self {
- self.selected = selected;
- self
- }
-
- pub fn left_content(mut self, left_content: GraphicSlot) -> Self {
- self.left_slot = Some(left_content);
- self
- }
-
- pub fn left_icon(mut self, left_icon: Icon) -> Self {
- self.left_slot = Some(GraphicSlot::Icon(left_icon));
- self
- }
-
- pub fn left_avatar(mut self, left_avatar: impl Into<ImageSource>) -> Self {
- self.left_slot = Some(GraphicSlot::Avatar(left_avatar.into()));
- self
- }
-}
-
-impl RenderOnce for ListItem {
- type Rendered = Stateful<Div>;
-
- fn render(self, cx: &mut WindowContext) -> Self::Rendered {
- div()
- .id(self.id)
- .relative()
- // TODO: Add focus state
- // .when(self.state == InteractionState::Focused, |this| {
- // this.border()
- // .border_color(cx.theme().colors().border_focused)
- // })
- .when(self.inset, |this| this.rounded_md())
- .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
- .active(|style| style.bg(cx.theme().colors().ghost_element_active))
- .when(self.selected, |this| {
- this.bg(cx.theme().colors().ghost_element_selected)
- })
- .when_some(self.on_click, |this, on_click| {
- this.cursor_pointer().on_click(move |event, cx| {
- // HACK: GPUI currently fires `on_click` with any mouse button,
- // but we only care about the left button.
- if event.down.button == MouseButton::Left {
- (on_click)(event, cx)
- }
- })
- })
- .when_some(self.on_secondary_mouse_down, |this, on_mouse_down| {
- this.on_mouse_down(MouseButton::Right, move |event, cx| {
- (on_mouse_down)(event, cx)
- })
- })
- .child(
- div()
- .when(self.inset, |this| this.px_2())
- .ml(self.indent_level as f32 * self.indent_step_size)
- .flex()
- .gap_1()
- .items_center()
- .relative()
- .child(disclosure_control(self.toggle, self.on_toggle))
- .map(|this| match self.left_slot {
- Some(GraphicSlot::Icon(i)) => this.child(
- IconElement::new(i)
- .size(IconSize::Small)
- .color(Color::Muted),
- ),
- Some(GraphicSlot::Avatar(src)) => this.child(Avatar::source(src)),
- Some(GraphicSlot::PublicActor(src)) => this.child(Avatar::uri(src)),
- None => this,
- })
- .children(self.children),
- )
- }
}
-impl ParentElement for ListItem {
+impl ParentElement for List {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
&mut self.children
}
}
-#[derive(IntoElement, Clone)]
-pub struct ListSeparator;
-
-impl ListSeparator {
- pub fn new() -> Self {
- Self
- }
-}
-
-impl RenderOnce for ListSeparator {
- type Rendered = Div;
-
- fn render(self, cx: &mut WindowContext) -> Self::Rendered {
- div().h_px().w_full().bg(cx.theme().colors().border_variant)
- }
-}
-
-#[derive(IntoElement)]
-pub struct List {
- /// Message to display when the list is empty
- /// Defaults to "No items"
- empty_message: SharedString,
- header: Option<ListHeader>,
- toggle: Toggle,
- children: SmallVec<[AnyElement; 2]>,
-}
-
impl RenderOnce for List {
type Rendered = Div;
@@ -385,35 +71,3 @@ impl RenderOnce for List {
})
}
}
-
-impl List {
- pub fn new() -> Self {
- Self {
- empty_message: "No items".into(),
- header: None,
- toggle: Toggle::NotToggleable,
- children: SmallVec::new(),
- }
- }
-
- pub fn empty_message(mut self, empty_message: impl Into<SharedString>) -> Self {
- self.empty_message = empty_message.into();
- self
- }
-
- pub fn header(mut self, header: ListHeader) -> Self {
- self.header = Some(header);
- self
- }
-
- pub fn toggle(mut self, toggle: Toggle) -> Self {
- self.toggle = toggle;
- self
- }
-}
-
-impl ParentElement for List {
- fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
- &mut self.children
- }
-}
@@ -0,0 +1,123 @@
+use std::rc::Rc;
+
+use gpui::{ClickEvent, Div};
+
+use crate::prelude::*;
+use crate::{disclosure_control, h_stack, Icon, IconButton, IconElement, IconSize, Label, Toggle};
+
+pub enum ListHeaderMeta {
+ Tools(Vec<IconButton>),
+ // TODO: This should be a button
+ Button(Label),
+ Text(Label),
+}
+
+#[derive(IntoElement)]
+pub struct ListHeader {
+ label: SharedString,
+ left_icon: Option<Icon>,
+ meta: Option<ListHeaderMeta>,
+ toggle: Toggle,
+ on_toggle: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
+ inset: bool,
+ selected: bool,
+}
+
+impl ListHeader {
+ pub fn new(label: impl Into<SharedString>) -> Self {
+ Self {
+ label: label.into(),
+ left_icon: None,
+ meta: None,
+ inset: false,
+ toggle: Toggle::NotToggleable,
+ on_toggle: None,
+ selected: false,
+ }
+ }
+
+ pub fn toggle(mut self, toggle: Toggle) -> Self {
+ self.toggle = toggle;
+ self
+ }
+
+ pub fn on_toggle(
+ mut self,
+ on_toggle: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
+ ) -> Self {
+ self.on_toggle = Some(Rc::new(on_toggle));
+ self
+ }
+
+ pub fn left_icon(mut self, left_icon: Option<Icon>) -> Self {
+ self.left_icon = left_icon;
+ self
+ }
+
+ pub fn right_button(self, button: IconButton) -> Self {
+ self.meta(Some(ListHeaderMeta::Tools(vec![button])))
+ }
+
+ pub fn meta(mut self, meta: Option<ListHeaderMeta>) -> Self {
+ self.meta = meta;
+ self
+ }
+
+ pub fn selected(mut self, selected: bool) -> Self {
+ self.selected = selected;
+ self
+ }
+}
+
+impl RenderOnce for ListHeader {
+ type Rendered = Div;
+
+ fn render(self, cx: &mut WindowContext) -> Self::Rendered {
+ let disclosure_control = disclosure_control(self.toggle, self.on_toggle);
+
+ let meta = match self.meta {
+ Some(ListHeaderMeta::Tools(icons)) => div().child(
+ h_stack()
+ .gap_2()
+ .items_center()
+ .children(icons.into_iter().map(|i| i.color(Color::Muted))),
+ ),
+ Some(ListHeaderMeta::Button(label)) => div().child(label),
+ Some(ListHeaderMeta::Text(label)) => div().child(label),
+ None => div(),
+ };
+
+ h_stack().w_full().relative().child(
+ div()
+ .h_5()
+ .when(self.inset, |this| this.px_2())
+ .when(self.selected, |this| {
+ this.bg(cx.theme().colors().ghost_element_selected)
+ })
+ .flex()
+ .flex_1()
+ .items_center()
+ .justify_between()
+ .w_full()
+ .gap_1()
+ .child(
+ h_stack()
+ .gap_1()
+ .child(
+ div()
+ .flex()
+ .gap_1()
+ .items_center()
+ .children(self.left_icon.map(|i| {
+ IconElement::new(i)
+ .color(Color::Muted)
+ .size(IconSize::Small)
+ }))
+ .child(Label::new(self.label.clone()).color(Color::Muted)),
+ )
+ .child(disclosure_control),
+ )
+ .child(meta),
+ )
+ }
+}
@@ -0,0 +1,167 @@
+use std::rc::Rc;
+
+use gpui::{
+ px, AnyElement, ClickEvent, Div, ImageSource, MouseButton, MouseDownEvent, Pixels, Stateful,
+};
+use smallvec::SmallVec;
+
+use crate::prelude::*;
+use crate::{disclosure_control, Avatar, GraphicSlot, Icon, IconElement, IconSize, Toggle};
+
+#[derive(IntoElement)]
+pub struct ListItem {
+ id: ElementId,
+ selected: bool,
+ // TODO: Reintroduce this
+ // disclosure_control_style: DisclosureControlVisibility,
+ indent_level: usize,
+ indent_step_size: Pixels,
+ left_slot: Option<GraphicSlot>,
+ toggle: Toggle,
+ inset: bool,
+ on_click: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
+ on_toggle: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
+ on_secondary_mouse_down: Option<Rc<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>,
+ children: SmallVec<[AnyElement; 2]>,
+}
+
+impl ListItem {
+ pub fn new(id: impl Into<ElementId>) -> Self {
+ Self {
+ id: id.into(),
+ selected: false,
+ indent_level: 0,
+ indent_step_size: px(12.),
+ left_slot: None,
+ toggle: Toggle::NotToggleable,
+ inset: false,
+ on_click: None,
+ on_secondary_mouse_down: None,
+ on_toggle: None,
+ children: SmallVec::new(),
+ }
+ }
+
+ pub fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
+ self.on_click = Some(Rc::new(handler));
+ self
+ }
+
+ pub fn on_secondary_mouse_down(
+ mut self,
+ handler: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
+ ) -> Self {
+ self.on_secondary_mouse_down = Some(Rc::new(handler));
+ self
+ }
+
+ pub fn inset(mut self, inset: bool) -> Self {
+ self.inset = inset;
+ self
+ }
+
+ pub fn indent_level(mut self, indent_level: usize) -> Self {
+ self.indent_level = indent_level;
+ self
+ }
+
+ pub fn indent_step_size(mut self, indent_step_size: Pixels) -> Self {
+ self.indent_step_size = indent_step_size;
+ self
+ }
+
+ pub fn toggle(mut self, toggle: Toggle) -> Self {
+ self.toggle = toggle;
+ self
+ }
+
+ pub fn on_toggle(
+ mut self,
+ on_toggle: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
+ ) -> Self {
+ self.on_toggle = Some(Rc::new(on_toggle));
+ self
+ }
+
+ pub fn selected(mut self, selected: bool) -> Self {
+ self.selected = selected;
+ self
+ }
+
+ pub fn left_content(mut self, left_content: GraphicSlot) -> Self {
+ self.left_slot = Some(left_content);
+ self
+ }
+
+ pub fn left_icon(mut self, left_icon: Icon) -> Self {
+ self.left_slot = Some(GraphicSlot::Icon(left_icon));
+ self
+ }
+
+ pub fn left_avatar(mut self, left_avatar: impl Into<ImageSource>) -> Self {
+ self.left_slot = Some(GraphicSlot::Avatar(left_avatar.into()));
+ self
+ }
+}
+
+impl ParentElement for ListItem {
+ fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
+ &mut self.children
+ }
+}
+
+impl RenderOnce for ListItem {
+ type Rendered = Stateful<Div>;
+
+ fn render(self, cx: &mut WindowContext) -> Self::Rendered {
+ div()
+ .id(self.id)
+ .relative()
+ // TODO: Add focus state
+ // .when(self.state == InteractionState::Focused, |this| {
+ // this.border()
+ // .border_color(cx.theme().colors().border_focused)
+ // })
+ .when(self.inset, |this| this.rounded_md())
+ .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
+ .active(|style| style.bg(cx.theme().colors().ghost_element_active))
+ .when(self.selected, |this| {
+ this.bg(cx.theme().colors().ghost_element_selected)
+ })
+ .when_some(self.on_click, |this, on_click| {
+ this.cursor_pointer().on_click(move |event, cx| {
+ // HACK: GPUI currently fires `on_click` with any mouse button,
+ // but we only care about the left button.
+ if event.down.button == MouseButton::Left {
+ (on_click)(event, cx)
+ }
+ })
+ })
+ .when_some(self.on_secondary_mouse_down, |this, on_mouse_down| {
+ this.on_mouse_down(MouseButton::Right, move |event, cx| {
+ (on_mouse_down)(event, cx)
+ })
+ })
+ .child(
+ div()
+ .when(self.inset, |this| this.px_2())
+ .ml(self.indent_level as f32 * self.indent_step_size)
+ .flex()
+ .gap_1()
+ .items_center()
+ .relative()
+ .child(disclosure_control(self.toggle, self.on_toggle))
+ .map(|this| match self.left_slot {
+ Some(GraphicSlot::Icon(i)) => this.child(
+ IconElement::new(i)
+ .size(IconSize::Small)
+ .color(Color::Muted),
+ ),
+ Some(GraphicSlot::Avatar(src)) => this.child(Avatar::source(src)),
+ Some(GraphicSlot::PublicActor(src)) => this.child(Avatar::uri(src)),
+ None => this,
+ })
+ .children(self.children),
+ )
+ }
+}
@@ -0,0 +1,14 @@
+use gpui::Div;
+
+use crate::prelude::*;
+
+#[derive(IntoElement)]
+pub struct ListSeparator;
+
+impl RenderOnce for ListSeparator {
+ type Rendered = Div;
+
+ fn render(self, cx: &mut WindowContext) -> Self::Rendered {
+ div().h_px().w_full().bg(cx.theme().colors().border_variant)
+ }
+}
@@ -0,0 +1,56 @@
+use gpui::Div;
+
+use crate::prelude::*;
+use crate::{h_stack, Icon, IconElement, IconSize, Label};
+
+#[derive(IntoElement)]
+pub struct ListSubHeader {
+ label: SharedString,
+ left_icon: Option<Icon>,
+ inset: bool,
+}
+
+impl ListSubHeader {
+ pub fn new(label: impl Into<SharedString>) -> Self {
+ Self {
+ label: label.into(),
+ left_icon: None,
+ inset: false,
+ }
+ }
+
+ pub fn left_icon(mut self, left_icon: Option<Icon>) -> Self {
+ self.left_icon = left_icon;
+ self
+ }
+}
+
+impl RenderOnce for ListSubHeader {
+ type Rendered = Div;
+
+ fn render(self, _cx: &mut WindowContext) -> Self::Rendered {
+ h_stack().flex_1().w_full().relative().py_1().child(
+ div()
+ .h_6()
+ .when(self.inset, |this| this.px_2())
+ .flex()
+ .flex_1()
+ .w_full()
+ .gap_1()
+ .items_center()
+ .justify_between()
+ .child(
+ div()
+ .flex()
+ .gap_1()
+ .items_center()
+ .children(self.left_icon.map(|i| {
+ IconElement::new(i)
+ .color(Color::Muted)
+ .size(IconSize::Small)
+ }))
+ .child(Label::new(self.label.clone()).color(Color::Muted)),
+ ),
+ )
+ }
+}
@@ -6,7 +6,9 @@ mod icon;
mod icon_button;
mod keybinding;
mod label;
+mod list;
mod list_item;
+
pub use avatar::*;
pub use button::*;
pub use checkbox::*;
@@ -15,4 +17,5 @@ pub use icon::*;
pub use icon_button::*;
pub use keybinding::*;
pub use label::*;
+pub use list::*;
pub use list_item::*;
@@ -0,0 +1,38 @@
+use gpui::{Div, Render};
+use story::Story;
+
+use crate::{prelude::*, ListHeader, ListSeparator, ListSubHeader};
+use crate::{List, ListItem};
+
+pub struct ListStory;
+
+impl Render for ListStory {
+ type Element = Div;
+
+ fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
+ Story::container()
+ .child(Story::title_for::<List>())
+ .child(Story::label("Default"))
+ .child(
+ List::new()
+ .child(ListItem::new("apple").child("Apple"))
+ .child(ListItem::new("banana").child("Banana"))
+ .child(ListItem::new("cherry").child("Cherry")),
+ )
+ .child(Story::label("With sections"))
+ .child(
+ List::new()
+ .child(ListHeader::new("Fruits"))
+ .child(ListItem::new("apple").child("Apple"))
+ .child(ListItem::new("banana").child("Banana"))
+ .child(ListItem::new("cherry").child("Cherry"))
+ .child(ListSeparator)
+ .child(ListHeader::new("Vegetables"))
+ .child(ListSubHeader::new("Root Vegetables"))
+ .child(ListItem::new("carrot").child("Carrot"))
+ .child(ListItem::new("potato").child("Potato"))
+ .child(ListSubHeader::new("Leafy Vegetables"))
+ .child(ListItem::new("kale").child("Kale")),
+ )
+ }
+}