components/shared/DropUpload.vue
<template>
<div class="my-3">
<div
v-if="url && isModelMedia"
class="p-2 mx-auto"
>
<BaseMediaItem
:src="url"
:mime-type="mimeType"
:preview="false"
@error="hasError = true"
/>
</div>
<div class="field">
<NeoField
class="file is-primary"
:addons="false"
>
<NeoUpload
ref="upload"
v-model="file"
:required="required"
drag-drop
:expanded="expanded"
:accept="accept"
>
<section class="px-6 py-12">
<div class="content text-center">
<NeoIcon
v-if="!file && !url"
:icon="icon"
custom-size="fa-2x"
/>
<div
v-if="url && !isModelMedia"
@click.prevent
>
<BaseMediaItem
:src="url"
:mime-type="mimeType"
:preview="false"
@error="hasError = true"
/>
</div>
<NeoIcon
v-if="hasError"
icon="eye-slash"
custom-size="fa-2x"
/>
<p v-if="!file">
{{ label }}
</p>
<p
v-else
class="mt-2"
>
Awesome your file is <b>{{ file.name }}</b>. Click or drop to change
</p>
</div>
</section>
</NeoUpload>
</NeoField>
<transition name="fade">
<div
v-if="fileSizeFailed"
class="help is-danger"
>
{{ $t('tooltip.failedMaxSize') }}
</div>
</transition>
<transition name="fade">
<div
v-if="checkFailed"
class="help is-danger"
>
{{ $t('tooltip.needToUploadNFTFile') }}
</div>
</transition>
</div>
</div>
</template>
<script lang="ts" setup>
import { NeoField, NeoIcon, NeoUpload } from '@kodadot1/brick'
import { useEventListener } from '@vueuse/core'
import { MAX_UPLOADED_FILE_SIZE } from '@/utils/constants'
const props = defineProps({
label: {
type: String,
default:
'Drop your NFT here or click to upload or simply paste image from clipboard',
},
icon: { type: String, default: 'upload' },
required: { type: Boolean, default: false },
expanded: { type: Boolean },
preview: { type: Boolean },
accept: {
type: String,
default: '',
},
})
const emit = defineEmits(['update:modelValue'])
const file = ref<File | null>(null)
const fileSizeLimit = ref(MAX_UPLOADED_FILE_SIZE)
const url = ref('')
const hasError = ref(false)
const checkFailed = ref(false)
const fileSizeFailed = ref(false)
const upload = ref()
const mimeType = computed(() => {
if (file.value?.type) {
return file.value.type
}
// workaround for model media in chrome
const fileExtension = file.value?.name.split('.').pop() || ''
const extensionToMimeType = {
glb: 'model/gltf-binary',
}
if (fileExtension in extensionToMimeType) {
return extensionToMimeType[fileExtension]
}
return ''
})
const isModelMedia = computed(() => mimeType.value.startsWith('model'))
const checkValidity = () => {
checkFailed.value = !file.value
return !checkFailed.value
}
const onPasteImage = (pasteEvent: ClipboardEvent) => {
/* handling paste logic */
const item: DataTransferItem | any = pasteEvent?.clipboardData?.items[0]
if (item?.type.indexOf('image') === 0) {
const blob = item.getAsFile()
file.value = blob
createInput(blob)
}
}
const createInput = (inputFile: Blob): undefined | boolean => {
const fileSize = inputFile.size / Math.pow(1024, 2)
if (fileSize > fileSizeLimit.value) {
fileSizeFailed.value = true
file.value = null
return false
}
fileSizeFailed.value = false
checkFailed.value = false
const reader = new FileReader()
emit('update:modelValue', inputFile)
if (props.preview) {
url.value = URL.createObjectURL(inputFile)
hasError.value = false
}
reader.readAsText(inputFile)
}
watch(file, () => {
if (file.value) {
createInput(file.value)
}
})
useEventListener(window, 'paste', onPasteImage)
defineExpose({ checkValidity })
</script>