gitlab-org--gitlab-foss/app/assets/javascripts/vue_shared/components/upload_dropzone/upload_dropzone.vue

175 lines
4.8 KiB
Vue

<script>
import { GlIcon, GlLink, GlSprintf } from '@gitlab/ui';
import { __ } from '~/locale';
import { isValidImage } from './utils';
import { VALID_DATA_TRANSFER_TYPE, VALID_IMAGE_FILE_MIMETYPE } from './constants';
export default {
components: {
GlIcon,
GlLink,
GlSprintf,
},
props: {
displayAsCard: {
type: Boolean,
required: false,
default: false,
},
enableDragBehavior: {
type: Boolean,
required: false,
default: false,
},
dropToStartMessage: {
type: String,
required: false,
default: __('Drop your files to start your upload.'),
},
isFileValid: {
type: Function,
required: false,
default: isValidImage,
},
validFileMimetypes: {
type: Array,
required: false,
default: () => [VALID_IMAGE_FILE_MIMETYPE.mimetype],
},
},
data() {
return {
dragCounter: 0,
isDragDataValid: false,
};
},
computed: {
dragging() {
return this.dragCounter !== 0;
},
iconStyles() {
return {
size: this.displayAsCard ? 24 : 16,
class: this.displayAsCard ? 'gl-mb-2' : 'gl-mr-3 gl-text-gray-500',
};
},
validMimeTypeString() {
return this.validFileMimetypes.join();
},
},
methods: {
isValidUpload(files) {
return files.every(this.isFileValid);
},
isValidDragDataType({ dataTransfer }) {
return Boolean(
dataTransfer && dataTransfer.types.some((t) => t === VALID_DATA_TRANSFER_TYPE),
);
},
ondrop({ dataTransfer = {} }) {
this.dragCounter = 0;
// User already had feedback when dropzone was active, so bail here
if (!this.isDragDataValid) {
return;
}
const { files } = dataTransfer;
if (!this.isValidUpload(Array.from(files))) {
this.$emit('error');
return;
}
this.$emit('change', files);
},
ondragenter(e) {
this.dragCounter += 1;
this.isDragDataValid = this.isValidDragDataType(e);
},
ondragleave() {
this.dragCounter -= 1;
},
openFileUpload() {
this.$refs.fileUpload.click();
},
onFileInputChange(e) {
this.$emit('change', e.target.files);
},
},
};
</script>
<template>
<div
class="gl-w-full gl-relative"
@dragstart.prevent.stop
@dragend.prevent.stop
@dragover.prevent.stop
@dragenter.prevent.stop="ondragenter"
@dragleave.prevent.stop="ondragleave"
@drop.prevent.stop="ondrop"
>
<slot>
<button
class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-3"
@click="openFileUpload"
>
<div
:class="{ 'gl-flex-direction-column': displayAsCard }"
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-text-center"
data-testid="dropzone-area"
>
<gl-icon name="upload" :size="iconStyles.size" :class="iconStyles.class" />
<p class="gl-mb-0">
<slot name="upload-text" :openFileUpload="openFileUpload">
<gl-sprintf :message="__('Drop or %{linkStart}upload%{linkEnd} files to attach')">
<template #link="{ content }">
<gl-link @click.stop="openFileUpload">
{{ content }}
</gl-link>
</template>
</gl-sprintf>
</slot>
</p>
</div>
</button>
<input
ref="fileUpload"
type="file"
name="upload_file"
:accept="validFileMimetypes"
class="hide"
multiple
@change="onFileInputChange"
/>
</slot>
<transition name="upload-dropzone-fade">
<div
v-show="dragging && !enableDragBehavior"
class="card upload-dropzone-border upload-dropzone-overlay gl-w-full gl-h-full gl-absolute gl-display-flex gl-align-items-center gl-justify-content-center gl-p-3 gl-bg-white"
>
<div v-show="!isDragDataValid" class="mw-50 gl-text-center">
<slot name="invalid-drag-data-slot">
<h3 :class="{ 'gl-font-base gl-display-inline': !displayAsCard }">
{{ __('Oh no!') }}
</h3>
<span>{{
__(
'You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico.',
)
}}</span>
</slot>
</div>
<div v-show="isDragDataValid" class="mw-50 gl-text-center">
<slot name="valid-drag-data-slot">
<h3 :class="{ 'gl-font-base gl-display-inline': !displayAsCard }">
{{ __('Incoming!') }}
</h3>
<span>{{ dropToStartMessage }}</span>
</slot>
</div>
</div>
</transition>
</div>
</template>