Files
natreex d4765e6576 Add foundational components and logic for migration, UI design, and input handling
- Introduced foundational UI components (`Badge`, `LockCard`, `SectionHeader`, `AvatarIcon`, etc.) for flexible layouts and consistent design.
- Added migration support with the `MigrationModal` component and backend integration for exporting/importing data between Electron and Tauri.
- Extended form components with `TextAreaInput`, `OrderInput`, `ToggleField`, and `ToolbarSelect` for improved input handling.
- Updated `ScribeShell` with migration popup logic to prompt users for data migration.
- Integrated `AlertStack` for better alert handling and notification management.
- Enhanced Rust/Tauri services with migration command implementations.
- Added translations and styles for new components.
2026-04-05 12:52:54 -04:00

85 lines
3.0 KiB
TypeScript

import React, {ReactNode} from "react";
import {Loader2, LucideIcon} from "lucide-react";
type ButtonVariant = 'primary' | 'secondary' | 'danger' | 'ghost' | 'warning' | 'info' | 'success' | 'dashed';
type ButtonSize = 'sm' | 'md' | 'lg';
interface ButtonProps {
variant?: ButtonVariant;
size?: ButtonSize;
icon?: LucideIcon;
isLoading?: boolean;
loadingText?: string;
disabled?: boolean;
children: ReactNode;
onClick?: () => void | Promise<void>;
type?: 'button' | 'submit';
fullWidth?: boolean;
}
const variantClasses: Record<ButtonVariant, string> = {
primary: 'bg-primary-dark text-text-primary hover:bg-primary',
secondary: 'bg-secondary text-text-primary border border-secondary hover:bg-gray-dark hover:border-gray-dark',
danger: 'bg-error text-text-primary hover:bg-error/80',
warning: 'bg-warning text-text-primary hover:bg-warning/80',
info: 'bg-info text-text-primary hover:bg-info/80',
success: 'bg-success text-text-primary hover:bg-success/80',
ghost: 'text-muted hover:text-primary hover:bg-primary/10',
dashed: 'border-2 border-dashed border-secondary bg-dark-background hover:bg-tertiary text-text-primary',
};
const sizeClasses: Record<ButtonSize, string> = {
sm: 'px-3 py-1.5 text-xs rounded-lg gap-1.5',
md: 'px-5 py-2.5 text-sm rounded-xl gap-2',
lg: 'px-6 py-3 text-base rounded-xl gap-2',
};
export default function Button(
{
variant = 'primary',
size = 'md',
icon: Icon,
isLoading = false,
loadingText,
disabled = false,
children,
onClick,
type = 'button',
fullWidth = false,
}: ButtonProps) {
const isDisabled: boolean = disabled || isLoading;
return (
<button
type={type}
onClick={onClick}
disabled={isDisabled}
className={`
font-semibold transition-all duration-200
flex items-center justify-center relative overflow-hidden
${variantClasses[variant]}
${sizeClasses[size]}
${isDisabled ? 'opacity-50 cursor-not-allowed' : ''}
${fullWidth ? 'w-full' : ''}
`}
>
<span
className={`flex items-center gap-2 transition-opacity duration-200 ${isLoading ? 'opacity-0' : 'opacity-100'}`}>
{Icon && (
<Icon className="w-4 h-4" strokeWidth={1.75}/>
)}
{children}
</span>
{isLoading && (
<div className="absolute inset-0 flex items-center justify-center">
<Loader2 className="w-4 h-4 animate-spin" strokeWidth={1.75}/>
{loadingText && (
<span className="ml-2 text-sm font-medium">{loadingText}</span>
)}
</div>
)}
</button>
);
}