@flusys/ng-shared 0.1.0-beta.3 → 1.1.0-beta

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,20 +1,24 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, PLATFORM_ID, Injectable, DOCUMENT, REQUEST, signal, computed, ElementRef, input, effect, Directive, TemplateRef, ViewContainerRef, output, HostListener, NgModule, resource, Component, Injector, model, runInInjectionContext, untracked, forwardRef, viewChild, ChangeDetectionStrategy, InjectionToken } from '@angular/core';
2
+ import { inject, PLATFORM_ID, Injectable, DOCUMENT, REQUEST, signal, computed, ElementRef, input, effect, Directive, TemplateRef, ViewContainerRef, output, HostListener, NgModule, Injector, runInInjectionContext, resource, Component, model, untracked, forwardRef, viewChild, ChangeDetectionStrategy, InjectionToken, DestroyRef, afterNextRender, isDevMode } from '@angular/core';
3
3
  import * as i1 from '@angular/common';
4
4
  import { isPlatformServer, CommonModule, NgOptimizedImage, NgComponentOutlet, DatePipe } from '@angular/common';
5
5
  import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
6
6
  import { APP_CONFIG, getServiceUrl, ApiLoaderService } from '@flusys/ng-core';
7
- import { of, firstValueFrom, skip, debounceTime, distinctUntilChanged, tap as tap$1 } from 'rxjs';
8
- import { tap, catchError, map } from 'rxjs/operators';
9
- import * as i4 from '@angular/forms';
10
- import { NgControl, ReactiveFormsModule, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
7
+ import { of, firstValueFrom, skip, debounceTime, distinctUntilChanged, tap as tap$1, map as map$1 } from 'rxjs';
8
+ import { map, tap, catchError } from 'rxjs/operators';
9
+ import * as i1$2 from '@angular/forms';
10
+ import { NgControl, FormsModule, ReactiveFormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
11
11
  import { RouterOutlet, RouterLink, Router } from '@angular/router';
12
12
  import { AutoCompleteModule } from 'primeng/autocomplete';
13
+ import { AvatarModule } from 'primeng/avatar';
14
+ import * as i1$3 from 'primeng/button';
13
15
  import { ButtonModule } from 'primeng/button';
14
16
  import { CardModule } from 'primeng/card';
15
- import * as i2 from 'primeng/checkbox';
17
+ import * as i1$1 from 'primeng/checkbox';
16
18
  import { CheckboxModule } from 'primeng/checkbox';
19
+ import { ConfirmDialogModule } from 'primeng/confirmdialog';
17
20
  import { DatePickerModule } from 'primeng/datepicker';
21
+ import * as i5 from 'primeng/dialog';
18
22
  import { DialogModule } from 'primeng/dialog';
19
23
  import { DividerModule } from 'primeng/divider';
20
24
  import { FileUploadModule } from 'primeng/fileupload';
@@ -22,7 +26,7 @@ import { IconFieldModule } from 'primeng/iconfield';
22
26
  import { ImageModule } from 'primeng/image';
23
27
  import { InputIconModule } from 'primeng/inputicon';
24
28
  import { InputNumberModule } from 'primeng/inputnumber';
25
- import * as i1$1 from 'primeng/inputtext';
29
+ import * as i2 from 'primeng/inputtext';
26
30
  import { InputTextModule } from 'primeng/inputtext';
27
31
  import { ListboxModule } from 'primeng/listbox';
28
32
  import { MultiSelectModule } from 'primeng/multiselect';
@@ -30,26 +34,100 @@ import { PaginatorModule } from 'primeng/paginator';
30
34
  import { PanelModule } from 'primeng/panel';
31
35
  import { PasswordModule } from 'primeng/password';
32
36
  import { PopoverModule } from 'primeng/popover';
37
+ import * as i2$1 from 'primeng/progressbar';
33
38
  import { ProgressBarModule } from 'primeng/progressbar';
34
39
  import { RadioButtonModule } from 'primeng/radiobutton';
35
40
  import { RippleModule } from 'primeng/ripple';
36
41
  import * as i3 from 'primeng/select';
37
42
  import { SelectModule } from 'primeng/select';
38
43
  import { SelectButtonModule } from 'primeng/selectbutton';
44
+ import { SkeletonModule } from 'primeng/skeleton';
39
45
  import { SplitButtonModule } from 'primeng/splitbutton';
40
46
  import { StepsModule } from 'primeng/steps';
41
47
  import { TableModule } from 'primeng/table';
42
48
  import { TabsModule } from 'primeng/tabs';
43
49
  import { TagModule } from 'primeng/tag';
44
50
  import { TextareaModule } from 'primeng/textarea';
51
+ import { ToastModule } from 'primeng/toast';
45
52
  import { ToggleSwitchModule } from 'primeng/toggleswitch';
46
53
  import { TooltipModule } from 'primeng/tooltip';
47
54
  import { TreeTableModule } from 'primeng/treetable';
48
55
  import { toSignal, toObservable } from '@angular/core/rxjs-interop';
56
+ import * as i3$1 from 'primeng/api';
57
+ import { MessageService } from 'primeng/api';
49
58
 
50
59
  ;
51
60
  ;
52
61
 
62
+ /**
63
+ * Common file type filters
64
+ */
65
+ const FILE_TYPE_FILTERS = {
66
+ IMAGES: ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml'],
67
+ DOCUMENTS: [
68
+ 'application/pdf',
69
+ 'application/msword',
70
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
71
+ 'application/vnd.ms-excel',
72
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
73
+ ],
74
+ VIDEOS: ['video/mp4', 'video/webm', 'video/ogg', 'video/quicktime'],
75
+ AUDIO: ['audio/mpeg', 'audio/wav', 'audio/ogg', 'audio/webm'],
76
+ ALL: [],
77
+ };
78
+ /**
79
+ * Get accept string for file input from content types
80
+ */
81
+ function getAcceptString(contentTypes) {
82
+ if (!contentTypes.length)
83
+ return '*/*';
84
+ return contentTypes.join(',');
85
+ }
86
+ /**
87
+ * Check if file matches allowed content types
88
+ */
89
+ function isFileTypeAllowed(file, allowedTypes) {
90
+ if (!allowedTypes.length)
91
+ return true;
92
+ return allowedTypes.some((type) => {
93
+ if (type.endsWith('/*')) {
94
+ return file.type.startsWith(type.replace('/*', '/'));
95
+ }
96
+ return file.type === type;
97
+ });
98
+ }
99
+ /**
100
+ * Get file icon class based on content type
101
+ */
102
+ function getFileIconClass(contentType) {
103
+ if (contentType.startsWith('image/'))
104
+ return 'pi pi-image';
105
+ if (contentType.startsWith('video/'))
106
+ return 'pi pi-video';
107
+ if (contentType.startsWith('audio/'))
108
+ return 'pi pi-volume-up';
109
+ if (contentType.includes('pdf'))
110
+ return 'pi pi-file-pdf';
111
+ if (contentType.includes('word') || contentType.includes('document'))
112
+ return 'pi pi-file-word';
113
+ if (contentType.includes('excel') || contentType.includes('spreadsheet'))
114
+ return 'pi pi-file-excel';
115
+ return 'pi pi-file';
116
+ }
117
+ /**
118
+ * Format file size for display
119
+ */
120
+ function formatFileSize(sizeInKb) {
121
+ const kb = typeof sizeInKb === 'string' ? parseFloat(sizeInKb) : sizeInKb;
122
+ if (kb < 1024)
123
+ return `${kb.toFixed(1)} KB`;
124
+ const mb = kb / 1024;
125
+ if (mb < 1024)
126
+ return `${mb.toFixed(1)} MB`;
127
+ const gb = mb / 1024;
128
+ return `${gb.toFixed(2)} GB`;
129
+ }
130
+
53
131
  var ContactTypeEnum;
54
132
  (function (ContactTypeEnum) {
55
133
  ContactTypeEnum[ContactTypeEnum["PHONE"] = 1] = "PHONE";
@@ -68,10 +146,10 @@ class PlatformService {
68
146
  get isServer() {
69
147
  return isPlatformServer(this.platformId);
70
148
  }
71
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: PlatformService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
72
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: PlatformService, providedIn: 'root' });
149
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PlatformService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
150
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PlatformService, providedIn: 'root' });
73
151
  }
