Files
kit-ui/components/qrcode/QROptions.tsx

149 lines
5.2 KiB
TypeScript
Raw Normal View History

'use client';
import { Slider } from '@/components/ui/slider';
import { cn } from '@/lib/utils/cn';
import type { ErrorCorrectionLevel } from '@/types/qrcode';
interface QROptionsProps {
errorCorrection: ErrorCorrectionLevel;
foregroundColor: string;
backgroundColor: string;
margin: number;
onErrorCorrectionChange: (ec: ErrorCorrectionLevel) => void;
onForegroundColorChange: (color: string) => void;
onBackgroundColorChange: (color: string) => void;
onMarginChange: (margin: number) => void;
}
const EC_OPTIONS: { value: ErrorCorrectionLevel; label: string; desc: string }[] = [
{ value: 'L', label: 'L', desc: '7%' },
{ value: 'M', label: 'M', desc: '15%' },
{ value: 'Q', label: 'Q', desc: '25%' },
{ value: 'H', label: 'H', desc: '30%' },
];
const inputCls =
'w-full bg-transparent border border-border/40 rounded-lg px-3 py-1.5 text-xs font-mono outline-none focus:border-primary/50 transition-colors text-foreground/80 placeholder:text-muted-foreground/30';
export function QROptions({
errorCorrection,
foregroundColor,
backgroundColor,
margin,
onErrorCorrectionChange,
onForegroundColorChange,
onBackgroundColorChange,
onMarginChange,
}: QROptionsProps) {
const isTransparent = backgroundColor === '#00000000';
return (
<div className="space-y-5">
{/* Error Correction */}
<div>
<span className="text-[10px] font-semibold text-muted-foreground uppercase tracking-widest block mb-2">
Error Correction
</span>
<div className="flex gap-1.5">
{EC_OPTIONS.map((opt) => (
<button
key={opt.value}
onClick={() => onErrorCorrectionChange(opt.value)}
className={cn(
'flex-1 flex flex-col items-center py-2 rounded-lg border text-xs font-mono transition-all',
errorCorrection === opt.value
? 'bg-primary/10 border-primary/40 text-primary'
: 'border-border/30 text-muted-foreground hover:border-primary/30 hover:text-foreground'
)}
>
<span className="font-semibold">{opt.label}</span>
<span className="text-[9px] opacity-50 mt-0.5">{opt.desc}</span>
</button>
))}
</div>
</div>
{/* Colors */}
<div className="space-y-3">
<span className="text-[10px] font-semibold text-muted-foreground uppercase tracking-widest block">
Colors
</span>
{/* Foreground */}
<div>
<label className="text-[9px] text-muted-foreground/50 font-mono block mb-1.5">Foreground</label>
<div className="flex gap-1.5">
<input
type="color"
value={foregroundColor}
onChange={(e) => onForegroundColorChange(e.target.value)}
className="w-8 h-8 rounded-lg cursor-pointer border border-border/40 bg-transparent shrink-0 p-0.5"
/>
<input
type="text"
value={foregroundColor}
onChange={(e) => onForegroundColorChange(e.target.value)}
className={inputCls}
/>
</div>
</div>
{/* Background */}
<div>
<div className="flex items-center justify-between mb-1.5">
<label className="text-[9px] text-muted-foreground/50 font-mono">Background</label>
<button
onClick={() => onBackgroundColorChange(isTransparent ? '#ffffff' : '#00000000')}
className={cn(
'text-[9px] font-mono px-1.5 py-0.5 rounded border transition-all',
isTransparent
? 'border-primary/40 text-primary bg-primary/10'
: 'border-border/30 text-muted-foreground/50 hover:border-primary/30 hover:text-primary'
)}
>
Transparent
</button>
</div>
<div className="flex gap-1.5">
<input
type="color"
disabled={isTransparent}
value={isTransparent ? '#ffffff' : backgroundColor}
onChange={(e) => onBackgroundColorChange(e.target.value)}
className={cn(
'w-8 h-8 rounded-lg cursor-pointer border border-border/40 bg-transparent shrink-0 p-0.5 transition-opacity',
isTransparent && 'opacity-30 cursor-not-allowed'
)}
/>
<input
type="text"
disabled={isTransparent}
value={isTransparent ? 'transparent' : backgroundColor}
onChange={(e) => onBackgroundColorChange(e.target.value)}
className={cn(inputCls, isTransparent && 'opacity-30')}
/>
</div>
</div>
</div>
{/* Margin */}
<div>
<div className="flex items-center justify-between mb-2">
<span className="text-[10px] font-semibold text-muted-foreground uppercase tracking-widest">
Margin
</span>
<span className="text-[10px] text-muted-foreground/40 font-mono tabular-nums">{margin}</span>
</div>
<Slider
value={[margin]}
onValueChange={([v]) => onMarginChange(v)}
min={0}
max={8}
step={1}
/>
</div>
</div>
);
}