{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "map-measure-01",
  "title": "Measure a distance example",
  "description": "Click points on the map and read the distance as you go, composed from the Unmap map and the measure item.",
  "registryDependencies": [
    "https://registry.unmap.dev/r/map.json",
    "https://registry.unmap.dev/r/measure.json"
  ],
  "files": [
    {
      "path": "registry/blocks/map-measure-01.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { Map } from \"@/components/ui/map\";\nimport { Measure, isRepeatClick, measureClick, type MapPointer, type MeasureChange, type MeasureClick } from \"@/components/ui/measure\";\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: \"Measure a distance\",\n    hint: \"Click the map to measure. Double-click or double-tap to finish.\",\n    result: (distance: string, points: number) => `${distance} over ${points} points`,\n    done: \"Done. Undo to keep going, or Clear to start over.\",\n  },\n  fr: {\n    noKey: \"Ajoutez votre clé unmap pour charger la carte.\",\n    title: \"Mesurer une distance\",\n    hint: \"Cliquez sur la carte pour mesurer. Double-cliquez ou touchez deux fois pour terminer.\",\n    result: (distance: string, points: number) => `${distance} sur ${points} points`,\n    done: \"Terminé. Annulez pour continuer, ou effacez pour recommencer.\",\n  },\n} as const;\n\n/** Metres under 1 km, kilometres with one decimal above, written the English or the French way. */\nfunction distance(metres: number, lang: \"en\" | \"fr\"): string {\n  const number = new Intl.NumberFormat(lang === \"fr\" ? \"fr-CA\" : \"en-CA\", { maximumFractionDigits: 1 });\n  return metres < 999.5 ? `${number.format(Math.round(metres))} m` : `${number.format(metres / 1000)} km`;\n}\n\n/**\n * Calls `onFinish` on a double-click or double-tap, read the way `<Measure>` reads one: two clicks\n * a few pixels and half a second apart. `<Measure>` adds one point for the pair; this ends the line.\n * `onFinish` says whether it did (a line needs two points). Ending the line hands the map its\n * double-click zoom back, so the browser's `dblclick` that follows the pair is cancelled here, or\n * finishing would also zoom in.\n */\nfunction FinishOnDoubleClick({ onFinish }: { onFinish: () => boolean }) {\n  const map = useMap();\n  const finish = useRef(onFinish);\n  finish.current = onFinish;\n  useEffect(() => {\n    if (!map) return;\n    let last: MeasureClick | null = null;\n    let finished = false;\n    const onClick = (event: MapPointer) => {\n      const click = measureClick(event);\n      finished = isRepeatClick(last, click) && finish.current();\n      last = click;\n    };\n    const onDoubleClick = (event: { preventDefault: () => void }) => {\n      if (finished) event.preventDefault();\n      finished = false;\n    };\n    map.on(\"click\", onClick);\n    map.on(\"dblclick\", onDoubleClick);\n    return () => {\n      map.off(\"click\", onClick);\n      map.off(\"dblclick\", onDoubleClick);\n    };\n  }, [map]);\n  return null;\n}\n\n/**\n * A minimal example: click points on a map of Calgary and read the distance as you go. A\n * double-click ends the line; Undo reopens it and Clear starts again. Measuring happens in the\n * browser: the only requests are the map's own.\n */\nexport function MapMeasure({\n  apiKey,\n  lang = \"en\",\n  className,\n}: {\n  /** Your Unmap browser key. */\n  apiKey: string;\n  /** Language of the copy, the distances and the map labels: \"en\" (default) or \"fr\". */\n  lang?: \"en\" | \"fr\";\n  className?: string;\n}) {\n  const copy = COPY[lang];\n  const [measured, setMeasured] = useState<MeasureChange>({ points: [], metres: 0 });\n  const [finished, setFinished] = useState(false);\n  const count = measured.points.length;\n\n  const onChange = (change: MeasureChange) => {\n    setMeasured(change);\n    // Only Undo and Clear report while the line is finished: either one reopens it.\n    setFinished(false);\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={[-114.07, 51.05]} zoom={13} lang={lang}>\n          <Measure\n            lang={lang}\n            // The label on the line and the result below it read the same number.\n            format={(metres) => distance(metres, lang)}\n            active={!finished}\n            onChange={onChange}\n            // The line below the map reads the total, so the tool's own readout is hidden, and\n            // Undo and Clear stay out of the way until there is something to undo.\n            className={cn(\n              \"absolute top-3 left-3 z-10 [&_[data-unmap-measure-total]]:hidden\",\n              count === 0 && \"invisible\",\n            )}\n          />\n          <FinishOnDoubleClick\n            onFinish={() => {\n              if (finished || count < 2) return false;\n              setFinished(true);\n              return true;\n            }}\n          />\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      <p aria-live=\"polite\" className=\"text-sm text-muted-foreground tabular-nums\">\n        {count < 2 ? (\n          copy.hint\n        ) : (\n          <>\n            <span className=\"font-medium text-foreground\">{copy.result(distance(measured.metres, lang), count)}</span>\n            {finished && <span> {copy.done}</span>}\n          </>\n        )}\n      </p>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/unmap-map-measure.tsx"
    }
  ],
  "docs": "Import MapMeasure from your components/unmap-map-measure 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-measure",
  "categories": [
    "examples",
    "maps"
  ],
  "type": "registry:block"
}