diff --git a/src/components/HoverImagePreview.tsx b/src/components/HoverImagePreview.tsx index a70f7fc..e0ba875 100644 --- a/src/components/HoverImagePreview.tsx +++ b/src/components/HoverImagePreview.tsx @@ -1,12 +1,14 @@ 'use client'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; +import { createPortal } from 'react-dom'; import type { PointerEvent } from 'react'; type PreviewState = { left: number; top: number; width: number; + height: number; }; function parseRatio(aspectRatio?: string) { @@ -18,17 +20,33 @@ function parseRatio(aspectRatio?: string) { } function nextPreviewState(event: PointerEvent, aspectRatio?: string): PreviewState { - const gap = 18; - const margin = 12; + const gap = 12; + const margin = 10; const ratio = parseRatio(aspectRatio); - const maxWidth = ratio < 0.8 ? 380 : ratio > 1.35 ? 620 : 500; - const width = Math.min(maxWidth, Math.max(260, window.innerWidth * 0.38)); - const height = Math.min(window.innerHeight * 0.82, width / ratio); - let left = event.clientX + gap; - let top = event.clientY + gap; + const maxWidth = ratio < 0.8 ? 340 : ratio > 1.35 ? 520 : 420; + const maxHeight = Math.min(window.innerHeight * 0.68, window.innerHeight - margin * 2); + let width = Math.min(maxWidth, Math.max(240, window.innerWidth * 0.28)); + let height = width / ratio; + + if (height > maxHeight) { + height = maxHeight; + width = height * ratio; + } + + const canOpenRight = event.clientX + gap + width <= window.innerWidth - margin; + const canOpenLeft = event.clientX - gap - width >= margin; + let left = canOpenRight || !canOpenLeft + ? event.clientX + gap + : event.clientX - width - gap; + + const canOpenBelow = event.clientY + gap + height <= window.innerHeight - margin; + const canOpenAbove = event.clientY - gap - height >= margin; + let top = canOpenBelow || !canOpenAbove + ? event.clientY + gap + : event.clientY - height - gap; if (left + width > window.innerWidth - margin) { - left = event.clientX - width - gap; + left = window.innerWidth - width - margin; } if (top + height > window.innerHeight - margin) { top = window.innerHeight - height - margin; @@ -37,6 +55,7 @@ function nextPreviewState(event: PointerEvent, aspectRatio?: string left: Math.max(margin, left), top: Math.max(margin, top), width, + height, }; } @@ -54,6 +73,11 @@ export function HoverImagePreview({ onImageLoad?: (image: HTMLImageElement) => void; }) { const [preview, setPreview] = useState(null); + const [mounted, setMounted] = useState(false); + + useEffect(() => { + setMounted(true); + }, []); return ( <> @@ -68,13 +92,18 @@ export function HoverImagePreview({ onPointerLeave={() => setPreview(null)} onLoad={event => onImageLoad?.(event.currentTarget)} /> - {preview && ( + {preview && mounted && createPortal(
- -
+ + , + document.body )} );