From 9380cdfad690e836c2f6b0bfdae1c673598e21d5 Mon Sep 17 00:00:00 2001 From: luoxiao Date: Sun, 15 Oct 2023 12:23:26 +0800 Subject: [PATCH] feat: add auto complete component --- demo/src/app.rs | 1 + demo/src/pages/auto_complete/mod.rs | 59 ++++++++++++++++++ demo/src/pages/components.rs | 13 ++-- demo/src/pages/mod.rs | 2 + src/auto_complete/auto-complete.css | 20 ++++++ src/auto_complete/mod.rs | 96 +++++++++++++++++++++++++++++ src/input/mod.rs | 34 +++++++--- src/lib.rs | 2 + 8 files changed, 215 insertions(+), 12 deletions(-) create mode 100644 demo/src/pages/auto_complete/mod.rs create mode 100644 src/auto_complete/auto-complete.css create mode 100644 src/auto_complete/mod.rs diff --git a/demo/src/app.rs b/demo/src/app.rs index 8c8eb64..2a6cecd 100644 --- a/demo/src/app.rs +++ b/demo/src/app.rs @@ -26,6 +26,7 @@ pub fn App() -> impl IntoView { + diff --git a/demo/src/pages/auto_complete/mod.rs b/demo/src/pages/auto_complete/mod.rs new file mode 100644 index 0000000..66c8a1c --- /dev/null +++ b/demo/src/pages/auto_complete/mod.rs @@ -0,0 +1,59 @@ +use crate::components::{Demo, DemoCode}; +use leptos::*; +use melt_ui::*; +use prisms::highlight_str; + +#[component] +pub fn AutoCompletePage() -> impl IntoView { + let value = create_rw_signal(String::new()); + let options = create_memo(move |_| { + let prefix = value + .get() + .split_once('@') + .map_or(value.get(), |v| v.0.to_string()); + vec!["@gmail.com", "@163.com"] + .into_iter() + .map(|suffix| AutoCompleteOption { + label: format!("{prefix}{suffix}"), + value: format!("{prefix}{suffix}"), + }) + .collect() + }); + + view! { +
+

"Auto Complete"

+ + + + } + "#, + "rust" + ) + > + + "" + + +
+ } +} diff --git a/demo/src/pages/components.rs b/demo/src/pages/components.rs index b2088a7..8f64c36 100644 --- a/demo/src/pages/components.rs +++ b/demo/src/pages/components.rs @@ -32,20 +32,23 @@ pub fn ComponentsPage() -> impl IntoView { - - - - - + + + + + + + + diff --git a/demo/src/pages/mod.rs b/demo/src/pages/mod.rs index ab05bdb..d674481 100644 --- a/demo/src/pages/mod.rs +++ b/demo/src/pages/mod.rs @@ -1,4 +1,5 @@ mod alert; +mod auto_complete; mod button; mod checkbox; mod color_picker; @@ -20,6 +21,7 @@ mod tabs; mod toast; pub use alert::*; +pub use auto_complete::*; pub use button::*; pub use checkbox::*; pub use color_picker::*; diff --git a/src/auto_complete/auto-complete.css b/src/auto_complete/auto-complete.css new file mode 100644 index 0000000..57fd629 --- /dev/null +++ b/src/auto_complete/auto-complete.css @@ -0,0 +1,20 @@ +.melt-auto-complete__menu { + position: relative; + display: inline-block; + max-height: 200px; + padding: 5px; + background-color: #fff; + border-radius: 3px; + box-sizing: border-box; + box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12), + 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05); + z-index: 2000; +} +.melt-auto-complete__menu-item { + padding: 6px 5px; + border-radius: 2px; + cursor: pointer; +} +.melt-auto-complete__menu-item:hover { + background-color: #f6f6f7; +} diff --git a/src/auto_complete/mod.rs b/src/auto_complete/mod.rs new file mode 100644 index 0000000..79e782e --- /dev/null +++ b/src/auto_complete/mod.rs @@ -0,0 +1,96 @@ +use crate::{mount_style, teleport::Teleport, utils::maybe_rw_signal::MaybeRwSignal, Input}; +use leptos::*; + +#[derive(Clone, PartialEq)] +pub struct AutoCompleteOption { + pub label: String, + pub value: String, +} + +#[component] +pub fn AutoComplete( + #[prop(optional, into)] value: MaybeRwSignal, + #[prop(optional, into)] placeholder: MaybeSignal, + #[prop(optional, into)] options: MaybeSignal>, +) -> impl IntoView { + mount_style("auto-complete", include_str!("./auto-complete.css")); + let is_show_menu = create_rw_signal(false); + let auto_complete_ref = create_node_ref::(); + let auto_complete_menu_ref = create_node_ref::(); + let show_menu = move || { + is_show_menu.set(true); + let rect = auto_complete_ref + .get_untracked() + .unwrap() + .get_bounding_client_rect(); + + let auto_complete_menu = auto_complete_menu_ref.get_untracked().unwrap(); + auto_complete_menu + .style("width", format!("{}px", rect.width())) + .style( + "transform", + format!( + "translateX({}px) translateY({}px)", + rect.x(), + rect.y() + rect.height() + ), + ); + }; + + let allow_value = move |_| { + if !is_show_menu.get_untracked() { + show_menu(); + } + true + }; + + view! { +
+ +
+ +
+ + {move || { + options + .get() + .into_iter() + .map(|v| { + let AutoCompleteOption { value: option_value, label } = v; + let on_click = move |_| { + value.set(option_value.clone()); + is_show_menu.set(false); + }; + let on_mousedown = move |ev: ev::MouseEvent| { + ev.prevent_default(); + }; + view! { +
+ {label} +
+ } + }) + .collect_view() + }} + +
+
+ } +} diff --git a/src/input/mod.rs b/src/input/mod.rs index 4334dba..475790a 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -26,17 +26,34 @@ impl InputVariant { #[component] pub fn Input( #[prop(optional, into)] value: MaybeRwSignal, + #[prop(optional, into)] allow_value: Option>, #[prop(optional, into)] variant: MaybeSignal, + #[prop(optional, into)] placeholder: MaybeSignal, + #[prop(optional, into)] on_focus: Option>, + #[prop(optional, into)] on_blur: Option>, ) -> impl IntoView { let theme = use_theme(Theme::light); mount_style("input", include_str!("./input.css")); - let input_ref = create_node_ref::(); - input_ref.on_load(move |input| { - input.on(ev::input, move |ev| { - value.set(event_target_value(&ev)); - }); - }); + let on_input = move |ev| { + let input_value = event_target_value(&ev); + if let Some(allow_value) = allow_value.as_ref() { + if !allow_value.call(input_value.clone()) { + return; + } + } + value.set(input_value); + }; + let on_internal_focus = move |ev| { + if let Some(on_focus) = on_focus.as_ref() { + on_focus.call(ev); + } + }; + let on_internal_blur = move |ev| { + if let Some(on_blur) = on_blur.as_ref() { + on_blur.call(ev); + } + }; let css_vars = create_memo(move |_| { let mut css_vars = String::new(); @@ -52,8 +69,11 @@ pub fn Input( } diff --git a/src/lib.rs b/src/lib.rs index 7610a6f..8ae04c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ mod alert; +mod auto_complete; mod button; mod card; mod checkbox; @@ -25,6 +26,7 @@ mod utils; mod wave; pub use alert::*; +pub use auto_complete::*; pub use button::*; pub use card::*; pub use checkbox::*;