Compare commits

...

3 Commits

Author SHA1 Message Date
kolaente 3798dba9a3
Merge branch 'main' into codex/fix-drag-and-drop-behavior-inconsistency 2025-11-16 11:14:10 +01:00
kolaente 6fde6688a5 fix: handle dropzone events correctly 2025-10-20 00:10:15 +02:00
kolaente 7bf6246f52 fix: filter attachment dropzone drags 2025-10-19 23:42:58 +02:00
2 changed files with 81 additions and 7 deletions

View File

@ -126,7 +126,7 @@
<Teleport to="body"> <Teleport to="body">
<div <div
v-if="editEnabled" v-if="editEnabled"
:class="{hidden: !isOverDropZone}" :class="{hidden: !showDropzone}"
class="dropzone" class="dropzone"
> >
<div class="drop-hint"> <div class="drop-hint">
@ -172,7 +172,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {ref, shallowReactive, computed} from 'vue' import {ref, shallowReactive, computed, watch} from 'vue'
import {useDropZone} from '@vueuse/core' import {useDropZone} from '@vueuse/core'
import User from '@/components/misc/User.vue' import User from '@/components/misc/User.vue'
@ -215,13 +215,87 @@ const attachments = computed(() => attachmentStore.attachments)
const loading = computed(() => attachmentService.loading || taskStore.isLoading) const loading = computed(() => attachmentService.loading || taskStore.isLoading)
function onDrop(files: File[] | null) { const isDraggingFiles = ref(false)
if (files && files.length !== 0) { const isDragOverEditor = ref(false)
uploadFilesToTask(files)
const EDITOR_SELECTOR = '.tiptap, .tiptap__editor, [contenteditable]'
function eventTargetsEditor(event: Event | null | undefined): boolean {
if (!event) {
return false
} }
const target = event.target
if (target instanceof HTMLElement && target.closest(EDITOR_SELECTOR)) {
return true
}
if (typeof event.composedPath === 'function') {
return event.composedPath().some(element => element instanceof HTMLElement && element.matches(EDITOR_SELECTOR))
}
return false
} }
const {isOverDropZone} = useDropZone(document, onDrop) function resetDragState() {
isDraggingFiles.value = false
isDragOverEditor.value = false
}
const dropTarget = computed(() => (props.editEnabled && typeof document !== 'undefined' ? document : null))
const {isOverDropZone} = useDropZone(dropTarget, {
dataTypes: ['Files'],
onEnter(event) {
if (!props.editEnabled) {
return
}
isDraggingFiles.value = true
isDragOverEditor.value = eventTargetsEditor(event)
},
onOver(event) {
if (!props.editEnabled) {
return
}
isDragOverEditor.value = eventTargetsEditor(event)
},
onLeave(event) {
if (!props.editEnabled) {
return
}
if (!isOverDropZone.value) {
resetDragState()
return
}
isDragOverEditor.value = eventTargetsEditor(event)
},
onDrop(files, event) {
if (!props.editEnabled) {
return
}
const dropOverEditor = eventTargetsEditor(event)
resetDragState()
if (dropOverEditor || !files || files.length === 0) {
return
}
uploadFilesToTask(files)
},
})
watch(() => props.editEnabled, enabled => {
if (!enabled) {
resetDragState()
}
})
const showDropzone = computed(() => props.editEnabled && isDraggingFiles.value && !isDragOverEditor.value)
function downloadAttachment(attachment: IAttachment) { function downloadAttachment(attachment: IAttachment) {
attachmentService.download(attachment) attachmentService.download(attachment)

View File

@ -346,7 +346,7 @@
<!-- Attachments --> <!-- Attachments -->
<div <div
v-if="activeFields.attachments || hasAttachments" v-show="activeFields.attachments || hasAttachments"
class="content attachments" class="content attachments"
> >
<Attachments <Attachments