Skip to main content

ui/components/list/
list_sub_header.rs

1use crate::prelude::*;
2use component::{Component, ComponentScope, example_group_with_title, single_example};
3
4#[derive(IntoElement, RegisterComponent)]
5pub struct ListSubHeader {
6    label: SharedString,
7    start_slot: Option<IconName>,
8    end_slot: Option<AnyElement>,
9    inset: bool,
10    selected: bool,
11}
12
13impl ListSubHeader {
14    pub fn new(label: impl Into<SharedString>) -> Self {
15        Self {
16            label: label.into(),
17            start_slot: None,
18            end_slot: None,
19            inset: false,
20            selected: false,
21        }
22    }
23
24    pub fn left_icon(mut self, left_icon: Option<IconName>) -> Self {
25        self.start_slot = left_icon;
26        self
27    }
28
29    pub fn end_slot(mut self, end_slot: AnyElement) -> Self {
30        self.end_slot = Some(end_slot);
31        self
32    }
33
34    pub fn inset(mut self, inset: bool) -> Self {
35        self.inset = inset;
36        self
37    }
38}
39
40impl Toggleable for ListSubHeader {
41    fn toggle_state(mut self, selected: bool) -> Self {
42        self.selected = selected;
43        self
44    }
45}
46
47impl RenderOnce for ListSubHeader {
48    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
49        h_flex()
50            .flex_1()
51            .w_full()
52            .relative()
53            .pb(DynamicSpacing::Base04.rems(cx))
54            .px(DynamicSpacing::Base02.rems(cx))
55            .child(
56                div()
57                    .h_5()
58                    .when(self.inset, |this| this.px_2())
59                    .when(self.selected, |this| this.bg(semantic::active_bg(cx)))
60                    .flex()
61                    .flex_1()
62                    .w_full()
63                    .gap_1()
64                    .items_center()
65                    .justify_between()
66                    .child(
67                        div()
68                            .flex()
69                            .gap_1()
70                            .items_center()
71                            .children(
72                                self.start_slot.map(|i| {
73                                    Icon::new(i).color(Color::Muted).size(IconSize::Small)
74                                }),
75                            )
76                            .child(
77                                Label::new(self.label.clone())
78                                    .color(Color::Muted)
79                                    .size(LabelSize::Small),
80                            ),
81                    )
82                    .children(self.end_slot),
83            )
84    }
85}
86
87impl Component for ListSubHeader {
88    fn scope() -> ComponentScope {
89        ComponentScope::DataDisplay
90    }
91
92    fn description() -> Option<&'static str> {
93        Some(
94            "A sub-header component for organizing list content into subsections with optional icons and end slots.",
95        )
96    }
97
98    fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
99        Some(
100            v_flex()
101                .gap_6()
102                .children(vec![
103                    example_group_with_title(
104                        "Basic Sub-headers",
105                        vec![
106                            single_example(
107                                "Simple",
108                                ListSubHeader::new("Subsection").into_any_element(),
109                            ),
110                            single_example(
111                                "With Icon",
112                                ListSubHeader::new("Documents")
113                                    .left_icon(Some(IconName::File))
114                                    .into_any_element(),
115                            ),
116                            single_example(
117                                "With End Slot",
118                                ListSubHeader::new("Recent")
119                                    .end_slot(
120                                        Label::new("3").color(Color::Muted).into_any_element(),
121                                    )
122                                    .into_any_element(),
123                            ),
124                        ],
125                    ),
126                    example_group_with_title(
127                        "States",
128                        vec![
129                            single_example(
130                                "Selected",
131                                ListSubHeader::new("Selected")
132                                    .toggle_state(true)
133                                    .into_any_element(),
134                            ),
135                            single_example(
136                                "Inset",
137                                ListSubHeader::new("Inset Sub-header")
138                                    .inset(true)
139                                    .into_any_element(),
140                            ),
141                        ],
142                    ),
143                ])
144                .into_any_element(),
145        )
146    }
147}