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
|
||||
html! { <input onclick=1 /> };
|
||||
html! { <input onclick={Callback::from(|a: String| ()) /> };
|
||||
html! { <input onclick={Callback::from(|a: String| ())} /> };
|
||||
html! { <input onfocus={Some(5)} /> };
|
||||
|
||||
// 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
|
||||
--> $DIR/element-fail.rs:7:13
|
||||
|
|
||||
@ -121,19 +106,109 @@ error: `ref` can only be specified once
|
||||
33 | html! { <input ref={()} ref={()} /> };
|
||||
| ^^^
|
||||
|
||||
error: unexpected end of input, expected token tree
|
||||
--> $DIR/element-fail.rs:52:5
|
||||
error: `ref` can only be specified once
|
||||
--> $DIR/element-fail.rs:63:20
|
||||
|
|
||||
52 | / html! { <input onclick={Callback::from(|a: String| ()) /> };
|
||||
53 | | html! { <input onfocus={Some(5)} /> };
|
||||
54 | |
|
||||
55 | | // NodeRef type mismatch
|
||||
... |
|
||||
92 | | html! { <input string=NotToString /> };
|
||||
93 | | }
|
||||
| |_^
|
||||
63 | html! { <input ref={()} ref={()} /> };
|
||||
| ^^^
|
||||
|
||||
error: the tag `<input>` is a void element and cannot have children (hint: rewrite this as `<input/>`)
|
||||
--> $DIR/element-fail.rs:66:13
|
||||
|
|
||||
= 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
|
||||
--> $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>>>>
|
||||
= 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
|
||||
|
|
||||
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
|
||||
|
|
||||
@ -244,9 +319,123 @@ error[E0277]: the trait bound `{integer}: IntoPropValue<Option<yew::Callback<Mou
|
||||
102 | | }
|
||||
| |_- 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:
|
||||
<&'static str as IntoPropValue<Cow<'static, str>>>
|
||||
<&'static str as IntoPropValue<Option<Cow<'static, str>>>>
|
||||
<&'static str as IntoPropValue<Option<String>>>
|
||||
<&'static str as IntoPropValue<String>>
|
||||
and 11 others
|
||||
<Option<T> as IntoEventCallback<EVENT>>
|
||||
<Option<yew::Callback<EVENT>> as IntoEventCallback<EVENT>>
|
||||
|
||||
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
|
||||
= 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)]
|
||||
#[inline]
|
||||
pub fn __macro_new(callback: impl IntoPropValue<Option<Callback<Event>>>) -> Option<Rc<dyn Listener>> {
|
||||
let callback = callback.into_prop_value()?;
|
||||
pub fn __macro_new(callback: impl IntoEventCallback<Event>) -> Option<Rc<dyn Listener>> {
|
||||
let callback = callback.into_event_callback()?;
|
||||
Some(Rc::new(Self::new(callback)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ use web_sys::{
|
||||
HtmlTextAreaElement as TextAreaElement, InputEvent,
|
||||
};
|
||||
|
||||
use crate::Callback;
|
||||
pub use events::*;
|
||||
|
||||
/// 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