Files
Genarrative/src/components/image-editor/useImageCanvasCanvasDropWorkflow.ts
kdletters 53d1283083 拆分图片画布拖拽入画布流程
新增画布拖拽 drop workflow,承接素材库图片和本地文件拖入画布分流

补充拖拽入画布 hook 测试,覆盖遮罩、默认文件夹和无关拖拽不拦截

更新前端拆分文档和 TRACKING 浏览器回归记录
2026-06-17 10:17:07 +08:00

132 lines
3.5 KiB
TypeScript

import {
type DragEvent as ReactDragEvent,
type Dispatch,
type SetStateAction,
useCallback,
useMemo,
} from 'react';
import {
ASSET_DRAG_MIME_TYPE,
getDraggedAssetId,
hasDataTransferType,
} from './ImageCanvasEditorModel';
import type { EditorAsset, EditorAssetFolder } from './ImageCanvasEditorTypes';
type UploadFilesToCanvasOptions = {
folderId?: string;
canvasPoint: { x: number; y: number };
addToCanvas: true;
};
type UseImageCanvasCanvasDropWorkflowOptions = {
assets: EditorAsset[];
assetFolders: EditorAssetFolder[];
setUploadDropTarget: Dispatch<SetStateAction<'canvas' | 'assets' | null>>;
updateAssetMoveDropFolder: (folderId: string | null) => void;
getCanvasDropPoint: (clientX: number, clientY: number) => {
x: number;
y: number;
};
addAssetLayer: (
asset: EditorAsset,
position?: { x: number; y: number },
) => void;
addUploadedFiles: (
files: FileList | File[],
options: UploadFilesToCanvasOptions,
) => void;
};
export function useImageCanvasCanvasDropWorkflow({
assets,
assetFolders,
setUploadDropTarget,
updateAssetMoveDropFolder,
getCanvasDropPoint,
addAssetLayer,
addUploadedFiles,
}: UseImageCanvasCanvasDropWorkflowOptions) {
const handleCanvasDragOver = useCallback(
(event: ReactDragEvent<HTMLDivElement>) => {
if (hasDataTransferType(event.dataTransfer, ASSET_DRAG_MIME_TYPE)) {
event.preventDefault();
setUploadDropTarget('canvas');
event.dataTransfer.dropEffect = 'copy';
return;
}
if (hasDataTransferType(event.dataTransfer, 'Files')) {
event.preventDefault();
setUploadDropTarget('canvas');
event.dataTransfer.dropEffect = 'copy';
}
},
[setUploadDropTarget],
);
const handleCanvasDragLeave = useCallback(
(event: ReactDragEvent<HTMLDivElement>) => {
if (!event.currentTarget.contains(event.relatedTarget as Node | null)) {
setUploadDropTarget((currentTarget) =>
currentTarget === 'canvas' ? null : currentTarget,
);
}
},
[setUploadDropTarget],
);
const handleCanvasDrop = useCallback(
(event: ReactDragEvent<HTMLDivElement>) => {
const draggedAssetId = getDraggedAssetId(event.dataTransfer);
if (draggedAssetId) {
const draggedAsset = assets.find((asset) => asset.id === draggedAssetId);
if (!draggedAsset) {
return;
}
event.preventDefault();
setUploadDropTarget(null);
updateAssetMoveDropFolder(null);
addAssetLayer(
draggedAsset,
getCanvasDropPoint(event.clientX, event.clientY),
);
return;
}
const files = event.dataTransfer.files;
if (!files.length) {
return;
}
event.preventDefault();
setUploadDropTarget(null);
updateAssetMoveDropFolder(null);
const canvasPoint = getCanvasDropPoint(event.clientX, event.clientY);
const defaultFolder =
assetFolders.find((folder) => folder.systemDefault) ?? assetFolders[0];
addUploadedFiles(files, {
folderId: defaultFolder?.id,
canvasPoint,
addToCanvas: true,
});
},
[
addAssetLayer,
addUploadedFiles,
assetFolders,
assets,
getCanvasDropPoint,
setUploadDropTarget,
updateAssetMoveDropFolder,
],
);
return useMemo(
() => ({
handleCanvasDragOver,
handleCanvasDragLeave,
handleCanvasDrop,
}),
[handleCanvasDragLeave, handleCanvasDragOver, handleCanvasDrop],
);
}