@winexist/ngp 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,12 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, Component } from '@angular/core';
2
+ import { input, Component, output, signal, effect, ViewChild, ContentChild, Input } from '@angular/core';
3
+ import * as i2 from '@angular/common';
4
+ import { CommonModule } from '@angular/common';
5
+ import * as i1 from 'primeng/fileupload';
6
+ import { FileUploadModule } from 'primeng/fileupload';
7
+ import { NgControl } from '@angular/forms';
8
+ import * as i1$1 from 'primeng/message';
9
+ import { MessageModule } from 'primeng/message';
3
10
 
4
11
  class EmptyComponent {
5
12
  title = input('Section is empty.', ...(ngDevMode ? [{ debugName: "title" }] : []));
@@ -13,12 +20,159 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
13
20
  args: [{ selector: 'ngp-empty', imports: [], template: "<div class=\"w-full flex justify-center items-center p-5 h-full\">\n <div class=\"flex items-center gap-3\">\n @if (imgSrc()) {\n <img alt=\"Empty\" [src]=\"imgSrc()\" class=\"w-[80px]\" />\n } @else {\n <div class=\"!rounded-md p-5 !bg-slate-100 mb-6\">\n <span class=\"material-icons !text-gray-400 !text-7xl\"> search_off </span>\n </div>\n }\n\n <div class=\"flex flex-col\">\n <span class=\"!text-gray-900 !text-2xl !font-bold\">{{ title() }}</span>\n @if (message()) {\n <span class=\"!text-gray-400 !text-base\">{{ message() }}</span>\n }\n </div>\n </div>\n</div>" }]
14
21
  }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: false }] }], imgSrc: [{ type: i0.Input, args: [{ isSignal: true, alias: "imgSrc", required: false }] }] } });
15
22
 
