@memberjunction/ng-entity-viewer 5.23.0 → 5.25.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.
Files changed (42) hide show
  1. package/dist/lib/aggregate-panel/aggregate-panel.component.d.ts +1 -1
  2. package/dist/lib/aggregate-panel/aggregate-panel.component.d.ts.map +1 -1
  3. package/dist/lib/aggregate-panel/aggregate-panel.component.js.map +1 -1
  4. package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.d.ts +3 -1
  5. package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.d.ts.map +1 -1
  6. package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.js +1 -1
  7. package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.js.map +1 -1
  8. package/dist/lib/entity-cards/entity-cards.component.d.ts +10 -1
  9. package/dist/lib/entity-cards/entity-cards.component.d.ts.map +1 -1
  10. package/dist/lib/entity-cards/entity-cards.component.js +43 -20
  11. package/dist/lib/entity-cards/entity-cards.component.js.map +1 -1
  12. package/dist/lib/entity-data-grid/entity-data-grid.component.d.ts +1 -1
  13. package/dist/lib/entity-data-grid/entity-data-grid.component.d.ts.map +1 -1
  14. package/dist/lib/entity-data-grid/entity-data-grid.component.js +12 -17
  15. package/dist/lib/entity-data-grid/entity-data-grid.component.js.map +1 -1
  16. package/dist/lib/entity-data-grid/models/grid-types.js +3 -0
  17. package/dist/lib/entity-data-grid/models/grid-types.js.map +1 -1
  18. package/dist/lib/entity-viewer/entity-viewer.component.d.ts +43 -2
  19. package/dist/lib/entity-viewer/entity-viewer.component.d.ts.map +1 -1
  20. package/dist/lib/entity-viewer/entity-viewer.component.js +161 -53
  21. package/dist/lib/entity-viewer/entity-viewer.component.js.map +1 -1
  22. package/dist/lib/types.d.ts +21 -36
  23. package/dist/lib/types.d.ts.map +1 -1
  24. package/dist/lib/types.js +3 -1
  25. package/dist/lib/types.js.map +1 -1
  26. package/dist/lib/utils/record.util.d.ts +1 -1
  27. package/dist/lib/utils/record.util.d.ts.map +1 -1
  28. package/dist/lib/utils/record.util.js +16 -0
  29. package/dist/lib/utils/record.util.js.map +1 -1
  30. package/dist/lib/view-config-panel/view-config-panel.component.d.ts +1 -1
  31. package/dist/lib/view-config-panel/view-config-panel.component.d.ts.map +1 -1
  32. package/dist/lib/view-config-panel/view-config-panel.component.js +2 -2
  33. package/dist/lib/view-config-panel/view-config-panel.component.js.map +1 -1
  34. package/dist/module.d.ts +2 -1
  35. package/dist/module.d.ts.map +1 -1
  36. package/dist/module.js +7 -3
  37. package/dist/module.js.map +1 -1
  38. package/dist/public-api.d.ts +1 -0
  39. package/dist/public-api.d.ts.map +1 -1
  40. package/dist/public-api.js +2 -0
  41. package/dist/public-api.js.map +1 -1
  42. package/package.json +11 -10
@@ -11,25 +11,26 @@ import * as i0 from "@angular/core";
11
11
  import * as i1 from "@angular/forms";
12
12
  import * as i2 from "@memberjunction/ng-shared-generic";
13
13
  import * as i3 from "@memberjunction/ng-timeline";
14
- import * as i4 from "../entity-cards/entity-cards.component";
15
- import * as i5 from "../entity-data-grid/entity-data-grid.component";
16
- import * as i6 from "@angular/common";
14
+ import * as i4 from "@memberjunction/ng-map-view";
15
+ import * as i5 from "../entity-cards/entity-cards.component";
16
+ import * as i6 from "../entity-data-grid/entity-data-grid.component";
17
+ import * as i7 from "@angular/common";
17
18
  const _forTrack0 = ($index, $item) => $item.Name;
18
19
  function EntityViewerComponent_Conditional_1_Conditional_1_Conditional_3_Template(rf, ctx) { if (rf & 1) {
19
20
  const _r3 = i0.ɵɵgetCurrentView();
20
- i0.ɵɵelementStart(0, "button", 19);
21
+ i0.ɵɵelementStart(0, "button", 20);
21
22
  i0.ɵɵlistener("click", function EntityViewerComponent_Conditional_1_Conditional_1_Conditional_3_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.clearFilter()); });
22
- i0.ɵɵelement(1, "i", 20);
23
+ i0.ɵɵelement(1, "i", 21);
23
24
  i0.ɵɵelementEnd();
24
25
  } }
25
26
  function EntityViewerComponent_Conditional_1_Conditional_1_Template(rf, ctx) { if (rf & 1) {
26
27
  const _r1 = i0.ɵɵgetCurrentView();
27
- i0.ɵɵelementStart(0, "div", 13);
28
- i0.ɵɵelement(1, "i", 16);
29
- i0.ɵɵelementStart(2, "input", 17);
28
+ i0.ɵɵelementStart(0, "div", 14);
29
+ i0.ɵɵelement(1, "i", 17);
30
+ i0.ɵɵelementStart(2, "input", 18);
30
31
  i0.ɵɵlistener("input", function EntityViewerComponent_Conditional_1_Conditional_1_Template_input_input_2_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.onFilterChange($event.target.value)); });
31
32
  i0.ɵɵelementEnd();
32
- i0.ɵɵconditionalCreate(3, EntityViewerComponent_Conditional_1_Conditional_1_Conditional_3_Template, 2, 0, "button", 18);
33
+ i0.ɵɵconditionalCreate(3, EntityViewerComponent_Conditional_1_Conditional_1_Conditional_3_Template, 2, 0, "button", 19);
33
34
  i0.ɵɵelementEnd();
34
35
  } if (rf & 2) {
35
36
  const ctx_r1 = i0.ɵɵnextContext(2);
@@ -60,7 +61,7 @@ function EntityViewerComponent_Conditional_1_Conditional_2_Conditional_2_Templat
60
61
  i0.ɵɵtextInterpolate1("", i0.ɵɵpipeBind1(2, 1, ctx_r1.totalRecordCount), " records");
61
62
  } }
62
63
  function EntityViewerComponent_Conditional_1_Conditional_2_Template(rf, ctx) { if (rf & 1) {
63
- i0.ɵɵelementStart(0, "div", 14);
64
+ i0.ɵɵelementStart(0, "div", 15);
64
65
  i0.ɵɵconditionalCreate(1, EntityViewerComponent_Conditional_1_Conditional_2_Conditional_1_Template, 4, 6, "span")(2, EntityViewerComponent_Conditional_1_Conditional_2_Conditional_2_Template, 3, 3, "span");
65
66
  i0.ɵɵelementEnd();
66
67
  } if (rf & 2) {
@@ -70,9 +71,9 @@ function EntityViewerComponent_Conditional_1_Conditional_2_Template(rf, ctx) { i
70
71
  } }
71
72
  function EntityViewerComponent_Conditional_1_Conditional_3_Conditional_5_Template(rf, ctx) { if (rf & 1) {
72
73
  const _r5 = i0.ɵɵgetCurrentView();
73
- i0.ɵɵelementStart(0, "button", 26);
74
+ i0.ɵɵelementStart(0, "button", 29);
74
75
  i0.ɵɵlistener("click", function EntityViewerComponent_Conditional_1_Conditional_3_Conditional_5_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.setViewMode("timeline")); });
75
- i0.ɵɵelement(1, "i", 27);
76
+ i0.ɵɵelement(1, "i", 30);
76
77
  i0.ɵɵelementEnd();
77
78
  } if (rf & 2) {
78
79
  const ctx_r1 = i0.ɵɵnextContext(3);
@@ -80,16 +81,19 @@ function EntityViewerComponent_Conditional_1_Conditional_3_Conditional_5_Templat
80
81
  } }
81
82
  function EntityViewerComponent_Conditional_1_Conditional_3_Template(rf, ctx) { if (rf & 1) {
82
83
  const _r4 = i0.ɵɵgetCurrentView();
83
- i0.ɵɵelementStart(0, "div", 15)(1, "button", 21);
84
+ i0.ɵɵelementStart(0, "div", 16)(1, "button", 22);
84
85
  i0.ɵɵlistener("click", function EntityViewerComponent_Conditional_1_Conditional_3_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.setViewMode("grid")); });
85
- i0.ɵɵelement(2, "i", 22);
86
+ i0.ɵɵelement(2, "i", 23);
86
87
  i0.ɵɵelementEnd();
87
- i0.ɵɵelementStart(3, "button", 23);
88
+ i0.ɵɵelementStart(3, "button", 24);
88
89
  i0.ɵɵlistener("click", function EntityViewerComponent_Conditional_1_Conditional_3_Template_button_click_3_listener() { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.setViewMode("cards")); });
89
- i0.ɵɵelement(4, "i", 24);
90
- i0.ɵɵelementEnd();
91
- i0.ɵɵconditionalCreate(5, EntityViewerComponent_Conditional_1_Conditional_3_Conditional_5_Template, 2, 2, "button", 25);
90
+ i0.ɵɵelement(4, "i", 25);
92
91
  i0.ɵɵelementEnd();
