From 0e075d60924780b2ffd6eb8dbeac5e1a1e46129c Mon Sep 17 00:00:00 2001 From: spinline Date: Fri, 13 Feb 2026 13:39:53 +0300 Subject: [PATCH] feat: implement high-performance CSS-based ButtonAction component --- frontend/src/components/ui/button_action.rs | 65 +++++++++++++++++++++ frontend/src/components/ui/mod.rs | 1 + 2 files changed, 66 insertions(+) create mode 100644 frontend/src/components/ui/button_action.rs diff --git a/frontend/src/components/ui/button_action.rs b/frontend/src/components/ui/button_action.rs new file mode 100644 index 0000000..e7efaf6 --- /dev/null +++ b/frontend/src/components/ui/button_action.rs @@ -0,0 +1,65 @@ +use leptos::prelude::*; +use tailwind_fuse::tw_merge; +use crate::components::ui::button::{Button, ButtonVariant}; + +#[component] +pub fn ButtonAction( + children: Children, + #[prop(into)] on_action: Callback<()>, + #[prop(optional, into)] class: String, + #[prop(default = 1000)] hold_duration: u64, + #[prop(default = ButtonVariant::Default)] variant: ButtonVariant, +) -> impl IntoView { + let is_holding = RwSignal::new(false); + + let on_down = move |_| is_holding.set(true); + let on_up = move |_| is_holding.set(false); + + Effect::new(move |_| { + if is_holding.get() { + leptos::task::spawn_local(async move { + gloo_timers::future::TimeoutFuture::new(hold_duration as u32).await; + if is_holding.get_untracked() { + on_action.run(()); + is_holding.set(false); + } + }); + } + }); + + let merged_class = move || tw_merge!( + "relative overflow-hidden transition-all active:scale-[0.98]", + class.clone() + ); + + view! { + + + } +} diff --git a/frontend/src/components/ui/mod.rs b/frontend/src/components/ui/mod.rs index e8810ef..884bb2e 100644 --- a/frontend/src/components/ui/mod.rs +++ b/frontend/src/components/ui/mod.rs @@ -2,6 +2,7 @@ pub mod accordion; pub mod alert_dialog; pub mod badge; pub mod button; +pub mod button_action; pub mod card; pub mod checkbox; pub mod context_menu;