From 4a65c6ce02aec4be8018713fdc816fd780788e61 Mon Sep 17 00:00:00 2001 From: luoxiao Date: Sun, 7 Apr 2024 22:38:18 +0800 Subject: [PATCH 1/5] feat: Drawer adds mask_closeable and close_on_esc prop --- demo_markdown/docs/drawer/mod.md | 2 ++ thaw/src/drawer/mod.rs | 52 ++++++++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/demo_markdown/docs/drawer/mod.md b/demo_markdown/docs/drawer/mod.md index 3594d798..3a098039 100644 --- a/demo_markdown/docs/drawer/mod.md +++ b/demo_markdown/docs/drawer/mod.md @@ -45,6 +45,8 @@ view! { | --- | --- | --- | --- | | class | `OptionalProp>` | `Default::default()` | Addtional classes for the drawer element. | | show | `Model` | | Whether to show drawer. | +| mask_closeable | `MaybeSignal` | `true` | Whether to emit hide event when click mask. | +| close_on_esc | `bool` | `true` | Whether to close drawer on Esc is pressed. | | title | `OptionalProp>` | `Default::default()` | Drawer title. | | placement | `MaybeSignal` | `DrawerPlacement::Right` | Drawer placement. | | width | `MaybeSignal` | `520px` | Drawer width. | diff --git a/thaw/src/drawer/mod.rs b/thaw/src/drawer/mod.rs index dcc4c7f3..5c8d4d22 100644 --- a/thaw/src/drawer/mod.rs +++ b/thaw/src/drawer/mod.rs @@ -1,11 +1,13 @@ use crate::Card; -use leptos::*; +use leptos::{leptos_dom::helpers::WindowListenerHandle, *}; use thaw_components::{CSSTransition, Teleport}; use thaw_utils::{class_list, mount_style, use_lock_html_scroll, Model, OptionalProp}; #[component] pub fn Drawer( #[prop(into)] show: Model, + #[prop(default = true.into(), into)] mask_closeable: MaybeSignal, + #[prop(default = true, into)] close_on_esc: bool, #[prop(optional, into)] title: OptionalProp>, #[prop(optional, into)] placement: MaybeSignal, #[prop(default = MaybeSignal::Static("520px".to_string()), into)] width: MaybeSignal, @@ -29,6 +31,8 @@ pub fn Drawer( #[component] fn DrawerInnr( show: Model, + mask_closeable: MaybeSignal, + close_on_esc: bool, title: OptionalProp>, placement: MaybeSignal, class: OptionalProp>, @@ -56,11 +60,31 @@ pub fn Drawer( }; let is_lock = RwSignal::new(show.get_untracked()); - Effect::new(move |_| { - if show.get() { + let esc_handle = StoredValue::new(None::); + Effect::new(move |prev| { + let is_show = show.get(); + if is_show { is_lock.set(true); is_css_transition.set(true); } + if close_on_esc { + if is_show && !prev.unwrap_or(false) { + let handle = window_event_listener(ev::keydown, move |e| { + if &e.code() == "Escape" { + show.set(false); + } + }); + esc_handle.set_value(Some(handle)); + } else { + esc_handle.update_value(|handle| { + if let Some(handle) = handle.take() { + handle.remove(); + } + }) + } + } + + is_show }); use_lock_html_scroll(is_lock.into()); let on_after_leave = move |_| { @@ -68,6 +92,20 @@ pub fn Drawer( is_css_transition.set(false); }; + let on_mask_click = move |_| { + if mask_closeable.get_untracked() { + show.set(false); + } + }; + + on_cleanup(move || { + esc_handle.update_value(|handle| { + if let Some(handle) = handle.take() { + handle.remove(); + } + }) + }); + view! {
@@ -113,10 +151,12 @@ pub fn Drawer( } match mount { - DrawerMount::None => view! { }, + DrawerMount::None => { + view! { } + } DrawerMount::Body => view! { - + }, } From 0dcdf12a50e3d83605081417d6591bad3126642f Mon Sep 17 00:00:00 2001 From: luoxiao Date: Sun, 7 Apr 2024 23:36:22 +0800 Subject: [PATCH 2/5] fix: Drawer and Modal esc close --- thaw/src/drawer/mod.rs | 116 +++++++++------------- thaw/src/modal/mod.rs | 138 +++++++++++--------------- thaw_components/Cargo.toml | 1 + thaw_components/src/focus_trap/mod.rs | 59 +++++++++++ thaw_components/src/lib.rs | 2 + 5 files changed, 167 insertions(+), 149 deletions(-) create mode 100644 thaw_components/src/focus_trap/mod.rs diff --git a/thaw/src/drawer/mod.rs b/thaw/src/drawer/mod.rs index 5c8d4d22..4e66740f 100644 --- a/thaw/src/drawer/mod.rs +++ b/thaw/src/drawer/mod.rs @@ -1,6 +1,6 @@ use crate::Card; -use leptos::{leptos_dom::helpers::WindowListenerHandle, *}; -use thaw_components::{CSSTransition, Teleport}; +use leptos::*; +use thaw_components::{CSSTransition, FocusTrap, Teleport}; use thaw_utils::{class_list, mount_style, use_lock_html_scroll, Model, OptionalProp}; #[component] @@ -60,31 +60,12 @@ pub fn Drawer( }; let is_lock = RwSignal::new(show.get_untracked()); - let esc_handle = StoredValue::new(None::); - Effect::new(move |prev| { + Effect::new(move |_| { let is_show = show.get(); if is_show { is_lock.set(true); is_css_transition.set(true); } - if close_on_esc { - if is_show && !prev.unwrap_or(false) { - let handle = window_event_listener(ev::keydown, move |e| { - if &e.code() == "Escape" { - show.set(false); - } - }); - esc_handle.set_value(Some(handle)); - } else { - esc_handle.update_value(|handle| { - if let Some(handle) = handle.take() { - handle.remove(); - } - }) - } - } - - is_show }); use_lock_html_scroll(is_lock.into()); let on_after_leave = move |_| { @@ -97,56 +78,53 @@ pub fn Drawer( show.set(false); } }; - - on_cleanup(move || { - esc_handle.update_value(|handle| { - if let Some(handle) = handle.take() { - handle.remove(); - } - }) - }); + let on_esc = move |_: ev::KeyboardEvent| { + show.set(false); + }; view! { -
- -
-
- - + + +
+ } } diff --git a/thaw/src/modal/mod.rs b/thaw/src/modal/mod.rs index 790d8fe5..6140cfa2 100644 --- a/thaw/src/modal/mod.rs +++ b/thaw/src/modal/mod.rs @@ -1,6 +1,6 @@ use crate::{Card, CardFooter, CardHeader, CardHeaderExtra, Icon}; -use leptos::{leptos_dom::helpers::WindowListenerHandle, *}; -use thaw_components::{CSSTransition, OptionComp, Teleport}; +use leptos::*; +use thaw_components::{CSSTransition, FocusTrap, OptionComp, Teleport}; use thaw_utils::{mount_style, use_click_position, Model}; #[slot] @@ -22,30 +22,11 @@ pub fn Modal( mount_style("modal", include_str!("./modal.css")); let displayed = RwSignal::new(show.get_untracked()); - let esc_handle = StoredValue::new(None::); Effect::new(move |prev| { let is_show = show.get(); if prev.is_some() && is_show { displayed.set(true); } - - if close_on_esc { - if is_show && !prev.unwrap_or(false) { - let handle = window_event_listener(ev::keydown, move |e| { - if &e.code() == "Escape" { - show.set(false); - } - }); - esc_handle.set_value(Some(handle)); - } else { - esc_handle.update_value(|handle| { - if let Some(handle) = handle.take() { - handle.remove(); - } - }) - } - } - is_show }); let on_mask_click = move |_| { @@ -53,6 +34,9 @@ pub fn Modal( show.set(false); } }; + let on_esc = move |_: ev::KeyboardEvent| { + show.set(false); + }; let mask_ref = NodeRef::::new(); let scroll_ref = NodeRef::::new(); @@ -79,77 +63,71 @@ pub fn Modal( let _ = modal_el.attr("style", format!("transform-origin: {}px {}px", x, y)); }); - on_cleanup(move || { - esc_handle.update_value(|handle| { - if let Some(handle) = handle.take() { - handle.remove(); - } - }) - }); - view! { -
- -
-
+
+ on:click=on_mask_click + ref=mask_ref + >
+
+ + + +
- +
} } diff --git a/thaw_components/Cargo.toml b/thaw_components/Cargo.toml index 39b9a2c6..5d4a1baf 100644 --- a/thaw_components/Cargo.toml +++ b/thaw_components/Cargo.toml @@ -16,6 +16,7 @@ leptos = { version = "0.6.10" } thaw_utils = { workspace = true } web-sys = { version = "0.3.69", features = ["DomRect"] } cfg-if = "1.0.0" +uuid = { version = "1.7.0", features = ["v4"] } [features] csr = ["leptos/csr"] diff --git a/thaw_components/src/focus_trap/mod.rs b/thaw_components/src/focus_trap/mod.rs new file mode 100644 index 00000000..7bc8b560 --- /dev/null +++ b/thaw_components/src/focus_trap/mod.rs @@ -0,0 +1,59 @@ +use leptos::{leptos_dom::helpers::WindowListenerHandle, *}; +use std::cell::RefCell; + +#[cfg(any(feature = "csr", feature = "hydrate"))] +thread_local! { + static STACK: RefCell> = Default::default(); +} + +#[component] +pub fn FocusTrap( + disabled: bool, + #[prop(into)] active: MaybeSignal, + #[prop(into)] on_esc: Callback, + children: Children, +) -> impl IntoView { + #[cfg(any(feature = "csr", feature = "hydrate"))] + if disabled == false { + let esc_handle = StoredValue::new(None::); + let id = StoredValue::new(uuid::Uuid::new_v4()); + + let is_current_active = + move || STACK.with_borrow(|stack| id.with_value(|id| stack.last() == Some(id))); + let deactivate = move || { + esc_handle.update_value(|handle| { + if let Some(handle) = handle.take() { + handle.remove(); + } + }); + STACK.with_borrow_mut(|stack| stack.retain(|value| id.with_value(|id| id != value))); + }; + + Effect::new(move |prev| { + let is_active = active.get(); + if is_active && !prev.unwrap_or(false) { + let handle = window_event_listener(ev::keydown, move |e| { + if &e.code() == "Escape" { + if is_current_active() { + on_esc.call(e); + } + } + }); + esc_handle.set_value(Some(handle)); + STACK.with_borrow_mut(|stack| { + stack.push(id.get_value()); + }); + } else { + deactivate(); + } + + is_active + }); + + on_cleanup(move || { + deactivate(); + }); + } + + children() +} diff --git a/thaw_components/src/lib.rs b/thaw_components/src/lib.rs index c5f2e01b..49012966 100644 --- a/thaw_components/src/lib.rs +++ b/thaw_components/src/lib.rs @@ -1,5 +1,6 @@ mod binder; mod css_transition; +mod focus_trap; mod if_comp; mod option_comp; mod teleport; @@ -7,6 +8,7 @@ mod wave; pub use binder::{Binder, Follower, FollowerPlacement, FollowerWidth}; pub use css_transition::CSSTransition; +pub use focus_trap::FocusTrap; pub use if_comp::{ElseIf, If, Then}; pub use option_comp::OptionComp; pub use teleport::Teleport; From c45ca4355ac84e96a657c83b7a972aed498c6732 Mon Sep 17 00:00:00 2001 From: luoxiao Date: Mon, 8 Apr 2024 21:15:24 +0800 Subject: [PATCH 3/5] feat: Drawer content scroll --- demo_markdown/docs/drawer/mod.md | 13 +++++++++++++ thaw/src/drawer/drawer.css | 3 ++- thaw/src/drawer/mod.rs | 8 ++++++-- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/demo_markdown/docs/drawer/mod.md b/demo_markdown/docs/drawer/mod.md index 3a098039..234fd4e9 100644 --- a/demo_markdown/docs/drawer/mod.md +++ b/demo_markdown/docs/drawer/mod.md @@ -39,6 +39,19 @@ view! { } ``` +### Scroll content + +```rust demo +let show = create_rw_signal(false); + +view! { + + + r#"This being said, the world is moving in the direction opposite to Clarke's predictions. In 2001: A Space Odyssey, in the year of 2001, which has already passed, human beings have built magnificent cities in space, and established permanent colonies on the moon, and huge nuclear-powered spacecraft have sailed to Saturn. However, today, in 2018, the walk on the moon has become a distant memory.And the farthest reach of our manned space flights is just as long as the two-hour mileage of a high-speed train passing through my city. At the same time, information technology is developing at an unimaginable speed. With the entire world covered by the Internet, people have gradually lost their interest in space, as they find themselves increasingly comfortable in the space created by IT. Instead of an exploration of the real space, which is full of real difficulties, people now just prefer to experience virtual space through VR. Just like someone said, "You promised me an ocean of stars, but you actually gave me Facebook.""# + +} +``` + ### Drawer Props | Name | Type | Default | Desciption | diff --git a/thaw/src/drawer/drawer.css b/thaw/src/drawer/drawer.css index a1403e5c..e3fb15ea 100644 --- a/thaw/src/drawer/drawer.css +++ b/thaw/src/drawer/drawer.css @@ -37,7 +37,8 @@ .thaw-drawer > .thaw-card > .thaw-card__content { flex: 1; - padding: 20px 28px; + padding: 0; + overflow: hidden; } .thaw-drawer--placement-top { diff --git a/thaw/src/drawer/mod.rs b/thaw/src/drawer/mod.rs index 4e66740f..90e38d4d 100644 --- a/thaw/src/drawer/mod.rs +++ b/thaw/src/drawer/mod.rs @@ -1,4 +1,4 @@ -use crate::Card; +use crate::{Card, Scrollbar}; use leptos::*; use thaw_components::{CSSTransition, FocusTrap, Teleport}; use thaw_utils::{class_list, mount_style, use_lock_html_scroll, Model, OptionalProp}; @@ -120,7 +120,11 @@ pub fn Drawer( role="dialog" aria-modal="true" > - {children()} + + + {children()} + + From 4237b3a806e59aa639edce33b7cb2dba3c5f296e Mon Sep 17 00:00:00 2001 From: luoxiao Date: Mon, 8 Apr 2024 22:10:03 +0800 Subject: [PATCH 4/5] feat: Modal scrollbar --- thaw/src/modal/mod.rs | 46 +++++++++++++++++++-------------------- thaw/src/modal/modal.css | 7 ------ thaw/src/scrollbar/mod.rs | 20 ++++++++++++++++- 3 files changed, 42 insertions(+), 31 deletions(-) diff --git a/thaw/src/modal/mod.rs b/thaw/src/modal/mod.rs index 6140cfa2..6add3738 100644 --- a/thaw/src/modal/mod.rs +++ b/thaw/src/modal/mod.rs @@ -1,7 +1,7 @@ -use crate::{Card, CardFooter, CardHeader, CardHeaderExtra, Icon}; +use crate::{Card, CardFooter, CardHeader, CardHeaderExtra, Icon, Scrollbar, ScrollbarRef}; use leptos::*; use thaw_components::{CSSTransition, FocusTrap, OptionComp, Teleport}; -use thaw_utils::{mount_style, use_click_position, Model}; +use thaw_utils::{mount_style, use_click_position, ComponentRef, Model}; #[slot] pub struct ModalFooter { @@ -39,7 +39,7 @@ pub fn Modal( }; let mask_ref = NodeRef::::new(); - let scroll_ref = NodeRef::::new(); + let scrollbar_ref = ComponentRef::::new(); let modal_ref = NodeRef::::new(); let click_position = use_click_position(); @@ -48,10 +48,10 @@ pub fn Modal( return; }; - let Some(scroll_el) = scroll_ref.get_untracked() else { + let Some(scroll_el) = scrollbar_ref.get_untracked() else { return; }; - let scroll_top = scroll_el.scroll_top(); + let scroll_top = scroll_el.container_scroll_top(); let Some(modal_el) = modal_ref.get_untracked() else { return; @@ -71,24 +71,24 @@ pub fn Modal( style:z-index=move || z_index.get() style=("--thaw-width", move || width.get()) > - -
-
-
+ +
+
- + diff --git a/thaw/src/modal/modal.css b/thaw/src/modal/modal.css index 97efb784..867fc3e9 100644 --- a/thaw/src/modal/modal.css +++ b/thaw/src/modal/modal.css @@ -13,13 +13,6 @@ pointer-events: auto; } -.thaw-modal-scroll { - display: flex; - min-height: 100%; - min-width: 100%; - overflow: scroll; -} - .thaw-modal-mask { position: fixed; top: 0; diff --git a/thaw/src/scrollbar/mod.rs b/thaw/src/scrollbar/mod.rs index 6329c9e6..f56bcd8b 100644 --- a/thaw/src/scrollbar/mod.rs +++ b/thaw/src/scrollbar/mod.rs @@ -4,7 +4,7 @@ pub use theme::ScrollbarTheme; use crate::{use_theme, Theme}; use leptos::{leptos_dom::helpers::WindowListenerHandle, *}; -use thaw_utils::{class_list, mount_style, OptionalProp}; +use thaw_utils::{class_list, mount_style, ComponentRef, OptionalProp}; #[component] pub fn Scrollbar( @@ -13,6 +13,7 @@ pub fn Scrollbar( #[prop(optional, into)] content_class: OptionalProp>, #[prop(optional, into)] content_style: OptionalProp>, #[prop(default = 8)] size: u8, + #[prop(optional)] comp_ref: Option>, children: Children, ) -> impl IntoView { mount_style("scrollbar", include_str!("./scrollbar.css")); @@ -49,6 +50,12 @@ pub fn Scrollbar( let content_height = RwSignal::new(0); let thumb_status = StoredValue::new(None::); + if let Some(comp_ref) = comp_ref { + comp_ref.load(ScrollbarRef { + container_scroll_top, + }); + } + let x_thumb_width = Memo::new(move |_| { let content_width = f64::from(content_width.get()); let x_track_width = f64::from(x_track_width.get()); @@ -322,3 +329,14 @@ enum ThumbStatus { Enter, DelayLeave, } + +#[derive(Clone)] +pub struct ScrollbarRef { + container_scroll_top: RwSignal, +} + +impl ScrollbarRef { + pub fn container_scroll_top(&self) -> i32 { + self.container_scroll_top.get() + } +} From 21b7a408e715f2d7aa95c072eb7d7dae0a338128 Mon Sep 17 00:00:00 2001 From: luoxiao Date: Mon, 8 Apr 2024 23:07:54 +0800 Subject: [PATCH 5/5] fix: nightly Callback --- thaw/src/drawer/mod.rs | 4 ++-- thaw/src/modal/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/thaw/src/drawer/mod.rs b/thaw/src/drawer/mod.rs index 90e38d4d..fa33ebea 100644 --- a/thaw/src/drawer/mod.rs +++ b/thaw/src/drawer/mod.rs @@ -78,9 +78,9 @@ pub fn Drawer( show.set(false); } }; - let on_esc = move |_: ev::KeyboardEvent| { + let on_esc = Callback::new(move |_: ev::KeyboardEvent| { show.set(false); - }; + }); view! { diff --git a/thaw/src/modal/mod.rs b/thaw/src/modal/mod.rs index 6add3738..7000d20e 100644 --- a/thaw/src/modal/mod.rs +++ b/thaw/src/modal/mod.rs @@ -34,9 +34,9 @@ pub fn Modal( show.set(false); } }; - let on_esc = move |_: ev::KeyboardEvent| { + let on_esc = Callback::new(move |_: ev::KeyboardEvent| { show.set(false); - }; + }); let mask_ref = NodeRef::::new(); let scrollbar_ref = ComponentRef::::new();