111 lines
2.9 KiB
TypeScript
111 lines
2.9 KiB
TypeScript
import { ref, type Ref, type ComputedRef } from 'vue';
|
|
import type { Sprite } from '@/types/sprites';
|
|
import { getMaxDimensions } from './useSprites';
|
|
|
|
export interface FileDropOptions {
|
|
sprites: Ref<Sprite[]> | ComputedRef<Sprite[]> | Sprite[];
|
|
onAddSprite: (file: File) => void;
|
|
onAddSpriteWithResize: (file: File) => void;
|
|
}
|
|
|
|
export function useFileDrop(options: FileDropOptions) {
|
|
const { onAddSprite, onAddSpriteWithResize } = options;
|
|
|
|
// Helper to get sprites array
|
|
const getSprites = () => (Array.isArray(options.sprites) ? options.sprites : options.sprites.value);
|
|
|
|
const isDragOver = ref(false);
|
|
|
|
const handleDragOver = (event: DragEvent) => {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
if (event.dataTransfer) {
|
|
event.dataTransfer.dropEffect = 'copy';
|
|
}
|
|
isDragOver.value = true;
|
|
};
|
|
|
|
const handleDragEnter = (event: DragEvent) => {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
isDragOver.value = true;
|
|
};
|
|
|
|
const handleDragLeave = (event: DragEvent, canvasRef?: HTMLCanvasElement | null) => {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
if (canvasRef) {
|
|
const rect = canvasRef.getBoundingClientRect();
|
|
if (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom) {
|
|
isDragOver.value = false;
|
|
}
|
|
} else {
|
|
isDragOver.value = false;
|
|
}
|
|
};
|
|
|
|
const processDroppedImage = (file: File): Promise<void> => {
|
|
return new Promise((resolve, reject) => {
|
|
const img = new Image();
|
|
const reader = new FileReader();
|
|
|
|
reader.onload = e => {
|
|
if (e.target?.result) {
|
|
img.src = e.target.result as string;
|
|
}
|
|
};
|
|
|
|
img.onload = () => {
|
|
const sprites = getSprites();
|
|
const { maxWidth, maxHeight } = getMaxDimensions(sprites);
|
|
|
|
// Check if the dropped image is larger than current cells
|
|
if (img.naturalWidth > maxWidth || img.naturalHeight > maxHeight) {
|
|
onAddSpriteWithResize(file);
|
|
} else {
|
|
onAddSprite(file);
|
|
}
|
|
resolve();
|
|
};
|
|
|
|
img.onerror = () => {
|
|
console.error('Failed to load image:', file.name);
|
|
reject(new Error('Failed to load image'));
|
|
};
|
|
|
|
reader.readAsDataURL(file);
|
|
});
|
|
};
|
|
|
|
const handleDrop = async (event: DragEvent) => {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
isDragOver.value = false;
|
|
|
|
if (!event.dataTransfer?.files || event.dataTransfer.files.length === 0) {
|
|
return;
|
|
}
|
|
|
|
const files = Array.from(event.dataTransfer.files).filter(file => file.type.startsWith('image/'));
|
|
|
|
if (files.length === 0) {
|
|
alert('Please drop image files only.');
|
|
return;
|
|
}
|
|
|
|
// Process each dropped file
|
|
for (const file of files) {
|
|
await processDroppedImage(file);
|
|
}
|
|
};
|
|
|
|
return {
|
|
isDragOver,
|
|
handleDragOver,
|
|
handleDragEnter,
|
|
handleDragLeave,
|
|
handleDrop,
|
|
};
|
|
}
|