#![allow(clippy::type_complexity)]
use crate::vdom::ComponentEventCallback;
use crate::vdom::EventCallback;
use derive_where::derive_where;
use indexmap::IndexMap;
pub use attribute_value::AttributeValue;
pub use callback::Callback;
pub use style::Style;
pub use value::Value;
mod attribute_value;
pub mod callback;
pub(crate) mod special;
mod style;
mod value;
pub type Namespace = &'static str;
pub type Tag = &'static str;
pub type AttributeName = &'static str;
#[derive_where(Clone, Debug, PartialEq, Eq)]
pub struct Attribute<MSG> {
pub namespace: Option<Namespace>,
pub name: AttributeName,
pub value: Vec<AttributeValue<MSG>>,
}
pub struct GroupedAttributeValues<'a, MSG> {
pub listeners: Vec<&'a EventCallback<MSG>>,
pub component_callbacks: Vec<&'a ComponentEventCallback>,
pub plain_values: Vec<&'a Value>,
pub styles: Vec<&'a Style>,
}
impl<MSG> Attribute<MSG> {
pub fn new(
namespace: Option<Namespace>,
name: AttributeName,
value: AttributeValue<MSG>,
) -> Self {
Attribute {
name,
value: vec![value],
namespace,
}
}
pub fn with_multiple_values(
namespace: Option<Namespace>,
name: AttributeName,
value: impl IntoIterator<Item = AttributeValue<MSG>>,
) -> Self {
Attribute {
name,
value: value.into_iter().collect(),
namespace,
}
}
pub fn name(&self) -> &AttributeName {
&self.name
}
pub fn value(&self) -> &[AttributeValue<MSG>] {
&self.value
}
pub fn namespace(&self) -> Option<&Namespace> {
self.namespace.as_ref()
}
pub fn is_event_listener(&self) -> bool {
self.value
.first()
.map(|v| v.is_event_listener())
.unwrap_or(false)
}
pub(crate) fn group_values(attr: &Attribute<MSG>) -> GroupedAttributeValues<MSG> {
let mut listeners = vec![];
let mut component_callbacks = vec![];
let mut plain_values = vec![];
let mut styles = vec![];
for av in attr.value() {
match av {
AttributeValue::Simple(v) => {
plain_values.push(v);
}
AttributeValue::Style(v) => {
styles.extend(v);
}
AttributeValue::EventListener(cb) => {
listeners.push(cb);
}
AttributeValue::ComponentEventListener(cb) => {
component_callbacks.push(cb);
}
AttributeValue::Empty => (),
}
}
GroupedAttributeValues {
listeners,
component_callbacks,
plain_values,
styles,
}
}
fn is_just_empty(&self) -> bool {
self.value
.first()
.map(|av| av.is_just_empty())
.unwrap_or(false)
}
pub(crate) fn is_mount_callback(&self) -> bool {
self.name == "mount"
}
pub fn merge_attributes_of_same_name<'a>(
attributes: impl IntoIterator<Item = &'a Attribute<MSG>> + Iterator,
) -> Vec<Attribute<MSG>>
where
MSG: 'a,
{
let mut merged: IndexMap<&AttributeName, Attribute<MSG>> =
IndexMap::with_capacity(attributes.size_hint().0);
for att in attributes.into_iter() {
if !att.is_just_empty() {
if let Some(existing) = merged.get_mut(&att.name) {
existing.value.extend(att.value.clone());
} else {
merged.insert(
&att.name,
Attribute {
namespace: att.namespace,
name: att.name,
value: att.value.clone(),
},
);
}
}
}
merged.into_values().collect()
}
#[doc(hidden)]
pub fn group_attributes_per_name<'a>(
attributes: impl IntoIterator<Item = &'a Attribute<MSG>> + Iterator,
) -> IndexMap<&'a AttributeName, Vec<&'a Attribute<MSG>>> {
let mut grouped: IndexMap<&'a AttributeName, Vec<&'a Attribute<MSG>>> =
IndexMap::with_capacity(attributes.size_hint().0);
for attr in attributes {
if let Some(existing) = grouped.get_mut(&attr.name) {
existing.push(attr);
} else {
grouped.insert(&attr.name, vec![attr]);
}
}
grouped
}
}
#[inline]
pub fn attr<MSG>(name: AttributeName, value: impl Into<AttributeValue<MSG>>) -> Attribute<MSG> {
attr_ns(None, name, value)
}
#[inline]
pub fn attr_ns<MSG>(
namespace: Option<Namespace>,
name: AttributeName,
value: impl Into<AttributeValue<MSG>>,
) -> Attribute<MSG> {
Attribute::new(namespace, name, value.into())
}