@memberjunction/ng-entity-viewer 5.49.0 → 5.50.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/lib/entity-data-grid/entity-data-grid.component.d.ts +23 -6
- package/dist/lib/entity-data-grid/entity-data-grid.component.d.ts.map +1 -1
- package/dist/lib/entity-data-grid/entity-data-grid.component.js +142 -55
- package/dist/lib/entity-data-grid/entity-data-grid.component.js.map +1 -1
- package/dist/lib/entity-viewer/entity-viewer.component.d.ts +29 -0
- package/dist/lib/entity-viewer/entity-viewer.component.d.ts.map +1 -1
- package/dist/lib/entity-viewer/entity-viewer.component.js +90 -1
- package/dist/lib/entity-viewer/entity-viewer.component.js.map +1 -1
- package/dist/lib/view-selector/view-selector.component.js +2 -2
- package/dist/lib/view-selector/view-selector.component.js.map +1 -1
- package/dist/lib/view-types/descriptors/grid-view-type.d.ts +6 -0
- package/dist/lib/view-types/descriptors/grid-view-type.d.ts.map +1 -1
- package/dist/lib/view-types/descriptors/grid-view-type.js +6 -0
- package/dist/lib/view-types/descriptors/grid-view-type.js.map +1 -1
- package/dist/lib/view-types/renderers/grid-view-renderer.component.d.ts +6 -1
- package/dist/lib/view-types/renderers/grid-view-renderer.component.d.ts.map +1 -1
- package/dist/lib/view-types/renderers/grid-view-renderer.component.js +11 -3
- package/dist/lib/view-types/renderers/grid-view-renderer.component.js.map +1 -1
- package/dist/lib/view-types/view-type.contracts.d.ts +15 -0
- package/dist/lib/view-types/view-type.contracts.d.ts.map +1 -1
- package/dist/lib/view-types/view-type.contracts.js +6 -0
- package/dist/lib/view-types/view-type.contracts.js.map +1 -1
- package/dist/lib/view-workspace/view-workspace.component.d.ts +19 -0
- package/dist/lib/view-workspace/view-workspace.component.d.ts.map +1 -1
- package/dist/lib/view-workspace/view-workspace.component.js +100 -24
- package/dist/lib/view-workspace/view-workspace.component.js.map +1 -1
- package/package.json +19 -18
|
@@ -139,6 +139,11 @@ function EntityDataGridComponent_Conditional_2_Conditional_12_Template(rf, ctx)
|
|
|
139
139
|
i0.ɵɵelementStart(2, "span", 45);
|
|
140
140
|
i0.ɵɵtext(3, "Export");
|
|
141
141
|
i0.ɵɵelementEnd()();
|
|
142
|
+
} if (rf & 2) {
|
|
143
|
+
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
144
|
+
i0.ɵɵproperty("disabled", ctx_r2.IsPreparingExport);
|
|
145
|
+
i0.ɵɵadvance();
|
|
146
|
+
i0.ɵɵclassProp("fa-file-excel", !ctx_r2.IsPreparingExport)("fa-spinner", ctx_r2.IsPreparingExport)("fa-spin", ctx_r2.IsPreparingExport);
|
|
142
147
|
} }
|
|
143
148
|
function EntityDataGridComponent_Conditional_2_Conditional_13_Template(rf, ctx) { if (rf & 1) {
|
|
144
149
|
const _r10 = i0.ɵɵgetCurrentView();
|
|
@@ -177,19 +182,21 @@ function EntityDataGridComponent_Conditional_2_Conditional_16_Template(rf, ctx)
|
|
|
177
182
|
const _r13 = i0.ɵɵgetCurrentView();
|
|
178
183
|
i0.ɵɵelementStart(0, "button", 56);
|
|
179
184
|
i0.ɵɵlistener("click", function EntityDataGridComponent_Conditional_2_Conditional_16_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r13); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onAddToListClick()); });
|
|
180
|
-
i0.ɵɵelement(1, "i",
|
|
185
|
+
i0.ɵɵelement(1, "i", 49);
|
|
181
186
|
i0.ɵɵelementStart(2, "span", 45);
|
|
182
187
|
i0.ɵɵtext(3, "Add to List");
|
|
183
188
|
i0.ɵɵelementEnd()();
|
|
184
189
|
} if (rf & 2) {
|
|
185
190
|
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
186
|
-
i0.ɵɵproperty("disabled", !ctx_r2.HasSelection);
|
|
191
|
+
i0.ɵɵproperty("title", ctx_r2.HasSelection ? "Add selected records to a list" : "Add all matching records to a list")("disabled", !ctx_r2.HasSelection && !ctx_r2.ExportDataProvider || ctx_r2.IsPreparingAddToList);
|
|
192
|
+
i0.ɵɵadvance();
|
|
193
|
+
i0.ɵɵclassProp("fa-list-check", !ctx_r2.IsPreparingAddToList)("fa-spinner", ctx_r2.IsPreparingAddToList)("fa-spin", ctx_r2.IsPreparingAddToList);
|
|
187
194
|
} }
|
|
188
195
|
function EntityDataGridComponent_Conditional_2_Conditional_17_Template(rf, ctx) { if (rf & 1) {
|
|
189
196
|
const _r14 = i0.ɵɵgetCurrentView();
|
|
190
|
-
i0.ɵɵelementStart(0, "button",
|
|
197
|
+
i0.ɵɵelementStart(0, "button", 57);
|
|
191
198
|
i0.ɵɵlistener("click", function EntityDataGridComponent_Conditional_2_Conditional_17_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r14); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onDuplicateSearchClick()); });
|
|
192
|
-
i0.ɵɵelement(1, "i",
|
|
199
|
+
i0.ɵɵelement(1, "i", 58);
|
|
193
200
|
i0.ɵɵelementStart(2, "span", 45);
|
|
194
201
|
i0.ɵɵtext(3, "Find Duplicates");
|
|
195
202
|
i0.ɵɵelementEnd()();
|
|
@@ -199,9 +206,9 @@ function EntityDataGridComponent_Conditional_2_Conditional_17_Template(rf, ctx)
|
|
|
199
206
|
} }
|
|
200
207
|
function EntityDataGridComponent_Conditional_2_Conditional_18_Template(rf, ctx) { if (rf & 1) {
|
|
201
208
|
const _r15 = i0.ɵɵgetCurrentView();
|
|
202
|
-
i0.ɵɵelementStart(0, "button",
|
|
209
|
+
i0.ɵɵelementStart(0, "button", 59);
|
|
203
210
|
i0.ɵɵlistener("click", function EntityDataGridComponent_Conditional_2_Conditional_18_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r15); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onCommunicationClick()); });
|
|
204
|
-
i0.ɵɵelement(1, "i",
|
|
211
|
+
i0.ɵɵelement(1, "i", 60);
|
|
205
212
|
i0.ɵɵelementStart(2, "span", 45);
|
|
206
213
|
i0.ɵɵtext(3, "Send Message");
|
|
207
214
|
i0.ɵɵelementEnd()();
|
|
@@ -211,16 +218,16 @@ function EntityDataGridComponent_Conditional_2_Conditional_18_Template(rf, ctx)
|
|
|
211
218
|
} }
|
|
212
219
|
function EntityDataGridComponent_Conditional_2_Conditional_19_Template(rf, ctx) { if (rf & 1) {
|
|
213
220
|
const _r16 = i0.ɵɵgetCurrentView();
|
|
214
|
-
i0.ɵɵelementStart(0, "button",
|
|
221
|
+
i0.ɵɵelementStart(0, "button", 61);
|
|
215
222
|
i0.ɵɵlistener("click", function EntityDataGridComponent_Conditional_2_Conditional_19_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r16); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onAddClick()); });
|
|
216
223
|
i0.ɵɵelement(1, "i", 44);
|
|
217
224
|
i0.ɵɵelementEnd();
|
|
218
225
|
} }
|
|
219
226
|
function EntityDataGridComponent_Conditional_2_Conditional_20_Template(rf, ctx) { if (rf & 1) {
|
|
220
227
|
const _r17 = i0.ɵɵgetCurrentView();
|
|
221
|
-
i0.ɵɵelementStart(0, "button",
|
|
228
|
+
i0.ɵɵelementStart(0, "button", 62);
|
|
222
229
|
i0.ɵɵlistener("click", function EntityDataGridComponent_Conditional_2_Conditional_20_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r17); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onRefreshClick()); });
|
|
223
|
-
i0.ɵɵelement(1, "i",
|
|
230
|
+
i0.ɵɵelement(1, "i", 63);
|
|
224
231
|
i0.ɵɵelementEnd();
|
|
225
232
|
} if (rf & 2) {
|
|
226
233
|
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
@@ -230,23 +237,23 @@ function EntityDataGridComponent_Conditional_2_Conditional_20_Template(rf, ctx)
|
|
|
230
237
|
} }
|
|
231
238
|
function EntityDataGridComponent_Conditional_2_Conditional_21_Template(rf, ctx) { if (rf & 1) {
|
|
232
239
|
const _r18 = i0.ɵɵgetCurrentView();
|
|
233
|
-
i0.ɵɵelementStart(0, "button",
|
|
240
|
+
i0.ɵɵelementStart(0, "button", 64);
|
|
234
241
|
i0.ɵɵlistener("click", function EntityDataGridComponent_Conditional_2_Conditional_21_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r18); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onDeleteClick()); });
|
|
235
242
|
i0.ɵɵelement(1, "i", 51);
|
|
236
243
|
i0.ɵɵelementEnd();
|
|
237
244
|
} }
|
|
238
245
|
function EntityDataGridComponent_Conditional_2_Conditional_22_Template(rf, ctx) { if (rf & 1) {
|
|
239
246
|
const _r19 = i0.ɵɵgetCurrentView();
|
|
240
|
-
i0.ɵɵelementStart(0, "button",
|
|
247
|
+
i0.ɵɵelementStart(0, "button", 65);
|
|
241
248
|
i0.ɵɵlistener("click", function EntityDataGridComponent_Conditional_2_Conditional_22_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r19); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onExportClick()); });
|
|
242
|
-
i0.ɵɵelement(1, "i",
|
|
249
|
+
i0.ɵɵelement(1, "i", 66);
|
|
243
250
|
i0.ɵɵelementEnd();
|
|
244
251
|
} }
|
|
245
252
|
function EntityDataGridComponent_Conditional_2_Conditional_23_Template(rf, ctx) { if (rf & 1) {
|
|
246
253
|
const _r20 = i0.ɵɵgetCurrentView();
|
|
247
|
-
i0.ɵɵelementStart(0, "button",
|
|
254
|
+
i0.ɵɵelementStart(0, "button", 67);
|
|
248
255
|
i0.ɵɵlistener("click", function EntityDataGridComponent_Conditional_2_Conditional_23_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r20); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onColumnChooserClick()); });
|
|
249
|
-
i0.ɵɵelement(1, "i",
|
|
256
|
+
i0.ɵɵelement(1, "i", 68);
|
|
250
257
|
i0.ɵɵelementEnd();
|
|
251
258
|
} }
|
|
252
259
|
function EntityDataGridComponent_Conditional_2_For_25_Conditional_0_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
@@ -290,11 +297,11 @@ function EntityDataGridComponent_Conditional_2_For_25_Template(rf, ctx) { if (rf
|
|
|
290
297
|
} }
|
|
291
298
|
function EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
292
299
|
const _r24 = i0.ɵɵgetCurrentView();
|
|
293
|
-
i0.ɵɵelementStart(0, "button",
|
|
300
|
+
i0.ɵɵelementStart(0, "button", 75);
|
|
294
301
|
i0.ɵɵlistener("click", function EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Conditional_1_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r24); const ctx_r2 = i0.ɵɵnextContext(4); ctx_r2.onExportClick(); return i0.ɵɵresetView(ctx_r2.closeOverflowMenu()); });
|
|
295
|
-
i0.ɵɵelement(1, "i",
|
|
302
|
+
i0.ɵɵelement(1, "i", 76);
|
|
296
303
|
i0.ɵɵelementStart(2, "span");
|
|
297
|
-
i0.ɵɵtext(3, "Export
|
|
304
|
+
i0.ɵɵtext(3, "Export");
|
|
298
305
|
i0.ɵɵelementEnd()();
|
|
299
306
|
} }
|
|
300
307
|
function EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Conditional_2_For_4_Template(rf, ctx) { if (rf & 1) {
|
|
@@ -315,7 +322,7 @@ function EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Cond
|
|
|
315
322
|
i0.ɵɵtextInterpolate(action_r26.name);
|
|
316
323
|
} }
|
|
317
324
|
function EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
318
|
-
i0.ɵɵelement(0, "div",
|
|
325
|
+
i0.ɵɵelement(0, "div", 74);
|
|
319
326
|
i0.ɵɵelementStart(1, "div", 77);
|
|
320
327
|
i0.ɵɵtext(2, "Actions");
|
|
321
328
|
i0.ɵɵelementEnd();
|
|
@@ -326,40 +333,40 @@ function EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Cond
|
|
|
326
333
|
i0.ɵɵrepeater(ctx_r2.EntityActions);
|
|
327
334
|
} }
|
|
328
335
|
function EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
329
|
-
i0.ɵɵelement(0, "div",
|
|
336
|
+
i0.ɵɵelement(0, "div", 74);
|
|
330
337
|
} }
|
|
331
338
|
function EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
332
339
|
const _r27 = i0.ɵɵgetCurrentView();
|
|
333
|
-
i0.ɵɵelementStart(0, "button",
|
|
340
|
+
i0.ɵɵelementStart(0, "button", 75);
|
|
334
341
|
i0.ɵɵlistener("click", function EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Conditional_4_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r27); const ctx_r2 = i0.ɵɵnextContext(4); ctx_r2.onColumnChooserClick(); return i0.ɵɵresetView(ctx_r2.closeOverflowMenu()); });
|
|
335
|
-
i0.ɵɵelement(1, "i",
|
|
342
|
+
i0.ɵɵelement(1, "i", 68);
|
|
336
343
|
i0.ɵɵelementStart(2, "span");
|
|
337
344
|
i0.ɵɵtext(3, "Manage Columns");
|
|
338
345
|
i0.ɵɵelementEnd()();
|
|
339
346
|
} }
|
|
340
347
|
function EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Conditional_5_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
341
348
|
const _r28 = i0.ɵɵgetCurrentView();
|
|
342
|
-
i0.ɵɵelementStart(0, "button",
|
|
349
|
+
i0.ɵɵelementStart(0, "button", 75);
|
|
343
350
|
i0.ɵɵlistener("click", function EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Conditional_5_Conditional_1_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r28); const ctx_r2 = i0.ɵɵnextContext(5); ctx_r2.onCommunicationClick(); return i0.ɵɵresetView(ctx_r2.closeOverflowMenu()); });
|
|
344
|
-
i0.ɵɵelement(1, "i",
|
|
351
|
+
i0.ɵɵelement(1, "i", 60);
|
|
345
352
|
i0.ɵɵelementStart(2, "span");
|
|
346
353
|
i0.ɵɵtext(3, "Send Message");
|
|
347
354
|
i0.ɵɵelementEnd()();
|
|
348
355
|
} }
|
|
349
356
|
function EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
350
|
-
i0.ɵɵelement(0, "div",
|
|
351
|
-
i0.ɵɵconditionalCreate(1, EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Conditional_5_Conditional_1_Template, 4, 0, "button",
|
|
357
|
+
i0.ɵɵelement(0, "div", 74);
|
|
358
|
+
i0.ɵɵconditionalCreate(1, EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Conditional_5_Conditional_1_Template, 4, 0, "button", 73);
|
|
352
359
|
} if (rf & 2) {
|
|
353
360
|
const ctx_r2 = i0.ɵɵnextContext(4);
|
|
354
361
|
i0.ɵɵadvance();
|
|
355
362
|
i0.ɵɵconditional(ctx_r2.showCommunicationInOverflow ? 1 : -1);
|
|
356
363
|
} }
|
|
357
364
|
function EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
358
|
-
i0.ɵɵelementStart(0, "div",
|
|
359
|
-
i0.ɵɵconditionalCreate(1, EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Conditional_1_Template, 4, 0, "button",
|
|
365
|
+
i0.ɵɵelementStart(0, "div", 72);
|
|
366
|
+
i0.ɵɵconditionalCreate(1, EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Conditional_1_Template, 4, 0, "button", 73);
|
|
360
367
|
i0.ɵɵconditionalCreate(2, EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Conditional_2_Template, 5, 0);
|
|
361
|
-
i0.ɵɵconditionalCreate(3, EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Conditional_3_Template, 1, 0, "div",
|
|
362
|
-
i0.ɵɵconditionalCreate(4, EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Conditional_4_Template, 4, 0, "button",
|
|
368
|
+
i0.ɵɵconditionalCreate(3, EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Conditional_3_Template, 1, 0, "div", 74);
|
|
369
|
+
i0.ɵɵconditionalCreate(4, EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Conditional_4_Template, 4, 0, "button", 73);
|
|
363
370
|
i0.ɵɵconditionalCreate(5, EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Conditional_5_Template, 2, 1);
|
|
364
371
|
i0.ɵɵelementEnd();
|
|
365
372
|
} if (rf & 2) {
|
|
@@ -378,13 +385,13 @@ function EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Temp
|
|
|
378
385
|
} }
|
|
379
386
|
function EntityDataGridComponent_Conditional_2_Conditional_26_Template(rf, ctx) { if (rf & 1) {
|
|
380
387
|
const _r23 = i0.ɵɵgetCurrentView();
|
|
381
|
-
i0.ɵɵelementStart(0, "div",
|
|
388
|
+
i0.ɵɵelementStart(0, "div", 69);
|
|
382
389
|
i0.ɵɵlistener("click", function EntityDataGridComponent_Conditional_2_Conditional_26_Template_div_click_0_listener($event) { i0.ɵɵrestoreView(_r23); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
383
|
-
i0.ɵɵelementStart(1, "button",
|
|
390
|
+
i0.ɵɵelementStart(1, "button", 70);
|
|
384
391
|
i0.ɵɵlistener("click", function EntityDataGridComponent_Conditional_2_Conditional_26_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r23); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.toggleOverflowMenu()); });
|
|
385
|
-
i0.ɵɵelement(2, "i",
|
|
392
|
+
i0.ɵɵelement(2, "i", 71);
|
|
386
393
|
i0.ɵɵelementEnd();
|
|
387
|
-
i0.ɵɵconditionalCreate(3, EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Template, 6, 6, "div",
|
|
394
|
+
i0.ɵɵconditionalCreate(3, EntityDataGridComponent_Conditional_2_Conditional_26_Conditional_3_Template, 6, 6, "div", 72);
|
|
388
395
|
i0.ɵɵelementEnd();
|
|
389
396
|
} if (rf & 2) {
|
|
390
397
|
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
@@ -404,11 +411,11 @@ function EntityDataGridComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
|
404
411
|
i0.ɵɵconditionalCreate(9, EntityDataGridComponent_Conditional_2_Conditional_9_Template, 1, 1, "mj-recycle-bin-chip", 19);
|
|
405
412
|
i0.ɵɵconditionalCreate(10, EntityDataGridComponent_Conditional_2_Conditional_10_Template, 4, 0, "button", 20);
|
|
406
413
|
i0.ɵɵconditionalCreate(11, EntityDataGridComponent_Conditional_2_Conditional_11_Template, 4, 3, "button", 21);
|
|
407
|
-
i0.ɵɵconditionalCreate(12, EntityDataGridComponent_Conditional_2_Conditional_12_Template, 4,
|
|
414
|
+
i0.ɵɵconditionalCreate(12, EntityDataGridComponent_Conditional_2_Conditional_12_Template, 4, 7, "button", 22);
|
|
408
415
|
i0.ɵɵconditionalCreate(13, EntityDataGridComponent_Conditional_2_Conditional_13_Template, 4, 0, "button", 23);
|
|
409
416
|
i0.ɵɵconditionalCreate(14, EntityDataGridComponent_Conditional_2_Conditional_14_Template, 4, 1, "button", 24);
|
|
410
417
|
i0.ɵɵconditionalCreate(15, EntityDataGridComponent_Conditional_2_Conditional_15_Template, 4, 1, "button", 25);
|
|
411
|
-
i0.ɵɵconditionalCreate(16, EntityDataGridComponent_Conditional_2_Conditional_16_Template, 4,
|
|
418
|
+
i0.ɵɵconditionalCreate(16, EntityDataGridComponent_Conditional_2_Conditional_16_Template, 4, 8, "button", 26);
|
|
412
419
|
i0.ɵɵconditionalCreate(17, EntityDataGridComponent_Conditional_2_Conditional_17_Template, 4, 1, "button", 27);
|
|
413
420
|
i0.ɵɵconditionalCreate(18, EntityDataGridComponent_Conditional_2_Conditional_18_Template, 4, 1, "button", 28);
|
|
414
421
|
i0.ɵɵconditionalCreate(19, EntityDataGridComponent_Conditional_2_Conditional_19_Template, 2, 0, "button", 29);
|
|
@@ -742,6 +749,18 @@ export class EntityDataGridComponent extends BaseAngularComponent {
|
|
|
742
749
|
* TotalRowCount from server responses. The pager auto-hides when there's only one page.
|
|
743
750
|
*/
|
|
744
751
|
ShowPager = false;
|
|
752
|
+
/**
|
|
753
|
+
* Optional provider that returns the FULL result set for export. Because the grid is server-side
|
|
754
|
+
* paged, `Data`/`rowData` only hold the current page (default 100 rows), so exporting them would
|
|
755
|
+
* silently cap the export at the page size (bug C1). The host (entity-viewer) supplies this callback
|
|
756
|
+
* to fetch every matching row (honoring the active filter/sort) on demand; the grid awaits it when
|
|
757
|
+
* the user opens the export dialog. When not provided, export falls back to the loaded page.
|
|
758
|
+
*/
|
|
759
|
+
ExportDataProvider = null;
|
|
760
|
+
/** True while an export is fetching the full result set via ExportDataProvider. */
|
|
761
|
+
IsPreparingExport = false;
|
|
762
|
+
/** True while "Add to List" (with no explicit selection) is fetching the full result set. */
|
|
763
|
+
IsPreparingAddToList = false;
|
|
745
764
|
/**
|
|
746
765
|
* Whether to render the Recycle Bin chip in the toolbar. The chip
|
|
747
766
|
* auto-hides itself when the entity has no deleted records, doesn't
|
|
@@ -1164,7 +1183,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
|
|
|
1164
1183
|
}
|
|
1165
1184
|
_showExportButton = true;
|
|
1166
1185
|
/**
|
|
1167
|
-
* Show the "Export
|
|
1186
|
+
* Show the "Export" button in toolbar (opens the export dialog — Excel/CSV/JSON)
|
|
1168
1187
|
*/
|
|
1169
1188
|
set ShowExportButton(value) {
|
|
1170
1189
|
this._showExportButton = value;
|
|
@@ -2468,8 +2487,6 @@ export class EntityDataGridComponent extends BaseAngularComponent {
|
|
|
2468
2487
|
const sortedColumns = [...columnSettings].sort((a, b) => (a.orderIndex ?? 0) - (b.orderIndex ?? 0));
|
|
2469
2488
|
const cols = [];
|
|
2470
2489
|
for (const colConfig of sortedColumns) {
|
|
2471
|
-
if (colConfig.hidden)
|
|
2472
|
-
continue;
|
|
2473
2490
|
const field = this._entityInfo.Fields.find(f => f.Name.toLowerCase() === colConfig.Name.toLowerCase());
|
|
2474
2491
|
if (!field)
|
|
2475
2492
|
continue;
|
|
@@ -2479,7 +2496,14 @@ export class EntityDataGridComponent extends BaseAngularComponent {
|
|
|
2479
2496
|
headerName: colConfig.userDisplayName || colConfig.DisplayName || field.DisplayNameOrName,
|
|
2480
2497
|
width: colConfig.width || this.estimateColumnWidth(field),
|
|
2481
2498
|
sortable: this._allowSorting,
|
|
2482
|
-
resizable: this._allowColumnResize
|
|
2499
|
+
resizable: this._allowColumnResize,
|
|
2500
|
+
// Hidden columns are still added as col defs with hide:true rather than omitted
|
|
2501
|
+
// entirely (bug C2). A column omitted from the col defs is invisible to AG Grid's
|
|
2502
|
+
// quick filter, so search couldn't match values in columns the user had hidden.
|
|
2503
|
+
// Adding it with hide:true keeps it out of the visible layout and CSV export while
|
|
2504
|
+
// making its values quick-filterable. (Server-side filtered grids additionally need
|
|
2505
|
+
// the field flagged IncludeInUserSearchAPI for the server to return matching rows.)
|
|
2506
|
+
hide: colConfig.hidden === true
|
|
2483
2507
|
};
|
|
2484
2508
|
// Add type-specific formatters with optional custom format
|
|
2485
2509
|
this.applyFieldFormatter(colDef, field, colConfig.format);
|
|
@@ -2771,7 +2795,17 @@ export class EntityDataGridComponent extends BaseAngularComponent {
|
|
|
2771
2795
|
// Regular number formatting
|
|
2772
2796
|
else if (fieldType === 'number') {
|
|
2773
2797
|
const num = Number(params.value);
|
|
2774
|
-
|
|
2798
|
+
if (isNaN(num)) {
|
|
2799
|
+
displayValue = String(params.value);
|
|
2800
|
+
}
|
|
2801
|
+
else if (field.IsPrimaryKey) {
|
|
2802
|
+
// Primary-key integers are identifiers, not quantities — never group them with
|
|
2803
|
+
// thousands separators (an ID of 12345 must render as "12345", not "12,345").
|
|
2804
|
+
displayValue = String(params.value);
|
|
2805
|
+
}
|
|
2806
|
+
else {
|
|
2807
|
+
displayValue = num.toLocaleString();
|
|
2808
|
+
}
|
|
2775
2809
|
}
|
|
2776
2810
|
// Email formatting
|
|
2777
2811
|
else if (isEmail && vc.clickableEmails) {
|
|
@@ -3388,6 +3422,11 @@ export class EntityDataGridComponent extends BaseAngularComponent {
|
|
|
3388
3422
|
// ========================================
|
|
3389
3423
|
onGridReady(event) {
|
|
3390
3424
|
this.gridApi = event.api;
|
|
3425
|
+
// Let the quick filter (search box) match values in HIDDEN columns too (bug C2). AG Grid
|
|
3426
|
+
// defaults `includeHiddenColumnsInQuickFilter` to false, so without this a search term that
|
|
3427
|
+
// only exists in a column the user has hidden would match nothing — even though we now add
|
|
3428
|
+
// hidden columns to the col defs with hide:true precisely so they remain searchable.
|
|
3429
|
+
this.gridApi.setGridOption('includeHiddenColumnsInQuickFilter', true);
|
|
3391
3430
|
this.updateSelection();
|
|
3392
3431
|
if (this._sortState.length > 0) {
|
|
3393
3432
|
this.applySortStateToGrid();
|
|
@@ -3988,13 +4027,22 @@ export class EntityDataGridComponent extends BaseAngularComponent {
|
|
|
3988
4027
|
this.ExportRequested.emit();
|
|
3989
4028
|
this.ExportButtonClick.emit();
|
|
3990
4029
|
// Show the export dialog
|
|
3991
|
-
this.showExportDialogForCurrentData();
|
|
4030
|
+
void this.showExportDialogForCurrentData();
|
|
3992
4031
|
}
|
|
3993
4032
|
/**
|
|
3994
|
-
* Shows the export dialog for the
|
|
4033
|
+
* Shows the export dialog for the full result set (falling back to the loaded page when no
|
|
4034
|
+
* ExportDataProvider is supplied).
|
|
3995
4035
|
*/
|
|
3996
|
-
showExportDialogForCurrentData() {
|
|
3997
|
-
|
|
4036
|
+
async showExportDialogForCurrentData() {
|
|
4037
|
+
this.IsPreparingExport = true;
|
|
4038
|
+
this.cdr.detectChanges();
|
|
4039
|
+
let data;
|
|
4040
|
+
try {
|
|
4041
|
+
data = await this.resolveExportData();
|
|
4042
|
+
}
|
|
4043
|
+
finally {
|
|
4044
|
+
this.IsPreparingExport = false;
|
|
4045
|
+
}
|
|
3998
4046
|
const columns = this.getExportColumns();
|
|
3999
4047
|
const fileName = this.getDefaultExportFileName();
|
|
4000
4048
|
this.exportDialogConfig = {
|
|
@@ -4026,7 +4074,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
|
|
|
4026
4074
|
* @returns Export result with data buffer and metadata
|
|
4027
4075
|
*/
|
|
4028
4076
|
async Export(options, download = true) {
|
|
4029
|
-
const data = this.
|
|
4077
|
+
const data = await this.resolveExportData();
|
|
4030
4078
|
const columns = this.getExportColumns();
|
|
4031
4079
|
const fileName = options?.fileName || this.getDefaultExportFileName();
|
|
4032
4080
|
const exportOptions = {
|
|
@@ -4064,9 +4112,28 @@ export class EntityDataGridComponent extends BaseAngularComponent {
|
|
|
4064
4112
|
return this.Export({ format: 'json' }, download);
|
|
4065
4113
|
}
|
|
4066
4114
|
/**
|
|
4067
|
-
*
|
|
4115
|
+
* Resolves the rows to export. An explicit row selection always wins — export exactly the rows the
|
|
4116
|
+
* user picked (matches the Query viewer's behavior). With NO selection, the FULL result set is
|
|
4117
|
+
* fetched via the host's ExportDataProvider so export isn't silently capped at the current page
|
|
4118
|
+
* (bug C1). So: "export these" = select rows first; "export everything" = export with nothing
|
|
4119
|
+
* selected. If no provider is supplied or the fetch yields nothing, falls back to the loaded page.
|
|
4068
4120
|
*/
|
|
4069
|
-
|
|
4121
|
+
async resolveExportData() {
|
|
4122
|
+
const selected = this.GetSelectedRows();
|
|
4123
|
+
if (selected.length > 0) {
|
|
4124
|
+
return selected.map(row => row);
|
|
4125
|
+
}
|
|
4126
|
+
if (this.ExportDataProvider) {
|
|
4127
|
+
try {
|
|
4128
|
+
const allRows = await this.ExportDataProvider();
|
|
4129
|
+
if (allRows && allRows.length > 0) {
|
|
4130
|
+
return allRows.map(row => row);
|
|
4131
|
+
}
|
|
4132
|
+
}
|
|
4133
|
+
catch {
|
|
4134
|
+
// Fall through to the loaded page on any fetch failure.
|
|
4135
|
+
}
|
|
4136
|
+
}
|
|
4070
4137
|
// rowData is already plain Record<string, unknown>[] objects - return directly
|
|
4071
4138
|
return this.rowData.map(row => row);
|
|
4072
4139
|
}
|
|
@@ -4157,19 +4224,37 @@ export class EntityDataGridComponent extends BaseAngularComponent {
|
|
|
4157
4224
|
}
|
|
4158
4225
|
}
|
|
4159
4226
|
}
|
|
4160
|
-
onAddToListClick() {
|
|
4161
|
-
|
|
4162
|
-
|
|
4227
|
+
async onAddToListClick() {
|
|
4228
|
+
let rows = this.GetSelectedRows();
|
|
4229
|
+
// No explicit selection → add EVERY matching record, not just the loaded page. Without this,
|
|
4230
|
+
// building a list from a sizeable view meant selecting-all page by page (bug E3). We fetch the
|
|
4231
|
+
// full result set via the host-supplied provider; the downstream list dialog still requires the
|
|
4232
|
+
// user to pick a target list and confirm, so nothing is committed implicitly.
|
|
4233
|
+
if (rows.length === 0 && this.ExportDataProvider) {
|
|
4234
|
+
this.IsPreparingAddToList = true;
|
|
4235
|
+
this.cdr.detectChanges();
|
|
4236
|
+
try {
|
|
4237
|
+
rows = await this.ExportDataProvider();
|
|
4238
|
+
}
|
|
4239
|
+
catch {
|
|
4240
|
+
rows = [];
|
|
4241
|
+
}
|
|
4242
|
+
finally {
|
|
4243
|
+
this.IsPreparingAddToList = false;
|
|
4244
|
+
this.cdr.detectChanges();
|
|
4245
|
+
}
|
|
4246
|
+
}
|
|
4247
|
+
if (rows.length > 0) {
|
|
4163
4248
|
// Emit legacy event for backward compatibility
|
|
4164
|
-
this.AddToListButtonClick.emit(
|
|
4249
|
+
this.AddToListButtonClick.emit(rows);
|
|
4165
4250
|
// Emit new structured event with record IDs for list management
|
|
4166
4251
|
if (this._entityInfo) {
|
|
4167
|
-
const recordIds =
|
|
4252
|
+
const recordIds = rows.map(r => {
|
|
4168
4253
|
return buildPkString(r, this._entityInfo);
|
|
4169
4254
|
});
|
|
4170
4255
|
this.AddToListRequested.emit({
|
|
4171
4256
|
entityInfo: this._entityInfo,
|
|
4172
|
-
records:
|
|
4257
|
+
records: rows,
|
|
4173
4258
|
recordIds
|
|
4174
4259
|
});
|
|
4175
4260
|
}
|
|
@@ -4448,7 +4533,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
|
|
|
4448
4533
|
} if (rf & 2) {
|
|
4449
4534
|
let _t;
|
|
4450
4535
|
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.gridContainer = _t.first);
|
|
4451
|
-
} }, inputs: { Params: "Params", AllowLoad: "AllowLoad", AutoRefreshOnParamsChange: "AutoRefreshOnParamsChange", PaginationMode: "PaginationMode", PageSize: "PageSize", CacheBlockSize: "CacheBlockSize", MaxBlocksInCache: "MaxBlocksInCache", ShowPager: "ShowPager", ShowRecycleBin: "ShowRecycleBin", PagerPageNumber: "PagerPageNumber", TotalRowCount: "TotalRowCount", Data: "Data", Columns: "Columns", GridState: "GridState", AllowColumnReorder: "AllowColumnReorder", AllowColumnResize: "AllowColumnResize", AllowColumnToggle: "AllowColumnToggle", ShowHeader: "ShowHeader", AllowSorting: "AllowSorting", AllowMultiSort: "AllowMultiSort", ServerSideSorting: "ServerSideSorting", AllowColumnFilters: "AllowColumnFilters", ShowSearch: "ShowSearch", SelectionMode: "SelectionMode", SelectedKeys: "SelectedKeys", KeyField: "KeyField", EditMode: "EditMode", AllowAdd: "AllowAdd", AllowDelete: "AllowDelete", Height: "Height", RowHeight: "RowHeight", WrapText: "WrapText", VirtualScroll: "VirtualScroll", ShowRowNumbers: "ShowRowNumbers", Striped: "Striped", GridLines: "GridLines", VisualConfig: "VisualConfig", ShowToolbar: "ShowToolbar", ToolbarConfig: "ToolbarConfig", StateKey: "StateKey", AutoPersistState: "AutoPersistState", StatePersistDebounce: "StatePersistDebounce", RefreshDebounce: "RefreshDebounce", FilterText: "FilterText", ShowNewButton: "ShowNewButton", ShowRefreshButton: "ShowRefreshButton", ShowExportButton: "ShowExportButton", ShowDeleteButton: "ShowDeleteButton", ShowCompareButton: "ShowCompareButton", ShowMergeButton: "ShowMergeButton", ShowAddToListButton: "ShowAddToListButton", ShowDuplicateSearchButton: "ShowDuplicateSearchButton", ShowCommunicationButton: "ShowCommunicationButton", AutoNavigate: "AutoNavigate", NavigateOnDoubleClick: "NavigateOnDoubleClick", CreateRecordMode: "CreateRecordMode", NewRecordValues: "NewRecordValues", ShowEntityActionButtons: "ShowEntityActionButtons", EntityActions: "EntityActions", AutoLoadEntityActions: "AutoLoadEntityActions", AggregatesConfig: "AggregatesConfig" }, outputs: { PageChange: "PageChange", AggregatesLoaded: "AggregatesLoaded", BeforeRowSelect: "BeforeRowSelect", AfterRowSelect: "AfterRowSelect", BeforeRowDeselect: "BeforeRowDeselect", AfterRowDeselect: "AfterRowDeselect", SelectionChange: "SelectionChange", BeforeRowClick: "BeforeRowClick", AfterRowClick: "AfterRowClick", BeforeRowDoubleClick: "BeforeRowDoubleClick", AfterRowDoubleClick: "AfterRowDoubleClick", ForeignKeyClick: "ForeignKeyClick", BeforeCellEdit: "BeforeCellEdit", AfterCellEditBegin: "AfterCellEditBegin", BeforeCellEditCommit: "BeforeCellEditCommit", AfterCellEditCommit: "AfterCellEditCommit", BeforeCellEditCancel: "BeforeCellEditCancel", AfterCellEditCancel: "AfterCellEditCancel", BeforeRowSave: "BeforeRowSave", AfterRowSave: "AfterRowSave", BeforeRowDelete: "BeforeRowDelete", AfterRowDelete: "AfterRowDelete", BeforeDataLoad: "BeforeDataLoad", AfterDataLoad: "AfterDataLoad", BeforeDataRefresh: "BeforeDataRefresh", AfterDataRefresh: "AfterDataRefresh", BeforeSort: "BeforeSort", AfterSort: "AfterSort", BeforeColumnReorder: "BeforeColumnReorder", AfterColumnReorder: "AfterColumnReorder", BeforeColumnResize: "BeforeColumnResize", AfterColumnResize: "AfterColumnResize", BeforeColumnVisibilityChange: "BeforeColumnVisibilityChange", AfterColumnVisibilityChange: "AfterColumnVisibilityChange", GridStateChanged: "GridStateChanged", AddRequested: "AddRequested", DeleteRequested: "DeleteRequested", ExportRequested: "ExportRequested", NewButtonClick: "NewButtonClick", RefreshButtonClick: "RefreshButtonClick", ExportButtonClick: "ExportButtonClick", DeleteButtonClick: "DeleteButtonClick", CompareButtonClick: "CompareButtonClick", MergeButtonClick: "MergeButtonClick", AddToListButtonClick: "AddToListButtonClick", DuplicateSearchButtonClick: "DuplicateSearchButtonClick", CommunicationButtonClick: "CommunicationButtonClick", ManageColumnsRequested: "ManageColumnsRequested", NavigationRequested: "NavigationRequested", NewRecordDialogRequested: "NewRecordDialogRequested", NewRecordTabRequested: "NewRecordTabRequested", CompareRecordsRequested: "CompareRecordsRequested", MergeRecordsRequested: "MergeRecordsRequested", CommunicationRequested: "CommunicationRequested", DuplicateSearchRequested: "DuplicateSearchRequested", AddToListRequested: "AddToListRequested", LoadEntityActionsRequested: "LoadEntityActionsRequested", EntityActionRequested: "EntityActionRequested" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 14, vars: 16, consts: [["gridContainer", ""], [1, "mj-grid-toolbar"], [1, "mj-aggregate-cards"], [1, "mj-grid-content"], [1, "mj-grid-loading-overlay"], ["Variant", "error", "Icon", "fa-solid fa-triangle-exclamation", "Title", "Could not load data", "ActionText", "Retry", "ActionIcon", "fa-solid fa-arrows-rotate", 1, "mj-grid-state-fill", 3, "Message"], ["Icon", "fa-solid fa-inbox", "Title", "No data to display", 1, "mj-grid-state-fill"], [1, "mj-ag-grid", "ag-theme-alpine", 3, "theme", "columnDefs", "rowData", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight"], [1, "mj-ag-grid", "ag-theme-alpine", 3, "theme", "columnDefs", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight", "rowModelType", "cacheBlockSize", "maxBlocksInCache", "infiniteInitialRowCount", "cacheOverflowSize"], [3, "TotalRowCount", "PageNumber", "PageSize", "IsLoading"], [1, "mj-aggregate-summary"], [3, "closed", "visible", "config"], [3, "DriverClass", "Context"], [1, "toolbar-left"], [1, "toolbar-search"], [1, "toolbar-center"], [1, "row-count"], [1, "selection-count"], [1, "toolbar-right"], [3, "EntityName"], ["title", "Create new record", 1, "toolbar-button"], ["title", "Refresh data", 1, "toolbar-button", 3, "disabled"], ["title", "Export to Excel", 1, "toolbar-button"], ["title", "Delete selected records", 1, "toolbar-button", "toolbar-button-danger"], ["title", "Compare selected records", 1, "toolbar-button", 3, "disabled"], ["title", "Merge selected records", 1, "toolbar-button", 3, "disabled"], ["title", "Add selected records to a list", 1, "toolbar-button", 3, "disabled"], ["title", "Search for duplicate records", 1, "toolbar-button", 3, "disabled"], ["title", "Send message to selected records", 1, "toolbar-button", 3, "disabled"], ["title", "Add New", 1, "toolbar-button"], ["title", "Refresh", 1, "toolbar-button", 3, "disabled"], ["title", "Delete Selected", 1, "toolbar-button", "toolbar-button-danger"], ["title", "Export", 1, "toolbar-button"], ["title", "Column Chooser", 1, "toolbar-button"], [1, "toolbar-overflow"], [1, "fa-solid", "fa-search", "search-icon"], ["type", "text", 1, "search-input", 3, "input", "placeholder", "value"], ["aria-label", "Clear search", 1, "search-clear"], ["aria-label", "Clear search", 1, "search-clear", 3, "click"], [1, "fa-solid", "fa-times"], [1, "toolbar-button", 3, "class", "disabled", "title"], [1, "toolbar-button", 3, "click", "disabled", "title"], [3, "class"], ["title", "Create new record", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-plus"], [1, "button-text"], ["title", "Refresh data", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-arrows-rotate"], ["title", "Export to Excel", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-file-excel"], ["title", "Delete selected records", 1, "toolbar-button", "toolbar-button-danger", 3, "click"], [1, "fa-solid", "fa-trash"], ["title", "Compare selected records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-code-compare"], ["title", "Merge selected records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-code-merge"], ["title", "Add selected records to a list", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-list-check"], ["title", "Search for duplicate records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-magnifying-glass-plus"], ["title", "Send message to selected records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-envelope"], ["title", "Add New", 1, "toolbar-button", 3, "click"], ["title", "Refresh", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-refresh"], ["title", "Delete Selected", 1, "toolbar-button", "toolbar-button-danger", 3, "click"], ["title", "Export", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-download"], ["title", "Column Chooser", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-columns"], [1, "toolbar-overflow", 3, "click"], ["title", "More actions", 1, "toolbar-button", "overflow-trigger", 3, "click"], [1, "fa-solid", "fa-ellipsis-vertical"], [1, "overflow-menu"], [1, "overflow-item"], [1, "overflow-divider"], [1, "overflow-item", 3, "click"], [1, "overflow-section-label"], [1, "overflow-item", 3, "disabled"], [1, "overflow-item", 3, "click", "disabled"], [1, "aggregate-card"], [1, "aggregate-card-loading"], [1, "aggregate-card-icon"], [1, "aggregate-card-content"], [1, "aggregate-card-label"], [1, "aggregate-card-value"], [1, "fa-solid", "fa-spinner", "fa-spin"], ["text", "Loading..."], ["Variant", "error", "Icon", "fa-solid fa-triangle-exclamation", "Title", "Could not load data", "ActionText", "Retry", "ActionIcon", "fa-solid fa-arrows-rotate", 1, "mj-grid-state-fill", 3, "Action", "Message"], [1, "mj-ag-grid", "ag-theme-alpine", 3, "gridReady", "cellClicked", "rowClicked", "rowDoubleClicked", "sortChanged", "selectionChanged", "columnResized", "columnMoved", "theme", "columnDefs", "rowData", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight"], [1, "mj-ag-grid", "ag-theme-alpine", 3, "gridReady", "cellClicked", "rowClicked", "rowDoubleClicked", "sortChanged", "selectionChanged", "columnResized", "columnMoved", "theme", "columnDefs", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight", "rowModelType", "cacheBlockSize", "maxBlocksInCache", "infiniteInitialRowCount", "cacheOverflowSize"], [3, "PageChange", "TotalRowCount", "PageNumber", "PageSize", "IsLoading"], [1, "aggregate-summary-content"], [1, "aggregate-summary-item"], [1, "aggregate-loading"], [1, "agg-summary-label"], [1, "agg-summary-value"], [3, "Completed", "Cancelled", "DriverClass", "Context"]], template: function EntityDataGridComponent_Template(rf, ctx) { if (rf & 1) {
|
|
4536
|
+
} }, inputs: { Params: "Params", AllowLoad: "AllowLoad", AutoRefreshOnParamsChange: "AutoRefreshOnParamsChange", PaginationMode: "PaginationMode", PageSize: "PageSize", CacheBlockSize: "CacheBlockSize", MaxBlocksInCache: "MaxBlocksInCache", ShowPager: "ShowPager", ExportDataProvider: "ExportDataProvider", ShowRecycleBin: "ShowRecycleBin", PagerPageNumber: "PagerPageNumber", TotalRowCount: "TotalRowCount", Data: "Data", Columns: "Columns", GridState: "GridState", AllowColumnReorder: "AllowColumnReorder", AllowColumnResize: "AllowColumnResize", AllowColumnToggle: "AllowColumnToggle", ShowHeader: "ShowHeader", AllowSorting: "AllowSorting", AllowMultiSort: "AllowMultiSort", ServerSideSorting: "ServerSideSorting", AllowColumnFilters: "AllowColumnFilters", ShowSearch: "ShowSearch", SelectionMode: "SelectionMode", SelectedKeys: "SelectedKeys", KeyField: "KeyField", EditMode: "EditMode", AllowAdd: "AllowAdd", AllowDelete: "AllowDelete", Height: "Height", RowHeight: "RowHeight", WrapText: "WrapText", VirtualScroll: "VirtualScroll", ShowRowNumbers: "ShowRowNumbers", Striped: "Striped", GridLines: "GridLines", VisualConfig: "VisualConfig", ShowToolbar: "ShowToolbar", ToolbarConfig: "ToolbarConfig", StateKey: "StateKey", AutoPersistState: "AutoPersistState", StatePersistDebounce: "StatePersistDebounce", RefreshDebounce: "RefreshDebounce", FilterText: "FilterText", ShowNewButton: "ShowNewButton", ShowRefreshButton: "ShowRefreshButton", ShowExportButton: "ShowExportButton", ShowDeleteButton: "ShowDeleteButton", ShowCompareButton: "ShowCompareButton", ShowMergeButton: "ShowMergeButton", ShowAddToListButton: "ShowAddToListButton", ShowDuplicateSearchButton: "ShowDuplicateSearchButton", ShowCommunicationButton: "ShowCommunicationButton", AutoNavigate: "AutoNavigate", NavigateOnDoubleClick: "NavigateOnDoubleClick", CreateRecordMode: "CreateRecordMode", NewRecordValues: "NewRecordValues", ShowEntityActionButtons: "ShowEntityActionButtons", EntityActions: "EntityActions", AutoLoadEntityActions: "AutoLoadEntityActions", AggregatesConfig: "AggregatesConfig" }, outputs: { PageChange: "PageChange", AggregatesLoaded: "AggregatesLoaded", BeforeRowSelect: "BeforeRowSelect", AfterRowSelect: "AfterRowSelect", BeforeRowDeselect: "BeforeRowDeselect", AfterRowDeselect: "AfterRowDeselect", SelectionChange: "SelectionChange", BeforeRowClick: "BeforeRowClick", AfterRowClick: "AfterRowClick", BeforeRowDoubleClick: "BeforeRowDoubleClick", AfterRowDoubleClick: "AfterRowDoubleClick", ForeignKeyClick: "ForeignKeyClick", BeforeCellEdit: "BeforeCellEdit", AfterCellEditBegin: "AfterCellEditBegin", BeforeCellEditCommit: "BeforeCellEditCommit", AfterCellEditCommit: "AfterCellEditCommit", BeforeCellEditCancel: "BeforeCellEditCancel", AfterCellEditCancel: "AfterCellEditCancel", BeforeRowSave: "BeforeRowSave", AfterRowSave: "AfterRowSave", BeforeRowDelete: "BeforeRowDelete", AfterRowDelete: "AfterRowDelete", BeforeDataLoad: "BeforeDataLoad", AfterDataLoad: "AfterDataLoad", BeforeDataRefresh: "BeforeDataRefresh", AfterDataRefresh: "AfterDataRefresh", BeforeSort: "BeforeSort", AfterSort: "AfterSort", BeforeColumnReorder: "BeforeColumnReorder", AfterColumnReorder: "AfterColumnReorder", BeforeColumnResize: "BeforeColumnResize", AfterColumnResize: "AfterColumnResize", BeforeColumnVisibilityChange: "BeforeColumnVisibilityChange", AfterColumnVisibilityChange: "AfterColumnVisibilityChange", GridStateChanged: "GridStateChanged", AddRequested: "AddRequested", DeleteRequested: "DeleteRequested", ExportRequested: "ExportRequested", NewButtonClick: "NewButtonClick", RefreshButtonClick: "RefreshButtonClick", ExportButtonClick: "ExportButtonClick", DeleteButtonClick: "DeleteButtonClick", CompareButtonClick: "CompareButtonClick", MergeButtonClick: "MergeButtonClick", AddToListButtonClick: "AddToListButtonClick", DuplicateSearchButtonClick: "DuplicateSearchButtonClick", CommunicationButtonClick: "CommunicationButtonClick", ManageColumnsRequested: "ManageColumnsRequested", NavigationRequested: "NavigationRequested", NewRecordDialogRequested: "NewRecordDialogRequested", NewRecordTabRequested: "NewRecordTabRequested", CompareRecordsRequested: "CompareRecordsRequested", MergeRecordsRequested: "MergeRecordsRequested", CommunicationRequested: "CommunicationRequested", DuplicateSearchRequested: "DuplicateSearchRequested", AddToListRequested: "AddToListRequested", LoadEntityActionsRequested: "LoadEntityActionsRequested", EntityActionRequested: "EntityActionRequested" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 14, vars: 16, consts: [["gridContainer", ""], [1, "mj-grid-toolbar"], [1, "mj-aggregate-cards"], [1, "mj-grid-content"], [1, "mj-grid-loading-overlay"], ["Variant", "error", "Icon", "fa-solid fa-triangle-exclamation", "Title", "Could not load data", "ActionText", "Retry", "ActionIcon", "fa-solid fa-arrows-rotate", 1, "mj-grid-state-fill", 3, "Message"], ["Icon", "fa-solid fa-inbox", "Title", "No data to display", 1, "mj-grid-state-fill"], [1, "mj-ag-grid", "ag-theme-alpine", 3, "theme", "columnDefs", "rowData", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight"], [1, "mj-ag-grid", "ag-theme-alpine", 3, "theme", "columnDefs", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight", "rowModelType", "cacheBlockSize", "maxBlocksInCache", "infiniteInitialRowCount", "cacheOverflowSize"], [3, "TotalRowCount", "PageNumber", "PageSize", "IsLoading"], [1, "mj-aggregate-summary"], [3, "closed", "visible", "config"], [3, "DriverClass", "Context"], [1, "toolbar-left"], [1, "toolbar-search"], [1, "toolbar-center"], [1, "row-count"], [1, "selection-count"], [1, "toolbar-right"], [3, "EntityName"], ["title", "Create new record", 1, "toolbar-button"], ["title", "Refresh data", 1, "toolbar-button", 3, "disabled"], ["title", "Export", 1, "toolbar-button", 3, "disabled"], ["title", "Delete selected records", 1, "toolbar-button", "toolbar-button-danger"], ["title", "Compare selected records", 1, "toolbar-button", 3, "disabled"], ["title", "Merge selected records", 1, "toolbar-button", 3, "disabled"], [1, "toolbar-button", 3, "title", "disabled"], ["title", "Search for duplicate records", 1, "toolbar-button", 3, "disabled"], ["title", "Send message to selected records", 1, "toolbar-button", 3, "disabled"], ["title", "Add New", 1, "toolbar-button"], ["title", "Refresh", 1, "toolbar-button", 3, "disabled"], ["title", "Delete Selected", 1, "toolbar-button", "toolbar-button-danger"], ["title", "Export", 1, "toolbar-button"], ["title", "Column Chooser", 1, "toolbar-button"], [1, "toolbar-overflow"], [1, "fa-solid", "fa-search", "search-icon"], ["type", "text", 1, "search-input", 3, "input", "placeholder", "value"], ["aria-label", "Clear search", 1, "search-clear"], ["aria-label", "Clear search", 1, "search-clear", 3, "click"], [1, "fa-solid", "fa-times"], [1, "toolbar-button", 3, "class", "disabled", "title"], [1, "toolbar-button", 3, "click", "disabled", "title"], [3, "class"], ["title", "Create new record", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-plus"], [1, "button-text"], ["title", "Refresh data", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-arrows-rotate"], ["title", "Export", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid"], ["title", "Delete selected records", 1, "toolbar-button", "toolbar-button-danger", 3, "click"], [1, "fa-solid", "fa-trash"], ["title", "Compare selected records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-code-compare"], ["title", "Merge selected records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-code-merge"], [1, "toolbar-button", 3, "click", "title", "disabled"], ["title", "Search for duplicate records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-magnifying-glass-plus"], ["title", "Send message to selected records", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-envelope"], ["title", "Add New", 1, "toolbar-button", 3, "click"], ["title", "Refresh", 1, "toolbar-button", 3, "click", "disabled"], [1, "fa-solid", "fa-refresh"], ["title", "Delete Selected", 1, "toolbar-button", "toolbar-button-danger", 3, "click"], ["title", "Export", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-download"], ["title", "Column Chooser", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-columns"], [1, "toolbar-overflow", 3, "click"], ["title", "More actions", 1, "toolbar-button", "overflow-trigger", 3, "click"], [1, "fa-solid", "fa-ellipsis-vertical"], [1, "overflow-menu"], [1, "overflow-item"], [1, "overflow-divider"], [1, "overflow-item", 3, "click"], [1, "fa-solid", "fa-file-excel"], [1, "overflow-section-label"], [1, "overflow-item", 3, "disabled"], [1, "overflow-item", 3, "click", "disabled"], [1, "aggregate-card"], [1, "aggregate-card-loading"], [1, "aggregate-card-icon"], [1, "aggregate-card-content"], [1, "aggregate-card-label"], [1, "aggregate-card-value"], [1, "fa-solid", "fa-spinner", "fa-spin"], ["text", "Loading..."], ["Variant", "error", "Icon", "fa-solid fa-triangle-exclamation", "Title", "Could not load data", "ActionText", "Retry", "ActionIcon", "fa-solid fa-arrows-rotate", 1, "mj-grid-state-fill", 3, "Action", "Message"], [1, "mj-ag-grid", "ag-theme-alpine", 3, "gridReady", "cellClicked", "rowClicked", "rowDoubleClicked", "sortChanged", "selectionChanged", "columnResized", "columnMoved", "theme", "columnDefs", "rowData", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight"], [1, "mj-ag-grid", "ag-theme-alpine", 3, "gridReady", "cellClicked", "rowClicked", "rowDoubleClicked", "sortChanged", "selectionChanged", "columnResized", "columnMoved", "theme", "columnDefs", "defaultColDef", "rowSelection", "getRowId", "suppressCellFocus", "rowHeight", "headerHeight", "rowModelType", "cacheBlockSize", "maxBlocksInCache", "infiniteInitialRowCount", "cacheOverflowSize"], [3, "PageChange", "TotalRowCount", "PageNumber", "PageSize", "IsLoading"], [1, "aggregate-summary-content"], [1, "aggregate-summary-item"], [1, "aggregate-loading"], [1, "agg-summary-label"], [1, "agg-summary-value"], [3, "Completed", "Cancelled", "DriverClass", "Context"]], template: function EntityDataGridComponent_Template(rf, ctx) { if (rf & 1) {
|
|
4452
4537
|
const _r1 = i0.ɵɵgetCurrentView();
|
|
4453
4538
|
i0.ɵɵelementStart(0, "div", null, 0);
|
|
4454
4539
|
i0.ɵɵconditionalCreate(2, EntityDataGridComponent_Conditional_2_Template, 27, 19, "div", 1);
|
|
@@ -4516,7 +4601,7 @@ export class EntityDataGridComponent extends BaseAngularComponent {
|
|
|
4516
4601
|
animate('100ms ease-in', style({ opacity: 0, transform: 'translateY(-8px)' }))
|
|
4517
4602
|
])
|
|
4518
4603
|
])
|
|
4519
|
-
], template: "<!-- Grid Container -->\n<div\n #gridContainer\n [class]=\"gridContainerClasses.join(' ')\"\n [style.height]=\"gridHeightStyle\">\n\n <!-- Toolbar -->\n @if (ShowToolbar) {\n <div class=\"mj-grid-toolbar\">\n <div class=\"toolbar-left\">\n <!-- Search -->\n @if (ShowSearch) {\n <div class=\"toolbar-search\">\n <i class=\"fa-solid fa-search search-icon\"></i>\n <input\n type=\"text\"\n class=\"search-input\"\n [placeholder]=\"ToolbarConfig.searchPlaceholder || 'Search...'\"\n [value]=\"FilterText\"\n (input)=\"FilterText = $any($event.target).value\" />\n @if (FilterText) {\n <button\n class=\"search-clear\"\n (click)=\"FilterText = ''\"\n aria-label=\"Clear search\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n }\n </div>\n }\n <!-- Custom Left Buttons -->\n @for (button of ToolbarConfig.customButtons; track button) {\n @if (button.position !== 'right' && isButtonVisible(button)) {\n <button\n class=\"toolbar-button\"\n [class]=\"button.cssClass\"\n [disabled]=\"isButtonDisabled(button)\"\n [title]=\"button.tooltip || ''\"\n (click)=\"onToolbarButtonClick(button)\">\n @if (button.icon) {\n <i [class]=\"button.icon\"></i>\n }\n @if (button.text) {\n <span>{{ button.text }}</span>\n }\n </button>\n }\n }\n </div>\n <div class=\"toolbar-center\">\n <!-- Row Count -->\n @if (ToolbarConfig.showRowCount !== false) {\n <span class=\"row-count\">\n {{ totalRowCount }} {{ totalRowCount === 1 ? 'row' : 'rows' }}\n </span>\n }\n <!-- Selection Count -->\n @if (ToolbarConfig.showSelectionCount && SelectedKeys.length > 0) {\n <span class=\"selection-count\">\n ({{ SelectedKeys.length }} selected)\n </span>\n }\n </div>\n <div class=\"toolbar-right\">\n <!-- Recycle Bin chip \u2014 auto-hides when no deleted records, no tracking,\n or user lacks Delete permission. -->\n @if (ShowRecycleBin) {\n <mj-recycle-bin-chip [EntityName]=\"entityInfoName\"></mj-recycle-bin-chip>\n }\n <!-- New/Add Button (predefined) -->\n @if (ShowNewButton) {\n <button\n class=\"toolbar-button\"\n title=\"Create new record\"\n (click)=\"onAddClick()\">\n <i class=\"fa-solid fa-plus\"></i>\n <span class=\"button-text\">New</span>\n </button>\n }\n <!-- Refresh Button (predefined) -->\n @if (ShowRefreshButton) {\n <button\n class=\"toolbar-button\"\n title=\"Refresh data\"\n [disabled]=\"loading\"\n (click)=\"onRefreshClick()\">\n <i class=\"fa-solid fa-arrows-rotate\" [class.fa-spin]=\"loading\"></i>\n <span class=\"button-text\">Refresh</span>\n </button>\n }\n <!-- Export Button (predefined) -->\n @if (ShowExportButton) {\n <button\n class=\"toolbar-button\"\n title=\"Export to Excel\"\n (click)=\"onExportClick()\">\n <i class=\"fa-solid fa-file-excel\"></i>\n <span class=\"button-text\">Export</span>\n </button>\n }\n <!-- Delete Button (predefined) -->\n @if (ShowDeleteButton && HasSelection) {\n <button\n class=\"toolbar-button toolbar-button-danger\"\n title=\"Delete selected records\"\n (click)=\"onDeleteClick()\">\n <i class=\"fa-solid fa-trash\"></i>\n <span class=\"button-text\">Delete</span>\n </button>\n }\n <!-- Compare Button (predefined) -->\n @if (ShowCompareButton) {\n <button\n class=\"toolbar-button\"\n title=\"Compare selected records\"\n [disabled]=\"!HasMultipleSelection\"\n (click)=\"onCompareClick()\">\n <i class=\"fa-solid fa-code-compare\"></i>\n <span class=\"button-text\">Compare</span>\n </button>\n }\n <!-- Merge Button (predefined) -->\n @if (ShowMergeButton) {\n <button\n class=\"toolbar-button\"\n title=\"Merge selected records\"\n [disabled]=\"!HasMultipleSelection\"\n (click)=\"onMergeClick()\">\n <i class=\"fa-solid fa-code-merge\"></i>\n <span class=\"button-text\">Merge</span>\n </button>\n }\n <!-- Add to List Button (predefined) -->\n @if (ShowAddToListButton) {\n <button\n class=\"toolbar-button\"\n title=\"Add selected records to a list\"\n [disabled]=\"!HasSelection\"\n (click)=\"onAddToListClick()\">\n <i class=\"fa-solid fa-list-check\"></i>\n <span class=\"button-text\">Add to List</span>\n </button>\n }\n <!-- Search for Duplicates Button (predefined) -->\n @if (ShowDuplicateSearchButton) {\n <button\n class=\"toolbar-button\"\n title=\"Search for duplicate records\"\n [disabled]=\"!HasMultipleSelection\"\n (click)=\"onDuplicateSearchClick()\">\n <i class=\"fa-solid fa-magnifying-glass-plus\"></i>\n <span class=\"button-text\">Find Duplicates</span>\n </button>\n }\n <!-- Communication Button (predefined) -->\n @if (ShowCommunicationButton) {\n <button\n class=\"toolbar-button\"\n title=\"Send message to selected records\"\n [disabled]=\"!HasSelection\"\n (click)=\"onCommunicationClick()\">\n <i class=\"fa-solid fa-envelope\"></i>\n <span class=\"button-text\">Send Message</span>\n </button>\n }\n <!-- Legacy ToolbarConfig buttons -->\n <!-- Add Button (legacy) -->\n @if (ToolbarConfig.showAdd && AllowAdd && !ShowNewButton) {\n <button\n class=\"toolbar-button\"\n title=\"Add New\"\n (click)=\"onAddClick()\">\n <i class=\"fa-solid fa-plus\"></i>\n </button>\n }\n <!-- Refresh Button (legacy) -->\n @if (ToolbarConfig.showRefresh !== false && !ShowRefreshButton) {\n <button\n class=\"toolbar-button\"\n title=\"Refresh\"\n [disabled]=\"loading\"\n (click)=\"onRefreshClick()\">\n <i class=\"fa-solid fa-refresh\" [class.fa-spin]=\"loading\"></i>\n </button>\n }\n <!-- Delete Button (legacy) -->\n @if (ToolbarConfig.showDelete && AllowDelete && HasSelection && !ShowDeleteButton) {\n <button\n class=\"toolbar-button toolbar-button-danger\"\n title=\"Delete Selected\"\n (click)=\"onDeleteClick()\">\n <i class=\"fa-solid fa-trash\"></i>\n </button>\n }\n <!-- Export Button (legacy) -->\n @if (ToolbarConfig.showExport && !ShowExportButton) {\n <button\n class=\"toolbar-button\"\n title=\"Export\"\n (click)=\"onExportClick()\">\n <i class=\"fa-solid fa-download\"></i>\n </button>\n }\n <!-- Column Chooser Button -->\n @if (ToolbarConfig.showColumnChooser && AllowColumnToggle) {\n <button\n class=\"toolbar-button\"\n title=\"Column Chooser\"\n (click)=\"onColumnChooserClick()\">\n <i class=\"fa-solid fa-columns\"></i>\n </button>\n }\n <!-- Custom Right Buttons -->\n @for (button of ToolbarConfig.customButtons; track button) {\n @if (button.position === 'right' && isButtonVisible(button)) {\n <button\n class=\"toolbar-button\"\n [class]=\"button.cssClass\"\n [disabled]=\"isButtonDisabled(button)\"\n [title]=\"button.tooltip || ''\"\n (click)=\"onToolbarButtonClick(button)\">\n @if (button.icon) {\n <i [class]=\"button.icon\"></i>\n }\n @if (button.text) {\n <span>{{ button.text }}</span>\n }\n </button>\n }\n }\n <!-- Overflow Menu -->\n @if (hasOverflowMenuItems) {\n <div class=\"toolbar-overflow\" (click)=\"$event.stopPropagation()\">\n <button\n class=\"toolbar-button overflow-trigger\"\n title=\"More actions\"\n (click)=\"toggleOverflowMenu()\">\n <i class=\"fa-solid fa-ellipsis-vertical\"></i>\n </button>\n @if (showOverflowMenu) {\n <div class=\"overflow-menu\" [@fadeIn]>\n <!-- Export (if in overflow) -->\n @if (showExportInOverflow) {\n <button class=\"overflow-item\" (click)=\"onExportClick(); closeOverflowMenu()\">\n <i class=\"fa-solid fa-file-excel\"></i>\n <span>Export to Excel</span>\n </button>\n }\n <!-- Entity Actions -->\n @if (ShowEntityActionButtons && EntityActions.length > 0) {\n <div class=\"overflow-divider\"></div>\n <div class=\"overflow-section-label\">Actions</div>\n @for (action of EntityActions; track action) {\n <button\n class=\"overflow-item\"\n [disabled]=\"!isEntityActionEnabled(action)\"\n (click)=\"onEntityActionClick(action); closeOverflowMenu()\">\n <i [class]=\"action.icon || 'fa-solid fa-bolt'\"></i>\n <span>{{ action.name }}</span>\n </button>\n }\n }\n <!-- Column Chooser (if in overflow) -->\n @if (showColumnChooserInOverflow) {\n <div class=\"overflow-divider\"></div>\n }\n @if (showColumnChooserInOverflow) {\n <button class=\"overflow-item\" (click)=\"onColumnChooserClick(); closeOverflowMenu()\">\n <i class=\"fa-solid fa-columns\"></i>\n <span>Manage Columns</span>\n </button>\n }\n <!-- Selection-dependent actions in overflow -->\n @if (HasSelection && hasSelectionDependentOverflowActions) {\n <div class=\"overflow-divider\"></div>\n @if (showCommunicationInOverflow) {\n <button class=\"overflow-item\" (click)=\"onCommunicationClick(); closeOverflowMenu()\">\n <i class=\"fa-solid fa-envelope\"></i>\n <span>Send Message</span>\n </button>\n }\n }\n </div>\n }\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Aggregate Cards (displayed as summary cards above the grid) -->\n @if (ShowAggregatePanel) {\n <div class=\"mj-aggregate-cards\">\n @for (agg of CardAggregates; track agg) {\n <div class=\"aggregate-card\">\n @if (agg.icon) {\n <div class=\"aggregate-card-icon\">\n <i [class]=\"agg.icon\"></i>\n </div>\n }\n <div class=\"aggregate-card-content\">\n <span class=\"aggregate-card-label\">{{ agg.label }}</span>\n <span class=\"aggregate-card-value\">{{ getAggregateValue(agg) }}</span>\n </div>\n </div>\n }\n @if (AggregatesLoading) {\n <div class=\"aggregate-card-loading\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n </div>\n }\n </div>\n }\n\n <!-- Grid Content -->\n <div class=\"mj-grid-content\">\n <!-- Loading Overlay -->\n @if (loading && rowData.length === 0) {\n <div class=\"mj-grid-loading-overlay\">\n <mj-loading text=\"Loading...\"></mj-loading>\n </div>\n }\n\n <!-- Error State -->\n @if (errorMessage && !loading) {\n <mj-empty-state\n class=\"mj-grid-state-fill\"\n Variant=\"error\"\n Icon=\"fa-solid fa-triangle-exclamation\"\n Title=\"Could not load data\"\n [Message]=\"errorMessage\"\n ActionText=\"Retry\"\n ActionIcon=\"fa-solid fa-arrows-rotate\"\n (Action)=\"Refresh()\" />\n }\n\n <!-- Empty State -->\n @if (!loading && !errorMessage && rowData.length === 0) {\n <mj-empty-state\n class=\"mj-grid-state-fill\"\n Icon=\"fa-solid fa-inbox\"\n Title=\"No data to display\" />\n }\n\n <!-- AG Grid (Client-side mode) -->\n @if (!errorMessage && PaginationMode === 'client' && (rowData.length > 0 || loading)) {\n <ag-grid-angular\n class=\"mj-ag-grid ag-theme-alpine\"\n [theme]=\"agGridTheme\"\n [columnDefs]=\"agColumnDefs\"\n [rowData]=\"rowData\"\n [defaultColDef]=\"defaultColDef\"\n [rowSelection]=\"agRowSelection\"\n [getRowId]=\"getRowId\"\n [suppressCellFocus]=\"true\"\n [rowHeight]=\"RowHeight\"\n [headerHeight]=\"ShowHeader ? undefined : 0\"\n (gridReady)=\"onGridReady($event)\"\n (cellClicked)=\"onAgCellClicked($event)\"\n (rowClicked)=\"onAgRowClicked($event)\"\n (rowDoubleClicked)=\"onAgRowDoubleClicked($event)\"\n (sortChanged)=\"onAgSortChanged($event)\"\n (selectionChanged)=\"onAgSelectionChanged($event)\"\n (columnResized)=\"onAgColumnResized($event)\"\n (columnMoved)=\"onAgColumnMoved($event)\">\n </ag-grid-angular>\n }\n\n <!-- AG Grid (Infinite Scroll mode) -->\n @if (!errorMessage && PaginationMode === 'infinite') {\n <ag-grid-angular\n class=\"mj-ag-grid ag-theme-alpine\"\n [theme]=\"agGridTheme\"\n [columnDefs]=\"agColumnDefs\"\n [defaultColDef]=\"defaultColDef\"\n [rowSelection]=\"agRowSelection\"\n [getRowId]=\"getRowId\"\n [suppressCellFocus]=\"true\"\n [rowHeight]=\"RowHeight\"\n [headerHeight]=\"ShowHeader ? undefined : 0\"\n [rowModelType]=\"'infinite'\"\n [cacheBlockSize]=\"CacheBlockSize\"\n [maxBlocksInCache]=\"MaxBlocksInCache\"\n [infiniteInitialRowCount]=\"1\"\n [cacheOverflowSize]=\"2\"\n (gridReady)=\"onGridReady($event)\"\n (cellClicked)=\"onAgCellClicked($event)\"\n (rowClicked)=\"onAgRowClicked($event)\"\n (rowDoubleClicked)=\"onAgRowDoubleClicked($event)\"\n (sortChanged)=\"onAgSortChanged($event)\"\n (selectionChanged)=\"onAgSelectionChanged($event)\"\n (columnResized)=\"onAgColumnResized($event)\"\n (columnMoved)=\"onAgColumnMoved($event)\">\n </ag-grid-angular>\n }\n\n </div>\n\n <!-- Pagination (opt-in via ShowPager input) -->\n @if (ShowPager) {\n <mj-pagination\n [TotalRowCount]=\"totalRowCount\"\n [PageNumber]=\"PagerPageNumber\"\n [PageSize]=\"PageSize\"\n [IsLoading]=\"loading\"\n (PageChange)=\"PageChange.emit($event)\">\n </mj-pagination>\n }\n\n <!-- Aggregate Summary Row (outside mj-grid-content so it's always visible) -->\n @if (ShowAggregateSummary) {\n <div class=\"mj-aggregate-summary\">\n <div class=\"aggregate-summary-content\">\n @for (agg of ColumnAggregates; track agg) {\n <div class=\"aggregate-summary-item\">\n @if (agg.icon) {\n <i [class]=\"agg.icon\"></i>\n }\n <span class=\"agg-summary-label\">{{ agg.label }}:</span>\n <span class=\"agg-summary-value\">{{ getAggregateValue(agg) }}</span>\n </div>\n }\n </div>\n @if (AggregatesLoading) {\n <div class=\"aggregate-loading\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n </div>\n }\n </div>\n }\n</div>\n\n<!-- Export Dialog -->\n<mj-export-dialog\n [visible]=\"showExportDialog\"\n [config]=\"exportDialogConfig\"\n (closed)=\"onExportDialogClosed($event)\">\n</mj-export-dialog>\n\n<!-- Runtime-UX driver for an entity action that names a RuntimeUXDriverClass (e.g. the Record Process\n bulk-update runner). Mounted in-place over the grid \u2014 no parent wiring required. -->\n@if (ActiveRuntimeDriver) {\n <mj-entity-action-ux-host\n [DriverClass]=\"ActiveRuntimeDriver.DriverClass\"\n [Context]=\"ActiveRuntimeDriver.Context\"\n (Completed)=\"OnRuntimeDriverCompleted($event)\"\n (Cancelled)=\"OnRuntimeDriverCancelled()\">\n </mj-entity-action-ux-host>\n}\n", styles: ["/* ========================================\n CSS Custom Properties (Theme Variables)\n ======================================== */\n\n:host {\n /* Grid container */\n --grid-border-color: var(--mj-border-default);\n --grid-border-radius: 0px;\n --grid-background: var(--mj-bg-surface);\n\n /* Header */\n --grid-header-bg: var(--mj-bg-surface-card);\n --grid-header-text: var(--mj-text-primary);\n --grid-header-font-weight: 600;\n --grid-header-height: 40px;\n --grid-header-border-color: var(--mj-border-default);\n\n /* Rows */\n --grid-row-height: 40px;\n --grid-row-bg: var(--mj-bg-surface);\n --grid-row-bg-alt: var(--mj-bg-surface-card);\n --grid-row-hover-bg: var(--mj-bg-surface-hover);\n --grid-row-selected-bg: color-mix(in srgb, var(--mj-status-warning) 10%, var(--mj-bg-surface));\n --grid-row-selected-hover-bg: color-mix(in srgb, var(--mj-status-warning) 18%, var(--mj-bg-surface));\n\n /* Cells */\n --grid-cell-padding: 8px 12px;\n --grid-cell-text: var(--mj-text-primary);\n --grid-cell-border-color: var(--mj-border-subtle);\n\n /* Selection - mellow yellow to avoid conflict with blue hyperlinks */\n --grid-checkbox-color: var(--mj-brand-primary);\n --grid-selection-indicator-color: var(--mj-status-warning);\n\n /* Editing */\n --grid-edit-cell-bg: var(--mj-bg-surface);\n --grid-edit-cell-border: var(--mj-brand-primary);\n --grid-edit-cell-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n\n /* Sorting */\n --grid-sort-indicator-color: var(--mj-brand-primary);\n\n /* Toolbar */\n --grid-toolbar-bg: var(--mj-bg-surface);\n --grid-toolbar-height: 48px;\n --grid-toolbar-border-color: var(--mj-border-default);\n\n /* Loading */\n --grid-loading-overlay-bg: color-mix(in srgb, var(--mj-bg-surface) 80%, transparent);\n\n /* Empty state */\n --grid-empty-text-color: var(--mj-text-disabled);\n --grid-empty-icon-color: var(--mj-border-strong);\n\n display: block;\n height: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;\n}\n\n/* ========================================\n Grid Container\n ======================================== */\n\n.mj-grid-container {\n display: flex;\n flex-direction: column;\n border: 1px solid var(--grid-border-color);\n border-radius: var(--grid-border-radius);\n background: var(--grid-background);\n overflow: hidden;\n}\n\n/* ========================================\n Toolbar\n ======================================== */\n\n.mj-grid-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n min-height: var(--grid-toolbar-height);\n padding: 0 12px;\n background: var(--grid-toolbar-bg);\n border-bottom: 1px solid var(--grid-toolbar-border-color);\n gap: 12px;\n}\n\n.toolbar-left,\n.toolbar-right {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.toolbar-center {\n display: flex;\n align-items: center;\n gap: 8px;\n color: var(--mj-text-secondary);\n font-size: 13px;\n}\n\n.toolbar-search {\n display: flex;\n align-items: center;\n position: relative;\n}\n\n.search-icon {\n position: absolute;\n left: 10px;\n color: var(--mj-text-disabled);\n font-size: 13px;\n}\n\n.search-input {\n padding: 6px 30px 6px 32px;\n border: 1px solid var(--mj-border-strong);\n border-radius: 4px;\n font-size: 13px;\n width: 200px;\n transition: border-color 0.2s, box-shadow 0.2s;\n}\n\n.search-input:focus {\n outline: none;\n border-color: var(--grid-selection-indicator-color);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n.search-clear {\n position: absolute;\n right: 6px;\n background: none;\n border: none;\n cursor: pointer;\n padding: 4px;\n color: var(--mj-text-disabled);\n font-size: 12px;\n}\n\n.search-clear:hover {\n color: var(--mj-text-secondary);\n}\n\n.toolbar-button {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-strong);\n border-radius: 4px;\n cursor: pointer;\n font-size: 13px;\n color: var(--mj-text-primary);\n transition: background-color 0.2s, border-color 0.2s;\n}\n\n.toolbar-button:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-strong);\n}\n\n.toolbar-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.toolbar-button i {\n font-size: 14px;\n}\n\n.toolbar-button-danger {\n color: var(--mj-status-error);\n}\n\n.toolbar-button-danger:hover:not(:disabled) {\n background: var(--mj-status-error-bg);\n border-color: var(--mj-status-error);\n}\n\n.row-count,\n.selection-count {\n font-weight: 500;\n}\n\n/* ========================================\n Grid Content\n ======================================== */\n\n.mj-grid-content {\n flex: 1;\n position: relative;\n overflow: hidden;\n}\n\n.mj-grid-scroll-container {\n height: 100%;\n overflow: auto;\n}\n\n/* ========================================\n Loading Overlay\n ======================================== */\n\n.mj-grid-loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--grid-loading-overlay-bg);\n z-index: 10;\n}\n\n/* ========================================\n Error State\n ======================================== */\n\n/* ========================================\n Empty / Error State\n ======================================== */\n\n/* Flex-fill so <mj-empty-state> placeholders occupy the grid content area\n (the host centers its content but does not grow to fill the parent). */\n.mj-grid-state-fill {\n flex: 1 1 auto;\n}\n\n/* ========================================\n Header Row\n ======================================== */\n\n.mj-grid-header {\n display: flex;\n min-height: var(--grid-header-height);\n background: var(--grid-header-bg);\n border-bottom: 2px solid var(--grid-header-border-color);\n position: sticky;\n top: 0;\n z-index: 5;\n}\n\n.mj-grid-header-cell {\n display: flex;\n align-items: center;\n padding: var(--grid-cell-padding);\n font-weight: var(--grid-header-font-weight);\n color: var(--grid-header-text);\n font-size: 13px;\n user-select: none;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n border-right: 1px solid var(--grid-cell-border-color);\n flex-shrink: 0;\n}\n\n.mj-grid-header-cell:last-child {\n border-right: none;\n}\n\n.mj-grid-header-cell.sortable {\n cursor: pointer;\n}\n\n.mj-grid-header-cell.sortable:hover {\n background: rgba(0, 0, 0, 0.04);\n}\n\n.header-text {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.sort-indicator {\n display: flex;\n align-items: center;\n margin-left: 6px;\n color: var(--grid-sort-indicator-color);\n}\n\n.sort-indicator i {\n font-size: 12px;\n}\n\n.sort-index {\n font-size: 10px;\n margin-left: 2px;\n font-weight: normal;\n}\n\n/* ========================================\n Data Rows\n ======================================== */\n\n.mj-grid-row {\n display: flex;\n align-items: stretch;\n background: var(--grid-row-bg);\n transition: background-color 0.15s;\n cursor: default;\n}\n\n.mj-grid-row:hover {\n background: var(--grid-row-hover-bg);\n}\n\n.mj-grid-row.grid-row-alt {\n background: var(--grid-row-bg-alt);\n}\n\n.mj-grid-row.grid-row-alt:hover {\n background: var(--grid-row-hover-bg);\n}\n\n.mj-grid-row.grid-row-selected {\n background: var(--grid-row-selected-bg);\n}\n\n.mj-grid-row.grid-row-selected:hover {\n background: var(--grid-row-selected-hover-bg);\n}\n\n.mj-grid-row.grid-row-editing {\n background: color-mix(in srgb, var(--mj-status-warning) 8%, var(--mj-bg-surface));\n}\n\n.mj-grid-row.grid-row-dirty {\n border-left: 3px solid var(--mj-status-warning);\n}\n\n/* ========================================\n Data Cells\n ======================================== */\n\n.mj-grid-cell {\n display: flex;\n align-items: center;\n padding: var(--grid-cell-padding);\n color: var(--grid-cell-text);\n font-size: 13px;\n overflow: hidden;\n border-right: 1px solid transparent;\n flex-shrink: 0;\n}\n\n.mj-grid-cell:last-child {\n border-right: none;\n}\n\n/* Grid lines modes */\n.grid-lines-horizontal .mj-grid-row {\n border-bottom: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-vertical .mj-grid-cell {\n border-right: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-both .mj-grid-row {\n border-bottom: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-both .mj-grid-cell {\n border-right: 1px solid var(--grid-cell-border-color);\n}\n\n/* Cell alignment */\n.mj-grid-cell.align-left {\n justify-content: flex-start;\n}\n\n.mj-grid-cell.align-center {\n justify-content: center;\n}\n\n.mj-grid-cell.align-right {\n justify-content: flex-end;\n}\n\n.cell-content {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Special cells */\n.row-number-cell {\n width: 50px;\n min-width: 50px;\n max-width: 50px;\n justify-content: center;\n color: var(--mj-text-disabled);\n font-size: 12px;\n background: var(--grid-header-bg);\n}\n\n.checkbox-cell {\n width: 40px;\n min-width: 40px;\n max-width: 40px;\n justify-content: center;\n}\n\n.checkbox-cell input[type=\"checkbox\"] {\n width: 16px;\n height: 16px;\n cursor: pointer;\n accent-color: var(--grid-checkbox-color);\n}\n\n/* ========================================\n Virtual Scrolling\n ======================================== */\n\n.mj-grid-virtual-spacer {\n flex-shrink: 0;\n}\n\n/* ========================================\n Responsive Adjustments\n ======================================== */\n\n@media (max-width: 768px) {\n .mj-grid-toolbar {\n flex-wrap: wrap;\n padding: 8px;\n }\n\n .toolbar-search {\n order: 3;\n width: 100%;\n margin-top: 8px;\n }\n\n .search-input {\n width: 100%;\n }\n\n .toolbar-center {\n order: 2;\n }\n\n /* Hide button text on mobile */\n .toolbar-button .button-text {\n display: none;\n }\n}\n\n/* ========================================\n Toolbar Button Text\n ======================================== */\n\n.toolbar-button .button-text {\n font-size: 13px;\n}\n\n/* ========================================\n Overflow Menu\n ======================================== */\n\n.toolbar-overflow {\n position: relative;\n}\n\n.overflow-trigger {\n padding: 6px 8px !important;\n}\n\n.overflow-trigger i {\n font-size: 16px;\n}\n\n.overflow-menu {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 4px;\n min-width: 220px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.08);\n z-index: 1000;\n overflow: hidden;\n}\n\n.overflow-item {\n display: flex;\n align-items: center;\n width: 100%;\n padding: 10px 16px;\n border: none;\n background: none;\n cursor: pointer;\n font-size: 14px;\n color: var(--mj-text-primary);\n text-align: left;\n gap: 12px;\n transition: background-color 0.15s;\n}\n\n.overflow-item:hover:not(:disabled) {\n background: var(--mj-bg-surface-card);\n}\n\n.overflow-item:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.overflow-item i {\n width: 18px;\n font-size: 14px;\n color: var(--mj-text-secondary);\n text-align: center;\n}\n\n.overflow-item span {\n flex: 1;\n}\n\n.overflow-divider {\n height: 1px;\n background: var(--mj-border-default);\n margin: 4px 0;\n}\n\n.overflow-section-label {\n padding: 8px 16px 4px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n color: var(--mj-text-disabled);\n letter-spacing: 0.5px;\n}\n\n/* Entity Actions submenu styling */\n.overflow-item.action-item i {\n color: var(--mj-brand-primary);\n}\n\n/* ========================================\n Highlight Matches\n ======================================== */\n\n::ng-deep .highlight-match {\n background-color: color-mix(in srgb, var(--mj-status-warning) 40%, var(--mj-bg-surface));\n border-radius: 2px;\n padding: 0 1px;\n}\n\n/* ========================================\n AG Grid Customizations\n ======================================== */\n\n.mj-ag-grid {\n width: 100%;\n height: 100%;\n}\n\n/* AG Grid v35 uses JS-based theming (colorSchemeVariable) \u2014 no CSS variable overrides needed */\n\n/* Selected row styling - left indicator bar only, background handled by AG Grid theme */\n::ng-deep .ag-row-selected {\n box-shadow: inset 4px 0 0 0 var(--mj-status-warning);\n}\n\n/* Selection checkbox styling */\n::ng-deep .ag-checkbox-input-wrapper {\n width: 18px;\n height: 18px;\n}\n\n::ng-deep .ag-checkbox-input-wrapper.ag-checked::after {\n color: var(--grid-checkbox-color, var(--mj-brand-primary));\n}\n\n/* ========================================\n Visual Config: Header Styles\n ======================================== */\n\n/* Flat header (minimal) */\n.header-style-flat ::ng-deep .ag-header {\n background: var(--grid-header-bg, var(--mj-bg-surface-card));\n border-bottom: 1px solid var(--grid-header-border-color, var(--mj-border-default));\n}\n\n/* Elevated header (default - subtle shadow) */\n.header-style-elevated ::ng-deep .ag-header {\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--grid-header-border-color, var(--mj-border-default));\n}\n\n.header-style-elevated.header-shadow ::ng-deep .ag-header {\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.06);\n}\n\n/* Gradient header (more prominent) */\n.header-style-gradient ::ng-deep .ag-header {\n background: var(--mj-bg-surface-sunken);\n border-bottom: none;\n}\n\n.header-style-gradient.header-shadow ::ng-deep .ag-header {\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n}\n\n/* Bold header (high contrast) */\n.header-style-bold ::ng-deep .ag-header {\n background: var(--mj-text-secondary);\n border-bottom: none;\n}\n\n.header-style-bold ::ng-deep .ag-header-cell-text {\n color: var(--mj-text-inverse);\n font-weight: 600;\n}\n\n.header-style-bold ::ng-deep .ag-header-icon {\n color: color-mix(in srgb, var(--mj-text-inverse) 70%, transparent);\n}\n\n.header-style-bold ::ng-deep .ag-header-cell:hover {\n background: color-mix(in srgb, var(--mj-text-inverse) 10%, transparent);\n}\n\n/* Header sort icons enhancement */\n::ng-deep .ag-header-cell-sorted-asc .ag-icon-asc,\n::ng-deep .ag-header-cell-sorted-desc .ag-icon-desc {\n color: var(--grid-accent-color, var(--grid-sort-indicator-color, var(--mj-brand-primary)));\n}\n\n/* ========================================\n Visual Config: Zebra Striping\n ======================================== */\n\n/* Subtle contrast */\n.alternate-rows-subtle ::ng-deep .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.015);\n}\n\n.alternate-rows-subtle ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, var(--mj-bg-surface-hover));\n}\n\n/* Medium contrast (default) */\n.alternate-rows-medium ::ng-deep .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.025);\n}\n\n.alternate-rows-medium ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, var(--mj-bg-surface-hover));\n}\n\n/* Strong contrast */\n.alternate-rows-strong ::ng-deep .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.04);\n}\n\n.alternate-rows-strong ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, var(--mj-bg-surface-hover));\n}\n\n/* ========================================\n Visual Config: Hover Transitions\n ======================================== */\n\n.hover-transitions ::ng-deep .ag-row {\n transition: background-color var(--grid-hover-transition, 150ms) ease;\n}\n\n.hover-transitions ::ng-deep .ag-cell {\n transition: background-color var(--grid-hover-transition, 150ms) ease;\n}\n\n/* ========================================\n Visual Config: Cell Padding\n ======================================== */\n\n.cell-padding-compact ::ng-deep .ag-cell {\n padding-left: 8px;\n padding-right: 8px;\n}\n\n.cell-padding-compact ::ng-deep .ag-header-cell {\n padding-left: 8px;\n padding-right: 8px;\n}\n\n.cell-padding-normal ::ng-deep .ag-cell {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n.cell-padding-normal ::ng-deep .ag-header-cell {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n.cell-padding-comfortable ::ng-deep .ag-cell {\n padding-left: 16px;\n padding-right: 16px;\n}\n\n.cell-padding-comfortable ::ng-deep .ag-header-cell {\n padding-left: 16px;\n padding-right: 16px;\n}\n\n/* ========================================\n Visual Config: Checkbox Styles\n ======================================== */\n\n/* Rounded checkbox */\n.checkbox-style-rounded ::ng-deep .ag-checkbox-input-wrapper {\n border-radius: 4px;\n}\n\n.checkbox-style-rounded ::ng-deep .ag-checkbox-input-wrapper::after {\n border-radius: 3px;\n}\n\n/* Filled checkbox */\n.checkbox-style-filled ::ng-deep .ag-checkbox-input-wrapper.ag-checked {\n background-color: var(--grid-checkbox-color, var(--mj-brand-primary));\n border-color: var(--grid-checkbox-color, var(--mj-brand-primary));\n}\n\n.checkbox-style-filled ::ng-deep .ag-checkbox-input-wrapper.ag-checked::after {\n color: var(--mj-text-inverse);\n}\n\n/* ========================================\n Cell Content Formatting\n ======================================== */\n\n/* Right-aligned cells (numbers) */\n::ng-deep .cell-align-right {\n text-align: right;\n justify-content: flex-end;\n}\n\n::ng-deep .header-align-right .ag-header-cell-label {\n justify-content: flex-end;\n}\n\n/* Empty cell placeholder */\n::ng-deep .cell-empty {\n color: var(--mj-text-disabled);\n font-style: normal;\n}\n\n/* Boolean icons */\n::ng-deep .cell-boolean-true {\n color: var(--mj-status-success);\n font-size: 14px;\n}\n\n::ng-deep .cell-boolean-false {\n color: var(--mj-text-disabled);\n font-size: 14px;\n}\n\n/* Clickable links */\n::ng-deep .cell-link {\n color: var(--grid-accent-color, var(--mj-brand-primary));\n text-decoration: none;\n transition: color 0.15s;\n font-size: inherit;\n font-family: inherit;\n line-height: inherit;\n}\n\n::ng-deep .cell-link:hover {\n color: var(--mj-brand-primary);\n text-decoration: underline;\n}\n\n/* Email cells */\n::ng-deep .cell-email {\n font-family: inherit;\n font-size: 13px;\n}\n\n/* Foreign key link cells */\n::ng-deep .cell-fk-link {\n color: var(--grid-accent-color, var(--mj-brand-primary));\n text-decoration: none;\n cursor: pointer;\n transition: color 0.15s, text-decoration 0.15s;\n}\n\n::ng-deep .cell-fk-link:hover {\n color: var(--mj-brand-primary-hover);\n text-decoration: underline;\n}\n\n::ng-deep .cell-fk-link:active {\n color: var(--mj-brand-primary-active);\n}\n\n/* URL cells */\n::ng-deep .cell-url {\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 100%;\n font-size: 13px;\n}\n\n/* Phone cells */\n::ng-deep .cell-phone {\n font-variant-numeric: tabular-nums;\n letter-spacing: 0.3px;\n}\n\n/* ========================================\n Skeleton Loading Animation\n ======================================== */\n\n@keyframes skeleton-shimmer {\n 0% {\n background-position: -200px 0;\n }\n 100% {\n background-position: calc(200px + 100%) 0;\n }\n}\n\n.skeleton-row {\n display: flex;\n height: 40px;\n align-items: center;\n padding: 0 12px;\n border-bottom: 1px solid var(--grid-cell-border-color, var(--mj-border-subtle));\n}\n\n.skeleton-cell {\n height: 16px;\n border-radius: 4px;\n background: linear-gradient(\n 90deg,\n var(--mj-bg-surface-sunken) 0px,\n var(--mj-bg-surface-hover) 40px,\n var(--mj-bg-surface-sunken) 80px\n );\n background-size: 200px 100%;\n animation: skeleton-shimmer 1.5s ease-in-out infinite;\n}\n\n.skeleton-cell-short {\n width: 60px;\n}\n\n.skeleton-cell-medium {\n width: 120px;\n}\n\n.skeleton-cell-long {\n width: 180px;\n}\n\n/* Selection checkbox column header */\n::ng-deep .ag-header-select-all {\n margin-right: 0;\n}\n\n/* ========================================\n Row Border Enhancement\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-row {\n border-bottom: 1px solid var(--grid-cell-border-color, var(--mj-border-subtle));\n}\n\n::ng-deep .ag-theme-alpine .ag-row:last-child {\n border-bottom: none;\n}\n\n/* ========================================\n Focus States\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-cell-focus {\n border: none !important;\n outline: none !important;\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell:focus {\n outline: 2px solid var(--grid-accent-color, var(--mj-brand-primary));\n outline-offset: -2px;\n}\n\n/* ========================================\n Scrollbar Styling\n ======================================== */\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar {\n width: 8px;\n height: 8px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-track {\n background: var(--mj-bg-surface-card);\n border-radius: 4px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-thumb {\n background: var(--mj-border-strong);\n border-radius: 4px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-thumb:hover {\n background: var(--mj-text-disabled);\n}\n\n/* ========================================\n Pinned Column Styling\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-pinned-left-cols-container {\n border-right: 2px solid var(--grid-border-color, var(--mj-border-default));\n}\n\n::ng-deep .ag-theme-alpine .ag-pinned-right-cols-container {\n border-left: 2px solid var(--grid-border-color, var(--mj-border-default));\n}\n\n/* ========================================\n Header Cell Enhancements\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-header-cell {\n font-weight: 600;\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n color: var(--mj-text-secondary);\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell:hover {\n background-color: rgba(0, 0, 0, 0.04);\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell-sortable:hover .ag-header-cell-label {\n color: var(--grid-accent-color, var(--mj-brand-primary));\n}\n\n/* Sort icon animation */\n::ng-deep .ag-theme-alpine .ag-sort-indicator-icon {\n transition: transform 0.2s ease;\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell:hover .ag-sort-indicator-icon {\n transform: scale(1.1);\n}\n\n/* ========================================\n Enhanced Multi-Sort Indicators\n These styles use :host to ensure they're scoped to this component\n and !important to override AG Grid's built-in styles\n ======================================== */\n\n/* Highlight sorted column headers with eye-catching background */\n/* Ascending = blue tint */\n:host ::ng-deep .ag-header-cell-sorted-asc {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface)) !important;\n position: relative;\n}\n\n/* Descending = pink tint */\n:host ::ng-deep .ag-header-cell-sorted-desc {\n background: color-mix(in srgb, var(--mj-status-error) 12%, var(--mj-bg-surface)) !important;\n position: relative;\n}\n\n/* Bottom border accent for sorted columns - ascending = blue */\n:host ::ng-deep .ag-header-cell-sorted-asc::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 3px;\n background: var(--mj-brand-primary);\n}\n\n/* Bottom border accent for sorted columns - descending = pink */\n:host ::ng-deep .ag-header-cell-sorted-desc::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 3px;\n background: var(--mj-status-error);\n}\n\n/* Sorted column header text - ascending = blue, bold */\n:host ::ng-deep .ag-header-cell-sorted-asc .ag-header-cell-text {\n color: var(--mj-brand-primary) !important;\n font-weight: 700 !important;\n}\n\n/* Sorted column header text - descending = pink, bold */\n:host ::ng-deep .ag-header-cell-sorted-desc .ag-header-cell-text {\n color: var(--mj-status-error) !important;\n font-weight: 700 !important;\n}\n\n/* Sort icons - larger and more prominent */\n/* Ascending = blue */\n:host ::ng-deep .ag-sort-ascending-icon {\n color: var(--mj-brand-primary) !important;\n}\n\n/* Descending = pink/magenta */\n:host ::ng-deep .ag-sort-descending-icon {\n color: var(--mj-status-error) !important;\n}\n\n:host ::ng-deep .ag-sort-ascending-icon .ag-icon,\n:host ::ng-deep .ag-sort-descending-icon .ag-icon {\n font-size: 14px !important;\n}\n\n/* Sort order number (1, 2, 3) - show as plain number, no bubble */\n/* This avoids AG Grid bug where .ag-sort-order is always present but empty when unsorted */\n/* The number is only visible when AG Grid populates it (i.e., when column is sorted) */\n/* Default blue for ascending */\n:host ::ng-deep .ag-sort-order {\n margin-left: 4px;\n font-size: 11px !important;\n font-weight: 700 !important;\n color: var(--mj-brand-primary) !important;\n}\n\n/* Sort order number - pink for descending */\n:host ::ng-deep .ag-header-cell-sorted-desc .ag-sort-order {\n color: var(--mj-status-error) !important;\n}\n\n/* Hover state for sortable headers */\n:host ::ng-deep .ag-header-cell-sortable:hover {\n background: rgba(0, 0, 0, 0.04);\n}\n\n:host ::ng-deep .ag-header-cell-sortable:hover .ag-header-cell-text {\n color: var(--mj-brand-primary);\n}\n\n/* ========================================\n Aggregate Summary Row\n ======================================== */\n\n.mj-aggregate-summary {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 16px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-default);\n font-size: 13px;\n min-height: 44px;\n}\n\n.aggregate-summary-content {\n display: flex;\n flex-wrap: wrap;\n gap: 20px;\n align-items: center;\n}\n\n.aggregate-summary-item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);\n}\n\n.aggregate-summary-item i {\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n\n.agg-summary-label {\n color: var(--mj-text-muted);\n font-weight: 500;\n}\n\n.agg-summary-value {\n color: var(--mj-text-primary);\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n}\n\n.aggregate-loading {\n display: flex;\n align-items: center;\n color: var(--mj-text-disabled);\n}\n\n.aggregate-loading i {\n font-size: 14px;\n}\n\n/* ========================================\n AGGREGATE CARDS (displayed above grid)\n ======================================== */\n\n.mj-aggregate-cards {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n padding: 12px 16px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.aggregate-card {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);\n min-width: 140px;\n transition: all 0.15s ease;\n}\n\n.aggregate-card:hover {\n border-color: var(--mj-border-strong);\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);\n}\n\n.aggregate-card-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n border-radius: 10px;\n color: var(--mj-brand-primary);\n font-size: 16px;\n flex-shrink: 0;\n}\n\n.aggregate-card-content {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.aggregate-card-label {\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.aggregate-card-value {\n font-size: 18px;\n font-weight: 700;\n color: var(--mj-text-primary);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n\n.aggregate-card-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 12px;\n color: var(--mj-text-disabled);\n}\n\n.aggregate-card-loading i {\n font-size: 16px;\n}\n\n/* ========================================\n Text Wrapping Styles\n ======================================== */\n\n/* Cell class for wrapped text */\n::ng-deep .cell-wrap-text {\n white-space: normal !important;\n word-wrap: break-word;\n line-height: 1.4;\n padding-top: 8px !important;\n padding-bottom: 8px !important;\n}\n\n/* Ensure AG Grid cells allow wrapping */\n::ng-deep .ag-cell.cell-wrap-text {\n overflow: visible;\n text-overflow: clip;\n}\n\n/* Maintain minimum row height when wrapping */\n::ng-deep .ag-row[style*=\"auto-height\"] {\n min-height: 40px;\n}\n"] }]
|
|
4604
|
+
], template: "<!-- Grid Container -->\n<div\n #gridContainer\n [class]=\"gridContainerClasses.join(' ')\"\n [style.height]=\"gridHeightStyle\">\n\n <!-- Toolbar -->\n @if (ShowToolbar) {\n <div class=\"mj-grid-toolbar\">\n <div class=\"toolbar-left\">\n <!-- Search -->\n @if (ShowSearch) {\n <div class=\"toolbar-search\">\n <i class=\"fa-solid fa-search search-icon\"></i>\n <input\n type=\"text\"\n class=\"search-input\"\n [placeholder]=\"ToolbarConfig.searchPlaceholder || 'Search...'\"\n [value]=\"FilterText\"\n (input)=\"FilterText = $any($event.target).value\" />\n @if (FilterText) {\n <button\n class=\"search-clear\"\n (click)=\"FilterText = ''\"\n aria-label=\"Clear search\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n }\n </div>\n }\n <!-- Custom Left Buttons -->\n @for (button of ToolbarConfig.customButtons; track button) {\n @if (button.position !== 'right' && isButtonVisible(button)) {\n <button\n class=\"toolbar-button\"\n [class]=\"button.cssClass\"\n [disabled]=\"isButtonDisabled(button)\"\n [title]=\"button.tooltip || ''\"\n (click)=\"onToolbarButtonClick(button)\">\n @if (button.icon) {\n <i [class]=\"button.icon\"></i>\n }\n @if (button.text) {\n <span>{{ button.text }}</span>\n }\n </button>\n }\n }\n </div>\n <div class=\"toolbar-center\">\n <!-- Row Count -->\n @if (ToolbarConfig.showRowCount !== false) {\n <span class=\"row-count\">\n {{ totalRowCount }} {{ totalRowCount === 1 ? 'row' : 'rows' }}\n </span>\n }\n <!-- Selection Count -->\n @if (ToolbarConfig.showSelectionCount && SelectedKeys.length > 0) {\n <span class=\"selection-count\">\n ({{ SelectedKeys.length }} selected)\n </span>\n }\n </div>\n <div class=\"toolbar-right\">\n <!-- Recycle Bin chip \u2014 auto-hides when no deleted records, no tracking,\n or user lacks Delete permission. -->\n @if (ShowRecycleBin) {\n <mj-recycle-bin-chip [EntityName]=\"entityInfoName\"></mj-recycle-bin-chip>\n }\n <!-- New/Add Button (predefined) -->\n @if (ShowNewButton) {\n <button\n class=\"toolbar-button\"\n title=\"Create new record\"\n (click)=\"onAddClick()\">\n <i class=\"fa-solid fa-plus\"></i>\n <span class=\"button-text\">New</span>\n </button>\n }\n <!-- Refresh Button (predefined) -->\n @if (ShowRefreshButton) {\n <button\n class=\"toolbar-button\"\n title=\"Refresh data\"\n [disabled]=\"loading\"\n (click)=\"onRefreshClick()\">\n <i class=\"fa-solid fa-arrows-rotate\" [class.fa-spin]=\"loading\"></i>\n <span class=\"button-text\">Refresh</span>\n </button>\n }\n <!-- Export Button (predefined) -->\n @if (ShowExportButton) {\n <button\n class=\"toolbar-button\"\n title=\"Export\"\n [disabled]=\"IsPreparingExport\"\n (click)=\"onExportClick()\">\n <i class=\"fa-solid\" [class.fa-file-excel]=\"!IsPreparingExport\" [class.fa-spinner]=\"IsPreparingExport\" [class.fa-spin]=\"IsPreparingExport\"></i>\n <span class=\"button-text\">Export</span>\n </button>\n }\n <!-- Delete Button (predefined) -->\n @if (ShowDeleteButton && HasSelection) {\n <button\n class=\"toolbar-button toolbar-button-danger\"\n title=\"Delete selected records\"\n (click)=\"onDeleteClick()\">\n <i class=\"fa-solid fa-trash\"></i>\n <span class=\"button-text\">Delete</span>\n </button>\n }\n <!-- Compare Button (predefined) -->\n @if (ShowCompareButton) {\n <button\n class=\"toolbar-button\"\n title=\"Compare selected records\"\n [disabled]=\"!HasMultipleSelection\"\n (click)=\"onCompareClick()\">\n <i class=\"fa-solid fa-code-compare\"></i>\n <span class=\"button-text\">Compare</span>\n </button>\n }\n <!-- Merge Button (predefined) -->\n @if (ShowMergeButton) {\n <button\n class=\"toolbar-button\"\n title=\"Merge selected records\"\n [disabled]=\"!HasMultipleSelection\"\n (click)=\"onMergeClick()\">\n <i class=\"fa-solid fa-code-merge\"></i>\n <span class=\"button-text\">Merge</span>\n </button>\n }\n <!-- Add to List Button (predefined) -->\n @if (ShowAddToListButton) {\n <button\n class=\"toolbar-button\"\n [title]=\"HasSelection ? 'Add selected records to a list' : 'Add all matching records to a list'\"\n [disabled]=\"(!HasSelection && !ExportDataProvider) || IsPreparingAddToList\"\n (click)=\"onAddToListClick()\">\n <i class=\"fa-solid\" [class.fa-list-check]=\"!IsPreparingAddToList\" [class.fa-spinner]=\"IsPreparingAddToList\" [class.fa-spin]=\"IsPreparingAddToList\"></i>\n <span class=\"button-text\">Add to List</span>\n </button>\n }\n <!-- Search for Duplicates Button (predefined) -->\n @if (ShowDuplicateSearchButton) {\n <button\n class=\"toolbar-button\"\n title=\"Search for duplicate records\"\n [disabled]=\"!HasMultipleSelection\"\n (click)=\"onDuplicateSearchClick()\">\n <i class=\"fa-solid fa-magnifying-glass-plus\"></i>\n <span class=\"button-text\">Find Duplicates</span>\n </button>\n }\n <!-- Communication Button (predefined) -->\n @if (ShowCommunicationButton) {\n <button\n class=\"toolbar-button\"\n title=\"Send message to selected records\"\n [disabled]=\"!HasSelection\"\n (click)=\"onCommunicationClick()\">\n <i class=\"fa-solid fa-envelope\"></i>\n <span class=\"button-text\">Send Message</span>\n </button>\n }\n <!-- Legacy ToolbarConfig buttons -->\n <!-- Add Button (legacy) -->\n @if (ToolbarConfig.showAdd && AllowAdd && !ShowNewButton) {\n <button\n class=\"toolbar-button\"\n title=\"Add New\"\n (click)=\"onAddClick()\">\n <i class=\"fa-solid fa-plus\"></i>\n </button>\n }\n <!-- Refresh Button (legacy) -->\n @if (ToolbarConfig.showRefresh !== false && !ShowRefreshButton) {\n <button\n class=\"toolbar-button\"\n title=\"Refresh\"\n [disabled]=\"loading\"\n (click)=\"onRefreshClick()\">\n <i class=\"fa-solid fa-refresh\" [class.fa-spin]=\"loading\"></i>\n </button>\n }\n <!-- Delete Button (legacy) -->\n @if (ToolbarConfig.showDelete && AllowDelete && HasSelection && !ShowDeleteButton) {\n <button\n class=\"toolbar-button toolbar-button-danger\"\n title=\"Delete Selected\"\n (click)=\"onDeleteClick()\">\n <i class=\"fa-solid fa-trash\"></i>\n </button>\n }\n <!-- Export Button (legacy) -->\n @if (ToolbarConfig.showExport && !ShowExportButton) {\n <button\n class=\"toolbar-button\"\n title=\"Export\"\n (click)=\"onExportClick()\">\n <i class=\"fa-solid fa-download\"></i>\n </button>\n }\n <!-- Column Chooser Button -->\n @if (ToolbarConfig.showColumnChooser && AllowColumnToggle) {\n <button\n class=\"toolbar-button\"\n title=\"Column Chooser\"\n (click)=\"onColumnChooserClick()\">\n <i class=\"fa-solid fa-columns\"></i>\n </button>\n }\n <!-- Custom Right Buttons -->\n @for (button of ToolbarConfig.customButtons; track button) {\n @if (button.position === 'right' && isButtonVisible(button)) {\n <button\n class=\"toolbar-button\"\n [class]=\"button.cssClass\"\n [disabled]=\"isButtonDisabled(button)\"\n [title]=\"button.tooltip || ''\"\n (click)=\"onToolbarButtonClick(button)\">\n @if (button.icon) {\n <i [class]=\"button.icon\"></i>\n }\n @if (button.text) {\n <span>{{ button.text }}</span>\n }\n </button>\n }\n }\n <!-- Overflow Menu -->\n @if (hasOverflowMenuItems) {\n <div class=\"toolbar-overflow\" (click)=\"$event.stopPropagation()\">\n <button\n class=\"toolbar-button overflow-trigger\"\n title=\"More actions\"\n (click)=\"toggleOverflowMenu()\">\n <i class=\"fa-solid fa-ellipsis-vertical\"></i>\n </button>\n @if (showOverflowMenu) {\n <div class=\"overflow-menu\" [@fadeIn]>\n <!-- Export (if in overflow) -->\n @if (showExportInOverflow) {\n <button class=\"overflow-item\" (click)=\"onExportClick(); closeOverflowMenu()\">\n <i class=\"fa-solid fa-file-excel\"></i>\n <span>Export</span>\n </button>\n }\n <!-- Entity Actions -->\n @if (ShowEntityActionButtons && EntityActions.length > 0) {\n <div class=\"overflow-divider\"></div>\n <div class=\"overflow-section-label\">Actions</div>\n @for (action of EntityActions; track action) {\n <button\n class=\"overflow-item\"\n [disabled]=\"!isEntityActionEnabled(action)\"\n (click)=\"onEntityActionClick(action); closeOverflowMenu()\">\n <i [class]=\"action.icon || 'fa-solid fa-bolt'\"></i>\n <span>{{ action.name }}</span>\n </button>\n }\n }\n <!-- Column Chooser (if in overflow) -->\n @if (showColumnChooserInOverflow) {\n <div class=\"overflow-divider\"></div>\n }\n @if (showColumnChooserInOverflow) {\n <button class=\"overflow-item\" (click)=\"onColumnChooserClick(); closeOverflowMenu()\">\n <i class=\"fa-solid fa-columns\"></i>\n <span>Manage Columns</span>\n </button>\n }\n <!-- Selection-dependent actions in overflow -->\n @if (HasSelection && hasSelectionDependentOverflowActions) {\n <div class=\"overflow-divider\"></div>\n @if (showCommunicationInOverflow) {\n <button class=\"overflow-item\" (click)=\"onCommunicationClick(); closeOverflowMenu()\">\n <i class=\"fa-solid fa-envelope\"></i>\n <span>Send Message</span>\n </button>\n }\n }\n </div>\n }\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Aggregate Cards (displayed as summary cards above the grid) -->\n @if (ShowAggregatePanel) {\n <div class=\"mj-aggregate-cards\">\n @for (agg of CardAggregates; track agg) {\n <div class=\"aggregate-card\">\n @if (agg.icon) {\n <div class=\"aggregate-card-icon\">\n <i [class]=\"agg.icon\"></i>\n </div>\n }\n <div class=\"aggregate-card-content\">\n <span class=\"aggregate-card-label\">{{ agg.label }}</span>\n <span class=\"aggregate-card-value\">{{ getAggregateValue(agg) }}</span>\n </div>\n </div>\n }\n @if (AggregatesLoading) {\n <div class=\"aggregate-card-loading\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n </div>\n }\n </div>\n }\n\n <!-- Grid Content -->\n <div class=\"mj-grid-content\">\n <!-- Loading Overlay -->\n @if (loading && rowData.length === 0) {\n <div class=\"mj-grid-loading-overlay\">\n <mj-loading text=\"Loading...\"></mj-loading>\n </div>\n }\n\n <!-- Error State -->\n @if (errorMessage && !loading) {\n <mj-empty-state\n class=\"mj-grid-state-fill\"\n Variant=\"error\"\n Icon=\"fa-solid fa-triangle-exclamation\"\n Title=\"Could not load data\"\n [Message]=\"errorMessage\"\n ActionText=\"Retry\"\n ActionIcon=\"fa-solid fa-arrows-rotate\"\n (Action)=\"Refresh()\" />\n }\n\n <!-- Empty State -->\n @if (!loading && !errorMessage && rowData.length === 0) {\n <mj-empty-state\n class=\"mj-grid-state-fill\"\n Icon=\"fa-solid fa-inbox\"\n Title=\"No data to display\" />\n }\n\n <!-- AG Grid (Client-side mode) -->\n @if (!errorMessage && PaginationMode === 'client' && (rowData.length > 0 || loading)) {\n <ag-grid-angular\n class=\"mj-ag-grid ag-theme-alpine\"\n [theme]=\"agGridTheme\"\n [columnDefs]=\"agColumnDefs\"\n [rowData]=\"rowData\"\n [defaultColDef]=\"defaultColDef\"\n [rowSelection]=\"agRowSelection\"\n [getRowId]=\"getRowId\"\n [suppressCellFocus]=\"true\"\n [rowHeight]=\"RowHeight\"\n [headerHeight]=\"ShowHeader ? undefined : 0\"\n (gridReady)=\"onGridReady($event)\"\n (cellClicked)=\"onAgCellClicked($event)\"\n (rowClicked)=\"onAgRowClicked($event)\"\n (rowDoubleClicked)=\"onAgRowDoubleClicked($event)\"\n (sortChanged)=\"onAgSortChanged($event)\"\n (selectionChanged)=\"onAgSelectionChanged($event)\"\n (columnResized)=\"onAgColumnResized($event)\"\n (columnMoved)=\"onAgColumnMoved($event)\">\n </ag-grid-angular>\n }\n\n <!-- AG Grid (Infinite Scroll mode) -->\n @if (!errorMessage && PaginationMode === 'infinite') {\n <ag-grid-angular\n class=\"mj-ag-grid ag-theme-alpine\"\n [theme]=\"agGridTheme\"\n [columnDefs]=\"agColumnDefs\"\n [defaultColDef]=\"defaultColDef\"\n [rowSelection]=\"agRowSelection\"\n [getRowId]=\"getRowId\"\n [suppressCellFocus]=\"true\"\n [rowHeight]=\"RowHeight\"\n [headerHeight]=\"ShowHeader ? undefined : 0\"\n [rowModelType]=\"'infinite'\"\n [cacheBlockSize]=\"CacheBlockSize\"\n [maxBlocksInCache]=\"MaxBlocksInCache\"\n [infiniteInitialRowCount]=\"1\"\n [cacheOverflowSize]=\"2\"\n (gridReady)=\"onGridReady($event)\"\n (cellClicked)=\"onAgCellClicked($event)\"\n (rowClicked)=\"onAgRowClicked($event)\"\n (rowDoubleClicked)=\"onAgRowDoubleClicked($event)\"\n (sortChanged)=\"onAgSortChanged($event)\"\n (selectionChanged)=\"onAgSelectionChanged($event)\"\n (columnResized)=\"onAgColumnResized($event)\"\n (columnMoved)=\"onAgColumnMoved($event)\">\n </ag-grid-angular>\n }\n\n </div>\n\n <!-- Pagination (opt-in via ShowPager input) -->\n @if (ShowPager) {\n <mj-pagination\n [TotalRowCount]=\"totalRowCount\"\n [PageNumber]=\"PagerPageNumber\"\n [PageSize]=\"PageSize\"\n [IsLoading]=\"loading\"\n (PageChange)=\"PageChange.emit($event)\">\n </mj-pagination>\n }\n\n <!-- Aggregate Summary Row (outside mj-grid-content so it's always visible) -->\n @if (ShowAggregateSummary) {\n <div class=\"mj-aggregate-summary\">\n <div class=\"aggregate-summary-content\">\n @for (agg of ColumnAggregates; track agg) {\n <div class=\"aggregate-summary-item\">\n @if (agg.icon) {\n <i [class]=\"agg.icon\"></i>\n }\n <span class=\"agg-summary-label\">{{ agg.label }}:</span>\n <span class=\"agg-summary-value\">{{ getAggregateValue(agg) }}</span>\n </div>\n }\n </div>\n @if (AggregatesLoading) {\n <div class=\"aggregate-loading\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n </div>\n }\n </div>\n }\n</div>\n\n<!-- Export Dialog -->\n<mj-export-dialog\n [visible]=\"showExportDialog\"\n [config]=\"exportDialogConfig\"\n (closed)=\"onExportDialogClosed($event)\">\n</mj-export-dialog>\n\n<!-- Runtime-UX driver for an entity action that names a RuntimeUXDriverClass (e.g. the Record Process\n bulk-update runner). Mounted in-place over the grid \u2014 no parent wiring required. -->\n@if (ActiveRuntimeDriver) {\n <mj-entity-action-ux-host\n [DriverClass]=\"ActiveRuntimeDriver.DriverClass\"\n [Context]=\"ActiveRuntimeDriver.Context\"\n (Completed)=\"OnRuntimeDriverCompleted($event)\"\n (Cancelled)=\"OnRuntimeDriverCancelled()\">\n </mj-entity-action-ux-host>\n}\n", styles: ["/* ========================================\n CSS Custom Properties (Theme Variables)\n ======================================== */\n\n:host {\n /* Grid container */\n --grid-border-color: var(--mj-border-default);\n --grid-border-radius: 0px;\n --grid-background: var(--mj-bg-surface);\n\n /* Header */\n --grid-header-bg: var(--mj-bg-surface-card);\n --grid-header-text: var(--mj-text-primary);\n --grid-header-font-weight: 600;\n --grid-header-height: 40px;\n --grid-header-border-color: var(--mj-border-default);\n\n /* Rows */\n --grid-row-height: 40px;\n --grid-row-bg: var(--mj-bg-surface);\n --grid-row-bg-alt: var(--mj-bg-surface-card);\n --grid-row-hover-bg: var(--mj-bg-surface-hover);\n --grid-row-selected-bg: color-mix(in srgb, var(--mj-status-warning) 10%, var(--mj-bg-surface));\n --grid-row-selected-hover-bg: color-mix(in srgb, var(--mj-status-warning) 18%, var(--mj-bg-surface));\n\n /* Cells */\n --grid-cell-padding: 8px 12px;\n --grid-cell-text: var(--mj-text-primary);\n --grid-cell-border-color: var(--mj-border-subtle);\n\n /* Selection - mellow yellow to avoid conflict with blue hyperlinks */\n --grid-checkbox-color: var(--mj-brand-primary);\n --grid-selection-indicator-color: var(--mj-status-warning);\n\n /* Editing */\n --grid-edit-cell-bg: var(--mj-bg-surface);\n --grid-edit-cell-border: var(--mj-brand-primary);\n --grid-edit-cell-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n\n /* Sorting */\n --grid-sort-indicator-color: var(--mj-brand-primary);\n\n /* Toolbar */\n --grid-toolbar-bg: var(--mj-bg-surface);\n --grid-toolbar-height: 48px;\n --grid-toolbar-border-color: var(--mj-border-default);\n\n /* Loading */\n --grid-loading-overlay-bg: color-mix(in srgb, var(--mj-bg-surface) 80%, transparent);\n\n /* Empty state */\n --grid-empty-text-color: var(--mj-text-disabled);\n --grid-empty-icon-color: var(--mj-border-strong);\n\n display: block;\n height: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;\n}\n\n/* ========================================\n Grid Container\n ======================================== */\n\n.mj-grid-container {\n display: flex;\n flex-direction: column;\n border: 1px solid var(--grid-border-color);\n border-radius: var(--grid-border-radius);\n background: var(--grid-background);\n overflow: hidden;\n}\n\n/* ========================================\n Toolbar\n ======================================== */\n\n.mj-grid-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n min-height: var(--grid-toolbar-height);\n padding: 0 12px;\n background: var(--grid-toolbar-bg);\n border-bottom: 1px solid var(--grid-toolbar-border-color);\n gap: 12px;\n}\n\n.toolbar-left,\n.toolbar-right {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.toolbar-center {\n display: flex;\n align-items: center;\n gap: 8px;\n color: var(--mj-text-secondary);\n font-size: 13px;\n}\n\n.toolbar-search {\n display: flex;\n align-items: center;\n position: relative;\n}\n\n.search-icon {\n position: absolute;\n left: 10px;\n color: var(--mj-text-disabled);\n font-size: 13px;\n}\n\n.search-input {\n padding: 6px 30px 6px 32px;\n border: 1px solid var(--mj-border-strong);\n border-radius: 4px;\n font-size: 13px;\n width: 200px;\n transition: border-color 0.2s, box-shadow 0.2s;\n}\n\n.search-input:focus {\n outline: none;\n border-color: var(--grid-selection-indicator-color);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n.search-clear {\n position: absolute;\n right: 6px;\n background: none;\n border: none;\n cursor: pointer;\n padding: 4px;\n color: var(--mj-text-disabled);\n font-size: 12px;\n}\n\n.search-clear:hover {\n color: var(--mj-text-secondary);\n}\n\n.toolbar-button {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-strong);\n border-radius: 4px;\n cursor: pointer;\n font-size: 13px;\n color: var(--mj-text-primary);\n transition: background-color 0.2s, border-color 0.2s;\n}\n\n.toolbar-button:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-strong);\n}\n\n.toolbar-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.toolbar-button i {\n font-size: 14px;\n}\n\n.toolbar-button-danger {\n color: var(--mj-status-error);\n}\n\n.toolbar-button-danger:hover:not(:disabled) {\n background: var(--mj-status-error-bg);\n border-color: var(--mj-status-error);\n}\n\n.row-count,\n.selection-count {\n font-weight: 500;\n}\n\n/* ========================================\n Grid Content\n ======================================== */\n\n.mj-grid-content {\n flex: 1;\n position: relative;\n overflow: hidden;\n}\n\n.mj-grid-scroll-container {\n height: 100%;\n overflow: auto;\n}\n\n/* ========================================\n Loading Overlay\n ======================================== */\n\n.mj-grid-loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--grid-loading-overlay-bg);\n z-index: 10;\n}\n\n/* ========================================\n Error State\n ======================================== */\n\n/* ========================================\n Empty / Error State\n ======================================== */\n\n/* Flex-fill so <mj-empty-state> placeholders occupy the grid content area\n (the host centers its content but does not grow to fill the parent). */\n.mj-grid-state-fill {\n flex: 1 1 auto;\n}\n\n/* ========================================\n Header Row\n ======================================== */\n\n.mj-grid-header {\n display: flex;\n min-height: var(--grid-header-height);\n background: var(--grid-header-bg);\n border-bottom: 2px solid var(--grid-header-border-color);\n position: sticky;\n top: 0;\n z-index: 5;\n}\n\n.mj-grid-header-cell {\n display: flex;\n align-items: center;\n padding: var(--grid-cell-padding);\n font-weight: var(--grid-header-font-weight);\n color: var(--grid-header-text);\n font-size: 13px;\n user-select: none;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n border-right: 1px solid var(--grid-cell-border-color);\n flex-shrink: 0;\n}\n\n.mj-grid-header-cell:last-child {\n border-right: none;\n}\n\n.mj-grid-header-cell.sortable {\n cursor: pointer;\n}\n\n.mj-grid-header-cell.sortable:hover {\n background: rgba(0, 0, 0, 0.04);\n}\n\n.header-text {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.sort-indicator {\n display: flex;\n align-items: center;\n margin-left: 6px;\n color: var(--grid-sort-indicator-color);\n}\n\n.sort-indicator i {\n font-size: 12px;\n}\n\n.sort-index {\n font-size: 10px;\n margin-left: 2px;\n font-weight: normal;\n}\n\n/* ========================================\n Data Rows\n ======================================== */\n\n.mj-grid-row {\n display: flex;\n align-items: stretch;\n background: var(--grid-row-bg);\n transition: background-color 0.15s;\n cursor: default;\n}\n\n.mj-grid-row:hover {\n background: var(--grid-row-hover-bg);\n}\n\n.mj-grid-row.grid-row-alt {\n background: var(--grid-row-bg-alt);\n}\n\n.mj-grid-row.grid-row-alt:hover {\n background: var(--grid-row-hover-bg);\n}\n\n.mj-grid-row.grid-row-selected {\n background: var(--grid-row-selected-bg);\n}\n\n.mj-grid-row.grid-row-selected:hover {\n background: var(--grid-row-selected-hover-bg);\n}\n\n.mj-grid-row.grid-row-editing {\n background: color-mix(in srgb, var(--mj-status-warning) 8%, var(--mj-bg-surface));\n}\n\n.mj-grid-row.grid-row-dirty {\n border-left: 3px solid var(--mj-status-warning);\n}\n\n/* ========================================\n Data Cells\n ======================================== */\n\n.mj-grid-cell {\n display: flex;\n align-items: center;\n padding: var(--grid-cell-padding);\n color: var(--grid-cell-text);\n font-size: 13px;\n overflow: hidden;\n border-right: 1px solid transparent;\n flex-shrink: 0;\n}\n\n.mj-grid-cell:last-child {\n border-right: none;\n}\n\n/* Grid lines modes */\n.grid-lines-horizontal .mj-grid-row {\n border-bottom: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-vertical .mj-grid-cell {\n border-right: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-both .mj-grid-row {\n border-bottom: 1px solid var(--grid-cell-border-color);\n}\n\n.grid-lines-both .mj-grid-cell {\n border-right: 1px solid var(--grid-cell-border-color);\n}\n\n/* Cell alignment */\n.mj-grid-cell.align-left {\n justify-content: flex-start;\n}\n\n.mj-grid-cell.align-center {\n justify-content: center;\n}\n\n.mj-grid-cell.align-right {\n justify-content: flex-end;\n}\n\n.cell-content {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Special cells */\n.row-number-cell {\n width: 50px;\n min-width: 50px;\n max-width: 50px;\n justify-content: center;\n color: var(--mj-text-disabled);\n font-size: 12px;\n background: var(--grid-header-bg);\n}\n\n.checkbox-cell {\n width: 40px;\n min-width: 40px;\n max-width: 40px;\n justify-content: center;\n}\n\n.checkbox-cell input[type=\"checkbox\"] {\n width: 16px;\n height: 16px;\n cursor: pointer;\n accent-color: var(--grid-checkbox-color);\n}\n\n/* ========================================\n Virtual Scrolling\n ======================================== */\n\n.mj-grid-virtual-spacer {\n flex-shrink: 0;\n}\n\n/* ========================================\n Responsive Adjustments\n ======================================== */\n\n@media (max-width: 768px) {\n .mj-grid-toolbar {\n flex-wrap: wrap;\n padding: 8px;\n }\n\n .toolbar-search {\n order: 3;\n width: 100%;\n margin-top: 8px;\n }\n\n .search-input {\n width: 100%;\n }\n\n .toolbar-center {\n order: 2;\n }\n\n /* Hide button text on mobile */\n .toolbar-button .button-text {\n display: none;\n }\n}\n\n/* ========================================\n Toolbar Button Text\n ======================================== */\n\n.toolbar-button .button-text {\n font-size: 13px;\n}\n\n/* ========================================\n Overflow Menu\n ======================================== */\n\n.toolbar-overflow {\n position: relative;\n}\n\n.overflow-trigger {\n padding: 6px 8px !important;\n}\n\n.overflow-trigger i {\n font-size: 16px;\n}\n\n.overflow-menu {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 4px;\n min-width: 220px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.08);\n z-index: 1000;\n overflow: hidden;\n}\n\n.overflow-item {\n display: flex;\n align-items: center;\n width: 100%;\n padding: 10px 16px;\n border: none;\n background: none;\n cursor: pointer;\n font-size: 14px;\n color: var(--mj-text-primary);\n text-align: left;\n gap: 12px;\n transition: background-color 0.15s;\n}\n\n.overflow-item:hover:not(:disabled) {\n background: var(--mj-bg-surface-card);\n}\n\n.overflow-item:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.overflow-item i {\n width: 18px;\n font-size: 14px;\n color: var(--mj-text-secondary);\n text-align: center;\n}\n\n.overflow-item span {\n flex: 1;\n}\n\n.overflow-divider {\n height: 1px;\n background: var(--mj-border-default);\n margin: 4px 0;\n}\n\n.overflow-section-label {\n padding: 8px 16px 4px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n color: var(--mj-text-disabled);\n letter-spacing: 0.5px;\n}\n\n/* Entity Actions submenu styling */\n.overflow-item.action-item i {\n color: var(--mj-brand-primary);\n}\n\n/* ========================================\n Highlight Matches\n ======================================== */\n\n::ng-deep .highlight-match {\n background-color: color-mix(in srgb, var(--mj-status-warning) 40%, var(--mj-bg-surface));\n border-radius: 2px;\n padding: 0 1px;\n}\n\n/* ========================================\n AG Grid Customizations\n ======================================== */\n\n.mj-ag-grid {\n width: 100%;\n height: 100%;\n}\n\n/* AG Grid v35 uses JS-based theming (colorSchemeVariable) \u2014 no CSS variable overrides needed */\n\n/* Selected row styling - left indicator bar only, background handled by AG Grid theme */\n::ng-deep .ag-row-selected {\n box-shadow: inset 4px 0 0 0 var(--mj-status-warning);\n}\n\n/* Selection checkbox styling */\n::ng-deep .ag-checkbox-input-wrapper {\n width: 18px;\n height: 18px;\n}\n\n::ng-deep .ag-checkbox-input-wrapper.ag-checked::after {\n color: var(--grid-checkbox-color, var(--mj-brand-primary));\n}\n\n/* ========================================\n Visual Config: Header Styles\n ======================================== */\n\n/* Flat header (minimal) */\n.header-style-flat ::ng-deep .ag-header {\n background: var(--grid-header-bg, var(--mj-bg-surface-card));\n border-bottom: 1px solid var(--grid-header-border-color, var(--mj-border-default));\n}\n\n/* Elevated header (default - subtle shadow) */\n.header-style-elevated ::ng-deep .ag-header {\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--grid-header-border-color, var(--mj-border-default));\n}\n\n.header-style-elevated.header-shadow ::ng-deep .ag-header {\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.06);\n}\n\n/* Gradient header (more prominent) */\n.header-style-gradient ::ng-deep .ag-header {\n background: var(--mj-bg-surface-sunken);\n border-bottom: none;\n}\n\n.header-style-gradient.header-shadow ::ng-deep .ag-header {\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n}\n\n/* Bold header (high contrast) */\n.header-style-bold ::ng-deep .ag-header {\n background: var(--mj-text-secondary);\n border-bottom: none;\n}\n\n.header-style-bold ::ng-deep .ag-header-cell-text {\n color: var(--mj-text-inverse);\n font-weight: 600;\n}\n\n.header-style-bold ::ng-deep .ag-header-icon {\n color: color-mix(in srgb, var(--mj-text-inverse) 70%, transparent);\n}\n\n.header-style-bold ::ng-deep .ag-header-cell:hover {\n background: color-mix(in srgb, var(--mj-text-inverse) 10%, transparent);\n}\n\n/* Header sort icons enhancement */\n::ng-deep .ag-header-cell-sorted-asc .ag-icon-asc,\n::ng-deep .ag-header-cell-sorted-desc .ag-icon-desc {\n color: var(--grid-accent-color, var(--grid-sort-indicator-color, var(--mj-brand-primary)));\n}\n\n/* ========================================\n Visual Config: Zebra Striping\n ======================================== */\n\n/* Subtle contrast */\n.alternate-rows-subtle ::ng-deep .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.015);\n}\n\n.alternate-rows-subtle ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, var(--mj-bg-surface-hover));\n}\n\n/* Medium contrast (default) */\n.alternate-rows-medium ::ng-deep .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.025);\n}\n\n.alternate-rows-medium ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, var(--mj-bg-surface-hover));\n}\n\n/* Strong contrast */\n.alternate-rows-strong ::ng-deep .ag-row-odd {\n background-color: rgba(0, 0, 0, 0.04);\n}\n\n.alternate-rows-strong ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--grid-row-hover-bg, var(--mj-bg-surface-hover));\n}\n\n/* ========================================\n Visual Config: Hover Transitions\n ======================================== */\n\n.hover-transitions ::ng-deep .ag-row {\n transition: background-color var(--grid-hover-transition, 150ms) ease;\n}\n\n.hover-transitions ::ng-deep .ag-cell {\n transition: background-color var(--grid-hover-transition, 150ms) ease;\n}\n\n/* ========================================\n Visual Config: Cell Padding\n ======================================== */\n\n.cell-padding-compact ::ng-deep .ag-cell {\n padding-left: 8px;\n padding-right: 8px;\n}\n\n.cell-padding-compact ::ng-deep .ag-header-cell {\n padding-left: 8px;\n padding-right: 8px;\n}\n\n.cell-padding-normal ::ng-deep .ag-cell {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n.cell-padding-normal ::ng-deep .ag-header-cell {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n.cell-padding-comfortable ::ng-deep .ag-cell {\n padding-left: 16px;\n padding-right: 16px;\n}\n\n.cell-padding-comfortable ::ng-deep .ag-header-cell {\n padding-left: 16px;\n padding-right: 16px;\n}\n\n/* ========================================\n Visual Config: Checkbox Styles\n ======================================== */\n\n/* Rounded checkbox */\n.checkbox-style-rounded ::ng-deep .ag-checkbox-input-wrapper {\n border-radius: 4px;\n}\n\n.checkbox-style-rounded ::ng-deep .ag-checkbox-input-wrapper::after {\n border-radius: 3px;\n}\n\n/* Filled checkbox */\n.checkbox-style-filled ::ng-deep .ag-checkbox-input-wrapper.ag-checked {\n background-color: var(--grid-checkbox-color, var(--mj-brand-primary));\n border-color: var(--grid-checkbox-color, var(--mj-brand-primary));\n}\n\n.checkbox-style-filled ::ng-deep .ag-checkbox-input-wrapper.ag-checked::after {\n color: var(--mj-text-inverse);\n}\n\n/* ========================================\n Cell Content Formatting\n ======================================== */\n\n/* Right-aligned cells (numbers) */\n::ng-deep .cell-align-right {\n text-align: right;\n justify-content: flex-end;\n}\n\n::ng-deep .header-align-right .ag-header-cell-label {\n justify-content: flex-end;\n}\n\n/* Empty cell placeholder */\n::ng-deep .cell-empty {\n color: var(--mj-text-disabled);\n font-style: normal;\n}\n\n/* Boolean icons */\n::ng-deep .cell-boolean-true {\n color: var(--mj-status-success);\n font-size: 14px;\n}\n\n::ng-deep .cell-boolean-false {\n color: var(--mj-text-disabled);\n font-size: 14px;\n}\n\n/* Clickable links */\n::ng-deep .cell-link {\n color: var(--grid-accent-color, var(--mj-brand-primary));\n text-decoration: none;\n transition: color 0.15s;\n font-size: inherit;\n font-family: inherit;\n line-height: inherit;\n}\n\n::ng-deep .cell-link:hover {\n color: var(--mj-brand-primary);\n text-decoration: underline;\n}\n\n/* Email cells */\n::ng-deep .cell-email {\n font-family: inherit;\n font-size: 13px;\n}\n\n/* Foreign key link cells */\n::ng-deep .cell-fk-link {\n color: var(--grid-accent-color, var(--mj-brand-primary));\n text-decoration: none;\n cursor: pointer;\n transition: color 0.15s, text-decoration 0.15s;\n}\n\n::ng-deep .cell-fk-link:hover {\n color: var(--mj-brand-primary-hover);\n text-decoration: underline;\n}\n\n::ng-deep .cell-fk-link:active {\n color: var(--mj-brand-primary-active);\n}\n\n/* URL cells */\n::ng-deep .cell-url {\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 100%;\n font-size: 13px;\n}\n\n/* Phone cells */\n::ng-deep .cell-phone {\n font-variant-numeric: tabular-nums;\n letter-spacing: 0.3px;\n}\n\n/* ========================================\n Skeleton Loading Animation\n ======================================== */\n\n@keyframes skeleton-shimmer {\n 0% {\n background-position: -200px 0;\n }\n 100% {\n background-position: calc(200px + 100%) 0;\n }\n}\n\n.skeleton-row {\n display: flex;\n height: 40px;\n align-items: center;\n padding: 0 12px;\n border-bottom: 1px solid var(--grid-cell-border-color, var(--mj-border-subtle));\n}\n\n.skeleton-cell {\n height: 16px;\n border-radius: 4px;\n background: linear-gradient(\n 90deg,\n var(--mj-bg-surface-sunken) 0px,\n var(--mj-bg-surface-hover) 40px,\n var(--mj-bg-surface-sunken) 80px\n );\n background-size: 200px 100%;\n animation: skeleton-shimmer 1.5s ease-in-out infinite;\n}\n\n.skeleton-cell-short {\n width: 60px;\n}\n\n.skeleton-cell-medium {\n width: 120px;\n}\n\n.skeleton-cell-long {\n width: 180px;\n}\n\n/* Selection checkbox column header */\n::ng-deep .ag-header-select-all {\n margin-right: 0;\n}\n\n/* ========================================\n Row Border Enhancement\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-row {\n border-bottom: 1px solid var(--grid-cell-border-color, var(--mj-border-subtle));\n}\n\n::ng-deep .ag-theme-alpine .ag-row:last-child {\n border-bottom: none;\n}\n\n/* ========================================\n Focus States\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-cell-focus {\n border: none !important;\n outline: none !important;\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell:focus {\n outline: 2px solid var(--grid-accent-color, var(--mj-brand-primary));\n outline-offset: -2px;\n}\n\n/* ========================================\n Scrollbar Styling\n ======================================== */\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar {\n width: 8px;\n height: 8px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-track {\n background: var(--mj-bg-surface-card);\n border-radius: 4px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-thumb {\n background: var(--mj-border-strong);\n border-radius: 4px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-thumb:hover {\n background: var(--mj-text-disabled);\n}\n\n/* ========================================\n Pinned Column Styling\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-pinned-left-cols-container {\n border-right: 2px solid var(--grid-border-color, var(--mj-border-default));\n}\n\n::ng-deep .ag-theme-alpine .ag-pinned-right-cols-container {\n border-left: 2px solid var(--grid-border-color, var(--mj-border-default));\n}\n\n/* ========================================\n Header Cell Enhancements\n ======================================== */\n\n::ng-deep .ag-theme-alpine .ag-header-cell {\n font-weight: 600;\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n color: var(--mj-text-secondary);\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell:hover {\n background-color: rgba(0, 0, 0, 0.04);\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell-sortable:hover .ag-header-cell-label {\n color: var(--grid-accent-color, var(--mj-brand-primary));\n}\n\n/* Sort icon animation */\n::ng-deep .ag-theme-alpine .ag-sort-indicator-icon {\n transition: transform 0.2s ease;\n}\n\n::ng-deep .ag-theme-alpine .ag-header-cell:hover .ag-sort-indicator-icon {\n transform: scale(1.1);\n}\n\n/* ========================================\n Enhanced Multi-Sort Indicators\n These styles use :host to ensure they're scoped to this component\n and !important to override AG Grid's built-in styles\n ======================================== */\n\n/* Highlight sorted column headers with eye-catching background */\n/* Ascending = blue tint */\n:host ::ng-deep .ag-header-cell-sorted-asc {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface)) !important;\n position: relative;\n}\n\n/* Descending = pink tint */\n:host ::ng-deep .ag-header-cell-sorted-desc {\n background: color-mix(in srgb, var(--mj-status-error) 12%, var(--mj-bg-surface)) !important;\n position: relative;\n}\n\n/* Bottom border accent for sorted columns - ascending = blue */\n:host ::ng-deep .ag-header-cell-sorted-asc::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 3px;\n background: var(--mj-brand-primary);\n}\n\n/* Bottom border accent for sorted columns - descending = pink */\n:host ::ng-deep .ag-header-cell-sorted-desc::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 3px;\n background: var(--mj-status-error);\n}\n\n/* Sorted column header text - ascending = blue, bold */\n:host ::ng-deep .ag-header-cell-sorted-asc .ag-header-cell-text {\n color: var(--mj-brand-primary) !important;\n font-weight: 700 !important;\n}\n\n/* Sorted column header text - descending = pink, bold */\n:host ::ng-deep .ag-header-cell-sorted-desc .ag-header-cell-text {\n color: var(--mj-status-error) !important;\n font-weight: 700 !important;\n}\n\n/* Sort icons - larger and more prominent */\n/* Ascending = blue */\n:host ::ng-deep .ag-sort-ascending-icon {\n color: var(--mj-brand-primary) !important;\n}\n\n/* Descending = pink/magenta */\n:host ::ng-deep .ag-sort-descending-icon {\n color: var(--mj-status-error) !important;\n}\n\n:host ::ng-deep .ag-sort-ascending-icon .ag-icon,\n:host ::ng-deep .ag-sort-descending-icon .ag-icon {\n font-size: 14px !important;\n}\n\n/* Sort order number (1, 2, 3) - show as plain number, no bubble */\n/* This avoids AG Grid bug where .ag-sort-order is always present but empty when unsorted */\n/* The number is only visible when AG Grid populates it (i.e., when column is sorted) */\n/* Default blue for ascending */\n:host ::ng-deep .ag-sort-order {\n margin-left: 4px;\n font-size: 11px !important;\n font-weight: 700 !important;\n color: var(--mj-brand-primary) !important;\n}\n\n/* Sort order number - pink for descending */\n:host ::ng-deep .ag-header-cell-sorted-desc .ag-sort-order {\n color: var(--mj-status-error) !important;\n}\n\n/* Hover state for sortable headers */\n:host ::ng-deep .ag-header-cell-sortable:hover {\n background: rgba(0, 0, 0, 0.04);\n}\n\n:host ::ng-deep .ag-header-cell-sortable:hover .ag-header-cell-text {\n color: var(--mj-brand-primary);\n}\n\n/* ========================================\n Aggregate Summary Row\n ======================================== */\n\n.mj-aggregate-summary {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 16px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-default);\n font-size: 13px;\n min-height: 44px;\n}\n\n.aggregate-summary-content {\n display: flex;\n flex-wrap: wrap;\n gap: 20px;\n align-items: center;\n}\n\n.aggregate-summary-item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 12px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);\n}\n\n.aggregate-summary-item i {\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n\n.agg-summary-label {\n color: var(--mj-text-muted);\n font-weight: 500;\n}\n\n.agg-summary-value {\n color: var(--mj-text-primary);\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n}\n\n.aggregate-loading {\n display: flex;\n align-items: center;\n color: var(--mj-text-disabled);\n}\n\n.aggregate-loading i {\n font-size: 14px;\n}\n\n/* ========================================\n AGGREGATE CARDS (displayed above grid)\n ======================================== */\n\n.mj-aggregate-cards {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n padding: 12px 16px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.aggregate-card {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);\n min-width: 140px;\n transition: all 0.15s ease;\n}\n\n.aggregate-card:hover {\n border-color: var(--mj-border-strong);\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);\n}\n\n.aggregate-card-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n border-radius: 10px;\n color: var(--mj-brand-primary);\n font-size: 16px;\n flex-shrink: 0;\n}\n\n.aggregate-card-content {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.aggregate-card-label {\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.aggregate-card-value {\n font-size: 18px;\n font-weight: 700;\n color: var(--mj-text-primary);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n\n.aggregate-card-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 12px;\n color: var(--mj-text-disabled);\n}\n\n.aggregate-card-loading i {\n font-size: 16px;\n}\n\n/* ========================================\n Text Wrapping Styles\n ======================================== */\n\n/* Cell class for wrapped text */\n::ng-deep .cell-wrap-text {\n white-space: normal !important;\n word-wrap: break-word;\n line-height: 1.4;\n padding-top: 8px !important;\n padding-bottom: 8px !important;\n}\n\n/* Ensure AG Grid cells allow wrapping */\n::ng-deep .ag-cell.cell-wrap-text {\n overflow: visible;\n text-overflow: clip;\n}\n\n/* Maintain minimum row height when wrapping */\n::ng-deep .ag-row[style*=\"auto-height\"] {\n min-height: 40px;\n}\n"] }]
|
|
4520
4605
|
}], () => [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i1.ExportService }, { type: i0.NgZone }], { Params: [{
|
|
4521
4606
|
type: Input
|
|
4522
4607
|
}], AllowLoad: [{
|
|
@@ -4533,6 +4618,8 @@ export class EntityDataGridComponent extends BaseAngularComponent {
|
|
|
4533
4618
|
type: Input
|
|
4534
4619
|
}], ShowPager: [{
|
|
4535
4620
|
type: Input
|
|
4621
|
+
}], ExportDataProvider: [{
|
|
4622
|
+
type: Input
|
|
4536
4623
|
}], ShowRecycleBin: [{
|
|
4537
4624
|
type: Input
|
|
4538
4625
|
}], PagerPageNumber: [{
|