@flusys/ng-shared 1.0.0-rc → 1.0.1

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,20 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { inject, PLATFORM_ID, Injectable, DOCUMENT, REQUEST, signal, computed, ElementRef, input, effect, Directive, TemplateRef, ViewContainerRef, output, NgModule, Injector, runInInjectionContext, resource, model, untracked, forwardRef, ChangeDetectionStrategy, Component, DestroyRef, viewChild, afterNextRender, InjectionToken, isDevMode } from '@angular/core';
3
- import * as i1 from '@angular/common';
3
+ import * as i3 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
- import { APP_CONFIG, getServiceUrl, ApiLoaderService } from '@flusys/ng-core';
6
+ import { APP_CONFIG, getServiceUrl } from '@flusys/ng-core';
7
7
  import { of, firstValueFrom, map as map$1 } from 'rxjs';
8
8
  import { map, tap, catchError } from 'rxjs/operators';
9
- import * as i1$2 from '@angular/forms';
9
+ import * as i1$1 from '@angular/forms';
10
10
  import { NgControl, FormsModule, ReactiveFormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
11
11
  import { RouterOutlet, RouterLink, RouterLinkActive, Router, ActivatedRoute } from '@angular/router';
12
12
  import { AutoCompleteModule } from 'primeng/autocomplete';
13
13
  import { AvatarModule } from 'primeng/avatar';
14
- import * as i1$3 from 'primeng/button';
14
+ import * as i1$2 from 'primeng/button';
15
15
  import { ButtonModule } from 'primeng/button';
16
16
  import { CardModule } from 'primeng/card';
17
- import * as i1$1 from 'primeng/checkbox';
17
+ import * as i1 from 'primeng/checkbox';
18
18
  import { CheckboxModule } from 'primeng/checkbox';
19
19
  import { ConfirmDialogModule } from 'primeng/confirmdialog';
20
20
  import { DatePickerModule } from 'primeng/datepicker';
@@ -38,7 +38,7 @@ import * as i2$1 from 'primeng/progressbar';
38
38
  import { ProgressBarModule } from 'primeng/progressbar';
39
39
  import { RadioButtonModule } from 'primeng/radiobutton';
40
40
  import { RippleModule } from 'primeng/ripple';
41
- import * as i3 from 'primeng/select';
41
+ import * as i3$1 from 'primeng/select';
42
42
  import { SelectModule } from 'primeng/select';
43
43
  import { SelectButtonModule } from 'primeng/selectbutton';
44
44
  import { SkeletonModule } from 'primeng/skeleton';
@@ -266,10 +266,10 @@ class PlatformService {
266
266
  get isServer() {
267
267
  return isPlatformServer(this.platformId);
268
268
  }
269
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PlatformService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
270
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PlatformService, providedIn: 'root' });
269
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: PlatformService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
270
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: PlatformService, providedIn: 'root' });
271
271
  }
272
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PlatformService, decorators: [{
272
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: PlatformService, decorators: [{
273
273
  type: Injectable,
274
274
  args: [{ providedIn: 'root' }]
275
275
  }] });
@@ -281,10 +281,10 @@ class CookieService {
281
281
  get() {
282
282
  return !this.platformService.isServer ? this.doc.cookie : this.request?.headers.get('cookie') ?? "";
283
283
  }
284
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CookieService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
285
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CookieService, providedIn: 'root' });
284
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: CookieService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
285
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: CookieService, providedIn: 'root' });
286
286
  }
287
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CookieService, decorators: [{
287
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: CookieService, decorators: [{
288
288
  type: Injectable,
289
289
  args: [{
290
290
  providedIn: 'root',
@@ -293,193 +293,171 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
293
293
 
294
294
  /**
295
295
  * Service to fetch file URLs from the backend.
296
- * Uses POST /file-manager/get-files endpoint which:
297
- * - Handles presigned URLs for cloud storage (AWS S3, Azure)
298
- * - Auto-refreshes expired URLs
299
- * - Validates file access permissions
300
- * - Works with all storage providers (Local, S3, Azure, SFTP)
296
+ * Handles presigned URLs for cloud storage, auto-refresh, and caching.
301
297
  */
302
298
  class FileUrlService {
303
299
  http = inject(HttpClient);
304
300
  appConfig = inject(APP_CONFIG);
305
- /** Cache of file URLs by file ID */
306
- urlCache = signal(new Map(), ...(ngDevMode ? [{ debugName: "urlCache" }] : []));
307
- /**
308
- * Get file URL by ID from cache (reactive signal)
309
- */
301
+ _cache = signal(new Map(), ...(ngDevMode ? [{ debugName: "_cache" }] : []));
302
+ cache = this._cache.asReadonly();
303
+ /** Get file URL by ID from cache (synchronous) */
310
304
  getFileUrl(fileId) {
311
305
  if (!fileId)
312
306
  return null;
313
- return this.urlCache().get(fileId)?.url ?? null;
307
+ return this._cache().get(fileId)?.url ?? null;
314
308
  }
315
- /**
316
- * Get file URL signal (computed from cache)
317
- */
309
+ /** Get file URL as computed signal */
318
310
  fileUrlSignal(fileId) {
319
311
  return computed(() => this.getFileUrl(fileId));
320
312
  }
321
- /**
322
- * Fetch file URLs from backend and update cache.
323
- * Skips IDs already in cache to prevent duplicate calls.
324
- * Returns Observable of fetched files (including cached ones).
325
- */
313
+ /** Fetch file URLs from backend and update cache */
326
314
  fetchFileUrls(fileIds, forceRefresh = false) {
327
315
  if (!fileIds.length)
328
316
  return of([]);
329
- const cache = this.urlCache();
330
- // Filter out IDs already in cache (unless force refresh)
317
+ const cache = this._cache();
331
318
  const missingIds = forceRefresh
332
319
  ? fileIds
333
320
  : fileIds.filter((id) => !cache.has(id));
334
- // If all files are cached, return from cache
335
- if (missingIds.length === 0) {
336
- const cachedFiles = fileIds
337
- .map((id) => cache.get(id))
338
- .filter((f) => !!f);
339
- return of(cachedFiles);
321
+ if (!missingIds.length) {
322
+ return of(this.getFromCache(fileIds));
340
323
  }
341
324
  const requestDto = missingIds.map((id) => ({ id }));
342
325
  return this.http
343
326
  .post(`${getServiceUrl(this.appConfig, 'storage')}/file-manager/get-files`, requestDto)
344
- .pipe(map((response) => response.data ?? []), tap((files) => {
345
- // Update cache with new files
346
- const newCache = new Map(this.urlCache());
347
- files.forEach((file) => newCache.set(file.id, file));
348
- this.urlCache.set(newCache);
349
- }), map(() => {
350
- // Return all requested files (cached + newly fetched)
351
- const allCache = this.urlCache();
352
- return fileIds
353
- .map((id) => allCache.get(id))
354
- .filter((f) => !!f);
355
- }), catchError(() => of([])));
327
+ .pipe(map((response) => response.data ?? []), tap((files) => this.addToCache(files)), map(() => this.getFromCache(fileIds)), catchError(() => of([])));
356
328
  }
357
- /**
358
- * Fetch a single file URL.
359
- * Uses cache if available to prevent duplicate calls.
360
- * Returns Observable of file info or null if not found.
361
- */
329
+ /** Fetch single file URL (delegates to fetchFileUrls) */
362
330
  fetchSingleFileUrl(fileId, forceRefresh = false) {
363
- // Return from cache immediately if available
364
- if (!forceRefresh) {
365
- const cached = this.urlCache().get(fileId);
366
- if (cached) {
367
- return of(cached);
368
- }
369
- }
370
331
  return this.fetchFileUrls([fileId], forceRefresh).pipe(map((files) => files[0] ?? null));
371
332
  }
372
- /**
373
- * Clear the URL cache.
374
- * Useful on logout or when switching contexts.
375
- */
333
+ /** Clear entire cache */
376
334
  clearCache() {
377
- this.urlCache.set(new Map());
335
+ this._cache.set(new Map());
378
336
  }
379
- /**
380
- * Remove specific file from cache.
381
- */
337
+ /** Remove specific file from cache */
382
338
  removeFromCache(fileId) {
383
- const cache = new Map(this.urlCache());
384
- cache.delete(fileId);
385
- this.urlCache.set(cache);
339
+ this._cache.update((cache) => {
340
+ const next = new Map(cache);
341
+ next.delete(fileId);
342
+ return next;
343
+ });
386
344
  }
387
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileUrlService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
388
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileUrlService, providedIn: 'root' });
345
+ addToCache(files) {
346
+ this._cache.update((cache) => {
347
+ const next = new Map(cache);
348
+ for (const file of files) {
349
+ next.set(file.id, file);
350
+ }
351
+ return next;
352
+ });
353
+ }
354
+ getFromCache(fileIds) {
355
+ const cache = this._cache();
356
+ return fileIds
357
+ .map((id) => cache.get(id))
358
+ .filter((f) => f !== undefined);
359
+ }
360
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FileUrlService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
361
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FileUrlService, providedIn: 'root' });
389
362
  }
390
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileUrlService, decorators: [{
363
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FileUrlService, decorators: [{
391
364
  type: Injectable,
392
365
  args: [{ providedIn: 'root' }]
393
366
  }] });
394
367
 
395
368
  /**
396
- * Permission Validator Service
397
- *
398
- * Centralized service for permission validation across all packages.
399
- * Provides signal-based state management and reactive permission checking.
400
- *
401
- * Features:
402
- * - Signal-based permission storage
403
- * - Individual permission checks
404
- * - Permission change detection
405
- * - Reactive permission state updates
406
- *
407
- * Usage:
408
- * ```typescript
409
- * // In component
410
- * readonly permissionValidator = inject(PermissionValidatorService);
411
- *
412
- * // Set permissions (typically from auth/IAM)
413
- * this.permissionValidator.setPermissions(['user.view', 'user.create']);
414
- *
415
- * // Or set wildcard for no-IAM mode
416
- * this.permissionValidator.setPermissions(['*']);
417
- *
418
- * // Check individual permission
419
- * if (this.permissionValidator.hasPermission('user.view')) {
420
- * // Show UI element
421
- * }
422
- *
423
- * // Access current permissions reactively
424
- * const permissions = this.permissionValidator.permissions();
425
- * ```
426
- *
427
- * @packageDocumentation
369
+ * Check if user has a specific permission using wildcard matching.
370
+ * Supports:
371
+ * - Exact match: 'user.create' matches 'user.create'
372
+ * - Full wildcard: '*' matches everything
373
+ * - Module wildcard: 'user.*' matches 'user.create', 'user.read', etc.
374
+ */
375
+ function hasPermission(requiredPermission, userPermissions) {
376
+ // Exact match
377
+ if (userPermissions.includes(requiredPermission))
378
+ return true;
379
+ // Wildcard matching
380
+ for (const permission of userPermissions) {
381
+ // Full wildcard - grants all permissions
382
+ if (permission === '*')
383
+ return true;
384
+ // Module wildcard (e.g., 'user.*' matches 'user.create')
385
+ if (permission.endsWith('.*') &&
386
+ requiredPermission.startsWith(permission.slice(0, -1))) {
387
+ return true;
388
+ }
389
+ }
390
+ return false;
391
+ }
392
+ /** Evaluate permission logic (string or ILogicNode) against user permissions */
393
+ function evaluatePermission(logic, permissions) {
394
+ if (!logic)
395
+ return false;
396
+ if (typeof logic === 'string')
397
+ return hasPermission(logic, permissions);
398
+ return evaluateLogicNode(logic, permissions);
399
+ }
400
+ /** Recursively evaluate an ILogicNode tree */
401
+ function evaluateLogicNode(node, permissions) {
402
+ switch (node.type) {
403
+ case 'action':
404
+ return node.actionId ? hasPermission(node.actionId, permissions) : false;
405
+ case 'group':
406
+ if (!node.children || node.children.length === 0)
407
+ return false;
408
+ return node.operator === 'AND'
409
+ ? node.children.every((child) => evaluateLogicNode(child, permissions))
410
+ : node.children.some((child) => evaluateLogicNode(child, permissions));
411
+ default:
412
+ return false;
413
+ }
414
+ }
415
+ /** Check if user has ANY of the specified permissions (OR logic) */
416
+ function hasAnyPermission(permissionCodes, permissions) {
417
+ if (!permissionCodes?.length)
418
+ return false;
419
+ return permissionCodes.some((code) => hasPermission(code, permissions));
420
+ }
421
+ /** Check if user has ALL of the specified permissions (AND logic) */
422
+ function hasAllPermissions(permissionCodes, permissions) {
423
+ if (!permissionCodes?.length)
424
+ return false;
425
+ return permissionCodes.every((code) => hasPermission(code, permissions));
426
+ }
427
+
428
+ /**
429
+ * Permission state management service.
430
+ * Provides signal-based storage and permission checking with wildcard support.
428
431
  */
429
432
  class PermissionValidatorService {
430
- // ==================== SIGNALS ====================
431
- /**
432
- * Private writable signal for permissions
433
- */
434
433
  _permissions = signal([], ...(ngDevMode ? [{ debugName: "_permissions" }] : []));
435
- /**
436
- * Readonly permission signal for external consumers
437
- */
438
434
  permissions = this._permissions.asReadonly();
439
- /**
440
- * Private writable signal for loaded state
441
- */
442
435
  _isLoaded = signal(false, ...(ngDevMode ? [{ debugName: "_isLoaded" }] : []));
443
- /**
444
- * Set permissions (replaces existing permissions)
445
- * @param permissions - Array of permission codes
446
- */
436
+ isLoaded = this._isLoaded.asReadonly();
437
+ /** Set permissions (replaces existing) */
447
438
  setPermissions(permissions) {
448
439
  this._permissions.set(permissions);
449
440
  this._isLoaded.set(true);
450
441
  }
451
- /**
452
- * Clear all permissions
453
- */
442
+ /** Clear all permissions */
454
443
  clearPermissions() {
455
444
  this._permissions.set([]);
456
445
  this._isLoaded.set(false);
457
446
  }
458
- // ==================== PERMISSION CHECKING ====================
459
- /**
460
- * Check if user has a specific permission
461
- * @param permissionCode - Required permission code
462
- * @returns True if user has permission
463
- */
464
- hasPermission(permissionCode) {
465
- return this.permissions().includes(permissionCode);
447
+ /** Check if user has permission (supports wildcards: '*', 'module.*') */
448
+ hasPermission(code) {
449
+ return hasPermission(code, this._permissions());
466
450
  }
467
- // ==================== UTILITY METHODS ====================
468
- /**
469
- * Check if permissions are loaded
470
- * @returns True if permissions have been loaded
471
- */
451
+ /** @deprecated Use `isLoaded()` signal instead */
472
452
  isPermissionsLoaded() {
473
453
  return this._isLoaded();
474
454
  }
475
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PermissionValidatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
476
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PermissionValidatorService, providedIn: 'root' });
455
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: PermissionValidatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
456
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: PermissionValidatorService, providedIn: 'root' });
477
457
  }
