mirror of https://github.com/nocodb/nocodb
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
191 lines
5.9 KiB
191 lines
5.9 KiB
5 months ago
|
<script setup lang="ts">
|
||
|
import { useAttachmentCell } from '../utils'
|
||
|
|
||
|
const emits = defineEmits<{
|
||
|
'update:visible': [value: boolean]
|
||
|
'upload': [fileList: File[]]
|
||
|
}>()
|
||
|
|
||
|
const dropZoneRef = ref<HTMLDivElement>()
|
||
|
|
||
|
const tempFiles = ref<File[]>([])
|
||
|
|
||
|
const { isLoading } = useAttachmentCell()!
|
||
|
|
||
|
const { files, open: _open } = useFileDialog({
|
||
|
reset: true,
|
||
|
})
|
||
|
|
||
|
watch(files, (newFiles) => {
|
||
|
if (!newFiles) return
|
||
|
Object.values(newFiles).forEach((file) => {
|
||
|
tempFiles.value.push(file)
|
||
|
})
|
||
|
})
|
||
|
|
||
|
const onDrop = (files: File[], event: DragEvent) => {
|
||
|
tempFiles.value.push(...files)
|
||
|
event.preventDefault()
|
||
|
event.stopPropagation()
|
||
|
}
|
||
|
|
||
|
const thumbnails = computedAsync(async () => {
|
||
|
const map = new Map()
|
||
|
await Promise.all(
|
||
|
tempFiles.value.map(async (file) => {
|
||
|
const thumbnail = await createThumbnail(file)
|
||
|
if (thumbnail) {
|
||
|
map.set(file, thumbnail)
|
||
|
}
|
||
|
}),
|
||
|
)
|
||
|
return map
|
||
|
})
|
||
|
|
||
|
const onRemoveFileClick = (file: File) => {
|
||
|
tempFiles.value = tempFiles.value.filter((f) => f !== file)
|
||
|
}
|
||
|
|
||
|
const { isOverDropZone } = useDropZone(dropZoneRef, onDrop)
|
||
|
|
||
|
const clearAll = () => {
|
||
|
tempFiles.value = []
|
||
|
}
|
||
|
|
||
|
const open = () => {
|
||
|
_open()
|
||
|
}
|
||
|
|
||
|
const closeMenu = () => {
|
||
|
emits('update:visible', false)
|
||
|
}
|
||
|
|
||
|
onBeforeUnmount(() => {
|
||
|
tempFiles.value = []
|
||
|
})
|
||
|
</script>
|
||
|
|
||
|
<template>
|
||
|
<div
|
||
|
:class="{
|
||
|
'flex flex-col relative justify-center items-center': !tempFiles.length,
|
||
|
}"
|
||
|
class="w-full p-2 h-full"
|
||
|
>
|
||
|
<NcTooltip v-if="tempFiles.length === 0" class="absolute top-3 right-3">
|
||
|
<NcButton type="text" class="!border-0" size="xsmall" @click="closeMenu">
|
||
|
<GeneralIcon icon="close" />
|
||
|
</NcButton>
|
||
|
|
||
|
<template #title> {{ $t('general.close') }} </template>
|
||
|
</NcTooltip>
|
||
|
|
||
|
<div v-if="tempFiles.length > 0" class="flex w-full border-b-1 py-1 h-9.5 items-center justify-between top-0">
|
||
|
<NcButton type="text" size="small" @click="clearAll">
|
||
|
{{ $t('labels.clearAllFiles') }}
|
||
|
</NcButton>
|
||
|
|
||
|
<span class="text-xs">
|
||
|
{{ tempFiles.length }} files, total size:
|
||
|
{{
|
||
|
formatBytes(
|
||
|
tempFiles.reduce((acc, file) => acc + file.size, 0),
|
||
|
0,
|
||
|
)
|
||
|
}}
|
||
|
</span>
|
||
|
|
||
|
<NcButton type="text" size="small" @click="open">
|
||
|
<div class="flex gap-1 items-center">
|
||
|
<component :is="iconMap.plus" />
|
||
|
{{ $t('labels.addMore') }}
|
||
|
</div>
|
||
|
</NcButton>
|
||
|
</div>
|
||
|
<div
|
||
|
ref="dropZoneRef"
|
||
|
:class="{
|
||
|
'border-brand-500': isOverDropZone,
|
||
|
'border-dashed border-1': !tempFiles.length,
|
||
|
}"
|
||
|
data-testid="attachment-drop-zone"
|
||
|
:style="`height: ${tempFiles.length > 0 ? '324px' : '100%'}`"
|
||
|
class="flex flex-col items-center justify-center h-full w-full flex-grow-1 rounded-lg"
|
||
|
@click="tempFiles.length > 0 ? () => {} : open()"
|
||
|
>
|
||
|
<div v-if="!tempFiles.length" class="flex cursor-pointer items-center justify-center flex-col gap-2">
|
||
|
<template v-if="!isOverDropZone">
|
||
|
<component :is="iconMap.upload" class="w-5 h-5" />
|
||
|
<h1>
|
||
|
{{ $t('labels.clickTo') }}
|
||
|
|
||
|
<span class="font-semibold"> {{ $t('labels.browseFiles') }} </span>
|
||
|
{{ $t('general.or') }}
|
||
|
<span class="font-semibold"> {{ $t('labels.dragFilesHere') }} </span>
|
||
|
|
||
|
{{ $t('labels.toUpload') }}
|
||
|
</h1>
|
||
|
</template>
|
||
|
<template v-if="isOverDropZone">
|
||
|
<component :is="iconMap.upload" class="w-5 text-brand-500 h-5" />
|
||
|
<h1 class="text-brand-500 font-bold">{{ $t('labels.dropHere') }}</h1>
|
||
|
</template>
|
||
|
</div>
|
||
|
<template v-else>
|
||
|
<div
|
||
|
class="grid overflow-y-auto flex-grow-1 nc-scrollbar-md grid-cols-4 w-full h-full items-start py-2 justify-center gap-4"
|
||
|
>
|
||
|
<div v-for="file in tempFiles" :key="file.name" class="flex gap-1.5 group min-w-34 max-w-28 pb-4 flex-col relative">
|
||
|
<div
|
||
|
v-if="!thumbnails.get(file)"
|
||
|
style="height: 140px"
|
||
|
class="flex items-center justify-center rounded-md bg-gray-300"
|
||
|
>
|
||
|
<component :is="iconMap.file" class="w-16 h-16" />
|
||
|
</div>
|
||
|
<img v-else :src="thumbnails.get(file)" style="height: 140px" alt="thumbnail" class="rounded-md object-cover" />
|
||
|
|
||
|
<div class="relative text-[12px] font-semibold items-center text-gray-800 flex">
|
||
|
<NcTooltip class="flex-auto truncate" placement="bottom">
|
||
|
<template #title> {{ file.name }} </template>
|
||
|
{{ file.name }}
|
||
|
</NcTooltip>
|
||
|
|
||
|
<div class="flex-none hide-ui transition-all transition-ease-in-out !h-4 flex items-center bg-white">
|
||
|
<NcTooltip placement="bottom">
|
||
|
<template #title> {{ $t('title.removeFile') }} </template>
|
||
|
<component :is="iconMap.delete" class="!text-red-500 w-3 h-3 cursor-pointer" @click="onRemoveFileClick(file)" />
|
||
|
</NcTooltip>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div class="flex-none text-[10px] font-semibold text-gray-500">
|
||
|
{{ formatBytes(file.size, 0) }}
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</template>
|
||
|
</div>
|
||
|
<div v-if="tempFiles.length" class="flex gap-2 pt-1 bg-white w-full items-center justify-end">
|
||
|
<NcButton :disabled="isLoading" type="secondary" size="small" @click="closeMenu">
|
||
|
{{ $t('labels.cancel') }}
|
||
|
</NcButton>
|
||
|
|
||
|
<NcButton :loading="isLoading" data-testid="nc-upload-file" size="small" @click="emits('upload', tempFiles)">
|
||
|
<template v-if="isLoading">
|
||
|
{{ $t('labels.uploading') }}
|
||
|
</template>
|
||
|
<template v-else> {{ $t('general.upload') }} {{ tempFiles.length }} {{ $t('objects.files') }} </template>
|
||
|
</NcButton>
|
||
|
</div>
|
||
|
</div>
|
||
|
</template>
|
||
|
|
||
|
<style lang="scss">
|
||
|
.hide-ui {
|
||
|
@apply h-0 w-0 overflow-hidden whitespace-nowrap;
|
||
|
.group:hover & {
|
||
|
@apply h-auto w-auto overflow-visible whitespace-normal;
|
||
|
}
|
||
|
}
|
||
|
</style>
|