@memberjunction/ng-map-view 6.2.0-edge.0 → 6.2.0-edge.1

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.
@@ -74,15 +74,15 @@ export declare class MapViewComponent implements OnInit, AfterViewInit, OnDestro
74
74
  /**
75
75
  * Initialize the map engine via MapCore.
76
76
  */
77
- private InitializeMap;
77
+ private initializeMap;
78
78
  /**
79
79
  * Render records using the current mode, update marker count.
80
80
  */
81
- private DoRender;
81
+ private doRender;
82
82
  /**
83
83
  * True if at least one loaded record carries a non-empty value in the boundary field.
84
84
  */
85
- private RecordsHaveBoundaryData;
85
+ private recordsHaveBoundaryData;
86
86
  /**
87
87
  * Set rendering mode (called from template toolbar buttons).
88
88
  */
@@ -90,11 +90,11 @@ export declare class MapViewComponent implements OnInit, AfterViewInit, OnDestro
90
90
  /**
91
91
  * Get a field value from a record, supporting both BaseEntity (with .Get()) and plain objects.
92
92
  */
93
- private GetField;
93
+ private getField;
94
94
  /**
95
95
  * Get a numeric field value from a record, returning null if not a valid number.
96
96
  */
97
- private GetNumericField;
97
+ private getNumericField;
98
98
  static ɵfac: i0.ɵɵFactoryDeclaration<MapViewComponent, never>;
99
99
  static ɵcmp: i0.ɵɵComponentDeclaration<MapViewComponent, "mj-map-view", never, { "Entity": { "alias": "Entity"; "required": false; }; "Records": { "alias": "Records"; "required": false; }; "LatitudeField": { "alias": "LatitudeField"; "required": false; }; "LongitudeField": { "alias": "LongitudeField"; "required": false; }; "RenderMode": { "alias": "RenderMode"; "required": false; }; "BoundaryField": { "alias": "BoundaryField"; "required": false; }; "DisplayState": { "alias": "DisplayState"; "required": false; }; "TotalRecordCount": { "alias": "TotalRecordCount"; "required": false; }; }, { "MarkerClick": "MarkerClick"; "RegionClick": "RegionClick"; "DisplayStateChange": "DisplayStateChange"; "RenderModeChange": "RenderModeChange"; }, never, never, false, never>;
100
100
  }