92
+ i0.ɵɵconditionalCreate(5, EntityViewerComponent_Conditional_1_Conditional_3_Conditional_5_Template, 2, 2, "button", 26);
93
+ i0.ɵɵelementStart(6, "button", 27);
94
+ i0.ɵɵlistener("click", function EntityViewerComponent_Conditional_1_Conditional_3_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.setViewMode("map")); });
95
+ i0.ɵɵelement(7, "i", 28);
96
+ i0.ɵɵelementEnd()();
93
97
  } if (rf & 2) {
94
98
  const ctx_r1 = i0.ɵɵnextContext(2);
95
99
  i0.ɵɵadvance();
@@ -98,9 +102,11 @@ function EntityViewerComponent_Conditional_1_Conditional_3_Template(rf, ctx) { i
98
102
  i0.ɵɵclassProp("active", ctx_r1.effectiveViewMode === "cards");
99
103
  i0.ɵɵadvance(2);
100
104
  i0.ɵɵconditional(ctx_r1.hasDateFields ? 5 : -1);
105
+ i0.ɵɵadvance();
106
+ i0.ɵɵclassProp("active", ctx_r1.effectiveViewMode === "map")("geo-hidden", !ctx_r1.HasGeoCoding);
101
107
  } }
102
108
  function EntityViewerComponent_Conditional_1_Conditional_4_Conditional_2_Template(rf, ctx) { if (rf & 1) {
103
- i0.ɵɵelementStart(0, "span", 30);
109
+ i0.ɵɵelementStart(0, "span", 33);
104
110
  i0.ɵɵtext(1);
105
111
  i0.ɵɵelementEnd();
106
112
  } if (rf & 2) {
@@ -109,7 +115,7 @@ function EntityViewerComponent_Conditional_1_Conditional_4_Conditional_2_Templat
109
115
  i0.ɵɵtextInterpolate(ctx_r1.selectedDateFieldDisplayName);
110
116
  } }
111
117
  function EntityViewerComponent_Conditional_1_Conditional_4_Conditional_3_For_2_Template(rf, ctx) { if (rf & 1) {
112
- i0.ɵɵelementStart(0, "option", 36);
118
+ i0.ɵɵelementStart(0, "option", 39);
113
119
  i0.ɵɵtext(1);
114
120
  i0.ɵɵelementEnd();
115
121
  } if (rf & 2) {
@@ -120,9 +126,9 @@ function EntityViewerComponent_Conditional_1_Conditional_4_Conditional_3_For_2_T
120
126
  } }
121
127
  function EntityViewerComponent_Conditional_1_Conditional_4_Conditional_3_Template(rf, ctx) { if (rf & 1) {
122
128
  const _r7 = i0.ɵɵgetCurrentView();
123
- i0.ɵɵelementStart(0, "select", 35);
129
+ i0.ɵɵelementStart(0, "select", 38);
124
130
  i0.ɵɵlistener("change", function EntityViewerComponent_Conditional_1_Conditional_4_Conditional_3_Template_select_change_0_listener($event) { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.setTimelineDateField($event.target.value)); });
125
- i0.ɵɵrepeaterCreate(1, EntityViewerComponent_Conditional_1_Conditional_4_Conditional_3_For_2_Template, 2, 2, "option", 36, _forTrack0);
131
+ i0.ɵɵrepeaterCreate(1, EntityViewerComponent_Conditional_1_Conditional_4_Conditional_3_For_2_Template, 2, 2, "option", 39, _forTrack0);
126
132
  i0.ɵɵelementEnd();
127
133
  } if (rf & 2) {
128
134
  const ctx_r1 = i0.ɵɵnextContext(3);
@@ -132,15 +138,15 @@ function EntityViewerComponent_Conditional_1_Conditional_4_Conditional_3_Templat
132
138
  } }
133
139
  function EntityViewerComponent_Conditional_1_Conditional_4_Template(rf, ctx) { if (rf & 1) {
134
140
  const _r6 = i0.ɵɵgetCurrentView();
135
- i0.ɵɵelementStart(0, "div", 28);
136
- i0.ɵɵelement(1, "i", 29);
137
- i0.ɵɵconditionalCreate(2, EntityViewerComponent_Conditional_1_Conditional_4_Conditional_2_Template, 2, 1, "span", 30)(3, EntityViewerComponent_Conditional_1_Conditional_4_Conditional_3_Template, 3, 1, "select", 31);
141
+ i0.ɵɵelementStart(0, "div", 31);
142
+ i0.ɵɵelement(1, "i", 32);
143
+ i0.ɵɵconditionalCreate(2, EntityViewerComponent_Conditional_1_Conditional_4_Conditional_2_Template, 2, 1, "span", 33)(3, EntityViewerComponent_Conditional_1_Conditional_4_Conditional_3_Template, 3, 1, "select", 34);
138
144
  i0.ɵɵelementEnd();
139
- i0.ɵɵelementStart(4, "div", 32)(5, "button", 33);
145
+ i0.ɵɵelementStart(4, "div", 35)(5, "button", 36);
140
146
  i0.ɵɵlistener("click", function EntityViewerComponent_Conditional_1_Conditional_4_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.toggleTimelineOrientation()); });
141
147
  i0.ɵɵelement(6, "i");
142
148
  i0.ɵɵelementEnd()();
143
- i0.ɵɵelementStart(7, "div", 34)(8, "button", 33);
149
+ i0.ɵɵelementStart(7, "div", 37)(8, "button", 36);
144
150
  i0.ɵɵlistener("click", function EntityViewerComponent_Conditional_1_Conditional_4_Template_button_click_8_listener() { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.toggleTimelineSortOrder()); });
145
151
  i0.ɵɵelement(9, "i");
146
152
  i0.ɵɵelementEnd()();
@@ -159,9 +165,9 @@ function EntityViewerComponent_Conditional_1_Conditional_4_Template(rf, ctx) { i
159
165
  } }
160
166
  function EntityViewerComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
161
167
  i0.ɵɵelementStart(0, "div", 1);
162
- i0.ɵɵconditionalCreate(1, EntityViewerComponent_Conditional_1_Conditional_1_Template, 4, 3, "div", 13);
163
- i0.ɵɵconditionalCreate(2, EntityViewerComponent_Conditional_1_Conditional_2_Template, 3, 1, "div", 14);
164
- i0.ɵɵconditionalCreate(3, EntityViewerComponent_Conditional_1_Conditional_3_Template, 6, 5, "div", 15);
168
+ i0.ɵɵconditionalCreate(1, EntityViewerComponent_Conditional_1_Conditional_1_Template, 4, 3, "div", 14);
169
+ i0.ɵɵconditionalCreate(2, EntityViewerComponent_Conditional_1_Conditional_2_Template, 3, 1, "div", 15);
170
+ i0.ɵɵconditionalCreate(3, EntityViewerComponent_Conditional_1_Conditional_3_Template, 8, 9, "div", 16);
165
171
  i0.ɵɵconditionalCreate(4, EntityViewerComponent_Conditional_1_Conditional_4_Template, 10, 7);
166
172
  i0.ɵɵelementEnd();
167
173
  } if (rf & 2) {
@@ -175,6 +181,15 @@ function EntityViewerComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
175
181
  i0.ɵɵadvance();
176
182
  i0.ɵɵconditional(ctx_r1.effectiveViewMode === "timeline" && ctx_r1.hasDateFields ? 4 : -1);
177
183
  } }
184
+ function EntityViewerComponent_Conditional_18_Template(rf, ctx) { if (rf & 1) {
185
+ const _r9 = i0.ɵɵgetCurrentView();
186
+ i0.ɵɵelementStart(0, "mj-map-view", 40);
187
+ i0.ɵɵlistener("MarkerClick", function EntityViewerComponent_Conditional_18_Template_mj_map_view_MarkerClick_0_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onMapMarkerClick($event)); })("RenderModeChange", function EntityViewerComponent_Conditional_18_Template_mj_map_view_RenderModeChange_0_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onMapRenderModeChange($event)); })("DisplayStateChange", function EntityViewerComponent_Conditional_18_Template_mj_map_view_DisplayStateChange_0_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onMapDisplayStateChange($event)); });
188
+ i0.ɵɵelementEnd();
189
+ } if (rf & 2) {
190
+ const ctx_r1 = i0.ɵɵnextContext();
191
+ i0.ɵɵproperty("hidden", ctx_r1.effectiveViewMode !== "map")("Entity", ctx_r1.effectiveEntity)("Records", ctx_r1.filteredRecords)("TotalRecordCount", ctx_r1.totalRecordCount)("RenderMode", ctx_r1.mapRenderMode)("DisplayState", ctx_r1.mapDisplayState);
192
+ } }
178
193
  /**
179
194
  * EntityViewerComponent - Full-featured composite component for viewing entity data
180
195
  *
@@ -211,6 +226,12 @@ function EntityViewerComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
211
226
  export class EntityViewerComponent {
212
227
  cdr;
213
228
  ngZone;
229
+ /**
230
+ * Maximum records to load in map mode. Map view needs all records for
231
+ * geographic visualization — paging doesn't make sense for maps. This cap
232
+ * prevents unbounded queries on very large entities.
233
+ */
234
+ static MAP_MAX_RECORDS = 10000;
214
235
  // ========================================
