mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 14:54:16 +00:00
Deal w/ merge issues and get SSR working
This commit is contained in:
parent
dbb0fca1cb
commit
0230b1ffa5
7 changed files with 381 additions and 519 deletions
|
@ -46,7 +46,7 @@ pub struct EachRepr {
|
|||
document_fragment: web_sys::DocumentFragment,
|
||||
#[cfg(debug_assertions)]
|
||||
opening: Comment,
|
||||
children: Rc<RefCell<Vec<Option<EachItem>>>>,
|
||||
pub(crate) children: Rc<RefCell<Vec<Option<EachItem>>>>,
|
||||
closing: Comment,
|
||||
}
|
||||
|
||||
|
@ -112,12 +112,12 @@ impl Mountable for EachRepr {
|
|||
|
||||
/// The internal representation of an [`EachKey`] item.
|
||||
#[derive(Debug)]
|
||||
struct EachItem {
|
||||
pub(crate) struct EachItem {
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
document_fragment: web_sys::DocumentFragment,
|
||||
#[cfg(debug_assertions)]
|
||||
opening: Comment,
|
||||
child: View,
|
||||
pub(crate) child: View,
|
||||
closing: Comment,
|
||||
}
|
||||
|
||||
|
|
|
@ -259,17 +259,23 @@ impl<El: IntoElement> HtmlElement<El> {
|
|||
else {
|
||||
let mut attr = attr.into_attribute(cx);
|
||||
while let Attribute::Fn(f) = attr {
|
||||
self.dynamic = true;
|
||||
attr = f();
|
||||
}
|
||||
match attr {
|
||||
Attribute::String(value) => self.attr(name, value),
|
||||
Attribute::String(value) => {
|
||||
self.attrs.push((name, value.into()));
|
||||
self
|
||||
},
|
||||
Attribute::Bool(include) => if include {
|
||||
self.attr_bool(name)
|
||||
self.attrs.push((name, "".into()));
|
||||
self
|
||||
} else {
|
||||
self
|
||||
},
|
||||
Attribute::Option(maybe) => if let Some(value) = maybe {
|
||||
self.attr(name, value)
|
||||
self.attrs.push((name, value.into()));
|
||||
self
|
||||
} else {
|
||||
self
|
||||
}
|
||||
|
@ -279,7 +285,7 @@ impl<El: IntoElement> HtmlElement<El> {
|
|||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// Adds a class to an element.
|
||||
#[track_caller]
|
||||
pub fn class(
|
||||
mut self,
|
||||
|
@ -289,96 +295,87 @@ impl<El: IntoElement> HtmlElement<El> {
|
|||
) -> Self {
|
||||
let name = name.into();
|
||||
cfg_if! {
|
||||
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
|
||||
create_effect(
|
||||
self.cx,
|
||||
clone!([{ *self.element.get_element() } as element], move |_| {
|
||||
if let Some(value) = f() {
|
||||
let value = value.into();
|
||||
|
||||
element.set_attribute(intern(&name), intern(&value)).unwrap();
|
||||
} else {
|
||||
element.remove_attribute(intern(&name)).unwrap();
|
||||
}
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
if let Some(value) = f() {
|
||||
let value = value.into();
|
||||
|
||||
self.attrs.push((name, value));
|
||||
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
|
||||
let el = self.element.get_element();
|
||||
let class_list = el.class_list();
|
||||
let value = class.into_class(cx);
|
||||
match value {
|
||||
Class::Fn(f) => {
|
||||
create_render_effect(cx, move |old| {
|
||||
let new = f();
|
||||
if old.as_ref() != Some(&new) && (old.is_some() || new) {
|
||||
class_expression(&class_list, &name, new)
|
||||
}
|
||||
new
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Creates a boolean attribute which changes automatically when it's
|
||||
/// signal changes.
|
||||
#[track_caller]
|
||||
pub fn dyn_attr_bool<F>(
|
||||
self,
|
||||
name: impl Into<Cow<'static, str>>,
|
||||
f: F,
|
||||
) -> Self
|
||||
where
|
||||
F: Fn() -> bool + 'static,
|
||||
{
|
||||
self.dyn_attr(name, move || f().then_some(""))
|
||||
}
|
||||
|
||||
/// Addes the provided classes to the element. You can call this
|
||||
/// as many times as needed, seperating the classes by spaces.
|
||||
#[track_caller]
|
||||
pub fn class(self, classes: impl Into<Cow<'static, str>>) -> Self {
|
||||
self.attr("class", classes)
|
||||
}
|
||||
|
||||
/// Addes the provided classes to the element when the predicate is true.
|
||||
/// You can call this as many times as needed, seperating the classes
|
||||
/// by spaces.
|
||||
#[track_caller]
|
||||
pub fn class_bool(
|
||||
self,
|
||||
classes: impl Into<Cow<'static, str>>,
|
||||
predicate: bool,
|
||||
) -> Self {
|
||||
if predicate {
|
||||
self.class(classes)
|
||||
} else {
|
||||
Class::Value(value) => class_expression(&class_list, &name, value),
|
||||
};
|
||||
self
|
||||
}
|
||||
else {
|
||||
let mut class = class.into_class(cx);
|
||||
|
||||
let include = match class {
|
||||
Class::Value(include) => include,
|
||||
Class::Fn(f) => {
|
||||
self.dynamic = true;
|
||||
f()
|
||||
}
|
||||
};
|
||||
|
||||
if include {
|
||||
if let Some((_, ref mut value)) = self.attrs.iter_mut().find(|(name, _)| name == "class") {
|
||||
*value = format!("{value} {name}").into();
|
||||
self
|
||||
} else {
|
||||
self.attrs.push(("class".into(), name));
|
||||
self
|
||||
}
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// Sets a property on an element.
|
||||
#[track_caller]
|
||||
pub fn prop(
|
||||
self,
|
||||
mut self,
|
||||
cx: Scope,
|
||||
name: impl Into<Cow<'static, str>>,
|
||||
value: impl IntoProperty,
|
||||
) -> Self {
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
{
|
||||
let name = name.into();
|
||||
let value = value.into();
|
||||
|
||||
let name: &str = &name;
|
||||
|
||||
js_sys::Reflect::set(&self, &name.into(), &value)
|
||||
.expect("set property to not err");
|
||||
cfg_if! {
|
||||
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
|
||||
let name = name.into();
|
||||
let value = value.into_property(cx);
|
||||
let el = self.element.get_element();
|
||||
match value {
|
||||
Property::Fn(f) => {
|
||||
let el = el.clone();
|
||||
create_render_effect(cx, move |old| {
|
||||
let new = f();
|
||||
let prop_name = wasm_bindgen::intern(&name);
|
||||
if old.as_ref() != Some(&new) && !(old.is_none() && new == wasm_bindgen::JsValue::UNDEFINED) {
|
||||
property_expression(&el, &prop_name, new.clone())
|
||||
}
|
||||
new
|
||||
});
|
||||
}
|
||||
Property::Value(value) => {
|
||||
let prop_name = wasm_bindgen::intern(&name);
|
||||
property_expression(&el, &prop_name, value)
|
||||
},
|
||||
};
|
||||
self
|
||||
}
|
||||
else {
|
||||
self.dynamic = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
///
|
||||
/// Binds the element reference to [`NodeRef`].
|
||||
pub fn node_ref(self, node_ref: &NodeRef) -> Self {
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
node_ref.load(&self);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds an event listener to this element.
|
||||
|
@ -417,29 +414,13 @@ impl<El: IntoElement> HtmlElement<El> {
|
|||
mount_child(MountKind::Append(self.element.get_element()), &child.into_view(cx))
|
||||
}
|
||||
else {
|
||||
self.children.push(Box::new(move |cx| child.into_node(cx)));
|
||||
self.children.push(Box::new(move |cx| child.into_view(cx)));
|
||||
}
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Creates a child which will automatically re-render when
|
||||
/// it's signal dependencies change.
|
||||
pub fn dyn_child<CF, N>(mut self, child_fn: CF) -> Self
|
||||
where
|
||||
CF: Fn() -> N + 'static,
|
||||
N: IntoView,
|
||||
{
|
||||
cfg_if! {
|
||||
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
|
||||
mount_child(MountKind::Append(self.element.get_element()), &DynChild::new(child_fn).into_view(self.cx))
|
||||
} else {
|
||||
self
|
||||
.children
|
||||
.push(Box::new(move |cx| DynChild::new(child_fn).into_node(cx)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<El: IntoElement> IntoView for HtmlElement<El> {
|
||||
#[cfg_attr(debug_assertions, instrument(level = "trace", name = "<HtmlElement />", skip_all, fields(tag = %self.element.name())))]
|
||||
|
@ -572,154 +553,6 @@ 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 _class(
|
||||
mut self,
|
||||
cx: Scope,
|
||||
name: impl Into<Cow<'static, str>>,
|
||||
class: impl IntoClass,
|
||||
) -> Self {
|
||||
let name = name.into();
|
||||
cfg_if! {
|
||||
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
|
||||
let el = self.element.get_element();
|
||||
let class_list = el.class_list();
|
||||
let value = class.into_class(cx);
|
||||
match value {
|
||||
Class::Fn(f) => {
|
||||
create_render_effect(cx, move |old| {
|
||||
let new = f();
|
||||
if old.as_ref() != Some(&new) && (old.is_some() || new) {
|
||||
class_expression(&class_list, &name, new)
|
||||
}
|
||||
new
|
||||
});
|
||||
}
|
||||
Class::Value(value) => class_expression(&class_list, &name, value),
|
||||
};
|
||||
self
|
||||
}
|
||||
else {
|
||||
let mut class = class.into_class(cx);
|
||||
match class {
|
||||
Class::Value(include) => self.class_bool(name, include),
|
||||
Class::Fn(f) => self.class_bool(name, f())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[track_caller]
|
||||
pub fn _prop(
|
||||
mut self,
|
||||
cx: Scope,
|
||||
name: impl Into<Cow<'static, str>>,
|
||||
value: impl IntoProperty,
|
||||
) -> Self {
|
||||
cfg_if! {
|
||||
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
|
||||
let name = name.into();
|
||||
let value = value.into_property(cx);
|
||||
let el = self.element.get_element();
|
||||
match value {
|
||||
Property::Fn(f) => {
|
||||
let el = el.clone();
|
||||
create_render_effect(cx, move |old| {
|
||||
let new = f();
|
||||
let prop_name = wasm_bindgen::intern(&name);
|
||||
if old.as_ref() != Some(&new) && !(old.is_none() && new == wasm_bindgen::JsValue::UNDEFINED) {
|
||||
property_expression(&el, &prop_name, new.clone())
|
||||
}
|
||||
new
|
||||
});
|
||||
}
|
||||
Property::Value(value) => {
|
||||
let prop_name = wasm_bindgen::intern(&name);
|
||||
property_expression(&el, &prop_name, value)
|
||||
},
|
||||
};
|
||||
self
|
||||
}
|
||||
else {
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[track_caller]
|
||||
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_view(cx))
|
||||
}
|
||||
else {
|
||||
self.children.push(Box::new(move |cx| child.into_node(cx)));
|
||||
}
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
generate_html_tags![
|
||||
// ==========================
|
||||
// Main root
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
use leptos_reactive::Scope;
|
||||
use wasm_bindgen::UnwrapThrowExt;
|
||||
use wasm_bindgen::{intern, UnwrapThrowExt};
|
||||
|
||||
/// Represents the different possible values an attribute node could have.
|
||||
///
|
||||
|
@ -153,12 +153,14 @@ pub fn attribute_expression(
|
|||
) {
|
||||
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();
|
||||
if attr_name == "class" && !value.is_empty() {
|
||||
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) => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
text, Component, ComponentRepr, DynChild, EachKey, Element, Fragment,
|
||||
HtmlElement, IntoElement, IntoView, Text, Unit, View,
|
||||
Component, ComponentRepr, DynChild, EachKey, Element, Fragment, HtmlElement,
|
||||
IntoElement, IntoView, Text, Unit, View,
|
||||
};
|
||||
use cfg_if::cfg_if;
|
||||
use leptos_reactive::{create_effect, Scope};
|
||||
|
@ -26,7 +26,7 @@ impl IntoView for Child {
|
|||
match self {
|
||||
Child::Node(node) => node,
|
||||
Child::Unit => Unit.into_view(cx),
|
||||
Child::Text(data) => text(data),
|
||||
Child::Text(data) => crate::html::text(data),
|
||||
Child::Fn(f) => DynChild::new(move || {
|
||||
let mut value = (f.borrow_mut())();
|
||||
while let Child::Fn(f) = value {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use cfg_if::cfg_if;
|
||||
use itertools::Itertools;
|
||||
use std::{borrow::Cow, fmt::Display, iter::once};
|
||||
use std::{borrow::Cow, fmt::Display};
|
||||
|
||||
use crate::{CoreComponent, Node};
|
||||
use crate::{CoreComponent, View};
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
|
||||
impl Node {
|
||||
impl View {
|
||||
/// Consumes the node and renders it into an HTML string.
|
||||
pub fn render_to_string(self) -> Cow<'static, str> {
|
||||
self.render_to_string_with_id(Default::default())
|
||||
|
@ -13,8 +13,8 @@ impl Node {
|
|||
|
||||
fn render_to_string_with_id(self, id: TopoId) -> Cow<'static, str> {
|
||||
match self {
|
||||
Node::Text(node) => node.content,
|
||||
Node::Component(node) => {
|
||||
View::Text(node) => node.content,
|
||||
View::Component(node) => {
|
||||
let depth = id.first_child().depth;
|
||||
let content = node
|
||||
.children
|
||||
|
@ -32,7 +32,7 @@ impl Node {
|
|||
}
|
||||
}
|
||||
}
|
||||
Node::CoreComponent(node) => {
|
||||
View::CoreComponent(node) => {
|
||||
let content = match node {
|
||||
CoreComponent::Unit(_) => " ".into(),
|
||||
CoreComponent::DynChild(node) => {
|
||||
|
@ -69,7 +69,7 @@ impl Node {
|
|||
}
|
||||
}
|
||||
}
|
||||
Node::Element(el) => {
|
||||
View::Element(el) => {
|
||||
let tag_name = el.name;
|
||||
let mut has_id = false;
|
||||
let mut attrs = el
|
||||
|
@ -182,7 +182,7 @@ mod tests {
|
|||
use leptos::*;
|
||||
|
||||
#[component]
|
||||
fn Counter(cx: Scope, initial_value: i32) -> Node {
|
||||
fn Counter(cx: Scope, initial_value: i32) -> View {
|
||||
let (value, set_value) = create_signal(cx, initial_value);
|
||||
view! {
|
||||
cx,
|
||||
|
|
|
@ -3,160 +3,164 @@
|
|||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use quote::{quote, ToTokens, TokenStreamExt};
|
||||
use syn::{
|
||||
parse::{Parse, ParseStream},
|
||||
punctuated::Punctuated,
|
||||
*,
|
||||
parse::{Parse, ParseStream},
|
||||
punctuated::Punctuated,
|
||||
*,
|
||||
};
|
||||
|
||||
pub struct InlinePropsBody {
|
||||
pub attrs: Vec<Attribute>,
|
||||
pub vis: syn::Visibility,
|
||||
pub fn_token: Token![fn],
|
||||
pub ident: Ident,
|
||||
pub cx_token: Box<Pat>,
|
||||
pub generics: Generics,
|
||||
pub paren_token: token::Paren,
|
||||
pub inputs: Punctuated<FnArg, Token![,]>,
|
||||
// pub fields: FieldsNamed,
|
||||
pub output: ReturnType,
|
||||
pub where_clause: Option<WhereClause>,
|
||||
pub block: Box<Block>,
|
||||
pub attrs: Vec<Attribute>,
|
||||
pub vis: syn::Visibility,
|
||||
pub fn_token: Token![fn],
|
||||
pub ident: Ident,
|
||||
pub cx_token: Box<Pat>,
|
||||
pub generics: Generics,
|
||||
pub paren_token: token::Paren,
|
||||
pub inputs: Punctuated<FnArg, Token![,]>,
|
||||
// pub fields: FieldsNamed,
|
||||
pub output: ReturnType,
|
||||
pub where_clause: Option<WhereClause>,
|
||||
pub block: Box<Block>,
|
||||
}
|
||||
|
||||
/// The custom rusty variant of parsing rsx!
|
||||
impl Parse for InlinePropsBody {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let attrs: Vec<Attribute> = input.call(Attribute::parse_outer)?;
|
||||
let vis: Visibility = input.parse()?;
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let attrs: Vec<Attribute> = input.call(Attribute::parse_outer)?;
|
||||
let vis: Visibility = input.parse()?;
|
||||
|
||||
let fn_token = input.parse()?;
|
||||
let ident = input.parse()?;
|
||||
let generics: Generics = input.parse()?;
|
||||
let fn_token = input.parse()?;
|
||||
let ident = input.parse()?;
|
||||
let generics: Generics = input.parse()?;
|
||||
|
||||
let content;
|
||||
let paren_token = syn::parenthesized!(content in input);
|
||||
let content;
|
||||
let paren_token = syn::parenthesized!(content in input);
|
||||
|
||||
let first_arg: FnArg = content.parse()?;
|
||||
let cx_token = {
|
||||
match first_arg {
|
||||
FnArg::Receiver(_) => panic!("first argument must not be a receiver argument"),
|
||||
FnArg::Typed(f) => f.pat,
|
||||
}
|
||||
};
|
||||
let first_arg: FnArg = content.parse()?;
|
||||
let cx_token = {
|
||||
match first_arg {
|
||||
FnArg::Receiver(_) => {
|
||||
panic!("first argument must not be a receiver argument")
|
||||
}
|
||||
FnArg::Typed(f) => f.pat,
|
||||
}
|
||||
};
|
||||
|
||||
let _: Result<Token![,]> = content.parse();
|
||||
let _: Result<Token![,]> = content.parse();
|
||||
|
||||
let inputs = syn::punctuated::Punctuated::parse_terminated(&content)?;
|
||||
let inputs = syn::punctuated::Punctuated::parse_terminated(&content)?;
|
||||
|
||||
let output = input.parse()?;
|
||||
let output = input.parse()?;
|
||||
|
||||
let where_clause = input
|
||||
.peek(syn::token::Where)
|
||||
.then(|| input.parse())
|
||||
.transpose()?;
|
||||
let where_clause = input
|
||||
.peek(syn::token::Where)
|
||||
.then(|| input.parse())
|
||||
.transpose()?;
|
||||
|
||||
let block = input.parse()?;
|
||||
let block = input.parse()?;
|
||||
|
||||
Ok(Self {
|
||||
vis,
|
||||
fn_token,
|
||||
ident,
|
||||
generics,
|
||||
paren_token,
|
||||
inputs,
|
||||
output,
|
||||
where_clause,
|
||||
block,
|
||||
cx_token,
|
||||
attrs,
|
||||
})
|
||||
}
|
||||
Ok(Self {
|
||||
vis,
|
||||
fn_token,
|
||||
ident,
|
||||
generics,
|
||||
paren_token,
|
||||
inputs,
|
||||
output,
|
||||
where_clause,
|
||||
block,
|
||||
cx_token,
|
||||
attrs,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Serialize the same way, regardless of flavor
|
||||
impl ToTokens for InlinePropsBody {
|
||||
fn to_tokens(&self, out_tokens: &mut TokenStream2) {
|
||||
let Self {
|
||||
vis,
|
||||
ident,
|
||||
generics,
|
||||
inputs,
|
||||
output,
|
||||
where_clause,
|
||||
block,
|
||||
cx_token,
|
||||
attrs,
|
||||
..
|
||||
} = self;
|
||||
fn to_tokens(&self, out_tokens: &mut TokenStream2) {
|
||||
let Self {
|
||||
vis,
|
||||
ident,
|
||||
generics,
|
||||
inputs,
|
||||
output,
|
||||
where_clause,
|
||||
block,
|
||||
cx_token,
|
||||
attrs,
|
||||
..
|
||||
} = self;
|
||||
|
||||
let fields = inputs.iter().map(|f| {
|
||||
let typed_arg = match f {
|
||||
FnArg::Receiver(_) => todo!(),
|
||||
FnArg::Typed(t) => t,
|
||||
};
|
||||
if let Type::Path(pat) = &*typed_arg.ty {
|
||||
if pat.path.segments[0].ident == "Option" {
|
||||
quote! {
|
||||
#[doc = "My comment."]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
#vis #f
|
||||
}
|
||||
} else {
|
||||
quote! { #vis #f }
|
||||
}
|
||||
} else {
|
||||
quote! { #vis #f }
|
||||
let fields = inputs.iter().map(|f| {
|
||||
let typed_arg = match f {
|
||||
FnArg::Receiver(_) => todo!(),
|
||||
FnArg::Typed(t) => t,
|
||||
};
|
||||
if let Type::Path(pat) = &*typed_arg.ty {
|
||||
if pat.path.segments[0].ident == "Option" {
|
||||
quote! {
|
||||
#[doc = "My comment."]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
#vis #f
|
||||
}
|
||||
} else {
|
||||
quote! { #vis #f }
|
||||
}
|
||||
} else {
|
||||
quote! { #vis #f }
|
||||
}
|
||||
});
|
||||
|
||||
let component_name_str = ident.to_string();
|
||||
let struct_name = Ident::new(&format!("{}Props", ident), Span::call_site());
|
||||
|
||||
let field_names = inputs.iter().filter_map(|f| match f {
|
||||
FnArg::Receiver(_) => todo!(),
|
||||
FnArg::Typed(t) => Some(&t.pat),
|
||||
});
|
||||
|
||||
let first_lifetime =
|
||||
if let Some(GenericParam::Lifetime(lt)) = generics.params.first() {
|
||||
Some(lt)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let modifiers = quote! { #[derive(leptos::TypedBuilder)] };
|
||||
|
||||
let (_scope_lifetime, fn_generics, struct_generics) =
|
||||
if let Some(lt) = first_lifetime {
|
||||
let struct_generics: Punctuated<_, token::Comma> = generics
|
||||
.params
|
||||
.iter()
|
||||
.map(|it| match it {
|
||||
GenericParam::Type(tp) => {
|
||||
let mut tp = tp.clone();
|
||||
tp.bounds.push(parse_quote!( 'a ));
|
||||
|
||||
GenericParam::Type(tp)
|
||||
}
|
||||
});
|
||||
_ => it.clone(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let component_name_str = ident.to_string();
|
||||
let struct_name = Ident::new(&format!("{}Props", ident), Span::call_site());
|
||||
(
|
||||
quote! { #lt, },
|
||||
generics.clone(),
|
||||
quote! { <#struct_generics> },
|
||||
)
|
||||
} else {
|
||||
let lifetime: LifetimeDef = parse_quote! { 'a };
|
||||
|
||||
let field_names = inputs.iter().filter_map(|f| match f {
|
||||
FnArg::Receiver(_) => todo!(),
|
||||
FnArg::Typed(t) => Some(&t.pat),
|
||||
});
|
||||
let mut fn_generics = generics.clone();
|
||||
fn_generics
|
||||
.params
|
||||
.insert(0, GenericParam::Lifetime(lifetime.clone()));
|
||||
|
||||
let first_lifetime = if let Some(GenericParam::Lifetime(lt)) = generics.params.first() {
|
||||
Some(lt)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
(quote! { #lifetime, }, fn_generics, quote! { #generics })
|
||||
};
|
||||
|
||||
let modifiers = quote! { #[derive(leptos::TypedBuilder)] };
|
||||
|
||||
let (_scope_lifetime, fn_generics, struct_generics) = if let Some(lt) = first_lifetime {
|
||||
let struct_generics: Punctuated<_, token::Comma> = generics
|
||||
.params
|
||||
.iter()
|
||||
.map(|it| match it {
|
||||
GenericParam::Type(tp) => {
|
||||
let mut tp = tp.clone();
|
||||
tp.bounds.push(parse_quote!( 'a ));
|
||||
|
||||
GenericParam::Type(tp)
|
||||
}
|
||||
_ => it.clone(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
(
|
||||
quote! { #lt, },
|
||||
generics.clone(),
|
||||
quote! { <#struct_generics> },
|
||||
)
|
||||
} else {
|
||||
let lifetime: LifetimeDef = parse_quote! { 'a };
|
||||
|
||||
let mut fn_generics = generics.clone();
|
||||
fn_generics
|
||||
.params
|
||||
.insert(0, GenericParam::Lifetime(lifetime.clone()));
|
||||
|
||||
(quote! { #lifetime, }, fn_generics, quote! { #generics })
|
||||
};
|
||||
|
||||
out_tokens.append_all(quote! {
|
||||
out_tokens.append_all(quote! {
|
||||
#modifiers
|
||||
#[allow(non_camel_case_types)]
|
||||
#vis struct #struct_name #struct_generics
|
||||
|
@ -175,8 +179,8 @@ impl ToTokens for InlinePropsBody {
|
|||
#component_name_str,
|
||||
move |#cx_token| #block
|
||||
)
|
||||
.into_node(#cx_token)
|
||||
.into_view(#cx_token)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,116 +1,138 @@
|
|||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::{quote, quote_spanned};
|
||||
use std::collections::HashMap;
|
||||
use syn::{spanned::Spanned, ExprPath};
|
||||
use syn_rsx::{Node, NodeAttribute, NodeElement, NodeName};
|
||||
|
||||
use crate::{is_component_node, Mode};
|
||||
|
||||
macro_rules! generate_event_types {
|
||||
[$m:ident, $([$web_sys_event:ident, [$($event:ident),* $(,)?]]),* $(,)?] => {
|
||||
$(
|
||||
$(
|
||||
$m.insert(stringify!($event).to_ascii_lowercase(), stringify!($event));
|
||||
)*
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref EVENTS: HashMap<String, &'static str> = {
|
||||
let mut m = HashMap::new();
|
||||
generate_event_types![m,
|
||||
// ClipboardEvent is unstable
|
||||
[Event, [Copy, Cut, Paste]],
|
||||
[
|
||||
CompositionEvent,
|
||||
[CompositionEnd, CompositionStart, CompositionUpdate]
|
||||
],
|
||||
[KeyboardEvent, [KeyDown, Keypress, Keyup]],
|
||||
[FocusEvent, [Focus, FocusOut, FocusIn, Blur]],
|
||||
[Event, [Change, Input, Invalid, Reset]],
|
||||
[
|
||||
MouseEvent,
|
||||
[
|
||||
Click,
|
||||
ContextMenu,
|
||||
DoubleClick,
|
||||
DblClick,
|
||||
Drag,
|
||||
DragEnd,
|
||||
DragEnter,
|
||||
DragExit,
|
||||
DragLeave,
|
||||
DragOver,
|
||||
DragStart,
|
||||
Drop,
|
||||
MouseDown,
|
||||
MouseEnter,
|
||||
MouseLeave,
|
||||
MouseMove,
|
||||
MouseOut,
|
||||
MouseOver,
|
||||
MouseUp,
|
||||
]
|
||||
],
|
||||
[Event, [Scroll]],
|
||||
[Event, [SubmitEvent]],
|
||||
[
|
||||
PointerEvent,
|
||||
[
|
||||
PointerDown,
|
||||
PointerMove,
|
||||
PointerUp,
|
||||
PointerCancel,
|
||||
GotPointerCapture,
|
||||
LostPointerCapture,
|
||||
PointerEnter,
|
||||
PointerLeave,
|
||||
PointerOver,
|
||||
PointerOut,
|
||||
]
|
||||
],
|
||||
[Event, [Select]],
|
||||
[TouchEvent, [TouchCancel, TouchEnd, TouchMove, TouchStart]],
|
||||
[WheelEvent, [Wheel]],
|
||||
[
|
||||
Event,
|
||||
[
|
||||
Abort,
|
||||
CanPlay,
|
||||
CanPlayThrough,
|
||||
DurationChange,
|
||||
Emptied,
|
||||
Encrypted,
|
||||
Ended,
|
||||
Error,
|
||||
LoadedData,
|
||||
LoadedMetadata,
|
||||
LoadStart,
|
||||
Pause,
|
||||
Play,
|
||||
Playing,
|
||||
Progress,
|
||||
RateChange,
|
||||
Seeked,
|
||||
Seeking,
|
||||
Stalled,
|
||||
Suspend,
|
||||
TimeUpdate,
|
||||
VolumeChange,
|
||||
Waiting,
|
||||
]
|
||||
],
|
||||
[
|
||||
AnimationEvent,
|
||||
[AnimationStart, AnimationEnd, AnimationIteration,]
|
||||
],
|
||||
[TransitionEvent, [TransitionEnd]],
|
||||
[Event, [Toggle]]
|
||||
];
|
||||
m
|
||||
};
|
||||
}
|
||||
const TYPED_EVENTS: [&str; 126] = [
|
||||
"afterprint",
|
||||
"beforeprint",
|
||||
"beforeunload",
|
||||
"gamepadconnected",
|
||||
"gamepaddisconnected",
|
||||
"hashchange",
|
||||
"languagechange",
|
||||
"message",
|
||||
"messageerror",
|
||||
"offline",
|
||||
"online",
|
||||
"pagehide",
|
||||
"pageshow",
|
||||
"popstate",
|
||||
"rejectionhandled",
|
||||
"storage",
|
||||
"unhandledrejection",
|
||||
"unload",
|
||||
"abort",
|
||||
"animationcancel",
|
||||
"animationend",
|
||||
"animationiteration",
|
||||
"animationstart",
|
||||
"auxclick",
|
||||
"beforeinput",
|
||||
"blur",
|
||||
"canplay",
|
||||
"canplaythrough",
|
||||
"change",
|
||||
"click",
|
||||
"close",
|
||||
"compositionend",
|
||||
"compositionstart",
|
||||
"compositionupdate",
|
||||
"contextmenu",
|
||||
"cuechange",
|
||||
"dblclick",
|
||||
"drag",
|
||||
"dragend",
|
||||
"dragenter",
|
||||
"dragleave",
|
||||
"dragover",
|
||||
"dragstart",
|
||||
"drop",
|
||||
"durationchange",
|
||||
"emptied",
|
||||
"ended",
|
||||
"error",
|
||||
"focus",
|
||||
"focusin",
|
||||
"focusout",
|
||||
"formdata",
|
||||
"gotpointercapture",
|
||||
"input",
|
||||
"invalid",
|
||||
"keydown",
|
||||
"keypress",
|
||||
"keyup",
|
||||
"load",
|
||||
"loadeddata",
|
||||
"loadedmetadata",
|
||||
"loadstart",
|
||||
"lostpointercapture",
|
||||
"mousedown",
|
||||
"mouseenter",
|
||||
"mouseleave",
|
||||
"mousemove",
|
||||
"mouseout",
|
||||
"mouseover",
|
||||
"mouseup",
|
||||
"pause",
|
||||
"play",
|
||||
"playing",
|
||||
"pointercancel",
|
||||
"pointerdown",
|
||||
"pointerenter",
|
||||
"pointerleave",
|
||||
"pointermove",
|
||||
"pointerout",
|
||||
"pointerover",
|
||||
"pointerup",
|
||||
"progress",
|
||||
"ratechange",
|
||||
"reset",
|
||||
"resize",
|
||||
"scroll",
|
||||
"securitypolicyviolation",
|
||||
"seeked",
|
||||
"seeking",
|
||||
"select",
|
||||
"selectionchange",
|
||||
"selectstart",
|
||||
"slotchange",
|
||||
"stalled",
|
||||
"submit",
|
||||
"suspend",
|
||||
"timeupdate",
|
||||
"toggle",
|
||||
"touchcancel",
|
||||
"touchend",
|
||||
"touchmove",
|
||||
"touchstart",
|
||||
"transitioncancel",
|
||||
"transitionend",
|
||||
"transitionrun",
|
||||
"transitionstart",
|
||||
"volumechange",
|
||||
"waiting",
|
||||
"webkitanimationend",
|
||||
"webkitanimationiteration",
|
||||
"webkitanimationstart",
|
||||
"webkittransitionend",
|
||||
"wheel",
|
||||
"DOMContentLoaded",
|
||||
"devicemotion",
|
||||
"deviceorientation",
|
||||
"orientationchange",
|
||||
"copy",
|
||||
"cut",
|
||||
"paste",
|
||||
"fullscreenchange",
|
||||
"fullscreenerror",
|
||||
"pointerlockchange",
|
||||
"pointerlockerror",
|
||||
"readystatechange",
|
||||
"visibilitychange",
|
||||
];
|
||||
|
||||
pub(crate) fn render_view(
|
||||
cx: &Ident,
|
||||
|
@ -120,7 +142,7 @@ pub(crate) fn render_view(
|
|||
if nodes.is_empty() {
|
||||
let span = Span::call_site();
|
||||
quote_spanned! {
|
||||
span => leptos::Unit.into_node(#cx)
|
||||
span => leptos::Unit.into_view(#cx)
|
||||
}
|
||||
} else if nodes.len() == 1 {
|
||||
node_to_tokens(cx, &nodes[0], mode)
|
||||
|
@ -139,14 +161,14 @@ fn fragment_to_tokens(
|
|||
let node = node_to_tokens(cx, node, mode);
|
||||
let span = node.span();
|
||||
quote_spanned! {
|
||||
span => #node.into_node(#cx),
|
||||
span => #node.into_view(#cx),
|
||||
}
|
||||
});
|
||||
quote_spanned! {
|
||||
span => {
|
||||
vec![
|
||||
#(#nodes)*
|
||||
].into_node(#cx)
|
||||
].into_view(#cx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -168,7 +190,7 @@ fn node_to_tokens(cx: &Ident, node: &Node, mode: Mode) -> TokenStream {
|
|||
let span = node.value.span();
|
||||
let value = node.value.as_ref();
|
||||
quote_spanned! {
|
||||
span => #value.into_node(#cx)
|
||||
span => #value.into_view(#cx)
|
||||
}
|
||||
}
|
||||
Node::Attribute(node) => attribute_to_tokens(cx, node, mode),
|
||||
|
@ -222,14 +244,14 @@ fn element_to_tokens(
|
|||
Node::Comment(_) | Node::Doctype(_) | Node::Attribute(_) => quote! {},
|
||||
};
|
||||
quote! {
|
||||
._child(cx, #child)
|
||||
.child(cx, #child)
|
||||
}
|
||||
});
|
||||
quote_spanned! {
|
||||
span => #name
|
||||
#(#attrs)*
|
||||
#(#children)*
|
||||
.into_node(#cx)
|
||||
.into_view(#cx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -250,7 +272,7 @@ fn attribute_to_tokens(
|
|||
.expect("'_ref' needs to be passed a variable name");
|
||||
quote_spanned! {
|
||||
span => #[allow(unused_braces)]
|
||||
._ref(#value)
|
||||
.ref(#value)
|
||||
}
|
||||
/* } else {
|
||||
todo!()
|
||||
|
@ -263,10 +285,11 @@ fn attribute_to_tokens(
|
|||
.as_ref()
|
||||
.expect("event listener attributes need a value")
|
||||
.as_ref();
|
||||
let event_type = EVENTS
|
||||
.get(&name.to_string())
|
||||
let event_type = TYPED_EVENTS
|
||||
.iter()
|
||||
.find(|e| **e == name)
|
||||
.copied()
|
||||
.unwrap_or_else(|| panic!("couldn't parse event name {name}"));
|
||||
.unwrap_or("Custom");
|
||||
let event_type = event_type
|
||||
.parse::<TokenStream>()
|
||||
.expect("couldn't parse event name");
|
||||
|
@ -285,7 +308,7 @@ fn attribute_to_tokens(
|
|||
.as_ref();
|
||||
//if mode != Mode::Ssr {
|
||||
quote_spanned! {
|
||||
span => ._prop(#cx, #name, #[allow(unused_braces)] #value)
|
||||
span => .prop(#cx, #name, #[allow(unused_braces)] #value)
|
||||
}
|
||||
/* } else {
|
||||
todo!()
|
||||
|
@ -298,7 +321,7 @@ fn attribute_to_tokens(
|
|||
.as_ref();
|
||||
//if mode != Mode::Ssr {
|
||||
quote_spanned! {
|
||||
span => ._class(#cx, #name, #[allow(unused_braces)] #value)
|
||||
span => .class(#cx, #name, #[allow(unused_braces)] #value)
|
||||
}
|
||||
/* } else {
|
||||
todo!()
|
||||
|
@ -315,7 +338,7 @@ fn attribute_to_tokens(
|
|||
};
|
||||
//if mode != Mode::Ssr {
|
||||
quote_spanned! {
|
||||
span => ._attr(#cx, #name, #[allow(unused_braces)] #value)
|
||||
span => .attr(#cx, #name, #[allow(unused_braces)] #value)
|
||||
}
|
||||
/* } else {
|
||||
quote! { }
|
||||
|
|
Loading…
Reference in a new issue