@flusys/ng-shared 8.1.4 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,13 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, PLATFORM_ID, Service, DOCUMENT, REQUEST, signal, computed, ElementRef, input, effect, Directive, TemplateRef, ViewContainerRef, output, Pipe, Injector, isDevMode, resource, model, runInInjectionContext, untracked, forwardRef, contentChildren, Component, InjectionToken, DestroyRef, ViewEncapsulation, ApplicationRef, viewChild, afterNextRender } from '@angular/core';
2
+ import { inject, PLATFORM_ID, Service, DOCUMENT, REQUEST, signal, computed, ElementRef, input, effect, Directive, TemplateRef, ViewContainerRef, output, Pipe, Injector, isDevMode, resource, model, runInInjectionContext, untracked, forwardRef, InjectionToken, Component, contentChildren, DestroyRef, ViewEncapsulation, ApplicationRef, viewChild, afterNextRender } from '@angular/core';
3
3
  import { isPlatformServer, DOCUMENT as DOCUMENT$1, isPlatformBrowser, NgTemplateOutlet, DatePipe, DecimalPipe } from '@angular/common';
4
- import { HttpClient, HttpContextToken, HttpHeaders, HttpParams } from '@angular/common/http';
4
+ import { HttpClient, HttpContextToken, HttpErrorResponse, HttpHeaders, HttpParams } from '@angular/common/http';
5
5
  import { APP_CONFIG, getServiceUrl, LOCALIZATION_STATE_SERVICE, FALLBACK_MESSAGES_REGISTRY, LOCALIZATION_API_SERVICE, isServiceEnabled } from '@flusys/ng-core';
6
6
  import { of, catchError as catchError$1, throwError, firstValueFrom, map as map$1 } from 'rxjs';
7
7
  import { map, tap, catchError } from 'rxjs/operators';
8
8
  import * as i1 from '@angular/forms';
