duid-core 0.1.0

Core crate used for Duid
Documentation

#[macro_use]
mod attribute_macros;
mod attribute_value;
mod style;
mod attribute;

use crate::core::{
    events::Event,
    events::listener::Listener
};
pub use attribute_macros::*;
pub use attribute_value::AttributeValue;
pub use jss::Value;
pub use style::Style;
pub use attribute::*;


pub fn style<MSG>(
    style_name: &'static str,
    value: impl Into<Value>,
) -> Attribute<MSG> {
    attr(
        style_name,
        AttributeValue::from_styles([Style::new("", value.into())]),
    )
}

pub fn styles<MSG>(
    pairs: impl IntoIterator<Item = (impl ToString, impl Into<Value>)>,
) -> Attribute<MSG> {
    let styles = pairs.into_iter().map(|(key, value)| {
        Style::new(key.to_string(), Into::<Value>::into(value))
    });
    attr("style", AttributeValue::from_styles(styles))
}

pub fn styles_values<MSG>(
    pairs: impl IntoIterator<Item = (impl ToString, impl Into<Value>)>,
) -> Attribute<MSG> {
    let styles = pairs
        .into_iter()
        .map(|(key, value)| Style::new(key.to_string(), value));
    attr("style", AttributeValue::from_styles(styles))
}


pub fn styles_flag<MSG>(
    trio: impl IntoIterator<Item = (impl ToString, impl Into<Value>, bool)>,
) -> Attribute<MSG> {
    let styles = trio.into_iter().filter_map(|(key, value, flag)| {
        if flag {
            Some(Style::new(key, value))
        } else {
            None
        }
    });
    attr("style", AttributeValue::from_styles(styles))
}


pub fn classes_flag<MSG>(
    pair: impl IntoIterator<Item = (impl ToString, bool)>,
) -> Attribute<MSG> {
    let class_list = pair.into_iter().filter_map(|(class, flag)| {
        if flag {
            Some(class.to_string())
        } else {
            None
        }
    });

    classes(class_list)
}


pub fn classes<MSG>(
    class_list: impl IntoIterator<Item = impl ToString>,
) -> Attribute<MSG> {
    let class_values: Vec<_> = class_list
        .into_iter()
        .map(|v| AttributeValue::from_value(Value::from(v.to_string()))).collect();

    Attribute::with_multiple_values(None, "class", &class_values)
}


pub fn class_namespaced<MSG>(
    namespace: impl ToString,
    class_names: impl ToString,
) -> Attribute<MSG> {
    class(jss::class_namespaced(namespace, class_names))
}


pub fn classes_flag_namespaced<MSG>(
    namespace: impl ToString,
    pair: impl IntoIterator<Item = (impl ToString, bool)>,
) -> Attribute<MSG> {
    let class_list = pair.into_iter().filter_map(|(class_name, flag)| {
        if flag {
            Some(jss::class_namespaced(namespace.to_string(), class_name))
        } else {
            None
        }
    });
    classes(class_list)
}


pub fn attrs_flag<MSG>(
    trio: impl IntoIterator<Item = (&'static str, impl Into<Value>, bool)>,
) -> impl IntoIterator<Item = Attribute<MSG>> {
    trio.into_iter().filter_map(|(key, value, flag)| {
        if flag {
            Some(into_attr(key, value.into()))
        } else {
            None
        }
    })
}


pub fn maybe_attr<MSG>(
    name: &'static str,
    value: Option<impl Into<Value>>,
) -> Attribute<MSG> {
    if let Some(value) = value {
        into_attr(name, value)
    } else {
        empty_attr()
    }
}


pub fn checked<MSG>(is_checked: bool) -> Attribute<MSG> {
    if is_checked {
        #[cfg(not(feature = "with-dom"))]
        {
            into_attr("checked", "checked")
        }
        #[cfg(feature = "with-dom")]
        {
            into_attr("checked", true)
        }
    } else {
        empty_attr()
    }
}


pub fn disabled<MSG>(is_disabled: bool) -> Attribute<MSG> {
    if is_disabled {
        into_attr("disabled", true)
    } else {
        empty_attr()
    }
}


pub fn inner_html<V, MSG>(inner_html: V) -> Attribute<MSG>
where
    V: Into<Value> + Clone,
{
    attr(
        "inner_html",
        AttributeValue::function_call(inner_html.into()),
    )
}


pub fn focus<MSG>(is_focus: bool) -> Attribute<MSG> {
    into_attr("focus", is_focus)
}


pub fn into_attr<V: Into<Value>, MSG>(att: &'static str, v: V) -> Attribute<MSG> {
    attr(att, AttributeValue::from_value(v.into()))
}


pub fn empty_attr<MSG>() -> Attribute<MSG> {
    attr("", AttributeValue::Empty)
}

#[doc(hidden)]
pub(crate) fn merge_plain_attributes_values<MSG>(
    attr_values: &[&AttributeValue<MSG>],
) -> Option<String> {
    let plain_values: Vec<String> = attr_values
        .iter()
        .flat_map(|att_value| match att_value {
            AttributeValue::Simple(simple) => Some(simple.to_string()),
            AttributeValue::FunctionCall(fvalue) => Some(fvalue.to_string()),
            _ => None,
        })
        .collect();
    if !plain_values.is_empty() {
        Some(plain_values.join(" "))
    } else {
        None
    }
}

#[doc(hidden)]
pub(crate) fn merge_styles_attributes_values<MSG>(
    attr_values: &[&AttributeValue<MSG>],
) -> Option<String> {
    use std::fmt::Write;

    let styles_values: Vec<String> = attr_values
        .iter()
        .flat_map(|att_value| match att_value {
            AttributeValue::Style(styles) => {
                let mut style_str = String::new();
                styles.iter().for_each(|s| {
                    write!(style_str, "{}", s).expect("must write")
                });
                Some(style_str)
            }
            _ => None,
        })
        .collect();

    if !styles_values.is_empty() {
        Some(styles_values.join(" "))
    } else {
        None
    }
}

pub struct SegregatedAttributes<'a, MSG> {
    pub listeners: Vec<&'a Listener<Event, MSG>>,
    pub plain_values: Vec<&'a AttributeValue<MSG>>,
    pub styles: Vec<&'a AttributeValue<MSG>>,
    pub function_calls: Vec<&'a AttributeValue<MSG>>,
}

#[doc(hidden)]
pub(crate) fn partition_callbacks_from_plain_styles_and_func_calls<MSG>(
    attr: &Attribute<MSG>,
) -> SegregatedAttributes<MSG> {
    let mut listeners = vec![];
    let mut plain_values = vec![];
    let mut styles = vec![];
    let mut function_calls = vec![];
    for av in attr.value() {
        match av {
            AttributeValue::Simple(_plain) => {
                plain_values.push(av);
            }
            AttributeValue::FunctionCall(_call) => {
                function_calls.push(av);
            }
            AttributeValue::Style(_) => {
                styles.push(av);
            }
            AttributeValue::EventListener(cb) => {
                listeners.push(cb);
            }
            _ => (),
        }
    }
    SegregatedAttributes {
        listeners,
        plain_values,
        styles,
        function_calls,
    }
}