@@ -135,13 +135,13 @@ export class MapViewComponent {
135
135
  const entry = entries[0];
136
136
  if (entry.isIntersecting && !this.engine) {
137
137
  // First time visible — initialize the map
138
- this.InitializeMap();
138
+ this.initializeMap();
139
139
  }
140
140
  else if (entry.isIntersecting && this.engine) {
141
141
  // Becoming visible again — fix tile rendering and re-fit
142
142
  this.engine.invalidateSize();
143
143
  if (this.pendingRender) {
144
- this.DoRender();
144
+ this.doRender();
145
145
  this.pendingRender = false;
146
146
  }
147
147
  }
@@ -161,7 +161,7 @@ export class MapViewComponent {
161
161
  this.IsLoading = true;
162
162
  this.cdr.detectChanges();
163
163
  if (this.mapContainer?.nativeElement) {
164
- this.InitializeMap();
164
+ this.initializeMap();
165
165
  }
166
166
  else {
167
167
  this.pendingRender = true;
@@ -173,7 +173,7 @@ export class MapViewComponent {
173
173
  setTimeout(() => {
174
174
  if (this.engine) {
175
175
  this.engine.invalidateSize();
176
- this.DoRender();
176
+ this.doRender();
177
177
  }
178
178
  }, 100);
179
179
  }
@@ -196,7 +196,7 @@ export class MapViewComponent {
196
196
  /**
197
197
  * Initialize the map engine via MapCore.
198
198
  */
199
- InitializeMap() {
199
+ initializeMap() {
200
200
  if (!this.mapContainer?.nativeElement || this.engine)
201
201
  return;
202
202
  this.engine = MapCore.createEngine({
@@ -212,11 +212,11 @@ export class MapViewComponent {
212
212
  geoResolver: GeoDataEngine.Instance,
213
213
  getRecordId: (record) => {
214
214
  const pkFields = this.Entity?.PrimaryKeys ?? [];
215
- return pkFields.map(pk => this.GetField(record, pk.Name)).join('||');
215
+ return pkFields.map(pk => this.getField(record, pk.Name)).join('||');
216
216
  },
217
217
  getRecordName: (record) => {
218
218
  const nameField = this.Entity?.NameField;
219
- return nameField ? String(this.GetField(record, nameField.Name) ?? '') : 'Record';
219
+ return nameField ? String(this.getField(record, nameField.Name) ?? '') : 'Record';
220
220
  },
221
221
  onMarkerClick: (event) => {
222
222
  this.MarkerClick.emit({
@@ -237,12 +237,12 @@ export class MapViewComponent {
237
237
  onPopupRecordClick: (recordId) => {
238
238
  const record = this.Records.find(r => {
239
239
  const pkFields = this.Entity?.PrimaryKeys ?? [];
240
- const id = pkFields.map(pk => this.GetField(r, pk.Name)).join('||');
240
+ const id = pkFields.map(pk => this.getField(r, pk.Name)).join('||');
241
241
  return id === recordId;
242
242
  });
243
243
  if (record) {
244
- const lat = this.GetNumericField(record, this.LatitudeField) ?? 0;
245
- const lng = this.GetNumericField(record, this.LongitudeField) ?? 0;
244
+ const lat = this.getNumericField(record, this.LatitudeField) ?? 0;
245
+ const lng = this.getNumericField(record, this.LongitudeField) ?? 0;
246
246
  this.MarkerClick.emit({
247
247
  RecordID: recordId,
248
248
  Latitude: lat,
@@ -266,7 +266,7 @@ export class MapViewComponent {
266
266
  setTimeout(() => {
267
267
  if (this.engine) {
268
268
  this.engine.invalidateSize();
269
- this.DoRender();
269
+ this.doRender();
270
270
  this.IsLoading = false;
271
271
  this.cdr.detectChanges();
272
272
  // Double-invalidate after tiles start loading to fix partial rendering
@@ -280,7 +280,7 @@ export class MapViewComponent {
280
280
  /**
281
281
  * Render records using the current mode, update marker count.
282
282
  */
283
- DoRender() {
283
+ doRender() {
284
284
  if (!this.engine)
285
285
  return;
286
286
  this.engine.render(this.Records, this.RenderMode);
@@ -293,7 +293,7 @@ export class MapViewComponent {
293
293
  && this.RenderMode === 'point'
294
294
  && this.MarkerCount === 0
295
295
  && this.Records.length > 0
296
- && this.RecordsHaveBoundaryData()) {
296
+ && this.recordsHaveBoundaryData()) {
297
297
  this.autoModeApplied = true;
298
298
  this.SetRenderMode('boundary');
299
299
  }
@@ -301,10 +301,10 @@ export class MapViewComponent {
301
301
  /**
302
302
  * True if at least one loaded record carries a non-empty value in the boundary field.
303
303
  */
304
- RecordsHaveBoundaryData() {
304
+ recordsHaveBoundaryData() {
305
305
  const field = this.BoundaryField;
306
306
  for (const record of this.Records) {
307
- const val = this.GetField(record, field);
307
+ const val = this.getField(record, field);
308
308
  if (val != null && val !== '')
309
309
  return true;
310
310
  }
@@ -316,12 +316,12 @@ export class MapViewComponent {
316
316
  SetRenderMode(mode) {
317
317
  this.RenderMode = mode;
318
318
  this.RenderModeChange.emit(mode);
319
- this.DoRender();
319
+ this.doRender();
320
320
  }
321
321
  /**
322
322
  * Get a field value from a record, supporting both BaseEntity (with .Get()) and plain objects.
323
323
  */
324
- GetField(record, fieldName) {
324
+ getField(record, fieldName) {
325
325
  if ('Get' in record && typeof record['Get'] === 'function') {
326
326
  return record.Get(fieldName);
327
327
  }
@@ -330,8 +330,8 @@ export class MapViewComponent {
330
330
  /**
331
331
  * Get a numeric field value from a record, returning null if not a valid number.
332
332
  */
333
- GetNumericField(record, fieldName) {
334
- const val = this.GetField(record, fieldName);
333
+ getNumericField(record, fieldName) {
334
+ const val = this.getField(record, fieldName);
335
335
  if (val == null)
336
336
  return null;
337
337
  const num = Number(val);
@@ -1 +1 @@
1
- {"version":3,"file":"map-view.component.js","sourceRoot":"","sources":["../../src/lib/map-view.component.ts","../../src/lib/map-view.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n Input,\n Output,\n EventEmitter,\n OnInit,\n OnDestroy,\n OnChanges,\n SimpleChanges,\n ElementRef,\n ViewChild,\n ChangeDetectorRef,\n AfterViewInit,\n inject\n} from '@angular/core';\nimport { EntityInfo, ResolveMapLatitudeField, ResolveMapLongitudeField } from '@memberjunction/core';\nimport { GeoDataEngine } from '@memberjunction/core-entities';\nimport { MapRenderMode, MapDisplayState, MapMarkerClickEvent, MapRegionClickEvent } from './map-view.types';\nimport * as MapCore from '@memberjunction/geo-maps';\n// Leaflet is loaded as a global script at runtime via angular.json scripts array.\n// The L namespace is available globally — see leaflet.d.ts for type declarations.\n\n/**\n * MapViewComponent — Leaflet-based map visualization for geo-enabled MJ entities.\n *\n * Reads `__mj_Latitude` and `__mj_Longitude` from entity records (or custom field overrides)\n * and renders them as interactive markers on an OpenStreetMap tile layer.\n *\n * Supports three rendering modes:\n * - **point** (default): Individual markers with optional clustering\n * - **choropleth**: Region shading by country/state (requires BoundaryGeoJSON)\n * - **heatmap**: Density visualization\n *\n * Delegates all rendering logic to @memberjunction/geo-maps.\n * This component is a thin Angular wrapper managing lifecycle, inputs/outputs,\n * and IntersectionObserver-based deferred initialization.\n */\n@Component({\n standalone: false,\n selector: 'mj-map-view',\n templateUrl: './map-view.component.html',\n styleUrls: ['./map-view.component.css']\n})\nexport class MapViewComponent implements OnInit, AfterViewInit, OnDestroy, OnChanges {\n private cdr = inject(ChangeDetectorRef);\n\n @ViewChild('mapContainer', { static: false })\n private mapContainer!: ElementRef<HTMLDivElement>;\n\n /** Entity metadata for the displayed records. */\n @Input() Entity!: EntityInfo;\n\n /** Array of entity records to render on the map. Accepts BaseEntity[] or plain Record objects. */\n @Input() Records: Record<string, unknown>[] = [];\n\n /**\n * Latitude field name. If left at the default, auto-resolves to the field\n * on `Entity` tagged with `ExtendedType='GeoLatitude'`.\n */\n @Input() LatitudeField: string = '__mj_Latitude';\n\n /** Longitude field name. Same auto-resolution as LatitudeField. */\n @Input() LongitudeField: string = '__mj_Longitude';\n\n /** Current render mode. */\n @Input() RenderMode: MapRenderMode = 'point';\n\n /** Field name containing GeoJSON boundary data on each record (for boundary mode). */\n @Input() BoundaryField: string = 'BoundaryGeoJSON';\n\n\n /** Persisted display state from UserView. */\n @Input() DisplayState: Partial<MapDisplayState> | null = null;\n\n /**\n * Total record count from the server (before any client-side cap).\n * When this exceeds the number of Records passed in, the map shows\n * a truncation indicator so the user knows not all data is displayed.\n */\n @Input() TotalRecordCount: number = 0;\n\n /** Emitted when a marker is clicked. */\n @Output() MarkerClick = new EventEmitter<MapMarkerClickEvent>();\n\n /** Emitted when a choropleth region is clicked. */\n @Output() RegionClick = new EventEmitter<MapRegionClickEvent>();\n\n /** Emitted when display state changes (for persistence). */\n @Output() DisplayStateChange = new EventEmitter<MapDisplayState>();\n\n /** Emitted when render mode changes (supports two-way binding: [(RenderMode)]). */\n @Output() RenderModeChange = new EventEmitter<MapRenderMode>();\n\n private engine: MapCore.MapEngine | null = null;\n private visibilityObserver: IntersectionObserver | null = null;\n private pendingRender = false;\n private autoModeApplied = false;\n IsLoading = true;\n MarkerCount = 0;\n\n /** Whether the displayed records are truncated (more exist than were loaded). */\n get IsTruncated(): boolean {\n return this.TotalRecordCount > 0 && this.Records.length < this.TotalRecordCount;\n }\n\n /** True when the entity has a per-record boundary GeoJSON field — gates the Boundary toolbar button. */\n get HasBoundaryField(): boolean {\n if (!this.Entity) return false;\n const fieldName = this.BoundaryField || 'BoundaryGeoJSON';\n return this.Entity.Fields.some(f => f.Name === fieldName);\n }\n\n /**\n * Resolve a lat/lng field name. Explicit override wins; otherwise picks the\n * field tagged with the matching `ExtendedType` on `Entity`.\n */\n private get effectiveLatitudeField(): string {\n if (!this.Entity) return this.LatitudeField || '__mj_Latitude';\n return ResolveMapLatitudeField(this.Entity, this.LatitudeField);\n }\n private get effectiveLongitudeField(): string {\n if (!this.Entity) return this.LongitudeField || '__mj_Longitude';\n return ResolveMapLongitudeField(this.Entity, this.LongitudeField);\n }\n\n ngOnInit(): void {\n // Snap saved 'boundary' → 'point' when the entity has no boundary field\n // (the button is hidden, so the user would be stuck on it).\n if (this.RenderMode === 'boundary' && !this.HasBoundaryField) {\n this.RenderMode = 'point';\n this.RenderModeChange.emit(this.RenderMode);\n }\n }\n\n ngAfterViewInit(): void {\n // Use IntersectionObserver to detect when the map container becomes visible.\n // Leaflet requires the container to have actual dimensions before initialization.\n // Since the map starts hidden (grid view is default), we defer init until visible.\n this.visibilityObserver = new IntersectionObserver((entries) => {\n const entry = entries[0];\n if (entry.isIntersecting && !this.engine) {\n // First time visible — initialize the map\n this.InitializeMap();\n } else if (entry.isIntersecting && this.engine) {\n // Becoming visible again — fix tile rendering and re-fit\n this.engine.invalidateSize();\n if (this.pendingRender) {\n this.DoRender();\n this.pendingRender = false;\n }\n }\n }, { threshold: 0.1 });\n\n if (this.mapContainer?.nativeElement) {\n this.visibilityObserver.observe(this.mapContainer.nativeElement);\n }\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n // Entity change invalidates per-Entity config (lat/lng field names,\n // Boundary visibility) cached at engine-init. Tear down so the next\n // render rebuilds against the new Entity.\n if (changes['Entity'] && !changes['Entity'].firstChange && this.engine) {\n this.engine.destroy();\n this.engine = null;\n this.autoModeApplied = false;\n this.IsLoading = true;\n this.cdr.detectChanges();\n if (this.mapContainer?.nativeElement) {\n this.InitializeMap();\n } else {\n this.pendingRender = true;\n }\n return;\n }\n\n if (changes['Records'] || changes['RenderMode']) {\n if (this.engine) {\n setTimeout(() => {\n if (this.engine) {\n this.engine.invalidateSize();\n this.DoRender();\n }\n }, 100);\n } else {\n // Map not initialized yet — mark for render when visible\n this.pendingRender = true;\n }\n }\n }\n\n ngOnDestroy(): void {\n if (this.visibilityObserver) {\n this.visibilityObserver.disconnect();\n this.visibilityObserver = null;\n }\n if (this.engine) {\n this.engine.destroy();\n this.engine = null;\n }\n }\n\n /**\n * Initialize the map engine via MapCore.\n */\n private InitializeMap(): void {\n if (!this.mapContainer?.nativeElement || this.engine) return;\n\n this.engine = MapCore.createEngine({\n container: this.mapContainer.nativeElement,\n center: {\n lat: this.DisplayState?.CenterLat ?? 20,\n lng: this.DisplayState?.CenterLng ?? 0\n },\n zoom: this.DisplayState?.ZoomLevel ?? 2,\n latitudeField: this.effectiveLatitudeField,\n longitudeField: this.effectiveLongitudeField,\n boundaryField: this.BoundaryField,\n geoResolver: GeoDataEngine.Instance,\n getRecordId: (record: Record<string, unknown>) => {\n const pkFields = this.Entity?.PrimaryKeys ?? [];\n return pkFields.map(pk => this.GetField(record, pk.Name)).join('||');\n },\n getRecordName: (record: Record<string, unknown>) => {\n const nameField = this.Entity?.NameField;\n return nameField ? String(this.GetField(record, nameField.Name) ?? '') : 'Record';\n },\n onMarkerClick: (event: MapCore.MarkerClickEvent) => {\n this.MarkerClick.emit({\n RecordID: event.recordId,\n Latitude: event.lat,\n Longitude: event.lng,\n Record: event.record\n });\n },\n onRegionClick: (event: MapCore.RegionClickEvent) => {\n this.RegionClick.emit({\n RegionName: event.regionName,\n GroupBy: event.groupBy as 'country' | 'state_province',\n RecordCount: event.recordCount,\n Records: event.records\n });\n },\n onPopupRecordClick: (recordId: string) => {\n const record = this.Records.find(r => {\n const pkFields = this.Entity?.PrimaryKeys ?? [];\n const id = pkFields.map(pk => this.GetField(r, pk.Name)).join('||');\n return id === recordId;\n });\n if (record) {\n const lat = this.GetNumericField(record, this.LatitudeField) ?? 0;\n const lng = this.GetNumericField(record, this.LongitudeField) ?? 0;\n this.MarkerClick.emit({\n RecordID: recordId,\n Latitude: lat,\n Longitude: lng,\n Record: record\n });\n }\n },\n onMoveEnd: (state: MapCore.MoveEndEvent) => {\n this.DisplayStateChange.emit({\n ZoomLevel: state.zoom,\n CenterLat: state.centerLat,\n CenterLng: state.centerLng,\n ClusterMarkers: true\n });\n },\n maxPopupRecords: 5\n });\n\n // invalidateSize after a short delay to ensure tile rendering is correct\n // then render markers and fit bounds\n setTimeout(() => {\n if (this.engine) {\n this.engine.invalidateSize();\n this.DoRender();\n this.IsLoading = false;\n this.cdr.detectChanges();\n\n // Double-invalidate after tiles start loading to fix partial rendering\n setTimeout(() => {\n if (this.engine) this.engine.invalidateSize();\n }, 500);\n }\n }, 100);\n }\n\n /**\n * Render records using the current mode, update marker count.\n */\n private DoRender(): void {\n if (!this.engine) return;\n this.engine.render(this.Records, this.RenderMode);\n this.MarkerCount = this.engine.getStats().markerCount;\n this.cdr.detectChanges();\n\n // One-shot: if 'point' rendered nothing but records carry GeoJSON\n // (e.g. MJ: Countries), flip to 'boundary' so the map isn't blank.\n // autoModeApplied prevents second-guessing manual mode changes.\n if (!this.autoModeApplied\n && this.RenderMode === 'point'\n && this.MarkerCount === 0\n && this.Records.length > 0\n && this.RecordsHaveBoundaryData()) {\n this.autoModeApplied = true;\n this.SetRenderMode('boundary');\n }\n }\n\n /**\n * True if at least one loaded record carries a non-empty value in the boundary field.\n */\n private RecordsHaveBoundaryData(): boolean {\n const field = this.BoundaryField;\n for (const record of this.Records) {\n const val = this.GetField(record, field);\n if (val != null && val !== '') return true;\n }\n return false;\n }\n\n /**\n * Set rendering mode (called from template toolbar buttons).\n */\n SetRenderMode(mode: MapRenderMode): void {\n this.RenderMode = mode;\n this.RenderModeChange.emit(mode);\n this.DoRender();\n }\n\n /**\n * Get a field value from a record, supporting both BaseEntity (with .Get()) and plain objects.\n */\n private GetField(record: Record<string, unknown>, fieldName: string): unknown {\n if ('Get' in record && typeof record['Get'] === 'function') {\n return (record as { Get: (name: string) => unknown }).Get(fieldName);\n }\n return record[fieldName];\n }\n\n /**\n * Get a numeric field value from a record, returning null if not a valid number.\n */\n private GetNumericField(record: Record<string, unknown>, fieldName: string): number | null {\n const val = this.GetField(record, fieldName);\n if (val == null) return null;\n const num = Number(val);\n return isNaN(num) ? null : num;\n }\n}\n","<div class=\"mj-map-view-wrapper\">\n <div class=\"mj-map-toolbar\">\n <div class=\"mj-map-mode-buttons\">\n <button class=\"mj-map-mode-btn\"\n [class.active]=\"RenderMode === 'point'\"\n (click)=\"SetRenderMode('point')\">\n <i class=\"fa-solid fa-location-dot\"></i> Points\n </button>\n @if (HasBoundaryField) {\n <button class=\"mj-map-mode-btn\"\n [class.active]=\"RenderMode === 'boundary'\"\n (click)=\"SetRenderMode('boundary')\">\n <i class=\"fa-solid fa-draw-polygon\"></i> Boundary\n </button>\n }\n <button class=\"mj-map-mode-btn\"\n [class.active]=\"RenderMode === 'choropleth'\"\n (click)=\"SetRenderMode('choropleth')\">\n <i class=\"fa-solid fa-map\"></i> Regions\n </button>\n <button class=\"mj-map-mode-btn\"\n [class.active]=\"RenderMode === 'heatmap'\"\n (click)=\"SetRenderMode('heatmap')\">\n <i class=\"fa-solid fa-fire\"></i> Heat Map\n </button>\n </div>\n <div class=\"mj-map-info\">\n {{ MarkerCount }} location{{ MarkerCount !== 1 ? 's' : '' }} mapped\n @if (IsTruncated) {\n <span class=\"mj-map-truncated\" title=\"Not all records are displayed. Showing {{ Records.length | number }} of {{ TotalRecordCount | number }} total records.\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i> Showing {{ Records.length | number }} of {{ TotalRecordCount | number }}\n </span>\n }\n </div>\n </div>\n @if (IsLoading) {\n <div class=\"mj-map-loading\">\n <mj-loading text=\"Loading map...\"></mj-loading>\n </div>\n }\n <div #mapContainer class=\"mj-map-container\"></div>\n</div>\n"]}
1
+ {"version":3,"file":"map-view.component.js","sourceRoot":"","sources":["../../src/lib/map-view.component.ts","../../src/lib/map-view.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n Input,\n Output,\n EventEmitter,\n OnInit,\n OnDestroy,\n OnChanges,\n SimpleChanges,\n ElementRef,\n ViewChild,\n ChangeDetectorRef,\n AfterViewInit,\n inject\n} from '@angular/core';\nimport { EntityInfo, ResolveMapLatitudeField, ResolveMapLongitudeField } from '@memberjunction/core';\nimport { GeoDataEngine } from '@memberjunction/core-entities';\nimport { MapRenderMode, MapDisplayState, MapMarkerClickEvent, MapRegionClickEvent } from './map-view.types';\nimport * as MapCore from '@memberjunction/geo-maps';\n// Leaflet is loaded as a global script at runtime via angular.json scripts array.\n// The L namespace is available globally — see leaflet.d.ts for type declarations.\n\n/**\n * MapViewComponent — Leaflet-based map visualization for geo-enabled MJ entities.\n *\n * Reads `__mj_Latitude` and `__mj_Longitude` from entity records (or custom field overrides)\n * and renders them as interactive markers on an OpenStreetMap tile layer.\n *\n * Supports three rendering modes:\n * - **point** (default): Individual markers with optional clustering\n * - **choropleth**: Region shading by country/state (requires BoundaryGeoJSON)\n * - **heatmap**: Density visualization\n *\n * Delegates all rendering logic to @memberjunction/geo-maps.\n * This component is a thin Angular wrapper managing lifecycle, inputs/outputs,\n * and IntersectionObserver-based deferred initialization.\n */\n@Component({\n standalone: false,\n selector: 'mj-map-view',\n templateUrl: './map-view.component.html',\n styleUrls: ['./map-view.component.css']\n})\nexport class MapViewComponent implements OnInit, AfterViewInit, OnDestroy, OnChanges {\n private cdr = inject(ChangeDetectorRef);\n\n @ViewChild('mapContainer', { static: false })\n private mapContainer!: ElementRef<HTMLDivElement>;\n\n /** Entity metadata for the displayed records. */\n @Input() Entity!: EntityInfo;\n\n /** Array of entity records to render on the map. Accepts BaseEntity[] or plain Record objects. */\n @Input() Records: Record<string, unknown>[] = [];\n\n /**\n * Latitude field name. If left at the default, auto-resolves to the field\n * on `Entity` tagged with `ExtendedType='GeoLatitude'`.\n */\n @Input() LatitudeField: string = '__mj_Latitude';\n\n /** Longitude field name. Same auto-resolution as LatitudeField. */\n @Input() LongitudeField: string = '__mj_Longitude';\n\n /** Current render mode. */\n @Input() RenderMode: MapRenderMode = 'point';\n\n /** Field name containing GeoJSON boundary data on each record (for boundary mode). */\n @Input() BoundaryField: string = 'BoundaryGeoJSON';\n\n\n /** Persisted display state from UserView. */\n @Input() DisplayState: Partial<MapDisplayState> | null = null;\n\n /**\n * Total record count from the server (before any client-side cap).\n * When this exceeds the number of Records passed in, the map shows\n * a truncation indicator so the user knows not all data is displayed.\n */\n @Input() TotalRecordCount: number = 0;\n\n /** Emitted when a marker is clicked. */\n @Output() MarkerClick = new EventEmitter<MapMarkerClickEvent>();\n\n /** Emitted when a choropleth region is clicked. */\n @Output() RegionClick = new EventEmitter<MapRegionClickEvent>();\n\n /** Emitted when display state changes (for persistence). */\n @Output() DisplayStateChange = new EventEmitter<MapDisplayState>();\n\n /** Emitted when render mode changes (supports two-way binding: [(RenderMode)]). */\n @Output() RenderModeChange = new EventEmitter<MapRenderMode>();\n\n private engine: MapCore.MapEngine | null = null;\n private visibilityObserver: IntersectionObserver | null = null;\n private pendingRender = false;\n private autoModeApplied = false;\n IsLoading = true;\n MarkerCount = 0;\n\n /** Whether the displayed records are truncated (more exist than were loaded). */\n get IsTruncated(): boolean {\n return this.TotalRecordCount > 0 && this.Records.length < this.TotalRecordCount;\n }\n\n /** True when the entity has a per-record boundary GeoJSON field — gates the Boundary toolbar button. */\n get HasBoundaryField(): boolean {\n if (!this.Entity) return false;\n const fieldName = this.BoundaryField || 'BoundaryGeoJSON';\n return this.Entity.Fields.some(f => f.Name === fieldName);\n }\n\n /**\n * Resolve a lat/lng field name. Explicit override wins; otherwise picks the\n * field tagged with the matching `ExtendedType` on `Entity`.\n */\n private get effectiveLatitudeField(): string {\n if (!this.Entity) return this.LatitudeField || '__mj_Latitude';\n return ResolveMapLatitudeField(this.Entity, this.LatitudeField);\n }\n private get effectiveLongitudeField(): string {\n if (!this.Entity) return this.LongitudeField || '__mj_Longitude';\n return ResolveMapLongitudeField(this.Entity, this.LongitudeField);\n }\n\n ngOnInit(): void {\n // Snap saved 'boundary' → 'point' when the entity has no boundary field\n // (the button is hidden, so the user would be stuck on it).\n if (this.RenderMode === 'boundary' && !this.HasBoundaryField) {\n this.RenderMode = 'point';\n this.RenderModeChange.emit(this.RenderMode);\n }\n }\n\n ngAfterViewInit(): void {\n // Use IntersectionObserver to detect when the map container becomes visible.\n // Leaflet requires the container to have actual dimensions before initialization.\n // Since the map starts hidden (grid view is default), we defer init until visible.\n this.visibilityObserver = new IntersectionObserver((entries) => {\n const entry = entries[0];\n if (entry.isIntersecting && !this.engine) {\n // First time visible — initialize the map\n this.initializeMap();\n } else if (entry.isIntersecting && this.engine) {\n // Becoming visible again — fix tile rendering and re-fit\n this.engine.invalidateSize();\n if (this.pendingRender) {\n this.doRender();\n this.pendingRender = false;\n }\n }\n }, { threshold: 0.1 });\n\n if (this.mapContainer?.nativeElement) {\n this.visibilityObserver.observe(this.mapContainer.nativeElement);\n }\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n // Entity change invalidates per-Entity config (lat/lng field names,\n // Boundary visibility) cached at engine-init. Tear down so the next\n // render rebuilds against the new Entity.\n if (changes['Entity'] && !changes['Entity'].firstChange && this.engine) {\n this.engine.destroy();\n this.engine = null;\n this.autoModeApplied = false;\n this.IsLoading = true;\n this.cdr.detectChanges();\n if (this.mapContainer?.nativeElement) {\n this.initializeMap();\n } else {\n this.pendingRender = true;\n }\n return;\n }\n\n if (changes['Records'] || changes['RenderMode']) {\n if (this.engine) {\n setTimeout(() => {\n if (this.engine) {\n this.engine.invalidateSize();\n this.doRender();\n }\n }, 100);\n } else {\n // Map not initialized yet — mark for render when visible\n this.pendingRender = true;\n }\n }\n }\n\n ngOnDestroy(): void {\n if (this.visibilityObserver) {\n this.visibilityObserver.disconnect();\n this.visibilityObserver = null;\n }\n if (this.engine) {\n this.engine.destroy();\n this.engine = null;\n }\n }\n\n /**\n * Initialize the map engine via MapCore.\n */\n private initializeMap(): void {\n if (!this.mapContainer?.nativeElement || this.engine) return;\n\n this.engine = MapCore.createEngine({\n container: this.mapContainer.nativeElement,\n center: {\n lat: this.DisplayState?.CenterLat ?? 20,\n lng: this.DisplayState?.CenterLng ?? 0\n },\n zoom: this.DisplayState?.ZoomLevel ?? 2,\n latitudeField: this.effectiveLatitudeField,\n longitudeField: this.effectiveLongitudeField,\n boundaryField: this.BoundaryField,\n geoResolver: GeoDataEngine.Instance,\n getRecordId: (record: Record<string, unknown>) => {\n const pkFields = this.Entity?.PrimaryKeys ?? [];\n return pkFields.map(pk => this.getField(record, pk.Name)).join('||');\n },\n getRecordName: (record: Record<string, unknown>) => {\n const nameField = this.Entity?.NameField;\n return nameField ? String(this.getField(record, nameField.Name) ?? '') : 'Record';\n },\n onMarkerClick: (event: MapCore.MarkerClickEvent) => {\n this.MarkerClick.emit({\n RecordID: event.recordId,\n Latitude: event.lat,\n Longitude: event.lng,\n Record: event.record\n });\n },\n onRegionClick: (event: MapCore.RegionClickEvent) => {\n this.RegionClick.emit({\n RegionName: event.regionName,\n GroupBy: event.groupBy as 'country' | 'state_province',\n RecordCount: event.recordCount,\n Records: event.records\n });\n },\n onPopupRecordClick: (recordId: string) => {\n const record = this.Records.find(r => {\n const pkFields = this.Entity?.PrimaryKeys ?? [];\n const id = pkFields.map(pk => this.getField(r, pk.Name)).join('||');\n return id === recordId;\n });\n if (record) {\n const lat = this.getNumericField(record, this.LatitudeField) ?? 0;\n const lng = this.getNumericField(record, this.LongitudeField) ?? 0;\n this.MarkerClick.emit({\n RecordID: recordId,\n Latitude: lat,\n Longitude: lng,\n Record: record\n });\n }\n },\n onMoveEnd: (state: MapCore.MoveEndEvent) => {\n this.DisplayStateChange.emit({\n ZoomLevel: state.zoom,\n CenterLat: state.centerLat,\n CenterLng: state.centerLng,\n ClusterMarkers: true\n });\n },\n maxPopupRecords: 5\n });\n\n // invalidateSize after a short delay to ensure tile rendering is correct\n // then render markers and fit bounds\n setTimeout(() => {\n if (this.engine) {\n this.engine.invalidateSize();\n this.doRender();\n this.IsLoading = false;\n this.cdr.detectChanges();\n\n // Double-invalidate after tiles start loading to fix partial rendering\n setTimeout(() => {\n if (this.engine) this.engine.invalidateSize();\n }, 500);\n }\n }, 100);\n }\n\n /**\n * Render records using the current mode, update marker count.\n */\n private doRender(): void {\n if (!this.engine) return;\n this.engine.render(this.Records, this.RenderMode);\n this.MarkerCount = this.engine.getStats().markerCount;\n this.cdr.detectChanges();\n\n // One-shot: if 'point' rendered nothing but records carry GeoJSON\n // (e.g. MJ: Countries), flip to 'boundary' so the map isn't blank.\n // autoModeApplied prevents second-guessing manual mode changes.\n if (!this.autoModeApplied\n && this.RenderMode === 'point'\n && this.MarkerCount === 0\n && this.Records.length > 0\n && this.recordsHaveBoundaryData()) {\n this.autoModeApplied = true;\n this.SetRenderMode('boundary');\n }\n }\n\n /**\n * True if at least one loaded record carries a non-empty value in the boundary field.\n */\n private recordsHaveBoundaryData(): boolean {\n const field = this.BoundaryField;\n for (const record of this.Records) {\n const val = this.getField(record, field);\n if (val != null && val !== '') return true;\n }\n return false;\n }\n\n /**\n * Set rendering mode (called from template toolbar buttons).\n */\n SetRenderMode(mode: MapRenderMode): void {\n this.RenderMode = mode;\n this.RenderModeChange.emit(mode);\n this.doRender();\n }\n\n /**\n * Get a field value from a record, supporting both BaseEntity (with .Get()) and plain objects.\n */\n private getField(record: Record<string, unknown>, fieldName: string): unknown {\n if ('Get' in record && typeof record['Get'] === 'function') {\n return (record as { Get: (name: string) => unknown }).Get(fieldName);\n }\n return record[fieldName];\n }\n\n /**\n * Get a numeric field value from a record, returning null if not a valid number.\n */\n private getNumericField(record: Record<string, unknown>, fieldName: string): number | null {\n const val = this.getField(record, fieldName);\n if (val == null) return null;\n const num = Number(val);\n return isNaN(num) ? null : num;\n }\n}\n","<div class=\"mj-map-view-wrapper\">\n <div class=\"mj-map-toolbar\">\n <div class=\"mj-map-mode-buttons\">\n <button class=\"mj-map-mode-btn\"\n [class.active]=\"RenderMode === 'point'\"\n (click)=\"SetRenderMode('point')\">\n <i class=\"fa-solid fa-location-dot\"></i> Points\n </button>\n @if (HasBoundaryField) {\n <button class=\"mj-map-mode-btn\"\n [class.active]=\"RenderMode === 'boundary'\"\n (click)=\"SetRenderMode('boundary')\">\n <i class=\"fa-solid fa-draw-polygon\"></i> Boundary\n </button>\n }\n <button class=\"mj-map-mode-btn\"\n [class.active]=\"RenderMode === 'choropleth'\"\n (click)=\"SetRenderMode('choropleth')\">\n <i class=\"fa-solid fa-map\"></i> Regions\n </button>\n <button class=\"mj-map-mode-btn\"\n [class.active]=\"RenderMode === 'heatmap'\"\n (click)=\"SetRenderMode('heatmap')\">\n <i class=\"fa-solid fa-fire\"></i> Heat Map\n </button>\n </div>\n <div class=\"mj-map-info\">\n {{ MarkerCount }} location{{ MarkerCount !== 1 ? 's' : '' }} mapped\n @if (IsTruncated) {\n <span class=\"mj-map-truncated\" title=\"Not all records are displayed. Showing {{ Records.length | number }} of {{ TotalRecordCount | number }} total records.\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i> Showing {{ Records.length | number }} of {{ TotalRecordCount | number }}\n </span>\n }\n </div>\n </div>\n @if (IsLoading) {\n <div class=\"mj-map-loading\">\n <mj-loading text=\"Loading map...\"></mj-loading>\n </div>\n }\n <div #mapContainer class=\"mj-map-container\"></div>\n</div>\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/ng-map-view",
3
- "version": "6.2.0-edge.0",
3
+ "version": "6.2.0-edge.1",
4
4
  "description": "MemberJunction: Angular Leaflet-based map component for visualizing geo-enabled entity data",
5
5
  "main": "./dist/public-api.js",
6
6
  "typings": "./dist/public-api.d.ts",
@@ -12,7 +12,7 @@
12
12
  "devDependencies": {
13
13
  "@angular/compiler": "21.2.22",
14
14
  "@angular/compiler-cli": "21.2.22",
15
- "@memberjunction/ng-test-utils": "6.2.0-edge.0",
15
+ "@memberjunction/ng-test-utils": "6.2.0-edge.1",
16
16
  "vitest": "^4.0.18"
17
17
  },
18
18
  "peerDependencies": {
@@ -20,11 +20,11 @@
20
20
  "@angular/core": "^21.2.22"
21
21
  },
22
22
  "dependencies": {
23
- "@memberjunction/core": "6.2.0-edge.0",
24
- "@memberjunction/core-entities": "6.2.0-edge.0",
25
- "@memberjunction/global": "6.2.0-edge.0",
26
- "@memberjunction/geo-maps": "6.2.0-edge.0",
27
- "@memberjunction/ng-shared-generic": "6.2.0-edge.0",
23
+ "@memberjunction/core": "6.2.0-edge.1",
24
+ "@memberjunction/core-entities": "6.2.0-edge.1",
25
+ "@memberjunction/global": "6.2.0-edge.1",
26
+ "@memberjunction/geo-maps": "6.2.0-edge.1",
27
+ "@memberjunction/ng-shared-generic": "6.2.0-edge.1",
28
28
  "leaflet": "^1.9.4",
29
29
  "@types/leaflet": "^1.9.17",
30
30
  "tslib": "^2.8.1",