Files
Stirling-PDF/frontend/src/core/components/shared/FileCard.tsx
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

200 lines
5.3 KiB
TypeScript
Raw Normal View History

2025-08-11 09:16:16 +01:00
import { useState } from "react";
2026-04-17 10:50:16 +01:00
import {
Card,
Stack,
Text,
Group,
Badge,
Button,
Box,
ActionIcon,
Tooltip,
} from "@mantine/core";
2025-06-04 19:04:56 +01:00
import { useTranslation } from "react-i18next";
import StorageIcon from "@mui/icons-material/Storage";
2025-06-20 17:51:24 +01:00
import VisibilityIcon from "@mui/icons-material/Visibility";
import EditIcon from "@mui/icons-material/Edit";
2025-06-04 19:04:56 +01:00
import { StirlingFileStub } from "@app/types/fileContext";
import { getFileSize, getFileDate } from "@app/utils/fileUtils";
import { useFileThumbnail } from "@app/hooks/useFileThumbnail";
import DocumentThumbnail from "@app/components/shared/filePreview/DocumentThumbnail";
2025-06-04 19:04:56 +01:00
interface FileCardProps {
file: File;
2025-09-16 15:08:11 +01:00
fileStub?: StirlingFileStub;
2025-06-04 19:04:56 +01:00
onRemove: () => void;
onDoubleClick?: () => void;
2025-06-20 17:51:24 +01:00
onView?: () => void;
onEdit?: () => void;
isSelected?: boolean;
onSelect?: () => void;
2025-08-01 16:08:04 +01:00
isSupported?: boolean; // Whether the file format is supported by the current tool
2025-06-04 19:04:56 +01:00
}
const FileCard = ({
file,
fileStub,
onRemove,
onDoubleClick,
onView,
onEdit,
isSelected,
onSelect,
isSupported = true,
}: FileCardProps) => {
2025-06-04 19:04:56 +01:00
const { t } = useTranslation();
const {
isEncrypted,
thumbnail: thumb,
isGenerating,
} = useFileThumbnail(fileStub);
2025-06-20 17:51:24 +01:00
const [isHovered, setIsHovered] = useState(false);
2025-06-04 19:04:56 +01:00
const isPdf = file.type === "application/pdf";
const isHydrating = isPdf && !isEncrypted && !thumb && !isGenerating;
2026-01-06 11:54:14 -05:00
2025-06-04 19:04:56 +01:00
return (
<Card
shadow="xs"
radius="md"
withBorder
p="xs"
2025-06-20 23:00:26 +01:00
style={{
width: 225,
minWidth: 180,
maxWidth: 260,
2025-08-01 16:08:04 +01:00
cursor: onDoubleClick && isSupported ? "pointer" : undefined,
position: "relative",
2026-04-17 10:50:16 +01:00
border: isSelected
? "2px solid var(--mantine-color-blue-6)"
: undefined,
backgroundColor: isSelected ? "var(--mantine-color-blue-0)" : undefined,
opacity: 1,
2025-06-04 19:04:56 +01:00
}}
onDoubleClick={onDoubleClick}
2025-06-20 17:51:24 +01:00
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
onClick={onSelect}
2025-08-01 16:08:04 +01:00
data-testid="file-card"
2025-10-20 15:07:40 +01:00
data-tour="file-card-checkbox"
2025-06-04 19:04:56 +01:00
>
<Stack gap={6} align="center">
<Box
style={{
border: "2px solid #e0e0e0",
borderRadius: 8,
width: 90,
height: 120,
display: "flex",
alignItems: "center",
justifyContent: "center",
margin: "0 auto",
background: "#fafbfc",
position: "relative",
2025-06-04 19:04:56 +01:00
}}
>
2025-06-20 17:51:24 +01:00
{/* Hover action buttons */}
{isHovered && (onView || onEdit) && (
<div
style={{
position: "absolute",
2025-06-20 17:51:24 +01:00
top: 4,
right: 4,
display: "flex",
2025-06-20 17:51:24 +01:00
gap: 4,
zIndex: 10,
backgroundColor: "rgba(255, 255, 255, 0.9)",
2025-06-20 17:51:24 +01:00
borderRadius: 4,
padding: 2,
2025-06-20 17:51:24 +01:00
}}
onClick={(e) => e.stopPropagation()}
>
{onView && (
<Tooltip label="View in Viewer">
<ActionIcon
size="sm"
variant="subtle"
color="blue"
onClick={(e) => {
e.stopPropagation();
onView();
}}
>
<VisibilityIcon style={{ fontSize: 16 }} />
</ActionIcon>
</Tooltip>
)}
{onEdit && (
<Tooltip label="Open in File Editor">
<ActionIcon
size="sm"
variant="subtle"
color="orange"
onClick={(e) => {
e.stopPropagation();
onEdit();
}}
>
<EditIcon style={{ fontSize: 16 }} />
</ActionIcon>
</Tooltip>
)}
</div>
)}
<DocumentThumbnail
file={file}
thumbnail={thumb || undefined}
isEncrypted={isEncrypted}
isLoading={isGenerating || isHydrating}
iconSize="3rem"
/>
2025-06-04 19:04:56 +01:00
</Box>
2025-06-20 23:00:26 +01:00
2025-06-04 19:04:56 +01:00
<Text fw={500} size="sm" lineClamp={1} ta="center">
{file.name}
</Text>
2025-06-20 23:00:26 +01:00
2025-06-04 19:04:56 +01:00
<Group gap="xs" justify="center">
2025-06-20 23:00:26 +01:00
<Badge color="red" variant="light" size="sm">
2025-06-04 19:04:56 +01:00
{getFileSize(file)}
</Badge>
<Badge color="blue" variant="light" size="sm">
{getFileDate(file)}
</Badge>
2025-09-16 15:08:11 +01:00
{fileStub?.id && (
2026-04-17 10:50:16 +01:00
<Badge
color="green"
variant="light"
size="sm"
leftSection={<StorageIcon style={{ fontSize: 12 }} />}
>
2025-06-04 19:04:56 +01:00
DB
</Badge>
)}
2025-08-01 16:08:04 +01:00
{!isSupported && (
<Badge color="orange" variant="filled" size="sm">
{t("fileManager.unsupported", "Unsupported")}
</Badge>
)}
2025-06-04 19:04:56 +01:00
</Group>
2025-06-20 23:00:26 +01:00
2025-06-04 19:04:56 +01:00
<Button
color="red"
size="xs"
variant="light"
2025-06-20 17:51:24 +01:00
onClick={(e) => {
e.stopPropagation();
onRemove();
}}
2025-06-04 19:04:56 +01:00
mt={4}
>
{t("delete", "Remove")}
</Button>
</Stack>
</Card>
);
};
2025-06-20 23:00:26 +01:00
export default FileCard;