@burdenoff/microfe-bigconsole 2026.612.6 → 2026.613.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.
|
@@ -1,125 +1,172 @@
|
|
|
1
|
-
import { memo as e,
|
|
2
|
-
import { jsx as
|
|
1
|
+
import { memo as e, useEffect as t, useMemo as n, useRef as r, useState as i } from "react";
|
|
2
|
+
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
3
|
+
import s from "leaflet";
|
|
3
4
|
//#region src/bigconsole/components/widgets/map-widget/MapWidget.tsx
|
|
4
|
-
var
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
var c = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.css", l = "sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=", u = !1;
|
|
6
|
+
function d() {
|
|
7
|
+
if (u || document.getElementById("leaflet-css")) return;
|
|
8
|
+
u = !0;
|
|
9
|
+
let e = document.createElement("link");
|
|
10
|
+
e.id = "leaflet-css", e.rel = "stylesheet", e.href = c, e.integrity = l, e.crossOrigin = "anonymous", e.referrerPolicy = "no-referrer", e.onerror = () => {
|
|
11
|
+
console.warn("[bigconsole] Leaflet CSS failed to load; map styling may be broken."), u = !1, e.remove();
|
|
12
|
+
}, document.head.appendChild(e);
|
|
13
|
+
}
|
|
14
|
+
var f = "var(--color-chart-1, #3b82f6)";
|
|
15
|
+
function p(e) {
|
|
16
|
+
if (typeof e != "string") return f;
|
|
17
|
+
let t = e.trim();
|
|
18
|
+
return t.length > 64 || /[;{}<>"'\\]/.test(t) || /\burl\s*\(/i.test(t) || !/^[#a-zA-Z0-9(),.%/_\s-]+$/.test(t) ? f : t;
|
|
19
|
+
}
|
|
20
|
+
function m(e) {
|
|
21
|
+
return typeof e == "number" && Number.isFinite(e);
|
|
22
|
+
}
|
|
23
|
+
function h(e) {
|
|
24
|
+
let t = e;
|
|
25
|
+
return t && m(t.lat) && m(t.lng) ? {
|
|
26
|
+
lat: t.lat,
|
|
27
|
+
lng: t.lng
|
|
28
|
+
} : void 0;
|
|
29
|
+
}
|
|
30
|
+
function g(e, t) {
|
|
31
|
+
let n = Array.isArray(e) ? void 0 : e, r = Array.isArray(e) ? e : e?.markers;
|
|
32
|
+
return {
|
|
33
|
+
markers: Array.isArray(r) ? r.filter((e) => e && m(e.lat) && m(e.lng)) : [],
|
|
34
|
+
center: h(n?.center),
|
|
35
|
+
zoom: m(n?.zoom) ? n?.zoom : void 0,
|
|
36
|
+
fallbackCenter: h(t.center),
|
|
37
|
+
fallbackZoom: m(t.zoom) ? t.zoom : void 0
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
var _ = e(function({ widget: e, data: c, onClick: l, onDrilldown: u }) {
|
|
41
|
+
let f = r(null), h = r(null), _ = r(null), [v, y] = i("loading"), b = n(() => e?.config ?? {}, [e?.config]), x = b.showMarkers !== !1, { markers: S, center: C, zoom: w, fallbackCenter: T, fallbackZoom: E } = n(() => g(c, b), [c, b]), D = r(() => void 0);
|
|
42
|
+
return t(() => {
|
|
43
|
+
D.current = (e) => {
|
|
44
|
+
u ? u(e) : l && l(e);
|
|
45
|
+
};
|
|
46
|
+
}, [l, u]), t(() => {
|
|
47
|
+
let e = f.current;
|
|
48
|
+
if (!e) return;
|
|
49
|
+
d();
|
|
50
|
+
let t;
|
|
51
|
+
try {
|
|
52
|
+
t = s.map(e, {
|
|
53
|
+
center: [20, 0],
|
|
54
|
+
zoom: 2,
|
|
55
|
+
scrollWheelZoom: !0,
|
|
56
|
+
attributionControl: !0
|
|
57
|
+
});
|
|
58
|
+
let n = s.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
|
59
|
+
attribution: "© OpenStreetMap contributors",
|
|
60
|
+
maxZoom: 18
|
|
61
|
+
}), r = !1;
|
|
62
|
+
n.on("tileload", () => {
|
|
63
|
+
r = !0, y((e) => e === "error" ? "ready" : e);
|
|
64
|
+
}), n.on("tileerror", () => {
|
|
65
|
+
r || y("error");
|
|
66
|
+
}), n.addTo(t), h.current = t, _.current = null, y("ready");
|
|
67
|
+
} catch {
|
|
68
|
+
y("error");
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
let n = typeof ResizeObserver < "u" ? new ResizeObserver(() => h.current?.invalidateSize()) : void 0;
|
|
72
|
+
n?.observe(e);
|
|
73
|
+
let r = window.setTimeout(() => t.invalidateSize(), 80);
|
|
74
|
+
return () => {
|
|
75
|
+
window.clearTimeout(r), n?.disconnect(), t.remove(), h.current = null;
|
|
76
|
+
};
|
|
77
|
+
}, []), t(() => {
|
|
78
|
+
let e = h.current;
|
|
79
|
+
if (!e) return;
|
|
80
|
+
let t = s.layerGroup();
|
|
81
|
+
if (x) for (let e of S) {
|
|
82
|
+
let n = document.createElement("div");
|
|
83
|
+
Object.assign(n.style, {
|
|
84
|
+
width: "14px",
|
|
85
|
+
height: "14px",
|
|
86
|
+
borderRadius: "50% 50% 50% 0",
|
|
87
|
+
transform: "rotate(-45deg)",
|
|
88
|
+
background: p(e.color),
|
|
89
|
+
border: "2px solid #fff",
|
|
90
|
+
boxShadow: "0 1px 4px rgba(0,0,0,.4)"
|
|
91
|
+
});
|
|
92
|
+
let r = s.divIcon({
|
|
93
|
+
className: "bc-map-marker",
|
|
94
|
+
html: n,
|
|
95
|
+
iconSize: [14, 14],
|
|
96
|
+
iconAnchor: [7, 14]
|
|
97
|
+
}), i = s.marker([e.lat, e.lng], { icon: r }).addTo(t), a = document.createElement("div"), o = document.createElement("div");
|
|
98
|
+
if (o.style.fontWeight = "600", o.textContent = e.popup || e.name || "Location", a.appendChild(o), m(e.value)) {
|
|
99
|
+
let t = document.createElement("div");
|
|
100
|
+
t.textContent = e.value.toLocaleString(), a.appendChild(t);
|
|
101
|
+
}
|
|
102
|
+
i.bindPopup(a), i.on("click", () => D.current(e));
|
|
103
|
+
}
|
|
104
|
+
t.addTo(e);
|
|
105
|
+
let n = JSON.stringify({
|
|
106
|
+
m: S.map((e) => [e.lat, e.lng]),
|
|
107
|
+
c: C ? [C.lat, C.lng] : null,
|
|
108
|
+
z: w ?? null,
|
|
109
|
+
fc: T ? [T.lat, T.lng] : null,
|
|
110
|
+
fz: E ?? null
|
|
111
|
+
});
|
|
112
|
+
if (n !== _.current) if (_.current = n, C) e.setView([C.lat, C.lng], w ?? e.getZoom());
|
|
113
|
+
else if (S.length === 1) e.setView([S[0].lat, S[0].lng], w ?? 8);
|
|
114
|
+
else if (S.length > 1) {
|
|
115
|
+
let t = s.latLngBounds(S.map((e) => [e.lat, e.lng]));
|
|
116
|
+
e.fitBounds(t, {
|
|
117
|
+
padding: [32, 32],
|
|
118
|
+
maxZoom: 12
|
|
119
|
+
});
|
|
120
|
+
} else T ? e.setView([T.lat, T.lng], w ?? E ?? e.getZoom()) : m(w ?? E) && e.setZoom(w ?? E);
|
|
121
|
+
let r = window.setTimeout(() => e.invalidateSize(), 0);
|
|
122
|
+
return () => {
|
|
123
|
+
window.clearTimeout(r), t.remove();
|
|
124
|
+
};
|
|
125
|
+
}, [
|
|
126
|
+
S,
|
|
127
|
+
C,
|
|
128
|
+
w,
|
|
129
|
+
T,
|
|
130
|
+
E,
|
|
131
|
+
x
|
|
132
|
+
]), /* @__PURE__ */ o("div", {
|
|
133
|
+
className: "relative h-full w-full overflow-hidden rounded-lg bg-bg-muted",
|
|
13
134
|
children: [
|
|
14
|
-
/* @__PURE__ */
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
135
|
+
/* @__PURE__ */ a("div", {
|
|
136
|
+
ref: f,
|
|
137
|
+
className: "h-full w-full"
|
|
138
|
+
}),
|
|
139
|
+
v !== "error" && S.length === 0 && /* @__PURE__ */ a("div", {
|
|
140
|
+
className: "pointer-events-none absolute inset-0 z-[500] flex items-center justify-center bg-bg-muted/70 text-sm text-text-secondary",
|
|
141
|
+
children: /* @__PURE__ */ o("div", {
|
|
142
|
+
className: "text-center",
|
|
143
|
+
children: [/* @__PURE__ */ o("svg", {
|
|
144
|
+
className: "mx-auto mb-2 h-12 w-12 text-text-tertiary",
|
|
20
145
|
fill: "none",
|
|
21
146
|
stroke: "currentColor",
|
|
22
147
|
viewBox: "0 0 24 24",
|
|
23
|
-
children: /* @__PURE__ */
|
|
148
|
+
children: [/* @__PURE__ */ a("path", {
|
|
24
149
|
strokeLinecap: "round",
|
|
25
150
|
strokeLinejoin: "round",
|
|
26
|
-
strokeWidth: .5,
|
|
27
|
-
d: "
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
className: "flex flex-wrap gap-2 justify-center",
|
|
34
|
-
children: s.slice(0, 8).map((e, t) => /* @__PURE__ */ r("div", {
|
|
35
|
-
className: `
|
|
36
|
-
flex items-center gap-1.5 px-2 py-1 rounded-full
|
|
37
|
-
bg-bg-surface border border-border-default shadow-sm
|
|
38
|
-
${u ? "cursor-pointer hover:border-action-primary-bg" : ""}
|
|
39
|
-
transition-colors
|
|
40
|
-
`,
|
|
41
|
-
onClick: () => l(e),
|
|
42
|
-
title: e.popup || e.name,
|
|
43
|
-
children: [
|
|
44
|
-
/* @__PURE__ */ n("div", {
|
|
45
|
-
className: "w-2 h-2 rounded-full",
|
|
46
|
-
style: { backgroundColor: e.color || "var(--color-chart-1)" }
|
|
47
|
-
}),
|
|
48
|
-
/* @__PURE__ */ n("span", {
|
|
49
|
-
className: "text-xs text-text-primary truncate max-w-[80px]",
|
|
50
|
-
children: e.name
|
|
51
|
-
}),
|
|
52
|
-
e.value !== void 0 && /* @__PURE__ */ n("span", {
|
|
53
|
-
className: "text-xs text-text-secondary",
|
|
54
|
-
children: e.value.toLocaleString()
|
|
55
|
-
})
|
|
56
|
-
]
|
|
57
|
-
}, t))
|
|
58
|
-
})
|
|
59
|
-
})]
|
|
60
|
-
}),
|
|
61
|
-
/* @__PURE__ */ n("div", {
|
|
62
|
-
className: "mt-3 space-y-1 max-h-[120px] overflow-auto",
|
|
63
|
-
children: s.map((e, t) => /* @__PURE__ */ r("div", {
|
|
64
|
-
className: `
|
|
65
|
-
flex items-center justify-between p-2 rounded
|
|
66
|
-
hover:bg-bg-muted/50 transition-colors
|
|
67
|
-
${u ? "cursor-pointer" : ""}
|
|
68
|
-
`,
|
|
69
|
-
onClick: () => l(e),
|
|
70
|
-
children: [/* @__PURE__ */ r("div", {
|
|
71
|
-
className: "flex items-center gap-2",
|
|
72
|
-
children: [/* @__PURE__ */ n("div", {
|
|
73
|
-
className: "w-2 h-2 rounded-full flex-shrink-0",
|
|
74
|
-
style: { backgroundColor: e.color || "var(--color-chart-1)" }
|
|
75
|
-
}), /* @__PURE__ */ n("span", {
|
|
76
|
-
className: "text-xs text-text-primary",
|
|
77
|
-
children: e.name
|
|
151
|
+
strokeWidth: 1.5,
|
|
152
|
+
d: "M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
|
|
153
|
+
}), /* @__PURE__ */ a("path", {
|
|
154
|
+
strokeLinecap: "round",
|
|
155
|
+
strokeLinejoin: "round",
|
|
156
|
+
strokeWidth: 1.5,
|
|
157
|
+
d: "M15 11a3 3 0 11-6 0 3 3 0 016 0z"
|
|
78
158
|
})]
|
|
79
|
-
}),
|
|
80
|
-
className: "text-xs font-medium text-text-secondary",
|
|
81
|
-
children: e.value.toLocaleString()
|
|
82
|
-
})]
|
|
83
|
-
}, t))
|
|
84
|
-
}),
|
|
85
|
-
/* @__PURE__ */ n("div", {
|
|
86
|
-
className: "mt-2 pt-2 border-t border-border-default",
|
|
87
|
-
children: /* @__PURE__ */ r("p", {
|
|
88
|
-
className: "text-[10px] text-text-tertiary text-center",
|
|
89
|
-
children: [
|
|
90
|
-
"Center: ",
|
|
91
|
-
c.lat.toFixed(2),
|
|
92
|
-
", ",
|
|
93
|
-
c.lng.toFixed(2)
|
|
94
|
-
]
|
|
159
|
+
}), /* @__PURE__ */ a("p", { children: "No map data" })]
|
|
95
160
|
})
|
|
161
|
+
}),
|
|
162
|
+
v === "error" && /* @__PURE__ */ a("div", {
|
|
163
|
+
className: "absolute inset-0 z-[500] flex items-center justify-center bg-bg-muted px-4 text-center text-sm text-text-secondary",
|
|
164
|
+
children: "The map needs an internet connection and couldn’t load."
|
|
96
165
|
})
|
|
97
166
|
]
|
|
98
|
-
}) : /* @__PURE__ */ n("div", {
|
|
99
|
-
className: "flex items-center justify-center h-full text-text-secondary text-sm",
|
|
100
|
-
children: /* @__PURE__ */ r("div", {
|
|
101
|
-
className: "text-center",
|
|
102
|
-
children: [/* @__PURE__ */ r("svg", {
|
|
103
|
-
className: "w-12 h-12 mx-auto mb-2 text-text-tertiary",
|
|
104
|
-
fill: "none",
|
|
105
|
-
stroke: "currentColor",
|
|
106
|
-
viewBox: "0 0 24 24",
|
|
107
|
-
children: [/* @__PURE__ */ n("path", {
|
|
108
|
-
strokeLinecap: "round",
|
|
109
|
-
strokeLinejoin: "round",
|
|
110
|
-
strokeWidth: 1.5,
|
|
111
|
-
d: "M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
|
|
112
|
-
}), /* @__PURE__ */ n("path", {
|
|
113
|
-
strokeLinecap: "round",
|
|
114
|
-
strokeLinejoin: "round",
|
|
115
|
-
strokeWidth: 1.5,
|
|
116
|
-
d: "M15 11a3 3 0 11-6 0 3 3 0 016 0z"
|
|
117
|
-
})]
|
|
118
|
-
}), /* @__PURE__ */ n("p", { children: "No map data" })]
|
|
119
|
-
})
|
|
120
167
|
});
|
|
121
168
|
});
|
|
122
169
|
//#endregion
|
|
123
|
-
export {
|
|
170
|
+
export { _ as default };
|
|
124
171
|
|
|
125
172
|
//# sourceMappingURL=MapWidget.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MapWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/map-widget/MapWidget.tsx"],"sourcesContent":["/**\n * MapWidget Component\n *\n * Displays geographic data with markers (placeholder implementation).\n */\n\nimport { type FC, memo, useCallback } from 'react';\nimport type { Widget } from '../../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface MapWidgetProps {\n widget: Widget;\n data?: MapData;\n onClick?: (marker: MapMarker) => void;\n /** Drilldown handler - receives selected marker data */\n onDrilldown?: (selectedData: Record<string, unknown>) => void;\n}\n\nexport interface MapMarker {\n name: string;\n lat: number;\n lng: number;\n value?: number;\n popup?: string;\n color?: string;\n}\n\nexport interface MapData {\n center?: { lat: number; lng: number };\n zoom?: number;\n markers?: MapMarker[];\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const MapWidget: FC<MapWidgetProps> = memo(function MapWidget({ widget: _widget, data, onClick, onDrilldown }) {\n const markers = data?.markers || [];\n const center = data?.center || { lat: 0, lng: 0 };\n\n // Handle marker click with drilldown support\n const handleMarkerClick = useCallback(\n (marker: MapMarker) => {\n if (onDrilldown) {\n onDrilldown(marker as unknown as Record<string, unknown>);\n } else if (onClick) {\n onClick(marker);\n }\n },\n [onClick, onDrilldown]\n );\n\n const isClickable = Boolean(onDrilldown || onClick);\n\n if (!markers.length) {\n return (\n <div className=\"flex items-center justify-center h-full text-text-secondary text-sm\">\n <div className=\"text-center\">\n <svg\n className=\"w-12 h-12 mx-auto mb-2 text-text-tertiary\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z\"\n />\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={1.5} d=\"M15 11a3 3 0 11-6 0 3 3 0 016 0z\" />\n </svg>\n <p>No map data</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col h-full p-4\">\n {/* Map placeholder with markers list */}\n <div className=\"flex-1 relative bg-bg-muted rounded-lg overflow-hidden\">\n {/* Simulated map background */}\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <svg className=\"w-32 h-32 text-text-tertiary/30\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={0.5}\n d=\"M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n </div>\n\n {/* Markers overlay */}\n <div className=\"absolute inset-0 p-4\">\n <div className=\"flex flex-wrap gap-2 justify-center\">\n {markers.slice(0, 8).map((marker, index) => (\n <div\n key={index}\n className={`\n flex items-center gap-1.5 px-2 py-1 rounded-full\n bg-bg-surface border border-border-default shadow-sm\n ${isClickable ? 'cursor-pointer hover:border-action-primary-bg' : ''}\n transition-colors\n `}\n onClick={() => handleMarkerClick(marker)}\n title={marker.popup || marker.name}\n >\n <div\n className=\"w-2 h-2 rounded-full\"\n style={{ backgroundColor: marker.color || 'var(--color-chart-1)' }}\n />\n <span className=\"text-xs text-text-primary truncate max-w-[80px]\">{marker.name}</span>\n {marker.value !== undefined && (\n <span className=\"text-xs text-text-secondary\">{marker.value.toLocaleString()}</span>\n )}\n </div>\n ))}\n </div>\n </div>\n </div>\n\n {/* Location list */}\n <div className=\"mt-3 space-y-1 max-h-[120px] overflow-auto\">\n {markers.map((marker, index) => (\n <div\n key={index}\n className={`\n flex items-center justify-between p-2 rounded\n hover:bg-bg-muted/50 transition-colors\n ${isClickable ? 'cursor-pointer' : ''}\n `}\n onClick={() => handleMarkerClick(marker)}\n >\n <div className=\"flex items-center gap-2\">\n <div\n className=\"w-2 h-2 rounded-full flex-shrink-0\"\n style={{ backgroundColor: marker.color || 'var(--color-chart-1)' }}\n />\n <span className=\"text-xs text-text-primary\">{marker.name}</span>\n </div>\n {marker.value !== undefined && (\n <span className=\"text-xs font-medium text-text-secondary\">{marker.value.toLocaleString()}</span>\n )}\n </div>\n ))}\n </div>\n\n {/* Center coordinates */}\n <div className=\"mt-2 pt-2 border-t border-border-default\">\n <p className=\"text-[10px] text-text-tertiary text-center\">\n Center: {center.lat.toFixed(2)}, {center.lng.toFixed(2)}\n </p>\n </div>\n </div>\n );\n});\n\nexport default MapWidget;\n"],"mappings":";;;AAwCA,IAAa,IAAgC,EAAK,SAAmB,EAAE,QAAQ,GAAS,SAAM,YAAS,kBAAe;CACpH,IAAM,IAAU,GAAM,WAAW,EAAE,EAC7B,IAAS,GAAM,UAAU;EAAE,KAAK;EAAG,KAAK;EAAG,EAG3C,IAAoB,GACvB,MAAsB;AACrB,EAAI,IACF,EAAY,EAA6C,GAChD,KACT,EAAQ,EAAO;IAGnB,CAAC,GAAS,EAAY,CACvB,EAEK,IAAc,GAAQ,KAAe;AA0B3C,QAxBK,EAAQ,SAyBX,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,OAAD;IAAK,WAAU;cAAf,CAEE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;MAAkC,MAAK;MAAO,QAAO;MAAe,SAAQ;gBACzF,kBAAC,QAAD;OACE,eAAc;OACd,gBAAe;OACf,aAAa;OACb,GAAE;OACF,CAAA;MACE,CAAA;KACF,CAAA,EAGN,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBACZ,EAAQ,MAAM,GAAG,EAAE,CAAC,KAAK,GAAQ,MAChC,kBAAC,OAAD;OAEE,WAAW;;;sBAGL,IAAc,kDAAkD,GAAG;;;OAGzE,eAAe,EAAkB,EAAO;OACxC,OAAO,EAAO,SAAS,EAAO;iBAThC;QAWE,kBAAC,OAAD;SACE,WAAU;SACV,OAAO,EAAE,iBAAiB,EAAO,SAAS,wBAAwB;SAClE,CAAA;QACF,kBAAC,QAAD;SAAM,WAAU;mBAAmD,EAAO;SAAY,CAAA;QACrF,EAAO,UAAU,KAAA,KAChB,kBAAC,QAAD;SAAM,WAAU;mBAA+B,EAAO,MAAM,gBAAgB;SAAQ,CAAA;QAElF;SAlBC,EAkBD,CACN;MACE,CAAA;KACF,CAAA,CACF;;GAGN,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAQ,KAAK,GAAQ,MACpB,kBAAC,OAAD;KAEE,WAAW;;;kBAGL,IAAc,mBAAmB,GAAG;;KAE1C,eAAe,EAAkB,EAAO;eAP1C,CASE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OACE,WAAU;OACV,OAAO,EAAE,iBAAiB,EAAO,SAAS,wBAAwB;OAClE,CAAA,EACF,kBAAC,QAAD;OAAM,WAAU;iBAA6B,EAAO;OAAY,CAAA,CAC5D;SACL,EAAO,UAAU,KAAA,KAChB,kBAAC,QAAD;MAAM,WAAU;gBAA2C,EAAO,MAAM,gBAAgB;MAAQ,CAAA,CAE9F;OAlBC,EAkBD,CACN;IACE,CAAA;GAGN,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,KAAD;KAAG,WAAU;eAAb;MAA0D;MAC/C,EAAO,IAAI,QAAQ,EAAE;MAAC;MAAG,EAAO,IAAI,QAAQ,EAAE;MACrD;;IACA,CAAA;GACF;MAnGJ,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IACE,WAAU;IACV,MAAK;IACL,QAAO;IACP,SAAQ;cAJV,CAME,kBAAC,QAAD;KACE,eAAc;KACd,gBAAe;KACf,aAAa;KACb,GAAE;KACF,CAAA,EACF,kBAAC,QAAD;KAAM,eAAc;KAAQ,gBAAe;KAAQ,aAAa;KAAK,GAAE;KAAqC,CAAA,CACxG;OACN,kBAAC,KAAD,EAAA,UAAG,eAAe,CAAA,CACd;;EACF,CAAA;EAmFV"}
|
|
1
|
+
{"version":3,"file":"MapWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/map-widget/MapWidget.tsx"],"sourcesContent":["/**\n * MapWidget Component\n *\n * Interactive geographic map (Leaflet + OpenStreetMap tiles) with data-bound\n * markers. Leaflet's stylesheet is loaded from a CDN at runtime (with SRI) because\n * the build-time MFE library bundle does not ship a component's CSS to the host\n * shell — consistent with the map being an inherently online feature (tiles).\n */\n\nimport { type FC, memo, useEffect, useMemo, useRef, useState } from 'react';\nimport L from 'leaflet';\nimport type { Widget } from '../../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface MapWidgetProps {\n widget: Widget;\n data?: MapData;\n onClick?: (marker: MapMarker) => void;\n /** Drilldown handler - receives selected marker data */\n onDrilldown?: (selectedData: Record<string, unknown>) => void;\n}\n\nexport interface MapMarker {\n name: string;\n lat: number;\n lng: number;\n value?: number;\n popup?: string;\n color?: string;\n}\n\nexport interface MapData {\n center?: { lat: number; lng: number };\n zoom?: number;\n markers?: MapMarker[];\n}\n\n// ============================================================================\n// Leaflet CSS (runtime CDN injection)\n// ============================================================================\n\nconst LEAFLET_CSS = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css';\n// SRI for leaflet@1.9.4/dist/leaflet.css (immutable versioned URL) — guards against a CDN compromise.\nconst LEAFLET_CSS_SRI = 'sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=';\n\nlet leafletCssRequested = false;\n\nfunction ensureLeafletCss(): void {\n // Module-level guard in addition to the DOM check so two MapWidgets mounting in\n // the same tick can't both append the <link> before either lands in the DOM.\n if (leafletCssRequested || document.getElementById('leaflet-css')) return;\n leafletCssRequested = true;\n const link = document.createElement('link');\n link.id = 'leaflet-css';\n link.rel = 'stylesheet';\n link.href = LEAFLET_CSS;\n link.integrity = LEAFLET_CSS_SRI;\n link.crossOrigin = 'anonymous';\n // Don't leak the current path (workspace/dashboard IDs) to the CDN.\n link.referrerPolicy = 'no-referrer';\n // Leaflet needs its stylesheet for correct tile/marker positioning. If the CDN\n // is unreachable or the SRI check fails the load is otherwise silent, so warn and\n // clear the guard/element to allow a later mount to retry.\n link.onerror = () => {\n console.warn('[bigconsole] Leaflet CSS failed to load; map styling may be broken.');\n leafletCssRequested = false;\n link.remove();\n };\n document.head.appendChild(link);\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nconst DEFAULT_MARKER_COLOR = 'var(--color-chart-1, #3b82f6)';\n\n/**\n * Marker colours come from widget data and are interpolated into an inline\n * `style` attribute, so restrict them to a safe character set (hex / rgb() /\n * hsl() / css var() / named colours). Anything outside that — or containing\n * style-breaking characters — falls back to the default token colour.\n */\nfunction safeColor(color: unknown): string {\n // Marker data is untrusted: a parser can emit a non-string colour (e.g. a numeric\n // status code) that slips past the lat/lng filter, so guard the type before trim().\n if (typeof color !== 'string') return DEFAULT_MARKER_COLOR;\n const trimmed = color.trim();\n if (trimmed.length > 64) return DEFAULT_MARKER_COLOR;\n if (/[;{}<>\"'\\\\]/.test(trimmed)) return DEFAULT_MARKER_COLOR;\n // Block `url()` so a marker colour can't smuggle an exfiltration beacon\n // (e.g. `url(//evil.com/pixel)`) through the otherwise-permissive char set.\n if (/\\burl\\s*\\(/i.test(trimmed)) return DEFAULT_MARKER_COLOR;\n // Allow `_` (custom-property names) and `/` (modern `rgb(r g b / a)` syntax);\n // the dangerous-char guard above still blocks anything that could break out of\n // the inline style.\n if (!/^[#a-zA-Z0-9(),.%/_\\s-]+$/.test(trimmed)) return DEFAULT_MARKER_COLOR;\n return trimmed;\n}\n\nfunction isFiniteNumber(v: unknown): v is number {\n return typeof v === 'number' && Number.isFinite(v);\n}\n\n/**\n * Normalize the bound widget data into markers + optional center/zoom. Accepts\n * the documented `{ markers, center, zoom }` shape, or a bare array of markers\n * (parser output), and drops any entry without valid coordinates.\n */\ninterface NormalizedMapData {\n markers: MapMarker[];\n /** Center explicitly supplied by the data payload — wins over markers. */\n center?: { lat: number; lng: number };\n /** Zoom explicitly supplied by the data payload. */\n zoom?: number;\n /** Center from widget config — a static fallback used only when there are no markers. */\n fallbackCenter?: { lat: number; lng: number };\n /** Zoom from widget config — fallback for the no-marker case. */\n fallbackZoom?: number;\n}\n\nfunction asLatLng(v: unknown): { lat: number; lng: number } | undefined {\n const c = v as { lat?: unknown; lng?: unknown } | undefined;\n return c && isFiniteNumber(c.lat) && isFiniteNumber(c.lng) ? { lat: c.lat, lng: c.lng } : undefined;\n}\n\nfunction normalizeMapData(data: MapData | MapMarker[] | undefined, config: Record<string, unknown>): NormalizedMapData {\n // The widget may be handed either the documented { markers, center, zoom } shape\n // or a bare marker array (parser output); narrow once so the rest is type-safe.\n const payload: MapData | undefined = Array.isArray(data) ? undefined : data;\n const rawMarkers: unknown = Array.isArray(data) ? data : data?.markers;\n const markers: MapMarker[] = Array.isArray(rawMarkers)\n ? (rawMarkers as MapMarker[]).filter((m) => m && isFiniteNumber(m.lat) && isFiniteNumber(m.lng))\n : [];\n\n // Data-payload center/zoom are an explicit intent and win over markers. Config\n // center/zoom (e.g. the WidgetRegistry default of {0,0}/2) are only a static\n // fallback for marker-less maps — otherwise that default would pin every marker\n // dataset to the world view instead of framing the markers.\n return {\n markers,\n center: asLatLng(payload?.center),\n zoom: isFiniteNumber(payload?.zoom) ? payload?.zoom : undefined,\n fallbackCenter: asLatLng(config.center),\n fallbackZoom: isFiniteNumber(config.zoom) ? (config.zoom as number) : undefined,\n };\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const MapWidget: FC<MapWidgetProps> = memo(function MapWidget({ widget, data, onClick, onDrilldown }) {\n const containerRef = useRef<HTMLDivElement>(null);\n const mapRef = useRef<L.Map | null>(null);\n // Signature of the last view we auto-framed to; lets us re-render markers on\n // every data change without snapping the viewport back when the framing\n // inputs (coords/center/zoom) are unchanged — e.g. a refetch of identical data.\n const lastFrameSigRef = useRef<string | null>(null);\n const [status, setStatus] = useState<'loading' | 'ready' | 'error'>('loading');\n\n const config = useMemo(() => (widget?.config ?? {}) as Record<string, unknown>, [widget?.config]);\n const showMarkers = config.showMarkers !== false;\n const { markers, center, zoom, fallbackCenter, fallbackZoom } = useMemo(\n () => normalizeMapData(data, config),\n [data, config]\n );\n\n // Keep the latest click handler in a ref so marker bindings don't need rebinding.\n // Assign in an effect (not during render) per the React 19 useRef guidance.\n const clickRef = useRef<(marker: MapMarker) => void>(() => undefined);\n useEffect(() => {\n clickRef.current = (marker: MapMarker) => {\n if (onDrilldown) onDrilldown(marker as unknown as Record<string, unknown>);\n else if (onClick) onClick(marker);\n };\n }, [onClick, onDrilldown]);\n\n // Initialise the map once.\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return undefined;\n ensureLeafletCss();\n\n let map: L.Map;\n try {\n map = L.map(el, { center: [20, 0], zoom: 2, scrollWheelZoom: true, attributionControl: true });\n const tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {\n attribution: '© OpenStreetMap contributors',\n maxZoom: 18,\n });\n // Leaflet reports tile failures asynchronously, so addTo() won't throw when\n // tiles are blocked/offline. Surface the error overlay only if a tile errors\n // before any tile has loaded (true offline/blocked), tolerating the odd\n // transient miss once the base map is up.\n let anyTileLoaded = false;\n tiles.on('tileload', () => {\n anyTileLoaded = true;\n // Recover if an early tile miss had shown the offline overlay — a flaky\n // connection that later succeeds shouldn't stay hidden behind it.\n setStatus((s) => (s === 'error' ? 'ready' : s));\n });\n tiles.on('tileerror', () => {\n if (!anyTileLoaded) setStatus('error');\n });\n tiles.addTo(map);\n mapRef.current = map;\n // Fresh map ⇒ force the next marker effect to (re)frame it. Without this the\n // frame-signature ref would survive a remount (e.g. React StrictMode's\n // double-invoke) and the new map would stay stuck at this init view.\n lastFrameSigRef.current = null;\n setStatus('ready');\n } catch {\n setStatus('error');\n return undefined;\n }\n\n // Widgets live in a resizable grid; keep tiles correct as the box changes.\n const ro =\n typeof ResizeObserver !== 'undefined' ? new ResizeObserver(() => mapRef.current?.invalidateSize()) : undefined;\n ro?.observe(el);\n const t = window.setTimeout(() => map.invalidateSize(), 80);\n\n return () => {\n window.clearTimeout(t);\n ro?.disconnect();\n map.remove();\n mapRef.current = null;\n };\n }, []);\n\n // Render markers + frame the view whenever the data changes.\n useEffect(() => {\n const map = mapRef.current;\n if (!map) return undefined;\n\n const layer = L.layerGroup();\n if (showMarkers) {\n for (const marker of markers) {\n // Build the pin as a DOM element (not an HTML string) so the marker colour\n // is assigned via the style API and can never be interpreted as markup —\n // defense-in-depth alongside safeColor().\n const pin = document.createElement('div');\n Object.assign(pin.style, {\n width: '14px',\n height: '14px',\n borderRadius: '50% 50% 50% 0',\n transform: 'rotate(-45deg)',\n background: safeColor(marker.color),\n border: '2px solid #fff',\n boxShadow: '0 1px 4px rgba(0,0,0,.4)',\n });\n const icon = L.divIcon({\n className: 'bc-map-marker',\n html: pin,\n iconSize: [14, 14],\n iconAnchor: [7, 14],\n });\n const m = L.marker([marker.lat, marker.lng], { icon }).addTo(layer);\n\n // bindPopup treats a string as HTML; build a text node so marker-supplied\n // text can never be interpreted as markup (defense-in-depth).\n const popupEl = document.createElement('div');\n const title = document.createElement('div');\n title.style.fontWeight = '600';\n title.textContent = marker.popup || marker.name || 'Location';\n popupEl.appendChild(title);\n if (isFiniteNumber(marker.value)) {\n const val = document.createElement('div');\n val.textContent = marker.value.toLocaleString();\n popupEl.appendChild(val);\n }\n m.bindPopup(popupEl);\n m.on('click', () => clickRef.current(marker));\n }\n }\n layer.addTo(map);\n\n // Only (re)frame the view when the framing inputs actually change — otherwise\n // a data-prop reference change (e.g. a refetch returning identical markers)\n // would discard the user's current pan/zoom.\n const frameSig = JSON.stringify({\n m: markers.map((mk) => [mk.lat, mk.lng]),\n c: center ? [center.lat, center.lng] : null,\n z: zoom ?? null,\n fc: fallbackCenter ? [fallbackCenter.lat, fallbackCenter.lng] : null,\n fz: fallbackZoom ?? null,\n });\n if (frameSig !== lastFrameSigRef.current) {\n lastFrameSigRef.current = frameSig;\n // Framing priority: data-payload center → frame the markers → config fallback\n // center (no markers) → config/payload zoom only → world view. Markers win over\n // the config default so a palette-created map frames its data instead of {0,0}.\n if (center) {\n map.setView([center.lat, center.lng], zoom ?? map.getZoom());\n } else if (markers.length === 1) {\n map.setView([markers[0].lat, markers[0].lng], zoom ?? 8);\n } else if (markers.length > 1) {\n const bounds = L.latLngBounds(markers.map((m) => [m.lat, m.lng] as [number, number]));\n map.fitBounds(bounds, { padding: [32, 32], maxZoom: 12 });\n } else if (fallbackCenter) {\n map.setView([fallbackCenter.lat, fallbackCenter.lng], zoom ?? fallbackZoom ?? map.getZoom());\n } else if (isFiniteNumber(zoom ?? fallbackZoom)) {\n map.setZoom((zoom ?? fallbackZoom) as number);\n }\n }\n\n const resize = window.setTimeout(() => map.invalidateSize(), 0);\n\n return () => {\n window.clearTimeout(resize);\n layer.remove();\n };\n }, [markers, center, zoom, fallbackCenter, fallbackZoom, showMarkers]);\n\n return (\n <div className=\"relative h-full w-full overflow-hidden rounded-lg bg-bg-muted\">\n <div ref={containerRef} className=\"h-full w-full\" />\n\n {status !== 'error' && markers.length === 0 && (\n <div className=\"pointer-events-none absolute inset-0 z-[500] flex items-center justify-center bg-bg-muted/70 text-sm text-text-secondary\">\n <div className=\"text-center\">\n <svg\n className=\"mx-auto mb-2 h-12 w-12 text-text-tertiary\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z\"\n />\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M15 11a3 3 0 11-6 0 3 3 0 016 0z\"\n />\n </svg>\n <p>No map data</p>\n </div>\n </div>\n )}\n\n {status === 'error' && (\n <div className=\"absolute inset-0 z-[500] flex items-center justify-center bg-bg-muted px-4 text-center text-sm text-text-secondary\">\n The map needs an internet connection and couldn’t load.\n </div>\n )}\n </div>\n );\n});\n\nexport default MapWidget;\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burdenoff/microfe-bigconsole",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.613.1",
|
|
4
4
|
"description": "BigConsole - AI-powered analytics and dashboard platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"graphql": "^16.10.0",
|
|
54
54
|
"graphql-ws": "^5.16.0",
|
|
55
55
|
"immer": "^11.1.0",
|
|
56
|
+
"leaflet": "^1.9.4",
|
|
56
57
|
"lucide-react": "^0.468.0",
|
|
57
58
|
"nanoid": "^5.1.6",
|
|
58
59
|
"popmotion": "^11.0.5",
|
|
@@ -78,6 +79,7 @@
|
|
|
78
79
|
"@graphql-codegen/typescript-react-apollo": "^4.0.0",
|
|
79
80
|
"@types/react": "^19.0.2",
|
|
80
81
|
"@types/react-dom": "^19.0.2",
|
|
82
|
+
"@types/leaflet": "^1.9.21",
|
|
81
83
|
"@types/react-grid-layout": "^1.3.5",
|
|
82
84
|
"@types/uuid": "^11.0.0",
|
|
83
85
|
"@vitejs/plugin-react": "^4.3.4",
|