use crate::prelude::*; use std::collections::HashMap; #[cfg(feature = "validate")] use validator::ValidationErrors; use yew::{prelude::*, virtual_dom::VNode}; pub(crate) fn collect_props<'a, T>(t: &'a Option, ts: &'a [T]) -> Vec<&'a T> { if let Some(t) = t.as_ref() { let mut r = vec![t]; r.append(&mut ts.iter().collect()); r } else { ts.iter().collect() } } #[derive(Properties, Clone, PartialEq, Default)] pub struct Props { // html specific #[prop_or_default] pub id: Option, #[prop_or_default] pub aria_label: Option, #[prop_or_default] pub role: Option, #[prop_or_default] pub class: Classes, #[prop_or_default] pub style: Option, #[prop_or_default] pub children: Children, // optional validation support #[cfg(feature = "validate")] #[prop_or_default] pub error_code: Option, #[cfg(feature = "validate")] #[prop_or_default] pub errors: Option, // bootstrap specific #[prop_or_default] pub border: Option, #[prop_or_default] pub borders: Vec, #[prop_or_default] pub margin: Option, #[prop_or_default] pub margins: Vec, #[prop_or_default] pub padding: Option, #[prop_or_default] pub paddings: Vec, } impl<'a> From<&'a Props> for BootstrapProps<'a> { fn from(props: &Props) -> BootstrapProps { let class = &props.class; let borders = collect_props(&props.border, &props.borders); let margins = collect_props(&props.margin, &props.margins); let paddings = collect_props(&props.padding, &props.paddings); let mut attributes = HashMap::new(); add_opt_attr(&mut attributes, "id", &props.id); add_opt_attr(&mut attributes, "aria-label", &props.aria_label); add_opt_attr(&mut attributes, "role", &props.role); add_opt_attr(&mut attributes, "style", &props.style); BootstrapProps { class, borders, margins, paddings, attributes, } } } pub struct BootstrapProps<'a> { pub class: &'a Classes, pub borders: Vec<&'a Border>, pub margins: Vec<&'a Margin>, pub paddings: Vec<&'a Padding>, pub attributes: HashMap<&'static str, &'a String>, } impl<'a> BootstrapProps<'a> { pub fn add_attributes(&self, html: &mut VNode) { if let VNode::VTag(tag) = html { for key in self.attributes.keys() { if let Some(value) = &self.attributes.get(key) { let value = **value; tag.add_attribute(key, value.clone()); } } } } pub fn calculate_classes>(&self, prefix: C) -> Classes { let BootstrapProps { class, borders, margins, paddings, .. } = self; let mut classes: Classes = prefix.into(); classes.push((*class).to_owned()); classes.extend(borders); classes.extend(margins); classes.extend(paddings); classes } } pub(crate) fn add_opt_attr<'a>( attrs: &mut HashMap<&str, &'a String>, name: &'static str, opt: &'a Option, ) { if let Some(val) = opt.as_ref() { attrs.insert(name, val); } } #[cfg(test)] mod test { use super::*; #[test] fn test_aria_label_override() { let props = Props { aria_label: Some("override".into()), ..Props::default() }; let expected = html! {
}; crate::test::assert_attrs_eq( expected, crate::render::render_with_prefix(&props, "", html! {
}), ); } #[test] fn test_opt_attr() { let props = Props { aria_label: Some("some-label".into()), ..Props::default() }; let expected = html! {
}; crate::test::assert_attrs_eq( expected, crate::render::render_with_prefix(&props, "", html! {
}), ); } }