04e8586f91
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 42s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 1m9s
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
PR Automation / Auto Approve on CI Green (pull_request) Successful in 1m22s
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 1m24s
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 2m16s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 2m21s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 2m21s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 2m52s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 3m27s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 3m27s
AI Code Review / AI Code Review (pull_request) Failing after 4m21s
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 7m14s
CI/CD Pipeline / CI Gate (pull_request) Successful in 11s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 22m13s
Phase 1: 抽 types/constants/utils - types/index.ts — AssetSelectorProps, ViewMode - constants.ts — TYPE_OPTIONS - utils/index.ts — formatSize, formatDuration, getQualityLevel, getQualityColor Phase 2: 拆 6 个 UI 组件 - SelectorToolbar — 搜索 + 类型/质量筛选 + 视图切换 - SelectorBatchBar — 批量操作栏 - AssetCard — 网格卡片(缩略图/角标/checkbox) - AssetListItem — 列表项 - PreviewOverlay — 悬浮预览浮层 - EmptyState — 空状态 Phase 3: 抽 4 个业务 Hook - useAssetFilter — 搜索 + 类型/质量分筛选 + 视图切换 - useAssetSelection — 单选/多选/Shift 连选 - useDragReorder — HTML5 DnD 拖拽排序 + 批量数据携带 - useAssetPreview — 悬浮延迟预览 同步更新 index.ts 导出类型
55 lines
1.5 KiB
TypeScript
55 lines
1.5 KiB
TypeScript
import { useState, useRef, useCallback, useEffect } from "react"
|
|
import type { MediaAsset } from "@/api/template-editor"
|
|
|
|
interface UseAssetPreviewReturn {
|
|
previewAsset: MediaAsset | null
|
|
previewPos: { x: number; y: number }
|
|
handleMouseEnter: (asset: MediaAsset, e: React.MouseEvent) => void
|
|
handleMouseLeave: () => void
|
|
}
|
|
|
|
/**
|
|
* 悬浮预览 Hook —— 延迟 400ms 显示预览浮层
|
|
*/
|
|
const useAssetPreview = (): UseAssetPreviewReturn => {
|
|
const [previewAsset, setPreviewAsset] = useState<MediaAsset | null>(null)
|
|
const [previewPos, setPreviewPos] = useState({ x: 0, y: 0 })
|
|
const previewTimer = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
|
|
const handleMouseEnter = useCallback((asset: MediaAsset, e: React.MouseEvent) => {
|
|
if (previewTimer.current) clearTimeout(previewTimer.current)
|
|
previewTimer.current = setTimeout(() => {
|
|
const rect = (e.currentTarget as HTMLElement).getBoundingClientRect()
|
|
setPreviewAsset(asset)
|
|
setPreviewPos({
|
|
x: rect.right + 12,
|
|
y: Math.max(8, rect.top - 20),
|
|
})
|
|
}, 400)
|
|
}, [])
|
|
|
|
const handleMouseLeave = useCallback(() => {
|
|
if (previewTimer.current) {
|
|
clearTimeout(previewTimer.current)
|
|
previewTimer.current = null
|
|
}
|
|
setPreviewAsset(null)
|
|
}, [])
|
|
|
|
// 清理定时器
|
|
useEffect(() => {
|
|
return () => {
|
|
if (previewTimer.current) clearTimeout(previewTimer.current)
|
|
}
|
|
}, [])
|
|
|
|
return {
|
|
previewAsset,
|
|
previewPos,
|
|
handleMouseEnter,
|
|
handleMouseLeave,
|
|
}
|
|
}
|
|
|
|
export default useAssetPreview
|