mirror of
https://github.com/adoyle0/thaw.git
synced 2025-02-02 08:34:15 -05:00
Feat/drawer transition (#97)
* feat: drawer adds CSSTransition * fix: drawer scrollbar
This commit is contained in:
parent
e361d86c4b
commit
70553b8069
6 changed files with 232 additions and 30 deletions
|
@ -5,8 +5,8 @@ let show = create_rw_signal(false);
|
||||||
let placement = create_rw_signal(DrawerPlacement::Top);
|
let placement = create_rw_signal(DrawerPlacement::Top);
|
||||||
|
|
||||||
let open = Callback::new(move |new_placement: DrawerPlacement| {
|
let open = Callback::new(move |new_placement: DrawerPlacement| {
|
||||||
show.set(true);
|
|
||||||
placement.set(new_placement);
|
placement.set(new_placement);
|
||||||
|
show.set(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
|
@ -28,9 +28,9 @@ view! {
|
||||||
let show = create_rw_signal(false);
|
let show = create_rw_signal(false);
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<div style="position: relative; height: 200px; background-color: #0078ff88;">
|
<div style="position: relative; overflow: hidden; height: 200px; background-color: #0078ff88;">
|
||||||
<Button on_click=move |_| show.set(true)>"Open"</Button>
|
<Button on_click=move |_| show.set(true)>"Open"</Button>
|
||||||
<Drawer show mount=DrawerMount::None>
|
<Drawer show mount=DrawerMount::None width="50%">
|
||||||
"Current position"
|
"Current position"
|
||||||
</Drawer>
|
</Drawer>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,6 +6,7 @@ pub fn CSSTransition<T, CF, IV>(
|
||||||
node_ref: NodeRef<T>,
|
node_ref: NodeRef<T>,
|
||||||
#[prop(into)] show: MaybeSignal<bool>,
|
#[prop(into)] show: MaybeSignal<bool>,
|
||||||
#[prop(into)] name: MaybeSignal<String>,
|
#[prop(into)] name: MaybeSignal<String>,
|
||||||
|
#[prop(optional, into)] on_after_leave: Option<Callback<()>>,
|
||||||
children: CF,
|
children: CF,
|
||||||
) -> impl IntoView
|
) -> impl IntoView
|
||||||
where
|
where
|
||||||
|
@ -30,6 +31,9 @@ where
|
||||||
RemoveClassName::Leave(active, to) => {
|
RemoveClassName::Leave(active, to) => {
|
||||||
let _ = class_list.remove_2(&active, &to);
|
let _ = class_list.remove_2(&active, &to);
|
||||||
display.set(Some("display: none;"));
|
display.set(Some("display: none;"));
|
||||||
|
if let Some(on_after_leave) = on_after_leave {
|
||||||
|
on_after_leave.call(());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,11 @@
|
||||||
right: 0;
|
right: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thaw-drawer-container > * {
|
||||||
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.thaw-drawer-mask {
|
.thaw-drawer-mask {
|
||||||
|
@ -62,3 +67,117 @@
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
right: 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;
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
components::Teleport,
|
components::{CSSTransition, Teleport},
|
||||||
utils::{class_list::class_list, mount_style, Model, OptionalProp},
|
utils::{class_list::class_list, mount_style, use_lock_html_scroll, Model, OptionalProp},
|
||||||
Card,
|
Card,
|
||||||
};
|
};
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
@ -18,11 +18,14 @@ pub fn Drawer(
|
||||||
children: Children,
|
children: Children,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
mount_style("drawer", include_str!("./drawer.css"));
|
mount_style("drawer", include_str!("./drawer.css"));
|
||||||
let css_vars = create_memo(move |_| {
|
let style = create_memo(move |_| {
|
||||||
let mut css_vars = String::new();
|
let mut style = String::new();
|
||||||
css_vars.push_str(&format!("--thaw-width: {};", width.get()));
|
|
||||||
css_vars.push_str(&format!("--thaw-height: {};", height.get()));
|
style.push_str(&format!("--thaw-width: {};", width.get()));
|
||||||
css_vars
|
style.push_str(&format!("--thaw-height: {};", height.get()));
|
||||||
|
|
||||||
|
style.push_str(&format!("z-index: {};", z_index.get()));
|
||||||
|
style
|
||||||
});
|
});
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
|
@ -30,38 +33,71 @@ pub fn Drawer(
|
||||||
show: Model<bool>,
|
show: Model<bool>,
|
||||||
title: OptionalProp<MaybeSignal<String>>,
|
title: OptionalProp<MaybeSignal<String>>,
|
||||||
placement: MaybeSignal<DrawerPlacement>,
|
placement: MaybeSignal<DrawerPlacement>,
|
||||||
z_index: MaybeSignal<i16>,
|
|
||||||
class: OptionalProp<MaybeSignal<String>>,
|
class: OptionalProp<MaybeSignal<String>>,
|
||||||
css_vars: Memo<String>,
|
style: Memo<String>,
|
||||||
children: Children,
|
children: Children,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
view! {
|
let mask_ref = NodeRef::<html::Div>::new();
|
||||||
<div
|
let drawer_ref = NodeRef::<html::Div>::new();
|
||||||
class="thaw-drawer-container"
|
|
||||||
style=move || if show.get() { format!("z-index: {}", z_index.get()) } else { "display: none".to_string() }
|
|
||||||
>
|
|
||||||
<div class="thaw-drawer-mask" on:click=move |_| show.set(false)></div>
|
|
||||||
<div
|
|
||||||
class=class_list![
|
|
||||||
"thaw-drawer", move || format!("thaw-drawer--placement-{}", placement.get()
|
|
||||||
.as_str()), class.map(| c | move || c.get())
|
|
||||||
]
|
|
||||||
|
|
||||||
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! {
|
||||||
|
<div class="thaw-drawer-container" style=move || style.get()>
|
||||||
|
<CSSTransition
|
||||||
|
node_ref=mask_ref
|
||||||
|
show=show.signal()
|
||||||
|
name="fade-in-transition"
|
||||||
|
let:display
|
||||||
>
|
>
|
||||||
<Card title>{children()}</Card>
|
<div
|
||||||
</div>
|
class="thaw-drawer-mask"
|
||||||
|
style=move || display.get()
|
||||||
|
on:click=move |_| show.set(false)
|
||||||
|
ref=mask_ref
|
||||||
|
></div>
|
||||||
|
</CSSTransition>
|
||||||
|
<CSSTransition
|
||||||
|
node_ref=drawer_ref
|
||||||
|
show=show.signal()
|
||||||
|
name=Memo::new(move |_| {
|
||||||
|
format!("slide-in-from-{}-transition", placement.get().as_str())
|
||||||
|
})
|
||||||
|
on_after_leave
|
||||||
|
let:display
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class=class_list![
|
||||||
|
"thaw-drawer", move || format!("thaw-drawer--placement-{}", placement
|
||||||
|
.get().as_str()), class.map(| c | move || c.get())
|
||||||
|
]
|
||||||
|
|
||||||
|
style=move || display.get()
|
||||||
|
ref=drawer_ref
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
>
|
||||||
|
<Card title>{children()}</Card>
|
||||||
|
</div>
|
||||||
|
</CSSTransition>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match mount {
|
match mount {
|
||||||
DrawerMount::None => view! {
|
DrawerMount::None => view! { <DrawerInnr show title placement class style children/> },
|
||||||
<DrawerInnr show title placement z_index class css_vars children />
|
|
||||||
},
|
|
||||||
DrawerMount::Body => view! {
|
DrawerMount::Body => view! {
|
||||||
<Teleport>
|
<Teleport>
|
||||||
<DrawerInnr show title placement z_index class css_vars children />
|
<DrawerInnr show title placement class style children/>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -76,6 +112,8 @@ pub enum DrawerPlacement {
|
||||||
Right,
|
Right,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Copy for DrawerPlacement {}
|
||||||
|
|
||||||
impl DrawerPlacement {
|
impl DrawerPlacement {
|
||||||
pub fn as_str(&self) -> &'static str {
|
pub fn as_str(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
@ -8,6 +8,7 @@ mod optional_prop;
|
||||||
mod signal;
|
mod signal;
|
||||||
mod stored_maybe_signal;
|
mod stored_maybe_signal;
|
||||||
mod time;
|
mod time;
|
||||||
|
mod use_lock_html_scroll;
|
||||||
|
|
||||||
// pub use callback::AsyncCallback;
|
// pub use callback::AsyncCallback;
|
||||||
pub use component_ref::{create_component_ref, ComponentRef};
|
pub use component_ref::{create_component_ref, ComponentRef};
|
||||||
|
@ -18,6 +19,7 @@ pub(crate) use optional_prop::OptionalProp;
|
||||||
pub use signal::SignalWatch;
|
pub use signal::SignalWatch;
|
||||||
pub(crate) use stored_maybe_signal::*;
|
pub(crate) use stored_maybe_signal::*;
|
||||||
pub(crate) use time::*;
|
pub(crate) use time::*;
|
||||||
|
pub(crate) use use_lock_html_scroll::*;
|
||||||
|
|
||||||
pub(crate) fn with_hydration_off<T>(f: impl FnOnce() -> T) -> T {
|
pub(crate) fn with_hydration_off<T>(f: impl FnOnce() -> T) -> T {
|
||||||
#[cfg(feature = "hydrate")]
|
#[cfg(feature = "hydrate")]
|
||||||
|
|
39
thaw/src/utils/use_lock_html_scroll.rs
Normal file
39
thaw/src/utils/use_lock_html_scroll.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
use leptos::MaybeSignal;
|
||||||
|
|
||||||
|
pub fn use_lock_html_scroll(is_lock: MaybeSignal<bool>) {
|
||||||
|
#[cfg(any(feature = "csr", feature = "hydrate"))]
|
||||||
|
{
|
||||||
|
use leptos::{create_render_effect, document, on_cleanup, SignalGet, StoredValue};
|
||||||
|
let style_el = StoredValue::new(None::<web_sys::Element>);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue