adui_dioxus/components/
spin.rs

1use dioxus::prelude::*;
2
3/// Size variants for the Spin component.
4#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
5pub enum SpinSize {
6    Small,
7    #[default]
8    Default,
9    Large,
10}
11
12/// Props for the Spin component (MVP subset).
13#[derive(Props, Clone, PartialEq)]
14pub struct SpinProps {
15    /// Whether the spin indicator is active. Defaults to true.
16    #[props(optional)]
17    pub spinning: Option<bool>,
18    /// Visual size of the indicator.
19    #[props(optional)]
20    pub size: Option<SpinSize>,
21    /// Optional text shown under the indicator.
22    #[props(optional)]
23    pub tip: Option<String>,
24    /// Extra class for the root element.
25    #[props(optional)]
26    pub class: Option<String>,
27    /// Inline style for the root element.
28    #[props(optional)]
29    pub style: Option<String>,
30    /// Whether to treat this as a fullscreen overlay. MVP only exposes
31    /// a class hook, concrete layout can be refined later.
32    #[props(default)]
33    pub fullscreen: bool,
34    /// Optional content wrapped by the spinner. When present, Spin will
35    /// render children and, when spinning, show a semi-transparent mask
36    /// with the indicator on top.
37    pub children: Element,
38}
39
40/// Ant Design flavored loading spinner (MVP).
41#[component]
42pub fn Spin(props: SpinProps) -> Element {
43    let SpinProps {
44        spinning,
45        size,
46        tip,
47        class,
48        style,
49        fullscreen,
50        children,
51    } = props;
52
53    let is_spinning = spinning.unwrap_or(true);
54    let size = size.unwrap_or_default();
55
56    // Build root class list.
57    let mut classes = vec!["adui-spin".to_string(), "adui-spin-nested".to_string()];
58    match size {
59        SpinSize::Small => classes.push("adui-spin-sm".into()),
60        SpinSize::Large => classes.push("adui-spin-lg".into()),
61        SpinSize::Default => {}
62    }
63    if fullscreen {
64        classes.push("adui-spin-fullscreen".into());
65    }
66    if let Some(extra) = class {
67        classes.push(extra);
68    }
69    let class_attr = classes.join(" ");
70    let style_attr = style.unwrap_or_default();
71
72    let tip_text = tip.unwrap_or_default();
73
74    // When not spinning we just render child content.
75    if !is_spinning {
76        return rsx! {
77            div { class: "{class_attr}", style: "{style_attr}",
78                div { class: "adui-spin-nested-container", {children} }
79            }
80        };
81    }
82
83    // Spinning: render child content with an overlay mask and indicator.
84    rsx! {
85        div { class: "{class_attr}", style: "{style_attr}",
86            div { class: "adui-spin-nested-container", {children} }
87            div { class: "adui-spin-nested-mask",
88                div { class: "adui-spin-indicator",
89                    span { class: "adui-spin-dot" }
90                }
91                if !tip_text.is_empty() {
92                    div { class: "adui-spin-text", "{tip_text}" }
93                }
94            }
95        }
96    }
97}