@flusys/ng-shared 8.1.4 → 9.1.0-rc.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.
- package/README.md +42 -1
- package/fesm2022/flusys-ng-shared.mjs +461 -115
- package/fesm2022/flusys-ng-shared.mjs.map +1 -1
- package/package.json +4 -3
- package/types/flusys-ng-shared.d.ts +124 -5
|
@@ -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,
|
|
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,
|
|
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
|
|
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
|
|
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
|
-
//
|
|
1032
|
-
detail = translateAdapter.
|
|
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
|
-
|
|
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.
|
|
1357
|
+
return this.post('insert', dto);
|
|
1308
1358
|
}
|
|
1309
1359
|
insertMany(dtos) {
|
|
1310
|
-
return this.
|
|
1360
|
+
return this.post('insert-many', dtos);
|
|
1311
1361
|
}
|
|
1312
1362
|
findById(id, select) {
|
|
1313
|
-
return this.
|
|
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.
|
|
1369
|
+
return this.post('update', dto);
|
|
1320
1370
|
}
|
|
1321
1371
|
updateMany(dtos) {
|
|
1322
|
-
return this.
|
|
1372
|
+
return this.post('update-many', dtos);
|
|
1323
1373
|
}
|
|
1324
1374
|
delete(deleteDto) {
|
|
1325
|
-
return this.
|
|
1375
|
+
return this.post('delete', deleteDto);
|
|
1326
1376
|
}
|
|
1327
1377
|
findByIds(ids, select) {
|
|
1328
|
-
return this.
|
|
1378
|
+
return this.post('get-by-ids', { ids, select });
|
|
1329
1379
|
}
|
|
1330
1380
|
getByFilter(filter) {
|
|
1331
|
-
return this.
|
|
1381
|
+
return this.post('get-by-filter', filter);
|
|
1332
1382
|
}
|
|
1333
1383
|
async bulkUpsert(toInsert, toUpdate) {
|
|
1334
|
-
const res = await this.
|
|
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.
|
|
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
|
-
|
|
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 = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' };
|
|
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
|