diff --git a/demo_markdown/docs/drawer/mod.md b/demo_markdown/docs/drawer/mod.md index ff84c2c..8761017 100644 --- a/demo_markdown/docs/drawer/mod.md +++ b/demo_markdown/docs/drawer/mod.md @@ -5,8 +5,8 @@ let show = create_rw_signal(false); let placement = create_rw_signal(DrawerPlacement::Top); let open = Callback::new(move |new_placement: DrawerPlacement| { - show.set(true); placement.set(new_placement); + show.set(true); }); view! { @@ -28,9 +28,9 @@ view! { let show = create_rw_signal(false); view! { -
+
- + "Current position"
diff --git a/thaw/src/components/css_transition/mod.rs b/thaw/src/components/css_transition/mod.rs index 4b284bc..7ad07e8 100644 --- a/thaw/src/components/css_transition/mod.rs +++ b/thaw/src/components/css_transition/mod.rs @@ -6,6 +6,7 @@ pub fn CSSTransition( node_ref: NodeRef, #[prop(into)] show: MaybeSignal, #[prop(into)] name: MaybeSignal, + #[prop(optional, into)] on_after_leave: Option>, children: CF, ) -> impl IntoView where @@ -30,6 +31,9 @@ where RemoveClassName::Leave(active, to) => { let _ = class_list.remove_2(&active, &to); display.set(Some("display: none;")); + if let Some(on_after_leave) = on_after_leave { + on_after_leave.call(()); + } } } } diff --git a/thaw/src/drawer/drawer.css b/thaw/src/drawer/drawer.css index 737ea35..a1403e5 100644 --- a/thaw/src/drawer/drawer.css +++ b/thaw/src/drawer/drawer.css @@ -4,6 +4,11 @@ right: 0; left: 0; bottom: 0; + pointer-events: none; +} + +.thaw-drawer-container > * { + pointer-events: auto; } .thaw-drawer-mask { @@ -62,3 +67,117 @@ bottom: 0; right: 0; } + +.thaw-drawer.slide-in-from-right-transition-leave-active { + transition: transform 0.2s cubic-bezier(0.4, 0, 1, 1); +} + +.thaw-drawer.slide-in-from-right-transition-enter-active { + transition: transform 0.3s cubic-bezier(0, 0, 0.2, 1); +} + +.thaw-drawer.slide-in-from-right-transition-enter-to { + transform: translateX(0); +} + +.thaw-drawer.slide-in-from-right-transition-enter-from { + transform: translateX(100%); +} + +.thaw-drawer.slide-in-from-right-transition-leave-from { + transform: translateX(0); +} + +.thaw-drawer.slide-in-from-right-transition-leave-to { + transform: translateX(100%); +} + +.thaw-drawer.slide-in-from-left-transition-leave-active { + transition: transform 0.2s cubic-bezier(0.4, 0, 1, 1); +} + +.thaw-drawer.slide-in-from-left-transition-enter-active { + transition: transform 0.3s cubic-bezier(0, 0, 0.2, 1); +} + +.thaw-drawer.slide-in-from-left-transition-enter-to { + transform: translateX(0); +} + +.thaw-drawer.slide-in-from-left-transition-enter-from { + transform: translateX(-100%); +} + +.thaw-drawer.slide-in-from-left-transition-leave-from { + transform: translateX(0); +} + +.thaw-drawer.slide-in-from-left-transition-leave-to { + transform: translateX(-100%); +} + +.thaw-drawer.slide-in-from-top-transition-leave-active { + transition: transform 0.2s cubic-bezier(0.4, 0, 1, 1); +} + +.thaw-drawer.slide-in-from-top-transition-enter-active { + transition: transform 0.3s cubic-bezier(0, 0, 0.2, 1); +} + +.thaw-drawer.slide-in-from-top-transition-enter-to { + transform: translateY(0); +} + +.thaw-drawer.slide-in-from-top-transition-enter-from { + transform: translateY(-100%); +} + +.thaw-drawer.slide-in-from-top-transition-leave-from { + transform: translateY(0); +} + +.thaw-drawer.slide-in-from-top-transition-leave-to { + transform: translateY(-100%); +} + +.thaw-drawer.slide-in-from-bottom-transition-leave-active { + transition: transform 0.2s cubic-bezier(0.4, 0, 1, 1); +} + +.thaw-drawer.slide-in-from-bottom-transition-enter-active { + transition: transform 0.3s cubic-bezier(0, 0, 0.2, 1); +} + +.thaw-drawer.slide-in-from-bottom-transition-enter-to { + transform: translateY(0); +} + +.thaw-drawer.slide-in-from-bottom-transition-enter-from { + transform: translateY(100%); +} + +.thaw-drawer.slide-in-from-bottom-transition-leave-from { + transform: translateY(0); +} + +.thaw-drawer.slide-in-from-bottom-transition-leave-to { + transform: translateY(100%); +} + +.thaw-drawer-mask.fade-in-transition-enter-active { + transition: all 0.2s cubic-bezier(0.4, 0, 1, 1); +} + +.thaw-drawer-mask.fade-in-transition-leave-active { + transition: all 0.2s cubic-bezier(0, 0, 0.2, 1); +} + +.thaw-drawer-mask.fade-in-transition-enter-from, +.thaw-drawer-mask.fade-in-transition-leave-to { + opacity: 0; +} + +.thaw-drawer-mask.fade-in-transition-leave-from, +.thaw-drawer-mask.fade-in-transition-enter-to { + opacity: 1; +} diff --git a/thaw/src/drawer/mod.rs b/thaw/src/drawer/mod.rs index 627d719..a219b65 100644 --- a/thaw/src/drawer/mod.rs +++ b/thaw/src/drawer/mod.rs @@ -1,6 +1,6 @@ use crate::{ - components::Teleport, - utils::{class_list::class_list, mount_style, Model, OptionalProp}, + components::{CSSTransition, Teleport}, + utils::{class_list::class_list, mount_style, use_lock_html_scroll, Model, OptionalProp}, Card, }; use leptos::*; @@ -18,11 +18,14 @@ pub fn Drawer( children: Children, ) -> impl IntoView { mount_style("drawer", include_str!("./drawer.css")); - let css_vars = create_memo(move |_| { - let mut css_vars = String::new(); - css_vars.push_str(&format!("--thaw-width: {};", width.get())); - css_vars.push_str(&format!("--thaw-height: {};", height.get())); - css_vars + let style = create_memo(move |_| { + let mut style = String::new(); + + style.push_str(&format!("--thaw-width: {};", width.get())); + style.push_str(&format!("--thaw-height: {};", height.get())); + + style.push_str(&format!("z-index: {};", z_index.get())); + style }); #[component] @@ -30,38 +33,71 @@ pub fn Drawer( show: Model, title: OptionalProp>, placement: MaybeSignal, - z_index: MaybeSignal, class: OptionalProp>, - css_vars: Memo, + style: Memo, children: Children, ) -> impl IntoView { - view! { -
-
-
::new(); + let drawer_ref = NodeRef::::new(); - style=move || css_vars.get() + let is_lock = RwSignal::new(show.get_untracked()); + Effect::new(move |_| { + if show.get() { + is_lock.set(true); + } + }); + use_lock_html_scroll(is_lock.into()); + let on_after_leave = move |_| { + is_lock.set(false); + }; + + view! { +
+ - {children()} -
+
+ + + +
} } match mount { - DrawerMount::None => view! { - - }, + DrawerMount::None => view! { }, DrawerMount::Body => view! { - + }, } @@ -76,6 +112,8 @@ pub enum DrawerPlacement { Right, } +impl Copy for DrawerPlacement {} + impl DrawerPlacement { pub fn as_str(&self) -> &'static str { match self { diff --git a/thaw/src/utils/mod.rs b/thaw/src/utils/mod.rs index e953ee2..59dc9ac 100644 --- a/thaw/src/utils/mod.rs +++ b/thaw/src/utils/mod.rs @@ -8,6 +8,7 @@ mod optional_prop; mod signal; mod stored_maybe_signal; mod time; +mod use_lock_html_scroll; // pub use callback::AsyncCallback; pub use component_ref::{create_component_ref, ComponentRef}; @@ -18,6 +19,7 @@ pub(crate) use optional_prop::OptionalProp; pub use signal::SignalWatch; pub(crate) use stored_maybe_signal::*; pub(crate) use time::*; +pub(crate) use use_lock_html_scroll::*; pub(crate) fn with_hydration_off(f: impl FnOnce() -> T) -> T { #[cfg(feature = "hydrate")] diff --git a/thaw/src/utils/use_lock_html_scroll.rs b/thaw/src/utils/use_lock_html_scroll.rs new file mode 100644 index 0000000..091178c --- /dev/null +++ b/thaw/src/utils/use_lock_html_scroll.rs @@ -0,0 +1,39 @@ +use leptos::MaybeSignal; + +pub fn use_lock_html_scroll(is_lock: MaybeSignal) { + #[cfg(any(feature = "csr", feature = "hydrate"))] + { + use leptos::{create_render_effect, document, on_cleanup, SignalGet, StoredValue}; + let style_el = StoredValue::new(None::); + let remove_style_el = move || { + style_el.update_value(move |el| { + if let Some(el) = el.take() { + let head = document().head().expect("head no exist"); + _ = head.remove_child(&el); + } + }); + }; + + create_render_effect(move |_| { + if is_lock.get() { + let head = document().head().expect("head no exist"); + let style = document() + .create_element("style") + .expect("create style element error"); + _ = style.set_attribute("data-id", &format!("thaw-lock-html-scroll")); + style.set_text_content(Some("html { overflow: hidden; }")); + _ = head.append_child(&style); + style_el.set_value(Some(style)); + } else { + remove_style_el(); + } + }); + + on_cleanup(remove_style_el) + } + + #[cfg(not(any(feature = "csr", feature = "hydrate")))] + { + _ = is_lock; + } +}