478
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PermissionValidatorService, decorators: [{
458
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: PermissionValidatorService, decorators: [{
479
459
  type: Injectable,
480
- args: [{
481
- providedIn: 'root',
482
- }]
460
+ args: [{ providedIn: 'root' }]
483
461
  }] });
484
462
 
485
463
  class EditModeElementChangerDirective {
@@ -535,77 +513,16 @@ class EditModeElementChangerDirective {
535
513
  }
536
514
  }
537
515
  }
538
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EditModeElementChangerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
539
- 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 });
516
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: EditModeElementChangerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
517
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.5", type: EditModeElementChangerDirective, isStandalone: true, selector: "[appEditModeElementChanger]", inputs: { isEditMode: { classPropertyName: "isEditMode", publicName: "isEditMode", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
540
518
  }
541
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EditModeElementChangerDirective, decorators: [{
519
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: EditModeElementChangerDirective, decorators: [{
542
520
  type: Directive,
543
521
  args: [{
544
522
  selector: '[appEditModeElementChanger]',
545
- standalone: true,
546
523
  }]
547
524
  }], ctorParameters: () => [], propDecorators: { isEditMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "isEditMode", required: true }] }] } });
548
525
 
549
- /**
550
- * Check if user has a specific permission using wildcard matching.
551
- * Supports:
552
- * - Exact match: 'user.create' matches 'user.create'
553
- * - Full wildcard: '*' matches everything
554
- * - Module wildcard: 'user.*' matches 'user.create', 'user.read', etc.
555
- */
556
- function hasPermission(requiredPermission, userPermissions) {
557
- // Exact match
558
- if (userPermissions.includes(requiredPermission))
559
- return true;
560
- // Wildcard matching
561
- for (const permission of userPermissions) {
562
- // Full wildcard - grants all permissions
563
- if (permission === '*')
564
- return true;
565
- // Module wildcard (e.g., 'user.*' matches 'user.create')
566
- if (permission.endsWith('.*') &&
567
- requiredPermission.startsWith(permission.slice(0, -1))) {
568
- return true;
569
- }
570
- }
571
- return false;
572
- }
573
- /** Evaluate permission logic (string or ILogicNode) against user permissions */
574
- function evaluatePermission(logic, permissions) {
575
- if (!logic)
576
- return false;
577
- if (typeof logic === 'string')
578
- return hasPermission(logic, permissions);
579
- return evaluateLogicNode(logic, permissions);
580
- }
581
- /** Recursively evaluate an ILogicNode tree */
582
- function evaluateLogicNode(node, permissions) {
583
- switch (node.type) {
584
- case 'action':
585
- return node.actionId ? hasPermission(node.actionId, permissions) : false;
586
- case 'group':
587
- if (!node.children || node.children.length === 0)
588
- return false;
589
- return node.operator === 'AND'
590
- ? node.children.every((child) => evaluateLogicNode(child, permissions))
591
- : node.children.some((child) => evaluateLogicNode(child, permissions));
592
- default:
593
- return false;
594
- }
595
- }
596
- /** Check if user has ANY of the specified permissions (OR logic) */
597
- function hasAnyPermission(permissionCodes, permissions) {
598
- if (!permissionCodes?.length)
599
- return false;
600
- return permissionCodes.some((code) => hasPermission(code, permissions));
601
- }
602
- /** Check if user has ALL of the specified permissions (AND logic) */
603
- function hasAllPermissions(permissionCodes, permissions) {
604
- if (!permissionCodes?.length)
605
- return false;
606
- return permissionCodes.every((code) => hasPermission(code, permissions));
607
- }
608
-
609
526
  /**
610
527
  * HasPermission Directive
611
528
  *
@@ -719,14 +636,13 @@ class HasPermissionDirective {
719
636
  this.viewCreated = false;
720
637
  }
721
638
  }
722
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: HasPermissionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
723
- 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 });
639
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: HasPermissionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
640
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.5", type: HasPermissionDirective, isStandalone: true, selector: "[hasPermission]", inputs: { hasPermission: { classPropertyName: "hasPermission", publicName: "hasPermission", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
724
641
  }
725
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: HasPermissionDirective, decorators: [{
642
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: HasPermissionDirective, decorators: [{
726
643
  type: Directive,
727
644
  args: [{
728
645
  selector: '[hasPermission]',
729
- standalone: true,
730
646
  }]
731
647
  }], ctorParameters: () => [], propDecorators: { hasPermission: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasPermission", required: false }] }] } });
732
648
 
@@ -744,10 +660,10 @@ class IsEmptyImageDirective {
744
660
  onError() {
745
661
  this.hasError.set(true);
746
662
  }
747
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: IsEmptyImageDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
748
- 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 });
663
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: IsEmptyImageDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
664
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.5", 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 });
749
665
  }
750
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: IsEmptyImageDirective, decorators: [{
666
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: IsEmptyImageDirective, decorators: [{
751
667
  type: Directive,
752
668
  args: [{
753
669
  selector: 'img',
@@ -755,7 +671,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
755
671
  '[src]': 'imageSrc()',
756
672
  '(error)': 'onError()',
757
673
  },
758
- standalone: true,
759
674
  }]
760
675
  }], propDecorators: { src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }] } });
761
676
 
@@ -786,14 +701,13 @@ class PreventDefaultDirective {
786
701
  event.preventDefault();
787
702
  this.action.emit(event);
788
703
  }
789
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PreventDefaultDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
790
- 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 });
704
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: PreventDefaultDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
705
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.5", 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 });
791
706
  }
792
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PreventDefaultDirective, decorators: [{
707
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: PreventDefaultDirective, decorators: [{
793
708
  type: Directive,
794
709
  args: [{
795
710
  selector: '[appPreventDefault]',
796
- standalone: true,
797
711
  host: {
798
712
  '(click)': 'onClick($event)',
799
713
  '(keydown)': 'onKeydown($event)',
@@ -803,8 +717,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
803
717
  }], propDecorators: { eventType: [{ type: i0.Input, args: [{ isSignal: true, alias: "eventType", required: false }] }], preventKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "preventKey", required: false }] }], action: [{ type: i0.Output, args: ["action"] }] } });
804
718
 
805
719
  class AngularModule {
806
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AngularModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
807
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: AngularModule, imports: [CommonModule,
720
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AngularModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
721
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.5", ngImport: i0, type: AngularModule, imports: [CommonModule,
808
722
  FormsModule,
809
723
  ReactiveFormsModule,
810
724
  RouterOutlet,
@@ -823,13 +737,13 @@ class AngularModule {
823
737
  NgOptimizedImage,
824
738
  NgComponentOutlet,
825
739
  PreventDefaultDirective] });
826
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AngularModule, providers: [DatePipe], imports: [CommonModule,
740
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AngularModule, providers: [DatePipe], imports: [CommonModule,
827
741
  FormsModule,
828
742
  ReactiveFormsModule, CommonModule,
829
743
  FormsModule,
830
744
  ReactiveFormsModule] });
831
745
  }
832
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AngularModule, decorators: [{
746
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AngularModule, decorators: [{
833
747
  type: NgModule,
834
748
  args: [{
835
749
  imports: [
@@ -861,8 +775,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
861
775
  }] });
862
776
 
863
777
  class PrimeModule {
864
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PrimeModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
865
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: PrimeModule, exports: [AutoCompleteModule,
778
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: PrimeModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
779
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.5", ngImport: i0, type: PrimeModule, exports: [AutoCompleteModule,
866
780
  AvatarModule,
867
781
  ButtonModule,
868
782
  CardModule,
@@ -899,7 +813,7 @@ class PrimeModule {
899
813
  ToggleSwitchModule,
900
814
  TooltipModule,
901
815
  TreeTableModule] });
902
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PrimeModule, imports: [AutoCompleteModule,
816
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: PrimeModule, imports: [AutoCompleteModule,
903
817
  AvatarModule,
904
818
  ButtonModule,
905
819
  CardModule,
@@ -937,7 +851,7 @@ class PrimeModule {
937
851
  TooltipModule,
938
852
  TreeTableModule] });
939
853
  }
940
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: PrimeModule, decorators: [{
854
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: PrimeModule, decorators: [{
941
855
  type: NgModule,
942
856
  args: [{
943
857
  exports: [
@@ -1003,11 +917,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
1003
917
  *
1004
918
  * @example
1005
919
  * ```typescript
1006
- * // Define service
920
+ * // Define service with global apiBaseUrl
1007
921
  * @Injectable({ providedIn: 'root' })
1008
922
  * export class UserService extends ApiResourceService<UserDto, User> {
1009
- * constructor(http: HttpClient) {
1010
- * super('users', http);
923
+ * constructor() {
924
+ * super('auth/users', inject(HttpClient));
925
+ * }
926
+ * }
927
+ *
928
+ * // Define service with feature-specific baseUrl
929
+ * @Injectable({ providedIn: 'root' })
930
+ * export class FormService extends ApiResourceService<FormDto, Form> {
931
+ * constructor() {
932
+ * super('form', inject(HttpClient), 'formBuilder');
933
+ * // URL: services.formBuilder.baseUrl + '/form'
1011
934
  * }
1012
935
  * }
1013
936
  *
@@ -1028,7 +951,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
1028
951
  */
