@memberjunction/ng-map-view 6.1.0-edge.5 → 6.1.0-edge.6
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.
|
@@ -65,7 +65,6 @@ export declare class MapViewComponent implements OnInit, AfterViewInit, OnDestro
|
|
|
65
65
|
* Resolve a lat/lng field name. Explicit override wins; otherwise picks the
|
|
66
66
|
* field tagged with the matching `ExtendedType` on `Entity`.
|
|
67
67
|
*/
|
|
68
|
-
private resolveGeoField;
|
|
69
68
|
private get effectiveLatitudeField();
|
|
70
69
|
private get effectiveLongitudeField();
|
|
71
70
|
ngOnInit(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"map-view.component.d.ts","sourceRoot":"","sources":["../../src/lib/map-view.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAIH,YAAY,EACZ,MAAM,EACN,SAAS,EACT,SAAS,EACT,aAAa,EAIb,aAAa,EAEhB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"map-view.component.d.ts","sourceRoot":"","sources":["../../src/lib/map-view.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAIH,YAAY,EACZ,MAAM,EACN,SAAS,EACT,SAAS,EACT,aAAa,EAIb,aAAa,EAEhB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,UAAU,EAAqD,MAAM,sBAAsB,CAAC;AAErG,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;;AAK5G;;;;;;;;;;;;;;GAcG;AACH,qBAMa,gBAAiB,YAAW,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS;IAChF,OAAO,CAAC,GAAG,CAA6B;IAGxC,OAAO,CAAC,YAAY,CAA8B;IAElD,iDAAiD;IACxC,MAAM,EAAG,UAAU,CAAC;IAE7B,kGAAkG;IACzF,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAM;IAEjD;;;OAGG;IACM,aAAa,EAAE,MAAM,CAAmB;IAEjD,mEAAmE;IAC1D,cAAc,EAAE,MAAM,CAAoB;IAEnD,2BAA2B;IAClB,UAAU,EAAE,aAAa,CAAW;IAE7C,sFAAsF;IAC7E,aAAa,EAAE,MAAM,CAAqB;IAGnD,6CAA6C;IACpC,YAAY,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI,CAAQ;IAE9D;;;;OAIG;IACM,gBAAgB,EAAE,MAAM,CAAK;IAEtC,wCAAwC;IAC9B,WAAW,oCAA2C;IAEhE,mDAAmD;IACzC,WAAW,oCAA2C;IAEhE,4DAA4D;IAClD,kBAAkB,gCAAuC;IAEnE,mFAAmF;IACzE,gBAAgB,8BAAqC;IAE/D,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,kBAAkB,CAAqC;IAC/D,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,eAAe,CAAS;IAChC,SAAS,UAAQ;IACjB,WAAW,SAAK;IAEhB,iFAAiF;IACjF,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,wGAAwG;IACxG,IAAI,gBAAgB,IAAI,OAAO,CAI9B;IAED;;;OAGG;IACH,OAAO,KAAK,sBAAsB,GAGjC;IACD,OAAO,KAAK,uBAAuB,GAGlC;IAED,QAAQ,IAAI,IAAI;IAShB,eAAe,IAAI,IAAI;IAwBvB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAiCzC,WAAW,IAAI,IAAI;IAWnB;;OAEG;IACH,OAAO,CAAC,aAAa;IAmFrB;;OAEG;IACH,OAAO,CAAC,QAAQ;IAmBhB;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAS/B;;OAEG;IACH,aAAa,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;IAMxC;;OAEG;IACH,OAAO,CAAC,QAAQ;IAOhB;;OAEG;IACH,OAAO,CAAC,eAAe;yCA7Sd,gBAAgB;2CAAhB,gBAAgB;CAmT5B"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Component, Input, Output, EventEmitter, ViewChild, ChangeDetectorRef, inject } from '@angular/core';
|
|
2
|
+
import { ResolveMapLatitudeField, ResolveMapLongitudeField } from '@memberjunction/core';
|
|
2
3
|
import { GeoDataEngine } from '@memberjunction/core-entities';
|
|
3
4
|
import * as MapCore from '@memberjunction/geo-maps';
|
|
4
5
|
import * as i0 from "@angular/core";
|
|
@@ -6,15 +7,15 @@ import * as i1 from "@memberjunction/ng-shared-generic";
|
|
|
6
7
|
import * as i2 from "@angular/common";
|
|
7
8
|
const _c0 = ["mapContainer"];
|
|
8
9
|
function MapViewComponent_Conditional_6_Template(rf, ctx) { if (rf & 1) {
|
|
9
|
-
const
|
|
10
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
10
11
|
i0.ɵɵelementStart(0, "button", 4);
|
|
11
|
-
i0.ɵɵlistener("click", function MapViewComponent_Conditional_6_Template_button_click_0_listener() { i0.ɵɵrestoreView(
|
|
12
|
+
i0.ɵɵlistener("click", function MapViewComponent_Conditional_6_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.SetRenderMode("boundary")); });
|
|
12
13
|
i0.ɵɵelement(1, "i", 13);
|
|
13
14
|
i0.ɵɵtext(2, " Boundary ");
|
|
14
15
|
i0.ɵɵelementEnd();
|
|
15
16
|
} if (rf & 2) {
|
|
16
|
-
const
|
|
17
|
-
i0.ɵɵclassProp("active",
|
|
17
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
18
|
+
i0.ɵɵclassProp("active", ctx_r1.RenderMode === "boundary");
|
|
18
19
|
} }
|
|
19
20
|
function MapViewComponent_Conditional_15_Template(rf, ctx) { if (rf & 1) {
|
|
20
21
|
i0.ɵɵelementStart(0, "span", 10);
|
|
@@ -26,10 +27,10 @@ function MapViewComponent_Conditional_15_Template(rf, ctx) { if (rf & 1) {
|
|
|
26
27
|
i0.ɵɵpipe(6, "number");
|
|
27
28
|
i0.ɵɵelementEnd();
|
|
28
29
|
} if (rf & 2) {
|
|
29
|
-
const
|
|
30
|
-
i0.ɵɵproperty("title", i0.ɵɵinterpolate2("Not all records are displayed. Showing ", i0.ɵɵpipeBind1(1, 5,
|
|
30
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
31
|
+
i0.ɵɵproperty("title", i0.ɵɵinterpolate2("Not all records are displayed. Showing ", i0.ɵɵpipeBind1(1, 5, ctx_r1.Records.length), " of ", i0.ɵɵpipeBind1(2, 7, ctx_r1.TotalRecordCount), " total records."));
|
|
31
32
|
i0.ɵɵadvance(4);
|
|
32
|
-
i0.ɵɵtextInterpolate2(" Showing ", i0.ɵɵpipeBind1(5, 9,
|
|
33
|
+
i0.ɵɵtextInterpolate2(" Showing ", i0.ɵɵpipeBind1(5, 9, ctx_r1.Records.length), " of ", i0.ɵɵpipeBind1(6, 11, ctx_r1.TotalRecordCount), " ");
|
|
33
34
|
} }
|
|
34
35
|
function MapViewComponent_Conditional_16_Template(rf, ctx) { if (rf & 1) {
|
|
35
36
|
i0.ɵɵelementStart(0, "div", 11);
|
|
@@ -108,21 +109,15 @@ export class MapViewComponent {
|
|
|
108
109
|
* Resolve a lat/lng field name. Explicit override wins; otherwise picks the
|
|
109
110
|
* field tagged with the matching `ExtendedType` on `Entity`.
|
|
110
111
|
*/
|
|
111
|
-
resolveGeoField(input, defaultName, extendedType) {
|
|
112
|
-
if (input && input !== defaultName)
|
|
113
|
-
return input; // explicit override
|
|
114
|
-
if (this.Entity) {
|
|
115
|
-
const tagged = this.Entity.Fields.find(f => f.ExtendedType === extendedType);
|
|
116
|
-
if (tagged)
|
|
117
|
-
return tagged.Name;
|
|
118
|
-
}
|
|
119
|
-
return input || defaultName;
|
|
120
|
-
}
|
|
121
112
|
get effectiveLatitudeField() {
|
|
122
|
-
|
|
113
|
+
if (!this.Entity)
|
|
114
|
+
return this.LatitudeField || '__mj_Latitude';
|
|
115
|
+
return ResolveMapLatitudeField(this.Entity, this.LatitudeField);
|
|
123
116
|
}
|
|
124
117
|
get effectiveLongitudeField() {
|
|
125
|
-
|
|
118
|
+
if (!this.Entity)
|
|
119
|
+
return this.LongitudeField || '__mj_Longitude';
|
|
120
|
+
return ResolveMapLongitudeField(this.Entity, this.LongitudeField);
|
|
126
121
|
}
|
|
127
122
|
ngOnInit() {
|
|
128
123
|
// Snap saved 'boundary' → 'point' when the entity has no boundary field
|
|
@@ -349,20 +344,19 @@ export class MapViewComponent {
|
|
|
349
344
|
let _t;
|
|
350
345
|
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.mapContainer = _t.first);
|
|
351
346
|
} }, inputs: { Entity: "Entity", Records: "Records", LatitudeField: "LatitudeField", LongitudeField: "LongitudeField", RenderMode: "RenderMode", BoundaryField: "BoundaryField", DisplayState: "DisplayState", TotalRecordCount: "TotalRecordCount" }, outputs: { MarkerClick: "MarkerClick", RegionClick: "RegionClick", DisplayStateChange: "DisplayStateChange", RenderModeChange: "RenderModeChange" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], decls: 19, vars: 11, consts: [["mapContainer", ""], [1, "mj-map-view-wrapper"], [1, "mj-map-toolbar"], [1, "mj-map-mode-buttons"], [1, "mj-map-mode-btn", 3, "click"], [1, "fa-solid", "fa-location-dot"], [1, "mj-map-mode-btn", 3, "active"], [1, "fa-solid", "fa-map"], [1, "fa-solid", "fa-fire"], [1, "mj-map-info"], [1, "mj-map-truncated", 3, "title"], [1, "mj-map-loading"], [1, "mj-map-container"], [1, "fa-solid", "fa-draw-polygon"], [1, "fa-solid", "fa-triangle-exclamation"], ["text", "Loading map..."]], template: function MapViewComponent_Template(rf, ctx) { if (rf & 1) {
|
|
352
|
-
const _r1 = i0.ɵɵgetCurrentView();
|
|
353
347
|
i0.ɵɵelementStart(0, "div", 1)(1, "div", 2)(2, "div", 3)(3, "button", 4);
|
|
354
|
-
i0.ɵɵlistener("click", function MapViewComponent_Template_button_click_3_listener() {
|
|
348
|
+
i0.ɵɵlistener("click", function MapViewComponent_Template_button_click_3_listener() { return ctx.SetRenderMode("point"); });
|
|
355
349
|
i0.ɵɵelement(4, "i", 5);
|
|
356
350
|
i0.ɵɵtext(5, " Points ");
|
|
357
351
|
i0.ɵɵelementEnd();
|
|
358
352
|
i0.ɵɵconditionalCreate(6, MapViewComponent_Conditional_6_Template, 3, 2, "button", 6);
|
|
359
353
|
i0.ɵɵelementStart(7, "button", 4);
|
|
360
|
-
i0.ɵɵlistener("click", function MapViewComponent_Template_button_click_7_listener() {
|
|
354
|
+
i0.ɵɵlistener("click", function MapViewComponent_Template_button_click_7_listener() { return ctx.SetRenderMode("choropleth"); });
|
|
361
355
|
i0.ɵɵelement(8, "i", 7);
|
|
362
356
|
i0.ɵɵtext(9, " Regions ");
|
|
363
357
|
i0.ɵɵelementEnd();
|
|
364
358
|
i0.ɵɵelementStart(10, "button", 4);
|
|
365
|
-
i0.ɵɵlistener("click", function MapViewComponent_Template_button_click_10_listener() {
|
|
359
|
+
i0.ɵɵlistener("click", function MapViewComponent_Template_button_click_10_listener() { return ctx.SetRenderMode("heatmap"); });
|
|
366
360
|
i0.ɵɵelement(11, "i", 8);
|
|
367
361
|
i0.ɵɵtext(12, " Heat Map ");
|
|
368
362
|
i0.ɵɵelementEnd()();
|
|
@@ -388,7 +382,7 @@ export class MapViewComponent {
|
|
|
388
382
|
i0.ɵɵconditional(ctx.IsTruncated ? 15 : -1);
|
|
389
383
|
i0.ɵɵadvance();
|
|
390
384
|
i0.ɵɵconditional(ctx.IsLoading ? 16 : -1);
|
|
391
|
-
} }, dependencies: [i1.LoadingComponent, i2.DecimalPipe], styles: ["[_nghost-%COMP%] {\n display: block;\n width: 100%;\n height: 100%;\n \n\n\n\n\n\n
|
|
385
|
+
} }, dependencies: [i1.LoadingComponent, i2.DecimalPipe], styles: ["[_nghost-%COMP%] {\n display: block;\n width: 100%;\n height: 100%;\n \n\n\n\n\n\n isolation: isolate;\n}\n\n.mj-map-view-wrapper[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n position: relative;\n}\n\n.mj-map-toolbar[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 12px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n z-index: 1000;\n}\n\n.mj-map-mode-buttons[_ngcontent-%COMP%] {\n display: flex;\n gap: 4px;\n}\n\n.mj-map-mode-btn[_ngcontent-%COMP%] {\n padding: 6px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n font-size: 13px;\n transition: all 0.15s ease;\n}\n\n.mj-map-mode-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.mj-map-mode-btn.active[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-color: var(--mj-brand-primary);\n}\n\n.mj-map-info[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-muted);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.mj-map-truncated[_ngcontent-%COMP%] {\n color: var(--mj-status-warning-text);\n font-size: 12px;\n}\n\n.mj-map-loading[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 500;\n background: var(--mj-bg-surface);\n}\n\n.mj-map-container[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 400px;\n width: 100%;\n}\n\n.mj-map-container.hidden[_ngcontent-%COMP%] {\n display: none;\n}\n\n\n\n\n[_nghost-%COMP%] .leaflet-popup-content-wrapper {\n border-radius: 6px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n}\n\n[_nghost-%COMP%] .leaflet-popup-content {\n color: #1e293b;\n font-size: 13px;\n margin: 10px 14px;\n}\n\n\n[_nghost-%COMP%] .leaflet-container {\n width: 100% !important;\n height: 100% !important;\n}\n\n\n[_nghost-%COMP%] .leaflet-control-attribution {\n font-size: 9px;\n padding: 1px 5px;\n background: rgba(255, 255, 255, 0.6);\n color: #666;\n}\n\n[_nghost-%COMP%] .leaflet-control-attribution a {\n color: #666;\n}"] });
|
|
392
386
|
}
|
|
393
387
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MapViewComponent, [{
|
|
394
388
|
type: Component,
|
|
@@ -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":"AAAA,OAAO,EACH,SAAS,EACT,KAAK,EACL,MAAM,EACN,YAAY,EAMZ,SAAS,EACT,iBAAiB,EAEjB,MAAM,EACT,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAE9D,OAAO,KAAK,OAAO,MAAM,0BAA0B,CAAC;;;;;;;ICTpC,iCAE4C;IAApC,oLAAS,qBAAc,UAAU,CAAC,KAAC;IACvC,wBAAwC;IAAC,0BAC7C;IAAA,iBAAS;;;IAHD,0DAA0C;;;IAmBlD,gCAA8J;;;IAC1J,wBAAgD;IAAC,YACrD;;;IAAA,iBAAO;;;IAFwB,uBAAA,mLAA8H,CAAA;IACxG,eACrD;IADqD,4IACrD;;;IAKR,+BAA4B;IACxB,iCAA+C;IACnD,iBAAM;;ADnBd,kFAAkF;AAClF,kFAAkF;AAElF;;;;;;;;;;;;;;GAcG;AAOH,MAAM,OAAO,gBAAgB;IACjB,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAGhC,YAAY,CAA8B;IAElD,iDAAiD;IACxC,MAAM,CAAc;IAE7B,kGAAkG;IACzF,OAAO,GAA8B,EAAE,CAAC;IAEjD;;;OAGG;IACM,aAAa,GAAW,eAAe,CAAC;IAEjD,mEAAmE;IAC1D,cAAc,GAAW,gBAAgB,CAAC;IAEnD,2BAA2B;IAClB,UAAU,GAAkB,OAAO,CAAC;IAE7C,sFAAsF;IAC7E,aAAa,GAAW,iBAAiB,CAAC;IAGnD,6CAA6C;IACpC,YAAY,GAAoC,IAAI,CAAC;IAE9D;;;;OAIG;IACM,gBAAgB,GAAW,CAAC,CAAC;IAEtC,wCAAwC;IAC9B,WAAW,GAAG,IAAI,YAAY,EAAuB,CAAC;IAEhE,mDAAmD;IACzC,WAAW,GAAG,IAAI,YAAY,EAAuB,CAAC;IAEhE,4DAA4D;IAClD,kBAAkB,GAAG,IAAI,YAAY,EAAmB,CAAC;IAEnE,mFAAmF;IACzE,gBAAgB,GAAG,IAAI,YAAY,EAAiB,CAAC;IAEvD,MAAM,GAA6B,IAAI,CAAC;IACxC,kBAAkB,GAAgC,IAAI,CAAC;IACvD,aAAa,GAAG,KAAK,CAAC;IACtB,eAAe,GAAG,KAAK,CAAC;IAChC,SAAS,GAAG,IAAI,CAAC;IACjB,WAAW,GAAG,CAAC,CAAC;IAEhB,iFAAiF;IACjF,IAAI,WAAW;QACX,OAAO,IAAI,CAAC,gBAAgB,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,gBAAgB,CAAC;IACpF,CAAC;IAED,wGAAwG;IACxG,IAAI,gBAAgB;QAChB,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,aAAa,IAAI,iBAAiB,CAAC;QAC1D,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC;IAC9D,CAAC;IAED;;;OAGG;IACK,eAAe,CAAC,KAAa,EAAE,WAAmB,EAAE,YAA4C;QACpG,IAAI,KAAK,IAAI,KAAK,KAAK,WAAW;YAAE,OAAO,KAAK,CAAC,CAAC,oBAAoB;QACtE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,KAAK,YAAY,CAAC,CAAC;YAC7E,IAAI,MAAM;gBAAE,OAAO,MAAM,CAAC,IAAI,CAAC;QACnC,CAAC;QACD,OAAO,KAAK,IAAI,WAAW,CAAC;IAChC,CAAC;IACD,IAAY,sBAAsB;QAC9B,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,aAAa,EAAE,eAAe,EAAE,aAAa,CAAC,CAAC;IACpF,CAAC;IACD,IAAY,uBAAuB;QAC/B,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,cAAc,EAAE,gBAAgB,EAAE,cAAc,CAAC,CAAC;IACvF,CAAC;IAED,QAAQ;QACJ,wEAAwE;QACxE,4DAA4D;QAC5D,IAAI,IAAI,CAAC,UAAU,KAAK,UAAU,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC3D,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC;YAC1B,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAChD,CAAC;IACL,CAAC;IAED,eAAe;QACX,6EAA6E;QAC7E,kFAAkF;QAClF,mFAAmF;QACnF,IAAI,CAAC,kBAAkB,GAAG,IAAI,oBAAoB,CAAC,CAAC,OAAO,EAAE,EAAE;YAC3D,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YACzB,IAAI,KAAK,CAAC,cAAc,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACvC,0CAA0C;gBAC1C,IAAI,CAAC,aAAa,EAAE,CAAC;YACzB,CAAC;iBAAM,IAAI,KAAK,CAAC,cAAc,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAC7C,yDAAyD;gBACzD,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;gBAC7B,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;oBACrB,IAAI,CAAC,QAAQ,EAAE,CAAC;oBAChB,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;gBAC/B,CAAC;YACL,CAAC;QACL,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;QAEvB,IAAI,IAAI,CAAC,YAAY,EAAE,aAAa,EAAE,CAAC;YACnC,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC;QACrE,CAAC;IACL,CAAC;IAED,WAAW,CAAC,OAAsB;QAC9B,oEAAoE;QACpE,oEAAoE;QACpE,0CAA0C;QAC1C,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACrE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACtB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;YAC7B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACtB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;YACzB,IAAI,IAAI,CAAC,YAAY,EAAE,aAAa,EAAE,CAAC;gBACnC,IAAI,CAAC,aAAa,EAAE,CAAC;YACzB,CAAC;iBAAM,CAAC;gBACJ,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;YAC9B,CAAC;YACD,OAAO;QACX,CAAC;QAED,IAAI,OAAO,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;YAC9C,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBACd,UAAU,CAAC,GAAG,EAAE;oBACZ,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;wBACd,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;wBAC7B,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACpB,CAAC;gBACL,CAAC,EAAE,GAAG,CAAC,CAAC;YACZ,CAAC;iBAAM,CAAC;gBACJ,yDAAyD;gBACzD,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;YAC9B,CAAC;QACL,CAAC;IACL,CAAC;IAED,WAAW;QACP,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC1B,IAAI,CAAC,kBAAkB,CAAC,UAAU,EAAE,CAAC;YACrC,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;QACnC,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACtB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACvB,CAAC;IACL,CAAC;IAED;;OAEG;IACK,aAAa;QACjB,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,aAAa,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QAE7D,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;YAC/B,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,aAAa;YAC1C,MAAM,EAAE;gBACJ,GAAG,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,IAAI,EAAE;gBACvC,GAAG,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,IAAI,CAAC;aACzC;YACD,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,IAAI,CAAC;YACvC,aAAa,EAAE,IAAI,CAAC,sBAAsB;YAC1C,cAAc,EAAE,IAAI,CAAC,uBAAuB;YAC5C,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,WAAW,EAAE,aAAa,CAAC,QAAQ;YACnC,WAAW,EAAE,CAAC,MAA+B,EAAE,EAAE;gBAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,WAAW,IAAI,EAAE,CAAC;gBAChD,OAAO,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzE,CAAC;YACD,aAAa,EAAE,CAAC,MAA+B,EAAE,EAAE;gBAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC;gBACzC,OAAO,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;YACtF,CAAC;YACD,aAAa,EAAE,CAAC,KAA+B,EAAE,EAAE;gBAC/C,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;oBAClB,QAAQ,EAAE,KAAK,CAAC,QAAQ;oBACxB,QAAQ,EAAE,KAAK,CAAC,GAAG;oBACnB,SAAS,EAAE,KAAK,CAAC,GAAG;oBACpB,MAAM,EAAE,KAAK,CAAC,MAAM;iBACvB,CAAC,CAAC;YACP,CAAC;YACD,aAAa,EAAE,CAAC,KAA+B,EAAE,EAAE;gBAC/C,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;oBAClB,UAAU,EAAE,KAAK,CAAC,UAAU;oBAC5B,OAAO,EAAE,KAAK,CAAC,OAAuC;oBACtD,WAAW,EAAE,KAAK,CAAC,WAAW;oBAC9B,OAAO,EAAE,KAAK,CAAC,OAAO;iBACzB,CAAC,CAAC;YACP,CAAC;YACD,kBAAkB,EAAE,CAAC,QAAgB,EAAE,EAAE;gBACrC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;oBACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,WAAW,IAAI,EAAE,CAAC;oBAChD,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBACpE,OAAO,EAAE,KAAK,QAAQ,CAAC;gBAC3B,CAAC,CAAC,CAAC;gBACH,IAAI,MAAM,EAAE,CAAC;oBACT,MAAM,GAAG,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;oBAClE,MAAM,GAAG,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;oBACnE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;wBAClB,QAAQ,EAAE,QAAQ;wBAClB,QAAQ,EAAE,GAAG;wBACb,SAAS,EAAE,GAAG;wBACd,MAAM,EAAE,MAAM;qBACjB,CAAC,CAAC;gBACP,CAAC;YACL,CAAC;YACD,SAAS,EAAE,CAAC,KAA2B,EAAE,EAAE;gBACvC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC;oBACzB,SAAS,EAAE,KAAK,CAAC,IAAI;oBACrB,SAAS,EAAE,KAAK,CAAC,SAAS;oBAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;oBAC1B,cAAc,EAAE,IAAI;iBACvB,CAAC,CAAC;YACP,CAAC;YACD,eAAe,EAAE,CAAC;SACrB,CAAC,CAAC;QAEH,yEAAyE;QACzE,qCAAqC;QACrC,UAAU,CAAC,GAAG,EAAE;YACZ,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBACd,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;gBAC7B,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAChB,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;gBACvB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;gBAEzB,uEAAuE;gBACvE,UAAU,CAAC,GAAG,EAAE;oBACZ,IAAI,IAAI,CAAC,MAAM;wBAAE,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;gBAClD,CAAC,EAAE,GAAG,CAAC,CAAC;YACZ,CAAC;QACL,CAAC,EAAE,GAAG,CAAC,CAAC;IACZ,CAAC;IAED;;OAEG;IACK,QAAQ;QACZ,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO;QACzB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAClD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,WAAW,CAAC;QACtD,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAEzB,kEAAkE;QAClE,mEAAmE;QACnE,gEAAgE;QAChE,IAAI,CAAC,IAAI,CAAC,eAAe;eAClB,IAAI,CAAC,UAAU,KAAK,OAAO;eAC3B,IAAI,CAAC,WAAW,KAAK,CAAC;eACtB,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;eACvB,IAAI,CAAC,uBAAuB,EAAE,EAAE,CAAC;YACpC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;YAC5B,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;QACnC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,uBAAuB;QAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC;QACjC,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAChC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YACzC,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,KAAK,EAAE;gBAAE,OAAO,IAAI,CAAC;QAC/C,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;IAED;;OAEG;IACH,aAAa,CAAC,IAAmB;QAC7B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,CAAC,QAAQ,EAAE,CAAC;IACpB,CAAC;IAED;;OAEG;IACK,QAAQ,CAAC,MAA+B,EAAE,SAAiB;QAC/D,IAAI,KAAK,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,KAAK,CAAC,KAAK,UAAU,EAAE,CAAC;YACzD,OAAQ,MAA6C,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACzE,CAAC;QACD,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC;IAC7B,CAAC;IAED;;OAEG;IACK,eAAe,CAAC,MAA+B,EAAE,SAAiB;QACtE,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAC7C,IAAI,GAAG,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;QAC7B,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QACxB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;IACnC,CAAC;0GAxTQ,gBAAgB;6DAAhB,gBAAgB;;;;;;;YCxCjB,AADJ,AADJ,AADJ,8BAAiC,aACD,aACS,gBAGY;YAAjC,mIAAS,kBAAc,OAAO,CAAC,KAAC;YACpC,uBAAwC;YAAC,wBAC7C;YAAA,iBAAS;YACT,qFAAwB;YAOxB,iCAE8C;YAAtC,mIAAS,kBAAc,YAAY,CAAC,KAAC;YACzC,uBAA+B;YAAC,yBACpC;YAAA,iBAAS;YACT,kCAE2C;YAAnC,oIAAS,kBAAc,SAAS,CAAC,KAAC;YACtC,wBAAgC;YAAC,2BACrC;YACJ,AADI,iBAAS,EACP;YACN,+BAAyB;YACrB,aACA;YAAA,uFAAmB;YAM3B,AADI,iBAAM,EACJ;YACN,qFAAiB;YAKjB,8BAAkD;YACtD,iBAAM;;YArCc,eAAuC;YAAvC,oDAAuC;YAI/C,eAMC;YAND,+CAMC;YAEO,cAA4C;YAA5C,yDAA4C;YAK5C,eAAyC;YAAzC,sDAAyC;YAMjD,eACA;YADA,sGACA;YAAA,cAIC;YAJD,2CAIC;YAGT,cAIC;YAJD,yCAIC;;;iFDIQ,gBAAgB;cAN5B,SAAS;6BACM,KAAK,YACP,aAAa;;kBAOtB,SAAS;mBAAC,cAAc,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;;kBAI3C,KAAK;;kBAGL,KAAK;;kBAML,KAAK;;kBAGL,KAAK;;kBAGL,KAAK;;kBAGL,KAAK;;kBAIL,KAAK;;kBAOL,KAAK;;kBAGL,MAAM;;kBAGN,MAAM;;kBAGN,MAAM;;kBAGN,MAAM;;kFAhDE,gBAAgB","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 } 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 resolveGeoField(input: string, defaultName: string, extendedType: 'GeoLatitude' | 'GeoLongitude'): string {\n if (input && input !== defaultName) return input; // explicit override\n if (this.Entity) {\n const tagged = this.Entity.Fields.find(f => f.ExtendedType === extendedType);\n if (tagged) return tagged.Name;\n }\n return input || defaultName;\n }\n private get effectiveLatitudeField(): string {\n return this.resolveGeoField(this.LatitudeField, '__mj_Latitude', 'GeoLatitude');\n }\n private get effectiveLongitudeField(): string {\n return this.resolveGeoField(this.LongitudeField, '__mj_Longitude', 'GeoLongitude');\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.1.0-edge.
|
|
3
|
+
"version": "6.1.0-edge.6",
|
|
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",
|
|
@@ -10,21 +10,21 @@
|
|
|
10
10
|
"author": "MemberJunction.com",
|
|
11
11
|
"license": "BUSL-1.1",
|
|
12
12
|
"devDependencies": {
|
|
13
|
-
"@angular/compiler": "21.
|
|
14
|
-
"@angular/compiler-cli": "21.
|
|
15
|
-
"@memberjunction/ng-test-utils": "6.1.0-edge.
|
|
13
|
+
"@angular/compiler": "21.2.22",
|
|
14
|
+
"@angular/compiler-cli": "21.2.22",
|
|
15
|
+
"@memberjunction/ng-test-utils": "6.1.0-edge.6",
|
|
16
16
|
"vitest": "^4.0.18"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
|
19
|
-
"@angular/common": "^21.
|
|
20
|
-
"@angular/core": "^21.
|
|
19
|
+
"@angular/common": "^21.2.22",
|
|
20
|
+
"@angular/core": "^21.2.22"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@memberjunction/core": "6.1.0-edge.
|
|
24
|
-
"@memberjunction/core-entities": "6.1.0-edge.
|
|
25
|
-
"@memberjunction/global": "6.1.0-edge.
|
|
26
|
-
"@memberjunction/geo-maps": "6.1.0-edge.
|
|
27
|
-
"@memberjunction/ng-shared-generic": "6.1.0-edge.
|
|
23
|
+
"@memberjunction/core": "6.1.0-edge.6",
|
|
24
|
+
"@memberjunction/core-entities": "6.1.0-edge.6",
|
|
25
|
+
"@memberjunction/global": "6.1.0-edge.6",
|
|
26
|
+
"@memberjunction/geo-maps": "6.1.0-edge.6",
|
|
27
|
+
"@memberjunction/ng-shared-generic": "6.1.0-edge.6",
|
|
28
28
|
"leaflet": "^1.9.4",
|
|
29
29
|
"@types/leaflet": "^1.9.17",
|
|
30
30
|
"tslib": "^2.8.1",
|