diff options
Diffstat (limited to 'src/ui/c-file-upload/c-file-upload.vue')
-rw-r--r-- | src/ui/c-file-upload/c-file-upload.vue | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/src/ui/c-file-upload/c-file-upload.vue b/src/ui/c-file-upload/c-file-upload.vue new file mode 100644 index 0000000..b48d8c3 --- /dev/null +++ b/src/ui/c-file-upload/c-file-upload.vue @@ -0,0 +1,95 @@ +<script lang="ts" setup> +import _ from 'lodash'; + +const props = withDefaults(defineProps<{ + multiple?: boolean + accept?: string + title?: string +}>(), { + multiple: false, + accept: undefined, + title: 'Drag and drop files here, or click to select files', +}); + +const emit = defineEmits<{ + (event: 'filesUpload', files: File[]): void + (event: 'fileUpload', file: File): void +}>(); + +const { multiple } = toRefs(props); + +const isOverDropZone = ref(false); + +const fileInput = ref<HTMLInputElement | null>(null); + +function triggerFileInput() { + fileInput.value?.click(); +} + +function handleFileInput(event: Event) { + const files = (event.target as HTMLInputElement).files; + + handleUpload(files); +} + +function handleDrop(event: DragEvent) { + event.preventDefault(); + const files = event.dataTransfer?.files; + + handleUpload(files); +} + +function handleUpload(files: FileList | null | undefined) { + if (_.isNil(files) || _.isEmpty(files)) { + return; + } + + if (multiple.value) { + emit('filesUpload', Array.from(files)); + return; + } + + emit('fileUpload', files[0]); +} +</script> + +<template> + <div + class="flex flex-col cursor-pointer items-center justify-center border-2px border-gray-300 border-opacity-50 rounded-lg border-dashed p-8 transition-colors" + :class="{ + 'border-primary border-opacity-100': isOverDropZone, + }" + @click="triggerFileInput" + @drop.prevent="handleDrop" + @dragover.prevent + @dragenter="isOverDropZone = true" + @dragleave="isOverDropZone = false" + > + <input + ref="fileInput" + type="file" + class="hidden" + :multiple="multiple" + :accept="accept" + @change="handleFileInput" + > + <slot> + <span op-70> + {{ title }} + </span> + + <!-- separator --> + <div my-4 w-full flex items-center justify-center op-70> + <div class="h-1px max-w-100px flex-1 bg-gray-300 op-50" /> + <div class="mx-2 text-gray-400"> + or + </div> + <div class="h-1px max-w-100px flex-1 bg-gray-300 op-50" /> + </div> + + <c-button> + Browse files + </c-button> + </slot> + </div> +</template> |