feat: add templates, history, comparison mode, animations, and empty states
- Add text templates with 16 pre-made options across 4 categories (greeting, tech, fun, seasonal) - Add copy history panel tracking last 10 copied items with restore functionality - Add font comparison mode to view multiple fonts side-by-side (up to 6 fonts) - Add smooth animations: slide-down, slide-up, scale-in, fade-in, pulse, and shimmer - Add loading skeletons for better perceived performance - Add EmptyState component with contextual messages and icons - Add hover effects and transitions throughout the UI - Improve visual feedback with animated badges and shadows 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
124
components/converter/ComparisonMode.tsx
Normal file
124
components/converter/ComparisonMode.tsx
Normal file
@@ -0,0 +1,124 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import { Card } from '@/components/ui/Card';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { EmptyState } from '@/components/ui/EmptyState';
|
||||
import { Copy, X, Download, GitCompare } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils/cn';
|
||||
import type { FigletFont } from '@/types/figlet';
|
||||
|
||||
export interface ComparisonModeProps {
|
||||
text: string;
|
||||
selectedFonts: string[];
|
||||
fontResults: Record<string, string>;
|
||||
onRemoveFont: (fontName: string) => void;
|
||||
onCopyFont: (fontName: string, result: string) => void;
|
||||
onDownloadFont: (fontName: string, result: string) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function ComparisonMode({
|
||||
text,
|
||||
selectedFonts,
|
||||
fontResults,
|
||||
onRemoveFont,
|
||||
onCopyFont,
|
||||
onDownloadFont,
|
||||
className,
|
||||
}: ComparisonModeProps) {
|
||||
return (
|
||||
<div className={cn('space-y-4', className)}>
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-lg font-semibold">Font Comparison</h2>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{selectedFonts.length} font{selectedFonts.length !== 1 ? 's' : ''} selected
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{selectedFonts.length === 0 ? (
|
||||
<Card>
|
||||
<EmptyState
|
||||
icon={GitCompare}
|
||||
title="No fonts selected for comparison"
|
||||
description="Click the + icon next to any font in the font selector to add it to the comparison"
|
||||
className="py-12"
|
||||
/>
|
||||
</Card>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
{selectedFonts.map((fontName, index) => (
|
||||
<Card
|
||||
key={fontName}
|
||||
className="relative scale-in"
|
||||
style={{ animationDelay: `${index * 50}ms` }}
|
||||
>
|
||||
<div className="p-4 space-y-3">
|
||||
{/* Font Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm font-mono font-semibold px-2 py-1 bg-primary/10 text-primary rounded">
|
||||
{fontName}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onCopyFont(fontName, fontResults[fontName] || '')}
|
||||
className="h-8 w-8 p-0"
|
||||
title="Copy to clipboard"
|
||||
>
|
||||
<Copy className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onDownloadFont(fontName, fontResults[fontName] || '')}
|
||||
className="h-8 w-8 p-0"
|
||||
title="Download"
|
||||
>
|
||||
<Download className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onRemoveFont(fontName)}
|
||||
className="h-8 w-8 p-0 text-destructive hover:text-destructive"
|
||||
title="Remove from comparison"
|
||||
>
|
||||
<X className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ASCII Art Preview */}
|
||||
<div className="relative">
|
||||
<pre className="p-4 bg-muted rounded-md overflow-x-auto">
|
||||
<code className="text-xs font-mono whitespace-pre">
|
||||
{fontResults[fontName] || 'Loading...'}
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
{/* Stats */}
|
||||
{fontResults[fontName] && (
|
||||
<div className="flex gap-4 text-xs text-muted-foreground">
|
||||
<span>
|
||||
{fontResults[fontName].split('\n').length} lines
|
||||
</span>
|
||||
<span>
|
||||
{Math.max(
|
||||
...fontResults[fontName].split('\n').map((line) => line.length)
|
||||
)} chars wide
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user