Skip to content

Commit 61746ff

Browse files
committed
on_select instaed of on_click
1 parent ee9c8d2 commit 61746ff

File tree

6 files changed

+86
-93
lines changed

6 files changed

+86
-93
lines changed

demo_markdown/docs/dropdown/mod.md

+29-31
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,32 @@
33
```rust demo
44
let value = create_rw_signal(None::<String>);
55
let message = use_message();
6-
let facebook = move |_| {
7-
message.create(
8-
"Facebook".into(),
9-
MessageVariant::Success,
10-
Default::default(),
11-
);
12-
};
13-
let twitter = move |_| {
14-
message.create(
15-
"Twitter".into(),
16-
MessageVariant::Warning,
17-
Default::default(),
18-
);
6+
7+
let on_select = move |key: String| {
8+
match key.as_str() {
9+
"facebook" => message.create( "Facebook".into(), MessageVariant::Success, Default::default(),),
10+
"twitter" => message.create( "Twitter".into(), MessageVariant::Warning, Default::default(),),
11+
_ => ()
12+
}
1913
};
14+
15+
2016
view! {
2117
<Space>
22-
<Dropdown>
18+
<Dropdown on_select>
2319
<DropdownTrigger slot>
2420
<Button>"Click"</Button>
2521
</DropdownTrigger>
26-
<DropdownItem on_click=facebook icon=icondata::AiFacebookOutlined label="Facebook"></DropdownItem>
27-
<DropdownItem on_click=twitter disabled=true icon=icondata::AiTwitterOutlined label="Twitter"></DropdownItem>
22+
<DropdownItem key="facebook" icon=icondata::AiFacebookOutlined label="Facebook"></DropdownItem>
23+
<DropdownItem key="twitter" disabled=true icon=icondata::AiTwitterOutlined label="Twitter"></DropdownItem>
2824
</Dropdown>
2925

30-
<Dropdown trigger_type=DropdownTriggerType::Hover>
26+
<Dropdown on_select trigger_type=DropdownTriggerType::Hover>
3127
<DropdownTrigger slot>
3228
<Button>"Hover"</Button>
3329
</DropdownTrigger>
34-
<DropdownItem on_click=facebook icon=icondata::AiFacebookOutlined label="Facebook"></DropdownItem>
35-
<DropdownItem on_click=twitter disabled=true icon=icondata::AiTwitterOutlined label="Twitter"></DropdownItem>
30+
<DropdownItem key="facebook" icon=icondata::AiFacebookOutlined label="Facebook"></DropdownItem>
31+
<DropdownItem key="twitter" disabled=true icon=icondata::AiTwitterOutlined label="Twitter"></DropdownItem>
3632
</Dropdown>
3733
</Space>
3834
}
@@ -43,101 +39,103 @@ view! {
4339
```rust demo
4440
use leptos_meta::Style;
4541

