--- /dev/null
+use indexmap::IndexMap;
+use yew::{html::IntoPropValue, AttrValue};
+
+/// Holds the CSS styles to set on elements
+#[derive(Clone, Default, Debug, PartialEq)]
+pub struct CssStyles {
+ styles: IndexMap<AttrValue, AttrValue>,
+}
+
+impl CssStyles {
+ /// Method to set style attributes
+ ///
+ /// Note: Value 'None' removes the attribute.
+ /// Note: In debug mode, panics on invalid characters (';' and ':')
+ pub fn set_style(
+ &mut self,
+ key: impl Into<AttrValue>,
+ value: impl IntoPropValue<Option<AttrValue>>,
+ ) {
+ let key = key.into();
+ #[cfg(debug_assertions)]
+ if key.contains(|x| x == ';' || x == ':') {
+ panic!("invalid character in style key: '{key}'");
+ }
+ if let Some(value) = value.into_prop_value() {
+ #[cfg(debug_assertions)]
+ if value.contains(|x| x == ';' || x == ':') {
+ panic!("invalid character in style value '{value}' for '{key}'");
+ }
+ self.styles
+ .insert(AttrValue::from(key), AttrValue::from(value));
+ } else {
+ self.styles.swap_remove(&AttrValue::from(key));
+ }
+ }
+
+ /// Method to compile the finished style attribute to use
+ ///
+ /// Optionally takes an additional [yew::AttrValue] to append
+ pub fn compile_style_attribute(&self, additional_style: Option<AttrValue>) -> AttrValue {
+ let mut style = String::new();
+
+ for (key, value) in self.styles.iter() {
+ style += &format!("{key}: {value};");
+ }
+
+ if let Some(additional_style) = additional_style {
+ style += &additional_style;
+ }
+
+ AttrValue::from(style)
+ }
+}
+
+// not completely spec compliant, since we ignore 'at-rules', but there are
+// no valid ones currently, so this should not be an issue
+// https://w3c.github.io/csswg-drafts/css-style-attr/
+impl<T: AsRef<str>> From<T> for CssStyles {
+ fn from(value: T) -> Self {
+ let mut this: CssStyles = Default::default();
+ for rule in value.as_ref().split(';') {
+ if let Some((key, val)) = rule.split_once(':') {
+ this.set_style(key.to_owned(), val.to_owned());
+ }
+ }
+ this
+ }
+}
+
+/// Trait which provides mutable access to the style property.
+pub trait AsCssStylesMut {
+ fn as_css_styles_mut(&mut self) -> &mut CssStyles;
+}
+
+impl AsCssStylesMut for CssStyles {
+ fn as_css_styles_mut(&mut self) -> &mut CssStyles {
+ self
+ }
+}
use yew::prelude::*;
use yew::virtual_dom::{ApplyAttributeAs, Attributes, Key, Listeners, VList, VNode, VTag};
-use crate::props::ListenersWrapper;
+use crate::props::{AsCssStylesMut, CssStyles, ListenersWrapper};
/// Standard widget properties.
#[derive(PartialEq, Debug, Default, Clone)]
/// Additional Html attributes.
pub attributes: Attributes,
+
+ /// Additional CSS styles
+ pub styles: CssStyles,
}
impl WidgetStdProps {
(class.into_prop_value(), ApplyAttributeAs::Attribute),
);
+ let style = self
+ .styles
+ .compile_style_attribute(attr_map.get("style").map(|a| a.0.clone()));
+
+ attr_map.insert(
+ AttrValue::Static("style"),
+ (style, ApplyAttributeAs::Attribute),
+ );
+
attributes
}
)
}
}
+
+impl AsCssStylesMut for WidgetStdProps {
+ fn as_css_styles_mut(&mut self) -> &mut CssStyles {
+ &mut self.styles
+ }
+}
--- /dev/null
+use std::fmt::Display;
+
+use yew::{html::IntoPropValue, AttrValue};
+
+use crate::props::{AsCssStylesMut, CssStyles};
+
+/// CSS length in pixel, em or percentage.
+#[derive(Copy, Clone, PartialEq)]
+pub enum CssLength {
+ Px(f64),
+ Em(f64),
+ Fraction(f32),
+ None,
+}
+
+impl Default for CssLength {
+ fn default() -> Self {
+ CssLength::Px(0.0)
+ }
+}
+
+impl Display for CssLength {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+ match self {
+ CssLength::Px(v) => write!(f, "{v}px"),
+ CssLength::Em(v) => write!(f, "{v}em"),
+ CssLength::Fraction(v) => write!(f, "{}%", v * 100.0),
+ CssLength::None => Ok(()),
+ }
+ }
+}
+
+impl From<f32> for CssLength {
+ fn from(v: f32) -> CssLength {
+ CssLength::Px(v as f64)
+ }
+}
+
+impl From<f64> for CssLength {
+ fn from(v: f64) -> CssLength {
+ CssLength::Px(v)
+ }
+}
+
+impl From<usize> for CssLength {
+ fn from(v: usize) -> CssLength {
+ CssLength::Px(v as f64)
+ }
+}
+
+impl From<i32> for CssLength {
+ fn from(v: i32) -> CssLength {
+ CssLength::Px(v as f64)
+ }
+}
+
+impl From<CssLength> for AttrValue {
+ fn from(v: CssLength) -> Self {
+ v.to_string().into()
+ }
+}
+
+impl IntoPropValue<Option<AttrValue>> for CssLength {
+ fn into_prop_value(self) -> Option<AttrValue> {
+ match self {
+ CssLength::None => None,
+ other => Some(other.into()),
+ }
+ }
+}
+
+// macro to generate the trait functions
+
+macro_rules! generate_style_trait_fn {
+ ($func:ident, $builder:ident, $name:literal) => {
+ /// Builder style method to set the $name of the element style.
+ ///
+ /// Note: Value [CssLength::None] removes it.
+ fn $builder(mut self, value: impl Into<CssLength>) -> Self {
+ self.$func(value);
+ self
+ }
+
+ /// Sets the $name of the element style.
+ ///
+ /// Note: Value [CssLength::None] removes it.
+ fn $func(&mut self, value: impl Into<CssLength>) {
+ self.as_css_styles_mut().set_style($name, value.into());
+ }
+ };
+}
+
+pub trait WidgetStyleBuilder: AsCssStylesMut + Sized {
+ /// Builder style method to override all styles for the element with the given ones
+ fn styles(mut self, styles: CssStyles) -> Self {
+ self.set_styles(styles);
+ self
+ }
+
+ /// Overrides all styles for the element with the given ones
+ fn set_styles(&mut self, styles: CssStyles) {
+ *self.as_css_styles_mut() = styles;
+ }
+
+ /// Builder style method to set additional css styles via the 'style' attribute
+ ///
+ /// Note: Value 'None' removes the style.
+ fn style(
+ mut self,
+ key: impl Into<AttrValue>,
+ value: impl IntoPropValue<Option<AttrValue>>,
+ ) -> Self {
+ self.set_style(key, value);
+ self
+ }
+
+ /// Method to set additional css styles via the 'style' attribute
+ ///
+ /// Note: Value 'None' removes the style.
+ fn set_style(
+ &mut self,
+ key: impl Into<AttrValue>,
+ value: impl IntoPropValue<Option<AttrValue>>,
+ ) {
+ self.as_css_styles_mut().set_style(key, value)
+ }
+
+ generate_style_trait_fn!(set_width, width, "width");
+ generate_style_trait_fn!(set_min_width, min_width, "min-width");
+ generate_style_trait_fn!(set_max_width, max_width, "max-width");
+ generate_style_trait_fn!(set_height, height, "height");
+ generate_style_trait_fn!(set_min_height, min_height, "min-height");
+ generate_style_trait_fn!(set_max_height, max_height, "max-height");
+}