1029
952
  class ApiResourceService {
1030
953
  baseUrl;
1031
- loaderService = inject(ApiLoaderService);
1032
954
  injector = inject(Injector);
1033
955
  http;
1034
956
  moduleApiName;
@@ -1132,9 +1054,17 @@ class ApiResourceService {
1132
1054
  return false;
1133
1055
  return meta.hasMore ?? (meta.page + 1) * meta.pageSize < meta.total;
1134
1056
  }, ...(ngDevMode ? [{ debugName: "hasMore" }] : []));
1135
- constructor(moduleApiName, http) {
1057
+ /**
1058
+ * @param moduleApiName - The API resource path (e.g., 'form' for /form-builder/form)
1059
+ * @param http - HttpClient instance
1060
+ * @param serviceName - Optional service name for feature-specific base URL (e.g., 'formBuilder')
1061
+ */
1062
+ constructor(moduleApiName, http, serviceName) {
1136
1063
  this.moduleApiName = moduleApiName;
1137
- this.baseUrl = inject(APP_CONFIG).apiBaseUrl + '/' + moduleApiName;
1064
+ const config = inject(APP_CONFIG);
1065
+ // Use service-specific URL if provided, otherwise fallback to global apiBaseUrl
1066
+ const serviceBaseUrl = serviceName ? getServiceUrl(config, serviceName) : config.apiBaseUrl;
1067
+ this.baseUrl = `${serviceBaseUrl}/${moduleApiName}`;
1138
1068
  this.http = http;
1139
1069
  // Resource is now lazy-initialized, not created in constructor
1140
1070
  }
@@ -1398,10 +1328,10 @@ class BaseFormControl {
1398
1328
  this.onTouched();
1399
1329
  }
1400
1330
  }
1401
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: BaseFormControl, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1402
- 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 });
1331
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BaseFormControl, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1332
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.5", 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 });
1403
1333
  }
