Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/runtime/components/FileUpload.vue
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export interface FileUploadSlots<M extends boolean = false> {
<script setup lang="ts" generic="M extends boolean = false">
import { computed, watch } from 'vue'
import { Primitive } from 'reka-ui'
import { createReusableTemplate } from '@vueuse/core'
import { createReusableTemplate, reactiveComputed } from '@vueuse/core'
import { useAppConfig, useLocale } from '#imports'
import { useFormField } from '../composables/useFormField'
import { useFileUpload } from '../composables/useFileUpload'
Expand Down Expand Up @@ -157,13 +157,13 @@ const { t } = useLocale()

const [DefineFilesTemplate, ReuseFilesTemplate] = createReusableTemplate()

const { isDragging, open, inputRef, dropzoneRef } = useFileUpload({
const { isDragging, open, inputRef, dropzoneRef } = useFileUpload(reactiveComputed(() => ({
accept: props.accept,
reset: props.reset,
multiple: props.multiple,
dropzone: props.dropzone,
onUpdate
})
})))
const { emitFormInput, emitFormChange, id, name, disabled, ariaAttrs } = useFormField<FileUploadProps>(props)

const variant = computed(() => props.multiple ? 'area' : props.variant)
Expand Down
64 changes: 41 additions & 23 deletions src/runtime/composables/useFileUpload.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ref, computed, unref, onMounted, watch, reactive } from 'vue'
import { ref, computed, unref, onMounted, reactive, readonly } from 'vue'
import { useFileDialog, useDropZone } from '@vueuse/core'
import type { MaybeRef } from '@vueuse/core'
import { defu } from 'defu'

export interface UseFileUploadOptions {
/**
Expand Down Expand Up @@ -31,17 +32,17 @@ function parseAcceptToDataTypes(accept: string): string[] | undefined {
}

export function useFileUpload(options: UseFileUploadOptions) {
const {
accept = '*',
reset = false,
multiple = false,
dropzone = true,
onUpdate
} = options
const optionsComputed = computed(() => defu({
accept: '*',
reset: false,
multiple: false,
dropzone: true
}, options))

const inputRef = ref<HTMLInputElement>()
const dropzoneRef = ref<HTMLDivElement>()

const dataTypes = computed(() => parseAcceptToDataTypes(unref(accept)))
const dataTypes = computed(() => parseAcceptToDataTypes(unref(optionsComputed.value.accept)))

const onDrop = (files: FileList | File[] | null) => {
if (!files || files.length === 0) {
Expand All @@ -50,13 +51,12 @@ export function useFileUpload(options: UseFileUploadOptions) {
if (files instanceof FileList) {
files = Array.from(files)
}
if (files.length > 1 && !multiple) {
if (files.length > 1 && !optionsComputed.value.multiple) {
files = [files[0]!]
}
onUpdate(files)
optionsComputed.value.onUpdate(files)
}

const isDragging = ref(false)
const fileDialog = reactive({
open: () => {
}
Expand All @@ -66,20 +66,38 @@ export function useFileUpload(options: UseFileUploadOptions) {
fileDialog.open()
}

onMounted(() => {
const { isOverDropZone } = dropzone
? useDropZone(dropzoneRef, { dataTypes: dataTypes.value, onDrop })
: { isOverDropZone: ref(false) }
const { isOverDropZone: isDragging } = useDropZone(dropzoneRef, {
dataTypes: (types) => {
if (dataTypes.value === undefined || optionsComputed.value.accept === '*') {
return true
}

watch(isOverDropZone, (value) => {
isDragging.value = value
})
return types.some((type) => {
return dataTypes.value?.some((accepted) => {
if (accepted.endsWith('/*')) {
const base = accepted.slice(0, accepted.indexOf('/'))
return type.startsWith(base + '/')
} else {
return type === accepted
}
})
})
}, onDrop: (files, event) => {
if (!optionsComputed.value.dropzone) {
event.preventDefault()
return
}

onDrop(files)
}
})

onMounted(() => {
const { onChange, open } = useFileDialog({
accept: unref(accept),
multiple,
accept: unref(optionsComputed.value.accept),
multiple: optionsComputed.value.multiple,
input: unref(inputRef),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be refactored to be outside of onMounted which would simplify things, but there seems to be some kind of reactivity issue with the inputRef inside of useFileDialog, because it should be possible to do input: inputRef and then there is no need for the onMounted hook, but it doesn't work, but in minimal repro it does. Perhaps a bug in VueUse?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it's a VueUse bug, probably: vueuse/vueuse#4867

reset
reset: optionsComputed.value.reset
})

fileDialog.open = open
Expand All @@ -88,7 +106,7 @@ export function useFileUpload(options: UseFileUploadOptions) {
})

return {
isDragging,
isDragging: readonly(isDragging),
open,
inputRef,
dropzoneRef
Expand Down
Loading