fix: preserve result thumbnail aspect ratios
This commit is contained in:
@@ -11,7 +11,9 @@ type PreviewState = {
|
|||||||
|
|
||||||
function parseRatio(aspectRatio?: string) {
|
function parseRatio(aspectRatio?: string) {
|
||||||
if (!aspectRatio || aspectRatio === 'long') return aspectRatio === 'long' ? 1 / 3 : 1;
|
if (!aspectRatio || aspectRatio === 'long') return aspectRatio === 'long' ? 1 / 3 : 1;
|
||||||
const [w, h] = aspectRatio.split(':').map(Number);
|
const [w, h] = aspectRatio.includes('/')
|
||||||
|
? aspectRatio.split('/').map(part => Number(part.trim()))
|
||||||
|
: aspectRatio.split(':').map(Number);
|
||||||
return w && h ? w / h : 1;
|
return w && h ? w / h : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,11 +44,13 @@ export function HoverImagePreview({
|
|||||||
alt,
|
alt,
|
||||||
imageClassName,
|
imageClassName,
|
||||||
aspectRatio,
|
aspectRatio,
|
||||||
|
onImageLoad,
|
||||||
}: {
|
}: {
|
||||||
src: string;
|
src: string;
|
||||||
alt: string;
|
alt: string;
|
||||||
imageClassName?: string;
|
imageClassName?: string;
|
||||||
aspectRatio?: string;
|
aspectRatio?: string;
|
||||||
|
onImageLoad?: (image: HTMLImageElement) => void;
|
||||||
}) {
|
}) {
|
||||||
const [preview, setPreview] = useState<PreviewState | null>(null);
|
const [preview, setPreview] = useState<PreviewState | null>(null);
|
||||||
|
|
||||||
@@ -61,6 +65,7 @@ export function HoverImagePreview({
|
|||||||
setPreview(nextPreviewState(event, aspectRatio));
|
setPreview(nextPreviewState(event, aspectRatio));
|
||||||
}}
|
}}
|
||||||
onPointerLeave={() => setPreview(null)}
|
onPointerLeave={() => setPreview(null)}
|
||||||
|
onLoad={event => onImageLoad?.(event.currentTarget)}
|
||||||
/>
|
/>
|
||||||
{preview && (
|
{preview && (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useEffect } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import type { GenImage } from '@/lib/types';
|
import type { GenImage } from '@/lib/types';
|
||||||
import { HoverImagePreview } from './HoverImagePreview';
|
import { HoverImagePreview } from './HoverImagePreview';
|
||||||
|
|
||||||
@@ -10,6 +10,8 @@ export type ResultGridProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function ResultGrid({ images, onAction }: ResultGridProps) {
|
export default function ResultGrid({ images, onAction }: ResultGridProps) {
|
||||||
|
const [ratios, setRatios] = useState<Record<string, string>>({});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function handler(e: KeyboardEvent) {
|
function handler(e: KeyboardEvent) {
|
||||||
const target = e.target as HTMLElement;
|
const target = e.target as HTMLElement;
|
||||||
@@ -27,7 +29,7 @@ export default function ResultGrid({ images, onAction }: ResultGridProps) {
|
|||||||
return () => window.removeEventListener('keydown', handler);
|
return () => window.removeEventListener('keydown', handler);
|
||||||
}, [images, onAction]);
|
}, [images, onAction]);
|
||||||
|
|
||||||
const cols = images.length <= 4 ? 'grid-cols-2' : images.length <= 9 ? 'grid-cols-3' : 'grid-cols-4';
|
const cols = images.length === 1 ? 'grid-cols-[minmax(220px,520px)]' : images.length <= 4 ? 'grid-cols-2' : images.length <= 9 ? 'grid-cols-3' : 'grid-cols-4';
|
||||||
const selectedCount = images.filter(i => i.status === 'selected').length;
|
const selectedCount = images.filter(i => i.status === 'selected').length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -51,13 +53,24 @@ export default function ResultGrid({ images, onAction }: ResultGridProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={`grid ${cols} gap-3`}>
|
<div className={`grid ${cols} items-start gap-3`}>
|
||||||
{images.map((img, i) => (
|
{images.map((img, i) => (
|
||||||
<div
|
<div
|
||||||
key={img.id}
|
key={img.id}
|
||||||
className={`tile group ${img.status === 'selected' ? 'tile-selected' : ''} ${img.status === 'rejected' ? 'tile-rejected' : ''}`}
|
className={`tile group ${img.status === 'selected' ? 'tile-selected' : ''} ${img.status === 'rejected' ? 'tile-rejected' : ''}`}
|
||||||
|
style={{ aspectRatio: ratios[img.id] ?? '1 / 1' }}
|
||||||
>
|
>
|
||||||
<HoverImagePreview src={img.url} alt={`gen ${i + 1}`} imageClassName="w-full h-full object-contain bg-white" />
|
<HoverImagePreview
|
||||||
|
src={img.url}
|
||||||
|
alt={`gen ${i + 1}`}
|
||||||
|
aspectRatio={ratios[img.id]}
|
||||||
|
imageClassName="w-full h-full object-contain bg-white"
|
||||||
|
onImageLoad={image => {
|
||||||
|
if (!image.naturalWidth || !image.naturalHeight) return;
|
||||||
|
const next = `${image.naturalWidth} / ${image.naturalHeight}`;
|
||||||
|
setRatios(prev => prev[img.id] === next ? prev : { ...prev, [img.id]: next });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<div className="tile-keynum">{i + 1}</div>
|
<div className="tile-keynum">{i + 1}</div>
|
||||||
|
|
||||||
{img.status === 'selected' && (
|
{img.status === 'selected' && (
|
||||||
|
|||||||
Reference in New Issue
Block a user