{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "vector-map",
  "title": "Vector Map",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-react/components/charts/vector-map/VectorMap.tsx",
      "content": "'use client'\n\nimport * as React from 'react'\nimport type { LineLayerSpecification } from 'mapbox-gl'\nimport { Map, MapMarker, MapSource, MapLayer, type MapVariant } from '@/components/ui/map'\nimport { cn } from '@/lib/utils'\nimport { Globe } from 'lucide-react'\n\nexport interface MapRegion {\n  id: string\n  name: string\n  path?: string\n}\n\nexport interface MapPin {\n  id?: string\n  lat?: number\n  lng?: number\n  x?: number\n  y?: number\n  label?: string\n  value?: string | number\n  color?: string\n  status?: string\n  description?: string\n}\n\nexport interface FlowRoute {\n  id?: string\n  from: { lat?: number; lng?: number; x?: number; y?: number }\n  to: { lat?: number; lng?: number; x?: number; y?: number }\n  color?: string\n  width?: number\n  curvature?: number\n  animated?: boolean\n  dashed?: boolean\n  duration?: number\n  label?: string\n}\n\nexport interface RegionDataRecord {\n  value?: number\n  label?: string\n  status?: string\n  color?: string\n  description?: string\n}\n\nexport interface VectorMapProps {\n  mode?: 'countries' | 'continents'\n  regions?: MapRegion[]\n  regionData?: Record<string, RegionDataRecord>\n  selectedRegion?: string\n  onSelectedRegionChange?: (id: string) => void\n  pins?: MapPin[]\n  routes?: FlowRoute[]\n  height?: number | string\n  interactive?: boolean\n  showGraticule?: boolean\n  showRegionLabels?: boolean\n  fillColor?: string\n  hoverColor?: string\n  strokeColor?: string\n  className?: string\n  ariaLabel?: string\n  projection?: 'globe' | 'mercator'\n}\n\nexport const WORLD_REGIONS: MapRegion[] = [\n  { id: 'northAmerica', name: 'North America' },\n  { id: 'southAmerica', name: 'South America' },\n  { id: 'europe', name: 'Europe' },\n  { id: 'asia', name: 'Asia' },\n  { id: 'africa', name: 'Africa' },\n  { id: 'australiaOceania', name: 'Oceania' },\n  { id: 'unitedKingdom', name: 'United Kingdom' },\n  { id: 'japan', name: 'Japan' },\n]\n\nexport const WORLD_COUNTRIES: MapRegion[] = [\n  { id: 'US', name: 'United States' },\n  { id: 'CA', name: 'Canada' },\n  { id: 'GB', name: 'United Kingdom' },\n  { id: 'DE', name: 'Germany' },\n  { id: 'FR', name: 'France' },\n  { id: 'JP', name: 'Japan' },\n  { id: 'CN', name: 'China' },\n  { id: 'IN', name: 'India' },\n  { id: 'BR', name: 'Brazil' },\n  { id: 'AU', name: 'Australia' },\n]\n\nexport function projectPoint(pt: { lat?: number; lng?: number; x?: number; y?: number }) {\n  if (pt.x !== undefined && pt.y !== undefined) return { x: pt.x, y: pt.y }\n  const lng = pt.lng ?? 0\n  const lat = pt.lat ?? 0\n  const x = ((lng + 180) / 360) * 1000\n  const y = ((90 - lat) / 180) * 500\n  return { x, y }\n}\n\nexport function VectorMap({\n  mode = 'continents',\n  regions = WORLD_REGIONS,\n  regionData = {},\n  selectedRegion: controlledRegion,\n  onSelectedRegionChange,\n  pins = [],\n  routes = [],\n  height = 460,\n  interactive = true,\n  projection: initialProjection = 'globe',\n  className,\n}: VectorMapProps) {\n  const [internalRegion, setInternalRegion] = React.useState<string>('northAmerica')\n  const [projection, setProjection] = React.useState<'globe' | 'mercator'>(initialProjection)\n  const [hoveredPin, setHoveredPin] = React.useState<MapPin | null>(null)\n\n  const activeRegion = controlledRegion !== undefined ? controlledRegion : internalRegion\n  const activeRecord = regionData[activeRegion] || null\n\n  const handleSelectRegion = (id: string) => {\n    if (!interactive) return\n    setInternalRegion(id)\n    onSelectedRegionChange?.(id)\n  }\n\n  const toggleProjection = () => {\n    setProjection((prev) => (prev === 'globe' ? 'mercator' : 'globe'))\n  }\n\n  const routesGeoJson = React.useMemo(() => {\n    if (!routes || !routes.length) return null\n    return {\n      type: 'FeatureCollection',\n      features: routes.map((r, i) => {\n        const fromLng = r.from.lng ?? -74\n        const fromLat = r.from.lat ?? 40\n        const toLng = r.to.lng ?? 8\n        const toLat = r.to.lat ?? 50\n        return {\n          type: 'Feature',\n          id: i,\n          properties: {\n            color: r.color || 'rgba(56, 189, 248, 0.75)',\n          },\n          geometry: {\n            type: 'LineString',\n            coordinates: [\n              [fromLng, fromLat],\n              [(fromLng + toLng) / 2, (fromLat + toLat) / 2 + 5],\n              [toLng, toLat],\n            ],\n          },\n        }\n      }),\n    }\n  }, [routes])\n\n  // mapbox reads a data-driven paint value as an expression tuple; without the\n  // annotation TS widens ['get', 'color'] to string[] and the layer rejects it.\n  const routeLinePaint: LineLayerSpecification['paint'] = {\n    'line-color': ['get', 'color'],\n    'line-width': 1.5,\n    'line-dasharray': [2, 2],\n  }\n\n  return (\n    <div\n      className={cn(\n        'border-border bg-card group relative w-full overflow-hidden rounded-xl border shadow-xs',\n        className,\n      )}\n      style={{ height: typeof height === 'number' ? `${height}px` : height }}\n    >\n      <Map variant=\"dark\" projection={projection} center={[10, 25]} zoom={1.6} className=\"size-full\">\n        {routesGeoJson && (\n          <MapSource id=\"vector-routes-source\" type=\"geojson\" data={routesGeoJson}>\n            <MapLayer id=\"vector-routes-layer\" type=\"line\" paint={routeLinePaint} />\n          </MapSource>\n        )}\n\n        {pins.map((pin, i) => (\n          <MapMarker\n            key={i}\n            longitude={pin.lng ?? 0}\n            latitude={pin.lat ?? 0}\n            anchor=\"center\"\n            className=\"cursor-pointer select-none\"\n          >\n            <div\n              className=\"relative flex size-6 items-center justify-center\"\n              onMouseEnter={() => setHoveredPin(pin)}\n              onMouseLeave={() => setHoveredPin(null)}\n            >\n              <span\n                className=\"absolute inline-flex size-full animate-ping rounded-full opacity-60\"\n                style={{ backgroundColor: pin.color || 'oklch(0.65 0.20 145)' }}\n              />\n              <span\n                className=\"ring-background relative inline-flex size-2.5 rounded-full shadow-xs ring-2\"\n                style={{\n                  backgroundColor: pin.color || 'oklch(0.65 0.20 145)',\n                  boxShadow: `0 0 10px ${pin.color || 'oklch(0.65 0.20 145)'}`,\n                }}\n              />\n            </div>\n          </MapMarker>\n        ))}\n      </Map>\n\n      <div className=\"border-border/70 bg-card/85 absolute top-3 left-3 z-10 hidden max-w-md flex-wrap gap-1 rounded-lg border p-1.5 shadow-xs backdrop-blur-md md:flex\">\n        {regions.map((r) => (\n          <button\n            key={r.id}\n            type=\"button\"\n            className={cn(\n              'rounded-md px-2 py-0.5 text-xs font-medium transition',\n              activeRegion === r.id\n                ? 'bg-primary text-primary-foreground font-semibold'\n                : 'text-muted-foreground hover:bg-muted hover:text-foreground',\n            )}\n            onClick={() => handleSelectRegion(r.id)}\n          >\n            {r.name}\n          </button>\n        ))}\n      </div>\n\n      <div className=\"border-border/70 bg-card/85 absolute top-3 right-3 z-10 flex items-center gap-1 rounded-lg border p-1 shadow-xs backdrop-blur-md\">\n        <button\n          type=\"button\"\n          className=\"text-muted-foreground hover:bg-muted hover:text-foreground flex items-center gap-1.5 rounded-md px-2.5 py-1 text-xs font-medium transition\"\n          onClick={toggleProjection}\n        >\n          <Globe className=\"size-3.5\" />\n          <span className=\"capitalize\">{projection}</span>\n        </button>\n      </div>\n\n      {activeRecord && (\n        <div className=\"border-border/80 bg-card/95 animate-in fade-in slide-in-from-bottom-2 absolute bottom-3 left-3 z-10 max-w-sm rounded-xl border p-3.5 shadow-lg backdrop-blur-md duration-150\">\n          <div className=\"flex items-center gap-2\">\n            <span\n              className=\"size-2 rounded-full\"\n              style={{ backgroundColor: activeRecord.color || 'oklch(0.65 0.20 145)' }}\n            />\n            <h4 className=\"text-foreground text-sm font-semibold capitalize\">\n              {activeRegion.replace(/([A-Z])/g, ' $1')}\n            </h4>\n            <span className=\"text-muted-foreground ml-auto font-mono text-xs uppercase\">\n              {activeRecord.status || 'Optimal'}\n            </span>\n          </div>\n\n          {activeRecord.value !== undefined && (\n            <div className=\"border-border/60 mt-2.5 flex items-baseline justify-between border-t pt-2 font-mono text-xs\">\n              <span className=\"text-muted-foreground\">Active Nodes</span>\n              <span className=\"text-foreground font-semibold\">{activeRecord.value.toLocaleString()}</span>\n            </div>\n          )}\n\n          {activeRecord.description && (\n            <p className=\"text-muted-foreground mt-1.5 text-xs leading-relaxed\">{activeRecord.description}</p>\n          )}\n        </div>\n      )}\n\n      {hoveredPin && (\n        <div className=\"border-border/80 bg-card/95 animate-in fade-in slide-in-from-bottom-2 absolute right-3 bottom-3 z-10 max-w-xs rounded-xl border p-3 shadow-lg backdrop-blur-md duration-150\">\n          <div className=\"flex items-center gap-2\">\n            <span\n              className=\"size-2 rounded-full\"\n              style={{ backgroundColor: hoveredPin.color || 'oklch(0.65 0.20 145)' }}\n            />\n            <h5 className=\"text-foreground text-xs font-semibold\">{hoveredPin.label || 'Hub'}</h5>\n          </div>\n          {hoveredPin.description && <p className=\"text-muted-foreground mt-1 text-xs\">{hoveredPin.description}</p>}\n          {hoveredPin.value !== undefined && (\n            <div className=\"border-border/60 mt-2 flex items-baseline justify-between border-t pt-1.5 font-mono text-xs\">\n              <span className=\"text-muted-foreground\">Throughput</span>\n              <span className=\"text-foreground font-semibold\">{hoveredPin.value}</span>\n            </div>\n          )}\n        </div>\n      )}\n    </div>\n  )\n}\n\nexport default VectorMap\n",
      "type": "registry:ui",
      "target": "~/components/ui/charts/vector-map/VectorMap.tsx"
    },
    {
      "path": "packages/registry-react/components/charts/vector-map/index.ts",
      "content": "export {\n  VectorMap,\n  WORLD_REGIONS,\n  WORLD_COUNTRIES,\n  projectPoint,\n  type VectorMapProps,\n  type MapRegion,\n  type MapPin,\n  type FlowRoute,\n  type RegionDataRecord,\n} from './VectorMap'\nexport { default } from './VectorMap'\n",
      "type": "registry:ui",
      "target": "~/components/ui/charts/vector-map/index.ts"
    }
  ],
  "dependencies": [
    "mapbox-gl",
    "lucide-react"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/react/map.json"
  ],
  "description": "React mirror of @uipkge/vector-map — see the Vue registry item for the canonical description.",
  "categories": [
    "chart"
  ]
}