I have some code for dropdown link.
It works fine, but I'd like this code to work as usual. When we click somewhere outside or click the link inside, dropdown should be closed. How to implement this thing? If I don't want use Bootstrap js but only css.
use yew::prelude::*;
#[function_component(App)]
pub fn app() -> Html {
let is_open = use_state(|| false);
let onclick = {
let is_open = is_open.clone();
Callback::from(move |_| is_open.set(!*is_open))
};
html! {
<main>
<div class="dropdown">
<a
class={classes!("btn", "btn-secondary", "dropdown-toggle")}
href="#" role="button"
data-bs-toggle="dropdown"
aria-expanded="false"
{onclick}
>
{"Dropdown link"}
</a>
<ul class={classes!("dropdown-menu", (*is_open).clone().then(|| Some("show")))}>
<li><a class="dropdown-item" href="#">{"Action"}</a></li>
<li><a class="dropdown-item" href="#">{"Another action"}</a></li>
<li><a class="dropdown-item" href="#">{"Something else here"}</a></li>
</ul>
</div>
</main>
}
}
You need to do two things:
body
to close your dropdown when clickedclick
event from your dropdown to this event handler on body
. Otherwise, the event will bubble up to body
as well, and you will get an open+close, effectively not allowing you to open the dropdown.Add the following dependencies for ease:
gloo = "0.10.0"
wasm-bindgen = "0.2.87"
And change your component as follows:
use std::rc::Rc;
use gloo::utils::document;
use wasm_bindgen::{closure::Closure, JsCast};
use yew::prelude::*;
#[function_component(App)]
pub fn app() -> Html {
let is_open = use_state(|| false);
let onclick = {
let is_open = is_open.clone();
Callback::from(move |e: MouseEvent| {
e.stop_propagation();
is_open.set(!*is_open)
})
};
{
let is_open = is_open.clone();
// Make a callback function that closes the dropdown
let close = Rc::new(Closure::<dyn Fn()>::new({
move || {
is_open.set(false);
}
}));
// When the component is mounted, attach the event listener to body
use_effect_with_deps(
move |_| {
let body = document().body().unwrap();
body.add_event_listener_with_callback("click", (*close).as_ref().unchecked_ref())
.unwrap();
// Clean up the event listener when the component is unmounted
let close_clone = close.clone();
move || {
body.remove_event_listener_with_callback(
"click",
(*close_clone).as_ref().unchecked_ref(),
)
.unwrap();
}
},
(),
);
}
html! {
<main>
<div class="dropdown">
<a
class={classes!("btn", "btn-secondary", "dropdown-toggle")}
href="#" role="button"
data-bs-toggle="dropdown"
aria-expanded="false"
{onclick}
>
{"Dropdown link"}
</a>
<ul class={classes!("dropdown-menu", (*is_open).clone().then(|| Some("show")))}>
<li><a class="dropdown-item" href="#">{"Action"}</a></li>
<li><a class="dropdown-item" href="#">{"Another action"}</a></li>
<li><a class="dropdown-item" href="#">{"Something else here"}</a></li>
</ul>
</div>
</main>
}
}