{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "map-arcs-01",
  "title": "Arcs from Winnipeg example",
  "description": "Arcs from Winnipeg to six capitals; click one or pick a city for its distance, composed from the Unmap map, the arc-layer item and your ToggleGroup.",
  "registryDependencies": [
    "https://registry.unmap.dev/r/map.json",
    "https://registry.unmap.dev/r/arc-layer.json",
    "toggle-group"
  ],
  "files": [
    {
      "path": "registry/blocks/map-arcs-01.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { Map } from \"@/components/ui/map\";\nimport { ArcLayer, type ArcDatum } from \"@/components/ui/arc-layer\";\nimport { ToggleGroup, ToggleGroupItem } from \"@/components/ui/toggle-group\";\nimport { useMap } from \"@/lib/map-context\";\nimport { cn } from \"@/lib/utils\";\n\nconst COPY = {\n  en: {\n    noKey: \"Add your unmap key to load the map.\",\n    title: \"Arcs from Winnipeg\",\n    hint: \"Click an arc or pick a city.\",\n    pick: \"Destination\",\n    result: (city: string, km: string) => `Winnipeg to ${city}, ${km} km as the crow flies`,\n    credit:\n      \"Capitals: Canadian Geographical Names Database. Contains information licensed under the Open Government Licence – Canada.\",\n  },\n  fr: {\n    noKey: \"Ajoutez votre clé unmap pour charger la carte.\",\n    title: \"Arcs depuis Winnipeg\",\n    hint: \"Cliquez sur un arc ou choisissez une ville.\",\n    pick: \"Destination\",\n    result: (city: string, km: string) => `Winnipeg vers ${city}, ${km} km à vol d'oiseau`,\n    credit:\n      \"Capitales : Base de données toponymiques du Canada. Contient de l'information visée par la Licence du gouvernement ouvert – Canada.\",\n  },\n} as const;\n\n/**\n * Illustrative links: Winnipeg to six provincial capitals. The capitals' positions are from the\n * Canadian Geographical Names Database (Natural Resources Canada), Open Government Licence, Canada;\n * the links themselves stand for whatever yours carry (routes, shipments, calls).\n */\nconst WINNIPEG: [number, number] = [-97.14639, 49.88444];\nconst CAPITALS: { id: string; name: string; name_fr: string; at: [number, number] }[] = [\n  { id: \"victoria\", name: \"Victoria\", name_fr: \"Victoria\", at: [-123.36472, 48.42833] },\n  { id: \"edmonton\", name: \"Edmonton\", name_fr: \"Edmonton\", at: [-113.49028, 53.53444] },\n  { id: \"regina\", name: \"Regina\", name_fr: \"Regina\", at: [-104.60661, 50.45484] },\n  { id: \"toronto\", name: \"Toronto\", name_fr: \"Toronto\", at: [-79.3733, 43.7417] },\n  { id: \"quebec\", name: \"Québec City\", name_fr: \"Québec\", at: [-71.208, 46.81382] },\n  { id: \"halifax\", name: \"Halifax\", name_fr: \"Halifax\", at: [-63.59065, 44.6474] },\n];\n\nconst LINKS: ArcDatum[] = CAPITALS.map(({ id, name, name_fr, at }) => ({ id, from: WINNIPEG, to: at, name, name_fr }));\n\n/** Great-circle distance in kilometres. */\nfunction kilometres(a: [number, number], b: [number, number]): number {\n  const rad = Math.PI / 180;\n  const h =\n    Math.sin(((b[1] - a[1]) * rad) / 2) ** 2 +\n    Math.cos(a[1] * rad) * Math.cos(b[1] * rad) * Math.sin(((b[0] - a[0]) * rad) / 2) ** 2;\n  return 2 * 6371.0088 * Math.asin(Math.sqrt(h));\n}\n\n/** Frames every capital once the map exists, so the links fit a phone as well as a desktop. */\nfunction FitLinks() {\n  const map = useMap();\n  useEffect(() => {\n    map?.fitBounds([[-124.5, 43], [-62.5, 54.5]], { padding: 24, animate: false });\n  }, [map]);\n  return null;\n}\n\n/**\n * A minimal example: arcs from Winnipeg to six provincial capitals in your --primary. Click an\n * arc, or pick its city below the map (a thin line is hard to hit with a finger, and a keyboard\n * cannot reach one), and the link is drawn again on top with its straight-line distance. The arcs\n * are drawn in the browser: the only requests are the map's own.\n */\nexport function MapArcs({\n  apiKey,\n  lang = \"en\",\n  className,\n}: {\n  /** Your Unmap browser key. */\n  apiKey: string;\n  /** Language of the copy and the map labels: \"en\" (default) or \"fr\". */\n  lang?: \"en\" | \"fr\";\n  className?: string;\n}) {\n  const copy = COPY[lang];\n  const [picked, setPicked] = useState<string | null>(null);\n  const capital = CAPITALS.find((entry) => entry.id === picked);\n  // To the nearest 10 km: the arcs end at a city's reference point, not its edge.\n  const km = capital\n    ? new Intl.NumberFormat(lang === \"fr\" ? \"fr-CA\" : \"en-CA\").format(Math.round(kilometres(WINNIPEG, capital.at) / 10) * 10)\n    : \"\";\n\n  return (\n    <div className={cn(\"flex min-w-0 flex-col gap-3\", className)}>\n      <div role=\"region\" aria-label={copy.title} className=\"relative h-96\">\n        <Map apiKey={apiKey} center={[-94, 49.5]} zoom={3.5} lang={lang}>\n          <FitLinks />\n          <ArcLayer data={LINKS} onClick={(event) => setPicked(String(event.feature.properties.id ?? \"\"))} />\n          {/* The picked link again, wider, on top: the item's own colour, only the width changes. */}\n          <ArcLayer id=\"picked\" data={LINKS.filter((link) => link.id === picked)} width={4} />\n        </Map>\n      </div>\n      {/* No key, no map: one line says what to add. */}\n      {apiKey.trim() === \"\" ? <p className=\"text-xs text-muted-foreground\">{copy.noKey}</p> : null}\n      <ToggleGroup\n        aria-label={copy.pick}\n        variant=\"outline\"\n        size=\"sm\"\n        className=\"flex-wrap\"\n        value={picked ? [picked] : []}\n        onValueChange={(next: unknown[]) => setPicked(typeof next[0] === \"string\" ? next[0] : null)}\n      >\n        {CAPITALS.map((entry) => (\n          <ToggleGroupItem key={entry.id} value={entry.id}>\n            {lang === \"fr\" ? entry.name_fr : entry.name}\n          </ToggleGroupItem>\n        ))}\n      </ToggleGroup>\n      <p aria-live=\"polite\" className=\"text-sm text-muted-foreground tabular-nums\">\n        {capital ? (\n          <span className=\"font-medium text-foreground\">{copy.result(lang === \"fr\" ? capital.name_fr : capital.name, km)}</span>\n        ) : (\n          copy.hint\n        )}\n      </p>\n      <p className=\"text-xs text-muted-foreground\">{copy.credit}</p>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/unmap-map-arcs.tsx"
    }
  ],
  "docs": "Import MapArcs from your components/unmap-map-arcs file and pass apiKey (your Unmap browser key). Use lang=\"fr\" for French. Allow your site origin on the key. Preview: https://unmap.dev/templates/map-arcs",
  "categories": [
    "examples",
    "maps"
  ],
  "type": "registry:block"
}