42+
let on_select = move |key| println!("{}", key);
43+
4644
view! {
4745
<Style>
4846
".demo-dropdown .thaw-button { width: 100% } .demo-dropdown .thaw-dropdown-trigger { display: block }"
4947
</Style>
5048
<Grid x_gap=8 y_gap=8 cols=3 class="demo-dropdown">
5149
<GridItem>
52-
<Dropdown placement=DropdownPlacement::TopStart>
50+
<Dropdown on_select placement=DropdownPlacement::TopStart>
5351
<DropdownTrigger slot>
5452
<Button>"Top Start"</Button>
5553
</DropdownTrigger>
5654
"Content"
5755
</Dropdown>
5856
</GridItem>
5957
<GridItem>
60-
<Dropdown placement=DropdownPlacement::Top>
58+
<Dropdown on_select placement=DropdownPlacement::Top>
6159
<DropdownTrigger slot>
6260
<Button>"Top"</Button>
6361
</DropdownTrigger>
6462
"Content"
6563
</Dropdown>
6664
</GridItem>
6765
<GridItem>
68-
<Dropdown placement=DropdownPlacement::TopEnd>
66+
<Dropdown on_select placement=DropdownPlacement::TopEnd>
6967
<DropdownTrigger slot>
7068
<Button>"Top End"</Button>
7169
</DropdownTrigger>
7270
"Content"
7371
</Dropdown>
7472
</GridItem>
7573
<GridItem>
76-
<Dropdown placement=DropdownPlacement::LeftStart>
74+
<Dropdown on_select placement=DropdownPlacement::LeftStart>
7775
<DropdownTrigger slot>
7876
<Button>"Left Start"</Button>
7977
</DropdownTrigger>
8078
"Content"
8179
</Dropdown>
8280
</GridItem>
8381
<GridItem offset=1>
84-
<Dropdown placement=DropdownPlacement::RightStart>
82+
<Dropdown on_select placement=DropdownPlacement::RightStart>
8583
<DropdownTrigger slot>
8684
<Button>"Right Start"</Button>
8785
</DropdownTrigger>
8886
"Content"
8987
</Dropdown>
9088
</GridItem>
9189
<GridItem>
92-
<Dropdown placement=DropdownPlacement::Left>
90+
<Dropdown on_select placement=DropdownPlacement::Left>
9391
<DropdownTrigger slot>
9492
<Button>"Left"</Button>
9593
</DropdownTrigger>
9694
"Content"
9795
</Dropdown>
9896
</GridItem>
9997
<GridItem offset=1>
100-
<Dropdown placement=DropdownPlacement::Right>
98+
<Dropdown on_select placement=DropdownPlacement::Right>
10199
<DropdownTrigger slot>
102100
<Button>"Right"</Button>
103101
</DropdownTrigger>
104102
"Content"
105103
</Dropdown>
106104
</GridItem>
107105
<GridItem>
108-
<Dropdown placement=DropdownPlacement::LeftEnd>
106+
<Dropdown on_select placement=DropdownPlacement::LeftEnd>
109107
<DropdownTrigger slot>
110108
<Button>"Left End"</Button>
111109
</DropdownTrigger>
112110
"Content"
113111
</Dropdown>
114112
</GridItem>
115113
<GridItem offset=1>
116-
<Dropdown placement=DropdownPlacement::RightEnd>
114+
<Dropdown on_select placement=DropdownPlacement::RightEnd>
117115
<DropdownTrigger slot>
118116
<Button>"Right End"</Button>
119117
</DropdownTrigger>
120118
"Content"
121119
</Dropdown>
122120
</GridItem>
123121
<GridItem>
124-
<Dropdown placement=DropdownPlacement::BottomStart>
122+
<Dropdown on_select placement=DropdownPlacement::BottomStart>
125123
<DropdownTrigger slot>
126124
<Button>"Bottom Start"</Button>
127125
</DropdownTrigger>
128126
"Content"
129127
</Dropdown>
130128
</GridItem>
131129
<GridItem>
132-
<Dropdown placement=DropdownPlacement::Bottom>
130+
<Dropdown on_select placement=DropdownPlacement::Bottom>
133131
<DropdownTrigger slot>
134132
<Button>"Bottom"</Button>
135133
</DropdownTrigger>
136134
"Content"
137135
</Dropdown>
138136
</GridItem>
139137
<GridItem>
140-
<Dropdown placement=DropdownPlacement::BottomEnd>
138+
<Dropdown on_select placement=DropdownPlacement::BottomEnd>
141139
<DropdownTrigger slot>
142140
<Button>"Bottom End"</Button>
143141
</DropdownTrigger>

thaw/src/dropdown/dropdown_item.rs

+9-7
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@ use leptos::*;
22
use thaw_components::{Fallback, If, OptionComp, Then};
33
use thaw_utils::{class_list, mount_style, OptionalMaybeSignal, OptionalProp};
44

5-
use crate::{dropdown::HasIcon, use_theme, Icon, Theme};
5+
use crate::{
6+
dropdown::{HasIcon, OnSelect},
7+
use_theme, Icon, Theme,
8+
};
69

710
#[component]
811
pub fn DropdownItem(
912
#[prop(optional, into)] icon: OptionalMaybeSignal<icondata_core::Icon>,
1013
#[prop(into)] label: MaybeSignal<String>,
14+
#[prop(into)] key: MaybeSignal<String>,
1115
#[prop(optional, into)] disabled: MaybeSignal<bool>,
1216
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
13-
#[prop(optional, into)] on_click: Option<Callback<ev::MouseEvent>>,
1417
) -> impl IntoView {
1518
mount_style("dropdown-item", include_str!("./dropdown-item.css"));
1619
let theme = use_theme(Theme::light);
@@ -35,14 +38,13 @@ pub fn DropdownItem(
3538
has_icon.set(true);
3639
}
3740

38-
let on_click = move |event| {
41+
let on_select = use_context::<OnSelect>().expect("OnSelect not provided").0;
42+
43+
let on_click = move |_| {
3944
if disabled.get() {
4045
return;
4146
}
42-
let Some(callback) = on_click.as_ref() else {
43-
return;
44-
};
45-
callback.call(event);
47+
on_select.call(key.get());
4648
};
4749

4850
view! {

thaw/src/dropdown/mod.rs

+10-27
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ use thaw_components::{Binder, CSSTransition, Follower, FollowerPlacement};
99
pub use theme::DropdownTheme;
1010

1111
use leptos::{html::Div, leptos_dom::helpers::TimeoutHandle, *};
12-
use thaw_utils::{add_event_listener, class_list, mount_style, OptionalProp};
12+
use thaw_utils::{
13+
add_event_listener, call_on_click_outside, class_list, mount_style, OptionalProp,
14+
};
1315

1416
use crate::{use_theme, Theme};
1517

@@ -23,39 +25,16 @@ pub struct DropdownTrigger {
2325
#[derive(Copy, Clone)]
2426
struct HasIcon(RwSignal<bool>);
2527

26-
fn call_on_click_outside(element: NodeRef<Div>, on_click: Callback<()>) {
27-
#[cfg(any(feature = "csr", feature = "hydrate"))]
28-
{
29-
let handle = window_event_listener(ev::click, move |ev| {
30-
use leptos::wasm_bindgen::__rt::IntoJsResult;
31-
let el = ev.target();
32-
let mut el: Option<web_sys::Element> =
33-
el.into_js_result().map_or(None, |el| Some(el.into()));
34-
let body = document().body().unwrap();
35-
while let Some(current_el) = el {
36-
if current_el == *body {
37-
break;
38-
};
39-
let Some(dropdown_el) = element.get_untracked() else {
40-
break;
41-
};
42-
if current_el == ***dropdown_el {
43-
return;
44-
}
45-
el = current_el.parent_element();
46-
}
47-
on_click.call(());
48-
});
49-
on_cleanup(move || handle.remove());
50-
}
51-
}
28+
#[derive(Copy, Clone)]
29+
struct OnSelect(Callback<String>);
5230

5331
#[component]
5432
pub fn Dropdown(
5533
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
5634
dropdown_trigger: DropdownTrigger,
5735
#[prop(optional)] trigger_type: DropdownTriggerType,
5836
#[prop(optional)] placement: DropdownPlacement,
37+
#[prop(into)] on_select: Callback<String>,
5938
children: Children,
6039
) -> impl IntoView {
6140
mount_style("dropdown", include_str!("./dropdown.css"));
@@ -121,6 +100,10 @@ pub fn Dropdown(
121100
} = dropdown_trigger;
122101

123102
provide_context(HasIcon(create_rw_signal(false)));
103+
provide_context(OnSelect(Callback::<String>::new(move |key| {
104+
is_show_dropdown.set(false);
105+
on_select.call(key);
106+
})));
124107

125108
view! {
126109
<Binder target_ref>

thaw/src/popover/mod.rs

+8-28
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ use crate::{use_theme, Theme};
66
use leptos::{leptos_dom::helpers::TimeoutHandle, *};
77
use std::time::Duration;
88
use thaw_components::{Binder, CSSTransition, Follower, FollowerPlacement};
9-
use thaw_utils::{add_event_listener, class_list, mount_style, OptionalProp};
9+
use thaw_utils::{
10+
add_event_listener, call_on_click_outside, class_list, mount_style, OptionalProp,
11+
};
1012

1113
#[slot]
1214
pub struct PopoverTrigger {
@@ -77,33 +79,11 @@ pub fn Popover(
7779
.ok();
7880
});
7981
};
80-
#[cfg(any(feature = "csr", feature = "hydrate"))]
81-
{
82-
let handle = window_event_listener(ev::click, move |ev| {
83-
use leptos::wasm_bindgen::__rt::IntoJsResult;
84-
if trigger_type != PopoverTriggerType::Click {
85-
return;
86-
}
87-
let el = ev.target();
88-
let mut el: Option<web_sys::Element> =
89-
el.into_js_result().map_or(None, |el| Some(el.into()));
90-
let body = document().body().unwrap();
91-
while let Some(current_el) = el {
92-
if current_el == *body {
93-
break;
94-
};
95-
let Some(popover_el) = popover_ref.get_untracked() else {
96-
break;
97-
};
98-
if current_el == ***popover_el {
99-
return;
100-
}
101-
el = current_el.parent_element();
102-
}
103-
is_show_popover.set(false);
104-
});
105-
on_cleanup(move || handle.remove());
106-
}
82+
83+
call_on_click_outside(
84+
popover_ref,
85+
Callback::new(move |_| is_show_popover.set(false)),
86+
);
10787

10888
target_ref.on_load(move |target_el| {
10989
add_event_listener(target_el.into_any(), ev::click, move |event| {

thaw_utils/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub mod class_list;
22
mod dom;
33
mod event_listener;
44
mod hooks;
5+
mod on_click_outside;
56
mod optional_prop;
67
mod signals;
78
mod throttle;
@@ -12,6 +13,7 @@ pub use event_listener::{
1213
add_event_listener, add_event_listener_with_bool, EventListenerHandle, IntoEventTarget,
1314
};
1415
pub use hooks::{use_click_position, use_lock_html_scroll, use_next_frame, NextFrame};
16+
pub use on_click_outside::call_on_click_outside;
1517
pub use optional_prop::OptionalProp;
1618
pub use signals::{
1719
create_component_ref, ComponentRef, Model, OptionalMaybeSignal, SignalWatch, StoredMaybeSignal,

thaw_utils/src/on_click_outside.rs

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use leptos::{html::Div, *};
2+
3+
pub fn call_on_click_outside(element: NodeRef<Div>, on_click: Callback<()>) {
4+
#[cfg(any(feature = "csr", feature = "hydrate"))]
5+
{
6+
let handle = window_event_listener(ev::click, move |ev| {
7+
use leptos::wasm_bindgen::__rt::IntoJsResult;
8+
let el = ev.target();
9+
let mut el: Option<web_sys::Element> =
10+
el.into_js_result().map_or(None, |el| Some(el.into()));
11+
let body = document().body().unwrap();
12+
while let Some(current_el) = el {
13+
if current_el == *body {
14+
break;
15+
};
16+
let Some(dropdown_el) = element.get_untracked() else {
17+
break;
18+
};
19+
if current_el == ***dropdown_el {
20+
return;
21+
}
22+
el = current_el.parent_element();
23+
}
24+
on_click.call(());
25+
});
26+
on_cleanup(move || handle.remove());
27+
}
28+
}

0 commit comments

Comments
 (0)