dioxus_bootstrap_css/placeholder.rs
1use dioxus::prelude::*;
2
3use crate::types::{Color, Size};
4
5/// Bootstrap Placeholder component — loading skeleton.
6///
7/// # Bootstrap HTML → Dioxus
8///
9/// | HTML | Dioxus |
10/// |---|---|
11/// | `<span class="placeholder col-6">` | `Placeholder { width: 6 }` |
12/// | `<span class="placeholder col-8 bg-primary placeholder-lg">` | `Placeholder { width: 8, color: Color::Primary, size: Size::Lg }` |
13/// | `<p class="placeholder-glow"><span class="placeholder col-7">` | `Placeholder { width: 7, glow: true }` |
14/// | `<p class="placeholder-wave"><span class="placeholder col-5">` | `Placeholder { width: 5, wave: true }` |
15///
16/// ```rust,no_run
17/// # use dioxus::prelude::*;
18/// # use dioxus_bootstrap_css::prelude::*;
19/// # fn _doctest() -> Element {
20/// rsx! {
21/// Placeholder { width: 75 }
22/// Placeholder { width: 50, color: Color::Primary, size: Size::Lg }
23/// // Placeholder card
24/// Card {
25/// body: rsx! {
26/// PlaceholderParagraph { lines: 3 }
27/// Placeholder { width: 30, tag: "button" }
28/// },
29/// }
30/// }
31/// # }
32/// ```
33#[derive(Clone, PartialEq, Props)]
34pub struct PlaceholderProps {
35 /// Width percentage (1-100). Maps to Bootstrap's col-* classes.
36 #[props(default = 100)]
37 pub width: u8,
38 /// Placeholder color.
39 #[props(default)]
40 pub color: Option<Color>,
41 /// Placeholder size.
42 #[props(default)]
43 pub size: Size,
44 /// Use wave animation.
45 #[props(default)]
46 pub wave: bool,
47 /// Use glow animation.
48 #[props(default)]
49 pub glow: bool,
50 /// HTML tag to render (default: "span").
51 #[props(default = "span".to_string())]
52 pub tag: String,
53 /// Additional CSS classes.
54 #[props(default)]
55 pub class: String,
56}
57
58#[component]
59pub fn Placeholder(props: PlaceholderProps) -> Element {
60 let col = format!("col-{}", props.width.min(12));
61 let color_class = match &props.color {
62 Some(c) => format!(" bg-{c}"),
63 None => String::new(),
64 };
65 let size_class = match props.size {
66 Size::Sm => " placeholder-sm",
67 Size::Lg => " placeholder-lg",
68 Size::Md => "",
69 };
70
71 let full_class = if props.class.is_empty() {
72 format!("placeholder {col}{color_class}{size_class}")
73 } else {
74 format!("placeholder {col}{color_class}{size_class} {}", props.class)
75 };
76
77 let animation = if props.wave {
78 "placeholder-wave"
79 } else if props.glow {
80 "placeholder-glow"
81 } else {
82 ""
83 };
84
85 if animation.is_empty() {
86 rsx! {
87 span { class: "{full_class}", "aria-hidden": "true" }
88 }
89 } else {
90 rsx! {
91 p { class: "{animation}",
92 span { class: "{full_class}", "aria-hidden": "true" }
93 }
94 }
95 }
96}
97
98/// Quick placeholder paragraph with multiple lines.
99///
100/// ```rust,no_run
101/// # use dioxus::prelude::*;
102/// # use dioxus_bootstrap_css::prelude::*;
103/// # fn _doctest() -> Element {
104/// rsx! {
105/// PlaceholderParagraph { lines: 3, glow: true }
106/// }
107/// # }
108/// ```
109#[derive(Clone, PartialEq, Props)]
110pub struct PlaceholderParagraphProps {
111 /// Number of placeholder lines.
112 #[props(default = 3)]
113 pub lines: usize,
114 /// Use wave animation.
115 #[props(default)]
116 pub wave: bool,
117 /// Use glow animation.
118 #[props(default)]
119 pub glow: bool,
120 /// Additional CSS classes.
121 #[props(default)]
122 pub class: String,
123}
124
125#[component]
126pub fn PlaceholderParagraph(props: PlaceholderParagraphProps) -> Element {
127 let animation = if props.wave {
128 " placeholder-wave"
129 } else if props.glow {
130 " placeholder-glow"
131 } else {
132 ""
133 };
134
135 let full_class = if props.class.is_empty() {
136 animation.trim().to_string()
137 } else {
138 format!("{} {}", animation.trim(), props.class)
139 };
140
141 // Vary widths for a natural look
142 let widths = [7, 9, 6, 8, 5, 10, 7, 4, 11, 6];
143
144 rsx! {
145 p { class: "{full_class}",
146 for i in 0..props.lines {
147 {
148 let w = widths[i % widths.len()];
149 let cls = format!("placeholder col-{w}");
150 rsx! {
151 span {
152 class: "{cls}",
153 "aria-hidden": "true",
154 }
155 " "
156 }
157 }
158 }
159 }
160 }
161}