I'm implementing material 3 from scratch with web_sys and writing some adapters for different frameworks among them yew.
I have implemented a basic structure for buttons something like this to use like this.
This works fine on web_sys but I'm trying to write the yew adapter and I'm having problems by casting between yew::Callback<web_sys::MouseEvent>
to FnMut(web_sys::MouseEvent)
.
My adapter looks like this...
this is the definition for button in my core module
And this is the definition in my adapter crate:
In adapter props takes both InnerData and ComponentOpts in one structure
// the properties in yew adapter
#[derive(PartialEq, Properties)]
pub struct ElevatedButtonProps {
pub label: AttrValue,
#[prop_or(false)]
pub disable: bool,
#[prop_or_default]
pub icon: Option<AttrValue>,
#[prop_or_default]
pub onclick: Option<Callback<MouseEvent>>,
}
And the functional component in yew, which is the adapter per se, takes the properties passed to it and with them creates the button with my "core" library and returns it as a virtual node of yew.
/// this is my core library https://gitlab.com/material-rs/material_you_rs/-/tree/agnostic-api/crates/core?ref_type=heads
use core::components::buttons::elevated::{self, ElevatedButtonData, ElevatedButtonOpts};
use web_sys::{HtmlElement, MouseEvent};
use yew::{function_component, virtual_dom::VNode, AttrValue, Callback, Html, Properties};
#[function_component(ElevatedButton)]
pub fn elevated_button(props: &ElevatedButtonProps) -> Html {
let ElevatedButtonProps {
label,
disable,
icon,
onclick,
} = props;
let icon = if let Some(icon) = icon {
Some(icon.to_string())
} else {
None
};
let inner = if let Some(onclick) = onclick {
// problem is here when i'm trying to cast
let cb = move |e: MouseEvent| {
onclick.clone().emit(e.clone());
};
ElevatedButtonData {
label: label.to_string(),
icon,
onclick: Some(Box::new(cb.clone())),
}
} else {
ElevatedButtonData {
label: label.to_string(),
icon,
onclick: None,
}
};
let opts = ElevatedButtonOpts { disable: *disable };
let button: HtmlElement = elevated::ElevatedButton::new(inner, opts);
VNode::VRef(button.into())
}
But this is not working...
The compiler tells me that the yew::Callback
must outlive 'static
, but at the same time the macro annotation function_component
does not allow me lifetimes annotations.
20 | pub fn elevated_button(props: &ElevatedButtonProps) -> Html {
| - let's call the lifetime of this reference `'1`
...
42 | onclick: Some(Box::new(cb.clone())),
| ^^^^^^^^^^^^^^^^^^^^ cast requires that `'1` must outlive `'static`
does anyone have any idea how I can deal with this?
edit:
"core" is my library where I'm implementing the components only over web_sys and in the adapter I'm "instantiating" it in a comfortable way depending on the framework of the shift
As your example is too complex and incomplete to be reproduced, I will assume that the following is your actual example:
The reason this fails is because you
clone()
theonclick
object inside of the closure, moving it first before you clone it.Also, as a minor remark, cloning
e
is pretty pointless as you already own it. It just creates a copy, then discards the original object. Use the original object directly instead.Then, it should compile:
You can also put those two actions into a nested scope, if you don't want to leak the cloned
onclick
object into the outer scope: