primeng
Version:
PrimeNG is a premium UI library for Angular featuring a rich set of 90+ components, a theme designer, various theme alternatives such as Material, Bootstrap, Tailwind, premium templates and professional support. In addition, it integrates with PrimeBlock,
1 lines • 96.1 kB
Source Map (JSON)
{"version":3,"file":"primeng-fileupload.mjs","sources":["../../src/fileupload/fileupload-token.ts","../../src/fileupload/style/fileuploadstyle.ts","../../src/fileupload/file-content.ts","../../src/fileupload/fileupload.ts","../../src/fileupload/primeng-fileupload.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\n\nexport const FILEUPLOAD_INSTANCE = new InjectionToken<any>('FILEUPLOAD_INSTANCE');\n","import { Injectable } from '@angular/core';\nimport { style } from '@primeuix/styles/fileupload';\nimport { BaseStyle } from 'primeng/base';\n\nconst classes = {\n root: ({ instance }) => `p-fileupload p-fileupload-${instance.mode()} p-component`,\n header: 'p-fileupload-header',\n pcChooseButton: 'p-fileupload-choose-button',\n pcUploadButton: 'p-fileupload-upload-button',\n pcCancelButton: 'p-fileupload-cancel-button',\n content: 'p-fileupload-content',\n fileList: 'p-fileupload-file-list',\n file: 'p-fileupload-file',\n fileThumbnail: 'p-fileupload-file-thumbnail',\n fileInfo: 'p-fileupload-file-info',\n fileName: 'p-fileupload-file-name',\n fileSize: 'p-fileupload-file-size',\n pcFileBadge: 'p-fileupload-file-badge',\n fileActions: 'p-fileupload-file-actions',\n pcFileRemoveButton: 'p-fileupload-file-remove-button',\n basicContent: 'p-fileupload-basic-content'\n};\n\n@Injectable()\nexport class FileUploadStyle extends BaseStyle {\n name = 'fileupload';\n\n style = style;\n\n classes = classes;\n}\n\n/**\n *\n * FileUpload is an advanced uploader with dragdrop support, multi file uploads, auto uploading, progress tracking and validations.\n *\n * [Live Demo](https://www.primeng.org/fileupload/)\n *\n * @module fileuploadstyle\n *\n */\n\nexport enum FileUploadClasses {\n /**\n * Class name of the root element\n */\n root = 'p-fileupload',\n /**\n * Class name of the header element\n */\n header = 'p-fileupload-header',\n /**\n * Class name of the choose button element\n */\n pcChooseButton = 'p-fileupload-choose-button',\n /**\n * Class name of the upload button element\n */\n pcUploadButton = 'p-fileupload-upload-button',\n /**\n * Class name of the cancel button element\n */\n pcCancelButton = 'p-fileupload-cancel-button',\n /**\n * Class name of the content element\n */\n content = 'p-fileupload-content',\n /**\n * Class name of the file list element\n */\n fileList = 'p-fileupload-file-list',\n /**\n * Class name of the file element\n */\n file = 'p-fileupload-file',\n /**\n * Class name of the file thumbnail element\n */\n fileThumbnail = 'p-fileupload-file-thumbnail',\n /**\n * Class name of the file info element\n */\n fileInfo = 'p-fileupload-file-info',\n /**\n * Class name of the file name element\n */\n fileName = 'p-fileupload-file-name',\n /**\n * Class name of the file size element\n */\n fileSize = 'p-fileupload-file-size',\n /**\n * Class name of the file badge element\n */\n pcFileBadge = 'p-fileupload-file-badge',\n /**\n * Class name of the file actions element\n */\n fileActions = 'p-fileupload-file-actions',\n /**\n * Class name of the file remove button element\n */\n pcFileRemoveButton = 'p-fileupload-file-remove-button',\n /**\n * Class name of the content in basic mode\n */\n basicContent = 'p-fileupload-basic-content'\n}\n\nexport interface FileUploadStyle extends BaseStyle {}\n","import { NgTemplateOutlet } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, inject, input, output, TemplateRef } from '@angular/core';\nimport { TranslationKeys } from 'primeng/api';\nimport { Badge } from 'primeng/badge';\nimport { BaseComponent } from 'primeng/basecomponent';\nimport { Bind } from 'primeng/bind';\nimport { ButtonDirective } from 'primeng/button';\nimport { Times as TimesIcon } from '@primeicons/angular/times';\nimport { FileContentRemoveEvent, FileRemoveIconTemplateContext } from 'primeng/types/fileupload';\nimport type { FileUpload } from './fileupload';\nimport { FILEUPLOAD_INSTANCE } from './fileupload-token';\nimport { FileUploadStyle } from './style/fileuploadstyle';\nimport { BadgeSeverity } from 'primeng/types/badge';\n\n@Component({\n selector: '[pFileContent]',\n standalone: true,\n template: `@for (file of files(); track file?.name + '-' + $index; let index = $index) {\n <div [class]=\"cx('file')\" [pBind]=\"$pcFileUpload.ptm('file')\">\n <img role=\"presentation\" [class]=\"cx('fileThumbnail')\" [attr.alt]=\"file.name\" [src]=\"file.objectURL\" [width]=\"previewWidth()\" [pBind]=\"$pcFileUpload.ptm('fileThumbnail')\" />\n <div [class]=\"cx('fileInfo')\" [pBind]=\"$pcFileUpload.ptm('fileInfo')\">\n <div [class]=\"cx('fileName')\" [pBind]=\"$pcFileUpload.ptm('fileName')\">{{ file.name }}</div>\n <span [class]=\"cx('fileSize')\" [pBind]=\"$pcFileUpload.ptm('fileSize')\">{{ formatSize(file.size) }}</span>\n </div>\n <p-badge [value]=\"badgeValue()\" [severity]=\"badgeSeverity()\" [class]=\"cx('pcFileBadge')\" [pt]=\"$pcFileUpload.ptm('pcFileBadge')\" [unstyled]=\"unstyled()\" />\n <div [class]=\"cx('fileActions')\" [pBind]=\"$pcFileUpload.ptm('fileActions')\">\n <button type=\"button\" iconOnly pButton text rounded severity=\"danger\" [class]=\"cx('pcFileRemoveButton')\" [pButtonPT]=\"$pcFileUpload.ptm('pcFileRemoveButton')\" [pButtonUnstyled]=\"unstyled()\" (click)=\"onRemoveClick($event, index)\">\n @if (fileRemoveIconTemplate()) {\n <ng-template *ngTemplateOutlet=\"fileRemoveIconTemplate(); context: getRemoveIconContext('', file, index)\"></ng-template>\n } @else {\n <svg data-p-icon=\"times\" [attr.aria-hidden]=\"true\" />\n }\n </button>\n </div>\n </div>\n }`,\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [FileUploadStyle],\n imports: [NgTemplateOutlet, Badge, ButtonDirective, TimesIcon, Bind]\n})\nexport class FileContent extends BaseComponent {\n _componentStyle = inject(FileUploadStyle);\n\n $pcFileUpload = inject<FileUpload>(FILEUPLOAD_INSTANCE);\n\n onRemove = output<FileContentRemoveEvent>();\n\n files = input<File[]>();\n\n badgeSeverity = input<BadgeSeverity>('warn');\n\n badgeValue = input<string>();\n\n previewWidth = input<number>(50);\n\n fileRemoveIconTemplate = input<TemplateRef<unknown>>();\n\n onRemoveClick(event: Event, index: number) {\n this.onRemove.emit({ event, index });\n }\n\n getRemoveIconContext(iconClass: string, file: File, index: number): FileRemoveIconTemplateContext {\n return { class: iconClass, file, index };\n }\n\n formatSize(bytes: number) {\n const k = 1024;\n const dm = 3;\n const sizes = this.translate(TranslationKeys.FILE_SIZE_TYPES);\n\n if (bytes === 0) {\n return `0 ${sizes[0]}`;\n }\n\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n const formattedSize = (bytes / Math.pow(k, i)).toFixed(dm);\n\n return `${formattedSize} ${sizes[i]}`;\n }\n}\n","import { isPlatformBrowser, NgTemplateOutlet } from '@angular/common';\nimport { HttpClient, HttpEvent, HttpEventType, HttpHeaders } from '@angular/common/http';\nimport { booleanAttribute, ChangeDetectionStrategy, Component, computed, contentChild, effect, ElementRef, inject, input, NgModule, numberAttribute, output, signal, TemplateRef, viewChild, ViewEncapsulation } from '@angular/core';\nimport { DomSanitizer } from '@angular/platform-browser';\nimport { addClass, removeClass } from '@primeuix/utils';\nimport { BlockableUI, SharedModule, TranslationKeys } from 'primeng/api';\nimport { BaseComponent, PARENT_INSTANCE } from 'primeng/basecomponent';\nimport { Bind } from 'primeng/bind';\nimport { ButtonDirective } from 'primeng/button';\nimport type { ButtonDirectiveOptions } from 'primeng/types/button';\nimport { Plus as PlusIcon } from '@primeicons/angular/plus';\nimport { Times as TimesIcon } from '@primeicons/angular/times';\nimport { Upload as UploadIcon } from '@primeicons/angular/upload';\nimport { Message } from 'primeng/message';\nimport { ProgressBar } from 'primeng/progressbar';\nimport type { CSSProperties } from 'primeng/types/shared';\nimport { VoidListener } from 'primeng/ts-helpers';\nimport {\n FileBeforeUploadEvent,\n FileContentRemoveEvent,\n FileProgressEvent,\n FileRemoveEvent,\n FileSelectEvent,\n FileSendEvent,\n FileUploadContentTemplateContext,\n FileUploadErrorEvent,\n FileUploadEvent,\n FileUploadFileLabelTemplateContext,\n FileUploadFileTemplateContext,\n FileUploadHandlerEvent,\n FileUploadHeaderTemplateContext,\n FileUploadMessage,\n FileUploadPassThrough,\n RemoveUploadedFileEvent\n} from 'primeng/types/fileupload';\nimport { Subscription } from 'rxjs';\nimport { FileContent } from './file-content';\nimport { FILEUPLOAD_INSTANCE } from './fileupload-token';\nimport { FileUploadStyle } from './style/fileuploadstyle';\n\n/**\n * FileUpload is an advanced uploader with dragdrop support, multi file uploads, auto uploading, progress tracking and validations.\n * @group Components\n */\n@Component({\n selector: 'p-fileupload',\n standalone: true,\n imports: [NgTemplateOutlet, ButtonDirective, ProgressBar, Message, PlusIcon, UploadIcon, TimesIcon, SharedModule, FileContent, Bind],\n template: `\n @if (isAdvancedMode()) {\n <div [class]=\"cn(cx('root'), styleClass())\" [style]=\"style()\" [pBind]=\"ptm('root')\">\n <input [attr.aria-label]=\"browseFilesLabel\" #advancedfileinput type=\"file\" (change)=\"onFileSelect($event)\" [multiple]=\"multiple()\" [accept]=\"accept()\" [disabled]=\"chooseButtonDisabled()\" [attr.title]=\"''\" [pBind]=\"ptm('input')\" />\n <div [class]=\"cx('header')\" [pBind]=\"ptm('header')\">\n @if (!headerTemplate()) {\n <button\n type=\"button\"\n [pButton]=\"chooseButtonProps()\"\n [disabled]=\"chooseButtonDisabled()\"\n [class]=\"cn(cx('pcChooseButton'), chooseStyleClass())\"\n [pButtonPT]=\"ptm('pcChooseButton')\"\n [pButtonUnstyled]=\"unstyled()\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n (click)=\"choose()\"\n (keydown.enter)=\"choose()\"\n >\n @if (chooseIcon()) {\n <span [class]=\"chooseIcon()\" [attr.aria-label]=\"true\" [pBind]=\"ptm('pcChooseButton')?.icon\"></span>\n } @else {\n @if (!chooseIconTemplate()) {\n <svg data-p-icon=\"plus\" [attr.aria-label]=\"true\" [pBind]=\"ptm('pcChooseButton')?.icon\" />\n } @else {\n <span [attr.aria-label]=\"true\" [pBind]=\"ptm('pcChooseButton')?.icon\">\n <ng-container *ngTemplateOutlet=\"chooseIconTemplate()\"></ng-container>\n </span>\n }\n }\n {{ chooseButtonLabel }}\n </button>\n\n @if (showUploadBtn()) {\n <button\n type=\"button\"\n [pButton]=\"uploadButtonProps()\"\n [disabled]=\"uploadButtonDisabled()\"\n [class]=\"cn(cx('pcUploadButton'), uploadStyleClass())\"\n [pButtonPT]=\"ptm('pcUploadButton')\"\n [pButtonUnstyled]=\"unstyled()\"\n (click)=\"upload()\"\n >\n @if (uploadIcon()) {\n <span [class]=\"uploadIcon()\" [attr.aria-hidden]=\"true\" [pBind]=\"ptm('pcUploadButton')?.icon\"></span>\n } @else {\n @if (!uploadIconTemplate()) {\n <svg data-p-icon=\"upload\" [pBind]=\"ptm('pcUploadButton')?.icon\" />\n } @else {\n <span [attr.aria-hidden]=\"true\" [pBind]=\"ptm('pcUploadButton')?.icon\">\n <ng-container *ngTemplateOutlet=\"uploadIconTemplate()\"></ng-container>\n </span>\n }\n }\n {{ uploadButtonLabel }}\n </button>\n }\n @if (showCancelBtn()) {\n <button\n type=\"button\"\n [pButton]=\"cancelButtonProps()\"\n [disabled]=\"cancelButtonDisabled()\"\n [class]=\"cn(cx('pcCancelButton'), cancelStyleClass())\"\n [pButtonPT]=\"ptm('pcCancelButton')\"\n [pButtonUnstyled]=\"unstyled()\"\n (click)=\"clear()\"\n >\n @if (cancelIcon()) {\n <span [class]=\"cancelIcon()\" [pBind]=\"ptm('pcCancelButton')?.icon\"></span>\n } @else {\n @if (!cancelIconTemplate()) {\n <svg data-p-icon=\"times\" [attr.aria-hidden]=\"true\" [pBind]=\"ptm('pcCancelButton')?.icon\" />\n } @else {\n <span [attr.aria-hidden]=\"true\" [pBind]=\"ptm('pcCancelButton')?.icon\">\n <ng-container *ngTemplateOutlet=\"cancelIconTemplate()\"></ng-container>\n </span>\n }\n }\n {{ cancelButtonLabel }}\n </button>\n }\n }\n <ng-container *ngTemplateOutlet=\"headerTemplate(); context: headerTemplateContext\"></ng-container>\n <ng-container *ngTemplateOutlet=\"toolbarTemplate()\"></ng-container>\n </div>\n <div #content [class]=\"cx('content')\" (dragenter)=\"onDragEnter($event)\" (dragleave)=\"onDragLeave($event)\" (drop)=\"onDrop($event)\" [pBind]=\"ptm('content')\">\n @if (contentTemplate()) {\n <ng-container *ngTemplateOutlet=\"contentTemplate(); context: contentTemplateContext\"></ng-container>\n } @else {\n @if (hasFiles()) {\n <p-progressbar [value]=\"progress()\" [showValue]=\"false\" [pt]=\"ptm('pcProgressBar')\"></p-progressbar>\n }\n @for (message of msgs(); track $index) {\n <p-message [severity]=\"message.severity\" [pt]=\"ptm('pcMessage')\" [unstyled]=\"unstyled()\">{{ message.text }}</p-message>\n }\n\n @if (hasFiles()) {\n <div [class]=\"cx('fileList')\" [pBind]=\"ptm('fileList')\">\n @if (fileTemplate()) {\n @for (file of files; track file.name) {\n <ng-container *ngTemplateOutlet=\"fileTemplate(); context: getFileTemplateContext(file)\"></ng-container>\n }\n } @else {\n <div pFileContent [unstyled]=\"unstyled()\" [files]=\"files\" (onRemove)=\"onRemoveClick($event)\" [badgeValue]=\"pendingLabel\" [previewWidth]=\"previewWidth()\" [fileRemoveIconTemplate]=\"cancelIconTemplate()\"></div>\n }\n </div>\n }\n @if (hasUploadedFiles()) {\n <div [class]=\"cx('fileList')\" [pBind]=\"ptm('fileList')\">\n @if (fileTemplate()) {\n @for (file of uploadedFiles(); track file.name) {\n <ng-container *ngTemplateOutlet=\"fileTemplate(); context: getFileTemplateContext(file)\"></ng-container>\n }\n } @else {\n <div\n pFileContent\n [unstyled]=\"unstyled()\"\n [files]=\"uploadedFiles()\"\n (onRemove)=\"onRemoveUploadedFileClick($event)\"\n [badgeValue]=\"completedLabel()\"\n badgeSeverity=\"success\"\n [previewWidth]=\"previewWidth()\"\n [fileRemoveIconTemplate]=\"cancelIconTemplate()\"\n ></div>\n }\n </div>\n }\n }\n @if (showEmpty()) {\n <ng-container *ngTemplateOutlet=\"emptyTemplate()\" [pBind]=\"ptm('empty')\"></ng-container>\n }\n </div>\n </div>\n }\n @if (isBasicMode()) {\n <div [class]=\"cn(cx('root'), styleClass())\" [pBind]=\"ptm('root')\">\n @for (message of msgs(); track $index) {\n <p-message [severity]=\"message.severity\" [pt]=\"ptm('pcMessage')\" [unstyled]=\"unstyled()\">{{ message.text }}</p-message>\n }\n\n <div [class]=\"cx('basicContent')\" [pBind]=\"ptm('basicContent')\">\n <button\n type=\"button\"\n [pButton]=\"chooseButtonProps()\"\n [disabled]=\"disabled()\"\n [class]=\"cn(cx('pcChooseButton'), chooseStyleClass())\"\n [style]=\"style()\"\n [pButtonPT]=\"ptm('pcChooseButton')\"\n [pButtonUnstyled]=\"unstyled()\"\n (click)=\"onBasicUploaderClick()\"\n (keydown)=\"onBasicKeydown($event)\"\n >\n @if (hasFiles() && !auto()) {\n @if (uploadIcon()) {\n <span [class]=\"cn('p-button-icon p-button-icon-left', uploadIcon())\" [pBind]=\"ptm('pcChooseButton')?.icon\"></span>\n } @else {\n @if (!uploadIconTemplate()) {\n <svg data-p-icon=\"upload\" [class]=\"'p-button-icon p-button-icon-left'\" [pBind]=\"ptm('pcChooseButton')?.icon\" />\n } @else {\n <span class=\"p-button-icon p-button-icon-left\" [pBind]=\"ptm('pcChooseButton')?.icon\">\n <ng-container *ngTemplateOutlet=\"uploadIconTemplate()\"></ng-container>\n </span>\n }\n }\n } @else {\n @if (chooseIcon()) {\n <span [class]=\"cn('p-button-icon p-button-icon-left pi', chooseIcon())\" [pBind]=\"ptm('pcChooseButton')?.icon\"></span>\n } @else {\n @if (!chooseIconTemplate()) {\n <svg data-p-icon=\"plus\" [pBind]=\"ptm('pcChooseButton')?.icon\" />\n } @else {\n <ng-container *ngTemplateOutlet=\"chooseIconTemplate()\"></ng-container>\n }\n }\n }\n {{ chooseButtonLabel }}\n </button>\n <input\n [attr.aria-label]=\"browseFilesLabel\"\n #basicfileinput\n type=\"file\"\n [accept]=\"accept()\"\n [multiple]=\"multiple()\"\n [disabled]=\"disabled()\"\n (change)=\"onFileSelect($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n [pBind]=\"ptm('input')\"\n />\n @if (!auto()) {\n @if (!fileLabelTemplate()) {\n <span>\n {{ basicFileChosenLabel() }}\n </span>\n } @else {\n <ng-container *ngTemplateOutlet=\"fileLabelTemplate(); context: fileLabelTemplateContext\"></ng-container>\n }\n }\n </div>\n </div>\n }\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n providers: [FileUploadStyle, { provide: FILEUPLOAD_INSTANCE, useExisting: FileUpload }, { provide: PARENT_INSTANCE, useExisting: FileUpload }],\n hostDirectives: [Bind]\n})\nexport class FileUpload extends BaseComponent<FileUploadPassThrough> implements BlockableUI {\n componentName = 'FileUpload';\n\n bindDirectiveInstance = inject(Bind, { self: true });\n\n onAfterViewChecked() {\n this.bindDirectiveInstance.setAttrs(this.ptm('host'));\n }\n\n /**\n * Name of the request parameter to identify the files at backend.\n * @group Props\n */\n name = input<string>();\n /**\n * Remote url to upload the files.\n * @group Props\n */\n url = input<string>();\n /**\n * HTTP method to send the files to the url such as \"post\" and \"put\".\n * @group Props\n */\n method = input<'post' | 'put'>('post');\n /**\n * Used to select multiple files at once from file dialog.\n * @group Props\n */\n multiple = input(false, { transform: booleanAttribute });\n /**\n * 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\").\n * @group Props\n */\n accept = input<string>();\n /**\n * Disables the upload functionality.\n * @group Props\n */\n disabled = input(false, { transform: booleanAttribute });\n /**\n * When enabled, upload begins automatically after selection is completed.\n * @group Props\n */\n auto = input(false, { transform: booleanAttribute });\n /**\n * Cross-site Access-Control requests should be made using credentials such as cookies, authorization headers or TLS client certificates.\n * @group Props\n */\n withCredentials = input(false, { transform: booleanAttribute });\n /**\n * Maximum file size allowed in bytes.\n * @group Props\n */\n maxFileSize = input<number, unknown>(undefined, { transform: numberAttribute });\n /**\n * Summary message of the invalid file size.\n * @group Props\n */\n invalidFileSizeMessageSummary = input('{0}: Invalid file size, ');\n /**\n * Detail message of the invalid file size.\n * @group Props\n */\n invalidFileSizeMessageDetail = input('maximum upload size is {0}.');\n /**\n * Summary message of the invalid file type.\n * @group Props\n */\n invalidFileTypeMessageSummary = input('{0}: Invalid file type, ');\n /**\n * Detail message of the invalid file type.\n * @group Props\n */\n invalidFileTypeMessageDetail = input('allowed file types: {0}.');\n /**\n * Detail message of the invalid file type.\n * @group Props\n */\n invalidFileLimitMessageDetail = input('limit is {0} at most.');\n /**\n * Summary message of the invalid file type.\n * @group Props\n */\n invalidFileLimitMessageSummary = input('Maximum number of files exceeded, ');\n /**\n * Inline style of the element.\n * @group Props\n */\n style = input<CSSProperties>();\n /**\n * Class of the element.\n * @group Props\n */\n styleClass = input<string>();\n /**\n * Width of the image thumbnail in pixels.\n * @group Props\n */\n previewWidth = input(50, { transform: numberAttribute });\n /**\n * Label of the choose button. Defaults to PrimeNG Locale configuration.\n * @group Props\n */\n chooseLabel = input<string>();\n /**\n * Label of the upload button. Defaults to PrimeNG Locale configuration.\n * @group Props\n */\n uploadLabel = input<string>();\n /**\n * Label of the cancel button. Defaults to PrimeNG Locale configuration.\n * @group Props\n */\n cancelLabel = input<string>();\n /**\n * Icon of the choose button.\n * @group Props\n */\n chooseIcon = input<string>();\n /**\n * Icon of the upload button.\n * @group Props\n */\n uploadIcon = input<string>();\n /**\n * Icon of the cancel button.\n * @group Props\n */\n cancelIcon = input<string>();\n /**\n * Whether to show the upload button.\n * @group Props\n */\n showUploadButton = input(true, { transform: booleanAttribute });\n /**\n * Whether to show the cancel button.\n * @group Props\n */\n showCancelButton = input(true, { transform: booleanAttribute });\n /**\n * Defines the UI of the component.\n * @group Props\n */\n mode = input<'advanced' | 'basic'>('advanced');\n /**\n * HttpHeaders class represents the header configuration options for an HTTP request.\n * @group Props\n */\n headers = input<HttpHeaders>();\n /**\n * Whether to use the default upload or a manual implementation defined in uploadHandler callback. Defaults to PrimeNG Locale configuration.\n * @group Props\n */\n customUpload = input(false, { transform: booleanAttribute });\n /**\n * Maximum number of files that can be uploaded.\n * @group Props\n */\n fileLimit = input<number, unknown>(undefined, { transform: numberAttribute });\n /**\n * Style class of the upload button.\n * @group Props\n */\n uploadStyleClass = input<string>();\n /**\n * Style class of the cancel button.\n * @group Props\n */\n cancelStyleClass = input<string>();\n /**\n * Style class of the remove button.\n * @group Props\n */\n removeStyleClass = input<string>();\n /**\n * Style class of the choose button.\n * @group Props\n */\n chooseStyleClass = input<string>();\n /**\n * Configuration object forwarded to the choose button.\n * @group Props\n */\n chooseButtonProps = input<ButtonDirectiveOptions>();\n /**\n * Configuration object forwarded to the upload button.\n * @group Props\n */\n uploadButtonProps = input<ButtonDirectiveOptions>({ severity: 'secondary' });\n /**\n * Configuration object forwarded to the cancel button.\n * @group Props\n */\n cancelButtonProps = input<ButtonDirectiveOptions>({ severity: 'secondary' });\n /**\n * Callback to invoke before file upload is initialized.\n * @param {FileBeforeUploadEvent} event - Custom upload event.\n * @group Emits\n */\n onBeforeUpload = output<FileBeforeUploadEvent>();\n /**\n * 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.\n * @param {FileSendEvent} event - Custom send event.\n * @group Emits\n */\n onSend = output<FileSendEvent>();\n /**\n * Callback to invoke when file upload is complete.\n * @param {FileUploadEvent} event - Custom upload event.\n * @group Emits\n */\n onUpload = output<FileUploadEvent>();\n /**\n * Callback to invoke if file upload fails.\n * @param {FileUploadErrorEvent} event - Custom error event.\n * @group Emits\n */\n onError = output<FileUploadErrorEvent>();\n /**\n * Callback to invoke when files in queue are removed without uploading using clear all button.\n * @param {Event} event - Browser event.\n * @group Emits\n */\n onClear = output<void>();\n /**\n * Callback to invoke when a file is removed without uploading using clear button of a file.\n * @param {FileRemoveEvent} event - Remove event.\n * @group Emits\n */\n onRemove = output<FileRemoveEvent>();\n /**\n * Callback to invoke when files are selected.\n * @param {FileSelectEvent} event - Select event.\n * @group Emits\n */\n onSelect = output<FileSelectEvent>();\n /**\n * Callback to invoke when files are being uploaded.\n * @param {FileProgressEvent} event - Progress event.\n * @group Emits\n */\n onProgress = output<FileProgressEvent>();\n /**\n * Callback to invoke in custom upload mode to upload the files manually.\n * @param {FileUploadHandlerEvent} event - Upload handler event.\n * @group Emits\n */\n uploadHandler = output<FileUploadHandlerEvent>();\n /**\n * This event is triggered if an error occurs while loading an image file.\n * @param {Event} event - Browser event.\n * @group Emits\n */\n onImageError = output<Event>();\n /**\n * This event is triggered if an error occurs while loading an image file.\n * @param {RemoveUploadedFileEvent} event - Remove event.\n * @group Emits\n */\n onRemoveUploadedFile = output<RemoveUploadedFileEvent>();\n\n /**\n * Custom file template.\n * @group Templates\n */\n fileTemplate = contentChild<TemplateRef<void>>('file', { descendants: false });\n\n /**\n * Custom header template.\n * @param {FileUploadHeaderTemplateContext} context - header template context.\n * @group Templates\n */\n headerTemplate = contentChild<TemplateRef<FileUploadHeaderTemplateContext>>('header', { descendants: false });\n\n /**\n * Custom content template.\n * @param {FileUploadContentTemplateContext} context - content template context.\n * @group Templates\n */\n contentTemplate = contentChild<TemplateRef<FileUploadContentTemplateContext>>('content', { descendants: false });\n\n /**\n * Custom toolbar template.\n * @group Templates\n */\n toolbarTemplate = contentChild<TemplateRef<void>>('toolbar', { descendants: false });\n\n /**\n * Custom choose icon template.\n * @group Templates\n */\n chooseIconTemplate = contentChild<TemplateRef<void>>('chooseicon', { descendants: false });\n\n /**\n * Custom file label template.\n * @param {FileUploadFileLabelTemplateContext} context - file label template context.\n * @group Templates\n */\n fileLabelTemplate = contentChild<TemplateRef<FileUploadFileLabelTemplateContext>>('filelabel', { descendants: false });\n\n /**\n * Custom upload icon template.\n * @group Templates\n */\n uploadIconTemplate = contentChild<TemplateRef<void>>('uploadicon', { descendants: false });\n\n /**\n * Custom cancel icon template.\n * @group Templates\n */\n cancelIconTemplate = contentChild<TemplateRef<void>>('cancelicon', { descendants: false });\n\n /**\n * Custom empty state template.\n * @group Templates\n */\n emptyTemplate = contentChild<TemplateRef<void>>('empty', { descendants: false });\n\n isAdvancedMode = computed(() => this.mode() === 'advanced');\n\n isBasicMode = computed(() => this.mode() === 'basic');\n\n showUploadBtn = computed(() => !this.auto() && this.showUploadButton());\n\n showCancelBtn = computed(() => !this.auto() && this.showCancelButton());\n\n chooseButtonDisabled = computed(() => this.disabled() || this.isChooseDisabled());\n\n uploadButtonDisabled = computed(() => !this.hasFiles() || this.isFileLimitExceeded());\n\n cancelButtonDisabled = computed(() => !this.hasFiles() || this.uploading());\n\n showEmpty = computed(() => !!this.emptyTemplate() && !this.hasFiles() && !this.hasUploadedFiles());\n\n get headerTemplateContext(): FileUploadHeaderTemplateContext {\n return {\n $implicit: this.files,\n uploadedFiles: this.uploadedFiles(),\n chooseCallback: this.choose.bind(this),\n clearCallback: this.clear.bind(this),\n uploadCallback: this.upload.bind(this)\n };\n }\n\n get contentTemplateContext(): FileUploadContentTemplateContext {\n return {\n $implicit: this.files,\n uploadedFiles: this.uploadedFiles(),\n chooseCallback: this.choose.bind(this),\n clearCallback: this.clear.bind(this),\n removeUploadedFileCallback: this.removeUploadedFile.bind(this),\n removeFileCallback: this.remove.bind(this),\n progress: this.progress(),\n messages: this.msgs()\n };\n }\n\n get fileLabelTemplateContext(): FileUploadFileLabelTemplateContext {\n return {\n $implicit: this.files\n };\n }\n\n advancedFileInput = viewChild<ElementRef>('advancedfileinput');\n\n basicFileInput = viewChild<ElementRef>('basicfileinput');\n\n contentEl = viewChild<ElementRef>('content');\n\n /**\n * Files input.\n * @group Props\n */\n filesInput = input<File[]>([], { alias: 'files' });\n\n get files(): File[] {\n return this._files();\n }\n\n set files(value: File[]) {\n this._files.set(value);\n }\n\n public get basicButtonLabel(): string {\n if (this.auto() || !this.hasFiles()) {\n return this.chooseLabel() as string;\n }\n\n return this.uploadLabel() ?? this.files[0].name;\n }\n\n public _files = signal<File[]>([]);\n\n public progress = signal(0);\n\n public dragHighlight = signal(false);\n\n public msgs = signal<FileUploadMessage[]>([]);\n\n public uploadedFileCount: number = 0;\n\n focus = signal(false);\n\n uploading = signal(false);\n\n duplicateIEEvent: boolean | undefined; // flag to recognize duplicate onchange event for file input\n\n translationSubscription: Subscription | undefined;\n\n dragOverListener: VoidListener;\n\n public uploadedFiles = signal<File[]>([]);\n\n sanitizer = inject(DomSanitizer);\n\n http = inject(HttpClient);\n\n _componentStyle = inject(FileUploadStyle);\n\n constructor() {\n super();\n\n effect(() => {\n const inputFiles = this.filesInput();\n if (inputFiles && inputFiles.length > 0) {\n const newFiles: File[] = [];\n for (let i = 0; i < inputFiles.length; i++) {\n let file = inputFiles[i];\n if (this.validate(file)) {\n if (this.isImage(file)) {\n (file as any).objectURL = this.sanitizer.bypassSecurityTrustUrl(window.URL.createObjectURL(inputFiles[i]));\n }\n newFiles.push(inputFiles[i]);\n }\n }\n this._files.set(newFiles);\n }\n });\n }\n\n onInit() {\n this.translationSubscription = this.config.translationObserver.subscribe(() => {\n this.cd.markForCheck();\n });\n }\n\n onAfterViewInit() {\n if (isPlatformBrowser(this.platformId)) {\n if (this.mode() === 'advanced') {\n const contentEl = this.contentEl();\n if (contentEl) {\n this.dragOverListener = this.renderer.listen(contentEl.nativeElement, 'dragover', this.onDragOver.bind(this));\n }\n }\n }\n }\n\n basicFileChosenLabel() {\n if (this.auto()) return this.chooseButtonLabel;\n else if (this.hasFiles()) {\n if (this.files && this.files.length === 1) return this.files[0].name;\n\n return this.translate('fileChosenMessage')?.replace('{0}', this.files.length.toString());\n }\n\n return this.translate('noFileChosenMessage') || '';\n }\n\n completedLabel() {\n return this.translate('completed') || '';\n }\n\n choose() {\n this.advancedFileInput()?.nativeElement.click();\n }\n\n onFileSelect(event: Event) {\n if (event.type !== 'drop' && this.isIE11() && this.duplicateIEEvent) {\n this.duplicateIEEvent = false;\n return;\n }\n\n if (!this.multiple()) {\n this.files = [];\n }\n\n this.msgs.set([]);\n this.files = this.files || [];\n let files = (event as DragEvent).dataTransfer ? (event as DragEvent).dataTransfer!.files : (event.target as HTMLInputElement).files!;\n\n for (let i = 0; i < files.length; i++) {\n let file = files[i];\n\n if (!this.isFileSelected(file)) {\n if (this.validate(file)) {\n if (this.isImage(file)) {\n (file as any).objectURL = this.sanitizer.bypassSecurityTrustUrl(window.URL.createObjectURL(files[i]));\n }\n\n this.files.push(files[i]);\n }\n }\n }\n\n this.files = [...this.files];\n this.onSelect.emit({ originalEvent: event, files: files, currentFiles: this.files });\n\n // this will check the fileLimit with the uploaded files\n this.checkFileLimit(files);\n\n if (this.hasFiles() && this.auto() && (this.mode() !== 'advanced' || !this.isFileLimitExceeded())) {\n this.upload();\n }\n\n if (event.type !== 'drop' && this.isIE11()) {\n this.clearIEInput();\n } else {\n this.clearInputElement();\n }\n }\n\n isFileSelected(file: File): boolean {\n for (let sFile of this.files) {\n if (sFile.name + sFile.type + sFile.size === file.name + file.type + file.size) {\n return true;\n }\n }\n\n return false;\n }\n\n isIE11() {\n if (isPlatformBrowser(this.platformId)) {\n return !!(this.document.defaultView as any)['MSInputMethodContext'] && !!(this.document as any)['documentMode'];\n }\n }\n\n validate(file: File): boolean {\n const acceptVal = this.accept();\n if (acceptVal && !this.isFileTypeValid(file)) {\n const text = `${this.invalidFileTypeMessageSummary().replace('{0}', file.name)} ${this.invalidFileTypeMessageDetail().replace('{0}', acceptVal)}`;\n this.msgs.update((msgs) => [...msgs, { severity: 'error', text }]);\n return false;\n }\n\n const maxFileSizeVal = this.maxFileSize();\n if (maxFileSizeVal && file.size > maxFileSizeVal) {\n const text = `${this.invalidFileSizeMessageSummary().replace('{0}', file.name)} ${this.invalidFileSizeMessageDetail().replace('{0}', this.formatSize(maxFileSizeVal))}`;\n this.msgs.update((msgs) => [...msgs, { severity: 'error', text }]);\n return false;\n }\n\n return true;\n }\n\n private isFileTypeValid(file: File): boolean {\n const acceptVal = this.accept();\n let acceptableTypes = acceptVal?.split(',').map((type) => type.trim());\n for (let type of acceptableTypes!) {\n let acceptable = this.isWildcard(type) ? this.getTypeClass(file.type) === this.getTypeClass(type) : file.type == type || this.getFileExtension(file).toLowerCase() === type.toLowerCase();\n\n if (acceptable) {\n return true;\n }\n }\n\n return false;\n }\n\n getTypeClass(fileType: string): string {\n return fileType.substring(0, fileType.indexOf('/'));\n }\n\n isWildcard(fileType: string): boolean {\n return fileType.indexOf('*') !== -1;\n }\n\n getFileExtension(file: File): string {\n return '.' + file.name.split('.').pop();\n }\n\n isImage(file: File): boolean {\n return /^image\\//.test(file.type);\n }\n\n onImageLoad(img: HTMLImageElement) {\n window.URL.revokeObjectURL(img.src);\n }\n /**\n * Uploads the selected files.\n * @group Method\n */\n uploader() {\n if (this.customUpload()) {\n const fileLimitVal = this.fileLimit();\n if (fileLimitVal) {\n this.uploadedFileCount += this.files.length;\n }\n\n this.uploadHandler.emit({\n files: this.files\n });\n } else {\n this.uploading.set(true);\n this.msgs.set([]);\n let formData = new FormData();\n\n this.onBeforeUpload.emit({\n formData: formData\n });\n\n const nameVal = this.name();\n for (let i = 0; i < this.files.length; i++) {\n formData.append(nameVal!, this.files[i], this.files[i].name);\n }\n\n this.http\n .request(this.method(), this.url() as string, {\n body: formData,\n headers: this.headers(),\n reportProgress: true,\n observe: 'events',\n withCredentials: this.withCredentials()\n })\n .subscribe({\n next: (event: HttpEvent<any>) => {\n switch (event.type) {\n case HttpEventType.Sent:\n this.onSend.emit({\n originalEvent: event,\n formData: formData\n });\n break;\n case HttpEventType.Response:\n this.uploading.set(false);\n this.progress.set(0);\n\n if (event['status'] >= 200 && event['status'] < 300) {\n const fileLimitVal = this.fileLimit();\n if (fileLimitVal) {\n this.uploadedFileCount += this.files.length;\n }\n\n this.onUpload.emit({ originalEvent: event, files: this.files });\n } else {\n this.onError.emit({ files: this.files });\n }\n this.uploadedFiles.update((files) => [...files, ...this.files]);\n this.clear();\n break;\n case HttpEventType.UploadProgress: {\n if (event['loaded']) {\n this.progress.set(Math.round((event['loaded'] * 100) / event['total']!));\n }\n\n this.onProgress.emit({ originalEvent: event, progress: this.progress() });\n break;\n }\n }\n },\n error: (error: ErrorEvent) => {\n this.uploading.set(false);\n this.onError.emit({ files: this.files, error: error });\n }\n });\n }\n }\n onRemoveClick(e: FileContentRemoveEvent) {\n const { event, index } = e;\n if (this.hasFiles()) {\n this.remove(event, index);\n }\n }\n onRemoveUploadedFileClick(e: Pick<FileContentRemoveEvent, 'index'>) {\n const { index } = e;\n if (this.hasUploadedFiles()) {\n this.removeUploadedFile(index);\n }\n }\n /**\n * Clears the files list.\n * @group Method\n */\n clear() {\n this.files = [];\n this.onClear.emit();\n this.clearInputElement();\n this.msgs.set([]);\n }\n /**\n * Removes a single file.\n * @param {Event} event - Browser event.\n * @param {Number} index - Index of the file.\n * @group Method\n */\n remove(event: Event, index: number) {\n this.clearInputElement();\n this.onRemove.emit({ originalEvent: event, file: this.files[index] });\n this.files.splice(index, 1);\n this.files = [...this.files];\n this.checkFileLimit(this.files);\n }\n /**\n * Removes uploaded file.\n * @param {Number} index - Index of the file to be removed.\n * @group Method\n */\n removeUploadedFile(index: number) {\n const currentFiles = this.uploadedFiles();\n const removedFile = currentFiles[index];\n this.uploadedFiles.set(currentFiles.filter((_, i) => i !== index));\n this.onRemoveUploadedFile.emit({ file: removedFile, files: this.uploadedFiles() });\n }\n\n isFileLimitExceeded() {\n const isAutoMode = this.auto();\n const totalFileCount = isAutoMode ? this.files.length : this.files.length + this.uploadedFileCount;\n const fileLimitVal = this.fileLimit();\n\n if (fileLimitVal && fileLimitVal <= totalFileCount && this.focus()) {\n this.focus.set(false);\n }\n\n return fileLimitVal && fileLimitVal < totalFileCount;\n }\n\n isChooseDisabled() {\n const fileLimitVal = this.fileLimit();\n if (this.auto()) {\n return fileLimitVal && fileLimitVal <= this.files.length;\n } else {\n return fileLimitVal && fileLimitVal <= this.files.length + this.uploadedFileCount;\n }\n }\n\n checkFileLimit(files: FileList | File[]) {\n const fileLimitVal = this.fileLimit();\n const currentMsgs = this.msgs();\n const hasExistingValidationMessages = currentMsgs.length > 0 && fileLimitVal && fileLimitVal < files.length;\n\n if (this.isFileLimitExceeded() || hasExistingValidationMessages) {\n const text = `${this.invalidFileLimitMessageSummary().replace('{0}', (fileLimitVal as number).toString())} ${this.invalidFileLimitMessageDetail().replace('{0}', (fileLimitVal as number).toString())}`;\n this.msgs.update((msgs) => [...msgs, { severity: 'error', text }]);\n } else {\n this.msgs.update((msgs) => msgs.filter((msg) => !msg.text.includes(this.invalidFileLimitMessageSummary())));\n }\n }\n\n clearInputElement() {\n const advancedInput = this.advancedFileInput();\n if (advancedInput && advancedInput.nativeElement) {\n advancedInput.nativeElement.value = '';\n }\n\n const basicInput = this.basicFileInput();\n if (basicInput && basicInput.nativeElement) {\n basicInput.nativeElement.value = '';\n }\n }\n\n clearIEInput() {\n const advancedInput = this.advancedFileInput();\n if (advancedInput && advancedInput.nativeElement) {\n this.duplicateIEEvent = true; //IE11 fix to prevent onFileChange trigger again\n advancedInput.nativeElement.value = '';\n }\n }\n\n hasFiles(): boolean {\n return this.files && this.files.length > 0;\n }\n\n hasUploadedFiles() {\n return this.uploadedFiles().length > 0;\n }\n\n getFileTemplateContext(file: File): FileUploadFileTemplateContext {\n return { $implicit: file };\n }\n\n onDragEnter(e: DragEvent) {\n if (!this.disabled()) {\n e.stopPropagation();\n e.preventDefault();\n }\n }\n\n onDragOver(e: DragEvent) {\n if (!this.disabled()) {\n const contentEl = this.contentEl();\n !this.$unstyled() && addClass(contentEl?.nativeElement, 'p-fileupload-highlight');\n contentEl?.nativeElement.setAttribute('data-p-highlight', true);\n this.dragHighlight.set(true);\n e.stopPropagation();\n e.preventDefault();\n }\n }\n\n onDragLeave(_event: DragEvent) {\n if (!this.disabled()) {\n const contentEl = this.contentEl();\n !this.$unstyled() && removeClass(contentEl?.nativeElement, 'p-fileupload-highlight');\n contentEl?.nativeElement.setAttribute('data-p-highlight', false);\n }\n }\n\n onDrop(event: DragEvent) {\n if (!this.disabled()) {\n const contentEl = this.contentEl();\n !this.$unstyled() && removeClass(contentEl?.nativeElement, 'p-fileupload-highlight');\n