feat(component): new file-input component (#166)

* file input field

* input file

* empty lines

* dropzone value update

* dropzone value update

* fix drag and drop

* Update docs/components/fileInput.md

Co-authored-by: Ilya Artamonov <ilya.sosidka@gmail.com>

* Update docs/components/fileInput/examples/FileInpDropZone.vue

Co-authored-by: Ilya Artamonov <ilya.sosidka@gmail.com>

* Update src/components/FileInput/FileInput.vue

Co-authored-by: Ilya Artamonov <ilya.sosidka@gmail.com>

* Update src/components/FileInput/FileInput.vue

Co-authored-by: Ilya Artamonov <ilya.sosidka@gmail.com>

* Update src/components/FileInput/FileInput.vue

Co-authored-by: Ilya Artamonov <ilya.sosidka@gmail.com>

* Update src/components/FileInput/FileInput.vue

Co-authored-by: Ilya Artamonov <ilya.sosidka@gmail.com>

* multiple files and file type

---------

Co-authored-by: Ilya Artamonov <ilya.sosidka@gmail.com>
This commit is contained in:
Vasu Singh
2023-07-15 22:35:41 +05:30
committed by GitHub
parent 960a318ca6
commit 3501e946d3
10 changed files with 311 additions and 1 deletions

View File

@@ -0,0 +1,96 @@
<template>
<div>
<div v-if="!dropzone">
<label>
<span :class="labelClasses">{{ label }}</span>
<input :class="fileInpClasses" :multiple="multiple" @change="handleChange" type="file">
</label>
<slot />
</div>
<div v-else @change="handleChange" @drop="dropFileHandler" @dragover="dragOverHandler"
class="flex items-center justify-center">
<label :class="dropzoneLabelClasses">
<div :class="dropzoneWrapClasses">
<svg class="w-8 h-8 text-gray-500 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 20 16">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M13 13h3a3 3 0 0 0 0-6h-.025A5.56 5.56 0 0 0 16 6.5 5.5 5.5 0 0 0 5.207 5.021C5.137 5.017 5.071 5 5 5a4 4 0 0 0 0 8h2.167M10 15V6m0 0L8 8m2-2 2 2" />
</svg>
<div v-if="!model">
<p :class="dropzoneTextClasses"><span class="font-semibold">Click to upload</span> or
drag and drop
</p>
<slot />
</div>
<p v-else>File: {{ model.name }}</p>
</div>
<input type="file" class="hidden" />
</label>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useFileInputClasses } from '@/components/FileInput/composables/useFileInputClasses'
interface FileInputProps {
modelValue?: any;
label?: string;
size?: string;
dropzone?: boolean;
multiple?: boolean;
}
const props = withDefaults(defineProps<FileInputProps>(), {
value: null,
label: '',
size: 'sm',
dropzone: false,
multiple: false,
})
const emit = defineEmits(['update:modelValue'])
const model = computed({
get() {
return props.modelValue
},
set(val) {
emit('update:modelValue', val)
},
})
const handleChange = (event: Event) => {
const target = event.target as HTMLInputElement
if(props.multiple) { model.value = target.files }
else { model.value = target.files?.[0] }
}
const dropFileHandler = (event: any) => {
event.preventDefault()
if (event.dataTransfer.items) {
[...event.dataTransfer.items].forEach((item, i) => {
if (item.kind === 'file') {
const file = item.getAsFile()
model.value = file
}
})
} else {
[...event.dataTransfer.files].forEach((file, i) => {
model.value = file
})
}
}
const dragOverHandler = (event: Event) => {
event.preventDefault()
}
const {
fileInpClasses,
labelClasses,
dropzoneLabelClasses,
dropzoneWrapClasses,
dropzoneTextClasses,
} = useFileInputClasses(props.size)
</script>

View File

@@ -0,0 +1,38 @@
import { simplifyTailwindClasses } from '@/utils/simplifyTailwindClasses'
import { computed } from 'vue'
const fileInpDefaultClasses = 'block w-full text-sm text-gray-900 border-[1px] border-gray-300 rounded-lg cursor-pointer bg-gray-50 dark:text-gray-400 focus:outline-none dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400'
const fileInpLabelClasses = 'block mb-2 text-sm font-medium text-gray-900 dark:text-white'
const fileInpDropzoneClasses = 'flex flex-col items-center justify-center w-full h-64 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:hover:bg-bray-800 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600'
const fileDropzoneWrapClasses = 'flex flex-col items-center justify-center pt-5 pb-6'
const fileDropzoneDefaultTextClasses = '!-mb-2 text-sm text-gray-500 dark:text-gray-400'
export function useFileInputClasses(size: string) {
const fileInpClasses = computed(() => {
return simplifyTailwindClasses(fileInpDefaultClasses, 'text-' + size)
})
const labelClasses = computed(() => {
return fileInpLabelClasses
})
const dropzoneLabelClasses = computed(() => {
return fileInpDropzoneClasses
})
const dropzoneWrapClasses = computed(() => {
return fileDropzoneWrapClasses
})
const dropzoneTextClasses = computed(() => {
return fileDropzoneDefaultTextClasses
})
return {
fileInpClasses,
labelClasses,
dropzoneLabelClasses,
dropzoneWrapClasses,
dropzoneTextClasses,
}
}

View File

@@ -62,6 +62,6 @@ export { default as Range } from './components/Range/Range.vue'
export { default as Radio } from './components/Radio/Radio.vue'
export { default as Textarea } from './components/Textarea/Textarea.vue'
export { default as FileInput } from './components/FileInput/FileInput.vue'
export * from './composables'