add full-page drag hint overlay for file drop on upload.
This commit is contained in:
@@ -38,6 +38,16 @@
|
||||
:metadata-error-count="metadataErrorCount"
|
||||
@upload="onUploadFromModal"
|
||||
/>
|
||||
|
||||
<Teleport to="body">
|
||||
<div v-if="showFileDragOverlay" class="pointer-events-none fixed inset-0 z-70 bg-background/70">
|
||||
<div
|
||||
class="absolute inset-[5%] flex items-center justify-center rounded-lg border-2 border-dashed border-primary"
|
||||
>
|
||||
<p class="text-2xl font-medium text-foreground sm:text-3xl">Drop files</p>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -77,19 +87,28 @@ const emit = defineEmits<{
|
||||
|
||||
const fileInputRef = ref<HTMLInputElement | null>(null);
|
||||
const configureModalOpen = ref(false);
|
||||
const fileDragDepth = ref(0);
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('dragenter', onWindowDragEnter);
|
||||
window.addEventListener('dragleave', onWindowDragLeave);
|
||||
window.addEventListener('dragover', onWindowDragOver);
|
||||
window.addEventListener('drop', onWindowDrop);
|
||||
window.addEventListener('dragend', onWindowDragEnd);
|
||||
window.addEventListener('paste', onWindowPaste);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('dragenter', onWindowDragEnter);
|
||||
window.removeEventListener('dragleave', onWindowDragLeave);
|
||||
window.removeEventListener('dragover', onWindowDragOver);
|
||||
window.removeEventListener('drop', onWindowDrop);
|
||||
window.removeEventListener('dragend', onWindowDragEnd);
|
||||
window.removeEventListener('paste', onWindowPaste);
|
||||
});
|
||||
|
||||
const showFileDragOverlay = computed(() => fileDragDepth.value > 0 && !props.disabled);
|
||||
|
||||
const maxImageSizeLabel = computed(() => prettyBytes(maxImageBytes, { binary: true, maximumFractionDigits: 1 }));
|
||||
|
||||
const emitPicked = (picked: File[]): void => {
|
||||
@@ -117,15 +136,37 @@ const isFileDrag = (event: DragEvent): boolean => {
|
||||
return Boolean(event.dataTransfer?.types.includes('Files'));
|
||||
};
|
||||
|
||||
const onWindowDragEnter = (event: DragEvent): void => {
|
||||
if (props.disabled || !isFileDrag(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
fileDragDepth.value += 1;
|
||||
};
|
||||
|
||||
const onWindowDragLeave = (event: DragEvent): void => {
|
||||
if (!isFileDrag(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
fileDragDepth.value = Math.max(0, fileDragDepth.value - 1);
|
||||
};
|
||||
|
||||
const onWindowDragOver = (event: DragEvent): void => {
|
||||
if (props.disabled || !isFileDrag(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
if (fileDragDepth.value === 0) {
|
||||
fileDragDepth.value = 1;
|
||||
}
|
||||
};
|
||||
|
||||
const onWindowDrop = (event: DragEvent): void => {
|
||||
resetFileDragDepth();
|
||||
|
||||
if (props.disabled) {
|
||||
return;
|
||||
}
|
||||
@@ -137,6 +178,10 @@ const onWindowDrop = (event: DragEvent): void => {
|
||||
emitPicked(picked);
|
||||
};
|
||||
|
||||
const onWindowDragEnd = (): void => {
|
||||
resetFileDragDepth();
|
||||
};
|
||||
|
||||
const isPasswordInputTarget = (target: EventTarget | null): boolean => {
|
||||
if (!(target instanceof HTMLElement)) {
|
||||
return false;
|
||||
@@ -175,5 +220,9 @@ const closeConfigureModal = (): void => {
|
||||
configureModalOpen.value = false;
|
||||
};
|
||||
|
||||
const resetFileDragDepth = (): void => {
|
||||
fileDragDepth.value = 0;
|
||||
};
|
||||
|
||||
defineExpose({ clearInput, closeConfigureModal });
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user