use crate::ConfigInjection; use leptos::*; use thaw_components::{Binder, CSSTransition, Follower, FollowerPlacement, FollowerWidth}; use thaw_utils::{add_event_listener, mount_style, Model}; #[component] pub fn Combobox( #[prop(optional, into)] value: Model>, #[prop(optional)] multiselect: bool, #[prop(optional)] clearable: bool, children: Children, ) -> impl IntoView { mount_style("combobox", include_str!("./combobox.css")); let config_provider = ConfigInjection::use_(); let trigger_ref = NodeRef::::new(); let listbox_ref = NodeRef::::new(); let is_show_listbox = RwSignal::new(false); let clear_icon_ref = NodeRef::::new(); let is_show_clear_icon = Memo::new(move |_| { if clearable { value.with(|value| !value.is_empty()) } else { false } }); if clearable { clear_icon_ref.on_load(move |clear_icon_el| { let handler = add_event_listener(clear_icon_el.into_any(), ev::click, move |e| { e.stop_propagation(); value.set(vec![]); }); on_cleanup(move || handler.remove()); }); } #[cfg(any(feature = "csr", feature = "hydrate"))] { let handle = window_event_listener(ev::click, move |ev| { use leptos::wasm_bindgen::__rt::IntoJsResult; if !is_show_listbox.get_untracked() { return; } let el = ev.target(); let mut el: Option = el.into_js_result().map_or(None, |el| Some(el.into())); let body = document().body().unwrap(); if let Some(listbox_el) = listbox_ref.get_untracked() { if let Some(trigger_el) = trigger_ref.get_untracked() { while let Some(current_el) = el { if current_el == *body { break; }; if current_el == ***listbox_el { return; } if current_el == ***trigger_el { return; } el = current_el.parent_element(); } } } is_show_listbox.set(false); }); on_cleanup(move || handle.remove()); } view! {
{ if clearable { view! { }.into() } else { None } }
{children()}
} } #[derive(Clone, Copy)] pub(crate) struct ComboboxInjection { value: Model>, is_show_listbox: RwSignal, pub multiselect: bool, } impl ComboboxInjection { pub fn use_() -> Self { expect_context() } pub fn is_selected(&self, key: &String) -> bool { self.value.with(|value| value.contains(key)) } pub fn on_option_select(&self, key: &String) { self.value.update(|value| { if self.multiselect { if let Some(index) = value.iter().position(|k| k == key) { value.remove(index); return; } value.push(key.clone()); } else { *value = vec![key.clone()]; self.is_show_listbox.set(false); } }); } }