23
+ class FileUploadComponent {
24
+ fileUploader;
25
+ // Signal-based inputs with defaults
26
+ files = input([], ...(ngDevMode ? [{ debugName: "files" }] : []));
27
+ multiple = input(true, ...(ngDevMode ? [{ debugName: "multiple" }] : []));
28
+ accept = input('.pdf,.png,.jpg,.jpeg,.doc,.docx,.csv,.xls,.xlsx', ...(ngDevMode ? [{ debugName: "accept" }] : []));
29
+ maxFileSize = input(2097152, ...(ngDevMode ? [{ debugName: "maxFileSize" }] : [])); // 2MB in bytes
30
+ placeholder = input('Drag and drop new files here to upload.', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
31
+ showUploadButton = input(false, ...(ngDevMode ? [{ debugName: "showUploadButton" }] : []));
32
+ cancelLabel = input('Clear', ...(ngDevMode ? [{ debugName: "cancelLabel" }] : []));
33
+ // Signal-based outputs
34
+ filesSelected = output();
35
+ filesCleared = output();
36
+ // Internal state
37
+ uploadedFiles = signal([], ...(ngDevMode ? [{ debugName: "uploadedFiles" }] : []));
38
+ constructor() {
39
+ effect(() => {
40
+ const _files = this.files();
41
+ if (_files.length) {
42
+ this.uploadedFiles.set(_files.map((file) => this.addFileType(file)));
43
+ }
44
+ });
45
+ }
46
+ onFileSelect(event) {
47
+ const validFiles = event.currentFiles.filter((file) => file.size <= this.maxFileSize() && this.isValidFileType(file));
48
+ if (validFiles.length) {
49
+ // Add type to files before adding to uploadedFiles
50
+ const typedFiles = validFiles.map((file) => this.addFileType(file));
51
+ if (this.multiple()) {
52
+ this.uploadedFiles.update((files) => Array.from(new Set([...files, ...typedFiles])));
53
+ }
54
+ else {
55
+ this.uploadedFiles.set(typedFiles);
56
+ }
57
+ this.filesSelected.emit(typedFiles);
58
+ }
59
+ }
60
+ onClear() {
61
+ this.uploadedFiles.set([]);
62
+ this.filesCleared.emit();
63
+ }
64
+ removeFile(index) {
65
+ this.uploadedFiles.update((files) => files.filter((_, i) => i !== index));
66
+ this.filesSelected.emit(this.uploadedFiles());
67
+ }
68
+ isValidFileType(file) {
69
+ const acceptedTypes = this.accept().split(',');
70
+ return acceptedTypes.some((type) => file.name.toLowerCase().endsWith(type.trim().toLowerCase()));
71
+ }
72
+ formatFileSize(bytes) {
73
+ if (bytes === 0)
74
+ return '0 Bytes';
75
+ const k = 1024;
76
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
77
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
78
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
79
+ }
80
+ clear() {
81
+ this.uploadedFiles.set([]);
82
+ if (this.fileUploader?.nativeElement?.clear) {
83
+ this.fileUploader.nativeElement.clear();
84
+ }
85
+ }
86
+ // Function to determine file type based on extension
87
+ getFileTypeByExtension(file) {
88
+ const fileName = file.name.toLowerCase();
89
+ const imageExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.svg', '.webp'];
90
+ const pdfExtension = '.pdf';
91
+ if (imageExtensions.some((ext) => fileName.endsWith(ext))) {
92
+ return 'image';
93
+ }
94
+ else if (fileName.endsWith(pdfExtension)) {
95
+ return 'pdf';
96
+ }
97
+ else {
98
+ return 'other';
99
+ }
100
+ }
101
+ // Function to add type property to file
102
+ addFileType(file) {
103
+ const typedFile = file;
104
+ typedFile.fileType = this.getFileTypeByExtension(file);
105
+ return typedFile;
106
+ }
107
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FileUploadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
108
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: FileUploadComponent, isStandalone: true, selector: "ngp-file-upload", inputs: { files: { classPropertyName: "files", publicName: "files", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, maxFileSize: { classPropertyName: "maxFileSize", publicName: "maxFileSize", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, showUploadButton: { classPropertyName: "showUploadButton", publicName: "showUploadButton", isSignal: true, isRequired: false, transformFunction: null }, cancelLabel: { classPropertyName: "cancelLabel", publicName: "cancelLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { filesSelected: "filesSelected", filesCleared: "filesCleared" }, viewQueries: [{ propertyName: "fileUploader", first: true, predicate: ["fileUploader"], descendants: true }], ngImport: i0, template: "<p-fileupload #fileUploader [multiple]=\"multiple()\" [showUploadButton]=\"showUploadButton()\"\n [cancelLabel]=\"cancelLabel()\" [accept]=\"accept()\" auto=\"false\" [maxFileSize]=\"maxFileSize()\" mode=\"advanced\"\n [customUpload]=\"true\" styleClass=\"!bg-gray-50\" (onSelect)=\"onFileSelect($event)\" (onClear)=\"onClear()\">\n <ng-template #content>\n @if (uploadedFiles().length) {\n <ul class=\"space-y-2\">\n @for (file of uploadedFiles(); track $index) {\n <li class=\"flex justify-between gap-4 p-2 bg-gray-100 rounded\">\n <div class=\"flex gap-2\">\n <ng-container [ngTemplateOutlet]=\"iconTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: file.fileType }\"></ng-container>\n <span class=\"text-sm\"> {{ file.name }} - {{ formatFileSize(file.size) }} </span>\n </div>\n <button type=\"button\" (click)=\"removeFile($index)\" class=\"text-red-500 hover:text-red-700 text-sm\">\n <span class=\"material-icons !text-base\"> cancel </span>\n </button>\n </li>\n }\n </ul>\n } @else {\n <div class=\"flex flex-col items-center gap-3\">\n <img src=\"assets/images/file-upload.svg\" alt=\"File Upload\" class=\"w-28 mx-auto\" />\n <span class=\"font-semibold text-center text-gray-400 text-sm\">\n Drag & drop or<br />\n click the \"Choose\" button to add new files\n </span>\n </div>\n }\n </ng-template>\n <ng-template #file></ng-template>\n</p-fileupload>\n\n<ng-template #iconTemplate let-type>\n @if (type === 'image') {\n <span class=\"material-icons\"> image </span>\n } @else if (type === 'pdf') {\n <span class=\"material-icons\"> picture_as_pdf </span>\n } @else {\n <span class=\"material-icons\"> insert_drive_file </span>\n }\n</ng-template>", styles: [""], dependencies: [{ kind: "ngmodule", type: FileUploadModule }, { kind: "component", type: i1.FileUpload, selector: "p-fileupload, p-fileUpload", inputs: ["name", "url", "method", "multiple", "accept", "disabled", "auto", "withCredentials", "maxFileSize", "invalidFileSizeMessageSummary", "invalidFileSizeMessageDetail", "invalidFileTypeMessageSummary", "invalidFileTypeMessageDetail", "invalidFileLimitMessageDetail", "invalidFileLimitMessageSummary", "style", "styleClass", "previewWidth", "chooseLabel", "uploadLabel", "cancelLabel", "chooseIcon", "uploadIcon", "cancelIcon", "showUploadButton", "showCancelButton", "mode", "headers", "customUpload", "fileLimit", "uploadStyleClass", "cancelStyleClass", "removeStyleClass", "chooseStyleClass", "chooseButtonProps", "uploadButtonProps", "cancelButtonProps", "files"], outputs: ["onBeforeUpload", "onSend", "onUpload", "onError", "onClear", "onRemove", "onSelect", "onProgress", "uploadHandler", "onImageError", "onRemoveUploadedFile"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
109
+ }
110
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FileUploadComponent, decorators: [{
111
+ type: Component,
112
+ args: [{ selector: 'ngp-file-upload', imports: [FileUploadModule, CommonModule], template: "<p-fileupload #fileUploader [multiple]=\"multiple()\" [showUploadButton]=\"showUploadButton()\"\n [cancelLabel]=\"cancelLabel()\" [accept]=\"accept()\" auto=\"false\" [maxFileSize]=\"maxFileSize()\" mode=\"advanced\"\n [customUpload]=\"true\" styleClass=\"!bg-gray-50\" (onSelect)=\"onFileSelect($event)\" (onClear)=\"onClear()\">\n <ng-template #content>\n @if (uploadedFiles().length) {\n <ul class=\"space-y-2\">\n @for (file of uploadedFiles(); track $index) {\n <li class=\"flex justify-between gap-4 p-2 bg-gray-100 rounded\">\n <div class=\"flex gap-2\">\n <ng-container [ngTemplateOutlet]=\"iconTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: file.fileType }\"></ng-container>\n <span class=\"text-sm\"> {{ file.name }} - {{ formatFileSize(file.size) }} </span>\n </div>\n <button type=\"button\" (click)=\"removeFile($index)\" class=\"text-red-500 hover:text-red-700 text-sm\">\n <span class=\"material-icons !text-base\"> cancel </span>\n </button>\n </li>\n }\n </ul>\n } @else {\n <div class=\"flex flex-col items-center gap-3\">\n <img src=\"assets/images/file-upload.svg\" alt=\"File Upload\" class=\"w-28 mx-auto\" />\n <span class=\"font-semibold text-center text-gray-400 text-sm\">\n Drag & drop or<br />\n click the \"Choose\" button to add new files\n </span>\n </div>\n }\n </ng-template>\n <ng-template #file></ng-template>\n</p-fileupload>\n\n<ng-template #iconTemplate let-type>\n @if (type === 'image') {\n <span class=\"material-icons\"> image </span>\n } @else if (type === 'pdf') {\n <span class=\"material-icons\"> picture_as_pdf </span>\n } @else {\n <span class=\"material-icons\"> insert_drive_file </span>\n }\n</ng-template>" }]
113
+ }], ctorParameters: () => [], propDecorators: { fileUploader: [{
114
+ type: ViewChild,
115
+ args: ['fileUploader']
116
+ }], files: [{ type: i0.Input, args: [{ isSignal: true, alias: "files", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], maxFileSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFileSize", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], showUploadButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showUploadButton", required: false }] }], cancelLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "cancelLabel", required: false }] }], filesSelected: [{ type: i0.Output, args: ["filesSelected"] }], filesCleared: [{ type: i0.Output, args: ["filesCleared"] }] } });
117
+
118
+ // form-field-wrapper.component.ts
119
+ class FormFieldWrapperComponent {
120
+ validationMessages = {
121
+ required: 'This field is required',
122
+ email: 'Please enter a valid email',
123
+ minlength: 'Input does not meet minimum length',
124
+ maxlength: 'Input exceeds maximum length',
125
+ pattern: 'Input format is invalid',
126
+ endTimeBeforeStartTime: 'End time must not be before start time',
127
+ breakStartBeforeEarliestStart: 'Break start time cannot be before the earliest start time'
128
+ };
129
+ control;
130
+ subscription;
131
+ ngAfterContentInit() {
132
+ if (this.control) {
133
+ this.subscription = this.control.statusChanges?.subscribe(() => { });
134
+ }
135
+ }
136
+ ngOnDestroy() {
137
+ this.subscription?.unsubscribe();
138
+ }
139
+ shouldShowError() {
140
+ return (!!this.control &&
141
+ this.control.invalid === true &&
142
+ (this.control.dirty === true || this.control.touched === true));
143
+ }
144
+ getErrorMessage() {
145
+ if (!this.control?.errors) {
146
+ return '';
147
+ }
148
+ const firstErrorKey = Object.keys(this.control.errors)[0];
149
+ const error = this.control.errors[firstErrorKey];
150
+ if (this.control.errors['message']) {
151
+ return this.control.errors['message'];
152
+ }
153
+ if (error?.message) {
154
+ return error.message;
155
+ }
156
+ return this.validationMessages[firstErrorKey] || `Invalid input: ${firstErrorKey}`;
157
+ }
158
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FormFieldWrapperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
159
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: FormFieldWrapperComponent, isStandalone: true, selector: "ngp-form-field-wrapper", inputs: { validationMessages: "validationMessages" }, queries: [{ propertyName: "control", first: true, predicate: NgControl, descendants: true }], ngImport: i0, template: "<div class=\"form-field-container\">\n <ng-content></ng-content>\n @if (shouldShowError()) {\n <p-message severity=\"error\" variant=\"simple\" size=\"small\" [text]=\"getErrorMessage()\">\n </p-message>\n }\n</div>", styles: [":host{display:block;margin-bottom:1rem}.form-field-container{display:flex;flex-direction:column;gap:4px}:host ::ng-deep .ng-invalid.ng-touched,:host ::ng-deep .ng-invalid.ng-dirty{border-color:#f44336!important;outline-color:#f44336!important}:host ::ng-deep .ng-invalid.ng-touched input,:host ::ng-deep .ng-invalid.ng-dirty input{border-color:#f44336!important;outline-color:#f44336!important}:host ::ng-deep .ng-invalid.ng-touched .ng-select-container,:host ::ng-deep .ng-invalid.ng-dirty .ng-select-container{border-color:#f44336!important;outline-color:#f44336!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MessageModule }, { kind: "component", type: i1$1.Message, selector: "p-message", inputs: ["severity", "text", "escape", "style", "styleClass", "closable", "icon", "closeIcon", "life", "showTransitionOptions", "hideTransitionOptions", "size", "variant"], outputs: ["onClose"] }] });
160
+ }
161
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FormFieldWrapperComponent, decorators: [{
162
+ type: Component,
163
+ args: [{ selector: 'ngp-form-field-wrapper', standalone: true, imports: [CommonModule, MessageModule], template: "<div class=\"form-field-container\">\n <ng-content></ng-content>\n @if (shouldShowError()) {\n <p-message severity=\"error\" variant=\"simple\" size=\"small\" [text]=\"getErrorMessage()\">\n </p-message>\n }\n</div>", styles: [":host{display:block;margin-bottom:1rem}.form-field-container{display:flex;flex-direction:column;gap:4px}:host ::ng-deep .ng-invalid.ng-touched,:host ::ng-deep .ng-invalid.ng-dirty{border-color:#f44336!important;outline-color:#f44336!important}:host ::ng-deep .ng-invalid.ng-touched input,:host ::ng-deep .ng-invalid.ng-dirty input{border-color:#f44336!important;outline-color:#f44336!important}:host ::ng-deep .ng-invalid.ng-touched .ng-select-container,:host ::ng-deep .ng-invalid.ng-dirty .ng-select-container{border-color:#f44336!important;outline-color:#f44336!important}\n"] }]
164
+ }], propDecorators: { validationMessages: [{
165
+ type: Input
166
+ }], control: [{
167
+ type: ContentChild,
168
+ args: [NgControl]
169
+ }] } });
170
+
16
171
  // export * from './lib/components/components';
