83 lines
2.9 KiB
TypeScript
83 lines
2.9 KiB
TypeScript
|
|
'use client';
|
|||
|
|
|
|||
|
|
import { useLayerStore } from '@/store';
|
|||
|
|
import { Eye, EyeOff, Trash2 } from 'lucide-react';
|
|||
|
|
import { cn } from '@/lib/utils';
|
|||
|
|
|
|||
|
|
export function LayersPanel() {
|
|||
|
|
const { layers, activeLayerId, setActiveLayer, updateLayer, deleteLayer } = useLayerStore();
|
|||
|
|
|
|||
|
|
// Sort layers by order (highest first)
|
|||
|
|
const sortedLayers = [...layers].sort((a, b) => b.order - a.order);
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="flex h-full flex-col bg-card">
|
|||
|
|
<div className="border-b border-border p-3">
|
|||
|
|
<h2 className="text-sm font-semibold text-card-foreground">Layers</h2>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div className="flex-1 overflow-y-auto p-2 space-y-1">
|
|||
|
|
{sortedLayers.length === 0 ? (
|
|||
|
|
<div className="flex h-full items-center justify-center">
|
|||
|
|
<p className="text-sm text-muted-foreground">No layers</p>
|
|||
|
|
</div>
|
|||
|
|
) : (
|
|||
|
|
sortedLayers.map((layer) => (
|
|||
|
|
<div
|
|||
|
|
key={layer.id}
|
|||
|
|
className={cn(
|
|||
|
|
'group flex items-center gap-2 rounded-md border p-2 transition-colors cursor-pointer',
|
|||
|
|
activeLayerId === layer.id
|
|||
|
|
? 'border-primary bg-primary/10'
|
|||
|
|
: 'border-border hover:border-primary/50 hover:bg-accent/50'
|
|||
|
|
)}
|
|||
|
|
onClick={() => setActiveLayer(layer.id)}
|
|||
|
|
>
|
|||
|
|
<button
|
|||
|
|
className="shrink-0 text-muted-foreground hover:text-foreground"
|
|||
|
|
onClick={(e) => {
|
|||
|
|
e.stopPropagation();
|
|||
|
|
updateLayer(layer.id, { visible: !layer.visible });
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
{layer.visible ? (
|
|||
|
|
<Eye className="h-4 w-4" />
|
|||
|
|
) : (
|
|||
|
|
<EyeOff className="h-4 w-4" />
|
|||
|
|
)}
|
|||
|
|
</button>
|
|||
|
|
|
|||
|
|
<div className="flex-1 min-w-0">
|
|||
|
|
<p className="text-sm font-medium text-card-foreground truncate">
|
|||
|
|
{layer.name}
|
|||
|
|
</p>
|
|||
|
|
<p className="text-xs text-muted-foreground">
|
|||
|
|
{layer.width} × {layer.height}
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div className="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
|
|||
|
|
<button
|
|||
|
|
className="shrink-0 text-muted-foreground hover:text-destructive"
|
|||
|
|
onClick={(e) => {
|
|||
|
|
e.stopPropagation();
|
|||
|
|
if (confirm('Delete this layer?')) {
|
|||
|
|
deleteLayer(layer.id);
|
|||
|
|
}
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
<Trash2 className="h-4 w-4" />
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div className="shrink-0 text-xs text-muted-foreground">
|
|||
|
|
{Math.round(layer.opacity * 100)}%
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
))
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
}
|