@praxisui/list 1.0.0-beta.29 → 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/fesm2022/praxisui-list.mjs +514 -23
- package/fesm2022/praxisui-list.mjs.map +1 -1
- package/index.d.ts +62 -1
- package/package.json +1 -1
|
@@ -16,21 +16,23 @@ import * as i4$1 from '@angular/material/form-field';
|
|
|
16
16
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
17
17
|
import * as i6 from '@angular/material/select';
|
|
18
18
|
import { MatSelectModule } from '@angular/material/select';
|
|
19
|
+
import * as i5 from '@angular/material/input';
|
|
20
|
+
import { MatInputModule } from '@angular/material/input';
|
|
19
21
|
import * as i2 from '@angular/forms';
|
|
20
22
|
import { FormsModule, FormControl, ReactiveFormsModule } from '@angular/forms';
|
|
21
|
-
import { BehaviorSubject, combineLatest, of, Subject } from 'rxjs';
|
|
23
|
+
import { BehaviorSubject, combineLatest, of, Subject, debounceTime as debounceTime$1, distinctUntilChanged as distinctUntilChanged$1 } from 'rxjs';
|
|
22
24
|
import { auditTime, switchMap, map, catchError, finalize, shareReplay, debounceTime, distinctUntilChanged, tap } from 'rxjs/operators';
|
|
23
25
|
import { SETTINGS_PANEL_DATA, SettingsPanelService } from '@praxisui/settings-panel';
|
|
24
26
|
import * as i3$1 from '@angular/material/tabs';
|
|
25
27
|
import { MatTabsModule } from '@angular/material/tabs';
|
|
26
|
-
import * as i5 from '@angular/material/input';
|
|
27
|
-
import { MatInputModule } from '@angular/material/input';
|
|
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';
|
|
@@ -100,7 +102,9 @@ function evalExpr(expr, ctx) {
|
|
|
100
102
|
function evaluateTemplate(def, item) {
|
|
101
103
|
if (!def)
|
|
102
104
|
return null;
|
|
103
|
-
|
|
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);
|
|
104
108
|
let value = evalExpr(baseExpr, { item });
|
|
105
109
|
if (pipe) {
|
|
106
110
|
const { name, args } = parsePipe(pipe);
|
|
@@ -112,7 +116,7 @@ function evaluateTemplate(def, item) {
|
|
|
112
116
|
}
|
|
113
117
|
const out = { type: def.type, value, class: def.class, style: def.style, color: def.color, variant: def.variant };
|
|
114
118
|
if (def.badge?.expr) {
|
|
115
|
-
const { baseExpr: b2, pipe: p2 } =
|
|
119
|
+
const { baseExpr: b2, pipe: p2 } = splitFirstTopLevelPipe(def.badge.expr);
|
|
116
120
|
let badgeVal = evalExpr(b2, { item });
|
|
117
121
|
if (p2) {
|
|
118
122
|
const { name, args } = parsePipe(p2);
|
|
@@ -136,6 +140,26 @@ function splitFirstPipe(expr) {
|
|
|
136
140
|
return { baseExpr: expr.trim() };
|
|
137
141
|
return { baseExpr: expr.slice(0, idx).trim(), pipe: expr.slice(idx + 1).trim() };
|
|
138
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
|
+
}
|
|
139
163
|
function parsePipe(pipeSpec) {
|
|
140
164
|
const idx = pipeSpec.indexOf(':');
|
|
141
165
|
if (idx === -1)
|
|
@@ -214,6 +238,7 @@ class ListDataService {
|
|
|
214
238
|
query$ = new BehaviorSubject({});
|
|
215
239
|
loading$ = new BehaviorSubject(false);
|
|
216
240
|
total$ = new BehaviorSubject(0);
|
|
241
|
+
pageState$ = this.pageable$.asObservable();
|
|
217
242
|
lastSig = '';
|
|
218
243
|
crud = inject(GenericCrudService, {
|
|
219
244
|
optional: true,
|
|
@@ -314,6 +339,9 @@ class ListDataService {
|
|
|
314
339
|
}
|
|
315
340
|
setQuery(q) {
|
|
316
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 });
|
|
317
345
|
}
|
|
318
346
|
groupedStream() {
|
|
319
347
|
return this.stream().pipe(switchMap((items) => this.config$.pipe(map((cfg) => cfg?.layout?.groupBy), map((groupBy) => {
|
|
@@ -515,6 +543,7 @@ class PraxisListConfigEditor {
|
|
|
515
543
|
selection: { mode: 'none', return: 'value' },
|
|
516
544
|
skin: { type: 'elevated', gradient: { from: '', to: '', angle: 135 } },
|
|
517
545
|
i18n: { locale: 'en-US', currency: 'USD' },
|
|
546
|
+
ui: {},
|
|
518
547
|
};
|
|
519
548
|
isDirty$ = new BehaviorSubject(false);
|
|
520
549
|
isValid$ = new BehaviorSubject(true);
|
|
@@ -533,6 +562,10 @@ class PraxisListConfigEditor {
|
|
|
533
562
|
mappingLeading = { type: 'icon' };
|
|
534
563
|
mapping = {};
|
|
535
564
|
mappingDirty = false;
|
|
565
|
+
statusPosition = undefined;
|
|
566
|
+
iconColorMapEntries = [];
|
|
567
|
+
// UI (search/sort/range) editor state
|
|
568
|
+
uiSortRows = [];
|
|
536
569
|
// Meta composition (multi-field)
|
|
537
570
|
mappingMetaFields = [];
|
|
538
571
|
mappingMetaSeparator = ' • ';
|
|
@@ -552,6 +585,7 @@ class PraxisListConfigEditor {
|
|
|
552
585
|
this.working = this.normalize(structuredClone(cfg));
|
|
553
586
|
// Initialize mapping UI from existing templating
|
|
554
587
|
this.hydrateMappingFromTemplating(this.working.templating);
|
|
588
|
+
this.hydrateUiEditorFromConfig();
|
|
555
589
|
}
|
|
556
590
|
this.initialJson = JSON.stringify(this.working);
|
|
557
591
|
this.lastJson = this.initialJson;
|
|
@@ -632,6 +666,7 @@ class PraxisListConfigEditor {
|
|
|
632
666
|
class: cfg.skin?.class,
|
|
633
667
|
},
|
|
634
668
|
i18n: cfg.i18n || {},
|
|
669
|
+
ui: cfg.ui || {},
|
|
635
670
|
};
|
|
636
671
|
return out;
|
|
637
672
|
}
|
|
@@ -807,6 +842,72 @@ class PraxisListConfigEditor {
|
|
|
807
842
|
this.markDirty();
|
|
808
843
|
this.verify();
|
|
809
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
|
+
}
|
|
810
911
|
// Legacy method retained to avoid breaking references; no-op now.
|
|
811
912
|
loadFieldsIfNeeded() { }
|
|
812
913
|
updateSortConfig() {
|
|
@@ -828,8 +929,10 @@ class PraxisListConfigEditor {
|
|
|
828
929
|
if (!slot?.field)
|
|
829
930
|
return undefined;
|
|
830
931
|
const base = '${item.' + slot.field + '}';
|
|
831
|
-
if (slot.type === 'text')
|
|
832
|
-
|
|
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
|
+
}
|
|
833
936
|
if (slot.type === 'chip')
|
|
834
937
|
return { type: 'chip', expr: base, class: slot.class, style: slot.style, color: slot.chipColor, variant: slot.chipVariant };
|
|
835
938
|
if (slot.type === 'rating')
|
|
@@ -852,6 +955,11 @@ class PraxisListConfigEditor {
|
|
|
852
955
|
param = ':' + style;
|
|
853
956
|
return { type: 'date', expr: param ? base + '|' + param : base, class: slot.class, style: slot.style };
|
|
854
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
|
+
}
|
|
855
963
|
return undefined;
|
|
856
964
|
};
|
|
857
965
|
const p = build(this.mappingPrimary);
|
|
@@ -900,6 +1008,22 @@ class PraxisListConfigEditor {
|
|
|
900
1008
|
t.metaPlacement = this.mappingMeta.placement;
|
|
901
1009
|
}
|
|
902
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
|
+
}
|
|
903
1027
|
// Apply features
|
|
904
1028
|
if (this.features && this.features.length) {
|
|
905
1029
|
t.features = this.features
|
|
@@ -945,6 +1069,37 @@ class PraxisListConfigEditor {
|
|
|
945
1069
|
this.mappingDirty = true;
|
|
946
1070
|
this.isDirty$.next(true);
|
|
947
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
|
+
}
|
|
948
1103
|
// ===============
|
|
949
1104
|
// Expr helpers (Features): click-to-insert fields and literals
|
|
950
1105
|
// ===============
|
|
@@ -1023,6 +1178,8 @@ class PraxisListConfigEditor {
|
|
|
1023
1178
|
Object.assign(base, parseCurrency(param));
|
|
1024
1179
|
if (templ.primary.type === 'date')
|
|
1025
1180
|
Object.assign(base, parseDate(param));
|
|
1181
|
+
if (templ.primary.type === 'text')
|
|
1182
|
+
base.extraPipe = param;
|
|
1026
1183
|
this.mappingPrimary = base;
|
|
1027
1184
|
}
|
|
1028
1185
|
if (templ.secondary) {
|
|
@@ -1032,6 +1189,8 @@ class PraxisListConfigEditor {
|
|
|
1032
1189
|
Object.assign(base, parseCurrency(param));
|
|
1033
1190
|
if (templ.secondary.type === 'date')
|
|
1034
1191
|
Object.assign(base, parseDate(param));
|
|
1192
|
+
if (templ.secondary.type === 'text')
|
|
1193
|
+
base.extraPipe = param;
|
|
1035
1194
|
this.mappingSecondary = base;
|
|
1036
1195
|
}
|
|
1037
1196
|
if (templ.meta) {
|
|
@@ -1041,6 +1200,8 @@ class PraxisListConfigEditor {
|
|
|
1041
1200
|
Object.assign(base, parseCurrency(param));
|
|
1042
1201
|
if (templ.meta.type === 'date')
|
|
1043
1202
|
Object.assign(base, parseDate(param));
|
|
1203
|
+
if (templ.meta.type === 'text')
|
|
1204
|
+
base.extraPipe = param;
|
|
1044
1205
|
this.mappingMeta = base;
|
|
1045
1206
|
if (templ.metaPlacement)
|
|
1046
1207
|
this.mappingMeta.placement = templ.metaPlacement;
|
|
@@ -1068,12 +1229,27 @@ class PraxisListConfigEditor {
|
|
|
1068
1229
|
Object.assign(base, parseCurrency(param));
|
|
1069
1230
|
if (templ.trailing.type === 'date')
|
|
1070
1231
|
Object.assign(base, parseDate(param));
|
|
1232
|
+
if (templ.trailing.type === 'icon' || templ.trailing.type === 'text')
|
|
1233
|
+
base.extraPipe = param;
|
|
1071
1234
|
this.mappingTrailing = base;
|
|
1072
1235
|
if (templ.trailing?.color)
|
|
1073
1236
|
this.mappingTrailing.chipColor = templ.trailing.color;
|
|
1074
1237
|
if (templ.trailing?.variant)
|
|
1075
1238
|
this.mappingTrailing.chipVariant = templ.trailing.variant;
|
|
1076
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
|
+
}
|
|
1077
1253
|
if (templ.leading?.type === 'icon' && typeof templ.leading.expr === 'string') {
|
|
1078
1254
|
this.mappingLeading = { type: 'icon', icon: templ.leading.expr };
|
|
1079
1255
|
}
|
|
@@ -1117,8 +1293,27 @@ class PraxisListConfigEditor {
|
|
|
1117
1293
|
const compareBy = (this.working?.selection?.compareBy || '').trim();
|
|
1118
1294
|
if (compareBy && this.fields.length && !this.fields.includes(compareBy))
|
|
1119
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
|
+
}
|
|
1120
1313
|
this.isValid$.next(valid);
|
|
1121
1314
|
}
|
|
1315
|
+
// UI editor hydration already called in ctor
|
|
1316
|
+
// Ensure UI.sortOptions are kept when saving via Settings Panel
|
|
1122
1317
|
inferFromFields() {
|
|
1123
1318
|
if (!this.fields?.length)
|
|
1124
1319
|
return;
|
|
@@ -1216,6 +1411,52 @@ class PraxisListConfigEditor {
|
|
|
1216
1411
|
</mat-select>
|
|
1217
1412
|
</mat-form-field>
|
|
1218
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>
|
|
1219
1460
|
</div>
|
|
1220
1461
|
</mat-tab>
|
|
1221
1462
|
<mat-tab label="Ações">
|
|
@@ -1381,6 +1622,18 @@ class PraxisListConfigEditor {
|
|
|
1381
1622
|
</mat-select>
|
|
1382
1623
|
</mat-form-field>
|
|
1383
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>
|
|
1384
1637
|
<mat-form-field appearance="outline">
|
|
1385
1638
|
<mat-label>Classe CSS</mat-label>
|
|
1386
1639
|
<input matInput [(ngModel)]="mappingPrimary.class" (ngModelChange)="onMappingChanged()" />
|
|
@@ -1436,6 +1689,18 @@ class PraxisListConfigEditor {
|
|
|
1436
1689
|
</mat-select>
|
|
1437
1690
|
</mat-form-field>
|
|
1438
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>
|
|
1439
1704
|
<mat-form-field appearance="outline">
|
|
1440
1705
|
<mat-label>Classe CSS</mat-label>
|
|
1441
1706
|
<input matInput [(ngModel)]="mappingSecondary.class" (ngModelChange)="onMappingChanged()" />
|
|
@@ -1530,6 +1795,18 @@ class PraxisListConfigEditor {
|
|
|
1530
1795
|
</mat-select>
|
|
1531
1796
|
</mat-form-field>
|
|
1532
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>
|
|
1533
1810
|
</div>
|
|
1534
1811
|
</mat-expansion-panel>
|
|
1535
1812
|
<mat-expansion-panel>
|
|
@@ -1550,8 +1827,19 @@ class PraxisListConfigEditor {
|
|
|
1550
1827
|
<mat-option value="chip">Chip</mat-option>
|
|
1551
1828
|
<mat-option value="currency">Moeda</mat-option>
|
|
1552
1829
|
<mat-option value="date">Data</mat-option>
|
|
1830
|
+
<mat-option value="icon">Ícone</mat-option>
|
|
1553
1831
|
</mat-select>
|
|
1554
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>
|
|
1555
1843
|
<ng-container *ngIf="mappingTrailing.type==='chip'">
|
|
1556
1844
|
<mat-form-field appearance="outline">
|
|
1557
1845
|
<mat-label>Cor do chip</mat-label>
|
|
@@ -1603,6 +1891,44 @@ class PraxisListConfigEditor {
|
|
|
1603
1891
|
<mat-label>Style inline</mat-label>
|
|
1604
1892
|
<input matInput [(ngModel)]="mappingTrailing.style" (ngModelChange)="onMappingChanged()" />
|
|
1605
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>
|
|
1606
1932
|
</div>
|
|
1607
1933
|
</mat-expansion-panel>
|
|
1608
1934
|
<mat-expansion-panel>
|
|
@@ -1863,7 +2189,7 @@ class PraxisListConfigEditor {
|
|
|
1863
2189
|
</div>
|
|
1864
2190
|
</mat-tab>
|
|
1865
2191
|
</mat-tab-group>
|
|
1866
|
-
`, 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"] }] });
|
|
1867
2193
|
}
|
|
1868
2194
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PraxisListConfigEditor, decorators: [{
|
|
1869
2195
|
type: Component,
|
|
@@ -1882,6 +2208,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
1882
2208
|
MatTooltipModule,
|
|
1883
2209
|
MatDividerModule,
|
|
1884
2210
|
MatChipsModule,
|
|
2211
|
+
MatMenuModule,
|
|
1885
2212
|
PraxisIconDirective,
|
|
1886
2213
|
PraxisListSkinPreviewComponent,
|
|
1887
2214
|
], template: `
|
|
@@ -1911,6 +2238,52 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
1911
2238
|
</mat-select>
|
|
1912
2239
|
</mat-form-field>
|
|
1913
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>
|
|
1914
2287
|
</div>
|
|
1915
2288
|
</mat-tab>
|
|
1916
2289
|
<mat-tab label="Ações">
|
|
@@ -2076,6 +2449,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
2076
2449
|
</mat-select>
|
|
2077
2450
|
</mat-form-field>
|
|
2078
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>
|
|
2079
2464
|
<mat-form-field appearance="outline">
|
|
2080
2465
|
<mat-label>Classe CSS</mat-label>
|
|
2081
2466
|
<input matInput [(ngModel)]="mappingPrimary.class" (ngModelChange)="onMappingChanged()" />
|
|
@@ -2131,6 +2516,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
2131
2516
|
</mat-select>
|
|
2132
2517
|
</mat-form-field>
|
|
2133
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>
|
|
2134
2531
|
<mat-form-field appearance="outline">
|
|
2135
2532
|
<mat-label>Classe CSS</mat-label>
|
|
2136
2533
|
<input matInput [(ngModel)]="mappingSecondary.class" (ngModelChange)="onMappingChanged()" />
|
|
@@ -2225,6 +2622,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
2225
2622
|
</mat-select>
|
|
2226
2623
|
</mat-form-field>
|
|
2227
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>
|
|
2228
2637
|
</div>
|
|
2229
2638
|
</mat-expansion-panel>
|
|
2230
2639
|
<mat-expansion-panel>
|
|
@@ -2245,8 +2654,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
2245
2654
|
<mat-option value="chip">Chip</mat-option>
|
|
2246
2655
|
<mat-option value="currency">Moeda</mat-option>
|
|
2247
2656
|
<mat-option value="date">Data</mat-option>
|
|
2657
|
+
<mat-option value="icon">Ícone</mat-option>
|
|
2248
2658
|
</mat-select>
|
|
2249
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>
|
|
2250
2670
|
<ng-container *ngIf="mappingTrailing.type==='chip'">
|
|
2251
2671
|
<mat-form-field appearance="outline">
|
|
2252
2672
|
<mat-label>Cor do chip</mat-label>
|
|
@@ -2298,6 +2718,44 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
2298
2718
|
<mat-label>Style inline</mat-label>
|
|
2299
2719
|
<input matInput [(ngModel)]="mappingTrailing.style" (ngModelChange)="onMappingChanged()" />
|
|
2300
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>
|
|
2301
2759
|
</div>
|
|
2302
2760
|
</mat-expansion-panel>
|
|
2303
2761
|
<mat-expansion-panel>
|
|
@@ -2558,7 +3016,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
2558
3016
|
</div>
|
|
2559
3017
|
</mat-tab>
|
|
2560
3018
|
</mat-tab-group>
|
|
2561
|
-
`, 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"] }]
|
|
2562
3020
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
2563
3021
|
type: Inject,
|
|
2564
3022
|
args: [SETTINGS_PANEL_DATA]
|
|
@@ -2578,6 +3036,9 @@ class PraxisList {
|
|
|
2578
3036
|
sections$;
|
|
2579
3037
|
loading$;
|
|
2580
3038
|
total$;
|
|
3039
|
+
page$;
|
|
3040
|
+
lastQuery = {};
|
|
3041
|
+
search$ = new Subject();
|
|
2581
3042
|
get layoutLines() {
|
|
2582
3043
|
return this.config?.layout?.lines ?? 2;
|
|
2583
3044
|
}
|
|
@@ -2599,8 +3060,18 @@ class PraxisList {
|
|
|
2599
3060
|
this.sections$ = this.data.groupedStream();
|
|
2600
3061
|
this.loading$ = this.data.loading$;
|
|
2601
3062
|
this.total$ = this.data.total$;
|
|
3063
|
+
this.page$ = this.data.pageState$;
|
|
2602
3064
|
this.setupSelectionBinding();
|
|
2603
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
|
+
});
|
|
2604
3075
|
}
|
|
2605
3076
|
ngOnChanges(changes) {
|
|
2606
3077
|
if (changes['config']) {
|
|
@@ -2615,8 +3086,10 @@ class PraxisList {
|
|
|
2615
3086
|
this.sections$ = this.data.groupedStream();
|
|
2616
3087
|
this.loading$ = this.data.loading$;
|
|
2617
3088
|
this.total$ = this.data.total$;
|
|
3089
|
+
this.page$ = this.data.pageState$;
|
|
2618
3090
|
this.setupSelectionBinding();
|
|
2619
3091
|
this.applySkins();
|
|
3092
|
+
this.lastQuery = this.config?.dataSource?.query || {};
|
|
2620
3093
|
}
|
|
2621
3094
|
}
|
|
2622
3095
|
applyPersistence() {
|
|
@@ -2664,7 +3137,8 @@ class PraxisList {
|
|
|
2664
3137
|
trailing = (item) => this.evalSlot('trailing', item);
|
|
2665
3138
|
sectionHeader = (key) => evaluateTemplate(this.config?.templating?.sectionHeader, { key })?.value || key;
|
|
2666
3139
|
featureLabel(item, expr) {
|
|
2667
|
-
|
|
3140
|
+
const out = evaluateTemplate({ type: 'text', expr: expr || '' }, item);
|
|
3141
|
+
return out?.value ?? '';
|
|
2668
3142
|
}
|
|
2669
3143
|
featuresVisible() {
|
|
2670
3144
|
const t = this.config?.templating;
|
|
@@ -2755,6 +3229,31 @@ class PraxisList {
|
|
|
2755
3229
|
if (isFinite(size) && size > 0)
|
|
2756
3230
|
this.data.setPageSize(size);
|
|
2757
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
|
+
}
|
|
2758
3257
|
onEditorApplied(newCfg) {
|
|
2759
3258
|
this.config = newCfg;
|
|
2760
3259
|
// Optionally infer templates from backend schema if not defined
|
|
@@ -2922,17 +3421,8 @@ class PraxisList {
|
|
|
2922
3421
|
return [spec.slice(0, idx).trim(), spec.slice(idx + 1).trim()];
|
|
2923
3422
|
}
|
|
2924
3423
|
evalString(expr, ctx) {
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
const value = path
|
|
2928
|
-
.split('.')
|
|
2929
|
-
.reduce((acc, k) => (acc == null ? undefined : acc[k]), ctx);
|
|
2930
|
-
return value == null ? '' : String(value);
|
|
2931
|
-
}
|
|
2932
|
-
catch {
|
|
2933
|
-
return '';
|
|
2934
|
-
}
|
|
2935
|
-
});
|
|
3424
|
+
const res = evaluateTemplate({ type: 'text', expr }, ctx?.item ?? ctx);
|
|
3425
|
+
return res?.value ?? '';
|
|
2936
3426
|
}
|
|
2937
3427
|
parseTwoParams(param) {
|
|
2938
3428
|
if (!param)
|
|
@@ -2994,7 +3484,7 @@ class PraxisList {
|
|
|
2994
3484
|
trackBySection = (_, s) => s?.key ?? _;
|
|
2995
3485
|
trackByItem = (i, it) => this.config?.selection?.compareBy ? (it?.[this.config.selection.compareBy] ?? i) : (it?.id ?? i);
|
|
2996
3486
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PraxisList, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2997
|
-
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\">\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; margin-left: 8px;\" 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 (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: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: "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 });
|
|
2998
3488
|
}
|
|
2999
3489
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PraxisList, decorators: [{
|
|
3000
3490
|
type: Component,
|
|
@@ -3009,7 +3499,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
3009
3499
|
MatButtonModule,
|
|
3010
3500
|
MatFormFieldModule,
|
|
3011
3501
|
MatSelectModule,
|
|
3012
|
-
], 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\">\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; margin-left: 8px;\" 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 (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: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"] }]
|
|
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"] }]
|
|
3013
3504
|
}], propDecorators: { config: [{
|
|
3014
3505
|
type: Input
|
|
3015
3506
|
}], form: [{
|