list.rs

  1use component::{Component, ComponentScope, example_group_with_title, single_example};
  2use gpui::AnyElement;
  3use smallvec::SmallVec;
  4
  5use crate::{Label, ListHeader, ListItem, prelude::*};
  6
  7pub enum EmptyMessage {
  8    Text(SharedString),
  9    Element(AnyElement),
 10}
 11
 12#[derive(IntoElement, RegisterComponent)]
 13pub struct List {
 14    /// Message to display when the list is empty
 15    /// Defaults to "No items"
 16    empty_message: EmptyMessage,
 17    header: Option<ListHeader>,
 18    toggle: Option<bool>,
 19    children: SmallVec<[AnyElement; 2]>,
 20}
 21
 22impl Default for List {
 23    fn default() -> Self {
 24        Self::new()
 25    }
 26}
 27
 28impl List {
 29    pub fn new() -> Self {
 30        Self {
 31            empty_message: EmptyMessage::Text("No items".into()),
 32            header: None,
 33            toggle: None,
 34            children: SmallVec::new(),
 35        }
 36    }
 37
 38    pub fn empty_message(mut self, message: impl Into<EmptyMessage>) -> Self {
 39        self.empty_message = message.into();
 40        self
 41    }
 42
 43    pub fn header(mut self, header: impl Into<Option<ListHeader>>) -> Self {
 44        self.header = header.into();
 45        self
 46    }
 47
 48    pub fn toggle(mut self, toggle: impl Into<Option<bool>>) -> Self {
 49        self.toggle = toggle.into();
 50        self
 51    }
 52}
 53
 54impl ParentElement for List {
 55    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
 56        self.children.extend(elements)
 57    }
 58}
 59
 60impl From<String> for EmptyMessage {
 61    fn from(s: String) -> Self {
 62        EmptyMessage::Text(SharedString::from(s))
 63    }
 64}
 65
 66impl From<&str> for EmptyMessage {
 67    fn from(s: &str) -> Self {
 68        EmptyMessage::Text(SharedString::from(s.to_owned()))
 69    }
 70}
 71
 72impl From<AnyElement> for EmptyMessage {
 73    fn from(e: AnyElement) -> Self {
 74        EmptyMessage::Element(e)
 75    }
 76}
 77
 78impl RenderOnce for List {
 79    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
 80        v_flex()
 81            .w_full()
 82            .py(DynamicSpacing::Base04.rems(cx))
 83            .children(self.header)
 84            .map(|this| match (self.children.is_empty(), self.toggle) {
 85                (false, _) => this.children(self.children),
 86                (true, Some(false)) => this,
 87                (true, _) => match self.empty_message {
 88                    EmptyMessage::Text(text) => {
 89                        this.px_2().child(Label::new(text).color(Color::Muted))
 90                    }
 91                    EmptyMessage::Element(element) => this.child(element),
 92                },
 93            })
 94    }
 95}
 96
 97impl Component for List {
 98    fn scope() -> ComponentScope {
 99        ComponentScope::Layout
100    }
101
102    fn description() -> Option<&'static str> {
103        Some(
104            "A container component for displaying a collection of list items with optional header and empty state.",
105        )
106    }
107
108    fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
109        Some(
110            v_flex()
111                .gap_6()
112                .children(vec![example_group_with_title(
113                    "Basic Lists",
114                    vec![
115                        single_example(
116                            "Simple List",
117                            List::new()
118                                .child(ListItem::new("item1").child(Label::new("Item 1")))
119                                .child(ListItem::new("item2").child(Label::new("Item 2")))
120                                .child(ListItem::new("item3").child(Label::new("Item 3")))
121                                .into_any_element(),
122                        ),
123                        single_example(
124                            "With Header",
125                            List::new()
126                                .header(ListHeader::new("Section Header"))
127                                .child(ListItem::new("item1").child(Label::new("Item 1")))
128                                .child(ListItem::new("item2").child(Label::new("Item 2")))
129                                .into_any_element(),
130                        ),
131                        single_example(
132                            "Empty List",
133                            List::new()
134                                .empty_message("No items to display")
135                                .into_any_element(),
136                        ),
137                    ],
138                )])
139                .into_any_element(),
140        )
141    }
142}