Files
xiaoxia-saas/apps/web/src/components/asset-selector/hooks/useAssetPreview.ts
T
xiaoxia 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
refactor(asset-selector): 三阶段重构 AssetSelector(504→168行, -67%)
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 导出类型
2026-07-28 00:37:37 +08:00

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