fix: files tab drag and drop (#6325)

* fix: files drag drop

* fix: standardize drag and drop + fix files tab permissions
This commit is contained in:
Calum H.
2026-06-08 17:03:30 +00:00
committed by GitHub
parent 9729737d7d
commit 926c72de42
10 changed files with 312 additions and 41 deletions
@@ -1,16 +1,17 @@
<template>
<div
@dragenter.prevent="handleDragEnter"
@dragover.prevent="handleDragOver"
@dragleave.prevent="handleDragLeave"
@drop.prevent="handleDrop"
ref="dropTargetRef"
@dragenter="dropTargetProps.onDragenter"
@dragover="dropTargetProps.onDragover"
@dragleave="dropTargetProps.onDragleave"
@drop="dropTargetProps.onDrop"
>
<slot />
<div
v-if="isDragging"
v-if="showOverlay"
:class="[
'absolute inset-0 flex items-center justify-center rounded-2xl bg-black/60 text-contrast shadow',
overlayClass,
props.overlayClass,
]"
>
<div class="text-center">
@@ -18,7 +19,7 @@
<p class="mt-2 text-xl">
{{
formatMessage(messages.dropToUpload, {
type: formatFileItemType(formatMessage, type?.toLocaleLowerCase(), true),
type: formatFileItemType(formatMessage, props.type?.toLocaleLowerCase(), true),
})
}}
</p>
@@ -29,8 +30,9 @@
<script setup lang="ts">
import { UploadIcon } from '@modrinth/assets'
import { ref } from 'vue'
import { computed, ref } from 'vue'
import { useFileDropTarget } from '#ui/composables/file-drop'
import { defineMessages, useVIntl } from '#ui/composables/i18n'
import { formatFileItemType } from '#ui/utils/common-messages'
@@ -38,9 +40,11 @@ const { formatMessage } = useVIntl()
const emit = defineEmits<{
filesDropped: [files: File[]]
dropError: [error: unknown]
}>()
defineProps<{
const props = defineProps<{
disabled?: boolean
overlayClass?: string
type?: string
}>()
@@ -52,35 +56,12 @@ const messages = defineMessages({
},
})
const isDragging = ref(false)
const dragCounter = ref(0)
const handleDragEnter = (event: DragEvent) => {
event.preventDefault()
dragCounter.value++
isDragging.value = true
}
const handleDragOver = (event: DragEvent) => {
event.preventDefault()
}
const handleDragLeave = (event: DragEvent) => {
event.preventDefault()
dragCounter.value--
if (dragCounter.value === 0) {
isDragging.value = false
}
}
const handleDrop = (event: DragEvent) => {
event.preventDefault()
isDragging.value = false
dragCounter.value = 0
const files = event.dataTransfer?.files
if (files) {
emit('filesDropped', Array.from(files))
}
}
const dropTargetRef = ref<HTMLElement | null>(null)
const { isDragging, dropTargetProps } = useFileDropTarget({
target: dropTargetRef,
disabled: computed(() => props.disabled ?? false),
onFiles: (files) => emit('filesDropped', files),
onError: (error) => emit('dropError', error),
})
const showOverlay = computed(() => isDragging.value)
</script>
@@ -69,6 +69,8 @@
<FileUploadDragAndDrop
ref="fileUploadRef"
class="@container relative flex flex-col overflow-clip rounded-[20px] border border-solid border-surface-4 shadow-sm"
:disabled="isBusy"
@drop-error="handleDropError"
@files-dropped="handleDroppedFiles"
>
<FileTableHeader
@@ -591,6 +593,14 @@ function handleDroppedFiles(files: File[]) {
ctx.uploadFiles(files)
}
function handleDropError(error: unknown) {
addNotification({
title: formatMessage(commonMessages.uploadFailedLabel),
text: error instanceof Error ? error.message : undefined,
type: 'error',
})
}
function initiateFileUpload() {
if (isBusy.value) return
const input = document.createElement('input')