mirror of
https://github.com/adoyle0/thaw.git
synced 2025-01-22 22:09:22 -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 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! {
|
||||
<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>
|
||||
<Drawer show mount=DrawerMount::None>
|
||||
<Drawer show mount=DrawerMount::None width="50%">
|
||||
"Current position"
|
||||
</Drawer>
|
||||
</div>
|
||||
|
|
|
@ -6,6 +6,7 @@ pub fn CSSTransition<T, CF, IV>(
|
|||
node_ref: NodeRef<T>,
|
||||
#[prop(into)] show: MaybeSignal<bool>,
|
||||
#[prop(into)] name: MaybeSignal<String>,
|
||||
#[prop(optional, into)] on_after_leave: Option<Callback<()>>,
|
||||
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(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<bool>,
|
||||
title: OptionalProp<MaybeSignal<String>>,
|
||||
placement: MaybeSignal<DrawerPlacement>,
|
||||
z_index: MaybeSignal<i16>,
|
||||
class: OptionalProp<MaybeSignal<String>>,
|
||||
css_vars: Memo<String>,
|
||||
style: Memo<String>,
|
||||
children: Children,
|
||||
) -> impl IntoView {
|
||||
view! {
|
||||
<div
|
||||
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())
|
||||
]
|
||||
let mask_ref = NodeRef::<html::Div>::new();
|
||||
let drawer_ref = NodeRef::<html::Div>::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! {
|
||||
<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>
|
||||
}
|
||||
}
|
||||
|
||||
match mount {
|
||||
DrawerMount::None => view! {
|
||||
<DrawerInnr show title placement z_index class css_vars children />
|
||||
},
|
||||
DrawerMount::None => view! { <DrawerInnr show title placement class style children/> },
|
||||
DrawerMount::Body => view! {
|
||||
<Teleport>
|
||||
<DrawerInnr show title placement z_index class css_vars children />
|
||||
<DrawerInnr show title placement class style children/>
|
||||
</Teleport>
|
||||
},
|
||||
}
|
||||
|
@ -76,6 +112,8 @@ pub enum DrawerPlacement {
|
|||
Right,
|
||||
}
|
||||
|
||||
impl Copy for DrawerPlacement {}
|
||||
|
||||
impl DrawerPlacement {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
|
|
|
@ -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<T>(f: impl FnOnce() -> T) -> T {
|
||||
#[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