{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "map-inspect-01",
  "title": "Inspect map features example",
  "description": "Click anything on a map of Ottawa to list the features there and read their properties, composed from the Unmap map and the feature-inspector item.",
  "registryDependencies": [
    "https://registry.unmap.dev/r/map.json",
    "https://registry.unmap.dev/r/feature-inspector.json"
  ],
  "files": [
    {
      "path": "registry/blocks/map-inspect-01.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useRef, useState, type ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { Map } from \"@/components/ui/map\";\nimport { FeatureInspector, featureLabel, type InspectedFeature } from \"@/components/ui/feature-inspector\";\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: \"Inspect map features\",\n    hint: \"Click anything on the map to see what it is.\",\n    result: (name: string, layer: string) => `${name} on the ${layer} layer`,\n    unnamed: (kind: string | null, layer: string) =>\n      kind ? `An unnamed ${kind} on the ${layer} layer` : `An unnamed feature on the ${layer} layer`,\n  },\n  fr: {\n    noKey: \"Ajoutez votre clé unmap pour charger la carte.\",\n    title: \"Inspecter les entités de la carte\",\n    hint: \"Cliquez sur un élément de la carte pour voir ce que c'est.\",\n    result: (name: string, layer: string) => `${name}, couche ${layer}`,\n    unnamed: (kind: string | null, layer: string) =>\n      kind ? `Entité sans nom (${kind}), couche ${layer}` : `Entité sans nom, couche ${layer}`,\n  },\n} as const;\n\n/** A name worth showing: non-blank text, or a number. */\nfunction hasName(value: unknown): boolean {\n  return (typeof value === \"string\" && value.trim() !== \"\") || typeof value === \"number\";\n}\n\n/**\n * The line under the map for `feature`: its name and layer, or, for the usual click on a park, a\n * building or the ground, what kind of thing it is rather than an id.\n */\nfunction describe(feature: InspectedFeature, lang: \"en\" | \"fr\"): string {\n  const copy = COPY[lang];\n  const { properties, layer } = feature;\n  if (hasName(properties[`name:${lang}`]) || hasName(properties.name)) return copy.result(featureLabel(feature, lang), layer);\n  // Protomaps kinds are snake_case (\"minor_road\"); a sentence reads them with spaces.\n  const kind = typeof properties.kind === \"string\" && properties.kind !== layer ? properties.kind.replace(/_/g, \" \") : null;\n  return copy.unnamed(kind, layer);\n}\n\n/** Calls `onClick` on every map click: the inspector's card stays out of the way until the first. */\nfunction OnMapClick({ onClick }: { onClick: () => void }) {\n  const map = useMap();\n  const latest = useRef(onClick);\n  latest.current = onClick;\n  useEffect(() => {\n    if (!map) return;\n    const click = () => latest.current();\n    map.on(\"click\", click);\n    return () => {\n      map.off(\"click\", click);\n    };\n  }, [map]);\n  return null;\n}\n\n/**\n * Renders `children` into `slot`, below the map, while keeping them inside `<Map>`'s React tree\n * (a portal keeps context), so the inspector reaches the map without a card over it.\n */\nfunction BelowMap({ slot, children }: { slot: HTMLElement | null; children: ReactNode }) {\n  return slot ? createPortal(children, slot) : null;\n}\n\n/**\n * A minimal example: click anything on a map of Ottawa's Parliament Hill and the features there\n * are listed below the map, the topmost selected and outlined. Pick another row to read its\n * properties. Inspecting reads what the map already drew: the only requests are the map's own.\n */\nexport function MapInspect({\n  apiKey,\n  lang = \"en\",\n  className,\n}: {\n  /** Your Unmap browser key. */\n  apiKey: string;\n  /** Language of the copy, the inspector and the map labels: \"en\" (default) or \"fr\". */\n  lang?: \"en\" | \"fr\";\n  className?: string;\n}) {\n  const copy = COPY[lang];\n  const [slot, setSlot] = useState<HTMLDivElement | null>(null);\n  const [clicked, setClicked] = useState(false);\n  const [selected, setSelected] = useState<InspectedFeature | null>(null);\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={[-75.6995, 45.4236]} zoom={15} lang={lang}>\n          <OnMapClick onClick={() => setClicked(true)} />\n          <BelowMap slot={slot}>\n            {/* Hidden, not unmounted, until the first click: the inspector listens from the start. */}\n            <FeatureInspector lang={lang} onSelect={setSelected} className={cn(!clicked && \"hidden\")} />\n          </BelowMap>\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\">\n        {selected ? (\n          <span className=\"font-medium text-foreground\">{describe(selected, lang)}</span>\n        ) : (\n          copy.hint\n        )}\n      </p>\n      <div ref={setSlot} className=\"min-w-0\" />\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/unmap-map-inspect.tsx"
    }
  ],
  "docs": "Import MapInspect from your components/unmap-map-inspect 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-inspect",
  "categories": [
    "examples",
    "maps"
  ],
  "type": "registry:block"
}