@praxisui/list 8.0.0-beta.85 → 8.0.0-beta.87
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/fesm2022/praxisui-list.mjs +106 -22
- package/package.json +6 -6
- package/types/praxisui-list.d.ts +8 -2
|
@@ -31,7 +31,7 @@ import { MatTooltipModule } from '@angular/material/tooltip';
|
|
|
31
31
|
import * as i1$1 from '@angular/forms';
|
|
32
32
|
import { FormsModule, FormControl, ReactiveFormsModule, FormGroup } from '@angular/forms';
|
|
33
33
|
import { BehaviorSubject, combineLatest, of, Subject, debounceTime, takeUntil, firstValueFrom, distinctUntilChanged as distinctUntilChanged$1, Subscription } from 'rxjs';
|
|
34
|
-
import {
|
|
34
|
+
import { map, distinctUntilChanged, switchMap, catchError, finalize, shareReplay, debounceTime as debounceTime$1, tap, take, takeUntil as takeUntil$1 } from 'rxjs/operators';
|
|
35
35
|
import { PraxisRichContent } from '@praxisui/rich-content';
|
|
36
36
|
import { SETTINGS_PANEL_DATA, SettingsPanelService } from '@praxisui/settings-panel';
|
|
37
37
|
import * as i3$3 from '@angular/material/tabs';
|
|
@@ -60,9 +60,7 @@ function evalExpr(expr, ctx, options) {
|
|
|
60
60
|
return expr.replace(/\$\{([^}]+)\}/g, (_, raw) => {
|
|
61
61
|
try {
|
|
62
62
|
const { baseExpr, pipe } = splitFirstPipe(raw);
|
|
63
|
-
const value = baseExpr
|
|
64
|
-
.split('.')
|
|
65
|
-
.reduce((acc, k) => (acc == null ? undefined : acc[k]), ctx);
|
|
63
|
+
const value = resolveTemplateExpressionValue(baseExpr, ctx);
|
|
66
64
|
let out = value == null ? '' : String(value);
|
|
67
65
|
if (pipe) {
|
|
68
66
|
const { name, args } = parsePipe(pipe);
|
|
@@ -73,7 +71,9 @@ function evalExpr(expr, ctx, options) {
|
|
|
73
71
|
}
|
|
74
72
|
if (name === 'date') {
|
|
75
73
|
const [localeArg, styleArg] = parseTwoArgs(args);
|
|
76
|
-
const dt = value
|
|
74
|
+
const dt = value
|
|
75
|
+
? new Date(value)
|
|
76
|
+
: null;
|
|
77
77
|
if (dt && !isNaN(dt.getTime())) {
|
|
78
78
|
const presentation = resolveTemplatePresentation('date', {
|
|
79
79
|
localeArg,
|
|
@@ -132,6 +132,30 @@ function evalExpr(expr, ctx, options) {
|
|
|
132
132
|
}
|
|
133
133
|
});
|
|
134
134
|
}
|
|
135
|
+
function resolveTemplateExpressionValue(expr, ctx) {
|
|
136
|
+
const candidates = expr
|
|
137
|
+
.split(/\?\?/)
|
|
138
|
+
.map((candidate) => candidate.trim())
|
|
139
|
+
.filter(Boolean);
|
|
140
|
+
for (const candidate of candidates.length ? candidates : [expr]) {
|
|
141
|
+
const value = resolvePathValue(candidate, ctx);
|
|
142
|
+
if (isPresentTemplateValue(value)) {
|
|
143
|
+
return value;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
function resolvePathValue(expr, ctx) {
|
|
149
|
+
return expr
|
|
150
|
+
.split('.')
|
|
151
|
+
.map((part) => part.trim())
|
|
152
|
+
.reduce((acc, key) => (acc == null ? undefined : acc[key]), ctx);
|
|
153
|
+
}
|
|
154
|
+
function isPresentTemplateValue(value) {
|
|
155
|
+
return typeof value === 'string'
|
|
156
|
+
? value.trim().length > 0
|
|
157
|
+
: value !== undefined && value !== null;
|
|
158
|
+
}
|
|
135
159
|
/**
|
|
136
160
|
* Evaluate a template definition with basic pipe support.
|
|
137
161
|
* Supported top-level pipes:
|
|
@@ -436,8 +460,8 @@ class ListDataService {
|
|
|
436
460
|
const changed = sig !== this.lastSig;
|
|
437
461
|
this.lastSig = sig;
|
|
438
462
|
if (changed) {
|
|
439
|
-
this.config$.next(config);
|
|
440
463
|
const ps = config.layout?.pageSize ?? 10;
|
|
464
|
+
this.config$.next(null);
|
|
441
465
|
this.pageable$.next({
|
|
442
466
|
...this.pageable$.value,
|
|
443
467
|
pageNumber: 0,
|
|
@@ -445,6 +469,7 @@ class ListDataService {
|
|
|
445
469
|
sort: config.dataSource?.sort,
|
|
446
470
|
});
|
|
447
471
|
this.query$.next(config.dataSource?.query || {});
|
|
472
|
+
this.config$.next(config);
|
|
448
473
|
}
|
|
449
474
|
}
|
|
450
475
|
refresh() {
|
|
@@ -458,7 +483,12 @@ class ListDataService {
|
|
|
458
483
|
this.pageable$,
|
|
459
484
|
this.query$,
|
|
460
485
|
this.refresh$,
|
|
461
|
-
]).pipe(
|
|
486
|
+
]).pipe(map(([cfg, pageable, query]) => ({
|
|
487
|
+
cfg,
|
|
488
|
+
pageable,
|
|
489
|
+
query,
|
|
490
|
+
signature: this.buildRequestSignature(cfg, pageable, query),
|
|
491
|
+
})), distinctUntilChanged((previous, current) => previous.signature === current.signature), switchMap(({ cfg, pageable, query }) => {
|
|
462
492
|
if (!cfg)
|
|
463
493
|
return of([]);
|
|
464
494
|
const ds = cfg.dataSource;
|
|
@@ -582,6 +612,19 @@ class ListDataService {
|
|
|
582
612
|
localData: this.buildLocalDataSignature(dataSource?.data),
|
|
583
613
|
});
|
|
584
614
|
}
|
|
615
|
+
buildRequestSignature(config, pageable, query) {
|
|
616
|
+
if (!config)
|
|
617
|
+
return 'no-config';
|
|
618
|
+
const dataSource = config.dataSource;
|
|
619
|
+
return this.safeSerialize({
|
|
620
|
+
resourcePath: dataSource?.resourcePath || '',
|
|
621
|
+
localData: this.buildLocalDataSignature(dataSource?.data),
|
|
622
|
+
query: query || {},
|
|
623
|
+
pageNumber: pageable.pageNumber,
|
|
624
|
+
pageSize: pageable.pageSize,
|
|
625
|
+
sort: pageable.sort || [],
|
|
626
|
+
});
|
|
627
|
+
}
|
|
585
628
|
buildLocalDataSignature(data) {
|
|
586
629
|
if (!Array.isArray(data))
|
|
587
630
|
return 'none';
|
|
@@ -11212,7 +11255,14 @@ class PraxisList {
|
|
|
11212
11255
|
listId;
|
|
11213
11256
|
componentInstanceId;
|
|
11214
11257
|
configPersistenceStrategy = 'local-first';
|
|
11215
|
-
|
|
11258
|
+
runtimeQueryContext;
|
|
11259
|
+
set queryContext(value) {
|
|
11260
|
+
this.runtimeQueryContext = value ?? null;
|
|
11261
|
+
this.applyRuntimeQueryContext();
|
|
11262
|
+
}
|
|
11263
|
+
get queryContext() {
|
|
11264
|
+
return this.runtimeQueryContext;
|
|
11265
|
+
}
|
|
11216
11266
|
form;
|
|
11217
11267
|
set enableCustomization(value) {
|
|
11218
11268
|
this.customizationEnabled = value;
|
|
@@ -11971,7 +12021,8 @@ class PraxisList {
|
|
|
11971
12021
|
: [];
|
|
11972
12022
|
const items = itemDefs
|
|
11973
12023
|
.map((node) => this.evaluateTemplateNode(node, item, true))
|
|
11974
|
-
.filter((node) => !!node)
|
|
12024
|
+
.filter((node) => !!node)
|
|
12025
|
+
.filter((node) => !this.isEmptyComposeNode(node));
|
|
11975
12026
|
const direction = compose.direction ||
|
|
11976
12027
|
(compose.orientation === 'vertical' ? 'column' : 'row');
|
|
11977
12028
|
return {
|
|
@@ -12012,6 +12063,15 @@ class PraxisList {
|
|
|
12012
12063
|
style: this.joinStyles(this.metricCssVar('--p-list-metric-progress-fill', color), this.metricCssVar('--p-list-metric-progress-track', progress.trackColor)),
|
|
12013
12064
|
};
|
|
12014
12065
|
}
|
|
12066
|
+
isEmptyComposeNode(node) {
|
|
12067
|
+
if (node.type === 'compose') {
|
|
12068
|
+
return !Array.isArray(node.items) || node.items.length === 0;
|
|
12069
|
+
}
|
|
12070
|
+
if (node.type === 'component')
|
|
12071
|
+
return false;
|
|
12072
|
+
const value = node.value;
|
|
12073
|
+
return value === null || value === undefined || String(value).trim() === '';
|
|
12074
|
+
}
|
|
12015
12075
|
metricLayout(metric, icon, caption, subcaption) {
|
|
12016
12076
|
const explicit = metric?.layout;
|
|
12017
12077
|
if (explicit)
|
|
@@ -13003,7 +13063,7 @@ class PraxisList {
|
|
|
13003
13063
|
}
|
|
13004
13064
|
applyRuntimeQueryContext() {
|
|
13005
13065
|
this.initializeDataStreams();
|
|
13006
|
-
const normalized = normalizePraxisDataQueryContext(this.
|
|
13066
|
+
const normalized = normalizePraxisDataQueryContext(this.runtimeQueryContext);
|
|
13007
13067
|
const baseQuery = this.config?.dataSource?.query || {};
|
|
13008
13068
|
const nextQuery = {
|
|
13009
13069
|
...baseQuery,
|
|
@@ -13051,6 +13111,7 @@ class PraxisList {
|
|
|
13051
13111
|
if (plan.runtime?.applyConfig) {
|
|
13052
13112
|
this.data.setConfig(this.config);
|
|
13053
13113
|
this.lastQuery = this.config?.dataSource?.query || {};
|
|
13114
|
+
this.lastRuntimeQueryContextSignature = '';
|
|
13054
13115
|
this.applyRuntimeQueryContext();
|
|
13055
13116
|
}
|
|
13056
13117
|
if (plan.runtime?.rebindSelection) {
|
|
@@ -13666,9 +13727,7 @@ class PraxisList {
|
|
|
13666
13727
|
}
|
|
13667
13728
|
// trackBy helpers for ngFor
|
|
13668
13729
|
trackBySection = (_, s) => s?.key ?? _;
|
|
13669
|
-
trackByItem = (i, it) => this.
|
|
13670
|
-
? (it?.[this.config.selection.compareBy] ?? i)
|
|
13671
|
-
: (it?.id ?? i);
|
|
13730
|
+
trackByItem = (i, it) => this.itemStableKey(it) ?? i;
|
|
13672
13731
|
isActionLoading(actionId, item, index) {
|
|
13673
13732
|
const key = this.buildActionLoadingKey(actionId, item, index);
|
|
13674
13733
|
return this.actionLoadingState[key] === true;
|
|
@@ -13752,13 +13811,8 @@ class PraxisList {
|
|
|
13752
13811
|
return merged;
|
|
13753
13812
|
}
|
|
13754
13813
|
buildActionLoadingKey(actionId, item, index) {
|
|
13755
|
-
const
|
|
13756
|
-
|
|
13757
|
-
? item?.[compareBy]
|
|
13758
|
-
: item?.id;
|
|
13759
|
-
if (idCandidate !== null &&
|
|
13760
|
-
idCandidate !== undefined &&
|
|
13761
|
-
idCandidate !== '') {
|
|
13814
|
+
const idCandidate = this.itemStableKey(item);
|
|
13815
|
+
if (idCandidate !== null && idCandidate !== undefined) {
|
|
13762
13816
|
return `${actionId}::${String(idCandidate)}`;
|
|
13763
13817
|
}
|
|
13764
13818
|
if (item && typeof item === 'object') {
|
|
@@ -13816,8 +13870,8 @@ class PraxisList {
|
|
|
13816
13870
|
}
|
|
13817
13871
|
}
|
|
13818
13872
|
itemExpansionKey(item, index) {
|
|
13819
|
-
const candidate = item
|
|
13820
|
-
if (candidate !== null && candidate !== undefined
|
|
13873
|
+
const candidate = this.itemStableKey(item, false);
|
|
13874
|
+
if (candidate !== null && candidate !== undefined) {
|
|
13821
13875
|
return String(candidate);
|
|
13822
13876
|
}
|
|
13823
13877
|
if (item && typeof item === 'object') {
|
|
@@ -13825,6 +13879,36 @@ class PraxisList {
|
|
|
13825
13879
|
}
|
|
13826
13880
|
return `idx-${index}`;
|
|
13827
13881
|
}
|
|
13882
|
+
itemStableKey(item, includeSelectionCompareBy = true) {
|
|
13883
|
+
if (!item || typeof item !== 'object')
|
|
13884
|
+
return null;
|
|
13885
|
+
const fields = [
|
|
13886
|
+
includeSelectionCompareBy ? this.config?.selection?.compareBy : undefined,
|
|
13887
|
+
'id',
|
|
13888
|
+
'uuid',
|
|
13889
|
+
'key',
|
|
13890
|
+
'value',
|
|
13891
|
+
'code',
|
|
13892
|
+
'codigo',
|
|
13893
|
+
'slug',
|
|
13894
|
+
'cpf',
|
|
13895
|
+
'email',
|
|
13896
|
+
].filter((field) => !!field);
|
|
13897
|
+
for (const field of fields) {
|
|
13898
|
+
const candidate = item?.[field];
|
|
13899
|
+
if (this.isStablePrimitiveKey(candidate)) {
|
|
13900
|
+
return candidate;
|
|
13901
|
+
}
|
|
13902
|
+
}
|
|
13903
|
+
return null;
|
|
13904
|
+
}
|
|
13905
|
+
isStablePrimitiveKey(value) {
|
|
13906
|
+
if (value === null || value === undefined || value === '')
|
|
13907
|
+
return false;
|
|
13908
|
+
return (typeof value === 'string' ||
|
|
13909
|
+
typeof value === 'number' ||
|
|
13910
|
+
typeof value === 'boolean');
|
|
13911
|
+
}
|
|
13828
13912
|
t(key, fallback) {
|
|
13829
13913
|
return this.i18n.t(key, undefined, fallback, PRAXIS_LIST_I18N_NAMESPACE);
|
|
13830
13914
|
}
|
package/package.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praxisui/list",
|
|
3
|
-
"version": "8.0.0-beta.
|
|
3
|
+
"version": "8.0.0-beta.87",
|
|
4
4
|
"description": "List components and helpers for Praxis UI.",
|
|
5
5
|
"peerDependencies": {
|
|
6
6
|
"@angular/common": "^21.0.0",
|
|
7
7
|
"@angular/core": "^21.0.0",
|
|
8
8
|
"@angular/material": "^21.0.0",
|
|
9
|
-
"@praxisui/dynamic-fields": "^8.0.0-beta.
|
|
9
|
+
"@praxisui/dynamic-fields": "^8.0.0-beta.87",
|
|
10
10
|
"rxjs": ">=7 <9",
|
|
11
11
|
"@angular/forms": "^21.0.0",
|
|
12
12
|
"@angular/router": "^21.0.0",
|
|
13
|
-
"@praxisui/ai": "^8.0.0-beta.
|
|
14
|
-
"@praxisui/core": "^8.0.0-beta.
|
|
15
|
-
"@praxisui/rich-content": "^8.0.0-beta.
|
|
16
|
-
"@praxisui/settings-panel": "^8.0.0-beta.
|
|
13
|
+
"@praxisui/ai": "^8.0.0-beta.87",
|
|
14
|
+
"@praxisui/core": "^8.0.0-beta.87",
|
|
15
|
+
"@praxisui/rich-content": "^8.0.0-beta.87",
|
|
16
|
+
"@praxisui/settings-panel": "^8.0.0-beta.87"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"tslib": "^2.3.0",
|
package/types/praxisui-list.d.ts
CHANGED
|
@@ -401,6 +401,7 @@ declare class ListDataService<T = any> {
|
|
|
401
401
|
getQuerySnapshot(): Record<string, any>;
|
|
402
402
|
groupedStream(): Observable<ListSection<T>[]>;
|
|
403
403
|
private buildConfigSignature;
|
|
404
|
+
private buildRequestSignature;
|
|
404
405
|
private buildLocalDataSignature;
|
|
405
406
|
private ensureObjectId;
|
|
406
407
|
private safeSerialize;
|
|
@@ -464,7 +465,9 @@ declare class PraxisList implements OnInit, OnChanges, OnDestroy {
|
|
|
464
465
|
listId: string;
|
|
465
466
|
componentInstanceId?: string;
|
|
466
467
|
configPersistenceStrategy: 'local-first' | 'input-first';
|
|
467
|
-
|
|
468
|
+
private runtimeQueryContext?;
|
|
469
|
+
set queryContext(value: PraxisDataQueryContext | null | undefined);
|
|
470
|
+
get queryContext(): PraxisDataQueryContext | null | undefined;
|
|
468
471
|
form?: FormGroup | null;
|
|
469
472
|
set enableCustomization(value: boolean);
|
|
470
473
|
get enableCustomization(): boolean;
|
|
@@ -614,6 +617,7 @@ declare class PraxisList implements OnInit, OnChanges, OnDestroy {
|
|
|
614
617
|
private buildMetricView;
|
|
615
618
|
private buildComposeView;
|
|
616
619
|
private buildMetricProgress;
|
|
620
|
+
private isEmptyComposeNode;
|
|
617
621
|
private metricLayout;
|
|
618
622
|
private metricAlign;
|
|
619
623
|
private metricIconPosition;
|
|
@@ -793,7 +797,7 @@ declare class PraxisList implements OnInit, OnChanges, OnDestroy {
|
|
|
793
797
|
ratingIconStyle(def?: any): string;
|
|
794
798
|
ratingRange(def?: any): number[];
|
|
795
799
|
trackBySection: (_: number, s: ListSection<any>) => string | number;
|
|
796
|
-
trackByItem: (i: number, it: any) =>
|
|
800
|
+
trackByItem: (i: number, it: any) => string | number | boolean;
|
|
797
801
|
isActionLoading(actionId: string, item: any, index: number): boolean;
|
|
798
802
|
private evaluateActionVisibility;
|
|
799
803
|
private evaluateRuntimeCondition;
|
|
@@ -807,6 +811,8 @@ declare class PraxisList implements OnInit, OnChanges, OnDestroy {
|
|
|
807
811
|
private toggleExpanded;
|
|
808
812
|
private syncExpansionState;
|
|
809
813
|
private itemExpansionKey;
|
|
814
|
+
private itemStableKey;
|
|
815
|
+
private isStablePrimitiveKey;
|
|
810
816
|
t(key: string, fallback: string): string;
|
|
811
817
|
private tWithLocale;
|
|
812
818
|
private ensureExpansionItemObjectId;
|