Continuation of separting logic into domain specific composables
This commit is contained in:
110
src/composables/useFileDrop.ts
Normal file
110
src/composables/useFileDrop.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import { ref, type Ref } from 'vue';
|
||||
import type { Sprite } from '@/types/sprites';
|
||||
import { getMaxDimensions } from './useSprites';
|
||||
|
||||
export interface FileDropOptions {
|
||||
sprites: Ref<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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user