mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
Remove support for element listener magical closures (#782)
This commit is contained in:
parent
f48bc90edb
commit
0e8ffcc54c
@ -149,7 +149,10 @@ impl ToTokens for HtmlTag {
|
|||||||
#vtag.node_ref = #node_ref;
|
#vtag.node_ref = #node_ref;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let listeners = listeners.iter().map(|(name, callback)| {
|
let listeners = listeners.iter().map(|listener| {
|
||||||
|
let name = &listener.label.name;
|
||||||
|
let callback = &listener.value;
|
||||||
|
|
||||||
quote_spanned! {name.span()=> {
|
quote_spanned! {name.span()=> {
|
||||||
::yew::html::#name::Wrapper::new(
|
::yew::html::#name::Wrapper::new(
|
||||||
<::yew::virtual_dom::vtag::VTag<_> as ::yew::virtual_dom::Transformer<_, _, _>>::transform(
|
<::yew::virtual_dom::vtag::VTag<_> as ::yew::virtual_dom::Transformer<_, _, _>>::transform(
|
||||||
|
|||||||
@ -1,15 +1,14 @@
|
|||||||
use crate::html_tree::HtmlProp as TagAttribute;
|
use crate::html_tree::HtmlProp as TagAttribute;
|
||||||
use crate::PeekValue;
|
use crate::PeekValue;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use proc_macro2::TokenStream;
|
use std::collections::HashSet;
|
||||||
use quote::{quote, quote_spanned, ToTokens};
|
use std::iter::FromIterator;
|
||||||
use std::collections::HashMap;
|
|
||||||
use syn::parse::{Parse, ParseStream, Result as ParseResult};
|
use syn::parse::{Parse, ParseStream, Result as ParseResult};
|
||||||
use syn::{Expr, ExprClosure, ExprTuple, Ident, Pat};
|
use syn::{Expr, ExprTuple};
|
||||||
|
|
||||||
pub struct TagAttributes {
|
pub struct TagAttributes {
|
||||||
pub attributes: Vec<TagAttribute>,
|
pub attributes: Vec<TagAttribute>,
|
||||||
pub listeners: Vec<(Ident, TokenStream)>,
|
pub listeners: Vec<TagAttribute>,
|
||||||
pub classes: Option<ClassesForm>,
|
pub classes: Option<ClassesForm>,
|
||||||
pub value: Option<Expr>,
|
pub value: Option<Expr>,
|
||||||
pub kind: Option<Expr>,
|
pub kind: Option<Expr>,
|
||||||
@ -25,75 +24,67 @@ pub enum ClassesForm {
|
|||||||
Single(Expr),
|
Single(Expr),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TagListener {
|
|
||||||
name: Ident,
|
|
||||||
handler: Expr,
|
|
||||||
event_name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref LISTENER_MAP: HashMap<&'static str, &'static str> = {
|
static ref LISTENER_SET: HashSet<&'static str> = {
|
||||||
let mut m = HashMap::new();
|
HashSet::from_iter(
|
||||||
m.insert("onclick", "ClickEvent");
|
vec![
|
||||||
m.insert("ondoubleclick", "DoubleClickEvent");
|
"onclick",
|
||||||
m.insert("onkeypress", "KeyPressEvent");
|
"ondoubleclick",
|
||||||
m.insert("onkeydown", "KeyDownEvent");
|
"onkeypress",
|
||||||
m.insert("onkeyup", "KeyUpEvent");
|
"onkeydown",
|
||||||
m.insert("onmousedown", "MouseDownEvent");
|
"onkeyup",
|
||||||
m.insert("onmousemove", "MouseMoveEvent");
|
"onmousedown",
|
||||||
m.insert("onmouseout", "MouseOutEvent");
|
"onmousemove",
|
||||||
m.insert("onmouseenter", "MouseEnterEvent");
|
"onmouseout",
|
||||||
m.insert("onmouseleave", "MouseLeaveEvent");
|
"onmouseenter",
|
||||||
m.insert("onmousewheel", "MouseWheelEvent");
|
"onmouseleave",
|
||||||
m.insert("onmouseover", "MouseOverEvent");
|
"onmousewheel",
|
||||||
m.insert("onmouseup", "MouseUpEvent");
|
"onmouseover",
|
||||||
m.insert("ontouchcancel", "TouchCancel");
|
"onmouseup",
|
||||||
m.insert("ontouchend", "TouchEnd");
|
"ontouchcancel",
|
||||||
m.insert("ontouchenter", "TouchEnter");
|
"ontouchend",
|
||||||
m.insert("ontouchmove", "TouchMove");
|
"ontouchenter",
|
||||||
m.insert("ontouchstart", "TouchStart");
|
"ontouchmove",
|
||||||
m.insert("ongotpointercapture", "GotPointerCaptureEvent");
|
"ontouchstart",
|
||||||
m.insert("onlostpointercapture", "LostPointerCaptureEvent");
|
"ongotpointercapture",
|
||||||
m.insert("onpointercancel", "PointerCancelEvent");
|
"onlostpointercapture",
|
||||||
m.insert("onpointerdown", "PointerDownEvent");
|
"onpointercancel",
|
||||||
m.insert("onpointerenter", "PointerEnterEvent");
|
"onpointerdown",
|
||||||
m.insert("onpointerleave", "PointerLeaveEvent");
|
"onpointerenter",
|
||||||
m.insert("onpointermove", "PointerMoveEvent");
|
"onpointerleave",
|
||||||
m.insert("onpointerout", "PointerOutEvent");
|
"onpointermove",
|
||||||
m.insert("onpointerover", "PointerOverEvent");
|
"onpointerout",
|
||||||
m.insert("onpointerup", "PointerUpEvent");
|
"onpointerover",
|
||||||
m.insert("onscroll", "ScrollEvent");
|
"onpointerup",
|
||||||
m.insert("onblur", "BlurEvent");
|
"onscroll",
|
||||||
m.insert("onfocus", "FocusEvent");
|
"onblur",
|
||||||
m.insert("onsubmit", "SubmitEvent");
|
"onfocus",
|
||||||
m.insert("oninput", "InputData");
|
"onsubmit",
|
||||||
m.insert("onchange", "ChangeData");
|
"oninput",
|
||||||
m.insert("ondrag", "DragEvent");
|
"onchange",
|
||||||
m.insert("ondragstart", "DragStartEvent");
|
"ondrag",
|
||||||
m.insert("ondragend", "DragEndEvent");
|
"ondragstart",
|
||||||
m.insert("ondragenter", "DragEnterEvent");
|
"ondragend",
|
||||||
m.insert("ondragleave", "DragLeaveEvent");
|
"ondragenter",
|
||||||
m.insert("ondragover", "DragOverEvent");
|
"ondragleave",
|
||||||
m.insert("ondragexit", "DragExitEvent");
|
"ondragover",
|
||||||
m.insert("ondrop", "DragDropEvent");
|
"ondragexit",
|
||||||
m.insert("oncontextmenu", "ContextMenuEvent");
|
"ondrop",
|
||||||
m
|
"oncontextmenu",
|
||||||
|
]
|
||||||
|
.into_iter(),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TagAttributes {
|
impl TagAttributes {
|
||||||
fn drain_listeners(attrs: &mut Vec<TagAttribute>) -> Vec<TagListener> {
|
fn drain_listeners(attrs: &mut Vec<TagAttribute>) -> Vec<TagAttribute> {
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
let mut drained = Vec::new();
|
let mut drained = Vec::new();
|
||||||
while i < attrs.len() {
|
while i < attrs.len() {
|
||||||
let name_str = attrs[i].label.to_string();
|
let name_str = attrs[i].label.to_string();
|
||||||
if let Some(event_type) = LISTENER_MAP.get(&name_str.as_str()) {
|
if LISTENER_SET.contains(&name_str.as_str()) {
|
||||||
let TagAttribute { label, value } = attrs.remove(i);
|
drained.push(attrs.remove(i));
|
||||||
drained.push(TagListener {
|
|
||||||
name: label.name,
|
|
||||||
handler: value,
|
|
||||||
event_name: event_type.to_owned().to_string(),
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
@ -119,54 +110,6 @@ impl TagAttributes {
|
|||||||
expr => ClassesForm::Single(expr),
|
expr => ClassesForm::Single(expr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_listener(listener: TagListener) -> ParseResult<(Ident, TokenStream)> {
|
|
||||||
let TagListener {
|
|
||||||
name,
|
|
||||||
event_name,
|
|
||||||
handler,
|
|
||||||
} = listener;
|
|
||||||
|
|
||||||
let callback: TokenStream = match handler {
|
|
||||||
Expr::Closure(closure) => {
|
|
||||||
let ExprClosure {
|
|
||||||
inputs,
|
|
||||||
body,
|
|
||||||
or1_token,
|
|
||||||
or2_token,
|
|
||||||
..
|
|
||||||
} = closure;
|
|
||||||
|
|
||||||
let or_span = quote! {#or1_token#or2_token};
|
|
||||||
if inputs.len() != 1 {
|
|
||||||
return Err(syn::Error::new_spanned(
|
|
||||||
or_span,
|
|
||||||
"there must be one closure argument",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let var = match inputs.first().unwrap() {
|
|
||||||
Pat::Ident(pat) => Ok(pat.into_token_stream()),
|
|
||||||
Pat::Wild(pat) => Ok(pat.into_token_stream()),
|
|
||||||
_ => Err(syn::Error::new_spanned(or_span, "invalid closure argument")),
|
|
||||||
}?;
|
|
||||||
let callback =
|
|
||||||
Ident::new(&format!("__yew_{}_callback", name.to_string()), name.span());
|
|
||||||
let segment = syn::PathSegment {
|
|
||||||
ident: Ident::new(&event_name, name.span()),
|
|
||||||
arguments: syn::PathArguments::None,
|
|
||||||
};
|
|
||||||
|
|
||||||
quote_spanned! {name.span()=> {
|
|
||||||
let #callback = move | #var: ::yew::events::#segment | #body;
|
|
||||||
#callback
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
callback => callback.into_token_stream(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((name, callback))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for TagAttributes {
|
impl Parse for TagAttributes {
|
||||||
@ -178,7 +121,7 @@ impl Parse for TagAttributes {
|
|||||||
|
|
||||||
let mut listeners = Vec::new();
|
let mut listeners = Vec::new();
|
||||||
for listener in TagAttributes::drain_listeners(&mut attributes) {
|
for listener in TagAttributes::drain_listeners(&mut attributes) {
|
||||||
listeners.push(TagAttributes::map_listener(listener)?);
|
listeners.push(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multiple listener attributes are allowed, but no others
|
// Multiple listener attributes are allowed, but no others
|
||||||
|
|||||||
@ -30,9 +30,7 @@ fn compile_fail() {
|
|||||||
html! { <a href=() /> };
|
html! { <a href=() /> };
|
||||||
|
|
||||||
html! { <input onclick=1 /> };
|
html! { <input onclick=1 /> };
|
||||||
html! { <input onclick=|| () /> };
|
html! { <input onclick=Callback::from(|a: String| ()) /> };
|
||||||
html! { <input onclick=|a, b| () /> };
|
|
||||||
html! { <input onclick=|a: String| () /> };
|
|
||||||
|
|
||||||
html! { <input string=NotToString /> };
|
html! { <input string=NotToString /> };
|
||||||
|
|
||||||
|
|||||||
@ -102,28 +102,10 @@ error: only one `class` attribute allowed
|
|||||||
23 | html! { <div class="first" class="second" /> };
|
23 | html! { <div class="first" class="second" /> };
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
|
||||||
error: there must be one closure argument
|
|
||||||
--> $DIR/html-tag-fail.rs:33:28
|
|
||||||
|
|
|
||||||
33 | html! { <input onclick=|| () /> };
|
|
||||||
| ^^
|
|
||||||
|
|
||||||
error: there must be one closure argument
|
|
||||||
--> $DIR/html-tag-fail.rs:34:28
|
|
||||||
|
|
|
||||||
34 | html! { <input onclick=|a, b| () /> };
|
|
||||||
| ^^^^^^
|
|
||||||
|
|
||||||
error: invalid closure argument
|
|
||||||
--> $DIR/html-tag-fail.rs:35:28
|
|
||||||
|
|
|
||||||
35 | html! { <input onclick=|a: String| () /> };
|
|
||||||
| ^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: only one `ref` attribute allowed
|
error: only one `ref` attribute allowed
|
||||||
--> $DIR/html-tag-fail.rs:40:27
|
--> $DIR/html-tag-fail.rs:38:27
|
||||||
|
|
|
|
||||||
40 | html! { <input ref=() ref=() /> };
|
38 | html! { <input ref=() ref=() /> };
|
||||||
| ^^^
|
| ^^^
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
@ -193,13 +175,22 @@ error[E0308]: mismatched types
|
|||||||
= note: expected type `yew::callback::Callback<stdweb::webapi::events::mouse::ClickEvent>`
|
= note: expected type `yew::callback::Callback<stdweb::webapi::events::mouse::ClickEvent>`
|
||||||
found type `{integer}`
|
found type `{integer}`
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/html-tag-fail.rs:33:20
|
||||||
|
|
|
||||||
|
33 | html! { <input onclick=Callback::from(|a: String| ()) /> };
|
||||||
|
| ^^^^^^^ expected struct `stdweb::webapi::events::mouse::ClickEvent`, found struct `std::string::String`
|
||||||
|
|
|
||||||
|
= note: expected type `yew::callback::Callback<stdweb::webapi::events::mouse::ClickEvent>`
|
||||||
|
found type `yew::callback::Callback<std::string::String>`
|
||||||
|
|
||||||
error[E0599]: no method named `to_string` found for type `NotToString` in the current scope
|
error[E0599]: no method named `to_string` found for type `NotToString` in the current scope
|
||||||
--> $DIR/html-tag-fail.rs:37:27
|
--> $DIR/html-tag-fail.rs:35:27
|
||||||
|
|
|
|
||||||
3 | struct NotToString;
|
3 | struct NotToString;
|
||||||
| ------------------- method `to_string` not found for this
|
| ------------------- method `to_string` not found for this
|
||||||
...
|
...
|
||||||
37 | html! { <input string=NotToString /> };
|
35 | html! { <input string=NotToString /> };
|
||||||
| ^^^^^^^^^^^ method not found in `NotToString`
|
| ^^^^^^^^^^^ method not found in `NotToString`
|
||||||
|
|
|
|
||||||
= note: the method `to_string` exists but the following trait bounds were not satisfied:
|
= note: the method `to_string` exists but the following trait bounds were not satisfied:
|
||||||
@ -209,9 +200,9 @@ error[E0599]: no method named `to_string` found for type `NotToString` in the cu
|
|||||||
candidate #1: `std::string::ToString`
|
candidate #1: `std::string::ToString`
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/html-tag-fail.rs:39:24
|
--> $DIR/html-tag-fail.rs:37:24
|
||||||
|
|
|
|
||||||
39 | html! { <input ref=() /> };
|
37 | html! { <input ref=() /> };
|
||||||
| ^^ expected struct `yew::html::NodeRef`, found ()
|
| ^^ expected struct `yew::html::NodeRef`, found ()
|
||||||
|
|
|
|
||||||
= note: expected type `yew::html::NodeRef`
|
= note: expected type `yew::html::NodeRef`
|
||||||
|
|||||||
@ -37,7 +37,6 @@ pass_helper! {
|
|||||||
</svg>
|
</svg>
|
||||||
<img class=("avatar", "hidden") src="http://pic.com" />
|
<img class=("avatar", "hidden") src="http://pic.com" />
|
||||||
<img class="avatar hidden", />
|
<img class="avatar hidden", />
|
||||||
<button onclick=|e| panic!(e) />
|
|
||||||
<button onclick=&onclick />
|
<button onclick=&onclick />
|
||||||
<button onclick=onclick />
|
<button onclick=onclick />
|
||||||
<a href="http://google.com" />
|
<a href="http://google.com" />
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user