mirror of
https://github.com/adoyle0/thaw.git
synced 2025-02-02 08:34:15 -05:00
feat: button add wave
This commit is contained in:
parent
4597b823e7
commit
ec551cd2e6
7 changed files with 118 additions and 55 deletions
|
@ -5,7 +5,7 @@
|
||||||
color: var(--font-color);
|
color: var(--font-color);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
position: relative;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
@ -54,3 +54,34 @@
|
||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.melt-button .melt-wave {
|
||||||
|
pointer-events: none;
|
||||||
|
animation-iteration-count: 1;
|
||||||
|
animation-duration: 0.6s;
|
||||||
|
animation-timing-function: cubic-bezier(0, 0, 0.2, 1),
|
||||||
|
cubic-bezier(0, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.melt-button .melt-wave.melt-wave--active {
|
||||||
|
z-index: 1;
|
||||||
|
animation-name: meltButtonWaveSpread, meltButtonWaveOpacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes meltButtonWaveSpread {
|
||||||
|
from {
|
||||||
|
box-shadow: 0 0 0.5px 0 var(--melt-ripple-color);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
box-shadow: 0 0 0.5px 6px var(--melt-ripple-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes meltButtonWaveOpacity {
|
||||||
|
from {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
mod theme;
|
mod theme;
|
||||||
|
|
||||||
use crate::{components::*, icon::*, theme::*, utils::mount_style::mount_style};
|
use crate::{
|
||||||
|
components::*,
|
||||||
|
icon::*,
|
||||||
|
theme::*,
|
||||||
|
utils::{mount_style::mount_style, ComponentRef},
|
||||||
|
wave::{Wave, WaveRef},
|
||||||
|
};
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
pub use theme::ButtonTheme;
|
pub use theme::ButtonTheme;
|
||||||
|
|
||||||
|
@ -76,6 +82,7 @@ pub fn Button(
|
||||||
css_vars.push_str("--font-color: #fff;");
|
css_vars.push_str("--font-color: #fff;");
|
||||||
css_vars.push_str(&format!("--border-color: {bg_color};"));
|
css_vars.push_str(&format!("--border-color: {bg_color};"));
|
||||||
css_vars.push_str(&format!("--border-color-hover: {bg_color};"));
|
css_vars.push_str(&format!("--border-color-hover: {bg_color};"));
|
||||||
|
css_vars.push_str(&format!("--melt-ripple-color: {bg_color};"));
|
||||||
} else if variant.get() == ButtonVariant::Text {
|
} else if variant.get() == ButtonVariant::Text {
|
||||||
css_vars.push_str(&format!("--font-color-hover: {bg_color};"));
|
css_vars.push_str(&format!("--font-color-hover: {bg_color};"));
|
||||||
css_vars.push_str(&format!(
|
css_vars.push_str(&format!(
|
||||||
|
@ -86,10 +93,12 @@ pub fn Button(
|
||||||
"--background-color-active: {};",
|
"--background-color-active: {};",
|
||||||
theme.button.color_text_active
|
theme.button.color_text_active
|
||||||
));
|
));
|
||||||
|
css_vars.push_str(&format!("--melt-ripple-color: #0000;"));
|
||||||
} else {
|
} else {
|
||||||
css_vars.push_str(&format!("--font-color-hover: {bg_color};"));
|
css_vars.push_str(&format!("--font-color-hover: {bg_color};"));
|
||||||
css_vars.push_str("--border-color: #555a;");
|
css_vars.push_str("--border-color: #555a;");
|
||||||
css_vars.push_str("--border-color-hover: #555;");
|
css_vars.push_str("--border-color-hover: #555;");
|
||||||
|
css_vars.push_str(&format!("--melt-ripple-color: #0000;"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -111,10 +120,15 @@ pub fn Button(
|
||||||
disabled.get()
|
disabled.get()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let wave_ref = ComponentRef::<WaveRef>::default();
|
||||||
|
|
||||||
let on_click = move |event| {
|
let on_click = move |event| {
|
||||||
if disabled.get() {
|
if disabled.get() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if let Some(wave_ref) = wave_ref.get_untracked() {
|
||||||
|
wave_ref.play();
|
||||||
|
}
|
||||||
let Some(callback) = on_click.as_ref() else {
|
let Some(callback) = on_click.as_ref() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@ -131,6 +145,7 @@ pub fn Button(
|
||||||
style=move || format!("{}{}", css_vars.get(), style.get())
|
style=move || format!("{}{}", css_vars.get(), style.get())
|
||||||
on:click=on_click
|
on:click=on_click
|
||||||
>
|
>
|
||||||
|
<Wave comp_ref=wave_ref/>
|
||||||
{move || {
|
{move || {
|
||||||
if loading.get() {
|
if loading.get() {
|
||||||
view! {
|
view! {
|
||||||
|
|
|
@ -69,4 +69,3 @@ pub use tag::*;
|
||||||
pub use theme::*;
|
pub use theme::*;
|
||||||
pub use upload::*;
|
pub use upload::*;
|
||||||
pub use utils::{mount_style::mount_style, signal::SignalWatch};
|
pub use utils::{mount_style::mount_style, signal::SignalWatch};
|
||||||
pub use wave::*;
|
|
||||||
|
|
30
src/utils/component_ref.rs
Normal file
30
src/utils/component_ref.rs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
use leptos::{create_rw_signal, RwSignal, SignalGetUntracked, SignalSet};
|
||||||
|
|
||||||
|
pub struct ComponentRef<T: 'static>(RwSignal<Option<T>>);
|
||||||
|
|
||||||
|
impl<T> Default for ComponentRef<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(create_rw_signal(None))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Clone for ComponentRef<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static> Copy for ComponentRef<T> {}
|
||||||
|
|
||||||
|
impl<T> ComponentRef<T> {
|
||||||
|
pub fn get_untracked(&self) -> Option<T>
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
self.0.get_untracked()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load(&self, comp_ref: T) {
|
||||||
|
self.0.set(Some(comp_ref));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,10 @@
|
||||||
// mod callback;
|
// mod callback;
|
||||||
|
mod component_ref;
|
||||||
pub mod maybe_rw_signal;
|
pub mod maybe_rw_signal;
|
||||||
mod maybe_signal_store;
|
mod maybe_signal_store;
|
||||||
pub mod mount_style;
|
pub mod mount_style;
|
||||||
pub mod signal;
|
pub mod signal;
|
||||||
|
|
||||||
// pub use callback::AsyncCallback;
|
// pub use callback::AsyncCallback;
|
||||||
|
pub use component_ref::ComponentRef;
|
||||||
pub use maybe_signal_store::*;
|
pub use maybe_signal_store::*;
|
||||||
|
|
|
@ -1,28 +1,44 @@
|
||||||
use crate::utils::mount_style::mount_style;
|
use crate::utils::{mount_style::mount_style, ComponentRef};
|
||||||
use leptos::*;
|
use leptos::{leptos_dom::helpers::TimeoutHandle, *};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct WaveRef {
|
||||||
|
play: Callback<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WaveRef {
|
||||||
|
pub fn play(&self) {
|
||||||
|
self.play.call(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Wave(children: Children) -> impl IntoView {
|
pub fn Wave(#[prop(optional)] comp_ref: ComponentRef<WaveRef>) -> impl IntoView {
|
||||||
mount_style("wave", include_str!("./wave.css"));
|
mount_style("wave", include_str!("./wave.css"));
|
||||||
let (css_vars, set_css_vars) = create_signal(String::new());
|
|
||||||
let wave_ref = create_node_ref::<html::Div>();
|
let wave_ref = create_node_ref::<html::Div>();
|
||||||
wave_ref.on_load(move |wave| {
|
let animation_timeout_handle = create_rw_signal(None::<TimeoutHandle>);
|
||||||
_ = wave.on(ev::mousedown, move |ev| {
|
let play = Callback::new(move |_: ()| {
|
||||||
wave_ref.on_load(move |wave| {
|
if let Some(handle) = animation_timeout_handle.get() {
|
||||||
let rect = wave.get_bounding_client_rect();
|
handle.clear();
|
||||||
let client_x = f64::from(ev.client_x());
|
animation_timeout_handle.set(None);
|
||||||
let client_y = f64::from(ev.client_y());
|
}
|
||||||
set_css_vars.set(format!(
|
if let Some(wave_ref) = wave_ref.get() {
|
||||||
"--x: {}px; --y: {}px",
|
_ = wave_ref.offset_height();
|
||||||
client_x - rect.left(),
|
}
|
||||||
client_y - rect.top()
|
let handle = set_timeout_with_handle(
|
||||||
));
|
move || {
|
||||||
})
|
animation_timeout_handle.set(None);
|
||||||
});
|
},
|
||||||
|
Duration::from_secs(1),
|
||||||
|
);
|
||||||
|
if let Ok(handle) = handle {
|
||||||
|
animation_timeout_handle.set(Some(handle))
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
comp_ref.load(WaveRef { play });
|
||||||
view! {
|
view! {
|
||||||
<div class="melt-wave" ref=wave_ref style=move || css_vars.get()>
|
<div class="melt-wave" class=("melt-wave--active", move || animation_timeout_handle.with(|handle| handle.is_some())) ref=wave_ref>
|
||||||
{children()}
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +1,8 @@
|
||||||
.melt-wave {
|
.melt-wave {
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.melt-wave::before {
|
|
||||||
content: "";
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
left: 0;
|
left: 0;
|
||||||
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
transition: 0.2s;
|
bottom: 0;
|
||||||
background: #fff;
|
border-radius: inherit;
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
.melt-wave:active::before {
|
|
||||||
opacity: 0.2;
|
|
||||||
}
|
|
||||||
.melt-wave::after {
|
|
||||||
content: "";
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
width: 200%;
|
|
||||||
height: 100%;
|
|
||||||
left: var(--x, 0);
|
|
||||||
top: var(--y, 0);
|
|
||||||
background-image: radial-gradient(circle, #fff 10%, transparent 10.01%);
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: 50%;
|
|
||||||
transform: translate(-50%, -50%) scale(10);
|
|
||||||
opacity: 0;
|
|
||||||
transition: transform 0.8s, opacity 0.8s;
|
|
||||||
}
|
|
||||||
.melt-wave:active::after {
|
|
||||||
transform: translate(-50%, -50%) scale(0);
|
|
||||||
opacity: 0.3;
|
|
||||||
transition: 0s;
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue