{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "vector-map",
  "title": "Vector Map",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-vue/components/charts/vector-map/VectorMap.vue",
      "content": "<script lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\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  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  class?: HTMLAttributes['class']\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\nconst REGION_CENTROIDS: Record<string, [number, number]> = {\n  northAmerica: [-100, 45],\n  southAmerica: [-60, -15],\n  europe: [15, 52],\n  asia: [100, 40],\n  africa: [20, 5],\n  australiaOceania: [135, -25],\n  unitedKingdom: [-2, 54],\n  japan: [138, 38],\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\nimport { Map, MapMarker, MapSource, MapLayer, type MapVariant } from '@/components/ui/map'\nimport { cn } from '@/lib/utils'\nimport { Globe } from 'lucide-vue-next'\n\nconst props = withDefaults(defineProps<VectorMapProps>(), {\n  mode: 'continents',\n  regions: () => WORLD_REGIONS,\n  regionData: () => ({}),\n  selectedRegion: undefined,\n  pins: () => [],\n  routes: () => [],\n  height: 460,\n  interactive: true,\n  projection: 'globe',\n})\n\nconst emit = defineEmits<{\n  (e: 'update:selectedRegion', id: string): void\n  (e: 'selectRegion', id: string): void\n  (e: 'selectPin', pin: MapPin): void\n}>()\n\nconst activeRegion = ref(props.selectedRegion || 'northAmerica')\nconst currentProjection = ref<'globe' | 'mercator'>(props.projection)\nconst hoveredPin = ref<MapPin | null>(null)\n\nwatch(\n  () => props.selectedRegion,\n  (newVal) => {\n    if (newVal !== undefined) activeRegion.value = newVal\n  },\n)\n\nconst activeRecord = computed(() => {\n  return props.regionData[activeRegion.value] || null\n})\n\nfunction selectRegion(id: string) {\n  if (!props.interactive) return\n  activeRegion.value = id\n  emit('update:selectedRegion', id)\n  emit('selectRegion', id)\n}\n\nfunction toggleProjection() {\n  currentProjection.value = currentProjection.value === 'globe' ? 'mercator' : 'globe'\n}\n\nconst routesGeoJson = computed(() => {\n  if (!props.routes || !props.routes.length) return null\n  return {\n    type: 'FeatureCollection',\n    features: props.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})\n\nconst routeLinePaint = {\n  'line-color': ['get', 'color'],\n  'line-width': 1.5,\n  'line-dasharray': [2, 2],\n}\n</script>\n\n<template>\n  <div\n    :class=\"cn('border-border bg-card group relative w-full overflow-hidden rounded-xl border shadow-xs', props.class)\"\n    :style=\"{\n      height:\n        typeof props.height === 'number'\n          ? `${props.height}px`\n          : /^\\d+$/.test(String(props.height))\n            ? `${props.height}px`\n            : props.height,\n    }\"\n  >\n    <Map variant=\"dark\" :projection=\"currentProjection\" :center=\"[10, 25]\" :zoom=\"1.6\" class=\"size-full\">\n      <!-- Flow Routes Layer -->\n      <MapSource v-if=\"routesGeoJson\" id=\"vector-routes-source\" type=\"geojson\" :data=\"routesGeoJson\">\n        <MapLayer id=\"vector-routes-layer\" type=\"line\" :paint=\"routeLinePaint\" />\n      </MapSource>\n\n      <!-- Pins Layer -->\n      <MapMarker\n        v-for=\"(pin, i) in pins\"\n        :key=\"i\"\n        :lng-lat=\"[pin.lng ?? 0, pin.lat ?? 0]\"\n        anchor=\"center\"\n        class=\"cursor-pointer select-none\"\n      >\n        <div\n          class=\"relative flex size-6 items-center justify-center\"\n          @mouseenter=\"hoveredPin = pin\"\n          @mouseleave=\"hoveredPin = null\"\n          @click=\"emit('selectPin', pin)\"\n        >\n          <span\n            class=\"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            class=\"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    </Map>\n\n    <!-- Top-Left Region Selector Chips -->\n    <div\n      class=\"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    >\n      <button\n        v-for=\"r in regions\"\n        :key=\"r.id\"\n        type=\"button\"\n        :class=\"\n          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        \"\n        @click=\"selectRegion(r.id)\"\n      >\n        {{ r.name }}\n      </button>\n    </div>\n\n    <!-- Top-Right Controls -->\n    <div\n      class=\"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    >\n      <button\n        type=\"button\"\n        class=\"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        @click=\"toggleProjection\"\n      >\n        <Globe class=\"size-3.5\" />\n        <span class=\"capitalize\">{{ currentProjection }}</span>\n      </button>\n    </div>\n\n    <!-- Bottom-Left Region Detail Card -->\n    <div\n      v-if=\"activeRecord\"\n      class=\"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    >\n      <div class=\"flex items-center gap-2\">\n        <span class=\"size-2 rounded-full\" :style=\"{ backgroundColor: activeRecord.color || 'oklch(0.65 0.20 145)' }\" />\n        <h4 class=\"text-foreground text-sm font-semibold capitalize\">\n          {{ activeRegion.replace(/([A-Z])/g, ' $1') }}\n        </h4>\n        <span class=\"text-muted-foreground ml-auto font-mono text-xs uppercase\">\n          {{ activeRecord.status || 'Optimal' }}\n        </span>\n      </div>\n\n      <div\n        v-if=\"activeRecord.value !== undefined\"\n        class=\"border-border/60 mt-2.5 flex items-baseline justify-between border-t pt-2 font-mono text-xs\"\n      >\n        <span class=\"text-muted-foreground\">Active Nodes</span>\n        <span class=\"text-foreground font-semibold\">\n          {{ activeRecord.value.toLocaleString() }}\n        </span>\n      </div>\n\n      <p v-if=\"activeRecord.description\" class=\"text-muted-foreground mt-1.5 text-xs leading-relaxed\">\n        {{ activeRecord.description }}\n      </p>\n    </div>\n\n    <!-- Hovered Pin HUD -->\n    <div\n      v-if=\"hoveredPin\"\n      class=\"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    >\n      <div class=\"flex items-center gap-2\">\n        <span class=\"size-2 rounded-full\" :style=\"{ backgroundColor: hoveredPin.color || 'oklch(0.65 0.20 145)' }\" />\n        <h5 class=\"text-foreground text-xs font-semibold\">\n          {{ hoveredPin.label || 'Hub' }}\n        </h5>\n      </div>\n      <p v-if=\"hoveredPin.description\" class=\"text-muted-foreground mt-1 text-xs\">\n        {{ hoveredPin.description }}\n      </p>\n      <div\n        v-if=\"hoveredPin.value !== undefined\"\n        class=\"border-border/60 mt-2 flex items-baseline justify-between border-t pt-1.5 font-mono text-xs\"\n      >\n        <span class=\"text-muted-foreground\">Throughput</span>\n        <span class=\"text-foreground font-semibold\">{{ hoveredPin.value }}</span>\n      </div>\n    </div>\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/charts/vector-map/VectorMap.vue"
    },
    {
      "path": "packages/registry-vue/components/charts/vector-map/index.ts",
      "content": "export {\n  default as VectorMap,\n  default,\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.vue'\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/charts/vector-map/index.ts"
    }
  ],
  "dependencies": [
    "lucide-vue-next"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/vue/map.json"
  ],
  "description": "Pure dependency-free SVG world landmass vector map. Features high-resolution continental paths (North America, South America, Europe, Africa, Asia, Oceania, UK, Japan) with region data shading, interactive pulsing pins, curved flight routes, and rich telemetry hover popovers.",
  "categories": [
    "chart"
  ]
}