{
  "name": "world-map",
  "type": "registry:ui",
  "dependencies": [
    "dotted-map",
    "motion"
  ],
  "files": [
    {
      "path": "components/ui/world-map.tsx",
      "content": "\"use client\";\n\nimport { useRef } from \"react\";\nimport { motion } from \"motion/react\";\nimport DottedMap from \"dotted-map\";\n\nimport { useTheme } from \"next-themes\";\n\ninterface MapProps {\n  dots?: Array<{\n    start: { lat: number; lng: number; label?: string };\n    end: { lat: number; lng: number; label?: string };\n  }>;\n  lineColor?: string;\n}\n\nexport default function WorldMap({\n  dots = [],\n  lineColor = \"#0ea5e9\",\n}: MapProps) {\n  const svgRef = useRef<SVGSVGElement>(null);\n  const map = new DottedMap({ height: 100, grid: \"diagonal\" });\n\n  const { theme } = useTheme();\n\n  const svgMap = map.getSVG({\n    radius: 0.22,\n    color: theme === \"dark\" ? \"#FFFFFF40\" : \"#00000040\",\n    shape: \"circle\",\n    backgroundColor: theme === \"dark\" ? \"black\" : \"white\",\n  });\n\n  const projectPoint = (lat: number, lng: number) => {\n    const x = (lng + 180) * (800 / 360);\n    const y = (90 - lat) * (400 / 180);\n    return { x, y };\n  };\n\n  const createCurvedPath = (\n    start: { x: number; y: number },\n    end: { x: number; y: number }\n  ) => {\n    const midX = (start.x + end.x) / 2;\n    const midY = Math.min(start.y, end.y) - 50;\n    return `M ${start.x} ${start.y} Q ${midX} ${midY} ${end.x} ${end.y}`;\n  };\n\n  return (\n    <div className=\"w-full aspect-[2/1] dark:bg-black bg-white rounded-lg  relative font-sans\">\n      <img\n        src={`data:image/svg+xml;utf8,${encodeURIComponent(svgMap)}`}\n        className=\"h-full w-full [mask-image:linear-gradient(to_bottom,transparent,white_10%,white_90%,transparent)] pointer-events-none select-none\"\n        alt=\"world map\"\n        height=\"495\"\n        width=\"1056\"\n        draggable={false}\n      />\n      <svg\n        ref={svgRef}\n        viewBox=\"0 0 800 400\"\n        className=\"w-full h-full absolute inset-0 pointer-events-none select-none\"\n      >\n        {dots.map((dot, i) => {\n          const startPoint = projectPoint(dot.start.lat, dot.start.lng);\n          const endPoint = projectPoint(dot.end.lat, dot.end.lng);\n          return (\n            <g key={`path-group-${i}`}>\n              <motion.path\n                d={createCurvedPath(startPoint, endPoint)}\n                fill=\"none\"\n                stroke=\"url(#path-gradient)\"\n                strokeWidth=\"1\"\n                initial={{\n                  pathLength: 0,\n                }}\n                animate={{\n                  pathLength: 1,\n                }}\n                transition={{\n                  duration: 1,\n                  delay: 0.5 * i,\n                  ease: \"easeOut\",\n                }}\n                key={`start-upper-${i}`}\n              ></motion.path>\n            </g>\n          );\n        })}\n\n        <defs>\n          <linearGradient id=\"path-gradient\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"0%\">\n            <stop offset=\"0%\" stopColor=\"white\" stopOpacity=\"0\" />\n            <stop offset=\"5%\" stopColor={lineColor} stopOpacity=\"1\" />\n            <stop offset=\"95%\" stopColor={lineColor} stopOpacity=\"1\" />\n            <stop offset=\"100%\" stopColor=\"white\" stopOpacity=\"0\" />\n          </linearGradient>\n        </defs>\n\n        {dots.map((dot, i) => (\n          <g key={`points-group-${i}`}>\n            <g key={`start-${i}`}>\n              <circle\n                cx={projectPoint(dot.start.lat, dot.start.lng).x}\n                cy={projectPoint(dot.start.lat, dot.start.lng).y}\n                r=\"2\"\n                fill={lineColor}\n              />\n              <circle\n                cx={projectPoint(dot.start.lat, dot.start.lng).x}\n                cy={projectPoint(dot.start.lat, dot.start.lng).y}\n                r=\"2\"\n                fill={lineColor}\n                opacity=\"0.5\"\n              >\n                <animate\n                  attributeName=\"r\"\n                  from=\"2\"\n                  to=\"8\"\n                  dur=\"1.5s\"\n                  begin=\"0s\"\n                  repeatCount=\"indefinite\"\n                />\n                <animate\n                  attributeName=\"opacity\"\n                  from=\"0.5\"\n                  to=\"0\"\n                  dur=\"1.5s\"\n                  begin=\"0s\"\n                  repeatCount=\"indefinite\"\n                />\n              </circle>\n            </g>\n            <g key={`end-${i}`}>\n              <circle\n                cx={projectPoint(dot.end.lat, dot.end.lng).x}\n                cy={projectPoint(dot.end.lat, dot.end.lng).y}\n                r=\"2\"\n                fill={lineColor}\n              />\n              <circle\n                cx={projectPoint(dot.end.lat, dot.end.lng).x}\n                cy={projectPoint(dot.end.lat, dot.end.lng).y}\n                r=\"2\"\n                fill={lineColor}\n                opacity=\"0.5\"\n              >\n                <animate\n                  attributeName=\"r\"\n                  from=\"2\"\n                  to=\"8\"\n                  dur=\"1.5s\"\n                  begin=\"0s\"\n                  repeatCount=\"indefinite\"\n                />\n                <animate\n                  attributeName=\"opacity\"\n                  from=\"0.5\"\n                  to=\"0\"\n                  dur=\"1.5s\"\n                  begin=\"0s\"\n                  repeatCount=\"indefinite\"\n                />\n              </circle>\n            </g>\n          </g>\n        ))}\n      </svg>\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/ui/world-map.tsx"
    }
  ],
  "author": "Manu Arora <hi@manuarora.in>",
  "title": "World Map"
}