import type { Document } from "dbtype"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card.tsx"; import TagBadge, { toPrettyTagname } from "@/components/gallery/TagBadge.tsx"; import { Fragment, useLayoutEffect, useRef, useState } from "react"; import { LazyImage } from "./LazyImage.tsx"; import StyledLink from "./StyledLink.tsx"; import React from "react"; import { Skeleton } from "../ui/skeleton.tsx"; import { Palette, Users, Trash2, Clock, LayersIcon } from "lucide-react"; function clipTagsWhenOverflow(tags: string[], limit: number) { let l = 0; for (let i = 0; i < tags.length; i++) { l += tags[i].length; if (l > limit) { return tags.slice(0, i); } l += 1; // for space } return tags; } function GalleryCardImpl({ doc: x }: { doc: Document; }) { const ref = useRef(null); const [clipCharCount, setClipCharCount] = useState(200); const isDeleted = x.deleted_at !== null; const artists = x.tags.filter(x => x.startsWith("artist:")).map(x => x.replace("artist:", "")); const groups = x.tags.filter(x => x.startsWith("group:")).map(x => x.replace("group:", "")); const originalTags = x.tags.filter(x => !x.startsWith("artist:") && !x.startsWith("group:")); const clippedTags = clipTagsWhenOverflow(originalTags, clipCharCount); useLayoutEffect(() => { const listener = () => { if (ref.current) { const { width } = ref.current.getBoundingClientRect(); const canvas = document.createElement("canvas"); const context = canvas.getContext("2d"); if (context) { // 스타일에 맞는 폰트 설정 (Tailwind의 기본 폰트 스타일에 맞게 설정) context.font = getComputedStyle(ref.current).font || "16px sans-serif"; let totalWidth = 0; let charCount = 0; for (const tag of originalTags) { // prefix와 패딩을 고려한 너비 계산 const prettyTag = toPrettyTagname(tag); // prefix가 포함된 태그 이름 const tagWidth = context.measureText(prettyTag).width + 8; // 양쪽 패딩 4px씩 추가 const spaceWidth = context.measureText(" ").width; // 공백 너비 if (totalWidth + tagWidth + spaceWidth > width * 3) { // 3줄 제한 break; } totalWidth += tagWidth + spaceWidth; charCount += tag.length + 1; // 태그 길이 + 공백 } setClipCharCount(charCount); } else { const charWidth = 7; // rough estimate const newClipCharCount = Math.floor(width / charWidth) * 3; setClipCharCount(newClipCharCount); } } }; listener(); window.addEventListener("resize", listener); return () => { window.removeEventListener("resize", listener); }; }, [originalTags]); return {isDeleted ? (
Deleted
) : (
{x.pagenum}
)}
{x.title} {artists.length > 0 && (
{artists.map((x, i) => ( {x} {i + 1 < artists.length && ,} ))}
)} {groups.length > 0 && (
{groups.map((x, i) => ( {x} {i + 1 < groups.length && ,} ))}
)}
{new Date(x.created_at).toLocaleDateString()}
    {clippedTags.map(tag => ( ))} {clippedTags.length < originalTags.length && ( )}
} export function GalleryCardSkeleton({ tagCount = 20 }: { tagCount?: number; }) { return
    {Array.from({ length: tagCount }).map((_, i) => )}
} export const GalleryCard = React.memo(GalleryCardImpl);