Files
spritesheet-generator/src/composables/useFileDrop.ts
2025-11-22 02:52:36 +01:00

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,
};
}