1use dioxus::prelude::*;
2
3#[derive(Clone, Debug, PartialEq)]
5pub enum EmptyImage {
6 Default,
8 Simple,
10 Small,
12 Custom(String),
14}
15
16#[derive(Props, Clone, PartialEq)]
18pub struct EmptyProps {
19 #[props(optional)]
21 pub description: Option<String>,
22 #[props(optional)]
24 pub image: Option<EmptyImage>,
25 #[props(optional)]
27 pub class: Option<String>,
28 #[props(optional)]
30 pub style: Option<String>,
31 #[props(optional)]
33 pub footer: Option<Element>,
34}
35
36#[component]
38pub fn Empty(props: EmptyProps) -> Element {
39 let EmptyProps {
40 description,
41 image,
42 class,
43 style,
44 footer,
45 } = props;
46
47 let mut classes = vec!["adui-empty".to_string()];
48 if let Some(extra) = class {
49 classes.push(extra);
50 }
51
52 if matches!(image, Some(EmptyImage::Small)) {
54 classes.push("adui-empty-sm".to_string());
55 }
56
57 let class_attr = classes.join(" ");
58 let style_attr = style.unwrap_or_default();
59
60 let description_text = description.unwrap_or_else(|| "暂无数据".to_string());
61
62 let image_node = match image.unwrap_or(EmptyImage::Default) {
64 EmptyImage::Default => rsx! {
65 svg {
66 class: "adui-empty-image-svg",
67 view_box: "0 0 64 41",
68 xmlns: "http://www.w3.org/2000/svg",
69 path { d: "M8 33h48v2H8z", fill: "#f5f5f5" }
70 rect { x: "16", y: "13", width: "32", height: "16", rx: "2", fill: "#fafafa", stroke: "#e5e5e5" }
71 circle { cx: "24", cy: "21", r: "3", fill: "#e5e5e5" }
72 rect { x: "30", y: "19", width: "12", height: "2", fill: "#e5e5e5" }
73 rect { x: "30", y: "23", width: "10", height: "2", fill: "#f0f0f0" }
74 }
75 },
76 EmptyImage::Simple => rsx! {
77 div { class: "adui-empty-image-simple" }
78 },
79 EmptyImage::Small => rsx! {
80 div { class: "adui-empty-image-simple" }
81 },
82 EmptyImage::Custom(url) => rsx! {
83 img { class: "adui-empty-image-img", src: "{url}", alt: "empty" }
84 },
85 };
86
87 rsx! {
88 div { class: "{class_attr}", style: "{style_attr}",
89 div { class: "adui-empty-image",
90 {image_node}
91 }
92 p { class: "adui-empty-description", "{description_text}" }
93 if let Some(footer_node) = footer {
94 div { class: "adui-empty-footer", {footer_node} }
95 }
96 }
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[test]
105 fn empty_image_variants() {
106 assert!(EmptyImage::Default != EmptyImage::Simple);
107 assert!(EmptyImage::Simple != EmptyImage::Small);
108 assert!(EmptyImage::Default != EmptyImage::Small);
109 }
110
111 #[test]
112 fn empty_image_custom() {
113 let custom1 = EmptyImage::Custom("url1".to_string());
114 let custom2 = EmptyImage::Custom("url2".to_string());
115 let custom3 = EmptyImage::Custom("url1".to_string());
116
117 assert!(custom1 != custom2);
118 assert!(custom1 == custom3);
119 }
120
121 #[test]
122 fn empty_image_equality() {
123 assert!(EmptyImage::Default == EmptyImage::Default);
124 assert!(EmptyImage::Simple == EmptyImage::Simple);
125 assert!(EmptyImage::Small == EmptyImage::Small);
126 }
127
128 #[test]
129 fn empty_image_clone() {
130 let original = EmptyImage::Default;
131 let cloned = original.clone();
132 assert!(original == cloned);
133
134 let custom_original = EmptyImage::Custom("test".to_string());
135 let custom_cloned = custom_original.clone();
136 assert!(custom_original == custom_cloned);
137 }
138
139 #[test]
140 fn empty_props_defaults() {
141 let props = EmptyProps {
142 description: None,
143 image: None,
144 class: None,
145 style: None,
146 footer: None,
147 };
148 assert!(props.description.is_none());
149 assert!(props.image.is_none());
150 }
151
152 #[test]
153 fn empty_image_custom_string() {
154 let url = "https://example.com/image.png";
155 let custom = EmptyImage::Custom(url.to_string());
156 match custom {
157 EmptyImage::Custom(s) => assert_eq!(s, url),
158 _ => panic!("Expected Custom variant"),
159 }
160 }
161
162 #[test]
163 fn empty_image_all_variants() {
164 let default = EmptyImage::Default;
165 let simple = EmptyImage::Simple;
166 let small = EmptyImage::Small;
167 let custom = EmptyImage::Custom("test".to_string());
168
169 assert_ne!(default, simple);
170 assert_ne!(default, small);
171 assert_ne!(default, custom);
172 assert_ne!(simple, small);
173 assert_ne!(simple, custom);
174 assert_ne!(small, custom);
175 }
176
177 #[test]
178 fn empty_image_debug() {
179 let default = EmptyImage::Default;
180 let debug_str = format!("{:?}", default);
181 assert!(!debug_str.is_empty());
183 }
184
185 #[test]
186 fn empty_props_with_all_fields() {
187 let props = EmptyProps {
188 description: Some("Custom description".to_string()),
189 image: Some(EmptyImage::Simple),
190 class: Some("custom-class".to_string()),
191 style: Some("color: red;".to_string()),
192 footer: None,
193 };
194 assert_eq!(props.description, Some("Custom description".to_string()));
195 assert_eq!(props.image, Some(EmptyImage::Simple));
196 assert_eq!(props.class, Some("custom-class".to_string()));
197 }
198
199 #[test]
200 fn empty_props_minimal() {
201 let props = EmptyProps {
202 description: None,
203 image: None,
204 class: None,
205 style: None,
206 footer: None,
207 };
208 assert!(props.description.is_none());
209 assert!(props.image.is_none());
210 assert!(props.class.is_none());
211 assert!(props.style.is_none());
212 assert!(props.footer.is_none());
213 }
214
215 #[test]
216 fn empty_image_custom_empty_string() {
217 let custom = EmptyImage::Custom(String::new());
218 match custom {
219 EmptyImage::Custom(s) => assert_eq!(s, ""),
220 _ => panic!("Expected Custom variant"),
221 }
222 }
223
224 #[test]
225 fn empty_image_custom_long_string() {
226 let long_url = "https://example.com/very/long/path/to/image.png?query=param&other=value";
227 let custom = EmptyImage::Custom(long_url.to_string());
228 match custom {
229 EmptyImage::Custom(s) => assert_eq!(s, long_url),
230 _ => panic!("Expected Custom variant"),
231 }
232 }
233
234 #[test]
235 fn empty_props_clone() {
236 let props = EmptyProps {
237 description: Some("Test".to_string()),
238 image: Some(EmptyImage::Default),
239 class: None,
240 style: None,
241 footer: None,
242 };
243 let cloned = props.clone();
244 assert_eq!(props.description, cloned.description);
245 assert_eq!(props.image, cloned.image);
246 }
247
248 #[test]
249 fn empty_image_custom_equality_with_same_string() {
250 let custom1 = EmptyImage::Custom("test".to_string());
251 let custom2 = EmptyImage::Custom("test".to_string());
252 assert_eq!(custom1, custom2);
253 }
254
255 #[test]
256 fn empty_image_custom_equality_with_different_string() {
257 let custom1 = EmptyImage::Custom("test1".to_string());
258 let custom2 = EmptyImage::Custom("test2".to_string());
259 assert_ne!(custom1, custom2);
260 }
261}