9
9
  import { NgControl, NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
10
- import { MessageService, CardComponent, InputTextDirective, PasswordComponent, UiIconComponent, InputNumberComponent, TextareaDirective, SelectComponent, MultiSelectComponent, DatePickerComponent, CheckboxComponent, ToggleSwitchComponent, FButtonDirective, ConfirmationService, IconFieldComponent, InputIconComponent, TableComponent, TagComponent, TooltipDirective, ProgressbarComponent, DialogComponent, ButtonComponent, OverlayStackService, attachOverlayPosition } from '@flusys/ng-ui';
10
+ import { MessageService, InputTextDirective, TagComponent, TreeSelectComponent, UiIconComponent, CardComponent, PasswordComponent, InputNumberComponent, TextareaDirective, SelectComponent, MultiSelectComponent, DatePickerComponent, CheckboxComponent, ToggleSwitchComponent, FButtonDirective, ConfirmationService, IconFieldComponent, InputIconComponent, TableComponent, TooltipDirective, ProgressbarComponent, DialogComponent, ButtonComponent, OverlayStackService, attachOverlayPosition } from '@flusys/ng-ui';
11
11
  import { Router, RouterLink, RouterLinkActive } from '@angular/router';
12
12
  import { DomSanitizer } from '@angular/platform-browser';
13
13
 
@@ -113,6 +113,11 @@ const SHARED_MESSAGES = {
113
113
  'shared.multi.select.items.selected': '{{count}} Items Selected',
114
114
  // components/user-select/base-user-select.class.ts
115
115
  'shared.user.select.placeholder': 'Select User',
116
+ // components/action-select/action-select.component.ts
117
+ 'shared.action.select.placeholder': 'Select a permission',
118
+ 'shared.action.select.placeholder.multiple': 'Select permissions',
119
+ 'shared.action.select.type.code': 'Type a permission code and press Enter',
120
+ 'shared.action.select.empty': 'No permissions found',
116
121
  // cross-package shared keys (shared.* — used across feature packages)
117
122
  'shared.actions': 'Actions',
118
123
  'shared.active': 'Active',
@@ -283,6 +288,32 @@ const FORM_RESULT_PERMISSIONS = {
283
288
  UPDATE: 'form-result.update',
284
289
  DELETE: 'form-result.delete',
285
290
  };
291
+ // ==================== ENTITY BUILDER MODULE ====================
292
+ // Static metadata permissions only - matches nestjs-entity-builder's
293
+ // ENTITY_DEFINITION_PERMISSIONS/FIELD_DEFINITION_PERMISSIONS exactly. Per-dynamic-entity
294
+ // record permissions (`entity_builder.entity.<entityCode>.<action>`) are runtime-computed and
295
+ // have no static constant - flows running as the caller check them per entity step.
296
+ const ENTITY_DEFINITION_PERMISSIONS = {
297
+ CREATE: 'entity_builder.entity_definition.create',
298
+ READ: 'entity_builder.entity_definition.read',
299
+ UPDATE: 'entity_builder.entity_definition.update',
300
+ DELETE: 'entity_builder.entity_definition.delete',
301
+ };
302
+ const FIELD_DEFINITION_PERMISSIONS = {
303
+ READ: 'entity_builder.field_definition.read',
304
+ UPDATE: 'entity_builder.field_definition.update',
305
+ };
306
+ const FLOW_DEFINITION_PERMISSIONS = {
307
+ CREATE: 'entity_builder.flow_definition.create',
308
+ READ: 'entity_builder.flow_definition.read',
309
+ UPDATE: 'entity_builder.flow_definition.update',
310
+ DELETE: 'entity_builder.flow_definition.delete',
311
+ TEST: 'entity_builder.flow_definition.test',
312
+ /** Needed to save a flow that runs as the system (bypassing per-entity permissions). */
313
+ SYSTEM: 'entity_builder.flow_definition.system',
314
+ /** Needed to save a flow containing a code node (sandboxed JavaScript). */
315
+ CODE: 'entity_builder.flow_definition.code',
316
+ };
286
317
  // ==================== EVENT MANAGER MODULE ====================
287
318
  const EVENT_PERMISSIONS = {
288
319
  CREATE: 'event.create',
@@ -606,11 +637,21 @@ class TranslateService {
606
637
  }
607
638
  return this.interpolate(value, params);
608
639
  }
640
+ translateRef(ref) {
641
+ const params = {};
642
+ for (const [name, value] of Object.entries(ref.messageVariables ?? {})) {
643
+ params[name] = this.isMessageRef(value) ? this.translateRef(value) : value;
644
+ }
645
+ return this.translate(ref.messageKey, params);
646
+ }
609
647
  translateSignal(key, params) {
610
648
  return computed(() => {
611
649
  return this.translate(key, params);
612
650
  });
613
651
  }
652
+ isMessageRef(value) {
653
+ return typeof value === 'object' && value !== null && typeof value.messageKey === 'string';
654
+ }
614
655
  interpolate(template, params) {
615
656
  if (!params || !template)
616
657
  return template;
@@ -933,6 +974,8 @@ class TranslatePipe {
933
974
  transform(key, params, fallback) {
934
975
  if (!key)
935
976
  return fallback ?? '';
977
+ if (typeof key === 'object')
978
+ return this.translateAdapter.translateRef(key);
936
979
  const result = this.translateAdapter.translate(key, params ?? undefined);
937
980
  return fallback !== undefined && result === key ? fallback : result;
938
981
  }
@@ -959,6 +1002,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
959
1002
  * ```
960
1003
  */
961
1004
  const SKIP_ERROR_TOAST = new HttpContextToken(() => false);
1005
+ /** 401 is handled by tokenRefreshInterceptor, 403 by the permission-denied UI. */
1006
+ const SILENT_STATUSES = new Set([401, 403]);
1007
+ const isCheckLoginUrl = (url) => !!url?.includes('/check-login');
1008
+ /**
1009
+ * Whether errorCatchingInterceptor already showed this failure as a toast, so a caller's own
1010
+ * catch block adds one only for failures the user has not been told about yet.
1011
+ */
1012
+ function isErrorToastShown(error) {
1013
+ return error instanceof HttpErrorResponse && !SILENT_STATUSES.has(error.status) && !isCheckLoginUrl(error.url);
1014
+ }
962
1015
  /**
963
1016
  * Error Catching Interceptor
964
1017
  * Displays error messages for failed HTTP requests using backend response.
@@ -991,7 +1044,7 @@ function errorCatchingInterceptor(req, next) {
991
1044
  const translateAdapter = inject(TranslateService, { optional: true });
992
1045
  const isBrowser = isPlatformBrowser(platformId);
993
1046
  // Skip error display for check-login (expected to fail when not logged in)
994
- const isCheckLogin = req.url.includes('/check-login');
1047
+ const isCheckLogin = isCheckLoginUrl(req.url);
995
1048
  // Skip if component handles error locally
996
1049
  const skipErrorToast = req.context.get(SKIP_ERROR_TOAST);
997
1050
  return next(req).pipe(catchError$1((error) => {
@@ -999,10 +1052,7 @@ function errorCatchingInterceptor(req, next) {
999
1052
  // Skip 403 errors - handled by permission denied UI in components
1000
1053
  // Skip check-login errors - expected behavior
1001
1054
  // Skip if component handles error locally
1002
- if (error.status === 401 ||
1003
- error.status === 403 ||
1004
- isCheckLogin ||
1005
- skipErrorToast) {
1055
+ if (SILENT_STATUSES.has(error.status) || isCheckLogin || skipErrorToast) {
1006
1056
  return throwError(() => error);
1007
1057
  }
1008
1058
  // Display error message for other errors
@@ -1028,8 +1078,8 @@ function extractErrorInfo(error, translateAdapter) {
1028
1078
  const messageVariables = error.error?.messageVariables;
1029
1079
  let detail;
1030
1080
  if (messageKey && translateAdapter) {
1031
- // Use translated message if available
1032
- detail = translateAdapter.translate(messageKey, messageVariables);
1081
+ // translateRef also translates variables that are message refs themselves (a nested reason)
1082
+ detail = translateAdapter.translateRef({ messageKey, messageVariables });
1033
1083
  }
1034
1084
  else if (typeof error.error?.message === 'string') {
1035
1085
  detail = error.error.message;
@@ -1299,39 +1349,39 @@ class ApiResourceService {
1299
1349
  // ==========================================================================
1300
1350
  // Promise API (IApiService implementation)
1301
1351
  // ==========================================================================
1302
- // ── DRY helper: all promise methods are one-liners via this ───────────────
1303
- _post(endpoint, body, params) {
1352
+ // ── DRY helper: all promise methods are one-liners via this; subclasses use it for their own endpoints ──
1353
+ post(endpoint, body, params) {
1304
1354
  return firstValueFrom(this.http.post(`${this.baseUrl}/${endpoint}`, body, this.getHttpOptions(endpoint, params)));
1305
1355
  }
1306
1356
  insert(dto) {
1307
- return this._post('insert', dto);
1357
+ return this.post('insert', dto);
1308
1358
  }
1309
1359
  insertMany(dtos) {
1310
- return this._post('insert-many', dtos);
1360
+ return this.post('insert-many', dtos);
1311
1361
  }
1312
1362
  findById(id, select) {
1313
- return this._post(`get/${id}`, { select });
1363
+ return this.post(`get/${id}`, { select });
1314
1364
  }
1315
1365
  getAll(filter, search = '') {
1316
1366
  return firstValueFrom(this._getAllObs(filter, search));
1317
1367
  }
1318
1368
  update(dto) {
1319
- return this._post('update', dto);
1369
+ return this.post('update', dto);
1320
1370
  }
1321
1371
  updateMany(dtos) {
1322
- return this._post('update-many', dtos);
1372
+ return this.post('update-many', dtos);
1323
1373
  }
1324
1374
  delete(deleteDto) {
1325
- return this._post('delete', deleteDto);
1375
+ return this.post('delete', deleteDto);
1326
1376
  }
1327
1377
  findByIds(ids, select) {
1328
- return this._post('get-by-ids', { ids, select });
1378
+ return this.post('get-by-ids', { ids, select });
1329
1379
  }
1330
1380
  getByFilter(filter) {
1331
- return this._post('get-by-filter', filter);
1381
+ return this.post('get-by-filter', filter);
1332
1382
  }
1333
1383
  async bulkUpsert(toInsert, toUpdate) {
1334
- const res = await this._post('bulk-upsert', [
1384
+ const res = await this.post('bulk-upsert', [
1335
1385
  ...toInsert,
1336
1386
  ...toUpdate,
1337
1387
  ]);
@@ -1432,6 +1482,370 @@ function provideValueAccessor(component) {
1432
1482
  };
1433
1483
  }
1434
1484
 
1485
+ /**
1486
+ * Injection Tokens for Provider Interfaces
1487
+ *
1488
+ * These tokens enable dependency injection of provider implementations
1489
+ * without creating direct package dependencies.
1490
+ *
1491
+ * Pattern:
1492
+ * 1. ng-shared defines interfaces and tokens (no implementation)
1493
+ * 2. ng-auth provides implementations
1494
+ * 3. ng-iam and ng-storage inject via tokens
1495
+ * 4. app.config.ts wires everything together
1496
+ *
1497
+ * @example
1498
+ * // In IAM component
1499
+ * private readonly userProvider = inject(USER_PROVIDER);
1500
+ *
1501
+ * // In app.config.ts
1502
+ * providers: [
1503
+ * { provide: USER_PROVIDER, useClass: AuthUserProvider },
1504
+ * ]
1505
+ */
1506
+ /**
1507
+ * User Provider Token
1508
+ *
1509
+ * Provides user data access for IAM user selection.
1510
+ */
1511
+ const USER_PROVIDER = new InjectionToken('USER_PROVIDER', {
1512
+ providedIn: 'root',
1513
+ factory: () => {
1514
+ throw new Error('USER_PROVIDER not configured. Please provide an implementation in app.config.ts');
1515
+ },
1516
+ });
1517
+ /**
1518
+ * Company API Provider Token
1519
+ *
1520
+ * Provides company data access for IAM company selection.
1521
+ */
1522
+ const COMPANY_API_PROVIDER = new InjectionToken('COMPANY_API_PROVIDER', {
1523
+ providedIn: 'root',
1524
+ factory: () => {
1525
+ throw new Error('COMPANY_API_PROVIDER not configured. Please provide an implementation in app.config.ts');
1526
+ },
1527
+ });
1528
+ /**
1529
+ * User Permission Provider Token
1530
+ *
1531
+ * Provides user permission assignment operations for IAM.
1532
+ */
1533
+ const USER_PERMISSION_PROVIDER = new InjectionToken('USER_PERMISSION_PROVIDER', {
1534
+ providedIn: 'root',
1535
+ factory: () => {
1536
+ throw new Error('USER_PERMISSION_PROVIDER not configured. Please provide an implementation in app.config.ts');
1537
+ },
1538
+ });
1539
+ /**
1540
+ * Auth State Provider Token
1541
+ *
1542
+ * Provides auth state access for feature packages (form-builder, etc.)
1543
+ */
1544
+ const AUTH_STATE_PROVIDER = new InjectionToken('AUTH_STATE_PROVIDER', {
1545
+ providedIn: 'root',
1546
+ factory: () => {
1547
+ throw new Error('AUTH_STATE_PROVIDER not configured. Please provide an implementation in app.config.ts');
1548
+ },
1549
+ });
1550
+ /**
1551
+ * Profile Permission Provider Token
1552
+ *
1553
+ * Provides user permission data for profile display.
1554
+ * Optional - if not configured, profile permissions section is hidden.
1555
+ * Use with `inject(PROFILE_PERMISSION_PROVIDER, { optional: true })`.
1556
+ */
1557
+ const PROFILE_PERMISSION_PROVIDER = new InjectionToken('PROFILE_PERMISSION_PROVIDER');
1558
+ /**
1559
+ * File Provider Token
1560
+ *
1561
+ * Provides file loading and upload functionality for file selectors.
1562
+ * Optional - if not configured, file selector requires loadFiles/uploadFile inputs.
1563
+ * Use with `inject(FILE_PROVIDER, { optional: true })`.
1564
+ *
1565
+ * @example
1566
+ * // In app.config.ts
1567
+ * providers: [
1568
+ * { provide: FILE_PROVIDER, useClass: StorageFileProvider },
1569
+ * ]
1570
+ */
1571
+ const FILE_PROVIDER = new InjectionToken('FILE_PROVIDER');
1572
+ /**
1573
+ * Action Provider Token
1574
+ *
1575
+ * Lists permission actions for pickers such as `lib-action-select`.
1576
+ * Optional - if not configured, permission pickers fall back to a free-text code input.
1577
+ * Use with `inject(ACTION_PROVIDER, { optional: true })`.
1578
+ */
1579
+ const ACTION_PROVIDER = new InjectionToken('ACTION_PROVIDER');
1580
+
1581
+ function byOrder(a, b) {
1582
+ return (a.serial ?? Number.MAX_SAFE_INTEGER) - (b.serial ?? Number.MAX_SAFE_INTEGER) || a.name.localeCompare(b.name);
1583
+ }
1584
+ /** Nests actions by parentId; code-less groups can only be expanded, and groups with nothing pickable are dropped. */
1585
+ function buildActionTree(actions) {
1586
+ const ids = new Set(actions.map((a) => a.id));
1587
+ const byParent = new Map();
1588
+ for (const action of actions) {
1589
+ const parentId = action.parentId && ids.has(action.parentId) ? action.parentId : null;
1590
+ byParent.set(parentId, [...(byParent.get(parentId) ?? []), action]);
1591
+ }
1592
+ const build = (parentId) => [...(byParent.get(parentId) ?? [])].sort(byOrder).flatMap((action) => {
1593
+ const children = build(action.id);
1594
+ if (!action.code && !children.length)
1595
+ return [];
1596
+ return [{
1597
+ key: action.code ?? `group:${action.id}`,
1598
+ label: action.name,
1599
+ code: action.code ?? '',
1600
+ selectable: !!action.code,
1601
+ leaf: !children.length,
1602
+ children: children.length ? children : undefined,
1603
+ }];
1604
+ });
1605
+ return build(null);
1606
+ }
1607
+ /** Shared action loading for the single and multi permission pickers. */
1608
+ class BaseActionSelectComponent {
1609
+ provider = inject(ACTION_PROVIDER, { optional: true });
1610
+ placeholder = input('', /* @ts-ignore */
1611
+ ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
1612
+ inputId = input('', /* @ts-ignore */
1613
+ ...(ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []));
1614
+ disabled = input(false, /* @ts-ignore */
1615
+ ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
1616
+ hasProvider = !!this.provider && this.provider.isAvailable?.() !== false;
1617
+ actions = resource({ ...(ngDevMode ? { debugName: "actions" } : /* istanbul ignore next */ {}), loader: async () => {
1618
+ const provider = this.hasProvider ? this.provider : null;
1619
+ if (!provider)
1620
+ return [];
1621
+ const response = await firstValueFrom(provider.getActions());
1622
+ return response.data ?? [];
1623
+ } });
1624
+ /** Picker mode; typing codes is the fallback when there is no provider or the actions could not be loaded. */
1625
+ useSelect = computed(() => this.hasProvider && !this.actions.error(), /* @ts-ignore */
1626
+ ...(ngDevMode ? [{ debugName: "useSelect" }] : /* istanbul ignore next */ []));
1627
+ actionTree = computed(() => buildActionTree(this.actions.hasValue() ? this.actions.value() : []), /* @ts-ignore */
1628
+ ...(ngDevMode ? [{ debugName: "actionTree" }] : /* istanbul ignore next */ []));
1629
+ knownCodes = computed(() => new Set((this.actions.hasValue() ? this.actions.value() : []).flatMap((a) => (a.code ? [a.code] : []))), /* @ts-ignore */
1630
+ ...(ngDevMode ? [{ debugName: "knownCodes" }] : /* istanbul ignore next */ []));
1631
+ /** The action tree plus any saved code that is not (or no longer) an action, so the selection still shows. */
1632
+ treeWith(saved) {
1633
+ const known = this.knownCodes();
1634
+ const unknown = saved.filter((code) => !known.has(code)).map((code) => ({ key: code, label: code, code, leaf: true }));
1635
+ return [...unknown, ...this.actionTree()];
1636
+ }
1637
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: BaseActionSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1638
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.5", type: BaseActionSelectComponent, isStandalone: true, inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
1639
+ }
1640
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: BaseActionSelectComponent, decorators: [{
1641
+ type: Directive
1642
+ }], propDecorators: { placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
1643
+
1644
+ /**
1645
+ * Picks several permission action codes from the action tree of ACTION_PROVIDER (wired by ng-iam);
1646
+ * without an available provider, or when loading fails, it falls back to typing codes as chips (Enter adds one).
1647
+ *
1648
+ * @example
1649
+ * <lib-action-multi-select [(value)]="actionGroups" inputId="actionGroups" />
1650
+ */
1651
+ class ActionMultiSelectComponent extends BaseActionSelectComponent {
1652
+ value = model([], /* @ts-ignore */
1653
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
1654
+ draft = signal('', /* @ts-ignore */
1655
+ ...(ngDevMode ? [{ debugName: "draft" }] : /* istanbul ignore next */ []));
1656
+ options = computed(() => this.treeWith(this.value()), /* @ts-ignore */
1657
+ ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
1658
+ add(event) {
1659
+ event.preventDefault();
1660
+ const code = this.draft().trim();
1661
+ if (code && !this.value().includes(code))
1662
+ this.value.set([...this.value(), code]);
1663
+ this.draft.set('');
1664
+ }
1665
+ remove(code) {
1666
+ this.value.set(this.value().filter((c) => c !== code));
1667
+ }
1668
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: ActionMultiSelectComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
1669
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: ActionMultiSelectComponent, isStandalone: true, selector: "lib-action-multi-select", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { classAttribute: "block w-full min-w-0" }, usesInheritance: true, ngImport: i0, template: `
1670
+ @if (useSelect()) {
1671
+ <f-treeselect
1672
+ [inputId]="inputId()"
1673
+ [options]="options()"
1674
+ selectionMode="checkbox"
1675
+ [propagateSelectionDown]="false"
1676
+ [propagateSelectionUp]="false"
1677
+ display="chip"
1678
+ filterBy="label,code"
1679
+ [filter]="true"
1680
+ [showClear]="true"
1681
+ [disabled]="disabled()"
1682
+ [placeholder]="placeholder() || ('shared.action.select.placeholder.multiple' | translate)"
1683
+ [emptyMessage]="'shared.action.select.empty' | translate"
1684
+ [ngModel]="value()"
1685
+ (ngModelChange)="value.set($event ?? [])"
1686
+ />
1687
+ } @else {
1688
+ <div class="flex flex-wrap items-center gap-1">
1689
+ @for (code of value(); track code) {
1690
+ <f-tag [rounded]="true" severity="secondary">
1691
+ <span class="flex items-center gap-1 font-mono text-xs">
1692
+ {{ code }}
1693
+ @if (!disabled()) {
1694
+ <button type="button" class="cursor-pointer hover:text-danger" [attr.aria-label]="'shared.remove' | translate" (click)="remove(code)">
1695
+ <ui-icon name="x" [size]="12" />
1696
+ </button>
1697
+ }
1698
+ </span>
1699
+ </f-tag>
1700
+ }
1701
+ <input
1702
+ fInputText
1703
+ class="flex-1 min-w-40"
1704
+ [id]="inputId()"
1705
+ [disabled]="disabled()"
1706
+ [placeholder]="placeholder() || ('shared.action.select.type.code' | translate)"
1707
+ [ngModel]="draft()"
1708
+ (ngModelChange)="draft.set($event)"
1709
+ (keydown.enter)="add($event)"
1710
+ />
1711
+ </div>
1712
+ }
1713
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: InputTextDirective, selector: "[fInputText]", inputs: ["invalid", "touched", "variant", "size", "fluid"] }, { kind: "component", type: TagComponent, selector: "f-tag", inputs: ["value", "severity", "icon", "rounded", "color", "styleClass", "style"] }, { kind: "component", type: TreeSelectComponent, selector: "f-treeselect", inputs: ["options", "placeholder", "inputId", "disabled", "filter", "filterBy", "selectionMode", "display", "propagateSelectionDown", "propagateSelectionUp", "showClear", "virtualScroll", "virtualScrollItemSize", "emptyMessage", "invalid", "touched", "styleClass", "style", "appendTo"] }, { kind: "component", type: UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1714
+ }
1715
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: ActionMultiSelectComponent, decorators: [{
1716
+ type: Component,
1717
+ args: [{
1718
+ selector: 'lib-action-multi-select',
1719
+ imports: [FormsModule, InputTextDirective, TagComponent, TreeSelectComponent, UiIconComponent, TranslatePipe],
1720
+ host: { class: 'block w-full min-w-0' },
1721
+ template: `
1722
+ @if (useSelect()) {
1723
+ <f-treeselect
1724
+ [inputId]="inputId()"
1725
+ [options]="options()"
1726
+ selectionMode="checkbox"
1727
+ [propagateSelectionDown]="false"
1728
+ [propagateSelectionUp]="false"
1729
+ display="chip"
1730
+ filterBy="label,code"
1731
+ [filter]="true"
1732
+ [showClear]="true"
1733
+ [disabled]="disabled()"
1734
+ [placeholder]="placeholder() || ('shared.action.select.placeholder.multiple' | translate)"
1735
+ [emptyMessage]="'shared.action.select.empty' | translate"
1736
+ [ngModel]="value()"
1737
+ (ngModelChange)="value.set($event ?? [])"
1738
+ />
1739
+ } @else {
1740
+ <div class="flex flex-wrap items-center gap-1">
1741
+ @for (code of value(); track code) {
1742
+ <f-tag [rounded]="true" severity="secondary">
1743
+ <span class="flex items-center gap-1 font-mono text-xs">
1744
+ {{ code }}
1745
+ @if (!disabled()) {
1746
+ <button type="button" class="cursor-pointer hover:text-danger" [attr.aria-label]="'shared.remove' | translate" (click)="remove(code)">
1747
+ <ui-icon name="x" [size]="12" />
1748
+ </button>
1749
+ }
1750
+ </span>
1751
+ </f-tag>
1752
+ }
1753
+ <input
1754
+ fInputText
1755
+ class="flex-1 min-w-40"
1756
+ [id]="inputId()"
1757
+ [disabled]="disabled()"
1758
+ [placeholder]="placeholder() || ('shared.action.select.type.code' | translate)"
1759
+ [ngModel]="draft()"
1760
+ (ngModelChange)="draft.set($event)"
1761
+ (keydown.enter)="add($event)"
1762
+ />
1763
+ </div>
1764
+ }
1765
+ `,
1766
+ }]
1767
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }] } });
1768
+
1769
+ /**
1770
+ * Picks a permission action code from the action tree of ACTION_PROVIDER (wired by ng-iam);
1771
+ * without an available provider, or when loading fails, it falls back to a free-text code input.
1772
+ *
1773
+ * @example
1774
+ * <lib-action-select [(value)]="permissionCode" inputId="permission" [placeholder]="defaultCode" />
1775
+ */
1776
+ class ActionSelectComponent extends BaseActionSelectComponent {
1777
+ value = model(null, /* @ts-ignore */
1778
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
1779
+ showClear = input(true, /* @ts-ignore */
1780
+ ...(ngDevMode ? [{ debugName: "showClear" }] : /* istanbul ignore next */ []));
1781
+ options = computed(() => {
1782
+ const current = this.value();
1783
+ return this.treeWith(current ? [current] : []);
1784
+ }, /* @ts-ignore */
1785
+ ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
1786
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: ActionSelectComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
1787
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: ActionSelectComponent, isStandalone: true, selector: "lib-action-select", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, showClear: { classPropertyName: "showClear", publicName: "showClear", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { classAttribute: "block w-full min-w-0" }, usesInheritance: true, ngImport: i0, template: `
1788
+ @if (useSelect()) {
1789
+ <f-treeselect
1790
+ [inputId]="inputId()"
1791
+ [options]="options()"
1792
+ filterBy="label,code"
1793
+ [filter]="true"
1794
+ [showClear]="showClear()"
1795
+ [disabled]="disabled()"
1796
+ [placeholder]="placeholder() || ('shared.action.select.placeholder' | translate)"
1797
+ [emptyMessage]="'shared.action.select.empty' | translate"
1798
+ [ngModel]="value()"
1799
+ (ngModelChange)="value.set($event ?? null)"
1800
+ />
1801
+ } @else {
1802
+ <input
1803
+ fInputText
1804
+ class="w-full"
1805
+ [id]="inputId()"
1806
+ [disabled]="disabled()"
1807
+ [placeholder]="placeholder()"
1808
+ [ngModel]="value() ?? ''"
1809
+ (ngModelChange)="value.set($event || null)"
1810
+ />
1811
+ }
1812
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: InputTextDirective, selector: "[fInputText]", inputs: ["invalid", "touched", "variant", "size", "fluid"] }, { kind: "component", type: TreeSelectComponent, selector: "f-treeselect", inputs: ["options", "placeholder", "inputId", "disabled", "filter", "filterBy", "selectionMode", "display", "propagateSelectionDown", "propagateSelectionUp", "showClear", "virtualScroll", "virtualScrollItemSize", "emptyMessage", "invalid", "touched", "styleClass", "style", "appendTo"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1813
+ }
1814
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: ActionSelectComponent, decorators: [{
1815
+ type: Component,
1816
+ args: [{
1817
+ selector: 'lib-action-select',
1818
+ imports: [FormsModule, InputTextDirective, TreeSelectComponent, TranslatePipe],
1819
+ host: { class: 'block w-full min-w-0' },
1820
+ template: `
1821
+ @if (useSelect()) {
1822
+ <f-treeselect
1823
+ [inputId]="inputId()"
1824
+ [options]="options()"
1825
+ filterBy="label,code"
1826
+ [filter]="true"
1827
+ [showClear]="showClear()"
1828
+ [disabled]="disabled()"
1829
+ [placeholder]="placeholder() || ('shared.action.select.placeholder' | translate)"
1830
+ [emptyMessage]="'shared.action.select.empty' | translate"
1831
+ [ngModel]="value()"
1832
+ (ngModelChange)="value.set($event ?? null)"
1833
+ />
1834
+ } @else {
1835
+ <input
1836
+ fInputText
1837
+ class="w-full"
1838
+ [id]="inputId()"
1839
+ [disabled]="disabled()"
1840
+ [placeholder]="placeholder()"
1841
+ [ngModel]="value() ?? ''"
1842
+ (ngModelChange)="value.set($event || null)"
1843
+ />
1844
+ }
1845
+ `,
1846
+ }]
1847
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], showClear: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClear", required: false }] }] } });
1848
+
1435
1849
  class BaseFormTemplateDirective {
1436
1850
  key = input.required({ ...(ngDevMode ? { debugName: "key" } : /* istanbul ignore next */ {}), alias: 'baseFormTemplate' });
1437
1851
  templateRef = inject((TemplateRef));
@@ -1913,94 +2327,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
1913
2327
  ], template: "<f-card>\n <ng-template #header>\n @if (title() || searchable() || showCreateButton()) {\n <div\n class=\"flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 p-4\"\n >\n @if (title()) {\n <h2 class=\"text-xl font-semibold m-0\">{{ title() }}</h2>\n }\n <div class=\"flex items-center gap-2\" [class.ml-auto]=\"!!title()\">\n @if (searchable()) {\n <f-iconfield>\n <f-inputicon icon=\"search\" />\n <input\n fInputText\n type=\"text\"\n [ngModel]=\"searchTerm()\"\n (ngModelChange)=\"onSearchChange($event)\"\n [placeholder]=\"t('shared.search')\"\n />\n </f-iconfield>\n }\n @if (showCreateButton()) {\n <button fButton (click)=\"handleCreate()\">\n <ui-icon pButtonIcon [name]=\"createButtonIcon()\"></ui-icon>\n <span pButtonLabel>{{ resolvedCreateLabel() }}</span>\n </button>\n }\n </div>\n </div>\n }\n </ng-template>\n\n <div class=\"overflow-x-auto\">\n <f-table\n [value]=\"effectiveData()\"\n [loading]=\"effectiveIsLoading()\"\n [lazy]=\"true\"\n [scrollable]=\"true\"\n [scrollHeight]=\"scrollHeight()\"\n [paginator]=\"effectiveTotal() > pageSize()\"\n [rows]=\"pageSize()\"\n [totalRecords]=\"effectiveTotal()\"\n [first]=\"first()\"\n (onLazyLoad)=\"handleLazyLoad($event)\"\n [showCurrentPageReport]=\"true\"\n [currentPageReportTemplate]=\"t('shared.pagination.report')\"\n class=\"p-datatable-striped\"\n >\n <ng-template #header>\n <tr>\n @for (col of visibleColumns(); track col.field) {\n <th\n [style.width]=\"col.width\"\n [style.min-width]=\"col.minWidth\"\n [class]=\"getCellClass(col)\"\n >\n {{ col.header }}\n </th>\n }\n @if (effectiveActions().length > 0) {\n <th style=\"width: 120px; text-align: center\">\n {{ t(\"shared.actions\") }}\n </th>\n }\n </tr>\n </ng-template>\n\n <ng-template #body let-item>\n <tr>\n @for (col of visibleColumns(); track col.field) {\n <td [class]=\"getCellClass(col)\">\n @switch (col.type ?? \"text\") {\n @case (\"tag\") {\n @if (\n getTagConfig(col, getFieldValue(item, col.field));\n as tagCfg\n ) {\n <f-tag\n [severity]=\"tagCfg.severity\"\n [value]=\"\n tagCfg.label ?? getFieldValueAsString(item, col.field)\n \"\n />\n } @else {\n {{ getFieldValueAsString(item, col.field) }}\n }\n }\n @case (\"boolean\") {\n <f-tag\n [severity]=\"\n getFieldValue(item, col.field) ? 'success' : 'danger'\n \"\n [value]=\"\n getFieldValue(item, col.field)\n ? t('shared.yes')\n : t('shared.no')\n \"\n />\n }\n @case (\"date\") {\n {{\n getFieldValueAsString(item, col.field)\n | date: col.dateFormat ?? \"dd/MM/yyyy\"\n }}\n }\n @case (\"number\") {\n {{ getFieldValueAsString(item, col.field) | number }}\n }\n @case (\"custom\") {\n <ng-container\n [ngTemplateOutlet]=\"getColumnTemplate(col.field)\"\n [ngTemplateOutletContext]=\"{ $implicit: item, field: col.field }\"\n />\n }\n @default {\n {{\n truncateText(getFieldValue(item, col.field), col.truncate)\n }}\n }\n }\n </td>\n }\n @if (effectiveActions().length > 0) {\n <td>\n <div class=\"flex items-center justify-center gap-2\">\n @for (action of effectiveActions(); track $index) {\n @if (!action.show || action.show(item)) {\n <button\n fButton\n [severity]=\"action.severity ?? 'secondary'\"\n [fTooltip]=\"action.tooltip ? t(action.tooltip) : ''\"\n [disabled]=\"\n action.disabled ? action.disabled(item) : false\n \"\n (click)=\"action.handler(item)\"\n [rounded]=\"true\"\n [text]=\"true\"\n >\n <ui-icon pButtonIcon [name]=\"action.icon\"></ui-icon>\n </button>\n }\n }\n </div>\n </td>\n }\n </tr>\n </ng-template>\n\n <ng-template #emptymessage>\n <tr>\n <td [attr.colspan]=\"columnCount()\">\n <div\n class=\"flex flex-col items-center justify-center py-12 text-surface-400\"\n >\n <ui-icon name=\"inbox\" class=\"text-5xl mb-3\"></ui-icon>\n <span>{{ resolvedEmptyMessage() }}</span>\n </div>\n </td>\n </tr>\n </ng-template>\n </f-table>\n </div>\n</f-card>\n" }]
1914
2328
  }], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], showCreateButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCreateButton", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], scrollHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollHeight", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], createButtonLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "createButtonLabel", required: false }] }], createButtonIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "createButtonIcon", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], total: [{ type: i0.Input, args: [{ isSignal: true, alias: "total", required: false }] }], isLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "isLoading", required: false }] }], service: [{ type: i0.Input, args: [{ isSignal: true, alias: "service", required: false }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], createRoute: [{ type: i0.Input, args: [{ isSignal: true, alias: "createRoute", required: false }] }], editRoute: [{ type: i0.Input, args: [{ isSignal: true, alias: "editRoute", required: false }] }], deleteConfirmField: [{ type: i0.Input, args: [{ isSignal: true, alias: "deleteConfirmField", required: false }] }], deleteType: [{ type: i0.Input, args: [{ isSignal: true, alias: "deleteType", required: false }] }], idField: [{ type: i0.Input, args: [{ isSignal: true, alias: "idField", required: false }] }], canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], canDelete: [{ type: i0.Input, args: [{ isSignal: true, alias: "canDelete", required: false }] }], onCreate: [{ type: i0.Output, args: ["onCreate"] }], onLazyLoad: [{ type: i0.Output, args: ["onLazyLoad"] }], onSearch: [{ type: i0.Output, args: ["onSearch"] }], columnTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => BaseListTemplateDirective), { isSignal: true }] }] } });
1915
2329
 
1916
- /**
1917
- * Injection Tokens for Provider Interfaces
1918
- *
1919
- * These tokens enable dependency injection of provider implementations
1920
- * without creating direct package dependencies.
1921
- *
1922
- * Pattern:
1923
- * 1. ng-shared defines interfaces and tokens (no implementation)
1924
- * 2. ng-auth provides implementations
1925
- * 3. ng-iam and ng-storage inject via tokens
1926
- * 4. app.config.ts wires everything together
1927
- *
1928
- * @example
1929
- * // In IAM component
1930
- * private readonly userProvider = inject(USER_PROVIDER);
1931
- *
1932
- * // In app.config.ts
1933
- * providers: [
1934
- * { provide: USER_PROVIDER, useClass: AuthUserProvider },
1935
- * ]
1936
- */
1937
- /**
1938
- * User Provider Token
1939
- *
1940
- * Provides user data access for IAM user selection.
1941
- */
1942
- const USER_PROVIDER = new InjectionToken('USER_PROVIDER', {
1943
- providedIn: 'root',
1944
- factory: () => {
1945
- throw new Error('USER_PROVIDER not configured. Please provide an implementation in app.config.ts');
1946
- },
1947
- });
1948
- /**
1949
- * Company API Provider Token
1950
- *
1951
- * Provides company data access for IAM company selection.
1952
- */
1953
- const COMPANY_API_PROVIDER = new InjectionToken('COMPANY_API_PROVIDER', {
1954
- providedIn: 'root',
1955
- factory: () => {
1956
- throw new Error('COMPANY_API_PROVIDER not configured. Please provide an implementation in app.config.ts');
1957
- },
1958
- });
1959
- /**
1960
- * User Permission Provider Token
1961
- *
1962
- * Provides user permission assignment operations for IAM.
1963
- */
1964
- const USER_PERMISSION_PROVIDER = new InjectionToken('USER_PERMISSION_PROVIDER', {
1965
- providedIn: 'root',
1966
- factory: () => {
1967
- throw new Error('USER_PERMISSION_PROVIDER not configured. Please provide an implementation in app.config.ts');
1968
- },
1969
- });
1970
- /**
1971
- * Auth State Provider Token
1972
- *
1973
- * Provides auth state access for feature packages (form-builder, etc.)
1974
- */
1975
- const AUTH_STATE_PROVIDER = new InjectionToken('AUTH_STATE_PROVIDER', {
1976
- providedIn: 'root',
1977
- factory: () => {
1978
- throw new Error('AUTH_STATE_PROVIDER not configured. Please provide an implementation in app.config.ts');
1979
- },
1980
- });
1981
- /**
1982
- * Profile Permission Provider Token
1983
- *
1984
- * Provides user permission data for profile display.
1985
- * Optional - if not configured, profile permissions section is hidden.
1986
- * Use with `inject(PROFILE_PERMISSION_PROVIDER, { optional: true })`.
1987
- */
1988
- const PROFILE_PERMISSION_PROVIDER = new InjectionToken('PROFILE_PERMISSION_PROVIDER');
1989
- /**
1990
- * File Provider Token
1991
- *
1992
- * Provides file loading and upload functionality for file selectors.
1993
- * Optional - if not configured, file selector requires loadFiles/uploadFile inputs.
1994
- * Use with `inject(FILE_PROVIDER, { optional: true })`.
1995
- *
1996
- * @example
1997
- * // In app.config.ts
1998
- * providers: [
1999
- * { provide: FILE_PROVIDER, useClass: StorageFileProvider },
2000
- * ]
2001
- */
2002
- const FILE_PROVIDER = new InjectionToken('FILE_PROVIDER');
2003
-
2004
2330
  /**
2005
2331
  * Check if scroll has reached near bottom and calculate next page if available.
2006
2332
  * Returns next pagination if should load more, null otherwise.
@@ -3749,7 +4075,7 @@ class LazyMultiSelectComponent extends BaseFormControl {
3749
4075
  return key;
3750
4076
  }
3751
4077
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LazyMultiSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3752
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: LazyMultiSelectComponent, isStandalone: true, selector: "lib-lazy-multi-select", inputs: { placeHolder: { classPropertyName: "placeHolder", publicName: "placeHolder", isSignal: true, isRequired: false, transformFunction: null }, showClear: { classPropertyName: "showClear", publicName: "showClear", 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 }, selectedObjects: { classPropertyName: "selectedObjects", publicName: "selectedObjects", isSignal: true, isRequired: false, 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 }, { propertyName: "overlayTemplate", first: true, predicate: ["overlayTpl"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div\n class=\"w-full flex items-center justify-between px-3 py-2 text-sm rounded-lg border border-(--f-form-field-border-color) bg-(--f-form-field-background) cursor-pointer min-h-[34px]\"\n #pSelect\n (click)=\"onSelectClick()\"\n [class.opacity-50]=\"disabled()\"\n [class.pointer-events-none]=\"disabled()\"\n [class.edit-mode-element-css]=\"!isEditMode()\"\n [class.overflow-hidden]=\"!isEditMode()\"\n>\n @if (selectedValueDisplay()) {\n <span\n class=\"flex-1 truncate text-(--f-form-field-color)\"\n [class.edit-mode-element-css]=\"!isEditMode()\"\n >\n {{ selectedValueDisplay() }}\n </span>\n } @else {\n <span class=\"flex-1 truncate text-(--f-form-field-placeholder-color)\">{{\n displayPlaceholder()\n }}</span>\n }\n\n @if (showClear() && hasSelectedValues()) {\n <span\n class=\"inline-flex items-center px-1 text-(--f-text-muted-color) hover:text-(--f-text-hover-color) cursor-pointer\"\n (click)=\"clear($event)\"\n >\n <ui-icon name=\"times\" class=\"text-xs\"></ui-icon>\n </span>\n }\n\n @if (isEditMode()) {\n <span\n class=\"inline-flex items-center px-1 text-(--f-form-field-placeholder-color)\"\n >\n <ui-icon name=\"chevron-down\" class=\"text-xs\"></ui-icon>\n </span>\n }\n</div>\n\n<ng-template #overlayTpl>\n <div\n class=\"flex flex-col max-h-[250px] overflow-hidden\"\n (click)=\"onOverlayClick($event)\"\n >\n <div\n class=\"p-3 border-b border-(--f-content-border-color) bg-(--f-content-hover-background) shrink-0 w-full flex flex-row gap-2 items-center\"\n >\n <f-checkbox\n [binary]=\"true\"\n [ngModel]=\"isSelectAll()\"\n [disabled]=\"disabled()\"\n (onChange)=\"changeSelectAll($event)\"\n />\n <input\n type=\"text\"\n fInputText\n class=\"w-full\"\n [placeholder]=\"'shared.search' | translate\"\n [ngModel]=\"searchTerm()\"\n (ngModelChange)=\"searchTerm.set($event)\"\n />\n </div>\n <div\n class=\"flex-1 overflow-y-auto min-h-0 w-full\"\n (scroll)=\"onScroll($event)\"\n >\n <ul\n class=\"m-0 py-1 list-none bg-(--f-overlay-popover-background) text-(--f-text-color) w-full\"\n >\n @for (data of mergedDataList(); track data.value) {\n @let isItemSelected = isSelected(data);\n <li\n class=\"px-3 py-2 cursor-pointer transition-colors flex flex-row gap-2 items-center text-sm\"\n [class]=\"\n isItemSelected\n ? 'bg-primary/10 text-primary'\n : 'text-(--f-text-color) hover:bg-(--f-content-hover-background)'\n \"\n >\n <f-checkbox\n [binary]=\"true\"\n [ngModel]=\"isItemSelected\"\n [disabled]=\"disabled()\"\n (onChange)=\"selectValue($event, data)\"\n />\n <span>{{ data.label }}</span>\n </li>\n }\n </ul>\n </div>\n </div>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }, { kind: "component", type: CheckboxComponent, selector: "f-checkbox", inputs: ["label", "binary", "disabled", "readonly", "inputId", "invalid", "indeterminate", "variant", "size", "touched", "styleClass", "style", "checked"], outputs: ["onChange", "onBlur", "touch", "checkedChange"] }, { kind: "directive", type: InputTextDirective, selector: "[fInputText]", inputs: ["invalid", "touched", "variant", "size", "fluid"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
4078
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: LazyMultiSelectComponent, isStandalone: true, selector: "lib-lazy-multi-select", inputs: { placeHolder: { classPropertyName: "placeHolder", publicName: "placeHolder", isSignal: true, isRequired: false, transformFunction: null }, showClear: { classPropertyName: "showClear", publicName: "showClear", 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 }, selectedObjects: { classPropertyName: "selectedObjects", publicName: "selectedObjects", isSignal: true, isRequired: false, 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 }, { propertyName: "overlayTemplate", first: true, predicate: ["overlayTpl"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div\n class=\"w-full flex items-center justify-between px-3 py-2 text-sm rounded-lg border border-(--f-form-field-border-color) bg-(--f-form-field-background) cursor-pointer min-h-[34px]\"\n #pSelect\n (click)=\"onSelectClick()\"\n [class.opacity-50]=\"disabled()\"\n [class.pointer-events-none]=\"disabled()\"\n [class.edit-mode-element-css]=\"!isEditMode()\"\n [class.overflow-hidden]=\"!isEditMode()\"\n>\n @if (selectedValueDisplay()) {\n <span\n class=\"flex-1 truncate text-(--f-form-field-color)\"\n [class.edit-mode-element-css]=\"!isEditMode()\"\n >\n {{ selectedValueDisplay() }}\n </span>\n } @else {\n <span class=\"flex-1 truncate text-(--f-form-field-placeholder-color)\">{{\n displayPlaceholder()\n }}</span>\n }\n\n @if (showClear() && hasSelectedValues()) {\n <span\n class=\"inline-flex items-center px-1 text-(--f-text-muted-color) hover:text-(--f-text-hover-color) cursor-pointer\"\n (click)=\"clear($event)\"\n >\n <ui-icon name=\"times\" class=\"text-xs\"></ui-icon>\n </span>\n }\n\n @if (isEditMode()) {\n <span\n class=\"inline-flex items-center px-1 text-(--f-form-field-placeholder-color)\"\n >\n <ui-icon name=\"chevron-down\" class=\"text-xs\"></ui-icon>\n </span>\n }\n</div>\n\n<ng-template #overlayTpl>\n <div\n class=\"flex flex-col max-h-[250px] overflow-hidden\"\n (click)=\"onOverlayClick($event)\"\n >\n <div\n class=\"p-3 border-b border-(--f-content-border-color) bg-(--f-content-hover-background) shrink-0 w-full flex flex-row gap-2 items-center\"\n >\n <f-checkbox\n [binary]=\"true\"\n [ngModel]=\"isSelectAll()\"\n [disabled]=\"disabled()\"\n (onChange)=\"changeSelectAll($event)\"\n />\n <input\n type=\"text\"\n fInputText\n class=\"w-full\"\n [placeholder]=\"'shared.search' | translate\"\n [ngModel]=\"searchTerm()\"\n (ngModelChange)=\"searchTerm.set($event)\"\n />\n </div>\n <div\n class=\"flex-1 overflow-y-auto min-h-0 w-full\"\n (scroll)=\"onScroll($event)\"\n >\n <ul\n class=\"m-0 py-1 list-none bg-(--f-overlay-popover-background) text-(--f-text-color) w-full\"\n >\n @for (data of mergedDataList(); track data.value) {\n @let isItemSelected = isSelected(data);\n <li\n class=\"px-3 py-2 cursor-pointer transition-colors flex flex-row gap-2 items-center text-sm\"\n [class]=\"\n isItemSelected\n ? 'bg-primary/10 text-primary'\n : 'text-(--f-text-color) hover:bg-(--f-content-hover-background)'\n \"\n >\n <f-checkbox\n [binary]=\"true\"\n [ngModel]=\"isItemSelected\"\n [disabled]=\"disabled()\"\n (onChange)=\"selectValue($event, data)\"\n />\n <span class=\"flex flex-col min-w-0 flex-1\">\n <span class=\"truncate\">{{ data.label }}</span>\n @if (data.subLabel) {\n <span class=\"truncate text-xs text-(--f-text-muted-color)\">{{\n data.subLabel\n }}</span>\n }\n </span>\n </li>\n }\n </ul>\n </div>\n </div>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }, { kind: "component", type: CheckboxComponent, selector: "f-checkbox", inputs: ["label", "binary", "disabled", "readonly", "inputId", "invalid", "indeterminate", "variant", "size", "touched", "styleClass", "style", "checked"], outputs: ["onChange", "onBlur", "touch", "checkedChange"] }, { kind: "directive", type: InputTextDirective, selector: "[fInputText]", inputs: ["invalid", "touched", "variant", "size", "fluid"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
3753
4079
  }
3754
4080
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LazyMultiSelectComponent, decorators: [{
3755
4081
  type: Component,
@@ -3759,7 +4085,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
3759
4085
  CheckboxComponent,
3760
4086
  InputTextDirective,
3761
4087
  TranslatePipe,
3762
- ], providers: [provideValueAccessor(LazyMultiSelectComponent)], template: "<div\n class=\"w-full flex items-center justify-between px-3 py-2 text-sm rounded-lg border border-(--f-form-field-border-color) bg-(--f-form-field-background) cursor-pointer min-h-[34px]\"\n #pSelect\n (click)=\"onSelectClick()\"\n [class.opacity-50]=\"disabled()\"\n [class.pointer-events-none]=\"disabled()\"\n [class.edit-mode-element-css]=\"!isEditMode()\"\n [class.overflow-hidden]=\"!isEditMode()\"\n>\n @if (selectedValueDisplay()) {\n <span\n class=\"flex-1 truncate text-(--f-form-field-color)\"\n [class.edit-mode-element-css]=\"!isEditMode()\"\n >\n {{ selectedValueDisplay() }}\n </span>\n } @else {\n <span class=\"flex-1 truncate text-(--f-form-field-placeholder-color)\">{{\n displayPlaceholder()\n }}</span>\n }\n\n @if (showClear() && hasSelectedValues()) {\n <span\n class=\"inline-flex items-center px-1 text-(--f-text-muted-color) hover:text-(--f-text-hover-color) cursor-pointer\"\n (click)=\"clear($event)\"\n >\n <ui-icon name=\"times\" class=\"text-xs\"></ui-icon>\n </span>\n }\n\n @if (isEditMode()) {\n <span\n class=\"inline-flex items-center px-1 text-(--f-form-field-placeholder-color)\"\n >\n <ui-icon name=\"chevron-down\" class=\"text-xs\"></ui-icon>\n </span>\n }\n</div>\n\n<ng-template #overlayTpl>\n <div\n class=\"flex flex-col max-h-[250px] overflow-hidden\"\n (click)=\"onOverlayClick($event)\"\n >\n <div\n class=\"p-3 border-b border-(--f-content-border-color) bg-(--f-content-hover-background) shrink-0 w-full flex flex-row gap-2 items-center\"\n >\n <f-checkbox\n [binary]=\"true\"\n [ngModel]=\"isSelectAll()\"\n [disabled]=\"disabled()\"\n (onChange)=\"changeSelectAll($event)\"\n />\n <input\n type=\"text\"\n fInputText\n class=\"w-full\"\n [placeholder]=\"'shared.search' | translate\"\n [ngModel]=\"searchTerm()\"\n (ngModelChange)=\"searchTerm.set($event)\"\n />\n </div>\n <div\n class=\"flex-1 overflow-y-auto min-h-0 w-full\"\n (scroll)=\"onScroll($event)\"\n >\n <ul\n class=\"m-0 py-1 list-none bg-(--f-overlay-popover-background) text-(--f-text-color) w-full\"\n >\n @for (data of mergedDataList(); track data.value) {\n @let isItemSelected = isSelected(data);\n <li\n class=\"px-3 py-2 cursor-pointer transition-colors flex flex-row gap-2 items-center text-sm\"\n [class]=\"\n isItemSelected\n ? 'bg-primary/10 text-primary'\n : 'text-(--f-text-color) hover:bg-(--f-content-hover-background)'\n \"\n >\n <f-checkbox\n [binary]=\"true\"\n [ngModel]=\"isItemSelected\"\n [disabled]=\"disabled()\"\n (onChange)=\"selectValue($event, data)\"\n />\n <span>{{ data.label }}</span>\n </li>\n }\n </ul>\n </div>\n </div>\n</ng-template>\n" }]
4088
+ ], providers: [provideValueAccessor(LazyMultiSelectComponent)], template: "<div\n class=\"w-full flex items-center justify-between px-3 py-2 text-sm rounded-lg border border-(--f-form-field-border-color) bg-(--f-form-field-background) cursor-pointer min-h-[34px]\"\n #pSelect\n (click)=\"onSelectClick()\"\n [class.opacity-50]=\"disabled()\"\n [class.pointer-events-none]=\"disabled()\"\n [class.edit-mode-element-css]=\"!isEditMode()\"\n [class.overflow-hidden]=\"!isEditMode()\"\n>\n @if (selectedValueDisplay()) {\n <span\n class=\"flex-1 truncate text-(--f-form-field-color)\"\n [class.edit-mode-element-css]=\"!isEditMode()\"\n >\n {{ selectedValueDisplay() }}\n </span>\n } @else {\n <span class=\"flex-1 truncate text-(--f-form-field-placeholder-color)\">{{\n displayPlaceholder()\n }}</span>\n }\n\n @if (showClear() && hasSelectedValues()) {\n <span\n class=\"inline-flex items-center px-1 text-(--f-text-muted-color) hover:text-(--f-text-hover-color) cursor-pointer\"\n (click)=\"clear($event)\"\n >\n <ui-icon name=\"times\" class=\"text-xs\"></ui-icon>\n </span>\n }\n\n @if (isEditMode()) {\n <span\n class=\"inline-flex items-center px-1 text-(--f-form-field-placeholder-color)\"\n >\n <ui-icon name=\"chevron-down\" class=\"text-xs\"></ui-icon>\n </span>\n }\n</div>\n\n<ng-template #overlayTpl>\n <div\n class=\"flex flex-col max-h-[250px] overflow-hidden\"\n (click)=\"onOverlayClick($event)\"\n >\n <div\n class=\"p-3 border-b border-(--f-content-border-color) bg-(--f-content-hover-background) shrink-0 w-full flex flex-row gap-2 items-center\"\n >\n <f-checkbox\n [binary]=\"true\"\n [ngModel]=\"isSelectAll()\"\n [disabled]=\"disabled()\"\n (onChange)=\"changeSelectAll($event)\"\n />\n <input\n type=\"text\"\n fInputText\n class=\"w-full\"\n [placeholder]=\"'shared.search' | translate\"\n [ngModel]=\"searchTerm()\"\n (ngModelChange)=\"searchTerm.set($event)\"\n />\n </div>\n <div\n class=\"flex-1 overflow-y-auto min-h-0 w-full\"\n (scroll)=\"onScroll($event)\"\n >\n <ul\n class=\"m-0 py-1 list-none bg-(--f-overlay-popover-background) text-(--f-text-color) w-full\"\n >\n @for (data of mergedDataList(); track data.value) {\n @let isItemSelected = isSelected(data);\n <li\n class=\"px-3 py-2 cursor-pointer transition-colors flex flex-row gap-2 items-center text-sm\"\n [class]=\"\n isItemSelected\n ? 'bg-primary/10 text-primary'\n : 'text-(--f-text-color) hover:bg-(--f-content-hover-background)'\n \"\n >\n <f-checkbox\n [binary]=\"true\"\n [ngModel]=\"isItemSelected\"\n [disabled]=\"disabled()\"\n (onChange)=\"selectValue($event, data)\"\n />\n <span class=\"flex flex-col min-w-0 flex-1\">\n <span class=\"truncate\">{{ data.label }}</span>\n @if (data.subLabel) {\n <span class=\"truncate text-xs text-(--f-text-muted-color)\">{{\n data.subLabel\n }}</span>\n }\n </span>\n </li>\n }\n </ul>\n </div>\n </div>\n</ng-template>\n" }]
3763
4089
  }], ctorParameters: () => [], propDecorators: { pSelectRef: [{ type: i0.ViewChild, args: ['pSelect', { isSignal: true }] }], overlayTemplate: [{ type: i0.ViewChild, args: ['overlayTpl', { isSignal: true }] }], placeHolder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeHolder", required: false }] }], showClear: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClear", 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 }] }], selectedObjects: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedObjects", required: false }] }], 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"] }] } });
3764
4090
 
3765
4091
  /**
@@ -3898,7 +4224,7 @@ class LazySelectComponent extends BaseFormControl {
3898
4224
  return key;
3899
4225
  }
3900
4226
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LazySelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3901
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.5", type: LazySelectComponent, isStandalone: true, selector: "lib-lazy-select", inputs: { placeHolder: { classPropertyName: "placeHolder", publicName: "placeHolder", isSignal: true, isRequired: false, transformFunction: null }, showClear: { classPropertyName: "showClear", publicName: "showClear", 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 }, selectedObject: { classPropertyName: "selectedObject", publicName: "selectedObject", isSignal: true, isRequired: false, 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 <f-select\n class=\"w-full\"\n [options]=\"selectDataList()\"\n [optionLabel]=\"optionLabel()\"\n [optionValue]=\"optionValue()\"\n [filter]=\"true\"\n [showClear]=\"showClear()\"\n [placeholder]=\"displayPlaceholder()\"\n [disabled]=\"disabled()\"\n [(ngModel)]=\"value\"\n [selectedObject]=\"selectedObject()\"\n appEditModeElementChanger\n [isEditMode]=\"isEditMode()\"\n (click)=\"showPanel()\"\n (onBlur)=\"markAsTouched()\"\n >\n <ng-template #filter let-filter>\n <input\n fInputText\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 </f-select>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: SelectComponent, selector: "f-select", inputs: ["options", "optionLabel", "optionValue", "placeholder", "disabled", "filter", "filterBy", "filterMatchMode", "filterLocale", "filterPlaceholder", "resetFilterOnHide", "autofocusFilter", "emptyMessage", "emptyFilterMessage", "showClear", "loading", "loadingIcon", "dropdownIcon", "clearIcon", "inputId", "invalid", "touched", "styleClass", "style", "appendTo", "editable", "readonly", "autofocus", "checkmark", "group", "optionGroupLabel", "optionGroupChildren", "virtualScroll", "virtualScrollItemSize", "lazy", "selectedObject"], outputs: ["onFilter", "onChange", "onShow", "onHide", "onBlur", "onFocus", "onClear", "onLazyLoad"] }, { kind: "directive", type: InputTextDirective, selector: "[fInputText]", inputs: ["invalid", "touched", "variant", "size", "fluid"] }, { kind: "directive", type: EditModeElementChangerDirective, selector: "[appEditModeElementChanger]", inputs: ["isEditMode"] }] });
4227
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: LazySelectComponent, isStandalone: true, selector: "lib-lazy-select", inputs: { placeHolder: { classPropertyName: "placeHolder", publicName: "placeHolder", isSignal: true, isRequired: false, transformFunction: null }, showClear: { classPropertyName: "showClear", publicName: "showClear", 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 }, selectedObject: { classPropertyName: "selectedObject", publicName: "selectedObject", isSignal: true, isRequired: false, 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 <f-select\n class=\"w-full\"\n [options]=\"selectDataList()\"\n [optionLabel]=\"optionLabel()\"\n [optionValue]=\"optionValue()\"\n [filter]=\"true\"\n [showClear]=\"showClear()\"\n [placeholder]=\"displayPlaceholder()\"\n [disabled]=\"disabled()\"\n [(ngModel)]=\"value\"\n [selectedObject]=\"selectedObject()\"\n appEditModeElementChanger\n [isEditMode]=\"isEditMode()\"\n (click)=\"showPanel()\"\n (onBlur)=\"markAsTouched()\"\n >\n <ng-template #filter let-filter>\n <input\n fInputText\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 <div class=\"flex flex-col min-w-0\">\n <span class=\"truncate\">{{ item[optionLabel()] }}</span>\n @if (item.subLabel) {\n <span class=\"truncate text-xs text-(--f-text-muted-color)\">{{\n item.subLabel\n }}</span>\n }\n </div>\n </ng-template>\n </f-select>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: SelectComponent, selector: "f-select", inputs: ["options", "optionLabel", "optionValue", "placeholder", "disabled", "filter", "filterBy", "filterMatchMode", "filterLocale", "filterPlaceholder", "resetFilterOnHide", "autofocusFilter", "emptyMessage", "emptyFilterMessage", "showClear", "loading", "loadingIcon", "dropdownIcon", "clearIcon", "inputId", "invalid", "touched", "styleClass", "style", "appendTo", "editable", "readonly", "autofocus", "checkmark", "group", "optionGroupLabel", "optionGroupChildren", "virtualScroll", "virtualScrollItemSize", "lazy", "selectedObject"], outputs: ["onFilter", "onChange", "onShow", "onHide", "onBlur", "onFocus", "onClear", "onLazyLoad"] }, { kind: "directive", type: InputTextDirective, selector: "[fInputText]", inputs: ["invalid", "touched", "variant", "size", "fluid"] }, { kind: "directive", type: EditModeElementChangerDirective, selector: "[appEditModeElementChanger]", inputs: ["isEditMode"] }] });
3902
4228
  }
3903
4229
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: LazySelectComponent, decorators: [{
3904
4230
  type: Component,
@@ -3907,7 +4233,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
3907
4233
  SelectComponent,
3908
4234
  InputTextDirective,
3909
4235
  EditModeElementChangerDirective,
3910
- ], providers: [provideValueAccessor(LazySelectComponent)], template: "<div #scrollContainer class=\"lib-scroll-container\">\n <f-select\n class=\"w-full\"\n [options]=\"selectDataList()\"\n [optionLabel]=\"optionLabel()\"\n [optionValue]=\"optionValue()\"\n [filter]=\"true\"\n [showClear]=\"showClear()\"\n [placeholder]=\"displayPlaceholder()\"\n [disabled]=\"disabled()\"\n [(ngModel)]=\"value\"\n [selectedObject]=\"selectedObject()\"\n appEditModeElementChanger\n [isEditMode]=\"isEditMode()\"\n (click)=\"showPanel()\"\n (onBlur)=\"markAsTouched()\"\n >\n <ng-template #filter let-filter>\n <input\n fInputText\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 </f-select>\n</div>\n" }]
4236
+ ], providers: [provideValueAccessor(LazySelectComponent)], template: "<div #scrollContainer class=\"lib-scroll-container\">\n <f-select\n class=\"w-full\"\n [options]=\"selectDataList()\"\n [optionLabel]=\"optionLabel()\"\n [optionValue]=\"optionValue()\"\n [filter]=\"true\"\n [showClear]=\"showClear()\"\n [placeholder]=\"displayPlaceholder()\"\n [disabled]=\"disabled()\"\n [(ngModel)]=\"value\"\n [selectedObject]=\"selectedObject()\"\n appEditModeElementChanger\n [isEditMode]=\"isEditMode()\"\n (click)=\"showPanel()\"\n (onBlur)=\"markAsTouched()\"\n >\n <ng-template #filter let-filter>\n <input\n fInputText\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 <div class=\"flex flex-col min-w-0\">\n <span class=\"truncate\">{{ item[optionLabel()] }}</span>\n @if (item.subLabel) {\n <span class=\"truncate text-xs text-(--f-text-muted-color)\">{{\n item.subLabel\n }}</span>\n }\n </div>\n </ng-template>\n </f-select>\n</div>\n" }]
3911
4237
  }], ctorParameters: () => [], propDecorators: { scrollContainer: [{ type: i0.ViewChild, args: ['scrollContainer', { isSignal: true }] }], placeHolder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeHolder", required: false }] }], showClear: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClear", 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 }] }], selectedObject: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedObject", required: false }] }], 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"] }] } });
3912
4238
 
3913
4239
  class RouterTabsComponent {
@@ -4006,7 +4332,12 @@ class BaseUserSelectComponent {
4006
4332
  _knownUsers = signal(new Map(), /* @ts-ignore */
4007
4333
  ...(ngDevMode ? [{ debugName: "_knownUsers" }] : /* istanbul ignore next */ []));
4008
4334
  toDropDown(user) {
4009
- return { label: user.name || user.email, value: user.id };
4335
+ // Names collide across users; showing the email as a subLabel keeps entries distinguishable.
4336
+ return {
4337
+ label: user.name || user.email,
4338
+ value: user.id,
4339
+ subLabel: user.name ? user.email : undefined,
4340
+ };
4010
4341
  }
4011
4342
  /** Resolves a single previously-seen user id, even if it's no longer in `users()`. */
4012
4343
  resolveKnownUser(id) {
@@ -4509,11 +4840,26 @@ function getUserTimezone() {
4509
4840
  return Intl.DateTimeFormat().resolvedOptions().timeZone;
4510
4841
  }
4511
4842
 
4843
+ /** The translatable reason in a failed HTTP call: the backend's `messageKey`/`messageVariables`, or `fallbackKey` when the response carries none. */
4844
+ function errorToMessageRef(error, fallbackKey = 'shared.unexpected.error') {
4845
+ const body = error?.error;
4846
+ if (typeof body?.messageKey === 'string') {
4847
+ return body.messageVariables ? { messageKey: body.messageKey, messageVariables: body.messageVariables } : { messageKey: body.messageKey };
4848
+ }
4849
+ return { messageKey: fallbackKey };
4850
+ }
4851
+
4852
+ const HTML_ESCAPES = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' };
4853
+ /** Escapes user-authored text interpolated into markup rendered via [innerHTML] (e.g. confirm dialog messages). */
4854
+ function escapeHtml(value) {
4855
+ return value.replace(/[&<>"']/g, (ch) => HTML_ESCAPES[ch]);
4856
+ }
4857
+
4512
4858
  // Constants
4513
4859
 
4514
4860
  /**
4515
4861
  * Generated bundle index. Do not edit.
4516
4862
  */
4517
4863
 
4518
- export { ACTION_PERMISSIONS, AUTH_STATE_PROVIDER, ApiResourceService, ApiResourceService as ApiService, BRANCH_PERMISSIONS, BaseFormComponent, BaseFormControl, BaseFormTemplateDirective, BaseListComponent, BaseListTemplateDirective, BaseUserSelectComponent, COMPANY_ACTION_PERMISSIONS, COMPANY_API_PROVIDER, COMPANY_PERMISSIONS, CookieService, EMAIL_CONFIG_PERMISSIONS, EMAIL_TEMPLATE_PERMISSIONS, EVENT_PERMISSIONS, EditModeElementChangerDirective, FILE_PERMISSIONS, FILE_PROVIDER, FILE_TYPE_FILTERS, FOLDER_PERMISSIONS, FORM_PERMISSIONS, FORM_RESULT_PERMISSIONS, FaviconService, FileSelectorDialogComponent, FileUploaderComponent, FileUrlService, HasPermissionDirective, IconComponent, IconTypeEnum, InlineSvgComponent, IsEmptyImageDirective, LANGUAGE_PERMISSIONS, LazyMultiSelectComponent, LazySelectComponent, NOTIFICATION_PERMISSIONS, PERMISSIONS, PROFILE_PERMISSION_PROVIDER, PermissionValidatorService, PlatformService, PreventDefaultDirective, ROLE_ACTION_PERMISSIONS, ROLE_PERMISSIONS, RouterTabsComponent, SHARED_MESSAGES, SKIP_ERROR_TOAST, SOCIAL_AUTH_CONFIG_PERMISSIONS, STORAGE_CONFIG_PERMISSIONS, TRANSLATION_KEY_PERMISSIONS, TRANSLATION_PERMISSIONS, TranslatePipe, TranslateService, UPLOAD_CONFIG_PERMISSIONS, USER_ACTION_PERMISSIONS, USER_PERMISSIONS, USER_PERMISSION_PROVIDER, USER_PROVIDER, USER_ROLE_PERMISSIONS, UserMultiSelectComponent, UserSelectComponent, allPermissionsGuard, anyPermissionGuard, authenticatedGuard, checkScrollPagination, errorCatchingInterceptor, evaluateLogicNode, evaluatePermission, formatFileSize, formatInTimezone, getAcceptString, getFileIconClass, getUserTimezone, hasAllPermissions, hasAnyPermission, hasPermission, isFileTypeAllowed, localToUtc, permissionGuard, provideValueAccessor, resolveTranslationModule, utcToLocal };
4864
+ export { ACTION_PERMISSIONS, ACTION_PROVIDER, AUTH_STATE_PROVIDER, ActionMultiSelectComponent, ActionSelectComponent, ApiResourceService, ApiResourceService as ApiService, BRANCH_PERMISSIONS, BaseActionSelectComponent, BaseFormComponent, BaseFormControl, BaseFormTemplateDirective, BaseListComponent, BaseListTemplateDirective, BaseUserSelectComponent, COMPANY_ACTION_PERMISSIONS, COMPANY_API_PROVIDER, COMPANY_PERMISSIONS, CookieService, EMAIL_CONFIG_PERMISSIONS, EMAIL_TEMPLATE_PERMISSIONS, ENTITY_DEFINITION_PERMISSIONS, EVENT_PERMISSIONS, EditModeElementChangerDirective, FIELD_DEFINITION_PERMISSIONS, FILE_PERMISSIONS, FILE_PROVIDER, FILE_TYPE_FILTERS, FLOW_DEFINITION_PERMISSIONS, FOLDER_PERMISSIONS, FORM_PERMISSIONS, FORM_RESULT_PERMISSIONS, FaviconService, FileSelectorDialogComponent, FileUploaderComponent, FileUrlService, HasPermissionDirective, IconComponent, IconTypeEnum, InlineSvgComponent, IsEmptyImageDirective, LANGUAGE_PERMISSIONS, LazyMultiSelectComponent, LazySelectComponent, NOTIFICATION_PERMISSIONS, PERMISSIONS, PROFILE_PERMISSION_PROVIDER, PermissionValidatorService, PlatformService, PreventDefaultDirective, ROLE_ACTION_PERMISSIONS, ROLE_PERMISSIONS, RouterTabsComponent, SHARED_MESSAGES, SKIP_ERROR_TOAST, SOCIAL_AUTH_CONFIG_PERMISSIONS, STORAGE_CONFIG_PERMISSIONS, TRANSLATION_KEY_PERMISSIONS, TRANSLATION_PERMISSIONS, TranslatePipe, TranslateService, UPLOAD_CONFIG_PERMISSIONS, USER_ACTION_PERMISSIONS, USER_PERMISSIONS, USER_PERMISSION_PROVIDER, USER_PROVIDER, USER_ROLE_PERMISSIONS, UserMultiSelectComponent, UserSelectComponent, allPermissionsGuard, anyPermissionGuard, authenticatedGuard, checkScrollPagination, errorCatchingInterceptor, errorToMessageRef, escapeHtml, evaluateLogicNode, evaluatePermission, formatFileSize, formatInTimezone, getAcceptString, getFileIconClass, getUserTimezone, hasAllPermissions, hasAnyPermission, hasPermission, isErrorToastShown, isFileTypeAllowed, localToUtc, permissionGuard, provideValueAccessor, resolveTranslationModule, utcToLocal };
4519
4865
  //# sourceMappingURL=flusys-ng-shared.mjs.map