yew_bs/components/
placeholders.rs

1use yew::prelude::*;
2#[derive(Clone, Copy, PartialEq, Debug)]
3pub enum PlaceholderAnimation {
4    Glow,
5    Wave,
6}
7impl PlaceholderAnimation {
8    pub fn as_str(&self) -> &'static str {
9        match self {
10            PlaceholderAnimation::Glow => "placeholder-glow",
11            PlaceholderAnimation::Wave => "placeholder-wave",
12        }
13    }
14}
15/// Placeholder size variants
16#[derive(Clone, Copy, PartialEq, Debug)]
17pub enum PlaceholderSize {
18    ExtraSmall,
19    Small,
20    Medium,
21    Large,
22    ExtraLarge,
23}
24impl PlaceholderSize {
25    pub fn as_str(&self) -> &'static str {
26        match self {
27            PlaceholderSize::ExtraSmall => "placeholder-xs",
28            PlaceholderSize::Small => "placeholder-sm",
29            PlaceholderSize::Medium => "",
30            PlaceholderSize::Large => "placeholder-lg",
31            PlaceholderSize::ExtraLarge => "placeholder-xl",
32        }
33    }
34}
35#[derive(Properties, PartialEq)]
36pub struct PlaceholderProps {
37    #[prop_or_default]
38    pub children: Children,
39    #[prop_or(PlaceholderSize::Medium)]
40    pub size: PlaceholderSize,
41    #[prop_or_default]
42    pub class: Option<AttrValue>,
43    #[prop_or_default]
44    pub width: Option<AttrValue>,
45    #[prop_or_default]
46    pub height: Option<AttrValue>,
47    #[prop_or_default]
48    pub variant: Option<AttrValue>,
49    #[prop_or_default]
50    pub aria_label: Option<AttrValue>,
51}
52#[function_component(Placeholder)]
53pub fn placeholder(props: &PlaceholderProps) -> Html {
54    let mut classes_vec = vec!["placeholder".to_string()];
55    let size_class = props.size.as_str();
56    if !size_class.is_empty() {
57        classes_vec.push(size_class.to_string());
58    }
59    if let Some(variant) = &props.variant {
60        classes_vec.push(format!("bg-{}", variant.as_str()));
61    }
62    if let Some(class) = &props.class {
63        classes_vec.push(class.to_string());
64    }
65    let classes = classes!(classes_vec);
66    let mut style = String::new();
67    if let Some(width) = &props.width {
68        style.push_str(&format!("width: {};", width.as_str()));
69    }
70    if let Some(height) = &props.height {
71        style.push_str(&format!("height: {};", height.as_str()));
72    }
73    let style_attr: Option<AttrValue> = if style.is_empty() {
74        None
75    } else {
76        Some(style.into())
77    };
78    html! {
79        < span class = { classes } style = { style_attr } aria - label = { props
80        .aria_label.clone() } > { for props.children.iter() } </ span >
81    }
82}
83#[derive(Properties, PartialEq)]
84pub struct PlaceholderButtonProps {
85    #[prop_or(PlaceholderSize::Medium)]
86    pub size: PlaceholderSize,
87    #[prop_or_default]
88    pub class: Option<AttrValue>,
89    #[prop_or_default]
90    pub width: Option<AttrValue>,
91    #[prop_or_default]
92    pub variant: Option<AttrValue>,
93}
94#[function_component(PlaceholderButton)]
95pub fn placeholder_button(props: &PlaceholderButtonProps) -> Html {
96    let mut classes_vec = vec![
97        "btn".to_string(), "disabled".to_string(), "placeholder".to_string()
98    ];
99    let size_class = props.size.as_str();
100    if !size_class.is_empty() {
101        classes_vec.push(size_class.to_string());
102    }
103    if let Some(variant) = &props.variant {
104        classes_vec.push(format!("btn-{}", variant.as_str()));
105    }
106    if let Some(class) = &props.class {
107        classes_vec.push(class.to_string());
108    }
109    let classes = classes!(classes_vec);
110    let style = props.width.as_ref().map(|w| format!("width: {};", w.as_str()));
111    html! {
112        < span class = { classes } style = { style } aria - hidden = "true" > < span
113        class = "placeholder" > { "Button" } </ span > </ span >
114    }
115}
116#[derive(Properties, PartialEq)]
117pub struct PlaceholderWrapperProps {
118    #[prop_or_default]
119    pub children: Children,
120    #[prop_or_default]
121    pub animation: Option<PlaceholderAnimation>,
122    #[prop_or_default]
123    pub class: Option<AttrValue>,
124}
125#[function_component(PlaceholderWrapper)]
126pub fn placeholder_wrapper(props: &PlaceholderWrapperProps) -> Html {
127    let mut classes_vec = Vec::new();
128    if let Some(animation) = &props.animation {
129        classes_vec.push(animation.as_str().to_string());
130    }
131    if let Some(class) = &props.class {
132        classes_vec.push(class.to_string());
133    }
134    let classes = if classes_vec.is_empty() {
135        None
136    } else {
137        Some(classes!(classes_vec))
138    };
139    html! {
140        < div class = { classes } > { for props.children.iter() } </ div >
141    }
142}
143#[derive(Properties, PartialEq)]
144pub struct PlaceholderCardProps {
145    #[prop_or_default]
146    pub animation: Option<PlaceholderAnimation>,
147    #[prop_or_default]
148    pub class: Option<AttrValue>,
149}
150#[function_component(PlaceholderCard)]
151pub fn placeholder_card(props: &PlaceholderCardProps) -> Html {
152    let mut wrapper_classes = Vec::new();
153    if let Some(animation) = &props.animation {
154        wrapper_classes.push(animation.as_str().to_string());
155    }
156    if let Some(class) = &props.class {
157        wrapper_classes.push(class.to_string());
158    }
159    let wrapper_classes = if wrapper_classes.is_empty() {
160        None
161    } else {
162        Some(classes!(wrapper_classes))
163    };
164    html! {
165        < div class = { wrapper_classes } > < div class = "card" > < div class =
166        "card-body" > < div class = "d-flex align-items-center mb-3" > < Placeholder size
167        = { PlaceholderSize::Large } width = { Some(AttrValue::from("3rem")) } height = {
168        Some(AttrValue::from("3rem")) } class = {
169        Some(AttrValue::from("rounded-circle me-3")) } /> < div class = "flex-grow-1" > <
170        Placeholder width = { Some(AttrValue::from("60%")) } class = {
171        Some(AttrValue::from("mb-1")) } /> < Placeholder width = {
172        Some(AttrValue::from("40%")) } size = { PlaceholderSize::Small } /> </ div > </
173        div > < Placeholder class = { Some(AttrValue::from("mb-2")) } /> < Placeholder
174        width = { Some(AttrValue::from("80%")) } class = { Some(AttrValue::from("mb-2"))
175        } /> < Placeholder width = { Some(AttrValue::from("70%")) } /> </ div > < div
176        class = "card-footer" > < div class =
177        "d-flex justify-content-between align-items-center" > < Placeholder width = {
178        Some(AttrValue::from("30%")) } size = { PlaceholderSize::Small } /> < div > <
179        PlaceholderButton size = { PlaceholderSize::Small } variant = {
180        Some(AttrValue::from("primary")) } class = { Some(AttrValue::from("me-2")) } /> <
181        PlaceholderButton size = { PlaceholderSize::Small } variant = {
182        Some(AttrValue::from("secondary")) } /> </ div > </ div > </ div > </ div > </
183        div >
184    }
185}
186#[derive(Properties, PartialEq)]
187pub struct PlaceholderTableProps {
188    #[prop_or(5)]
189    pub rows: usize,
190    #[prop_or(4)]
191    pub columns: usize,
192    #[prop_or_default]
193    pub animation: Option<PlaceholderAnimation>,
194    #[prop_or_default]
195    pub class: Option<AttrValue>,
196}
197#[function_component(PlaceholderTable)]
198pub fn placeholder_table(props: &PlaceholderTableProps) -> Html {
199    let mut wrapper_classes = Vec::new();
200    if let Some(animation) = &props.animation {
201        wrapper_classes.push(animation.as_str().to_string());
202    }
203    if let Some(class) = &props.class {
204        wrapper_classes.push(class.to_string());
205    }
206    let wrapper_classes = if wrapper_classes.is_empty() {
207        None
208    } else {
209        Some(classes!(wrapper_classes))
210    };
211    html! {
212        < div class = { wrapper_classes } > < table class = "table" > < thead > < tr > {
213        (0..props.columns).map(| _ | { html! { < th >< Placeholder width = {
214        Some(AttrValue::from("80%")) } size = { PlaceholderSize::Small } /></ th > } })
215        .collect::< Html > () } </ tr > </ thead > < tbody > { (0..props.rows).map(| _ |
216        { html! { < tr > { (0..props.columns).map(| _ | { html! { < td >< Placeholder
217        width = { Some(AttrValue::from("70%")) } /></ td > } }).collect::< Html > () } </
218        tr > } }).collect::< Html > () } </ tbody > </ table > </ div >
219    }
220}