Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FileUpload templating changes #3033

57 changes: 57 additions & 0 deletions src/components/fileupload/FileContent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<template>
<div v-for="(file, index) of files" :key="file.name + file.type + file.size" class="card flex justify-content-between border-1 surface-border">
<div class="align-self-center">
<img role="presentation" :alt="file.name" :src="file.objectURL" height="50" width="50" />
</div>
<div class="p-fileupload-content-body flex flex-column justify-content-between">
<div>{{ file.name }}</div>
<div class="flex">
<div>{{ formatSize(file.size) }}</div>
<Badge :value="badgeValue" class="ml-2" :severity="badgeSeverity" />
</div>
</div>
<div class="align-self-center">
<Button icon="pi pi-times" @click="$emit('remove', index)" class="p-button-text p-button-secondary" />
</div>
</div>
</template>

<script>
export default {
emits: ['remove'],
props: {
files: {
type: Array,
default: () => []
},
badgeSeverity: {
type: String,
default: 'warning'
},
badgeValue: {
type: String,
default: 'Pending'
}
},
methods: {
formatSize(bytes) {
if (bytes === 0) {
return '0 B';
}

let k = 1000,
dm = 3,
sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
i = Math.floor(Math.log(bytes) / Math.log(k));

return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
}
};
</script>

<style scoped>
.p-fileupload-content-body {
flex: 0.96;
}
</style>
74 changes: 45 additions & 29 deletions src/components/fileupload/FileUpload.vue
Original file line number Diff line number Diff line change
@@ -1,29 +1,24 @@
<template>
<div v-if="isAdvanced" class="p-fileupload p-fileupload-advanced p-component">
<div class="p-fileupload-buttonbar">
<span v-ripple :class="advancedChooseButtonClass" :style="style" @click="choose" @keydown.enter="choose" @focus="onFocus" @blur="onBlur" tabindex="0">
<input ref="fileInput" type="file" @change="onFileSelect" :multiple="multiple" :accept="accept" :disabled="chooseDisabled" />
<span :class="advancedChooseIconClass"></span>
<span class="p-button-label">{{ chooseButtonLabel }}</span>
</span>
<FileUploadButton v-if="showUploadButton" :label="uploadButtonLabel" :icon="uploadIcon" @click="upload" :disabled="uploadDisabled" />
<FileUploadButton v-if="showCancelButton" :label="cancelButtonLabel" :icon="cancelIcon" @click="clear" :disabled="cancelDisabled" />
<input ref="fileInput" type="file" @change="onFileSelect" :multiple="multiple" :accept="accept" :disabled="chooseDisabled" />
<slot name="header" :uploadDisabled="uploadDisabled" :cancelDisabled="cancelDisabled" :choose="headerSlotCallbacks.choose" :upload="headerSlotCallbacks.upload" :clear="headerSlotCallbacks.clear">
<span v-ripple :class="advancedChooseButtonClass" :style="style" @click="choose" @keydown.enter="choose" @focus="onFocus" @blur="onBlur" tabindex="0">
<span :class="advancedChooseIconClass"></span>
<span class="p-button-label">{{ chooseButtonLabel }}</span>
</span>
<FileUploadButton v-if="showUploadButton" :label="uploadButtonLabel" :icon="uploadIcon" @click="upload" :disabled="uploadDisabled" />
<FileUploadButton v-if="showCancelButton" :label="cancelButtonLabel" :icon="cancelIcon" @click="clear" :disabled="cancelDisabled" />
</slot>
</div>
<div ref="content" class="p-fileupload-content" @dragenter="onDragEnter" @dragover="onDragOver" @dragleave="onDragLeave" @drop="onDrop">
<FileUploadProgressBar v-if="hasFiles" :value="progress" />
<FileUploadProgressBar v-if="hasFiles" :value="progress" style="height: 14px" />

<FileUploadMessage v-for="msg of messages" :key="msg" severity="error" @close="onMessageClose">{{ msg }}</FileUploadMessage>
<div v-if="hasFiles" class="p-fileupload-files">
<div v-for="(file, index) of files" :key="file.name + file.type + file.size" class="p-fileupload-row">
<div>
<img v-if="isImage(file)" role="presentation" :alt="file.name" :src="file.objectURL" :width="previewWidth" />
</div>
<div class="p-fileupload-filename">{{ file.name }}</div>
<div>{{ formatSize(file.size) }}</div>
<div>
<FileUploadButton type="button" icon="pi pi-times" @click="remove(index)" />
</div>
</div>
</div>
<slot name="fileContent" :files="files" :uploadedFiles="uploadedFiles" :onUploadedFileRemove="removeUploadedFile" :onFileRemove="remove">
<FileContent v-if="hasFiles" :files="files" @remove="remove" />
<FileContent :files="uploadedFiles" badge-value="Completed" badge-severity="success" @remove="removeUploadedFile" />
</slot>
<div v-if="$slots.content && !hasFiles">
<slot name="content"></slot>
</div>
Expand All @@ -48,10 +43,11 @@ import Message from 'primevue/message';
import ProgressBar from 'primevue/progressbar';
import Ripple from 'primevue/ripple';
import { DomHandler } from 'primevue/utils';
import FileContent from './FileContent.vue';

export default {
name: 'FileUpload',
emits: ['select', 'uploader', 'before-upload', 'progress', 'upload', 'error', 'before-send', 'clear', 'remove'],
emits: ['select', 'uploader', 'before-upload', 'progress', 'upload', 'error', 'before-send', 'clear', 'remove', 'removeUploadedFile'],
props: {
name: {
type: String,
Expand Down Expand Up @@ -155,9 +151,22 @@ export default {
files: [],
messages: [],
focused: false,
progress: null
progress: null,
uploadedFiles: [],
headerSlotCallbacks: {
choose: () => {
this.choose();
},
upload: () => {
this.upload();
},
clear: () => {
this.clear();
}
}
};
},

methods: {
onFileSelect(event) {
if (event.type !== 'drop' && this.isIE11() && this.duplicateIEEvent) {
Expand Down Expand Up @@ -253,6 +262,7 @@ export default {
});
}

this.uploadedFiles.push(...this.files);
this.clear();
}
};
Expand Down Expand Up @@ -382,6 +392,15 @@ export default {
files: this.files
});
},
removeUploadedFile(index) {
let removedFile = this.uploadedFiles.splice(index, 1)[0];

this.uploadedFiles = [...this.uploadedFiles];
this.$emit('removeUploadedFile', {
file: removedFile,
files: this.uploadedFiles
});
},
clearInputElement() {
this.$refs.fileInput.value = '';
},
Expand Down Expand Up @@ -481,15 +500,16 @@ export default {
components: {
FileUploadButton: Button,
FileUploadProgressBar: ProgressBar,
FileUploadMessage: Message
FileUploadMessage: Message,
FileContent
},
directives: {
ripple: Ripple
}
};
</script>