215
236
  // INPUTS (using getter/setter pattern)
216
237
  // ========================================
@@ -236,6 +257,8 @@ export class EntityViewerComponent {
236
257
  this._entity = value;
237
258
  // Detect date fields for timeline support
238
259
  this.detectDateFields();
260
+ // Detect geocoding support for map view
261
+ this.updateGeoCodingSupport();
239
262
  if (this._initialized) {
240
263
  // If entity changed to a different entity, clear all stale state from the old entity
241
264
  if (value && previousEntity && !UUIDsEqual(value.ID, previousEntity.ID)) {
@@ -539,6 +562,8 @@ export class EntityViewerComponent {
539
562
  // ========================================
540
563
  /** Whether the current entity has date fields available for timeline view */
541
564
  hasDateFields = false;
565
+ /** Whether the current entity supports geocoding (has SupportsGeoCoding = 1) */
566
+ HasGeoCoding = false;
542
567
  /** Available date fields from the entity (sorted by priority) */
543
568
  availableDateFields = [];
544
569
  /** Timeline groups configuration for the timeline component */
@@ -940,19 +965,14 @@ export class EntityViewerComponent {
940
965
  }
941
966
  // Priority 2: GridState.sortSettings (sort may only be stored here)
942
967
  if (view.GridState) {
943
- try {
944
- const gridState = JSON.parse(view.GridState);
945
- if (gridState.sortSettings && gridState.sortSettings.length > 0) {
946
- const firstSort = gridState.sortSettings[0];
947
- this.internalSortState = {
948
- field: firstSort.field,
949
- direction: firstSort.dir === 'desc' ? 'desc' : 'asc'
950
- };
951
- return;
952
- }
953
- }
954
- catch {
955
- // Invalid GridState JSON — ignore
968
+ const gridState = view.GridStateObject;
969
+ if (gridState?.sortSettings && gridState.sortSettings.length > 0) {
970
+ const firstSort = gridState.sortSettings[0];
971
+ this.internalSortState = {
972
+ field: firstSort.field,
973
+ direction: firstSort.dir === 'desc' ? 'desc' : 'asc'
974
+ };
975
+ return;
956
976
  }
957
977
  }
958
978
  // No sort defined — reset to prevent stale sort from previous view
@@ -1068,8 +1088,12 @@ export class EntityViewerComponent {
1068
1088
  .map(s => `${s.field} ${(s.dir || 'asc').toUpperCase()}`)
1069
1089
  .join(', ');
1070
1090
  }
1071
- // Calculate StartRow for pagination
1072
- const startRow = this.pagination.currentPage * config.pageSize;
1091
+ // Map mode loads all records (up to MAP_MAX_RECORDS) since paging
1092
+ // doesn't make sense for geographic visualization. Other modes use
1093
+ // standard page-based pagination.
1094
+ const isMapMode = this.effectiveViewMode === 'map';
1095
+ const maxRows = isMapMode ? EntityViewerComponent.MAP_MAX_RECORDS : config.pageSize;
1096
+ const startRow = isMapMode ? 0 : this.pagination.currentPage * config.pageSize;
1073
1097
  // Build ExtraFilter from view's WhereClause if available
1074
1098
  // The view's WhereClause is the "business filter" - UserSearchString is additive
1075
1099
  const extraFilter = this.viewEntity?.WhereClause || undefined;
@@ -1077,7 +1101,7 @@ export class EntityViewerComponent {
1077
1101
  EntityName: entity.Name,
1078
1102
  ResultType: 'simple',
1079
1103
  Fields: computeFieldsList(entity, this.gridState),
1080
- MaxRows: config.pageSize,
1104
+ MaxRows: maxRows,
1081
1105
  StartRow: startRow,
1082
1106
  OrderBy: orderBy,
1083
1107
  ExtraFilter: extraFilter,
@@ -1099,6 +1123,8 @@ export class EntityViewerComponent {
1099
1123
  // Update pagination state
1100
1124
  this.pagination.totalRecords = result.TotalRowCount;
1101
1125
  this.pagination.hasMore = false; // No longer used with page-based paging
1126
+ // Re-check geo support after data loads (effectiveEntity may have resolved via viewEntity)
1127
+ this.updateGeoCodingSupport();
1102
1128
  this.dataLoaded.emit({
1103
1129
  totalRowCount: result.TotalRowCount,
1104
1130
  loadedRowCount: this.internalRecords.length,
@@ -1171,10 +1197,21 @@ export class EntityViewerComponent {
1171
1197
  * Set the view mode and emit change event
1172
1198
  */
1173
1199
  setViewMode(mode) {
1174
- if (this.effectiveViewMode !== mode) {
1200
+ const previousMode = this.effectiveViewMode;
1201
+ if (previousMode !== mode) {
1175
1202
  this.internalViewMode = mode;
1176
1203
  this.viewModeChange.emit(mode);
1177
- this.cdr.detectChanges();
1204
+ // Reload data when switching to/from map mode because map loads all
1205
+ // records (up to MAP_MAX_RECORDS) while other modes use page-based pagination.
1206
+ const switchingToMap = mode === 'map' && previousMode !== 'map';
1207
+ const switchingFromMap = mode !== 'map' && previousMode === 'map';
1208
+ if (switchingToMap || switchingFromMap) {
1209
+ this.resetPaginationState();
1210
+ this.loadData();
1211
+ }
1212
+ else {
1213
+ this.cdr.detectChanges();
1214
+ }
1178
1215
  }
1179
1216
  }
1180
1217
  // ========================================
@@ -1393,6 +1430,56 @@ export class EntityViewerComponent {
1393
1430
  });
1394
1431
  }
1395
1432
  }
1433
+ /**
1434
+ * Update HasGeoCoding based on the current effectiveEntity.
1435
+ * Called from entity setter and after data loads (when effectiveEntity may resolve via viewEntity).
1436
+ */
1437
+ updateGeoCodingSupport() {
1438
+ const entity = this.effectiveEntity;
1439
+ const newValue = !!(entity && entity.SupportsGeoCoding);
1440
+ if (newValue !== this.HasGeoCoding) {
1441
+ this.HasGeoCoding = newValue;
1442
+ this.fallbackFromMapIfNeeded();
1443
+ this.cdr.detectChanges();
1444
+ }
1445
+ }
1446
+ /**
1447
+ * Handle map marker click — emit the record for the parent to handle (open record, etc.)
1448
+ */
1449
+ onMapMarkerClick(event) {
1450
+ const entity = this.effectiveEntity;
1451
+ if (event.Record && entity) {
1452
+ const compositeKey = buildCompositeKey(event.Record, entity);
1453
+ // Emit both recordSelected (for detail panels) and recordOpened (for navigation)
1454
+ this.recordSelected.emit({
1455
+ record: event.Record,
1456
+ entity: entity,
1457
+ compositeKey
1458
+ });
1459
+ this.recordOpened.emit({
1460
+ record: event.Record,
1461
+ entity: entity,
1462
+ compositeKey
1463
+ });
1464
+ }
1465
+ }
1466
+ /** Map display state (zoom, center) — passed from parent for persistence across reloads. */
1467
+ mapDisplayState = null;
1468
+ /** Map render mode — separate from DisplayState for clear single-source-of-truth. */
1469
+ mapRenderMode = 'point';
1470
+ /** Emitted when the map's display state changes (zoom, center). */
1471
+ mapDisplayStateChange = new EventEmitter();
1472
+ /** Emitted when the map's render mode changes (user clicks mode buttons). */
1473
+ mapRenderModeChange = new EventEmitter();
1474
+ /**
1475
+ * Handle map display state changes — bubble up to parent for persistence.
1476
+ */
1477
+ onMapDisplayStateChange(state) {
1478
+ this.mapDisplayStateChange.emit(state);
1479
+ }
1480
+ onMapRenderModeChange(mode) {
1481
+ this.mapRenderModeChange.emit(mode);
1482
+ }
1396
1483
  /**
1397
1484
  * Toggle timeline orientation between vertical and horizontal
1398
1485
  */
@@ -1480,6 +1567,15 @@ export class EntityViewerComponent {
1480
1567
  this.setViewMode('grid');
1481
1568
  }
1482
1569
  }
1570
+ /**
1571
+ * If currently on map view but geocoding is no longer available,
1572
+ * fall back to grid view
1573
+ */
1574
+ fallbackFromMapIfNeeded() {
1575
+ if (this.effectiveViewMode === 'map' && !this.HasGeoCoding) {
1576
+ this.setViewMode('grid');
1577
+ }
1578
+ }
1483
1579
  /**
1484
1580
  * Sort date fields by priority:
1485
1581
  * 1. DefaultInView=true fields, sorted by Sequence (lowest first)
@@ -1627,7 +1723,7 @@ export class EntityViewerComponent {
1627
1723
  } if (rf & 2) {
1628
1724
  let _t;
1629
1725
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.dataGridRef = _t.first);
1630
- } }, hostAttrs: [2, "display", "block", "height", "100%"], inputs: { entity: "entity", records: "records", config: "config", selectedRecordId: "selectedRecordId", viewMode: "viewMode", filterText: "filterText", sortState: "sortState", gridColumns: "gridColumns", cardTemplate: "cardTemplate", viewEntity: "viewEntity", gridState: "gridState", timelineConfig: "timelineConfig", showGridToolbar: "showGridToolbar", gridToolbarConfig: "gridToolbarConfig", gridSelectionMode: "gridSelectionMode", showAddToListButton: "showAddToListButton" }, outputs: { recordSelected: "recordSelected", recordOpened: "recordOpened", dataLoaded: "dataLoaded", viewModeChange: "viewModeChange", filterTextChange: "filterTextChange", filteredCountChanged: "filteredCountChanged", sortChanged: "sortChanged", gridStateChanged: "gridStateChanged", timelineConfigChange: "timelineConfigChange", addRequested: "addRequested", deleteRequested: "deleteRequested", refreshRequested: "refreshRequested", exportRequested: "exportRequested", addToListRequested: "addToListRequested", selectionChanged: "selectionChanged" }, standalone: false, decls: 18, vars: 41, consts: [[1, "entity-viewer-container"], [1, "viewer-header"], [1, "viewer-content"], [1, "loading-container", 3, "hidden"], ["size", "medium", 3, "text"], [1, "loading-overlay", 3, "hidden"], ["size", "small", 3, "text"], [1, "empty-state", 3, "hidden"], [1, "fa-solid", "fa-database"], [1, "fa-solid", "fa-inbox"], [3, "AfterRowClick", "AfterRowDoubleClick", "AfterSort", "GridStateChanged", "SelectionChange", "NewButtonClick", "RefreshButtonClick", "DeleteButtonClick", "ExportButtonClick", "AddToListRequested", "ForeignKeyClick", "PageChange", "hidden", "Data", "Params", "FilterText", "GridState", "Height", "ShowToolbar", "ToolbarConfig", "SelectionMode", "ShowAddToListButton", "AllowLoad", "ShowPager", "PageSize", "TotalRowCount", "PagerPageNumber"], [3, "recordSelected", "recordOpened", "hidden", "entity", "records", "selectedRecordId", "cardTemplate", "hiddenFieldMatches", "filterText"], [3, "afterEventClick", "hidden", "groups", "orientation", "layout", "sortOrder", "segmentGrouping", "segmentsCollapsible", "segmentsDefaultExpanded", "selectedEventId"], [1, "filter-container"], [1, "record-count"], [1, "view-mode-toggle"], [1, "fa-solid", "fa-search", "filter-icon"], ["type", "text", 1, "filter-input", 3, "input", "placeholder", "value"], ["title", "Clear filter", 1, "clear-filter-btn"], ["title", "Clear filter", 1, "clear-filter-btn", 3, "click"], [1, "fa-solid", "fa-times"], ["title", "Grid View", 1, "toggle-btn", 3, "click"], [1, "fa-solid", "fa-list"], ["title", "Cards View", 1, "toggle-btn", 3, "click"], [1, "fa-solid", "fa-grip"], ["title", "Timeline View", 1, "toggle-btn", 3, "active"], ["title", "Timeline View", 1, "toggle-btn", 3, "click"], [1, "fa-solid", "fa-timeline"], [1, "timeline-date-selector"], [1, "fa-solid", "fa-calendar-days"], [1, "date-field-label"], [1, "date-field-select", 3, "value"], [1, "timeline-orientation-toggle"], [1, "toggle-btn", 3, "click", "title"], [1, "timeline-sort-toggle"], [1, "date-field-select", 3, "change", "value"], [3, "value"]], template: function EntityViewerComponent_Template(rf, ctx) { if (rf & 1) {
1726
+ } }, hostAttrs: [2, "display", "block", "height", "100%"], inputs: { entity: "entity", records: "records", config: "config", selectedRecordId: "selectedRecordId", viewMode: "viewMode", filterText: "filterText", sortState: "sortState", gridColumns: "gridColumns", cardTemplate: "cardTemplate", viewEntity: "viewEntity", gridState: "gridState", timelineConfig: "timelineConfig", showGridToolbar: "showGridToolbar", gridToolbarConfig: "gridToolbarConfig", gridSelectionMode: "gridSelectionMode", showAddToListButton: "showAddToListButton", mapDisplayState: "mapDisplayState", mapRenderMode: "mapRenderMode" }, outputs: { recordSelected: "recordSelected", recordOpened: "recordOpened", dataLoaded: "dataLoaded", viewModeChange: "viewModeChange", filterTextChange: "filterTextChange", filteredCountChanged: "filteredCountChanged", sortChanged: "sortChanged", gridStateChanged: "gridStateChanged", timelineConfigChange: "timelineConfigChange", addRequested: "addRequested", deleteRequested: "deleteRequested", refreshRequested: "refreshRequested", exportRequested: "exportRequested", addToListRequested: "addToListRequested", selectionChanged: "selectionChanged", mapDisplayStateChange: "mapDisplayStateChange", mapRenderModeChange: "mapRenderModeChange" }, standalone: false, decls: 19, vars: 42, consts: [[1, "entity-viewer-container"], [1, "viewer-header"], [1, "viewer-content"], [1, "loading-container", 3, "hidden"], ["size", "medium", 3, "text"], [1, "loading-overlay", 3, "hidden"], ["size", "small", 3, "text"], [1, "empty-state", 3, "hidden"], [1, "fa-solid", "fa-database"], [1, "fa-solid", "fa-inbox"], [3, "AfterRowClick", "AfterRowDoubleClick", "AfterSort", "GridStateChanged", "SelectionChange", "NewButtonClick", "RefreshButtonClick", "DeleteButtonClick", "ExportButtonClick", "AddToListRequested", "ForeignKeyClick", "PageChange", "hidden", "Data", "Params", "FilterText", "GridState", "Height", "ShowToolbar", "ToolbarConfig", "SelectionMode", "ShowAddToListButton", "AllowLoad", "ShowPager", "PageSize", "TotalRowCount", "PagerPageNumber"], [3, "recordSelected", "recordOpened", "hidden", "entity", "records", "selectedRecordId", "cardTemplate", "hiddenFieldMatches", "filterText"], [3, "afterEventClick", "hidden", "groups", "orientation", "layout", "sortOrder", "segmentGrouping", "segmentsCollapsible", "segmentsDefaultExpanded", "selectedEventId"], [3, "hidden", "Entity", "Records", "TotalRecordCount", "RenderMode", "DisplayState"], [1, "filter-container"], [1, "record-count"], [1, "view-mode-toggle"], [1, "fa-solid", "fa-search", "filter-icon"], ["type", "text", 1, "filter-input", 3, "input", "placeholder", "value"], ["title", "Clear filter", 1, "clear-filter-btn"], ["title", "Clear filter", 1, "clear-filter-btn", 3, "click"], [1, "fa-solid", "fa-times"], ["title", "Grid View", 1, "toggle-btn", 3, "click"], [1, "fa-solid", "fa-list"], ["title", "Cards View", 1, "toggle-btn", 3, "click"], [1, "fa-solid", "fa-grip"], ["title", "Timeline View", 1, "toggle-btn", 3, "active"], ["title", "Map View", 1, "toggle-btn", 3, "click"], [1, "fa-solid", "fa-map-location-dot"], ["title", "Timeline View", 1, "toggle-btn", 3, "click"], [1, "fa-solid", "fa-timeline"], [1, "timeline-date-selector"], [1, "fa-solid", "fa-calendar-days"], [1, "date-field-label"], [1, "date-field-select", 3, "value"], [1, "timeline-orientation-toggle"], [1, "toggle-btn", 3, "click", "title"], [1, "timeline-sort-toggle"], [1, "date-field-select", 3, "change", "value"], [3, "value"], [3, "MarkerClick", "RenderModeChange", "DisplayStateChange", "hidden", "Entity", "Records", "TotalRecordCount", "RenderMode", "DisplayState"]], template: function EntityViewerComponent_Template(rf, ctx) { if (rf & 1) {
1631
1727
  i0.ɵɵelementStart(0, "div", 0);
1632
1728
  i0.ɵɵconditionalCreate(1, EntityViewerComponent_Conditional_1_Template, 5, 4, "div", 1);
1633
1729
  i0.ɵɵelementStart(2, "div", 2)(3, "div", 3);
@@ -1654,7 +1750,9 @@ export class EntityViewerComponent {
1654
1750
  i0.ɵɵelementEnd();
1655
1751
  i0.ɵɵelementStart(17, "mj-timeline", 12);
1656
1752
  i0.ɵɵlistener("afterEventClick", function EntityViewerComponent_Template_mj_timeline_afterEventClick_17_listener($event) { return ctx.onTimelineEventClick($event); });
1657
- i0.ɵɵelementEnd()()();
1753
+ i0.ɵɵelementEnd();
1754
+ i0.ɵɵconditionalCreate(18, EntityViewerComponent_Conditional_18_Template, 1, 6, "mj-map-view", 13);
1755
+ i0.ɵɵelementEnd()();
1658
1756
  } if (rf & 2) {
1659
1757
  i0.ɵɵstyleProp("height", ctx.effectiveConfig.height);
1660
1758
  i0.ɵɵadvance();
@@ -1679,13 +1777,15 @@ export class EntityViewerComponent {
1679
1777
  i0.ɵɵproperty("hidden", ctx.effectiveViewMode !== "cards" || !ctx.effectiveEntity)("entity", ctx.effectiveEntity)("records", ctx.filteredRecords)("selectedRecordId", ctx.selectedRecordId)("cardTemplate", ctx.cardTemplate)("hiddenFieldMatches", ctx.hiddenFieldMatches)("filterText", ctx.debouncedFilterText);
1680
1778
  i0.ɵɵadvance();
1681
1779
  i0.ɵɵproperty("hidden", ctx.effectiveViewMode !== "timeline" || !ctx.hasDateFields)("groups", ctx.timelineGroups)("orientation", ctx.timelineOrientation)("layout", ctx.timelineOrientation === "vertical" ? "alternating" : "single")("sortOrder", ctx.timelineSortOrder)("segmentGrouping", ctx.timelineSegmentGrouping)("segmentsCollapsible", true)("segmentsDefaultExpanded", true)("selectedEventId", ctx.timelineSelectedEventId);
1682
- } }, dependencies: [i1.NgSelectOption, i1.ɵNgSelectMultipleOption, i2.LoadingComponent, i3.TimelineComponent, i4.EntityCardsComponent, i5.EntityDataGridComponent, i6.DecimalPipe], styles: [".entity-viewer-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n background: var(--mj-bg-surface-card);\n}\n\n\n\n.viewer-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 12px 16px;\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n\n\n.filter-container[_ngcontent-%COMP%] {\n flex: 1;\n max-width: 400px;\n position: relative;\n display: flex;\n align-items: center;\n}\n\n.filter-icon[_ngcontent-%COMP%] {\n position: absolute;\n left: 12px;\n color: var(--mj-text-disabled);\n font-size: 14px;\n pointer-events: none;\n}\n\n.filter-input[_ngcontent-%COMP%] {\n width: 100%;\n padding: 8px 36px 8px 36px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 14px;\n outline: none;\n transition: border-color 0.15s ease;\n}\n\n.filter-input[_ngcontent-%COMP%]:focus {\n border-color: var(--mj-brand-primary);\n}\n\n.filter-input[_ngcontent-%COMP%]::placeholder {\n color: var(--mj-text-disabled);\n}\n\n.clear-filter-btn[_ngcontent-%COMP%] {\n position: absolute;\n right: 8px;\n width: 20px;\n height: 20px;\n border: none;\n background: transparent;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-disabled);\n transition: all 0.15s ease;\n}\n\n.clear-filter-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-secondary);\n}\n\n\n\n.record-count[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n}\n\n\n\n.view-mode-toggle[_ngcontent-%COMP%] {\n display: flex;\n background: var(--mj-bg-surface-card);\n border-radius: 6px;\n padding: 2px;\n}\n\n.toggle-btn[_ngcontent-%COMP%] {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n transition: all 0.15s ease;\n}\n\n.toggle-btn[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-secondary);\n}\n\n.toggle-btn.active[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n\n\n\n.timeline-date-selector[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n color: var(--mj-text-secondary);\n}\n\n.timeline-date-selector[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-text-disabled);\n}\n\n.date-field-label[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n font-weight: 500;\n}\n\n.date-field-select[_ngcontent-%COMP%] {\n padding: 4px 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n font-size: 13px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n outline: none;\n transition: border-color 0.15s ease;\n}\n\n.date-field-select[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-border-strong);\n}\n\n.date-field-select[_ngcontent-%COMP%]:focus {\n border-color: var(--mj-brand-primary);\n}\n\n\n\n.timeline-orientation-toggle[_ngcontent-%COMP%] {\n display: flex;\n background: var(--mj-bg-surface-card);\n border-radius: 6px;\n padding: 2px;\n}\n\n\n\n.viewer-content[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n overflow: hidden;\n position: relative;\n background: var(--mj-bg-surface);\n}\n\n\n\n.loading-container[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n}\n\n\n\n.loading-overlay[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: color-mix(in srgb, var(--mj-bg-surface) 80%, transparent);\n z-index: 10;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n\n\n.loading-container[hidden][_ngcontent-%COMP%], \n.loading-overlay[hidden][_ngcontent-%COMP%] {\n display: none !important;\n}\n\n\n\n.empty-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--mj-text-disabled);\n text-align: center;\n}\n\n.empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n margin-bottom: 16px;\n opacity: 0.5;\n}\n\n.empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n}\n\n\n\nmj-entity-cards[hidden][_ngcontent-%COMP%], \nmj-timeline[hidden][_ngcontent-%COMP%] {\n display: none !important;\n}\n\n\n\nmj-entity-cards[_ngcontent-%COMP%]:not([hidden]), \nmj-timeline[_ngcontent-%COMP%]:not([hidden]) {\n display: block;\n height: 100%;\n width: 100%;\n}"] });
1780
+ i0.ɵɵadvance();
1781
+ i0.ɵɵconditional(ctx.HasGeoCoding ? 18 : -1);
1782
+ } }, dependencies: [i1.NgSelectOption, i1.ɵNgSelectMultipleOption, i2.LoadingComponent, i3.TimelineComponent, i4.MapViewComponent, i5.EntityCardsComponent, i6.EntityDataGridComponent, i7.DecimalPipe], styles: [".entity-viewer-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n background: var(--mj-bg-surface-card);\n}\n\n\n\n.viewer-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 12px 16px;\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n\n\n.filter-container[_ngcontent-%COMP%] {\n flex: 1;\n max-width: 400px;\n position: relative;\n display: flex;\n align-items: center;\n}\n\n.filter-icon[_ngcontent-%COMP%] {\n position: absolute;\n left: 12px;\n color: var(--mj-text-disabled);\n font-size: 14px;\n pointer-events: none;\n}\n\n.filter-input[_ngcontent-%COMP%] {\n width: 100%;\n padding: 8px 36px 8px 36px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 14px;\n outline: none;\n transition: border-color 0.15s ease;\n}\n\n.filter-input[_ngcontent-%COMP%]:focus {\n border-color: var(--mj-brand-primary);\n}\n\n.filter-input[_ngcontent-%COMP%]::placeholder {\n color: var(--mj-text-disabled);\n}\n\n.clear-filter-btn[_ngcontent-%COMP%] {\n position: absolute;\n right: 8px;\n width: 20px;\n height: 20px;\n border: none;\n background: transparent;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-disabled);\n transition: all 0.15s ease;\n}\n\n.clear-filter-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-secondary);\n}\n\n\n\n.record-count[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n}\n\n\n\n.view-mode-toggle[_ngcontent-%COMP%] {\n display: flex;\n background: var(--mj-bg-surface-card);\n border-radius: 6px;\n padding: 2px;\n}\n\n.toggle-btn[_ngcontent-%COMP%] {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n transition: all 0.15s ease;\n}\n\n.toggle-btn[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-secondary);\n}\n\n.toggle-btn.active[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n\n\n\n.timeline-date-selector[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n color: var(--mj-text-secondary);\n}\n\n.timeline-date-selector[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-text-disabled);\n}\n\n.date-field-label[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n font-weight: 500;\n}\n\n.date-field-select[_ngcontent-%COMP%] {\n padding: 4px 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n font-size: 13px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n outline: none;\n transition: border-color 0.15s ease;\n}\n\n.date-field-select[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-border-strong);\n}\n\n.date-field-select[_ngcontent-%COMP%]:focus {\n border-color: var(--mj-brand-primary);\n}\n\n\n\n.timeline-orientation-toggle[_ngcontent-%COMP%] {\n display: flex;\n background: var(--mj-bg-surface-card);\n border-radius: 6px;\n padding: 2px;\n}\n\n\n\n.viewer-content[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n overflow: hidden;\n position: relative;\n background: var(--mj-bg-surface);\n}\n\n\n\n.loading-container[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n}\n\n\n\n.loading-overlay[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: color-mix(in srgb, var(--mj-bg-surface) 80%, transparent);\n z-index: 10;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n\n\n.loading-container[hidden][_ngcontent-%COMP%], \n.loading-overlay[hidden][_ngcontent-%COMP%] {\n display: none !important;\n}\n\n\n\n.empty-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--mj-text-disabled);\n text-align: center;\n}\n\n.empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n margin-bottom: 16px;\n opacity: 0.5;\n}\n\n.empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n}\n\n\n\nmj-entity-cards[hidden][_ngcontent-%COMP%], \nmj-timeline[hidden][_ngcontent-%COMP%], \nmj-map-view[hidden][_ngcontent-%COMP%] {\n display: none !important;\n}\n\n\n\nmj-entity-cards[_ngcontent-%COMP%]:not([hidden]), \nmj-timeline[_ngcontent-%COMP%]:not([hidden]), \nmj-map-view[_ngcontent-%COMP%]:not([hidden]) {\n display: block;\n height: 100%;\n width: 100%;\n}\n\n\n\n.toggle-btn.geo-hidden[_ngcontent-%COMP%] {\n display: none !important;\n}"] });
1683
1783
  }
1684
1784
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(EntityViewerComponent, [{
1685
1785
  type: Component,
1686
1786
  args: [{ standalone: false, selector: 'mj-entity-viewer', host: {
1687
1787
  'style': 'display: block; height: 100%;'
1688
- }, template: "<div class=\"entity-viewer-container\" [style.height]=\"effectiveConfig.height\">\n <!-- Header -->\n @if (effectiveConfig.showFilter || effectiveConfig.showViewModeToggle || effectiveConfig.showRecordCount) {\n <div class=\"viewer-header\">\n <!-- Filter Input -->\n @if (effectiveConfig.showFilter) {\n <div class=\"filter-container\">\n <i class=\"fa-solid fa-search filter-icon\"></i>\n <input\n type=\"text\"\n class=\"filter-input\"\n [placeholder]=\"effectiveConfig.filterPlaceholder\"\n [value]=\"effectiveFilterText\"\n (input)=\"onFilterChange($any($event.target).value)\"\n />\n @if (effectiveFilterText) {\n <button class=\"clear-filter-btn\" (click)=\"clearFilter()\" title=\"Clear filter\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n }\n </div>\n }\n\n <!-- Record Count -->\n @if (effectiveConfig.showRecordCount && effectiveEntity) {\n <div class=\"record-count\">\n @if (filteredRecordCount !== totalRecordCount) {\n <span>{{ filteredRecordCount | number }} of {{ totalRecordCount | number }} records</span>\n } @else {\n <span>{{ totalRecordCount | number }} records</span>\n }\n </div>\n }\n\n <!-- View Mode Toggle -->\n @if (effectiveConfig.showViewModeToggle) {\n <div class=\"view-mode-toggle\">\n <button\n class=\"toggle-btn\"\n [class.active]=\"effectiveViewMode === 'grid'\"\n (click)=\"setViewMode('grid')\"\n title=\"Grid View\">\n <i class=\"fa-solid fa-list\"></i>\n </button>\n <button\n class=\"toggle-btn\"\n [class.active]=\"effectiveViewMode === 'cards'\"\n (click)=\"setViewMode('cards')\"\n title=\"Cards View\">\n <i class=\"fa-solid fa-grip\"></i>\n </button>\n @if (hasDateFields) {\n <button\n class=\"toggle-btn\"\n [class.active]=\"effectiveViewMode === 'timeline'\"\n (click)=\"setViewMode('timeline')\"\n title=\"Timeline View\">\n <i class=\"fa-solid fa-timeline\"></i>\n </button>\n }\n </div>\n }\n\n <!-- Timeline Controls (only shown when timeline is active) -->\n @if (effectiveViewMode === 'timeline' && hasDateFields) {\n <!-- Date Field Selector -->\n <div class=\"timeline-date-selector\">\n <i class=\"fa-solid fa-calendar-days\"></i>\n @if (availableDateFields.length === 1) {\n <span class=\"date-field-label\">{{ selectedDateFieldDisplayName }}</span>\n } @else {\n <select\n class=\"date-field-select\"\n [value]=\"selectedTimelineDateField\"\n (change)=\"setTimelineDateField($any($event.target).value)\">\n @for (field of availableDateFields; track field.Name) {\n <option [value]=\"field.Name\">{{ field.DisplayNameOrName }}</option>\n }\n </select>\n }\n </div>\n\n <!-- Orientation Toggle -->\n <div class=\"timeline-orientation-toggle\">\n <button\n class=\"toggle-btn\"\n (click)=\"toggleTimelineOrientation()\"\n [title]=\"timelineOrientation === 'vertical' ? 'Switch to Horizontal' : 'Switch to Vertical'\">\n <i [class]=\"timelineOrientation === 'vertical' ? 'fa-solid fa-ellipsis-vertical' : 'fa-solid fa-ellipsis'\"></i>\n </button>\n </div>\n\n <!-- Sort Order Toggle -->\n <div class=\"timeline-sort-toggle\">\n <button\n class=\"toggle-btn\"\n (click)=\"toggleTimelineSortOrder()\"\n [title]=\"timelineSortOrder === 'desc' ? 'Showing Newest First (click for Oldest First)' : 'Showing Oldest First (click for Newest First)'\">\n <i [class]=\"timelineSortOrder === 'desc' ? 'fa-solid fa-arrow-down-wide-short' : 'fa-solid fa-arrow-up-wide-short'\"></i>\n </button>\n </div>\n }\n </div>\n }\n\n <!-- Content -->\n <div class=\"viewer-content\">\n <!-- Loading container - full page when no data exists -->\n <div class=\"loading-container\" [hidden]=\"!(isLoading && filteredRecords.length === 0)\">\n <mj-loading [text]=\"loadingMessage\" size=\"medium\"></mj-loading>\n </div>\n\n <!-- Loading overlay - shown on top of content when loading with existing data -->\n <div class=\"loading-overlay\" [hidden]=\"!(isLoading && filteredRecords.length > 0)\">\n <mj-loading [text]=\"loadingMessage\" size=\"small\"></mj-loading>\n </div>\n\n <!-- Empty state: no entity selected -->\n <div class=\"empty-state\" [hidden]=\"!!effectiveEntity\">\n <i class=\"fa-solid fa-database\"></i>\n <p>Select an entity to view records</p>\n </div>\n\n <!-- Empty state: no records found -->\n <div class=\"empty-state\" [hidden]=\"!effectiveEntity || filteredRecords.length > 0 || isLoading\">\n <i class=\"fa-solid fa-inbox\"></i>\n <p>{{ debouncedFilterText ? 'No matching records' : 'No records found' }}</p>\n </div>\n\n <!-- Grid View - always rendered, visibility controlled by hidden -->\n <mj-entity-data-grid\n [hidden]=\"effectiveViewMode !== 'grid' || !effectiveEntity\"\n [Data]=\"filteredRecords\"\n [Params]=\"gridParams\"\n [FilterText]=\"debouncedFilterText\"\n [GridState]=\"gridState\"\n [Height]=\"'auto'\"\n [ShowToolbar]=\"showGridToolbar\"\n [ToolbarConfig]=\"effectiveGridToolbarConfig\"\n [SelectionMode]=\"gridSelectionMode\"\n [ShowAddToListButton]=\"showAddToListButton\"\n [AllowLoad]=\"false\"\n [ShowPager]=\"effectiveConfig.showPagination\"\n [PageSize]=\"effectiveConfig.pageSize\"\n [TotalRowCount]=\"pagination.totalRecords\"\n [PagerPageNumber]=\"pagination.currentPage + 1\"\n (AfterRowClick)=\"onDataGridRowClick($event)\"\n (AfterRowDoubleClick)=\"onDataGridRowDoubleClick($event)\"\n (AfterSort)=\"onDataGridSortChanged($event)\"\n (GridStateChanged)=\"onGridStateChanged($event)\"\n (SelectionChange)=\"onGridSelectionChange($event)\"\n (NewButtonClick)=\"onGridAddRequested()\"\n (RefreshButtonClick)=\"onGridRefreshRequested()\"\n (DeleteButtonClick)=\"onGridDeleteRequested($event)\"\n (ExportButtonClick)=\"onGridExportRequested()\"\n (AddToListRequested)=\"onGridAddToListRequested($event)\"\n (ForeignKeyClick)=\"onForeignKeyClick($event)\"\n (PageChange)=\"onGridPageChange($event)\">\n </mj-entity-data-grid>\n\n <!-- Cards View - always rendered, visibility controlled by hidden -->\n <mj-entity-cards\n [hidden]=\"effectiveViewMode !== 'cards' || !effectiveEntity\"\n [entity]=\"effectiveEntity\"\n [records]=\"filteredRecords\"\n [selectedRecordId]=\"selectedRecordId\"\n [cardTemplate]=\"cardTemplate\"\n [hiddenFieldMatches]=\"hiddenFieldMatches\"\n [filterText]=\"debouncedFilterText\"\n (recordSelected)=\"onRecordSelected($event)\"\n (recordOpened)=\"onRecordOpened($event)\">\n </mj-entity-cards>\n\n <!-- Timeline View - always rendered when date fields exist, visibility controlled by hidden -->\n <mj-timeline\n [hidden]=\"effectiveViewMode !== 'timeline' || !hasDateFields\"\n [groups]=\"timelineGroups\"\n [orientation]=\"timelineOrientation\"\n [layout]=\"timelineOrientation === 'vertical' ? 'alternating' : 'single'\"\n [sortOrder]=\"timelineSortOrder\"\n [segmentGrouping]=\"timelineSegmentGrouping\"\n [segmentsCollapsible]=\"true\"\n [segmentsDefaultExpanded]=\"true\"\n [selectedEventId]=\"timelineSelectedEventId\"\n (afterEventClick)=\"onTimelineEventClick($event)\">\n </mj-timeline>\n </div>\n\n</div>\n", styles: [".entity-viewer-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n background: var(--mj-bg-surface-card);\n}\n\n/* Header */\n.viewer-header {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 12px 16px;\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n/* Filter */\n.filter-container {\n flex: 1;\n max-width: 400px;\n position: relative;\n display: flex;\n align-items: center;\n}\n\n.filter-icon {\n position: absolute;\n left: 12px;\n color: var(--mj-text-disabled);\n font-size: 14px;\n pointer-events: none;\n}\n\n.filter-input {\n width: 100%;\n padding: 8px 36px 8px 36px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 14px;\n outline: none;\n transition: border-color 0.15s ease;\n}\n\n.filter-input:focus {\n border-color: var(--mj-brand-primary);\n}\n\n.filter-input::placeholder {\n color: var(--mj-text-disabled);\n}\n\n.clear-filter-btn {\n position: absolute;\n right: 8px;\n width: 20px;\n height: 20px;\n border: none;\n background: transparent;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-disabled);\n transition: all 0.15s ease;\n}\n\n.clear-filter-btn:hover {\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-secondary);\n}\n\n/* Record Count */\n.record-count {\n font-size: 13px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n}\n\n/* View Mode Toggle */\n.view-mode-toggle {\n display: flex;\n background: var(--mj-bg-surface-card);\n border-radius: 6px;\n padding: 2px;\n}\n\n.toggle-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n transition: all 0.15s ease;\n}\n\n.toggle-btn:hover {\n color: var(--mj-text-secondary);\n}\n\n.toggle-btn.active {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n\n/* Timeline Date Field Selector */\n.timeline-date-selector {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n color: var(--mj-text-secondary);\n}\n\n.timeline-date-selector i {\n color: var(--mj-text-disabled);\n}\n\n.date-field-label {\n color: var(--mj-text-secondary);\n font-weight: 500;\n}\n\n.date-field-select {\n padding: 4px 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n font-size: 13px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n outline: none;\n transition: border-color 0.15s ease;\n}\n\n.date-field-select:hover {\n border-color: var(--mj-border-strong);\n}\n\n.date-field-select:focus {\n border-color: var(--mj-brand-primary);\n}\n\n/* Timeline Orientation Toggle */\n.timeline-orientation-toggle {\n display: flex;\n background: var(--mj-bg-surface-card);\n border-radius: 6px;\n padding: 2px;\n}\n\n/* Content */\n.viewer-content {\n flex: 1;\n min-height: 0;\n overflow: hidden;\n position: relative;\n background: var(--mj-bg-surface);\n}\n\n/* Loading State - full-page centered loading for initial load when no data exists */\n.loading-container {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n}\n\n/* Loading overlay - semi-transparent overlay on top of existing content during refresh */\n.loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: color-mix(in srgb, var(--mj-bg-surface) 80%, transparent);\n z-index: 10;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n/* Ensure [hidden] attribute works properly on loading elements */\n.loading-container[hidden],\n.loading-overlay[hidden] {\n display: none !important;\n}\n\n/* Empty State */\n.empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--mj-text-disabled);\n text-align: center;\n}\n\n.empty-state i {\n font-size: 48px;\n margin-bottom: 16px;\n opacity: 0.5;\n}\n\n.empty-state p {\n margin: 0;\n font-size: 14px;\n}\n\n/* Hidden components should not display - ensure [hidden] attribute works properly */\nmj-entity-cards[hidden],\nmj-timeline[hidden] {\n display: none !important;\n}\n\n/* Visible view components should fill available space */\nmj-entity-cards:not([hidden]),\nmj-timeline:not([hidden]) {\n display: block;\n height: 100%;\n width: 100%;\n}\n"] }]
1788
+ }, template: "<div class=\"entity-viewer-container\" [style.height]=\"effectiveConfig.height\">\n <!-- Header -->\n @if (effectiveConfig.showFilter || effectiveConfig.showViewModeToggle || effectiveConfig.showRecordCount) {\n <div class=\"viewer-header\">\n <!-- Filter Input -->\n @if (effectiveConfig.showFilter) {\n <div class=\"filter-container\">\n <i class=\"fa-solid fa-search filter-icon\"></i>\n <input\n type=\"text\"\n class=\"filter-input\"\n [placeholder]=\"effectiveConfig.filterPlaceholder\"\n [value]=\"effectiveFilterText\"\n (input)=\"onFilterChange($any($event.target).value)\"\n />\n @if (effectiveFilterText) {\n <button class=\"clear-filter-btn\" (click)=\"clearFilter()\" title=\"Clear filter\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n }\n </div>\n }\n\n <!-- Record Count -->\n @if (effectiveConfig.showRecordCount && effectiveEntity) {\n <div class=\"record-count\">\n @if (filteredRecordCount !== totalRecordCount) {\n <span>{{ filteredRecordCount | number }} of {{ totalRecordCount | number }} records</span>\n } @else {\n <span>{{ totalRecordCount | number }} records</span>\n }\n </div>\n }\n\n <!-- View Mode Toggle -->\n @if (effectiveConfig.showViewModeToggle) {\n <div class=\"view-mode-toggle\">\n <button\n class=\"toggle-btn\"\n [class.active]=\"effectiveViewMode === 'grid'\"\n (click)=\"setViewMode('grid')\"\n title=\"Grid View\">\n <i class=\"fa-solid fa-list\"></i>\n </button>\n <button\n class=\"toggle-btn\"\n [class.active]=\"effectiveViewMode === 'cards'\"\n (click)=\"setViewMode('cards')\"\n title=\"Cards View\">\n <i class=\"fa-solid fa-grip\"></i>\n </button>\n @if (hasDateFields) {\n <button\n class=\"toggle-btn\"\n [class.active]=\"effectiveViewMode === 'timeline'\"\n (click)=\"setViewMode('timeline')\"\n title=\"Timeline View\">\n <i class=\"fa-solid fa-timeline\"></i>\n </button>\n }\n <button\n class=\"toggle-btn\"\n [class.active]=\"effectiveViewMode === 'map'\"\n [class.geo-hidden]=\"!HasGeoCoding\"\n (click)=\"setViewMode('map')\"\n title=\"Map View\">\n <i class=\"fa-solid fa-map-location-dot\"></i>\n </button>\n </div>\n }\n\n <!-- Timeline Controls (only shown when timeline is active) -->\n @if (effectiveViewMode === 'timeline' && hasDateFields) {\n <!-- Date Field Selector -->\n <div class=\"timeline-date-selector\">\n <i class=\"fa-solid fa-calendar-days\"></i>\n @if (availableDateFields.length === 1) {\n <span class=\"date-field-label\">{{ selectedDateFieldDisplayName }}</span>\n } @else {\n <select\n class=\"date-field-select\"\n [value]=\"selectedTimelineDateField\"\n (change)=\"setTimelineDateField($any($event.target).value)\">\n @for (field of availableDateFields; track field.Name) {\n <option [value]=\"field.Name\">{{ field.DisplayNameOrName }}</option>\n }\n </select>\n }\n </div>\n\n <!-- Orientation Toggle -->\n <div class=\"timeline-orientation-toggle\">\n <button\n class=\"toggle-btn\"\n (click)=\"toggleTimelineOrientation()\"\n [title]=\"timelineOrientation === 'vertical' ? 'Switch to Horizontal' : 'Switch to Vertical'\">\n <i [class]=\"timelineOrientation === 'vertical' ? 'fa-solid fa-ellipsis-vertical' : 'fa-solid fa-ellipsis'\"></i>\n </button>\n </div>\n\n <!-- Sort Order Toggle -->\n <div class=\"timeline-sort-toggle\">\n <button\n class=\"toggle-btn\"\n (click)=\"toggleTimelineSortOrder()\"\n [title]=\"timelineSortOrder === 'desc' ? 'Showing Newest First (click for Oldest First)' : 'Showing Oldest First (click for Newest First)'\">\n <i [class]=\"timelineSortOrder === 'desc' ? 'fa-solid fa-arrow-down-wide-short' : 'fa-solid fa-arrow-up-wide-short'\"></i>\n </button>\n </div>\n }\n </div>\n }\n\n <!-- Content -->\n <div class=\"viewer-content\">\n <!-- Loading container - full page when no data exists -->\n <div class=\"loading-container\" [hidden]=\"!(isLoading && filteredRecords.length === 0)\">\n <mj-loading [text]=\"loadingMessage\" size=\"medium\"></mj-loading>\n </div>\n\n <!-- Loading overlay - shown on top of content when loading with existing data -->\n <div class=\"loading-overlay\" [hidden]=\"!(isLoading && filteredRecords.length > 0)\">\n <mj-loading [text]=\"loadingMessage\" size=\"small\"></mj-loading>\n </div>\n\n <!-- Empty state: no entity selected -->\n <div class=\"empty-state\" [hidden]=\"!!effectiveEntity\">\n <i class=\"fa-solid fa-database\"></i>\n <p>Select an entity to view records</p>\n </div>\n\n <!-- Empty state: no records found -->\n <div class=\"empty-state\" [hidden]=\"!effectiveEntity || filteredRecords.length > 0 || isLoading\">\n <i class=\"fa-solid fa-inbox\"></i>\n <p>{{ debouncedFilterText ? 'No matching records' : 'No records found' }}</p>\n </div>\n\n <!-- Grid View - always rendered, visibility controlled by hidden -->\n <mj-entity-data-grid\n [hidden]=\"effectiveViewMode !== 'grid' || !effectiveEntity\"\n [Data]=\"filteredRecords\"\n [Params]=\"gridParams\"\n [FilterText]=\"debouncedFilterText\"\n [GridState]=\"gridState\"\n [Height]=\"'auto'\"\n [ShowToolbar]=\"showGridToolbar\"\n [ToolbarConfig]=\"effectiveGridToolbarConfig\"\n [SelectionMode]=\"gridSelectionMode\"\n [ShowAddToListButton]=\"showAddToListButton\"\n [AllowLoad]=\"false\"\n [ShowPager]=\"effectiveConfig.showPagination\"\n [PageSize]=\"effectiveConfig.pageSize\"\n [TotalRowCount]=\"pagination.totalRecords\"\n [PagerPageNumber]=\"pagination.currentPage + 1\"\n (AfterRowClick)=\"onDataGridRowClick($event)\"\n (AfterRowDoubleClick)=\"onDataGridRowDoubleClick($event)\"\n (AfterSort)=\"onDataGridSortChanged($event)\"\n (GridStateChanged)=\"onGridStateChanged($event)\"\n (SelectionChange)=\"onGridSelectionChange($event)\"\n (NewButtonClick)=\"onGridAddRequested()\"\n (RefreshButtonClick)=\"onGridRefreshRequested()\"\n (DeleteButtonClick)=\"onGridDeleteRequested($event)\"\n (ExportButtonClick)=\"onGridExportRequested()\"\n (AddToListRequested)=\"onGridAddToListRequested($event)\"\n (ForeignKeyClick)=\"onForeignKeyClick($event)\"\n (PageChange)=\"onGridPageChange($event)\">\n </mj-entity-data-grid>\n\n <!-- Cards View - always rendered, visibility controlled by hidden -->\n <mj-entity-cards\n [hidden]=\"effectiveViewMode !== 'cards' || !effectiveEntity\"\n [entity]=\"effectiveEntity\"\n [records]=\"filteredRecords\"\n [selectedRecordId]=\"selectedRecordId\"\n [cardTemplate]=\"cardTemplate\"\n [hiddenFieldMatches]=\"hiddenFieldMatches\"\n [filterText]=\"debouncedFilterText\"\n (recordSelected)=\"onRecordSelected($event)\"\n (recordOpened)=\"onRecordOpened($event)\">\n </mj-entity-cards>\n\n <!-- Timeline View - always rendered when date fields exist, visibility controlled by hidden -->\n <mj-timeline\n [hidden]=\"effectiveViewMode !== 'timeline' || !hasDateFields\"\n [groups]=\"timelineGroups\"\n [orientation]=\"timelineOrientation\"\n [layout]=\"timelineOrientation === 'vertical' ? 'alternating' : 'single'\"\n [sortOrder]=\"timelineSortOrder\"\n [segmentGrouping]=\"timelineSegmentGrouping\"\n [segmentsCollapsible]=\"true\"\n [segmentsDefaultExpanded]=\"true\"\n [selectedEventId]=\"timelineSelectedEventId\"\n (afterEventClick)=\"onTimelineEventClick($event)\">\n </mj-timeline>\n\n <!-- Map View - rendered when geocoding supported, visibility controlled by hidden -->\n @if (HasGeoCoding) {\n <mj-map-view\n [hidden]=\"effectiveViewMode !== 'map'\"\n [Entity]=\"effectiveEntity!\"\n [Records]=\"filteredRecords\"\n [TotalRecordCount]=\"totalRecordCount\"\n [RenderMode]=\"mapRenderMode\"\n [DisplayState]=\"mapDisplayState\"\n (MarkerClick)=\"onMapMarkerClick($event)\"\n (RenderModeChange)=\"onMapRenderModeChange($event)\"\n (DisplayStateChange)=\"onMapDisplayStateChange($event)\">\n </mj-map-view>\n }\n </div>\n\n</div>\n", styles: [".entity-viewer-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n background: var(--mj-bg-surface-card);\n}\n\n/* Header */\n.viewer-header {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 12px 16px;\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n/* Filter */\n.filter-container {\n flex: 1;\n max-width: 400px;\n position: relative;\n display: flex;\n align-items: center;\n}\n\n.filter-icon {\n position: absolute;\n left: 12px;\n color: var(--mj-text-disabled);\n font-size: 14px;\n pointer-events: none;\n}\n\n.filter-input {\n width: 100%;\n padding: 8px 36px 8px 36px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 14px;\n outline: none;\n transition: border-color 0.15s ease;\n}\n\n.filter-input:focus {\n border-color: var(--mj-brand-primary);\n}\n\n.filter-input::placeholder {\n color: var(--mj-text-disabled);\n}\n\n.clear-filter-btn {\n position: absolute;\n right: 8px;\n width: 20px;\n height: 20px;\n border: none;\n background: transparent;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-disabled);\n transition: all 0.15s ease;\n}\n\n.clear-filter-btn:hover {\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-secondary);\n}\n\n/* Record Count */\n.record-count {\n font-size: 13px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n}\n\n/* View Mode Toggle */\n.view-mode-toggle {\n display: flex;\n background: var(--mj-bg-surface-card);\n border-radius: 6px;\n padding: 2px;\n}\n\n.toggle-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n transition: all 0.15s ease;\n}\n\n.toggle-btn:hover {\n color: var(--mj-text-secondary);\n}\n\n.toggle-btn.active {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n\n/* Timeline Date Field Selector */\n.timeline-date-selector {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n color: var(--mj-text-secondary);\n}\n\n.timeline-date-selector i {\n color: var(--mj-text-disabled);\n}\n\n.date-field-label {\n color: var(--mj-text-secondary);\n font-weight: 500;\n}\n\n.date-field-select {\n padding: 4px 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n font-size: 13px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n outline: none;\n transition: border-color 0.15s ease;\n}\n\n.date-field-select:hover {\n border-color: var(--mj-border-strong);\n}\n\n.date-field-select:focus {\n border-color: var(--mj-brand-primary);\n}\n\n/* Timeline Orientation Toggle */\n.timeline-orientation-toggle {\n display: flex;\n background: var(--mj-bg-surface-card);\n border-radius: 6px;\n padding: 2px;\n}\n\n/* Content */\n.viewer-content {\n flex: 1;\n min-height: 0;\n overflow: hidden;\n position: relative;\n background: var(--mj-bg-surface);\n}\n\n/* Loading State - full-page centered loading for initial load when no data exists */\n.loading-container {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n}\n\n/* Loading overlay - semi-transparent overlay on top of existing content during refresh */\n.loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: color-mix(in srgb, var(--mj-bg-surface) 80%, transparent);\n z-index: 10;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n/* Ensure [hidden] attribute works properly on loading elements */\n.loading-container[hidden],\n.loading-overlay[hidden] {\n display: none !important;\n}\n\n/* Empty State */\n.empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--mj-text-disabled);\n text-align: center;\n}\n\n.empty-state i {\n font-size: 48px;\n margin-bottom: 16px;\n opacity: 0.5;\n}\n\n.empty-state p {\n margin: 0;\n font-size: 14px;\n}\n\n/* Hidden components should not display - ensure [hidden] attribute works properly */\nmj-entity-cards[hidden],\nmj-timeline[hidden],\nmj-map-view[hidden] {\n display: none !important;\n}\n\n/* Visible view components should fill available space */\nmj-entity-cards:not([hidden]),\nmj-timeline:not([hidden]),\nmj-map-view:not([hidden]) {\n display: block;\n height: 100%;\n width: 100%;\n}\n\n/* Hide map toggle when entity does not support geocoding */\n.toggle-btn.geo-hidden {\n display: none !important;\n}\n"] }]
1689
1789
  }], () => [{ type: i0.ChangeDetectorRef }, { type: i0.NgZone }], { entity: [{
1690
1790
  type: Input
1691
1791
  }], records: [{
@@ -1751,6 +1851,14 @@ export class EntityViewerComponent {
1751
1851
  }], dataGridRef: [{
1752
1852
  type: ViewChild,
1753
1853
  args: [EntityDataGridComponent]
1854
+ }], mapDisplayState: [{
1855
+ type: Input
1856
+ }], mapRenderMode: [{
1857
+ type: Input
1858
+ }], mapDisplayStateChange: [{
1859
+ type: Output
1860
+ }], mapRenderModeChange: [{
1861
+ type: Output
1754
1862
  }] }); })();
1755
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(EntityViewerComponent, { className: "EntityViewerComponent", filePath: "src/lib/entity-viewer/entity-viewer.component.ts", lineNumber: 79 }); })();
1863
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(EntityViewerComponent, { className: "EntityViewerComponent", filePath: "src/lib/entity-viewer/entity-viewer.component.ts", lineNumber: 80 }); })();
1756
1864
  //# sourceMappingURL=entity-viewer.component.js.map