diff --git a/thaw/src/combobox/combobox.css b/thaw/src/combobox/combobox.css index fbea8bf..196cad7 100644 --- a/thaw/src/combobox/combobox.css +++ b/thaw/src/combobox/combobox.css @@ -115,21 +115,6 @@ cursor: not-allowed; } -.thaw-combobox__listbox { - row-gap: var(--spacingHorizontalXXS); - display: flex; - flex-direction: column; - min-width: 160px; - max-height: 80vh; - background-color: var(--colorNeutralBackground1); - padding: var(--spacingHorizontalXS); - outline: 1px solid var(--colorTransparentStroke); - border-radius: var(--borderRadiusMedium); - box-shadow: var(--shadow16); - box-sizing: border-box; - overflow-y: auto; -} - .thaw-combobox-option { column-gap: var(--spacingHorizontalXS); position: relative; diff --git a/thaw/src/combobox/combobox_option_group.rs b/thaw/src/combobox/combobox_option_group.rs index b1bf8cb..9dd2052 100644 --- a/thaw/src/combobox/combobox_option_group.rs +++ b/thaw/src/combobox/combobox_option_group.rs @@ -1,5 +1,5 @@ +use super::option_group::OptionGroup; use leptos::prelude::*; -use thaw_utils::{class_list, mount_style}; #[component] pub fn ComboboxOptionGroup( @@ -9,17 +9,5 @@ pub fn ComboboxOptionGroup( label: String, children: Children, ) -> impl IntoView { - mount_style( - "combobox-option-group", - include_str!("./combobox-option-group.css"), - ); - - view! { -
- - {label} - - {children()} -
- } + view! { } } diff --git a/thaw/src/combobox/listbox.css b/thaw/src/combobox/common/listbox.css similarity index 61% rename from thaw/src/combobox/listbox.css rename to thaw/src/combobox/common/listbox.css index eb432b5..11d836b 100644 --- a/thaw/src/combobox/listbox.css +++ b/thaw/src/combobox/common/listbox.css @@ -1,3 +1,18 @@ +.thaw-listbox { + row-gap: var(--spacingHorizontalXXS); + display: flex; + flex-direction: column; + min-width: 160px; + max-height: 80vh; + background-color: var(--colorNeutralBackground1); + padding: var(--spacingHorizontalXS); + outline: 1px solid var(--colorTransparentStroke); + border-radius: var(--borderRadiusMedium); + box-shadow: var(--shadow16); + box-sizing: border-box; + overflow-y: auto; +} + .thaw-listbox.fade-in-scale-up-transition-leave-active { transform-origin: inherit; transition: opacity 0.2s cubic-bezier(0.4, 0, 1, 1), @@ -20,4 +35,4 @@ .thaw-listbox.fade-in-scale-up-transition-enter-to { opacity: 1; transform: scale(1); -} \ No newline at end of file +} diff --git a/thaw/src/combobox/listbox.rs b/thaw/src/combobox/common/listbox.rs similarity index 100% rename from thaw/src/combobox/listbox.rs rename to thaw/src/combobox/common/listbox.rs diff --git a/thaw/src/combobox/common/mod.rs b/thaw/src/combobox/common/mod.rs new file mode 100644 index 0000000..788010e --- /dev/null +++ b/thaw/src/combobox/common/mod.rs @@ -0,0 +1,3 @@ +pub mod listbox; +pub mod option_group; +mod utils; diff --git a/thaw/src/combobox/combobox-option-group.css b/thaw/src/combobox/common/option-group.css similarity index 84% rename from thaw/src/combobox/combobox-option-group.css rename to thaw/src/combobox/common/option-group.css index f03491e..d387dcf 100644 --- a/thaw/src/combobox/combobox-option-group.css +++ b/thaw/src/combobox/common/option-group.css @@ -1,10 +1,10 @@ -.thaw-combobox-option-group { +.thaw-option-group { display: flex; row-gap: var(--spacingHorizontalXXS); flex-direction: column; } -.thaw-combobox-option-group__label { +.thaw-option-group__label { display: block; color: var(--colorNeutralForeground3); font-weight: var(--fontWeightSemibold); @@ -14,7 +14,7 @@ border-radius: var(--borderRadiusMedium); } -.thaw-combobox-option-group:not(:last-child)::after { +.thaw-option-group:not(:last-child)::after { content: ""; display: block; margin: 0 calc(var(--spacingHorizontalXS) * -1) var(--spacingVerticalXS); diff --git a/thaw/src/combobox/common/option_group.rs b/thaw/src/combobox/common/option_group.rs new file mode 100644 index 0000000..3717e3c --- /dev/null +++ b/thaw/src/combobox/common/option_group.rs @@ -0,0 +1,25 @@ +use leptos::prelude::*; +use thaw_utils::{class_list, mount_style}; + +#[component] +pub fn OptionGroup( + class_prefix: &'static str, + class: MaybeProp, + /// Label of the group. + label: String, + children: Children, +) -> impl IntoView { + mount_style("option-group", include_str!("./option-group.css")); + + view! { +
+ + {label} + + {children()} +
+ } +} diff --git a/thaw/src/combobox/utils.rs b/thaw/src/combobox/common/utils.rs similarity index 100% rename from thaw/src/combobox/utils.rs rename to thaw/src/combobox/common/utils.rs diff --git a/thaw/src/combobox/mod.rs b/thaw/src/combobox/mod.rs index 993d458..0a18528 100644 --- a/thaw/src/combobox/mod.rs +++ b/thaw/src/combobox/mod.rs @@ -1,9 +1,9 @@ mod combobox; mod combobox_option; mod combobox_option_group; -pub(crate) mod listbox; -mod utils; +mod common; pub use combobox::*; pub use combobox_option::*; pub use combobox_option_group::*; +pub(crate) use common::*; diff --git a/thaw/src/tag_picker/docs/mod.md b/thaw/src/tag_picker/docs/mod.md index 503025a..d4e9207 100644 --- a/thaw/src/tag_picker/docs/mod.md +++ b/thaw/src/tag_picker/docs/mod.md @@ -37,6 +37,76 @@ view! { } ``` +### Grouped + +```rust demo +use leptos::either::Either; +let selected_options = RwSignal::new(vec![]); +let land = vec!["Cat", "Dog", "Ferret", "Hamster"]; +let water = vec!["Fish", "Jellyfish", "Octopus", "Seal"]; + +view! { + + + + {move || { + selected_options.get().into_iter().map(|option| view!{ + + {option} + + }).collect_view() + }} + + + + {move || { + selected_options.with(|selected_options| { + let land_view = land.iter().filter_map(|option| { + if selected_options.iter().any(|o| o == option) { + return None + } else { + Some(view! { + + }) + } + }).collect_view(); + if land_view.is_empty() { + Either::Left(()) + } else { + Either::Right(view! { + + {land_view} + + }) + } + }) + }} + {move || { + selected_options.with(|selected_options| { + let water_view = water.iter().filter_map(|option| { + if selected_options.iter().any(|o| o == option) { + return None + } else { + Some(view! { + + }) + } + }).collect_view(); + if water_view.is_empty() { + Either::Left(()) + } else { + Either::Right(view! { + + {water_view} + + }) + } + }) + }} + +} +``` + ### TagPicker Props | Name | Type | Default | Description | @@ -68,3 +138,11 @@ view! { | value | `String` | | Defines a unique identifier for the option. | | text | `String` | | An optional override the string value of the Option's display text, defaulting to the Option's child content. | | children | `Option` | `None` | | + +### TagPickerOptionGroup Props + +| Name | Type | Default | Desciption | +| -------- | ------------------- | -------------------- | ------------------- | +| class | `MaybeProp` | `Default::default()` | | +| label | `String` | | Label of the group. | +| children | `Children` | | | diff --git a/thaw/src/tag_picker/mod.rs b/thaw/src/tag_picker/mod.rs index 0556e7d..593909c 100644 --- a/thaw/src/tag_picker/mod.rs +++ b/thaw/src/tag_picker/mod.rs @@ -2,8 +2,10 @@ mod tag_picker; mod tag_picker_group; mod tag_picker_input; mod tag_picker_option; +mod tag_picker_option_group; pub use tag_picker::*; pub use tag_picker_group::*; pub use tag_picker_input::*; pub use tag_picker_option::*; +pub use tag_picker_option_group::*; diff --git a/thaw/src/tag_picker/tag-picker.css b/thaw/src/tag_picker/tag-picker.css index 397faee..2deb6e3 100644 --- a/thaw/src/tag_picker/tag-picker.css +++ b/thaw/src/tag_picker/tag-picker.css @@ -82,21 +82,6 @@ outline-style: none; } -.thaw-tag-picker__listbox { - display: flex; - flex-direction: column; - row-gap: var(--spacingHorizontalXXS); - overflow-y: auto; - min-width: 160px; - max-height: 80vh; - background-color: var(--colorNeutralBackground1); - padding: var(--spacingHorizontalXS); - outline: 1px solid var(--colorTransparentStroke); - border-radius: var(--borderRadiusMedium); - box-shadow: var(--shadow16); - box-sizing: border-box; -} - .thaw-tag-picker-option { grid-template-columns: auto 1fr; column-gap: var(--spacingHorizontalXS); diff --git a/thaw/src/tag_picker/tag_picker.rs b/thaw/src/tag_picker/tag_picker.rs index 67a262f..3aa0f3c 100644 --- a/thaw/src/tag_picker/tag_picker.rs +++ b/thaw/src/tag_picker/tag_picker.rs @@ -6,7 +6,7 @@ use crate::{ use leptos::{context::Provider, ev, html, prelude::*}; use std::collections::HashMap; use thaw_components::{Binder, Follower, FollowerPlacement, FollowerWidth}; -use thaw_utils::{call_on_click_outside, class_list, mount_style, BoxCallback, Model}; +use thaw_utils::{call_on_click_outside_with_list, class_list, mount_style, BoxCallback, Model}; #[component] pub fn TagPicker( @@ -55,8 +55,8 @@ pub fn TagPicker( } is_show_listbox.update(|show| *show = !*show); }; - call_on_click_outside( - trigger_ref, + call_on_click_outside_with_list( + vec![trigger_ref, listbox_ref], { move || { is_show_listbox.set(false); diff --git a/thaw/src/tag_picker/tag_picker_option_group.rs b/thaw/src/tag_picker/tag_picker_option_group.rs new file mode 100644 index 0000000..4f227c7 --- /dev/null +++ b/thaw/src/tag_picker/tag_picker_option_group.rs @@ -0,0 +1,13 @@ +use crate::option_group::OptionGroup; +use leptos::prelude::*; + +#[component] +pub fn TagPickerOptionGroup( + #[prop(optional, into)] class: MaybeProp, + /// Label of the group. + #[prop(into)] + label: String, + children: Children, +) -> impl IntoView { + view! { } +} diff --git a/thaw_utils/src/on_click_outside.rs b/thaw_utils/src/on_click_outside.rs index 98db865..c3fe30f 100644 --- a/thaw_utils/src/on_click_outside.rs +++ b/thaw_utils/src/on_click_outside.rs @@ -21,3 +21,28 @@ pub fn call_on_click_outside(element: NodeRef
, on_click: BoxCallback) { let _ = on_click; } } + +pub fn call_on_click_outside_with_list(refs: Vec>, on_click: BoxCallback) { + #[cfg(any(feature = "csr", feature = "hydrate"))] + { + let handle = window_event_listener(::leptos::ev::click, move |ev| { + let composed_path = ev.composed_path(); + if refs.iter().any(|r| { + if let Some(el) = r.get_untracked() { + composed_path.includes(&el, 0) + } else { + false + } + }) { + return; + } + on_click(); + }); + on_cleanup(move || handle.remove()); + } + #[cfg(not(any(feature = "csr", feature = "hydrate")))] + { + let _ = refs; + let _ = on_click; + } +}