@memberjunction/ng-entity-viewer 5.40.2 → 5.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/entity-cards-selection.test.d.ts +2 -0
- package/dist/__tests__/entity-cards-selection.test.d.ts.map +1 -0
- package/dist/__tests__/entity-cards-selection.test.js +149 -0
- package/dist/__tests__/entity-cards-selection.test.js.map +1 -0
- package/dist/__tests__/entity-cards-viewmodel.test.d.ts +2 -0
- package/dist/__tests__/entity-cards-viewmodel.test.d.ts.map +1 -0
- package/dist/__tests__/entity-cards-viewmodel.test.js +208 -0
- package/dist/__tests__/entity-cards-viewmodel.test.js.map +1 -0
- package/dist/lib/entity-cards/entity-cards.component.d.ts +108 -1
- package/dist/lib/entity-cards/entity-cards.component.d.ts.map +1 -1
- package/dist/lib/entity-cards/entity-cards.component.js +200 -83
- package/dist/lib/entity-cards/entity-cards.component.js.map +1 -1
- package/package.json +15 -15
|
@@ -7,8 +7,8 @@ import { HighlightUtil } from '../utils/highlight.util';
|
|
|
7
7
|
import * as i0 from "@angular/core";
|
|
8
8
|
import * as i1 from "@memberjunction/ng-shared-generic";
|
|
9
9
|
import * as i2 from "../pill/pill.component";
|
|
10
|
-
|
|
11
|
-
const _forTrack1 = ($index, $item) => $item.name;
|
|
10
|
+
const _forTrack0 = ($index, $item) => $item.trackId;
|
|
11
|
+
const _forTrack1 = ($index, $item) => $item.field.name;
|
|
12
12
|
function EntityCardsComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
13
13
|
i0.ɵɵelementStart(0, "div", 1);
|
|
14
14
|
i0.ɵɵelement(1, "mj-loading", 3);
|
|
@@ -19,48 +19,41 @@ function EntityCardsComponent_Conditional_2_For_2_Case_2_Template(rf, ctx) { if
|
|
|
19
19
|
i0.ɵɵelement(1, "img", 19);
|
|
20
20
|
i0.ɵɵelementEnd();
|
|
21
21
|
} if (rf & 2) {
|
|
22
|
-
const
|
|
23
|
-
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
22
|
+
const vm_r2 = i0.ɵɵnextContext().$implicit;
|
|
24
23
|
i0.ɵɵadvance();
|
|
25
|
-
i0.ɵɵproperty("src",
|
|
24
|
+
i0.ɵɵproperty("src", vm_r2.thumbnailUrl, i0.ɵɵsanitizeUrl);
|
|
26
25
|
} }
|
|
27
26
|
function EntityCardsComponent_Conditional_2_For_2_Case_3_Template(rf, ctx) { if (rf & 1) {
|
|
28
27
|
i0.ɵɵelementStart(0, "div", 20);
|
|
29
28
|
i0.ɵɵelement(1, "i");
|
|
30
29
|
i0.ɵɵelementEnd();
|
|
31
30
|
} if (rf & 2) {
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
i0.ɵɵstyleProp("background-color", ctx_r2.getRecordColor(record_r2));
|
|
31
|
+
const vm_r2 = i0.ɵɵnextContext().$implicit;
|
|
32
|
+
i0.ɵɵstyleProp("background-color", vm_r2.color);
|
|
35
33
|
i0.ɵɵadvance();
|
|
36
|
-
i0.ɵɵclassMap(
|
|
34
|
+
i0.ɵɵclassMap(vm_r2.thumbnailUrl);
|
|
37
35
|
} }
|
|
38
36
|
function EntityCardsComponent_Conditional_2_For_2_Case_4_Template(rf, ctx) { if (rf & 1) {
|
|
39
37
|
i0.ɵɵelementStart(0, "div", 20);
|
|
40
38
|
i0.ɵɵtext(1);
|
|
41
39
|
i0.ɵɵelementEnd();
|
|
42
40
|
} if (rf & 2) {
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
i0.ɵɵstyleProp("background-color", ctx_r2.getRecordColor(record_r2));
|
|
41
|
+
const vm_r2 = i0.ɵɵnextContext().$implicit;
|
|
42
|
+
i0.ɵɵstyleProp("background-color", vm_r2.color);
|
|
46
43
|
i0.ɵɵadvance();
|
|
47
|
-
i0.ɵɵtextInterpolate1(" ",
|
|
44
|
+
i0.ɵɵtextInterpolate1(" ", vm_r2.initials, " ");
|
|
48
45
|
} }
|
|
49
46
|
function EntityCardsComponent_Conditional_2_For_2_Conditional_7_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
50
47
|
i0.ɵɵelement(0, "mj-pill", 21);
|
|
51
48
|
} if (rf & 2) {
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
55
|
-
i0.ɵɵproperty("value", ctx_r2.getFieldValue(record_r2, subField_r4));
|
|
49
|
+
const vm_r2 = i0.ɵɵnextContext(2).$implicit;
|
|
50
|
+
i0.ɵɵproperty("value", vm_r2.subtitleValue);
|
|
56
51
|
} }
|
|
57
52
|
function EntityCardsComponent_Conditional_2_For_2_Conditional_7_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
58
53
|
i0.ɵɵelement(0, "span", 22);
|
|
59
54
|
} if (rf & 2) {
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
63
|
-
i0.ɵɵproperty("innerHTML", ctx_r2.highlightMatch(ctx_r2.getFieldValue(record_r2, subField_r4)), i0.ɵɵsanitizeHtml);
|
|
55
|
+
const vm_r2 = i0.ɵɵnextContext(2).$implicit;
|
|
56
|
+
i0.ɵɵproperty("innerHTML", vm_r2.highlightedSubtitle, i0.ɵɵsanitizeHtml);
|
|
64
57
|
} }
|
|
65
58
|
function EntityCardsComponent_Conditional_2_For_2_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
66
59
|
i0.ɵɵconditionalCreate(0, EntityCardsComponent_Conditional_2_For_2_Conditional_7_Conditional_0_Template, 1, 1, "mj-pill", 21)(1, EntityCardsComponent_Conditional_2_For_2_Conditional_7_Conditional_1_Template, 1, 1, "span", 22);
|
|
@@ -71,9 +64,8 @@ function EntityCardsComponent_Conditional_2_For_2_Conditional_7_Template(rf, ctx
|
|
|
71
64
|
function EntityCardsComponent_Conditional_2_For_2_Conditional_11_Template(rf, ctx) { if (rf & 1) {
|
|
72
65
|
i0.ɵɵelement(0, "p", 15);
|
|
73
66
|
} if (rf & 2) {
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
i0.ɵɵproperty("innerHTML", ctx_r2.highlightMatch(ctx_r2.getTextValue(record_r2, ctx, 100)), i0.ɵɵsanitizeHtml);
|
|
67
|
+
const vm_r2 = i0.ɵɵnextContext().$implicit;
|
|
68
|
+
i0.ɵɵproperty("innerHTML", vm_r2.highlightedDescription, i0.ɵɵsanitizeHtml);
|
|
77
69
|
} }
|
|
78
70
|
function EntityCardsComponent_Conditional_2_For_2_Conditional_12_For_2_Case_1_Template(rf, ctx) { if (rf & 1) {
|
|
79
71
|
i0.ɵɵelement(0, "i", 25);
|
|
@@ -81,12 +73,10 @@ function EntityCardsComponent_Conditional_2_For_2_Conditional_12_For_2_Case_1_Te
|
|
|
81
73
|
i0.ɵɵtext(2);
|
|
82
74
|
i0.ɵɵelementEnd();
|
|
83
75
|
} if (rf & 2) {
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
87
|
-
i0.ɵɵclassProp("fa-solid", true)("fa-check", ctx_r2.getBooleanValue(record_r2, field_r5.name))("fa-minus", !ctx_r2.getBooleanValue(record_r2, field_r5.name))("bool-true", ctx_r2.getBooleanValue(record_r2, field_r5.name))("bool-false", !ctx_r2.getBooleanValue(record_r2, field_r5.name));
|
|
76
|
+
const df_r4 = i0.ɵɵnextContext().$implicit;
|
|
77
|
+
i0.ɵɵclassProp("fa-solid", true)("fa-check", df_r4.booleanValue)("fa-minus", !df_r4.booleanValue)("bool-true", df_r4.booleanValue)("bool-false", !df_r4.booleanValue);
|
|
88
78
|
i0.ɵɵadvance(2);
|
|
89
|
-
i0.ɵɵtextInterpolate(
|
|
79
|
+
i0.ɵɵtextInterpolate(df_r4.field.label);
|
|
90
80
|
} }
|
|
91
81
|
function EntityCardsComponent_Conditional_2_For_2_Conditional_12_For_2_Case_2_Template(rf, ctx) { if (rf & 1) {
|
|
92
82
|
i0.ɵɵelementStart(0, "span", 27);
|
|
@@ -96,13 +86,11 @@ function EntityCardsComponent_Conditional_2_For_2_Conditional_12_For_2_Case_2_Te
|
|
|
96
86
|
i0.ɵɵtext(3);
|
|
97
87
|
i0.ɵɵelementEnd();
|
|
98
88
|
} if (rf & 2) {
|
|
99
|
-
const
|
|
100
|
-
const record_r2 = i0.ɵɵnextContext(2).$implicit;
|
|
101
|
-
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
89
|
+
const df_r4 = i0.ɵɵnextContext().$implicit;
|
|
102
90
|
i0.ɵɵadvance();
|
|
103
|
-
i0.ɵɵtextInterpolate(
|
|
91
|
+
i0.ɵɵtextInterpolate(df_r4.numericValue);
|
|
104
92
|
i0.ɵɵadvance(2);
|
|
105
|
-
i0.ɵɵtextInterpolate(
|
|
93
|
+
i0.ɵɵtextInterpolate(df_r4.field.label);
|
|
106
94
|
} }
|
|
107
95
|
function EntityCardsComponent_Conditional_2_For_2_Conditional_12_For_2_Case_3_Template(rf, ctx) { if (rf & 1) {
|
|
108
96
|
i0.ɵɵelementStart(0, "span", 28);
|
|
@@ -112,13 +100,11 @@ function EntityCardsComponent_Conditional_2_For_2_Conditional_12_For_2_Case_3_Te
|
|
|
112
100
|
i0.ɵɵtext(3);
|
|
113
101
|
i0.ɵɵelementEnd();
|
|
114
102
|
} if (rf & 2) {
|
|
115
|
-
const
|
|
116
|
-
const record_r2 = i0.ɵɵnextContext(2).$implicit;
|
|
117
|
-
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
103
|
+
const df_r4 = i0.ɵɵnextContext().$implicit;
|
|
118
104
|
i0.ɵɵadvance();
|
|
119
|
-
i0.ɵɵtextInterpolate(
|
|
105
|
+
i0.ɵɵtextInterpolate(df_r4.dateValue);
|
|
120
106
|
i0.ɵɵadvance(2);
|
|
121
|
-
i0.ɵɵtextInterpolate(
|
|
107
|
+
i0.ɵɵtextInterpolate(df_r4.field.label);
|
|
122
108
|
} }
|
|
123
109
|
function EntityCardsComponent_Conditional_2_For_2_Conditional_12_For_2_Case_4_Template(rf, ctx) { if (rf & 1) {
|
|
124
110
|
i0.ɵɵelementStart(0, "span", 26);
|
|
@@ -126,13 +112,11 @@ function EntityCardsComponent_Conditional_2_For_2_Conditional_12_For_2_Case_4_Te
|
|
|
126
112
|
i0.ɵɵelementEnd();
|
|
127
113
|
i0.ɵɵelement(2, "span", 29);
|
|
128
114
|
} if (rf & 2) {
|
|
129
|
-
const
|
|
130
|
-
const record_r2 = i0.ɵɵnextContext(2).$implicit;
|
|
131
|
-
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
115
|
+
const df_r4 = i0.ɵɵnextContext().$implicit;
|
|
132
116
|
i0.ɵɵadvance();
|
|
133
|
-
i0.ɵɵtextInterpolate(
|
|
117
|
+
i0.ɵɵtextInterpolate(df_r4.field.label);
|
|
134
118
|
i0.ɵɵadvance();
|
|
135
|
-
i0.ɵɵproperty("innerHTML",
|
|
119
|
+
i0.ɵɵproperty("innerHTML", df_r4.highlightedText, i0.ɵɵsanitizeHtml);
|
|
136
120
|
} }
|
|
137
121
|
function EntityCardsComponent_Conditional_2_For_2_Conditional_12_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
138
122
|
i0.ɵɵelementStart(0, "div", 24);
|
|
@@ -140,29 +124,28 @@ function EntityCardsComponent_Conditional_2_For_2_Conditional_12_For_2_Template(
|
|
|
140
124
|
i0.ɵɵelementEnd();
|
|
141
125
|
} if (rf & 2) {
|
|
142
126
|
let tmp_24_0;
|
|
143
|
-
const
|
|
144
|
-
i0.ɵɵclassProp("field-boolean",
|
|
127
|
+
const df_r4 = ctx.$implicit;
|
|
128
|
+
i0.ɵɵclassProp("field-boolean", df_r4.field.type === "boolean")("field-text", df_r4.field.type === "text");
|
|
145
129
|
i0.ɵɵadvance();
|
|
146
|
-
i0.ɵɵconditional((tmp_24_0 =
|
|
130
|
+
i0.ɵɵconditional((tmp_24_0 = df_r4.field.type) === "boolean" ? 1 : tmp_24_0 === "number" ? 2 : tmp_24_0 === "date" ? 3 : 4);
|
|
147
131
|
} }
|
|
148
132
|
function EntityCardsComponent_Conditional_2_For_2_Conditional_12_Template(rf, ctx) { if (rf & 1) {
|
|
149
133
|
i0.ɵɵelementStart(0, "div", 16);
|
|
150
134
|
i0.ɵɵrepeaterCreate(1, EntityCardsComponent_Conditional_2_For_2_Conditional_12_For_2_Template, 5, 5, "div", 23, _forTrack1);
|
|
151
135
|
i0.ɵɵelementEnd();
|
|
152
136
|
} if (rf & 2) {
|
|
153
|
-
const
|
|
137
|
+
const vm_r2 = i0.ɵɵnextContext().$implicit;
|
|
154
138
|
i0.ɵɵadvance();
|
|
155
|
-
i0.ɵɵrepeater(
|
|
139
|
+
i0.ɵɵrepeater(vm_r2.displayFields);
|
|
156
140
|
} }
|
|
157
141
|
function EntityCardsComponent_Conditional_2_For_2_Conditional_13_Template(rf, ctx) { if (rf & 1) {
|
|
158
142
|
i0.ɵɵelementStart(0, "div", 17);
|
|
159
143
|
i0.ɵɵelement(1, "mj-pill", 21);
|
|
160
144
|
i0.ɵɵelementEnd();
|
|
161
145
|
} if (rf & 2) {
|
|
162
|
-
const
|
|
163
|
-
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
146
|
+
const vm_r2 = i0.ɵɵnextContext().$implicit;
|
|
164
147
|
i0.ɵɵadvance();
|
|
165
|
-
i0.ɵɵproperty("value",
|
|
148
|
+
i0.ɵɵproperty("value", vm_r2.badgeValue);
|
|
166
149
|
} }
|
|
167
150
|
function EntityCardsComponent_Conditional_2_For_2_Conditional_14_Template(rf, ctx) { if (rf & 1) {
|
|
168
151
|
i0.ɵɵelementStart(0, "div", 18);
|
|
@@ -171,16 +154,15 @@ function EntityCardsComponent_Conditional_2_For_2_Conditional_14_Template(rf, ct
|
|
|
171
154
|
i0.ɵɵtext(3);
|
|
172
155
|
i0.ɵɵelementEnd()();
|
|
173
156
|
} if (rf & 2) {
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
i0.ɵɵproperty("title", "Matched in: " + ctx_r2.getHiddenMatchFieldName(record_r2));
|
|
157
|
+
const vm_r2 = i0.ɵɵnextContext().$implicit;
|
|
158
|
+
i0.ɵɵproperty("title", "Matched in: " + vm_r2.hiddenMatchFieldName);
|
|
177
159
|
i0.ɵɵadvance(3);
|
|
178
|
-
i0.ɵɵtextInterpolate(
|
|
160
|
+
i0.ɵɵtextInterpolate(vm_r2.hiddenMatchFieldName);
|
|
179
161
|
} }
|
|
180
162
|
function EntityCardsComponent_Conditional_2_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
181
163
|
const _r1 = i0.ɵɵgetCurrentView();
|
|
182
164
|
i0.ɵɵelementStart(0, "div", 6);
|
|
183
|
-
i0.ɵɵlistener("click", function EntityCardsComponent_Conditional_2_For_2_Template_div_click_0_listener() { const
|
|
165
|
+
i0.ɵɵlistener("click", function EntityCardsComponent_Conditional_2_For_2_Template_div_click_0_listener() { const vm_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onCardClick(vm_r2.record)); });
|
|
184
166
|
i0.ɵɵelementStart(1, "div", 7);
|
|
185
167
|
i0.ɵɵconditionalCreate(2, EntityCardsComponent_Conditional_2_For_2_Case_2_Template, 2, 1, "div", 8)(3, EntityCardsComponent_Conditional_2_For_2_Case_3_Template, 2, 4, "div", 9)(4, EntityCardsComponent_Conditional_2_For_2_Case_4_Template, 2, 3, "div", 9);
|
|
186
168
|
i0.ɵɵelementStart(5, "div", 10);
|
|
@@ -188,7 +170,7 @@ function EntityCardsComponent_Conditional_2_For_2_Template(rf, ctx) { if (rf & 1
|
|
|
188
170
|
i0.ɵɵconditionalCreate(7, EntityCardsComponent_Conditional_2_For_2_Conditional_7_Template, 2, 1);
|
|
189
171
|
i0.ɵɵelementEnd();
|
|
190
172
|
i0.ɵɵelementStart(8, "button", 12);
|
|
191
|
-
i0.ɵɵlistener("click", function EntityCardsComponent_Conditional_2_For_2_Template_button_click_8_listener($event) { const
|
|
173
|
+
i0.ɵɵlistener("click", function EntityCardsComponent_Conditional_2_For_2_Template_button_click_8_listener($event) { const vm_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onOpenClick($event, vm_r2.record)); });
|
|
192
174
|
i0.ɵɵelement(9, "i", 13);
|
|
193
175
|
i0.ɵɵelementEnd()();
|
|
194
176
|
i0.ɵɵelementStart(10, "div", 14);
|
|
@@ -200,26 +182,23 @@ function EntityCardsComponent_Conditional_2_For_2_Template(rf, ctx) { if (rf & 1
|
|
|
200
182
|
i0.ɵɵelementEnd();
|
|
201
183
|
} if (rf & 2) {
|
|
202
184
|
let tmp_12_0;
|
|
203
|
-
|
|
204
|
-
let tmp_15_0;
|
|
205
|
-
let tmp_17_0;
|
|
206
|
-
const record_r2 = ctx.$implicit;
|
|
185
|
+
const vm_r2 = ctx.$implicit;
|
|
207
186
|
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
208
|
-
i0.ɵɵclassProp("selected",
|
|
187
|
+
i0.ɵɵclassProp("selected", vm_r2.isSelected);
|
|
209
188
|
i0.ɵɵadvance(2);
|
|
210
|
-
i0.ɵɵconditional((tmp_12_0 =
|
|
189
|
+
i0.ɵɵconditional((tmp_12_0 = vm_r2.thumbnailType) === "image" ? 2 : tmp_12_0 === "icon" ? 3 : 4);
|
|
211
190
|
i0.ɵɵadvance(4);
|
|
212
|
-
i0.ɵɵproperty("innerHTML",
|
|
191
|
+
i0.ɵɵproperty("innerHTML", vm_r2.highlightedTitle, i0.ɵɵsanitizeHtml);
|
|
213
192
|
i0.ɵɵadvance();
|
|
214
|
-
i0.ɵɵconditional((
|
|
193
|
+
i0.ɵɵconditional((ctx_r2.effectiveTemplate == null ? null : ctx_r2.effectiveTemplate.subtitleField) ? 7 : -1);
|
|
215
194
|
i0.ɵɵadvance(4);
|
|
216
|
-
i0.ɵɵconditional((
|
|
195
|
+
i0.ɵɵconditional((ctx_r2.effectiveTemplate == null ? null : ctx_r2.effectiveTemplate.descriptionField) ? 11 : -1);
|
|
217
196
|
i0.ɵɵadvance();
|
|
218
|
-
i0.ɵɵconditional(
|
|
197
|
+
i0.ɵɵconditional(vm_r2.displayFields.length > 0 ? 12 : -1);
|
|
219
198
|
i0.ɵɵadvance();
|
|
220
|
-
i0.ɵɵconditional((
|
|
199
|
+
i0.ɵɵconditional((ctx_r2.effectiveTemplate == null ? null : ctx_r2.effectiveTemplate.badgeField) ? 13 : -1);
|
|
221
200
|
i0.ɵɵadvance();
|
|
222
|
-
i0.ɵɵconditional(
|
|
201
|
+
i0.ɵɵconditional(vm_r2.hasHiddenFieldMatch ? 14 : -1);
|
|
223
202
|
} }
|
|
224
203
|
function EntityCardsComponent_Conditional_2_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
225
204
|
i0.ɵɵelementStart(0, "div", 5);
|
|
@@ -230,13 +209,13 @@ function EntityCardsComponent_Conditional_2_Conditional_3_Template(rf, ctx) { if
|
|
|
230
209
|
} }
|
|
231
210
|
function EntityCardsComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
232
211
|
i0.ɵɵelementStart(0, "div", 2);
|
|
233
|
-
i0.ɵɵrepeaterCreate(1, EntityCardsComponent_Conditional_2_For_2_Template, 15, 9, "div", 4, _forTrack0
|
|
212
|
+
i0.ɵɵrepeaterCreate(1, EntityCardsComponent_Conditional_2_For_2_Template, 15, 9, "div", 4, _forTrack0);
|
|
234
213
|
i0.ɵɵconditionalCreate(3, EntityCardsComponent_Conditional_2_Conditional_3_Template, 4, 0, "div", 5);
|
|
235
214
|
i0.ɵɵelementEnd();
|
|
236
215
|
} if (rf & 2) {
|
|
237
216
|
const ctx_r2 = i0.ɵɵnextContext();
|
|
238
217
|
i0.ɵɵadvance();
|
|
239
|
-
i0.ɵɵrepeater(ctx_r2.
|
|
218
|
+
i0.ɵɵrepeater(ctx_r2.cardViewModels);
|
|
240
219
|
i0.ɵɵadvance(2);
|
|
241
220
|
i0.ɵɵconditional(ctx_r2.effectiveRecords.length === 0 ? 3 : -1);
|
|
242
221
|
} }
|
|
@@ -318,6 +297,14 @@ export class EntityCardsComponent extends BaseAngularComponent {
|
|
|
318
297
|
isLoading = false;
|
|
319
298
|
/** Flag to trigger scroll to selected card after view renders */
|
|
320
299
|
pendingScrollToSelected = false;
|
|
300
|
+
/**
|
|
301
|
+
* Precomputed per-record view-models bound by the template's @for. Rebuilt
|
|
302
|
+
* only when the source records, filter, selection, hidden matches, or template
|
|
303
|
+
* change — never per change-detection cycle. This eliminates the 4-5
|
|
304
|
+
* buildPkString() allocations + 4 RegExp-building highlight calls that the
|
|
305
|
+
* previous per-card-per-CD method bindings incurred.
|
|
306
|
+
*/
|
|
307
|
+
cardViewModels = [];
|
|
321
308
|
ngOnInit() {
|
|
322
309
|
this.standaloneMode = this.records === null;
|
|
323
310
|
if (this.entity?.Fields && !this.effectiveTemplate) {
|
|
@@ -326,6 +313,7 @@ export class EntityCardsComponent extends BaseAngularComponent {
|
|
|
326
313
|
if (this.standaloneMode && this.entity) {
|
|
327
314
|
this.loadData();
|
|
328
315
|
}
|
|
316
|
+
this.buildCardViewModels();
|
|
329
317
|
}
|
|
330
318
|
ngOnChanges(changes) {
|
|
331
319
|
if (changes['entity'] && this.entity?.Fields) {
|
|
@@ -348,6 +336,23 @@ export class EntityCardsComponent extends BaseAngularComponent {
|
|
|
348
336
|
if (changes['selectedRecordId'] && this.selectedRecordId) {
|
|
349
337
|
this.pendingScrollToSelected = true;
|
|
350
338
|
}
|
|
339
|
+
// Rebuild the precomputed VMs whenever any input that affects card *content*
|
|
340
|
+
// changes. cardTemplate is included because effectiveTemplate derives from it.
|
|
341
|
+
// A selection-only change must NOT trigger a full rebuild — that would re-run
|
|
342
|
+
// the expensive per-card buildPkString()/CompositeKey allocations and the 4
|
|
343
|
+
// RegExp-building highlightMatch() calls for every card on every selection
|
|
344
|
+
// click (O(n) heavy work). Instead we only flip the cheap isSelected flags.
|
|
345
|
+
const contentChanged = !!(changes['records'] || changes['filterText'] ||
|
|
346
|
+
changes['hiddenFieldMatches'] || changes['entity'] || changes['cardTemplate']);
|
|
347
|
+
if (contentChanged) {
|
|
348
|
+
// Full rebuild also recomputes isSelected correctly (buildCardViewModel does).
|
|
349
|
+
this.buildCardViewModels();
|
|
350
|
+
}
|
|
351
|
+
else if (changes['selectedRecordId']) {
|
|
352
|
+
// Selection-only change: just update the isSelected flags on the existing
|
|
353
|
+
// VMs (string compare per card — no rebuild, no regex, no allocation).
|
|
354
|
+
this.updateSelectionFlags();
|
|
355
|
+
}
|
|
351
356
|
}
|
|
352
357
|
ngAfterViewChecked() {
|
|
353
358
|
if (this.pendingScrollToSelected && this.selectedRecordId) {
|
|
@@ -392,6 +397,7 @@ export class EntityCardsComponent extends BaseAngularComponent {
|
|
|
392
397
|
});
|
|
393
398
|
if (result.Success) {
|
|
394
399
|
this.internalRecords = result.Results;
|
|
400
|
+
this.buildCardViewModels();
|
|
395
401
|
}
|
|
396
402
|
}
|
|
397
403
|
catch (error) {
|
|
@@ -652,6 +658,125 @@ export class EntityCardsComponent extends BaseAngularComponent {
|
|
|
652
658
|
compositeKey: buildCompositeKey(record, this.entity)
|
|
653
659
|
});
|
|
654
660
|
}
|
|
661
|
+
// ========================================
|
|
662
|
+
// PRECOMPUTED VIEW-MODELS (perf)
|
|
663
|
+
// ========================================
|
|
664
|
+
/**
|
|
665
|
+
* Rebuild the precomputed per-record view-models. Called from ngOnInit,
|
|
666
|
+
* ngOnChanges (on any render-affecting input), and after standalone loadData.
|
|
667
|
+
* Doing this work here — once per data/filter/selection change — instead of via
|
|
668
|
+
* template method bindings avoids re-running it on every Angular CD cycle.
|
|
669
|
+
*/
|
|
670
|
+
buildCardViewModels() {
|
|
671
|
+
const records = this.effectiveRecords;
|
|
672
|
+
this.cardViewModels = records.map((record, index) => this.buildCardViewModel(record, index));
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Cheaply update the isSelected flag on the EXISTING card view-models in place,
|
|
676
|
+
* preserving the array and every VM object reference. Called when ONLY
|
|
677
|
+
* selectedRecordId changes — avoids a full buildCardViewModels() rebuild (which
|
|
678
|
+
* re-runs buildPkString() allocations + RegExp-building highlightMatch() calls
|
|
679
|
+
* per card). Uses the already-computed vm.pkString, so this is a plain string
|
|
680
|
+
* compare per card with zero allocation or regex work.
|
|
681
|
+
*/
|
|
682
|
+
updateSelectionFlags() {
|
|
683
|
+
for (const vm of this.cardViewModels) {
|
|
684
|
+
vm.isSelected = vm.pkString.length > 0 && vm.pkString === this.selectedRecordId;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Build a single card view-model, computing the PK string exactly once and
|
|
689
|
+
* reusing it for selection, color, hidden-match, and track-id derivation.
|
|
690
|
+
*/
|
|
691
|
+
buildCardViewModel(record, index) {
|
|
692
|
+
const pkString = this.computePkString(record);
|
|
693
|
+
const trackId = pkString && pkString.trim().length > 0 ? pkString : `record_${index}`;
|
|
694
|
+
const template = this.effectiveTemplate;
|
|
695
|
+
const subtitleField = template?.subtitleField ?? null;
|
|
696
|
+
const subtitleValue = subtitleField ? this.getFieldValue(record, subtitleField) : '';
|
|
697
|
+
const descriptionField = template?.descriptionField ?? null;
|
|
698
|
+
return {
|
|
699
|
+
record,
|
|
700
|
+
pkString,
|
|
701
|
+
trackId,
|
|
702
|
+
isSelected: pkString.length > 0 && pkString === this.selectedRecordId,
|
|
703
|
+
color: this.computeRecordColor(pkString),
|
|
704
|
+
thumbnailType: this.getThumbnailType(record),
|
|
705
|
+
thumbnailUrl: this.getThumbnailUrl(record),
|
|
706
|
+
initials: this.getInitials(record),
|
|
707
|
+
highlightedTitle: this.highlightMatch(this.getCombinedTitle(record)),
|
|
708
|
+
subtitleValue,
|
|
709
|
+
highlightedSubtitle: this.highlightMatch(subtitleValue),
|
|
710
|
+
highlightedDescription: descriptionField
|
|
711
|
+
? this.highlightMatch(this.getTextValue(record, descriptionField, 100))
|
|
712
|
+
: '',
|
|
713
|
+
displayFields: this.buildDisplayFieldVMs(record, template),
|
|
714
|
+
badgeValue: template?.badgeField ? this.getFieldValue(record, template.badgeField) : '',
|
|
715
|
+
hasHiddenFieldMatch: this.computeHasHiddenFieldMatch(pkString),
|
|
716
|
+
hiddenMatchFieldName: this.computeHiddenMatchFieldName(pkString)
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* Precompute the display-field VMs for a record, in template order.
|
|
721
|
+
*/
|
|
722
|
+
buildDisplayFieldVMs(record, template) {
|
|
723
|
+
if (!template || template.displayFields.length === 0)
|
|
724
|
+
return [];
|
|
725
|
+
return template.displayFields.map(field => ({
|
|
726
|
+
field,
|
|
727
|
+
booleanValue: this.getBooleanValue(record, field.name),
|
|
728
|
+
numericValue: this.getNumericValue(record, field.name),
|
|
729
|
+
dateValue: this.getDateValue(record, field.name),
|
|
730
|
+
highlightedText: this.highlightMatch(this.getTextValue(record, field.name, 40))
|
|
731
|
+
}));
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* Compute the PK concatenated string for a record. Returns '' when no entity
|
|
735
|
+
* or on error, so callers can treat it as "no stable key".
|
|
736
|
+
*/
|
|
737
|
+
computePkString(record) {
|
|
738
|
+
if (!this.entity)
|
|
739
|
+
return '';
|
|
740
|
+
try {
|
|
741
|
+
return buildPkString(record, this.entity);
|
|
742
|
+
}
|
|
743
|
+
catch {
|
|
744
|
+
return '';
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Derive the avatar/background color from an already-computed PK string.
|
|
749
|
+
*/
|
|
750
|
+
computeRecordColor(pkString) {
|
|
751
|
+
if (!this.entity || !pkString)
|
|
752
|
+
return 'var(--mj-brand-primary)';
|
|
753
|
+
const colors = ['var(--mj-brand-primary)', 'var(--mj-status-success)', 'var(--mj-status-warning)', 'var(--mj-brand-primary)', 'var(--mj-status-error)', 'var(--mj-brand-primary)', 'var(--mj-text-muted)', 'var(--mj-text-secondary)'];
|
|
754
|
+
let hash = 0;
|
|
755
|
+
for (let i = 0; i < pkString.length; i++) {
|
|
756
|
+
hash = pkString.charCodeAt(i) + ((hash << 5) - hash);
|
|
757
|
+
}
|
|
758
|
+
return colors[Math.abs(hash) % colors.length];
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Whether the record (by PK string) matched on a hidden field.
|
|
762
|
+
*/
|
|
763
|
+
computeHasHiddenFieldMatch(pkString) {
|
|
764
|
+
if (!this.entity || !pkString)
|
|
765
|
+
return false;
|
|
766
|
+
return this.hiddenFieldMatches.has(pkString);
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Resolve the display name of the hidden field that matched, from a PK string.
|
|
770
|
+
*/
|
|
771
|
+
computeHiddenMatchFieldName(pkString) {
|
|
772
|
+
if (!this.entity || !pkString)
|
|
773
|
+
return '';
|
|
774
|
+
const fieldName = this.hiddenFieldMatches.get(pkString);
|
|
775
|
+
if (!fieldName)
|
|
776
|
+
return '';
|
|
777
|
+
const field = this.entity.Fields.find(f => f.Name === fieldName);
|
|
778
|
+
return field ? field.DisplayNameOrName : fieldName;
|
|
779
|
+
}
|
|
655
780
|
/**
|
|
656
781
|
* Get the combined title from all title fields for a record.
|
|
657
782
|
* Joins multiple IsNameField values with spaces (e.g., "Elizabeth Rodriguez").
|
|
@@ -744,15 +869,7 @@ export class EntityCardsComponent extends BaseAngularComponent {
|
|
|
744
869
|
return false;
|
|
745
870
|
}
|
|
746
871
|
getRecordColor(record) {
|
|
747
|
-
|
|
748
|
-
return 'var(--mj-brand-primary)';
|
|
749
|
-
const colors = ['var(--mj-brand-primary)', 'var(--mj-status-success)', 'var(--mj-status-warning)', 'var(--mj-brand-primary)', 'var(--mj-status-error)', 'var(--mj-brand-primary)', 'var(--mj-text-muted)', 'var(--mj-text-secondary)'];
|
|
750
|
-
const pk = buildPkString(record, this.entity);
|
|
751
|
-
let hash = 0;
|
|
752
|
-
for (let i = 0; i < pk.length; i++) {
|
|
753
|
-
hash = pk.charCodeAt(i) + ((hash << 5) - hash);
|
|
754
|
-
}
|
|
755
|
-
return colors[Math.abs(hash) % colors.length];
|
|
872
|
+
return this.computeRecordColor(this.computePkString(record));
|
|
756
873
|
}
|
|
757
874
|
isEnumField(fieldName) {
|
|
758
875
|
if (!this.entity)
|
|
@@ -811,7 +928,7 @@ export class EntityCardsComponent extends BaseAngularComponent {
|
|
|
811
928
|
}
|
|
812
929
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(EntityCardsComponent, [{
|
|
813
930
|
type: Component,
|
|
814
|
-
args: [{ standalone: false, selector: 'mj-entity-cards', encapsulation: ViewEncapsulation.None, template: "<div class=\"cards-view-wrapper\">\n @if (isLoading && effectiveRecords.length === 0) {\n <div class=\"loading-container\">\n <mj-loading text=\"Loading records...\"></mj-loading>\n </div>\n } @else {\n <div class=\"cards-container\">\n @for (record of effectiveRecords; track getRecordTrackId(record, $index)) {\n <div\n class=\"data-card\"\n [class.selected]=\"isSelected(record)\"\n (click)=\"onCardClick(record)\">\n\n <!-- Card Header with Avatar/Icon -->\n <div class=\"card-header\">\n @switch (getThumbnailType(record)) {\n @case ('image') {\n <div class=\"card-thumbnail\">\n <img [src]=\"getThumbnailUrl(record)\" alt=\"\" />\n </div>\n }\n @case ('icon') {\n <div class=\"card-avatar\" [style.background-color]=\"getRecordColor(record)\">\n <i [class]=\"getThumbnailUrl(record)\"></i>\n </div>\n }\n @default {\n <div class=\"card-avatar\" [style.background-color]=\"getRecordColor(record)\">\n {{ getInitials(record) }}\n </div>\n }\n }\n\n <div class=\"card-header-content\">\n <h3 class=\"card-title\" [innerHTML]=\"highlightMatch(getCombinedTitle(record))\"></h3>\n @if (effectiveTemplate?.subtitleField; as subField) {\n @if (subtitleIsPill) {\n <mj-pill [value]=\"getFieldValue(record, subField)\"></mj-pill>\n } @else {\n <span class=\"card-subtitle\" [innerHTML]=\"highlightMatch(getFieldValue(record, subField))\"></span>\n }\n }\n </div>\n\n <button class=\"card-open-btn\" (click)=\"onOpenClick($event, record)\" title=\"Open Record\">\n <i class=\"fa-solid fa-external-link-alt\"></i>\n </button>\n </div>\n\n <!-- Card Body -->\n <div class=\"card-body\">\n @if (effectiveTemplate?.descriptionField; as descField) {\n <p class=\"card-description\" [innerHTML]=\"highlightMatch(getTextValue(record, descField, 100))\"></p>\n }\n\n <!-- Display Fields -->\n @if (effectiveTemplate && effectiveTemplate.displayFields.length > 0) {\n <div class=\"card-fields\">\n @for (field of effectiveTemplate.displayFields; track field.name) {\n <div class=\"field-item\" [class.field-boolean]=\"field.type === 'boolean'\" [class.field-text]=\"field.type === 'text'\">\n @switch (field.type) {\n @case ('boolean') {\n <i class=\"field-icon\" [class.fa-solid]=\"true\"\n [class.fa-check]=\"getBooleanValue(record, field.name)\"\n [class.fa-minus]=\"!getBooleanValue(record, field.name)\"\n [class.bool-true]=\"getBooleanValue(record, field.name)\"\n [class.bool-false]=\"!getBooleanValue(record, field.name)\"></i>\n <span class=\"field-label\">{{ field.label }}</span>\n }\n @case ('number') {\n <span class=\"field-value\">{{ getNumericValue(record, field.name) }}</span>\n <span class=\"field-label\">{{ field.label }}</span>\n }\n @case ('date') {\n <span class=\"field-value field-date\">{{ getDateValue(record, field.name) }}</span>\n <span class=\"field-label\">{{ field.label }}</span>\n }\n @default {\n <span class=\"field-label\">{{ field.label }}</span>\n <span class=\"field-text-value\" [innerHTML]=\"highlightMatch(getTextValue(record, field.name, 40))\"></span>\n }\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Card Footer with Badge -->\n @if (effectiveTemplate?.badgeField; as badgeField) {\n <div class=\"card-footer\">\n <mj-pill [value]=\"getFieldValue(record, badgeField)\"></mj-pill>\n </div>\n }\n\n <!-- Hidden field match indicator -->\n @if (hasHiddenFieldMatch(record)) {\n <div class=\"hidden-match-indicator\" [title]=\"'Matched in: ' + getHiddenMatchFieldName(record)\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n <span>{{ getHiddenMatchFieldName(record) }}</span>\n </div>\n }\n </div>\n }\n\n @if (effectiveRecords.length === 0) {\n <div class=\"no-results\">\n <i class=\"fa-solid fa-inbox\"></i>\n <p>No records to display</p>\n </div>\n }\n </div>\n }\n</div>\n", styles: ["/* Scoped to mj-entity-cards to prevent style leakage with ViewEncapsulation.None */\nmj-entity-cards .cards-view-wrapper {\n height: 100%;\n overflow-y: auto;\n overflow-x: hidden;\n}\n\nmj-entity-cards .cards-container {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));\n gap: 20px;\n padding: 4px;\n}\n\nmj-entity-cards .data-card {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n border: 1px solid var(--mj-border-default);\n overflow: hidden;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n/* Hover state for unselected cards */\nmj-entity-cards .data-card:hover:not(.selected) {\n border-color: var(--mj-border-strong);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n transform: translateY(-2px);\n}\nmj-entity-cards .data-card:hover .card-open-btn {\n opacity: 1;\n}\n\n/* Selected state - subtle but clear */\nmj-entity-cards .data-card.selected {\n border-color: var(--mj-brand-primary);\n}\n\nmj-entity-cards .data-card.selected .card-title {\n color: var(--mj-brand-primary-hover);\n}\n\n/* Selected + hover state - same as regular hover */\nmj-entity-cards .data-card.selected:hover {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n transform: translateY(-2px);\n}\n\nmj-entity-cards .card-header {\n display: flex;\n align-items: flex-start;\n padding: 16px;\n gap: 12px;\n}\n\nmj-entity-cards .card-thumbnail {\n width: 48px;\n height: 48px;\n border-radius: 8px;\n overflow: hidden;\n flex-shrink: 0;\n}\nmj-entity-cards .card-thumbnail img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\nmj-entity-cards .card-avatar {\n width: 48px;\n height: 48px;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-inverse);\n flex-shrink: 0;\n}\n\nmj-entity-cards .card-header-content {\n flex: 1;\n min-width: 0;\n}\n\nmj-entity-cards .card-title {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n line-height: 1.3;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\nmj-entity-cards .card-subtitle {\n display: block;\n margin-top: 2px;\n font-size: 12px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\nmj-entity-cards .card-open-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n opacity: 0;\n transition: all 0.15s ease;\n flex-shrink: 0;\n}\nmj-entity-cards .card-open-btn:hover {\n background: var(--mj-bg-surface-card);\n color: var(--mj-brand-primary);\n}\n\nmj-entity-cards .card-body {\n padding: 0 16px 16px 16px;\n}\n\nmj-entity-cards .card-description {\n margin: 0 0 12px 0;\n font-size: 13px;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n}\n\n/* Card Display Fields */\nmj-entity-cards .card-fields {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n padding-top: 12px;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\nmj-entity-cards .field-item {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 60px;\n}\n\nmj-entity-cards .field-item .field-value {\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\nmj-entity-cards .field-item .field-label {\n font-size: 10px;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n/* Boolean fields */\nmj-entity-cards .field-item.field-boolean {\n flex-direction: row;\n align-items: center;\n gap: 6px;\n min-width: auto;\n}\n\nmj-entity-cards .field-item.field-boolean .field-icon {\n font-size: 14px;\n}\n\nmj-entity-cards .field-item.field-boolean .field-icon.bool-true {\n color: var(--mj-status-success);\n}\n\nmj-entity-cards .field-item.field-boolean .field-icon.bool-false {\n color: var(--mj-text-muted);\n}\n\nmj-entity-cards .field-item.field-boolean .field-label {\n font-size: 12px;\n text-transform: none;\n color: var(--mj-text-secondary);\n}\n\n/* Text fields */\nmj-entity-cards .field-item.field-text {\n flex: 1 1 100%;\n max-width: 100%;\n}\n\nmj-entity-cards .field-item .field-text-value {\n font-size: 13px;\n color: var(--mj-text-secondary);\n line-height: 1.4;\n word-break: break-word;\n}\n\n/* Date fields */\nmj-entity-cards .field-item .field-date {\n font-size: 13px;\n font-weight: 500;\n}\n\nmj-entity-cards .card-footer {\n padding: 12px 16px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n/* Highlight matches - scoped to work with innerHTML */\nmj-entity-cards .highlight-match {\n background-color: color-mix(in srgb, var(--mj-status-warning) 40%, var(--mj-bg-surface));\n border-radius: 2px;\n}\n\n/* Pill spacing when used as subtitle */\nmj-entity-cards .card-header-content mj-pill {\n display: block;\n margin-top: 4px;\n}\n\n/* Hidden field match indicator */\nmj-entity-cards .hidden-match-indicator {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n background: var(--mj-status-warning-bg);\n border-top: 1px solid var(--mj-status-warning-border);\n font-size: 11px;\n color: var(--mj-status-warning);\n}\n\nmj-entity-cards .hidden-match-indicator i {\n font-size: 10px;\n}\n\nmj-entity-cards .hidden-match-indicator span {\n font-weight: 500;\n}\n\n/* Loading state */\nmj-entity-cards .loading-container {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 200px;\n padding: 40px;\n}\n\n/* No results state */\nmj-entity-cards .no-results {\n grid-column: 1 / -1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 20px;\n color: var(--mj-text-muted);\n text-align: center;\n}\nmj-entity-cards .no-results i {\n font-size: 48px;\n margin-bottom: 16px;\n opacity: 0.5;\n}\nmj-entity-cards .no-results p {\n margin: 0;\n font-size: 14px;\n}\n"] }]
|
|
931
|
+
args: [{ standalone: false, selector: 'mj-entity-cards', encapsulation: ViewEncapsulation.None, template: "<div class=\"cards-view-wrapper\">\n @if (isLoading && effectiveRecords.length === 0) {\n <div class=\"loading-container\">\n <mj-loading text=\"Loading records...\"></mj-loading>\n </div>\n } @else {\n <div class=\"cards-container\">\n @for (vm of cardViewModels; track vm.trackId) {\n <div\n class=\"data-card\"\n [class.selected]=\"vm.isSelected\"\n (click)=\"onCardClick(vm.record)\">\n\n <!-- Card Header with Avatar/Icon -->\n <div class=\"card-header\">\n @switch (vm.thumbnailType) {\n @case ('image') {\n <div class=\"card-thumbnail\">\n <img [src]=\"vm.thumbnailUrl\" alt=\"\" />\n </div>\n }\n @case ('icon') {\n <div class=\"card-avatar\" [style.background-color]=\"vm.color\">\n <i [class]=\"vm.thumbnailUrl\"></i>\n </div>\n }\n @default {\n <div class=\"card-avatar\" [style.background-color]=\"vm.color\">\n {{ vm.initials }}\n </div>\n }\n }\n\n <div class=\"card-header-content\">\n <h3 class=\"card-title\" [innerHTML]=\"vm.highlightedTitle\"></h3>\n @if (effectiveTemplate?.subtitleField) {\n @if (subtitleIsPill) {\n <mj-pill [value]=\"vm.subtitleValue\"></mj-pill>\n } @else {\n <span class=\"card-subtitle\" [innerHTML]=\"vm.highlightedSubtitle\"></span>\n }\n }\n </div>\n\n <button class=\"card-open-btn\" (click)=\"onOpenClick($event, vm.record)\" title=\"Open Record\">\n <i class=\"fa-solid fa-external-link-alt\"></i>\n </button>\n </div>\n\n <!-- Card Body -->\n <div class=\"card-body\">\n @if (effectiveTemplate?.descriptionField) {\n <p class=\"card-description\" [innerHTML]=\"vm.highlightedDescription\"></p>\n }\n\n <!-- Display Fields -->\n @if (vm.displayFields.length > 0) {\n <div class=\"card-fields\">\n @for (df of vm.displayFields; track df.field.name) {\n <div class=\"field-item\" [class.field-boolean]=\"df.field.type === 'boolean'\" [class.field-text]=\"df.field.type === 'text'\">\n @switch (df.field.type) {\n @case ('boolean') {\n <i class=\"field-icon\" [class.fa-solid]=\"true\"\n [class.fa-check]=\"df.booleanValue\"\n [class.fa-minus]=\"!df.booleanValue\"\n [class.bool-true]=\"df.booleanValue\"\n [class.bool-false]=\"!df.booleanValue\"></i>\n <span class=\"field-label\">{{ df.field.label }}</span>\n }\n @case ('number') {\n <span class=\"field-value\">{{ df.numericValue }}</span>\n <span class=\"field-label\">{{ df.field.label }}</span>\n }\n @case ('date') {\n <span class=\"field-value field-date\">{{ df.dateValue }}</span>\n <span class=\"field-label\">{{ df.field.label }}</span>\n }\n @default {\n <span class=\"field-label\">{{ df.field.label }}</span>\n <span class=\"field-text-value\" [innerHTML]=\"df.highlightedText\"></span>\n }\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Card Footer with Badge -->\n @if (effectiveTemplate?.badgeField) {\n <div class=\"card-footer\">\n <mj-pill [value]=\"vm.badgeValue\"></mj-pill>\n </div>\n }\n\n <!-- Hidden field match indicator -->\n @if (vm.hasHiddenFieldMatch) {\n <div class=\"hidden-match-indicator\" [title]=\"'Matched in: ' + vm.hiddenMatchFieldName\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n <span>{{ vm.hiddenMatchFieldName }}</span>\n </div>\n }\n </div>\n }\n\n @if (effectiveRecords.length === 0) {\n <div class=\"no-results\">\n <i class=\"fa-solid fa-inbox\"></i>\n <p>No records to display</p>\n </div>\n }\n </div>\n }\n</div>\n", styles: ["/* Scoped to mj-entity-cards to prevent style leakage with ViewEncapsulation.None */\nmj-entity-cards .cards-view-wrapper {\n height: 100%;\n overflow-y: auto;\n overflow-x: hidden;\n}\n\nmj-entity-cards .cards-container {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));\n gap: 20px;\n padding: 4px;\n}\n\nmj-entity-cards .data-card {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n border: 1px solid var(--mj-border-default);\n overflow: hidden;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n/* Hover state for unselected cards */\nmj-entity-cards .data-card:hover:not(.selected) {\n border-color: var(--mj-border-strong);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n transform: translateY(-2px);\n}\nmj-entity-cards .data-card:hover .card-open-btn {\n opacity: 1;\n}\n\n/* Selected state - subtle but clear */\nmj-entity-cards .data-card.selected {\n border-color: var(--mj-brand-primary);\n}\n\nmj-entity-cards .data-card.selected .card-title {\n color: var(--mj-brand-primary-hover);\n}\n\n/* Selected + hover state - same as regular hover */\nmj-entity-cards .data-card.selected:hover {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n transform: translateY(-2px);\n}\n\nmj-entity-cards .card-header {\n display: flex;\n align-items: flex-start;\n padding: 16px;\n gap: 12px;\n}\n\nmj-entity-cards .card-thumbnail {\n width: 48px;\n height: 48px;\n border-radius: 8px;\n overflow: hidden;\n flex-shrink: 0;\n}\nmj-entity-cards .card-thumbnail img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\nmj-entity-cards .card-avatar {\n width: 48px;\n height: 48px;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-inverse);\n flex-shrink: 0;\n}\n\nmj-entity-cards .card-header-content {\n flex: 1;\n min-width: 0;\n}\n\nmj-entity-cards .card-title {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n line-height: 1.3;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\nmj-entity-cards .card-subtitle {\n display: block;\n margin-top: 2px;\n font-size: 12px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\nmj-entity-cards .card-open-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n opacity: 0;\n transition: all 0.15s ease;\n flex-shrink: 0;\n}\nmj-entity-cards .card-open-btn:hover {\n background: var(--mj-bg-surface-card);\n color: var(--mj-brand-primary);\n}\n\nmj-entity-cards .card-body {\n padding: 0 16px 16px 16px;\n}\n\nmj-entity-cards .card-description {\n margin: 0 0 12px 0;\n font-size: 13px;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n}\n\n/* Card Display Fields */\nmj-entity-cards .card-fields {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n padding-top: 12px;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\nmj-entity-cards .field-item {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 60px;\n}\n\nmj-entity-cards .field-item .field-value {\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\nmj-entity-cards .field-item .field-label {\n font-size: 10px;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n/* Boolean fields */\nmj-entity-cards .field-item.field-boolean {\n flex-direction: row;\n align-items: center;\n gap: 6px;\n min-width: auto;\n}\n\nmj-entity-cards .field-item.field-boolean .field-icon {\n font-size: 14px;\n}\n\nmj-entity-cards .field-item.field-boolean .field-icon.bool-true {\n color: var(--mj-status-success);\n}\n\nmj-entity-cards .field-item.field-boolean .field-icon.bool-false {\n color: var(--mj-text-muted);\n}\n\nmj-entity-cards .field-item.field-boolean .field-label {\n font-size: 12px;\n text-transform: none;\n color: var(--mj-text-secondary);\n}\n\n/* Text fields */\nmj-entity-cards .field-item.field-text {\n flex: 1 1 100%;\n max-width: 100%;\n}\n\nmj-entity-cards .field-item .field-text-value {\n font-size: 13px;\n color: var(--mj-text-secondary);\n line-height: 1.4;\n word-break: break-word;\n}\n\n/* Date fields */\nmj-entity-cards .field-item .field-date {\n font-size: 13px;\n font-weight: 500;\n}\n\nmj-entity-cards .card-footer {\n padding: 12px 16px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n/* Highlight matches - scoped to work with innerHTML */\nmj-entity-cards .highlight-match {\n background-color: color-mix(in srgb, var(--mj-status-warning) 40%, var(--mj-bg-surface));\n border-radius: 2px;\n}\n\n/* Pill spacing when used as subtitle */\nmj-entity-cards .card-header-content mj-pill {\n display: block;\n margin-top: 4px;\n}\n\n/* Hidden field match indicator */\nmj-entity-cards .hidden-match-indicator {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n background: var(--mj-status-warning-bg);\n border-top: 1px solid var(--mj-status-warning-border);\n font-size: 11px;\n color: var(--mj-status-warning);\n}\n\nmj-entity-cards .hidden-match-indicator i {\n font-size: 10px;\n}\n\nmj-entity-cards .hidden-match-indicator span {\n font-weight: 500;\n}\n\n/* Loading state */\nmj-entity-cards .loading-container {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 200px;\n padding: 40px;\n}\n\n/* No results state */\nmj-entity-cards .no-results {\n grid-column: 1 / -1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 20px;\n color: var(--mj-text-muted);\n text-align: center;\n}\nmj-entity-cards .no-results i {\n font-size: 48px;\n margin-bottom: 16px;\n opacity: 0.5;\n}\nmj-entity-cards .no-results p {\n margin: 0;\n font-size: 14px;\n}\n"] }]
|
|
815
932
|
}], () => [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }], { entity: [{
|
|
816
933
|
type: Input
|
|
817
934
|
}], records: [{
|
|
@@ -831,5 +948,5 @@ export class EntityCardsComponent extends BaseAngularComponent {
|
|
|
831
948
|
}], recordOpened: [{
|
|
832
949
|
type: Output
|
|
833
950
|
}] }); })();
|
|
834
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(EntityCardsComponent, { className: "EntityCardsComponent", filePath: "src/lib/entity-cards/entity-cards.component.ts", lineNumber:
|
|
951
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(EntityCardsComponent, { className: "EntityCardsComponent", filePath: "src/lib/entity-cards/entity-cards.component.ts", lineNumber: 95 }); })();
|
|
835
952
|
//# sourceMappingURL=entity-cards.component.js.map
|