@praxisui/list 1.0.0-beta.3 → 1.0.0-beta.30
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 +7 -0
- package/fesm2022/praxisui-list.mjs +662 -36
- package/fesm2022/praxisui-list.mjs.map +1 -1
- package/index.d.ts +68 -1
- package/package.json +4 -4
|
@@ -12,25 +12,27 @@ import { MatChipsModule } from '@angular/material/chips';
|
|
|
12
12
|
import { MatDividerModule } from '@angular/material/divider';
|
|
13
13
|
import * as i7 from '@angular/material/button';
|
|
14
14
|
import { MatButtonModule } from '@angular/material/button';
|
|
15
|
+
import * as i4$1 from '@angular/material/form-field';
|
|
16
|
+
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
17
|
+
import * as i6 from '@angular/material/select';
|
|
18
|
+
import { MatSelectModule } from '@angular/material/select';
|
|
19
|
+
import * as i5 from '@angular/material/input';
|
|
20
|
+
import { MatInputModule } from '@angular/material/input';
|
|
15
21
|
import * as i2 from '@angular/forms';
|
|
16
22
|
import { FormsModule, FormControl, ReactiveFormsModule } from '@angular/forms';
|
|
17
|
-
import { BehaviorSubject, combineLatest, of, Subject } from 'rxjs';
|
|
23
|
+
import { BehaviorSubject, combineLatest, of, Subject, debounceTime as debounceTime$1, distinctUntilChanged as distinctUntilChanged$1 } from 'rxjs';
|
|
18
24
|
import { auditTime, switchMap, map, catchError, finalize, shareReplay, debounceTime, distinctUntilChanged, tap } from 'rxjs/operators';
|
|
19
25
|
import { SETTINGS_PANEL_DATA, SettingsPanelService } from '@praxisui/settings-panel';
|
|
20
26
|
import * as i3$1 from '@angular/material/tabs';
|
|
21
27
|
import { MatTabsModule } from '@angular/material/tabs';
|
|
22
|
-
import * as i4$1 from '@angular/material/form-field';
|
|
23
|
-
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
24
|
-
import * as i5 from '@angular/material/input';
|
|
25
|
-
import { MatInputModule } from '@angular/material/input';
|
|
26
|
-
import * as i6 from '@angular/material/select';
|
|
27
|
-
import { MatSelectModule } from '@angular/material/select';
|
|
28
28
|
import * as i8 from '@angular/material/slide-toggle';
|
|
29
29
|
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
|
30
30
|
import * as i11 from '@angular/material/button-toggle';
|
|
31
31
|
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
|
32
32
|
import * as i12 from '@angular/material/tooltip';
|
|
33
33
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
34
|
+
import * as i15 from '@angular/material/menu';
|
|
35
|
+
import { MatMenuModule } from '@angular/material/menu';
|
|
34
36
|
import * as i10 from '@angular/material/expansion';
|
|
35
37
|
import { MatExpansionModule } from '@angular/material/expansion';
|
|
36
38
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
@@ -43,13 +45,49 @@ import { produce } from 'immer';
|
|
|
43
45
|
function evalExpr(expr, ctx) {
|
|
44
46
|
if (!expr)
|
|
45
47
|
return '';
|
|
46
|
-
// Replace ${...} with values from context (item)
|
|
47
|
-
return expr.replace(/\$\{([^}]+)\}/g, (_,
|
|
48
|
+
// Replace ${...} with values from context (item), supporting simple pipes inside placeholders
|
|
49
|
+
return expr.replace(/\$\{([^}]+)\}/g, (_, raw) => {
|
|
48
50
|
try {
|
|
49
|
-
const
|
|
51
|
+
const { baseExpr, pipe } = splitFirstPipe(raw);
|
|
52
|
+
const value = baseExpr
|
|
50
53
|
.split('.')
|
|
51
54
|
.reduce((acc, k) => (acc == null ? undefined : acc[k]), ctx);
|
|
52
|
-
|
|
55
|
+
let out = value == null ? '' : String(value);
|
|
56
|
+
if (pipe) {
|
|
57
|
+
const { name, args } = parsePipe(pipe);
|
|
58
|
+
if (name === 'bool') {
|
|
59
|
+
const [trueLabel, falseLabel] = parseTwoArgs(args);
|
|
60
|
+
const truthy = toBoolean(value);
|
|
61
|
+
out = truthy ? (trueLabel ?? 'Sim') : (falseLabel ?? 'Não');
|
|
62
|
+
}
|
|
63
|
+
if (name === 'date') {
|
|
64
|
+
const [localeArg, styleArg] = parseTwoArgs(args);
|
|
65
|
+
const dt = value ? new Date(value) : null;
|
|
66
|
+
if (dt && !isNaN(dt.getTime())) {
|
|
67
|
+
const fmt = new Intl.DateTimeFormat(localeArg || undefined, mapDateStyle(styleArg)).format(dt);
|
|
68
|
+
out = fmt;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
out = '';
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (name === 'map') {
|
|
75
|
+
const map = parseMap(args);
|
|
76
|
+
const key = String(value ?? '').trim();
|
|
77
|
+
out = map[key] ?? map[key.toLowerCase?.()] ?? map[key.toUpperCase?.()] ?? out;
|
|
78
|
+
}
|
|
79
|
+
if (name === 'number') {
|
|
80
|
+
const [localeArg, styleArg] = parseTwoArgs(args);
|
|
81
|
+
const num = Number(value);
|
|
82
|
+
if (isFinite(num)) {
|
|
83
|
+
const opt = {};
|
|
84
|
+
if ((styleArg || '').toLowerCase() === 'compact')
|
|
85
|
+
opt.notation = 'compact';
|
|
86
|
+
out = new Intl.NumberFormat(localeArg || undefined, opt).format(num);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return out;
|
|
53
91
|
}
|
|
54
92
|
catch {
|
|
55
93
|
return '';
|
|
@@ -64,11 +102,9 @@ function evalExpr(expr, ctx) {
|
|
|
64
102
|
function evaluateTemplate(def, item) {
|
|
65
103
|
if (!def)
|
|
66
104
|
return null;
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
const { baseExpr, pipe } = splitFirstPipe(def.expr);
|
|
105
|
+
// Support a top-level pipe outside of template placeholders,
|
|
106
|
+
// e.g.: "${item.active}|bool:Ativo:Inativo".
|
|
107
|
+
const { baseExpr, pipe } = splitFirstTopLevelPipe(def.expr);
|
|
72
108
|
let value = evalExpr(baseExpr, { item });
|
|
73
109
|
if (pipe) {
|
|
74
110
|
const { name, args } = parsePipe(pipe);
|
|
@@ -80,7 +116,7 @@ function evaluateTemplate(def, item) {
|
|
|
80
116
|
}
|
|
81
117
|
const out = { type: def.type, value, class: def.class, style: def.style, color: def.color, variant: def.variant };
|
|
82
118
|
if (def.badge?.expr) {
|
|
83
|
-
const { baseExpr: b2, pipe: p2 } =
|
|
119
|
+
const { baseExpr: b2, pipe: p2 } = splitFirstTopLevelPipe(def.badge.expr);
|
|
84
120
|
let badgeVal = evalExpr(b2, { item });
|
|
85
121
|
if (p2) {
|
|
86
122
|
const { name, args } = parsePipe(p2);
|
|
@@ -104,6 +140,26 @@ function splitFirstPipe(expr) {
|
|
|
104
140
|
return { baseExpr: expr.trim() };
|
|
105
141
|
return { baseExpr: expr.slice(0, idx).trim(), pipe: expr.slice(idx + 1).trim() };
|
|
106
142
|
}
|
|
143
|
+
// Find first '|' that is NOT inside a "${...}" placeholder
|
|
144
|
+
function splitFirstTopLevelPipe(expr) {
|
|
145
|
+
let inTpl = 0;
|
|
146
|
+
for (let i = 0; i < expr.length; i++) {
|
|
147
|
+
const c = expr[i];
|
|
148
|
+
if (c === '$' && expr[i + 1] === '{') {
|
|
149
|
+
inTpl++;
|
|
150
|
+
i++;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (c === '}' && inTpl > 0) {
|
|
154
|
+
inTpl--;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (c === '|' && inTpl === 0) {
|
|
158
|
+
return { baseExpr: expr.slice(0, i).trim(), pipe: expr.slice(i + 1).trim() };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return { baseExpr: expr.trim() };
|
|
162
|
+
}
|
|
107
163
|
function parsePipe(pipeSpec) {
|
|
108
164
|
const idx = pipeSpec.indexOf(':');
|
|
109
165
|
if (idx === -1)
|
|
@@ -124,6 +180,35 @@ function toBoolean(val) {
|
|
|
124
180
|
const s = String(val).trim().toLowerCase();
|
|
125
181
|
return s === 'true' || s === '1' || s === 'yes' || s === 'sim' || s === 'on';
|
|
126
182
|
}
|
|
183
|
+
function mapDateStyle(style) {
|
|
184
|
+
switch ((style || '').toLowerCase()) {
|
|
185
|
+
case 'short':
|
|
186
|
+
return { dateStyle: 'short' };
|
|
187
|
+
case 'long':
|
|
188
|
+
return { dateStyle: 'long' };
|
|
189
|
+
case 'full':
|
|
190
|
+
return { dateStyle: 'full' };
|
|
191
|
+
case 'medium':
|
|
192
|
+
default:
|
|
193
|
+
return { dateStyle: 'medium' };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function parseMap(spec) {
|
|
197
|
+
const out = {};
|
|
198
|
+
if (!spec)
|
|
199
|
+
return out;
|
|
200
|
+
const entries = spec.split(/[,;|]/g).map(s => s.trim()).filter(Boolean);
|
|
201
|
+
for (const e of entries) {
|
|
202
|
+
const idx = e.indexOf('=');
|
|
203
|
+
if (idx === -1)
|
|
204
|
+
continue;
|
|
205
|
+
const k = e.slice(0, idx).trim();
|
|
206
|
+
const v = e.slice(idx + 1).trim();
|
|
207
|
+
if (k)
|
|
208
|
+
out[k] = v;
|
|
209
|
+
}
|
|
210
|
+
return out;
|
|
211
|
+
}
|
|
127
212
|
|
|
128
213
|
function adaptSelection(config, items) {
|
|
129
214
|
const mode = (config?.selection?.mode ?? 'none');
|
|
@@ -153,6 +238,7 @@ class ListDataService {
|
|
|
153
238
|
query$ = new BehaviorSubject({});
|
|
154
239
|
loading$ = new BehaviorSubject(false);
|
|
155
240
|
total$ = new BehaviorSubject(0);
|
|
241
|
+
pageState$ = this.pageable$.asObservable();
|
|
156
242
|
lastSig = '';
|
|
157
243
|
crud = inject(GenericCrudService, {
|
|
158
244
|
optional: true,
|
|
@@ -253,6 +339,9 @@ class ListDataService {
|
|
|
253
339
|
}
|
|
254
340
|
setQuery(q) {
|
|
255
341
|
this.query$.next(q || {});
|
|
342
|
+
// Reset to first page when query changes
|
|
343
|
+
const p = this.pageable$.value;
|
|
344
|
+
this.pageable$.next({ ...p, pageNumber: 0 });
|
|
256
345
|
}
|
|
257
346
|
groupedStream() {
|
|
258
347
|
return this.stream().pipe(switchMap((items) => this.config$.pipe(map((cfg) => cfg?.layout?.groupBy), map((groupBy) => {
|
|
@@ -454,6 +543,7 @@ class PraxisListConfigEditor {
|
|
|
454
543
|
selection: { mode: 'none', return: 'value' },
|
|
455
544
|
skin: { type: 'elevated', gradient: { from: '', to: '', angle: 135 } },
|
|
456
545
|
i18n: { locale: 'en-US', currency: 'USD' },
|
|
546
|
+
ui: {},
|
|
457
547
|
};
|
|
458
548
|
isDirty$ = new BehaviorSubject(false);
|
|
459
549
|
isValid$ = new BehaviorSubject(true);
|
|
@@ -472,6 +562,10 @@ class PraxisListConfigEditor {
|
|
|
472
562
|
mappingLeading = { type: 'icon' };
|
|
473
563
|
mapping = {};
|
|
474
564
|
mappingDirty = false;
|
|
565
|
+
statusPosition = undefined;
|
|
566
|
+
iconColorMapEntries = [];
|
|
567
|
+
// UI (search/sort/range) editor state
|
|
568
|
+
uiSortRows = [];
|
|
475
569
|
// Meta composition (multi-field)
|
|
476
570
|
mappingMetaFields = [];
|
|
477
571
|
mappingMetaSeparator = ' • ';
|
|
@@ -491,6 +585,7 @@ class PraxisListConfigEditor {
|
|
|
491
585
|
this.working = this.normalize(structuredClone(cfg));
|
|
492
586
|
// Initialize mapping UI from existing templating
|
|
493
587
|
this.hydrateMappingFromTemplating(this.working.templating);
|
|
588
|
+
this.hydrateUiEditorFromConfig();
|
|
494
589
|
}
|
|
495
590
|
this.initialJson = JSON.stringify(this.working);
|
|
496
591
|
this.lastJson = this.initialJson;
|
|
@@ -571,6 +666,7 @@ class PraxisListConfigEditor {
|
|
|
571
666
|
class: cfg.skin?.class,
|
|
572
667
|
},
|
|
573
668
|
i18n: cfg.i18n || {},
|
|
669
|
+
ui: cfg.ui || {},
|
|
574
670
|
};
|
|
575
671
|
return out;
|
|
576
672
|
}
|
|
@@ -746,6 +842,72 @@ class PraxisListConfigEditor {
|
|
|
746
842
|
this.markDirty();
|
|
747
843
|
this.verify();
|
|
748
844
|
}
|
|
845
|
+
onUiChanged() {
|
|
846
|
+
this.markDirty();
|
|
847
|
+
this.verify();
|
|
848
|
+
}
|
|
849
|
+
addUiSortRow() {
|
|
850
|
+
this.uiSortRows = [...(this.uiSortRows || []), { label: '', field: '', dir: 'desc' }];
|
|
851
|
+
this.onUiSortRowsChanged();
|
|
852
|
+
}
|
|
853
|
+
removeUiSortRow(i) {
|
|
854
|
+
const arr = [...(this.uiSortRows || [])];
|
|
855
|
+
arr.splice(i, 1);
|
|
856
|
+
this.uiSortRows = arr;
|
|
857
|
+
this.onUiSortRowsChanged();
|
|
858
|
+
}
|
|
859
|
+
onUiSortRowsChanged() {
|
|
860
|
+
// Map rows to config.ui.sortOptions
|
|
861
|
+
const opts = (this.uiSortRows || [])
|
|
862
|
+
.filter(r => (r.field || '').trim())
|
|
863
|
+
.map(r => ({ label: (r.label || '').trim() || `${r.field},${r.dir || 'desc'}`, value: `${r.field},${r.dir || 'desc'}` }));
|
|
864
|
+
this.working = produce(this.working, (draft) => {
|
|
865
|
+
(draft.ui ||= {}).sortOptions = opts;
|
|
866
|
+
});
|
|
867
|
+
this.onUiChanged();
|
|
868
|
+
}
|
|
869
|
+
isUiSortRowDuplicate(index) {
|
|
870
|
+
const rows = this.uiSortRows || [];
|
|
871
|
+
const key = `${rows[index]?.field || ''},${rows[index]?.dir || 'desc'}`.trim();
|
|
872
|
+
if (!key || !rows[index]?.field)
|
|
873
|
+
return false;
|
|
874
|
+
let count = 0;
|
|
875
|
+
for (const r of rows) {
|
|
876
|
+
const k = `${r?.field || ''},${r?.dir || 'desc'}`.trim();
|
|
877
|
+
if (k === key)
|
|
878
|
+
count++;
|
|
879
|
+
}
|
|
880
|
+
return count > 1;
|
|
881
|
+
}
|
|
882
|
+
isIconColorDuplicate(index) {
|
|
883
|
+
const arr = this.iconColorMapEntries || [];
|
|
884
|
+
const key = (arr[index]?.key || '').trim();
|
|
885
|
+
if (!key)
|
|
886
|
+
return false;
|
|
887
|
+
let count = 0;
|
|
888
|
+
for (const e of arr) {
|
|
889
|
+
if ((e?.key || '').trim() === key)
|
|
890
|
+
count++;
|
|
891
|
+
}
|
|
892
|
+
return count > 1;
|
|
893
|
+
}
|
|
894
|
+
hydrateUiEditorFromConfig() {
|
|
895
|
+
const ui = (this.working.ui ||= {});
|
|
896
|
+
const arr = ui.sortOptions || [];
|
|
897
|
+
const rows = [];
|
|
898
|
+
for (const op of arr) {
|
|
899
|
+
if (typeof op === 'string') {
|
|
900
|
+
const [f, d] = op.split(',');
|
|
901
|
+
rows.push({ label: op, field: f, dir: d || 'asc' });
|
|
902
|
+
}
|
|
903
|
+
else if (op && typeof op === 'object') {
|
|
904
|
+
const v = String(op.value || '');
|
|
905
|
+
const [f, d] = v.split(',');
|
|
906
|
+
rows.push({ label: op.label, field: f, dir: d || 'asc' });
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
this.uiSortRows = rows;
|
|
910
|
+
}
|
|
749
911
|
// Legacy method retained to avoid breaking references; no-op now.
|
|
750
912
|
loadFieldsIfNeeded() { }
|
|
751
913
|
updateSortConfig() {
|
|
@@ -767,8 +929,10 @@ class PraxisListConfigEditor {
|
|
|
767
929
|
if (!slot?.field)
|
|
768
930
|
return undefined;
|
|
769
931
|
const base = '${item.' + slot.field + '}';
|
|
770
|
-
if (slot.type === 'text')
|
|
771
|
-
|
|
932
|
+
if (slot.type === 'text') {
|
|
933
|
+
const pipe = (slot.extraPipe || '').trim();
|
|
934
|
+
return { type: 'text', expr: pipe ? base + '|' + pipe : base, class: slot.class, style: slot.style };
|
|
935
|
+
}
|
|
772
936
|
if (slot.type === 'chip')
|
|
773
937
|
return { type: 'chip', expr: base, class: slot.class, style: slot.style, color: slot.chipColor, variant: slot.chipVariant };
|
|
774
938
|
if (slot.type === 'rating')
|
|
@@ -791,6 +955,11 @@ class PraxisListConfigEditor {
|
|
|
791
955
|
param = ':' + style;
|
|
792
956
|
return { type: 'date', expr: param ? base + '|' + param : base, class: slot.class, style: slot.style };
|
|
793
957
|
}
|
|
958
|
+
if (slot.type === 'icon') {
|
|
959
|
+
const pipe = (slot.extraPipe || '').trim();
|
|
960
|
+
const expr = pipe ? base + '|' + pipe : base;
|
|
961
|
+
return { type: 'icon', expr, class: slot.class, style: slot.style };
|
|
962
|
+
}
|
|
794
963
|
return undefined;
|
|
795
964
|
};
|
|
796
965
|
const p = build(this.mappingPrimary);
|
|
@@ -839,6 +1008,22 @@ class PraxisListConfigEditor {
|
|
|
839
1008
|
t.metaPlacement = this.mappingMeta.placement;
|
|
840
1009
|
}
|
|
841
1010
|
t.metaPrefixIcon = (this.mappingMetaPrefixIcon || '').trim() || undefined;
|
|
1011
|
+
// Status overlay and icon color map
|
|
1012
|
+
t.statusPosition = this.statusPosition || undefined;
|
|
1013
|
+
if (this.iconColorMapEntries && this.iconColorMapEntries.length) {
|
|
1014
|
+
const map = {};
|
|
1015
|
+
for (const e of this.iconColorMapEntries) {
|
|
1016
|
+
const k = (e.key || '').trim();
|
|
1017
|
+
if (!k)
|
|
1018
|
+
continue;
|
|
1019
|
+
if (e.color)
|
|
1020
|
+
map[k] = e.color;
|
|
1021
|
+
}
|
|
1022
|
+
t.iconColorMap = Object.keys(map).length ? map : undefined;
|
|
1023
|
+
}
|
|
1024
|
+
else {
|
|
1025
|
+
t.iconColorMap = undefined;
|
|
1026
|
+
}
|
|
842
1027
|
// Apply features
|
|
843
1028
|
if (this.features && this.features.length) {
|
|
844
1029
|
t.features = this.features
|
|
@@ -884,6 +1069,37 @@ class PraxisListConfigEditor {
|
|
|
884
1069
|
this.mappingDirty = true;
|
|
885
1070
|
this.isDirty$.next(true);
|
|
886
1071
|
}
|
|
1072
|
+
// Icon color map editor helpers
|
|
1073
|
+
addIconColorEntry() {
|
|
1074
|
+
this.iconColorMapEntries = [...(this.iconColorMapEntries || []), { key: '', color: undefined }];
|
|
1075
|
+
this.onIconColorMapChanged();
|
|
1076
|
+
}
|
|
1077
|
+
removeIconColorEntry(i) {
|
|
1078
|
+
const arr = [...(this.iconColorMapEntries || [])];
|
|
1079
|
+
arr.splice(i, 1);
|
|
1080
|
+
this.iconColorMapEntries = arr;
|
|
1081
|
+
this.onIconColorMapChanged();
|
|
1082
|
+
}
|
|
1083
|
+
onIconColorMapChanged() {
|
|
1084
|
+
// Normalize: last value wins, remove vazios
|
|
1085
|
+
const map = new Map();
|
|
1086
|
+
for (const e of (this.iconColorMapEntries || [])) {
|
|
1087
|
+
const k = (e?.key || '').trim();
|
|
1088
|
+
if (!k)
|
|
1089
|
+
continue;
|
|
1090
|
+
map.set(k, e?.color || undefined);
|
|
1091
|
+
}
|
|
1092
|
+
this.iconColorMapEntries = Array.from(map.entries()).map(([key, color]) => ({ key, color }));
|
|
1093
|
+
this.mappingDirty = true;
|
|
1094
|
+
this.isDirty$.next(true);
|
|
1095
|
+
}
|
|
1096
|
+
// Pipe assistant helper: sets extraPipe for a given mapping and marks dirty
|
|
1097
|
+
setPipe(target, spec) {
|
|
1098
|
+
if (!target)
|
|
1099
|
+
return;
|
|
1100
|
+
target.extraPipe = spec;
|
|
1101
|
+
this.onMappingChanged();
|
|
1102
|
+
}
|
|
887
1103
|
// ===============
|
|
888
1104
|
// Expr helpers (Features): click-to-insert fields and literals
|
|
889
1105
|
// ===============
|
|
@@ -962,6 +1178,8 @@ class PraxisListConfigEditor {
|
|
|
962
1178
|
Object.assign(base, parseCurrency(param));
|
|
963
1179
|
if (templ.primary.type === 'date')
|
|
964
1180
|
Object.assign(base, parseDate(param));
|
|
1181
|
+
if (templ.primary.type === 'text')
|
|
1182
|
+
base.extraPipe = param;
|
|
965
1183
|
this.mappingPrimary = base;
|
|
966
1184
|
}
|
|
967
1185
|
if (templ.secondary) {
|
|
@@ -971,6 +1189,8 @@ class PraxisListConfigEditor {
|
|
|
971
1189
|
Object.assign(base, parseCurrency(param));
|
|
972
1190
|
if (templ.secondary.type === 'date')
|
|
973
1191
|
Object.assign(base, parseDate(param));
|
|
1192
|
+
if (templ.secondary.type === 'text')
|
|
1193
|
+
base.extraPipe = param;
|
|
974
1194
|
this.mappingSecondary = base;
|
|
975
1195
|
}
|
|
976
1196
|
if (templ.meta) {
|
|
@@ -980,6 +1200,8 @@ class PraxisListConfigEditor {
|
|
|
980
1200
|
Object.assign(base, parseCurrency(param));
|
|
981
1201
|
if (templ.meta.type === 'date')
|
|
982
1202
|
Object.assign(base, parseDate(param));
|
|
1203
|
+
if (templ.meta.type === 'text')
|
|
1204
|
+
base.extraPipe = param;
|
|
983
1205
|
this.mappingMeta = base;
|
|
984
1206
|
if (templ.metaPlacement)
|
|
985
1207
|
this.mappingMeta.placement = templ.metaPlacement;
|
|
@@ -1007,12 +1229,27 @@ class PraxisListConfigEditor {
|
|
|
1007
1229
|
Object.assign(base, parseCurrency(param));
|
|
1008
1230
|
if (templ.trailing.type === 'date')
|
|
1009
1231
|
Object.assign(base, parseDate(param));
|
|
1232
|
+
if (templ.trailing.type === 'icon' || templ.trailing.type === 'text')
|
|
1233
|
+
base.extraPipe = param;
|
|
1010
1234
|
this.mappingTrailing = base;
|
|
1011
1235
|
if (templ.trailing?.color)
|
|
1012
1236
|
this.mappingTrailing.chipColor = templ.trailing.color;
|
|
1013
1237
|
if (templ.trailing?.variant)
|
|
1014
1238
|
this.mappingTrailing.chipVariant = templ.trailing.variant;
|
|
1015
1239
|
}
|
|
1240
|
+
// Overlay position and icon colors
|
|
1241
|
+
this.statusPosition = templ?.statusPosition;
|
|
1242
|
+
const icm = templ?.iconColorMap;
|
|
1243
|
+
this.iconColorMapEntries = Object.entries(icm || {}).map(([k, v]) => ({ key: k, color: v }));
|
|
1244
|
+
if ((this.mappingTrailing?.field || '').toLowerCase() === 'status' && (!this.iconColorMapEntries || this.iconColorMapEntries.length === 0)) {
|
|
1245
|
+
this.iconColorMapEntries = [
|
|
1246
|
+
{ key: 'PLANEJADA', color: 'primary' },
|
|
1247
|
+
{ key: 'EM_ANDAMENTO', color: 'accent' },
|
|
1248
|
+
{ key: 'PAUSADA', color: 'accent' },
|
|
1249
|
+
{ key: 'CONCLUIDA', color: 'primary' },
|
|
1250
|
+
{ key: 'FALHOU', color: 'warn' },
|
|
1251
|
+
];
|
|
1252
|
+
}
|
|
1016
1253
|
if (templ.leading?.type === 'icon' && typeof templ.leading.expr === 'string') {
|
|
1017
1254
|
this.mappingLeading = { type: 'icon', icon: templ.leading.expr };
|
|
1018
1255
|
}
|
|
@@ -1056,8 +1293,27 @@ class PraxisListConfigEditor {
|
|
|
1056
1293
|
const compareBy = (this.working?.selection?.compareBy || '').trim();
|
|
1057
1294
|
if (compareBy && this.fields.length && !this.fields.includes(compareBy))
|
|
1058
1295
|
valid = false;
|
|
1296
|
+
// Duplicatas em opções de ordenação (UI) tornam inválido
|
|
1297
|
+
if (valid && this.working?.ui?.showSort) {
|
|
1298
|
+
const rows = this.uiSortRows || [];
|
|
1299
|
+
const seen = new Map();
|
|
1300
|
+
for (const r of rows) {
|
|
1301
|
+
const k = `${(r?.field || '').trim()},${(r?.dir || 'desc').trim()}`;
|
|
1302
|
+
if (!r?.field)
|
|
1303
|
+
continue;
|
|
1304
|
+
seen.set(k, (seen.get(k) || 0) + 1);
|
|
1305
|
+
}
|
|
1306
|
+
for (const [, count] of seen) {
|
|
1307
|
+
if (count > 1) {
|
|
1308
|
+
valid = false;
|
|
1309
|
+
break;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1059
1313
|
this.isValid$.next(valid);
|
|
1060
1314
|
}
|
|
1315
|
+
// UI editor hydration already called in ctor
|
|
1316
|
+
// Ensure UI.sortOptions are kept when saving via Settings Panel
|
|
1061
1317
|
inferFromFields() {
|
|
1062
1318
|
if (!this.fields?.length)
|
|
1063
1319
|
return;
|
|
@@ -1155,6 +1411,52 @@ class PraxisListConfigEditor {
|
|
|
1155
1411
|
</mat-select>
|
|
1156
1412
|
</mat-form-field>
|
|
1157
1413
|
</div>
|
|
1414
|
+
<mat-divider class="my-8"></mat-divider>
|
|
1415
|
+
<div class="subtitle">UI (Buscar / Ordenar / Rodapé)</div>
|
|
1416
|
+
<div class="g g-auto-220 gap-12 ai-end">
|
|
1417
|
+
<mat-slide-toggle [(ngModel)]="working.ui.showSearch" (ngModelChange)="onUiChanged()">Mostrar busca</mat-slide-toggle>
|
|
1418
|
+
<mat-slide-toggle [(ngModel)]="working.ui.showSort" (ngModelChange)="onUiChanged()">Mostrar ordenar</mat-slide-toggle>
|
|
1419
|
+
<mat-slide-toggle [(ngModel)]="working.ui.showRange" (ngModelChange)="onUiChanged()">Mostrar faixa X–Y de Total</mat-slide-toggle>
|
|
1420
|
+
</div>
|
|
1421
|
+
<div class="g g-auto-220 gap-12 ai-end mt-12" *ngIf="working.ui?.showSearch">
|
|
1422
|
+
<mat-form-field appearance="outline">
|
|
1423
|
+
<mat-label>Campo para buscar</mat-label>
|
|
1424
|
+
<mat-select [(ngModel)]="working.ui.searchField" (ngModelChange)="onUiChanged()">
|
|
1425
|
+
<mat-option *ngFor="let f of fields" [value]="f">{{ f }}</mat-option>
|
|
1426
|
+
</mat-select>
|
|
1427
|
+
</mat-form-field>
|
|
1428
|
+
<mat-form-field appearance="outline">
|
|
1429
|
+
<mat-label>Placeholder</mat-label>
|
|
1430
|
+
<input matInput [(ngModel)]="working.ui.searchPlaceholder" (ngModelChange)="onUiChanged()" placeholder="ex.: Buscar por título" />
|
|
1431
|
+
</mat-form-field>
|
|
1432
|
+
</div>
|
|
1433
|
+
<div class="mt-12" *ngIf="working.ui?.showSort">
|
|
1434
|
+
<div class="g g-1-auto ai-center gap-8">
|
|
1435
|
+
<div class="muted">Opções de ordenação (rótulo → campo+direção)</div>
|
|
1436
|
+
<button mat-flat-button color="primary" (click)="addUiSortRow()">Adicionar opção</button>
|
|
1437
|
+
</div>
|
|
1438
|
+
<div class="g g-auto-220 gap-12 ai-end mt-12" *ngFor="let r of uiSortRows; let i = index">
|
|
1439
|
+
<mat-form-field appearance="outline">
|
|
1440
|
+
<mat-label>Rótulo</mat-label>
|
|
1441
|
+
<input matInput [(ngModel)]="r.label" (ngModelChange)="onUiSortRowsChanged()" placeholder="ex.: Mais recentes" />
|
|
1442
|
+
</mat-form-field>
|
|
1443
|
+
<mat-form-field appearance="outline">
|
|
1444
|
+
<mat-label>Campo</mat-label>
|
|
1445
|
+
<mat-select [(ngModel)]="r.field" (ngModelChange)="onUiSortRowsChanged()">
|
|
1446
|
+
<mat-option *ngFor="let f of fields" [value]="f">{{ f }}</mat-option>
|
|
1447
|
+
</mat-select>
|
|
1448
|
+
</mat-form-field>
|
|
1449
|
+
<mat-form-field appearance="outline">
|
|
1450
|
+
<mat-label>Direção</mat-label>
|
|
1451
|
+
<mat-select [(ngModel)]="r.dir" (ngModelChange)="onUiSortRowsChanged()">
|
|
1452
|
+
<mat-option value="desc">Descendente</mat-option>
|
|
1453
|
+
<mat-option value="asc">Ascendente</mat-option>
|
|
1454
|
+
</mat-select>
|
|
1455
|
+
</mat-form-field>
|
|
1456
|
+
<div class="error" *ngIf="isUiSortRowDuplicate(i)">Opção duplicada (campo+direção)</div>
|
|
1457
|
+
<div class="flex-end"><button mat-button color="warn" (click)="removeUiSortRow(i)">Remover</button></div>
|
|
1458
|
+
</div>
|
|
1459
|
+
</div>
|
|
1158
1460
|
</div>
|
|
1159
1461
|
</mat-tab>
|
|
1160
1462
|
<mat-tab label="Ações">
|
|
@@ -1320,6 +1622,18 @@ class PraxisListConfigEditor {
|
|
|
1320
1622
|
</mat-select>
|
|
1321
1623
|
</mat-form-field>
|
|
1322
1624
|
</ng-container>
|
|
1625
|
+
<mat-form-field appearance="outline" *ngIf="mappingPrimary.type==='text'">
|
|
1626
|
+
<mat-label>Pipe extra (ex.: map:K=V)</mat-label>
|
|
1627
|
+
<input matInput [(ngModel)]="mappingPrimary.extraPipe" (ngModelChange)="onMappingChanged()" placeholder="ex.: map:PLANEJADA=Planejada" />
|
|
1628
|
+
<button mat-icon-button matSuffix [matMenuTriggerFor]="pipeMenuPrimary" aria-label="Assistente de pipes" matTooltip="Assistente de pipes"><mat-icon>build</mat-icon></button>
|
|
1629
|
+
</mat-form-field>
|
|
1630
|
+
<mat-menu #pipeMenuPrimary="matMenu">
|
|
1631
|
+
<button mat-menu-item (click)="setPipe(mappingPrimary, 'map:KEY=VALUE,KEY2=VALUE2')"><mat-icon>tune</mat-icon><span>Map: KEY=VALUE…</span></button>
|
|
1632
|
+
<button mat-menu-item (click)="setPipe(mappingPrimary, 'number:pt-BR')"><mat-icon>push_pin</mat-icon><span>Número: pt-BR</span></button>
|
|
1633
|
+
<button mat-menu-item (click)="setPipe(mappingPrimary, 'number:pt-BR:compact')"><mat-icon>format_list_numbered</mat-icon><span>Número: compacto</span></button>
|
|
1634
|
+
<button mat-menu-item (click)="setPipe(mappingPrimary, 'bool:Sim:Não')"><mat-icon>check_circle</mat-icon><span>Bool: Sim/Não</span></button>
|
|
1635
|
+
<button mat-menu-item (click)="setPipe(mappingPrimary, 'date:pt-BR:short')"><mat-icon>schedule</mat-icon><span>Data: curto</span></button>
|
|
1636
|
+
</mat-menu>
|
|
1323
1637
|
<mat-form-field appearance="outline">
|
|
1324
1638
|
<mat-label>Classe CSS</mat-label>
|
|
1325
1639
|
<input matInput [(ngModel)]="mappingPrimary.class" (ngModelChange)="onMappingChanged()" />
|
|
@@ -1375,6 +1689,18 @@ class PraxisListConfigEditor {
|
|
|
1375
1689
|
</mat-select>
|
|
1376
1690
|
</mat-form-field>
|
|
1377
1691
|
</ng-container>
|
|
1692
|
+
<mat-form-field appearance="outline" *ngIf="mappingSecondary.type==='text'">
|
|
1693
|
+
<mat-label>Pipe extra (ex.: number:pt-BR:compact)</mat-label>
|
|
1694
|
+
<input matInput [(ngModel)]="mappingSecondary.extraPipe" (ngModelChange)="onMappingChanged()" placeholder="ex.: number:pt-BR:compact" />
|
|
1695
|
+
<button mat-icon-button matSuffix [matMenuTriggerFor]="pipeMenuSecondary" aria-label="Assistente de pipes" matTooltip="Assistente de pipes"><mat-icon>build</mat-icon></button>
|
|
1696
|
+
</mat-form-field>
|
|
1697
|
+
<mat-menu #pipeMenuSecondary="matMenu">
|
|
1698
|
+
<button mat-menu-item (click)="setPipe(mappingSecondary, 'map:KEY=VALUE,KEY2=VALUE2')"><mat-icon>tune</mat-icon><span>Map: KEY=VALUE…</span></button>
|
|
1699
|
+
<button mat-menu-item (click)="setPipe(mappingSecondary, 'number:pt-BR')"><mat-icon>push_pin</mat-icon><span>Número: pt-BR</span></button>
|
|
1700
|
+
<button mat-menu-item (click)="setPipe(mappingSecondary, 'number:pt-BR:compact')"><mat-icon>format_list_numbered</mat-icon><span>Número: compacto</span></button>
|
|
1701
|
+
<button mat-menu-item (click)="setPipe(mappingSecondary, 'bool:Sim:Não')"><mat-icon>check_circle</mat-icon><span>Bool: Sim/Não</span></button>
|
|
1702
|
+
<button mat-menu-item (click)="setPipe(mappingSecondary, 'date:pt-BR:short')"><mat-icon>schedule</mat-icon><span>Data: curto</span></button>
|
|
1703
|
+
</mat-menu>
|
|
1378
1704
|
<mat-form-field appearance="outline">
|
|
1379
1705
|
<mat-label>Classe CSS</mat-label>
|
|
1380
1706
|
<input matInput [(ngModel)]="mappingSecondary.class" (ngModelChange)="onMappingChanged()" />
|
|
@@ -1469,6 +1795,18 @@ class PraxisListConfigEditor {
|
|
|
1469
1795
|
</mat-select>
|
|
1470
1796
|
</mat-form-field>
|
|
1471
1797
|
</ng-container>
|
|
1798
|
+
<mat-form-field appearance="outline" *ngIf="mappingMeta.type==='text'">
|
|
1799
|
+
<mat-label>Pipe extra (ex.: map:K=V)</mat-label>
|
|
1800
|
+
<input matInput [(ngModel)]="mappingMeta.extraPipe" (ngModelChange)="onMappingChanged()" placeholder="ex.: map:ABERTO=Aberto,FECHADO=Fechado" />
|
|
1801
|
+
<button mat-icon-button matSuffix [matMenuTriggerFor]="pipeMenuMeta" aria-label="Assistente de pipes" matTooltip="Assistente de pipes"><mat-icon>build</mat-icon></button>
|
|
1802
|
+
</mat-form-field>
|
|
1803
|
+
<mat-menu #pipeMenuMeta="matMenu">
|
|
1804
|
+
<button mat-menu-item (click)="setPipe(mappingMeta, 'map:KEY=VALUE,KEY2=VALUE2')"><mat-icon>tune</mat-icon><span>Map: KEY=VALUE…</span></button>
|
|
1805
|
+
<button mat-menu-item (click)="setPipe(mappingMeta, 'number:pt-BR')"><mat-icon>push_pin</mat-icon><span>Número: pt-BR</span></button>
|
|
1806
|
+
<button mat-menu-item (click)="setPipe(mappingMeta, 'number:pt-BR:compact')"><mat-icon>format_list_numbered</mat-icon><span>Número: compacto</span></button>
|
|
1807
|
+
<button mat-menu-item (click)="setPipe(mappingMeta, 'bool:Sim:Não')"><mat-icon>check_circle</mat-icon><span>Bool: Sim/Não</span></button>
|
|
1808
|
+
<button mat-menu-item (click)="setPipe(mappingMeta, 'date:pt-BR:short')"><mat-icon>schedule</mat-icon><span>Data: curto</span></button>
|
|
1809
|
+
</mat-menu>
|
|
1472
1810
|
</div>
|
|
1473
1811
|
</mat-expansion-panel>
|
|
1474
1812
|
<mat-expansion-panel>
|
|
@@ -1489,8 +1827,19 @@ class PraxisListConfigEditor {
|
|
|
1489
1827
|
<mat-option value="chip">Chip</mat-option>
|
|
1490
1828
|
<mat-option value="currency">Moeda</mat-option>
|
|
1491
1829
|
<mat-option value="date">Data</mat-option>
|
|
1830
|
+
<mat-option value="icon">Ícone</mat-option>
|
|
1492
1831
|
</mat-select>
|
|
1493
1832
|
</mat-form-field>
|
|
1833
|
+
<mat-form-field appearance="outline" *ngIf="mappingTrailing.type==='text' || mappingTrailing.type==='icon'">
|
|
1834
|
+
<mat-label>Pipe extra (ex.: map:KEY=icon,...)</mat-label>
|
|
1835
|
+
<input matInput [(ngModel)]="mappingTrailing.extraPipe" (ngModelChange)="onMappingChanged()" placeholder="ex.: map:PLANEJADA=event,EM_ANDAMENTO=play_circle" />
|
|
1836
|
+
<button mat-icon-button matSuffix [matMenuTriggerFor]="pipeMenuTrailing" aria-label="Assistente de pipes" matTooltip="Assistente de pipes"><mat-icon>build</mat-icon></button>
|
|
1837
|
+
</mat-form-field>
|
|
1838
|
+
<mat-menu #pipeMenuTrailing="matMenu">
|
|
1839
|
+
<button mat-menu-item (click)="setPipe(mappingTrailing, 'map:PLANEJADA=event,EM_ANDAMENTO=play_circle,PAUSADA=pause_circle,CONCLUIDA=check_circle,FALHOU=error')"><mat-icon>tune</mat-icon><span>Map: status→ícone</span></button>
|
|
1840
|
+
<button mat-menu-item (click)="setPipe(mappingTrailing, 'date:pt-BR:short')"><mat-icon>schedule</mat-icon><span>Data: curto</span></button>
|
|
1841
|
+
<button mat-menu-item (click)="setPipe(mappingTrailing, 'number:pt-BR')"><mat-icon>push_pin</mat-icon><span>Número: pt-BR</span></button>
|
|
1842
|
+
</mat-menu>
|
|
1494
1843
|
<ng-container *ngIf="mappingTrailing.type==='chip'">
|
|
1495
1844
|
<mat-form-field appearance="outline">
|
|
1496
1845
|
<mat-label>Cor do chip</mat-label>
|
|
@@ -1542,6 +1891,44 @@ class PraxisListConfigEditor {
|
|
|
1542
1891
|
<mat-label>Style inline</mat-label>
|
|
1543
1892
|
<input matInput [(ngModel)]="mappingTrailing.style" (ngModelChange)="onMappingChanged()" />
|
|
1544
1893
|
</mat-form-field>
|
|
1894
|
+
<mat-form-field appearance="outline">
|
|
1895
|
+
<mat-label>Posição do status (cards)</mat-label>
|
|
1896
|
+
<mat-select [(ngModel)]="statusPosition" (ngModelChange)="onMappingChanged()">
|
|
1897
|
+
<mat-option [value]="undefined">Inline</mat-option>
|
|
1898
|
+
<mat-option value="inline">Inline</mat-option>
|
|
1899
|
+
<mat-option value="top-right">Canto superior direito</mat-option>
|
|
1900
|
+
</mat-select>
|
|
1901
|
+
</mat-form-field>
|
|
1902
|
+
<div class="subtitle">Cores por status (ícone)</div>
|
|
1903
|
+
<div class="chips-row" *ngIf="(mappingTrailing?.field || '').toLowerCase()==='status'">
|
|
1904
|
+
<span class="muted">Sugestões:</span>
|
|
1905
|
+
<button mat-stroked-button type="button" (click)="iconColorMapEntries = iconColorMapEntries.concat([{key:'PLANEJADA', color:'primary'}]); onIconColorMapChanged()">PLANEJADA</button>
|
|
1906
|
+
<button mat-stroked-button type="button" (click)="iconColorMapEntries = iconColorMapEntries.concat([{key:'EM_ANDAMENTO', color:'accent'}]); onIconColorMapChanged()">EM_ANDAMENTO</button>
|
|
1907
|
+
<button mat-stroked-button type="button" (click)="iconColorMapEntries = iconColorMapEntries.concat([{key:'PAUSADA', color:'accent'}]); onIconColorMapChanged()">PAUSADA</button>
|
|
1908
|
+
<button mat-stroked-button type="button" (click)="iconColorMapEntries = iconColorMapEntries.concat([{key:'CONCLUIDA', color:'primary'}]); onIconColorMapChanged()">CONCLUIDA</button>
|
|
1909
|
+
<button mat-stroked-button type="button" (click)="iconColorMapEntries = iconColorMapEntries.concat([{key:'FALHOU', color:'warn'}]); onIconColorMapChanged()">FALHOU</button>
|
|
1910
|
+
</div>
|
|
1911
|
+
<div class="g g-auto-220 gap-12 ai-end">
|
|
1912
|
+
<ng-container *ngFor="let ent of iconColorMapEntries; let i = index">
|
|
1913
|
+
<mat-form-field appearance="outline">
|
|
1914
|
+
<mat-label>Chave</mat-label>
|
|
1915
|
+
<input matInput [(ngModel)]="ent.key" (ngModelChange)="onIconColorMapChanged()" placeholder="ex.: EM_ANDAMENTO ou play_circle" />
|
|
1916
|
+
</mat-form-field>
|
|
1917
|
+
<mat-form-field appearance="outline">
|
|
1918
|
+
<mat-label>Cor</mat-label>
|
|
1919
|
+
<mat-select [(ngModel)]="ent.color" (ngModelChange)="onIconColorMapChanged()">
|
|
1920
|
+
<mat-option [value]="undefined">Default</mat-option>
|
|
1921
|
+
<mat-option value="primary">Primary</mat-option>
|
|
1922
|
+
<mat-option value="accent">Accent</mat-option>
|
|
1923
|
+
<mat-option value="warn">Warn</mat-option>
|
|
1924
|
+
</mat-select>
|
|
1925
|
+
</mat-form-field>
|
|
1926
|
+
<div class="error" *ngIf="isIconColorDuplicate(i)">Chave duplicada (consolidada)</div>
|
|
1927
|
+
<div class="flex-end"><button mat-button color="warn" (click)="removeIconColorEntry(i)">Remover</button></div>
|
|
1928
|
+
</ng-container>
|
|
1929
|
+
<div><button mat-flat-button color="primary" (click)="addIconColorEntry()">Adicionar cor</button></div>
|
|
1930
|
+
</div>
|
|
1931
|
+
<div class="muted">Entradas vazias são ignoradas e chaves repetidas são mescladas (última vence).</div>
|
|
1545
1932
|
</div>
|
|
1546
1933
|
</mat-expansion-panel>
|
|
1547
1934
|
<mat-expansion-panel>
|
|
@@ -1802,7 +2189,7 @@ class PraxisListConfigEditor {
|
|
|
1802
2189
|
</div>
|
|
1803
2190
|
</mat-tab>
|
|
1804
2191
|
</mat-tab-group>
|
|
1805
|
-
`, isInline: true, styles: [".g{display:grid}.g-auto-220{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.g-auto-200{grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}.g-1-auto{grid-template-columns:1fr auto}.row-flow{grid-auto-flow:column}.gap-6{gap:6px}.gap-8{gap:8px}.gap-12{gap:12px}.ai-center{align-items:center}.ai-end{align-items:end}.mt-12{margin-top:12px}.mb-8{margin-bottom:8px}.mb-6{margin-bottom:6px}.my-8{margin:8px 0}.subtitle{margin:8px 0 4px;opacity:.82}.chips-row{display:flex;flex-wrap:wrap;gap:6px;align-items:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i3$1.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i3$1.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i7.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i7.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i8.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "directive", type: i10.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i10.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i10.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i10.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "directive", type: i11.MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "disabledInteractive", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "component", type: i11.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i12.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i3.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i14.MatChip, selector: "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", inputs: ["role", "id", "aria-label", "aria-description", "value", "color", "removable", "highlighted", "disableRipple", "disabled"], outputs: ["removed", "destroyed"], exportAs: ["matChip"] }, { kind: "component", type: i14.MatChipSet, selector: "mat-chip-set", inputs: ["disabled", "role", "tabIndex"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisListSkinPreviewComponent, selector: "praxis-list-skin-preview", inputs: ["config", "items", "theme"] }] });
|
|
2192
|
+
`, isInline: true, styles: [".g{display:grid}.g-auto-220{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.g-auto-200{grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}.g-1-auto{grid-template-columns:1fr auto}.row-flow{grid-auto-flow:column}.gap-6{gap:6px}.gap-8{gap:8px}.gap-12{gap:12px}.ai-center{align-items:center}.ai-end{align-items:end}.mt-12{margin-top:12px}.mb-8{margin-bottom:8px}.mb-6{margin-bottom:6px}.my-8{margin:8px 0}.subtitle{margin:8px 0 4px;opacity:.82}.chips-row{display:flex;flex-wrap:wrap;gap:6px;align-items:center}.error{color:var(--mdc-theme-error,#b00020);font-size:.85rem}.muted{opacity:.7}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i3$1.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i3$1.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i7.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i7.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i8.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "directive", type: i10.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i10.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i10.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i10.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "directive", type: i11.MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "disabledInteractive", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "component", type: i11.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i12.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i3.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i14.MatChip, selector: "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", inputs: ["role", "id", "aria-label", "aria-description", "value", "color", "removable", "highlighted", "disableRipple", "disabled"], outputs: ["removed", "destroyed"], exportAs: ["matChip"] }, { kind: "component", type: i14.MatChipSet, selector: "mat-chip-set", inputs: ["disabled", "role", "tabIndex"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i15.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i15.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i15.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisListSkinPreviewComponent, selector: "praxis-list-skin-preview", inputs: ["config", "items", "theme"] }] });
|
|
1806
2193
|
}
|
|
1807
2194
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PraxisListConfigEditor, decorators: [{
|
|
1808
2195
|
type: Component,
|
|
@@ -1821,6 +2208,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
1821
2208
|
MatTooltipModule,
|
|
1822
2209
|
MatDividerModule,
|
|
1823
2210
|
MatChipsModule,
|
|
2211
|
+
MatMenuModule,
|
|
1824
2212
|
PraxisIconDirective,
|
|
1825
2213
|
PraxisListSkinPreviewComponent,
|
|
1826
2214
|
], template: `
|
|
@@ -1850,6 +2238,52 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
1850
2238
|
</mat-select>
|
|
1851
2239
|
</mat-form-field>
|
|
1852
2240
|
</div>
|
|
2241
|
+
<mat-divider class="my-8"></mat-divider>
|
|
2242
|
+
<div class="subtitle">UI (Buscar / Ordenar / Rodapé)</div>
|
|
2243
|
+
<div class="g g-auto-220 gap-12 ai-end">
|
|
2244
|
+
<mat-slide-toggle [(ngModel)]="working.ui.showSearch" (ngModelChange)="onUiChanged()">Mostrar busca</mat-slide-toggle>
|
|
2245
|
+
<mat-slide-toggle [(ngModel)]="working.ui.showSort" (ngModelChange)="onUiChanged()">Mostrar ordenar</mat-slide-toggle>
|
|
2246
|
+
<mat-slide-toggle [(ngModel)]="working.ui.showRange" (ngModelChange)="onUiChanged()">Mostrar faixa X–Y de Total</mat-slide-toggle>
|
|
2247
|
+
</div>
|
|
2248
|
+
<div class="g g-auto-220 gap-12 ai-end mt-12" *ngIf="working.ui?.showSearch">
|
|
2249
|
+
<mat-form-field appearance="outline">
|
|
2250
|
+
<mat-label>Campo para buscar</mat-label>
|
|
2251
|
+
<mat-select [(ngModel)]="working.ui.searchField" (ngModelChange)="onUiChanged()">
|
|
2252
|
+
<mat-option *ngFor="let f of fields" [value]="f">{{ f }}</mat-option>
|
|
2253
|
+
</mat-select>
|
|
2254
|
+
</mat-form-field>
|
|
2255
|
+
<mat-form-field appearance="outline">
|
|
2256
|
+
<mat-label>Placeholder</mat-label>
|
|
2257
|
+
<input matInput [(ngModel)]="working.ui.searchPlaceholder" (ngModelChange)="onUiChanged()" placeholder="ex.: Buscar por título" />
|
|
2258
|
+
</mat-form-field>
|
|
2259
|
+
</div>
|
|
2260
|
+
<div class="mt-12" *ngIf="working.ui?.showSort">
|
|
2261
|
+
<div class="g g-1-auto ai-center gap-8">
|
|
2262
|
+
<div class="muted">Opções de ordenação (rótulo → campo+direção)</div>
|
|
2263
|
+
<button mat-flat-button color="primary" (click)="addUiSortRow()">Adicionar opção</button>
|
|
2264
|
+
</div>
|
|
2265
|
+
<div class="g g-auto-220 gap-12 ai-end mt-12" *ngFor="let r of uiSortRows; let i = index">
|
|
2266
|
+
<mat-form-field appearance="outline">
|
|
2267
|
+
<mat-label>Rótulo</mat-label>
|
|
2268
|
+
<input matInput [(ngModel)]="r.label" (ngModelChange)="onUiSortRowsChanged()" placeholder="ex.: Mais recentes" />
|
|
2269
|
+
</mat-form-field>
|
|
2270
|
+
<mat-form-field appearance="outline">
|
|
2271
|
+
<mat-label>Campo</mat-label>
|
|
2272
|
+
<mat-select [(ngModel)]="r.field" (ngModelChange)="onUiSortRowsChanged()">
|
|
2273
|
+
<mat-option *ngFor="let f of fields" [value]="f">{{ f }}</mat-option>
|
|
2274
|
+
</mat-select>
|
|
2275
|
+
</mat-form-field>
|
|
2276
|
+
<mat-form-field appearance="outline">
|
|
2277
|
+
<mat-label>Direção</mat-label>
|
|
2278
|
+
<mat-select [(ngModel)]="r.dir" (ngModelChange)="onUiSortRowsChanged()">
|
|
2279
|
+
<mat-option value="desc">Descendente</mat-option>
|
|
2280
|
+
<mat-option value="asc">Ascendente</mat-option>
|
|
2281
|
+
</mat-select>
|
|
2282
|
+
</mat-form-field>
|
|
2283
|
+
<div class="error" *ngIf="isUiSortRowDuplicate(i)">Opção duplicada (campo+direção)</div>
|
|
2284
|
+
<div class="flex-end"><button mat-button color="warn" (click)="removeUiSortRow(i)">Remover</button></div>
|
|
2285
|
+
</div>
|
|
2286
|
+
</div>
|
|
1853
2287
|
</div>
|
|
1854
2288
|
</mat-tab>
|
|
1855
2289
|
<mat-tab label="Ações">
|
|
@@ -2015,6 +2449,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
2015
2449
|
</mat-select>
|
|
2016
2450
|
</mat-form-field>
|
|
2017
2451
|
</ng-container>
|
|
2452
|
+
<mat-form-field appearance="outline" *ngIf="mappingPrimary.type==='text'">
|
|
2453
|
+
<mat-label>Pipe extra (ex.: map:K=V)</mat-label>
|
|
2454
|
+
<input matInput [(ngModel)]="mappingPrimary.extraPipe" (ngModelChange)="onMappingChanged()" placeholder="ex.: map:PLANEJADA=Planejada" />
|
|
2455
|
+
<button mat-icon-button matSuffix [matMenuTriggerFor]="pipeMenuPrimary" aria-label="Assistente de pipes" matTooltip="Assistente de pipes"><mat-icon>build</mat-icon></button>
|
|
2456
|
+
</mat-form-field>
|
|
2457
|
+
<mat-menu #pipeMenuPrimary="matMenu">
|
|
2458
|
+
<button mat-menu-item (click)="setPipe(mappingPrimary, 'map:KEY=VALUE,KEY2=VALUE2')"><mat-icon>tune</mat-icon><span>Map: KEY=VALUE…</span></button>
|
|
2459
|
+
<button mat-menu-item (click)="setPipe(mappingPrimary, 'number:pt-BR')"><mat-icon>push_pin</mat-icon><span>Número: pt-BR</span></button>
|
|
2460
|
+
<button mat-menu-item (click)="setPipe(mappingPrimary, 'number:pt-BR:compact')"><mat-icon>format_list_numbered</mat-icon><span>Número: compacto</span></button>
|
|
2461
|
+
<button mat-menu-item (click)="setPipe(mappingPrimary, 'bool:Sim:Não')"><mat-icon>check_circle</mat-icon><span>Bool: Sim/Não</span></button>
|
|
2462
|
+
<button mat-menu-item (click)="setPipe(mappingPrimary, 'date:pt-BR:short')"><mat-icon>schedule</mat-icon><span>Data: curto</span></button>
|
|
2463
|
+
</mat-menu>
|
|
2018
2464
|
<mat-form-field appearance="outline">
|
|
2019
2465
|
<mat-label>Classe CSS</mat-label>
|
|
2020
2466
|
<input matInput [(ngModel)]="mappingPrimary.class" (ngModelChange)="onMappingChanged()" />
|
|
@@ -2070,6 +2516,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
2070
2516
|
</mat-select>
|
|
2071
2517
|
</mat-form-field>
|
|
2072
2518
|
</ng-container>
|
|
2519
|
+
<mat-form-field appearance="outline" *ngIf="mappingSecondary.type==='text'">
|
|
2520
|
+
<mat-label>Pipe extra (ex.: number:pt-BR:compact)</mat-label>
|
|
2521
|
+
<input matInput [(ngModel)]="mappingSecondary.extraPipe" (ngModelChange)="onMappingChanged()" placeholder="ex.: number:pt-BR:compact" />
|
|
2522
|
+
<button mat-icon-button matSuffix [matMenuTriggerFor]="pipeMenuSecondary" aria-label="Assistente de pipes" matTooltip="Assistente de pipes"><mat-icon>build</mat-icon></button>
|
|
2523
|
+
</mat-form-field>
|
|
2524
|
+
<mat-menu #pipeMenuSecondary="matMenu">
|
|
2525
|
+
<button mat-menu-item (click)="setPipe(mappingSecondary, 'map:KEY=VALUE,KEY2=VALUE2')"><mat-icon>tune</mat-icon><span>Map: KEY=VALUE…</span></button>
|
|
2526
|
+
<button mat-menu-item (click)="setPipe(mappingSecondary, 'number:pt-BR')"><mat-icon>push_pin</mat-icon><span>Número: pt-BR</span></button>
|
|
2527
|
+
<button mat-menu-item (click)="setPipe(mappingSecondary, 'number:pt-BR:compact')"><mat-icon>format_list_numbered</mat-icon><span>Número: compacto</span></button>
|
|
2528
|
+
<button mat-menu-item (click)="setPipe(mappingSecondary, 'bool:Sim:Não')"><mat-icon>check_circle</mat-icon><span>Bool: Sim/Não</span></button>
|
|
2529
|
+
<button mat-menu-item (click)="setPipe(mappingSecondary, 'date:pt-BR:short')"><mat-icon>schedule</mat-icon><span>Data: curto</span></button>
|
|
2530
|
+
</mat-menu>
|
|
2073
2531
|
<mat-form-field appearance="outline">
|
|
2074
2532
|
<mat-label>Classe CSS</mat-label>
|
|
2075
2533
|
<input matInput [(ngModel)]="mappingSecondary.class" (ngModelChange)="onMappingChanged()" />
|
|
@@ -2164,6 +2622,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
2164
2622
|
</mat-select>
|
|
2165
2623
|
</mat-form-field>
|
|
2166
2624
|
</ng-container>
|
|
2625
|
+
<mat-form-field appearance="outline" *ngIf="mappingMeta.type==='text'">
|
|
2626
|
+
<mat-label>Pipe extra (ex.: map:K=V)</mat-label>
|
|
2627
|
+
<input matInput [(ngModel)]="mappingMeta.extraPipe" (ngModelChange)="onMappingChanged()" placeholder="ex.: map:ABERTO=Aberto,FECHADO=Fechado" />
|
|
2628
|
+
<button mat-icon-button matSuffix [matMenuTriggerFor]="pipeMenuMeta" aria-label="Assistente de pipes" matTooltip="Assistente de pipes"><mat-icon>build</mat-icon></button>
|
|
2629
|
+
</mat-form-field>
|
|
2630
|
+
<mat-menu #pipeMenuMeta="matMenu">
|
|
2631
|
+
<button mat-menu-item (click)="setPipe(mappingMeta, 'map:KEY=VALUE,KEY2=VALUE2')"><mat-icon>tune</mat-icon><span>Map: KEY=VALUE…</span></button>
|
|
2632
|
+
<button mat-menu-item (click)="setPipe(mappingMeta, 'number:pt-BR')"><mat-icon>push_pin</mat-icon><span>Número: pt-BR</span></button>
|
|
2633
|
+
<button mat-menu-item (click)="setPipe(mappingMeta, 'number:pt-BR:compact')"><mat-icon>format_list_numbered</mat-icon><span>Número: compacto</span></button>
|
|
2634
|
+
<button mat-menu-item (click)="setPipe(mappingMeta, 'bool:Sim:Não')"><mat-icon>check_circle</mat-icon><span>Bool: Sim/Não</span></button>
|
|
2635
|
+
<button mat-menu-item (click)="setPipe(mappingMeta, 'date:pt-BR:short')"><mat-icon>schedule</mat-icon><span>Data: curto</span></button>
|
|
2636
|
+
</mat-menu>
|
|
2167
2637
|
</div>
|
|
2168
2638
|
</mat-expansion-panel>
|
|
2169
2639
|
<mat-expansion-panel>
|
|
@@ -2184,8 +2654,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
2184
2654
|
<mat-option value="chip">Chip</mat-option>
|
|
2185
2655
|
<mat-option value="currency">Moeda</mat-option>
|
|
2186
2656
|
<mat-option value="date">Data</mat-option>
|
|
2657
|
+
<mat-option value="icon">Ícone</mat-option>
|
|
2187
2658
|
</mat-select>
|
|
2188
2659
|
</mat-form-field>
|
|
2660
|
+
<mat-form-field appearance="outline" *ngIf="mappingTrailing.type==='text' || mappingTrailing.type==='icon'">
|
|
2661
|
+
<mat-label>Pipe extra (ex.: map:KEY=icon,...)</mat-label>
|
|
2662
|
+
<input matInput [(ngModel)]="mappingTrailing.extraPipe" (ngModelChange)="onMappingChanged()" placeholder="ex.: map:PLANEJADA=event,EM_ANDAMENTO=play_circle" />
|
|
2663
|
+
<button mat-icon-button matSuffix [matMenuTriggerFor]="pipeMenuTrailing" aria-label="Assistente de pipes" matTooltip="Assistente de pipes"><mat-icon>build</mat-icon></button>
|
|
2664
|
+
</mat-form-field>
|
|
2665
|
+
<mat-menu #pipeMenuTrailing="matMenu">
|
|
2666
|
+
<button mat-menu-item (click)="setPipe(mappingTrailing, 'map:PLANEJADA=event,EM_ANDAMENTO=play_circle,PAUSADA=pause_circle,CONCLUIDA=check_circle,FALHOU=error')"><mat-icon>tune</mat-icon><span>Map: status→ícone</span></button>
|
|
2667
|
+
<button mat-menu-item (click)="setPipe(mappingTrailing, 'date:pt-BR:short')"><mat-icon>schedule</mat-icon><span>Data: curto</span></button>
|
|
2668
|
+
<button mat-menu-item (click)="setPipe(mappingTrailing, 'number:pt-BR')"><mat-icon>push_pin</mat-icon><span>Número: pt-BR</span></button>
|
|
2669
|
+
</mat-menu>
|
|
2189
2670
|
<ng-container *ngIf="mappingTrailing.type==='chip'">
|
|
2190
2671
|
<mat-form-field appearance="outline">
|
|
2191
2672
|
<mat-label>Cor do chip</mat-label>
|
|
@@ -2237,6 +2718,44 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
2237
2718
|
<mat-label>Style inline</mat-label>
|
|
2238
2719
|
<input matInput [(ngModel)]="mappingTrailing.style" (ngModelChange)="onMappingChanged()" />
|
|
2239
2720
|
</mat-form-field>
|
|
2721
|
+
<mat-form-field appearance="outline">
|
|
2722
|
+
<mat-label>Posição do status (cards)</mat-label>
|
|
2723
|
+
<mat-select [(ngModel)]="statusPosition" (ngModelChange)="onMappingChanged()">
|
|
2724
|
+
<mat-option [value]="undefined">Inline</mat-option>
|
|
2725
|
+
<mat-option value="inline">Inline</mat-option>
|
|
2726
|
+
<mat-option value="top-right">Canto superior direito</mat-option>
|
|
2727
|
+
</mat-select>
|
|
2728
|
+
</mat-form-field>
|
|
2729
|
+
<div class="subtitle">Cores por status (ícone)</div>
|
|
2730
|
+
<div class="chips-row" *ngIf="(mappingTrailing?.field || '').toLowerCase()==='status'">
|
|
2731
|
+
<span class="muted">Sugestões:</span>
|
|
2732
|
+
<button mat-stroked-button type="button" (click)="iconColorMapEntries = iconColorMapEntries.concat([{key:'PLANEJADA', color:'primary'}]); onIconColorMapChanged()">PLANEJADA</button>
|
|
2733
|
+
<button mat-stroked-button type="button" (click)="iconColorMapEntries = iconColorMapEntries.concat([{key:'EM_ANDAMENTO', color:'accent'}]); onIconColorMapChanged()">EM_ANDAMENTO</button>
|
|
2734
|
+
<button mat-stroked-button type="button" (click)="iconColorMapEntries = iconColorMapEntries.concat([{key:'PAUSADA', color:'accent'}]); onIconColorMapChanged()">PAUSADA</button>
|
|
2735
|
+
<button mat-stroked-button type="button" (click)="iconColorMapEntries = iconColorMapEntries.concat([{key:'CONCLUIDA', color:'primary'}]); onIconColorMapChanged()">CONCLUIDA</button>
|
|
2736
|
+
<button mat-stroked-button type="button" (click)="iconColorMapEntries = iconColorMapEntries.concat([{key:'FALHOU', color:'warn'}]); onIconColorMapChanged()">FALHOU</button>
|
|
2737
|
+
</div>
|
|
2738
|
+
<div class="g g-auto-220 gap-12 ai-end">
|
|
2739
|
+
<ng-container *ngFor="let ent of iconColorMapEntries; let i = index">
|
|
2740
|
+
<mat-form-field appearance="outline">
|
|
2741
|
+
<mat-label>Chave</mat-label>
|
|
2742
|
+
<input matInput [(ngModel)]="ent.key" (ngModelChange)="onIconColorMapChanged()" placeholder="ex.: EM_ANDAMENTO ou play_circle" />
|
|
2743
|
+
</mat-form-field>
|
|
2744
|
+
<mat-form-field appearance="outline">
|
|
2745
|
+
<mat-label>Cor</mat-label>
|
|
2746
|
+
<mat-select [(ngModel)]="ent.color" (ngModelChange)="onIconColorMapChanged()">
|
|
2747
|
+
<mat-option [value]="undefined">Default</mat-option>
|
|
2748
|
+
<mat-option value="primary">Primary</mat-option>
|
|
2749
|
+
<mat-option value="accent">Accent</mat-option>
|
|
2750
|
+
<mat-option value="warn">Warn</mat-option>
|
|
2751
|
+
</mat-select>
|
|
2752
|
+
</mat-form-field>
|
|
2753
|
+
<div class="error" *ngIf="isIconColorDuplicate(i)">Chave duplicada (consolidada)</div>
|
|
2754
|
+
<div class="flex-end"><button mat-button color="warn" (click)="removeIconColorEntry(i)">Remover</button></div>
|
|
2755
|
+
</ng-container>
|
|
2756
|
+
<div><button mat-flat-button color="primary" (click)="addIconColorEntry()">Adicionar cor</button></div>
|
|
2757
|
+
</div>
|
|
2758
|
+
<div class="muted">Entradas vazias são ignoradas e chaves repetidas são mescladas (última vence).</div>
|
|
2240
2759
|
</div>
|
|
2241
2760
|
</mat-expansion-panel>
|
|
2242
2761
|
<mat-expansion-panel>
|
|
@@ -2497,7 +3016,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
2497
3016
|
</div>
|
|
2498
3017
|
</mat-tab>
|
|
2499
3018
|
</mat-tab-group>
|
|
2500
|
-
`, styles: [".g{display:grid}.g-auto-220{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.g-auto-200{grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}.g-1-auto{grid-template-columns:1fr auto}.row-flow{grid-auto-flow:column}.gap-6{gap:6px}.gap-8{gap:8px}.gap-12{gap:12px}.ai-center{align-items:center}.ai-end{align-items:end}.mt-12{margin-top:12px}.mb-8{margin-bottom:8px}.mb-6{margin-bottom:6px}.my-8{margin:8px 0}.subtitle{margin:8px 0 4px;opacity:.82}.chips-row{display:flex;flex-wrap:wrap;gap:6px;align-items:center}\n"] }]
|
|
3019
|
+
`, styles: [".g{display:grid}.g-auto-220{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.g-auto-200{grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}.g-1-auto{grid-template-columns:1fr auto}.row-flow{grid-auto-flow:column}.gap-6{gap:6px}.gap-8{gap:8px}.gap-12{gap:12px}.ai-center{align-items:center}.ai-end{align-items:end}.mt-12{margin-top:12px}.mb-8{margin-bottom:8px}.mb-6{margin-bottom:6px}.my-8{margin:8px 0}.subtitle{margin:8px 0 4px;opacity:.82}.chips-row{display:flex;flex-wrap:wrap;gap:6px;align-items:center}.error{color:var(--mdc-theme-error,#b00020);font-size:.85rem}.muted{opacity:.7}\n"] }]
|
|
2501
3020
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
2502
3021
|
type: Inject,
|
|
2503
3022
|
args: [SETTINGS_PANEL_DATA]
|
|
@@ -2517,6 +3036,9 @@ class PraxisList {
|
|
|
2517
3036
|
sections$;
|
|
2518
3037
|
loading$;
|
|
2519
3038
|
total$;
|
|
3039
|
+
page$;
|
|
3040
|
+
lastQuery = {};
|
|
3041
|
+
search$ = new Subject();
|
|
2520
3042
|
get layoutLines() {
|
|
2521
3043
|
return this.config?.layout?.lines ?? 2;
|
|
2522
3044
|
}
|
|
@@ -2538,8 +3060,18 @@ class PraxisList {
|
|
|
2538
3060
|
this.sections$ = this.data.groupedStream();
|
|
2539
3061
|
this.loading$ = this.data.loading$;
|
|
2540
3062
|
this.total$ = this.data.total$;
|
|
3063
|
+
this.page$ = this.data.pageState$;
|
|
2541
3064
|
this.setupSelectionBinding();
|
|
2542
3065
|
this.applySkins();
|
|
3066
|
+
this.lastQuery = this.config?.dataSource?.query || {};
|
|
3067
|
+
this.search$.pipe(debounceTime$1(300), distinctUntilChanged$1()).subscribe((term) => {
|
|
3068
|
+
const field = this.config?.ui?.searchField;
|
|
3069
|
+
if (!field)
|
|
3070
|
+
return;
|
|
3071
|
+
const q = { ...(this.lastQuery || {}), [field]: term };
|
|
3072
|
+
this.lastQuery = q;
|
|
3073
|
+
this.data.setQuery(q);
|
|
3074
|
+
});
|
|
2543
3075
|
}
|
|
2544
3076
|
ngOnChanges(changes) {
|
|
2545
3077
|
if (changes['config']) {
|
|
@@ -2554,8 +3086,10 @@ class PraxisList {
|
|
|
2554
3086
|
this.sections$ = this.data.groupedStream();
|
|
2555
3087
|
this.loading$ = this.data.loading$;
|
|
2556
3088
|
this.total$ = this.data.total$;
|
|
3089
|
+
this.page$ = this.data.pageState$;
|
|
2557
3090
|
this.setupSelectionBinding();
|
|
2558
3091
|
this.applySkins();
|
|
3092
|
+
this.lastQuery = this.config?.dataSource?.query || {};
|
|
2559
3093
|
}
|
|
2560
3094
|
}
|
|
2561
3095
|
applyPersistence() {
|
|
@@ -2603,7 +3137,8 @@ class PraxisList {
|
|
|
2603
3137
|
trailing = (item) => this.evalSlot('trailing', item);
|
|
2604
3138
|
sectionHeader = (key) => evaluateTemplate(this.config?.templating?.sectionHeader, { key })?.value || key;
|
|
2605
3139
|
featureLabel(item, expr) {
|
|
2606
|
-
|
|
3140
|
+
const out = evaluateTemplate({ type: 'text', expr: expr || '' }, item);
|
|
3141
|
+
return out?.value ?? '';
|
|
2607
3142
|
}
|
|
2608
3143
|
featuresVisible() {
|
|
2609
3144
|
const t = this.config?.templating;
|
|
@@ -2689,6 +3224,36 @@ class PraxisList {
|
|
|
2689
3224
|
prevPage() {
|
|
2690
3225
|
this.data.prevPage();
|
|
2691
3226
|
}
|
|
3227
|
+
setPageSize(ps) {
|
|
3228
|
+
const size = Number(ps);
|
|
3229
|
+
if (isFinite(size) && size > 0)
|
|
3230
|
+
this.data.setPageSize(size);
|
|
3231
|
+
}
|
|
3232
|
+
onSortChange(val) {
|
|
3233
|
+
if (!val)
|
|
3234
|
+
return;
|
|
3235
|
+
this.data.setSort([val]);
|
|
3236
|
+
}
|
|
3237
|
+
onSearchInput(val) {
|
|
3238
|
+
this.search$.next(val || '');
|
|
3239
|
+
}
|
|
3240
|
+
sortOptionValue(op) {
|
|
3241
|
+
return typeof op === 'string' ? op : ((op && op.value) ? String(op.value) : '');
|
|
3242
|
+
}
|
|
3243
|
+
sortOptionLabel(op) {
|
|
3244
|
+
if (op && typeof op === 'object' && op.label)
|
|
3245
|
+
return String(op.label);
|
|
3246
|
+
const v = this.sortOptionValue(op);
|
|
3247
|
+
return v;
|
|
3248
|
+
}
|
|
3249
|
+
rangeStart(ps) {
|
|
3250
|
+
return (ps?.pageNumber || 0) * (ps?.pageSize || 0) + 1;
|
|
3251
|
+
}
|
|
3252
|
+
rangeEnd(currLen, ps, total) {
|
|
3253
|
+
const startIndex = (ps?.pageNumber || 0) * (ps?.pageSize || 0);
|
|
3254
|
+
const to = startIndex + (currLen || 0);
|
|
3255
|
+
return Math.min(to, total || 0);
|
|
3256
|
+
}
|
|
2692
3257
|
onEditorApplied(newCfg) {
|
|
2693
3258
|
this.config = newCfg;
|
|
2694
3259
|
// Optionally infer templates from backend schema if not defined
|
|
@@ -2787,7 +3352,59 @@ class PraxisList {
|
|
|
2787
3352
|
value = truthy ? (t ?? 'Sim') : (f ?? 'Não');
|
|
2788
3353
|
}
|
|
2789
3354
|
}
|
|
2790
|
-
|
|
3355
|
+
// Optional color mapping from config.templating.chipColorMap
|
|
3356
|
+
let color = def.color;
|
|
3357
|
+
try {
|
|
3358
|
+
const map = this.config?.templating?.chipColorMap;
|
|
3359
|
+
if (map && typeof value === 'string') {
|
|
3360
|
+
const key = String(value).toLowerCase();
|
|
3361
|
+
const found = map[key] ?? map[value];
|
|
3362
|
+
if (found)
|
|
3363
|
+
color = found;
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
catch { }
|
|
3367
|
+
// Optional label mapping
|
|
3368
|
+
try {
|
|
3369
|
+
const lmap = this.config?.templating?.chipLabelMap;
|
|
3370
|
+
if (lmap && typeof value === 'string') {
|
|
3371
|
+
const key = String(value).toLowerCase();
|
|
3372
|
+
const lbl = lmap[key] ?? lmap[value];
|
|
3373
|
+
if (lbl)
|
|
3374
|
+
value = lbl;
|
|
3375
|
+
}
|
|
3376
|
+
else if (typeof value === 'string') {
|
|
3377
|
+
value = this.prettyLabel(value);
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3380
|
+
catch { }
|
|
3381
|
+
return { type: def.type, value, class: def.class, style: def.style, color, variant: def.variant };
|
|
3382
|
+
}
|
|
3383
|
+
if (def.type === 'icon') {
|
|
3384
|
+
const out = evaluateTemplate(def, item);
|
|
3385
|
+
// Optional color mapping from config.templating.iconColorMap
|
|
3386
|
+
try {
|
|
3387
|
+
const map = this.config?.templating?.iconColorMap;
|
|
3388
|
+
if (map) {
|
|
3389
|
+
const status = String(item?.status ?? '').trim();
|
|
3390
|
+
const icon = String(out?.value ?? '').trim();
|
|
3391
|
+
const tryKeys = [
|
|
3392
|
+
status,
|
|
3393
|
+
status.toLowerCase?.(),
|
|
3394
|
+
icon,
|
|
3395
|
+
icon.toLowerCase?.(),
|
|
3396
|
+
].filter(Boolean);
|
|
3397
|
+
for (const k of tryKeys) {
|
|
3398
|
+
const found = map[k];
|
|
3399
|
+
if (found) {
|
|
3400
|
+
out.color = found;
|
|
3401
|
+
break;
|
|
3402
|
+
}
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
}
|
|
3406
|
+
catch { }
|
|
3407
|
+
return out;
|
|
2791
3408
|
}
|
|
2792
3409
|
return evaluateTemplate(def, item);
|
|
2793
3410
|
}
|
|
@@ -2804,17 +3421,8 @@ class PraxisList {
|
|
|
2804
3421
|
return [spec.slice(0, idx).trim(), spec.slice(idx + 1).trim()];
|
|
2805
3422
|
}
|
|
2806
3423
|
evalString(expr, ctx) {
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
const value = path
|
|
2810
|
-
.split('.')
|
|
2811
|
-
.reduce((acc, k) => (acc == null ? undefined : acc[k]), ctx);
|
|
2812
|
-
return value == null ? '' : String(value);
|
|
2813
|
-
}
|
|
2814
|
-
catch {
|
|
2815
|
-
return '';
|
|
2816
|
-
}
|
|
2817
|
-
});
|
|
3424
|
+
const res = evaluateTemplate({ type: 'text', expr }, ctx?.item ?? ctx);
|
|
3425
|
+
return res?.value ?? '';
|
|
2818
3426
|
}
|
|
2819
3427
|
parseTwoParams(param) {
|
|
2820
3428
|
if (!param)
|
|
@@ -2835,6 +3443,21 @@ class PraxisList {
|
|
|
2835
3443
|
const s = String(val).trim().toLowerCase();
|
|
2836
3444
|
return s === 'true' || s === '1' || s === 'yes' || s === 'sim' || s === 'on';
|
|
2837
3445
|
}
|
|
3446
|
+
// Format labels like 'EM_ANDAMENTO' -> 'Em andamento'; 'PLANEJADA' -> 'Planejada'
|
|
3447
|
+
prettyLabel(v) {
|
|
3448
|
+
try {
|
|
3449
|
+
const t = String(v ?? '')
|
|
3450
|
+
.replace(/[\s_\-]+/g, ' ')
|
|
3451
|
+
.trim()
|
|
3452
|
+
.toLowerCase();
|
|
3453
|
+
if (!t)
|
|
3454
|
+
return '';
|
|
3455
|
+
return t.replace(/\b\w/g, (m) => m.toUpperCase());
|
|
3456
|
+
}
|
|
3457
|
+
catch {
|
|
3458
|
+
return String(v ?? '');
|
|
3459
|
+
}
|
|
3460
|
+
}
|
|
2838
3461
|
mapDateStyle(style) {
|
|
2839
3462
|
switch ((style || '').toLowerCase()) {
|
|
2840
3463
|
case 'short':
|
|
@@ -2861,7 +3484,7 @@ class PraxisList {
|
|
|
2861
3484
|
trackBySection = (_, s) => s?.key ?? _;
|
|
2862
3485
|
trackByItem = (i, it) => this.config?.selection?.compareBy ? (it?.[this.config.selection.compareBy] ?? i) : (it?.id ?? i);
|
|
2863
3486
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PraxisList, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2864
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.4", type: PraxisList, isStandalone: true, selector: "praxis-list", inputs: { config: "config", form: "form" }, outputs: { itemClick: "itemClick", actionClick: "actionClick", selectionChange: "selectionChange" }, providers: [GenericCrudService, ListDataService], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"praxis-list-root\"\n [ngClass]=\"skinClasses\"\n [attr.aria-label]=\"config.a11y?.ariaLabel || null\"\n [attr.aria-labelledby]=\"config.a11y?.ariaLabelledBy || null\"\n>\n @if (inlineCss) {\n <style [innerHTML]=\"inlineCss\"></style>\n }\n\n <!-- Skeleton while loading -->\n @if ((loading$ | async) && hasSkeleton()) {\n @if (isListVariant()) {\n <mat-list>\n @for (_ of skeletonItems(); track $index; let i = $index) {\n <mat-list-item>\n <div class=\"list-item-content\">\n <div class=\"skeleton skeleton-avatar\"></div>\n <div style=\"width:100%\">\n <div class=\"skeleton skeleton-line w-60\"></div>\n @if (layoutLines > 1) { <div class=\"skeleton skeleton-line w-40\"></div> }\n </div>\n <div class=\"skeleton skeleton-chip\"></div>\n </div>\n </mat-list-item>\n }\n </mat-list>\n } @else {\n <div class=\"cards-grid\">\n @for (_ of skeletonItems(); track $index; let i = $index) {\n <div class=\"item-card\">\n <div class=\"list-item-content\">\n <div class=\"skeleton skeleton-avatar\"></div>\n <div style=\"width:100%\">\n <div class=\"skeleton skeleton-line w-60\"></div>\n @if (layoutLines > 1) { <div class=\"skeleton skeleton-line w-40\"></div> }\n </div>\n <div class=\"skeleton skeleton-chip\"></div>\n </div>\n </div>\n }\n </div>\n }\n } @else {\n <ng-container *ngTemplateOutlet=\"notLoading\"></ng-container>\n }\n\n <ng-template #notLoading>\n <!-- Empty state -->\n @if (items$ | async; as all) {\n @if (all.length === 0) {\n <div class=\"section-header\">\n {{ (config.templating?.emptyState?.expr || 'Nenhum item dispon\u00EDvel') }}\n </div>\n }\n }\n\n <!-- List variant -->\n @if (isListVariant()) {\n <!-- Selection list -->\n @if (isSelectionEnabled()) {\n <mat-selection-list\n [multiple]=\"config.selection?.mode === 'multiple'\"\n [formControl]=\"boundControl\"\n (selectionChange)=\"onSelectionChange($event)\"\n >\n @for (section of sections$ | async; track trackBySection($index, section)) {\n @if (section.key) {\n <div class=\"section-header mat-subheader\">\n {{ sectionHeader(section.key) }}\n </div>\n }\n @for (item of section.items; track trackByItem($index, item); let i = $index) {\n <mat-list-option\n [value]=\"item\"\n (click)=\"onItemClick(item, i, section.key || undefined)\"\n >\n <div class=\"list-item-content\">\n @if (leading(item); as lead) {\n @switch (lead.type) {\n @case ('icon') {\n <mat-icon [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</mat-icon>\n }\n @case ('image') {\n <div class=\"lead-image\" [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">\n <img [src]=\"lead.value\" [alt]=\"config.templating?.leading?.imageAlt || ''\" />\n @if (lead.badge?.value) {\n <mat-chip class=\"lead-badge\" [color]=\"lead.badge?.color || undefined\" [ngClass]=\"(((lead.badge?.variant || 'filled') === 'outlined') ? 'chip-outlined' : '')\">{{ lead.badge?.value }}</mat-chip>\n }\n </div>\n }\n }\n }\n @if (meta(item); as m) {\n <div>\n <div class=\"primary\" [ngClass]=\"primary(item)?.class\" [style.cssText]=\"primary(item)?.style\">{{ primary(item)?.value }}</div>\n @if (layoutLines > 1) { <div class=\"secondary\" [ngClass]=\"secondary(item)?.class\" [style.cssText]=\"secondary(item)?.style\">{{ secondary(item)?.value }}</div> }\n @if ((config.templating?.metaPlacement === 'line' && m?.type === 'text') || (layoutLines > 2 && m?.type === 'text')) {\n <div class=\"tertiary\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">\n @if (config.templating?.metaPrefixIcon; as mpi) { <mat-icon [praxisIcon]=\"mpi\"></mat-icon> }\n {{ m.value }}\n </div>\n }\n @if (featuresVisible()) {\n @for (f of (config.templating?.features || []); track $index; let fi = $index) {\n <span class=\"feature\">\n @if (f.icon && featuresMode() !== 'labels-only') { <mat-icon [praxisIcon]=\"f.icon\"></mat-icon> }\n @if (featuresMode() !== 'icons-only') { <span [ngClass]=\"f.class\" [style.cssText]=\"f.style\">{{ featureLabel(item, f.expr) }}</span> }\n </span>\n }\n }\n </div>\n @if (!(((config.templating?.metaPlacement === 'line') || (layoutLines > 2)) && m?.type === 'text')) {\n <div class=\"meta\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">\n @switch (m.type) {\n @case ('chip') { <mat-chip [color]=\"m.color || undefined\" [ngClass]=\"((m.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\">{{ m.value }}</mat-chip> }\n @case ('rating') { @for (_ of [0,1,2,3,4]; track $index; let idx = $index) { <mat-icon [praxisIcon]=\"starIcon(idx, m.value)\"></mat-icon> } }\n @default { <span>@if (config.templating?.metaPrefixIcon; as mpi2) { <mat-icon>{{ mpi2 }}</mat-icon> }{{ m.value }}</span> }\n }\n </div>\n }\n @if (trailing(item); as tr) {\n <div class=\"trailing\" [ngClass]=\"tr.class\" [style.cssText]=\"tr.style\">\n @switch (tr.type) {\n @case ('chip') { <mat-chip [color]=\"tr.color || undefined\" [ngClass]=\"((tr.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\">{{ tr.value }}</mat-chip> }\n @default { <span>{{ tr.value }}</span> }\n }\n </div>\n }\n }\n @for (action of visibleActions(item); let aidx = $index; track action?.id ?? $index) {\n @if ((action.kind || 'icon') === 'icon') {\n <button mat-icon-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, item, i)\" [attr.aria-label]=\"action.label || action.id\">\n <mat-icon [praxisIcon]=\"action.icon\"></mat-icon>\n </button>\n } @else {\n @if (action.buttonVariant === 'stroked') { <button mat-stroked-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, item, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n @if (action.buttonVariant === 'raised') { <button mat-raised-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, item, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n @if (!action.buttonVariant || action.buttonVariant === 'flat') { <button mat-flat-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, item, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n }\n }\n </div>\n </mat-list-option>\n }\n }\n </mat-selection-list>\n } @else {\n <ng-container *ngTemplateOutlet=\"readList\"></ng-container>\n }\n\n <!-- Read-only list -->\n <ng-template #readList>\n <mat-list>\n @for (section of sections$ | async; track trackBySection($index, section); let sidx = $index) {\n @if (section.key) {\n <div class=\"section-header mat-subheader\">\n {{ sectionHeader(section.key) }}\n </div>\n }\n @for (item of section.items; track trackByItem($index, item); let i = $index) {\n <mat-list-item (click)=\"onItemClick(item, i, section.key || undefined)\">\n <div class=\"list-item-content\">\n @if (leading(item); as lead) { <mat-icon [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</mat-icon> }\n @if (meta(item); as m) {\n <div>\n <div class=\"primary\" [ngClass]=\"primary(item)?.class\" [style.cssText]=\"primary(item)?.style\">{{ primary(item)?.value }}</div>\n @if (layoutLines > 1) { <div class=\"secondary\" [ngClass]=\"secondary(item)?.class\" [style.cssText]=\"secondary(item)?.style\">{{ secondary(item)?.value }}</div> }\n @if ((config.templating?.metaPlacement === 'line' && m?.type === 'text') || (layoutLines > 2 && m?.type === 'text')) { <div class=\"tertiary\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">{{ m.value }}</div> }\n </div>\n @if (!(((config.templating?.metaPlacement === 'line') || (layoutLines > 2)) && m?.type === 'text')) {\n <div class=\"meta\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">\n @switch (m.type) {\n @case ('chip') { <mat-chip>{{ m.value }}</mat-chip> }\n @case ('rating') { @for (_ of [0,1,2,3,4]; track $index; let idx = $index) { <mat-icon>{{ starIcon(idx, m.value) }}</mat-icon> } }\n @default { <span>{{ m.value }}</span> }\n }\n </div>\n }\n @if (trailing(item); as tr) {\n <div class=\"trailing\" [ngClass]=\"tr.class\" [style.cssText]=\"tr.style\">\n @switch (tr.type) {\n @case ('chip') { <mat-chip>{{ tr.value }}</mat-chip> }\n @default { <span>{{ tr.value }}</span> }\n }\n </div>\n }\n }\n @for (action of visibleActions(item); let aidx = $index; track action?.id ?? $index) {\n <button mat-icon-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, item, i)\" [attr.aria-label]=\"action.label || action.id\">\n <mat-icon>{{ action.icon }}</mat-icon>\n </button>\n }\n </div>\n </mat-list-item>\n }\n @if (config.layout?.dividers === 'between') { <mat-divider></mat-divider> }\n }\n </mat-list>\n </ng-template>\n } @else {\n <ng-container *ngTemplateOutlet=\"cardsVariant\"></ng-container>\n }\n\n <!-- Cards variant -->\n <ng-template #cardsVariant>\n <div class=\"cards-grid\">\n @for (it of (items$ | async) ?? []; track $index; let i = $index) {\n <div class=\"item-card\" (click)=\"onItemClick(it, i)\">\n <div class=\"list-item-content\">\n @if (leading(it); as lead) {\n @switch (lead.type) {\n @case ('icon') { <mat-icon [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</mat-icon> }\n @case ('image') {\n <div class=\"lead-image\" [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">\n <img [src]=\"lead.value\" [alt]=\"config.templating?.leading?.imageAlt || ''\" />\n @if (lead.badge?.value) { <mat-chip class=\"lead-badge\" [color]=\"lead.badge?.color || undefined\" [ngClass]=\"(((lead.badge?.variant || 'filled') === 'outlined') ? 'chip-outlined' : '')\">{{ lead.badge?.value }}</mat-chip> }\n </div>\n }\n }\n }\n @if (meta(it); as m) {\n <div>\n <div class=\"primary\" [ngClass]=\"primary(it)?.class\" [style.cssText]=\"primary(it)?.style\">{{ primary(it)?.value }}</div>\n @if (layoutLines > 1) { <div class=\"secondary\" [ngClass]=\"secondary(it)?.class\" [style.cssText]=\"secondary(it)?.style\">{{ secondary(it)?.value }}</div> }\n @if ((config.templating?.metaPlacement === 'line' && m?.type === 'text') || (layoutLines > 2 && m?.type === 'text')) { <div class=\"tertiary\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">{{ m.value }}</div> }\n @if (featuresVisible()) {\n @for (f of (config.templating?.features || []); track $index; let fi = $index) {\n <span class=\"feature\">\n @if (f.icon && featuresMode() !== 'labels-only') { <mat-icon>{{ f.icon }}</mat-icon> }\n @if (featuresMode() !== 'icons-only') { <span [ngClass]=\"f.class\" [style.cssText]=\"f.style\">{{ featureLabel(it, f.expr) }}</span> }\n </span>\n }\n }\n </div>\n @if (!(((config.templating?.metaPlacement === 'line') || (layoutLines > 2)) && m?.type === 'text')) {\n <div class=\"meta\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">\n @switch (m.type) {\n @case ('chip') { <mat-chip [color]=\"m.color || undefined\" [ngClass]=\"((m.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\">{{ m.value }}</mat-chip> }\n @case ('rating') { @for (_ of [0,1,2,3,4]; track $index; let idx = $index) { <mat-icon>{{ starIcon(idx, m.value) }}</mat-icon> } }\n @default { <span>{{ m.value }}</span> }\n }\n </div>\n }\n @if (trailing(it); as tr) {\n <div class=\"trailing\" [ngClass]=\"tr.class\" [style.cssText]=\"tr.style\">\n @switch (tr.type) {\n @case ('chip') { <mat-chip [color]=\"tr.color || undefined\" [ngClass]=\"((tr.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\">{{ tr.value }}</mat-chip> }\n @default { <span>{{ tr.value }}</span> }\n }\n </div>\n }\n }\n </div>\n <div class=\"card-actions\" (click)=\"$event.stopPropagation()\">\n @for (action of visibleActions(it); let aidx = $index; track action?.id ?? $index) {\n @if ((action.kind || 'icon') === 'icon') {\n <button mat-icon-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, it, i)\" [attr.aria-label]=\"action.label || action.id\">\n <mat-icon>{{ action.icon }}</mat-icon>\n </button>\n } @else {\n @if (action.buttonVariant === 'stroked') { <button mat-stroked-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, it, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n @if (action.buttonVariant === 'raised') { <button mat-raised-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, it, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n @if (!action.buttonVariant || action.buttonVariant === 'flat') { <button mat-flat-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, it, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n }\n }\n </div>\n </div>\n }\n </div>\n </ng-template>\n \n <!-- Simple pagination controls for remote data -->\n @if (config.dataSource?.resourcePath) {\n <div class=\"paginator\">\n <button mat-stroked-button color=\"primary\" (click)=\"prevPage()\">Anterior</button>\n <button mat-stroked-button color=\"primary\" (click)=\"nextPage()\">Pr\u00F3ximo</button>\n @if (total$ | async; as total) { <span class=\"muted\">Total: {{ total }}</span> }\n </div>\n }\n </ng-template>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block}.praxis-list-root{--p-list-radius: 1rem;--p-list-shadow: 0 6px 20px rgba(0, 0, 0, .08);--p-list-border: 1px solid rgba(0, 0, 0, .08);--p-list-blur: 8px;--p-list-grad-from: #8e72ff;--p-list-grad-to: #ffaa70;--p-list-grad-angle: 135deg}.skin-elevated .item-card{background:var(--mdc-elevated-card-container-color, var(--md-sys-color-surface));border-radius:var(--p-list-radius);box-shadow:var(--p-list-shadow);border:var(--p-list-border)}.skin-pill-soft .item-card{background:var(--mdc-elevated-card-container-color, var(--md-sys-color-surface));border-radius:calc(var(--p-list-radius) * 1.3);box-shadow:0 4px 16px #00000014;border:var(--p-list-border)}.skin-pill-soft .mat-mdc-list-item .list-item-content{background:var(--mdc-elevated-card-container-color, var(--md-sys-color-surface));border-radius:calc(var(--p-list-radius) * 1.3);box-shadow:0 4px 16px #00000014;border:var(--p-list-border);color:var(--p-list-foreground, #1f2937)}.skin-gradient-tile .item-card,.skin-gradient-tile .mat-mdc-list-item .list-item-content{background:linear-gradient(var(--p-list-grad-angle),var(--p-list-grad-from),var(--p-list-grad-to));border-radius:var(--p-list-radius);color:#fff}.skin-glass .item-card{background:#ffffff2e;border-radius:var(--p-list-radius);border:1px solid rgba(255,255,255,.3);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur))}.skin-glass .mat-mdc-list-item .list-item-content{background:#ffffff2e;border-radius:var(--p-list-radius);border:1px solid rgba(255,255,255,.3);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur));color:var(--p-list-foreground, #111)}.density-compact .mat-mdc-list-item,.density-compact .item-card{--_item-padding: 4px 8px}.density-comfortable .mat-mdc-list-item,.density-comfortable .item-card{--_item-padding: 8px 12px}.list-item-content{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:12px;padding:var(--_item-padding, 12px 16px)}.lead-image{position:relative;width:96px;height:72px;border-radius:12px;overflow:hidden}.lead-image img{width:100%;height:100%;object-fit:cover;display:block}.lead-image .lead-badge{position:absolute;left:8px;bottom:8px}.model-media .lead-image{width:136px;height:96px;border-radius:16px}.model-media .list-item-content{gap:16px}.model-hotel .lead-image{width:160px;height:110px;border-radius:18px}.primary{font-weight:600;color:var(--sicoob-tertiary-default, #001E24)}.secondary{opacity:.8}.tertiary{opacity:.68}.features{display:flex;flex-wrap:wrap;gap:12px;margin-top:6px;color:#000000a8}.feature{display:inline-flex;align-items:center;gap:6px}.meta{opacity:.9}.trailing{opacity:.9;margin-left:8px}.section-header{font-size:.85rem;opacity:.7;padding:8px 12px}.cards-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px}.item-card{padding:12px;display:flex;flex-direction:column;min-height:160px}.item-card:hover{box-shadow:0 10px 24px var(--sicoob-shadow-medium, rgba(0, 0, 0, .12));transform:translateY(-1px);transition:box-shadow .15s ease,transform .15s ease}.card-actions{display:flex;align-items:center;justify-content:flex-end;gap:4px;padding:6px 8px 4px;border-top:1px solid var(--sicoob-stroke-medium, rgba(0, 0, 0, .08));margin-top:auto}.leading-icon.mat-icon{width:36px;height:36px;line-height:36px;font-size:20px;display:inline-flex;align-items:center;justify-content:center;border-radius:999px;background:color-mix(in srgb,var(--sicoob-info-lightest, #f0f9ff),transparent 30%);color:var(--sicoob-info-default, #0090e0)}.mat-mdc-chip{--mdc-chip-container-height: 22px;font-size:12px}.meta .mat-icon{font-size:18px;height:18px;width:18px}.primary,.secondary{display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis}.primary,.secondary{-webkit-line-clamp:1}.features{overflow:hidden}@keyframes shimmer{0%{background-position:-468px 0}to{background-position:468px 0}}.skeleton{animation-duration:1.2s;animation-fill-mode:forwards;animation-iteration-count:infinite;animation-name:shimmer;animation-timing-function:linear;background:#f6f7f8;background:linear-gradient(to right,#eee 8%,#ddd 18%,#eee 33%);background-size:800px 104px;position:relative}.skeleton-avatar{width:32px;height:32px;border-radius:50%}.skeleton-line{height:10px;margin:6px 0;border-radius:6px}.skeleton-chip{width:48px;height:18px;border-radius:999px}.chip-outlined.mat-mdc-chip{background:transparent!important;border:1px solid currentColor}.w-60{width:60%}.w-40{width:40%}.paginator{display:flex;align-items:center;gap:8px;padding:8px 4px}.muted{opacity:.7}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatListModule }, { kind: "component", type: i3.MatList, selector: "mat-list", exportAs: ["matList"] }, { kind: "component", type: i3.MatSelectionList, selector: "mat-selection-list", inputs: ["color", "compareWith", "multiple", "hideSingleSelectionIndicator", "disabled"], outputs: ["selectionChange"], exportAs: ["matSelectionList"] }, { kind: "component", type: i3.MatListItem, selector: "mat-list-item, a[mat-list-item], button[mat-list-item]", inputs: ["activated"], exportAs: ["matListItem"] }, { kind: "component", type: i3.MatListOption, selector: "mat-list-option", inputs: ["togglePosition", "checkboxPosition", "color", "value", "selected"], outputs: ["selectedChange"], exportAs: ["matListOption"] }, { kind: "component", type: i3.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i14.MatChip, selector: "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", inputs: ["role", "id", "aria-label", "aria-description", "value", "color", "removable", "highlighted", "disableRipple", "disabled"], outputs: ["removed", "destroyed"], exportAs: ["matChip"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i7.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i7.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3487
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.4", type: PraxisList, isStandalone: true, selector: "praxis-list", inputs: { config: "config", form: "form" }, outputs: { itemClick: "itemClick", actionClick: "actionClick", selectionChange: "selectionChange" }, providers: [GenericCrudService, ListDataService], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"praxis-list-root\"\n [ngClass]=\"skinClasses\"\n [attr.aria-label]=\"config.a11y?.ariaLabel || null\"\n [attr.aria-labelledby]=\"config.a11y?.ariaLabelledBy || null\"\n>\n @if (inlineCss) {\n <style [innerHTML]=\"inlineCss\"></style>\n }\n\n <!-- Skeleton while loading -->\n @if ((loading$ | async) && hasSkeleton()) {\n @if (isListVariant()) {\n <mat-list>\n @for (_ of skeletonItems(); track $index; let i = $index) {\n <mat-list-item>\n <div class=\"list-item-content\">\n <div class=\"skeleton skeleton-avatar\"></div>\n <div style=\"width:100%\">\n <div class=\"skeleton skeleton-line w-60\"></div>\n @if (layoutLines > 1) { <div class=\"skeleton skeleton-line w-40\"></div> }\n </div>\n <div class=\"skeleton skeleton-chip\"></div>\n </div>\n </mat-list-item>\n }\n </mat-list>\n } @else {\n <div class=\"cards-grid\">\n @for (_ of skeletonItems(); track $index; let i = $index) {\n <div class=\"item-card\">\n <div class=\"list-item-content\">\n <div class=\"skeleton skeleton-avatar\"></div>\n <div style=\"width:100%\">\n <div class=\"skeleton skeleton-line w-60\"></div>\n @if (layoutLines > 1) { <div class=\"skeleton skeleton-line w-40\"></div> }\n </div>\n <div class=\"skeleton skeleton-chip\"></div>\n </div>\n </div>\n }\n </div>\n }\n } @else {\n <ng-container *ngTemplateOutlet=\"notLoading\"></ng-container>\n }\n\n <ng-template #notLoading>\n <!-- Empty state -->\n @if (items$ | async; as all) {\n @if (all.length === 0) {\n <div class=\"section-header\">\n {{ (config.templating?.emptyState?.expr || 'Nenhum item dispon\u00EDvel') }}\n </div>\n }\n }\n\n <!-- List variant -->\n @if (isListVariant()) {\n <!-- Selection list -->\n @if (isSelectionEnabled()) {\n <mat-selection-list\n [multiple]=\"config.selection?.mode === 'multiple'\"\n [formControl]=\"boundControl\"\n (selectionChange)=\"onSelectionChange($event)\"\n >\n @for (section of sections$ | async; track trackBySection($index, section)) {\n @if (section.key) {\n <div class=\"section-header mat-subheader\">\n {{ sectionHeader(section.key) }}\n </div>\n }\n @for (item of section.items; track trackByItem($index, item); let i = $index) {\n <mat-list-option\n [value]=\"item\"\n (click)=\"onItemClick(item, i, section.key || undefined)\"\n >\n <div class=\"list-item-content\">\n @if (leading(item); as lead) {\n @switch (lead.type) {\n @case ('icon') {\n <mat-icon [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</mat-icon>\n }\n @case ('image') {\n <div class=\"lead-image\" [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">\n <img [src]=\"lead.value\" [alt]=\"config.templating?.leading?.imageAlt || ''\" />\n @if (lead.badge?.value) {\n <mat-chip class=\"lead-badge\" [color]=\"lead.badge?.color || undefined\" [ngClass]=\"(((lead.badge?.variant || 'filled') === 'outlined') ? 'chip-outlined' : '')\">{{ lead.badge?.value }}</mat-chip>\n }\n </div>\n }\n }\n }\n @if (meta(item); as m) {\n <div>\n <div class=\"primary\" [ngClass]=\"primary(item)?.class\" [style.cssText]=\"primary(item)?.style\">{{ primary(item)?.value }}</div>\n @if (layoutLines > 1) { <div class=\"secondary\" [ngClass]=\"secondary(item)?.class\" [style.cssText]=\"secondary(item)?.style\">{{ secondary(item)?.value }}</div> }\n @if ((config.templating?.metaPlacement === 'line' && (m?.type === 'text' || m?.type === 'date')) || (layoutLines > 2 && (m?.type === 'text' || m?.type === 'date'))) {\n <div class=\"tertiary\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">\n @if (config.templating?.metaPrefixIcon; as mpi) { <mat-icon [praxisIcon]=\"mpi\"></mat-icon> }\n {{ m.value }}\n </div>\n }\n @if (featuresVisible()) {\n @for (f of (config.templating?.features || []); track $index; let fi = $index) {\n <span class=\"feature\">\n @if (f.icon && featuresMode() !== 'labels-only') { <mat-icon [praxisIcon]=\"f.icon\"></mat-icon> }\n @if (featuresMode() !== 'icons-only') { <span [ngClass]=\"f.class\" [style.cssText]=\"f.style\">{{ featureLabel(item, f.expr) }}</span> }\n </span>\n }\n }\n </div>\n @if (!(((config.templating?.metaPlacement === 'line') || (layoutLines > 2)) && (m?.type === 'text' || m?.type === 'date'))) {\n <div class=\"meta\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">\n @switch (m.type) {\n @case ('chip') { <mat-chip [color]=\"m.color || undefined\" [ngClass]=\"((m.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\">{{ m.value }}</mat-chip> }\n @case ('rating') { @for (_ of [0,1,2,3,4]; track $index; let idx = $index) { <mat-icon [praxisIcon]=\"starIcon(idx, m.value)\"></mat-icon> } }\n @default { <span>@if (config.templating?.metaPrefixIcon; as mpi2) { <mat-icon>{{ mpi2 }}</mat-icon> }{{ m.value }}</span> }\n }\n </div>\n }\n @if (trailing(item); as tr) {\n <div class=\"trailing\" [ngClass]=\"tr.class\" [style.cssText]=\"tr.style\">\n @switch (tr.type) {\n @case ('chip') { <mat-chip [color]=\"tr.color || undefined\" [ngClass]=\"((tr.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\">{{ tr.value }}</mat-chip> }\n @case ('icon') { <mat-icon [praxisIcon]=\"tr.value\" [color]=\"tr.color || undefined\"></mat-icon> }\n @default { <span>{{ tr.value }}</span> }\n }\n </div>\n }\n }\n @for (action of visibleActions(item); let aidx = $index; track action?.id ?? $index) {\n @if ((action.kind || 'icon') === 'icon') {\n <button mat-icon-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, item, i)\" [attr.aria-label]=\"action.label || action.id\">\n <mat-icon [praxisIcon]=\"action.icon\"></mat-icon>\n </button>\n } @else {\n @if (action.buttonVariant === 'stroked') { <button mat-stroked-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, item, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n @if (action.buttonVariant === 'raised') { <button mat-raised-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, item, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n @if (!action.buttonVariant || action.buttonVariant === 'flat') { <button mat-flat-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, item, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n }\n }\n </div>\n </mat-list-option>\n }\n }\n </mat-selection-list>\n } @else {\n <ng-container *ngTemplateOutlet=\"readList\"></ng-container>\n }\n\n <!-- Read-only list -->\n <ng-template #readList>\n <mat-list>\n @for (section of sections$ | async; track trackBySection($index, section); let sidx = $index) {\n @if (section.key) {\n <div class=\"section-header mat-subheader\">\n {{ sectionHeader(section.key) }}\n </div>\n }\n @for (item of section.items; track trackByItem($index, item); let i = $index) {\n <mat-list-item (click)=\"onItemClick(item, i, section.key || undefined)\">\n <div class=\"list-item-content\">\n @if (leading(item); as lead) { <mat-icon [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</mat-icon> }\n @if (meta(item); as m) {\n <div>\n <div class=\"primary\" [ngClass]=\"primary(item)?.class\" [style.cssText]=\"primary(item)?.style\">{{ primary(item)?.value }}</div>\n @if (layoutLines > 1) { <div class=\"secondary\" [ngClass]=\"secondary(item)?.class\" [style.cssText]=\"secondary(item)?.style\">{{ secondary(item)?.value }}</div> }\n @if ((config.templating?.metaPlacement === 'line' && (m?.type === 'text' || m?.type === 'date')) || (layoutLines > 2 && (m?.type === 'text' || m?.type === 'date'))) { <div class=\"tertiary\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">{{ m.value }}</div> }\n </div>\n @if (!(((config.templating?.metaPlacement === 'line') || (layoutLines > 2)) && (m?.type === 'text' || m?.type === 'date'))) {\n <div class=\"meta\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">\n @switch (m.type) {\n @case ('chip') { <mat-chip>{{ m.value }}</mat-chip> }\n @case ('rating') { @for (_ of [0,1,2,3,4]; track $index; let idx = $index) { <mat-icon>{{ starIcon(idx, m.value) }}</mat-icon> } }\n @default { <span>{{ m.value }}</span> }\n }\n </div>\n }\n @if (trailing(item); as tr) {\n <div class=\"trailing\" [ngClass]=\"tr.class\" [style.cssText]=\"tr.style\">\n @switch (tr.type) {\n @case ('chip') { <mat-chip>{{ tr.value }}</mat-chip> }\n @case ('icon') { <mat-icon [praxisIcon]=\"tr.value\" [color]=\"tr.color || undefined\"></mat-icon> }\n @default { <span>{{ tr.value }}</span> }\n }\n </div>\n }\n }\n @for (action of visibleActions(item); let aidx = $index; track action?.id ?? $index) {\n <button mat-icon-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, item, i)\" [attr.aria-label]=\"action.label || action.id\">\n <mat-icon>{{ action.icon }}</mat-icon>\n </button>\n }\n </div>\n </mat-list-item>\n }\n @if (config.layout?.dividers === 'between') { <mat-divider></mat-divider> }\n }\n </mat-list>\n </ng-template>\n } @else {\n <ng-container *ngTemplateOutlet=\"cardsVariant\"></ng-container>\n }\n\n <!-- Cards variant -->\n <ng-template #cardsVariant>\n <div class=\"cards-grid\">\n @for (it of (items$ | async) ?? []; track $index; let i = $index) {\n <div class=\"item-card\" (click)=\"onItemClick(it, i)\">\n <div class=\"list-item-content\">\n @if (leading(it); as lead) {\n @switch (lead.type) {\n @case ('icon') { <mat-icon [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</mat-icon> }\n @case ('image') {\n <div class=\"lead-image\" [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">\n <img [src]=\"lead.value\" [alt]=\"config.templating?.leading?.imageAlt || ''\" />\n @if (lead.badge?.value) { <mat-chip class=\"lead-badge\" [color]=\"lead.badge?.color || undefined\" [ngClass]=\"(((lead.badge?.variant || 'filled') === 'outlined') ? 'chip-outlined' : '')\">{{ lead.badge?.value }}</mat-chip> }\n </div>\n }\n }\n }\n @if (meta(it); as m) {\n <div>\n <div class=\"primary\" [ngClass]=\"primary(it)?.class\" [style.cssText]=\"primary(it)?.style\">{{ primary(it)?.value }}</div>\n @if (layoutLines > 1) { <div class=\"secondary\" [ngClass]=\"secondary(it)?.class\" [style.cssText]=\"secondary(it)?.style\">{{ secondary(it)?.value }}</div> }\n @if ((config.templating?.metaPlacement === 'line' && (m?.type === 'text' || m?.type === 'date')) || (layoutLines > 2 && (m?.type === 'text' || m?.type === 'date'))) { <div class=\"tertiary\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">{{ m.value }}</div> }\n @if (featuresVisible()) {\n @for (f of (config.templating?.features || []); track $index; let fi = $index) {\n <span class=\"feature\">\n @if (f.icon && featuresMode() !== 'labels-only') { <mat-icon>{{ f.icon }}</mat-icon> }\n @if (featuresMode() !== 'icons-only') { <span [ngClass]=\"f.class\" [style.cssText]=\"f.style\">{{ featureLabel(it, f.expr) }}</span> }\n </span>\n }\n }\n </div>\n @if (!(((config.templating?.metaPlacement === 'line') || (layoutLines > 2)) && (m?.type === 'text' || m?.type === 'date'))) {\n <div class=\"meta\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">\n @switch (m.type) {\n @case ('chip') { <mat-chip [color]=\"m.color || undefined\" [ngClass]=\"((m.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\">{{ m.value }}</mat-chip> }\n @case ('rating') { @for (_ of [0,1,2,3,4]; track $index; let idx = $index) { <mat-icon>{{ starIcon(idx, m.value) }}</mat-icon> } }\n @default { <span>{{ m.value }}</span> }\n }\n </div>\n }\n @if (trailing(it); as tr) {\n @if ((config.templating?.statusPosition || 'inline') === 'top-right' && (tr?.type === 'chip' || tr?.type === 'icon')) {\n <div class=\"status-overlay\">\n @if (tr?.type === 'chip') { <mat-chip [color]=\"tr.color || undefined\" [ngClass]=\"((tr.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\">{{ tr.value }}</mat-chip> }\n @if (tr?.type === 'icon') { <mat-icon [praxisIcon]=\"tr.value\" [color]=\"tr.color || undefined\"></mat-icon> }\n </div>\n } @else {\n <div class=\"trailing\" [ngClass]=\"tr.class\" [style.cssText]=\"tr.style\">\n @switch (tr.type) {\n @case ('chip') { <mat-chip [color]=\"tr.color || undefined\" [ngClass]=\"((tr.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\">{{ tr.value }}</mat-chip> }\n @case ('icon') { <mat-icon [praxisIcon]=\"tr.value\" [color]=\"tr.color || undefined\"></mat-icon> }\n @default { <span>{{ tr.value }}</span> }\n }\n </div>\n }\n }\n }\n </div>\n <div class=\"card-actions\" (click)=\"$event.stopPropagation()\">\n @for (action of visibleActions(it); let aidx = $index; track action?.id ?? $index) {\n @if ((action.kind || 'icon') === 'icon') {\n <button mat-icon-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, it, i)\" [attr.aria-label]=\"action.label || action.id\">\n <mat-icon>{{ action.icon }}</mat-icon>\n </button>\n } @else {\n @if (action.buttonVariant === 'stroked') { <button mat-stroked-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, it, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n @if (action.buttonVariant === 'raised') { <button mat-raised-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, it, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n @if (!action.buttonVariant || action.buttonVariant === 'flat') { <button mat-flat-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, it, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n }\n }\n </div>\n </div>\n }\n </div>\n </ng-template>\n \n <!-- Simple pagination controls for remote data -->\n @if (config.dataSource?.resourcePath) {\n <div class=\"paginator\" style=\"flex-wrap: wrap; gap: 8px;\">\n <div style=\"display:flex; align-items:center; gap:8px; flex-wrap: wrap;\">\n <button mat-stroked-button color=\"primary\" (click)=\"prevPage()\">Anterior</button>\n <button mat-stroked-button color=\"primary\" (click)=\"nextPage()\">Pr\u00F3ximo</button>\n <mat-form-field style=\"width: 120px;\" appearance=\"outline\">\n <mat-label>Tam. p\u00E1gina</mat-label>\n <mat-select (selectionChange)=\"setPageSize($event.value)\" [value]=\"config.layout?.pageSize || 10\">\n <mat-option [value]=\"6\">6</mat-option>\n <mat-option [value]=\"8\">8</mat-option>\n <mat-option [value]=\"12\">12</mat-option>\n <mat-option [value]=\"24\">24</mat-option>\n </mat-select>\n </mat-form-field>\n @if (config.ui?.showSort) {\n <mat-form-field style=\"width: 180px;\" appearance=\"outline\">\n <mat-label>Ordenar</mat-label>\n <mat-select (selectionChange)=\"onSortChange($event.value)\">\n @for (op of (config.ui?.sortOptions || []); track $index) {\n <mat-option [value]=\"sortOptionValue(op)\">{{ sortOptionLabel(op) }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n @if (config.ui?.showSearch && config.ui?.searchField) {\n <mat-form-field style=\"min-width: 220px;\" appearance=\"outline\">\n <mat-label>{{ config.ui?.searchPlaceholder || 'Buscar' }}</mat-label>\n <input matInput type=\"search\" (input)=\"onSearchInput($any($event.target).value)\" />\n </mat-form-field>\n }\n </div>\n <div style=\"display:flex; align-items:center; gap:8px; margin-left:auto;\">\n @if ((config.ui?.showRange ?? true)) {\n @if (page$ | async; as ps) {\n @if (total$ | async; as total) {\n @if (items$ | async; as curr) {\n <span class=\"muted\">{{ rangeStart(ps) }}\u2013{{ rangeEnd(curr?.length || 0, ps, total || 0) }} de {{ total || 0 }}</span>\n }\n }\n }\n } @else {\n @if (total$ | async; as total2) { <span class=\"muted\">Total: {{ total2 }}</span> }\n }\n </div>\n </div>\n }\n </ng-template>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block}.praxis-list-root{--p-list-radius: 1rem;--p-list-shadow: 0 6px 20px rgba(0, 0, 0, .08);--p-list-border: 1px solid rgba(0, 0, 0, .08);--p-list-blur: 8px;--p-list-grad-from: #8e72ff;--p-list-grad-to: #ffaa70;--p-list-grad-angle: 135deg}.skin-elevated .item-card{background:var(--mdc-elevated-card-container-color, var(--md-sys-color-surface));border-radius:var(--p-list-radius);box-shadow:var(--p-list-shadow);border:var(--p-list-border)}.skin-pill-soft .item-card{background:var(--mdc-elevated-card-container-color, var(--md-sys-color-surface));border-radius:calc(var(--p-list-radius) * 1.3);box-shadow:0 4px 16px #00000014;border:var(--p-list-border)}.skin-pill-soft .mat-mdc-list-item .list-item-content{background:var(--mdc-elevated-card-container-color, var(--md-sys-color-surface));border-radius:calc(var(--p-list-radius) * 1.3);box-shadow:0 4px 16px #00000014;border:var(--p-list-border);color:var(--p-list-foreground, #1f2937)}.skin-gradient-tile .item-card,.skin-gradient-tile .mat-mdc-list-item .list-item-content{background:linear-gradient(var(--p-list-grad-angle),var(--p-list-grad-from),var(--p-list-grad-to));border-radius:var(--p-list-radius);color:#fff}.skin-glass .item-card{background:#ffffff2e;border-radius:var(--p-list-radius);border:1px solid rgba(255,255,255,.3);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur))}.skin-glass .mat-mdc-list-item .list-item-content{background:#ffffff2e;border-radius:var(--p-list-radius);border:1px solid rgba(255,255,255,.3);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur));color:var(--p-list-foreground, #111)}.density-compact .mat-mdc-list-item,.density-compact .item-card{--_item-padding: 4px 8px}.density-comfortable .mat-mdc-list-item,.density-comfortable .item-card{--_item-padding: 8px 12px}.list-item-content{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:12px;padding:var(--_item-padding, 12px 16px)}.lead-image{position:relative;width:96px;height:72px;border-radius:12px;overflow:hidden}.lead-image img{width:100%;height:100%;object-fit:cover;display:block}.lead-image .lead-badge{position:absolute;left:8px;bottom:8px}.model-media .lead-image{width:136px;height:96px;border-radius:16px}.model-media .list-item-content{gap:16px}.model-hotel .lead-image{width:160px;height:110px;border-radius:18px}.primary{font-weight:600;color:var(--sicoob-tertiary-default, #001E24)}.secondary{opacity:.8}.tertiary{opacity:.68}.features{display:flex;flex-wrap:wrap;gap:12px;margin-top:6px;color:inherit}.feature{display:inline-flex;align-items:center;gap:6px}.meta{opacity:.9}.trailing{opacity:.9;margin-left:8px}.section-header{font-size:.85rem;opacity:.7;padding:8px 12px}.cards-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px}.item-card{padding:12px;display:flex;flex-direction:column;min-height:160px;position:relative;cursor:pointer}.item-card:hover{box-shadow:0 10px 24px var(--sicoob-shadow-medium, rgba(0, 0, 0, .12));transform:translateY(-1px);transition:box-shadow .15s ease,transform .15s ease}.card-actions{display:flex;align-items:center;justify-content:flex-end;gap:4px;padding:6px 8px 4px;border-top:1px solid var(--sicoob-stroke-medium, rgba(0, 0, 0, .08));margin-top:auto}.status-overlay{position:absolute;top:10px;right:10px;pointer-events:none}.status-overlay .mat-mdc-chip{pointer-events:auto}.leading-icon.mat-icon{width:36px;height:36px;line-height:36px;font-size:20px;display:inline-flex;align-items:center;justify-content:center;border-radius:999px;background:color-mix(in srgb,var(--sicoob-info-lightest, #f0f9ff),transparent 30%);color:var(--sicoob-info-default, #0090e0)}.mat-mdc-chip{--mdc-chip-container-height: 22px;font-size:12px}.meta .mat-icon{font-size:18px;height:18px;width:18px}.primary,.secondary{display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis}.primary,.secondary{-webkit-line-clamp:1}.features{overflow:hidden}@keyframes shimmer{0%{background-position:-468px 0}to{background-position:468px 0}}.skeleton{animation-duration:1.2s;animation-fill-mode:forwards;animation-iteration-count:infinite;animation-name:shimmer;animation-timing-function:linear;background:#f6f7f8;background:linear-gradient(to right,#eee 8%,#ddd 18%,#eee 33%);background-size:800px 104px;position:relative}.skeleton-avatar{width:32px;height:32px;border-radius:50%}.skeleton-line{height:10px;margin:6px 0;border-radius:6px}.skeleton-chip{width:48px;height:18px;border-radius:999px}.chip-outlined.mat-mdc-chip{background:transparent!important;border:1px solid currentColor}.w-60{width:60%}.w-40{width:40%}.paginator{display:flex;align-items:center;gap:8px;padding:8px 4px}.muted{opacity:.7}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatListModule }, { kind: "component", type: i3.MatList, selector: "mat-list", exportAs: ["matList"] }, { kind: "component", type: i3.MatSelectionList, selector: "mat-selection-list", inputs: ["color", "compareWith", "multiple", "hideSingleSelectionIndicator", "disabled"], outputs: ["selectionChange"], exportAs: ["matSelectionList"] }, { kind: "component", type: i3.MatListItem, selector: "mat-list-item, a[mat-list-item], button[mat-list-item]", inputs: ["activated"], exportAs: ["matListItem"] }, { kind: "component", type: i3.MatListOption, selector: "mat-list-option", inputs: ["togglePosition", "checkboxPosition", "color", "value", "selected"], outputs: ["selectedChange"], exportAs: ["matListOption"] }, { kind: "component", type: i3.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i14.MatChip, selector: "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", inputs: ["role", "id", "aria-label", "aria-description", "value", "color", "removable", "highlighted", "disableRipple", "disabled"], outputs: ["removed", "destroyed"], exportAs: ["matChip"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i7.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i7.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4$1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2865
3488
|
}
|
|
2866
3489
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PraxisList, decorators: [{
|
|
2867
3490
|
type: Component,
|
|
@@ -2874,7 +3497,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
2874
3497
|
PraxisIconDirective,
|
|
2875
3498
|
MatChipsModule,
|
|
2876
3499
|
MatButtonModule,
|
|
2877
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, providers: [GenericCrudService, ListDataService], template: "<div\n class=\"praxis-list-root\"\n [ngClass]=\"skinClasses\"\n [attr.aria-label]=\"config.a11y?.ariaLabel || null\"\n [attr.aria-labelledby]=\"config.a11y?.ariaLabelledBy || null\"\n>\n @if (inlineCss) {\n <style [innerHTML]=\"inlineCss\"></style>\n }\n\n <!-- Skeleton while loading -->\n @if ((loading$ | async) && hasSkeleton()) {\n @if (isListVariant()) {\n <mat-list>\n @for (_ of skeletonItems(); track $index; let i = $index) {\n <mat-list-item>\n <div class=\"list-item-content\">\n <div class=\"skeleton skeleton-avatar\"></div>\n <div style=\"width:100%\">\n <div class=\"skeleton skeleton-line w-60\"></div>\n @if (layoutLines > 1) { <div class=\"skeleton skeleton-line w-40\"></div> }\n </div>\n <div class=\"skeleton skeleton-chip\"></div>\n </div>\n </mat-list-item>\n }\n </mat-list>\n } @else {\n <div class=\"cards-grid\">\n @for (_ of skeletonItems(); track $index; let i = $index) {\n <div class=\"item-card\">\n <div class=\"list-item-content\">\n <div class=\"skeleton skeleton-avatar\"></div>\n <div style=\"width:100%\">\n <div class=\"skeleton skeleton-line w-60\"></div>\n @if (layoutLines > 1) { <div class=\"skeleton skeleton-line w-40\"></div> }\n </div>\n <div class=\"skeleton skeleton-chip\"></div>\n </div>\n </div>\n }\n </div>\n }\n } @else {\n <ng-container *ngTemplateOutlet=\"notLoading\"></ng-container>\n }\n\n <ng-template #notLoading>\n <!-- Empty state -->\n @if (items$ | async; as all) {\n @if (all.length === 0) {\n <div class=\"section-header\">\n {{ (config.templating?.emptyState?.expr || 'Nenhum item dispon\u00EDvel') }}\n </div>\n }\n }\n\n <!-- List variant -->\n @if (isListVariant()) {\n <!-- Selection list -->\n @if (isSelectionEnabled()) {\n <mat-selection-list\n [multiple]=\"config.selection?.mode === 'multiple'\"\n [formControl]=\"boundControl\"\n (selectionChange)=\"onSelectionChange($event)\"\n >\n @for (section of sections$ | async; track trackBySection($index, section)) {\n @if (section.key) {\n <div class=\"section-header mat-subheader\">\n {{ sectionHeader(section.key) }}\n </div>\n }\n @for (item of section.items; track trackByItem($index, item); let i = $index) {\n <mat-list-option\n [value]=\"item\"\n (click)=\"onItemClick(item, i, section.key || undefined)\"\n >\n <div class=\"list-item-content\">\n @if (leading(item); as lead) {\n @switch (lead.type) {\n @case ('icon') {\n <mat-icon [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</mat-icon>\n }\n @case ('image') {\n <div class=\"lead-image\" [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">\n <img [src]=\"lead.value\" [alt]=\"config.templating?.leading?.imageAlt || ''\" />\n @if (lead.badge?.value) {\n <mat-chip class=\"lead-badge\" [color]=\"lead.badge?.color || undefined\" [ngClass]=\"(((lead.badge?.variant || 'filled') === 'outlined') ? 'chip-outlined' : '')\">{{ lead.badge?.value }}</mat-chip>\n }\n </div>\n }\n }\n }\n @if (meta(item); as m) {\n <div>\n <div class=\"primary\" [ngClass]=\"primary(item)?.class\" [style.cssText]=\"primary(item)?.style\">{{ primary(item)?.value }}</div>\n @if (layoutLines > 1) { <div class=\"secondary\" [ngClass]=\"secondary(item)?.class\" [style.cssText]=\"secondary(item)?.style\">{{ secondary(item)?.value }}</div> }\n @if ((config.templating?.metaPlacement === 'line' && m?.type === 'text') || (layoutLines > 2 && m?.type === 'text')) {\n <div class=\"tertiary\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">\n @if (config.templating?.metaPrefixIcon; as mpi) { <mat-icon [praxisIcon]=\"mpi\"></mat-icon> }\n {{ m.value }}\n </div>\n }\n @if (featuresVisible()) {\n @for (f of (config.templating?.features || []); track $index; let fi = $index) {\n <span class=\"feature\">\n @if (f.icon && featuresMode() !== 'labels-only') { <mat-icon [praxisIcon]=\"f.icon\"></mat-icon> }\n @if (featuresMode() !== 'icons-only') { <span [ngClass]=\"f.class\" [style.cssText]=\"f.style\">{{ featureLabel(item, f.expr) }}</span> }\n </span>\n }\n }\n </div>\n @if (!(((config.templating?.metaPlacement === 'line') || (layoutLines > 2)) && m?.type === 'text')) {\n <div class=\"meta\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">\n @switch (m.type) {\n @case ('chip') { <mat-chip [color]=\"m.color || undefined\" [ngClass]=\"((m.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\">{{ m.value }}</mat-chip> }\n @case ('rating') { @for (_ of [0,1,2,3,4]; track $index; let idx = $index) { <mat-icon [praxisIcon]=\"starIcon(idx, m.value)\"></mat-icon> } }\n @default { <span>@if (config.templating?.metaPrefixIcon; as mpi2) { <mat-icon>{{ mpi2 }}</mat-icon> }{{ m.value }}</span> }\n }\n </div>\n }\n @if (trailing(item); as tr) {\n <div class=\"trailing\" [ngClass]=\"tr.class\" [style.cssText]=\"tr.style\">\n @switch (tr.type) {\n @case ('chip') { <mat-chip [color]=\"tr.color || undefined\" [ngClass]=\"((tr.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\">{{ tr.value }}</mat-chip> }\n @default { <span>{{ tr.value }}</span> }\n }\n </div>\n }\n }\n @for (action of visibleActions(item); let aidx = $index; track action?.id ?? $index) {\n @if ((action.kind || 'icon') === 'icon') {\n <button mat-icon-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, item, i)\" [attr.aria-label]=\"action.label || action.id\">\n <mat-icon [praxisIcon]=\"action.icon\"></mat-icon>\n </button>\n } @else {\n @if (action.buttonVariant === 'stroked') { <button mat-stroked-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, item, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n @if (action.buttonVariant === 'raised') { <button mat-raised-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, item, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n @if (!action.buttonVariant || action.buttonVariant === 'flat') { <button mat-flat-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, item, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n }\n }\n </div>\n </mat-list-option>\n }\n }\n </mat-selection-list>\n } @else {\n <ng-container *ngTemplateOutlet=\"readList\"></ng-container>\n }\n\n <!-- Read-only list -->\n <ng-template #readList>\n <mat-list>\n @for (section of sections$ | async; track trackBySection($index, section); let sidx = $index) {\n @if (section.key) {\n <div class=\"section-header mat-subheader\">\n {{ sectionHeader(section.key) }}\n </div>\n }\n @for (item of section.items; track trackByItem($index, item); let i = $index) {\n <mat-list-item (click)=\"onItemClick(item, i, section.key || undefined)\">\n <div class=\"list-item-content\">\n @if (leading(item); as lead) { <mat-icon [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</mat-icon> }\n @if (meta(item); as m) {\n <div>\n <div class=\"primary\" [ngClass]=\"primary(item)?.class\" [style.cssText]=\"primary(item)?.style\">{{ primary(item)?.value }}</div>\n @if (layoutLines > 1) { <div class=\"secondary\" [ngClass]=\"secondary(item)?.class\" [style.cssText]=\"secondary(item)?.style\">{{ secondary(item)?.value }}</div> }\n @if ((config.templating?.metaPlacement === 'line' && m?.type === 'text') || (layoutLines > 2 && m?.type === 'text')) { <div class=\"tertiary\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">{{ m.value }}</div> }\n </div>\n @if (!(((config.templating?.metaPlacement === 'line') || (layoutLines > 2)) && m?.type === 'text')) {\n <div class=\"meta\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">\n @switch (m.type) {\n @case ('chip') { <mat-chip>{{ m.value }}</mat-chip> }\n @case ('rating') { @for (_ of [0,1,2,3,4]; track $index; let idx = $index) { <mat-icon>{{ starIcon(idx, m.value) }}</mat-icon> } }\n @default { <span>{{ m.value }}</span> }\n }\n </div>\n }\n @if (trailing(item); as tr) {\n <div class=\"trailing\" [ngClass]=\"tr.class\" [style.cssText]=\"tr.style\">\n @switch (tr.type) {\n @case ('chip') { <mat-chip>{{ tr.value }}</mat-chip> }\n @default { <span>{{ tr.value }}</span> }\n }\n </div>\n }\n }\n @for (action of visibleActions(item); let aidx = $index; track action?.id ?? $index) {\n <button mat-icon-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, item, i)\" [attr.aria-label]=\"action.label || action.id\">\n <mat-icon>{{ action.icon }}</mat-icon>\n </button>\n }\n </div>\n </mat-list-item>\n }\n @if (config.layout?.dividers === 'between') { <mat-divider></mat-divider> }\n }\n </mat-list>\n </ng-template>\n } @else {\n <ng-container *ngTemplateOutlet=\"cardsVariant\"></ng-container>\n }\n\n <!-- Cards variant -->\n <ng-template #cardsVariant>\n <div class=\"cards-grid\">\n @for (it of (items$ | async) ?? []; track $index; let i = $index) {\n <div class=\"item-card\" (click)=\"onItemClick(it, i)\">\n <div class=\"list-item-content\">\n @if (leading(it); as lead) {\n @switch (lead.type) {\n @case ('icon') { <mat-icon [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</mat-icon> }\n @case ('image') {\n <div class=\"lead-image\" [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">\n <img [src]=\"lead.value\" [alt]=\"config.templating?.leading?.imageAlt || ''\" />\n @if (lead.badge?.value) { <mat-chip class=\"lead-badge\" [color]=\"lead.badge?.color || undefined\" [ngClass]=\"(((lead.badge?.variant || 'filled') === 'outlined') ? 'chip-outlined' : '')\">{{ lead.badge?.value }}</mat-chip> }\n </div>\n }\n }\n }\n @if (meta(it); as m) {\n <div>\n <div class=\"primary\" [ngClass]=\"primary(it)?.class\" [style.cssText]=\"primary(it)?.style\">{{ primary(it)?.value }}</div>\n @if (layoutLines > 1) { <div class=\"secondary\" [ngClass]=\"secondary(it)?.class\" [style.cssText]=\"secondary(it)?.style\">{{ secondary(it)?.value }}</div> }\n @if ((config.templating?.metaPlacement === 'line' && m?.type === 'text') || (layoutLines > 2 && m?.type === 'text')) { <div class=\"tertiary\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">{{ m.value }}</div> }\n @if (featuresVisible()) {\n @for (f of (config.templating?.features || []); track $index; let fi = $index) {\n <span class=\"feature\">\n @if (f.icon && featuresMode() !== 'labels-only') { <mat-icon>{{ f.icon }}</mat-icon> }\n @if (featuresMode() !== 'icons-only') { <span [ngClass]=\"f.class\" [style.cssText]=\"f.style\">{{ featureLabel(it, f.expr) }}</span> }\n </span>\n }\n }\n </div>\n @if (!(((config.templating?.metaPlacement === 'line') || (layoutLines > 2)) && m?.type === 'text')) {\n <div class=\"meta\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">\n @switch (m.type) {\n @case ('chip') { <mat-chip [color]=\"m.color || undefined\" [ngClass]=\"((m.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\">{{ m.value }}</mat-chip> }\n @case ('rating') { @for (_ of [0,1,2,3,4]; track $index; let idx = $index) { <mat-icon>{{ starIcon(idx, m.value) }}</mat-icon> } }\n @default { <span>{{ m.value }}</span> }\n }\n </div>\n }\n @if (trailing(it); as tr) {\n <div class=\"trailing\" [ngClass]=\"tr.class\" [style.cssText]=\"tr.style\">\n @switch (tr.type) {\n @case ('chip') { <mat-chip [color]=\"tr.color || undefined\" [ngClass]=\"((tr.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\">{{ tr.value }}</mat-chip> }\n @default { <span>{{ tr.value }}</span> }\n }\n </div>\n }\n }\n </div>\n <div class=\"card-actions\" (click)=\"$event.stopPropagation()\">\n @for (action of visibleActions(it); let aidx = $index; track action?.id ?? $index) {\n @if ((action.kind || 'icon') === 'icon') {\n <button mat-icon-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, it, i)\" [attr.aria-label]=\"action.label || action.id\">\n <mat-icon>{{ action.icon }}</mat-icon>\n </button>\n } @else {\n @if (action.buttonVariant === 'stroked') { <button mat-stroked-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, it, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n @if (action.buttonVariant === 'raised') { <button mat-raised-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, it, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n @if (!action.buttonVariant || action.buttonVariant === 'flat') { <button mat-flat-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, it, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n }\n }\n </div>\n </div>\n }\n </div>\n </ng-template>\n \n <!-- Simple pagination controls for remote data -->\n @if (config.dataSource?.resourcePath) {\n <div class=\"paginator\">\n <button mat-stroked-button color=\"primary\" (click)=\"prevPage()\">Anterior</button>\n <button mat-stroked-button color=\"primary\" (click)=\"nextPage()\">Pr\u00F3ximo</button>\n @if (total$ | async; as total) { <span class=\"muted\">Total: {{ total }}</span> }\n </div>\n }\n </ng-template>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block}.praxis-list-root{--p-list-radius: 1rem;--p-list-shadow: 0 6px 20px rgba(0, 0, 0, .08);--p-list-border: 1px solid rgba(0, 0, 0, .08);--p-list-blur: 8px;--p-list-grad-from: #8e72ff;--p-list-grad-to: #ffaa70;--p-list-grad-angle: 135deg}.skin-elevated .item-card{background:var(--mdc-elevated-card-container-color, var(--md-sys-color-surface));border-radius:var(--p-list-radius);box-shadow:var(--p-list-shadow);border:var(--p-list-border)}.skin-pill-soft .item-card{background:var(--mdc-elevated-card-container-color, var(--md-sys-color-surface));border-radius:calc(var(--p-list-radius) * 1.3);box-shadow:0 4px 16px #00000014;border:var(--p-list-border)}.skin-pill-soft .mat-mdc-list-item .list-item-content{background:var(--mdc-elevated-card-container-color, var(--md-sys-color-surface));border-radius:calc(var(--p-list-radius) * 1.3);box-shadow:0 4px 16px #00000014;border:var(--p-list-border);color:var(--p-list-foreground, #1f2937)}.skin-gradient-tile .item-card,.skin-gradient-tile .mat-mdc-list-item .list-item-content{background:linear-gradient(var(--p-list-grad-angle),var(--p-list-grad-from),var(--p-list-grad-to));border-radius:var(--p-list-radius);color:#fff}.skin-glass .item-card{background:#ffffff2e;border-radius:var(--p-list-radius);border:1px solid rgba(255,255,255,.3);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur))}.skin-glass .mat-mdc-list-item .list-item-content{background:#ffffff2e;border-radius:var(--p-list-radius);border:1px solid rgba(255,255,255,.3);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur));color:var(--p-list-foreground, #111)}.density-compact .mat-mdc-list-item,.density-compact .item-card{--_item-padding: 4px 8px}.density-comfortable .mat-mdc-list-item,.density-comfortable .item-card{--_item-padding: 8px 12px}.list-item-content{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:12px;padding:var(--_item-padding, 12px 16px)}.lead-image{position:relative;width:96px;height:72px;border-radius:12px;overflow:hidden}.lead-image img{width:100%;height:100%;object-fit:cover;display:block}.lead-image .lead-badge{position:absolute;left:8px;bottom:8px}.model-media .lead-image{width:136px;height:96px;border-radius:16px}.model-media .list-item-content{gap:16px}.model-hotel .lead-image{width:160px;height:110px;border-radius:18px}.primary{font-weight:600;color:var(--sicoob-tertiary-default, #001E24)}.secondary{opacity:.8}.tertiary{opacity:.68}.features{display:flex;flex-wrap:wrap;gap:12px;margin-top:6px;color:#000000a8}.feature{display:inline-flex;align-items:center;gap:6px}.meta{opacity:.9}.trailing{opacity:.9;margin-left:8px}.section-header{font-size:.85rem;opacity:.7;padding:8px 12px}.cards-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px}.item-card{padding:12px;display:flex;flex-direction:column;min-height:160px}.item-card:hover{box-shadow:0 10px 24px var(--sicoob-shadow-medium, rgba(0, 0, 0, .12));transform:translateY(-1px);transition:box-shadow .15s ease,transform .15s ease}.card-actions{display:flex;align-items:center;justify-content:flex-end;gap:4px;padding:6px 8px 4px;border-top:1px solid var(--sicoob-stroke-medium, rgba(0, 0, 0, .08));margin-top:auto}.leading-icon.mat-icon{width:36px;height:36px;line-height:36px;font-size:20px;display:inline-flex;align-items:center;justify-content:center;border-radius:999px;background:color-mix(in srgb,var(--sicoob-info-lightest, #f0f9ff),transparent 30%);color:var(--sicoob-info-default, #0090e0)}.mat-mdc-chip{--mdc-chip-container-height: 22px;font-size:12px}.meta .mat-icon{font-size:18px;height:18px;width:18px}.primary,.secondary{display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis}.primary,.secondary{-webkit-line-clamp:1}.features{overflow:hidden}@keyframes shimmer{0%{background-position:-468px 0}to{background-position:468px 0}}.skeleton{animation-duration:1.2s;animation-fill-mode:forwards;animation-iteration-count:infinite;animation-name:shimmer;animation-timing-function:linear;background:#f6f7f8;background:linear-gradient(to right,#eee 8%,#ddd 18%,#eee 33%);background-size:800px 104px;position:relative}.skeleton-avatar{width:32px;height:32px;border-radius:50%}.skeleton-line{height:10px;margin:6px 0;border-radius:6px}.skeleton-chip{width:48px;height:18px;border-radius:999px}.chip-outlined.mat-mdc-chip{background:transparent!important;border:1px solid currentColor}.w-60{width:60%}.w-40{width:40%}.paginator{display:flex;align-items:center;gap:8px;padding:8px 4px}.muted{opacity:.7}\n"] }]
|
|
3500
|
+
MatFormFieldModule,
|
|
3501
|
+
MatSelectModule,
|
|
3502
|
+
MatInputModule,
|
|
3503
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, providers: [GenericCrudService, ListDataService], template: "<div\n class=\"praxis-list-root\"\n [ngClass]=\"skinClasses\"\n [attr.aria-label]=\"config.a11y?.ariaLabel || null\"\n [attr.aria-labelledby]=\"config.a11y?.ariaLabelledBy || null\"\n>\n @if (inlineCss) {\n <style [innerHTML]=\"inlineCss\"></style>\n }\n\n <!-- Skeleton while loading -->\n @if ((loading$ | async) && hasSkeleton()) {\n @if (isListVariant()) {\n <mat-list>\n @for (_ of skeletonItems(); track $index; let i = $index) {\n <mat-list-item>\n <div class=\"list-item-content\">\n <div class=\"skeleton skeleton-avatar\"></div>\n <div style=\"width:100%\">\n <div class=\"skeleton skeleton-line w-60\"></div>\n @if (layoutLines > 1) { <div class=\"skeleton skeleton-line w-40\"></div> }\n </div>\n <div class=\"skeleton skeleton-chip\"></div>\n </div>\n </mat-list-item>\n }\n </mat-list>\n } @else {\n <div class=\"cards-grid\">\n @for (_ of skeletonItems(); track $index; let i = $index) {\n <div class=\"item-card\">\n <div class=\"list-item-content\">\n <div class=\"skeleton skeleton-avatar\"></div>\n <div style=\"width:100%\">\n <div class=\"skeleton skeleton-line w-60\"></div>\n @if (layoutLines > 1) { <div class=\"skeleton skeleton-line w-40\"></div> }\n </div>\n <div class=\"skeleton skeleton-chip\"></div>\n </div>\n </div>\n }\n </div>\n }\n } @else {\n <ng-container *ngTemplateOutlet=\"notLoading\"></ng-container>\n }\n\n <ng-template #notLoading>\n <!-- Empty state -->\n @if (items$ | async; as all) {\n @if (all.length === 0) {\n <div class=\"section-header\">\n {{ (config.templating?.emptyState?.expr || 'Nenhum item dispon\u00EDvel') }}\n </div>\n }\n }\n\n <!-- List variant -->\n @if (isListVariant()) {\n <!-- Selection list -->\n @if (isSelectionEnabled()) {\n <mat-selection-list\n [multiple]=\"config.selection?.mode === 'multiple'\"\n [formControl]=\"boundControl\"\n (selectionChange)=\"onSelectionChange($event)\"\n >\n @for (section of sections$ | async; track trackBySection($index, section)) {\n @if (section.key) {\n <div class=\"section-header mat-subheader\">\n {{ sectionHeader(section.key) }}\n </div>\n }\n @for (item of section.items; track trackByItem($index, item); let i = $index) {\n <mat-list-option\n [value]=\"item\"\n (click)=\"onItemClick(item, i, section.key || undefined)\"\n >\n <div class=\"list-item-content\">\n @if (leading(item); as lead) {\n @switch (lead.type) {\n @case ('icon') {\n <mat-icon [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</mat-icon>\n }\n @case ('image') {\n <div class=\"lead-image\" [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">\n <img [src]=\"lead.value\" [alt]=\"config.templating?.leading?.imageAlt || ''\" />\n @if (lead.badge?.value) {\n <mat-chip class=\"lead-badge\" [color]=\"lead.badge?.color || undefined\" [ngClass]=\"(((lead.badge?.variant || 'filled') === 'outlined') ? 'chip-outlined' : '')\">{{ lead.badge?.value }}</mat-chip>\n }\n </div>\n }\n }\n }\n @if (meta(item); as m) {\n <div>\n <div class=\"primary\" [ngClass]=\"primary(item)?.class\" [style.cssText]=\"primary(item)?.style\">{{ primary(item)?.value }}</div>\n @if (layoutLines > 1) { <div class=\"secondary\" [ngClass]=\"secondary(item)?.class\" [style.cssText]=\"secondary(item)?.style\">{{ secondary(item)?.value }}</div> }\n @if ((config.templating?.metaPlacement === 'line' && (m?.type === 'text' || m?.type === 'date')) || (layoutLines > 2 && (m?.type === 'text' || m?.type === 'date'))) {\n <div class=\"tertiary\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">\n @if (config.templating?.metaPrefixIcon; as mpi) { <mat-icon [praxisIcon]=\"mpi\"></mat-icon> }\n {{ m.value }}\n </div>\n }\n @if (featuresVisible()) {\n @for (f of (config.templating?.features || []); track $index; let fi = $index) {\n <span class=\"feature\">\n @if (f.icon && featuresMode() !== 'labels-only') { <mat-icon [praxisIcon]=\"f.icon\"></mat-icon> }\n @if (featuresMode() !== 'icons-only') { <span [ngClass]=\"f.class\" [style.cssText]=\"f.style\">{{ featureLabel(item, f.expr) }}</span> }\n </span>\n }\n }\n </div>\n @if (!(((config.templating?.metaPlacement === 'line') || (layoutLines > 2)) && (m?.type === 'text' || m?.type === 'date'))) {\n <div class=\"meta\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">\n @switch (m.type) {\n @case ('chip') { <mat-chip [color]=\"m.color || undefined\" [ngClass]=\"((m.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\">{{ m.value }}</mat-chip> }\n @case ('rating') { @for (_ of [0,1,2,3,4]; track $index; let idx = $index) { <mat-icon [praxisIcon]=\"starIcon(idx, m.value)\"></mat-icon> } }\n @default { <span>@if (config.templating?.metaPrefixIcon; as mpi2) { <mat-icon>{{ mpi2 }}</mat-icon> }{{ m.value }}</span> }\n }\n </div>\n }\n @if (trailing(item); as tr) {\n <div class=\"trailing\" [ngClass]=\"tr.class\" [style.cssText]=\"tr.style\">\n @switch (tr.type) {\n @case ('chip') { <mat-chip [color]=\"tr.color || undefined\" [ngClass]=\"((tr.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\">{{ tr.value }}</mat-chip> }\n @case ('icon') { <mat-icon [praxisIcon]=\"tr.value\" [color]=\"tr.color || undefined\"></mat-icon> }\n @default { <span>{{ tr.value }}</span> }\n }\n </div>\n }\n }\n @for (action of visibleActions(item); let aidx = $index; track action?.id ?? $index) {\n @if ((action.kind || 'icon') === 'icon') {\n <button mat-icon-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, item, i)\" [attr.aria-label]=\"action.label || action.id\">\n <mat-icon [praxisIcon]=\"action.icon\"></mat-icon>\n </button>\n } @else {\n @if (action.buttonVariant === 'stroked') { <button mat-stroked-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, item, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n @if (action.buttonVariant === 'raised') { <button mat-raised-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, item, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n @if (!action.buttonVariant || action.buttonVariant === 'flat') { <button mat-flat-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, item, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n }\n }\n </div>\n </mat-list-option>\n }\n }\n </mat-selection-list>\n } @else {\n <ng-container *ngTemplateOutlet=\"readList\"></ng-container>\n }\n\n <!-- Read-only list -->\n <ng-template #readList>\n <mat-list>\n @for (section of sections$ | async; track trackBySection($index, section); let sidx = $index) {\n @if (section.key) {\n <div class=\"section-header mat-subheader\">\n {{ sectionHeader(section.key) }}\n </div>\n }\n @for (item of section.items; track trackByItem($index, item); let i = $index) {\n <mat-list-item (click)=\"onItemClick(item, i, section.key || undefined)\">\n <div class=\"list-item-content\">\n @if (leading(item); as lead) { <mat-icon [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</mat-icon> }\n @if (meta(item); as m) {\n <div>\n <div class=\"primary\" [ngClass]=\"primary(item)?.class\" [style.cssText]=\"primary(item)?.style\">{{ primary(item)?.value }}</div>\n @if (layoutLines > 1) { <div class=\"secondary\" [ngClass]=\"secondary(item)?.class\" [style.cssText]=\"secondary(item)?.style\">{{ secondary(item)?.value }}</div> }\n @if ((config.templating?.metaPlacement === 'line' && (m?.type === 'text' || m?.type === 'date')) || (layoutLines > 2 && (m?.type === 'text' || m?.type === 'date'))) { <div class=\"tertiary\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">{{ m.value }}</div> }\n </div>\n @if (!(((config.templating?.metaPlacement === 'line') || (layoutLines > 2)) && (m?.type === 'text' || m?.type === 'date'))) {\n <div class=\"meta\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">\n @switch (m.type) {\n @case ('chip') { <mat-chip>{{ m.value }}</mat-chip> }\n @case ('rating') { @for (_ of [0,1,2,3,4]; track $index; let idx = $index) { <mat-icon>{{ starIcon(idx, m.value) }}</mat-icon> } }\n @default { <span>{{ m.value }}</span> }\n }\n </div>\n }\n @if (trailing(item); as tr) {\n <div class=\"trailing\" [ngClass]=\"tr.class\" [style.cssText]=\"tr.style\">\n @switch (tr.type) {\n @case ('chip') { <mat-chip>{{ tr.value }}</mat-chip> }\n @case ('icon') { <mat-icon [praxisIcon]=\"tr.value\" [color]=\"tr.color || undefined\"></mat-icon> }\n @default { <span>{{ tr.value }}</span> }\n }\n </div>\n }\n }\n @for (action of visibleActions(item); let aidx = $index; track action?.id ?? $index) {\n <button mat-icon-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, item, i)\" [attr.aria-label]=\"action.label || action.id\">\n <mat-icon>{{ action.icon }}</mat-icon>\n </button>\n }\n </div>\n </mat-list-item>\n }\n @if (config.layout?.dividers === 'between') { <mat-divider></mat-divider> }\n }\n </mat-list>\n </ng-template>\n } @else {\n <ng-container *ngTemplateOutlet=\"cardsVariant\"></ng-container>\n }\n\n <!-- Cards variant -->\n <ng-template #cardsVariant>\n <div class=\"cards-grid\">\n @for (it of (items$ | async) ?? []; track $index; let i = $index) {\n <div class=\"item-card\" (click)=\"onItemClick(it, i)\">\n <div class=\"list-item-content\">\n @if (leading(it); as lead) {\n @switch (lead.type) {\n @case ('icon') { <mat-icon [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">{{ lead.value }}</mat-icon> }\n @case ('image') {\n <div class=\"lead-image\" [ngClass]=\"lead.class\" [style.cssText]=\"lead.style\">\n <img [src]=\"lead.value\" [alt]=\"config.templating?.leading?.imageAlt || ''\" />\n @if (lead.badge?.value) { <mat-chip class=\"lead-badge\" [color]=\"lead.badge?.color || undefined\" [ngClass]=\"(((lead.badge?.variant || 'filled') === 'outlined') ? 'chip-outlined' : '')\">{{ lead.badge?.value }}</mat-chip> }\n </div>\n }\n }\n }\n @if (meta(it); as m) {\n <div>\n <div class=\"primary\" [ngClass]=\"primary(it)?.class\" [style.cssText]=\"primary(it)?.style\">{{ primary(it)?.value }}</div>\n @if (layoutLines > 1) { <div class=\"secondary\" [ngClass]=\"secondary(it)?.class\" [style.cssText]=\"secondary(it)?.style\">{{ secondary(it)?.value }}</div> }\n @if ((config.templating?.metaPlacement === 'line' && (m?.type === 'text' || m?.type === 'date')) || (layoutLines > 2 && (m?.type === 'text' || m?.type === 'date'))) { <div class=\"tertiary\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">{{ m.value }}</div> }\n @if (featuresVisible()) {\n @for (f of (config.templating?.features || []); track $index; let fi = $index) {\n <span class=\"feature\">\n @if (f.icon && featuresMode() !== 'labels-only') { <mat-icon>{{ f.icon }}</mat-icon> }\n @if (featuresMode() !== 'icons-only') { <span [ngClass]=\"f.class\" [style.cssText]=\"f.style\">{{ featureLabel(it, f.expr) }}</span> }\n </span>\n }\n }\n </div>\n @if (!(((config.templating?.metaPlacement === 'line') || (layoutLines > 2)) && (m?.type === 'text' || m?.type === 'date'))) {\n <div class=\"meta\" [ngClass]=\"m.class\" [style.cssText]=\"m.style\">\n @switch (m.type) {\n @case ('chip') { <mat-chip [color]=\"m.color || undefined\" [ngClass]=\"((m.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\">{{ m.value }}</mat-chip> }\n @case ('rating') { @for (_ of [0,1,2,3,4]; track $index; let idx = $index) { <mat-icon>{{ starIcon(idx, m.value) }}</mat-icon> } }\n @default { <span>{{ m.value }}</span> }\n }\n </div>\n }\n @if (trailing(it); as tr) {\n @if ((config.templating?.statusPosition || 'inline') === 'top-right' && (tr?.type === 'chip' || tr?.type === 'icon')) {\n <div class=\"status-overlay\">\n @if (tr?.type === 'chip') { <mat-chip [color]=\"tr.color || undefined\" [ngClass]=\"((tr.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\">{{ tr.value }}</mat-chip> }\n @if (tr?.type === 'icon') { <mat-icon [praxisIcon]=\"tr.value\" [color]=\"tr.color || undefined\"></mat-icon> }\n </div>\n } @else {\n <div class=\"trailing\" [ngClass]=\"tr.class\" [style.cssText]=\"tr.style\">\n @switch (tr.type) {\n @case ('chip') { <mat-chip [color]=\"tr.color || undefined\" [ngClass]=\"((tr.variant || 'filled') === 'outlined') ? 'chip-outlined' : ''\">{{ tr.value }}</mat-chip> }\n @case ('icon') { <mat-icon [praxisIcon]=\"tr.value\" [color]=\"tr.color || undefined\"></mat-icon> }\n @default { <span>{{ tr.value }}</span> }\n }\n </div>\n }\n }\n }\n </div>\n <div class=\"card-actions\" (click)=\"$event.stopPropagation()\">\n @for (action of visibleActions(it); let aidx = $index; track action?.id ?? $index) {\n @if ((action.kind || 'icon') === 'icon') {\n <button mat-icon-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, it, i)\" [attr.aria-label]=\"action.label || action.id\">\n <mat-icon>{{ action.icon }}</mat-icon>\n </button>\n } @else {\n @if (action.buttonVariant === 'stroked') { <button mat-stroked-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, it, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n @if (action.buttonVariant === 'raised') { <button mat-raised-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, it, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n @if (!action.buttonVariant || action.buttonVariant === 'flat') { <button mat-flat-button [color]=\"action.color || undefined\" (click)=\"onActionClick($event, action.id, it, i)\" [attr.aria-label]=\"action.label || action.id\">{{ action.label || action.id }}</button> }\n }\n }\n </div>\n </div>\n }\n </div>\n </ng-template>\n \n <!-- Simple pagination controls for remote data -->\n @if (config.dataSource?.resourcePath) {\n <div class=\"paginator\" style=\"flex-wrap: wrap; gap: 8px;\">\n <div style=\"display:flex; align-items:center; gap:8px; flex-wrap: wrap;\">\n <button mat-stroked-button color=\"primary\" (click)=\"prevPage()\">Anterior</button>\n <button mat-stroked-button color=\"primary\" (click)=\"nextPage()\">Pr\u00F3ximo</button>\n <mat-form-field style=\"width: 120px;\" appearance=\"outline\">\n <mat-label>Tam. p\u00E1gina</mat-label>\n <mat-select (selectionChange)=\"setPageSize($event.value)\" [value]=\"config.layout?.pageSize || 10\">\n <mat-option [value]=\"6\">6</mat-option>\n <mat-option [value]=\"8\">8</mat-option>\n <mat-option [value]=\"12\">12</mat-option>\n <mat-option [value]=\"24\">24</mat-option>\n </mat-select>\n </mat-form-field>\n @if (config.ui?.showSort) {\n <mat-form-field style=\"width: 180px;\" appearance=\"outline\">\n <mat-label>Ordenar</mat-label>\n <mat-select (selectionChange)=\"onSortChange($event.value)\">\n @for (op of (config.ui?.sortOptions || []); track $index) {\n <mat-option [value]=\"sortOptionValue(op)\">{{ sortOptionLabel(op) }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n @if (config.ui?.showSearch && config.ui?.searchField) {\n <mat-form-field style=\"min-width: 220px;\" appearance=\"outline\">\n <mat-label>{{ config.ui?.searchPlaceholder || 'Buscar' }}</mat-label>\n <input matInput type=\"search\" (input)=\"onSearchInput($any($event.target).value)\" />\n </mat-form-field>\n }\n </div>\n <div style=\"display:flex; align-items:center; gap:8px; margin-left:auto;\">\n @if ((config.ui?.showRange ?? true)) {\n @if (page$ | async; as ps) {\n @if (total$ | async; as total) {\n @if (items$ | async; as curr) {\n <span class=\"muted\">{{ rangeStart(ps) }}\u2013{{ rangeEnd(curr?.length || 0, ps, total || 0) }} de {{ total || 0 }}</span>\n }\n }\n }\n } @else {\n @if (total$ | async; as total2) { <span class=\"muted\">Total: {{ total2 }}</span> }\n }\n </div>\n </div>\n }\n </ng-template>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block}.praxis-list-root{--p-list-radius: 1rem;--p-list-shadow: 0 6px 20px rgba(0, 0, 0, .08);--p-list-border: 1px solid rgba(0, 0, 0, .08);--p-list-blur: 8px;--p-list-grad-from: #8e72ff;--p-list-grad-to: #ffaa70;--p-list-grad-angle: 135deg}.skin-elevated .item-card{background:var(--mdc-elevated-card-container-color, var(--md-sys-color-surface));border-radius:var(--p-list-radius);box-shadow:var(--p-list-shadow);border:var(--p-list-border)}.skin-pill-soft .item-card{background:var(--mdc-elevated-card-container-color, var(--md-sys-color-surface));border-radius:calc(var(--p-list-radius) * 1.3);box-shadow:0 4px 16px #00000014;border:var(--p-list-border)}.skin-pill-soft .mat-mdc-list-item .list-item-content{background:var(--mdc-elevated-card-container-color, var(--md-sys-color-surface));border-radius:calc(var(--p-list-radius) * 1.3);box-shadow:0 4px 16px #00000014;border:var(--p-list-border);color:var(--p-list-foreground, #1f2937)}.skin-gradient-tile .item-card,.skin-gradient-tile .mat-mdc-list-item .list-item-content{background:linear-gradient(var(--p-list-grad-angle),var(--p-list-grad-from),var(--p-list-grad-to));border-radius:var(--p-list-radius);color:#fff}.skin-glass .item-card{background:#ffffff2e;border-radius:var(--p-list-radius);border:1px solid rgba(255,255,255,.3);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur))}.skin-glass .mat-mdc-list-item .list-item-content{background:#ffffff2e;border-radius:var(--p-list-radius);border:1px solid rgba(255,255,255,.3);backdrop-filter:blur(var(--p-list-blur));-webkit-backdrop-filter:blur(var(--p-list-blur));color:var(--p-list-foreground, #111)}.density-compact .mat-mdc-list-item,.density-compact .item-card{--_item-padding: 4px 8px}.density-comfortable .mat-mdc-list-item,.density-comfortable .item-card{--_item-padding: 8px 12px}.list-item-content{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:12px;padding:var(--_item-padding, 12px 16px)}.lead-image{position:relative;width:96px;height:72px;border-radius:12px;overflow:hidden}.lead-image img{width:100%;height:100%;object-fit:cover;display:block}.lead-image .lead-badge{position:absolute;left:8px;bottom:8px}.model-media .lead-image{width:136px;height:96px;border-radius:16px}.model-media .list-item-content{gap:16px}.model-hotel .lead-image{width:160px;height:110px;border-radius:18px}.primary{font-weight:600;color:var(--sicoob-tertiary-default, #001E24)}.secondary{opacity:.8}.tertiary{opacity:.68}.features{display:flex;flex-wrap:wrap;gap:12px;margin-top:6px;color:inherit}.feature{display:inline-flex;align-items:center;gap:6px}.meta{opacity:.9}.trailing{opacity:.9;margin-left:8px}.section-header{font-size:.85rem;opacity:.7;padding:8px 12px}.cards-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px}.item-card{padding:12px;display:flex;flex-direction:column;min-height:160px;position:relative;cursor:pointer}.item-card:hover{box-shadow:0 10px 24px var(--sicoob-shadow-medium, rgba(0, 0, 0, .12));transform:translateY(-1px);transition:box-shadow .15s ease,transform .15s ease}.card-actions{display:flex;align-items:center;justify-content:flex-end;gap:4px;padding:6px 8px 4px;border-top:1px solid var(--sicoob-stroke-medium, rgba(0, 0, 0, .08));margin-top:auto}.status-overlay{position:absolute;top:10px;right:10px;pointer-events:none}.status-overlay .mat-mdc-chip{pointer-events:auto}.leading-icon.mat-icon{width:36px;height:36px;line-height:36px;font-size:20px;display:inline-flex;align-items:center;justify-content:center;border-radius:999px;background:color-mix(in srgb,var(--sicoob-info-lightest, #f0f9ff),transparent 30%);color:var(--sicoob-info-default, #0090e0)}.mat-mdc-chip{--mdc-chip-container-height: 22px;font-size:12px}.meta .mat-icon{font-size:18px;height:18px;width:18px}.primary,.secondary{display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis}.primary,.secondary{-webkit-line-clamp:1}.features{overflow:hidden}@keyframes shimmer{0%{background-position:-468px 0}to{background-position:468px 0}}.skeleton{animation-duration:1.2s;animation-fill-mode:forwards;animation-iteration-count:infinite;animation-name:shimmer;animation-timing-function:linear;background:#f6f7f8;background:linear-gradient(to right,#eee 8%,#ddd 18%,#eee 33%);background-size:800px 104px;position:relative}.skeleton-avatar{width:32px;height:32px;border-radius:50%}.skeleton-line{height:10px;margin:6px 0;border-radius:6px}.skeleton-chip{width:48px;height:18px;border-radius:999px}.chip-outlined.mat-mdc-chip{background:transparent!important;border:1px solid currentColor}.w-60{width:60%}.w-40{width:40%}.paginator{display:flex;align-items:center;gap:8px;padding:8px 4px}.muted{opacity:.7}\n"] }]
|
|
2878
3504
|
}], propDecorators: { config: [{
|
|
2879
3505
|
type: Input
|
|
2880
3506
|
}], form: [{
|