1404
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: BaseFormControl, decorators: [{
1334
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BaseFormControl, decorators: [{
1405
1335
  type: Directive
1406
1336
  }], 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"] }] } });
1407
1337
  /**
@@ -1428,30 +1358,29 @@ class IconComponent {
1428
1358
  icon = input.required(...(ngDevMode ? [{ debugName: "icon" }] : []));
1429
1359
  iconType = input(IconTypeEnum.PRIMENG_ICON, ...(ngDevMode ? [{ debugName: "iconType" }] : []));
1430
1360
  IconTypeEnum = IconTypeEnum; // Needed for template reference
1431
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: IconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1432
- 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: `
1361
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: IconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1362
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", 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: `
1433
1363
  @if (icon()) {
1434
1364
  @if (iconType() === IconTypeEnum.PRIMENG_ICON) {
1435
- <i [ngClass]="icon()"></i>
1365
+ <i [class]="icon()"></i>
1436
1366
  } @else if (iconType() === IconTypeEnum.IMAGE_FILE_LINK) {
1437
1367
  <img [alt]="icon()" [src]="icon()" />
1438
1368
  } @else {
1439
1369
  <i class="pi pi-question"></i>
1440
1370
  }
1441
1371
  }
1442
- `, 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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1372
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: IsEmptyImageDirective, selector: "img", inputs: ["src"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1443
1373
  }
1444
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: IconComponent, decorators: [{
1374
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: IconComponent, decorators: [{
1445
1375
  type: Component,
1446
1376
  args: [{
1447
1377
  selector: 'lib-icon',
1448
- standalone: true,
1449
1378
  imports: [AngularModule],
1450
1379
  changeDetection: ChangeDetectionStrategy.OnPush,
1451
1380
  template: `
1452
1381
  @if (icon()) {
1453
1382
  @if (iconType() === IconTypeEnum.PRIMENG_ICON) {
1454
- <i [ngClass]="icon()"></i>
1383
+ <i [class]="icon()"></i>
1455
1384
  } @else if (iconType() === IconTypeEnum.IMAGE_FILE_LINK) {
1456
1385
  <img [alt]="icon()" [src]="icon()" />
1457
1386
  } @else {
@@ -1462,6 +1391,40 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
1462
1391
  }]
1463
1392
  }], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: true }] }], iconType: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconType", required: false }] }] } });
1464
1393
 
1394
+ /**
1395
+ * Check if scroll has reached near bottom and calculate next page if available.
1396
+ * Returns next pagination if should load more, null otherwise.
1397
+ *
1398
+ * @example
1399
+ * ```typescript
1400
+ * onScroll(event: Event): void {
1401
+ * const nextPagination = checkScrollPagination(event, {
1402
+ * pagination: this.pagination(),
1403
+ * total: this.total(),
1404
+ * isLoading: this.isLoading(),
1405
+ * });
1406
+ * if (nextPagination) {
1407
+ * this.onPagination.emit(nextPagination);
1408
+ * }
1409
+ * }
1410
+ * ```
1411
+ */
1412
+ function checkScrollPagination(event, config) {
1413
+ const el = event.target;
1414
+ if (!(el instanceof HTMLElement))
1415
+ return null;
1416
+ const threshold = config.threshold ?? 50;
1417
+ const nearBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - threshold;
1418
+ if (!nearBottom || config.isLoading)
1419
+ return null;
1420
+ const { pagination, total } = config;
1421
+ const nextPage = pagination.currentPage + 1;
1422
+ const hasMore = nextPage * pagination.pageSize < (total ?? 0);
1423
+ if (!hasMore)
1424
+ return null;
1425
+ return { ...pagination, currentPage: nextPage };
1426
+ }
1427
+
1465
1428
  /**
1466
1429
  * Lazy-loading multi-select component with search, pagination, and select-all.
1467
1430
  *
@@ -1543,17 +1506,13 @@ class LazyMultiSelectComponent extends BaseFormControl {
1543
1506
  });
1544
1507
  }
1545
1508
  onScroll(event) {
1546
- const el = event.target;
1547
- if (!(el instanceof HTMLElement))
1548
- return;
1549
- const nearBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 50;
1550
- if (nearBottom && !this.isLoading()) {
1551
- const pag = this.pagination();
1552
- const nextPage = pag.currentPage + 1;
1553
- const hasMore = nextPage * pag.pageSize < (this.total() ?? 0);
1554
- if (hasMore) {
1555
- this.onPagination.emit({ ...pag, currentPage: nextPage });
1556
- }
1509
+ const nextPagination = checkScrollPagination(event, {
1510
+ pagination: this.pagination(),
1511
+ total: this.total(),
1512
+ isLoading: this.isLoading(),
1513
+ });
1514
+ if (nextPagination) {
1515
+ this.onPagination.emit(nextPagination);
1557
1516
  }
1558
1517
  }
1559
1518
  onSelectClick(event) {
@@ -1602,12 +1561,12 @@ class LazyMultiSelectComponent extends BaseFormControl {
1602
1561
  event.stopPropagation();
1603
1562
  this.value.set([]);
1604
1563
  }
1605
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: LazyMultiSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1606
- 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" }, providers: [provideValueAccessor(LazyMultiSelectComponent)], viewQueries: [{ 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)\" [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\n <span class=\"p-select-clear-icon\" (click)=\"clear($event)\">\n <i class=\"pi pi-times\"></i>\n </span>\n\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 class=\"p-multiselect-dropdown-icon p-icon\" aria-hidden=\"true\">\n <path 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\" fill=\"currentColor\" />\n </svg>\n </span>\n </div>\n\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\n binary=\"true\"\n [ngModel]=\"isSelectAll()\"\n [disabled]=\"disabled()\"\n (onChange)=\"changeSelectAll($event)\"\n />\n <input\n type=\"text\"\n pInputText\n class=\"w-full\"\n placeholder=\"Search...\"\n [ngModel]=\"searchTerm()\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"searchTerm.set($event)\"\n />\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)) {\n <li class=\"p-select-option flex flex-row gap-2 items-center\"\n [ngClass]=\"{ 'p-select-option-selected': isSelected(data) }\">\n <p-checkbox\n binary=\"true\"\n [ngModel]=\"isSelected(data)\"\n [disabled]=\"disabled()\"\n (onChange)=\"selectValue($event, data)\"\n />\n <span>{{ data.label }}</span>\n </li>\n }\n </ul>\n </div>\n </div>\n }\n</div>", styles: [".p-select-overlay{position:absolute;top:100%;left:0;right:0;z-index:var(--p-overlay-select-zindex, 1004);margin-top:var(--p-select-overlay-offset, 2px);background:var(--p-select-overlay-background, var(--p-surface-0));border:1px solid var(--p-select-overlay-border-color, var(--p-surface-200));border-radius:var(--p-select-overlay-border-radius, var(--p-border-radius));box-shadow:var(--p-select-overlay-shadow, var(--p-overlay-shadow))}.p-select-header{padding:.75rem;border-bottom:1px solid var(--p-surface-200);background:var(--p-surface-50)}:host-context(.p-dark) .p-select-header,.dark .p-select-header{border-color:var(--p-surface-700);background:var(--p-surface-800)}.p-select-list-container{max-height:10rem;overflow-y:auto}@media(min-width:640px){.p-select-list-container{max-height:12.5rem}}.p-select-list{margin:0;padding:.25rem 0;list-style:none}.p-select-option{padding:.5rem .75rem;cursor:pointer;transition:background-color .2s ease}.p-select-option:hover{background:var(--p-select-option-focus-background, var(--p-surface-100));color:var(--p-select-option-focus-color, var(--p-text-color))}:host-context(.p-dark) .p-select-option:hover,.dark .p-select-option:hover{background:var(--p-surface-700)}.p-select-option.p-select-option-selected{background:var(--p-select-option-selected-background, var(--p-primary-50));color:var(--p-select-option-selected-color, var(--p-primary-color))}:host-context(.p-dark) .p-select-option.p-select-option-selected,.dark .p-select-option.p-select-option-selected{background:var(--p-primary-900)}.p-select-option.p-select-option-selected:hover{background:var(--p-select-option-selected-focus-background, var(--p-primary-100));color:var(--p-select-option-selected-focus-color, var(--p-primary-color))}:host-context(.p-dark) .p-select-option.p-select-option-selected:hover,.dark .p-select-option.p-select-option-selected:hover{background:var(--p-primary-800)}.p-select-clear-icon{display:flex;align-items:center;padding:0 .5rem;color:var(--p-text-color-secondary);cursor:pointer;transition:color .2s ease}.p-select-clear-icon:hover{color:var(--p-text-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 });
1564
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LazyMultiSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1565
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", 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" }, providers: [provideValueAccessor(LazyMultiSelectComponent)], viewQueries: [{ 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)\" [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\n <span class=\"p-select-clear-icon\" (click)=\"clear($event)\">\n <i class=\"pi pi-times\"></i>\n </span>\n\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 class=\"p-multiselect-dropdown-icon p-icon\" aria-hidden=\"true\">\n <path 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\" fill=\"currentColor\" />\n </svg>\n </span>\n </div>\n\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\n binary=\"true\"\n [ngModel]=\"isSelectAll()\"\n [disabled]=\"disabled()\"\n (onChange)=\"changeSelectAll($event)\"\n />\n <input\n type=\"text\"\n pInputText\n class=\"w-full\"\n placeholder=\"Search...\"\n [ngModel]=\"searchTerm()\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"searchTerm.set($event)\"\n />\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)) {\n <li class=\"p-select-option flex flex-row gap-2 items-center\"\n [ngClass]=\"{ 'p-select-option-selected': isSelected(data) }\">\n <p-checkbox\n binary=\"true\"\n [ngModel]=\"isSelected(data)\"\n [disabled]=\"disabled()\"\n (onChange)=\"selectValue($event, data)\"\n />\n <span>{{ data.label }}</span>\n </li>\n }\n </ul>\n </div>\n </div>\n }\n</div>", styles: [".p-select-overlay{position:absolute;top:100%;left:0;right:0;z-index:var(--p-overlay-select-zindex, 1004);margin-top:var(--p-select-overlay-offset, 2px);background:var(--p-select-overlay-background, var(--p-surface-0));border:1px solid var(--p-select-overlay-border-color, var(--p-surface-200));border-radius:var(--p-select-overlay-border-radius, var(--p-border-radius));box-shadow:var(--p-select-overlay-shadow, var(--p-overlay-shadow))}.p-select-header{padding:.75rem;border-bottom:1px solid var(--p-surface-200);background:var(--p-surface-50)}:host-context(.p-dark) .p-select-header,.dark .p-select-header{border-color:var(--p-surface-700);background:var(--p-surface-800)}.p-select-list-container{max-height:10rem;overflow-y:auto}@media(min-width:640px){.p-select-list-container{max-height:12.5rem}}.p-select-list{margin:0;padding:.25rem 0;list-style:none}.p-select-option{padding:.5rem .75rem;cursor:pointer;transition:background-color .2s ease}.p-select-option:hover{background:var(--p-select-option-focus-background, var(--p-surface-100));color:var(--p-select-option-focus-color, var(--p-text-color))}:host-context(.p-dark) .p-select-option:hover,.dark .p-select-option:hover{background:var(--p-surface-700)}.p-select-option.p-select-option-selected{background:var(--p-select-option-selected-background, var(--p-primary-50));color:var(--p-select-option-selected-color, var(--p-primary-color))}:host-context(.p-dark) .p-select-option.p-select-option-selected,.dark .p-select-option.p-select-option-selected{background:var(--p-primary-900)}.p-select-option.p-select-option-selected:hover{background:var(--p-select-option-selected-focus-background, var(--p-primary-100));color:var(--p-select-option-selected-focus-color, var(--p-primary-color))}:host-context(.p-dark) .p-select-option.p-select-option-selected:hover,.dark .p-select-option.p-select-option-selected:hover{background:var(--p-primary-800)}.p-select-clear-icon{display:flex;align-items:center;padding:0 .5rem;color:var(--p-text-color-secondary);cursor:pointer;transition:color .2s ease}.p-select-clear-icon:hover{color:var(--p-text-color)}\n"], dependencies: [{ kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i1.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: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1607
1566
  }
1608
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: LazyMultiSelectComponent, decorators: [{
1567
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LazyMultiSelectComponent, decorators: [{
1609
1568
  type: Component,
1610
- args: [{ selector: 'lib-lazy-multi-select', standalone: true, imports: [PrimeModule, AngularModule], changeDetection: ChangeDetectionStrategy.OnPush, providers: [provideValueAccessor(LazyMultiSelectComponent)], template: "<div class=\"p-select w-full\" #pSelect (click)=\"onSelectClick($event)\" [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\n <span class=\"p-select-clear-icon\" (click)=\"clear($event)\">\n <i class=\"pi pi-times\"></i>\n </span>\n\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 class=\"p-multiselect-dropdown-icon p-icon\" aria-hidden=\"true\">\n <path 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\" fill=\"currentColor\" />\n </svg>\n </span>\n </div>\n\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\n binary=\"true\"\n [ngModel]=\"isSelectAll()\"\n [disabled]=\"disabled()\"\n (onChange)=\"changeSelectAll($event)\"\n />\n <input\n type=\"text\"\n pInputText\n class=\"w-full\"\n placeholder=\"Search...\"\n [ngModel]=\"searchTerm()\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"searchTerm.set($event)\"\n />\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)) {\n <li class=\"p-select-option flex flex-row gap-2 items-center\"\n [ngClass]=\"{ 'p-select-option-selected': isSelected(data) }\">\n <p-checkbox\n binary=\"true\"\n [ngModel]=\"isSelected(data)\"\n [disabled]=\"disabled()\"\n (onChange)=\"selectValue($event, data)\"\n />\n <span>{{ data.label }}</span>\n </li>\n }\n </ul>\n </div>\n </div>\n }\n</div>", styles: [".p-select-overlay{position:absolute;top:100%;left:0;right:0;z-index:var(--p-overlay-select-zindex, 1004);margin-top:var(--p-select-overlay-offset, 2px);background:var(--p-select-overlay-background, var(--p-surface-0));border:1px solid var(--p-select-overlay-border-color, var(--p-surface-200));border-radius:var(--p-select-overlay-border-radius, var(--p-border-radius));box-shadow:var(--p-select-overlay-shadow, var(--p-overlay-shadow))}.p-select-header{padding:.75rem;border-bottom:1px solid var(--p-surface-200);background:var(--p-surface-50)}:host-context(.p-dark) .p-select-header,.dark .p-select-header{border-color:var(--p-surface-700);background:var(--p-surface-800)}.p-select-list-container{max-height:10rem;overflow-y:auto}@media(min-width:640px){.p-select-list-container{max-height:12.5rem}}.p-select-list{margin:0;padding:.25rem 0;list-style:none}.p-select-option{padding:.5rem .75rem;cursor:pointer;transition:background-color .2s ease}.p-select-option:hover{background:var(--p-select-option-focus-background, var(--p-surface-100));color:var(--p-select-option-focus-color, var(--p-text-color))}:host-context(.p-dark) .p-select-option:hover,.dark .p-select-option:hover{background:var(--p-surface-700)}.p-select-option.p-select-option-selected{background:var(--p-select-option-selected-background, var(--p-primary-50));color:var(--p-select-option-selected-color, var(--p-primary-color))}:host-context(.p-dark) .p-select-option.p-select-option-selected,.dark .p-select-option.p-select-option-selected{background:var(--p-primary-900)}.p-select-option.p-select-option-selected:hover{background:var(--p-select-option-selected-focus-background, var(--p-primary-100));color:var(--p-select-option-selected-focus-color, var(--p-primary-color))}:host-context(.p-dark) .p-select-option.p-select-option-selected:hover,.dark .p-select-option.p-select-option-selected:hover{background:var(--p-primary-800)}.p-select-clear-icon{display:flex;align-items:center;padding:0 .5rem;color:var(--p-text-color-secondary);cursor:pointer;transition:color .2s ease}.p-select-clear-icon:hover{color:var(--p-text-color)}\n"] }]
1569
+ 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)\" [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\n <span class=\"p-select-clear-icon\" (click)=\"clear($event)\">\n <i class=\"pi pi-times\"></i>\n </span>\n\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 class=\"p-multiselect-dropdown-icon p-icon\" aria-hidden=\"true\">\n <path 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\" fill=\"currentColor\" />\n </svg>\n </span>\n </div>\n\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\n binary=\"true\"\n [ngModel]=\"isSelectAll()\"\n [disabled]=\"disabled()\"\n (onChange)=\"changeSelectAll($event)\"\n />\n <input\n type=\"text\"\n pInputText\n class=\"w-full\"\n placeholder=\"Search...\"\n [ngModel]=\"searchTerm()\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"searchTerm.set($event)\"\n />\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)) {\n <li class=\"p-select-option flex flex-row gap-2 items-center\"\n [ngClass]=\"{ 'p-select-option-selected': isSelected(data) }\">\n <p-checkbox\n binary=\"true\"\n [ngModel]=\"isSelected(data)\"\n [disabled]=\"disabled()\"\n (onChange)=\"selectValue($event, data)\"\n />\n <span>{{ data.label }}</span>\n </li>\n }\n </ul>\n </div>\n </div>\n }\n</div>", styles: [".p-select-overlay{position:absolute;top:100%;left:0;right:0;z-index:var(--p-overlay-select-zindex, 1004);margin-top:var(--p-select-overlay-offset, 2px);background:var(--p-select-overlay-background, var(--p-surface-0));border:1px solid var(--p-select-overlay-border-color, var(--p-surface-200));border-radius:var(--p-select-overlay-border-radius, var(--p-border-radius));box-shadow:var(--p-select-overlay-shadow, var(--p-overlay-shadow))}.p-select-header{padding:.75rem;border-bottom:1px solid var(--p-surface-200);background:var(--p-surface-50)}:host-context(.p-dark) .p-select-header,.dark .p-select-header{border-color:var(--p-surface-700);background:var(--p-surface-800)}.p-select-list-container{max-height:10rem;overflow-y:auto}@media(min-width:640px){.p-select-list-container{max-height:12.5rem}}.p-select-list{margin:0;padding:.25rem 0;list-style:none}.p-select-option{padding:.5rem .75rem;cursor:pointer;transition:background-color .2s ease}.p-select-option:hover{background:var(--p-select-option-focus-background, var(--p-surface-100));color:var(--p-select-option-focus-color, var(--p-text-color))}:host-context(.p-dark) .p-select-option:hover,.dark .p-select-option:hover{background:var(--p-surface-700)}.p-select-option.p-select-option-selected{background:var(--p-select-option-selected-background, var(--p-primary-50));color:var(--p-select-option-selected-color, var(--p-primary-color))}:host-context(.p-dark) .p-select-option.p-select-option-selected,.dark .p-select-option.p-select-option-selected{background:var(--p-primary-900)}.p-select-option.p-select-option-selected:hover{background:var(--p-select-option-selected-focus-background, var(--p-primary-100));color:var(--p-select-option-selected-focus-color, var(--p-primary-color))}:host-context(.p-dark) .p-select-option.p-select-option-selected:hover,.dark .p-select-option.p-select-option-selected:hover{background:var(--p-primary-800)}.p-select-clear-icon{display:flex;align-items:center;padding:0 .5rem;color:var(--p-text-color-secondary);cursor:pointer;transition:color .2s ease}.p-select-clear-icon:hover{color:var(--p-text-color)}\n"] }]
1611
1570
  }], ctorParameters: () => [], propDecorators: { pSelectRef: [{ type: i0.ViewChild, args: ['pSelect', { isSignal: true }] }], 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"] }] } });
1612
1571
 
1613
1572
  /**
@@ -1673,17 +1632,13 @@ class LazySelectComponent extends BaseFormControl {
1673
1632
  });
1674
1633
  }
1675
1634
  onScroll(event) {
1676
- const el = event.target;
1677
- if (!(el instanceof HTMLElement))
1678
- return;
1679
- const nearBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 50;
1680
- if (nearBottom && !this.isLoading()) {
1681
- const pag = this.pagination();
1682
- const nextPage = pag.currentPage + 1;
1683
- const hasMore = nextPage * pag.pageSize < (this.total() ?? 0);
1684
- if (hasMore) {
1685
- this.onPagination.emit({ ...pag, currentPage: nextPage });
1686
- }
1635
+ const nextPagination = checkScrollPagination(event, {
1636
+ pagination: this.pagination(),
1637
+ total: this.total(),
1638
+ isLoading: this.isLoading(),
1639
+ });
1640
+ if (nextPagination) {
1641
+ this.onPagination.emit(nextPagination);
1687
1642
  }
1688
1643
  }
1689
1644
  showPanel() {
@@ -1709,12 +1664,12 @@ class LazySelectComponent extends BaseFormControl {
1709
1664
  onBlur() {
1710
1665
  this.markAsTouched();
1711
1666
  }
1712
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: LazySelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1713
- 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 class=\"w-full\"\n [options]=\"selectDataList()\"\n [optionLabel]=\"optionLabel()\"\n [optionValue]=\"optionValue()\"\n [filter]=\"true\"\n [showClear]=\"true\"\n [placeholder]=\"placeHolder()\"\n [disabled]=\"disabled()\"\n [(ngModel)]=\"value\"\n appEditModeElementChanger\n [isEditMode]=\"isEditMode()\"\n (click)=\"showPanel()\"\n (onBlur)=\"onBlur()\"\n >\n <ng-template #filter let-filter>\n <input\n pInputText\n class=\"w-full\"\n [ngModel]=\"searchTerm()\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"searchTerm.set($event)\"\n />\n </ng-template>\n <ng-template #selectedItem let-selectedOption>\n {{ selectedOption[optionLabel()] }}\n </ng-template>\n <ng-template #item let-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 });
1667
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LazySelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1668
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.1.5", 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 class=\"w-full\"\n [options]=\"selectDataList()\"\n [optionLabel]=\"optionLabel()\"\n [optionValue]=\"optionValue()\"\n [filter]=\"true\"\n [showClear]=\"true\"\n [placeholder]=\"placeHolder()\"\n [disabled]=\"disabled()\"\n [(ngModel)]=\"value\"\n appEditModeElementChanger\n [isEditMode]=\"isEditMode()\"\n (click)=\"showPanel()\"\n (onBlur)=\"onBlur()\"\n >\n <ng-template #filter let-filter>\n <input\n pInputText\n class=\"w-full\"\n [ngModel]=\"searchTerm()\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"searchTerm.set($event)\"\n />\n </ng-template>\n <ng-template #selectedItem let-selectedOption>\n {{ selectedOption[optionLabel()] }}\n </ng-template>\n <ng-template #item let-item>\n {{ item[optionLabel()] }}\n </ng-template>\n </p-select>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.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$1.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 });
1714
1669
  }
1715
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: LazySelectComponent, decorators: [{
1670
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LazySelectComponent, decorators: [{
1716
1671
  type: Component,
1717
- args: [{ selector: 'lib-lazy-select', standalone: true, imports: [AngularModule, PrimeModule, EditModeElementChangerDirective], changeDetection: ChangeDetectionStrategy.OnPush, providers: [provideValueAccessor(LazySelectComponent)], template: "<div #scrollContainer class=\"lib-scroll-container\">\n <p-select\n class=\"w-full\"\n [options]=\"selectDataList()\"\n [optionLabel]=\"optionLabel()\"\n [optionValue]=\"optionValue()\"\n [filter]=\"true\"\n [showClear]=\"true\"\n [placeholder]=\"placeHolder()\"\n [disabled]=\"disabled()\"\n [(ngModel)]=\"value\"\n appEditModeElementChanger\n [isEditMode]=\"isEditMode()\"\n (click)=\"showPanel()\"\n (onBlur)=\"onBlur()\"\n >\n <ng-template #filter let-filter>\n <input\n pInputText\n class=\"w-full\"\n [ngModel]=\"searchTerm()\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"searchTerm.set($event)\"\n />\n </ng-template>\n <ng-template #selectedItem let-selectedOption>\n {{ selectedOption[optionLabel()] }}\n </ng-template>\n <ng-template #item let-item>\n {{ item[optionLabel()] }}\n </ng-template>\n </p-select>\n</div>\n" }]
1672
+ args: [{ selector: 'lib-lazy-select', imports: [AngularModule, PrimeModule, EditModeElementChangerDirective], changeDetection: ChangeDetectionStrategy.OnPush, providers: [provideValueAccessor(LazySelectComponent)], template: "<div #scrollContainer class=\"lib-scroll-container\">\n <p-select\n class=\"w-full\"\n [options]=\"selectDataList()\"\n [optionLabel]=\"optionLabel()\"\n [optionValue]=\"optionValue()\"\n [filter]=\"true\"\n [showClear]=\"true\"\n [placeholder]=\"placeHolder()\"\n [disabled]=\"disabled()\"\n [(ngModel)]=\"value\"\n appEditModeElementChanger\n [isEditMode]=\"isEditMode()\"\n (click)=\"showPanel()\"\n (onBlur)=\"onBlur()\"\n >\n <ng-template #filter let-filter>\n <input\n pInputText\n class=\"w-full\"\n [ngModel]=\"searchTerm()\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"searchTerm.set($event)\"\n />\n </ng-template>\n <ng-template #selectedItem let-selectedOption>\n {{ selectedOption[optionLabel()] }}\n </ng-template>\n <ng-template #item let-item>\n {{ item[optionLabel()] }}\n </ng-template>\n </p-select>\n</div>\n" }]
1718
1673
  }], ctorParameters: () => [], propDecorators: { scrollContainer: [{ type: i0.ViewChild, args: ['scrollContainer', { isSignal: true }] }], placeHolder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeHolder", required: false }] }], optionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionLabel", required: true }] }], optionValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionValue", required: true }] }], 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"] }] } });
1719
1674
 
1720
1675
  /**
@@ -1813,56 +1768,33 @@ const PROFILE_UPLOAD_PROVIDER = new InjectionToken('PROFILE_UPLOAD_PROVIDER');
1813
1768
  */
1814
1769
  const USER_LIST_PROVIDER = new InjectionToken('USER_LIST_PROVIDER');
1815
1770
 
1816
- const DEFAULT_PAGE_SIZE$2 = 20;
1771
+ const DEFAULT_PAGE_SIZE$1 = 20;
1817
1772
  /**
1818
- * User Select Component - Single user selection with lazy loading.
1819
- *
1820
- * Uses USER_PROVIDER internally by default, or accepts custom `loadUsers` function.
1773
+ * Base class for user selection components.
1774
+ * Provides shared user loading, pagination, and search functionality.
1821
1775
  *
1822
- * Features:
1823
- * - Search with debouncing (handled by lazy-select)
1824
- * - Infinite scroll pagination
1825
- * - Filter active users by default (configurable)
1826
- * - Supports additional filters via `additionalFilters` input
1827
- *
1828
- * @example
1829
- * ```html
1830
- * <!-- Simple usage - uses USER_PROVIDER internally -->
1831
- * <lib-user-select
1832
- * [(value)]="selectedUserId"
1833
- * [isEditMode]="true"
1834
- * />
1835
- *
1836
- * <!-- With custom loadUsers function -->
1837
- * <lib-user-select
1838
- * [(value)]="selectedUserId"
1839
- * [isEditMode]="true"
1840
- * [loadUsers]="customLoadUsers"
1841
- * />
1842
- * ```
1776
+ * Subclasses must implement:
1777
+ * - `setupValueEffect()` to track value changes and emit selection events
1843
1778
  */
1844
- class UserSelectComponent {
1779
+ class BaseUserSelectComponent {
1845
1780
  destroyRef = inject(DestroyRef);
1781
+ injector = inject(Injector);
1846
1782
  userProvider = inject(USER_PROVIDER);
1847
1783
  abortController = null;
1848
- // Optional: custom function to load users (uses USER_PROVIDER if not provided)
1849
- loadUsers = input(...(ngDevMode ? [undefined, { debugName: "loadUsers" }] : []));
1850
1784
  // Inputs
1785
+ loadUsers = input(...(ngDevMode ? [undefined, { debugName: "loadUsers" }] : []));
1851
1786
  placeHolder = input('Select User', ...(ngDevMode ? [{ debugName: "placeHolder" }] : []));
1852
1787
  isEditMode = input.required(...(ngDevMode ? [{ debugName: "isEditMode" }] : []));
1853
1788
  filterActive = input(true, ...(ngDevMode ? [{ debugName: "filterActive" }] : []));
1854
1789
  additionalFilters = input({}, ...(ngDevMode ? [{ debugName: "additionalFilters" }] : []));
1855
- pageSize = input(DEFAULT_PAGE_SIZE$2, ...(ngDevMode ? [{ debugName: "pageSize" }] : []));
1856
- // Two-way bound value
1857
- value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : []));
1790
+ pageSize = input(DEFAULT_PAGE_SIZE$1, ...(ngDevMode ? [{ debugName: "pageSize" }] : []));
1858
1791
  // Outputs
1859
- userSelected = output();
1860
1792
  onError = output();
1861
1793
  // Internal state
1862
1794
  isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
1863
1795
  users = signal([], ...(ngDevMode ? [{ debugName: "users" }] : []));
1864
1796
  total = signal(undefined, ...(ngDevMode ? [{ debugName: "total" }] : []));
1865
- pagination = signal({ pageSize: DEFAULT_PAGE_SIZE$2, currentPage: 0 }, ...(ngDevMode ? [{ debugName: "pagination" }] : []));
1797
+ pagination = signal({ pageSize: DEFAULT_PAGE_SIZE$1, currentPage: 0 }, ...(ngDevMode ? [{ debugName: "pagination" }] : []));
1866
1798
  searchTerm = signal('', ...(ngDevMode ? [{ debugName: "searchTerm" }] : []));
1867
1799
  // Computed dropdown data
1868
1800
  dropdownUsers = computed(() => this.users().map((user) => ({
@@ -1885,20 +1817,8 @@ class UserSelectComponent {
1885
1817
  afterNextRender(() => {
1886
1818
  this.fetchUsers();
1887
1819
  });
1888
- // Emit selected user when value changes
1889
- effect(() => {
1890
- const selectedId = this.value();
1891
- const users = this.users();
1892
- untracked(() => {
1893
- if (selectedId) {
1894
- const user = users.find((u) => u.id === selectedId);
1895
- this.userSelected.emit(user ?? null);
1896
- }
1897
- else {
1898
- this.userSelected.emit(null);
1899
- }
1900
- });
1901
- });
1820
+ // Setup value change tracking (implemented by subclass)
1821
+ this.setupValueEffect();
1902
1822
  }
1903
1823
  handleSearch(search) {
1904
1824
  this.searchTerm.set(search);
@@ -1910,6 +1830,12 @@ class UserSelectComponent {
1910
1830
  this.pagination.set(pagination);
1911
1831
  this.fetchUsers(true);
1912
1832
  }
1833
+ /** Reload users (useful when filters change externally) */
1834
+ reload() {
1835
+ this.pagination.update((p) => ({ ...p, currentPage: 0 }));
1836
+ this.users.set([]);
1837
+ this.fetchUsers();
1838
+ }
1913
1839
  async fetchUsers(append = false) {
1914
1840
  if (this.isLoading())
1915
1841
  return;
@@ -1967,14 +1893,63 @@ class UserSelectComponent {
1967
1893
  })),
1968
1894
  })));
1969
1895
  }
1970
- /** Reload users (useful when filters change externally) */
1971
- reload() {
1972
- this.pagination.update((p) => ({ ...p, currentPage: 0 }));
1973
- this.users.set([]);
1974
- this.fetchUsers();
1896
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BaseUserSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1897
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.5", type: BaseUserSelectComponent, isStandalone: true, 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 } }, outputs: { onError: "onError" }, ngImport: i0 });
1898
+ }
1899
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BaseUserSelectComponent, decorators: [{
1900
+ type: Directive
1901
+ }], 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 }] }], onError: [{ type: i0.Output, args: ["onError"] }] } });
1902
+
1903
+ /**
1904
+ * User Select Component - Single user selection with lazy loading.
1905
+ *
1906
+ * Uses USER_PROVIDER internally by default, or accepts custom `loadUsers` function.
1907
+ *
1908
+ * Features:
1909
+ * - Search with debouncing (handled by lazy-select)
1910
+ * - Infinite scroll pagination
1911
+ * - Filter active users by default (configurable)
1912
+ * - Supports additional filters via `additionalFilters` input
1913
+ *
1914
+ * @example
1915
+ * ```html
1916
+ * <!-- Simple usage - uses USER_PROVIDER internally -->
1917
+ * <lib-user-select
1918
+ * [(value)]="selectedUserId"
1919
+ * [isEditMode]="true"
1920
+ * />
1921
+ *
1922
+ * <!-- With custom loadUsers function -->
1923
+ * <lib-user-select
1924
+ * [(value)]="selectedUserId"
1925
+ * [isEditMode]="true"
1926
+ * [loadUsers]="customLoadUsers"
1927
+ * />
1928
+ * ```
1929
+ */
1930
+ class UserSelectComponent extends BaseUserSelectComponent {
1931
+ // Two-way bound value
1932
+ value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : []));
1933
+ // Outputs
1934
+ userSelected = output();
1935
+ setupValueEffect() {
1936
+ // Emit selected user when value changes
1937
+ effect(() => {
1938
+ const selectedId = this.value();
1939
+ const users = this.users();
1940
+ untracked(() => {
1941
+ if (selectedId) {
1942
+ const user = users.find((u) => u.id === selectedId);
1943
+ this.userSelected.emit(user ?? null);
1944
+ }
1945
+ else {
1946
+ this.userSelected.emit(null);
1947
+ }
1948
+ });
1949
+ });
1975
1950
  }
