Merge branch 'leptos_dom_v2' of https://github.com/jquesada2016/leptos into leptos_dom_v2

This commit is contained in:
Jose Quesada 2022-12-03 12:29:45 -06:00
commit 234260a784
3 changed files with 233 additions and 5 deletions

View File

@ -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![

View File

@ -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)
/// macros 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"),
}
}

View File

@ -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::*;