74
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: PlatformService, decorators: [{
152
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PlatformService, decorators: [{
75
153
  type: Injectable,
76
154
  args: [{
77
155
  providedIn: 'root'
@@ -85,10 +163,10 @@ class CookieService {
85
163
  get() {
86
164
  return !this.platformService.isServer ? this.doc.cookie : this.request?.headers.get('cookie') ?? "";
87
165
  }
88
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: CookieService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
89
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: CookieService, providedIn: 'root' });
166
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CookieService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
167
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CookieService, providedIn: 'root' });
90
168
  }
91
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: CookieService, decorators: [{
169
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CookieService, decorators: [{
92
170
  type: Injectable,
93
171
  args: [{
94
172
  providedIn: 'root',
@@ -124,28 +202,54 @@ class FileUrlService {
124
202
  }
125
203
  /**
126
204
  * Fetch file URLs from backend and update cache.
127
- * Returns Observable of fetched files.
205
+ * Skips IDs already in cache to prevent duplicate calls.
206
+ * Returns Observable of fetched files (including cached ones).
128
207
  */
129
- fetchFileUrls(fileIds) {
208
+ fetchFileUrls(fileIds, forceRefresh = false) {
130
209
  if (!fileIds.length)
131
210
  return of([]);
132
- const requestDto = fileIds.map((id) => ({ id }));
133
- return this.http.post(`${getServiceUrl(this.appConfig, 'storage')}/file-manager/get-files`, requestDto).pipe(tap((files) => {
134
- // Update cache
135
- const cache = new Map(this.urlCache());
136
- files.forEach((file) => cache.set(file.id, file));
137
- this.urlCache.set(cache);
138
- }), catchError((error) => {
139
- console.error('Failed to fetch file URLs:', error);
140
- return of([]);
141
- }));
211
+ const cache = this.urlCache();
212
+ // Filter out IDs already in cache (unless force refresh)
213
+ const missingIds = forceRefresh
214
+ ? fileIds
215
+ : fileIds.filter((id) => !cache.has(id));
216
+ // If all files are cached, return from cache
217
+ if (missingIds.length === 0) {
218
+ const cachedFiles = fileIds
219
+ .map((id) => cache.get(id))
220
+ .filter((f) => !!f);
221
+ return of(cachedFiles);
222
+ }
223
+ const requestDto = missingIds.map((id) => ({ id }));
224
+ return this.http
225
+ .post(`${getServiceUrl(this.appConfig, 'storage')}/file-manager/get-files`, requestDto)
226
+ .pipe(map((response) => response.data ?? []), tap((files) => {
227
+ // Update cache with new files
228
+ const newCache = new Map(this.urlCache());
229
+ files.forEach((file) => newCache.set(file.id, file));
230
+ this.urlCache.set(newCache);
231
+ }), map(() => {
232
+ // Return all requested files (cached + newly fetched)
233
+ const allCache = this.urlCache();
234
+ return fileIds
235
+ .map((id) => allCache.get(id))
236
+ .filter((f) => !!f);
237
+ }), catchError(() => of([])));
142
238
  }
143
239
  /**
144
240
  * Fetch a single file URL.
241
+ * Uses cache if available to prevent duplicate calls.
145
242
  * Returns Observable of file info or null if not found.
146
243
  */
147
- fetchSingleFileUrl(fileId) {
148
- return this.fetchFileUrls([fileId]).pipe(map((files) => files[0] ?? null));
244
+ fetchSingleFileUrl(fileId, forceRefresh = false) {
245
+ // Return from cache immediately if available
246
+ if (!forceRefresh) {
247
+ const cached = this.urlCache().get(fileId);
248
+ if (cached) {
249
+ return of(cached);
250
+ }
251
+ }
252
+ return this.fetchFileUrls([fileId], forceRefresh).pipe(map((files) => files[0] ?? null));
149
253
  }
150
254
  /**
151
255
  * Clear the URL cache.
@@ -162,10 +266,10 @@ class FileUrlService {
162
266
  cache.delete(fileId);
163
267
  this.urlCache.set(cache);
164
268
  }
165
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: FileUrlService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
166
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: FileUrlService, providedIn: 'root' });
269
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileUrlService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
270
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileUrlService, providedIn: 'root' });
167
271
  }
168
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: FileUrlService, decorators: [{
272
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileUrlService, decorators: [{
169
273
  type: Injectable,
170
274
  args: [{ providedIn: 'root' }]
171
275
  }] });
@@ -247,10 +351,10 @@ class PermissionValidatorService {
247
351
  isPermissionsLoaded() {
248
352
  return this._isLoaded();
249
353
  }
250
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: PermissionValidatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
251
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: PermissionValidatorService, providedIn: 'root' });
354
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PermissionValidatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
355
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PermissionValidatorService, providedIn: 'root' });
252
356
  }
253
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: PermissionValidatorService, decorators: [{
357
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PermissionValidatorService, decorators: [{
254
358
  type: Injectable,
255
359
  args: [{
256
360
  providedIn: 'root',
@@ -268,11 +372,6 @@ class EditModeElementChangerDirective {
268
372
  this.updateControl(editMode);
269
373
  });
270
374
  }
271
- ngAfterViewInit() {
272
- const editMode = this.isEditMode();
273
- this.updateElement(editMode);
274
- this.updateControl(editMode);
275
- }
276
375
  updateControl(editMode) {
277
376
  if (!editMode)
278
377
  this.ngControl?.control?.disable();
@@ -284,44 +383,41 @@ class EditModeElementChangerDirective {
284
383
  if (inputElement instanceof HTMLInputElement) {
285
384
  if (!editMode) {
286
385
  inputElement.setAttribute('readonly', 'true');
287
- inputElement?.classList.add('edit-mode-element-css');
386
+ inputElement.classList.add('edit-mode-element-css');
288
387
  }
289
388
  else {
290
389
  inputElement.removeAttribute('readonly');
291
- inputElement?.classList.remove('edit-mode-element-css');
390
+ inputElement.classList.remove('edit-mode-element-css');
292
391
  }
293
392
  }
294
- if (inputElement.tagName == 'P-SELECT') {
393
+ if (inputElement.tagName === 'P-SELECT') {
295
394
  if (!editMode) {
296
- inputElement.classList.add('edit-mode-element-css');
297
- inputElement.classList.add('overflow-hidden');
395
+ inputElement.classList.add('edit-mode-element-css', 'overflow-hidden');
298
396
  inputElement.querySelector('.p-select-dropdown').style.display = 'none';
299
397
  inputElement.querySelector('.p-select-label').classList.add('edit-mode-element-css');
300
398
  }
301
399
  else {
302
- inputElement.classList.remove("edit-mode-element-css");
400
+ inputElement.classList.remove('edit-mode-element-css', 'overflow-hidden');
303
401
  inputElement.querySelector('.p-select-dropdown').style.display = 'flex';
304
- inputElement.querySelector('.p-select-label').classList.remove("edit-mode-element-css");
305
- inputElement.classList.remove('overflow-hidden');
402
+ inputElement.querySelector('.p-select-label').classList.remove('edit-mode-element-css');
306
403
  }
307
404
  }
308
- if (inputElement.tagName == 'P-CALENDAR') {
405
+ if (inputElement.tagName === 'P-CALENDAR') {
406
+ const firstChild = inputElement.firstElementChild;
309
407
  if (!editMode) {
310
- inputElement.firstElementChild.children[0]?.classList.add('edit-mode-element-css');
311
- inputElement.firstElementChild.children[0]?.classList.add('cursor-auto');
312
- inputElement.firstElementChild.children[1]?.classList.add('hidden');
408
+ firstChild?.children[0]?.classList.add('edit-mode-element-css', 'cursor-auto');
409
+ firstChild?.children[1]?.classList.add('hidden');
313
410
  }
314
411
  else {
315
- inputElement.firstElementChild.children[0]?.classList.remove('edit-mode-element-css');
316
- inputElement.firstElementChild.children[0]?.classList.remove('cursor-auto');
317
- inputElement.firstElementChild.children[1]?.classList.remove('hidden');
412
+ firstChild?.children[0]?.classList.remove('edit-mode-element-css', 'cursor-auto');
413
+ firstChild?.children[1]?.classList.remove('hidden');
318
414
  }
319
415
  }
320
416
  }
321
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: EditModeElementChangerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
322
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.0", type: EditModeElementChangerDirective, isStandalone: true, selector: "[appEditModeElementChanger]", inputs: { isEditMode: { classPropertyName: "isEditMode", publicName: "isEditMode", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
417
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EditModeElementChangerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
418
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.3", type: EditModeElementChangerDirective, isStandalone: true, selector: "[appEditModeElementChanger]", inputs: { isEditMode: { classPropertyName: "isEditMode", publicName: "isEditMode", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
323
419
  }
324
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: EditModeElementChangerDirective, decorators: [{
420
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EditModeElementChangerDirective, decorators: [{
325
421
  type: Directive,
326
422
  args: [{
327
423
  selector: '[appEditModeElementChanger]',
@@ -478,10 +574,10 @@ class HasPermissionDirective {
478
574
  this.viewCreated = false;
479
575
  }
480
576
  }
481
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: HasPermissionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
482
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.0", type: HasPermissionDirective, isStandalone: true, selector: "[hasPermission]", inputs: { hasPermission: { classPropertyName: "hasPermission", publicName: "hasPermission", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
577
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: HasPermissionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
578
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.3", type: HasPermissionDirective, isStandalone: true, selector: "[hasPermission]", inputs: { hasPermission: { classPropertyName: "hasPermission", publicName: "hasPermission", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
483
579
  }
484
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: HasPermissionDirective, decorators: [{
580
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: HasPermissionDirective, decorators: [{
485
581
  type: Directive,
486
582
  args: [{
487
583
  selector: '[hasPermission]',
@@ -503,10 +599,10 @@ class IsEmptyImageDirective {
503
599
  onError() {
504
600
  this.hasError.set(true);
505
601
  }
506
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: IsEmptyImageDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
507
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.0", type: IsEmptyImageDirective, isStandalone: true, selector: "img", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "error": "onError()" }, properties: { "src": "imageSrc()" } }, ngImport: i0 });
602
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: IsEmptyImageDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
603
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.3", type: IsEmptyImageDirective, isStandalone: true, selector: "img", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "error": "onError()" }, properties: { "src": "imageSrc()" } }, ngImport: i0 });
508
604
  }
509
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: IsEmptyImageDirective, decorators: [{
605
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: IsEmptyImageDirective, decorators: [{
510
606
  type: Directive,
511
607
  args: [{
512
608
  selector: 'img',
@@ -543,13 +639,12 @@ class PreventDefaultDirective {
543
639
  }
544
640
  processEvent(event) {
545
641
  event.preventDefault();
546
- if (this.action)
547
- this.action.emit(event);
642
+ this.action.emit(event);
548
643
  }
549
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: PreventDefaultDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
550
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.0", type: PreventDefaultDirective, isStandalone: true, selector: "[appPreventDefault]", inputs: { eventType: { classPropertyName: "eventType", publicName: "eventType", isSignal: true, isRequired: false, transformFunction: null }, preventKey: { classPropertyName: "preventKey", publicName: "preventKey", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { action: "action" }, host: { listeners: { "click": "onClick($event)", "keydown": "onKeydown($event)", "keyup": "onKeyup($event)" } }, ngImport: i0 });
644
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PreventDefaultDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
645
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.3", type: PreventDefaultDirective, isStandalone: true, selector: "[appPreventDefault]", inputs: { eventType: { classPropertyName: "eventType", publicName: "eventType", isSignal: true, isRequired: false, transformFunction: null }, preventKey: { classPropertyName: "preventKey", publicName: "preventKey", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { action: "action" }, host: { listeners: { "click": "onClick($event)", "keydown": "onKeydown($event)", "keyup": "onKeyup($event)" } }, ngImport: i0 });
551
646
  }
552
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: PreventDefaultDirective, decorators: [{
647
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PreventDefaultDirective, decorators: [{
553
648
  type: Directive,
554
649
  args: [{
555
650
  selector: '[appPreventDefault]',
@@ -567,162 +662,176 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
567
662
  }] } });
568
663
 
569
664
  class AngularModule {
570
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: AngularModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
571
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.0", ngImport: i0, type: AngularModule, imports: [CommonModule,
665
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AngularModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
666
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: AngularModule, imports: [CommonModule,
667
+ FormsModule,
668
+ ReactiveFormsModule,
572
669
  RouterOutlet,
573
670
  RouterLink,
574
671
  IsEmptyImageDirective,
575
672
  NgOptimizedImage,
576
673
  NgComponentOutlet,
577
674
  PreventDefaultDirective], exports: [CommonModule,
578
- ReactiveFormsModule,
579
675
  FormsModule,
676
+ ReactiveFormsModule,
580
677
  RouterOutlet,
581
678
  RouterLink,
582
679
  IsEmptyImageDirective,
583
680
  NgOptimizedImage,
584
681
  NgComponentOutlet,
585
682
  PreventDefaultDirective] });
586
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: AngularModule, providers: [
587
- DatePipe,
588
- ], imports: [CommonModule, CommonModule,
589
- ReactiveFormsModule,
590
- FormsModule] });
683
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AngularModule, providers: [DatePipe], imports: [CommonModule,
684
+ FormsModule,
685
+ ReactiveFormsModule, CommonModule,
686
+ FormsModule,
687
+ ReactiveFormsModule] });
591
688
  }
592
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: AngularModule, decorators: [{
689
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AngularModule, decorators: [{
593
690
  type: NgModule,
594
691
  args: [{
595
692
  imports: [
596
693
  CommonModule,
694
+ FormsModule,
695
+ ReactiveFormsModule,
597
696
  RouterOutlet,
598
697
  RouterLink,
599
698
  IsEmptyImageDirective,
600
699
  NgOptimizedImage,
601
700
  NgComponentOutlet,
602
- PreventDefaultDirective
603
- ],
604
- providers: [
605
- DatePipe,
701
+ PreventDefaultDirective,
606
702
  ],
703
+ providers: [DatePipe],
607
704
  exports: [
608
705
  CommonModule,
609
- ReactiveFormsModule,
610
706
  FormsModule,
707
+ ReactiveFormsModule,
611
708
  RouterOutlet,
612
709
  RouterLink,
613
710
  IsEmptyImageDirective,
614
711
  NgOptimizedImage,
615
712
  NgComponentOutlet,
616
- PreventDefaultDirective
713
+ PreventDefaultDirective,
617
714
  ],
618
715
  }]
619
716
  }] });
620
717
 
621
718
  class PrimeModule {
622
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: PrimeModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
623
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.0", ngImport: i0, type: PrimeModule, exports: [InputTextModule,
624
- TagModule,
625
- SelectButtonModule,
626
- PasswordModule,
719
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PrimeModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
720
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: PrimeModule, exports: [AutoCompleteModule,
721
+ AvatarModule,
627
722
  ButtonModule,
628
- TooltipModule,
723
+ CardModule,
629
724
  CheckboxModule,
630
- StepsModule,
631
- RippleModule,
632
- PanelModule,
633
- PaginatorModule,
634
- TableModule,
635
- InputNumberModule,
636
- TextareaModule,
637
- ProgressBarModule,
725
+ ConfirmDialogModule,
726
+ DatePickerModule,
727
+ DialogModule,
728
+ DividerModule,
638
729
  FileUploadModule,
639
- CardModule,
640
- SelectModule,
641
- InputIconModule,
642
730
  IconFieldModule,
643
- PopoverModule,
731
+ ImageModule,
732
+ InputIconModule,
733
+ InputNumberModule,
734
+ InputTextModule,
644
735
  ListboxModule,
736
+ MultiSelectModule,
737
+ PaginatorModule,
738
+ PanelModule,
739
+ PasswordModule,
740
+ PopoverModule,
741
+ ProgressBarModule,
645
742
  RadioButtonModule,
646
- ToggleSwitchModule,
647
- ImageModule,
648
- DatePickerModule,
743
+ RippleModule,
744
+ SelectButtonModule,
745
+ SelectModule,
746
+ SkeletonModule,
649
747
  SplitButtonModule,
650
- DividerModule,
651
- MultiSelectModule,
652
- AutoCompleteModule,
748
+ StepsModule,
749
+ TableModule,
653
750
  TabsModule,
654
- DialogModule,
655
- TreeTableModule] });
656
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: PrimeModule, imports: [InputTextModule,
657
751
  TagModule,
658
- SelectButtonModule,
659
- PasswordModule,
660
- ButtonModule,
752
+ TextareaModule,
753
+ ToastModule,
754
+ ToggleSwitchModule,
661
755
  TooltipModule,
756
+ TreeTableModule] });
757
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PrimeModule, imports: [AutoCompleteModule,
758
+ AvatarModule,
759
+ ButtonModule,
760
+ CardModule,
662
761
  CheckboxModule,
663
- StepsModule,
664
- RippleModule,
665
- PanelModule,
666
- PaginatorModule,
667
- TableModule,
668
- InputNumberModule,
669
- TextareaModule,
670
- ProgressBarModule,
762
+ ConfirmDialogModule,
763
+ DatePickerModule,
764
+ DialogModule,
765
+ DividerModule,
671
766
  FileUploadModule,
672
- CardModule,
673
- SelectModule,
674
- InputIconModule,
675
767
  IconFieldModule,
676
- PopoverModule,
768
+ ImageModule,
769
+ InputIconModule,
770
+ InputNumberModule,
771
+ InputTextModule,
677
772
  ListboxModule,
773
+ MultiSelectModule,
774
+ PaginatorModule,
775
+ PanelModule,
776
+ PasswordModule,
777
+ PopoverModule,
778
+ ProgressBarModule,
678
779
  RadioButtonModule,
679
- ToggleSwitchModule,
680
- ImageModule,
681
- DatePickerModule,
780
+ RippleModule,
781
+ SelectButtonModule,
782
+ SelectModule,
783
+ SkeletonModule,
682
784
  SplitButtonModule,
683
- DividerModule,
684
- MultiSelectModule,
685
- AutoCompleteModule,
785
+ StepsModule,
786
+ TableModule,
686
787
  TabsModule,
687
- DialogModule,
788
+ TagModule,
789
+ TextareaModule,
790
+ ToastModule,
791
+ ToggleSwitchModule,
792
+ TooltipModule,
688
793
  TreeTableModule] });
689
794
  }
690
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: PrimeModule, decorators: [{
795
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PrimeModule, decorators: [{
691
796
  type: NgModule,
692
797
  args: [{
693
798
  exports: [
694
- InputTextModule,
695
- TagModule,
696
- SelectButtonModule,
697
- PasswordModule,
799
+ AutoCompleteModule,
800
+ AvatarModule,
698
801
  ButtonModule,
699
- TooltipModule,
802
+ CardModule,
700
803
  CheckboxModule,
701
- StepsModule,
702
- RippleModule,
703
- PanelModule,
704
- PaginatorModule,
705
- TableModule,
706
- InputNumberModule,
707
- TextareaModule,
708
- ProgressBarModule,
804
+ ConfirmDialogModule,
805
+ DatePickerModule,
806
+ DialogModule,
807
+ DividerModule,
709
808
  FileUploadModule,
710
- CardModule,
711
- SelectModule,
712
- InputIconModule,
713
809
  IconFieldModule,
714
- PopoverModule,
810
+ ImageModule,
811
+ InputIconModule,
812
+ InputNumberModule,
813
+ InputTextModule,
715
814
  ListboxModule,
815
+ MultiSelectModule,
816
+ PaginatorModule,
817
+ PanelModule,
818
+ PasswordModule,
819
+ PopoverModule,
820
+ ProgressBarModule,
716
821
  RadioButtonModule,
717
- ToggleSwitchModule,
718
- ImageModule,
719
- DatePickerModule,
822
+ RippleModule,
823
+ SelectButtonModule,
824
+ SelectModule,
825
+ SkeletonModule,
720
826
  SplitButtonModule,
721
- DividerModule,
722
- MultiSelectModule,
723
- AutoCompleteModule,
827
+ StepsModule,
828
+ TableModule,
724
829
  TabsModule,
725
- DialogModule,
830
+ TagModule,
831
+ TextareaModule,
832
+ ToastModule,
833
+ ToggleSwitchModule,
834
+ TooltipModule,
726
835
  TreeTableModule,
727
836
  ],
728
837
  }]
@@ -775,6 +884,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
775
884
  class ApiResourceService {
776
885
  baseUrl;
777
886
  loaderService = inject(ApiLoaderService);
887
+ injector = inject(Injector);
778
888
  http;
779
889
  moduleApiName;
780
890
  // ==========================================================================
@@ -789,22 +899,54 @@ class ApiResourceService {
789
899
  select: [],
790
900
  sort: {},
791
901
  }, ...(ngDevMode ? [{ debugName: "filterData" }] : []));
792
- /** Resource for list data - uses IListResponse matching backend */
793
- listResource;
902
+ /**
903
+ * Resource for list data - lazy initialized to prevent auto-fetch on service injection.
904
+ * Call initListResource() or any list method (fetchList, reload, etc.) to initialize.
905
+ */
906
+ _listResource = null;
907
+ /** Whether the list resource has been initialized */
908
+ _resourceInitialized = false;
909
+ /** Get or create the list resource (lazy initialization) */
910
+ get listResource() {
911
+ if (!this._listResource) {
912
+ this.initListResource();
913
+ }
914
+ return this._listResource;
915
+ }
916
+ /**
917
+ * Initialize the list resource. Called automatically when accessing listResource
918
+ * or any list-related computed signals/methods.
919
+ * Uses runInInjectionContext to support lazy initialization outside constructor.
920
+ */
921
+ initListResource() {
922
+ if (this._resourceInitialized)
923
+ return;
924
+ this._resourceInitialized = true;
925
+ runInInjectionContext(this.injector, () => {
926
+ this._listResource = resource({ ...(ngDevMode ? { debugName: "_listResource" } : {}), params: () => ({
927
+ search: this.searchTerm(),
928
+ filter: this.filterData(),
929
+ }),
930
+ loader: async ({ params }) => {
931
+ const { search, filter } = params;
932
+ return this.fetchAllAsync(search, filter);
933
+ } });
934
+ });
935
+ }
794
936
  // ==========================================================================
795
937
  // Computed State Accessors
796
938
  // ==========================================================================
797
939
  /** Whether data is currently loading */
798
- isLoading = computed(() => this.listResource.isLoading(), ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
940
+ isLoading = computed(() => this._listResource?.isLoading() ?? false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
799
941
  /** List data array */
800
- data = computed(() => this.listResource.value()?.data ?? [], ...(ngDevMode ? [{ debugName: "data" }] : []));
942
+ data = computed(() => this._listResource?.value()?.data ?? [], ...(ngDevMode ? [{ debugName: "data" }] : []));
801
943
  /** Total count of items */
802
- total = computed(() => this.listResource.value()?.meta?.total ?? 0, ...(ngDevMode ? [{ debugName: "total" }] : []));
944
+ total = computed(() => this._listResource?.value()?.meta?.total ?? 0, ...(ngDevMode ? [{ debugName: "total" }] : []));
803
945
  /** Pagination metadata */
804
- pageInfo = computed(() => this.listResource.value()?.meta, ...(ngDevMode ? [{ debugName: "pageInfo" }] : []));
946
+ pageInfo = computed(() => this._listResource?.value()?.meta, ...(ngDevMode ? [{ debugName: "pageInfo" }] : []));
805
947
  /** Whether there are more pages */
806
948
  hasMore = computed(() => {
807
- const meta = this.listResource.value()?.meta;
949
+ const meta = this._listResource?.value()?.meta;
808
950
  if (!meta)
809
951
  return false;
810
952
  return meta.hasMore ?? (meta.page + 1) * meta.pageSize < meta.total;
@@ -813,15 +955,7 @@ class ApiResourceService {
813
955
  this.moduleApiName = moduleApiName;
814
956
  this.baseUrl = inject(APP_CONFIG).apiBaseUrl + '/' + moduleApiName;
815
957
  this.http = http;
816
- // Create resource that reacts to search and filter changes
817
- this.listResource = resource({ ...(ngDevMode ? { debugName: "listResource" } : {}), params: () => ({
818
- search: this.searchTerm(),
819
- filter: this.filterData(),
820
- }),
821
- loader: async ({ params }) => {
822
- const { search, filter } = params;
823
- return this.fetchAllAsync(search, filter);
824
- } });
958
+ // Resource is now lazy-initialized, not created in constructor
825
959
  }
826
960
  getHttpOptions(endpoint, params) {
827
961
  return {
@@ -835,9 +969,10 @@ class ApiResourceService {
835
969
  // List Management Methods
836
970
  // ==========================================================================
837
971
  /**
838
- * Fetch list data (triggers resource reload)
972
+ * Fetch list data (triggers resource initialization and reload)
839
973
  */
840
974
  fetchList(search = '', filter) {
975
+ this.initListResource();
841
976
  this.searchTerm.set(search);
842
977
  if (filter) {
843
978
  this.filterData.update((prev) => ({ ...prev, ...filter }));
@@ -847,12 +982,14 @@ class ApiResourceService {
847
982
  * Update pagination
848
983
  */
849
984
  setPagination(pagination) {
985
+ this.initListResource();
850
986
  this.filterData.update((prev) => ({ ...prev, pagination }));
851
987
  }
852
988
  /**
853
989
  * Go to next page
854
990
  */
855
991
  nextPage() {
992
+ this.initListResource();
856
993
  this.filterData.update((prev) => ({
857
994
  ...prev,
858
995
  pagination: {
@@ -865,6 +1002,7 @@ class ApiResourceService {
865
1002
  * Reset to first page
866
1003
  */
867
1004
  resetPagination() {
1005
+ this.initListResource();
868
1006
  this.filterData.update((prev) => ({
869
1007
  ...prev,
870
1008
  pagination: { currentPage: 0, pageSize: prev.pagination?.pageSize ?? 10 },
@@ -874,7 +1012,9 @@ class ApiResourceService {
874
1012
  * Reload current data
875
1013
  */
876
1014
  reload() {
877
- this.listResource.reload();
1015
+ if (this._listResource) {
1016
+ this._listResource.reload();
1017
+ }
878
1018
  }
879
1019
  // ==========================================================================
880
1020
  // Observable-based API Methods (IApiService interface)
@@ -984,8 +1124,8 @@ class IconComponent {
984
1124
  icon = input.required(...(ngDevMode ? [{ debugName: "icon" }] : []));
985
1125
  iconType = input(IconTypeEnum.PRIMENG_ICON, ...(ngDevMode ? [{ debugName: "iconType" }] : []));
986
1126
  IconTypeEnum = IconTypeEnum; // Needed for template reference
987
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: IconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
988
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: IconComponent, isStandalone: true, selector: "lib-icon", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: true, transformFunction: null }, iconType: { classPropertyName: "iconType", publicName: "iconType", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
1127
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: IconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1128
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: IconComponent, isStandalone: true, selector: "lib-icon", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: true, transformFunction: null }, iconType: { classPropertyName: "iconType", publicName: "iconType", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
989
1129
  @if(icon()){ @if(iconType()==IconTypeEnum.PRIMENG_ICON){
990
1130
  <i [ngClass]="icon()"></i>
991
1131
  }@else if(iconType()==IconTypeEnum.IMAGE_FILE_LINK){
@@ -995,7 +1135,7 @@ class IconComponent {
995
1135
  }@else{ I } }
996
1136
  `, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: IsEmptyImageDirective, selector: "img", inputs: ["src"] }] });
997
1137
  }
998
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: IconComponent, decorators: [{
1138
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: IconComponent, decorators: [{
999
1139
  type: Component,
1000
1140
  args: [{
1001
1141
  selector: 'lib-icon',
@@ -1109,10 +1249,10 @@ class BaseFormControl {
1109
1249
  this.onTouched();
1110
1250
  }
1111
1251
  }
1112
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: BaseFormControl, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1113
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.0", type: BaseFormControl, isStandalone: true, inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { disabled: "disabledChange", touched: "touchedChange" }, ngImport: i0 });
1252
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: BaseFormControl, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1253
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.3", type: BaseFormControl, isStandalone: true, inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { disabled: "disabledChange", touched: "touchedChange" }, ngImport: i0 });
1114
1254
  }
1115
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: BaseFormControl, decorators: [{
1255
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: BaseFormControl, decorators: [{
1116
1256
  type: Directive
1117
1257
  }], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }] } });
1118
1258
  /**
@@ -1189,8 +1329,6 @@ class LazyMultiSelectComponent extends BaseFormControl {
1189
1329
  })), { initialValue: this.searchTerm() });
1190
1330
  });
1191
1331
  }
1192
- // Signal to toggle panel
1193
- scrollTargetEl = null;
1194
1332
  onScrollBound = this.onScroll.bind(this);
1195
1333
  multiScrollContainer = viewChild.required('multiScrollContainer');
1196
1334
  onScroll(event) {
@@ -1254,10 +1392,10 @@ class LazyMultiSelectComponent extends BaseFormControl {
1254
1392
  event.stopPropagation();
1255
1393
  this.value.set([]);
1256
1394
  }
1257
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: LazyMultiSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1258
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: LazyMultiSelectComponent, isStandalone: true, selector: "lib-lazy-multi-select", inputs: { placeHolder: { classPropertyName: "placeHolder", publicName: "placeHolder", isSignal: true, isRequired: false, transformFunction: null }, isEditMode: { classPropertyName: "isEditMode", publicName: "isEditMode", isSignal: true, isRequired: true, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: true, transformFunction: null }, total: { classPropertyName: "total", publicName: "total", isSignal: true, isRequired: true, transformFunction: null }, pagination: { classPropertyName: "pagination", publicName: "pagination", isSignal: true, isRequired: true, transformFunction: null }, selectDataList: { classPropertyName: "selectDataList", publicName: "selectDataList", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", onSearch: "onSearch", onPagination: "onPagination" }, host: { listeners: { "document:click": "handleDocumentClick($event)" } }, providers: [provideValueAccessor(LazyMultiSelectComponent)], viewQueries: [{ propertyName: "multiScrollContainer", first: true, predicate: ["multiScrollContainer"], descendants: true, isSignal: true }, { propertyName: "pSelectRef", first: true, predicate: ["pSelect"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"p-select w-full\" #pSelect (click)=\"onSelectClick($event)\"\n [class.p-disabled]=\"disabled()\">\n @if(selectedValueDisplay()){\n <span class=\"p-select-label\">{{selectedValueDisplay()}}</span>\n }@else {\n <span class=\"p-select-label p-placeholder\">{{placeHolder()}}</span>\n }\n <span class=\"p-select-clear-icon\" (click)=\"clear($event)\"><i class=\"pi pi-times\"></i></span>\n <div class=\"p-select-dropdown\">\n <span class=\"p-select-dropdown-icon flex items-center\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"\n data-p-icon=\"chevron-down\" class=\"p-multiselect-dropdown-icon p-icon ng-star-inserted\"\n data-pc-section=\"triggericon\" aria-hidden=\"true\" pc75=\"\">\n <path\n d=\"M7.01744 10.398C6.91269 10.3985 6.8089 10.378 6.71215 10.3379C6.61541 10.2977 6.52766 10.2386 6.45405 10.1641L1.13907 4.84913C1.03306 4.69404 0.985221 4.5065 1.00399 4.31958C1.02276 4.13266 1.10693 3.95838 1.24166 3.82747C1.37639 3.69655 1.55301 3.61742 1.74039 3.60402C1.92777 3.59062 2.11386 3.64382 2.26584 3.75424L7.01744 8.47394L11.769 3.75424C11.9189 3.65709 12.097 3.61306 12.2748 3.62921C12.4527 3.64535 12.6199 3.72073 12.7498 3.84328C12.8797 3.96582 12.9647 4.12842 12.9912 4.30502C13.0177 4.48162 12.9841 4.662 12.8958 4.81724L7.58083 10.1322C7.50996 10.2125 7.42344 10.2775 7.32656 10.3232C7.22968 10.3689 7.12449 10.3944 7.01744 10.398Z\"\n fill=\"currentColor\"></path>\n </svg>\n </span>\n </div>\n @if(openOptions()){\n <div class=\"p-select-overlay\" (click)=\"onOverlayClick($event)\">\n <div class=\"p-select-header flex flex-row gap-2 items-center\">\n <p-checkbox binary=\"true\" (onChange)=\"changeSelectAll($event)\" [ngModel]=\"isSelectAll()\" [disabled]=\"disabled()\"/>\n <input type=\"text\" pInputText class=\"w-full\" [ngModel]=\"searchTerm()\"\n (ngModelChange)=\"searchTerm.set($event)\" [ngModelOptions]=\"{ standalone: true }\"\n placeholder=\"Search...\" />\n </div>\n <div class=\"p-select-list-container\" (scroll)=\"onScroll($event)\">\n <ul class=\"p-select-list\">\n @for (data of selectDataList(); track key(data); let i = $index) {\n <li class=\"p-select-option flex flex-row gap-2 items-center\"\n [ngClass]=\"{ 'p-select-option-selected': isSelected(data) }\">\n <p-checkbox binary=\"true\" (onChange)=\"selectValue($event,data)\" [ngModel]=\"isSelected(data)\" [disabled]=\"disabled()\" />\n <span>{{data.label}}</span>\n </li>\n }\n </ul>\n </div>\n </div>\n }\n</div>", styles: [".p-select-overlay{top:33px;z-index:1004;transform-origin:center top;margin-top:2px}.p-select-option:hover{background:var(--p-select-option-focus-background);color:var(--p-select-option-focus-color)}.p-select-list-container{max-height:200px}.p-select-option.p-select-option-selected{background:var(--p-select-option-selected-background);color:var(--p-select-option-selected-color)}.p-select-option.p-select-option-selected:hover{background:var(--p-select-option-selected-focus-background);color:var(--p-select-option-selected-focus-color)}\n"], dependencies: [{ kind: "ngmodule", type: PrimeModule }, { kind: "directive", type: i1$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: i2.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1395
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: LazyMultiSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1396
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: LazyMultiSelectComponent, isStandalone: true, selector: "lib-lazy-multi-select", inputs: { placeHolder: { classPropertyName: "placeHolder", publicName: "placeHolder", isSignal: true, isRequired: false, transformFunction: null }, isEditMode: { classPropertyName: "isEditMode", publicName: "isEditMode", isSignal: true, isRequired: true, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: true, transformFunction: null }, total: { classPropertyName: "total", publicName: "total", isSignal: true, isRequired: true, transformFunction: null }, pagination: { classPropertyName: "pagination", publicName: "pagination", isSignal: true, isRequired: true, transformFunction: null }, selectDataList: { classPropertyName: "selectDataList", publicName: "selectDataList", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", onSearch: "onSearch", onPagination: "onPagination" }, host: { listeners: { "document:click": "handleDocumentClick($event)" } }, providers: [provideValueAccessor(LazyMultiSelectComponent)], viewQueries: [{ propertyName: "multiScrollContainer", first: true, predicate: ["multiScrollContainer"], descendants: true, isSignal: true }, { propertyName: "pSelectRef", first: true, predicate: ["pSelect"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"p-select w-full\" #pSelect (click)=\"onSelectClick($event)\"\n [class.p-disabled]=\"disabled()\">\n @if(selectedValueDisplay()){\n <span class=\"p-select-label\">{{selectedValueDisplay()}}</span>\n }@else {\n <span class=\"p-select-label p-placeholder\">{{placeHolder()}}</span>\n }\n <span class=\"p-select-clear-icon\" (click)=\"clear($event)\"><i class=\"pi pi-times\"></i></span>\n <div class=\"p-select-dropdown\">\n <span class=\"p-select-dropdown-icon flex items-center\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"\n data-p-icon=\"chevron-down\" class=\"p-multiselect-dropdown-icon p-icon ng-star-inserted\"\n data-pc-section=\"triggericon\" aria-hidden=\"true\" pc75=\"\">\n <path\n d=\"M7.01744 10.398C6.91269 10.3985 6.8089 10.378 6.71215 10.3379C6.61541 10.2977 6.52766 10.2386 6.45405 10.1641L1.13907 4.84913C1.03306 4.69404 0.985221 4.5065 1.00399 4.31958C1.02276 4.13266 1.10693 3.95838 1.24166 3.82747C1.37639 3.69655 1.55301 3.61742 1.74039 3.60402C1.92777 3.59062 2.11386 3.64382 2.26584 3.75424L7.01744 8.47394L11.769 3.75424C11.9189 3.65709 12.097 3.61306 12.2748 3.62921C12.4527 3.64535 12.6199 3.72073 12.7498 3.84328C12.8797 3.96582 12.9647 4.12842 12.9912 4.30502C13.0177 4.48162 12.9841 4.662 12.8958 4.81724L7.58083 10.1322C7.50996 10.2125 7.42344 10.2775 7.32656 10.3232C7.22968 10.3689 7.12449 10.3944 7.01744 10.398Z\"\n fill=\"currentColor\"></path>\n </svg>\n </span>\n </div>\n @if(openOptions()){\n <div class=\"p-select-overlay\" (click)=\"onOverlayClick($event)\">\n <div class=\"p-select-header flex flex-row gap-2 items-center\">\n <p-checkbox binary=\"true\" (onChange)=\"changeSelectAll($event)\" [ngModel]=\"isSelectAll()\" [disabled]=\"disabled()\"/>\n <input type=\"text\" pInputText class=\"w-full\" [ngModel]=\"searchTerm()\"\n (ngModelChange)=\"searchTerm.set($event)\" [ngModelOptions]=\"{ standalone: true }\"\n placeholder=\"Search...\" />\n </div>\n <div class=\"p-select-list-container\" (scroll)=\"onScroll($event)\">\n <ul class=\"p-select-list\">\n @for (data of selectDataList(); track key(data); let i = $index) {\n <li class=\"p-select-option flex flex-row gap-2 items-center\"\n [ngClass]=\"{ 'p-select-option-selected': isSelected(data) }\">\n <p-checkbox binary=\"true\" (onChange)=\"selectValue($event,data)\" [ngModel]=\"isSelected(data)\" [disabled]=\"disabled()\" />\n <span>{{data.label}}</span>\n </li>\n }\n </ul>\n </div>\n </div>\n }\n</div>", styles: [".p-select-overlay{top:33px;z-index:1004;transform-origin:center top;margin-top:2px}.p-select-option:hover{background:var(--p-select-option-focus-background);color:var(--p-select-option-focus-color)}.p-select-list-container{max-height:200px}.p-select-option.p-select-option-selected{background:var(--p-select-option-selected-background);color:var(--p-select-option-selected-color)}.p-select-option.p-select-option-selected:hover{background:var(--p-select-option-selected-focus-background);color:var(--p-select-option-selected-focus-color)}\n"], dependencies: [{ kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i1$1.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "directive", type: i2.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1259
1397
  }
1260
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: LazyMultiSelectComponent, decorators: [{
1398
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: LazyMultiSelectComponent, decorators: [{
1261
1399
  type: Component,
1262
1400
  args: [{ selector: 'lib-lazy-multi-select', imports: [PrimeModule, AngularModule], changeDetection: ChangeDetectionStrategy.OnPush, providers: [provideValueAccessor(LazyMultiSelectComponent)], template: "<div class=\"p-select w-full\" #pSelect (click)=\"onSelectClick($event)\"\n [class.p-disabled]=\"disabled()\">\n @if(selectedValueDisplay()){\n <span class=\"p-select-label\">{{selectedValueDisplay()}}</span>\n }@else {\n <span class=\"p-select-label p-placeholder\">{{placeHolder()}}</span>\n }\n <span class=\"p-select-clear-icon\" (click)=\"clear($event)\"><i class=\"pi pi-times\"></i></span>\n <div class=\"p-select-dropdown\">\n <span class=\"p-select-dropdown-icon flex items-center\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"\n data-p-icon=\"chevron-down\" class=\"p-multiselect-dropdown-icon p-icon ng-star-inserted\"\n data-pc-section=\"triggericon\" aria-hidden=\"true\" pc75=\"\">\n <path\n d=\"M7.01744 10.398C6.91269 10.3985 6.8089 10.378 6.71215 10.3379C6.61541 10.2977 6.52766 10.2386 6.45405 10.1641L1.13907 4.84913C1.03306 4.69404 0.985221 4.5065 1.00399 4.31958C1.02276 4.13266 1.10693 3.95838 1.24166 3.82747C1.37639 3.69655 1.55301 3.61742 1.74039 3.60402C1.92777 3.59062 2.11386 3.64382 2.26584 3.75424L7.01744 8.47394L11.769 3.75424C11.9189 3.65709 12.097 3.61306 12.2748 3.62921C12.4527 3.64535 12.6199 3.72073 12.7498 3.84328C12.8797 3.96582 12.9647 4.12842 12.9912 4.30502C13.0177 4.48162 12.9841 4.662 12.8958 4.81724L7.58083 10.1322C7.50996 10.2125 7.42344 10.2775 7.32656 10.3232C7.22968 10.3689 7.12449 10.3944 7.01744 10.398Z\"\n fill=\"currentColor\"></path>\n </svg>\n </span>\n </div>\n @if(openOptions()){\n <div class=\"p-select-overlay\" (click)=\"onOverlayClick($event)\">\n <div class=\"p-select-header flex flex-row gap-2 items-center\">\n <p-checkbox binary=\"true\" (onChange)=\"changeSelectAll($event)\" [ngModel]=\"isSelectAll()\" [disabled]=\"disabled()\"/>\n <input type=\"text\" pInputText class=\"w-full\" [ngModel]=\"searchTerm()\"\n (ngModelChange)=\"searchTerm.set($event)\" [ngModelOptions]=\"{ standalone: true }\"\n placeholder=\"Search...\" />\n </div>\n <div class=\"p-select-list-container\" (scroll)=\"onScroll($event)\">\n <ul class=\"p-select-list\">\n @for (data of selectDataList(); track key(data); let i = $index) {\n <li class=\"p-select-option flex flex-row gap-2 items-center\"\n [ngClass]=\"{ 'p-select-option-selected': isSelected(data) }\">\n <p-checkbox binary=\"true\" (onChange)=\"selectValue($event,data)\" [ngModel]=\"isSelected(data)\" [disabled]=\"disabled()\" />\n <span>{{data.label}}</span>\n </li>\n }\n </ul>\n </div>\n </div>\n }\n</div>", styles: [".p-select-overlay{top:33px;z-index:1004;transform-origin:center top;margin-top:2px}.p-select-option:hover{background:var(--p-select-option-focus-background);color:var(--p-select-option-focus-color)}.p-select-list-container{max-height:200px}.p-select-option.p-select-option-selected{background:var(--p-select-option-selected-background);color:var(--p-select-option-selected-color)}.p-select-option.p-select-option-selected:hover{background:var(--p-select-option-selected-focus-background);color:var(--p-select-option-selected-focus-color)}\n"] }]
1263
1401
  }], ctorParameters: () => [], propDecorators: { placeHolder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeHolder", required: false }] }], isEditMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "isEditMode", required: true }] }], isLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "isLoading", required: true }] }], total: [{ type: i0.Input, args: [{ isSignal: true, alias: "total", required: true }] }], pagination: [{ type: i0.Input, args: [{ isSignal: true, alias: "pagination", required: true }] }], selectDataList: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectDataList", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], onSearch: [{ type: i0.Output, args: ["onSearch"] }], onPagination: [{ type: i0.Output, args: ["onPagination"] }], multiScrollContainer: [{ type: i0.ViewChild, args: ['multiScrollContainer', { isSignal: true }] }], pSelectRef: [{ type: i0.ViewChild, args: ['pSelect', { isSignal: true }] }], handleDocumentClick: [{
@@ -1331,9 +1469,6 @@ class LazySelectComponent extends BaseFormControl {
1331
1469
  target.addEventListener('scroll', this.onScrollBound);
1332
1470
  this.scrollTargetEl = target;
1333
1471
  }
1334
- else {
1335
- console.warn('.p-select-list-container not found after panel show');
1336
- }
1337
1472
  }, 0);
1338
1473
  }
1339
1474
  else {
@@ -1346,10 +1481,10 @@ class LazySelectComponent extends BaseFormControl {
1346
1481
  onBlur() {
1347
1482
  this.markAsTouched();
1348
1483
  }
1349
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: LazySelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1350
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.1.0", type: LazySelectComponent, isStandalone: true, selector: "lib-lazy-select", inputs: { placeHolder: { classPropertyName: "placeHolder", publicName: "placeHolder", isSignal: true, isRequired: false, transformFunction: null }, optionLabel: { classPropertyName: "optionLabel", publicName: "optionLabel", isSignal: true, isRequired: true, transformFunction: null }, optionValue: { classPropertyName: "optionValue", publicName: "optionValue", isSignal: true, isRequired: true, transformFunction: null }, isEditMode: { classPropertyName: "isEditMode", publicName: "isEditMode", isSignal: true, isRequired: true, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: true, transformFunction: null }, total: { classPropertyName: "total", publicName: "total", isSignal: true, isRequired: true, transformFunction: null }, pagination: { classPropertyName: "pagination", publicName: "pagination", isSignal: true, isRequired: true, transformFunction: null }, selectDataList: { classPropertyName: "selectDataList", publicName: "selectDataList", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", onSearch: "onSearch", onPagination: "onPagination" }, providers: [provideValueAccessor(LazySelectComponent)], viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["scrollContainer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div #scrollContainer class=\"lib-scroll-container\">\n <p-select\n [options]=\"selectDataList()\"\n [(ngModel)]=\"value\"\n [optionLabel]=\"optionLabel()\"\n [optionValue]=\"optionValue()\"\n [filter]=\"true\"\n [showClear]=\"true\"\n [placeholder]=\"placeHolder()\"\n [disabled]=\"disabled()\"\n class=\"w-full\"\n appEditModeElementChanger\n [isEditMode]=\"isEditMode()\"\n (click)=\"showPanel()\"\n (onBlur)=\"onBlur()\">\n <ng-template let-filter #filter>\n <input\n pInputText\n [ngModel]=\"searchTerm()\"\n (ngModelChange)=\"searchTerm.set($event)\"\n [ngModelOptions]=\"{standalone:true}\"\n class=\"w-full\" />\n </ng-template>\n <ng-template #selectedItem let-selectedOption>\n {{ selectedOption[optionLabel()] }}\n </ng-template>\n <ng-template let-item #item>\n {{ item[optionLabel()] }}\n </ng-template>\n </p-select>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "directive", type: i1$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: i3.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "directive", type: EditModeElementChangerDirective, selector: "[appEditModeElementChanger]", inputs: ["isEditMode"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1484
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: LazySelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1485
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.1.3", type: LazySelectComponent, isStandalone: true, selector: "lib-lazy-select", inputs: { placeHolder: { classPropertyName: "placeHolder", publicName: "placeHolder", isSignal: true, isRequired: false, transformFunction: null }, optionLabel: { classPropertyName: "optionLabel", publicName: "optionLabel", isSignal: true, isRequired: true, transformFunction: null }, optionValue: { classPropertyName: "optionValue", publicName: "optionValue", isSignal: true, isRequired: true, transformFunction: null }, isEditMode: { classPropertyName: "isEditMode", publicName: "isEditMode", isSignal: true, isRequired: true, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: true, transformFunction: null }, total: { classPropertyName: "total", publicName: "total", isSignal: true, isRequired: true, transformFunction: null }, pagination: { classPropertyName: "pagination", publicName: "pagination", isSignal: true, isRequired: true, transformFunction: null }, selectDataList: { classPropertyName: "selectDataList", publicName: "selectDataList", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", onSearch: "onSearch", onPagination: "onPagination" }, providers: [provideValueAccessor(LazySelectComponent)], viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["scrollContainer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div #scrollContainer class=\"lib-scroll-container\">\n <p-select\n [options]=\"selectDataList()\"\n [(ngModel)]=\"value\"\n [optionLabel]=\"optionLabel()\"\n [optionValue]=\"optionValue()\"\n [filter]=\"true\"\n [showClear]=\"true\"\n [placeholder]=\"placeHolder()\"\n [disabled]=\"disabled()\"\n class=\"w-full\"\n appEditModeElementChanger\n [isEditMode]=\"isEditMode()\"\n (click)=\"showPanel()\"\n (onBlur)=\"onBlur()\">\n <ng-template let-filter #filter>\n <input\n pInputText\n [ngModel]=\"searchTerm()\"\n (ngModelChange)=\"searchTerm.set($event)\"\n [ngModelOptions]=\"{standalone:true}\"\n class=\"w-full\" />\n </ng-template>\n <ng-template #selectedItem let-selectedOption>\n {{ selectedOption[optionLabel()] }}\n </ng-template>\n <ng-template let-item #item>\n {{ item[optionLabel()] }}\n </ng-template>\n </p-select>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "directive", type: i2.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: i3.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "directive", type: EditModeElementChangerDirective, selector: "[appEditModeElementChanger]", inputs: ["isEditMode"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1351
1486
  }
1352
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: LazySelectComponent, decorators: [{
1487
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: LazySelectComponent, decorators: [{
1353
1488
  type: Component,
1354
1489
  args: [{ selector: 'lib-lazy-select', imports: [
1355
1490
  AngularModule,
@@ -1412,7 +1547,1237 @@ const USER_PERMISSION_PROVIDER = new InjectionToken('USER_PERMISSION_PROVIDER',
1412
1547
  throw new Error('USER_PERMISSION_PROVIDER not configured. Please provide an implementation in app.config.ts');
1413
1548
  },
1414
1549
  });
1550
+ /**
1551
+ * Auth State Provider Token
1552
+ *
1553
+ * Provides auth state access for feature packages (form-builder, etc.)
1554
+ */
1555
+ const AUTH_STATE_PROVIDER = new InjectionToken('AUTH_STATE_PROVIDER', {
1556
+ providedIn: 'root',
1557
+ factory: () => {
1558
+ throw new Error('AUTH_STATE_PROVIDER not configured. Please provide an implementation in app.config.ts');
1559
+ },
1560
+ });
1561
+ /**
1562
+ * Profile Permission Provider Token
1563
+ *
1564
+ * Provides user permission data for profile display.
1565
+ * Optional - if not configured, profile permissions section is hidden.
1566
+ * Use with `inject(PROFILE_PERMISSION_PROVIDER, { optional: true })`.
1567
+ */
1568
+ const PROFILE_PERMISSION_PROVIDER = new InjectionToken('PROFILE_PERMISSION_PROVIDER');
1569
+ /**
1570
+ * Profile Upload Provider Token
1571
+ *
1572
+ * Provides file upload functionality for profile pictures.
1573
+ * Optional - if not configured or storage not enabled, upload section is hidden.
1574
+ * Use with `inject(PROFILE_UPLOAD_PROVIDER, { optional: true })`.
1575
+ */
1576
+ const PROFILE_UPLOAD_PROVIDER = new InjectionToken('PROFILE_UPLOAD_PROVIDER');
1577
+ /**
1578
+ * User List Provider Token
1579
+ *
1580
+ * Provides extra actions, columns, and data enrichment for user list.
1581
+ * Optional - if not configured, default user list behavior is used.
1582
+ * Use with `inject(USER_LIST_PROVIDER, { optional: true })`.
1583
+ *
1584
+ * @example
1585
+ * // In app.config.ts
1586
+ * providers: [
1587
+ * { provide: USER_LIST_PROVIDER, useClass: MyUserListProvider },
1588
+ * ]
1589
+ */
1590
+ const USER_LIST_PROVIDER = new InjectionToken('USER_LIST_PROVIDER');
1591
+
1592
+ const DEFAULT_PAGE_SIZE$2 = 20;
1593
+ /**
1594
+ * User Select Component - Single user selection with lazy loading.
1595
+ *
1596
+ * Uses USER_PROVIDER internally by default, or accepts custom `loadUsers` function.
1597
+ *
1598
+ * Features:
1599
+ * - Search with debouncing (handled by lazy-select)
1600
+ * - Infinite scroll pagination
1601
+ * - Filter active users by default (configurable)
1602
+ * - Supports additional filters via `additionalFilters` input
1603
+ *
1604
+ * @example
1605
+ * ```html
1606
+ * <!-- Simple usage - uses USER_PROVIDER internally -->
1607
+ * <lib-user-select
1608
+ * [(value)]="selectedUserId"
1609
+ * [isEditMode]="true"
1610
+ * />
1611
+ *
1612
+ * <!-- With custom loadUsers function -->
1613
+ * <lib-user-select
1614
+ * [(value)]="selectedUserId"
1615
+ * [isEditMode]="true"
1616
+ * [loadUsers]="customLoadUsers"
1617
+ * />
1618
+ * ```
1619
+ */
1620
+ class UserSelectComponent {
1621
+ destroyRef = inject(DestroyRef);
1622
+ userProvider = inject(USER_PROVIDER);
1623
+ abortController = null;
1624
+ // Optional: custom function to load users (uses USER_PROVIDER if not provided)
1625
+ loadUsers = input(...(ngDevMode ? [undefined, { debugName: "loadUsers" }] : []));
1626
+ // Inputs
1627
+ placeHolder = input('Select User', ...(ngDevMode ? [{ debugName: "placeHolder" }] : []));
1628
+ isEditMode = input.required(...(ngDevMode ? [{ debugName: "isEditMode" }] : []));
1629
+ filterActive = input(true, ...(ngDevMode ? [{ debugName: "filterActive" }] : []));
1630
+ additionalFilters = input({}, ...(ngDevMode ? [{ debugName: "additionalFilters" }] : []));
1631
+ pageSize = input(DEFAULT_PAGE_SIZE$2, ...(ngDevMode ? [{ debugName: "pageSize" }] : []));
1632
+ // Two-way bound value
1633
+ value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : []));
1634
+ // Outputs
1635
+ userSelected = output();
1636
+ onError = output();
1637
+ // Internal state
1638
+ isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
1639
+ users = signal([], ...(ngDevMode ? [{ debugName: "users" }] : []));
1640
+ total = signal(undefined, ...(ngDevMode ? [{ debugName: "total" }] : []));
1641
+ pagination = signal({ pageSize: DEFAULT_PAGE_SIZE$2, currentPage: 0 }, ...(ngDevMode ? [{ debugName: "pagination" }] : []));
1642
+ searchTerm = signal('', ...(ngDevMode ? [{ debugName: "searchTerm" }] : []));
1643
+ // Computed dropdown data
1644
+ dropdownUsers = computed(() => this.users().map((user) => ({
1645
+ label: user.name || user.email,
1646
+ value: user.id,
1647
+ })), ...(ngDevMode ? [{ debugName: "dropdownUsers" }] : []));
1648
+ constructor() {
1649
+ // Cleanup on destroy
1650
+ this.destroyRef.onDestroy(() => {
1651
+ this.abortController?.abort();
1652
+ });
1653
+ // Update page size from input
1654
+ effect(() => {
1655
+ const size = this.pageSize();
1656
+ untracked(() => {
1657
+ this.pagination.update((p) => ({ ...p, pageSize: size }));
1658
+ });
1659
+ });
1660
+ // Load initial users after render
1661
+ afterNextRender(() => {
1662
+ this.fetchUsers();
1663
+ });
1664
+ // Emit selected user when value changes
1665
+ effect(() => {
1666
+ const selectedId = this.value();
1667
+ const users = this.users();
1668
+ untracked(() => {
1669
+ if (selectedId) {
1670
+ const user = users.find((u) => u.id === selectedId);
1671
+ this.userSelected.emit(user ?? null);
1672
+ }
1673
+ else {
1674
+ this.userSelected.emit(null);
1675
+ }
1676
+ });
1677
+ });
1678
+ }
1679
+ handleSearch(search) {
1680
+ this.searchTerm.set(search);
1681
+ this.pagination.update((p) => ({ ...p, currentPage: 0 }));
1682
+ this.users.set([]);
1683
+ this.fetchUsers();
1684
+ }
1685
+ handlePagination(pagination) {
1686
+ this.pagination.set(pagination);
1687
+ this.fetchUsers(true);
1688
+ }
1689
+ async fetchUsers(append = false) {
1690
+ if (this.isLoading())
1691
+ return;
1692
+ // Cancel previous request
1693
+ this.abortController?.abort();
1694
+ this.abortController = new AbortController();
1695
+ this.isLoading.set(true);
1696
+ try {
1697
+ const pag = this.pagination();
1698
+ const filter = {
1699
+ page: pag.currentPage,
1700
+ pageSize: pag.pageSize,
1701
+ search: this.searchTerm(),
1702
+ ...this.additionalFilters(),
1703
+ };
1704
+ // Use custom loadUsers if provided, otherwise use USER_PROVIDER
1705
+ const customLoadUsers = this.loadUsers();
1706
+ const response = await firstValueFrom(customLoadUsers
1707
+ ? customLoadUsers(filter)
1708
+ : this.loadUsersFromProvider(filter));
1709
+ if (response.success && response.data) {
1710
+ if (append) {
1711
+ this.users.update((current) => [...current, ...response.data]);
1712
+ }
1713
+ else {
1714
+ this.users.set(response.data);
1715
+ }
1716
+ this.total.set(response.meta?.total);
1717
+ }
1718
+ }
1719
+ catch (error) {
1720
+ if (error.name !== 'AbortError') {
1721
+ this.onError.emit(error);
1722
+ }
1723
+ }
1724
+ finally {
1725
+ this.isLoading.set(false);
1726
+ }
1727
+ }
1728
+ /** Load users from USER_PROVIDER with active filter */
1729
+ loadUsersFromProvider(filter) {
1730
+ return this.userProvider
1731
+ .getUsers({
1732
+ page: filter.page,
1733
+ pageSize: filter.pageSize,
1734
+ search: filter.search,
1735
+ isActive: this.filterActive() ? true : undefined,
1736
+ })
1737
+ .pipe(map$1((res) => ({
1738
+ ...res,
1739
+ data: res.data?.map((u) => ({
1740
+ id: u.id,
1741
+ name: u.name,
1742
+ email: u.email,
1743
+ })),
1744
+ })));
1745
+ }
1746
+ /** Reload users (useful when filters change externally) */
1747
+ reload() {
1748
+ this.pagination.update((p) => ({ ...p, currentPage: 0 }));
1749
+ this.users.set([]);
1750
+ this.fetchUsers();
1751
+ }
1752
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UserSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1753
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.3", type: UserSelectComponent, isStandalone: true, selector: "lib-user-select", inputs: { loadUsers: { classPropertyName: "loadUsers", publicName: "loadUsers", isSignal: true, isRequired: false, transformFunction: null }, placeHolder: { classPropertyName: "placeHolder", publicName: "placeHolder", isSignal: true, isRequired: false, transformFunction: null }, isEditMode: { classPropertyName: "isEditMode", publicName: "isEditMode", isSignal: true, isRequired: true, transformFunction: null }, filterActive: { classPropertyName: "filterActive", publicName: "filterActive", isSignal: true, isRequired: false, transformFunction: null }, additionalFilters: { classPropertyName: "additionalFilters", publicName: "additionalFilters", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", userSelected: "userSelected", onError: "onError" }, ngImport: i0, template: `
1754
+ <lib-lazy-select
1755
+ [(value)]="value"
1756
+ [placeHolder]="placeHolder()"
1757
+ [optionLabel]="'label'"
1758
+ [optionValue]="'value'"
1759
+ [isEditMode]="isEditMode()"
1760
+ [isLoading]="isLoading()"
1761
+ [total]="total()"
1762
+ [pagination]="pagination()"
1763
+ [selectDataList]="dropdownUsers()"
1764
+ (onSearch)="handleSearch($event)"
1765
+ (onPagination)="handlePagination($event)"
1766
+ />
1767
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: LazySelectComponent, selector: "lib-lazy-select", inputs: ["placeHolder", "optionLabel", "optionValue", "isEditMode", "isLoading", "total", "pagination", "selectDataList", "value"], outputs: ["valueChange", "onSearch", "onPagination"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1768
+ }
1769
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UserSelectComponent, decorators: [{
1770
+ type: Component,
1771
+ args: [{
1772
+ selector: 'lib-user-select',
1773
+ standalone: true,
1774
+ imports: [AngularModule, PrimeModule, LazySelectComponent],
1775
+ template: `
1776
+ <lib-lazy-select
1777
+ [(value)]="value"
1778
+ [placeHolder]="placeHolder()"
1779
+ [optionLabel]="'label'"
1780
+ [optionValue]="'value'"
1781
+ [isEditMode]="isEditMode()"
1782
+ [isLoading]="isLoading()"
1783
+ [total]="total()"
1784
+ [pagination]="pagination()"
1785
+ [selectDataList]="dropdownUsers()"
1786
+ (onSearch)="handleSearch($event)"
1787
+ (onPagination)="handlePagination($event)"
1788
+ />
1789
+ `,
1790
+ changeDetection: ChangeDetectionStrategy.OnPush,
1791
+ }]
1792
+ }], ctorParameters: () => [], propDecorators: { loadUsers: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadUsers", required: false }] }], placeHolder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeHolder", required: false }] }], isEditMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "isEditMode", required: true }] }], filterActive: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterActive", required: false }] }], additionalFilters: [{ type: i0.Input, args: [{ isSignal: true, alias: "additionalFilters", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], userSelected: [{ type: i0.Output, args: ["userSelected"] }], onError: [{ type: i0.Output, args: ["onError"] }] } });
1793
+
1794
+ const DEFAULT_PAGE_SIZE$1 = 20;
1795
+ /**
1796
+ * User Multi-Select Component - Multiple user selection with lazy loading.
1797
+ *
1798
+ * Uses USER_PROVIDER internally by default, or accepts custom `loadUsers` function.
1799
+ *
1800
+ * Features:
1801
+ * - Search with debouncing (handled by lazy-multi-select)
1802
+ * - Infinite scroll pagination
1803
+ * - Select all / deselect all
1804
+ * - Filter active users by default (configurable)
1805
+ * - Supports additional filters via `additionalFilters` input
1806
+ *
1807
+ * @example
1808
+ * ```html
1809
+ * <!-- Simple usage - uses USER_PROVIDER internally -->
1810
+ * <lib-user-multi-select
1811
+ * [(value)]="selectedUserIds"
1812
+ * [isEditMode]="true"
1813
+ * />
1814
+ *
1815
+ * <!-- With custom loadUsers function -->
1816
+ * <lib-user-multi-select
1817
+ * [(value)]="selectedUserIds"
1818
+ * [isEditMode]="true"
1819
+ * [loadUsers]="customLoadUsers"
1820
+ * />
1821
+ * ```
1822
+ */
1823
+ class UserMultiSelectComponent {
1824
+ destroyRef = inject(DestroyRef);
1825
+ userProvider = inject(USER_PROVIDER);
1826
+ abortController = null;
1827
+ // Optional: custom function to load users (uses USER_PROVIDER if not provided)
1828
+ loadUsers = input(...(ngDevMode ? [undefined, { debugName: "loadUsers" }] : []));
1829
+ // Inputs
1830
+ placeHolder = input('Select Users', ...(ngDevMode ? [{ debugName: "placeHolder" }] : []));
1831
+ isEditMode = input.required(...(ngDevMode ? [{ debugName: "isEditMode" }] : []));
1832
+ filterActive = input(true, ...(ngDevMode ? [{ debugName: "filterActive" }] : []));
1833
+ additionalFilters = input({}, ...(ngDevMode ? [{ debugName: "additionalFilters" }] : []));
1834
+ pageSize = input(DEFAULT_PAGE_SIZE$1, ...(ngDevMode ? [{ debugName: "pageSize" }] : []));
1835
+ // Two-way bound value
1836
+ value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : []));
1837
+ // Outputs
1838
+ usersSelected = output();
1839
+ onError = output();
1840
+ // Internal state
1841
+ isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
1842
+ users = signal([], ...(ngDevMode ? [{ debugName: "users" }] : []));
1843
+ total = signal(undefined, ...(ngDevMode ? [{ debugName: "total" }] : []));
1844
+ pagination = signal({ pageSize: DEFAULT_PAGE_SIZE$1, currentPage: 0 }, ...(ngDevMode ? [{ debugName: "pagination" }] : []));
1845
+ searchTerm = signal('', ...(ngDevMode ? [{ debugName: "searchTerm" }] : []));
1846
+ // Computed dropdown data
1847
+ dropdownUsers = computed(() => this.users().map((user) => ({
1848
+ label: user.name || user.email,
1849
+ value: user.id,
1850
+ })), ...(ngDevMode ? [{ debugName: "dropdownUsers" }] : []));
1851
+ constructor() {
1852
+ // Cleanup on destroy
1853
+ this.destroyRef.onDestroy(() => {
1854
+ this.abortController?.abort();
1855
+ });
1856
+ // Update page size from input
1857
+ effect(() => {
1858
+ const size = this.pageSize();
1859
+ untracked(() => {
1860
+ this.pagination.update((p) => ({ ...p, pageSize: size }));
1861
+ });
1862
+ });
1863
+ // Load initial users after render
1864
+ afterNextRender(() => {
1865
+ this.fetchUsers();
1866
+ });
1867
+ // Emit selected users when value changes
1868
+ effect(() => {
1869
+ const selectedIds = this.value() ?? [];
1870
+ const users = this.users();
1871
+ untracked(() => {
1872
+ const selectedUsers = users.filter((u) => selectedIds.includes(u.id));
1873
+ this.usersSelected.emit(selectedUsers);
1874
+ });
1875
+ });
1876
+ }
1877
+ handleSearch(search) {
1878
+ this.searchTerm.set(search);
1879
+ this.pagination.update((p) => ({ ...p, currentPage: 0 }));
1880
+ this.users.set([]);
1881
+ this.fetchUsers();
1882
+ }
1883
+ handlePagination(pagination) {
1884
+ this.pagination.set(pagination);
1885
+ this.fetchUsers(true);
1886
+ }
1887
+ async fetchUsers(append = false) {
1888
+ if (this.isLoading())
1889
+ return;
1890
+ // Cancel previous request
1891
+ this.abortController?.abort();
1892
+ this.abortController = new AbortController();
1893
+ this.isLoading.set(true);
1894
+ try {
1895
+ const pag = this.pagination();
1896
+ const filter = {
1897
+ page: pag.currentPage,
1898
+ pageSize: pag.pageSize,
1899
+ search: this.searchTerm(),
1900
+ ...this.additionalFilters(),
1901
+ };
1902
+ // Use custom loadUsers if provided, otherwise use USER_PROVIDER
1903
+ const customLoadUsers = this.loadUsers();
1904
+ const response = await firstValueFrom(customLoadUsers
1905
+ ? customLoadUsers(filter)
1906
+ : this.loadUsersFromProvider(filter));
1907
+ if (response.success && response.data) {
1908
+ if (append) {
1909
+ this.users.update((current) => [...current, ...response.data]);
1910
+ }
1911
+ else {
1912
+ this.users.set(response.data);
1913
+ }
1914
+ this.total.set(response.meta?.total);
1915
+ }
1916
+ }
1917
+ catch (error) {
1918
+ if (error.name !== 'AbortError') {
1919
+ this.onError.emit(error);
1920
+ }
1921
+ }
1922
+ finally {
1923
+ this.isLoading.set(false);
1924
+ }
1925
+ }
1926
+ /** Load users from USER_PROVIDER with active filter */
1927
+ loadUsersFromProvider(filter) {
1928
+ return this.userProvider
1929
+ .getUsers({
1930
+ page: filter.page,
1931
+ pageSize: filter.pageSize,
1932
+ search: filter.search,
1933
+ isActive: this.filterActive() ? true : undefined,
1934
+ })
1935
+ .pipe(map$1((res) => ({
1936
+ ...res,
1937
+ data: res.data?.map((u) => ({
1938
+ id: u.id,
1939
+ name: u.name,
1940
+ email: u.email,
1941
+ })),
1942
+ })));
1943
+ }
1944
+ /** Reload users (useful when filters change externally) */
1945
+ reload() {
1946
+ this.pagination.update((p) => ({ ...p, currentPage: 0 }));
1947
+ this.users.set([]);
1948
+ this.fetchUsers();
1949
+ }
1950
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UserMultiSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1951
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.3", type: UserMultiSelectComponent, isStandalone: true, selector: "lib-user-multi-select", inputs: { loadUsers: { classPropertyName: "loadUsers", publicName: "loadUsers", isSignal: true, isRequired: false, transformFunction: null }, placeHolder: { classPropertyName: "placeHolder", publicName: "placeHolder", isSignal: true, isRequired: false, transformFunction: null }, isEditMode: { classPropertyName: "isEditMode", publicName: "isEditMode", isSignal: true, isRequired: true, transformFunction: null }, filterActive: { classPropertyName: "filterActive", publicName: "filterActive", isSignal: true, isRequired: false, transformFunction: null }, additionalFilters: { classPropertyName: "additionalFilters", publicName: "additionalFilters", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", usersSelected: "usersSelected", onError: "onError" }, ngImport: i0, template: `
1952
+ <lib-lazy-multi-select
1953
+ [(value)]="value"
1954
+ [placeHolder]="placeHolder()"
1955
+ [isEditMode]="isEditMode()"
1956
+ [isLoading]="isLoading()"
1957
+ [total]="total()"
1958
+ [pagination]="pagination()"
1959
+ [selectDataList]="dropdownUsers()"
1960
+ (onSearch)="handleSearch($event)"
1961
+ (onPagination)="handlePagination($event)"
1962
+ />
1963
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: LazyMultiSelectComponent, selector: "lib-lazy-multi-select", inputs: ["placeHolder", "isEditMode", "isLoading", "total", "pagination", "selectDataList", "value"], outputs: ["valueChange", "onSearch", "onPagination"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1964
+ }
1965
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UserMultiSelectComponent, decorators: [{
1966
+ type: Component,
1967
+ args: [{
1968
+ selector: 'lib-user-multi-select',
1969
+ standalone: true,
1970
+ imports: [AngularModule, PrimeModule, LazyMultiSelectComponent],
1971
+ template: `
1972
+ <lib-lazy-multi-select
1973
+ [(value)]="value"
1974
+ [placeHolder]="placeHolder()"
1975
+ [isEditMode]="isEditMode()"
1976
+ [isLoading]="isLoading()"
1977
+ [total]="total()"
1978
+ [pagination]="pagination()"
1979
+ [selectDataList]="dropdownUsers()"
1980
+ (onSearch)="handleSearch($event)"
1981
+ (onPagination)="handlePagination($event)"
1982
+ />
1983
+ `,
1984
+ changeDetection: ChangeDetectionStrategy.OnPush,
1985
+ }]
1986
+ }], ctorParameters: () => [], propDecorators: { loadUsers: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadUsers", required: false }] }], placeHolder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeHolder", required: false }] }], isEditMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "isEditMode", required: true }] }], filterActive: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterActive", required: false }] }], additionalFilters: [{ type: i0.Input, args: [{ isSignal: true, alias: "additionalFilters", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], usersSelected: [{ type: i0.Output, args: ["usersSelected"] }], onError: [{ type: i0.Output, args: ["onError"] }] } });
1987
+
1988
+ /**
1989
+ * File Uploader Component - Drag & drop file upload with type filtering.
1990
+ *
1991
+ * Pass your own `uploadFile` function - works with any storage API.
1992
+ *
1993
+ * Features:
1994
+ * - Drag & drop support
1995
+ * - File type filtering (images, documents, etc.)
1996
+ * - Upload progress indication
1997
+ * - Multiple file support (optional)
1998
+ * - Image compression options
1999
+ *
2000
+ * @example
2001
+ * ```typescript
2002
+ * // In component
2003
+ * readonly uploadService = inject(UploadService);
2004
+ *
2005
+ * readonly uploadFile: UploadFileFn = (file, options) =>
2006
+ * this.uploadService.uploadSingleFile(file, options);
2007
+ * ```
2008
+ *
2009
+ * ```html
2010
+ * <!-- Single image upload -->
2011
+ * <lib-file-uploader
2012
+ * [uploadFile]="uploadFile"
2013
+ * [acceptTypes]="['image/*']"
2014
+ * [multiple]="false"
2015
+ * (fileUploaded)="onFileUploaded($event)"
2016
+ * />
2017
+ *
2018
+ * <!-- Multiple document upload -->
2019
+ * <lib-file-uploader
2020
+ * [uploadFile]="uploadFile"
2021
+ * [acceptTypes]="FILE_TYPE_FILTERS.DOCUMENTS"
2022
+ * [multiple]="true"
2023
+ * [maxFiles]="5"
2024
+ * (filesUploaded)="onFilesUploaded($event)"
2025
+ * />
2026
+ * ```
2027
+ */
2028
+ class FileUploaderComponent {
2029
+ messageService = inject(MessageService);
2030
+ // Required: function to upload file
2031
+ uploadFile = input.required(...(ngDevMode ? [{ debugName: "uploadFile" }] : []));
2032
+ // Inputs
2033
+ acceptTypes = input([], ...(ngDevMode ? [{ debugName: "acceptTypes" }] : []));
2034
+ multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple" }] : []));
2035
+ maxFiles = input(10, ...(ngDevMode ? [{ debugName: "maxFiles" }] : []));
2036
+ maxSizeMb = input(10, ...(ngDevMode ? [{ debugName: "maxSizeMb" }] : []));
2037
+ uploadOptions = input({}, ...(ngDevMode ? [{ debugName: "uploadOptions" }] : []));
2038
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
2039
+ showPreview = input(true, ...(ngDevMode ? [{ debugName: "showPreview" }] : []));
2040
+ autoUpload = input(true, ...(ngDevMode ? [{ debugName: "autoUpload" }] : []));
2041
+ // Outputs
2042
+ fileUploaded = output();
2043
+ filesUploaded = output();
2044
+ onError = output();
2045
+ fileSelected = output();
2046
+ // Internal state
2047
+ isUploading = signal(false, ...(ngDevMode ? [{ debugName: "isUploading" }] : []));
2048
+ isDragOver = signal(false, ...(ngDevMode ? [{ debugName: "isDragOver" }] : []));
2049
+ uploadProgress = signal(0, ...(ngDevMode ? [{ debugName: "uploadProgress" }] : []));
2050
+ uploadingFileName = signal('', ...(ngDevMode ? [{ debugName: "uploadingFileName" }] : []));
2051
+ selectedFiles = signal([], ...(ngDevMode ? [{ debugName: "selectedFiles" }] : []));
2052
+ // Computed
2053
+ acceptString = computed(() => getAcceptString(this.acceptTypes()), ...(ngDevMode ? [{ debugName: "acceptString" }] : []));
2054
+ acceptTypesDisplay = computed(() => {
2055
+ const types = this.acceptTypes();
2056
+ if (!types.length)
2057
+ return '';
2058
+ // Check if types match predefined filters
2059
+ const typesStr = JSON.stringify(types);
2060
+ if (typesStr === JSON.stringify(FILE_TYPE_FILTERS.IMAGES))
2061
+ return 'Images';
2062
+ if (typesStr === JSON.stringify(FILE_TYPE_FILTERS.DOCUMENTS))
2063
+ return 'Documents';
2064
+ if (typesStr === JSON.stringify(FILE_TYPE_FILTERS.VIDEOS))
2065
+ return 'Videos';
2066
+ if (typesStr === JSON.stringify(FILE_TYPE_FILTERS.AUDIO))
2067
+ return 'Audio';
2068
+ return types.map(t => t.split('/')[1] || t).join(', ');
2069
+ }, ...(ngDevMode ? [{ debugName: "acceptTypesDisplay" }] : []));
2070
+ onDragOver(event) {
2071
+ event.preventDefault();
2072
+ event.stopPropagation();
2073
+ if (!this.disabled() && !this.isUploading()) {
2074
+ this.isDragOver.set(true);
2075
+ }
2076
+ }
2077
+ onDragLeave(event) {
2078
+ event.preventDefault();
2079
+ event.stopPropagation();
2080
+ this.isDragOver.set(false);
2081
+ }
2082
+ onDrop(event) {
2083
+ event.preventDefault();
2084
+ event.stopPropagation();
2085
+ this.isDragOver.set(false);
2086
+ if (this.disabled() || this.isUploading())
2087
+ return;
2088
+ const files = event.dataTransfer?.files;
2089
+ if (files?.length) {
2090
+ this.handleFiles(Array.from(files));
2091
+ }
2092
+ }
2093
+ onFileSelected(event) {
2094
+ const input = event.target;
2095
+ const files = input.files;
2096
+ if (files?.length) {
2097
+ this.handleFiles(Array.from(files));
2098
+ }
2099
+ // Reset input to allow selecting same file again
2100
+ input.value = '';
2101
+ }
2102
+ handleFiles(files) {
2103
+ // Filter by type
2104
+ const allowedTypes = this.acceptTypes();
2105
+ const validFiles = files.filter(file => {
2106
+ if (!isFileTypeAllowed(file, allowedTypes)) {
2107
+ this.messageService.add({
2108
+ severity: 'warn',
2109
+ summary: 'Invalid File Type',
2110
+ detail: `File type not allowed: ${file.name}`,
2111
+ });
2112
+ return false;
2113
+ }
2114
+ return true;
2115
+ });
2116
+ // Filter by size
2117
+ const maxSize = this.maxSizeMb() * 1024 * 1024;
2118
+ const sizeValidFiles = validFiles.filter(file => {
2119
+ if (file.size > maxSize) {
2120
+ this.messageService.add({
2121
+ severity: 'warn',
2122
+ summary: 'File Too Large',
2123
+ detail: `${file.name} exceeds ${this.maxSizeMb()}MB limit`,
2124
+ });
2125
+ return false;
2126
+ }
2127
+ return true;
2128
+ });
2129
+ // Limit number of files
2130
+ const limitedFiles = this.multiple()
2131
+ ? sizeValidFiles.slice(0, this.maxFiles())
2132
+ : sizeValidFiles.slice(0, 1);
2133
+ if (!limitedFiles.length)
2134
+ return;
2135
+ this.selectedFiles.set(limitedFiles);
2136
+ this.fileSelected.emit(limitedFiles);
2137
+ if (this.autoUpload()) {
2138
+ this.uploadFiles(limitedFiles);
2139
+ }
2140
+ }
2141
+ removeFile(file) {
2142
+ this.selectedFiles.update(files => files.filter(f => f !== file));
2143
+ }
2144
+ async uploadFiles(files) {
2145
+ const filesToUpload = files ?? this.selectedFiles();
2146
+ if (!filesToUpload.length || this.isUploading())
2147
+ return;
2148
+ this.isUploading.set(true);
2149
+ const uploadedFiles = [];
2150
+ try {
2151
+ for (const file of filesToUpload) {
2152
+ this.uploadingFileName.set(file.name);
2153
+ this.uploadProgress.set(0);
2154
+ const response = await firstValueFrom(this.uploadFile()(file, this.uploadOptions()));
2155
+ if (response.success && response.data) {
2156
+ uploadedFiles.push(response.data);
2157
+ this.fileUploaded.emit(response.data);
2158
+ }
2159
+ else {
2160
+ throw new Error(response.message || 'Upload failed');
2161
+ }
2162
+ }
2163
+ this.filesUploaded.emit(uploadedFiles);
2164
+ this.selectedFiles.set([]);
2165
+ this.messageService.add({
2166
+ severity: 'success',
2167
+ summary: 'Upload Complete',
2168
+ detail: `${uploadedFiles.length} file(s) uploaded successfully`,
2169
+ });
2170
+ }
2171
+ catch (error) {
2172
+ this.messageService.add({
2173
+ severity: 'error',
2174
+ summary: 'Upload Failed',
2175
+ detail: error.message || 'Failed to upload file',
2176
+ });
2177
+ this.onError.emit(error);
2178
+ }
2179
+ finally {
2180
+ this.isUploading.set(false);
2181
+ this.uploadingFileName.set('');
2182
+ this.uploadProgress.set(0);
2183
+ }
2184
+ }
2185
+ getFileIcon(file) {
2186
+ return getFileIconClass(file.type);
2187
+ }
2188
+ formatSize(bytes) {
2189
+ const kb = bytes / 1024;
2190
+ if (kb < 1024)
2191
+ return `${kb.toFixed(1)} KB`;
2192
+ const mb = kb / 1024;
2193
+ return `${mb.toFixed(1)} MB`;
2194
+ }
2195
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileUploaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2196
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: FileUploaderComponent, isStandalone: true, selector: "lib-file-uploader", inputs: { uploadFile: { classPropertyName: "uploadFile", publicName: "uploadFile", isSignal: true, isRequired: true, transformFunction: null }, acceptTypes: { classPropertyName: "acceptTypes", publicName: "acceptTypes", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, maxFiles: { classPropertyName: "maxFiles", publicName: "maxFiles", isSignal: true, isRequired: false, transformFunction: null }, maxSizeMb: { classPropertyName: "maxSizeMb", publicName: "maxSizeMb", isSignal: true, isRequired: false, transformFunction: null }, uploadOptions: { classPropertyName: "uploadOptions", publicName: "uploadOptions", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, showPreview: { classPropertyName: "showPreview", publicName: "showPreview", isSignal: true, isRequired: false, transformFunction: null }, autoUpload: { classPropertyName: "autoUpload", publicName: "autoUpload", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { fileUploaded: "fileUploaded", filesUploaded: "filesUploaded", onError: "onError", fileSelected: "fileSelected" }, ngImport: i0, template: `
2197
+ <div
2198
+ class="file-uploader"
2199
+ [class.drag-over]="isDragOver()"
2200
+ [class.disabled]="disabled()"
2201
+ (dragover)="onDragOver($event)"
2202
+ (dragleave)="onDragLeave($event)"
2203
+ (drop)="onDrop($event)"
2204
+ >
2205
+ <!-- Upload Area -->
2206
+ <div class="upload-area" (click)="fileInput.click()">
2207
+ @if (isUploading()) {
2208
+ <div class="uploading-state">
2209
+ <i class="pi pi-spin pi-spinner text-4xl text-primary"></i>
2210
+ <p class="mt-2">Uploading {{ uploadingFileName() }}...</p>
2211
+ @if (uploadProgress() > 0) {
2212
+ <p-progressBar [value]="uploadProgress()" [showValue]="true" />
2213
+ }
2214
+ </div>
2215
+ } @else {
2216
+ <div class="idle-state text-center">
2217
+ <i class="pi pi-cloud-upload text-4xl text-primary"></i>
2218
+ <p class="mt-2 mb-1 font-semibold">
2219
+ {{ multiple() ? 'Drop files here or click to upload' : 'Drop file here or click to upload' }}
2220
+ </p>
2221
+ <p class="text-sm text-color-secondary">
2222
+ @if (acceptTypesDisplay()) {
2223
+ Allowed: {{ acceptTypesDisplay() }}
2224
+ } @else {
2225
+ All file types allowed
2226
+ }
2227
+ @if (maxSizeMb()) {
2228
+ (Max {{ maxSizeMb() }}MB)
2229
+ }
2230
+ </p>
2231
+ </div>
2232
+ }
2233
+ </div>
2234
+
2235
+ <!-- Hidden File Input -->
2236
+ <input
2237
+ #fileInput
2238
+ type="file"
2239
+ [accept]="acceptString()"
2240
+ [multiple]="multiple()"
2241
+ [disabled]="disabled() || isUploading()"
2242
+ (change)="onFileSelected($event)"
2243
+ class="hidden"
2244
+ />
2245
+
2246
+ <!-- Selected Files Preview -->
2247
+ @if (selectedFiles().length > 0 && showPreview()) {
2248
+ <div class="selected-files mt-3">
2249
+ @for (file of selectedFiles(); track file.name) {
2250
+ <div class="file-item flex align-items-center gap-2 p-2 border-round surface-border border-1 mb-2">
2251
+ <i [class]="getFileIcon(file)"></i>
2252
+ <span class="flex-1 text-overflow-ellipsis overflow-hidden">{{ file.name }}</span>
2253
+ <span class="text-sm text-color-secondary">{{ formatSize(file.size) }}</span>
2254
+ <button
2255
+ pButton
2256
+ type="button"
2257
+ icon="pi pi-times"
2258
+ class="p-button-text p-button-rounded p-button-sm"
2259
+ (click)="removeFile(file)"
2260
+ [disabled]="isUploading()"
2261
+ ></button>
2262
+ </div>
2263
+ }
2264
+ </div>
2265
+ }
2266
+ </div>
2267
+ `, isInline: true, styles: [".file-uploader{width:100%}.upload-area{border:2px dashed var(--surface-border);border-radius:var(--border-radius);padding:2rem;cursor:pointer;transition:all .2s;background:var(--surface-ground)}.upload-area:hover{border-color:var(--primary-color);background:var(--surface-hover)}.drag-over .upload-area{border-color:var(--primary-color);background:var(--primary-100)}.disabled .upload-area{opacity:.6;cursor:not-allowed}.hidden{display:none}\n"], dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "ngmodule", type: PrimeModule }, { kind: "directive", type: i1$3.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "pButtonPT", "pButtonUnstyled", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }, { kind: "component", type: i2$1.ProgressBar, selector: "p-progressBar, p-progressbar, p-progress-bar", inputs: ["value", "showValue", "styleClass", "valueStyleClass", "unit", "mode", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2268
+ }
2269
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileUploaderComponent, decorators: [{
2270
+ type: Component,
2271
+ args: [{ selector: 'lib-file-uploader', standalone: true, imports: [AngularModule, PrimeModule], template: `
2272
+ <div
2273
+ class="file-uploader"
2274
+ [class.drag-over]="isDragOver()"
2275
+ [class.disabled]="disabled()"
2276
+ (dragover)="onDragOver($event)"
2277
+ (dragleave)="onDragLeave($event)"
2278
+ (drop)="onDrop($event)"
2279
+ >
2280
+ <!-- Upload Area -->
2281
+ <div class="upload-area" (click)="fileInput.click()">
2282
+ @if (isUploading()) {
2283
+ <div class="uploading-state">
2284
+ <i class="pi pi-spin pi-spinner text-4xl text-primary"></i>
2285
+ <p class="mt-2">Uploading {{ uploadingFileName() }}...</p>
2286
+ @if (uploadProgress() > 0) {
2287
+ <p-progressBar [value]="uploadProgress()" [showValue]="true" />
2288
+ }
2289
+ </div>
2290
+ } @else {
2291
+ <div class="idle-state text-center">
2292
+ <i class="pi pi-cloud-upload text-4xl text-primary"></i>
2293
+ <p class="mt-2 mb-1 font-semibold">
2294
+ {{ multiple() ? 'Drop files here or click to upload' : 'Drop file here or click to upload' }}
2295
+ </p>
2296
+ <p class="text-sm text-color-secondary">
2297
+ @if (acceptTypesDisplay()) {
2298
+ Allowed: {{ acceptTypesDisplay() }}
2299
+ } @else {
2300
+ All file types allowed
2301
+ }
2302
+ @if (maxSizeMb()) {
2303
+ (Max {{ maxSizeMb() }}MB)
2304
+ }
2305
+ </p>
2306
+ </div>
2307
+ }
2308
+ </div>
2309
+
2310
+ <!-- Hidden File Input -->
2311
+ <input
2312
+ #fileInput
2313
+ type="file"
2314
+ [accept]="acceptString()"
2315
+ [multiple]="multiple()"
2316
+ [disabled]="disabled() || isUploading()"
2317
+ (change)="onFileSelected($event)"
2318
+ class="hidden"
2319
+ />
2320
+
2321
+ <!-- Selected Files Preview -->
2322
+ @if (selectedFiles().length > 0 && showPreview()) {
2323
+ <div class="selected-files mt-3">
2324
+ @for (file of selectedFiles(); track file.name) {
2325
+ <div class="file-item flex align-items-center gap-2 p-2 border-round surface-border border-1 mb-2">
2326
+ <i [class]="getFileIcon(file)"></i>
2327
+ <span class="flex-1 text-overflow-ellipsis overflow-hidden">{{ file.name }}</span>
2328
+ <span class="text-sm text-color-secondary">{{ formatSize(file.size) }}</span>
2329
+ <button
2330
+ pButton
2331
+ type="button"
2332
+ icon="pi pi-times"
2333
+ class="p-button-text p-button-rounded p-button-sm"
2334
+ (click)="removeFile(file)"
2335
+ [disabled]="isUploading()"
2336
+ ></button>
2337
+ </div>
2338
+ }
2339
+ </div>
2340
+ }
2341
+ </div>
2342
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".file-uploader{width:100%}.upload-area{border:2px dashed var(--surface-border);border-radius:var(--border-radius);padding:2rem;cursor:pointer;transition:all .2s;background:var(--surface-ground)}.upload-area:hover{border-color:var(--primary-color);background:var(--surface-hover)}.drag-over .upload-area{border-color:var(--primary-color);background:var(--primary-100)}.disabled .upload-area{opacity:.6;cursor:not-allowed}.hidden{display:none}\n"] }]
2343
+ }], propDecorators: { uploadFile: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploadFile", required: true }] }], acceptTypes: [{ type: i0.Input, args: [{ isSignal: true, alias: "acceptTypes", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], maxFiles: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFiles", required: false }] }], maxSizeMb: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSizeMb", required: false }] }], uploadOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploadOptions", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], showPreview: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPreview", required: false }] }], autoUpload: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoUpload", required: false }] }], fileUploaded: [{ type: i0.Output, args: ["fileUploaded"] }], filesUploaded: [{ type: i0.Output, args: ["filesUploaded"] }], onError: [{ type: i0.Output, args: ["onError"] }], fileSelected: [{ type: i0.Output, args: ["fileSelected"] }] } });
2344
+
2345
+ const DEFAULT_PAGE_SIZE = 20;
2346
+ /**
2347
+ * File Selector Dialog - Browse and select existing files with filtering.
2348
+ *
2349
+ * Pass your own `loadFiles` function - works with any storage API.
2350
+ *
2351
+ * Features:
2352
+ * - Search with debouncing
2353
+ * - File type filtering
2354
+ * - Infinite scroll pagination
2355
+ * - Single or multiple selection
2356
+ * - File preview with icons
2357
+ *
2358
+ * @example
2359
+ * ```typescript
2360
+ * // In component
2361
+ * readonly fileService = inject(FileManagerApiService);
2362
+ *
2363
+ * readonly loadFiles: LoadFilesFn = (filter) =>
2364
+ * this.fileService.getAll(filter.search, {
2365
+ * pagination: { currentPage: filter.page, pageSize: filter.pageSize },
2366
+ * filter: { contentTypes: filter.contentTypes },
2367
+ * }).pipe(
2368
+ * map(res => ({
2369
+ * ...res,
2370
+ * data: res.data?.map(f => ({
2371
+ * id: f.id,
2372
+ * name: f.name,
2373
+ * contentType: f.contentType,
2374
+ * size: f.size,
2375
+ * url: f.url
2376
+ * }))
2377
+ * }))
2378
+ * );
2379
+ * ```
2380
+ *
2381
+ * ```html
2382
+ * <lib-file-selector-dialog
2383
+ * [(visible)]="showFileSelector"
2384
+ * [loadFiles]="loadFiles"
2385
+ * [acceptTypes]="['image/*']"
2386
+ * [multiple]="false"
2387
+ * (fileSelected)="onFileSelected($event)"
2388
+ * />
2389
+ * ```
2390
+ */
2391
+ class FileSelectorDialogComponent {
2392
+ destroyRef = inject(DestroyRef);
2393
+ abortController = null;
2394
+ searchDebounceTimer = null;
2395
+ // Required: function to load files
2396
+ loadFiles = input.required(...(ngDevMode ? [{ debugName: "loadFiles" }] : []));
2397
+ // Inputs
2398
+ header = input('Select File', ...(ngDevMode ? [{ debugName: "header" }] : []));
2399
+ acceptTypes = input([], ...(ngDevMode ? [{ debugName: "acceptTypes" }] : []));
2400
+ multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple" }] : []));
2401
+ maxSelection = input(10, ...(ngDevMode ? [{ debugName: "maxSelection" }] : []));
2402
+ pageSize = input(DEFAULT_PAGE_SIZE, ...(ngDevMode ? [{ debugName: "pageSize" }] : []));
2403
+ // Two-way visibility binding
2404
+ visible = model(false, ...(ngDevMode ? [{ debugName: "visible" }] : []));
2405
+ // Outputs
2406
+ fileSelected = output();
2407
+ filesSelected = output();
2408
+ closed = output();
2409
+ onError = output();
2410
+ // Internal state
2411
+ isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
2412
+ files = signal([], ...(ngDevMode ? [{ debugName: "files" }] : []));
2413
+ selectedFiles = signal([], ...(ngDevMode ? [{ debugName: "selectedFiles" }] : []));
2414
+ total = signal(undefined, ...(ngDevMode ? [{ debugName: "total" }] : []));
2415
+ pagination = signal({ pageSize: DEFAULT_PAGE_SIZE, currentPage: 0 }, ...(ngDevMode ? [{ debugName: "pagination" }] : []));
2416
+ searchTerm = signal('', ...(ngDevMode ? [{ debugName: "searchTerm" }] : []));
2417
+ // Computed
2418
+ acceptString = computed(() => getAcceptString(this.acceptTypes()), ...(ngDevMode ? [{ debugName: "acceptString" }] : []));
2419
+ constructor() {
2420
+ this.destroyRef.onDestroy(() => {
2421
+ this.abortController?.abort();
2422
+ if (this.searchDebounceTimer) {
2423
+ clearTimeout(this.searchDebounceTimer);
2424
+ }
2425
+ });
2426
+ // Load files when dialog becomes visible
2427
+ effect(() => {
2428
+ const isVisible = this.visible();
2429
+ if (isVisible) {
2430
+ untracked(() => {
2431
+ this.resetState();
2432
+ this.fetchFiles();
2433
+ });
2434
+ }
2435
+ });
2436
+ // Update page size from input
2437
+ effect(() => {
2438
+ const size = this.pageSize();
2439
+ untracked(() => {
2440
+ this.pagination.update((p) => ({ ...p, pageSize: size }));
2441
+ });
2442
+ });
2443
+ }
2444
+ onSearchChange(value) {
2445
+ // Debounce search
2446
+ if (this.searchDebounceTimer) {
2447
+ clearTimeout(this.searchDebounceTimer);
2448
+ }
2449
+ this.searchDebounceTimer = setTimeout(() => {
2450
+ this.searchTerm.set(value);
2451
+ this.pagination.update((p) => ({ ...p, currentPage: 0 }));
2452
+ this.files.set([]);
2453
+ this.fetchFiles();
2454
+ }, 500);
2455
+ }
2456
+ onScroll(event) {
2457
+ const el = event.target;
2458
+ const nearBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 50;
2459
+ if (nearBottom && !this.isLoading()) {
2460
+ const pag = this.pagination();
2461
+ const nextPage = pag.currentPage + 1;
2462
+ const hasMore = nextPage * pag.pageSize < (this.total() ?? 0);
2463
+ if (hasMore) {
2464
+ this.pagination.update((p) => ({ ...p, currentPage: nextPage }));
2465
+ this.fetchFiles(true);
2466
+ }
2467
+ }
2468
+ }
2469
+ toggleSelection(file) {
2470
+ if (!this.isFileAllowed(file))
2471
+ return;
2472
+ if (this.multiple()) {
2473
+ const selected = this.selectedFiles();
2474
+ const isSelected = selected.some((f) => f.id === file.id);
2475
+ if (isSelected) {
2476
+ this.selectedFiles.update((files) => files.filter((f) => f.id !== file.id));
2477
+ }
2478
+ else if (selected.length < this.maxSelection()) {
2479
+ this.selectedFiles.update((files) => [...files, file]);
2480
+ }
2481
+ }
2482
+ else {
2483
+ this.selectedFiles.set([file]);
2484
+ }
2485
+ }
2486
+ isSelected(file) {
2487
+ return this.selectedFiles().some((f) => f.id === file.id);
2488
+ }
2489
+ isFileAllowed(file) {
2490
+ const allowedTypes = this.acceptTypes();
2491
+ if (!allowedTypes.length)
2492
+ return true;
2493
+ return allowedTypes.some((type) => {
2494
+ if (type.endsWith('/*')) {
2495
+ return file.contentType.startsWith(type.replace('/*', '/'));
2496
+ }
2497
+ return file.contentType === type;
2498
+ });
2499
+ }
2500
+ isImage(file) {
2501
+ return file.contentType.startsWith('image/') && !!file.url;
2502
+ }
2503
+ getFileIcon(file) {
2504
+ return getFileIconClass(file.contentType);
2505
+ }
2506
+ formatSize(size) {
2507
+ return formatFileSize(size);
2508
+ }
2509
+ onCancel() {
2510
+ this.visible.set(false);
2511
+ }
2512
+ onConfirm() {
2513
+ const selected = this.selectedFiles();
2514
+ if (selected.length === 0)
2515
+ return;
2516
+ if (this.multiple()) {
2517
+ this.filesSelected.emit(selected);
2518
+ }
2519
+ else {
2520
+ this.fileSelected.emit(selected[0]);
2521
+ }
2522
+ this.visible.set(false);
2523
+ }
2524
+ onDialogHide() {
2525
+ this.closed.emit();
2526
+ }
2527
+ resetState() {
2528
+ this.files.set([]);
2529
+ this.selectedFiles.set([]);
2530
+ this.searchTerm.set('');
2531
+ this.pagination.set({ pageSize: this.pageSize(), currentPage: 0 });
2532
+ this.total.set(undefined);
2533
+ }
2534
+ async fetchFiles(append = false) {
2535
+ if (this.isLoading())
2536
+ return;
2537
+ this.abortController?.abort();
2538
+ this.abortController = new AbortController();
2539
+ this.isLoading.set(true);
2540
+ try {
2541
+ const pag = this.pagination();
2542
+ const filter = {
2543
+ page: pag.currentPage,
2544
+ pageSize: pag.pageSize,
2545
+ search: this.searchTerm(),
2546
+ contentTypes: this.acceptTypes().length ? this.acceptTypes() : undefined,
2547
+ };
2548
+ const response = await firstValueFrom(this.loadFiles()(filter));
2549
+ if (response.success && response.data) {
2550
+ if (append) {
2551
+ this.files.update((current) => [...current, ...response.data]);
2552
+ }
2553
+ else {
2554
+ this.files.set(response.data);
2555
+ }
2556
+ this.total.set(response.meta?.total);
2557
+ }
2558
+ }
2559
+ catch (error) {
2560
+ if (error.name !== 'AbortError') {
2561
+ this.onError.emit(error);
2562
+ }
2563
+ }
2564
+ finally {
2565
+ this.isLoading.set(false);
2566
+ }
2567
+ }
2568
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileSelectorDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2569
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: FileSelectorDialogComponent, isStandalone: true, selector: "lib-file-selector-dialog", inputs: { loadFiles: { classPropertyName: "loadFiles", publicName: "loadFiles", isSignal: true, isRequired: true, transformFunction: null }, header: { classPropertyName: "header", publicName: "header", isSignal: true, isRequired: false, transformFunction: null }, acceptTypes: { classPropertyName: "acceptTypes", publicName: "acceptTypes", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, maxSelection: { classPropertyName: "maxSelection", publicName: "maxSelection", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { visible: "visibleChange", fileSelected: "fileSelected", filesSelected: "filesSelected", closed: "closed", onError: "onError" }, ngImport: i0, template: `
2570
+ <p-dialog
2571
+ [header]="header()"
2572
+ [(visible)]="visible"
2573
+ [modal]="true"
2574
+ [closable]="true"
2575
+ [draggable]="false"
2576
+ [resizable]="false"
2577
+ [style]="{ width: '800px', maxHeight: '90vh' }"
2578
+ (onHide)="onDialogHide()"
2579
+ >
2580
+ <!-- Search Bar -->
2581
+ <div class="flex gap-2 mb-3">
2582
+ <span class="p-input-icon-left flex-1">
2583
+ <i class="pi pi-search"></i>
2584
+ <input
2585
+ pInputText
2586
+ type="text"
2587
+ [ngModel]="searchTerm()"
2588
+ (ngModelChange)="onSearchChange($event)"
2589
+ placeholder="Search files..."
2590
+ class="w-full"
2591
+ />
2592
+ </span>
2593
+ @if (multiple()) {
2594
+ <span class="text-sm text-color-secondary align-self-center">
2595
+ {{ selectedFiles().length }} selected
2596
+ </span>
2597
+ }
2598
+ </div>
2599
+
2600
+ <!-- File Grid -->
2601
+ <div
2602
+ class="file-grid"
2603
+ #scrollContainer
2604
+ (scroll)="onScroll($event)"
2605
+ >
2606
+ @if (isLoading() && files().length === 0) {
2607
+ <div class="flex justify-content-center p-4">
2608
+ <i class="pi pi-spin pi-spinner text-4xl"></i>
2609
+ </div>
2610
+ } @else if (files().length === 0) {
2611
+ <div class="text-center p-4 text-color-secondary">
2612
+ <i class="pi pi-inbox text-4xl mb-2"></i>
2613
+ <p>No files found</p>
2614
+ </div>
2615
+ } @else {
2616
+ @for (file of files(); track file.id) {
2617
+ <div
2618
+ class="file-card"
2619
+ [class.selected]="isSelected(file)"
2620
+ [class.disabled]="!isFileAllowed(file)"
2621
+ (click)="toggleSelection(file)"
2622
+ >
2623
+ <!-- File Preview -->
2624
+ <div class="file-preview">
2625
+ @if (isImage(file) && file.url) {
2626
+ <img [src]="file.url" [alt]="file.name" class="preview-image" />
2627
+ } @else {
2628
+ <i [class]="getFileIcon(file)" class="preview-icon"></i>
2629
+ }
2630
+ @if (isSelected(file)) {
2631
+ <div class="selected-overlay">
2632
+ <i class="pi pi-check"></i>
2633
+ </div>
2634
+ }
2635
+ </div>
2636
+
2637
+ <!-- File Info -->
2638
+ <div class="file-info">
2639
+ <span class="file-name" [title]="file.name">{{ file.name }}</span>
2640
+ <span class="file-size">{{ formatSize(file.size) }}</span>
2641
+ </div>
2642
+ </div>
2643
+ }
2644
+
2645
+ @if (isLoading()) {
2646
+ <div class="flex justify-content-center p-2 w-full">
2647
+ <i class="pi pi-spin pi-spinner"></i>
2648
+ </div>
2649
+ }
2650
+ }
2651
+ </div>
2652
+
2653
+ <!-- Footer -->
2654
+ <ng-template pTemplate="footer">
2655
+ <button
2656
+ pButton
2657
+ label="Cancel"
2658
+ class="p-button-text"
2659
+ (click)="onCancel()"
2660
+ ></button>
2661
+ <button
2662
+ pButton
2663
+ [label]="multiple() ? 'Select (' + selectedFiles().length + ')' : 'Select'"
2664
+ [disabled]="selectedFiles().length === 0"
2665
+ (click)="onConfirm()"
2666
+ ></button>
2667
+ </ng-template>
2668
+ </p-dialog>
2669
+ `, isInline: true, styles: [".file-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:1rem;max-height:400px;overflow-y:auto;padding:.5rem}.file-card{border:2px solid var(--surface-border);border-radius:var(--border-radius);cursor:pointer;transition:all .2s;overflow:hidden}.file-card:hover:not(.disabled){border-color:var(--primary-color)}.file-card.selected{border-color:var(--primary-color);background:var(--primary-50)}.file-card.disabled{opacity:.5;cursor:not-allowed}.file-preview{position:relative;height:100px;display:flex;align-items:center;justify-content:center;background:var(--surface-ground)}.preview-image{width:100%;height:100%;object-fit:cover}.preview-icon{font-size:3rem;color:var(--text-color-secondary)}.selected-overlay{position:absolute;inset:0;background:rgba(var(--primary-color-rgb),.3);display:flex;align-items:center;justify-content:center}.selected-overlay i{font-size:2rem;color:var(--primary-color);background:#fff;border-radius:50%;padding:.5rem}.file-info{padding:.5rem;text-align:center}.file-name{display:block;font-size:.875rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.file-size{display:block;font-size:.75rem;color:var(--text-color-secondary)}\n"], dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: IsEmptyImageDirective, selector: "img", inputs: ["src"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "directive", type: i3$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "directive", type: i1$3.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "pButtonPT", "pButtonUnstyled", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }, { kind: "component", type: i5.Dialog, selector: "p-dialog", inputs: ["hostName", "header", "draggable", "resizable", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "maskMotionOptions", "motionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "appendTo", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "directive", type: i2.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2670
+ }
2671
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileSelectorDialogComponent, decorators: [{
2672
+ type: Component,
2673
+ args: [{ selector: 'lib-file-selector-dialog', standalone: true, imports: [AngularModule, PrimeModule], template: `
2674
+ <p-dialog
2675
+ [header]="header()"
2676
+ [(visible)]="visible"
2677
+ [modal]="true"
2678
+ [closable]="true"
2679
+ [draggable]="false"
2680
+ [resizable]="false"
2681
+ [style]="{ width: '800px', maxHeight: '90vh' }"
2682
+ (onHide)="onDialogHide()"
2683
+ >
2684
+ <!-- Search Bar -->
2685
+ <div class="flex gap-2 mb-3">
2686
+ <span class="p-input-icon-left flex-1">
2687
+ <i class="pi pi-search"></i>
2688
+ <input
2689
+ pInputText
2690
+ type="text"
2691
+ [ngModel]="searchTerm()"
2692
+ (ngModelChange)="onSearchChange($event)"
2693
+ placeholder="Search files..."
2694
+ class="w-full"
2695
+ />
2696
+ </span>
2697
+ @if (multiple()) {
2698
+ <span class="text-sm text-color-secondary align-self-center">
2699
+ {{ selectedFiles().length }} selected
2700
+ </span>
2701
+ }
2702
+ </div>
2703
+
2704
+ <!-- File Grid -->
2705
+ <div
2706
+ class="file-grid"
2707
+ #scrollContainer
2708
+ (scroll)="onScroll($event)"
2709
+ >
2710
+ @if (isLoading() && files().length === 0) {
2711
+ <div class="flex justify-content-center p-4">
2712
+ <i class="pi pi-spin pi-spinner text-4xl"></i>
2713
+ </div>
2714
+ } @else if (files().length === 0) {
2715
+ <div class="text-center p-4 text-color-secondary">
2716
+ <i class="pi pi-inbox text-4xl mb-2"></i>
2717
+ <p>No files found</p>
2718
+ </div>
2719
+ } @else {
2720
+ @for (file of files(); track file.id) {
2721
+ <div
2722
+ class="file-card"
2723
+ [class.selected]="isSelected(file)"
2724
+ [class.disabled]="!isFileAllowed(file)"
2725
+ (click)="toggleSelection(file)"
2726
+ >
2727
+ <!-- File Preview -->
2728
+ <div class="file-preview">
2729
+ @if (isImage(file) && file.url) {
2730
+ <img [src]="file.url" [alt]="file.name" class="preview-image" />
2731
+ } @else {
2732
+ <i [class]="getFileIcon(file)" class="preview-icon"></i>
2733
+ }
2734
+ @if (isSelected(file)) {
2735
+ <div class="selected-overlay">
2736
+ <i class="pi pi-check"></i>
2737
+ </div>
2738
+ }
2739
+ </div>
2740
+
2741
+ <!-- File Info -->
2742
+ <div class="file-info">
2743
+ <span class="file-name" [title]="file.name">{{ file.name }}</span>
2744
+ <span class="file-size">{{ formatSize(file.size) }}</span>
2745
+ </div>
2746
+ </div>
2747
+ }
2748
+
2749
+ @if (isLoading()) {
2750
+ <div class="flex justify-content-center p-2 w-full">
2751
+ <i class="pi pi-spin pi-spinner"></i>
2752
+ </div>
2753
+ }
2754
+ }
2755
+ </div>
2756
+
2757
+ <!-- Footer -->
2758
+ <ng-template pTemplate="footer">
2759
+ <button
2760
+ pButton
2761
+ label="Cancel"
2762
+ class="p-button-text"
2763
+ (click)="onCancel()"
2764
+ ></button>
2765
+ <button
2766
+ pButton
2767
+ [label]="multiple() ? 'Select (' + selectedFiles().length + ')' : 'Select'"
2768
+ [disabled]="selectedFiles().length === 0"
2769
+ (click)="onConfirm()"
2770
+ ></button>
2771
+ </ng-template>
2772
+ </p-dialog>
2773
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".file-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:1rem;max-height:400px;overflow-y:auto;padding:.5rem}.file-card{border:2px solid var(--surface-border);border-radius:var(--border-radius);cursor:pointer;transition:all .2s;overflow:hidden}.file-card:hover:not(.disabled){border-color:var(--primary-color)}.file-card.selected{border-color:var(--primary-color);background:var(--primary-50)}.file-card.disabled{opacity:.5;cursor:not-allowed}.file-preview{position:relative;height:100px;display:flex;align-items:center;justify-content:center;background:var(--surface-ground)}.preview-image{width:100%;height:100%;object-fit:cover}.preview-icon{font-size:3rem;color:var(--text-color-secondary)}.selected-overlay{position:absolute;inset:0;background:rgba(var(--primary-color-rgb),.3);display:flex;align-items:center;justify-content:center}.selected-overlay i{font-size:2rem;color:var(--primary-color);background:#fff;border-radius:50%;padding:.5rem}.file-info{padding:.5rem;text-align:center}.file-name{display:block;font-size:.875rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.file-size{display:block;font-size:.75rem;color:var(--text-color-secondary)}\n"] }]
2774
+ }], ctorParameters: () => [], propDecorators: { loadFiles: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadFiles", required: true }] }], header: [{ type: i0.Input, args: [{ isSignal: true, alias: "header", required: false }] }], acceptTypes: [{ type: i0.Input, args: [{ isSignal: true, alias: "acceptTypes", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], maxSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSelection", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: false }] }, { type: i0.Output, args: ["visibleChange"] }], fileSelected: [{ type: i0.Output, args: ["fileSelected"] }], filesSelected: [{ type: i0.Output, args: ["filesSelected"] }], closed: [{ type: i0.Output, args: ["closed"] }], onError: [{ type: i0.Output, args: ["onError"] }] } });
1415
2775
 
2776
+ /** Log only in dev mode */
2777
+ const devLog = (message) => {
2778
+ if (isDevMode())
2779
+ console.log(message);
2780
+ };
1416
2781
  /**
1417
2782
  * Permission Guard
1418
2783
  *
@@ -1451,14 +2816,14 @@ function permissionGuard(permission, redirectTo = '/') {
1451
2816
  const router = inject(Router);
1452
2817
  // Check if permissions are loaded
1453
2818
  if (!permissionValidator.isPermissionsLoaded()) {
1454
- console.warn('[permissionGuard] Permissions not loaded, denying access to route');
2819
+ devLog('[permissionGuard] Permissions not loaded, denying access to route');
1455
2820
  return router.createUrlTree([redirectTo]);
1456
2821
  }
1457
2822
  const userPermissions = permissionValidator.permissions();
1458
2823
  const hasPermission = evaluatePermission(permission, userPermissions);
1459
2824
  if (!hasPermission) {
1460
2825
  const permissionCode = typeof permission === 'string' ? permission : 'complex-logic';
1461
- console.warn(`[permissionGuard] Access denied - missing permission: ${permissionCode}`);
2826
+ devLog(`[permissionGuard] Access denied - missing permission: ${permissionCode}`);
1462
2827
  return router.createUrlTree([redirectTo]);
1463
2828
  }
1464
2829
  return true;
@@ -1481,18 +2846,18 @@ function anyPermissionGuard(permissions, redirectTo = '/') {
1481
2846
  const router = inject(Router);
1482
2847
  // Validate permissions array
1483
2848
  if (!permissions || permissions.length === 0) {
1484
- console.warn('[anyPermissionGuard] Empty permissions array provided, denying access');
2849
+ devLog('[anyPermissionGuard] Empty permissions array provided, denying access');
1485
2850
  return router.createUrlTree([redirectTo]);
1486
2851
  }
1487
2852
  // Check if permissions are loaded
1488
2853
  if (!permissionValidator.isPermissionsLoaded()) {
1489
- console.warn('[anyPermissionGuard] Permissions not loaded, denying access to route');
2854
+ devLog('[anyPermissionGuard] Permissions not loaded, denying access to route');
1490
2855
  return router.createUrlTree([redirectTo]);
1491
2856
  }
1492
2857
  const userPermissions = permissionValidator.permissions();
1493
2858
  const hasPermission = hasAnyPermission(permissions, userPermissions);
1494
2859
  if (!hasPermission) {
1495
- console.warn(`[anyPermissionGuard] Access denied - missing any of: ${permissions.join(', ')}`);
2860
+ devLog(`[anyPermissionGuard] Access denied - missing any of: ${permissions.join(', ')}`);
1496
2861
  return router.createUrlTree([redirectTo]);
1497
2862
  }
1498
2863
  return true;
@@ -1515,18 +2880,18 @@ function allPermissionsGuard(permissions, redirectTo = '/') {
1515
2880
  const router = inject(Router);
1516
2881
  // Validate permissions array
1517
2882
  if (!permissions || permissions.length === 0) {
1518
- console.warn('[allPermissionsGuard] Empty permissions array provided, denying access');
2883
+ devLog('[allPermissionsGuard] Empty permissions array provided, denying access');
1519
2884
  return router.createUrlTree([redirectTo]);
1520
2885
  }
1521
2886
  // Check if permissions are loaded
1522
2887
  if (!permissionValidator.isPermissionsLoaded()) {
1523
- console.warn('[allPermissionsGuard] Permissions not loaded, denying access to route');
2888
+ devLog('[allPermissionsGuard] Permissions not loaded, denying access to route');
1524
2889
  return router.createUrlTree([redirectTo]);
1525
2890
  }
1526
2891
  const userPermissions = permissionValidator.permissions();
1527
2892
  const hasPermission = hasAllPermissions(permissions, userPermissions);
1528
2893
  if (!hasPermission) {
1529
- console.warn(`[allPermissionsGuard] Access denied - missing all of: ${permissions.join(', ')}`);
2894
+ devLog(`[allPermissionsGuard] Access denied - missing all of: ${permissions.join(', ')}`);
1530
2895
  return router.createUrlTree([redirectTo]);
1531
2896
  }
1532
2897
  return true;
@@ -1539,5 +2904,5 @@ function allPermissionsGuard(permissions, redirectTo = '/') {
1539
2904
  * Generated bundle index. Do not edit.
1540
2905
  */
1541
2906
 
1542
- export { AngularModule, ApiResourceService, ApiResourceService as ApiService, COMPANY_API_PROVIDER, ContactTypeEnum, CookieService, EditModeElementChangerDirective, FileUrlService, HasPermissionDirective, IconComponent, IconTypeEnum, IsEmptyImageDirective, LazyMultiSelectComponent, LazySelectComponent, PermissionValidatorService, PlatformService, PreventDefaultDirective, PrimeModule, USER_PERMISSION_PROVIDER, USER_PROVIDER, allPermissionsGuard, anyPermissionGuard, evaluateLogicNode, evaluatePermission, hasAllPermissions, hasAnyPermission, permissionGuard };
2907
+ export { AUTH_STATE_PROVIDER, AngularModule, ApiResourceService, ApiResourceService as ApiService, COMPANY_API_PROVIDER, ContactTypeEnum, CookieService, EditModeElementChangerDirective, FILE_TYPE_FILTERS, FileSelectorDialogComponent, FileUploaderComponent, FileUrlService, HasPermissionDirective, IconComponent, IconTypeEnum, IsEmptyImageDirective, LazyMultiSelectComponent, LazySelectComponent, PROFILE_PERMISSION_PROVIDER, PROFILE_UPLOAD_PROVIDER, PermissionValidatorService, PlatformService, PreventDefaultDirective, PrimeModule, USER_LIST_PROVIDER, USER_PERMISSION_PROVIDER, USER_PROVIDER, UserMultiSelectComponent, UserSelectComponent, allPermissionsGuard, anyPermissionGuard, evaluateLogicNode, evaluatePermission, formatFileSize, getAcceptString, getFileIconClass, hasAllPermissions, hasAnyPermission, isFileTypeAllowed, permissionGuard };
1543
2908
  //# sourceMappingURL=flusys-ng-shared.mjs.map