primeng
Version:
PrimeNG is an open source UI library for Angular featuring a rich set of 80+ components, a theme designer, various theme alternatives such as Material, Bootstrap, Tailwind, premium templates and professional support. In addition, it integrates with PrimeB
1,206 lines (1,188 loc) • 65.4 kB
JavaScript
import * as i1 from '@angular/common';
import { isPlatformBrowser, CommonModule } from '@angular/common';
import { HttpClient, HttpEventType } from '@angular/common/http';
import * as i0 from '@angular/core';
import { Injectable, EventEmitter, inject, NgZone, numberAttribute, booleanAttribute, ContentChildren, Input, ViewChild, ContentChild, Output, ViewEncapsulation, ChangeDetectionStrategy, Component, NgModule } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { addClass, removeClass } from '@primeuix/utils';
import { TranslationKeys, SharedModule, PrimeTemplate } from 'primeng/api';
import { BaseComponent } from 'primeng/basecomponent';
import { Button } from 'primeng/button';
import { PlusIcon, UploadIcon, TimesIcon } from 'primeng/icons';
import { Message } from 'primeng/message';
import { ProgressBar } from 'primeng/progressbar';
import { BaseStyle } from 'primeng/base';
const theme = ({ dt }) => `
.p-fileupload input[type="file"] {
display: none;
}
.p-fileupload-advanced {
border: 1px solid ${dt('fileupload.border.color')};
border-radius: ${dt('fileupload.border.radius')};
background: ${dt('fileupload.background')};
color: ${dt('fileupload.color')};
}
.p-fileupload-header {
display: flex;
align-items: center;
padding: ${dt('fileupload.header.padding')};
background: ${dt('fileupload.header.background')};
color: ${dt('fileupload.header.color')};
border-style: solid;
border-width: ${dt('fileupload.header.border.width')};
border-color: ${dt('fileupload.header.border.color')};
border-radius: ${dt('fileupload.header.border.radius')};
gap: ${dt('fileupload.header.gap')};
}
.p-fileupload-content {
border: 1px solid transparent;
display: flex;
flex-direction: column;
gap: ${dt('fileupload.content.gap')};
transition: border-color ${dt('fileupload.transition.duration')};
padding: ${dt('fileupload.content.padding')};
}
.p-fileupload-content .p-progressbar {
width: 100%;
height: ${dt('fileupload.progressbar.height')};
}
.p-fileupload-file-list {
display: flex;
flex-direction: column;
gap: ${dt('fileupload.filelist.gap')};
}
.p-fileupload-file {
display: flex;
flex-wrap: wrap;
align-items: center;
padding: ${dt('fileupload.file.padding')};
border-bottom: 1px solid ${dt('fileupload.file.border.color')};
gap: ${dt('fileupload.file.gap')};
}
.p-fileupload-file:last-child {
border-bottom: 0;
}
.p-fileupload-file-info {
display: flex;
flex-direction: column;
gap: ${dt('fileupload.file.info.gap')};
}
.p-fileupload-file-thumbnail {
flex-shrink: 0;
}
.p-fileupload-file-actions {
margin-left: auto;
}
.p-fileupload-highlight {
border: 1px dashed ${dt('fileupload.content.highlight.border.color')};
}
.p-fileupload-advanced .p-message {
margin-top: 0;
}
.p-fileupload-basic {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
gap: ${dt('fileupload.basic.gap')};
}
`;
const classes = {
root: ({ instance }) => `p-fileupload p-fileupload-${instance.mode} p-component`,
header: 'p-fileupload-header',
pcChooseButton: 'p-fileupload-choose-button',
pcUploadButton: 'p-fileupload-upload-button',
pcCancelButton: 'p-fileupload-cancel-button',
content: 'p-fileupload-content',
fileList: 'p-fileupload-file-list',
file: 'p-fileupload-file',
fileThumbnail: 'p-fileupload-file-thumbnail',
fileInfo: 'p-fileupload-file-info',
fileName: 'p-fileupload-file-name',
fileSize: 'p-fileupload-file-size',
pcFileBadge: 'p-fileupload-file-badge',
fileActions: 'p-fileupload-file-actions',
pcFileRemoveButton: 'p-fileupload-file-remove-button'
};
class FileUploadStyle extends BaseStyle {
name = 'fileupload';
theme = theme;
classes = classes;
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: FileUploadStyle, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: FileUploadStyle });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: FileUploadStyle, decorators: [{
type: Injectable
}] });
/**
*
* FileUpload is an advanced uploader with dragdrop support, multi file uploads, auto uploading, progress tracking and validations.
*
* [Live Demo](https://www.primeng.org/fileupload/)
*
* @module fileuploadstyle
*
*/
var FileUploadClasses;
(function (FileUploadClasses) {
/**
* Class name of the root element
*/
FileUploadClasses["root"] = "p-fileupload";
/**
* Class name of the header element
*/
FileUploadClasses["header"] = "p-fileupload-header";
/**
* Class name of the choose button element
*/
FileUploadClasses["pcChooseButton"] = "p-fileupload-choose-button";
/**
* Class name of the upload button element
*/
FileUploadClasses["pcUploadButton"] = "p-fileupload-upload-button";
/**
* Class name of the cancel button element
*/
FileUploadClasses["pcCancelButton"] = "p-fileupload-cancel-button";
/**
* Class name of the content element
*/
FileUploadClasses["content"] = "p-fileupload-content";
/**
* Class name of the file list element
*/
FileUploadClasses["fileList"] = "p-fileupload-file-list";
/**
* Class name of the file element
*/
FileUploadClasses["file"] = "p-fileupload-file";
/**
* Class name of the file thumbnail element
*/
FileUploadClasses["fileThumbnail"] = "p-fileupload-file-thumbnail";
/**
* Class name of the file info element
*/
FileUploadClasses["fileInfo"] = "p-fileupload-file-info";
/**
* Class name of the file name element
*/
FileUploadClasses["fileName"] = "p-fileupload-file-name";
/**
* Class name of the file size element
*/
FileUploadClasses["fileSize"] = "p-fileupload-file-size";
/**
* Class name of the file badge element
*/
FileUploadClasses["pcFileBadge"] = "p-fileupload-file-badge";
/**
* Class name of the file actions element
*/
FileUploadClasses["fileActions"] = "p-fileupload-file-actions";
/**
* Class name of the file remove button element
*/
FileUploadClasses["pcFileRemoveButton"] = "p-fileupload-file-remove-button";
})(FileUploadClasses || (FileUploadClasses = {}));
/**
* FileUpload is an advanced uploader with dragdrop support, multi file uploads, auto uploading, progress tracking and validations.
* @group Components
*/
class FileUpload extends BaseComponent {
/**
* Name of the request parameter to identify the files at backend.
* @group Props
*/
name;
/**
* Remote url to upload the files.
* @group Props
*/
url;
/**
* HTTP method to send the files to the url such as "post" and "put".
* @group Props
*/
method = 'post';
/**
* Used to select multiple files at once from file dialog.
* @group Props
*/
multiple;
/**
* Comma-separated list of pattern to restrict the allowed file types. Can be any combination of either the MIME types (such as "image/*") or the file extensions (such as ".jpg").
* @group Props
*/
accept;
/**
* Disables the upload functionality.
* @group Props
*/
disabled;
/**
* When enabled, upload begins automatically after selection is completed.
* @group Props
*/
auto;
/**
* Cross-site Access-Control requests should be made using credentials such as cookies, authorization headers or TLS client certificates.
* @group Props
*/
withCredentials;
/**
* Maximum file size allowed in bytes.
* @group Props
*/
maxFileSize;
/**
* Summary message of the invalid file size.
* @group Props
*/
invalidFileSizeMessageSummary = '{0}: Invalid file size, ';
/**
* Detail message of the invalid file size.
* @group Props
*/
invalidFileSizeMessageDetail = 'maximum upload size is {0}.';
/**
* Summary message of the invalid file type.
* @group Props
*/
invalidFileTypeMessageSummary = '{0}: Invalid file type, ';
/**
* Detail message of the invalid file type.
* @group Props
*/
invalidFileTypeMessageDetail = 'allowed file types: {0}.';
/**
* Detail message of the invalid file type.
* @group Props
*/
invalidFileLimitMessageDetail = 'limit is {0} at most.';
/**
* Summary message of the invalid file type.
* @group Props
*/
invalidFileLimitMessageSummary = 'Maximum number of files exceeded, ';
/**
* Inline style of the element.
* @group Props
*/
style;
/**
* Class of the element.
* @group Props
*/
styleClass;
/**
* Width of the image thumbnail in pixels.
* @group Props
*/
previewWidth = 50;
/**
* Label of the choose button. Defaults to PrimeNG Locale configuration.
* @group Props
*/
chooseLabel;
/**
* Label of the upload button. Defaults to PrimeNG Locale configuration.
* @group Props
*/
uploadLabel;
/**
* Label of the cancel button. Defaults to PrimeNG Locale configuration.
* @group Props
*/
cancelLabel;
/**
* Icon of the choose button.
* @group Props
*/
chooseIcon;
/**
* Icon of the upload button.
* @group Props
*/
uploadIcon;
/**
* Icon of the cancel button.
* @group Props
*/
cancelIcon;
/**
* Whether to show the upload button.
* @group Props
*/
showUploadButton = true;
/**
* Whether to show the cancel button.
* @group Props
*/
showCancelButton = true;
/**
* Defines the UI of the component.
* @group Props
*/
mode = 'advanced';
/**
* HttpHeaders class represents the header configuration options for an HTTP request.
* @group Props
*/
headers;
/**
* Whether to use the default upload or a manual implementation defined in uploadHandler callback. Defaults to PrimeNG Locale configuration.
* @group Props
*/
customUpload;
/**
* Maximum number of files that can be uploaded.
* @group Props
*/
fileLimit;
/**
* Style class of the upload button.
* @group Props
*/
uploadStyleClass;
/**
* Style class of the cancel button.
* @group Props
*/
cancelStyleClass;
/**
* Style class of the remove button.
* @group Props
*/
removeStyleClass;
/**
* Style class of the choose button.
* @group Props
*/
chooseStyleClass;
/**
* Used to pass all properties of the ButtonProps to the choose button inside the component.
* @group Props
*/
chooseButtonProps;
/**
* Used to pass all properties of the ButtonProps to the upload button inside the component.
* @group Props
*/
uploadButtonProps = { severity: 'secondary' };
/**
* Used to pass all properties of the ButtonProps to the cancel button inside the component.
* @group Props
*/
cancelButtonProps = { severity: 'secondary' };
/**
* Callback to invoke before file upload is initialized.
* @param {FileBeforeUploadEvent} event - Custom upload event.
* @group Emits
*/
onBeforeUpload = new EventEmitter();
/**
* An event indicating that the request was sent to the server. Useful when a request may be retried multiple times, to distinguish between retries on the final event stream.
* @param {FileSendEvent} event - Custom send event.
* @group Emits
*/
onSend = new EventEmitter();
/**
* Callback to invoke when file upload is complete.
* @param {FileUploadEvent} event - Custom upload event.
* @group Emits
*/
onUpload = new EventEmitter();
/**
* Callback to invoke if file upload fails.
* @param {FileUploadErrorEvent} event - Custom error event.
* @group Emits
*/
onError = new EventEmitter();
/**
* Callback to invoke when files in queue are removed without uploading using clear all button.
* @param {Event} event - Browser event.
* @group Emits
*/
onClear = new EventEmitter();
/**
* Callback to invoke when a file is removed without uploading using clear button of a file.
* @param {FileRemoveEvent} event - Remove event.
* @group Emits
*/
onRemove = new EventEmitter();
/**
* Callback to invoke when files are selected.
* @param {FileSelectEvent} event - Select event.
* @group Emits
*/
onSelect = new EventEmitter();
/**
* Callback to invoke when files are being uploaded.
* @param {FileProgressEvent} event - Progress event.
* @group Emits
*/
onProgress = new EventEmitter();
/**
* Callback to invoke in custom upload mode to upload the files manually.
* @param {FileUploadHandlerEvent} event - Upload handler event.
* @group Emits
*/
uploadHandler = new EventEmitter();
/**
* This event is triggered if an error occurs while loading an image file.
* @param {Event} event - Browser event.
* @group Emits
*/
onImageError = new EventEmitter();
/**
* This event is triggered if an error occurs while loading an image file.
* @param {RemoveUploadedFileEvent} event - Remove event.
* @group Emits
*/
onRemoveUploadedFile = new EventEmitter();
/**
* Template for file.
* @group Templates
*/
fileTemplate;
/**
* Template for header.
* @group Templates
*/
headerTemplate;
/**
* Template for content.
* @group Templates
*/
contentTemplate;
/**
* Template for toolbar.
* @group Templates
*/
toolbarTemplate;
/**
* Template for choose icon.
* @group Templates
*/
chooseIconTemplate;
/**
* Template for file label.
* @group Templates
*/
fileLabelTemplate;
/**
* Template for upload icon.
* @group Templates
*/
uploadIconTemplate;
/**
* Template for cancel icon.
* @group Templates
*/
cancelIconTemplate;
/**
* Template for empty state.
* @group Templates
*/
emptyTemplate;
advancedFileInput;
basicFileInput;
content;
set files(files) {
this._files = [];
for (let i = 0; i < files.length; i++) {
let file = files[i];
if (this.validate(file)) {
if (this.isImage(file)) {
file.objectURL = this.sanitizer.bypassSecurityTrustUrl(window.URL.createObjectURL(files[i]));
}
this._files.push(files[i]);
}
}
}
get files() {
return this._files;
}
get basicButtonLabel() {
if (this.auto || !this.hasFiles()) {
return this.chooseLabel;
}
return this.uploadLabel ?? this.files[0].name;
}
_files = [];
progress = 0;
dragHighlight;
msgs;
uploadedFileCount = 0;
focus;
uploading;
duplicateIEEvent; // flag to recognize duplicate onchange event for file input
translationSubscription;
dragOverListener;
uploadedFiles = [];
sanitizer = inject(DomSanitizer);
zone = inject(NgZone);
http = inject(HttpClient);
_componentStyle = inject(FileUploadStyle);
ngOnInit() {
super.ngOnInit();
this.translationSubscription = this.config.translationObserver.subscribe(() => {
this.cd.markForCheck();
});
}
ngAfterViewInit() {
super.ngAfterViewInit();
if (isPlatformBrowser(this.platformId)) {
if (this.mode === 'advanced') {
this.zone.runOutsideAngular(() => {
if (this.content) {
this.dragOverListener = this.renderer.listen(this.content.nativeElement, 'dragover', this.onDragOver.bind(this));
}
});
}
}
}
_headerTemplate;
_contentTemplate;
_toolbarTemplate;
_chooseIconTemplate;
_uploadIconTemplate;
_cancelIconTemplate;
_emptyTemplate;
_fileTemplate;
_fileLabelTemplate;
templates;
ngAfterContentInit() {
this.templates?.forEach((item) => {
switch (item.getType()) {
case 'header':
this._headerTemplate = item.template;
break;
case 'file':
this._fileTemplate = item.template;
break;
case 'content':
this._contentTemplate = item.template;
break;
case 'toolbar':
this._toolbarTemplate = item.template;
break;
case 'chooseicon':
this._chooseIconTemplate = item.template;
break;
case 'uploadicon':
this._uploadIconTemplate = item.template;
break;
case 'cancelicon':
this._cancelIconTemplate = item.template;
break;
case 'empty':
this._emptyTemplate = item.template;
break;
case 'filelabel':
this._fileLabelTemplate = item.template;
break;
default:
this._fileTemplate = item.template;
break;
}
});
}
basicFileChosenLabel() {
if (this.auto)
return this.chooseButtonLabel;
else if (this.hasFiles()) {
if (this.files && this.files.length === 1)
return this.files[0].name;
return this.config.getTranslation('fileChosenMessage')?.replace('{0}', this.files.length);
}
return this.config.getTranslation('noFileChosenMessage') || '';
}
getTranslation(option) {
return this.config.getTranslation(option);
}
choose() {
this.advancedFileInput?.nativeElement.click();
}
onFileSelect(event) {
if (event.type !== 'drop' && this.isIE11() && this.duplicateIEEvent) {
this.duplicateIEEvent = false;
return;
}
this.msgs = [];
if (!this.multiple) {
this.files = [];
}
let files = event.dataTransfer ? event.dataTransfer.files : event.target.files;
for (let i = 0; i < files.length; i++) {
let file = files[i];
if (!this.isFileSelected(file)) {
if (this.validate(file)) {
if (this.isImage(file)) {
file.objectURL = this.sanitizer.bypassSecurityTrustUrl(window.URL.createObjectURL(files[i]));
}
this.files.push(files[i]);
}
}
}
this.onSelect.emit({ originalEvent: event, files: files, currentFiles: this.files });
// this will check the fileLimit with the uploaded files
this.checkFileLimit(files);
if (this.hasFiles() && this.auto && (this.mode !== 'advanced' || !this.isFileLimitExceeded())) {
this.upload();
}
if (event.type !== 'drop' && this.isIE11()) {
this.clearIEInput();
}
else {
this.clearInputElement();
}
}
isFileSelected(file) {
for (let sFile of this.files) {
if (sFile.name + sFile.type + sFile.size === file.name + file.type + file.size) {
return true;
}
}
return false;
}
isIE11() {
if (isPlatformBrowser(this.platformId)) {
return !!this.document.defaultView['MSInputMethodContext'] && !!this.document['documentMode'];
}
}
validate(file) {
this.msgs = this.msgs || [];
if (this.accept && !this.isFileTypeValid(file)) {
const text = `${this.invalidFileTypeMessageSummary.replace('{0}', file.name)} ${this.invalidFileTypeMessageDetail.replace('{0}', this.accept)}`;
this.msgs.push({
severity: 'error',
text: text
});
return false;
}
if (this.maxFileSize && file.size > this.maxFileSize) {
const text = `${this.invalidFileSizeMessageSummary.replace('{0}', file.name)} ${this.invalidFileSizeMessageDetail.replace('{0}', this.formatSize(this.maxFileSize))}`;
this.msgs.push({
severity: 'error',
text: text
});
return false;
}
return true;
}
isFileTypeValid(file) {
let acceptableTypes = this.accept?.split(',').map((type) => type.trim());
for (let type of acceptableTypes) {
let acceptable = this.isWildcard(type) ? this.getTypeClass(file.type) === this.getTypeClass(type) : file.type == type || this.getFileExtension(file).toLowerCase() === type.toLowerCase();
if (acceptable) {
return true;
}
}
return false;
}
getTypeClass(fileType) {
return fileType.substring(0, fileType.indexOf('/'));
}
isWildcard(fileType) {
return fileType.indexOf('*') !== -1;
}
getFileExtension(file) {
return '.' + file.name.split('.').pop();
}
isImage(file) {
return /^image\//.test(file.type);
}
onImageLoad(img) {
window.URL.revokeObjectURL(img.src);
}
/**
* Uploads the selected files.
* @group Method
*/
uploader() {
if (this.customUpload) {
if (this.fileLimit) {
this.uploadedFileCount += this.files.length;
}
this.uploadHandler.emit({
files: this.files
});
this.cd.markForCheck();
}
else {
this.uploading = true;
this.msgs = [];
let formData = new FormData();
this.onBeforeUpload.emit({
formData: formData
});
for (let i = 0; i < this.files.length; i++) {
formData.append(this.name, this.files[i], this.files[i].name);
}
this.http
.request(this.method, this.url, {
body: formData,
headers: this.headers,
reportProgress: true,
observe: 'events',
withCredentials: this.withCredentials
})
.subscribe((event) => {
switch (event.type) {
case HttpEventType.Sent:
this.onSend.emit({
originalEvent: event,
formData: formData
});
break;
case HttpEventType.Response:
this.uploading = false;
this.progress = 0;
if (event['status'] >= 200 && event['status'] < 300) {
if (this.fileLimit) {
this.uploadedFileCount += this.files.length;
}
this.onUpload.emit({ originalEvent: event, files: this.files });
}
else {
this.onError.emit({ files: this.files });
}
this.uploadedFiles.push(...this.files);
this.clear();
break;
case HttpEventType.UploadProgress: {
if (event['loaded']) {
this.progress = Math.round((event['loaded'] * 100) / event['total']);
}
this.onProgress.emit({ originalEvent: event, progress: this.progress });
break;
}
}
this.cd.markForCheck();
}, (error) => {
this.uploading = false;
this.onError.emit({ files: this.files, error: error });
});
}
}
/**
* Clears the files list.
* @group Method
*/
clear() {
this.files = [];
this.uploadedFileCount = 0;
this.onClear.emit();
this.clearInputElement();
this.msgs = [];
this.cd.markForCheck();
}
/**
* Removes a single file.
* @param {Event} event - Browser event.
* @param {Number} index - Index of the file.
* @group Method
*/
remove(event, index) {
this.clearInputElement();
this.onRemove.emit({ originalEvent: event, file: this.files[index] });
this.files.splice(index, 1);
this.checkFileLimit(this.files);
}
/**
* Removes uploaded file.
* @param {Number} index - Index of the file to be removed.
* @group Method
*/
removeUploadedFile(index) {
let removedFile = this.uploadedFiles.splice(index, 1)[0];
this.uploadedFiles = [...this.uploadedFiles];
this.onRemoveUploadedFile.emit({ file: removedFile, files: this.uploadedFiles });
}
isFileLimitExceeded() {
const isAutoMode = this.auto;
const totalFileCount = isAutoMode ? this.files.length : this.files.length + this.uploadedFileCount;
if (this.fileLimit && this.fileLimit <= totalFileCount && this.focus) {
this.focus = false;
}
return this.fileLimit && this.fileLimit < totalFileCount;
}
isChooseDisabled() {
if (this.auto) {
return this.fileLimit && this.fileLimit <= this.files.length;
}
else {
return this.fileLimit && this.fileLimit <= this.files.length + this.uploadedFileCount;
}
}
checkFileLimit(files) {
this.msgs ??= [];
const hasExistingValidationMessages = this.msgs.length > 0 && this.fileLimit && this.fileLimit < files.length;
if (this.isFileLimitExceeded() || hasExistingValidationMessages) {
const text = `${this.invalidFileLimitMessageSummary.replace('{0}', this.fileLimit.toString())} ${this.invalidFileLimitMessageDetail.replace('{0}', this.fileLimit.toString())}`;
this.msgs.push({
severity: 'error',
text: text
});
}
else {
this.msgs = this.msgs.filter((msg) => !msg.text.includes(this.invalidFileLimitMessageSummary));
}
}
clearInputElement() {
if (this.advancedFileInput && this.advancedFileInput.nativeElement) {
this.advancedFileInput.nativeElement.value = '';
}
if (this.basicFileInput && this.basicFileInput.nativeElement) {
this.basicFileInput.nativeElement.value = '';
}
}
clearIEInput() {
if (this.advancedFileInput && this.advancedFileInput.nativeElement) {
this.duplicateIEEvent = true; //IE11 fix to prevent onFileChange trigger again
this.advancedFileInput.nativeElement.value = '';
}
}
hasFiles() {
return this.files && this.files.length > 0;
}
hasUploadedFiles() {
return this.uploadedFiles && this.uploadedFiles.length > 0;
}
onDragEnter(e) {
if (!this.disabled) {
e.stopPropagation();
e.preventDefault();
}
}
onDragOver(e) {
if (!this.disabled) {
addClass(this.content?.nativeElement, 'p-fileupload-highlight');
this.dragHighlight = true;
e.stopPropagation();
e.preventDefault();
}
}
onDragLeave(event) {
if (!this.disabled) {
removeClass(this.content?.nativeElement, 'p-fileupload-highlight');
}
}
onDrop(event) {
if (!this.disabled) {
removeClass(this.content?.nativeElement, 'p-fileupload-highlight');
event.stopPropagation();
event.preventDefault();
let files = event.dataTransfer ? event.dataTransfer.files : event.target.files;
let allowDrop = this.multiple || (files && files.length === 1);
if (allowDrop) {
this.onFileSelect(event);
}
}
}
onFocus() {
this.focus = true;
}
onBlur() {
this.focus = false;
}
formatSize(bytes) {
const k = 1024;
const dm = 3;
const sizes = this.getTranslation(TranslationKeys.FILE_SIZE_TYPES);
if (bytes === 0) {
return `0 ${sizes[0]}`;
}
const i = Math.floor(Math.log(bytes) / Math.log(k));
const formattedSize = (bytes / Math.pow(k, i)).toFixed(dm);
return `${formattedSize} ${sizes[i]}`;
}
upload() {
if (this.hasFiles())
this.uploader();
}
onBasicUploaderClick() {
this.basicFileInput?.nativeElement.click();
}
onBasicKeydown(event) {
switch (event.code) {
case 'Space':
case 'Enter':
this.onBasicUploaderClick();
event.preventDefault();
break;
}
}
imageError(event) {
this.onImageError.emit(event);
}
getBlockableElement() {
return this.el.nativeElement.children[0];
}
get chooseButtonLabel() {
return this.chooseLabel || this.config.getTranslation(TranslationKeys.CHOOSE);
}
get uploadButtonLabel() {
return this.uploadLabel || this.config.getTranslation(TranslationKeys.UPLOAD);
}
get cancelButtonLabel() {
return this.cancelLabel || this.config.getTranslation(TranslationKeys.CANCEL);
}
get browseFilesLabel() {
return this.config.getTranslation(TranslationKeys.ARIA)[TranslationKeys.BROWSE_FILES];
}
get pendingLabel() {
return this.config.getTranslation(TranslationKeys.PENDING);
}
ngOnDestroy() {
if (this.content && this.content.nativeElement) {
if (this.dragOverListener) {
this.dragOverListener();
this.dragOverListener = null;
}
}
if (this.translationSubscription) {
this.translationSubscription.unsubscribe();
}
super.ngOnDestroy();
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: FileUpload, deps: null, target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.10", type: FileUpload, isStandalone: true, selector: "p-fileupload, p-fileUpload", inputs: { name: "name", url: "url", method: "method", multiple: ["multiple", "multiple", booleanAttribute], accept: "accept", disabled: ["disabled", "disabled", booleanAttribute], auto: ["auto", "auto", booleanAttribute], withCredentials: ["withCredentials", "withCredentials", booleanAttribute], maxFileSize: ["maxFileSize", "maxFileSize", numberAttribute], invalidFileSizeMessageSummary: "invalidFileSizeMessageSummary", invalidFileSizeMessageDetail: "invalidFileSizeMessageDetail", invalidFileTypeMessageSummary: "invalidFileTypeMessageSummary", invalidFileTypeMessageDetail: "invalidFileTypeMessageDetail", invalidFileLimitMessageDetail: "invalidFileLimitMessageDetail", invalidFileLimitMessageSummary: "invalidFileLimitMessageSummary", style: "style", styleClass: "styleClass", previewWidth: ["previewWidth", "previewWidth", numberAttribute], chooseLabel: "chooseLabel", uploadLabel: "uploadLabel", cancelLabel: "cancelLabel", chooseIcon: "chooseIcon", uploadIcon: "uploadIcon", cancelIcon: "cancelIcon", showUploadButton: ["showUploadButton", "showUploadButton", booleanAttribute], showCancelButton: ["showCancelButton", "showCancelButton", booleanAttribute], mode: "mode", headers: "headers", customUpload: ["customUpload", "customUpload", booleanAttribute], fileLimit: ["fileLimit", "fileLimit", (value) => numberAttribute(value, null)], uploadStyleClass: "uploadStyleClass", cancelStyleClass: "cancelStyleClass", removeStyleClass: "removeStyleClass", chooseStyleClass: "chooseStyleClass", chooseButtonProps: "chooseButtonProps", uploadButtonProps: "uploadButtonProps", cancelButtonProps: "cancelButtonProps", files: "files" }, outputs: { onBeforeUpload: "onBeforeUpload", onSend: "onSend", onUpload: "onUpload", onError: "onError", onClear: "onClear", onRemove: "onRemove", onSelect: "onSelect", onProgress: "onProgress", uploadHandler: "uploadHandler", onImageError: "onImageError", onRemoveUploadedFile: "onRemoveUploadedFile" }, providers: [FileUploadStyle], queries: [{ propertyName: "fileTemplate", first: true, predicate: ["file"] }, { propertyName: "headerTemplate", first: true, predicate: ["header"] }, { propertyName: "contentTemplate", first: true, predicate: ["content"] }, { propertyName: "toolbarTemplate", first: true, predicate: ["toolbar"] }, { propertyName: "chooseIconTemplate", first: true, predicate: ["chooseicon"] }, { propertyName: "fileLabelTemplate", first: true, predicate: ["filelabel"] }, { propertyName: "uploadIconTemplate", first: true, predicate: ["uploadicon"] }, { propertyName: "cancelIconTemplate", first: true, predicate: ["cancelicon"] }, { propertyName: "emptyTemplate", first: true, predicate: ["empty"] }, { propertyName: "templates", predicate: PrimeTemplate }], viewQueries: [{ propertyName: "advancedFileInput", first: true, predicate: ["advancedfileinput"], descendants: true }, { propertyName: "basicFileInput", first: true, predicate: ["basicfileinput"], descendants: true }, { propertyName: "content", first: true, predicate: ["content"], descendants: true }], usesInheritance: true, ngImport: i0, template: `
<div [ngClass]="'p-fileupload p-fileupload-advanced p-component'" [ngStyle]="style" [class]="styleClass" *ngIf="mode === 'advanced'" [attr.data-pc-name]="'fileupload'" [attr.data-pc-section]="'root'">
<input
[attr.aria-label]="browseFilesLabel"
#advancedfileinput
type="file"
(change)="onFileSelect($event)"
[multiple]="multiple"
[accept]="accept"
[disabled]="disabled || isChooseDisabled()"
[attr.title]="''"
[attr.data-pc-section]="'input'"
[style.display]="'none'"
/>
<div class="p-fileupload-header">
<ng-container *ngIf="!headerTemplate && !_headerTemplate">
<p-button
[styleClass]="'p-fileupload-choose-button ' + chooseStyleClass"
[disabled]="disabled || isChooseDisabled()"
(focus)="onFocus()"
[label]="chooseButtonLabel"
(blur)="onBlur()"
(onClick)="choose()"
(keydown.enter)="choose()"
[attr.data-pc-section]="'choosebutton'"
[buttonProps]="chooseButtonProps"
>
<input
[attr.aria-label]="browseFilesLabel"
#advancedfileinput
type="file"
(change)="onFileSelect($event)"
[multiple]="multiple"
[accept]="accept"
[disabled]="disabled || isChooseDisabled()"
[attr.title]="''"
[attr.data-pc-section]="'input'"
/>
<span *ngIf="chooseIcon" [class]="chooseIcon" [attr.aria-label]="true" [attr.data-pc-section]="'chooseicon'"></span>
<ng-container *ngIf="!chooseIcon">
<PlusIcon *ngIf="!chooseIconTemplate && !_chooseIconTemplate" [attr.aria-label]="true" [attr.data-pc-section]="'chooseicon'" />
<span *ngIf="chooseIconTemplate || _chooseIconTemplate" [attr.aria-label]="true" [attr.data-pc-section]="'chooseicon'">
<ng-template *ngTemplateOutlet="chooseIconTemplate || _chooseIconTemplate"></ng-template>
</span>
</ng-container>
</p-button>
<p-button
*ngIf="!auto && showUploadButton"
[label]="uploadButtonLabel"
(onClick)="upload()"
[disabled]="!hasFiles() || isFileLimitExceeded()"
[styleClass]="'p-fileupload-upload-button ' + uploadStyleClass"
[buttonProps]="uploadButtonProps"
>
<span *ngIf="uploadIcon" [ngClass]="uploadIcon" [attr.aria-hidden]="true"></span>
<ng-container *ngIf="!uploadIcon">
<UploadIcon *ngIf="!uploadIconTemplate && !_uploadIconTemplate" />
<span *ngIf="uploadIconTemplate || _uploadIconTemplate" [attr.aria-hidden]="true">
<ng-template *ngTemplateOutlet="uploadIconTemplate || _uploadIconTemplate"></ng-template>
</span>
</ng-container>
</p-button>
<p-button *ngIf="!auto && showCancelButton" [label]="cancelButtonLabel" (onClick)="clear()" [disabled]="!hasFiles() || uploading" [styleClass]="'p-fileupload-cancel-button ' + cancelStyleClass" [buttonProps]="cancelButtonProps">
<span *ngIf="cancelIcon" [ngClass]="cancelIcon"></span>
<ng-container *ngIf="!cancelIcon">
<TimesIcon *ngIf="!cancelIconTemplate && !_cancelIconTemplate" [attr.aria-hidden]="true" />
<span *ngIf="cancelIconTemplate || _cancelIconTemplate" [attr.aria-hidden]="true">
<ng-template *ngTemplateOutlet="cancelIconTemplate || _cancelIconTemplate"></ng-template>
</span>
</ng-container>
</p-button>
</ng-container>
<ng-container
*ngTemplateOutlet="
headerTemplate || _headerTemplate;
context: {
$implicit: files,
uploadedFiles: uploadedFiles,
chooseCallback: choose.bind(this),
clearCallback: clear.bind(this),
uploadCallback: upload.bind(this)
}
"
></ng-container>
<ng-container *ngTemplateOutlet="toolbarTemplate || _toolbarTemplate"></ng-container>
</div>
<div #content class="p-fileupload-content" (dragenter)="onDragEnter($event)" (dragleave)="onDragLeave($event)" (drop)="onDrop($event)" [attr.data-pc-section]="'content'">
<p-progressbar [value]="progress" [showValue]="false" *ngIf="hasFiles()"></p-progressbar>
@for (message of msgs; track message) {
<p-message [severity]="message.severity" [text]="message.text"></p-message>
}
<div class="p-fileupload-file-list" *ngIf="hasFiles()">
@if (!fileTemplate && !_fileTemplate) {
<div class="p-fileupload-file" *ngFor="let file of files; let i = index">
<img [src]="file.objectURL" *ngIf="isImage(file)" [width]="previewWidth" (error)="imageError($event)" class="p-fileupload-file-thumbnail" />
<div class="p-fileupload-file-info">
<div class="p-fileupload-file-name">{{ file.name }}</div>
<span class="p-fileupload-file-size">{{ formatSize(file.size) }}</span>
</div>
<div class="p-fileupload-file-actions">
<p-button (onClick)="remove($event, i)" [disabled]="uploading" text rounded severity="danger" [styleClass]="'p-fileupload-file-remove-button ' + removeStyleClass">
<ng-template #icon>
<TimesIcon *ngIf="!cancelIconTemplate && !_cancelIconTemplate" />
<ng-template *ngTemplateOutlet="cancelIconTemplate || _cancelIconTemplate"></ng-template>
</ng-template>
</p-button>
</div>
</div>
}
@if (fileTemplate || _fileTemplate) {
<ng-template ngFor [ngForOf]="files" [ngForTemplate]="fileTemplate || _fileTemplate"></ng-template>
}
</div>
<ng-container
*ngTemplateOutlet="
contentTemplate || _contentTemplate;
context: {
$implicit: files,
uploadedFiles: uploadedFiles,
chooseCallback: choose.bind(this),
clearCallback: clear.bind(this),
removeUploadedFileCallback: removeUploadedFile.bind(this),
removeFileCallback: remove.bind(this),
progress: progress,
messages: msgs
}
"
></ng-container>
@if ((emptyTemplate || _emptyTemplate) && !hasFiles() && !hasUploadedFiles()) {
<ng-container *ngTemplateOutlet="emptyTemplate || _emptyTemplate"></ng-container>
}
</div>
</div>
<div [ngClass]="'p-fileupload p-fileupload-basic p-component'" [class]="styleClass" *ngIf="mode === 'basic'" [attr.data-pc-name]="'fileupload'">
@for (message of msgs; track message) {
<p-message [severity]="message.severity" [text]="message.text"></p-message>
}
<p-button
[styleClass]="'p-fileupload-choose-button ' + chooseStyleClass"
[disabled]="disabled"
[label]="chooseButtonLabel"
[style]="style"
(onClick)="onBasicUploaderClick()"
(keydown)="onBasicKeydown($event)"
[buttonProps]="chooseButtonProps"
>
<ng-template #icon>
@if (hasFiles() && !auto) {
<span *ngIf="uploadIcon" class="p-button-icon p-button-icon-left" [ngClass]="uploadIcon"></span>
<ng-container *ngIf="!uploadIcon">
<UploadIcon *ngIf="!uploadIconTemplate && !_uploadIconTemplate" [styleClass]="'p-button-icon p-button-icon-left'" />
<span *ngIf="_uploadIconTemplate || uploadIconTemplate" class="p-button-icon p-button-icon-left">
<ng-template *ngTemplateOutlet="_uploadIconTemplate || uploadIconTemplate"></ng-template>
</span>
</ng-container>
} @else {
<span *ngIf="chooseIcon" class="p-button-icon p-button-icon-left pi" [ngClass]="chooseIcon"></span>
<ng-container *ngIf="!chooseIcon">
<PlusIcon *ngIf="!chooseIconTemplate && !_chooseIconTemplate" [attr.data-pc-section]="'uploadicon'" />
<ng-template *ngTemplateOutlet="chooseIconTemplate || _chooseIconTemplate"></ng-template>
</ng-container>
}
</ng-template>
<input
[attr.aria-label]="browseFilesLabel"
#basicfileinput
type="file"
[accept]="accept"
[multiple]="multiple"
[disabled]="disabled"
(change)="onFileSelect($event)"
(focus)="onFocus()"
(blur)="onBlur()"
[attr.data-pc-section]="'input'"
/>
</p-button>
@if (!auto) {
@if (!fileLabelTemplate && !_fileLabelTemplate) {
<span [class]="cx('filelabel')">
{{ basicFileChosenLabel() }}
</span>
} @else {
<ng-container *ngTemplateOutlet="fileLabelTemplate || _fileLabelTemplate; context: { $implicit: files }"></ng-container>
}
}
</div>
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: ProgressBar, selector: "p-progressBar, p-progressbar, p-progress-bar", inputs: ["value", "showValue", "styleClass", "valueStyleClass", "style", "unit", "mode", "color"] }, { kind: "component", type: Message, selector: "p-message", inputs: ["severity", "text", "escape", "style", "styleClass", "closable", "icon", "closeIcon", "life", "showTransitionOptions", "hideTransitionOptions", "size", "variant"], outputs: ["onClose"] }, { kind: "component", type: PlusIcon, selector: "PlusIcon" }, { kind: "component", type: UploadIcon, selector: "UploadIcon" }, { kind: "component", type: TimesIcon, selector: "TimesIcon" }, { kind: "ngmodule", type: SharedModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: FileUpload, decorators: [{
type: Component,
args: [{
selector: 'p-fileupload, p-fileUpload',
standalone: true,
imports: [CommonModule, Button, ProgressBar, Message, PlusIcon, UploadIcon, TimesIcon, SharedModule],
template: `
<div [ngClass]="'p-fileupload p-fileupload-advanced p-component'" [ngStyle]="style" [class]="styleClass" *ngIf="mode === 'advanced'" [attr.data-pc-name]="'fileupload'" [attr.data-pc-section]="'root'">
<input
[attr.aria-label]="browseFilesLabel"
#advancedfileinput
type="file"
(change)="onFileSelect($event)"
[multiple]="multiple"
[accept]="accept"
[disabled]="disabled || isChooseDisabled()"
[attr.title]="''"
[attr.data-pc-section]="'input'"
[style.display]="'none'"
/>
<div class="p-fileupload-header">
<ng-container *ngIf="!headerTemplate && !_headerTemplate">
<p-button
[styleClass]="'p-fileupload-choose-button ' + chooseStyleClass"
[disabled]="disabled || isChooseDisabled()"
(focus)="onFocus()"
[label]="chooseButtonLabel"
(blur)="onBlur()"
(onClick)="choose()"
(keydown.enter)="choose()"
[attr.data-pc-section]="'choosebutton'"
[buttonProps]="chooseButtonProps"
>
<input
[attr.aria-label]="browseFilesLabel"
#advancedfileinput
type="file"
(change)="onFileSelect($event)"
[multiple]="multiple"
[accept]="accept"
[disabled]="disabled || isChooseDisabled()"
[attr.title]="''"
[attr.data-pc-section]="'input'"
/>
<span *ngIf="chooseIcon" [class]="chooseIcon" [attr.aria-label]="true" [attr.data-pc-section]="'chooseicon'"></span>
<ng-container *ngIf="!chooseIcon">
<PlusIcon *ngIf="!chooseIconTemplate && !_chooseIconTemplate" [attr.aria-label]="true" [attr.data-pc-section]="'chooseicon'" />
<span *ngIf="chooseIconTemplate || _chooseIconTemplate" [attr.aria-label]="true" [attr.data-pc-secti