mirror of
https://github.com/leptos-rs/leptos
synced 2024-09-20 14:32:00 +00:00
feat: attr:
and #[prop(attrs)]
syntax for passing attributes down to components (#1628)
This commit is contained in:
parent
8c3e0f23b0
commit
1c2327b2d6
5 changed files with 77 additions and 3 deletions
|
@ -265,6 +265,18 @@ pub trait Props {
|
|||
fn builder() -> Self::Builder;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait DynAttrs {
|
||||
fn dyn_attrs(self, _args: Vec<(&'static str, Attribute)>) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl DynAttrs for () {}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait PropsOrNoPropsBuilder {
|
||||
type Builder;
|
||||
|
|
|
@ -332,6 +332,39 @@ impl ToTokens for Model {
|
|||
}
|
||||
};
|
||||
|
||||
let count = props
|
||||
.iter()
|
||||
.filter(
|
||||
|Prop {
|
||||
prop_opts: PropOpt { attrs, .. },
|
||||
..
|
||||
}| *attrs,
|
||||
)
|
||||
.count();
|
||||
|
||||
let dyn_attrs_props = props
|
||||
.iter()
|
||||
.filter(
|
||||
|Prop {
|
||||
prop_opts: PropOpt { attrs, .. },
|
||||
..
|
||||
}| *attrs,
|
||||
)
|
||||
.enumerate()
|
||||
.map(|(idx, Prop { name, .. })| {
|
||||
let ident = &name.ident;
|
||||
if idx < count - 1 {
|
||||
quote! {
|
||||
self.#ident = v.clone().into();
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
self.#ident = v.into();
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<TokenStream>();
|
||||
|
||||
let body = quote! {
|
||||
#body
|
||||
#destructure_props
|
||||
|
@ -461,6 +494,13 @@ impl ToTokens for Model {
|
|||
}
|
||||
}
|
||||
|
||||
impl #impl_generics ::leptos::DynAttrs for #props_name #generics #where_clause {
|
||||
fn dyn_attrs(mut self, v: Vec<(&'static str, ::leptos::Attribute)>) -> Self {
|
||||
#dyn_attrs_props
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#into_view
|
||||
|
||||
#docs
|
||||
|
@ -699,6 +739,7 @@ struct PropOpt {
|
|||
#[attribute(example = "5 * 10")]
|
||||
default: Option<syn::Expr>,
|
||||
into: bool,
|
||||
attrs: bool,
|
||||
}
|
||||
|
||||
struct TypedBuilderOpts {
|
||||
|
@ -711,7 +752,7 @@ struct TypedBuilderOpts {
|
|||
impl TypedBuilderOpts {
|
||||
fn from_opts(opts: &PropOpt, is_ty_option: bool) -> Self {
|
||||
Self {
|
||||
default: opts.optional || opts.optional_no_strip,
|
||||
default: opts.optional || opts.optional_no_strip || opts.attrs,
|
||||
default_with_value: opts.default.clone(),
|
||||
strip_option: opts.strip_option || opts.optional && is_ty_option,
|
||||
into: opts.into,
|
||||
|
|
|
@ -33,6 +33,7 @@ pub(crate) fn component_to_tokens(
|
|||
!attr.key.to_string().starts_with("bind:")
|
||||
&& !attr.key.to_string().starts_with("clone:")
|
||||
&& !attr.key.to_string().starts_with("on:")
|
||||
&& !attr.key.to_string().starts_with("attr:")
|
||||
})
|
||||
.map(|attr| {
|
||||
let name = &attr.key;
|
||||
|
@ -70,6 +71,7 @@ pub(crate) fn component_to_tokens(
|
|||
.collect::<Vec<_>>();
|
||||
|
||||
let events = attrs
|
||||
.clone()
|
||||
.filter(|attr| attr.key.to_string().starts_with("on:"))
|
||||
.map(|attr| {
|
||||
let (event_type, handler) = event_from_attribute_node(attr, true);
|
||||
|
@ -80,6 +82,24 @@ pub(crate) fn component_to_tokens(
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let dyn_attrs = attrs
|
||||
.filter(|attr| attr.key.to_string().starts_with("attr:"))
|
||||
.filter_map(|attr| {
|
||||
let name = &attr.key.to_string();
|
||||
let name = name.strip_prefix("attr:");
|
||||
let value = attr.value().map(|v| {
|
||||
quote! { #v }
|
||||
})?;
|
||||
Some(quote! { (#name, #value.into_attribute()) })
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let dyn_attrs = if dyn_attrs.is_empty() {
|
||||
quote! {}
|
||||
} else {
|
||||
quote! { .dyn_attrs(vec![#(#dyn_attrs),*]) }
|
||||
};
|
||||
|
||||
let mut slots = HashMap::new();
|
||||
let children = if node.children.is_empty() {
|
||||
quote! {}
|
||||
|
@ -163,6 +183,7 @@ pub(crate) fn component_to_tokens(
|
|||
#(#slots)*
|
||||
#children
|
||||
.build()
|
||||
#dyn_attrs
|
||||
)
|
||||
};
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ error: return type is incorrect
|
|||
|
|
||||
= help: return signature must be `-> impl IntoView`
|
||||
|
||||
error: supported fields are `optional`, `optional_no_strip`, `strip_option`, `default` and `into`
|
||||
error: supported fields are `optional`, `optional_no_strip`, `strip_option`, `default`, `into` and `attrs`
|
||||
--> tests/ui/component.rs:10:31
|
||||
|
|
||||
10 | fn unknown_prop_option(#[prop(hello)] test: bool) -> impl IntoView {
|
||||
|
|
|
@ -6,7 +6,7 @@ error: return type is incorrect
|
|||
|
|
||||
= help: return signature must be `-> impl IntoView`
|
||||
|
||||
error: supported fields are `optional`, `optional_no_strip`, `strip_option`, `default` and `into`
|
||||
error: supported fields are `optional`, `optional_no_strip`, `strip_option`, `default`, `into` and `attrs`
|
||||
--> tests/ui/component_absolute.rs:5:31
|
||||
|
|
||||
5 | fn unknown_prop_option(#[prop(hello)] test: bool) -> impl ::leptos::IntoView {
|
||||
|
|
Loading…
Reference in a new issue