1use gpui::prelude::*;
4use gpui::{div, px, App, IntoElement, SharedString, Window};
5
6use crate::theme::{theme, Size};
7
8#[derive(IntoElement)]
10pub struct List {
11 items: Vec<SharedString>,
12 ordered: bool,
13 size: Size,
14 spacing: Size,
15 icon: Option<SharedString>,
17}
18
19impl List {
20 pub fn new() -> Self {
21 List {
22 items: Vec::new(),
23 ordered: false,
24 size: Size::Md,
25 spacing: Size::Xs,
26 icon: None,
27 }
28 }
29
30 pub fn item(mut self, item: impl Into<SharedString>) -> Self {
31 self.items.push(item.into());
32 self
33 }
34
35 pub fn items<I, S>(mut self, items: I) -> Self
36 where
37 I: IntoIterator<Item = S>,
38 S: Into<SharedString>,
39 {
40 self.items.extend(items.into_iter().map(Into::into));
41 self
42 }
43
44 pub fn ordered(mut self, ordered: bool) -> Self {
45 self.ordered = ordered;
46 self
47 }
48
49 pub fn size(mut self, size: Size) -> Self {
50 self.size = size;
51 self
52 }
53
54 pub fn spacing(mut self, spacing: Size) -> Self {
55 self.spacing = spacing;
56 self
57 }
58
59 pub fn icon(mut self, icon: impl Into<SharedString>) -> Self {
60 self.icon = Some(icon.into());
61 self
62 }
63}
64
65impl Default for List {
66 fn default() -> Self {
67 List::new()
68 }
69}
70
71impl RenderOnce for List {
72 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
73 let t = theme(cx);
74 let font = t.font_size(self.size);
75 let gap = t.spacing(self.spacing);
76 let text = t.text().hsla();
77 let marker = t.dimmed().hsla();
78 let ordered = self.ordered;
79 let icon = self.icon.clone();
80
81 let rows = self.items.into_iter().enumerate().map(move |(i, item)| {
82 let bullet: SharedString = if ordered {
83 SharedString::from(format!("{}.", i + 1))
84 } else if let Some(glyph) = icon.clone() {
85 glyph
86 } else {
87 SharedString::new_static("\u{2022}")
88 };
89 div()
90 .flex()
91 .items_start()
92 .gap(px(8.0))
93 .text_size(px(font))
94 .child(div().min_w(px(font * 1.2)).text_color(marker).child(bullet))
95 .child(div().text_color(text).child(item))
96 });
97
98 div().flex().flex_col().gap(px(gap)).children(rows)
99 }
100}