UNPKG

@avoraui/av-file-picker

Version:

A customizable Angular file picker component for images and PDFs with preview support.

1 lines 14.4 kB
{"version":3,"file":"avoraui-av-file-picker.mjs","sources":["../../../projects/av-file-picker/src/lib/av-file-picker.ts","../../../projects/av-file-picker/src/lib/av-file-picker.html","../../../projects/av-file-picker/src/public-api.ts","../../../projects/av-file-picker/src/avoraui-av-file-picker.ts"],"sourcesContent":["import {Component, forwardRef, Input, OnDestroy} from \"@angular/core\";\r\nimport {ControlValueAccessor, NG_VALUE_ACCESSOR, ReactiveFormsModule} from \"@angular/forms\";\r\nimport {MatCard} from \"@angular/material/card\";\r\nimport {MatButton} from \"@angular/material/button\";\r\nimport {MatIcon} from '@angular/material/icon';\r\n\r\n@Component({\r\n selector: 'av-file-picker',\r\n imports: [\r\n ReactiveFormsModule,\r\n MatCard,\r\n MatButton,\r\n MatIcon,\r\n ],\r\n providers: [\r\n {\r\n provide: NG_VALUE_ACCESSOR,\r\n useExisting: forwardRef(() => AvFilePicker),\r\n multi: true\r\n }\r\n ],\r\n templateUrl: './av-file-picker.html',\r\n standalone: true,\r\n styleUrls: ['./av-file-picker.css']\r\n})\r\nexport class AvFilePicker implements ControlValueAccessor, OnDestroy {\r\n\r\n @Input() defaultImage: any;\r\n @Input() showPdfDetails!: boolean;\r\n\r\n imageURL: any;\r\n pdfFileName: string = \"\";\r\n isPDF: boolean = false;\r\n viewPdf: boolean = false;\r\n\r\n private convertValue: string = \"\";\r\n private decodeValue!: any;\r\n public pdfUrl!: any;\r\n\r\n // Max file size of 10MB\r\n private readonly MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB\r\n\r\n constructor() {\r\n }\r\n\r\n private onChange: any = () => {\r\n };\r\n private onTouched: any = () => {\r\n };\r\n private disabled = false;\r\n\r\n\r\n /**\r\n * Handles the selection of an image or PDF file from an input event and processes the file accordingly.\r\n * It validates the file size, checks its format, and reads the file to produce a base64-encoded string.\r\n * Updates the relevant properties depending on whether the file is an image or a PDF.\r\n *\r\n * @param {any} event - The event triggered by the input file selection.\r\n * @return {void} This method does not return a value.\r\n */\r\n selectImage(event: any): void {\r\n if (event.target.files) {\r\n const file = event.target.files[0];\r\n const reader = new FileReader();\r\n\r\n // Validate file size\r\n if (file.size > this.MAX_FILE_SIZE) {\r\n console.error('File is too large (max 10MB).');\r\n alert('File size exceeds the 10MB limit.');\r\n return;\r\n }\r\n\r\n // Determine if the file is a PDF\r\n if (file.type === 'application/pdf') {\r\n if (this.pdfUrl) {\r\n this.viewPdf = false;\r\n URL.revokeObjectURL(this.pdfUrl);\r\n }\r\n this.isPDF = true;\r\n this.showPdfDetails = true;\r\n this.pdfFileName = file.name;\r\n\r\n reader.onload = (e: ProgressEvent<FileReader>) => {\r\n const binaryString = e.target?.result as string;\r\n this.convertValue = btoa(binaryString);\r\n this.onChange(this.convertValue);\r\n };\r\n\r\n reader.onerror = (e: any) => {\r\n console.error('Error reading PDF file', e);\r\n alert('Error reading the PDF file.');\r\n };\r\n\r\n reader.readAsBinaryString(file);\r\n\r\n } else {\r\n // Assume the file is an image\r\n reader.onload = (e: any) => {\r\n this.imageURL = e.target.result;\r\n this.convertValue = btoa(this.imageURL);\r\n this.onChange(this.convertValue);\r\n };\r\n\r\n reader.onerror = (e: any) => {\r\n console.error('Error reading image file', e);\r\n alert('Error reading the image file.');\r\n };\r\n\r\n reader.readAsDataURL(file);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Resets the image-related properties to their default states.\r\n * Sets the imageURL to the default image, marks the object as not a PDF,\r\n * disables the PDF viewer, and triggers an update with the encoded imageURL.\r\n *\r\n * @return {void} Does not return a value.\r\n */\r\n clearImage(): void {\r\n this.imageURL = this.defaultImage;\r\n this.isPDF = false;\r\n this.viewPdf = false;\r\n this.onChange(btoa(this.imageURL));\r\n }\r\n\r\n /**\r\n * Processes and decodes a provided Base64-encoded value, determining if it is an image or a PDF.\r\n * Updates the relevant properties based on the decoded content or handles errors gracefully.\r\n *\r\n * @param {any} value - The Base64-encoded string to process. If null, undefined, or an empty string, it defaults to a predefined image.\r\n * @return {void} No return value.\r\n */\r\n writeValue(value: any): void {\r\n if (value === null || value === undefined || value === '') {\r\n this.imageURL = this.defaultImage; // Set to default image if no value provided\r\n } else {\r\n try {\r\n // Decode the Base64 value\r\n this.decodeValue = atob(value);\r\n\r\n // Convert the decoded string into a Uint8Array (binary data)\r\n const byteCharacters = new Uint8Array(this.decodeValue.length);\r\n for (let i = 0; i < this.decodeValue.length; i++) {\r\n byteCharacters[i] = this.decodeValue.charCodeAt(i);\r\n }\r\n\r\n // Check for file signature to identify PDF or image\r\n const pdfSignature = byteCharacters.slice(0, 4); // First 4 bytes for PDF\r\n const isPDF = pdfSignature[0] === 0x25 && pdfSignature[1] === 0x50 && pdfSignature[2] === 0x44 && pdfSignature[3] === 0x46;\r\n\r\n if (isPDF) {\r\n // It's a PDF, create a Blob and Object URL\r\n const blob = new Blob([byteCharacters], {type: 'application/pdf'});\r\n this.pdfUrl = URL.createObjectURL(blob);\r\n this.viewPdf = true;\r\n this.showPdfDetails = true;\r\n this.pdfFileName = \"\";\r\n } else {\r\n this.isPDF = false;\r\n this.showPdfDetails = false;\r\n this.imageURL = this.decodeValue;\r\n }\r\n } catch (error) {\r\n console.error('Error decoding Base64 value or handling file:', error);\r\n this.imageURL = this.defaultImage; // Reset to default image on error\r\n }\r\n }\r\n }\r\n\r\n registerOnChange(fn: any): void {\r\n this.onChange = fn;\r\n }\r\n\r\n registerOnTouched(fn: any): void {\r\n this.onTouched = fn;\r\n }\r\n\r\n setDisabledState?(isDisabled: boolean): void {\r\n this.disabled = isDisabled;\r\n }\r\n\r\n /**\r\n * Performs cleanup and resource deallocation before the component is destroyed.\r\n * Specifically, it revokes any created Object URLs to prevent memory leaks.\r\n *\r\n * @return {void} Does not return a value.\r\n */\r\n ngOnDestroy(): void {\r\n // Clean up Object URL to prevent memory leaks\r\n if (this.pdfUrl) {\r\n URL.revokeObjectURL(this.pdfUrl);\r\n }\r\n }\r\n\r\n}\r\n","<mat-card class=\"img-upload-panel\">\r\n <div class=\"img-preview-container\">\r\n <div class=\"img-placeholder\" [class.has-image]=\"imageURL || isPDF\" [class.has-pdf]=\"isPDF\">\r\n\r\n <!-- Regular Image -->\r\n @if (imageURL && !isPDF) {\r\n <img [src]=\"imageURL\" class=\"preview-image\" alt=\"Selected file\">\r\n }\r\n\r\n <!-- PDF Icon Display -->\r\n @if (isPDF) {\r\n <div class=\"pdf-placeholder\">\r\n <mat-icon class=\"pdf-icon\">picture_as_pdf</mat-icon>\r\n <span class=\"pdf-text\">PDF Document</span>\r\n </div>\r\n }\r\n\r\n <!-- Empty State -->\r\n @if (!imageURL && !isPDF) {\r\n <div class=\"placeholder-content\">\r\n <mat-icon class=\"placeholder-icon\">cloud_upload</mat-icon>\r\n <span class=\"placeholder-text\">No file selected</span>\r\n </div>\r\n }\r\n\r\n <!-- PDF View Link Overlay -->\r\n @if (viewPdf && showPdfDetails && isPDF) {\r\n <div class=\"pdf-overlay\">\r\n <a [href]=\"pdfUrl\" target=\"_blank\" class=\"pdf-link\">\r\n <mat-icon>visibility</mat-icon>\r\n <span>View PDF</span>\r\n </a>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Hidden File Input -->\r\n <input #fileInput\r\n type=\"file\"\r\n class=\"file-input\"\r\n (change)=\"selectImage($event)\"\r\n accept=\"image/*,.pdf\">\r\n </div>\r\n\r\n <!-- File Name Display -->\r\n @if (isPDF && showPdfDetails) {\r\n <div class=\"file-info\">\r\n <mat-icon class=\"file-icon\">description</mat-icon>\r\n <span class=\"file-name\">{{ pdfFileName }}</span>\r\n </div>\r\n }\r\n\r\n <!-- Action Buttons -->\r\n <div class=\"action-buttons\">\r\n <button mat-raised-button\r\n color=\"primary\"\r\n class=\"select-btn\"\r\n (click)=\"fileInput.click()\">\r\n <mat-icon>folder_open</mat-icon>\r\n Select\r\n </button>\r\n\r\n <button mat-raised-button\r\n color=\"warn\"\r\n class=\"clear-btn\"\r\n (click)=\"clearImage()\"\r\n [disabled]=\"!imageURL && !isPDF\">\r\n <mat-icon>clear</mat-icon>\r\n Clear\r\n </button>\r\n </div>\r\n</mat-card>\r\n","/*\r\n * Public API Surface of av-file-picker\r\n */\r\n\r\nexport * from './lib/av-file-picker';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;;;MAyBa,YAAY,CAAA;AAEd,IAAA,YAAY;AACZ,IAAA,cAAc;AAEvB,IAAA,QAAQ;IACR,WAAW,GAAW,EAAE;IACxB,KAAK,GAAY,KAAK;IACtB,OAAO,GAAY,KAAK;IAEhB,YAAY,GAAW,EAAE;AACzB,IAAA,WAAW;AACZ,IAAA,MAAM;;IAGI,aAAa,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAElD,IAAA,WAAA,GAAA;;IAGQ,QAAQ,GAAQ,MAAK;AAC7B,KAAC;IACO,SAAS,GAAQ,MAAK;AAC9B,KAAC;IACO,QAAQ,GAAG,KAAK;AAGxB;;;;;;;AAOG;AACH,IAAA,WAAW,CAAC,KAAU,EAAA;AACpB,QAAA,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE;YACtB,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AAClC,YAAA,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE;;YAG/B,IAAI,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,aAAa,EAAE;AAClC,gBAAA,OAAO,CAAC,KAAK,CAAC,+BAA+B,CAAC;gBAC9C,KAAK,CAAC,mCAAmC,CAAC;gBAC1C;;;AAIF,YAAA,IAAI,IAAI,CAAC,IAAI,KAAK,iBAAiB,EAAE;AACnC,gBAAA,IAAI,IAAI,CAAC,MAAM,EAAE;AACf,oBAAA,IAAI,CAAC,OAAO,GAAG,KAAK;AACpB,oBAAA,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC;;AAElC,gBAAA,IAAI,CAAC,KAAK,GAAG,IAAI;AACjB,gBAAA,IAAI,CAAC,cAAc,GAAG,IAAI;AAC1B,gBAAA,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,IAAI;AAE5B,gBAAA,MAAM,CAAC,MAAM,GAAG,CAAC,CAA4B,KAAI;AAC/C,oBAAA,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,EAAE,MAAgB;AAC/C,oBAAA,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;AACtC,oBAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC;AAClC,iBAAC;AAED,gBAAA,MAAM,CAAC,OAAO,GAAG,CAAC,CAAM,KAAI;AAC1B,oBAAA,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,CAAC,CAAC;oBAC1C,KAAK,CAAC,6BAA6B,CAAC;AACtC,iBAAC;AAED,gBAAA,MAAM,CAAC,kBAAkB,CAAC,IAAI,CAAC;;iBAE1B;;AAEL,gBAAA,MAAM,CAAC,MAAM,GAAG,CAAC,CAAM,KAAI;oBACzB,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM;oBAC/B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;AACvC,oBAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC;AAClC,iBAAC;AAED,gBAAA,MAAM,CAAC,OAAO,GAAG,CAAC,CAAM,KAAI;AAC1B,oBAAA,OAAO,CAAC,KAAK,CAAC,0BAA0B,EAAE,CAAC,CAAC;oBAC5C,KAAK,CAAC,+BAA+B,CAAC;AACxC,iBAAC;AAED,gBAAA,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC;;;;AAKhC;;;;;;AAMG;IACH,UAAU,GAAA;AACR,QAAA,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,YAAY;AACjC,QAAA,IAAI,CAAC,KAAK,GAAG,KAAK;AAClB,QAAA,IAAI,CAAC,OAAO,GAAG,KAAK;QACpB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;;AAGpC;;;;;;AAMG;AACH,IAAA,UAAU,CAAC,KAAU,EAAA;AACnB,QAAA,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,EAAE;YACzD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC;;aAC7B;AACL,YAAA,IAAI;;AAEF,gBAAA,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC;;gBAG9B,MAAM,cAAc,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;AAC9D,gBAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AAChD,oBAAA,cAAc,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC;;;AAIpD,gBAAA,MAAM,YAAY,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAChD,gBAAA,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI;gBAE1H,IAAI,KAAK,EAAE;;AAET,oBAAA,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,cAAc,CAAC,EAAE,EAAC,IAAI,EAAE,iBAAiB,EAAC,CAAC;oBAClE,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC;AACvC,oBAAA,IAAI,CAAC,OAAO,GAAG,IAAI;AACnB,oBAAA,IAAI,CAAC,cAAc,GAAG,IAAI;AAC1B,oBAAA,IAAI,CAAC,WAAW,GAAG,EAAE;;qBAChB;AACL,oBAAA,IAAI,CAAC,KAAK,GAAG,KAAK;AAClB,oBAAA,IAAI,CAAC,cAAc,GAAG,KAAK;AAC3B,oBAAA,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,WAAW;;;YAElC,OAAO,KAAK,EAAE;AACd,gBAAA,OAAO,CAAC,KAAK,CAAC,+CAA+C,EAAE,KAAK,CAAC;gBACrE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC;;;;AAKxC,IAAA,gBAAgB,CAAC,EAAO,EAAA;AACtB,QAAA,IAAI,CAAC,QAAQ,GAAG,EAAE;;AAGpB,IAAA,iBAAiB,CAAC,EAAO,EAAA;AACvB,QAAA,IAAI,CAAC,SAAS,GAAG,EAAE;;AAGrB,IAAA,gBAAgB,CAAE,UAAmB,EAAA;AACnC,QAAA,IAAI,CAAC,QAAQ,GAAG,UAAU;;AAG5B;;;;;AAKG;IACH,WAAW,GAAA;;AAET,QAAA,IAAI,IAAI,CAAC,MAAM,EAAE;AACf,YAAA,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC;;;uGAvKzB,YAAY,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAZ,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,YAAY,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,gBAAA,EAAA,MAAA,EAAA,EAAA,YAAA,EAAA,cAAA,EAAA,cAAA,EAAA,gBAAA,EAAA,EAAA,SAAA,EAXZ;AACT,YAAA;AACE,gBAAA,OAAO,EAAE,iBAAiB;AAC1B,gBAAA,WAAW,EAAE,UAAU,CAAC,MAAM,YAAY,CAAC;AAC3C,gBAAA,KAAK,EAAE;AACR;SACF,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECpBH,wwEAwEA,i4GD/DI,mBAAmB,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EACnB,OAAO,EAAA,QAAA,EAAA,UAAA,EAAA,MAAA,EAAA,CAAA,YAAA,CAAA,EAAA,QAAA,EAAA,CAAA,SAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EACP,SAAS,yUACT,OAAO,EAAA,QAAA,EAAA,UAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,QAAA,EAAA,SAAA,EAAA,SAAA,EAAA,UAAA,CAAA,EAAA,QAAA,EAAA,CAAA,SAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;2FAaE,YAAY,EAAA,UAAA,EAAA,CAAA;kBAnBxB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,gBAAgB,EAAA,OAAA,EACjB;wBACP,mBAAmB;wBACnB,OAAO;wBACP,SAAS;wBACT,OAAO;qBACR,EAAA,SAAA,EACU;AACT,wBAAA;AACE,4BAAA,OAAO,EAAE,iBAAiB;AAC1B,4BAAA,WAAW,EAAE,UAAU,CAAC,kBAAkB,CAAC;AAC3C,4BAAA,KAAK,EAAE;AACR;AACF,qBAAA,EAAA,UAAA,EAEW,IAAI,EAAA,QAAA,EAAA,wwEAAA,EAAA,MAAA,EAAA,CAAA,00GAAA,CAAA,EAAA;wDAKP,YAAY,EAAA,CAAA;sBAApB;gBACQ,cAAc,EAAA,CAAA;sBAAtB;;;AE5BH;;AAEG;;ACFH;;AAEG;;;;"}