use leptos::*; use leptos::html::Div; use wasm_bindgen::JsCast; pub fn use_click_outside( target: NodeRef
, callback: impl Fn() + Clone + 'static, ) { create_effect(move |_| { if let Some(_) = target.get() { let handle_click = { let callback = callback.clone(); let target = target.clone(); move |ev: web_sys::MouseEvent| { if let Some(el) = target.get() { let ev_target = ev.target().unwrap().unchecked_into::(); let el_node = el.unchecked_ref::(); if !el_node.contains(Some(&ev_target)) { callback(); } } } }; let window = web_sys::window().unwrap(); let closure = wasm_bindgen::closure::Closure::::new(handle_click); let _ = window.add_event_listener_with_callback("click", closure.as_ref().unchecked_ref()); // Cleanup on_cleanup(move || { let window = web_sys::window().unwrap(); let _ = window.remove_event_listener_with_callback("click", closure.as_ref().unchecked_ref()); }); } }); } #[component] pub fn ContextMenu( position: (i32, i32), visible: bool, torrent_hash: String, on_close: Callback<()>, on_action: Callback<(String, String)>, // (Action, Hash) ) -> impl IntoView { let handle_action = move |action: &str| { let hash = torrent_hash.clone(); let action_str = action.to_string(); logging::log!("ContextMenu: Action '{}' for hash '{}'", action_str, hash); on_action.call((action_str, hash)); // Delegate FIRST on_close.call(()); // Close menu AFTER }; let target = create_node_ref::
(); use_click_outside(target, move || { if visible { on_close.call(()); } }); if !visible { return view! {}.into_view(); } view! {
}.into_view() }