Merge branch 'leptos_dom_v2' of https://github.com/jquesada2016/leptos into leptos_dom_v2
This commit is contained in:
commit
234260a784
|
@ -578,22 +578,70 @@ macro_rules! generate_html_tags {
|
|||
}
|
||||
|
||||
// view! macro helpers
|
||||
use crate::macro_helpers::*;
|
||||
use leptos_reactive::create_render_effect;
|
||||
impl<El: IntoElement> HtmlElement<El> {
|
||||
#[doc(hidden)]
|
||||
#[track_caller]
|
||||
pub fn _attr(mut self, cx: Scope, name: impl Into<Cow<'static, str>>, attr: impl IntoAttribute) -> Self {
|
||||
let name = name.into();
|
||||
cfg_if! {
|
||||
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
|
||||
let el = self.element.get_element();
|
||||
let value = attr.into_attribute(cx);
|
||||
match value {
|
||||
Attribute::Fn(f) => {
|
||||
let el = el.clone();
|
||||
create_render_effect(cx, move |old| {
|
||||
let new = f();
|
||||
if old.as_ref() != Some(&new) {
|
||||
attribute_expression(&el, &name, new.clone());
|
||||
}
|
||||
new
|
||||
});
|
||||
}
|
||||
_ => attribute_expression(el, &name, value),
|
||||
};
|
||||
self
|
||||
}
|
||||
else {
|
||||
let mut attr = attr.into_attribute(cx);
|
||||
while let Attribute::Fn(f) = attr {
|
||||
attr = f();
|
||||
}
|
||||
match attr {
|
||||
Attribute::String(value) => self.attr(name, value),
|
||||
Attribute::Bool(include) => if include {
|
||||
self.attr_bool(name)
|
||||
} else {
|
||||
self
|
||||
},
|
||||
Attribute::Option(maybe) => if let Some(value) = maybe {
|
||||
self.attr(name, value)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[track_caller]
|
||||
pub fn _child<C: crate::macro_helpers::IntoChild>(mut self, cx: Scope, child: C) -> Self {
|
||||
pub fn _child(mut self, cx: Scope, child: impl IntoChild) -> Self {
|
||||
let child = child.into_child(cx);
|
||||
cfg_if! {
|
||||
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
|
||||
mount_child(MountKind::Append(self.element.get_element()), &child.into_node(cx))
|
||||
mount_child(MountKind::Append(self.element.get_element()), &child.into_node(cx))
|
||||
}
|
||||
else {
|
||||
self.children.push(Box::new(move |cx| child.into_node(cx)));
|
||||
self.children.push(Box::new(move |cx| child.into_node(cx)));
|
||||
}
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
generate_html_tags![
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
use leptos_reactive::Scope;
|
||||
use wasm_bindgen::UnwrapThrowExt;
|
||||
|
||||
/// Represents the different possible values an attribute node could have.
|
||||
///
|
||||
/// This mostly exists for the [`view`](https://docs.rs/leptos_macro/latest/leptos_macro/macro.view.html)
|
||||
/// macro’s use. You usually won't need to interact with it directly.
|
||||
#[derive(Clone)]
|
||||
pub enum Attribute {
|
||||
/// A plain string value.
|
||||
String(String),
|
||||
/// A (presumably reactive) function, which will be run inside an effect to do targeted updates to the attribute.
|
||||
Fn(Rc<dyn Fn() -> Attribute>),
|
||||
/// An optional string value, which sets the attribute to the value if `Some` and removes the attribute if `None`.
|
||||
Option(Option<String>),
|
||||
/// A boolean attribute, which sets the attribute if `true` and removes the attribute if `false`.
|
||||
Bool(bool),
|
||||
}
|
||||
|
||||
impl Attribute {
|
||||
/// Converts the attribute to its HTML value at that moment so it can be rendered on the server.
|
||||
pub fn as_value_string(&self, attr_name: &'static str) -> String {
|
||||
match self {
|
||||
Attribute::String(value) => format!("{attr_name}=\"{value}\""),
|
||||
Attribute::Fn(f) => {
|
||||
let mut value = f();
|
||||
while let Attribute::Fn(f) = value {
|
||||
value = f();
|
||||
}
|
||||
value.as_value_string(attr_name)
|
||||
}
|
||||
Attribute::Option(value) => value
|
||||
.as_ref()
|
||||
.map(|value| format!("{attr_name}=\"{value}\""))
|
||||
.unwrap_or_default(),
|
||||
Attribute::Bool(include) => {
|
||||
if *include {
|
||||
attr_name.to_string()
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Attribute {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(Self::String(l0), Self::String(r0)) => l0 == r0,
|
||||
(Self::Fn(_), Self::Fn(_)) => false,
|
||||
(Self::Option(l0), Self::Option(r0)) => l0 == r0,
|
||||
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Attribute {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::String(arg0) => f.debug_tuple("String").field(arg0).finish(),
|
||||
Self::Fn(_) => f.debug_tuple("Fn").finish(),
|
||||
Self::Option(arg0) => f.debug_tuple("Option").field(arg0).finish(),
|
||||
Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts some type into an [Attribute].
|
||||
///
|
||||
/// This is implemented by default for Rust primitive and string types.
|
||||
pub trait IntoAttribute {
|
||||
/// Converts the object into an [Attribute].
|
||||
fn into_attribute(self, cx: Scope) -> Attribute;
|
||||
}
|
||||
|
||||
impl IntoAttribute for String {
|
||||
fn into_attribute(self, _cx: Scope) -> Attribute {
|
||||
Attribute::String(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAttribute for bool {
|
||||
fn into_attribute(self, _cx: Scope) -> Attribute {
|
||||
Attribute::Bool(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAttribute for Option<String> {
|
||||
fn into_attribute(self, _cx: Scope) -> Attribute {
|
||||
Attribute::Option(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> IntoAttribute for T
|
||||
where
|
||||
T: Fn() -> U + 'static,
|
||||
U: IntoAttribute,
|
||||
{
|
||||
fn into_attribute(self, cx: Scope) -> Attribute {
|
||||
let modified_fn = Rc::new(move || (self)().into_attribute(cx));
|
||||
Attribute::Fn(modified_fn)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! attr_type {
|
||||
($attr_type:ty) => {
|
||||
impl IntoAttribute for $attr_type {
|
||||
fn into_attribute(self, _cx: Scope) -> Attribute {
|
||||
Attribute::String(self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAttribute for Option<$attr_type> {
|
||||
fn into_attribute(self, _cx: Scope) -> Attribute {
|
||||
Attribute::Option(self.map(|n| n.to_string()))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
attr_type!(&String);
|
||||
attr_type!(&str);
|
||||
attr_type!(usize);
|
||||
attr_type!(u8);
|
||||
attr_type!(u16);
|
||||
attr_type!(u32);
|
||||
attr_type!(u64);
|
||||
attr_type!(u128);
|
||||
attr_type!(isize);
|
||||
attr_type!(i8);
|
||||
attr_type!(i16);
|
||||
attr_type!(i32);
|
||||
attr_type!(i64);
|
||||
attr_type!(i128);
|
||||
attr_type!(f32);
|
||||
attr_type!(f64);
|
||||
attr_type!(char);
|
||||
|
||||
pub fn attribute_expression(el: &web_sys::Element, attr_name: &str, value: Attribute) {
|
||||
match value {
|
||||
Attribute::String(value) => {
|
||||
let value = wasm_bindgen::intern(&value);
|
||||
if attr_name == "inner_html" {
|
||||
el.set_inner_html(&value);
|
||||
} else {
|
||||
let attr_name = wasm_bindgen::intern(attr_name);
|
||||
el.set_attribute(attr_name, &value).unwrap_throw();
|
||||
}
|
||||
}
|
||||
Attribute::Option(value) => {
|
||||
if attr_name == "inner_html" {
|
||||
el.set_inner_html(&value.unwrap_or_default());
|
||||
} else {
|
||||
let attr_name = wasm_bindgen::intern(attr_name);
|
||||
match value {
|
||||
Some(value) => {
|
||||
let value = wasm_bindgen::intern(&value);
|
||||
el.set_attribute(attr_name, &value).unwrap_throw();
|
||||
}
|
||||
None => el.remove_attribute(attr_name).unwrap_throw(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Attribute::Bool(value) => {
|
||||
let attr_name = wasm_bindgen::intern(attr_name);
|
||||
if value {
|
||||
el.set_attribute(attr_name, attr_name).unwrap_throw();
|
||||
} else {
|
||||
el.remove_attribute(attr_name).unwrap_throw();
|
||||
}
|
||||
}
|
||||
_ => panic!("Remove nested Fn in Attribute"),
|
||||
}
|
||||
}
|
|
@ -1,2 +1,4 @@
|
|||
mod into_attribute;
|
||||
mod into_child;
|
||||
pub(crate) use into_child::IntoChild;
|
||||
pub(crate) use into_attribute::*;
|
||||
pub(crate) use into_child::*;
|
Loading…
Reference in New Issue