- 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.
85 lines
3.0 KiB
TypeScript
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>
|
|
);
|
|
}
|