ui/components/list/
list_sub_header.rs1use 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}