mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
Allow taking Fn(EVENT) for callbacks too (#1989)
* Allow taking `Fn(EVENT)` for callbacks too * fix bad test * fmt * add tests
This commit is contained in:
parent
e474533daf
commit
c6458f124a
@ -49,7 +49,7 @@ fn compile_fail() {
|
|||||||
|
|
||||||
// listener type mismatch
|
// listener type mismatch
|
||||||
html! { <input onclick=1 /> };
|
html! { <input onclick=1 /> };
|
||||||
html! { <input onclick={Callback::from(|a: String| ()) /> };
|
html! { <input onclick={Callback::from(|a: String| ())} /> };
|
||||||
html! { <input onfocus={Some(5)} /> };
|
html! { <input onfocus={Some(5)} /> };
|
||||||
|
|
||||||
// NodeRef type mismatch
|
// NodeRef type mismatch
|
||||||
|
|||||||
@ -1,18 +1,3 @@
|
|||||||
error: this file contains an unclosed delimiter
|
|
||||||
--> $DIR/element-fail.rs:95:14
|
|
||||||
|
|
|
||||||
5 | fn compile_fail() {
|
|
||||||
| - unclosed delimiter
|
|
||||||
...
|
|
||||||
52 | html! { <input onclick={Callback::from(|a: String| ()) /> };
|
|
||||||
| - this delimiter might not be properly closed...
|
|
||||||
...
|
|
||||||
93 | }
|
|
||||||
| - ...as it matches this but it has different indentation
|
|
||||||
94 |
|
|
||||||
95 | fn main() {}
|
|
||||||
| ^
|
|
||||||
|
|
||||||
error: this opening tag has no corresponding closing tag
|
error: this opening tag has no corresponding closing tag
|
||||||
--> $DIR/element-fail.rs:7:13
|
--> $DIR/element-fail.rs:7:13
|
||||||
|
|
|
|
||||||
@ -121,19 +106,109 @@ error: `ref` can only be specified once
|
|||||||
33 | html! { <input ref={()} ref={()} /> };
|
33 | html! { <input ref={()} ref={()} /> };
|
||||||
| ^^^
|
| ^^^
|
||||||
|
|
||||||
error: unexpected end of input, expected token tree
|
error: `ref` can only be specified once
|
||||||
--> $DIR/element-fail.rs:52:5
|
--> $DIR/element-fail.rs:63:20
|
||||||
|
|
|
|
||||||
52 | / html! { <input onclick={Callback::from(|a: String| ()) /> };
|
63 | html! { <input ref={()} ref={()} /> };
|
||||||
53 | | html! { <input onfocus={Some(5)} /> };
|
| ^^^
|
||||||
54 | |
|
|
||||||
55 | | // NodeRef type mismatch
|
error: the tag `<input>` is a void element and cannot have children (hint: rewrite this as `<input/>`)
|
||||||
... |
|
--> $DIR/element-fail.rs:66:13
|
||||||
92 | | html! { <input string=NotToString /> };
|
|
||||||
93 | | }
|
|
||||||
| |_^
|
|
||||||
|
|
|
|
||||||
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
66 | html! { <input type="text"></input> };
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: the tag `<iNpUt>` is a void element and cannot have children (hint: rewrite this as `<iNpUt/>`)
|
||||||
|
--> $DIR/element-fail.rs:68:13
|
||||||
|
|
|
||||||
|
68 | html! { <iNpUt type="text"></iNpUt> };
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: this dynamic tag is missing an expression block defining its value
|
||||||
|
--> $DIR/element-fail.rs:71:14
|
||||||
|
|
|
||||||
|
71 | html! { <@></@> };
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: this dynamic tag is missing an expression block defining its value
|
||||||
|
--> $DIR/element-fail.rs:72:14
|
||||||
|
|
|
||||||
|
72 | html! { <@/> };
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: dynamic closing tags must not have a body (hint: replace it with just `</@>`)
|
||||||
|
--> $DIR/element-fail.rs:75:27
|
||||||
|
|
|
||||||
|
75 | html! { <@{"test"}></@{"test"}> };
|
||||||
|
| ^^^^^^^^
|
||||||
|
|
||||||
|
error: the property value must be either a literal or enclosed in braces. Consider adding braces around your expression.
|
||||||
|
--> $DIR/element-fail.rs:83:24
|
||||||
|
|
|
||||||
|
83 | html! { <div class=("deprecated", "warning") /> };
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: the property value must be either a literal or enclosed in braces. Consider adding braces around your expression.
|
||||||
|
--> $DIR/element-fail.rs:84:24
|
||||||
|
|
|
||||||
|
84 | html! { <input ref=() /> };
|
||||||
|
| ^^
|
||||||
|
|
||||||
|
error: the property value must be either a literal or enclosed in braces. Consider adding braces around your expression.
|
||||||
|
--> $DIR/element-fail.rs:85:24
|
||||||
|
|
|
||||||
|
85 | html! { <input ref=() ref=() /> };
|
||||||
|
| ^^
|
||||||
|
|
||||||
|
error: the property value must be either a literal or enclosed in braces. Consider adding braces around your expression.
|
||||||
|
--> $DIR/element-fail.rs:86:28
|
||||||
|
|
|
||||||
|
86 | html! { <input onfocus=Some(5) /> };
|
||||||
|
| ^^^^^^^
|
||||||
|
|
||||||
|
error: the property value must be either a literal or enclosed in braces. Consider adding braces around your expression.
|
||||||
|
--> $DIR/element-fail.rs:87:27
|
||||||
|
|
|
||||||
|
87 | html! { <input string=NotToString /> };
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: the property value must be either a literal or enclosed in braces. Consider adding braces around your expression.
|
||||||
|
--> $DIR/element-fail.rs:88:22
|
||||||
|
|
|
||||||
|
88 | html! { <a media=Some(NotToString) /> };
|
||||||
|
| ^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: the property value must be either a literal or enclosed in braces. Consider adding braces around your expression.
|
||||||
|
--> $DIR/element-fail.rs:89:21
|
||||||
|
|
|
||||||
|
89 | html! { <a href=Some(5) /> };
|
||||||
|
| ^^^^^^^
|
||||||
|
|
||||||
|
error: the property value must be either a literal or enclosed in braces. Consider adding braces around your expression.
|
||||||
|
--> $DIR/element-fail.rs:90:25
|
||||||
|
|
|
||||||
|
90 | html! { <input type=() /> };
|
||||||
|
| ^^
|
||||||
|
|
||||||
|
error: the property value must be either a literal or enclosed in braces. Consider adding braces around your expression.
|
||||||
|
--> $DIR/element-fail.rs:91:26
|
||||||
|
|
|
||||||
|
91 | html! { <input value=() /> };
|
||||||
|
| ^^
|
||||||
|
|
||||||
|
error: the property value must be either a literal or enclosed in braces. Consider adding braces around your expression.
|
||||||
|
--> $DIR/element-fail.rs:92:27
|
||||||
|
|
|
||||||
|
92 | html! { <input string=NotToString /> };
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
||||||
|
warning: use of deprecated function `compile_fail::deprecated_use_of_class`: the use of `(...)` with the attribute `class` is deprecated and will be removed in version 0.19. Use the `classes!` macro instead.
|
||||||
|
--> $DIR/element-fail.rs:80:25
|
||||||
|
|
|
||||||
|
80 | html! { <div class={("deprecated", "warning")} /> };
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `#[warn(deprecated)]` on by default
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/element-fail.rs:36:28
|
--> $DIR/element-fail.rs:36:28
|
||||||
@ -227,11 +302,11 @@ error[E0277]: the trait bound `Option<{integer}>: IntoPropValue<Option<Cow<'stat
|
|||||||
<Option<String> as IntoPropValue<Option<Cow<'static, str>>>>
|
<Option<String> as IntoPropValue<Option<Cow<'static, str>>>>
|
||||||
= note: required by `into_prop_value`
|
= note: required by `into_prop_value`
|
||||||
|
|
||||||
error[E0277]: the trait bound `{integer}: IntoPropValue<Option<yew::Callback<MouseEvent>>>` is not satisfied
|
error[E0277]: expected a `Fn<(MouseEvent,)>` closure, found `{integer}`
|
||||||
--> $DIR/element-fail.rs:51:28
|
--> $DIR/element-fail.rs:51:28
|
||||||
|
|
|
|
||||||
51 | html! { <input onclick=1 /> };
|
51 | html! { <input onclick=1 /> };
|
||||||
| ^ the trait `IntoPropValue<Option<yew::Callback<MouseEvent>>>` is not implemented for `{integer}`
|
| ^ expected an `Fn<(MouseEvent,)>` closure, found `{integer}`
|
||||||
|
|
|
|
||||||
::: $WORKSPACE/packages/yew/src/html/listener/events.rs
|
::: $WORKSPACE/packages/yew/src/html/listener/events.rs
|
||||||
|
|
|
|
||||||
@ -244,9 +319,123 @@ error[E0277]: the trait bound `{integer}: IntoPropValue<Option<yew::Callback<Mou
|
|||||||
102 | | }
|
102 | | }
|
||||||
| |_- required by this bound in `yew::html::onclick::Wrapper::__macro_new`
|
| |_- required by this bound in `yew::html::onclick::Wrapper::__macro_new`
|
||||||
|
|
|
|
||||||
|
= help: the trait `Fn<(MouseEvent,)>` is not implemented for `{integer}`
|
||||||
|
= note: required because of the requirements on the impl of `IntoEventCallback<MouseEvent>` for `{integer}`
|
||||||
|
|
||||||
|
error[E0277]: expected a `Fn<(MouseEvent,)>` closure, found `yew::Callback<String>`
|
||||||
|
--> $DIR/element-fail.rs:52:29
|
||||||
|
|
|
||||||
|
52 | html! { <input onclick={Callback::from(|a: String| ())} /> };
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
| |
|
||||||
|
| expected an implementor of trait `IntoEventCallback<MouseEvent>`
|
||||||
|
| help: consider borrowing here: `&Callback::from(|a: String| ())`
|
||||||
|
|
|
||||||
|
::: $WORKSPACE/packages/yew/src/html/listener/events.rs
|
||||||
|
|
|
||||||
|
| / impl_action! {
|
||||||
|
3 | | onabort(name: "abort", event: Event) -> web_sys::Event => |_, event| { event }
|
||||||
|
4 | | onauxclick(name: "auxclick", event: MouseEvent) -> web_sys::MouseEvent => |_, event| { event }
|
||||||
|
5 | | onblur(name: "blur", event: FocusEvent) -> web_sys::FocusEvent => |_, event| { event }
|
||||||
|
... |
|
||||||
|
101 | | ontransitionstart(name: "transitionstart", event: TransitionEvent) -> web_sys::TransitionEvent => |_, event| { event }
|
||||||
|
102 | | }
|
||||||
|
| |_- required by this bound in `yew::html::onclick::Wrapper::__macro_new`
|
||||||
|
|
|
||||||
|
= note: the trait bound `yew::Callback<String>: IntoEventCallback<MouseEvent>` is not satisfied
|
||||||
|
= note: required because of the requirements on the impl of `IntoEventCallback<MouseEvent>` for `yew::Callback<String>`
|
||||||
|
|
||||||
|
error[E0277]: the trait bound `Option<{integer}>: IntoEventCallback<FocusEvent>` is not satisfied
|
||||||
|
--> $DIR/element-fail.rs:53:29
|
||||||
|
|
|
||||||
|
53 | html! { <input onfocus={Some(5)} /> };
|
||||||
|
| ^^^^^^^ the trait `IntoEventCallback<FocusEvent>` is not implemented for `Option<{integer}>`
|
||||||
|
|
|
||||||
|
::: $WORKSPACE/packages/yew/src/html/listener/events.rs
|
||||||
|
|
|
||||||
|
| / impl_action! {
|
||||||
|
3 | | onabort(name: "abort", event: Event) -> web_sys::Event => |_, event| { event }
|
||||||
|
4 | | onauxclick(name: "auxclick", event: MouseEvent) -> web_sys::MouseEvent => |_, event| { event }
|
||||||
|
5 | | onblur(name: "blur", event: FocusEvent) -> web_sys::FocusEvent => |_, event| { event }
|
||||||
|
... |
|
||||||
|
101 | | ontransitionstart(name: "transitionstart", event: TransitionEvent) -> web_sys::TransitionEvent => |_, event| { event }
|
||||||
|
102 | | }
|
||||||
|
| |_- required by this bound in `yew::html::onfocus::Wrapper::__macro_new`
|
||||||
|
|
|
||||||
= help: the following implementations were found:
|
= help: the following implementations were found:
|
||||||
<&'static str as IntoPropValue<Cow<'static, str>>>
|
<Option<T> as IntoEventCallback<EVENT>>
|
||||||
<&'static str as IntoPropValue<Option<Cow<'static, str>>>>
|
<Option<yew::Callback<EVENT>> as IntoEventCallback<EVENT>>
|
||||||
<&'static str as IntoPropValue<Option<String>>>
|
|
||||||
<&'static str as IntoPropValue<String>>
|
error[E0277]: the trait bound `(): IntoPropValue<yew::NodeRef>` is not satisfied
|
||||||
|
--> $DIR/element-fail.rs:56:25
|
||||||
|
|
|
||||||
|
56 | html! { <input ref={()} /> };
|
||||||
|
| ^^ the trait `IntoPropValue<yew::NodeRef>` is not implemented for `()`
|
||||||
|
|
|
||||||
|
= note: required by `into_prop_value`
|
||||||
|
|
||||||
|
error[E0277]: the trait bound `Option<yew::NodeRef>: IntoPropValue<yew::NodeRef>` is not satisfied
|
||||||
|
--> $DIR/element-fail.rs:57:25
|
||||||
|
|
|
||||||
|
57 | html! { <input ref={Some(NodeRef::default())} /> };
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IntoPropValue<yew::NodeRef>` is not implemented for `Option<yew::NodeRef>`
|
||||||
|
|
|
||||||
|
= help: the following implementations were found:
|
||||||
|
<Option<&'static str> as IntoPropValue<Option<Cow<'static, str>>>>
|
||||||
|
<Option<&'static str> as IntoPropValue<Option<String>>>
|
||||||
|
<Option<String> as IntoPropValue<Option<Cow<'static, str>>>>
|
||||||
|
= note: required by `into_prop_value`
|
||||||
|
|
||||||
|
error[E0277]: expected a `Fn<(MouseEvent,)>` closure, found `yew::Callback<String>`
|
||||||
|
--> $DIR/element-fail.rs:58:29
|
||||||
|
|
|
||||||
|
58 | html! { <input onclick={Callback::from(|a: String| ())} /> };
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
| |
|
||||||
|
| expected an implementor of trait `IntoEventCallback<MouseEvent>`
|
||||||
|
| help: consider borrowing here: `&Callback::from(|a: String| ())`
|
||||||
|
|
|
||||||
|
::: $WORKSPACE/packages/yew/src/html/listener/events.rs
|
||||||
|
|
|
||||||
|
| / impl_action! {
|
||||||
|
3 | | onabort(name: "abort", event: Event) -> web_sys::Event => |_, event| { event }
|
||||||
|
4 | | onauxclick(name: "auxclick", event: MouseEvent) -> web_sys::MouseEvent => |_, event| { event }
|
||||||
|
5 | | onblur(name: "blur", event: FocusEvent) -> web_sys::FocusEvent => |_, event| { event }
|
||||||
|
... |
|
||||||
|
101 | | ontransitionstart(name: "transitionstart", event: TransitionEvent) -> web_sys::TransitionEvent => |_, event| { event }
|
||||||
|
102 | | }
|
||||||
|
| |_- required by this bound in `yew::html::onclick::Wrapper::__macro_new`
|
||||||
|
|
|
||||||
|
= note: the trait bound `yew::Callback<String>: IntoEventCallback<MouseEvent>` is not satisfied
|
||||||
|
= note: required because of the requirements on the impl of `IntoEventCallback<MouseEvent>` for `yew::Callback<String>`
|
||||||
|
|
||||||
|
error[E0277]: the trait bound `NotToString: IntoPropValue<Option<Cow<'static, str>>>` is not satisfied
|
||||||
|
--> $DIR/element-fail.rs:60:28
|
||||||
|
|
|
||||||
|
60 | html! { <input string={NotToString} /> };
|
||||||
|
| ^^^^^^^^^^^ the trait `IntoPropValue<Option<Cow<'static, str>>>` is not implemented for `NotToString`
|
||||||
|
|
|
||||||
|
= note: required by `into_prop_value`
|
||||||
|
|
||||||
|
error[E0277]: the trait bound `(): IntoPropValue<yew::NodeRef>` is not satisfied
|
||||||
|
--> $DIR/element-fail.rs:62:25
|
||||||
|
|
|
||||||
|
62 | html! { <input ref={()} /> };
|
||||||
|
| ^^ the trait `IntoPropValue<yew::NodeRef>` is not implemented for `()`
|
||||||
|
|
|
||||||
|
= note: required by `into_prop_value`
|
||||||
|
|
||||||
|
error[E0277]: the trait bound `Cow<'static, str>: From<{integer}>` is not satisfied
|
||||||
|
--> $DIR/element-fail.rs:77:15
|
||||||
|
|
|
||||||
|
77 | html! { <@{55}></@> };
|
||||||
|
| ^^^^ the trait `From<{integer}>` is not implemented for `Cow<'static, str>`
|
||||||
|
|
|
||||||
|
= help: the following implementations were found:
|
||||||
|
<Cow<'a, CStr> as From<&'a CStr>>
|
||||||
|
<Cow<'a, CStr> as From<&'a CString>>
|
||||||
|
<Cow<'a, CStr> as From<CString>>
|
||||||
|
<Cow<'a, OsStr> as From<&'a OsStr>>
|
||||||
and 11 others
|
and 11 others
|
||||||
|
= note: required because of the requirements on the impl of `Into<Cow<'static, str>>` for `{integer}`
|
||||||
|
= note: required by `into`
|
||||||
|
|||||||
@ -26,8 +26,8 @@ macro_rules! impl_action {
|
|||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn __macro_new(callback: impl IntoPropValue<Option<Callback<Event>>>) -> Option<Rc<dyn Listener>> {
|
pub fn __macro_new(callback: impl IntoEventCallback<Event>) -> Option<Rc<dyn Listener>> {
|
||||||
let callback = callback.into_prop_value()?;
|
let callback = callback.into_event_callback()?;
|
||||||
Some(Rc::new(Self::new(callback)))
|
Some(Rc::new(Self::new(callback)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ use web_sys::{
|
|||||||
HtmlTextAreaElement as TextAreaElement, InputEvent,
|
HtmlTextAreaElement as TextAreaElement, InputEvent,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::Callback;
|
||||||
pub use events::*;
|
pub use events::*;
|
||||||
|
|
||||||
/// A type representing data from `oninput` event.
|
/// A type representing data from `oninput` event.
|
||||||
@ -84,3 +85,66 @@ fn onchange_handler(this: &Element) -> ChangeData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A trait similar to `Into<T>` which allows conversion of a value into a [`Callback`].
|
||||||
|
/// This is used for event listeners.
|
||||||
|
pub trait IntoEventCallback<EVENT> {
|
||||||
|
/// Convert `self` to `Option<Callback<EVENT>>`
|
||||||
|
fn into_event_callback(self) -> Option<Callback<EVENT>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<EVENT> IntoEventCallback<EVENT> for Callback<EVENT> {
|
||||||
|
fn into_event_callback(self) -> Option<Callback<EVENT>> {
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<EVENT> IntoEventCallback<EVENT> for &Callback<EVENT> {
|
||||||
|
fn into_event_callback(self) -> Option<Callback<EVENT>> {
|
||||||
|
Some(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<EVENT> IntoEventCallback<EVENT> for Option<Callback<EVENT>> {
|
||||||
|
fn into_event_callback(self) -> Option<Callback<EVENT>> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, EVENT> IntoEventCallback<EVENT> for T
|
||||||
|
where
|
||||||
|
T: Fn(EVENT) + 'static,
|
||||||
|
{
|
||||||
|
fn into_event_callback(self) -> Option<Callback<EVENT>> {
|
||||||
|
Some(Callback::from(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, EVENT> IntoEventCallback<EVENT> for Option<T>
|
||||||
|
where
|
||||||
|
T: Fn(EVENT) + 'static,
|
||||||
|
{
|
||||||
|
fn into_event_callback(self) -> Option<Callback<EVENT>> {
|
||||||
|
Some(Callback::from(self?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn supported_into_event_callback_types() {
|
||||||
|
let f = |_: usize| ();
|
||||||
|
let cb = Callback::from(f);
|
||||||
|
|
||||||
|
// Callbacks
|
||||||
|
let _: Option<Callback<usize>> = cb.clone().into_event_callback();
|
||||||
|
let _: Option<Callback<usize>> = (&cb).into_event_callback();
|
||||||
|
let _: Option<Callback<usize>> = Some(cb).into_event_callback();
|
||||||
|
|
||||||
|
// Fns
|
||||||
|
let _: Option<Callback<usize>> = f.into_event_callback();
|
||||||
|
let _: Option<Callback<usize>> = Some(f).into_event_callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user