adui_dioxus/components/
divider.rs

1use crate::theme::use_theme;
2use dioxus::prelude::*;
3
4#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
5pub enum DividerOrientation {
6    Left,
7    #[default]
8    Center,
9    Right,
10}
11
12/// Props for rendering a horizontal or vertical divider.
13#[derive(Props, Clone, PartialEq)]
14pub struct DividerProps {
15    #[props(default)]
16    pub dashed: bool,
17    #[props(default)]
18    pub plain: bool,
19    #[props(default)]
20    pub vertical: bool,
21    #[props(default)]
22    pub orientation: DividerOrientation,
23    #[props(optional)]
24    pub orientation_margin: Option<String>,
25    #[props(optional)]
26    pub class: Option<String>,
27    #[props(optional)]
28    pub style: Option<String>,
29    /// 可选内容。
30    #[props(optional)]
31    pub content: Option<Element>,
32}
33
34/// Divider with optional title content and orientation.
35#[component]
36pub fn Divider(props: DividerProps) -> Element {
37    let DividerProps {
38        dashed,
39        plain,
40        vertical,
41        orientation,
42        orientation_margin,
43        class,
44        style,
45        content,
46    } = props;
47    let theme = use_theme();
48    let tokens = theme.tokens();
49
50    let has_content = content.is_some();
51
52    let mut class_list = vec!["adui-divider".to_string()];
53    if vertical {
54        class_list.push("adui-divider-vertical".into());
55    } else {
56        class_list.push("adui-divider-horizontal".into());
57        if has_content {
58            class_list.push(match orientation {
59                DividerOrientation::Left => "adui-divider-left".into(),
60                DividerOrientation::Center => "adui-divider-center".into(),
61                DividerOrientation::Right => "adui-divider-right".into(),
62            });
63        }
64    }
65    if dashed {
66        class_list.push("adui-divider-dashed".into());
67    }
68    if plain {
69        class_list.push("adui-divider-plain".into());
70    }
71    if let Some(extra) = class.as_ref() {
72        class_list.push(extra.clone());
73    }
74    let class_attr = class_list.join(" ");
75
76    let style_attr = format!(
77        "border-color:{};{}",
78        tokens.color_border,
79        style.unwrap_or_default()
80    );
81
82    if vertical {
83        return rsx! {
84            div {
85                class: "{class_attr}",
86                style: "{style_attr}",
87                role: "separator",
88                "aria-orientation": "vertical",
89            }
90        };
91    }
92
93    let margin = orientation_margin.unwrap_or_else(|| "16px".into());
94
95    rsx! {
96        div {
97            class: "{class_attr}",
98            style: "{style_attr}",
99            role: "separator",
100            "aria-orientation": "horizontal",
101            if let Some(node) = content {
102                span {
103                    class: "adui-divider-inner-text",
104                    style: format!("margin: 0 {margin};"),
105                    {node}
106                }
107            }
108        }
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115
116    #[test]
117    fn divider_orientation_default() {
118        assert_eq!(DividerOrientation::default(), DividerOrientation::Center);
119    }
120
121    #[test]
122    fn divider_orientation_variants() {
123        assert_ne!(DividerOrientation::Left, DividerOrientation::Center);
124        assert_ne!(DividerOrientation::Center, DividerOrientation::Right);
125        assert_ne!(DividerOrientation::Left, DividerOrientation::Right);
126    }
127
128    #[test]
129    fn divider_orientation_equality() {
130        assert_eq!(DividerOrientation::Left, DividerOrientation::Left);
131        assert_eq!(DividerOrientation::Center, DividerOrientation::Center);
132        assert_eq!(DividerOrientation::Right, DividerOrientation::Right);
133    }
134
135    #[test]
136    fn divider_props_defaults() {
137        let props = DividerProps {
138            dashed: false,
139            plain: false,
140            vertical: false,
141            orientation: DividerOrientation::default(),
142            orientation_margin: None,
143            class: None,
144            style: None,
145            content: None,
146        };
147        assert_eq!(props.dashed, false);
148        assert_eq!(props.plain, false);
149        assert_eq!(props.vertical, false);
150        assert_eq!(props.orientation, DividerOrientation::Center);
151    }
152
153    #[test]
154    fn divider_orientation_clone() {
155        let original = DividerOrientation::Left;
156        let cloned = original;
157        assert_eq!(original, cloned);
158    }
159
160    #[test]
161    fn divider_orientation_all_variants() {
162        // Test all variants exist
163        let left = DividerOrientation::Left;
164        let center = DividerOrientation::Center;
165        let right = DividerOrientation::Right;
166
167        assert_ne!(left, center);
168        assert_ne!(center, right);
169        assert_ne!(left, right);
170    }
171
172    #[test]
173    fn divider_orientation_debug() {
174        let left = DividerOrientation::Left;
175        let center = DividerOrientation::Center;
176        let right = DividerOrientation::Right;
177
178        let left_str = format!("{:?}", left);
179        let center_str = format!("{:?}", center);
180        let right_str = format!("{:?}", right);
181
182        assert!(left_str.contains("Left"));
183        assert!(center_str.contains("Center"));
184        assert!(right_str.contains("Right"));
185    }
186
187    #[test]
188    fn divider_props_with_all_fields() {
189        let props = DividerProps {
190            dashed: true,
191            plain: true,
192            vertical: true,
193            orientation: DividerOrientation::Left,
194            orientation_margin: Some("20px".into()),
195            class: Some("custom-class".into()),
196            style: Some("color: red;".into()),
197            content: None,
198        };
199        assert_eq!(props.dashed, true);
200        assert_eq!(props.plain, true);
201        assert_eq!(props.vertical, true);
202        assert_eq!(props.orientation, DividerOrientation::Left);
203        assert_eq!(props.orientation_margin, Some("20px".into()));
204        assert_eq!(props.class, Some("custom-class".into()));
205    }
206
207    #[test]
208    fn divider_props_dashed_only() {
209        let props = DividerProps {
210            dashed: true,
211            plain: false,
212            vertical: false,
213            orientation: DividerOrientation::Center,
214            orientation_margin: None,
215            class: None,
216            style: None,
217            content: None,
218        };
219        assert_eq!(props.dashed, true);
220        assert_eq!(props.plain, false);
221    }
222
223    #[test]
224    fn divider_props_plain_only() {
225        let props = DividerProps {
226            dashed: false,
227            plain: true,
228            vertical: false,
229            orientation: DividerOrientation::Center,
230            orientation_margin: None,
231            class: None,
232            style: None,
233            content: None,
234        };
235        assert_eq!(props.dashed, false);
236        assert_eq!(props.plain, true);
237    }
238
239    #[test]
240    fn divider_props_vertical() {
241        let props = DividerProps {
242            dashed: false,
243            plain: false,
244            vertical: true,
245            orientation: DividerOrientation::Center,
246            orientation_margin: None,
247            class: None,
248            style: None,
249            content: None,
250        };
251        assert_eq!(props.vertical, true);
252    }
253
254    #[test]
255    fn divider_props_orientation_left() {
256        let props = DividerProps {
257            dashed: false,
258            plain: false,
259            vertical: false,
260            orientation: DividerOrientation::Left,
261            orientation_margin: None,
262            class: None,
263            style: None,
264            content: None,
265        };
266        assert_eq!(props.orientation, DividerOrientation::Left);
267    }
268
269    #[test]
270    fn divider_props_orientation_right() {
271        let props = DividerProps {
272            dashed: false,
273            plain: false,
274            vertical: false,
275            orientation: DividerOrientation::Right,
276            orientation_margin: None,
277            class: None,
278            style: None,
279            content: None,
280        };
281        assert_eq!(props.orientation, DividerOrientation::Right);
282    }
283
284    #[test]
285    fn divider_props_clone() {
286        let props = DividerProps {
287            dashed: true,
288            plain: true,
289            vertical: false,
290            orientation: DividerOrientation::Left,
291            orientation_margin: Some("10px".into()),
292            class: Some("test".into()),
293            style: None,
294            content: None,
295        };
296        let cloned = props.clone();
297        assert_eq!(props.dashed, cloned.dashed);
298        assert_eq!(props.plain, cloned.plain);
299        assert_eq!(props.vertical, cloned.vertical);
300        assert_eq!(props.orientation, cloned.orientation);
301        assert_eq!(props.orientation_margin, cloned.orientation_margin);
302        assert_eq!(props.class, cloned.class);
303    }
304
305    #[test]
306    fn divider_props_with_custom_margin() {
307        let props = DividerProps {
308            dashed: false,
309            plain: false,
310            vertical: false,
311            orientation: DividerOrientation::Center,
312            orientation_margin: Some("24px".into()),
313            class: None,
314            style: None,
315            content: None,
316        };
317        assert_eq!(props.orientation_margin, Some("24px".into()));
318    }
319}