feat: integrate shadcn/ui, add Button component, and refactor App UI
This commit is contained in:
@@ -1,2 +1,4 @@
|
||||
pub mod context_menu;
|
||||
pub mod modal;
|
||||
pub mod context_menu;
|
||||
pub mod ui;
|
||||
|
||||
|
||||
@@ -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(()))
|
||||
|
||||
62
frontend/src/components/ui/button.rs
Normal file
62
frontend/src/components/ui/button.rs
Normal 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>
|
||||
}
|
||||
}
|
||||
1
frontend/src/components/ui/mod.rs
Normal file
1
frontend/src/components/ui/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod button;
|
||||
Reference in New Issue
Block a user