@seed-ship/mcp-ui-solid 6.1.0 → 6.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/dist/components/CarouselRenderer.cjs +41 -30
- package/dist/components/CarouselRenderer.cjs.map +1 -1
- package/dist/components/CarouselRenderer.d.ts.map +1 -1
- package/dist/components/CarouselRenderer.js +42 -31
- package/dist/components/CarouselRenderer.js.map +1 -1
- package/dist/components/CodeBlockRenderer.cjs +88 -25
- package/dist/components/CodeBlockRenderer.cjs.map +1 -1
- package/dist/components/CodeBlockRenderer.d.ts.map +1 -1
- package/dist/components/CodeBlockRenderer.js +89 -26
- package/dist/components/CodeBlockRenderer.js.map +1 -1
- package/dist/components/ImageGalleryRenderer.cjs +101 -77
- package/dist/components/ImageGalleryRenderer.cjs.map +1 -1
- package/dist/components/ImageGalleryRenderer.d.ts.map +1 -1
- package/dist/components/ImageGalleryRenderer.js +102 -78
- package/dist/components/ImageGalleryRenderer.js.map +1 -1
- package/dist/components/MapRenderer.cjs +94 -34
- package/dist/components/MapRenderer.cjs.map +1 -1
- package/dist/components/MapRenderer.d.ts.map +1 -1
- package/dist/components/MapRenderer.js +107 -47
- package/dist/components/MapRenderer.js.map +1 -1
- package/dist/components/VideoRenderer.cjs +95 -74
- package/dist/components/VideoRenderer.cjs.map +1 -1
- package/dist/components/VideoRenderer.d.ts.map +1 -1
- package/dist/components/VideoRenderer.js +96 -75
- package/dist/components/VideoRenderer.js.map +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/components/CarouselRenderer.tsx +9 -1
- package/src/components/CodeBlockRenderer.tsx +65 -5
- package/src/components/ImageGalleryRenderer.test.tsx +18 -7
- package/src/components/ImageGalleryRenderer.tsx +22 -3
- package/src/components/MapRenderer.tsx +68 -14
- package/src/components/VideoRenderer.tsx +14 -4
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MapRenderer.cjs","sources":["../../src/components/MapRenderer.tsx"],"sourcesContent":["/**\n * MapRenderer - Interactive map Component\n * Sprint 6: Markers + clustering\n * v3.1.0: GeoJSON, choropleth, popups, multi-layer, PMTiles\n */\n\nimport { Component, createEffect, onCleanup, createSignal, Show } from 'solid-js'\nimport { isServer } from 'solid-js/web'\nimport type { UIComponent, MapComponentParams, MapClusterOptions, MapGeoJSONStyle, MapPopupConfig, MapLayer, MapPMTilesConfig } from '../types'\n\n// Lazy load leaflet (it doesn't support SSR well)\nlet L: any = null\n// Track if marker cluster CSS has been loaded\nlet clusterCssLoaded = false\n\nexport interface MapRendererProps {\n /**\n * UIComponent containing map params\n */\n component?: UIComponent\n\n /**\n * Direct map params\n */\n params?: MapComponentParams\n}\n\n// ─── Helpers ────────────────────────────────────────────────\n\n/**\n * Resolve choropleth color for a feature based on property value and scale stops.\n */\nfunction getChoroplethColor(\n value: unknown,\n scale: Array<[number, string]>,\n fallback: string\n): string {\n if (value == null || typeof value !== 'number' || !isFinite(value)) return fallback\n\n // Scale is sorted ascending: [[0, '#eff3ff'], [100, '#084594']]\n if (scale.length === 0) return fallback\n if (value <= scale[0][0]) return scale[0][1]\n if (value >= scale[scale.length - 1][0]) return scale[scale.length - 1][1]\n\n // Find surrounding stops and interpolate (use upper bracket color)\n for (let i = 1; i < scale.length; i++) {\n if (value <= scale[i][0]) return scale[i][1]\n }\n return scale[scale.length - 1][1]\n}\n\n/**\n * Build a Leaflet style function from MapGeoJSONStyle config.\n */\nfunction buildStyleFn(style: MapGeoJSONStyle | undefined): (feature: any) => Record<string, unknown> {\n if (!style) {\n return () => ({\n fillColor: '#3388ff',\n fillOpacity: 0.6,\n color: '#333',\n weight: 1,\n opacity: 1,\n })\n }\n\n return (feature: any) => {\n let fillColor = style.fillColor || '#3388ff'\n\n // Choropleth: override fillColor based on feature property\n if (style.choroplethField && style.choroplethScale && feature?.properties) {\n const val = feature.properties[style.choroplethField]\n fillColor = getChoroplethColor(val, style.choroplethScale, style.choroplethFallback || '#ccc')\n }\n\n return {\n fillColor,\n fillOpacity: style.fillOpacity ?? 0.6,\n color: style.strokeColor || '#333',\n weight: style.strokeWeight ?? 1,\n opacity: style.strokeOpacity ?? 1,\n }\n }\n}\n\n/**\n * Build popup HTML from a feature's properties using popup config.\n */\nfunction buildPopupContent(feature: any, popup: MapPopupConfig | undefined): string | null {\n if (!popup || !feature?.properties) return null\n const props = feature.properties\n\n // Custom template\n if (popup.template) {\n return popup.template.replace(/\\{\\{(\\w+)\\}\\}/g, (_, key) => {\n const val = props[key]\n return val != null ? String(val) : ''\n })\n }\n\n // Auto-generated popup\n const parts: string[] = []\n\n if (popup.titleField && props[popup.titleField] != null) {\n parts.push(`<strong>${escapeHtml(String(props[popup.titleField]))}</strong>`)\n }\n\n const fields = popup.fields || Object.keys(props).slice(0, 8)\n for (const key of fields) {\n if (key === popup.titleField) continue\n const val = props[key]\n if (val == null) continue\n const formatted = typeof val === 'number' ? val.toLocaleString('fr-FR') : String(val)\n parts.push(`<span style=\"color:#666;font-size:11px\">${escapeHtml(key)}</span>: ${escapeHtml(formatted)}`)\n }\n\n return parts.join('<br/>')\n}\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n}\n\n/**\n * Add a GeoJSON layer to the map with style and popup support.\n * Returns the layer for bounds calculation.\n */\nfunction addGeoJSONLayer(\n mapInst: any,\n leaflet: any,\n geojson: unknown,\n style?: MapGeoJSONStyle,\n popup?: MapPopupConfig\n): any {\n const styleFn = buildStyleFn(style)\n\n const layer = leaflet.geoJSON(geojson, {\n style: styleFn,\n pointToLayer: (feature: any, latlng: any) => {\n // Render points as circle markers for consistency\n const s = styleFn(feature)\n return leaflet.circleMarker(latlng, {\n radius: 6,\n fillColor: s.fillColor,\n fillOpacity: s.fillOpacity,\n color: s.color,\n weight: s.weight,\n opacity: s.opacity,\n })\n },\n onEachFeature: (feature: any, featureLayer: any) => {\n const html = buildPopupContent(feature, popup)\n if (html) {\n featureLayer.bindPopup(html, { maxWidth: 300 })\n }\n },\n })\n\n layer.addTo(mapInst)\n return layer\n}\n\n// ─── Component ──────────────────────────────────────────────\n\nexport const MapRenderer: Component<MapRendererProps> = (props) => {\n let mapContainer: HTMLDivElement | undefined\n let mapInstance: any = null\n const [isLeafletLoaded, setIsLeafletLoaded] = createSignal(false)\n const [error, setError] = createSignal<string | null>(null)\n\n const params = () => props.params || (props.component?.params as MapComponentParams)\n\n // Initialize Map\n createEffect(async () => {\n if (isServer) return // Don't run on server\n\n if (!L) {\n try {\n const module = await import('leaflet')\n L = module.default || module\n await import('leaflet/dist/leaflet.css') // Import CSS\n setIsLeafletLoaded(true)\n } catch (e) {\n console.warn('Failed to load leaflet', e)\n setError('Map library could not be loaded.')\n return\n }\n } else {\n setIsLeafletLoaded(true)\n }\n\n if (isLeafletLoaded() && mapContainer && !mapInstance) {\n const p = params()\n const center = p?.center || [51.505, -0.09] // Default to London\n const zoom = p?.zoom || 13\n\n mapInstance = L.map(mapContainer, {\n zoomControl: p?.zoomControl !== false,\n scrollWheelZoom: p?.scrollWheelZoom !== false,\n attributionControl: false\n }).setView(center, zoom)\n\n // Add OpenStreetMap tile layer\n const tileLayerUrl = p?.tileLayer || 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'\n L.tileLayer(tileLayerUrl, {\n attribution: p?.attribution || '© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors'\n }).addTo(mapInstance)\n\n if (p?.attribution !== '') {\n L.control.attribution({ prefix: false }).addTo(mapInstance)\n }\n\n // Fix marker icons (Leaflet issue with bundlers)\n delete (L.Icon.Default.prototype as any)._getIconUrl\n L.Icon.Default.mergeOptions({\n iconRetinaUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png',\n iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',\n shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',\n })\n }\n\n // Update markers and view\n if (mapInstance && L) {\n const p = params()\n const allBoundsLayers: any[] = []\n\n // Clear existing layers (markers, cluster groups, GeoJSON)\n mapInstance.eachLayer((layer: any) => {\n if (layer instanceof L.Marker || layer instanceof L.GeoJSON\n || layer instanceof L.CircleMarker\n || layer._group || layer._featureGroup) {\n mapInstance.removeLayer(layer)\n }\n })\n\n // ─── Markers (legacy) ────────────────────────\n const markers: any[] = []\n const shouldCluster = p?.clustering && p?.markers && p.markers.length > 0\n\n if (shouldCluster) {\n try {\n await import('leaflet.markercluster')\n if (!clusterCssLoaded) {\n await import('leaflet.markercluster/dist/MarkerCluster.css')\n await import('leaflet.markercluster/dist/MarkerCluster.Default.css')\n clusterCssLoaded = true\n }\n const clusterOpts: MapClusterOptions = typeof p.clustering === 'object' ? p.clustering : {}\n const clusterGroup = (L as any).markerClusterGroup({\n maxClusterRadius: clusterOpts.maxClusterRadius ?? 80,\n spiderfyOnMaxZoom: clusterOpts.spiderfyOnMaxZoom ?? true,\n showCoverageOnHover: clusterOpts.showCoverageOnHover ?? true,\n disableClusteringAtZoom: clusterOpts.disableClusteringAtZoom,\n animate: clusterOpts.animateAddingMarkers ?? true\n })\n p?.markers?.forEach(marker => {\n const m = L.marker(marker.position)\n if (marker.tooltip) m.bindTooltip(marker.tooltip)\n if (marker.popup) m.bindPopup(marker.popup)\n clusterGroup.addLayer(m)\n markers.push(m)\n })\n mapInstance.addLayer(clusterGroup)\n } catch {\n p?.markers?.forEach(marker => {\n const m = L.marker(marker.position).addTo(mapInstance)\n if (marker.tooltip) m.bindTooltip(marker.tooltip)\n if (marker.popup) m.bindPopup(marker.popup)\n markers.push(m)\n })\n }\n } else {\n p?.markers?.forEach(marker => {\n const m = L.marker(marker.position).addTo(mapInstance)\n if (marker.tooltip) m.bindTooltip(marker.tooltip)\n if (marker.popup) m.bindPopup(marker.popup)\n markers.push(m)\n })\n }\n\n if (markers.length > 0) {\n allBoundsLayers.push(...markers)\n }\n\n // ─── GeoJSON (v3.1.0) ───────────────────────\n if (p?.geojson) {\n const geoLayer = addGeoJSONLayer(mapInstance, L, p.geojson, p.geojsonStyle, p.popup)\n allBoundsLayers.push(geoLayer)\n }\n\n // ─── Named layers (v3.1.0) ──────────────────\n if (p?.layers && p.layers.length > 0) {\n const overlays: Record<string, any> = {}\n\n for (const layerDef of p.layers) {\n const geoLayer = addGeoJSONLayer(\n mapInstance, L,\n layerDef.geojson,\n layerDef.style || p?.geojsonStyle,\n layerDef.popup || p?.popup\n )\n\n overlays[layerDef.name] = geoLayer\n allBoundsLayers.push(geoLayer)\n\n // Respect initial visibility\n if (layerDef.visible === false) {\n mapInstance.removeLayer(geoLayer)\n }\n }\n\n // Add layer control if multiple layers\n if (Object.keys(overlays).length > 1) {\n L.control.layers(null, overlays, { collapsed: true }).addTo(mapInstance)\n }\n }\n\n // ─── PMTiles (v3.1.0) ────────────────────────\n if (p?.pmtiles) {\n try {\n // @ts-ignore — optional peer dependency, may not be installed\n const protomaps = await import(/* @vite-ignore */ 'protomaps-leaflet')\n const pmConfig = p.pmtiles\n\n const paintRules = pmConfig.paintRules?.map(rule => ({\n dataLayer: rule.dataLayer,\n symbolizer: new (protomaps as any)[\n rule.symbolizer === 'polygon' ? 'PolygonSymbolizer' :\n rule.symbolizer === 'line' ? 'LineSymbolizer' :\n 'CircleSymbolizer'\n ]({\n fill: rule.color || '#3388ff',\n stroke: rule.color || '#333',\n width: rule.width ?? 1,\n opacity: rule.opacity ?? 0.6,\n }),\n })) || []\n\n const labelRules = pmConfig.labelRules?.map(rule => ({\n dataLayer: rule.dataLayer,\n symbolizer: new (protomaps as any).TextSymbolizer({\n label_props: [rule.textField],\n fontSize: rule.fontSize ?? 12,\n }),\n })) || []\n\n const pmLayer = (protomaps as any).leafletLayer({\n url: pmConfig.url,\n attribution: pmConfig.attribution,\n paintRules,\n labelRules,\n maxZoom: pmConfig.maxZoom,\n minZoom: pmConfig.minZoom,\n })\n\n pmLayer.addTo(mapInstance)\n } catch (e) {\n console.warn('[MCP-UI] Failed to load protomaps-leaflet for PMTiles:', e)\n }\n }\n\n // ─── Fit bounds ─────────────────────────────\n if (p?.fitBounds && allBoundsLayers.length > 0) {\n const group = L.featureGroup(allBoundsLayers)\n const bounds = group.getBounds()\n if (bounds.isValid()) {\n mapInstance.fitBounds(bounds.pad(0.1))\n }\n } else if (p?.center) {\n mapInstance.setView(p.center, p.zoom || mapInstance.getZoom())\n }\n }\n })\n\n // Cleanup\n onCleanup(() => {\n if (mapInstance) {\n mapInstance.remove()\n mapInstance = null\n }\n })\n\n return (\n <div class={`w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${params()?.className || ''}`}>\n <Show when={error()}>\n <div class=\"p-4 text-red-500 bg-red-50 dark:bg-red-900/20 text-center\">\n {error()}\n </div>\n </Show>\n <Show when={!error()}>\n <div\n ref={mapContainer}\n style={{ height: params()?.height || '400px', width: '100%', \"z-index\": 0 }}\n class=\"relative z-0\"\n />\n </Show>\n </div>\n )\n}\n"],"names":["L","clusterCssLoaded","getChoroplethColor","value","scale","fallback","isFinite","length","i","buildStyleFn","style","fillColor","fillOpacity","color","weight","opacity","feature","choroplethField","choroplethScale","properties","val","choroplethFallback","strokeColor","strokeWeight","strokeOpacity","buildPopupContent","popup","props","template","replace","_","key","String","parts","titleField","push","escapeHtml","fields","Object","keys","slice","formatted","toLocaleString","join","str","addGeoJSONLayer","mapInst","leaflet","geojson","styleFn","layer","geoJSON","pointToLayer","latlng","s","circleMarker","radius","onEachFeature","featureLayer","html","bindPopup","maxWidth","addTo","MapRenderer","mapContainer","mapInstance","isLeafletLoaded","setIsLeafletLoaded","createSignal","error","setError","params","component","createEffect","isServer","module","default","e","console","warn","p","center","zoom","map","zoomControl","scrollWheelZoom","attributionControl","setView","tileLayerUrl","tileLayer","attribution","control","prefix","Icon","Default","prototype","_getIconUrl","mergeOptions","iconRetinaUrl","iconUrl","shadowUrl","allBoundsLayers","eachLayer","Marker","GeoJSON","CircleMarker","_group","_featureGroup","removeLayer","markers","shouldCluster","clustering","clusterOpts","clusterGroup","markerClusterGroup","maxClusterRadius","spiderfyOnMaxZoom","showCoverageOnHover","disableClusteringAtZoom","animate","animateAddingMarkers","forEach","marker","m","position","tooltip","bindTooltip","addLayer","geoLayer","geojsonStyle","layers","overlays","layerDef","name","visible","collapsed","pmtiles","protomaps","pmConfig","paintRules","rule","dataLayer","symbolizer","fill","stroke","width","labelRules","TextSymbolizer","label_props","textField","fontSize","pmLayer","leafletLayer","url","maxZoom","minZoom","fitBounds","group","featureGroup","bounds","getBounds","isValid","pad","getZoom","onCleanup","remove","_el$","_$getNextElement","_tmpl$3","_el$4","firstChild","_el$5","_co$","_$getNextMarker","nextSibling","_el$6","_el$7","_co$2","_$insert","_$createComponent","Show","when","children","_el$2","_tmpl$","_el$3","_tmpl$2","_ref$","_$use","_$effect","_$p","_$setStyleProperty","height","_$className","className"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MapRenderer.cjs","sources":["../../src/components/MapRenderer.tsx"],"sourcesContent":["/**\n * MapRenderer - Interactive map Component\n * Sprint 6: Markers + clustering\n * v3.1.0: GeoJSON, choropleth, popups, multi-layer, PMTiles\n */\n\nimport { Component, createEffect, onCleanup, createSignal, Show } from 'solid-js'\nimport { isServer } from 'solid-js/web'\nimport type { UIComponent, MapComponentParams, MapClusterOptions, MapGeoJSONStyle, MapPopupConfig, MapLayer, MapPMTilesConfig } from '../types'\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper'\n\n// Lazy load leaflet (it doesn't support SSR well)\nlet L: any = null\n// Track if marker cluster CSS has been loaded\nlet clusterCssLoaded = false\n\nexport interface MapRendererProps {\n /**\n * UIComponent containing map params\n */\n component?: UIComponent\n\n /**\n * Direct map params\n */\n params?: MapComponentParams\n}\n\n// ─── Helpers ────────────────────────────────────────────────\n\n/**\n * Resolve choropleth color for a feature based on property value and scale stops.\n */\nfunction getChoroplethColor(\n value: unknown,\n scale: Array<[number, string]>,\n fallback: string\n): string {\n if (value == null || typeof value !== 'number' || !isFinite(value)) return fallback\n\n // Scale is sorted ascending: [[0, '#eff3ff'], [100, '#084594']]\n if (scale.length === 0) return fallback\n if (value <= scale[0][0]) return scale[0][1]\n if (value >= scale[scale.length - 1][0]) return scale[scale.length - 1][1]\n\n // Find surrounding stops and interpolate (use upper bracket color)\n for (let i = 1; i < scale.length; i++) {\n if (value <= scale[i][0]) return scale[i][1]\n }\n return scale[scale.length - 1][1]\n}\n\n/**\n * Build a Leaflet style function from MapGeoJSONStyle config.\n */\nfunction buildStyleFn(style: MapGeoJSONStyle | undefined): (feature: any) => Record<string, unknown> {\n if (!style) {\n return () => ({\n fillColor: '#3388ff',\n fillOpacity: 0.6,\n color: '#333',\n weight: 1,\n opacity: 1,\n })\n }\n\n return (feature: any) => {\n let fillColor = style.fillColor || '#3388ff'\n\n // Choropleth: override fillColor based on feature property\n if (style.choroplethField && style.choroplethScale && feature?.properties) {\n const val = feature.properties[style.choroplethField]\n fillColor = getChoroplethColor(val, style.choroplethScale, style.choroplethFallback || '#ccc')\n }\n\n return {\n fillColor,\n fillOpacity: style.fillOpacity ?? 0.6,\n color: style.strokeColor || '#333',\n weight: style.strokeWeight ?? 1,\n opacity: style.strokeOpacity ?? 1,\n }\n }\n}\n\n/**\n * Build popup HTML from a feature's properties using popup config.\n */\nfunction buildPopupContent(feature: any, popup: MapPopupConfig | undefined): string | null {\n if (!popup || !feature?.properties) return null\n const props = feature.properties\n\n // Custom template\n if (popup.template) {\n return popup.template.replace(/\\{\\{(\\w+)\\}\\}/g, (_, key) => {\n const val = props[key]\n return val != null ? String(val) : ''\n })\n }\n\n // Auto-generated popup\n const parts: string[] = []\n\n if (popup.titleField && props[popup.titleField] != null) {\n parts.push(`<strong>${escapeHtml(String(props[popup.titleField]))}</strong>`)\n }\n\n const fields = popup.fields || Object.keys(props).slice(0, 8)\n for (const key of fields) {\n if (key === popup.titleField) continue\n const val = props[key]\n if (val == null) continue\n const formatted = typeof val === 'number' ? val.toLocaleString('fr-FR') : String(val)\n parts.push(`<span style=\"color:#666;font-size:11px\">${escapeHtml(key)}</span>: ${escapeHtml(formatted)}`)\n }\n\n return parts.join('<br/>')\n}\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n}\n\n/**\n * Add a GeoJSON layer to the map with style and popup support.\n * Returns the layer for bounds calculation.\n */\nfunction addGeoJSONLayer(\n mapInst: any,\n leaflet: any,\n geojson: unknown,\n style?: MapGeoJSONStyle,\n popup?: MapPopupConfig\n): any {\n const styleFn = buildStyleFn(style)\n\n const layer = leaflet.geoJSON(geojson, {\n style: styleFn,\n pointToLayer: (feature: any, latlng: any) => {\n // Render points as circle markers for consistency\n const s = styleFn(feature)\n return leaflet.circleMarker(latlng, {\n radius: 6,\n fillColor: s.fillColor,\n fillOpacity: s.fillOpacity,\n color: s.color,\n weight: s.weight,\n opacity: s.opacity,\n })\n },\n onEachFeature: (feature: any, featureLayer: any) => {\n const html = buildPopupContent(feature, popup)\n if (html) {\n featureLayer.bindPopup(html, { maxWidth: 300 })\n }\n },\n })\n\n layer.addTo(mapInst)\n return layer\n}\n\n// ─── Component ──────────────────────────────────────────────\n\n/**\n * Build a GeoJSON FeatureCollection from the map's `markers` (and any\n * inlined GeoJSON layers, when present). Used by the \"Copy data\" button\n * shipped via `<ExpandableWrapper>` (v6.2.0). Best-effort — clusters,\n * tile layers, and choropleth-only data don't get round-tripped.\n */\nfunction mapToGeoJSON(p: MapComponentParams | undefined): string {\n if (!p) return '{\"type\":\"FeatureCollection\",\"features\":[]}'\n const features: any[] = []\n for (const marker of p.markers ?? []) {\n const pos: any = marker.position as any\n // Accept both [lat, lng] tuple and {lat, lng} object shapes (v5.0.2 spec)\n const lat = Array.isArray(pos) ? pos[0] : pos?.lat\n const lng = Array.isArray(pos) ? pos[1] : pos?.lng\n if (typeof lat !== 'number' || typeof lng !== 'number') continue\n features.push({\n type: 'Feature',\n geometry: { type: 'Point', coordinates: [lng, lat] },\n properties: {\n ...(marker.tooltip ? { tooltip: marker.tooltip } : {}),\n ...(marker.popup ? { popup: marker.popup } : {}),\n },\n })\n }\n return JSON.stringify({ type: 'FeatureCollection', features }, null, 2)\n}\n\nexport const MapRenderer: Component<MapRendererProps> = (props) => {\n let mapContainer: HTMLDivElement | undefined\n let mapInstance: any = null\n const [isLeafletLoaded, setIsLeafletLoaded] = createSignal(false)\n const [error, setError] = createSignal<string | null>(null)\n const isExpanded = useExpanded()\n\n const params = () => props.params || (props.component?.params as MapComponentParams)\n\n // v6.2.0 — Leaflet has to be told to re-measure when its container\n // resizes (e.g. transitioning to fullscreen via ExpandableWrapper).\n // We give the DOM a tick to settle the new dimensions, then ask\n // Leaflet to reflow tiles.\n createEffect(() => {\n const expanded = isExpanded()\n if (!mapInstance) return\n // Read the signal so the effect re-runs on toggle ; the value is\n // observed for its side effects on layout.\n void expanded\n setTimeout(() => mapInstance?.invalidateSize?.(), 100)\n })\n\n // Initialize Map\n createEffect(async () => {\n if (isServer) return // Don't run on server\n\n if (!L) {\n try {\n const module = await import('leaflet')\n L = module.default || module\n await import('leaflet/dist/leaflet.css') // Import CSS\n setIsLeafletLoaded(true)\n } catch (e) {\n console.warn('Failed to load leaflet', e)\n setError('Map library could not be loaded.')\n return\n }\n } else {\n setIsLeafletLoaded(true)\n }\n\n if (isLeafletLoaded() && mapContainer && !mapInstance) {\n const p = params()\n const center = p?.center || [51.505, -0.09] // Default to London\n const zoom = p?.zoom || 13\n\n mapInstance = L.map(mapContainer, {\n zoomControl: p?.zoomControl !== false,\n scrollWheelZoom: p?.scrollWheelZoom !== false,\n attributionControl: false\n }).setView(center, zoom)\n\n // Add OpenStreetMap tile layer\n const tileLayerUrl = p?.tileLayer || 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'\n L.tileLayer(tileLayerUrl, {\n attribution: p?.attribution || '© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors'\n }).addTo(mapInstance)\n\n if (p?.attribution !== '') {\n L.control.attribution({ prefix: false }).addTo(mapInstance)\n }\n\n // Fix marker icons (Leaflet issue with bundlers)\n delete (L.Icon.Default.prototype as any)._getIconUrl\n L.Icon.Default.mergeOptions({\n iconRetinaUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png',\n iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',\n shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',\n })\n }\n\n // Update markers and view\n if (mapInstance && L) {\n const p = params()\n const allBoundsLayers: any[] = []\n\n // Clear existing layers (markers, cluster groups, GeoJSON)\n mapInstance.eachLayer((layer: any) => {\n if (layer instanceof L.Marker || layer instanceof L.GeoJSON\n || layer instanceof L.CircleMarker\n || layer._group || layer._featureGroup) {\n mapInstance.removeLayer(layer)\n }\n })\n\n // ─── Markers (legacy) ────────────────────────\n const markers: any[] = []\n const shouldCluster = p?.clustering && p?.markers && p.markers.length > 0\n\n if (shouldCluster) {\n try {\n await import('leaflet.markercluster')\n if (!clusterCssLoaded) {\n await import('leaflet.markercluster/dist/MarkerCluster.css')\n await import('leaflet.markercluster/dist/MarkerCluster.Default.css')\n clusterCssLoaded = true\n }\n const clusterOpts: MapClusterOptions = typeof p.clustering === 'object' ? p.clustering : {}\n const clusterGroup = (L as any).markerClusterGroup({\n maxClusterRadius: clusterOpts.maxClusterRadius ?? 80,\n spiderfyOnMaxZoom: clusterOpts.spiderfyOnMaxZoom ?? true,\n showCoverageOnHover: clusterOpts.showCoverageOnHover ?? true,\n disableClusteringAtZoom: clusterOpts.disableClusteringAtZoom,\n animate: clusterOpts.animateAddingMarkers ?? true\n })\n p?.markers?.forEach(marker => {\n const m = L.marker(marker.position)\n if (marker.tooltip) m.bindTooltip(marker.tooltip)\n if (marker.popup) m.bindPopup(marker.popup)\n clusterGroup.addLayer(m)\n markers.push(m)\n })\n mapInstance.addLayer(clusterGroup)\n } catch {\n p?.markers?.forEach(marker => {\n const m = L.marker(marker.position).addTo(mapInstance)\n if (marker.tooltip) m.bindTooltip(marker.tooltip)\n if (marker.popup) m.bindPopup(marker.popup)\n markers.push(m)\n })\n }\n } else {\n p?.markers?.forEach(marker => {\n const m = L.marker(marker.position).addTo(mapInstance)\n if (marker.tooltip) m.bindTooltip(marker.tooltip)\n if (marker.popup) m.bindPopup(marker.popup)\n markers.push(m)\n })\n }\n\n if (markers.length > 0) {\n allBoundsLayers.push(...markers)\n }\n\n // ─── GeoJSON (v3.1.0) ───────────────────────\n if (p?.geojson) {\n const geoLayer = addGeoJSONLayer(mapInstance, L, p.geojson, p.geojsonStyle, p.popup)\n allBoundsLayers.push(geoLayer)\n }\n\n // ─── Named layers (v3.1.0) ──────────────────\n if (p?.layers && p.layers.length > 0) {\n const overlays: Record<string, any> = {}\n\n for (const layerDef of p.layers) {\n const geoLayer = addGeoJSONLayer(\n mapInstance, L,\n layerDef.geojson,\n layerDef.style || p?.geojsonStyle,\n layerDef.popup || p?.popup\n )\n\n overlays[layerDef.name] = geoLayer\n allBoundsLayers.push(geoLayer)\n\n // Respect initial visibility\n if (layerDef.visible === false) {\n mapInstance.removeLayer(geoLayer)\n }\n }\n\n // Add layer control if multiple layers\n if (Object.keys(overlays).length > 1) {\n L.control.layers(null, overlays, { collapsed: true }).addTo(mapInstance)\n }\n }\n\n // ─── PMTiles (v3.1.0) ────────────────────────\n if (p?.pmtiles) {\n try {\n // @ts-ignore — optional peer dependency, may not be installed\n const protomaps = await import(/* @vite-ignore */ 'protomaps-leaflet')\n const pmConfig = p.pmtiles\n\n const paintRules = pmConfig.paintRules?.map(rule => ({\n dataLayer: rule.dataLayer,\n symbolizer: new (protomaps as any)[\n rule.symbolizer === 'polygon' ? 'PolygonSymbolizer' :\n rule.symbolizer === 'line' ? 'LineSymbolizer' :\n 'CircleSymbolizer'\n ]({\n fill: rule.color || '#3388ff',\n stroke: rule.color || '#333',\n width: rule.width ?? 1,\n opacity: rule.opacity ?? 0.6,\n }),\n })) || []\n\n const labelRules = pmConfig.labelRules?.map(rule => ({\n dataLayer: rule.dataLayer,\n symbolizer: new (protomaps as any).TextSymbolizer({\n label_props: [rule.textField],\n fontSize: rule.fontSize ?? 12,\n }),\n })) || []\n\n const pmLayer = (protomaps as any).leafletLayer({\n url: pmConfig.url,\n attribution: pmConfig.attribution,\n paintRules,\n labelRules,\n maxZoom: pmConfig.maxZoom,\n minZoom: pmConfig.minZoom,\n })\n\n pmLayer.addTo(mapInstance)\n } catch (e) {\n console.warn('[MCP-UI] Failed to load protomaps-leaflet for PMTiles:', e)\n }\n }\n\n // ─── Fit bounds ─────────────────────────────\n if (p?.fitBounds && allBoundsLayers.length > 0) {\n const group = L.featureGroup(allBoundsLayers)\n const bounds = group.getBounds()\n if (bounds.isValid()) {\n mapInstance.fitBounds(bounds.pad(0.1))\n }\n } else if (p?.center) {\n mapInstance.setView(p.center, p.zoom || mapInstance.getZoom())\n }\n }\n })\n\n // Cleanup\n onCleanup(() => {\n if (mapInstance) {\n mapInstance.remove()\n mapInstance = null\n }\n })\n\n return (\n <ExpandableWrapper\n title={'Map'}\n copyData={mapToGeoJSON(params())}\n copyLabel=\"Copy markers as GeoJSON\"\n >\n <div class={`w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${params()?.className || ''} ${\n isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''\n }`}>\n <Show when={error()}>\n <div class=\"p-4 text-red-500 bg-red-50 dark:bg-red-900/20 text-center\">\n {error()}\n </div>\n </Show>\n <Show when={!error()}>\n <div\n ref={mapContainer}\n style={\n isExpanded()\n ? { height: '100%', width: '100%', 'z-index': 0 }\n : { height: params()?.height || '400px', width: '100%', 'z-index': 0 }\n }\n class={`relative z-0 ${isExpanded() ? 'flex-1 min-h-0' : ''}`}\n />\n </Show>\n </div>\n </ExpandableWrapper>\n )\n}\n"],"names":["L","clusterCssLoaded","getChoroplethColor","value","scale","fallback","isFinite","length","i","buildStyleFn","style","fillColor","fillOpacity","color","weight","opacity","feature","choroplethField","choroplethScale","properties","val","choroplethFallback","strokeColor","strokeWeight","strokeOpacity","buildPopupContent","popup","props","template","replace","_","key","String","parts","titleField","push","escapeHtml","fields","Object","keys","slice","formatted","toLocaleString","join","str","addGeoJSONLayer","mapInst","leaflet","geojson","styleFn","layer","geoJSON","pointToLayer","latlng","s","circleMarker","radius","onEachFeature","featureLayer","html","bindPopup","maxWidth","addTo","mapToGeoJSON","p","features","marker","markers","pos","position","lat","Array","isArray","lng","type","geometry","coordinates","tooltip","JSON","stringify","MapRenderer","mapContainer","mapInstance","isLeafletLoaded","setIsLeafletLoaded","createSignal","error","setError","isExpanded","useExpanded","params","component","createEffect","setTimeout","invalidateSize","isServer","module","default","e","console","warn","center","zoom","map","zoomControl","scrollWheelZoom","attributionControl","setView","tileLayerUrl","tileLayer","attribution","control","prefix","Icon","Default","prototype","_getIconUrl","mergeOptions","iconRetinaUrl","iconUrl","shadowUrl","allBoundsLayers","eachLayer","Marker","GeoJSON","CircleMarker","_group","_featureGroup","removeLayer","shouldCluster","clustering","clusterOpts","clusterGroup","markerClusterGroup","maxClusterRadius","spiderfyOnMaxZoom","showCoverageOnHover","disableClusteringAtZoom","animate","animateAddingMarkers","forEach","m","bindTooltip","addLayer","geoLayer","geojsonStyle","layers","overlays","layerDef","name","visible","collapsed","pmtiles","protomaps","pmConfig","paintRules","rule","dataLayer","symbolizer","fill","stroke","width","labelRules","TextSymbolizer","label_props","textField","fontSize","pmLayer","leafletLayer","url","maxZoom","minZoom","fitBounds","group","featureGroup","bounds","getBounds","isValid","pad","getZoom","onCleanup","remove","_$createComponent","ExpandableWrapper","title","copyData","copyLabel","children","_el$","_$getNextElement","_tmpl$3","_el$4","firstChild","_el$5","_co$","_$getNextMarker","nextSibling","_el$6","_el$7","_co$2","_$insert","Show","when","_el$2","_tmpl$","_el$3","_tmpl$2","_ref$","_$use","_$effect","_p$","_v$","height","_v$2","_$style","t","_$className","undefined","className"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MapRenderer.d.ts","sourceRoot":"","sources":["../../src/components/MapRenderer.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAA+C,MAAM,UAAU,CAAA;AAEjF,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAkF,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"MapRenderer.d.ts","sourceRoot":"","sources":["../../src/components/MapRenderer.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAA+C,MAAM,UAAU,CAAA;AAEjF,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAkF,MAAM,UAAU,CAAA;AAQ/I,MAAM,WAAW,gBAAgB;IAC7B;;OAEG;IACH,SAAS,CAAC,EAAE,WAAW,CAAA;IAEvB;;OAEG;IACH,MAAM,CAAC,EAAE,kBAAkB,CAAA;CAC9B;AAyKD,eAAO,MAAM,WAAW,EAAE,SAAS,CAAC,gBAAgB,CAoQnD,CAAA"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { isServer, getNextElement, template, getNextMarker, insert,
|
|
1
|
+
import { isServer, createComponent, getNextElement, template, getNextMarker, insert, use, effect, style, className } from "solid-js/web";
|
|
2
2
|
import { createSignal, createEffect, onCleanup, Show } from "solid-js";
|
|
3
|
-
|
|
3
|
+
import { useExpanded, ExpandableWrapper } from "./ExpandableWrapper.js";
|
|
4
|
+
var _tmpl$ = /* @__PURE__ */ template(`<div class="p-4 text-red-500 bg-red-50 dark:bg-red-900/20 text-center">`), _tmpl$2 = /* @__PURE__ */ template(`<div>`), _tmpl$3 = /* @__PURE__ */ template(`<div><!$><!/><!$><!/>`);
|
|
4
5
|
let L = null;
|
|
5
6
|
let clusterCssLoaded = false;
|
|
6
7
|
function getChoroplethColor(value, scale, fallback) {
|
|
@@ -13,8 +14,8 @@ function getChoroplethColor(value, scale, fallback) {
|
|
|
13
14
|
}
|
|
14
15
|
return scale[scale.length - 1][1];
|
|
15
16
|
}
|
|
16
|
-
function buildStyleFn(
|
|
17
|
-
if (!
|
|
17
|
+
function buildStyleFn(style2) {
|
|
18
|
+
if (!style2) {
|
|
18
19
|
return () => ({
|
|
19
20
|
fillColor: "#3388ff",
|
|
20
21
|
fillOpacity: 0.6,
|
|
@@ -24,17 +25,17 @@ function buildStyleFn(style) {
|
|
|
24
25
|
});
|
|
25
26
|
}
|
|
26
27
|
return (feature) => {
|
|
27
|
-
let fillColor =
|
|
28
|
-
if (
|
|
29
|
-
const val = feature.properties[
|
|
30
|
-
fillColor = getChoroplethColor(val,
|
|
28
|
+
let fillColor = style2.fillColor || "#3388ff";
|
|
29
|
+
if (style2.choroplethField && style2.choroplethScale && (feature == null ? void 0 : feature.properties)) {
|
|
30
|
+
const val = feature.properties[style2.choroplethField];
|
|
31
|
+
fillColor = getChoroplethColor(val, style2.choroplethScale, style2.choroplethFallback || "#ccc");
|
|
31
32
|
}
|
|
32
33
|
return {
|
|
33
34
|
fillColor,
|
|
34
|
-
fillOpacity:
|
|
35
|
-
color:
|
|
36
|
-
weight:
|
|
37
|
-
opacity:
|
|
35
|
+
fillOpacity: style2.fillOpacity ?? 0.6,
|
|
36
|
+
color: style2.strokeColor || "#333",
|
|
37
|
+
weight: style2.strokeWeight ?? 1,
|
|
38
|
+
opacity: style2.strokeOpacity ?? 1
|
|
38
39
|
};
|
|
39
40
|
};
|
|
40
41
|
}
|
|
@@ -64,8 +65,8 @@ function buildPopupContent(feature, popup) {
|
|
|
64
65
|
function escapeHtml(str) {
|
|
65
66
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
66
67
|
}
|
|
67
|
-
function addGeoJSONLayer(mapInst, leaflet, geojson,
|
|
68
|
-
const styleFn = buildStyleFn(
|
|
68
|
+
function addGeoJSONLayer(mapInst, leaflet, geojson, style2, popup) {
|
|
69
|
+
const styleFn = buildStyleFn(style2);
|
|
69
70
|
const layer = leaflet.geoJSON(geojson, {
|
|
70
71
|
style: styleFn,
|
|
71
72
|
pointToLayer: (feature, latlng) => {
|
|
@@ -91,15 +92,53 @@ function addGeoJSONLayer(mapInst, leaflet, geojson, style, popup) {
|
|
|
91
92
|
layer.addTo(mapInst);
|
|
92
93
|
return layer;
|
|
93
94
|
}
|
|
95
|
+
function mapToGeoJSON(p) {
|
|
96
|
+
if (!p) return '{"type":"FeatureCollection","features":[]}';
|
|
97
|
+
const features = [];
|
|
98
|
+
for (const marker of p.markers ?? []) {
|
|
99
|
+
const pos = marker.position;
|
|
100
|
+
const lat = Array.isArray(pos) ? pos[0] : pos == null ? void 0 : pos.lat;
|
|
101
|
+
const lng = Array.isArray(pos) ? pos[1] : pos == null ? void 0 : pos.lng;
|
|
102
|
+
if (typeof lat !== "number" || typeof lng !== "number") continue;
|
|
103
|
+
features.push({
|
|
104
|
+
type: "Feature",
|
|
105
|
+
geometry: {
|
|
106
|
+
type: "Point",
|
|
107
|
+
coordinates: [lng, lat]
|
|
108
|
+
},
|
|
109
|
+
properties: {
|
|
110
|
+
...marker.tooltip ? {
|
|
111
|
+
tooltip: marker.tooltip
|
|
112
|
+
} : {},
|
|
113
|
+
...marker.popup ? {
|
|
114
|
+
popup: marker.popup
|
|
115
|
+
} : {}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return JSON.stringify({
|
|
120
|
+
type: "FeatureCollection",
|
|
121
|
+
features
|
|
122
|
+
}, null, 2);
|
|
123
|
+
}
|
|
94
124
|
const MapRenderer = (props) => {
|
|
95
125
|
let mapContainer;
|
|
96
126
|
let mapInstance = null;
|
|
97
127
|
const [isLeafletLoaded, setIsLeafletLoaded] = createSignal(false);
|
|
98
128
|
const [error, setError] = createSignal(null);
|
|
129
|
+
const isExpanded = useExpanded();
|
|
99
130
|
const params = () => {
|
|
100
131
|
var _a;
|
|
101
132
|
return props.params || ((_a = props.component) == null ? void 0 : _a.params);
|
|
102
133
|
};
|
|
134
|
+
createEffect(() => {
|
|
135
|
+
isExpanded();
|
|
136
|
+
if (!mapInstance) return;
|
|
137
|
+
setTimeout(() => {
|
|
138
|
+
var _a;
|
|
139
|
+
return (_a = mapInstance == null ? void 0 : mapInstance.invalidateSize) == null ? void 0 : _a.call(mapInstance);
|
|
140
|
+
}, 100);
|
|
141
|
+
});
|
|
103
142
|
createEffect(async () => {
|
|
104
143
|
var _a, _b, _c, _d, _e;
|
|
105
144
|
if (isServer) return;
|
|
@@ -268,39 +307,60 @@ const MapRenderer = (props) => {
|
|
|
268
307
|
mapInstance = null;
|
|
269
308
|
}
|
|
270
309
|
});
|
|
271
|
-
return (
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
310
|
+
return createComponent(ExpandableWrapper, {
|
|
311
|
+
title: "Map",
|
|
312
|
+
get copyData() {
|
|
313
|
+
return mapToGeoJSON(params());
|
|
314
|
+
},
|
|
315
|
+
copyLabel: "Copy markers as GeoJSON",
|
|
316
|
+
get children() {
|
|
317
|
+
var _el$ = getNextElement(_tmpl$3), _el$4 = _el$.firstChild, [_el$5, _co$] = getNextMarker(_el$4.nextSibling), _el$6 = _el$5.nextSibling, [_el$7, _co$2] = getNextMarker(_el$6.nextSibling);
|
|
318
|
+
insert(_el$, createComponent(Show, {
|
|
319
|
+
get when() {
|
|
320
|
+
return error();
|
|
321
|
+
},
|
|
322
|
+
get children() {
|
|
323
|
+
var _el$2 = getNextElement(_tmpl$);
|
|
324
|
+
insert(_el$2, error);
|
|
325
|
+
return _el$2;
|
|
326
|
+
}
|
|
327
|
+
}), _el$5, _co$);
|
|
328
|
+
insert(_el$, createComponent(Show, {
|
|
329
|
+
get when() {
|
|
330
|
+
return !error();
|
|
331
|
+
},
|
|
332
|
+
get children() {
|
|
333
|
+
var _el$3 = getNextElement(_tmpl$2);
|
|
334
|
+
var _ref$ = mapContainer;
|
|
335
|
+
typeof _ref$ === "function" ? use(_ref$, _el$3) : mapContainer = _el$3;
|
|
336
|
+
effect((_p$) => {
|
|
337
|
+
var _a;
|
|
338
|
+
var _v$ = isExpanded() ? {
|
|
339
|
+
height: "100%",
|
|
340
|
+
width: "100%",
|
|
341
|
+
"z-index": 0
|
|
342
|
+
} : {
|
|
343
|
+
height: ((_a = params()) == null ? void 0 : _a.height) || "400px",
|
|
344
|
+
width: "100%",
|
|
345
|
+
"z-index": 0
|
|
346
|
+
}, _v$2 = `relative z-0 ${isExpanded() ? "flex-1 min-h-0" : ""}`;
|
|
347
|
+
_p$.e = style(_el$3, _v$, _p$.e);
|
|
348
|
+
_v$2 !== _p$.t && className(_el$3, _p$.t = _v$2);
|
|
349
|
+
return _p$;
|
|
350
|
+
}, {
|
|
351
|
+
e: void 0,
|
|
352
|
+
t: void 0
|
|
353
|
+
});
|
|
354
|
+
return _el$3;
|
|
355
|
+
}
|
|
356
|
+
}), _el$7, _co$2);
|
|
357
|
+
effect(() => {
|
|
358
|
+
var _a;
|
|
359
|
+
return className(_el$, `w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${((_a = params()) == null ? void 0 : _a.className) || ""} ${isExpanded() ? "flex-1 min-h-0 flex flex-col" : ""}`);
|
|
360
|
+
});
|
|
361
|
+
return _el$;
|
|
362
|
+
}
|
|
363
|
+
});
|
|
304
364
|
};
|
|
305
365
|
export {
|
|
306
366
|
MapRenderer
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MapRenderer.js","sources":["../../src/components/MapRenderer.tsx"],"sourcesContent":["/**\n * MapRenderer - Interactive map Component\n * Sprint 6: Markers + clustering\n * v3.1.0: GeoJSON, choropleth, popups, multi-layer, PMTiles\n */\n\nimport { Component, createEffect, onCleanup, createSignal, Show } from 'solid-js'\nimport { isServer } from 'solid-js/web'\nimport type { UIComponent, MapComponentParams, MapClusterOptions, MapGeoJSONStyle, MapPopupConfig, MapLayer, MapPMTilesConfig } from '../types'\n\n// Lazy load leaflet (it doesn't support SSR well)\nlet L: any = null\n// Track if marker cluster CSS has been loaded\nlet clusterCssLoaded = false\n\nexport interface MapRendererProps {\n /**\n * UIComponent containing map params\n */\n component?: UIComponent\n\n /**\n * Direct map params\n */\n params?: MapComponentParams\n}\n\n// ─── Helpers ────────────────────────────────────────────────\n\n/**\n * Resolve choropleth color for a feature based on property value and scale stops.\n */\nfunction getChoroplethColor(\n value: unknown,\n scale: Array<[number, string]>,\n fallback: string\n): string {\n if (value == null || typeof value !== 'number' || !isFinite(value)) return fallback\n\n // Scale is sorted ascending: [[0, '#eff3ff'], [100, '#084594']]\n if (scale.length === 0) return fallback\n if (value <= scale[0][0]) return scale[0][1]\n if (value >= scale[scale.length - 1][0]) return scale[scale.length - 1][1]\n\n // Find surrounding stops and interpolate (use upper bracket color)\n for (let i = 1; i < scale.length; i++) {\n if (value <= scale[i][0]) return scale[i][1]\n }\n return scale[scale.length - 1][1]\n}\n\n/**\n * Build a Leaflet style function from MapGeoJSONStyle config.\n */\nfunction buildStyleFn(style: MapGeoJSONStyle | undefined): (feature: any) => Record<string, unknown> {\n if (!style) {\n return () => ({\n fillColor: '#3388ff',\n fillOpacity: 0.6,\n color: '#333',\n weight: 1,\n opacity: 1,\n })\n }\n\n return (feature: any) => {\n let fillColor = style.fillColor || '#3388ff'\n\n // Choropleth: override fillColor based on feature property\n if (style.choroplethField && style.choroplethScale && feature?.properties) {\n const val = feature.properties[style.choroplethField]\n fillColor = getChoroplethColor(val, style.choroplethScale, style.choroplethFallback || '#ccc')\n }\n\n return {\n fillColor,\n fillOpacity: style.fillOpacity ?? 0.6,\n color: style.strokeColor || '#333',\n weight: style.strokeWeight ?? 1,\n opacity: style.strokeOpacity ?? 1,\n }\n }\n}\n\n/**\n * Build popup HTML from a feature's properties using popup config.\n */\nfunction buildPopupContent(feature: any, popup: MapPopupConfig | undefined): string | null {\n if (!popup || !feature?.properties) return null\n const props = feature.properties\n\n // Custom template\n if (popup.template) {\n return popup.template.replace(/\\{\\{(\\w+)\\}\\}/g, (_, key) => {\n const val = props[key]\n return val != null ? String(val) : ''\n })\n }\n\n // Auto-generated popup\n const parts: string[] = []\n\n if (popup.titleField && props[popup.titleField] != null) {\n parts.push(`<strong>${escapeHtml(String(props[popup.titleField]))}</strong>`)\n }\n\n const fields = popup.fields || Object.keys(props).slice(0, 8)\n for (const key of fields) {\n if (key === popup.titleField) continue\n const val = props[key]\n if (val == null) continue\n const formatted = typeof val === 'number' ? val.toLocaleString('fr-FR') : String(val)\n parts.push(`<span style=\"color:#666;font-size:11px\">${escapeHtml(key)}</span>: ${escapeHtml(formatted)}`)\n }\n\n return parts.join('<br/>')\n}\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n}\n\n/**\n * Add a GeoJSON layer to the map with style and popup support.\n * Returns the layer for bounds calculation.\n */\nfunction addGeoJSONLayer(\n mapInst: any,\n leaflet: any,\n geojson: unknown,\n style?: MapGeoJSONStyle,\n popup?: MapPopupConfig\n): any {\n const styleFn = buildStyleFn(style)\n\n const layer = leaflet.geoJSON(geojson, {\n style: styleFn,\n pointToLayer: (feature: any, latlng: any) => {\n // Render points as circle markers for consistency\n const s = styleFn(feature)\n return leaflet.circleMarker(latlng, {\n radius: 6,\n fillColor: s.fillColor,\n fillOpacity: s.fillOpacity,\n color: s.color,\n weight: s.weight,\n opacity: s.opacity,\n })\n },\n onEachFeature: (feature: any, featureLayer: any) => {\n const html = buildPopupContent(feature, popup)\n if (html) {\n featureLayer.bindPopup(html, { maxWidth: 300 })\n }\n },\n })\n\n layer.addTo(mapInst)\n return layer\n}\n\n// ─── Component ──────────────────────────────────────────────\n\nexport const MapRenderer: Component<MapRendererProps> = (props) => {\n let mapContainer: HTMLDivElement | undefined\n let mapInstance: any = null\n const [isLeafletLoaded, setIsLeafletLoaded] = createSignal(false)\n const [error, setError] = createSignal<string | null>(null)\n\n const params = () => props.params || (props.component?.params as MapComponentParams)\n\n // Initialize Map\n createEffect(async () => {\n if (isServer) return // Don't run on server\n\n if (!L) {\n try {\n const module = await import('leaflet')\n L = module.default || module\n await import('leaflet/dist/leaflet.css') // Import CSS\n setIsLeafletLoaded(true)\n } catch (e) {\n console.warn('Failed to load leaflet', e)\n setError('Map library could not be loaded.')\n return\n }\n } else {\n setIsLeafletLoaded(true)\n }\n\n if (isLeafletLoaded() && mapContainer && !mapInstance) {\n const p = params()\n const center = p?.center || [51.505, -0.09] // Default to London\n const zoom = p?.zoom || 13\n\n mapInstance = L.map(mapContainer, {\n zoomControl: p?.zoomControl !== false,\n scrollWheelZoom: p?.scrollWheelZoom !== false,\n attributionControl: false\n }).setView(center, zoom)\n\n // Add OpenStreetMap tile layer\n const tileLayerUrl = p?.tileLayer || 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'\n L.tileLayer(tileLayerUrl, {\n attribution: p?.attribution || '© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors'\n }).addTo(mapInstance)\n\n if (p?.attribution !== '') {\n L.control.attribution({ prefix: false }).addTo(mapInstance)\n }\n\n // Fix marker icons (Leaflet issue with bundlers)\n delete (L.Icon.Default.prototype as any)._getIconUrl\n L.Icon.Default.mergeOptions({\n iconRetinaUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png',\n iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',\n shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',\n })\n }\n\n // Update markers and view\n if (mapInstance && L) {\n const p = params()\n const allBoundsLayers: any[] = []\n\n // Clear existing layers (markers, cluster groups, GeoJSON)\n mapInstance.eachLayer((layer: any) => {\n if (layer instanceof L.Marker || layer instanceof L.GeoJSON\n || layer instanceof L.CircleMarker\n || layer._group || layer._featureGroup) {\n mapInstance.removeLayer(layer)\n }\n })\n\n // ─── Markers (legacy) ────────────────────────\n const markers: any[] = []\n const shouldCluster = p?.clustering && p?.markers && p.markers.length > 0\n\n if (shouldCluster) {\n try {\n await import('leaflet.markercluster')\n if (!clusterCssLoaded) {\n await import('leaflet.markercluster/dist/MarkerCluster.css')\n await import('leaflet.markercluster/dist/MarkerCluster.Default.css')\n clusterCssLoaded = true\n }\n const clusterOpts: MapClusterOptions = typeof p.clustering === 'object' ? p.clustering : {}\n const clusterGroup = (L as any).markerClusterGroup({\n maxClusterRadius: clusterOpts.maxClusterRadius ?? 80,\n spiderfyOnMaxZoom: clusterOpts.spiderfyOnMaxZoom ?? true,\n showCoverageOnHover: clusterOpts.showCoverageOnHover ?? true,\n disableClusteringAtZoom: clusterOpts.disableClusteringAtZoom,\n animate: clusterOpts.animateAddingMarkers ?? true\n })\n p?.markers?.forEach(marker => {\n const m = L.marker(marker.position)\n if (marker.tooltip) m.bindTooltip(marker.tooltip)\n if (marker.popup) m.bindPopup(marker.popup)\n clusterGroup.addLayer(m)\n markers.push(m)\n })\n mapInstance.addLayer(clusterGroup)\n } catch {\n p?.markers?.forEach(marker => {\n const m = L.marker(marker.position).addTo(mapInstance)\n if (marker.tooltip) m.bindTooltip(marker.tooltip)\n if (marker.popup) m.bindPopup(marker.popup)\n markers.push(m)\n })\n }\n } else {\n p?.markers?.forEach(marker => {\n const m = L.marker(marker.position).addTo(mapInstance)\n if (marker.tooltip) m.bindTooltip(marker.tooltip)\n if (marker.popup) m.bindPopup(marker.popup)\n markers.push(m)\n })\n }\n\n if (markers.length > 0) {\n allBoundsLayers.push(...markers)\n }\n\n // ─── GeoJSON (v3.1.0) ───────────────────────\n if (p?.geojson) {\n const geoLayer = addGeoJSONLayer(mapInstance, L, p.geojson, p.geojsonStyle, p.popup)\n allBoundsLayers.push(geoLayer)\n }\n\n // ─── Named layers (v3.1.0) ──────────────────\n if (p?.layers && p.layers.length > 0) {\n const overlays: Record<string, any> = {}\n\n for (const layerDef of p.layers) {\n const geoLayer = addGeoJSONLayer(\n mapInstance, L,\n layerDef.geojson,\n layerDef.style || p?.geojsonStyle,\n layerDef.popup || p?.popup\n )\n\n overlays[layerDef.name] = geoLayer\n allBoundsLayers.push(geoLayer)\n\n // Respect initial visibility\n if (layerDef.visible === false) {\n mapInstance.removeLayer(geoLayer)\n }\n }\n\n // Add layer control if multiple layers\n if (Object.keys(overlays).length > 1) {\n L.control.layers(null, overlays, { collapsed: true }).addTo(mapInstance)\n }\n }\n\n // ─── PMTiles (v3.1.0) ────────────────────────\n if (p?.pmtiles) {\n try {\n // @ts-ignore — optional peer dependency, may not be installed\n const protomaps = await import(/* @vite-ignore */ 'protomaps-leaflet')\n const pmConfig = p.pmtiles\n\n const paintRules = pmConfig.paintRules?.map(rule => ({\n dataLayer: rule.dataLayer,\n symbolizer: new (protomaps as any)[\n rule.symbolizer === 'polygon' ? 'PolygonSymbolizer' :\n rule.symbolizer === 'line' ? 'LineSymbolizer' :\n 'CircleSymbolizer'\n ]({\n fill: rule.color || '#3388ff',\n stroke: rule.color || '#333',\n width: rule.width ?? 1,\n opacity: rule.opacity ?? 0.6,\n }),\n })) || []\n\n const labelRules = pmConfig.labelRules?.map(rule => ({\n dataLayer: rule.dataLayer,\n symbolizer: new (protomaps as any).TextSymbolizer({\n label_props: [rule.textField],\n fontSize: rule.fontSize ?? 12,\n }),\n })) || []\n\n const pmLayer = (protomaps as any).leafletLayer({\n url: pmConfig.url,\n attribution: pmConfig.attribution,\n paintRules,\n labelRules,\n maxZoom: pmConfig.maxZoom,\n minZoom: pmConfig.minZoom,\n })\n\n pmLayer.addTo(mapInstance)\n } catch (e) {\n console.warn('[MCP-UI] Failed to load protomaps-leaflet for PMTiles:', e)\n }\n }\n\n // ─── Fit bounds ─────────────────────────────\n if (p?.fitBounds && allBoundsLayers.length > 0) {\n const group = L.featureGroup(allBoundsLayers)\n const bounds = group.getBounds()\n if (bounds.isValid()) {\n mapInstance.fitBounds(bounds.pad(0.1))\n }\n } else if (p?.center) {\n mapInstance.setView(p.center, p.zoom || mapInstance.getZoom())\n }\n }\n })\n\n // Cleanup\n onCleanup(() => {\n if (mapInstance) {\n mapInstance.remove()\n mapInstance = null\n }\n })\n\n return (\n <div class={`w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${params()?.className || ''}`}>\n <Show when={error()}>\n <div class=\"p-4 text-red-500 bg-red-50 dark:bg-red-900/20 text-center\">\n {error()}\n </div>\n </Show>\n <Show when={!error()}>\n <div\n ref={mapContainer}\n style={{ height: params()?.height || '400px', width: '100%', \"z-index\": 0 }}\n class=\"relative z-0\"\n />\n </Show>\n </div>\n )\n}\n"],"names":["L","clusterCssLoaded","getChoroplethColor","value","scale","fallback","isFinite","length","i","buildStyleFn","style","fillColor","fillOpacity","color","weight","opacity","feature","choroplethField","choroplethScale","properties","val","choroplethFallback","strokeColor","strokeWeight","strokeOpacity","buildPopupContent","popup","props","template","replace","_","key","String","parts","titleField","push","escapeHtml","fields","Object","keys","slice","formatted","toLocaleString","join","str","addGeoJSONLayer","mapInst","leaflet","geojson","styleFn","layer","geoJSON","pointToLayer","latlng","s","circleMarker","radius","onEachFeature","featureLayer","html","bindPopup","maxWidth","addTo","MapRenderer","mapContainer","mapInstance","isLeafletLoaded","setIsLeafletLoaded","createSignal","error","setError","params","component","createEffect","isServer","module","default","e","console","warn","p","center","zoom","map","zoomControl","scrollWheelZoom","attributionControl","setView","tileLayerUrl","tileLayer","attribution","control","prefix","Icon","Default","prototype","_getIconUrl","mergeOptions","iconRetinaUrl","iconUrl","shadowUrl","allBoundsLayers","eachLayer","Marker","GeoJSON","CircleMarker","_group","_featureGroup","removeLayer","markers","shouldCluster","clustering","clusterOpts","clusterGroup","markerClusterGroup","maxClusterRadius","spiderfyOnMaxZoom","showCoverageOnHover","disableClusteringAtZoom","animate","animateAddingMarkers","forEach","marker","m","position","tooltip","bindTooltip","addLayer","geoLayer","geojsonStyle","layers","overlays","layerDef","name","visible","collapsed","pmtiles","protomaps","pmConfig","paintRules","rule","dataLayer","symbolizer","fill","stroke","width","labelRules","TextSymbolizer","label_props","textField","fontSize","pmLayer","leafletLayer","url","maxZoom","minZoom","fitBounds","group","featureGroup","bounds","getBounds","isValid","pad","getZoom","onCleanup","remove","_el$","_$getNextElement","_tmpl$3","_el$4","firstChild","_el$5","_co$","_$getNextMarker","nextSibling","_el$6","_el$7","_co$2","_$insert","_$createComponent","Show","when","children","_el$2","_tmpl$","_el$3","_tmpl$2","_ref$","_$use","_$effect","_$p","_$setStyleProperty","height","_$className","className"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MapRenderer.js","sources":["../../src/components/MapRenderer.tsx"],"sourcesContent":["/**\n * MapRenderer - Interactive map Component\n * Sprint 6: Markers + clustering\n * v3.1.0: GeoJSON, choropleth, popups, multi-layer, PMTiles\n */\n\nimport { Component, createEffect, onCleanup, createSignal, Show } from 'solid-js'\nimport { isServer } from 'solid-js/web'\nimport type { UIComponent, MapComponentParams, MapClusterOptions, MapGeoJSONStyle, MapPopupConfig, MapLayer, MapPMTilesConfig } from '../types'\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper'\n\n// Lazy load leaflet (it doesn't support SSR well)\nlet L: any = null\n// Track if marker cluster CSS has been loaded\nlet clusterCssLoaded = false\n\nexport interface MapRendererProps {\n /**\n * UIComponent containing map params\n */\n component?: UIComponent\n\n /**\n * Direct map params\n */\n params?: MapComponentParams\n}\n\n// ─── Helpers ────────────────────────────────────────────────\n\n/**\n * Resolve choropleth color for a feature based on property value and scale stops.\n */\nfunction getChoroplethColor(\n value: unknown,\n scale: Array<[number, string]>,\n fallback: string\n): string {\n if (value == null || typeof value !== 'number' || !isFinite(value)) return fallback\n\n // Scale is sorted ascending: [[0, '#eff3ff'], [100, '#084594']]\n if (scale.length === 0) return fallback\n if (value <= scale[0][0]) return scale[0][1]\n if (value >= scale[scale.length - 1][0]) return scale[scale.length - 1][1]\n\n // Find surrounding stops and interpolate (use upper bracket color)\n for (let i = 1; i < scale.length; i++) {\n if (value <= scale[i][0]) return scale[i][1]\n }\n return scale[scale.length - 1][1]\n}\n\n/**\n * Build a Leaflet style function from MapGeoJSONStyle config.\n */\nfunction buildStyleFn(style: MapGeoJSONStyle | undefined): (feature: any) => Record<string, unknown> {\n if (!style) {\n return () => ({\n fillColor: '#3388ff',\n fillOpacity: 0.6,\n color: '#333',\n weight: 1,\n opacity: 1,\n })\n }\n\n return (feature: any) => {\n let fillColor = style.fillColor || '#3388ff'\n\n // Choropleth: override fillColor based on feature property\n if (style.choroplethField && style.choroplethScale && feature?.properties) {\n const val = feature.properties[style.choroplethField]\n fillColor = getChoroplethColor(val, style.choroplethScale, style.choroplethFallback || '#ccc')\n }\n\n return {\n fillColor,\n fillOpacity: style.fillOpacity ?? 0.6,\n color: style.strokeColor || '#333',\n weight: style.strokeWeight ?? 1,\n opacity: style.strokeOpacity ?? 1,\n }\n }\n}\n\n/**\n * Build popup HTML from a feature's properties using popup config.\n */\nfunction buildPopupContent(feature: any, popup: MapPopupConfig | undefined): string | null {\n if (!popup || !feature?.properties) return null\n const props = feature.properties\n\n // Custom template\n if (popup.template) {\n return popup.template.replace(/\\{\\{(\\w+)\\}\\}/g, (_, key) => {\n const val = props[key]\n return val != null ? String(val) : ''\n })\n }\n\n // Auto-generated popup\n const parts: string[] = []\n\n if (popup.titleField && props[popup.titleField] != null) {\n parts.push(`<strong>${escapeHtml(String(props[popup.titleField]))}</strong>`)\n }\n\n const fields = popup.fields || Object.keys(props).slice(0, 8)\n for (const key of fields) {\n if (key === popup.titleField) continue\n const val = props[key]\n if (val == null) continue\n const formatted = typeof val === 'number' ? val.toLocaleString('fr-FR') : String(val)\n parts.push(`<span style=\"color:#666;font-size:11px\">${escapeHtml(key)}</span>: ${escapeHtml(formatted)}`)\n }\n\n return parts.join('<br/>')\n}\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n}\n\n/**\n * Add a GeoJSON layer to the map with style and popup support.\n * Returns the layer for bounds calculation.\n */\nfunction addGeoJSONLayer(\n mapInst: any,\n leaflet: any,\n geojson: unknown,\n style?: MapGeoJSONStyle,\n popup?: MapPopupConfig\n): any {\n const styleFn = buildStyleFn(style)\n\n const layer = leaflet.geoJSON(geojson, {\n style: styleFn,\n pointToLayer: (feature: any, latlng: any) => {\n // Render points as circle markers for consistency\n const s = styleFn(feature)\n return leaflet.circleMarker(latlng, {\n radius: 6,\n fillColor: s.fillColor,\n fillOpacity: s.fillOpacity,\n color: s.color,\n weight: s.weight,\n opacity: s.opacity,\n })\n },\n onEachFeature: (feature: any, featureLayer: any) => {\n const html = buildPopupContent(feature, popup)\n if (html) {\n featureLayer.bindPopup(html, { maxWidth: 300 })\n }\n },\n })\n\n layer.addTo(mapInst)\n return layer\n}\n\n// ─── Component ──────────────────────────────────────────────\n\n/**\n * Build a GeoJSON FeatureCollection from the map's `markers` (and any\n * inlined GeoJSON layers, when present). Used by the \"Copy data\" button\n * shipped via `<ExpandableWrapper>` (v6.2.0). Best-effort — clusters,\n * tile layers, and choropleth-only data don't get round-tripped.\n */\nfunction mapToGeoJSON(p: MapComponentParams | undefined): string {\n if (!p) return '{\"type\":\"FeatureCollection\",\"features\":[]}'\n const features: any[] = []\n for (const marker of p.markers ?? []) {\n const pos: any = marker.position as any\n // Accept both [lat, lng] tuple and {lat, lng} object shapes (v5.0.2 spec)\n const lat = Array.isArray(pos) ? pos[0] : pos?.lat\n const lng = Array.isArray(pos) ? pos[1] : pos?.lng\n if (typeof lat !== 'number' || typeof lng !== 'number') continue\n features.push({\n type: 'Feature',\n geometry: { type: 'Point', coordinates: [lng, lat] },\n properties: {\n ...(marker.tooltip ? { tooltip: marker.tooltip } : {}),\n ...(marker.popup ? { popup: marker.popup } : {}),\n },\n })\n }\n return JSON.stringify({ type: 'FeatureCollection', features }, null, 2)\n}\n\nexport const MapRenderer: Component<MapRendererProps> = (props) => {\n let mapContainer: HTMLDivElement | undefined\n let mapInstance: any = null\n const [isLeafletLoaded, setIsLeafletLoaded] = createSignal(false)\n const [error, setError] = createSignal<string | null>(null)\n const isExpanded = useExpanded()\n\n const params = () => props.params || (props.component?.params as MapComponentParams)\n\n // v6.2.0 — Leaflet has to be told to re-measure when its container\n // resizes (e.g. transitioning to fullscreen via ExpandableWrapper).\n // We give the DOM a tick to settle the new dimensions, then ask\n // Leaflet to reflow tiles.\n createEffect(() => {\n const expanded = isExpanded()\n if (!mapInstance) return\n // Read the signal so the effect re-runs on toggle ; the value is\n // observed for its side effects on layout.\n void expanded\n setTimeout(() => mapInstance?.invalidateSize?.(), 100)\n })\n\n // Initialize Map\n createEffect(async () => {\n if (isServer) return // Don't run on server\n\n if (!L) {\n try {\n const module = await import('leaflet')\n L = module.default || module\n await import('leaflet/dist/leaflet.css') // Import CSS\n setIsLeafletLoaded(true)\n } catch (e) {\n console.warn('Failed to load leaflet', e)\n setError('Map library could not be loaded.')\n return\n }\n } else {\n setIsLeafletLoaded(true)\n }\n\n if (isLeafletLoaded() && mapContainer && !mapInstance) {\n const p = params()\n const center = p?.center || [51.505, -0.09] // Default to London\n const zoom = p?.zoom || 13\n\n mapInstance = L.map(mapContainer, {\n zoomControl: p?.zoomControl !== false,\n scrollWheelZoom: p?.scrollWheelZoom !== false,\n attributionControl: false\n }).setView(center, zoom)\n\n // Add OpenStreetMap tile layer\n const tileLayerUrl = p?.tileLayer || 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'\n L.tileLayer(tileLayerUrl, {\n attribution: p?.attribution || '© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors'\n }).addTo(mapInstance)\n\n if (p?.attribution !== '') {\n L.control.attribution({ prefix: false }).addTo(mapInstance)\n }\n\n // Fix marker icons (Leaflet issue with bundlers)\n delete (L.Icon.Default.prototype as any)._getIconUrl\n L.Icon.Default.mergeOptions({\n iconRetinaUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png',\n iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',\n shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',\n })\n }\n\n // Update markers and view\n if (mapInstance && L) {\n const p = params()\n const allBoundsLayers: any[] = []\n\n // Clear existing layers (markers, cluster groups, GeoJSON)\n mapInstance.eachLayer((layer: any) => {\n if (layer instanceof L.Marker || layer instanceof L.GeoJSON\n || layer instanceof L.CircleMarker\n || layer._group || layer._featureGroup) {\n mapInstance.removeLayer(layer)\n }\n })\n\n // ─── Markers (legacy) ────────────────────────\n const markers: any[] = []\n const shouldCluster = p?.clustering && p?.markers && p.markers.length > 0\n\n if (shouldCluster) {\n try {\n await import('leaflet.markercluster')\n if (!clusterCssLoaded) {\n await import('leaflet.markercluster/dist/MarkerCluster.css')\n await import('leaflet.markercluster/dist/MarkerCluster.Default.css')\n clusterCssLoaded = true\n }\n const clusterOpts: MapClusterOptions = typeof p.clustering === 'object' ? p.clustering : {}\n const clusterGroup = (L as any).markerClusterGroup({\n maxClusterRadius: clusterOpts.maxClusterRadius ?? 80,\n spiderfyOnMaxZoom: clusterOpts.spiderfyOnMaxZoom ?? true,\n showCoverageOnHover: clusterOpts.showCoverageOnHover ?? true,\n disableClusteringAtZoom: clusterOpts.disableClusteringAtZoom,\n animate: clusterOpts.animateAddingMarkers ?? true\n })\n p?.markers?.forEach(marker => {\n const m = L.marker(marker.position)\n if (marker.tooltip) m.bindTooltip(marker.tooltip)\n if (marker.popup) m.bindPopup(marker.popup)\n clusterGroup.addLayer(m)\n markers.push(m)\n })\n mapInstance.addLayer(clusterGroup)\n } catch {\n p?.markers?.forEach(marker => {\n const m = L.marker(marker.position).addTo(mapInstance)\n if (marker.tooltip) m.bindTooltip(marker.tooltip)\n if (marker.popup) m.bindPopup(marker.popup)\n markers.push(m)\n })\n }\n } else {\n p?.markers?.forEach(marker => {\n const m = L.marker(marker.position).addTo(mapInstance)\n if (marker.tooltip) m.bindTooltip(marker.tooltip)\n if (marker.popup) m.bindPopup(marker.popup)\n markers.push(m)\n })\n }\n\n if (markers.length > 0) {\n allBoundsLayers.push(...markers)\n }\n\n // ─── GeoJSON (v3.1.0) ───────────────────────\n if (p?.geojson) {\n const geoLayer = addGeoJSONLayer(mapInstance, L, p.geojson, p.geojsonStyle, p.popup)\n allBoundsLayers.push(geoLayer)\n }\n\n // ─── Named layers (v3.1.0) ──────────────────\n if (p?.layers && p.layers.length > 0) {\n const overlays: Record<string, any> = {}\n\n for (const layerDef of p.layers) {\n const geoLayer = addGeoJSONLayer(\n mapInstance, L,\n layerDef.geojson,\n layerDef.style || p?.geojsonStyle,\n layerDef.popup || p?.popup\n )\n\n overlays[layerDef.name] = geoLayer\n allBoundsLayers.push(geoLayer)\n\n // Respect initial visibility\n if (layerDef.visible === false) {\n mapInstance.removeLayer(geoLayer)\n }\n }\n\n // Add layer control if multiple layers\n if (Object.keys(overlays).length > 1) {\n L.control.layers(null, overlays, { collapsed: true }).addTo(mapInstance)\n }\n }\n\n // ─── PMTiles (v3.1.0) ────────────────────────\n if (p?.pmtiles) {\n try {\n // @ts-ignore — optional peer dependency, may not be installed\n const protomaps = await import(/* @vite-ignore */ 'protomaps-leaflet')\n const pmConfig = p.pmtiles\n\n const paintRules = pmConfig.paintRules?.map(rule => ({\n dataLayer: rule.dataLayer,\n symbolizer: new (protomaps as any)[\n rule.symbolizer === 'polygon' ? 'PolygonSymbolizer' :\n rule.symbolizer === 'line' ? 'LineSymbolizer' :\n 'CircleSymbolizer'\n ]({\n fill: rule.color || '#3388ff',\n stroke: rule.color || '#333',\n width: rule.width ?? 1,\n opacity: rule.opacity ?? 0.6,\n }),\n })) || []\n\n const labelRules = pmConfig.labelRules?.map(rule => ({\n dataLayer: rule.dataLayer,\n symbolizer: new (protomaps as any).TextSymbolizer({\n label_props: [rule.textField],\n fontSize: rule.fontSize ?? 12,\n }),\n })) || []\n\n const pmLayer = (protomaps as any).leafletLayer({\n url: pmConfig.url,\n attribution: pmConfig.attribution,\n paintRules,\n labelRules,\n maxZoom: pmConfig.maxZoom,\n minZoom: pmConfig.minZoom,\n })\n\n pmLayer.addTo(mapInstance)\n } catch (e) {\n console.warn('[MCP-UI] Failed to load protomaps-leaflet for PMTiles:', e)\n }\n }\n\n // ─── Fit bounds ─────────────────────────────\n if (p?.fitBounds && allBoundsLayers.length > 0) {\n const group = L.featureGroup(allBoundsLayers)\n const bounds = group.getBounds()\n if (bounds.isValid()) {\n mapInstance.fitBounds(bounds.pad(0.1))\n }\n } else if (p?.center) {\n mapInstance.setView(p.center, p.zoom || mapInstance.getZoom())\n }\n }\n })\n\n // Cleanup\n onCleanup(() => {\n if (mapInstance) {\n mapInstance.remove()\n mapInstance = null\n }\n })\n\n return (\n <ExpandableWrapper\n title={'Map'}\n copyData={mapToGeoJSON(params())}\n copyLabel=\"Copy markers as GeoJSON\"\n >\n <div class={`w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${params()?.className || ''} ${\n isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''\n }`}>\n <Show when={error()}>\n <div class=\"p-4 text-red-500 bg-red-50 dark:bg-red-900/20 text-center\">\n {error()}\n </div>\n </Show>\n <Show when={!error()}>\n <div\n ref={mapContainer}\n style={\n isExpanded()\n ? { height: '100%', width: '100%', 'z-index': 0 }\n : { height: params()?.height || '400px', width: '100%', 'z-index': 0 }\n }\n class={`relative z-0 ${isExpanded() ? 'flex-1 min-h-0' : ''}`}\n />\n </Show>\n </div>\n </ExpandableWrapper>\n )\n}\n"],"names":["L","clusterCssLoaded","getChoroplethColor","value","scale","fallback","isFinite","length","i","buildStyleFn","style","fillColor","fillOpacity","color","weight","opacity","feature","choroplethField","choroplethScale","properties","val","choroplethFallback","strokeColor","strokeWeight","strokeOpacity","buildPopupContent","popup","props","template","replace","_","key","String","parts","titleField","push","escapeHtml","fields","Object","keys","slice","formatted","toLocaleString","join","str","addGeoJSONLayer","mapInst","leaflet","geojson","styleFn","layer","geoJSON","pointToLayer","latlng","s","circleMarker","radius","onEachFeature","featureLayer","html","bindPopup","maxWidth","addTo","mapToGeoJSON","p","features","marker","markers","pos","position","lat","Array","isArray","lng","type","geometry","coordinates","tooltip","JSON","stringify","MapRenderer","mapContainer","mapInstance","isLeafletLoaded","setIsLeafletLoaded","createSignal","error","setError","isExpanded","useExpanded","params","component","createEffect","setTimeout","invalidateSize","isServer","module","default","e","console","warn","center","zoom","map","zoomControl","scrollWheelZoom","attributionControl","setView","tileLayerUrl","tileLayer","attribution","control","prefix","Icon","Default","prototype","_getIconUrl","mergeOptions","iconRetinaUrl","iconUrl","shadowUrl","allBoundsLayers","eachLayer","Marker","GeoJSON","CircleMarker","_group","_featureGroup","removeLayer","shouldCluster","clustering","clusterOpts","clusterGroup","markerClusterGroup","maxClusterRadius","spiderfyOnMaxZoom","showCoverageOnHover","disableClusteringAtZoom","animate","animateAddingMarkers","forEach","m","bindTooltip","addLayer","geoLayer","geojsonStyle","layers","overlays","layerDef","name","visible","collapsed","pmtiles","protomaps","pmConfig","paintRules","rule","dataLayer","symbolizer","fill","stroke","width","labelRules","TextSymbolizer","label_props","textField","fontSize","pmLayer","leafletLayer","url","maxZoom","minZoom","fitBounds","group","featureGroup","bounds","getBounds","isValid","pad","getZoom","onCleanup","remove","_$createComponent","ExpandableWrapper","title","copyData","copyLabel","children","_el$","_$getNextElement","_tmpl$3","_el$4","firstChild","_el$5","_co$","_$getNextMarker","nextSibling","_el$6","_el$7","_co$2","_$insert","Show","when","_el$2","_tmpl$","_el$3","_tmpl$2","_ref$","_$use","_$effect","_p$","_v$","height","_v$2","_$style","t","_$className","undefined","className"],"mappings":"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"}
|