Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot]
2026-05-14 16:42:34 +00:00
parent e8433bab7a
commit 93ffc04eb6
+121
View File
@@ -0,0 +1,121 @@
import { Eye, EyeOff, Lock, Unlock, Copy, Trash2, Box, Layers } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { useModelStore } from '@/stores/useModelStore';
import { toast } from 'sonner';
interface SceneModelListProps {
compact?: boolean;
}
export function SceneModelList({ compact = false }: SceneModelListProps) {
const models = useModelStore((s) => s.models);
const activeId = useModelStore((s) => s.activeModelId);
const setActive = useModelStore((s) => s.setActiveModel);
const removeModel = useModelStore((s) => s.removeModel);
const toggleVisible = useModelStore((s) => s.toggleModelVisible);
const toggleLocked = useModelStore((s) => s.toggleModelLocked);
const duplicate = useModelStore((s) => s.duplicateModel);
const max = useModelStore((s) => s.maxModels);
if (models.length === 0) {
return (
<div className="rounded-lg border border-dashed bg-muted/20 p-3 text-center">
<Layers className="mx-auto mb-1 h-4 w-4 text-muted-foreground" />
<p className="font-mono text-[10px] text-muted-foreground">Nenhuma peça na cena</p>
</div>
);
}
const textSize = compact ? 'text-[9px]' : 'text-[11px]';
const padding = compact ? 'p-1.5' : 'p-2';
return (
<div className="space-y-1">
<div className="flex items-center justify-between">
<div className="flex items-center gap-1.5">
<Layers className="h-3 w-3 text-primary" />
<span className={`font-mono ${textSize} font-semibold uppercase tracking-wider text-muted-foreground`}>
Cena
</span>
</div>
<span className={`font-mono ${textSize} text-muted-foreground`}>
{models.length}/{max}
</span>
</div>
<div className="space-y-1">
{models.map((m) => {
const isActive = m.id === activeId;
return (
<div
key={m.id}
className={`flex items-center gap-1 rounded-md border ${padding} transition-colors cursor-pointer ${
isActive
? 'border-primary bg-primary/10'
: 'border-border bg-muted/30 hover:bg-muted/50'
}`}
onClick={() => setActive(m.id)}
>
<div
className="h-2.5 w-2.5 shrink-0 rounded-full ring-1 ring-foreground/20"
style={{ backgroundColor: m.color }}
title="Cor da peça"
/>
<Box className={`h-3 w-3 shrink-0 ${isActive ? 'text-primary' : 'text-muted-foreground'}`} />
<span className={`flex-1 truncate font-mono ${textSize} ${isActive ? 'text-foreground font-semibold' : 'text-muted-foreground'}`}>
{m.fileName}
</span>
<Button
variant="ghost"
size="icon"
className="h-5 w-5 shrink-0"
onClick={(e) => { e.stopPropagation(); toggleVisible(m.id); }}
title={m.visible ? 'Ocultar' : 'Exibir'}
>
{m.visible ? <Eye className="h-3 w-3" /> : <EyeOff className="h-3 w-3 text-muted-foreground" />}
</Button>
<Button
variant="ghost"
size="icon"
className="h-5 w-5 shrink-0"
onClick={(e) => { e.stopPropagation(); toggleLocked(m.id); }}
title={m.locked ? 'Destravar' : 'Travar'}
>
{m.locked ? <Lock className="h-3 w-3 text-warning" /> : <Unlock className="h-3 w-3 text-muted-foreground" />}
</Button>
<Button
variant="ghost"
size="icon"
className="h-5 w-5 shrink-0"
onClick={(e) => {
e.stopPropagation();
if (models.length >= max) {
toast.error(`Limite de ${max} peças atingido`);
return;
}
duplicate(m.id);
}}
title="Duplicar"
>
<Copy className="h-3 w-3" />
</Button>
<Button
variant="ghost"
size="icon"
className="h-5 w-5 shrink-0"
onClick={(e) => {
e.stopPropagation();
removeModel(m.id);
toast.success(`"${m.fileName}" removido`);
}}
title="Remover"
>
<Trash2 className="h-3 w-3 text-destructive" />
</Button>
</div>
);
})}
</div>
</div>
);
}