Skip to content
This repository has been archived by the owner on Jan 29, 2019. It is now read-only.

Commit

Permalink
feat: added upload-area attribute/listener bindings
Browse files Browse the repository at this point in the history
  • Loading branch information
alexsasharegan committed Sep 25, 2017
1 parent 2838c7c commit 58432c6
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 15 deletions.
17 changes: 12 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,21 @@ A Vue.js drag & drop uploader based on Dropzone.js (`~26kB`, `~9kB` gzipped). Ma

## Features

Vue-Transmit is a fork of Dropzone.js that has been completely rewritten in ES6 and Vue.js. Instead of creating a Vue wrapper component that duplicates all of the methods and event logic from Dropzone, Vue-Transmit implements them directly on the component. This cuts down on library size, and since Vue already has a first-class event emitter, we get even more space savings.
Vue-Transmit is a fork of Dropzone.js that has been completely rewritten in ES6 for Vue.js. Instead of creating a Vue wrapper component that duplicates and proxies all of the methods and event logic between Dropzone and the component, Vue-Transmit implements them directly from the component. This cuts down on library size and offers a much tighter integration.

Also, a special File class has been written (`VTransmitFile`) to add useful information not present in the native File object (dimensions, upload stats, etc.). This is necessary for Vue to register these files reactively, since the native File object's properties are read-only.
Vue-Transmit takes an event-based approach to the upload cycle. Instead of passing callbacks to the component via an options object, use the template event binding syntax (`<vue-transmit @event="callback" />`). All events strictly conform to kebab-casing, including events proxied off native events (e.g. `dragover => @drag-over`). This is for uniformity and so events can be easily distinguished.

In order to comply with Vue.js reactivity, an object's properties must be defined up front and be configurable. A special File class has been written (`VTransmitFile`) to register file objects from uploads reactively, since the native File object's properties are read-only. This class also adds useful information not present in the native File object (dimensions, upload stats, etc.).

- HTML 5 file uploads
- Completely written in Vue.js&mdash;no wrapper components
- Emits upload lifecycle events (accepted, sending, progress, success, etc.)
- Image thumbnail previews
- Support for concurrent uploads
- Completely written in Vue.js&mdash;no wrapper components
- Scoped slots allow for fully customizable styling
- Written in modern ES6 with modules

_\* Note: this library uses some built-ins like `Array.from` & `Array.prototype.includes` that require a polyfill. All other ESNext language features (arrow fns, for of, etc.) are transpiled with babel._

![upload-example](./docs/vue-transmit-10fps.gif)

Expand All @@ -31,6 +36,8 @@ Also, a special File class has been written (`VTransmitFile`) to add useful info
|---|---|---|
|tag|String|"div"|
|uploadAreaClasses|Array, Object, String|null|
|uploadAreaAttrs|Object|{}|
|uploadAreaListeners|Object|{}|
|url|String|undefined|
|method|String|"post"|
|withCredentials|Boolean|false|
Expand Down Expand Up @@ -167,13 +174,13 @@ This slot receives a number of props:
tag="section"
v-bind="options"
drop-zone-classes="bg-faded"
ref="uploader"
>
ref="uploader">
<div class="d-flex align-items-center justify-content-center w-100"
style="height:50vh; border-radius: 1rem;">
<button class="btn btn-primary"
@click="triggerBrowse">Upload Files</button>
</div>
<!-- Scoped slot -->
<template slot="files" scope="props">
<div v-for="(file, i) in props.files" :key="file.id" :class="{'mt-5': i === 0}">
<div class="media">
Expand Down
43 changes: 33 additions & 10 deletions src/components/VueTransmit.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
<div class="v-transmit__upload-area"
:class="[isDraggingClass, uploadAreaClasses]"
draggable="true"
v-bind="uploadAreaAttrs"
v-on="uploadAreaListeners"
@click="handleClickUploaderAction"
@dragstart="$emit('drag-start', $event)"
@dragstart="handleDragStart"
@dragend="handleDragEnd"
@dragenter.prevent.stop="handleDragEnter"
@dragover.prevent.stop="handleDragOver"
Expand Down Expand Up @@ -97,12 +99,6 @@ export default {
inputEl() {
return this.$refs.hiddenFileInput
},
isDraggingClass() {
return {
"v-transmit__upload-area--is-dragging": this.dragging,
[this.dragClass]: this.dragging,
}
},
filesToAccept() {
return this.acceptedFileTypes.join(",")
},
Expand All @@ -128,10 +124,17 @@ export default {
return this.getFilesWithStatus(STATUSES.UPLOADING, STATUSES.QUEUED)
},
maxFilesReached() {
// Loose equality checks null && undefined
return this.maxFiles != null && this.acceptedFiles.length >= this.maxFiles
},
maxFilesReachedClass() {
return this.maxFilesReached ? 'v-transmit__max-files--reached' : null
return this.maxFilesReached ? "v-transmit__max-files--reached" : null
},
isDraggingClass() {
return {
"v-transmit__upload-area--is-dragging": this.dragging,
[this.dragClass]: this.dragging,
}
},
isUploading() {
return this.uploadingFiles.length > 0
Expand Down Expand Up @@ -164,8 +167,7 @@ export default {
return this.files.filter(f => statuses.includes(f.status))
},
onFileInputChange(e) {
const files = Array.from(this.$refs.hiddenFileInput.files).map(this.addFile)
this.$emit('added-files', files)
this.$emit('added-files', Array.from(this.$refs.hiddenFileInput.files).map(this.addFile))
},
addFile(file) {
const vTransmitFile = VTransmitFile.fromNativeFile(file)
Expand Down Expand Up @@ -585,6 +587,15 @@ export default {
return false
},
/**
* @param {DragEvent} e
*/
handleDragStart(e) {
this.$emit('drag-start', e)
},
/**
* @param {DragEvent} e
*/
handleDragOver(e) {
this.dragging = true
let effect
Expand All @@ -595,18 +606,30 @@ export default {
e.dataTransfer.dropEffect = effect === 'move' || effect === 'linkMove' ? 'move' : 'copy'
this.$emit('drag-over', e)
},
/**
* @param {DragEvent} e
*/
handleDragEnter(e) {
this.dragging = true
this.$emit('drag-enter', e)
},
/**
* @param {DragEvent} e
*/
handleDragLeave(e) {
this.dragging = false
this.$emit('drag-leave', e)
},
/**
* @param {DragEvent} e
*/
handleDragEnd(e) {
this.dragging = false
this.$emit('drag-end', e)
},
/**
* @param {DragEvent} e
*/
onDrop(e) {
this.dragging = false
if (!e.dataTransfer) {
Expand Down
8 changes: 8 additions & 0 deletions src/core/props.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ export default {
type: [Array, Object, String],
default: null
},
uploadAreaAttrs: {
type: Object,
default: () => ({})
},
uploadAreaListeners: {
type: Object,
default: () => ({})
},
dragClass: {
type: String,
default: null
Expand Down

0 comments on commit 58432c6

Please sign in to comment.