use super::attribute::{
maybe_next_attr_erasure_macros::next_attr_output_type, Attribute,
NextAttribute,
};
use crate::{
html::attribute::{
maybe_next_attr_erasure_macros::next_attr_combine, NamedAttributeKey,
},
renderer::Rndr,
view::{Position, ToTemplate},
};
use send_wrapper::SendWrapper;
use std::{borrow::Cow, sync::Arc};
use wasm_bindgen::JsValue;
#[inline(always)]
pub fn prop<K, P>(key: K, value: P) -> Property<K, P>
where
K: AsRef<str>,
P: IntoProperty,
{
Property {
key,
value: (!cfg!(feature = "ssr")).then(|| SendWrapper::new(value)),
}
}
#[derive(Debug)]
pub struct Property<K, P> {
key: K,
value: Option<SendWrapper<P>>,
}
impl<K, P> Clone for Property<K, P>
where
K: Clone,
P: Clone,
{
fn clone(&self) -> Self {
Self {
key: self.key.clone(),
value: self.value.clone(),
}
}
}
impl<K, P> Attribute for Property<K, P>
where
K: AsRef<str> + Send,
P: IntoProperty,
{
const MIN_LENGTH: usize = 0;
type AsyncOutput = Self;
type State = P::State;
type Cloneable = Property<Arc<str>, P::Cloneable>;
type CloneableOwned = Property<Arc<str>, P::CloneableOwned>;
#[inline(always)]
fn html_len(&self) -> usize {
0
}
fn to_html(
self,
_buf: &mut String,
_class: &mut String,
_style: &mut String,
_inner_html: &mut String,
) {
}
fn hydrate<const FROM_SERVER: bool>(
self,
el: &crate::renderer::types::Element,
) -> Self::State {
self.value
.expect("property removed early")
.take()
.hydrate::<FROM_SERVER>(el, self.key.as_ref())
}
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
self.value
.expect("property removed early")
.take()
.build(el, self.key.as_ref())
}
fn rebuild(self, state: &mut Self::State) {
self.value
.expect("property removed early")
.take()
.rebuild(state, self.key.as_ref())
}
fn into_cloneable(self) -> Self::Cloneable {
Property {
key: self.key.as_ref().into(),
value: self
.value
.map(|value| SendWrapper::new(value.take().into_cloneable())),
}
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
Property {
key: self.key.as_ref().into(),
value: self.value.map(|value| {
SendWrapper::new(value.take().into_cloneable_owned())
}),
}
}
fn dry_resolve(&mut self) {}
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn keys(&self) -> Vec<NamedAttributeKey> {
vec![NamedAttributeKey::Property(
self.key.as_ref().to_string().into(),
)]
}
}
impl<K, P> NextAttribute for Property<K, P>
where
K: AsRef<str> + Send,
P: IntoProperty,
{
next_attr_output_type!(Self, NewAttr);
fn add_any_attr<NewAttr: Attribute>(
self,
new_attr: NewAttr,
) -> Self::Output<NewAttr> {
next_attr_combine!(self, new_attr)
}
}
impl<K, P> ToTemplate for Property<K, P>
where
K: AsRef<str>,
P: IntoProperty,
{
fn to_template(
_buf: &mut String,
_class: &mut String,
_style: &mut String,
_inner_html: &mut String,
_position: &mut Position,
) {
}
}
pub trait IntoProperty {
type State;
type Cloneable: IntoProperty + Clone;
type CloneableOwned: IntoProperty + Clone + 'static;
fn hydrate<const FROM_SERVER: bool>(
self,
el: &crate::renderer::types::Element,
key: &str,
) -> Self::State;
fn build(
self,
el: &crate::renderer::types::Element,
key: &str,
) -> Self::State;
fn rebuild(self, state: &mut Self::State, key: &str);
fn into_cloneable(self) -> Self::Cloneable;
fn into_cloneable_owned(self) -> Self::CloneableOwned;
}
macro_rules! prop_type {
($prop_type:ty) => {
impl IntoProperty for $prop_type {
type State = (crate::renderer::types::Element, JsValue);
type Cloneable = Self;
type CloneableOwned = Self;
fn hydrate<const FROM_SERVER: bool>(
self,
el: &crate::renderer::types::Element,
key: &str,
) -> Self::State {
let value = self.into();
Rndr::set_property_or_value(el, key, &value);
(el.clone(), value)
}
fn build(
self,
el: &crate::renderer::types::Element,
key: &str,
) -> Self::State {
let value = self.into();
Rndr::set_property_or_value(el, key, &value);
(el.clone(), value)
}
fn rebuild(self, state: &mut Self::State, key: &str) {
let (el, prev) = state;
let value = self.into();
Rndr::set_property_or_value(el, key, &value);
*prev = value;
}
fn into_cloneable(self) -> Self::Cloneable {
self
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
self
}
}
impl IntoProperty for Option<$prop_type> {
type State = (crate::renderer::types::Element, JsValue);
type Cloneable = Self;
type CloneableOwned = Self;
fn hydrate<const FROM_SERVER: bool>(
self,
el: &crate::renderer::types::Element,
key: &str,
) -> Self::State {
let was_some = self.is_some();
let value = self.into();
if was_some {
Rndr::set_property_or_value(el, key, &value);
}
(el.clone(), value)
}
fn build(
self,
el: &crate::renderer::types::Element,
key: &str,
) -> Self::State {
let was_some = self.is_some();
let value = self.into();
if was_some {
Rndr::set_property_or_value(el, key, &value);
}
(el.clone(), value)
}
fn rebuild(self, state: &mut Self::State, key: &str) {
let (el, prev) = state;
let value = self.into();
Rndr::set_property_or_value(el, key, &value);
*prev = value;
}
fn into_cloneable(self) -> Self::Cloneable {
self
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
self
}
}
};
}
macro_rules! prop_type_str {
($prop_type:ty) => {
impl IntoProperty for $prop_type {
type State = (crate::renderer::types::Element, JsValue);
type Cloneable = Arc<str>;
type CloneableOwned = Arc<str>;
fn hydrate<const FROM_SERVER: bool>(
self,
el: &crate::renderer::types::Element,
key: &str,
) -> Self::State {
let value = JsValue::from(&*self);
Rndr::set_property_or_value(el, key, &value);
(el.clone(), value)
}
fn build(
self,
el: &crate::renderer::types::Element,
key: &str,
) -> Self::State {
let value = JsValue::from(&*self);
Rndr::set_property_or_value(el, key, &value);
(el.clone(), value)
}
fn rebuild(self, state: &mut Self::State, key: &str) {
let (el, prev) = state;
let value = JsValue::from(&*self);
Rndr::set_property_or_value(el, key, &value);
*prev = value;
}
fn into_cloneable(self) -> Self::Cloneable {
let this: &str = &*self;
this.into()
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
let this: &str = &*self;
this.into()
}
}
impl IntoProperty for Option<$prop_type> {
type State = (crate::renderer::types::Element, JsValue);
type Cloneable = Option<Arc<str>>;
type CloneableOwned = Option<Arc<str>>;
fn hydrate<const FROM_SERVER: bool>(
self,
el: &crate::renderer::types::Element,
key: &str,
) -> Self::State {
let was_some = self.is_some();
let value = JsValue::from(self.map(|n| JsValue::from_str(&n)));
if was_some {
Rndr::set_property_or_value(el, key, &value);
}
(el.clone(), value)
}
fn build(
self,
el: &crate::renderer::types::Element,
key: &str,
) -> Self::State {
let was_some = self.is_some();
let value = JsValue::from(self.map(|n| JsValue::from_str(&n)));
if was_some {
Rndr::set_property_or_value(el, key, &value);
}
(el.clone(), value)
}
fn rebuild(self, state: &mut Self::State, key: &str) {
let (el, prev) = state;
let value = JsValue::from(self.map(|n| JsValue::from_str(&n)));
Rndr::set_property_or_value(el, key, &value);
*prev = value;
}
fn into_cloneable(self) -> Self::Cloneable {
self.map(|n| {
let this: &str = &*n;
this.into()
})
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
self.map(|n| {
let this: &str = &*n;
this.into()
})
}
}
};
}
impl IntoProperty for Arc<str> {
type State = (crate::renderer::types::Element, JsValue);
type Cloneable = Self;
type CloneableOwned = Self;
fn hydrate<const FROM_SERVER: bool>(
self,
el: &crate::renderer::types::Element,
key: &str,
) -> Self::State {
let value = JsValue::from_str(self.as_ref());
Rndr::set_property_or_value(el, key, &value);
(el.clone(), value)
}
fn build(
self,
el: &crate::renderer::types::Element,
key: &str,
) -> Self::State {
let value = JsValue::from_str(self.as_ref());
Rndr::set_property_or_value(el, key, &value);
(el.clone(), value)
}
fn rebuild(self, state: &mut Self::State, key: &str) {
let (el, prev) = state;
let value = JsValue::from_str(self.as_ref());
Rndr::set_property_or_value(el, key, &value);
*prev = value;
}
fn into_cloneable(self) -> Self::Cloneable {
self
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
self
}
}
impl IntoProperty for Option<Arc<str>> {
type State = (crate::renderer::types::Element, JsValue);
type Cloneable = Self;
type CloneableOwned = Self;
fn hydrate<const FROM_SERVER: bool>(
self,
el: &crate::renderer::types::Element,
key: &str,
) -> Self::State {
let was_some = self.is_some();
let value = JsValue::from(self.map(|n| JsValue::from_str(&n)));
if was_some {
Rndr::set_property_or_value(el, key, &value);
}
(el.clone(), value)
}
fn build(
self,
el: &crate::renderer::types::Element,
key: &str,
) -> Self::State {
let was_some = self.is_some();
let value = JsValue::from(self.map(|n| JsValue::from_str(&n)));
if was_some {
Rndr::set_property_or_value(el, key, &value);
}
(el.clone(), value)
}
fn rebuild(self, state: &mut Self::State, key: &str) {
let (el, prev) = state;
let value = JsValue::from(self.map(|n| JsValue::from_str(&n)));
Rndr::set_property_or_value(el, key, &value);
*prev = value;
}
fn into_cloneable(self) -> Self::Cloneable {
self
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
self
}
}
prop_type!(JsValue);
prop_type!(usize);
prop_type!(u8);
prop_type!(u16);
prop_type!(u32);
prop_type!(u64);
prop_type!(u128);
prop_type!(isize);
prop_type!(i8);
prop_type!(i16);
prop_type!(i32);
prop_type!(i64);
prop_type!(i128);
prop_type!(f32);
prop_type!(f64);
prop_type!(bool);
prop_type_str!(String);
prop_type_str!(&String);
prop_type_str!(&str);
prop_type_str!(Cow<'_, str>);