@ng-prime-extensions/px-uploader
Version:
PrimeNG uploader extension
562 lines • 51.5 kB
JavaScript
import * as i0 from '@angular/core';
import { InjectionToken, inject, Pipe, EventEmitter, Input, ViewChild, Output, Optional, Inject, Component } from '@angular/core';
import * as i2 from 'primeng/config';
import { PrimeNG } from 'primeng/config';
import { catchError, of } from 'rxjs';
import * as i1 from '@angular/common/http';
import { HttpEventType } from '@angular/common/http';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { NgClass, NgTemplateOutlet } from '@angular/common';
import { ProgressBar } from 'primeng/progressbar';
import { Button } from 'primeng/button';
var PxUploaderIntl;
(function (PxUploaderIntl) {
PxUploaderIntl[PxUploaderIntl["NO_FILES_MSG"] = 0] = "NO_FILES_MSG";
PxUploaderIntl[PxUploaderIntl["UNKNOWN_ERROR"] = 1] = "UNKNOWN_ERROR";
PxUploaderIntl[PxUploaderIntl["INVALID_EXTENSION"] = 2] = "INVALID_EXTENSION";
PxUploaderIntl[PxUploaderIntl["MAX_SIZE_EXCEEDED"] = 3] = "MAX_SIZE_EXCEEDED";
PxUploaderIntl[PxUploaderIntl["ALLOWED_EXTENSIONS"] = 4] = "ALLOWED_EXTENSIONS";
PxUploaderIntl[PxUploaderIntl["MAX_FILE_SIZE"] = 5] = "MAX_FILE_SIZE";
PxUploaderIntl[PxUploaderIntl["IMAGE_SIZE_CHECK_FAILED"] = 6] = "IMAGE_SIZE_CHECK_FAILED";
PxUploaderIntl[PxUploaderIntl["IMAGE_SIZE_CHECK_TEXT"] = 7] = "IMAGE_SIZE_CHECK_TEXT";
})(PxUploaderIntl || (PxUploaderIntl = {}));
const PX_UPLOADER_INTL = new InjectionToken('px-uploader i18n strings');
class PxFileSizePipe {
constructor() {
this.units = inject(PrimeNG).translation.fileSizeTypes || ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
}
/**
* Converts bytes to a human-readable string
* @param bytes
* @param threshold Threshold after which the unit is increased. e.g. 1024B -> 1KB
* @param digits The number of fraction digits shown. E.g. 2.25MB
*/
transform(bytes, threshold = 1024, digits = 2) {
const units = this.units;
if (Math.abs(bytes) < threshold) {
return bytes + '' + units[0];
}
const maxUnits = units.length - 1;
let unit = 0;
const ra = 10 ** 1;
do {
bytes /= threshold;
++unit;
} while (Math.round(Math.abs(bytes) * ra) / ra >= threshold && unit < maxUnits);
return bytes.toFixed(digits) + ' ' + units[unit];
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PxFileSizePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.1.4", ngImport: i0, type: PxFileSizePipe, isStandalone: true, name: "pxFileSize" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PxFileSizePipe, decorators: [{
type: Pipe,
args: [{
standalone: true,
name: 'pxFileSize'
}]
}], ctorParameters: () => [] });
class PxUploaderComponent {
constructor(http, changeDetector, primeNGConfig, intl) {
this.http = http;
this.changeDetector = changeDetector;
this.primeNGConfig = primeNGConfig;
/**
* Uploader label.
*/
this.label = '';
/**
* Allow multiple files?
*/
this.multiple = false;
/**
* Determines how the added files are rendered.
*/
this.displayAs = 'list';
/**
* A list of allowed file extensions(lowercase), if empty the extension check is skipped.
* Example: ['txt', 'xlsx', ...]
*/
this.allowedExtensions = [];
/**
* Maximum file size in bytes. Default: 5MB.
*/
this.maxFileSize = 5 * 1024 * 1024;
/**
* How many files should we upload in parallel?
*/
this.maxParallelUploads = 5;
/**
* Whether to show a preview when the uploaded file is an image.
*/
this.showImagePreview = true;
/**
* Displays the file name
*/
this.showFileName = true;
/**
* Displays the file size in human-readable form
*/
this.showFileSize = true;
/**
* If the file is an image, displays the image height and width
*/
this.showImageSize = true;
/**
* Event triggered when all the files in the upload queue are uploaded.
*/
this.uploadFinished = new EventEmitter();
this._value = [];
this.filesQueue = [];
this.maxImageSizeText = '';
this.allowedExtensionsText = '';
this.hasFiles = false;
this.dragoverEventActive = false;
this.PxUploaderIntl = PxUploaderIntl;
this._disabled = false;
this.onChange = (value) => value;
this.onTouched = () => { };
this.isTouched = false;
this.isAngularFormValue = false;
this.uploadingFilesCount = 0;
this.processingUploadQueue = false;
this.viewInit = false;
this.intl = Object.assign({
[PxUploaderIntl.NO_FILES_MSG]: 'Drag & drop your files or click to browse',
[PxUploaderIntl.UNKNOWN_ERROR]: 'Upload Failed!',
[PxUploaderIntl.INVALID_EXTENSION]: 'Invalid file extension!',
[PxUploaderIntl.MAX_SIZE_EXCEEDED]: 'File size is too big!',
[PxUploaderIntl.ALLOWED_EXTENSIONS]: 'Allowed Extensions',
[PxUploaderIntl.MAX_FILE_SIZE]: 'Maximum file size',
[PxUploaderIntl.IMAGE_SIZE_CHECK_FAILED]: 'Maximum image width or height exceeded.',
[PxUploaderIntl.IMAGE_SIZE_CHECK_TEXT]: 'Allowed image dimensions (HxW)',
}, intl || {});
}
ngOnChanges(changes) {
if (changes['allowedExtensions']) {
this.allowedExtensionsText = this.allowedExtensions.join(', ');
!changes['allowedExtensions'].firstChange && this.changeDetector.detectChanges();
}
if (changes['maxImageSize']) {
const maxImageSize = this.maxImageSize;
this.maxImageSizeText = (maxImageSize?.height || '') + 'x' + (maxImageSize?.width || '');
!changes['maxImageSize'].firstChange && this.changeDetector.detectChanges();
}
if ((changes['multiple'] && !changes['multiple'].firstChange)
|| (changes['buttons'] && !changes['buttons'].firstChange)) {
this.changeDetector.detectChanges();
}
}
ngAfterViewInit() {
this.viewInit = true;
}
/**
* Opens the file input dialog.
*/
openFilesInput() {
this.getOpenFilesInputFn()();
}
/**
* Will always be null if there are no files (regardless of multiple attribute)
*/
get value() {
if (this.multiple) {
return this._value.length ? this._value : null;
}
return this._value[0] || null;
}
set value(value) {
if (value === null) {
this._value = [];
value = [];
}
else {
if (!Array.isArray(value)) {
value = [value];
}
else if (!this.multiple && value.length) {
value = [value[0]];
}
this._value = value;
}
this.hasFiles = value.length > 0;
if (!this.isAngularFormValue) {
this.onChange(this.value);
this.onTouched();
}
this.changeDetector.detectChanges();
}
get disabled() {
return this._disabled;
}
set disabled(state) {
this._disabled = state;
if (this.viewInit) {
this.changeDetector.detectChanges();
}
}
setDisabledState(isDisabled) {
this.disabled = isDisabled;
}
writeValue(value) {
if (!value) {
return;
}
this.isAngularFormValue = true;
this.value = value;
this.isAngularFormValue = false;
}
registerOnChange(onChange) {
this.onChange = onChange;
}
registerOnTouched(onTouched) {
this.onTouched = () => !this.isTouched && onTouched;
}
onFilesAdded(addedFiles) {
if (!(addedFiles && addedFiles.length)) {
return;
}
if (!this.multiple) {
this.filesQueue[0]?._internal.httpSubscription?.unsubscribe();
this.filesQueue = [];
this._value = [];
this.changeDetector.detectChanges();
}
const files = this.multiple ? addedFiles : [addedFiles[0]];
for (let i = files.length; i--;) {
const file = files[i];
this.checkFile(file).then(pxFile => {
this.filesQueue.push(pxFile);
this.processUploadQueue().then();
this.changeDetector.detectChanges();
});
}
this.fileInput.nativeElement.value = '';
}
async processUploadQueue() {
if (this.processingUploadQueue || this.uploadingFilesCount === this.maxParallelUploads) {
return;
}
this.processingUploadQueue = true;
const queuedFiles = this.filesQueue;
const queueLength = queuedFiles.length;
let nextUploadFile = null;
let fileUploadInProgress = false;
for (let i = 0; i < queueLength; ++i) {
const queuedFile = queuedFiles[i];
if (queuedFile._internal.uploadError) {
continue;
}
if (queuedFile._internal.isUploading) {
fileUploadInProgress = true;
continue;
}
nextUploadFile = queuedFile;
break;
}
this.processingUploadQueue = false;
if (nextUploadFile) {
this.uploadFile(nextUploadFile).then((sub) => {
nextUploadFile._internal.httpSubscription = sub;
if (this.uploadingFilesCount < this.maxParallelUploads) {
this.processUploadQueue().then();
}
});
}
else if (!fileUploadInProgress) {
this.uploadFinished.emit(this.value);
}
}
async uploadFile(queuedPxFile) {
++this.uploadingFilesCount;
const formData = new FormData();
const file = queuedPxFile._internal.uploadedFile;
const saveEndpoint = this.saveEndpoint;
const headers = saveEndpoint.headers;
queuedPxFile._internal.isUploading = true;
formData.append("file", file);
const requestHeaders = headers instanceof Promise ? await headers : headers;
return this.http.request(saveEndpoint.requestMethod || 'POST', saveEndpoint.url, {
reportProgress: true,
observe: 'events',
body: formData,
headers: requestHeaders
}).pipe(catchError(() => {
queuedPxFile._internal.uploadError = this.intl[PxUploaderIntl.UNKNOWN_ERROR];
queuedPxFile._internal.isUploading = false;
delete queuedPxFile._internal.uploadProgress;
--this.uploadingFilesCount;
this.changeDetector.detectChanges();
return of({});
})).subscribe((event) => {
switch (event.type) {
case HttpEventType.UploadProgress:
let progress = Math.floor(event.loaded / (event.total || 1) * 100);
if (progress > 100) {
progress = 100;
}
queuedPxFile._internal.uploadProgress = progress;
this.changeDetector.detectChanges();
break;
case HttpEventType.Response:
--this.uploadingFilesCount;
if (!this.multiple) {
this._value = [];
}
const uploadedFiles = this._value;
const uploadQueue = this.filesQueue;
const uploadedFileQueueIndex = uploadQueue.indexOf(queuedPxFile);
if (uploadedFileQueueIndex > -1) {
uploadQueue.splice(uploadedFileQueueIndex, 1);
}
queuedPxFile.metaData = event.body || {};
delete queuedPxFile._internal;
uploadedFiles.push(queuedPxFile);
this.value = uploadedFiles;
this.processUploadQueue();
break;
}
});
}
removeFileFromQueue(fileIndex) {
this.filesQueue.splice(fileIndex, 1);
}
/**
* Removes an uploaded file by index
* @param fileIndex
*/
async removeFile(fileIndex) {
const file = this._value.splice(fileIndex, 1)[0];
this.value = this._value;
if (this.deleteEndpoint) {
const deleteEndpoint = this.deleteEndpoint;
const requestHeaders = deleteEndpoint.headers instanceof Promise ? await deleteEndpoint.headers : deleteEndpoint.headers;
this.http.request(deleteEndpoint.url, deleteEndpoint.requestMethod || 'DELETE', {
body: [file],
headers: requestHeaders
}).subscribe();
}
}
onDragEnter(evt) {
evt.preventDefault();
this.dragoverEventActive = true;
}
onDragover(evt) {
evt.preventDefault();
if (!this.dragoverEventActive) {
this.dragoverEventActive = true;
}
}
onDragLeave(evt) {
evt.preventDefault();
this.dragoverEventActive = false;
}
onFilesDropped(evt) {
evt.preventDefault();
const files = evt.dataTransfer?.files;
if (files) {
this.onFilesAdded(files);
}
this.dragoverEventActive = false;
}
getRetryUploadFn(queuedFileIndex) {
return () => {
const queuedFile = this.filesQueue[queuedFileIndex];
if (queuedFile) {
delete queuedFile._internal.uploadError;
this.processUploadQueue().then();
}
};
}
getRemoveFileFn(isQueued, fileIndex) {
return () => {
isQueued ? this.removeFileFromQueue(fileIndex) : this.removeFile(fileIndex);
};
}
getOpenFilesInputFn() {
return () => this.fileInput.nativeElement.click();
}
async checkFile(file) {
const pxFile = {
name: file.name,
size: file.size,
type: file.type,
_internal: {
canRetryUpload: true,
isUploading: false,
uploadedFile: file,
uploadProgress: 0,
}
};
if (file.size > this.maxFileSize) {
return this.setPxFileError(PxUploaderIntl.MAX_SIZE_EXCEEDED, pxFile);
}
const allowedExtensions = this.allowedExtensions;
if (allowedExtensions.length) {
const dotIndex = file.name.lastIndexOf('.');
// File extension valid?
if (!((dotIndex > 0) && (allowedExtensions.indexOf(file.name.substring(dotIndex + 1).toLowerCase()) !== -1))) {
return this.setPxFileError(PxUploaderIntl.INVALID_EXTENSION, pxFile);
}
}
if (file.type.includes('image')) {
const imageDetails = await this.getImageDetails(file);
pxFile.imagePreviewUrl = imageDetails.previewUrl;
pxFile.imageSize = {
width: imageDetails.width,
height: imageDetails.height,
};
if (!this.checkImage(pxFile)) {
return pxFile;
}
}
return pxFile;
}
checkImage(pxFile) {
if (this.maxImageSize) {
const imageHeight = pxFile.imageSize?.height || 0;
const imageWidth = pxFile.imageSize?.width || 0;
const maxImageSize = this.maxImageSize;
let imageCheckFail = false;
if (maxImageSize.width !== undefined && maxImageSize.height !== undefined) {
if (maxImageSize.strict) {
imageCheckFail = maxImageSize.height !== imageHeight || maxImageSize.width !== imageWidth;
}
else {
imageCheckFail = maxImageSize.height > imageHeight || maxImageSize.width > imageWidth;
}
}
else if (maxImageSize.width !== undefined) {
if (maxImageSize.strict) {
imageCheckFail = maxImageSize.width !== imageWidth;
}
else {
imageCheckFail = maxImageSize.width > imageWidth;
}
}
else if (maxImageSize.height !== undefined) {
if (maxImageSize.strict) {
imageCheckFail = maxImageSize.height !== imageHeight;
}
else {
imageCheckFail = maxImageSize.height > imageHeight;
}
}
if (imageCheckFail) {
this.setPxFileError(PxUploaderIntl.IMAGE_SIZE_CHECK_FAILED, pxFile);
}
}
return true;
}
setPxFileError(errorKey, pxFile) {
const internalProps = pxFile._internal;
internalProps.canRetryUpload = false;
internalProps.uploadError = this.intl[errorKey];
return pxFile;
}
getImageDetails(file) {
return new Promise((resolve) => {
createImageBitmap(file).then(bitmap => {
resolve({
width: bitmap.width,
height: bitmap.height,
previewUrl: URL.createObjectURL(file)
});
}).catch(() => {
resolve({
width: 0,
height: 0,
previewUrl: ''
});
});
});
}
ngOnDestroy() {
this.uploadFinished.complete();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PxUploaderComponent, deps: [{ token: i1.HttpClient }, { token: i0.ChangeDetectorRef }, { token: i2.PrimeNG }, { token: PX_UPLOADER_INTL, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.4", type: PxUploaderComponent, isStandalone: true, selector: "px-uploader", inputs: { saveEndpoint: "saveEndpoint", deleteEndpoint: "deleteEndpoint", label: "label", multiple: "multiple", displayAs: "displayAs", allowedExtensions: "allowedExtensions", maxFileSize: "maxFileSize", maxParallelUploads: "maxParallelUploads", showImagePreview: "showImagePreview", maxImageSize: "maxImageSize", icons: "icons", buttons: "buttons", infoMessage: "infoMessage", showFileName: "showFileName", showFileSize: "showFileSize", showImageSize: "showImageSize", value: "value", disabled: "disabled" }, outputs: { uploadFinished: "uploadFinished" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: PxUploaderComponent
}
], viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\n [ngClass]=\"{'px-uploader': true, 'dragover': dragoverEventActive, 'no-label': label === '', 'px-uploader-disabled': disabled}\"\n (dragleave)=\"onDragLeave($event)\"\n (dragover)=\"onDragover($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (drop)=\"onFilesDropped($event)\"\n>\n <label class=\"px-uploader-label\">{{ label }}</label>\n <div class=\"px-uploader-info\">\n <div style=\"display: flex; align-items: center; flex-wrap: wrap;\">\n @if(buttons?.uploadButton)\n {\n <ng-container [ngTemplateOutlet]=\"buttons!.uploadButton!\" [ngTemplateOutletContext]=\"{icon: icons?.uploadButton || 'pi pi-upload', onClick: getOpenFilesInputFn(), disabled: disabled}\"></ng-container>\n }\n @else\n {\n <p-button class=\"px-uploader-upload-button\" [icon]=\"icons?.uploadButton || 'pi pi-upload'\" [label]=\"primeNGConfig.translation.upload\" [disabled]=\"disabled\" (onClick)=\"openFilesInput()\"></p-button>\n }\n <div class=\"px-uploader-info-primary\">\n <label class=\"px-uploader-msg\">{{ intl[PxUploaderIntl.NO_FILES_MSG] }}</label>\n </div>\n </div>\n <div class=\"px-uploader-info-secondary\">\n @if(allowedExtensions.length)\n {\n <span class=\"px-uploader-info-secondary-msg\">\n {{ intl[PxUploaderIntl.ALLOWED_EXTENSIONS] + ': ' + allowedExtensionsText }}\n </span>\n }\n\n <span>{{ intl[PxUploaderIntl.MAX_FILE_SIZE] + ': ' + (maxFileSize | pxFileSize) }}</span>\n\n @if(maxImageSizeText)\n {\n <span class=\"px-uploader-info-secondary-msg\">{{ intl[PxUploaderIntl.IMAGE_SIZE_CHECK_TEXT] + ': ' + maxImageSizeText }}</span>\n }\n\n @if(infoMessage)\n {\n <span class=\"px-uploader-info-secondary-info-msg\">{{infoMessage}}</span>\n }\n </div>\n </div>\n\n <div class=\"px-uploader-files\">\n @for (uploadedFile of _value; track $index)\n {\n <ng-container\n [ngTemplateOutlet]=\"file\"\n [ngTemplateOutletContext]=\"{file: uploadedFile, isQueuedFile: false, fileIndex: $index}\"></ng-container>\n }\n\n @for (queuedFile of filesQueue; track $index)\n {\n <ng-container\n [ngTemplateOutlet]=\"file\"\n [ngTemplateOutletContext]=\"{file: queuedFile, isQueuedFile: true, fileIndex: $index}\"></ng-container>\n }\n </div>\n</div>\n\n<input\n #fileInput\n style=\"display: none;\"\n type=\"file\"\n [accept]=\"allowedExtensions.length ? '.' + allowedExtensions.join(',.') : null\"\n [multiple]=\"multiple\"\n (change)=\"onFilesAdded($any($event.target).files)\">\n\n<ng-template #file let-file=\"file\" let-isQueued=\"isQueuedFile\" let-fileIndex=\"fileIndex\">\n <div class=\"px-uploader-file\" [ngClass]=\"[displayAs, showImagePreview && file.imagePreviewUrl ? 'preview' : '', file._internal?.uploadError ? 'px-uploader-error' : '', isQueued ? 'queued' : '']\">\n @if(showImagePreview && file.imagePreviewUrl)\n {\n <img\n class=\"px-uploader-file-preview\"\n [src]=\"file.imagePreviewUrl\" alt=\"\">\n }\n <div class=\"px-uploader-file-header-container\">\n @if(isQueued && !file._internal?.uploadError)\n {\n <p-progressBar\n [mode]=\"file._internal.uploadProgress > 0 && file._internal.uploadProgress !== 100 ? 'determinate' : 'indeterminate'\"\n [value]=\"file._internal.uploadProgress || 0\"\n [style]=\"{ height: '1rem', 'border-bottom-right-radius': 0, 'border-bottom-left-radius': 0 }\"\n ></p-progressBar>\n }\n\n <div class=\"px-uploader-file-header\">\n <div class=\"px-uploader-file-info\">\n @if(isQueued)\n {\n <i class=\"px-uploader-file-status-icon\" [class]=\"file._internal.isUploading ? icons?.fileStatusIconUploading || 'pi pi-spin pi-spinner' : (file._internal.uploadError !== undefined ? icons?.fileStatusIconError || 'pi pi-times-circle' : icons?.fileStatusIconQueued || 'pi pi-hourglass')\"></i>\n }\n\n <div class=\"px-uploader-file-info flex-column\">\n @if(showFileName)\n {\n <span class=\"px-uploader-file-info-primary\">{{file.name}}</span>\n }\n\n @if(showImageSize && file.imageSize)\n {\n <span class=\"px-uploader-file-info-secondary\">{{file.imageSize.width}}x{{file.imageSize.height}}</span>\n }\n\n @if(showFileSize)\n {\n <span class=\"px-uploader-file-info-secondary\">{{(file.size || 0) | pxFileSize}}</span>\n }\n </div>\n </div>\n\n <div class=\"px-uploader-file-info actions\">\n <div class=\"px-uploader-file-info\">\n <span class=\"px-uploader-file-info-primary\">{{file._internal?.uploadError}}</span>\n </div>\n @if(isQueued && file._internal.uploadError && file._internal.canRetryUpload)\n {\n @if(buttons?.retryUploadButton)\n {\n <ng-container [ngTemplateOutlet]=\"buttons!.retryUploadButton!\" [ngTemplateOutletContext]=\"{icon: icons?.retryUploadButton || 'pi pi-refresh', onClick: getRetryUploadFn(fileIndex), disabled: disabled}\"></ng-container>\n }\n @else\n {\n <p-button\n class=\"px-uploader-file-info-action-button\"\n [icon]=\"icons?.retryUploadButton || 'pi pi-refresh'\"\n [disabled]=\"disabled\"\n [style]=\"{'pointer-events': 'visible'}\"\n (onClick)=\"getRetryUploadFn(fileIndex)()\"\n ></p-button>\n }\n }\n\n @if(!file._internal?.isUploading && (file.allowDelete === undefined || file.allowDelete))\n {\n @if(buttons?.removeFileButton)\n {\n <ng-container [ngTemplateOutlet]=\"buttons!.removeFileButton!\" [ngTemplateOutletContext]=\"{icon: icons?.removeFileButton || 'pi pi-trash', onClick: getRemoveFileFn(isQueued, fileIndex), disabled: disabled}\"></ng-container>\n }\n @else\n {\n <p-button\n class=\"px-uploader-file-info-action-button\"\n [icon]=\"icons?.removeFileButton || 'pi pi-trash'\"\n severity=\"warn\"\n [style]=\"{'pointer-events': 'visible'}\"\n [disabled]=\"disabled\"\n (onClick)=\"getRemoveFileFn(isQueued, fileIndex)()\"\n ></p-button>\n }\n }\n </div>\n </div>\n </div>\n </div>\n</ng-template>\n", styles: [".px-uploader{display:flex;position:relative;flex-direction:column;box-sizing:border-box;min-height:50px;border:1px solid var(--p-content-border-color);border-radius:var(--p-border-radius-md);background:var(--p-content-background);color:inherit;padding:var(--px-uploader-body-padding, 1.2rem)}.px-uploader *,.px-uploader *:before,.px-uploader *:after{box-sizing:inherit}.px-uploader .px-uploader-label{color:inherit;font-size:var(--px-uploader-label-font-size, 1.2rem);font-weight:var(--px-uploader-label-font-weight, 700);padding-bottom:calc(var(--px-uploader-body-padding, 1.25rem) * .8)}.px-uploader.no-label .px-uploader-label{display:none}.px-uploader .px-uploader-info{display:flex;align-items:center;justify-content:space-between;color:inherit;padding-bottom:.4rem}.px-uploader .px-uploader-info .px-uploader-upload-button{margin:1rem 0}.px-uploader .px-uploader-info .px-uploader-info-primary{display:flex;flex-direction:column;justify-content:center;color:inherit;padding-right:.8rem;padding-left:1rem}.px-uploader .px-uploader-info .px-uploader-info-primary .px-uploader-msg{color:inherit}.px-uploader .px-uploader-info .px-uploader-info-secondary{display:flex;color:inherit;flex-wrap:wrap;flex-shrink:1;flex-direction:column;justify-items:center;font-size:.9rem}.px-uploader .px-uploader-info .px-uploader-info-secondary .px-uploader-info-secondary-info-msg{max-width:30rem}.px-uploader .px-uploader-files{display:flex;align-items:center;flex-wrap:wrap;max-height:var(--px-uploader-files-container-max-height, 20rem);overflow-y:auto}.px-uploader .px-uploader-files:not(:empty){border-top:1px solid var(--p-content-border-color)}.px-uploader .px-uploader-files .px-uploader-file{display:flex;align-items:flex-start;flex-direction:column;position:relative;border:1px solid var(--p-content-border-color);border-radius:var(--p-border-radius-md)}.px-uploader .px-uploader-files .px-uploader-file.list{flex-shrink:0;width:100%;margin-top:.8rem}.px-uploader .px-uploader-files .px-uploader-file.grid{flex-grow:0;flex-shrink:1;flex-basis:auto;margin:.4em}.px-uploader .px-uploader-files .px-uploader-file .px-uploader-file-header-container{display:flex;flex-direction:column;width:100%;border-radius:var(--p-border-radius-md) var(--p-border-radius-md) 0 0}.px-uploader .px-uploader-files .px-uploader-file .px-uploader-file-header-container .px-uploader-file-header{display:flex;justify-content:space-between;flex-wrap:wrap;width:100%;padding:var(--px-uploader-body-padding, 1rem)}.px-uploader .px-uploader-files .px-uploader-file .px-uploader-file-header-container .px-uploader-file-header .px-uploader-file-info{display:flex;align-items:center}.px-uploader .px-uploader-files .px-uploader-file .px-uploader-file-header-container .px-uploader-file-header .px-uploader-file-info.flex-column{flex-direction:column;align-items:flex-start}.px-uploader .px-uploader-files .px-uploader-file .px-uploader-file-header-container .px-uploader-file-header .px-uploader-file-info .px-uploader-file-status-icon{font-size:var(--px-uploader-file-info-status-icon-font-size, 2rem);margin-right:.4rem}.px-uploader .px-uploader-files .px-uploader-file .px-uploader-file-header-container .px-uploader-file-header .px-uploader-file-info .px-uploader-file-info-primary{text-rendering:optimizeLegibility;font-weight:var(--px-uploader-file-info-primary-font-weight, 500);font-size:var(--px-uploader-file-info-primary-font-size, 1rem);padding:0 .4rem}.px-uploader .px-uploader-files .px-uploader-file .px-uploader-file-header-container .px-uploader-file-header .px-uploader-file-info .px-uploader-file-info-secondary{text-rendering:optimizeLegibility;font-size:var(--px-uploader-file-info-secondary-font-size, .8rem);padding:0 .4rem}.px-uploader .px-uploader-files .px-uploader-file .px-uploader-file-header-container .px-uploader-file-header .px-uploader-file-info.actions{flex-wrap:nowrap;flex-direction:row;align-items:center}.px-uploader .px-uploader-files .px-uploader-file .px-uploader-file-header-container .px-uploader-file-header .px-uploader-file-info.actions .px-uploader-file-info-action-button{margin-left:.4rem}.px-uploader .px-uploader-files .px-uploader-file.px-uploader-error{border-color:var(--px-uploader-file-error-border-color, var(--p-red-500));color:var(--px-uploader-file-error-color, var(--p-red-500))}.px-uploader .px-uploader-files .px-uploader-file.px-uploader-error.preview .px-uploader-file-header-container{color:var(--px-uploader-file-error-color, #333333)}.px-uploader .px-uploader-files .px-uploader-file.preview{height:var(--px-uploader-file-preview-height, 18rem)}.px-uploader .px-uploader-files .px-uploader-file.preview .px-uploader-file-header-container{opacity:0;transition:opacity .3s ease;background-color:color-mix(in srgb,var(--px-uploader-file-preview-header-background-color, #d0d0d0),transparent 40%);color:var(--px-uploader-file-preview-header-color, #333333)}@supports (-webkit-backdrop-filter: blur(.2em)) or (backdrop-filter: blur(.2em)){.px-uploader .px-uploader-files .px-uploader-file.preview .px-uploader-file-header-container{-webkit-backdrop-filter:blur(.18rem);backdrop-filter:blur(.18rem)}}.px-uploader .px-uploader-files .px-uploader-file.preview:hover .px-uploader-file-header-container,.px-uploader .px-uploader-files .px-uploader-file.preview.queued .px-uploader-file-header-container,.px-uploader .px-uploader-files .px-uploader-file.preview.px-uploader-error .px-uploader-file-header-container{opacity:1}.px-uploader .px-uploader-files .px-uploader-file .px-uploader-file-preview{position:absolute;object-fit:contain;width:100%;height:100%;inset:0}.px-uploader.dragover{border:1px solid var(--p-content-border-color)}.px-uploader.dragover:before,.px-uploader.dragover:after{content:\"\";position:absolute;inset:-2px;border:2px solid var(--p-primary-color);transition:all .5s;animation:dragoverClip 3s infinite linear;border-radius:var(--p-border-radius-md)}.px-uploader.dragover:after{animation:dragoverClip 3s infinite -1.5s linear}.px-uploader.dragover .px-uploader-info{pointer-events:none}.px-uploader.dragover .px-uploader-info .px-uploader-info-primary,.px-uploader.dragover .px-uploader-info .px-uploader-info-secondary{color:var(--p-primary-color)}.px-uploader.dragover .px-uploader-files{pointer-events:none}.px-uploader.px-uploader-disabled{pointer-events:none;opacity:var(--px-uploader-disabled-opacity, .7)}@keyframes dragoverClip{0%,to{clip-path:inset(0 0 98% 0)}25%{clip-path:inset(0 98% 0 0)}50%{clip-path:inset(98% 0 0 0)}75%{clip-path:inset(0 0 0 98%)}}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ProgressBar, selector: "p-progressBar, p-progressbar, p-progress-bar", inputs: ["value", "showValue", "styleClass", "valueStyleClass", "unit", "mode", "color"] }, { 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", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: PxFileSizePipe, name: "pxFileSize" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PxUploaderComponent, decorators: [{
type: Component,
args: [{ selector: 'px-uploader', imports: [
NgClass,
ProgressBar,
Button,
NgTemplateOutlet,
PxFileSizePipe
], providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: PxUploaderComponent
}
], template: "<div\n [ngClass]=\"{'px-uploader': true, 'dragover': dragoverEventActive, 'no-label': label === '', 'px-uploader-disabled': disabled}\"\n (dragleave)=\"onDragLeave($event)\"\n (dragover)=\"onDragover($event)\"\n (dragenter)=\"onDragEnter($event)\"\n (drop)=\"onFilesDropped($event)\"\n>\n <label class=\"px-uploader-label\">{{ label }}</label>\n <div class=\"px-uploader-info\">\n <div style=\"display: flex; align-items: center; flex-wrap: wrap;\">\n @if(buttons?.uploadButton)\n {\n <ng-container [ngTemplateOutlet]=\"buttons!.uploadButton!\" [ngTemplateOutletContext]=\"{icon: icons?.uploadButton || 'pi pi-upload', onClick: getOpenFilesInputFn(), disabled: disabled}\"></ng-container>\n }\n @else\n {\n <p-button class=\"px-uploader-upload-button\" [icon]=\"icons?.uploadButton || 'pi pi-upload'\" [label]=\"primeNGConfig.translation.upload\" [disabled]=\"disabled\" (onClick)=\"openFilesInput()\"></p-button>\n }\n <div class=\"px-uploader-info-primary\">\n <label class=\"px-uploader-msg\">{{ intl[PxUploaderIntl.NO_FILES_MSG] }}</label>\n </div>\n </div>\n <div class=\"px-uploader-info-secondary\">\n @if(allowedExtensions.length)\n {\n <span class=\"px-uploader-info-secondary-msg\">\n {{ intl[PxUploaderIntl.ALLOWED_EXTENSIONS] + ': ' + allowedExtensionsText }}\n </span>\n }\n\n <span>{{ intl[PxUploaderIntl.MAX_FILE_SIZE] + ': ' + (maxFileSize | pxFileSize) }}</span>\n\n @if(maxImageSizeText)\n {\n <span class=\"px-uploader-info-secondary-msg\">{{ intl[PxUploaderIntl.IMAGE_SIZE_CHECK_TEXT] + ': ' + maxImageSizeText }}</span>\n }\n\n @if(infoMessage)\n {\n <span class=\"px-uploader-info-secondary-info-msg\">{{infoMessage}}</span>\n }\n </div>\n </div>\n\n <div class=\"px-uploader-files\">\n @for (uploadedFile of _value; track $index)\n {\n <ng-container\n [ngTemplateOutlet]=\"file\"\n [ngTemplateOutletContext]=\"{file: uploadedFile, isQueuedFile: false, fileIndex: $index}\"></ng-container>\n }\n\n @for (queuedFile of filesQueue; track $index)\n {\n <ng-container\n [ngTemplateOutlet]=\"file\"\n [ngTemplateOutletContext]=\"{file: queuedFile, isQueuedFile: true, fileIndex: $index}\"></ng-container>\n }\n </div>\n</div>\n\n<input\n #fileInput\n style=\"display: none;\"\n type=\"file\"\n [accept]=\"allowedExtensions.length ? '.' + allowedExtensions.join(',.') : null\"\n [multiple]=\"multiple\"\n (change)=\"onFilesAdded($any($event.target).files)\">\n\n<ng-template #file let-file=\"file\" let-isQueued=\"isQueuedFile\" let-fileIndex=\"fileIndex\">\n <div class=\"px-uploader-file\" [ngClass]=\"[displayAs, showImagePreview && file.imagePreviewUrl ? 'preview' : '', file._internal?.uploadError ? 'px-uploader-error' : '', isQueued ? 'queued' : '']\">\n @if(showImagePreview && file.imagePreviewUrl)\n {\n <img\n class=\"px-uploader-file-preview\"\n [src]=\"file.imagePreviewUrl\" alt=\"\">\n }\n <div class=\"px-uploader-file-header-container\">\n @if(isQueued && !file._internal?.uploadError)\n {\n <p-progressBar\n [mode]=\"file._internal.uploadProgress > 0 && file._internal.uploadProgress !== 100 ? 'determinate' : 'indeterminate'\"\n [value]=\"file._internal.uploadProgress || 0\"\n [style]=\"{ height: '1rem', 'border-bottom-right-radius': 0, 'border-bottom-left-radius': 0 }\"\n ></p-progressBar>\n }\n\n <div class=\"px-uploader-file-header\">\n <div class=\"px-uploader-file-info\">\n @if(isQueued)\n {\n <i class=\"px-uploader-file-status-icon\" [class]=\"file._internal.isUploading ? icons?.fileStatusIconUploading || 'pi pi-spin pi-spinner' : (file._internal.uploadError !== undefined ? icons?.fileStatusIconError || 'pi pi-times-circle' : icons?.fileStatusIconQueued || 'pi pi-hourglass')\"></i>\n }\n\n <div class=\"px-uploader-file-info flex-column\">\n @if(showFileName)\n {\n <span class=\"px-uploader-file-info-primary\">{{file.name}}</span>\n }\n\n @if(showImageSize && file.imageSize)\n {\n <span class=\"px-uploader-file-info-secondary\">{{file.imageSize.width}}x{{file.imageSize.height}}</span>\n }\n\n @if(showFileSize)\n {\n <span class=\"px-uploader-file-info-secondary\">{{(file.size || 0) | pxFileSize}}</span>\n }\n </div>\n </div>\n\n <div class=\"px-uploader-file-info actions\">\n <div class=\"px-uploader-file-info\">\n <span class=\"px-uploader-file-info-primary\">{{file._internal?.uploadError}}</span>\n </div>\n @if(isQueued && file._internal.uploadError && file._internal.canRetryUpload)\n {\n @if(buttons?.retryUploadButton)\n {\n <ng-container [ngTemplateOutlet]=\"buttons!.retryUploadButton!\" [ngTemplateOutletContext]=\"{icon: icons?.retryUploadButton || 'pi pi-refresh', onClick: getRetryUploadFn(fileIndex), disabled: disabled}\"></ng-container>\n }\n @else\n {\n <p-button\n class=\"px-uploader-file-info-action-button\"\n [icon]=\"icons?.retryUploadButton || 'pi pi-refresh'\"\n [disabled]=\"disabled\"\n [style]=\"{'pointer-events': 'visible'}\"\n (onClick)=\"getRetryUploadFn(fileIndex)()\"\n ></p-button>\n }\n }\n\n @if(!file._internal?.isUploading && (file.allowDelete === undefined || file.allowDelete))\n {\n @if(buttons?.removeFileButton)\n {\n <ng-container [ngTemplateOutlet]=\"buttons!.removeFileButton!\" [ngTemplateOutletContext]=\"{icon: icons?.removeFileButton || 'pi pi-trash', onClick: getRemoveFileFn(isQueued, fileIndex), disabled: disabled}\"></ng-container>\n }\n @else\n {\n <p-button\n class=\"px-uploader-file-info-action-button\"\n [icon]=\"icons?.removeFileButton || 'pi pi-trash'\"\n severity=\"warn\"\n [style]=\"{'pointer-events': 'visible'}\"\n [disabled]=\"disabled\"\n (onClick)=\"getRemoveFileFn(isQueued, fileIndex)()\"\n ></p-button>\n }\n }\n </div>\n </div>\n </div>\n </div>\n</ng-template>\n", styles: [".px-uploader{display:flex;position:relative;flex-direction:column;box-sizing:border-box;min-height:50px;border:1px solid var(--p-content-border-color);border-radius:var(--p-border-radius-md);background:var(--p-content-background);color:inherit;padding:var(--px-uploader-body-padding, 1.2rem)}.px-uploader *,.px-uploader *:before,.px-uploader *:after{box-sizing:inherit}.px-uploader .px-uploader-label{color:inherit;font-size:var(--px-uploader-label-font-size, 1.2rem);font-weight:var(--px-uploader-label-font-weight, 700);padding-bottom:calc(var(--px-uploader-body-padding, 1.25rem) * .8)}.px-uploader.no-label .px-uploader-label{display:none}.px-uploader .px-uploader-info{display:flex;align-items:center;justify-content:space-between;color:inherit;padding-bottom:.4rem}.px-uploader .px-uploader-info .px-uploader-upload-button{margin:1rem 0}.px-uploader .px-uploader-info .px-uploader-info-primary{display:flex;flex-direction:column;justify-content:center;color:inherit;padding-right:.8rem;padding-left:1rem}.px-uploader .px-uploader-info .px-uploader-info-primary .px-uploader-msg{color:inherit}.px-uploader .px-uploader-info .px-uploader-info-secondary{display:flex;color:inherit;flex-wrap:wrap;flex-shrink:1;flex-direction:column;justify-items:center;font-size:.9rem}.px-uploader .px-uploader-info .px-uploader-info-secondary .px-uploader-info-secondary-info-msg{max-width:30rem}.px-uploader .px-uploader-files{display:flex;align-items:center;flex-wrap:wrap;max-height:var(--px-uploader-files-container-max-height, 20rem);overflow-y:auto}.px-uploader .px-uploader-files:not(:empty){border-top:1px solid var(--p-content-border-color)}.px-uploader .px-uploader-files .px-uploader-file{display:flex;align-items:flex-start;flex-direction:column;position:relative;border:1px solid var(--p-content-border-color);border-radius:var(--p-border-radius-md)}.px-uploader .px-uploader-files .px-uploader-file.list{flex-shrink:0;width:100%;margin-top:.8rem}.px-uploader .px-uploader-files .px-uploader-file.grid{flex-grow:0;flex-shrink:1;flex-basis:auto;margin:.4em}.px-uploader .px-uploader-files .px-uploader-file .px-uploader-file-header-container{display:flex;flex-direction:column;width:100%;border-radius:var(--p-border-radius-md) var(--p-border-radius-md) 0 0}.px-uploader .px-uploader-files .px-uploader-file .px-uploader-file-header-container .px-uploader-file-header{display:flex;justify-content:space-between;flex-wrap:wrap;width:100%;padding:var(--px-uploader-body-padding, 1rem)}.px-uploader .px-uploader-files .px-uploader-file .px-uploader-file-header-container .px-uploader-file-header .px-uploader-file-info{display:flex;align-items:center}.px-uploader .px-uploader-files .px-uploader-file .px-uploader-file-header-container .px-uploader-file-header .px-uploader-file-info.flex-column{flex-direction:column;align-items:flex-start}.px-uploader .px-uploader-files .px-uploader-file .px-uploader-file-header-container .px-uploader-file-header .px-uploader-file-info .px-uploader-file-status-icon{font-size:var(--px-uploader-file-info-status-icon-font-size, 2rem);margin-right:.4rem}.px-uploader .px-uploader-files .px-uploader-file .px-uploader-file-header-container .px-uploader-file-header .px-uploader-file-info .px-uploader-file-info-primary{text-rendering:optimizeLegibility;font-weight:var(--px-uploader-file-info-primary-font-weight, 500);font-size:var(--px-uploader-file-info-primary-font-size, 1rem);padding:0 .4rem}.px-uploader .px-uploader-files .px-uploader-file .px-uploader-file-header-container .px-uploader-file-header .px-uploader-file-info .px-uploader-file-info-secondary{text-rendering:optimizeLegibility;font-size:var(--px-uploader-file-info-secondary-font-size, .8rem);padding:0 .4rem}.px-uploader .px-uploader-files .px-uploader-file .px-uploader-file-header-container .px-uploader-file-header .px-uploader-file-info.actions{flex-wrap:nowrap;flex-direction:row;align-items:center}.px-uploader .px-uploader-files .px-uploader-file .px-uploader-file-header-container .px-uploader-file-header .px-uploader-file-info.actions .px-uploader-file-info-action-button{margin-left:.4rem}.px-uploader .px-uploader-files .px-uploader-file.px-uploader-error{border-color:var(--px-uploader-file-error-border-color, var(--p-red-500));color:var(--px-uploader-file-error-color, var(--p-red-500))}.px-uploader .px-uploader-files .px-uploader-file.px-uploader-error.preview .px-uploader-file-header-container{color:var(--px-uploader-file-error-color, #333333)}.px-uploader .px-uploader-files .px-uploader-file.preview{height:var(--px-uploader-file-preview-height, 18rem)}.px-uploader .px-uploader-files .px-uploader-file.preview .px-uploader-file-header-container{opacity:0;transition:opacity .3s ease;background-color:color-mix(in srgb,var(--px-uploader-file-preview-header-background-color, #d0d0d0),transparent 40%);color:var(--px-uploader-file-preview-header-color, #333333)}@supports (-webkit-backdrop-filter: blur(.2em)) or (backdrop-filter: blur(.2em)){.px-uploader .px-uploader-files .px-uploader-file.preview .px-uploader-file-header-container{-webkit-backdrop-filter:blur(.18rem);backdrop-filter:blur(.18rem)}}.px-uploader .px-uploader-files .px-uploader-file.preview:hover .px-uploader-file-header-container,.px-uploader .px-uploader-files .px-uploader-file.preview.queued .px-uploader-file-header-container,.px-uploader .px-uploader-files .px-uploader-file.preview.px-uploader-error .px-uploader-file-header-container{opacity:1}.px-uploader .px-uploader-files .px-uploader-file .px-uploader-file-preview{position:absolute;object-fit:contain;width:100%;height:100%;inset:0}.px-uploader.dragover{border:1px solid var(--p-content-border-color)}.px-uploader.dragover:before,.px-uploader.dragover:after{content:\"\";position:absolute;inset:-2px;border:2px solid var(--p-primary-color);transition:all .5s;animation:dragoverClip 3s infinite linear;border-radius:var(--p-border-radius-md)}.px-uploader.dragover:after{animation:dragoverClip 3s infinite -1.5s linear}.px-uploader.dragover .px-uploader-info{pointer-events:none}.px-uploader.dragover .px-uploader-info .px-uploader-info-primary,.px-uploader.dragover .px-uploader-info .px-uploader-info-secondary{color:var(--p-primary-color)}.px-uploader.dragover .px-uploader-files{pointer-events:none}.px-uploader.px-uploader-disabled{pointer-events:none;opacity:var(--px-uploader-disabled-opacity, .7)}@keyframes dragoverClip{0%,to{clip-path:inset(0 0 98% 0)}25%{clip-path:inset(0 98% 0 0)}50%{clip-path:inset(98% 0 0 0)}75%{clip-path:inset(0 0 0 98%)}}\n"] }]
}], ctorParameters: () => [{ type: i1.HttpClient }, { type: i0.ChangeDetectorRef }, { type: i2.PrimeNG }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [PX_UPLOADER_INTL]
}] }], propDecorators: { saveEndpoint: [{
type: Input,
args: [{ required: true }]
}], deleteEndpoint: [{
type: Input
}], label: [{
type: Input
}], multiple: [{
type: Input
}], displayAs: [{
type: Input
}], allowedExtensions: [{
type: Input
}], maxFileSize: [{
type: Input
}], maxParallelUploads: [{
type: Input
}], showImagePreview: [{
type: Input
}], maxImageSize: [{
type: Input
}], icons: [{
type: Input
}], buttons: [{
type: Input
}], infoMessage: [{
type: Input
}], showFileName: [{
type: Input
}], showFileSize: [{
type: Input
}], showImageSize: [{
type: Input
}], uploadFinished: [{
type: Output
}], fileInput: [{
type: ViewChild,
args: ['fileInput']
}], value: [{
type: Input
}], disabled: [{
type: Input
}] } });
/*
* Public API Surface of px-uploader
*/
/**
* Generated bundle index. Do not edit.
*/
export { PX_UPLOADER_INTL, PxFileSizePipe, PxUploaderComponent, PxUploaderIntl };
//# sourceMappingURL=ng-prime-extensions-px-uploader.mjs.map