1976
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UserSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1977
- 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: `
1951
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserSelectComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
1952
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.5", type: UserSelectComponent, isStandalone: true, selector: "lib-user-select", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", userSelected: "userSelected" }, usesInheritance: true, ngImport: i0, template: `
1978
1953
  <lib-lazy-select
1979
1954
  [(value)]="value"
1980
1955
  [placeHolder]="placeHolder()"
@@ -1990,11 +1965,10 @@ class UserSelectComponent {
1990
1965
  />
1991
1966
  `, 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 });
1992
1967
  }
1993
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UserSelectComponent, decorators: [{
1968
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserSelectComponent, decorators: [{
1994
1969
  type: Component,
1995
1970
  args: [{
1996
1971
  selector: 'lib-user-select',
1997
- standalone: true,
1998
1972
  imports: [AngularModule, PrimeModule, LazySelectComponent],
1999
1973
  template: `
2000
1974
  <lib-lazy-select
@@ -2013,9 +1987,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
2013
1987
  `,
2014
1988
  changeDetection: ChangeDetectionStrategy.OnPush,
2015
1989
  }]
2016
- }], 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"] }] } });
1990
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], userSelected: [{ type: i0.Output, args: ["userSelected"] }] } });
2017
1991
 
2018
- const DEFAULT_PAGE_SIZE$1 = 20;
2019
1992
  /**
2020
1993
  * User Multi-Select Component - Multiple user selection with lazy loading.
2021
1994
  *
@@ -2044,50 +2017,12 @@ const DEFAULT_PAGE_SIZE$1 = 20;
2044
2017
  * />
2045
2018
  * ```
2046
2019
  */
2047
- class UserMultiSelectComponent {
2048
- destroyRef = inject(DestroyRef);
2049
- userProvider = inject(USER_PROVIDER);
2050
- abortController = null;
2051
- // Optional: custom function to load users (uses USER_PROVIDER if not provided)
2052
- loadUsers = input(...(ngDevMode ? [undefined, { debugName: "loadUsers" }] : []));
2053
- // Inputs
2054
- placeHolder = input('Select Users', ...(ngDevMode ? [{ debugName: "placeHolder" }] : []));
2055
- isEditMode = input.required(...(ngDevMode ? [{ debugName: "isEditMode" }] : []));
2056
- filterActive = input(true, ...(ngDevMode ? [{ debugName: "filterActive" }] : []));
2057
- additionalFilters = input({}, ...(ngDevMode ? [{ debugName: "additionalFilters" }] : []));
2058
- pageSize = input(DEFAULT_PAGE_SIZE$1, ...(ngDevMode ? [{ debugName: "pageSize" }] : []));
2020
+ class UserMultiSelectComponent extends BaseUserSelectComponent {
2059
2021
  // Two-way bound value
2060
2022
  value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : []));
2061
2023
  // Outputs
2062
2024
  usersSelected = output();
2063
- onError = output();
2064
- // Internal state
2065
- isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
2066
- users = signal([], ...(ngDevMode ? [{ debugName: "users" }] : []));
2067
- total = signal(undefined, ...(ngDevMode ? [{ debugName: "total" }] : []));
2068
- pagination = signal({ pageSize: DEFAULT_PAGE_SIZE$1, currentPage: 0 }, ...(ngDevMode ? [{ debugName: "pagination" }] : []));
2069
- searchTerm = signal('', ...(ngDevMode ? [{ debugName: "searchTerm" }] : []));
2070
- // Computed dropdown data
2071
- dropdownUsers = computed(() => this.users().map((user) => ({
2072
- label: user.name || user.email,
2073
- value: user.id,
2074
- })), ...(ngDevMode ? [{ debugName: "dropdownUsers" }] : []));
2075
- constructor() {
2076
- // Cleanup on destroy
2077
- this.destroyRef.onDestroy(() => {
2078
- this.abortController?.abort();
2079
- });
2080
- // Update page size from input
2081
- effect(() => {
2082
- const size = this.pageSize();
2083
- untracked(() => {
2084
- this.pagination.update((p) => ({ ...p, pageSize: size }));
2085
- });
2086
- });
2087
- // Load initial users after render
2088
- afterNextRender(() => {
2089
- this.fetchUsers();
2090
- });
2025
+ setupValueEffect() {
2091
2026
  // Emit selected users when value changes
2092
2027
  effect(() => {
2093
2028
  const selectedIds = this.value() ?? [];
@@ -2098,81 +2033,8 @@ class UserMultiSelectComponent {
2098
2033
  });
2099
2034
  });
2100
2035
  }
2101
- handleSearch(search) {
2102
- this.searchTerm.set(search);
2103
- this.pagination.update((p) => ({ ...p, currentPage: 0 }));
2104
- this.users.set([]);
2105
- this.fetchUsers();
2106
- }
2107
- handlePagination(pagination) {
2108
- this.pagination.set(pagination);
2109
- this.fetchUsers(true);
2110
- }
2111
- async fetchUsers(append = false) {
2112
- if (this.isLoading())
2113
- return;
2114
- // Cancel previous request
2115
- this.abortController?.abort();
2116
- this.abortController = new AbortController();
2117
- this.isLoading.set(true);
2118
- try {
2119
- const pag = this.pagination();
2120
- const filter = {
2121
- page: pag.currentPage,
2122
- pageSize: pag.pageSize,
2123
- search: this.searchTerm(),
2124
- ...this.additionalFilters(),
2125
- };
2126
- // Use custom loadUsers if provided, otherwise use USER_PROVIDER
2127
- const customLoadUsers = this.loadUsers();
2128
- const response = await firstValueFrom(customLoadUsers
2129
- ? customLoadUsers(filter)
2130
- : this.loadUsersFromProvider(filter));
2131
- if (response.success && response.data) {
2132
- if (append) {
2133
- this.users.update((current) => [...current, ...response.data]);
2134
- }
2135
- else {
2136
- this.users.set(response.data);
2137
- }
2138
- this.total.set(response.meta?.total);
2139
- }
2140
- }
2141
- catch (error) {
2142
- if (error.name !== 'AbortError') {
2143
- this.onError.emit(error);
2144
- }
2145
- }
2146
- finally {
2147
- this.isLoading.set(false);
2148
- }
2149
- }
2150
- /** Load users from USER_PROVIDER with active filter */
2151
- loadUsersFromProvider(filter) {
2152
- return this.userProvider
2153
- .getUsers({
2154
- page: filter.page,
2155
- pageSize: filter.pageSize,
2156
- search: filter.search,
2157
- isActive: this.filterActive() ? true : undefined,
2158
- })
2159
- .pipe(map$1((res) => ({
2160
- ...res,
2161
- data: res.data?.map((u) => ({
2162
- id: u.id,
2163
- name: u.name,
2164
- email: u.email,
2165
- })),
2166
- })));
2167
- }
2168
- /** Reload users (useful when filters change externally) */
2169
- reload() {
2170
- this.pagination.update((p) => ({ ...p, currentPage: 0 }));
2171
- this.users.set([]);
2172
- this.fetchUsers();
2173
- }
2174
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UserMultiSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2175
- 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: `
2036
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserMultiSelectComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
2037
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.5", type: UserMultiSelectComponent, isStandalone: true, selector: "lib-user-multi-select", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", usersSelected: "usersSelected" }, usesInheritance: true, ngImport: i0, template: `
2176
2038
  <lib-lazy-multi-select
2177
2039
  [(value)]="value"
2178
2040
  [placeHolder]="placeHolder()"
@@ -2186,11 +2048,10 @@ class UserMultiSelectComponent {
2186
2048
  />
2187
2049
  `, 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 });
2188
2050
  }
2189
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UserMultiSelectComponent, decorators: [{
2051
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserMultiSelectComponent, decorators: [{
2190
2052
  type: Component,
2191
2053
  args: [{
2192
2054
  selector: 'lib-user-multi-select',
2193
- standalone: true,
2194
2055
  imports: [AngularModule, PrimeModule, LazyMultiSelectComponent],
2195
2056
  template: `
2196
2057
  <lib-lazy-multi-select
@@ -2207,7 +2068,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
2207
2068
  `,
2208
2069
  changeDetection: ChangeDetectionStrategy.OnPush,
2209
2070
  }]
2210
- }], 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"] }] } });
2071
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], usersSelected: [{ type: i0.Output, args: ["usersSelected"] }] } });
2211
2072
 
2212
2073
  /**
2213
2074
  * File Uploader Component - Drag & drop file upload with type filtering.
@@ -2377,8 +2238,8 @@ class FileUploaderComponent {
2377
2238
  const mb = kb / 1024;
2378
2239
  return `${mb.toFixed(1)} MB`;
2379
2240
  }
2380
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileUploaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2381
- 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: `
2241
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FileUploaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2242
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", 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: `
2382
2243
  <div
2383
2244
  class="w-full"
2384
2245
  [class.opacity-60]="disabled()"
@@ -2454,11 +2315,11 @@ class FileUploaderComponent {
2454
2315
  </div>
2455
2316
  }
2456
2317
  </div>
2457
- `, isInline: true, styles: [".upload-zone{border-color:var(--p-surface-300);background:var(--p-surface-50)}.upload-zone:hover:not(.cursor-not-allowed){border-color:var(--p-primary-color);background:var(--p-surface-100)}.upload-zone.drag-over{border-color:var(--p-primary-color);background:var(--p-primary-50)}:host-context(.p-dark) .upload-zone,.dark .upload-zone{border-color:var(--p-surface-600);background:var(--p-surface-800)}:host-context(.p-dark) .upload-zone:hover:not(.cursor-not-allowed),.dark .upload-zone:hover:not(.cursor-not-allowed){border-color:var(--p-primary-color);background:var(--p-surface-700)}:host-context(.p-dark) .upload-zone.drag-over,.dark .upload-zone.drag-over{border-color:var(--p-primary-color);background:var(--p-primary-900)}.file-preview-item{border:1px solid var(--p-surface-200);background:var(--p-surface-0)}:host-context(.p-dark) .file-preview-item,.dark .file-preview-item{border-color:var(--p-surface-600);background:var(--p-surface-800)}\n"], dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i1$3.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { 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 });
2318
+ `, isInline: true, styles: [".upload-zone{border-color:var(--p-surface-300);background:var(--p-surface-50)}.upload-zone:hover:not(.cursor-not-allowed){border-color:var(--p-primary-color);background:var(--p-surface-100)}.upload-zone.drag-over{border-color:var(--p-primary-color);background:var(--p-primary-50)}:host-context(.p-dark) .upload-zone,.dark .upload-zone{border-color:var(--p-surface-600);background:var(--p-surface-800)}:host-context(.p-dark) .upload-zone:hover:not(.cursor-not-allowed),.dark .upload-zone:hover:not(.cursor-not-allowed){border-color:var(--p-primary-color);background:var(--p-surface-700)}:host-context(.p-dark) .upload-zone.drag-over,.dark .upload-zone.drag-over{border-color:var(--p-primary-color);background:var(--p-primary-900)}.file-preview-item{border:1px solid var(--p-surface-200);background:var(--p-surface-0)}:host-context(.p-dark) .file-preview-item,.dark .file-preview-item{border-color:var(--p-surface-600);background:var(--p-surface-800)}\n"], dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i1$2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { 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 });
2458
2319
  }
