ironhtml_bootstrap/
placeholder.rs1use ironhtml::typed::Element;
7use ironhtml_elements::{Div, Span, A, P};
8
9extern crate alloc;
10use alloc::format;
11
12#[derive(Clone, Copy)]
14pub enum PlaceholderWidth {
15 Col1,
16 Col2,
17 Col3,
18 Col4,
19 Col5,
20 Col6,
21 Col7,
22 Col8,
23 Col9,
24 Col10,
25 Col11,
26 Col12,
27}
28
29impl PlaceholderWidth {
30 const fn class(self) -> &'static str {
31 match self {
32 Self::Col1 => "col-1",
33 Self::Col2 => "col-2",
34 Self::Col3 => "col-3",
35 Self::Col4 => "col-4",
36 Self::Col5 => "col-5",
37 Self::Col6 => "col-6",
38 Self::Col7 => "col-7",
39 Self::Col8 => "col-8",
40 Self::Col9 => "col-9",
41 Self::Col10 => "col-10",
42 Self::Col11 => "col-11",
43 Self::Col12 => "col-12",
44 }
45 }
46}
47
48#[derive(Clone, Copy, Default)]
50pub enum PlaceholderSize {
51 ExtraSmall,
52 Small,
53 #[default]
54 Default,
55 Large,
56}
57
58impl PlaceholderSize {
59 const fn class(self) -> &'static str {
60 match self {
61 Self::ExtraSmall => "placeholder-xs",
62 Self::Small => "placeholder-sm",
63 Self::Default => "",
64 Self::Large => "placeholder-lg",
65 }
66 }
67}
68
69#[must_use]
80pub fn placeholder(width: PlaceholderWidth) -> Element<Span> {
81 let class = format!("placeholder {}", width.class());
82 Element::<Span>::new().class(&class)
83}
84
85#[must_use]
87pub fn placeholder_sized(width: PlaceholderWidth, size: PlaceholderSize) -> Element<Span> {
88 let size_class = size.class();
89 let class = if size_class.is_empty() {
90 format!("placeholder {}", width.class())
91 } else {
92 format!("placeholder {} {size_class}", width.class())
93 };
94 Element::<Span>::new().class(&class)
95}
96
97#[must_use]
99pub fn placeholder_colored(width: PlaceholderWidth, color: crate::Color) -> Element<Span> {
100 let class = format!("placeholder {} bg-{}", width.class(), color.as_str());
101 Element::<Span>::new().class(&class)
102}
103
104#[must_use]
115pub fn placeholder_paragraph() -> Element<P> {
116 Element::<P>::new()
117 .class("placeholder-glow")
118 .child::<Span, _>(|_| placeholder(PlaceholderWidth::Col7))
119 .text(" ")
120 .child::<Span, _>(|_| placeholder(PlaceholderWidth::Col4))
121 .text(" ")
122 .child::<Span, _>(|_| placeholder(PlaceholderWidth::Col4))
123 .text(" ")
124 .child::<Span, _>(|_| placeholder(PlaceholderWidth::Col6))
125 .text(" ")
126 .child::<Span, _>(|_| placeholder(PlaceholderWidth::Col8))
127}
128
129#[must_use]
131pub fn placeholder_glow<F>(f: F) -> Element<Div>
132where
133 F: FnOnce(Element<Div>) -> Element<Div>,
134{
135 f(Element::<Div>::new().class("placeholder-glow"))
136}
137
138#[must_use]
140pub fn placeholder_wave<F>(f: F) -> Element<Div>
141where
142 F: FnOnce(Element<Div>) -> Element<Div>,
143{
144 f(Element::<Div>::new().class("placeholder-wave"))
145}
146
147#[must_use]
149pub fn placeholder_button(color: crate::Color, width: PlaceholderWidth) -> Element<A> {
150 let class = format!(
151 "btn btn-{} disabled placeholder {}",
152 color.as_str(),
153 width.class()
154 );
155 Element::<A>::new()
156 .class(&class)
157 .attr("aria-disabled", "true")
158}
159
160#[must_use]
162pub fn placeholder_card() -> Element<Div> {
163 use ironhtml_elements::{Img, H5};
164
165 Element::<Div>::new()
166 .class("card")
167 .attr("aria-hidden", "true")
168 .child::<Img, _>(|i| {
169 i.attr("src", "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 286 180'%3E%3Crect fill='%23868e96' width='286' height='180'/%3E%3C/svg%3E")
170 .class("card-img-top")
171 .attr("alt", "")
172 })
173 .child::<Div, _>(|body| {
174 body.class("card-body")
175 .child::<H5, _>(|h| {
176 h.class("card-title placeholder-glow")
177 .child::<Span, _>(|_| placeholder(PlaceholderWidth::Col6))
178 })
179 .child::<P, _>(|p| {
180 p.class("card-text placeholder-glow")
181 .child::<Span, _>(|_| placeholder(PlaceholderWidth::Col7))
182 .text(" ")
183 .child::<Span, _>(|_| placeholder(PlaceholderWidth::Col4))
184 .text(" ")
185 .child::<Span, _>(|_| placeholder(PlaceholderWidth::Col4))
186 .text(" ")
187 .child::<Span, _>(|_| placeholder(PlaceholderWidth::Col6))
188 .text(" ")
189 .child::<Span, _>(|_| placeholder(PlaceholderWidth::Col8))
190 })
191 .child::<A, _>(|_| placeholder_button(crate::Color::Primary, PlaceholderWidth::Col6))
192 })
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198
199 #[test]
200 fn test_placeholder() {
201 let p = placeholder(PlaceholderWidth::Col6);
202 let html = p.render();
203 assert!(html.contains("placeholder"));
204 assert!(html.contains("col-6"));
205 }
206
207 #[test]
208 fn test_placeholder_sized() {
209 let p = placeholder_sized(PlaceholderWidth::Col4, PlaceholderSize::Large);
210 let html = p.render();
211 assert!(html.contains("placeholder-lg"));
212 }
213
214 #[test]
215 fn test_placeholder_colored() {
216 let p = placeholder_colored(PlaceholderWidth::Col3, crate::Color::Primary);
217 let html = p.render();
218 assert!(html.contains("bg-primary"));
219 }
220
221 #[test]
222 fn test_placeholder_paragraph() {
223 let p = placeholder_paragraph();
224 let html = p.render();
225 assert!(html.contains("placeholder-glow"));
226 assert!(html.contains("placeholder"));
227 }
228
229 #[test]
230 fn test_placeholder_card() {
231 let card = placeholder_card();
232 let html = card.render();
233 assert!(html.contains("card"));
234 assert!(html.contains("placeholder-glow"));
235 }
236}