mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat: add ThumbnailStrip filmstrip component
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
import { useRef, useEffect } from "react";
|
||||
import { CheckCircle2, XCircle } from "lucide-react";
|
||||
import type { FileEntry } from "@/stores/file-store";
|
||||
|
||||
interface ThumbnailStripProps {
|
||||
entries: FileEntry[];
|
||||
selectedIndex: number;
|
||||
onSelect: (index: number) => void;
|
||||
}
|
||||
|
||||
export function ThumbnailStrip({ entries, selectedIndex, onSelect }: ThumbnailStripProps) {
|
||||
const selectedRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
selectedRef.current?.scrollIntoView({
|
||||
block: "nearest",
|
||||
inline: "nearest",
|
||||
behavior: "smooth",
|
||||
});
|
||||
}, [selectedIndex]);
|
||||
|
||||
if (entries.length <= 1) return null;
|
||||
|
||||
return (
|
||||
<div className="flex gap-1.5 px-3 py-2 overflow-x-auto border-t border-border bg-muted/30" style={{ scrollBehavior: "smooth" }}>
|
||||
{entries.map((entry, i) => {
|
||||
const isSelected = i === selectedIndex;
|
||||
const isCompleted = entry.status === "completed";
|
||||
const isFailed = entry.status === "failed";
|
||||
return (
|
||||
<button
|
||||
key={`${entry.file.name}-${i}`}
|
||||
ref={isSelected ? selectedRef : undefined}
|
||||
onClick={() => onSelect(i)}
|
||||
className={`relative shrink-0 rounded overflow-hidden transition-all ${
|
||||
isSelected ? "outline outline-2 outline-primary outline-offset-1" : "hover:outline hover:outline-1 hover:outline-border"
|
||||
}`}
|
||||
style={{ width: 52, height: 38 }}
|
||||
title={entry.file.name}
|
||||
>
|
||||
<img src={entry.processedUrl ?? entry.blobUrl} alt={entry.file.name} className="w-full h-full object-cover" draggable={false} />
|
||||
{isCompleted && (
|
||||
<div className="absolute -top-0.5 -right-0.5 w-3.5 h-3.5 bg-green-500 rounded-full flex items-center justify-center">
|
||||
<CheckCircle2 className="h-2.5 w-2.5 text-white" />
|
||||
</div>
|
||||
)}
|
||||
{isFailed && (
|
||||
<div className="absolute -top-0.5 -right-0.5 w-3.5 h-3.5 bg-red-500 rounded-full flex items-center justify-center">
|
||||
<XCircle className="h-2.5 w-2.5 text-white" />
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user