17
- // export * from './lib/components/file-upload/file-upload.component'
18
172
 
19
173
  /**
20
174
  * Generated bundle index. Do not edit.
21
175
  */
22
176
 
23
- export { EmptyComponent };
177
+ export { EmptyComponent, FileUploadComponent, FormFieldWrapperComponent };
24
178
  //# sourceMappingURL=winexist-ngp.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"winexist-ngp.mjs","sources":["../../../packages/src/lib/components/empty/empty.component.ts","../../../packages/src/lib/components/empty/empty.component.html","../../../packages/src/index.ts","../../../packages/src/winexist-ngp.ts"],"sourcesContent":["import { Component, input } from '@angular/core';\n\n@Component({\n\tselector: 'ngp-empty',\n\timports: [],\n\ttemplateUrl: './empty.component.html',\n\tstyleUrl: './empty.component.css'\n})\nexport class EmptyComponent {\n\ttitle = input<string>('Section is empty.');\n\tmessage = input<string>('No data available.');\n\timgSrc = input<string>();\n\n}\n","<div class=\"w-full flex justify-center items-center p-5 h-full\">\n <div class=\"flex items-center gap-3\">\n @if (imgSrc()) {\n <img alt=\"Empty\" [src]=\"imgSrc()\" class=\"w-[80px]\" />\n } @else {\n <div class=\"!rounded-md p-5 !bg-slate-100 mb-6\">\n <span class=\"material-icons !text-gray-400 !text-7xl\"> search_off </span>\n </div>\n }\n\n <div class=\"flex flex-col\">\n <span class=\"!text-gray-900 !text-2xl !font-bold\">{{ title() }}</span>\n @if (message()) {\n <span class=\"!text-gray-400 !text-base\">{{ message() }}</span>\n }\n </div>\n </div>\n</div>","// export * from './lib/components/components';\nexport * from './lib/components/empty/empty.component'\n// export * from './lib/components/file-upload/file-upload.component'","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;MAQa,cAAc,CAAA;AAC1B,IAAA,KAAK,GAAG,KAAK,CAAS,mBAAmB,iDAAC;AAC1C,IAAA,OAAO,GAAG,KAAK,CAAS,oBAAoB,mDAAC;IAC7C,MAAM,GAAG,KAAK,CAAA,IAAA,SAAA,GAAA,CAAA,SAAA,EAAA,EAAA,SAAA,EAAA,QAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAU;wGAHZ,cAAc,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAd,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,cAAc,kcCR3B,gnBAiBM,EAAA,MAAA,EAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;4FDTO,cAAc,EAAA,UAAA,EAAA,CAAA;kBAN1B,SAAS;AACC,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,WAAW,WACZ,EAAE,EAAA,QAAA,EAAA,gnBAAA,EAAA;;;AEJZ;AAEA;;ACFA;;AAEG;;;;"}