<style>
<style scoped>
.p-fileupload-content {
position: relative;
}
Expand Down Expand Up @@ -520,11 +540,7 @@ export default {
overflow: hidden;
}

.p-button.p-fileupload-choose input[type='file'] {
display: none;
}

.p-fileupload-choose.p-fileupload-choose-selected input[type='file'] {
input[type='file'] {
display: none;
}

Expand Down
124 changes: 120 additions & 4 deletions src/views/fileupload/FileUploadDemo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,74 @@
</template>
</FileUpload>

<h5>Templating</h5>
<FileUpload name="demo[]" url="./upload.php" @upload="onAdvancedUpload($event)" :multiple="true" accept="image/*" :maxFileSize="1000000" @select="onSelectedFiles">
<template #header="{ uploadDisabled, cancelDisabled, choose, upload, clear }">
<div class="flex justify-content-between align-items-center">
<div>
<button @click="choose()" class="p-button p-fileupload-choose p-component p-button-icon-only custom-choose-btn p-button-rounded p-button-outlined">
<span class="p-button-icon p-button-icon-left p-clickable pi pi-fw pi-images"></span>
</button>
<button @click="upload()" type="button" class="p-button p-component custom-upload-btn p-button-success p-button-rounded p-button-outlined p-button-icon-only" :disabled="uploadDisabled">
<span class="p-button-icon p-c pi pi-fw pi-cloud-upload"></span>
</button>
<button @click="onClearTemplatingUpload(clear)" type="button" class="p-button p-component custom-cancel-btn p-button-danger p-button-rounded p-button-outlined p-button-icon-only" :disabled="cancelDisabled">
<span class="p-button-icon p-c pi pi-fw pi-times"></span>
</button>
</div>
<ProgressBar v-if="totalSizePercent > 100" :value="100" class="custom-progress-bar" style="width: 300px; height: 20px; margin-left: auto">{{ totalSize }}B / 1Mb</ProgressBar>
<ProgressBar v-else-if="totalSizePercent <= 100 && totalSizePercent !== 0" :value="totalSizePercent" style="width: 300px; height: 20px; margin-left: auto">{{ totalSize }}B / 1Mb</ProgressBar>
</div>
</template>
<template #fileContent="{ files, uploadedFiles, onUploadedFileRemove, onFileRemove }">
<div v-if="files.length > 0">
<h5>Pending</h5>
<div class="grid p-5">
<div v-for="(file, index) of files" :key="file.name + file.type + file.size" class="col-3 mx-3 card flex flex-column border-1 surface-border align-items-center">
<div>
<img role="presentation" :alt="file.name" :src="file.objectURL" height="50" width="50" />
</div>

<div class="mt-2 flex align-content-center">
<span v-tooltip="file.name" class="fileinput-file-name">{{ file.name }}</span>
</div>
<div class="mt-3">{{ formatSize(file.size) }}</div>
<Badge value="Pending" class="mt-3" severity="warning" />
<div class="mt-3">
<Button icon="pi pi-times" @click="onRemoveTemplatingFile(file, onFileRemove, index)" class="p-button-text p-button-secondary" />
</div>
</div>
</div>
</div>

<div v-if="uploadedFiles.length > 0">
<h5>Completed</h5>
<div class="grid p-5">
<div v-for="(file, index) of uploadedFiles" :key="file.name + file.type + file.size" class="col-3 mx-3 card flex flex-column border-1 surface-border align-items-center">
<div>
<img role="presentation" :alt="file.name" :src="file.objectURL" height="50" width="50" />
</div>
<div class="mt-2 flex align-content-center">
<span v-tooltip="file.name" class="fileinput-file-name">{{ file.name }}</span>
</div>
<div class="mt-3">{{ formatSize(file.size) }}</div>
<Badge value="Completed" class="mt-3" severity="success" />

<div class="mt-3">
<Button icon="pi pi-times" @click="onUploadedFileRemove(index)" class="p-button-text p-button-secondary" />
</div>
</div>
</div>
</div>
</template>
<template #empty>
<div class="flex align-items-center justify-content-center flex-column">
<i class="pi pi-cloud-upload border-1 border-circle border-solid surface-border p-5 text-8xl text-500" />
<p class="mt-4">Drag and drop files to here to upload.</p>
</div>
</template>
</FileUpload>

<h5>Basic</h5>
<FileUpload mode="basic" name="demo[]" url="./upload.php" accept="image/*" :maxFileSize="1000000" @upload="onUpload" />

Expand All @@ -36,20 +104,53 @@

<script>
import FileUploadDoc from './FileUploadDoc';

export default {
data() {
return {
uploadedFiles: []
uploadedFiles: [],
files: [],
totalSize: 0,
totalSizePercent: 0
};
},
methods: {
onAdvancedUpload(event) {
this.uploadedFiles.push(event.files);
onRemoveTemplatingFile(file, onFileRemove, index) {
onFileRemove(index);
this.totalSize -= parseInt(this.formatSize(file.size));
this.totalSizePercent = this.totalSize / 10;
},
onClearTemplatingUpload(clear) {
clear();
this.totalSize = 0;
this.totalSizePercent = 0;
},
onSelectedFiles(event) {
this.files = event.files;
this.files.forEach((file) => {
this.totalSize += parseInt(this.formatSize(file.size));
});

this.totalSizePercent = this.totalSize / 10;
},
onAdvancedUpload() {
this.totalSize = 0;
this.totalSizePercent = 0;
this.$toast.add({ severity: 'info', summary: 'Success', detail: 'File Uploaded', life: 3000 });
},
onUpload() {
this.$toast.add({ severity: 'info', summary: 'Success', detail: 'File Uploaded', life: 3000 });
},
formatSize(bytes) {
if (bytes === 0) {
return '0 B';
}

let k = 1000,
dm = 3,
sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
i = Math.floor(Math.log(bytes) / Math.log(k));

return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
},
components: {
Expand All @@ -62,4 +163,19 @@ export default {
p {
margin: 0;
}
.fileinput-file-name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 1500px) {
.fileinput-file-name {
width: 100px;
}
}
::v-deep(.custom-progress-bar) {
.p-progressbar-value {
background-color: #f44336;
}
}
</style>
Loading