feat: integrate shadcn/ui, add Button component, and refactor App UI

This commit is contained in:
spinline
2026-01-31 13:47:48 +03:00
parent 52fd512554
commit 63128f8501
11 changed files with 928 additions and 702 deletions

View File

@@ -1,2 +1,4 @@
pub mod context_menu;
pub mod modal;
pub mod context_menu;
pub mod ui;

View File

@@ -21,25 +21,26 @@ pub fn Modal(
view! {
<Show when=move || visible.get() fallback=|| ()>
<div class="fixed inset-0 bg-black/80 backdrop-blur-md flex items-end md:items-center justify-center z-[200] animate-in fade-in duration-200 sm:p-4">
<div class="bg-[#16161c] p-6 rounded-t-2xl md:rounded-2xl w-full max-w-sm shadow-2xl border border-white/10 ring-1 ring-white/5 transform transition-all animate-in slide-in-from-bottom-10 md:slide-in-from-bottom-0 md:zoom-in-95">
<h3 class="text-xl font-bold text-white mb-4">{title.get_value()}</h3>
<div class="fixed inset-0 bg-background/80 backdrop-blur-sm flex items-end md:items-center justify-center z-[200] animate-in fade-in duration-200 sm:p-4">
<div class="bg-card p-6 rounded-t-2xl md:rounded-lg w-full max-w-sm shadow-xl border border-border ring-0 transform transition-all animate-in slide-in-from-bottom-10 md:slide-in-from-bottom-0 md:zoom-in-95">
<h3 class="text-lg font-semibold text-card-foreground mb-4">{title.get_value()}</h3>
<div class="text-gray-400 mb-8">
<div class="text-muted-foreground mb-6 text-sm">
{child_view.with_value(|c| c.clone())}
</div>
<div class="flex gap-3">
<div class="flex justify-end gap-3">
<button
class="flex-1 px-4 py-3 bg-white/5 hover:bg-white/10 rounded-xl transition-all font-medium text-white"
class="inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border border-input bg-background hover:bg-accent hover:text-accent-foreground h-10 px-4 py-2"
on:click=move |_| on_cancel.with_value(|cb| cb.call(()))
>
{cancel_text.get_value()}
</button>
<button
class=format!("flex-1 px-4 py-3 rounded-xl transition-all font-bold text-white shadow-lg {}",
if is_danger { "bg-red-500 hover:bg-red-600 shadow-red-500/20" } else { "bg-blue-500 hover:bg-blue-600 shadow-blue-500/20" }
)
class=move || crate::utils::cn(format!("inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 h-10 px-4 py-2 {}",
if is_danger { "bg-destructive text-destructive-foreground hover:bg-destructive/90" }
else { "bg-primary text-primary-foreground hover:bg-primary/90" }
))
on:click=move |_| {
logging::log!("Modal: Confirm clicked");
on_confirm.with_value(|cb| cb.call(()))

View File

@@ -0,0 +1,62 @@
use leptos::*;
use crate::utils::cn;
#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub enum ButtonVariant {
#[default]
Default,
Destructive,
Outline,
Secondary,
Ghost,
Link,
}
#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub enum ButtonSize {
#[default]
Default,
Sm,
Lg,
Icon,
}
#[component]
pub fn Button(
#[prop(into, optional)] variant: ButtonVariant,
#[prop(into, optional)] size: ButtonSize,
#[prop(into, optional)] class: String,
#[prop(into, optional)] on_click: Option<Callback<web_sys::MouseEvent>>,
children: Children,
) -> impl IntoView {
let variant_classes = match variant {
ButtonVariant::Default => "bg-primary text-primary-foreground hover:bg-primary/90",
ButtonVariant::Destructive => "bg-destructive text-destructive-foreground hover:bg-destructive/90",
ButtonVariant::Outline => "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
ButtonVariant::Secondary => "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ButtonVariant::Ghost => "hover:bg-accent hover:text-accent-foreground",
ButtonVariant::Link => "text-primary underline-offset-4 hover:underline",
};
let size_classes = match size {
ButtonSize::Default => "h-10 px-4 py-2",
ButtonSize::Sm => "h-9 rounded-md px-3",
ButtonSize::Lg => "h-11 rounded-md px-8",
ButtonSize::Icon => "h-10 w-10",
};
let base_classes = "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50";
view! {
<button
class=cn(format!("{} {} {} {}", base_classes, variant_classes, size_classes, class))
on:click=move |e| {
if let Some(cb) = on_click {
cb.call(e);
}
}
>
{children()}
</button>
}
}

View File

@@ -0,0 +1 @@
pub mod button;