1use crate::components::config_provider::ComponentSize;
2use crate::components::empty::Empty;
3use crate::components::pagination::Pagination;
4use crate::components::spin::Spin;
5use dioxus::prelude::*;
6
7#[derive(Props, Clone, PartialEq)]
9pub struct ListProps {
10 #[props(optional)]
12 pub header: Option<Element>,
13 #[props(optional)]
15 pub footer: Option<Element>,
16 #[props(default)]
18 pub bordered: bool,
19 #[props(optional)]
21 pub size: Option<ComponentSize>,
22 #[props(optional)]
24 pub class: Option<String>,
25 #[props(optional)]
27 pub style: Option<String>,
28 #[props(default)]
31 pub loading: bool,
32 #[props(optional)]
34 pub is_empty: Option<bool>,
35 #[props(optional)]
38 pub empty: Option<Element>,
39 #[props(optional)]
42 pub pagination_total: Option<u32>,
43 #[props(optional)]
45 pub pagination_current: Option<u32>,
46 #[props(optional)]
48 pub pagination_page_size: Option<u32>,
49 #[props(optional)]
51 pub pagination_on_change: Option<EventHandler<(u32, u32)>>,
52 pub children: Element,
55}
56
57#[component]
59pub fn List(props: ListProps) -> Element {
60 let ListProps {
61 header,
62 footer,
63 bordered,
64 size,
65 class,
66 style,
67 loading,
68 is_empty,
69 empty,
70 pagination_total,
71 pagination_current,
72 pagination_page_size,
73 pagination_on_change,
74 children,
75 } = props;
76
77 let mut class_list = vec!["adui-list".to_string()];
78 if bordered {
79 class_list.push("adui-list-bordered".into());
80 }
81 if let Some(sz) = size {
82 match sz {
83 ComponentSize::Small => class_list.push("adui-list-sm".into()),
84 ComponentSize::Middle => {}
85 ComponentSize::Large => class_list.push("adui-list-lg".into()),
86 }
87 }
88 if let Some(extra) = class {
89 class_list.push(extra);
90 }
91 let class_attr = class_list.join(" ");
92 let style_attr = style.unwrap_or_default();
93
94 let show_empty = !loading && is_empty.unwrap_or(false);
95
96 rsx! {
97 div { class: "{class_attr}", style: "{style_attr}",
98 if let Some(head) = header {
99 div { class: "adui-list-header", {head} }
100 }
101
102 if loading {
104 div { class: "adui-list-body",
105 Spin {
106 spinning: Some(true),
107 tip: Some("加载中...".to_string()),
108 div { class: "adui-list-items", {children} }
109 }
110 }
111 } else if show_empty {
112 div { class: "adui-list-body",
113 div { class: "adui-list-empty",
114 if let Some(node) = empty {
115 {node}
116 } else {
117 Empty {}
118 }
119 }
120 }
121 } else {
122 div { class: "adui-list-body",
123 div { class: "adui-list-items", {children} }
124 }
125 }
126
127 if let Some(foot) = footer {
128 div { class: "adui-list-footer", {foot} }
129 }
130
131 if let Some(total) = pagination_total {
133 div { class: "adui-list-pagination",
134 Pagination {
135 total: total,
136 current: pagination_current,
137 page_size: pagination_page_size,
138 show_total: false,
139 show_size_changer: false,
140 on_change: move |(page, size)| {
141 if let Some(cb) = pagination_on_change {
142 cb.call((page, size));
143 }
144 },
145 }
146 }
147 }
148 }
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155
156 #[test]
157 fn list_props_defaults() {
158 let props = ListProps {
159 header: None,
160 footer: None,
161 bordered: false,
162 size: None,
163 class: None,
164 style: None,
165 loading: false,
166 is_empty: None,
167 empty: None,
168 pagination_total: None,
169 pagination_current: None,
170 pagination_page_size: None,
171 pagination_on_change: None,
172 children: rsx!(div {}),
173 };
174 assert_eq!(props.bordered, false);
175 assert_eq!(props.loading, false);
176 assert!(props.header.is_none());
177 assert!(props.footer.is_none());
178 }
179
180 #[test]
181 fn list_props_bordered() {
182 let props = ListProps {
183 header: None,
184 footer: None,
185 bordered: true,
186 size: None,
187 class: None,
188 style: None,
189 loading: false,
190 is_empty: None,
191 empty: None,
192 pagination_total: None,
193 pagination_current: None,
194 pagination_page_size: None,
195 pagination_on_change: None,
196 children: rsx!(div {}),
197 };
198 assert_eq!(props.bordered, true);
199 }
200
201 #[test]
202 fn list_props_loading() {
203 let props = ListProps {
204 header: None,
205 footer: None,
206 bordered: false,
207 size: None,
208 class: None,
209 style: None,
210 loading: true,
211 is_empty: None,
212 empty: None,
213 pagination_total: None,
214 pagination_current: None,
215 pagination_page_size: None,
216 pagination_on_change: None,
217 children: rsx!(div {}),
218 };
219 assert_eq!(props.loading, true);
220 }
221
222 #[test]
223 fn list_props_size() {
224 let props = ListProps {
225 header: None,
226 footer: None,
227 bordered: false,
228 size: Some(ComponentSize::Small),
229 class: None,
230 style: None,
231 loading: false,
232 is_empty: None,
233 empty: None,
234 pagination_total: None,
235 pagination_current: None,
236 pagination_page_size: None,
237 pagination_on_change: None,
238 children: rsx!(div {}),
239 };
240 assert_eq!(props.size, Some(ComponentSize::Small));
241 }
242
243 #[test]
244 fn list_props_pagination() {
245 let props = ListProps {
246 header: None,
247 footer: None,
248 bordered: false,
249 size: None,
250 class: None,
251 style: None,
252 loading: false,
253 is_empty: None,
254 empty: None,
255 pagination_total: Some(100),
256 pagination_current: Some(1),
257 pagination_page_size: Some(10),
258 pagination_on_change: None,
259 children: rsx!(div {}),
260 };
261 assert_eq!(props.pagination_total, Some(100));
262 assert_eq!(props.pagination_current, Some(1));
263 assert_eq!(props.pagination_page_size, Some(10));
264 }
265}