Files
2026-05-13 17:11:09 +02:00

102 lines
3.0 KiB
JavaScript

import { inject, ref } from 'vue';
/**
* Shared image upload composable used by both the left-panel ImageDropZone
* and the right-side preview block drop zones.
*
* @param {() => string | null} getUploadUrl A getter (or plain string) for the upload endpoint.
* @param {(url: string) => void} onSuccess Called immediately with a blob URL, then again with the server URL.
*/
export const useImageUpload = (getUploadUrl, onSuccess) => {
const isDragOver = ref(false);
const isUploading = ref(false);
const error = ref(null);
// Plugs into the parent editor's pending-upload counter so the form submit
// can be blocked while this upload is in flight.
const pendingUploads = inject('editorPendingUploads', null);
const uploadFile = async (file) => {
if (!file || !file.type.startsWith('image/')) {
return;
}
const uploadUrl = typeof getUploadUrl === 'function' ? getUploadUrl() : getUploadUrl;
if (!uploadUrl) {
return;
}
// Emit blob URL immediately so the preview renders at once
const blobUrl = URL.createObjectURL(file);
onSuccess(blobUrl);
isUploading.value = true;
error.value = null;
if (pendingUploads) pendingUploads.value++;
try {
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') ?? '';
const formData = new FormData();
formData.append('image', file);
const response = await fetch(uploadUrl, {
method: 'POST',
headers: { 'X-CSRF-TOKEN': csrfToken },
body: formData,
});
if (!response.ok) {
throw new Error('Upload failed');
}
const data = await response.json();
URL.revokeObjectURL(blobUrl);
onSuccess(data.url);
} catch {
error.value = 'Upload failed. Please try again.';
URL.revokeObjectURL(blobUrl);
onSuccess('');
} finally {
isUploading.value = false;
if (pendingUploads) pendingUploads.value--;
}
};
const onDragOver = (event) => {
event.preventDefault();
isDragOver.value = true;
};
const onDragLeave = () => {
isDragOver.value = false;
};
const onDrop = (event) => {
event.preventDefault();
isDragOver.value = false;
const file = event.dataTransfer?.files?.[0];
if (file) {
uploadFile(file);
}
};
const fileInputRef = ref(null);
const openPicker = () => {
if (!fileInputRef.value) return;
fileInputRef.value.value = '';
fileInputRef.value.click();
};
const onFileSelected = (event) => {
const file = event.target.files[0];
if (file) {
uploadFile(file);
}
};
return { isDragOver, isUploading, error, fileInputRef, onDragOver, onDragLeave, onDrop, openPicker, onFileSelected, uploadFile };
};