@gradeui/ui 2.0.0 → 2.1.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/components/ui/avatar.md +28 -3
- package/components/ui/grade-loader.md +31 -0
- package/components/ui/input.md +2 -1
- package/components/ui/logo.md +39 -13
- package/components/ui/map.md +19 -10
- package/components/ui/motion.md +109 -0
- package/dist/contracts.js +5 -5
- package/dist/contracts.js.map +1 -1
- package/dist/contracts.mjs +5 -5
- package/dist/contracts.mjs.map +1 -1
- package/dist/index.d.mts +309 -21
- package/dist/index.d.ts +309 -21
- package/dist/index.js +222 -45
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +222 -45
- package/dist/index.mjs.map +1 -1
- package/dist/map/leaflet.d.mts +6 -0
- package/dist/map/leaflet.d.ts +6 -0
- package/dist/map/leaflet.js +4 -0
- package/dist/map/leaflet.js.map +1 -0
- package/dist/map/leaflet.mjs +4 -0
- package/dist/map/leaflet.mjs.map +1 -0
- package/dist/styles.css +1 -1
- package/package.json +7 -2
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';var C="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css",h="gds-leaflet-css";function T(){if(typeof document>"u"||globalThis.__gradeLeafletCssBundled||document.getElementById(h))return;let s=document.createElement("link");s.id=h,s.rel="stylesheet",s.href=C,document.head.appendChild(s);}var k={light:"rastertiles/voyager",dark:"rastertiles/dark_all",satellite:"rastertiles/voyager"},E=s=>`https://{s}.basemaps.cartocdn.com/${s}/{z}/{x}/{y}{r}.png`,S="\xA9 OpenStreetMap contributors \xA9 CARTO",A=async(s,r,c)=>{T();let d;try{d=await import('leaflet'),d.default&&(d=d.default);}catch(e){throw c.onError({code:"sdk-missing",message:"@gradeui/ui Map: `leaflet` is not installed. Run `pnpm add leaflet` to use the worker-free Leaflet adapter.",cause:e}),e}let o=e=>[e[1],e[0]],t=d.map(s,{center:o(r.center),zoom:r.zoom,zoomControl:r.interactive,attributionControl:true,dragging:r.interactive,scrollWheelZoom:r.interactive,doubleClickZoom:r.interactive,boxZoom:r.interactive,keyboard:r.interactive,touchZoom:r.interactive}),v=e=>{let a=d.tileLayer(E(k[e]??k.light),{maxZoom:20,subdomains:"abcd",detectRetina:true,attribution:S,crossOrigin:true});return a.on("tileerror",()=>c.onError({code:"tile-load-failed",message:"map tile failed to load (is the tile host allowed by CSP?)"})),a.addTo(t),a},p=v(r.appearance),b=e=>{p&&t.removeLayer(p),p=v(e);};r.bounds&&t.fitBounds([o(r.bounds[0]),o(r.bounds[1])],{animate:false}),c.onLoad();let y=typeof ResizeObserver<"u"?new ResizeObserver(()=>t.invalidateSize(false)):null;y?.observe(s),requestAnimationFrame(()=>t.invalidateSize(false)),window.setTimeout(()=>t.invalidateSize(false),200),window.setTimeout(()=>t.invalidateSize(false),600);let u=new globalThis.Map;return {setCenter:e=>t.panTo(o(e),{animate:false}),setZoom:e=>t.setZoom(e),setBounds:(e,a)=>t.fitBounds([o(e),o(a)],{animate:false}),setAppearance:e=>b(e),setInteractive:e=>{let a=["dragging","scrollWheelZoom","doubleClickZoom","boxZoom","keyboard","touchZoom"];for(let i of a){let n=t[i];n&&(e?n.enable():n.disable());}},flyTo:(e,a)=>t.flyTo(o(e),a?.zoom??t.getZoom(),{duration:(a?.durationMs??800)/1e3}),panTo:(e,a)=>t.panTo(o(e),{duration:(a?.durationMs??600)/1e3}),fitBounds:(e,a)=>{if(e.length===0)return;let i=a?.paddingPx??40;t.fitBounds(e.map(o),{padding:[i,i],duration:(a?.durationMs??800)/1e3});},getCenter:()=>{let e=t.getCenter();return [e.lng,e.lat]},getZoom:()=>t.getZoom(),getBounds:()=>{let e=t.getBounds(),a=e.getSouthWest(),i=e.getNorthEast();return [[a.lng,a.lat],[i.lng,i.lat]]},addMarker:(e,a,i)=>{let n=document.createElement("div");n.dataset.gdsPart="map-marker",n.dataset.gdsState="idle",n.style.position="absolute",n.style.cursor="pointer",n.style.transform=i==="center"?"translate(-50%, -50%)":"translate(-50%, -100%)",n.addEventListener("mouseenter",()=>c.onMarkerHover(e)),n.addEventListener("mouseleave",()=>c.onMarkerHover(null)),n.addEventListener("click",l=>c.onMarkerClick(e,g.coords,l));let L=d.divIcon({className:"gds-leaflet-marker",html:"",iconSize:[0,0],iconAnchor:[0,0]}),f=d.marker(o(a),{icon:L,interactive:true,keyboard:false}).addTo(t),m=f.getElement();m&&(m.style.pointerEvents="auto",m.appendChild(n));let g={element:n,coords:a,setHovered:l=>{n.dataset.gdsState=l?"hovered":"idle",m&&(m.style.zIndex=l?"1000":"");},setPosition:l=>{g.coords=l,f.setLatLng(o(l));},remove:()=>{t.removeLayer(f),u.delete(e);}};return u.set(e,{marker:f,handle:g}),g},destroy:()=>{y?.disconnect(),u.forEach(({marker:e})=>t.removeLayer(e)),u.clear(),t.remove();},instance:t}};
|
|
3
|
+
exports.createLeafletAdapter=A;//# sourceMappingURL=leaflet.js.map
|
|
4
|
+
//# sourceMappingURL=leaflet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../components/ui/map/adapters/leaflet.ts"],"names":["LEAFLET_CSS_HREF","CSS_LINK_ID","ensureLeafletCss","link","CARTO_STYLE","cartoUrl","style","CARTO_ATTRIBUTION","createLeafletAdapter","container","opts","callbacks","L","err","toLatLng","c","map","addTiles","appearance","layer","tiles","applyAppearance","resizeObserver","markers","z","sw","ne","a","enabled","handlers","k","ctl","o","list","pad","b","id","coords","anchor","element","e","handle","icon","marker","iconEl","hovered","next"],"mappings":"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","file":"leaflet.js","sourcesContent":["import type {\n AdapterFactory,\n AdapterInstance,\n Coords,\n MarkerHandle,\n} from \"../types\";\n\n/**\n * Leaflet adapter — a WORKER-FREE raster-tile renderer.\n *\n * Why this exists alongside the maplibre adapter: maplibre-gl does all of its\n * rendering in a Web Worker (spawned from a `blob:` URL). Inside a strictly\n * sandboxed MCP App panel the default CSP is `default-src 'none'`, workers\n * fall back to that, and the MCP Apps CSP vocabulary has no `worker-src`\n * knob — so maplibre simply cannot run there. Leaflet paints raster tiles as\n * plain `<img>` elements on the main thread (no worker), so it runs fine; the\n * only thing it needs is network access to the tile host, which IS grantable\n * via the resource's `connectDomains` / `resourceDomains`.\n *\n * Keyless: OSM raster tiles, no API key, no referrer lock. The MCP View\n * forces this adapter (see the provider override in map.tsx); Studio and the\n * embed keep maplibre.\n */\n\nconst LEAFLET_CSS_HREF = \"https://unpkg.com/leaflet@1.9.4/dist/leaflet.css\";\nconst CSS_LINK_ID = \"gds-leaflet-css\";\n\nfunction ensureLeafletCss() {\n if (typeof document === \"undefined\") return;\n // The MCP View bundles Leaflet's CSS inline and sets this flag, so we skip\n // the network <link> there (it would be CSP-blocked anyway).\n if ((globalThis as { __gradeLeafletCssBundled?: boolean }).__gradeLeafletCssBundled)\n return;\n if (document.getElementById(CSS_LINK_ID)) return;\n const link = document.createElement(\"link\");\n link.id = CSS_LINK_ID;\n link.rel = \"stylesheet\";\n link.href = LEAFLET_CSS_HREF;\n document.head.appendChild(link);\n}\n\n// Keyless CARTO raster basemaps (built on OSM data). Much closer to Google\n// Maps' default look than raw OSM: `voyager` is the light, road-forward style;\n// `dark_all` is a real dark style (no CSS-invert hack). Served from the\n// a–d.basemaps.cartocdn.com CDN; those hosts are declared in the MCP View's\n// resource CSP. Free with attribution.\nconst CARTO_STYLE: Record<\"light\" | \"dark\" | \"satellite\", string> = {\n light: \"rastertiles/voyager\",\n dark: \"rastertiles/dark_all\",\n satellite: \"rastertiles/voyager\",\n};\nconst cartoUrl = (style: string) =>\n `https://{s}.basemaps.cartocdn.com/${style}/{z}/{x}/{y}{r}.png`;\nconst CARTO_ATTRIBUTION = \"© OpenStreetMap contributors © CARTO\";\n\nexport const createLeafletAdapter: AdapterFactory = async (\n container,\n opts,\n callbacks\n) => {\n ensureLeafletCss();\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let L: any;\n try {\n // leaflet is resolved + bundled by the consumer (the MCP preview View);\n // it's intentionally NOT a dependency of @gradeui/ui, so the dts build\n // can't resolve its types here. The value is `any`, so suppress the\n // module-resolution error rather than pull leaflet into this package.\n // @ts-ignore -- optional peer, resolved by the bundling consumer\n L = await import(\"leaflet\");\n if (L.default) L = L.default;\n } catch (err) {\n callbacks.onError({\n code: \"sdk-missing\",\n message:\n '@gradeui/ui Map: `leaflet` is not installed. Run `pnpm add leaflet` to use the worker-free Leaflet adapter.',\n cause: err,\n });\n throw err;\n }\n\n // Grade coords are [lng, lat]; Leaflet wants [lat, lng].\n const toLatLng = (c: Coords): [number, number] => [c[1], c[0]];\n\n const map = L.map(container, {\n center: toLatLng(opts.center),\n zoom: opts.zoom,\n zoomControl: opts.interactive,\n attributionControl: true,\n dragging: opts.interactive,\n scrollWheelZoom: opts.interactive,\n doubleClickZoom: opts.interactive,\n boxZoom: opts.interactive,\n keyboard: opts.interactive,\n touchZoom: opts.interactive,\n });\n\n // Appearance is a different tile STYLE (not a CSS filter), so switching it\n // swaps the layer. `{r}` is Leaflet's retina suffix; `detectRetina` fills it.\n const addTiles = (appearance: \"light\" | \"dark\" | \"satellite\") => {\n const layer = L.tileLayer(cartoUrl(CARTO_STYLE[appearance] ?? CARTO_STYLE.light), {\n maxZoom: 20,\n subdomains: \"abcd\",\n detectRetina: true,\n attribution: CARTO_ATTRIBUTION,\n crossOrigin: true,\n });\n layer.on(\"tileerror\", () =>\n callbacks.onError({\n code: \"tile-load-failed\",\n message: \"map tile failed to load (is the tile host allowed by CSP?)\",\n })\n );\n layer.addTo(map);\n return layer;\n };\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let tiles: any = addTiles(opts.appearance);\n const applyAppearance = (appearance: \"light\" | \"dark\" | \"satellite\") => {\n if (tiles) map.removeLayer(tiles);\n tiles = addTiles(appearance);\n };\n\n if (opts.bounds) {\n map.fitBounds([toLatLng(opts.bounds[0]), toLatLng(opts.bounds[1])], {\n animate: false,\n });\n }\n\n // Leaflet initialises synchronously — signal ready immediately.\n callbacks.onLoad();\n\n // CRITICAL for sandboxed/absolute-inset containers (the MCP preview panel):\n // the iframe is frequently sized AFTER the map mounts, so Leaflet's initial\n // measurement is 0×0 and it renders NOTHING — no tiles, no markers. Re-measure\n // on the next frames and whenever the container resizes.\n const resizeObserver =\n typeof ResizeObserver !== \"undefined\"\n ? new ResizeObserver(() => map.invalidateSize(false))\n : null;\n resizeObserver?.observe(container);\n requestAnimationFrame(() => map.invalidateSize(false));\n window.setTimeout(() => map.invalidateSize(false), 200);\n window.setTimeout(() => map.invalidateSize(false), 600);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const markers = new globalThis.Map<string, { marker: any; handle: MarkerHandle }>();\n\n const adapter: AdapterInstance = {\n setCenter: (c) => map.panTo(toLatLng(c), { animate: false }),\n setZoom: (z) => map.setZoom(z),\n setBounds: (sw, ne) =>\n map.fitBounds([toLatLng(sw), toLatLng(ne)], { animate: false }),\n setAppearance: (a) => applyAppearance(a),\n setInteractive: (enabled) => {\n const handlers = [\n \"dragging\",\n \"scrollWheelZoom\",\n \"doubleClickZoom\",\n \"boxZoom\",\n \"keyboard\",\n \"touchZoom\",\n ] as const;\n for (const k of handlers) {\n const ctl = map[k];\n if (ctl) (enabled ? ctl.enable() : ctl.disable());\n }\n },\n flyTo: (c, o) =>\n map.flyTo(toLatLng(c), o?.zoom ?? map.getZoom(), {\n duration: (o?.durationMs ?? 800) / 1000,\n }),\n panTo: (c, o) =>\n map.panTo(toLatLng(c), { duration: (o?.durationMs ?? 600) / 1000 }),\n fitBounds: (list, o) => {\n if (list.length === 0) return;\n const pad = o?.paddingPx ?? 40;\n map.fitBounds(list.map(toLatLng), {\n padding: [pad, pad],\n duration: (o?.durationMs ?? 800) / 1000,\n });\n },\n getCenter: () => {\n const c = map.getCenter();\n return [c.lng, c.lat];\n },\n getZoom: () => map.getZoom(),\n getBounds: () => {\n const b = map.getBounds();\n const sw = b.getSouthWest();\n const ne = b.getNorthEast();\n return [\n [sw.lng, sw.lat],\n [ne.lng, ne.lat],\n ];\n },\n addMarker: (id, coords, anchor) => {\n // Live DOM marker: <MapMarker> portals its React children into\n // `element`. Leaflet positions the icon container's top-left at the\n // coord (iconAnchor [0,0]); our absolutely-positioned element then\n // offsets via transform so its bottom-centre (or centre) sits on the\n // point — matching the maplibre adapter's anchor semantics.\n const element = document.createElement(\"div\");\n element.dataset.gdsPart = \"map-marker\";\n element.dataset.gdsState = \"idle\";\n element.style.position = \"absolute\";\n element.style.cursor = \"pointer\";\n element.style.transform =\n anchor === \"center\"\n ? \"translate(-50%, -50%)\"\n : \"translate(-50%, -100%)\";\n\n element.addEventListener(\"mouseenter\", () => callbacks.onMarkerHover(id));\n element.addEventListener(\"mouseleave\", () => callbacks.onMarkerHover(null));\n element.addEventListener(\"click\", (e) =>\n callbacks.onMarkerClick(id, handle.coords, e)\n );\n\n const icon = L.divIcon({\n className: \"gds-leaflet-marker\",\n html: \"\",\n iconSize: [0, 0],\n iconAnchor: [0, 0],\n });\n const marker = L.marker(toLatLng(coords), {\n icon,\n interactive: true,\n keyboard: false,\n }).addTo(map);\n\n const iconEl: HTMLElement | undefined = marker.getElement();\n if (iconEl) {\n iconEl.style.pointerEvents = \"auto\";\n iconEl.appendChild(element);\n }\n\n const handle: MarkerHandle = {\n element,\n coords,\n setHovered: (hovered) => {\n element.dataset.gdsState = hovered ? \"hovered\" : \"idle\";\n if (iconEl) iconEl.style.zIndex = hovered ? \"1000\" : \"\";\n },\n setPosition: (next) => {\n handle.coords = next;\n marker.setLatLng(toLatLng(next));\n },\n remove: () => {\n map.removeLayer(marker);\n markers.delete(id);\n },\n };\n\n markers.set(id, { marker, handle });\n return handle;\n },\n destroy: () => {\n resizeObserver?.disconnect();\n markers.forEach(({ marker }) => map.removeLayer(marker));\n markers.clear();\n map.remove();\n },\n instance: map,\n };\n\n return adapter;\n};\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
var C="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css",h="gds-leaflet-css";function T(){if(typeof document>"u"||globalThis.__gradeLeafletCssBundled||document.getElementById(h))return;let s=document.createElement("link");s.id=h,s.rel="stylesheet",s.href=C,document.head.appendChild(s);}var k={light:"rastertiles/voyager",dark:"rastertiles/dark_all",satellite:"rastertiles/voyager"},E=s=>`https://{s}.basemaps.cartocdn.com/${s}/{z}/{x}/{y}{r}.png`,S="\xA9 OpenStreetMap contributors \xA9 CARTO",A=async(s,r,c)=>{T();let d;try{d=await import('leaflet'),d.default&&(d=d.default);}catch(e){throw c.onError({code:"sdk-missing",message:"@gradeui/ui Map: `leaflet` is not installed. Run `pnpm add leaflet` to use the worker-free Leaflet adapter.",cause:e}),e}let o=e=>[e[1],e[0]],t=d.map(s,{center:o(r.center),zoom:r.zoom,zoomControl:r.interactive,attributionControl:true,dragging:r.interactive,scrollWheelZoom:r.interactive,doubleClickZoom:r.interactive,boxZoom:r.interactive,keyboard:r.interactive,touchZoom:r.interactive}),v=e=>{let a=d.tileLayer(E(k[e]??k.light),{maxZoom:20,subdomains:"abcd",detectRetina:true,attribution:S,crossOrigin:true});return a.on("tileerror",()=>c.onError({code:"tile-load-failed",message:"map tile failed to load (is the tile host allowed by CSP?)"})),a.addTo(t),a},p=v(r.appearance),b=e=>{p&&t.removeLayer(p),p=v(e);};r.bounds&&t.fitBounds([o(r.bounds[0]),o(r.bounds[1])],{animate:false}),c.onLoad();let y=typeof ResizeObserver<"u"?new ResizeObserver(()=>t.invalidateSize(false)):null;y?.observe(s),requestAnimationFrame(()=>t.invalidateSize(false)),window.setTimeout(()=>t.invalidateSize(false),200),window.setTimeout(()=>t.invalidateSize(false),600);let u=new globalThis.Map;return {setCenter:e=>t.panTo(o(e),{animate:false}),setZoom:e=>t.setZoom(e),setBounds:(e,a)=>t.fitBounds([o(e),o(a)],{animate:false}),setAppearance:e=>b(e),setInteractive:e=>{let a=["dragging","scrollWheelZoom","doubleClickZoom","boxZoom","keyboard","touchZoom"];for(let i of a){let n=t[i];n&&(e?n.enable():n.disable());}},flyTo:(e,a)=>t.flyTo(o(e),a?.zoom??t.getZoom(),{duration:(a?.durationMs??800)/1e3}),panTo:(e,a)=>t.panTo(o(e),{duration:(a?.durationMs??600)/1e3}),fitBounds:(e,a)=>{if(e.length===0)return;let i=a?.paddingPx??40;t.fitBounds(e.map(o),{padding:[i,i],duration:(a?.durationMs??800)/1e3});},getCenter:()=>{let e=t.getCenter();return [e.lng,e.lat]},getZoom:()=>t.getZoom(),getBounds:()=>{let e=t.getBounds(),a=e.getSouthWest(),i=e.getNorthEast();return [[a.lng,a.lat],[i.lng,i.lat]]},addMarker:(e,a,i)=>{let n=document.createElement("div");n.dataset.gdsPart="map-marker",n.dataset.gdsState="idle",n.style.position="absolute",n.style.cursor="pointer",n.style.transform=i==="center"?"translate(-50%, -50%)":"translate(-50%, -100%)",n.addEventListener("mouseenter",()=>c.onMarkerHover(e)),n.addEventListener("mouseleave",()=>c.onMarkerHover(null)),n.addEventListener("click",l=>c.onMarkerClick(e,g.coords,l));let L=d.divIcon({className:"gds-leaflet-marker",html:"",iconSize:[0,0],iconAnchor:[0,0]}),f=d.marker(o(a),{icon:L,interactive:true,keyboard:false}).addTo(t),m=f.getElement();m&&(m.style.pointerEvents="auto",m.appendChild(n));let g={element:n,coords:a,setHovered:l=>{n.dataset.gdsState=l?"hovered":"idle",m&&(m.style.zIndex=l?"1000":"");},setPosition:l=>{g.coords=l,f.setLatLng(o(l));},remove:()=>{t.removeLayer(f),u.delete(e);}};return u.set(e,{marker:f,handle:g}),g},destroy:()=>{y?.disconnect(),u.forEach(({marker:e})=>t.removeLayer(e)),u.clear(),t.remove();},instance:t}};
|
|
3
|
+
export{A as createLeafletAdapter};//# sourceMappingURL=leaflet.mjs.map
|
|
4
|
+
//# sourceMappingURL=leaflet.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../components/ui/map/adapters/leaflet.ts"],"names":["LEAFLET_CSS_HREF","CSS_LINK_ID","ensureLeafletCss","link","CARTO_STYLE","cartoUrl","style","CARTO_ATTRIBUTION","createLeafletAdapter","container","opts","callbacks","L","err","toLatLng","c","map","addTiles","appearance","layer","tiles","applyAppearance","resizeObserver","markers","z","sw","ne","a","enabled","handlers","k","ctl","o","list","pad","b","id","coords","anchor","element","e","handle","icon","marker","iconEl","hovered","next"],"mappings":"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","file":"leaflet.mjs","sourcesContent":["import type {\n AdapterFactory,\n AdapterInstance,\n Coords,\n MarkerHandle,\n} from \"../types\";\n\n/**\n * Leaflet adapter — a WORKER-FREE raster-tile renderer.\n *\n * Why this exists alongside the maplibre adapter: maplibre-gl does all of its\n * rendering in a Web Worker (spawned from a `blob:` URL). Inside a strictly\n * sandboxed MCP App panel the default CSP is `default-src 'none'`, workers\n * fall back to that, and the MCP Apps CSP vocabulary has no `worker-src`\n * knob — so maplibre simply cannot run there. Leaflet paints raster tiles as\n * plain `<img>` elements on the main thread (no worker), so it runs fine; the\n * only thing it needs is network access to the tile host, which IS grantable\n * via the resource's `connectDomains` / `resourceDomains`.\n *\n * Keyless: OSM raster tiles, no API key, no referrer lock. The MCP View\n * forces this adapter (see the provider override in map.tsx); Studio and the\n * embed keep maplibre.\n */\n\nconst LEAFLET_CSS_HREF = \"https://unpkg.com/leaflet@1.9.4/dist/leaflet.css\";\nconst CSS_LINK_ID = \"gds-leaflet-css\";\n\nfunction ensureLeafletCss() {\n if (typeof document === \"undefined\") return;\n // The MCP View bundles Leaflet's CSS inline and sets this flag, so we skip\n // the network <link> there (it would be CSP-blocked anyway).\n if ((globalThis as { __gradeLeafletCssBundled?: boolean }).__gradeLeafletCssBundled)\n return;\n if (document.getElementById(CSS_LINK_ID)) return;\n const link = document.createElement(\"link\");\n link.id = CSS_LINK_ID;\n link.rel = \"stylesheet\";\n link.href = LEAFLET_CSS_HREF;\n document.head.appendChild(link);\n}\n\n// Keyless CARTO raster basemaps (built on OSM data). Much closer to Google\n// Maps' default look than raw OSM: `voyager` is the light, road-forward style;\n// `dark_all` is a real dark style (no CSS-invert hack). Served from the\n// a–d.basemaps.cartocdn.com CDN; those hosts are declared in the MCP View's\n// resource CSP. Free with attribution.\nconst CARTO_STYLE: Record<\"light\" | \"dark\" | \"satellite\", string> = {\n light: \"rastertiles/voyager\",\n dark: \"rastertiles/dark_all\",\n satellite: \"rastertiles/voyager\",\n};\nconst cartoUrl = (style: string) =>\n `https://{s}.basemaps.cartocdn.com/${style}/{z}/{x}/{y}{r}.png`;\nconst CARTO_ATTRIBUTION = \"© OpenStreetMap contributors © CARTO\";\n\nexport const createLeafletAdapter: AdapterFactory = async (\n container,\n opts,\n callbacks\n) => {\n ensureLeafletCss();\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let L: any;\n try {\n // leaflet is resolved + bundled by the consumer (the MCP preview View);\n // it's intentionally NOT a dependency of @gradeui/ui, so the dts build\n // can't resolve its types here. The value is `any`, so suppress the\n // module-resolution error rather than pull leaflet into this package.\n // @ts-ignore -- optional peer, resolved by the bundling consumer\n L = await import(\"leaflet\");\n if (L.default) L = L.default;\n } catch (err) {\n callbacks.onError({\n code: \"sdk-missing\",\n message:\n '@gradeui/ui Map: `leaflet` is not installed. Run `pnpm add leaflet` to use the worker-free Leaflet adapter.',\n cause: err,\n });\n throw err;\n }\n\n // Grade coords are [lng, lat]; Leaflet wants [lat, lng].\n const toLatLng = (c: Coords): [number, number] => [c[1], c[0]];\n\n const map = L.map(container, {\n center: toLatLng(opts.center),\n zoom: opts.zoom,\n zoomControl: opts.interactive,\n attributionControl: true,\n dragging: opts.interactive,\n scrollWheelZoom: opts.interactive,\n doubleClickZoom: opts.interactive,\n boxZoom: opts.interactive,\n keyboard: opts.interactive,\n touchZoom: opts.interactive,\n });\n\n // Appearance is a different tile STYLE (not a CSS filter), so switching it\n // swaps the layer. `{r}` is Leaflet's retina suffix; `detectRetina` fills it.\n const addTiles = (appearance: \"light\" | \"dark\" | \"satellite\") => {\n const layer = L.tileLayer(cartoUrl(CARTO_STYLE[appearance] ?? CARTO_STYLE.light), {\n maxZoom: 20,\n subdomains: \"abcd\",\n detectRetina: true,\n attribution: CARTO_ATTRIBUTION,\n crossOrigin: true,\n });\n layer.on(\"tileerror\", () =>\n callbacks.onError({\n code: \"tile-load-failed\",\n message: \"map tile failed to load (is the tile host allowed by CSP?)\",\n })\n );\n layer.addTo(map);\n return layer;\n };\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let tiles: any = addTiles(opts.appearance);\n const applyAppearance = (appearance: \"light\" | \"dark\" | \"satellite\") => {\n if (tiles) map.removeLayer(tiles);\n tiles = addTiles(appearance);\n };\n\n if (opts.bounds) {\n map.fitBounds([toLatLng(opts.bounds[0]), toLatLng(opts.bounds[1])], {\n animate: false,\n });\n }\n\n // Leaflet initialises synchronously — signal ready immediately.\n callbacks.onLoad();\n\n // CRITICAL for sandboxed/absolute-inset containers (the MCP preview panel):\n // the iframe is frequently sized AFTER the map mounts, so Leaflet's initial\n // measurement is 0×0 and it renders NOTHING — no tiles, no markers. Re-measure\n // on the next frames and whenever the container resizes.\n const resizeObserver =\n typeof ResizeObserver !== \"undefined\"\n ? new ResizeObserver(() => map.invalidateSize(false))\n : null;\n resizeObserver?.observe(container);\n requestAnimationFrame(() => map.invalidateSize(false));\n window.setTimeout(() => map.invalidateSize(false), 200);\n window.setTimeout(() => map.invalidateSize(false), 600);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const markers = new globalThis.Map<string, { marker: any; handle: MarkerHandle }>();\n\n const adapter: AdapterInstance = {\n setCenter: (c) => map.panTo(toLatLng(c), { animate: false }),\n setZoom: (z) => map.setZoom(z),\n setBounds: (sw, ne) =>\n map.fitBounds([toLatLng(sw), toLatLng(ne)], { animate: false }),\n setAppearance: (a) => applyAppearance(a),\n setInteractive: (enabled) => {\n const handlers = [\n \"dragging\",\n \"scrollWheelZoom\",\n \"doubleClickZoom\",\n \"boxZoom\",\n \"keyboard\",\n \"touchZoom\",\n ] as const;\n for (const k of handlers) {\n const ctl = map[k];\n if (ctl) (enabled ? ctl.enable() : ctl.disable());\n }\n },\n flyTo: (c, o) =>\n map.flyTo(toLatLng(c), o?.zoom ?? map.getZoom(), {\n duration: (o?.durationMs ?? 800) / 1000,\n }),\n panTo: (c, o) =>\n map.panTo(toLatLng(c), { duration: (o?.durationMs ?? 600) / 1000 }),\n fitBounds: (list, o) => {\n if (list.length === 0) return;\n const pad = o?.paddingPx ?? 40;\n map.fitBounds(list.map(toLatLng), {\n padding: [pad, pad],\n duration: (o?.durationMs ?? 800) / 1000,\n });\n },\n getCenter: () => {\n const c = map.getCenter();\n return [c.lng, c.lat];\n },\n getZoom: () => map.getZoom(),\n getBounds: () => {\n const b = map.getBounds();\n const sw = b.getSouthWest();\n const ne = b.getNorthEast();\n return [\n [sw.lng, sw.lat],\n [ne.lng, ne.lat],\n ];\n },\n addMarker: (id, coords, anchor) => {\n // Live DOM marker: <MapMarker> portals its React children into\n // `element`. Leaflet positions the icon container's top-left at the\n // coord (iconAnchor [0,0]); our absolutely-positioned element then\n // offsets via transform so its bottom-centre (or centre) sits on the\n // point — matching the maplibre adapter's anchor semantics.\n const element = document.createElement(\"div\");\n element.dataset.gdsPart = \"map-marker\";\n element.dataset.gdsState = \"idle\";\n element.style.position = \"absolute\";\n element.style.cursor = \"pointer\";\n element.style.transform =\n anchor === \"center\"\n ? \"translate(-50%, -50%)\"\n : \"translate(-50%, -100%)\";\n\n element.addEventListener(\"mouseenter\", () => callbacks.onMarkerHover(id));\n element.addEventListener(\"mouseleave\", () => callbacks.onMarkerHover(null));\n element.addEventListener(\"click\", (e) =>\n callbacks.onMarkerClick(id, handle.coords, e)\n );\n\n const icon = L.divIcon({\n className: \"gds-leaflet-marker\",\n html: \"\",\n iconSize: [0, 0],\n iconAnchor: [0, 0],\n });\n const marker = L.marker(toLatLng(coords), {\n icon,\n interactive: true,\n keyboard: false,\n }).addTo(map);\n\n const iconEl: HTMLElement | undefined = marker.getElement();\n if (iconEl) {\n iconEl.style.pointerEvents = \"auto\";\n iconEl.appendChild(element);\n }\n\n const handle: MarkerHandle = {\n element,\n coords,\n setHovered: (hovered) => {\n element.dataset.gdsState = hovered ? \"hovered\" : \"idle\";\n if (iconEl) iconEl.style.zIndex = hovered ? \"1000\" : \"\";\n },\n setPosition: (next) => {\n handle.coords = next;\n marker.setLatLng(toLatLng(next));\n },\n remove: () => {\n map.removeLayer(marker);\n markers.delete(id);\n },\n };\n\n markers.set(id, { marker, handle });\n return handle;\n },\n destroy: () => {\n resizeObserver?.disconnect();\n markers.forEach(({ marker }) => map.removeLayer(marker));\n markers.clear();\n map.remove();\n },\n instance: map,\n };\n\n return adapter;\n};\n"]}
|