{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "blur-area",
  "title": "Map Blur Area",
  "description": "Blur sensitive areas on the map with customizable styling.",
  "dependencies": ["mapbox-gl"],
  "devDependencies": ["@types/mapbox-gl"],
  "registryDependencies": ["https://www.terrae.dev/map.json"],
  "files": [
    {
      "path": "src/registry/map/blur-area.tsx",
      "content": "\"use client\"\n\nimport { useEffect, useRef, useCallback, type CSSProperties } from \"react\"\nimport { useMap } from \"./hooks\"\nimport type { MapCoordinates } from \"./types\"\n\nconst DEFAULT_BLUR_AMOUNT = 8\nconst DEFAULT_ROUNDED = 0\nconst MIN_AREA_SIZE = 20\n\ntype BlurAreaConfig = {\n  coordinates: MapCoordinates[]\n  blur?: number\n  backgroundColor?: string\n  rounded?: number | \"full\"\n}\n\ntype MapBlurAreaProps = {\n  coordinates?: MapCoordinates[]\n  areas?: BlurAreaConfig[]\n  blur?: number\n  backgroundColor?: string\n  rounded?: number | \"full\"\n  blockInteraction?: boolean\n}\n\ntype Bounds = {\n  minX: number\n  maxX: number\n  minY: number\n  maxY: number\n}\n\nconst getBounds = (points: { x: number; y: number }[]): Bounds => {\n  const xs = points.map((p) => {\n    return p.x\n  })\n  const ys = points.map((p) => {\n    return p.y\n  })\n\n  return {\n    minX: Math.min(...xs),\n    maxX: Math.max(...xs),\n    minY: Math.min(...ys),\n    maxY: Math.max(...ys),\n  }\n}\n\ntype BlurOverlayProps = {\n  coordinates: MapCoordinates[]\n  blur: number\n  backgroundColor?: string\n  rounded: number | \"full\"\n  blockInteraction: boolean\n}\n\nconst BlurOverlay = ({ coordinates, blur, backgroundColor, rounded, blockInteraction }: BlurOverlayProps) => {\n  const { map, isLoaded } = useMap()\n  const overlayRef = useRef<HTMLDivElement | null>(null)\n\n  const updatePosition = useCallback(() => {\n    if (!overlayRef.current || !map) {\n      return\n    }\n\n    const canvas = map.getCanvas()\n    const canvasWidth = canvas.clientWidth\n    const canvasHeight = canvas.clientHeight\n\n    const projectedPoints = coordinates.map((coord) => {\n      return map.project(coord)\n    })\n    const bounds = getBounds(projectedPoints)\n\n    const width = bounds.maxX - bounds.minX\n    const height = bounds.maxY - bounds.minY\n    const isVisible =\n      bounds.maxX >= 0 &&\n      bounds.minX <= canvasWidth &&\n      bounds.maxY >= 0 &&\n      bounds.minY <= canvasHeight &&\n      width >= MIN_AREA_SIZE &&\n      height >= MIN_AREA_SIZE\n\n    if (!isVisible) {\n      overlayRef.current.style.opacity = \"0\"\n    } else {\n      overlayRef.current.style.opacity = \"1\"\n      overlayRef.current.style.left = `${bounds.minX}px`\n      overlayRef.current.style.top = `${bounds.minY}px`\n      overlayRef.current.style.width = `${width}px`\n      overlayRef.current.style.height = `${height}px`\n    }\n  }, [map, coordinates])\n\n  useEffect(() => {\n    if (!map || !isLoaded || coordinates.length < 3) {\n      return\n    }\n\n    updatePosition()\n\n    map.on(\"move\", updatePosition)\n    map.on(\"zoom\", updatePosition)\n    map.on(\"rotate\", updatePosition)\n    map.on(\"pitch\", updatePosition)\n    map.on(\"resize\", updatePosition)\n\n    return () => {\n      map.off(\"move\", updatePosition)\n      map.off(\"zoom\", updatePosition)\n      map.off(\"rotate\", updatePosition)\n      map.off(\"pitch\", updatePosition)\n      map.off(\"resize\", updatePosition)\n    }\n  }, [map, isLoaded, coordinates, updatePosition])\n\n  const borderRadius = rounded === \"full\" ? \"9999px\" : `${rounded}px`\n\n  const style: CSSProperties = {\n    position: \"absolute\",\n    backdropFilter: `blur(${blur}px)`,\n    WebkitBackdropFilter: `blur(${blur}px)`,\n    backgroundColor,\n    borderRadius,\n    pointerEvents: blockInteraction ? \"auto\" : \"none\",\n    cursor: blockInteraction ? \"not-allowed\" : \"default\",\n    transition: \"opacity 0.2s ease-out\",\n    zIndex: 10,\n  }\n\n  return <div ref={overlayRef} style={style} />\n}\n\nexport const MapBlurArea = ({\n  coordinates,\n  areas,\n  blur = DEFAULT_BLUR_AMOUNT,\n  backgroundColor,\n  rounded = DEFAULT_ROUNDED,\n  blockInteraction = false,\n}: MapBlurAreaProps) => {\n  if (areas && areas.length > 0) {\n    return (\n      <>\n        {areas.map((area, index) => {\n          return (\n            <BlurOverlay\n              key={index}\n              coordinates={area.coordinates}\n              blur={area.blur ?? blur}\n              backgroundColor={area.backgroundColor ?? backgroundColor}\n              rounded={area.rounded ?? rounded}\n              blockInteraction={blockInteraction}\n            />\n          )\n        })}\n      </>\n    )\n  }\n\n  if (!coordinates || coordinates.length < 3) {\n    return null\n  }\n\n  return (\n    <BlurOverlay\n      coordinates={coordinates}\n      blur={blur}\n      backgroundColor={backgroundColor}\n      rounded={rounded}\n      blockInteraction={blockInteraction}\n    />\n  )\n}\n",
      "type": "registry:ui",
      "target": "components/ui/map/blur-area.tsx"
    }
  ],
  "type": "registry:ui"
}
