{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "region-select",
  "title": "RegionSelect",
  "description": "Server location picker: an interactive dot-matrix world map synced with an accessible select.",
  "registryDependencies": [
    "utils",
    "https://ui.neon.com/r/select.json",
    "https://ui.neon.com/r/neon-tokens.json",
    "https://ui.neon.com/r/region-card.json"
  ],
  "files": [
    {
      "path": "src/components/region-select/region-select.tsx",
      "content": "\"use client\";\n\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\n\nimport { RegionCard } from \"@/components/region-card/region-card\";\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\";\nimport { cn } from \"@/lib/utils\";\n\nimport {\n  decodeRow,\n  MAP_BITS,\n  MAP_COLS,\n  MAP_LAT_MAX,\n  MAP_LAT_MIN,\n  MAP_ROWS,\n} from \"./map-dots\";\n\nexport interface ServerRegion {\n  /** Region id, e.g. \"aws-us-east-1\". */\n  id: string;\n  /** Human-readable name, e.g. \"US East 1 (N. Virginia)\". */\n  name: string;\n  /** Cloud provider label used as the name prefix, e.g. \"AWS\". */\n  provider?: string;\n  /** Latitude of the datacenter location. */\n  lat: number;\n  /** Longitude of the datacenter location. */\n  lng: number;\n  /** Mark a region as unavailable; the marker and option render dimmed. */\n  disabled?: boolean;\n}\n\nexport type RegionSelectProps = Omit<\n  ComponentProps<\"div\">,\n  \"defaultValue\" | \"onChange\"\n> & {\n  /** Regions to plot and list; order drives the select. */\n  regions: ServerRegion[];\n  /** Controlled selected region id. */\n  value?: string;\n  /** Uncontrolled initial region id. */\n  defaultValue?: string;\n  onValueChange?: (value: string) => void;\n  /** Placeholder shown in the select before a region is chosen. */\n  placeholder?: string;\n  /** Hide the select under the map; markers become the only input. */\n  hideSelect?: boolean;\n  /** Hide the floating region card on the map. */\n  hideCard?: boolean;\n  /** Extra content rendered inside the region card, e.g. a latency badge. */\n  cardSuffix?: ReactNode;\n  /** Measured round-trips by region id, e.g. from useRegionPing. */\n  latencies?: Record<string, number | null | undefined>;\n};\n\n/** Full display label: provider prefix plus region name. */\nconst regionLabel = (region: ServerRegion) =>\n  region.provider ? `${region.provider} ${region.name}` : region.name;\n\n/** Equirectangular position of a lat/lng pair as percentages of the map. */\nconst project = (lat: number, lng: number) => ({\n  left: ((lng + 180) / 360) * 100,\n  top: ((MAP_LAT_MAX - lat) / (MAP_LAT_MAX - MAP_LAT_MIN)) * 100,\n});\n\n/* ─────────────────────────────────────────────────────────\n * REGION CARD PLACEMENT\n *\n * The card anchors beside the selected marker and flips\n * quadrant so it always grows toward the map's center —\n * never covering the marker, never clipping at an edge.\n * ───────────────────────────────────────────────────────── */\nconst cardAnchor = (region: ServerRegion) => {\n  const { left, top } = project(region.lat, region.lng);\n  const LEFT_HALF = 50;\n\n  return {\n    ...(left <= LEFT_HALF ? { left: `${left}%` } : { right: `${100 - left}%` }),\n    top: `${top}%`,\n  };\n};\n\nconst cardPlacement = (region: ServerRegion) => {\n  const { left, top } = project(region.lat, region.lng);\n  const HALF = 50;\n  const vertical = top <= HALF ? \"mt-3\" : \"-translate-y-full -mt-3\";\n  const horizontal = left <= HALF ? \"ml-2\" : \"mr-2\";\n\n  return cn(vertical, horizontal);\n};\n\n/* ─────────────────────────────────────────────────────────\n * DOT MAP\n *\n * The land bitmap renders once to a canvas sized to the\n * container (device-pixel aware), in the current value of\n * `color` — so the map re-inks itself on theme flips via a\n * class observer, and never ships thousands of DOM nodes.\n * ───────────────────────────────────────────────────────── */\nconst DOT_RADIUS_RATIO = 0.32;\n\nconst drawMap = (canvas: HTMLCanvasElement) => {\n  const context = canvas.getContext(\"2d\");\n  const width = canvas.clientWidth;\n\n  if (!(context && width)) {\n    return;\n  }\n\n  const dpr = window.devicePixelRatio || 1;\n  const cell = width / MAP_COLS;\n  const height = cell * MAP_ROWS;\n\n  canvas.width = Math.round(width * dpr);\n  canvas.height = Math.round(height * dpr);\n  context.scale(dpr, dpr);\n  context.clearRect(0, 0, width, height);\n  context.fillStyle = getComputedStyle(canvas).color;\n\n  const radius = cell * DOT_RADIUS_RATIO;\n  const TAU = Math.PI * 2;\n\n  for (let row = 0; row < MAP_ROWS; row += 1) {\n    const bits = decodeRow(MAP_BITS[row] ?? \"\");\n    const cy = (row + 0.5) * cell;\n\n    for (let col = 0; col < MAP_COLS; col += 1) {\n      if (bits[col]) {\n        context.beginPath();\n        context.arc((col + 0.5) * cell, cy, radius, 0, TAU);\n        context.fill();\n      }\n    }\n  }\n};\n\nconst DotMap = () => {\n  const ref = useRef<HTMLCanvasElement>(null);\n\n  useEffect(() => {\n    const canvas = ref.current;\n\n    if (!canvas) {\n      return;\n    }\n\n    const redraw = () => drawMap(canvas);\n\n    redraw();\n\n    const resizeObserver = new ResizeObserver(redraw);\n    resizeObserver.observe(canvas);\n\n    // Re-ink on theme flips: class-based dark mode toggles on <html>.\n    const themeObserver = new MutationObserver(redraw);\n    themeObserver.observe(document.documentElement, {\n      attributeFilter: [\"class\", \"data-theme\", \"style\"],\n      attributes: true,\n    });\n\n    return () => {\n      resizeObserver.disconnect();\n      themeObserver.disconnect();\n    };\n  }, []);\n\n  return (\n    <canvas\n      aria-hidden=\"true\"\n      className=\"block w-full text-muted-foreground/50\"\n      data-slot=\"region-select-map\"\n      ref={ref}\n      style={{ aspectRatio: `${MAP_COLS} / ${MAP_ROWS}` }}\n    />\n  );\n};\n\n/* ─────────────────────────────────────────────────────────\n * MARKERS\n *\n * Each region is a real button on the map: hover or focus\n * lifts a label pill above it, click selects. The selected\n * marker glows primary with a slow ping halo (static under\n * reduced motion). The select below stays the keyboard and\n * screen-reader path, so markers skip roving-focus theater.\n * ───────────────────────────────────────────────────────── */\n/** Flips the hover pill away from nearby map edges so it never clips. */\nconst pillPlacement = (left: number, top: number) => {\n  const EDGE = 20;\n  const NEAR_TOP = 18;\n  const horizontal = (() => {\n    if (left <= EDGE) {\n      return \"-left-1\";\n    }\n\n    if (left >= 100 - EDGE) {\n      return \"-right-1\";\n    }\n\n    return \"-translate-x-1/2 left-1/2\";\n  })();\n  const vertical = top <= NEAR_TOP ? \"top-full mt-1.5\" : \"bottom-full mb-1.5\";\n\n  return cn(horizontal, vertical);\n};\n\nconst RegionMarker = ({\n  onSelect,\n  region,\n  selected,\n}: {\n  onSelect: () => void;\n  region: ServerRegion;\n  selected: boolean;\n}) => {\n  const { left, top } = project(region.lat, region.lng);\n\n  return (\n    <button\n      aria-label={regionLabel(region)}\n      aria-pressed={selected}\n      className=\"group -translate-x-1/2 -translate-y-1/2 absolute grid size-6 place-items-center rounded-full outline-none disabled:pointer-events-none disabled:opacity-40\"\n      data-selected={selected || undefined}\n      data-slot=\"region-select-marker\"\n      disabled={region.disabled}\n      onClick={onSelect}\n      style={{ left: `${left}%`, top: `${top}%` }}\n      type=\"button\"\n    >\n      {selected ? (\n        <span\n          aria-hidden=\"true\"\n          className=\"absolute size-3 rounded-full bg-primary/60 motion-safe:animate-ping\"\n        />\n      ) : null}\n      <span\n        className={cn(\n          \"relative size-2 rounded-full transition-[background-color,box-shadow,transform] duration-150\",\n          selected\n            ? \"bg-primary shadow-[0_0_10px_2px_var(--color-primary)]\"\n            : \"bg-muted-foreground/70 group-hover:scale-125 group-hover:bg-foreground group-focus-visible:scale-125 group-focus-visible:bg-foreground\"\n        )}\n      />\n      <span\n        className={cn(\n          \"pointer-events-none absolute z-10 hidden whitespace-nowrap rounded-md bg-popover px-2 py-1 text-popover-foreground text-xs ring-1 ring-border/60 group-focus-visible:block group-hover:block\",\n          pillPlacement(left, top)\n        )}\n        role=\"presentation\"\n      >\n        {region.name}\n        <span className=\"ml-1.5 font-mono text-[10px] text-muted-foreground\">\n          {region.id}\n        </span>\n      </span>\n      <span className=\"absolute inset-0 rounded-full ring-primary/50 group-focus-visible:ring-2\" />\n    </button>\n  );\n};\n\nexport const RegionSelect = ({\n  cardSuffix,\n  className,\n  defaultValue,\n  hideCard,\n  hideSelect,\n  latencies,\n  onValueChange,\n  placeholder = \"Select region\",\n  regions,\n  value,\n  ...props\n}: RegionSelectProps) => {\n  const [internal, setInternal] = useState(defaultValue);\n  const selectedId = value ?? internal;\n  const selected = regions.find((region) => region.id === selectedId);\n  const selectedPing = selectedId ? latencies?.[selectedId] : undefined;\n\n  const select = (next: string) => {\n    setInternal(next);\n    onValueChange?.(next);\n  };\n\n  const items = useMemo(\n    () =>\n      regions.map((region) => ({\n        label: regionLabel(region),\n        value: region.id,\n      })),\n    [regions]\n  );\n\n  return (\n    <div\n      className={cn(\"flex w-full flex-col gap-3\", className)}\n      data-slot=\"region-select\"\n      {...props}\n    >\n      <div className=\"relative overflow-hidden rounded-lg border border-border/60 bg-card/40 p-3\">\n        <DotMap />\n        <div className=\"absolute inset-3\">\n          {regions.map((region) => (\n            <RegionMarker\n              key={region.id}\n              onSelect={() => select(region.id)}\n              region={region}\n              selected={region.id === selectedId}\n            />\n          ))}\n        </div>\n\n        {hideCard || !selected ? null : (\n          <RegionCard\n            className={cn(\n              \"pointer-events-none absolute max-w-[75%]\",\n              cardPlacement(selected)\n            )}\n            key={selected.id}\n            ping={selectedPing}\n            regionId={selected.id}\n            style={cardAnchor(selected)}\n            title={regionLabel(selected)}\n          >\n            {cardSuffix}\n          </RegionCard>\n        )}\n      </div>\n\n      {hideSelect ? null : (\n        <Select\n          items={items}\n          onValueChange={(next) => {\n            if (typeof next === \"string\") {\n              select(next);\n            }\n          }}\n          value={selectedId ?? null}\n        >\n          <SelectTrigger\n            aria-label=\"Region\"\n            className=\"w-full\"\n            data-slot=\"region-select-trigger\"\n          >\n            <SelectValue>\n              {selected ? (\n                regionLabel(selected)\n              ) : (\n                <span className=\"text-muted-foreground\">{placeholder}</span>\n              )}\n            </SelectValue>\n          </SelectTrigger>\n          <SelectContent align=\"start\" alignItemWithTrigger={false}>\n            {regions.map((region) => (\n              <SelectItem\n                disabled={region.disabled}\n                key={region.id}\n                value={region.id}\n              >\n                {regionLabel(region)}\n              </SelectItem>\n            ))}\n          </SelectContent>\n        </Select>\n      )}\n    </div>\n  );\n};\n",
      "type": "registry:component"
    },
    {
      "path": "src/components/region-select/map-dots.ts",
      "content": "/**\n * Dot-matrix world land bitmap for the RegionSelect map.\n *\n * Generated from Natural Earth 110m land polygons sampled on an\n * equirectangular grid: 192 columns spanning 180°W-180°E, 69 rows\n * spanning 74.0°N-56.0°S (Antarctica clipped). Each row is a\n * hex-encoded bitmask; bit 1 = land dot.\n */\nexport const MAP_COLS = 192;\nexport const MAP_ROWS = 69;\nexport const MAP_LAT_MAX = 74;\nexport const MAP_LAT_MIN = -56;\n\nexport const MAP_BITS: readonly string[] = [\n  \"00000007a336de003ffff0000000000c001fffff38000000\",\n  \"800800023f067fc00fffd0000000000c0ffffffffbff8001\",\n  \"007ffcff3f9b31f017ffe000003fc000cfffffffffffffde\",\n  \"c0ffffffffffb97c1ffc000000fffd3ffeffffffffffffff\",\n  \"7bffffffffffe0740fe01f0003f7e7ffffffffffffffffff\",\n  \"083fffffffff28380fc0060007effffffffffffffffffffe\",\n  \"00fffffffffc0780038000001f9ffffffffffffffffffff8\",\n  \"01fd8ffffffc01e4000000001fc9ffffffffffffffffc300\",\n  \"000c007ffffc01fe000000040387ffffffffffffffe00c00\",\n  \"0030007fffff81fe000000040d1fffffffffffffff801e00\",\n  \"0000003ffffff3ffc000001b04ffffffffffffffff801c00\",\n  \"0000000ffffff3ffc0000013bfffffffffffffffffe01000\",\n  \"00000007ffffffff400000047fffffffffffffffffe80000\",\n  \"00000005ffffffec60000003ffffffffffffffffffe00000\",\n  \"00000003fffffff810000001ffffffefffffffffffc80000\",\n  \"00000003fffffff600000001feff2f9fffffffffff800000\",\n  \"00000003ffffffc00000001fc27e038ffffffffffd180000\",\n  \"00000003ffffff800000001f81be73c7fffffffff8000000\",\n  \"00000003ffffff000000001f0893ffcfffffffff10100000\",\n  \"00000001ffffff000000001e0113ffe7fffffffe98200000\",\n  \"00000001ffffff0000000001fc001fffffffffff19e00000\",\n  \"000000007ffffc000000000ffc001fffffffffff01800000\",\n  \"000000003ffff8000000001fff183fffffffffff84000000\",\n  \"000000002fff38000000001fffffffdfffffffff80000000\",\n  \"0000000017f008000000007fffffffdfffffffff80000000\",\n  \"000000000bf00800000000ffffffdfe1ffffffff00000000\",\n  \"0000000001f00200000000ffffffefe40ffffffe80000000\",\n  \"0000000000f01c00000001ffffffe7ff07ff7ff880000000\",\n  \"0000000000f0c100000001fffffff7fe01fc7f8000000000\",\n  \"0000000000798070000001fffffff3fc01f83f2000000000\",\n  \"00000000001f8000000001fffffff9f801f03f8080000000\",\n  \"000000000001f000000001fffffffde000e00fc080000000\",\n  \"0000000000006000000001ffffffff0000e003c080000000\",\n  \"00000000000020e0000000fffffffe2000e001c060000000\",\n  \"00000000000015ff0000007fffffffe00040088040000000\",\n  \"00000000000003ff8000007fffffffe00010040020000000\",\n  \"00000000000001fff000001f1fffffc00000060200000000\",\n  \"00000000000001fff800000007ffff8000000a0600000000\",\n  \"00000000000003fff800000007ffff000000053e00000000\",\n  \"00000000000007fff800000007fffe000000063e00000000\",\n  \"00000000000007ffff00000007fffc000000031c83400000\",\n  \"00000000000007fffff0000003fff8000000018400f84000\",\n  \"00000000000007fffff8000003fff80000000000003da000\",\n  \"00000000000003fffff8000001fff80000000070003e0800\",\n  \"00000000000003fffff8000001fff8000000000020030400\",\n  \"00000000000001fffff0000001fffc000000000002000000\",\n  \"00000000000001ffffe0000001fffc200000000007880000\",\n  \"00000000000000ffffe0000003fffc60000000001f0c0000\",\n  \"000000000000007fffe0000003fff8e0000000007fdc0000\",\n  \"000000000000001fffe0000001ffe0c0000000007ffc0000\",\n  \"000000000000001fffc0000001ffe1c000000003fffe0000\",\n  \"000000000000001fffc0000000ffe1c000000007ffff0000\",\n  \"000000000000003ffe00000000ffe1800000000fffff8000\",\n  \"000000000000003ffc00000000ffc0000000000fffffc000\",\n  \"000000000000003ffc000000007f800000000007ffffc000\",\n  \"000000000000003ff8000000007f000000000007ffffc000\",\n  \"000000000000003ff0000000003f000000000003f1ff8000\",\n  \"000000000000003ff0000000003c000000000007c0ff8000\",\n  \"000000000000007f800000000000000000000000003f0000\",\n  \"000000000000007f800000000000000000000000001f0004\",\n  \"000000000000007e00000000000000000000000000000004\",\n  \"00000000000000780000000000000000000000000006000c\",\n  \"00000000000000f800000000000000000000000000020010\",\n  \"00000000000000f800000000000000000000000000000060\",\n  \"00000000000000f000000000000000000000000000000000\",\n  \"00000000000000f000000000000000000000000000000000\",\n  \"00000000000000e000000000000000000000000000000000\",\n  \"00000000000000e000000000000000000000000000000000\",\n  \"000000000000007800000000000000000000000000000000\",\n];\n\n/** Decodes one hex row into per-column land flags. */\nexport const decodeRow = (hex: string): boolean[] => {\n  const flags: boolean[] = [];\n\n  for (const char of hex) {\n    const nibble = Number.parseInt(char, 16).toString(2).padStart(4, \"0\");\n\n    for (const bit of nibble) {\n      flags.push(bit === \"1\");\n    }\n  }\n\n  return flags;\n};\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}