2459
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileUploaderComponent, decorators: [{
2320
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FileUploaderComponent, decorators: [{
2460
2321
  type: Component,
2461
- args: [{ selector: 'lib-file-uploader', standalone: true, imports: [AngularModule, PrimeModule], template: `
2322
+ args: [{ selector: 'lib-file-uploader', imports: [AngularModule, PrimeModule], template: `
2462
2323
  <div
2463
2324
  class="w-full"
2464
2325
  [class.opacity-60]="disabled()"
@@ -2649,16 +2510,14 @@ class FileSelectorDialogComponent {
2649
2510
  }, 500);
2650
2511
  }
2651
2512
  onScroll(event) {
2652
- const el = event.target;
2653
- const nearBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 50;
2654
- if (nearBottom && !this.isLoading()) {
2655
- const pag = this.pagination();
2656
- const nextPage = pag.currentPage + 1;
2657
- const hasMore = nextPage * pag.pageSize < (this.total() ?? 0);
2658
- if (hasMore) {
2659
- this.pagination.update((p) => ({ ...p, currentPage: nextPage }));
2660
- this.fetchFiles(true);
2661
- }
2513
+ const nextPagination = checkScrollPagination(event, {
2514
+ pagination: this.pagination(),
2515
+ total: this.total(),
2516
+ isLoading: this.isLoading(),
2517
+ });
2518
+ if (nextPagination) {
2519
+ this.pagination.set(nextPagination);
2520
+ this.fetchFiles(true);
2662
2521
  }
2663
2522
  }
2664
2523
  toggleSelection(file) {
@@ -2760,8 +2619,8 @@ class FileSelectorDialogComponent {
2760
2619
  this.isLoading.set(false);
2761
2620
  }
2762
2621
  }
2763
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileSelectorDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2764
- 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: `
2622
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FileSelectorDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2623
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", 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: `
2765
2624
  <p-dialog
2766
2625
  [header]="header()"
2767
2626
  [(visible)]="visible"
@@ -2868,11 +2727,11 @@ class FileSelectorDialogComponent {
2868
2727
  </div>
2869
2728
  </ng-template>
2870
2729
  </p-dialog>
2871
- `, isInline: true, styles: [".file-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:.75rem;max-height:300px;overflow-y:auto;padding:.5rem}@media(min-width:480px){.file-grid{grid-template-columns:repeat(3,1fr);max-height:350px}}@media(min-width:640px){.file-grid{grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:1rem;max-height:400px}}.file-card{border:2px solid var(--p-surface-300);border-radius:var(--p-border-radius);cursor:pointer;transition:all .2s ease;overflow:hidden;background:var(--p-surface-0)}:host-context(.p-dark) .file-card,.dark .file-card{border-color:var(--p-surface-600);background:var(--p-surface-800)}.file-card:hover:not(.disabled){border-color:var(--p-primary-color);transform:translateY(-2px);box-shadow:var(--p-overlay-shadow)}.file-card.selected{border-color:var(--p-primary-color);background:var(--p-primary-50)}:host-context(.p-dark) .file-card.selected,.dark .file-card.selected{background:var(--p-primary-900)}.file-card.disabled{opacity:.5;cursor:not-allowed}.file-preview{position:relative;height:80px;display:flex;align-items:center;justify-content:center;background:var(--p-surface-100)}@media(min-width:640px){.file-preview{height:100px}}:host-context(.p-dark) .file-preview,.dark .file-preview{background:var(--p-surface-700)}.selected-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(var(--p-primary-500-rgb, 59, 130, 246),.3)}.selected-overlay i{color:var(--p-primary-color);background:var(--p-surface-0);border-radius:50%;padding:.5rem}:host-context(.p-dark) .selected-overlay i,.dark .selected-overlay i{background:var(--p-surface-900)}\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: 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: i4.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 });
2730
+ `, isInline: true, styles: [".file-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:.75rem;max-height:300px;overflow-y:auto;padding:.5rem}@media(min-width:480px){.file-grid{grid-template-columns:repeat(3,1fr);max-height:350px}}@media(min-width:640px){.file-grid{grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:1rem;max-height:400px}}.file-card{border:2px solid var(--p-surface-300);border-radius:var(--p-border-radius);cursor:pointer;transition:all .2s ease;overflow:hidden;background:var(--p-surface-0)}:host-context(.p-dark) .file-card,.dark .file-card{border-color:var(--p-surface-600);background:var(--p-surface-800)}.file-card:hover:not(.disabled){border-color:var(--p-primary-color);transform:translateY(-2px);box-shadow:var(--p-overlay-shadow)}.file-card.selected{border-color:var(--p-primary-color);background:var(--p-primary-50)}:host-context(.p-dark) .file-card.selected,.dark .file-card.selected{background:var(--p-primary-900)}.file-card.disabled{opacity:.5;cursor:not-allowed}.file-preview{position:relative;height:80px;display:flex;align-items:center;justify-content:center;background:var(--p-surface-100)}@media(min-width:640px){.file-preview{height:100px}}:host-context(.p-dark) .file-preview,.dark .file-preview{background:var(--p-surface-700)}.selected-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(var(--p-primary-500-rgb, 59, 130, 246),.3)}.selected-overlay i{color:var(--p-primary-color);background:var(--p-surface-0);border-radius:50%;padding:.5rem}:host-context(.p-dark) .selected-overlay i,.dark .selected-overlay i{background:var(--p-surface-900)}\n"], dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.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: i1$2.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: i4.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 });
2872
2731
  }
2873
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileSelectorDialogComponent, decorators: [{
2732
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: FileSelectorDialogComponent, decorators: [{
2874
2733
  type: Component,
2875
- args: [{ selector: 'lib-file-selector-dialog', standalone: true, imports: [AngularModule, PrimeModule], template: `
2734
+ args: [{ selector: 'lib-file-selector-dialog', imports: [AngularModule, PrimeModule], template: `
2876
2735
  <p-dialog
2877
2736
  [header]="header()"
2878
2737
  [(visible)]="visible"
@@ -2982,129 +2841,75 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
2982
2841
  `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".file-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:.75rem;max-height:300px;overflow-y:auto;padding:.5rem}@media(min-width:480px){.file-grid{grid-template-columns:repeat(3,1fr);max-height:350px}}@media(min-width:640px){.file-grid{grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:1rem;max-height:400px}}.file-card{border:2px solid var(--p-surface-300);border-radius:var(--p-border-radius);cursor:pointer;transition:all .2s ease;overflow:hidden;background:var(--p-surface-0)}:host-context(.p-dark) .file-card,.dark .file-card{border-color:var(--p-surface-600);background:var(--p-surface-800)}.file-card:hover:not(.disabled){border-color:var(--p-primary-color);transform:translateY(-2px);box-shadow:var(--p-overlay-shadow)}.file-card.selected{border-color:var(--p-primary-color);background:var(--p-primary-50)}:host-context(.p-dark) .file-card.selected,.dark .file-card.selected{background:var(--p-primary-900)}.file-card.disabled{opacity:.5;cursor:not-allowed}.file-preview{position:relative;height:80px;display:flex;align-items:center;justify-content:center;background:var(--p-surface-100)}@media(min-width:640px){.file-preview{height:100px}}:host-context(.p-dark) .file-preview,.dark .file-preview{background:var(--p-surface-700)}.selected-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(var(--p-primary-500-rgb, 59, 130, 246),.3)}.selected-overlay i{color:var(--p-primary-color);background:var(--p-surface-0);border-radius:50%;padding:.5rem}:host-context(.p-dark) .selected-overlay i,.dark .selected-overlay i{background:var(--p-surface-900)}\n"] }]
2983
2842
  }], 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"] }] } });
2984
2843
 
2985
- /** Log only in dev mode */
2986
- const devLog = (message) => {
2987
- if (isDevMode())
2988
- console.log(message);
2989
- };
2990
- /**
2991
- * Permission Guard
2992
- *
2993
- * Route-level guard for permission-based access control.
2994
- * Validates permissions before allowing navigation.
2995
- *
2996
- * Features:
2997
- * - Single permission check
2998
- * - Complex ILogicNode logic trees
2999
- * - Configurable redirect URL
3000
- * - Debug logging for denied access
3001
- *
3002
- * @example
3003
- * ```typescript
3004
- * // Simple permission check
3005
- * { path: 'users', canActivate: [permissionGuard('user.view')] }
3006
- *
3007
- * // Complex logic
3008
- * { path: 'admin', canActivate: [permissionGuard({
3009
- * id: 'root',
3010
- * type: 'group',
3011
- * operator: 'AND',
3012
- * children: [
3013
- * { id: '1', type: 'action', actionId: 'admin.view' },
3014
- * { id: '2', type: 'action', actionId: 'admin.manage' }
3015
- * ]
3016
- * })] }
3017
- *
3018
- * // With custom redirect
3019
- * { path: 'users', canActivate: [permissionGuard('user.view', '/access-denied')] }
3020
- * ```
3021
- */
3022
- function permissionGuard(permission, redirectTo = '/') {
2844
+ function createGuard(guardName, redirectTo, evaluate, getDenialMessage) {
3023
2845
  return () => {
3024
2846
  const permissionValidator = inject(PermissionValidatorService);
3025
2847
  const router = inject(Router);
3026
- // Check if permissions are loaded
3027
2848
  if (!permissionValidator.isPermissionsLoaded()) {
3028
- devLog('[permissionGuard] Permissions not loaded, denying access to route');
2849
+ if (isDevMode()) {
2850
+ console.log(`[${guardName}] Permissions not loaded, denying access`);
2851
+ }
3029
2852
  return router.createUrlTree([redirectTo]);
3030
2853
  }
3031
- const userPermissions = permissionValidator.permissions();
3032
- const hasPermission = evaluatePermission(permission, userPermissions);
3033
- if (!hasPermission) {
3034
- const permissionCode = typeof permission === 'string' ? permission : 'complex-logic';
3035
- devLog(`[permissionGuard] Access denied - missing permission: ${permissionCode}`);
2854
+ if (!evaluate(permissionValidator.permissions())) {
2855
+ if (isDevMode()) {
2856
+ console.log(`[${guardName}] ${getDenialMessage()}`);
2857
+ }
3036
2858
  return router.createUrlTree([redirectTo]);
3037
2859
  }
3038
2860
  return true;
3039
2861
  };
3040
2862
  }
3041
2863
  /**
3042
- * Any Permission Guard (OR logic)
2864
+ * Permission Guard - Single permission or ILogicNode check.
3043
2865
  *
3044
- * Allows access if user has ANY of the specified permissions.
2866
+ * @example
2867
+ * ```typescript
2868
+ * { path: 'users', canActivate: [permissionGuard('user.view')] }
2869
+ * { path: 'admin', canActivate: [permissionGuard(logicNode, '/access-denied')] }
2870
+ * ```
2871
+ */
2872
+ function permissionGuard(permission, redirectTo = '/') {
2873
+ const code = typeof permission === 'string' ? permission : 'complex-logic';
2874
+ return createGuard('permissionGuard', redirectTo, (perms) => evaluatePermission(permission, perms), () => `Access denied - missing: ${code}`);
2875
+ }
2876
+ /**
2877
+ * Any Permission Guard (OR logic) - Access if user has ANY permission.
3045
2878
  *
3046
2879
  * @example
3047
2880
  * ```typescript
3048
- * // Allow if user has view OR create permission
3049
2881
  * { path: 'users', canActivate: [anyPermissionGuard(['user.view', 'user.create'])] }
3050
2882
  * ```
3051
2883
  */
3052
2884
  function anyPermissionGuard(permissions, redirectTo = '/') {
3053
- return () => {
3054
- const permissionValidator = inject(PermissionValidatorService);
3055
- const router = inject(Router);
3056
- // Validate permissions array
3057
- if (!permissions || permissions.length === 0) {
3058
- devLog('[anyPermissionGuard] Empty permissions array provided, denying access');
3059
- return router.createUrlTree([redirectTo]);
3060
- }
3061
- // Check if permissions are loaded
3062
- if (!permissionValidator.isPermissionsLoaded()) {
3063
- devLog('[anyPermissionGuard] Permissions not loaded, denying access to route');
3064
- return router.createUrlTree([redirectTo]);
3065
- }
3066
- const userPermissions = permissionValidator.permissions();
3067
- const hasPermission = hasAnyPermission(permissions, userPermissions);
3068
- if (!hasPermission) {
3069
- devLog(`[anyPermissionGuard] Access denied - missing any of: ${permissions.join(', ')}`);
3070
- return router.createUrlTree([redirectTo]);
3071
- }
3072
- return true;
3073
- };
2885
+ if (!permissions?.length) {
2886
+ return () => {
2887
+ if (isDevMode()) {
2888
+ console.log('[anyPermissionGuard] Empty permissions array, denying');
2889
+ }
2890
+ return inject(Router).createUrlTree([redirectTo]);
2891
+ };
2892
+ }
2893
+ return createGuard('anyPermissionGuard', redirectTo, (perms) => hasAnyPermission(permissions, perms), () => `Access denied - missing any of: ${permissions.join(', ')}`);
3074
2894
  }
3075
2895
  /**
3076
- * All Permissions Guard (AND logic)
3077
- *
3078
- * Allows access only if user has ALL of the specified permissions.
2896
+ * All Permissions Guard (AND logic) - Access only if user has ALL permissions.
3079
2897
  *
3080
2898
  * @example
3081
2899
  * ```typescript
3082
- * // Allow only if user has BOTH view AND create permissions
3083
2900
  * { path: 'admin', canActivate: [allPermissionsGuard(['admin.view', 'admin.manage'])] }
3084
2901
  * ```
3085
2902
  */
3086
2903
  function allPermissionsGuard(permissions, redirectTo = '/') {
3087
- return () => {
3088
- const permissionValidator = inject(PermissionValidatorService);
3089
- const router = inject(Router);
3090
- // Validate permissions array
3091
- if (!permissions || permissions.length === 0) {
3092
- devLog('[allPermissionsGuard] Empty permissions array provided, denying access');
3093
- return router.createUrlTree([redirectTo]);
3094
- }
3095
- // Check if permissions are loaded
3096
- if (!permissionValidator.isPermissionsLoaded()) {
3097
- devLog('[allPermissionsGuard] Permissions not loaded, denying access to route');
3098
- return router.createUrlTree([redirectTo]);
3099
- }
3100
- const userPermissions = permissionValidator.permissions();
3101
- const hasPermission = hasAllPermissions(permissions, userPermissions);
3102
- if (!hasPermission) {
3103
- devLog(`[allPermissionsGuard] Access denied - missing all of: ${permissions.join(', ')}`);
3104
- return router.createUrlTree([redirectTo]);
3105
- }
3106
- return true;
3107
- };
2904
+ if (!permissions?.length) {
2905
+ return () => {
2906
+ if (isDevMode()) {
2907
+ console.log('[allPermissionsGuard] Empty permissions array, denying');
2908
+ }
2909
+ return inject(Router).createUrlTree([redirectTo]);
2910
+ };
2911
+ }
2912
+ return createGuard('allPermissionsGuard', redirectTo, (perms) => hasAllPermissions(permissions, perms), () => `Access denied - missing all of: ${permissions.join(', ')}`);
3108
2913
  }
3109
2914
 
3110
2915
  /**
@@ -3287,10 +3092,10 @@ class BaseFormPage {
3287
3092
  detail,
3288
3093
  });
3289
3094
  }
3290
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: BaseFormPage, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3291
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.3", type: BaseFormPage, isStandalone: true, ngImport: i0 });
3095
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BaseFormPage, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3096
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.5", type: BaseFormPage, isStandalone: true, ngImport: i0 });
3292
3097
  }
3293
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: BaseFormPage, decorators: [{
3098
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BaseFormPage, decorators: [{
3294
3099
  type: Directive
3295
3100
  }], ctorParameters: () => [] });
3296
3101
 
@@ -3451,10 +3256,10 @@ class BaseListPage {
3451
3256
  navigateTo(path) {
3452
3257
  this.router.navigate(path);
3453
3258
  }
3454
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: BaseListPage, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3455
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.3", type: BaseListPage, isStandalone: true, ngImport: i0 });
3259
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BaseListPage, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3260
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.5", type: BaseListPage, isStandalone: true, ngImport: i0 });
3456
3261
  }
3457
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: BaseListPage, decorators: [{
3262
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BaseListPage, decorators: [{
3458
3263
  type: Directive
3459
3264
  }] });
3460
3265
 
@@ -3464,5 +3269,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
3464
3269
  * Generated bundle index. Do not edit.
3465
3270
  */
3466
3271
 
3467
- export { ACTION_PERMISSIONS, AUTH_STATE_PROVIDER, AngularModule, ApiResourceService, ApiResourceService as ApiService, BRANCH_PERMISSIONS, BaseFormControl, BaseFormPage, BaseListPage, COMPANY_ACTION_PERMISSIONS, COMPANY_API_PROVIDER, COMPANY_PERMISSIONS, ContactTypeEnum, CookieService, EMAIL_CONFIG_PERMISSIONS, EMAIL_TEMPLATE_PERMISSIONS, EditModeElementChangerDirective, FILE_PERMISSIONS, FILE_TYPE_FILTERS, FOLDER_PERMISSIONS, FORM_PERMISSIONS, FileSelectorDialogComponent, FileUploaderComponent, FileUrlService, HasPermissionDirective, IconComponent, IconTypeEnum, IsEmptyImageDirective, LazyMultiSelectComponent, LazySelectComponent, PERMISSIONS, PROFILE_PERMISSION_PROVIDER, PROFILE_UPLOAD_PROVIDER, PermissionValidatorService, PlatformService, PreventDefaultDirective, PrimeModule, ROLE_ACTION_PERMISSIONS, ROLE_PERMISSIONS, STORAGE_CONFIG_PERMISSIONS, USER_ACTION_PERMISSIONS, USER_LIST_PROVIDER, USER_PERMISSIONS, USER_PERMISSION_PROVIDER, USER_PROVIDER, USER_ROLE_PERMISSIONS, UserMultiSelectComponent, UserSelectComponent, allPermissionsGuard, anyPermissionGuard, evaluateLogicNode, evaluatePermission, formatFileSize, getAcceptString, getFileIconClass, hasAllPermissions, hasAnyPermission, hasPermission, isFileTypeAllowed, permissionGuard, provideValueAccessor };
3272
+ export { ACTION_PERMISSIONS, AUTH_STATE_PROVIDER, AngularModule, ApiResourceService, ApiResourceService as ApiService, BRANCH_PERMISSIONS, BaseFormControl, BaseFormPage, BaseListPage, BaseUserSelectComponent, COMPANY_ACTION_PERMISSIONS, COMPANY_API_PROVIDER, COMPANY_PERMISSIONS, ContactTypeEnum, CookieService, EMAIL_CONFIG_PERMISSIONS, EMAIL_TEMPLATE_PERMISSIONS, EditModeElementChangerDirective, FILE_PERMISSIONS, FILE_TYPE_FILTERS, FOLDER_PERMISSIONS, FORM_PERMISSIONS, FileSelectorDialogComponent, FileUploaderComponent, FileUrlService, HasPermissionDirective, IconComponent, IconTypeEnum, IsEmptyImageDirective, LazyMultiSelectComponent, LazySelectComponent, PERMISSIONS, PROFILE_PERMISSION_PROVIDER, PROFILE_UPLOAD_PROVIDER, PermissionValidatorService, PlatformService, PreventDefaultDirective, PrimeModule, ROLE_ACTION_PERMISSIONS, ROLE_PERMISSIONS, STORAGE_CONFIG_PERMISSIONS, USER_ACTION_PERMISSIONS, USER_LIST_PROVIDER, USER_PERMISSIONS, USER_PERMISSION_PROVIDER, USER_PROVIDER, USER_ROLE_PERMISSIONS, UserMultiSelectComponent, UserSelectComponent, allPermissionsGuard, anyPermissionGuard, checkScrollPagination, evaluateLogicNode, evaluatePermission, formatFileSize, getAcceptString, getFileIconClass, hasAllPermissions, hasAnyPermission, hasPermission, isFileTypeAllowed, permissionGuard, provideValueAccessor };
3468
3273
  //# sourceMappingURL=flusys-ng-shared.mjs.map