1
+ {"version":3,"file":"winexist-ngp.mjs","sources":["../../../packages/src/lib/components/empty/empty.component.ts","../../../packages/src/lib/components/empty/empty.component.html","../../../packages/src/lib/components/file-upload/file-upload.component.ts","../../../packages/src/lib/components/file-upload/file-upload.component.html","../../../packages/src/lib/components/form-field-wrapper/form-field-wrapper.component.ts","../../../packages/src/lib/components/form-field-wrapper/form-field-wrapper.component.html","../../../packages/src/index.ts","../../../packages/src/winexist-ngp.ts"],"sourcesContent":["import { Component, input } from '@angular/core';\n\n@Component({\n\tselector: 'ngp-empty',\n\timports: [],\n\ttemplateUrl: './empty.component.html',\n\tstyleUrl: './empty.component.css'\n})\nexport class EmptyComponent {\n\ttitle = input<string>('Section is empty.');\n\tmessage = input<string>('No data available.');\n\timgSrc = input<string>();\n\n}\n","<div class=\"w-full flex justify-center items-center p-5 h-full\">\n <div class=\"flex items-center gap-3\">\n @if (imgSrc()) {\n <img alt=\"Empty\" [src]=\"imgSrc()\" class=\"w-[80px]\" />\n } @else {\n <div class=\"!rounded-md p-5 !bg-slate-100 mb-6\">\n <span class=\"material-icons !text-gray-400 !text-7xl\"> search_off </span>\n </div>\n }\n\n <div class=\"flex flex-col\">\n <span class=\"!text-gray-900 !text-2xl !font-bold\">{{ title() }}</span>\n @if (message()) {\n <span class=\"!text-gray-400 !text-base\">{{ message() }}</span>\n }\n </div>\n </div>\n</div>","import { CommonModule } from '@angular/common';\nimport { Component, effect, ElementRef, input, output, signal, ViewChild } from '@angular/core';\nimport { FileSelectEvent, FileUploadModule } from 'primeng/fileupload';\n\n// Add interface for typed files\ninterface TypedFile extends File {\n\tfileType?: 'image' | 'pdf' | 'other';\n}\n\n@Component({\n\tselector: 'ngp-file-upload',\n\timports: [FileUploadModule, CommonModule],\n\ttemplateUrl: './file-upload.component.html',\n\tstyleUrl: './file-upload.component.css',\n})\nexport class FileUploadComponent {\n\t@ViewChild('fileUploader') fileUploader!: ElementRef;\n\n\t// Signal-based inputs with defaults\n\tfiles = input<File[]>([]);\n\tmultiple = input(true);\n\taccept = input('.pdf,.png,.jpg,.jpeg,.doc,.docx,.csv,.xls,.xlsx');\n\tmaxFileSize = input(2097152); // 2MB in bytes\n\tplaceholder = input('Drag and drop new files here to upload.');\n\tshowUploadButton = input<boolean>(false);\n\tcancelLabel = input('Clear');\n\n\t// Signal-based outputs\n\tfilesSelected = output<File[]>();\n\tfilesCleared = output<void>();\n\n\t// Internal state\n\tuploadedFiles = signal<TypedFile[]>([]);\n\n\tconstructor() {\n\t\teffect(() => {\n\t\t\tconst _files = this.files();\n\t\t\tif (_files.length) {\n\t\t\t\tthis.uploadedFiles.set(_files.map((file) => this.addFileType(file)));\n\t\t\t}\n\t\t});\n\t}\n\n\tonFileSelect(event: FileSelectEvent) {\n\t\tconst validFiles = event.currentFiles.filter(\n\t\t\t(file) => file.size <= this.maxFileSize() && this.isValidFileType(file)\n\t\t);\n\n\t\tif (validFiles.length) {\n\t\t\t// Add type to files before adding to uploadedFiles\n\t\t\tconst typedFiles = validFiles.map((file) => this.addFileType(file));\n\n\t\t\tif (this.multiple()) {\n\t\t\t\tthis.uploadedFiles.update((files) => Array.from(new Set([...files, ...typedFiles])));\n\t\t\t} else {\n\t\t\t\tthis.uploadedFiles.set(typedFiles);\n\t\t\t}\n\t\t\tthis.filesSelected.emit(typedFiles);\n\t\t}\n\t}\n\n\tonClear() {\n\t\tthis.uploadedFiles.set([]);\n\t\tthis.filesCleared.emit();\n\t}\n\n\tremoveFile(index: number) {\n\t\tthis.uploadedFiles.update((files) => files.filter((_, i) => i !== index));\n\t\tthis.filesSelected.emit(this.uploadedFiles());\n\t}\n\n\tisValidFileType(file: File): boolean {\n\t\tconst acceptedTypes = this.accept().split(',');\n\t\treturn acceptedTypes.some((type) =>\n\t\t\tfile.name.toLowerCase().endsWith(type.trim().toLowerCase())\n\t\t);\n\t}\n\n\tformatFileSize(bytes: number): string {\n\t\tif (bytes === 0) return '0 Bytes';\n\t\tconst k = 1024;\n\t\tconst sizes = ['Bytes', 'KB', 'MB', 'GB'];\n\t\tconst i = Math.floor(Math.log(bytes) / Math.log(k));\n\t\treturn parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];\n\t}\n\n\tclear() {\n\t\tthis.uploadedFiles.set([]);\n\t\tif (this.fileUploader?.nativeElement?.clear) {\n\t\t\tthis.fileUploader.nativeElement.clear();\n\t\t}\n\t}\n\n\t// Function to determine file type based on extension\n\tgetFileTypeByExtension(file: File): 'image' | 'pdf' | 'other' {\n\t\tconst fileName = file.name.toLowerCase();\n\t\tconst imageExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.svg', '.webp'];\n\t\tconst pdfExtension = '.pdf';\n\n\t\tif (imageExtensions.some((ext) => fileName.endsWith(ext))) {\n\t\t\treturn 'image';\n\t\t} else if (fileName.endsWith(pdfExtension)) {\n\t\t\treturn 'pdf';\n\t\t} else {\n\t\t\treturn 'other';\n\t\t}\n\t}\n\n\t// Function to add type property to file\n\taddFileType(file: File): TypedFile {\n\t\tconst typedFile = file as TypedFile;\n\t\ttypedFile.fileType = this.getFileTypeByExtension(file);\n\t\treturn typedFile;\n\t}\n}\n","<p-fileupload #fileUploader [multiple]=\"multiple()\" [showUploadButton]=\"showUploadButton()\"\n [cancelLabel]=\"cancelLabel()\" [accept]=\"accept()\" auto=\"false\" [maxFileSize]=\"maxFileSize()\" mode=\"advanced\"\n [customUpload]=\"true\" styleClass=\"!bg-gray-50\" (onSelect)=\"onFileSelect($event)\" (onClear)=\"onClear()\">\n <ng-template #content>\n @if (uploadedFiles().length) {\n <ul class=\"space-y-2\">\n @for (file of uploadedFiles(); track $index) {\n <li class=\"flex justify-between gap-4 p-2 bg-gray-100 rounded\">\n <div class=\"flex gap-2\">\n <ng-container [ngTemplateOutlet]=\"iconTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: file.fileType }\"></ng-container>\n <span class=\"text-sm\"> {{ file.name }} - {{ formatFileSize(file.size) }} </span>\n </div>\n <button type=\"button\" (click)=\"removeFile($index)\" class=\"text-red-500 hover:text-red-700 text-sm\">\n <span class=\"material-icons !text-base\"> cancel </span>\n </button>\n </li>\n }\n </ul>\n } @else {\n <div class=\"flex flex-col items-center gap-3\">\n <img src=\"assets/images/file-upload.svg\" alt=\"File Upload\" class=\"w-28 mx-auto\" />\n <span class=\"font-semibold text-center text-gray-400 text-sm\">\n Drag & drop or<br />\n click the \"Choose\" button to add new files\n </span>\n </div>\n }\n </ng-template>\n <ng-template #file></ng-template>\n</p-fileupload>\n\n<ng-template #iconTemplate let-type>\n @if (type === 'image') {\n <span class=\"material-icons\"> image </span>\n } @else if (type === 'pdf') {\n <span class=\"material-icons\"> picture_as_pdf </span>\n } @else {\n <span class=\"material-icons\"> insert_drive_file </span>\n }\n</ng-template>","// form-field-wrapper.component.ts\nimport { CommonModule } from '@angular/common';\nimport { Component, ContentChild, Input, AfterContentInit, OnDestroy } from '@angular/core';\nimport { NgControl } from '@angular/forms';\nimport { Subscription } from 'rxjs';\nimport { MessageModule } from 'primeng/message';\n\n@Component({\n\tselector: 'ngp-form-field-wrapper',\n\tstandalone: true,\n\timports: [CommonModule, MessageModule],\n\ttemplateUrl: './form-field-wrapper.component.html',\n\tstyleUrls: ['./form-field-wrapper.component.scss']\n})\nexport class FormFieldWrapperComponent implements AfterContentInit, OnDestroy {\n\t@Input() validationMessages: { [key: string]: string } = {\n\t\trequired: 'This field is required',\n\t\temail: 'Please enter a valid email',\n\t\tminlength: 'Input does not meet minimum length',\n\t\tmaxlength: 'Input exceeds maximum length',\n\t\tpattern: 'Input format is invalid',\n\t\tendTimeBeforeStartTime: 'End time must not be before start time',\n\t\tbreakStartBeforeEarliestStart: 'Break start time cannot be before the earliest start time'\n\t};\n\n\t@ContentChild(NgControl) control?: NgControl;\n\n\tprivate subscription?: Subscription;\n\n\tngAfterContentInit(): void {\n\t\tif (this.control) {\n\t\t\tthis.subscription = this.control.statusChanges?.subscribe(() => { });\n\t\t}\n\t}\n\n\tngOnDestroy(): void {\n\t\tthis.subscription?.unsubscribe();\n\t}\n\n\tshouldShowError(): boolean {\n\t\treturn (\n\t\t\t!!this.control &&\n\t\t\tthis.control.invalid === true &&\n\t\t\t(this.control.dirty === true || this.control.touched === true)\n\t\t);\n\t}\n\n\tgetErrorMessage(): string {\n\t\tif (!this.control?.errors) {\n\t\t\treturn '';\n\t\t}\n\n\t\tconst firstErrorKey = Object.keys(this.control.errors)[0];\n\t\tconst error = this.control.errors[firstErrorKey];\n\n\t\tif (this.control.errors['message']) {\n\t\t\treturn this.control.errors['message'];\n\t\t}\n\t\tif (error?.message) {\n\t\t\treturn error.message;\n\t\t}\n\n\t\treturn this.validationMessages[firstErrorKey] || `Invalid input: ${firstErrorKey}`;\n\t}\n}\n","<div class=\"form-field-container\">\n <ng-content></ng-content>\n @if (shouldShowError()) {\n <p-message severity=\"error\" variant=\"simple\" size=\"small\" [text]=\"getErrorMessage()\">\n </p-message>\n }\n</div>","// export * from './lib/components/components';\nexport * from './lib/components/empty/empty.component'\nexport * from './lib/components/file-upload/file-upload.component'\nexport * from './lib/components/form-field-wrapper/form-field-wrapper.component'","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["i1"],"mappings":";;;;;;;;;;MAQa,cAAc,CAAA;AAC1B,IAAA,KAAK,GAAG,KAAK,CAAS,mBAAmB,iDAAC;AAC1C,IAAA,OAAO,GAAG,KAAK,CAAS,oBAAoB,mDAAC;IAC7C,MAAM,GAAG,KAAK,CAAA,IAAA,SAAA,GAAA,CAAA,SAAA,EAAA,EAAA,SAAA,EAAA,QAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAU;wGAHZ,cAAc,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAd,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,cAAc,kcCR3B,gnBAiBM,EAAA,MAAA,EAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;4FDTO,cAAc,EAAA,UAAA,EAAA,CAAA;kBAN1B,SAAS;AACC,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,WAAW,WACZ,EAAE,EAAA,QAAA,EAAA,gnBAAA,EAAA;;;MEWC,mBAAmB,CAAA;AACJ,IAAA,YAAY;;AAGvC,IAAA,KAAK,GAAG,KAAK,CAAS,EAAE,iDAAC;AACzB,IAAA,QAAQ,GAAG,KAAK,CAAC,IAAI,oDAAC;AACtB,IAAA,MAAM,GAAG,KAAK,CAAC,iDAAiD,kDAAC;AACjE,IAAA,WAAW,GAAG,KAAK,CAAC,OAAO,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,aAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC,CAAC;AAC7B,IAAA,WAAW,GAAG,KAAK,CAAC,yCAAyC,uDAAC;AAC9D,IAAA,gBAAgB,GAAG,KAAK,CAAU,KAAK,4DAAC;AACxC,IAAA,WAAW,GAAG,KAAK,CAAC,OAAO,uDAAC;;IAG5B,aAAa,GAAG,MAAM,EAAU;IAChC,YAAY,GAAG,MAAM,EAAQ;;AAG7B,IAAA,aAAa,GAAG,MAAM,CAAc,EAAE,yDAAC;AAEvC,IAAA,WAAA,GAAA;QACC,MAAM,CAAC,MAAK;AACX,YAAA,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE;AAC3B,YAAA,IAAI,MAAM,CAAC,MAAM,EAAE;gBAClB,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;YACrE;AACD,QAAA,CAAC,CAAC;IACH;AAEA,IAAA,YAAY,CAAC,KAAsB,EAAA;AAClC,QAAA,MAAM,UAAU,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,CAC3C,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,IAAI,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CACvE;AAED,QAAA,IAAI,UAAU,CAAC,MAAM,EAAE;;AAEtB,YAAA,MAAM,UAAU,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;AAEnE,YAAA,IAAI,IAAI,CAAC,QAAQ,EAAE,EAAE;gBACpB,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,KAAK,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;YACrF;iBAAO;AACN,gBAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC;YACnC;AACA,YAAA,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC;QACpC;IACD;IAEA,OAAO,GAAA;AACN,QAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;AAC1B,QAAA,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE;IACzB;AAEA,IAAA,UAAU,CAAC,KAAa,EAAA;QACvB,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,CAAC;QACzE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;IAC9C;AAEA,IAAA,eAAe,CAAC,IAAU,EAAA;QACzB,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC;QAC9C,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,KAC9B,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAC3D;IACF;AAEA,IAAA,cAAc,CAAC,KAAa,EAAA;QAC3B,IAAI,KAAK,KAAK,CAAC;AAAE,YAAA,OAAO,SAAS;QACjC,MAAM,CAAC,GAAG,IAAI;QACd,MAAM,KAAK,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;QACzC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACnD,OAAO,UAAU,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC;IACxE;IAEA,KAAK,GAAA;AACJ,QAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,IAAI,IAAI,CAAC,YAAY,EAAE,aAAa,EAAE,KAAK,EAAE;AAC5C,YAAA,IAAI,CAAC,YAAY,CAAC,aAAa,CAAC,KAAK,EAAE;QACxC;IACD;;AAGA,IAAA,sBAAsB,CAAC,IAAU,EAAA;QAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;AACxC,QAAA,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC;QAClF,MAAM,YAAY,GAAG,MAAM;AAE3B,QAAA,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE;AAC1D,YAAA,OAAO,OAAO;QACf;AAAO,aAAA,IAAI,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE;AAC3C,YAAA,OAAO,KAAK;QACb;aAAO;AACN,YAAA,OAAO,OAAO;QACf;IACD;;AAGA,IAAA,WAAW,CAAC,IAAU,EAAA;QACrB,MAAM,SAAS,GAAG,IAAiB;QACnC,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC;AACtD,QAAA,OAAO,SAAS;IACjB;wGAlGY,mBAAmB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAnB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,mBAAmB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,gBAAA,EAAA,EAAA,iBAAA,EAAA,kBAAA,EAAA,UAAA,EAAA,kBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,aAAA,EAAA,eAAA,EAAA,YAAA,EAAA,cAAA,EAAA,EAAA,WAAA,EAAA,CAAA,EAAA,YAAA,EAAA,cAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,cAAA,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECfhC,sxDAwCc,EAAA,MAAA,EAAA,CAAA,EAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,ED7BH,gBAAgB,47BAAE,YAAY,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,gBAAA,EAAA,QAAA,EAAA,oBAAA,EAAA,MAAA,EAAA,CAAA,yBAAA,EAAA,kBAAA,EAAA,0BAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;4FAI5B,mBAAmB,EAAA,UAAA,EAAA,CAAA;kBAN/B,SAAS;AACC,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,iBAAiB,EAAA,OAAA,EAClB,CAAC,gBAAgB,EAAE,YAAY,CAAC,EAAA,QAAA,EAAA,sxDAAA,EAAA;;sBAKxC,SAAS;uBAAC,cAAc;;;AEhB1B;MAca,yBAAyB,CAAA;AAC5B,IAAA,kBAAkB,GAA8B;AACxD,QAAA,QAAQ,EAAE,wBAAwB;AAClC,QAAA,KAAK,EAAE,4BAA4B;AACnC,QAAA,SAAS,EAAE,oCAAoC;AAC/C,QAAA,SAAS,EAAE,8BAA8B;AACzC,QAAA,OAAO,EAAE,yBAAyB;AAClC,QAAA,sBAAsB,EAAE,wCAAwC;AAChE,QAAA,6BAA6B,EAAE;KAC/B;AAEwB,IAAA,OAAO;AAExB,IAAA,YAAY;IAEpB,kBAAkB,GAAA;AACjB,QAAA,IAAI,IAAI,CAAC,OAAO,EAAE;AACjB,YAAA,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,SAAS,CAAC,MAAK,EAAG,CAAC,CAAC;QACrE;IACD;IAEA,WAAW,GAAA;AACV,QAAA,IAAI,CAAC,YAAY,EAAE,WAAW,EAAE;IACjC;IAEA,eAAe,GAAA;AACd,QAAA,QACC,CAAC,CAAC,IAAI,CAAC,OAAO;AACd,YAAA,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK,IAAI;AAC7B,aAAC,IAAI,CAAC,OAAO,CAAC,KAAK,KAAK,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK,IAAI,CAAC;IAEhE;IAEA,eAAe,GAAA;AACd,QAAA,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE;AAC1B,YAAA,OAAO,EAAE;QACV;AAEA,QAAA,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACzD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC;QAEhD,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE;YACnC,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC;QACtC;AACA,QAAA,IAAI,KAAK,EAAE,OAAO,EAAE;YACnB,OAAO,KAAK,CAAC,OAAO;QACrB;QAEA,OAAO,IAAI,CAAC,kBAAkB,CAAC,aAAa,CAAC,IAAI,CAAA,eAAA,EAAkB,aAAa,CAAA,CAAE;IACnF;wGAjDY,yBAAyB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAzB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,yBAAyB,6KAWvB,SAAS,EAAA,WAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECzBxB,8NAMM,EAAA,MAAA,EAAA,CAAA,mkBAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EDIK,YAAY,8BAAE,aAAa,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAAA,IAAA,CAAA,OAAA,EAAA,QAAA,EAAA,WAAA,EAAA,MAAA,EAAA,CAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,OAAA,EAAA,YAAA,EAAA,UAAA,EAAA,MAAA,EAAA,WAAA,EAAA,MAAA,EAAA,uBAAA,EAAA,uBAAA,EAAA,MAAA,EAAA,SAAA,CAAA,EAAA,OAAA,EAAA,CAAA,SAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;4FAIzB,yBAAyB,EAAA,UAAA,EAAA,CAAA;kBAPrC,SAAS;AACC,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,wBAAwB,cACtB,IAAI,EAAA,OAAA,EACP,CAAC,YAAY,EAAE,aAAa,CAAC,EAAA,QAAA,EAAA,8NAAA,EAAA,MAAA,EAAA,CAAA,mkBAAA,CAAA,EAAA;;sBAKrC;;sBAUA,YAAY;uBAAC,SAAS;;;AEzBxB;;ACAA;;AAEG;;;;"}
package/index.d.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  import * as _angular_core from '@angular/core';
2
+ import { ElementRef, AfterContentInit, OnDestroy } from '@angular/core';
3
+ import { FileSelectEvent } from 'primeng/fileupload';
4
+ import { NgControl } from '@angular/forms';
2
5
 
3
6
  declare class EmptyComponent {
4
7
  title: _angular_core.InputSignal<string>;
@@ -8,4 +11,46 @@ declare class EmptyComponent {
8
11
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<EmptyComponent, "ngp-empty", never, { "title": { "alias": "title"; "required": false; "isSignal": true; }; "message": { "alias": "message"; "required": false; "isSignal": true; }; "imgSrc": { "alias": "imgSrc"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
9
12
  }
10
13
 
11
- export { EmptyComponent };
14
+ interface TypedFile extends File {
15
+ fileType?: 'image' | 'pdf' | 'other';
16
+ }
17
+ declare class FileUploadComponent {
18
+ fileUploader: ElementRef;
19
+ files: _angular_core.InputSignal<File[]>;
20
+ multiple: _angular_core.InputSignal<boolean>;
21
+ accept: _angular_core.InputSignal<string>;
22
+ maxFileSize: _angular_core.InputSignal<number>;
23
+ placeholder: _angular_core.InputSignal<string>;
24
+ showUploadButton: _angular_core.InputSignal<boolean>;
25
+ cancelLabel: _angular_core.InputSignal<string>;
26
+ filesSelected: _angular_core.OutputEmitterRef<File[]>;
27
+ filesCleared: _angular_core.OutputEmitterRef<void>;
28
+ uploadedFiles: _angular_core.WritableSignal<TypedFile[]>;
29
+ constructor();
30
+ onFileSelect(event: FileSelectEvent): void;
31
+ onClear(): void;
32
+ removeFile(index: number): void;
33
+ isValidFileType(file: File): boolean;
34
+ formatFileSize(bytes: number): string;
35
+ clear(): void;
36
+ getFileTypeByExtension(file: File): 'image' | 'pdf' | 'other';
37
+ addFileType(file: File): TypedFile;
38
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<FileUploadComponent, never>;
39
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<FileUploadComponent, "ngp-file-upload", never, { "files": { "alias": "files"; "required": false; "isSignal": true; }; "multiple": { "alias": "multiple"; "required": false; "isSignal": true; }; "accept": { "alias": "accept"; "required": false; "isSignal": true; }; "maxFileSize": { "alias": "maxFileSize"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "showUploadButton": { "alias": "showUploadButton"; "required": false; "isSignal": true; }; "cancelLabel": { "alias": "cancelLabel"; "required": false; "isSignal": true; }; }, { "filesSelected": "filesSelected"; "filesCleared": "filesCleared"; }, never, never, true, never>;
40
+ }
41
+
42
+ declare class FormFieldWrapperComponent implements AfterContentInit, OnDestroy {
43
+ validationMessages: {
44
+ [key: string]: string;
45
+ };
46
+ control?: NgControl;
47
+ private subscription?;
48
+ ngAfterContentInit(): void;
49
+ ngOnDestroy(): void;
50
+ shouldShowError(): boolean;
51
+ getErrorMessage(): string;
52
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormFieldWrapperComponent, never>;
53
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<FormFieldWrapperComponent, "ngp-form-field-wrapper", never, { "validationMessages": { "alias": "validationMessages"; "required": false; }; }, {}, ["control"], ["*"], true, never>;
54
+ }
55
+
56
+ export { EmptyComponent, FileUploadComponent, FormFieldWrapperComponent };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@winexist/ngp",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^20.3.0",
6
6
  "@angular/core": "^20.3.0"