diff --git a/demo/src/components/site_header.rs b/demo/src/components/site_header.rs index d1f4413..960700e 100644 --- a/demo/src/components/site_header.rs +++ b/demo/src/components/site_header.rs @@ -149,6 +149,7 @@ pub fn SiteHeader() -> impl IntoView { +

diff --git a/demo_markdown/docs/auto_complete/mod.md b/demo_markdown/docs/auto_complete/mod.md index 9074973..4602621 100644 --- a/demo_markdown/docs/auto_complete/mod.md +++ b/demo_markdown/docs/auto_complete/mod.md @@ -1,23 +1,30 @@ # Auto Complete ```rust demo -let value = create_rw_signal(String::new()); -let options = create_memo(move |_| { +let value = RwSignal::new(String::new()); +let options = Memo::>::new(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}"), - }) + .map(|suffix| (format!("{prefix}{suffix}"), format!("{prefix}{suffix}"))) .collect() }); view! { - + + + + {option.1} + + + } ``` diff --git a/thaw/src/auto_complete/auto-complete.css b/thaw/src/auto_complete/auto-complete.css index 16bb61c..da394f7 100644 --- a/thaw/src/auto_complete/auto-complete.css +++ b/thaw/src/auto_complete/auto-complete.css @@ -1,13 +1,27 @@ -.thaw-auto-complete__menu { +.thaw-auto-complete { + display: inline-flex; +} + +.thaw-auto-complete > .thaw-input { + min-width: 250px; +} + +div.thaw-auto-complete__listbox { width: 100%; + + row-gap: var(--spacingHorizontalXXS); + display: flex; + flex-direction: column; + min-width: 160px; + /* max-height: 80vh; */ max-height: 200px; - padding: 5px; - background-color: var(--thaw-background-color); - border-radius: 3px; + background-color: var(--colorNeutralBackground1); + padding: var(--spacingHorizontalXS); + outline: 1px solid var(--colorTransparentStroke); + border-radius: var(--borderRadiusMedium); 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); - overflow: auto; + box-shadow: var(--shadow16); + overflow-y: auto; } .thaw-auto-complete__menu-item { padding: 6px 5px; @@ -19,7 +33,7 @@ background-color: var(--thaw-background-color-hover); } -.thaw-auto-complete__menu.fade-in-scale-up-transition-leave-active { +.thaw-auto-complete__listbox.fade-in-scale-up-transition-leave-active { transform-origin: inherit; transition: opacity 0.2s cubic-bezier(0.4, 0, 1, 1), transform 0.2s cubic-bezier(0.4, 0, 1, 1), @@ -27,7 +41,7 @@ box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1); } -.thaw-auto-complete__menu.fade-in-scale-up-transition-enter-active { +.thaw-auto-complete__listbox.fade-in-scale-up-transition-enter-active { transform-origin: inherit; transition: opacity 0.2s cubic-bezier(0, 0, 0.2, 1), transform 0.2s cubic-bezier(0, 0, 0.2, 1), @@ -35,14 +49,38 @@ box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1); } -.thaw-auto-complete__menu.fade-in-scale-up-transition-enter-from, -.thaw-auto-complete__menu.fade-in-scale-up-transition-leave-to { +.thaw-auto-complete__listbox.fade-in-scale-up-transition-enter-from, +.thaw-auto-complete__listbox.fade-in-scale-up-transition-leave-to { opacity: 0; transform: scale(0.9); } -.thaw-auto-complete__menu.fade-in-scale-up-transition-leave-from, -.thaw-auto-complete__menu.fade-in-scale-up-transition-enter-to { +.thaw-auto-complete__listbox.fade-in-scale-up-transition-leave-from, +.thaw-auto-complete__listbox.fade-in-scale-up-transition-enter-to { opacity: 1; transform: scale(1); } + +.thaw-auto-complete-option { + column-gap: var(--spacingHorizontalXS); + position: relative; + display: flex; + align-items: center; + padding: var(--spacingVerticalSNudge) var(--spacingHorizontalS); + line-height: var(--lineHeightBase300); + font-size: var(--fontSizeBase300); + font-family: var(--fontFamilyBase); + color: var(--colorNeutralForeground1); + border-radius: var(--borderRadiusMedium); + cursor: pointer; +} + +.thaw-auto-complete-option:hover { + color: var(--colorNeutralForeground1Hover); + background-color: var(--colorNeutralBackground1Hover); +} + +.thaw-auto-complete-option:active { + color: var(--colorNeutralForeground1Pressed); + background-color: var(--colorNeutralBackground1Pressed); +} \ No newline at end of file diff --git a/thaw/src/auto_complete/auto_complete_option.rs b/thaw/src/auto_complete/auto_complete_option.rs new file mode 100644 index 0000000..a242b5c --- /dev/null +++ b/thaw/src/auto_complete/auto_complete_option.rs @@ -0,0 +1,20 @@ +use leptos::*; +use super::AutoCompleteInjection; + +#[component] +pub fn AutoCompleteOption2(key: String, children: Children) -> impl IntoView { + let auto_complete = AutoCompleteInjection::use_(); + let is_selected = Memo::new(move |_| { + auto_complete.is_selected(&key) + }); + + view! { +
+ {children()} +
+ } +} diff --git a/thaw/src/auto_complete/mod.rs b/thaw/src/auto_complete/mod.rs index 4e3bbd5..9d8326d 100644 --- a/thaw/src/auto_complete/mod.rs +++ b/thaw/src/auto_complete/mod.rs @@ -1,10 +1,12 @@ -mod theme; +mod auto_complete_option; -pub use theme::AutoCompleteTheme; +pub use auto_complete_option::AutoCompleteOption2; -use crate::{use_theme, ComponentRef, Input, InputPrefix, InputRef, InputSuffix, Theme}; +use crate::{ComponentRef, ConfigInjection, Input, InputPrefix, InputRef, InputSuffix}; use leptos::*; -use thaw_components::{Binder, CSSTransition, Follower, FollowerPlacement, FollowerWidth}; +use thaw_components::{ + Binder, CSSTransition, Follower, FollowerPlacement, FollowerWidth, OptionComp, +}; use thaw_utils::{class_list, mount_style, Model, OptionalProp, StoredMaybeSignal}; #[derive(Clone, PartialEq)] @@ -39,24 +41,10 @@ pub fn AutoComplete( #[prop(optional)] auto_complete_suffix: Option, #[prop(optional)] comp_ref: ComponentRef, #[prop(attrs)] attrs: Vec<(&'static str, Attribute)>, + #[prop(optional)] children: Option, ) -> impl IntoView { mount_style("auto-complete", include_str!("./auto-complete.css")); - let theme = use_theme(Theme::light); - let menu_css_vars = create_memo(move |_| { - let mut css_vars = String::new(); - theme.with(|theme| { - css_vars.push_str(&format!( - "--thaw-background-color: {};", - theme.select.menu_background_color - )); - css_vars.push_str(&format!( - "--thaw-background-color-hover: {};", - theme.select.menu_background_color_hover - )); - }); - css_vars - }); - + let config_provider = ConfigInjection::use_(); let input_ref = ComponentRef::::new(); let default_index = if allow_free_input { None } else { Some(0) }; @@ -201,88 +189,101 @@ pub fn AutoComplete( placement=FollowerPlacement::BottomStart width=FollowerWidth::Target > - -
+ +
- {move || { - options - .get() - .into_iter() - .enumerate() - .map(|(index, v)| { - let AutoCompleteOption { value: option_value, label } = v; - let menu_item_ref = create_node_ref::(); - let on_click = move |_| { - select_value(option_value.clone()); - }; - let on_mouseenter = move |_| { - select_option_index.set(Some(index)); - }; - let on_mousedown = move |ev: ev::MouseEvent| { - ev.prevent_default(); - }; - create_effect(move |_| { - if Some(index) == select_option_index.get() { - if !is_show_menu.get() { - return; - } - if let Some(menu_item_ref) = menu_item_ref.get() { - let menu_ref = menu_ref.get().unwrap(); - let menu_rect = menu_ref.get_bounding_client_rect(); - let item_rect = menu_item_ref.get_bounding_client_rect(); - if item_rect.y() < menu_rect.y() { - menu_item_ref.scroll_into_view_with_bool(true); - } else if item_rect.y() + item_rect.height() - > menu_rect.y() + menu_rect.height() - { - menu_item_ref.scroll_into_view_with_bool(false); - } - } - } - }); - view! { -
(); + // let on_click = move |_| { + // select_value(option_value.clone()); + // }; + // let on_mouseenter = move |_| { + // select_option_index.set(Some(index)); + // }; + // let on_mousedown = move |ev: ev::MouseEvent| { + // ev.prevent_default(); + // }; + // create_effect(move |_| { + // if Some(index) == select_option_index.get() { + // if !is_show_menu.get() { + // return; + // } + // if let Some(menu_item_ref) = menu_item_ref.get() { + // let menu_ref = menu_ref.get().unwrap(); + // let menu_rect = menu_ref.get_bounding_client_rect(); + // let item_rect = menu_item_ref.get_bounding_client_rect(); + // if item_rect.y() < menu_rect.y() { + // menu_item_ref.scroll_into_view_with_bool(true); + // } else if item_rect.y() + item_rect.height() + // > menu_rect.y() + menu_rect.height() + // { + // menu_item_ref.scroll_into_view_with_bool(false); + // } + // } + // } + // }); + // view! { + //
- {label} -
- } - }) - .collect_view() - }} - -
- + // on:click=on_click + // on:mousedown=on_mousedown + // on:mouseenter=on_mouseenter + // ref=menu_item_ref + // > + // {label} + //
+ // } + // }) + // .collect_view() + // }} + + {children()} + +
+
+ } } +#[derive(Clone)] +pub(crate) struct AutoCompleteInjection(pub Model); + +impl AutoCompleteInjection { + pub fn use_() -> Self { + expect_context() + } + + pub fn is_selected(&self, key: &String) -> bool { + self.0.with(|value| value == key) + } +} + #[derive(Clone)] pub struct AutoCompleteRef { input_ref: ComponentRef, diff --git a/thaw/src/auto_complete/theme.rs b/thaw/src/auto_complete/theme.rs deleted file mode 100644 index 83549c7..0000000 --- a/thaw/src/auto_complete/theme.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::theme::ThemeMethod; - -#[derive(Clone)] -pub struct AutoCompleteTheme { - pub menu_background_color: String, - pub menu_background_color_hover: String, -} - -impl ThemeMethod for AutoCompleteTheme { - fn light() -> Self { - Self { - menu_background_color: "#fff".into(), - menu_background_color_hover: "#f3f5f6".into(), - } - } - - fn dark() -> Self { - Self { - menu_background_color: "#48484e".into(), - menu_background_color_hover: "#ffffff17".into(), - } - } -} diff --git a/thaw/src/popover/mod.rs b/thaw/src/popover/mod.rs index bf96551..0a6d416 100644 --- a/thaw/src/popover/mod.rs +++ b/thaw/src/popover/mod.rs @@ -1,6 +1,5 @@ use crate::ConfigInjection; use leptos::{leptos_dom::helpers::TimeoutHandle, *}; -use palette::bool_mask::BoolMask; use std::time::Duration; use thaw_components::{Binder, CSSTransition, Follower, FollowerPlacement}; use thaw_utils::{add_event_listener, class_list, mount_style}; diff --git a/thaw/src/theme/common.rs b/thaw/src/theme/common.rs index f720706..a184beb 100644 --- a/thaw/src/theme/common.rs +++ b/thaw/src/theme/common.rs @@ -58,6 +58,7 @@ pub struct CommonTheme { pub spacing_horizontal_m: String, pub spacing_horizontal_l: String, pub spacing_vertical_none: String, + pub spacing_vertical_s_nudge: String, pub spacing_vertical_s: String, pub spacing_vertical_m_nudge: String, pub spacing_vertical_m: String, @@ -139,6 +140,7 @@ impl CommonTheme { spacing_horizontal_m: "12px".into(), spacing_horizontal_l: "16px".into(), spacing_vertical_none: "0".into(), + spacing_vertical_s_nudge: "6px".into(), spacing_vertical_s: "8px".into(), spacing_vertical_m_nudge: "10px".into(), spacing_vertical_m: "12px".into(), diff --git a/thaw/src/theme/mod.rs b/thaw/src/theme/mod.rs index dce520d..7b2fe4a 100644 --- a/thaw/src/theme/mod.rs +++ b/thaw/src/theme/mod.rs @@ -4,9 +4,9 @@ mod common; use self::common::CommonTheme; use crate::{ mobile::{NavBarTheme, TabbarTheme}, - AlertTheme, AnchorTheme, AutoCompleteTheme, BackTopTheme, CalendarTheme, ColorPickerTheme, - DatePickerTheme, InputTheme, MessageTheme, ProgressTheme, ScrollbarTheme, - SelectTheme, TimePickerTheme, UploadTheme, + AlertTheme, AnchorTheme, BackTopTheme, CalendarTheme, ColorPickerTheme, DatePickerTheme, + InputTheme, MessageTheme, ProgressTheme, ScrollbarTheme, SelectTheme, TimePickerTheme, + UploadTheme, }; pub use color::ColorTheme; use leptos::*; @@ -28,7 +28,6 @@ pub struct Theme { pub upload: UploadTheme, pub nav_bar: NavBarTheme, pub tabbar: TabbarTheme, - pub auto_complete: AutoCompleteTheme, pub color_picker: ColorPickerTheme, pub progress: ProgressTheme, pub calendar: CalendarTheme, @@ -52,7 +51,6 @@ impl Theme { upload: UploadTheme::light(), nav_bar: NavBarTheme::light(), tabbar: TabbarTheme::light(), - auto_complete: AutoCompleteTheme::light(), color_picker: ColorPickerTheme::light(), progress: ProgressTheme::light(), calendar: CalendarTheme::light(), @@ -75,7 +73,6 @@ impl Theme { upload: UploadTheme::dark(), nav_bar: NavBarTheme::dark(), tabbar: TabbarTheme::dark(), - auto_complete: AutoCompleteTheme::dark(), color_picker: ColorPickerTheme::dark(), progress: ProgressTheme::dark(), calendar: CalendarTheme::dark(),