fix: refine reference frame previews

This commit is contained in:
2026-05-19 19:31:45 +08:00
parent b9bf50f851
commit a5979bb0d7
4 changed files with 41 additions and 14 deletions

View File

@@ -14,6 +14,8 @@ type MediaAssetAction = {
tone?: "neutral" | "cyan" | "rose"
}
type MediaAssetPreviewPlacement = "auto" | "left" | "right"
type MediaAssetTileProps = {
kind?: "image" | "video"
src?: string
@@ -29,6 +31,8 @@ type MediaAssetTileProps = {
objectFit?: "contain" | "cover"
previewObjectFit?: "contain" | "cover"
previewClassName?: string
previewPlacement?: MediaAssetPreviewPlacement
previewMaxWidth?: number
selected?: boolean
disabled?: boolean
busy?: boolean
@@ -55,15 +59,21 @@ function mediaObjectClass(fit: "contain" | "cover") {
return fit === "cover" ? "object-cover" : "object-contain"
}
function previewPosition(event: ReactMouseEvent<HTMLElement>) {
function previewPosition(event: ReactMouseEvent<HTMLElement>, placement: MediaAssetPreviewPlacement, maxWidth: number) {
const margin = 16
const previewWidth = Math.min(520, window.innerWidth - margin * 2)
const previewWidth = Math.min(maxWidth, window.innerWidth - margin * 2)
const previewHeight = Math.min(760, window.innerHeight - margin * 2)
let left = event.clientX + 18
let left = placement === "left" ? event.clientX - previewWidth - 18 : event.clientX + 18
let top = event.clientY + 18
if (left + previewWidth > window.innerWidth - margin) left = event.clientX - previewWidth - 18
if (placement === "auto" && left + previewWidth > window.innerWidth - margin) left = event.clientX - previewWidth - 18
if (placement === "right" && left + previewWidth > window.innerWidth - margin) left = window.innerWidth - previewWidth - margin
if (placement === "left" && left < margin) left = margin
if (top + previewHeight > window.innerHeight - margin) top = window.innerHeight - previewHeight - margin
return { left: Math.max(margin, left), top: Math.max(margin, top) }
return {
left: Math.max(margin, Math.min(left, window.innerWidth - previewWidth - margin)),
top: Math.max(margin, top),
width: previewWidth,
}
}
export function MediaAssetTile({
@@ -81,6 +91,8 @@ export function MediaAssetTile({
objectFit = "contain",
previewObjectFit,
previewClassName = "",
previewPlacement = "auto",
previewMaxWidth = 520,
selected = false,
disabled = false,
busy = false,
@@ -96,7 +108,7 @@ export function MediaAssetTile({
actions = [],
disablePreview = false,
}: MediaAssetTileProps) {
const [position, setPosition] = useState<{ left: number; top: number } | null>(null)
const [position, setPosition] = useState<{ left: number; top: number; width: number } | null>(null)
const mediaSrc = src || poster || ""
const canPreview = !!mediaSrc && !disablePreview
const fit = mediaObjectClass(objectFit)
@@ -104,7 +116,7 @@ export function MediaAssetTile({
const updatePreview = (event: ReactMouseEvent<HTMLElement>) => {
if (!canPreview) return
setPosition(previewPosition(event))
setPosition(previewPosition(event, previewPlacement, previewMaxWidth))
}
const media = kind === "video" && src ? (
@@ -136,7 +148,7 @@ export function MediaAssetTile({
? createPortal(
<div
className={`pointer-events-none fixed z-[10000] w-[min(520px,calc(100vw-32px))] rounded-xl border border-white/15 bg-black/94 p-3 shadow-[0_28px_80px_rgba(0,0,0,0.72)] ${previewClassName}`}
style={{ left: position.left, top: position.top }}
style={{ left: position.left, top: position.top, width: position.width }}
>
<div className="flex max-h-[min(76vh,720px)] items-center justify-center overflow-hidden rounded-lg bg-black">
{kind === "video" && src ? (