{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "mapbox-pointer",
  "files": [
    {
      "path": "components/ui/mapbox-pointer.tsx",
      "content": "\"use client\"\n\nimport { useEffect, useRef, useState } from \"react\"\nimport { useTheme } from \"next-themes\"\n\ntype MapboxStyle =\n  | \"streets-v12\"\n  | \"outdoors-v12\"\n  | \"light-v11\"\n  | \"dark-v11\"\n  | \"satellite-v9\"\n  | \"satellite-streets-v12\"\n  | \"navigation-day-v1\"\n  | \"navigation-night-v1\"\n\ninterface MapboxPointerLabelProps {\n  label: string\n  href?: string\n  className?: string\n}\n\nexport function MapboxPointerLabel({\n  label,\n  href,\n  className = \"\",\n}: MapboxPointerLabelProps) {\n  const baseClasses =\n    \"bg-background border-border text-foreground absolute right-4 bottom-4 z-10 flex items-center gap-1 rounded border px-3 py-1 text-xs font-normal\"\n\n  if (href) {\n    return (\n      <a\n        href={href}\n        target=\"_blank\"\n        rel=\"noopener noreferrer\"\n        className={`${baseClasses} cursor-pointer ${className}`}\n        onClick={(e) => e.stopPropagation()}\n      >\n        {label}\n      </a>\n    )\n  }\n\n  return <div className={`${baseClasses} ${className}`}>{label}</div>\n}\n\ninterface MapboxPointerProps {\n  latitude: number\n  longitude: number\n  zoom?: number\n  mapboxToken?: string\n  googleMapsUrl?: string\n  markerColor?: string\n  className?: string\n  interactive?: boolean\n  style?: MapboxStyle\n  themeAware?: boolean\n  label?: string\n  labelHref?: string\n  clickForDirections?: boolean\n  children?: React.ReactNode\n}\n\ndeclare global {\n  interface Window {\n    mapboxgl: any\n  }\n}\n\n// Global promise to track Mapbox script loading\nlet mapboxLoadPromise: Promise<void> | null = null\n\nexport function MapboxPointer({\n  latitude,\n  longitude,\n  zoom = 13,\n  mapboxToken,\n  googleMapsUrl,\n  markerColor = \"#679BFF\",\n  className = \"\",\n  interactive = true,\n  style = \"streets-v12\",\n  themeAware = false,\n  label,\n  labelHref,\n  clickForDirections = false,\n  children,\n}: MapboxPointerProps) {\n  const mapContainer = useRef<HTMLDivElement>(null)\n  const map = useRef<any>(null)\n  const [isLoading, setIsLoading] = useState(true)\n  const { theme, resolvedTheme } = useTheme()\n\n  // Get token from props or environment\n  const token = mapboxToken || process.env.NEXT_PUBLIC_MAPBOX_TOKEN || \"\"\n\n  // Determine the actual map style based on theme awareness\n  const actualStyle: MapboxStyle = themeAware\n    ? resolvedTheme === \"dark\"\n      ? \"dark-v11\"\n      : \"streets-v12\"\n    : style\n\n  useEffect(() => {\n    // Check if token is available\n    if (!token) {\n      console.warn(\n        \"MapboxPointer: No Mapbox token found. Please provide mapboxToken prop or set NEXT_PUBLIC_MAPBOX_TOKEN environment variable.\"\n      )\n      return\n    }\n\n    // Reset map if it exists\n    if (map.current) {\n      map.current.remove()\n      map.current = null\n    }\n\n    const loadMapbox = (): Promise<void> => {\n      // If already loading or loaded, return the existing promise\n      if (mapboxLoadPromise) {\n        return mapboxLoadPromise\n      }\n\n      mapboxLoadPromise = new Promise<void>((resolve, reject) => {\n        // Load CSS if not already loaded\n        if (!document.querySelector('link[href*=\"mapbox-gl.css\"]')) {\n          const link = document.createElement(\"link\")\n          link.href = \"https://api.mapbox.com/mapbox-gl-js/v3.0.1/mapbox-gl.css\"\n          link.rel = \"stylesheet\"\n          document.head.appendChild(link)\n        }\n\n        // Check if already loaded\n        if (window.mapboxgl) {\n          resolve()\n          return\n        }\n\n        // Load JS if not already loaded\n        if (!document.querySelector('script[src*=\"mapbox-gl.js\"]')) {\n          const script = document.createElement(\"script\")\n          script.src = \"https://api.mapbox.com/mapbox-gl-js/v3.0.1/mapbox-gl.js\"\n          script.async = true\n          script.onload = () => resolve()\n          script.onerror = () => {\n            console.error(\"Failed to load Mapbox GL JS\")\n            reject(new Error(\"Failed to load Mapbox GL JS\"))\n          }\n          document.head.appendChild(script)\n        } else {\n          // Script tag exists, wait for window.mapboxgl to be available\n          const checkLoaded = setInterval(() => {\n            if (window.mapboxgl) {\n              clearInterval(checkLoaded)\n              resolve()\n            }\n          }, 50)\n\n          // Timeout after 10 seconds\n          setTimeout(() => {\n            clearInterval(checkLoaded)\n            if (!window.mapboxgl) {\n              reject(new Error(\"Mapbox GL JS load timeout\"))\n            }\n          }, 10000)\n        }\n      })\n\n      return mapboxLoadPromise\n    }\n\n    const initializeMap = () => {\n      if (!mapContainer.current || !window.mapboxgl) return\n\n      try {\n        window.mapboxgl.accessToken = token\n\n        map.current = new window.mapboxgl.Map({\n          container: mapContainer.current,\n          style: `mapbox://styles/mapbox/${actualStyle}`,\n          center: [longitude, latitude],\n          zoom: zoom,\n          interactive: interactive,\n          attributionControl: false,\n        })\n\n        map.current.on(\"load\", () => {\n          setIsLoading(false)\n          setTimeout(() => {\n            const logoElements = document.querySelectorAll(\n              \".mapboxgl-ctrl-logo, .mapboxgl-ctrl-bottom-left, .mapboxgl-ctrl-bottom-right\"\n            )\n            logoElements.forEach((el: Element) => {\n              if (el instanceof HTMLElement) {\n                el.style.display = \"none\"\n                el.style.visibility = \"hidden\"\n                el.style.opacity = \"0\"\n              }\n            })\n            // Also hide parent containers\n            const ctrlContainers = document.querySelectorAll(\".mapboxgl-ctrl\")\n            ctrlContainers.forEach((el: Element) => {\n              if (\n                el instanceof HTMLElement &&\n                el.querySelector(\".mapboxgl-ctrl-logo\")\n              ) {\n                el.style.display = \"none\"\n              }\n            })\n          }, 100)\n\n          const el = document.createElement(\"div\")\n          el.className = \"custom-marker\"\n          el.style.width = \"24px\"\n          el.style.height = \"24px\"\n          el.innerHTML = `\n            <div style=\"position: relative; height: 100%; width: 100%;\">\n              <div class=\"animate-marker\" style=\"position: absolute; left: 50%; top: 50%; width: 16px; height: 16px; border-radius: 50%; background-color: ${markerColor};\"></div>\n              <div style=\"position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 24px; height: 24px; border-radius: 50%; border: 3px solid white; background-color: ${markerColor}; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\"></div>\n            </div>\n          `\n\n          new window.mapboxgl.Marker(el)\n            .setLngLat([longitude, latitude])\n            .addTo(map.current)\n        })\n      } catch (error) {\n        console.error(\"Error initializing map:\", error)\n      }\n    }\n\n    // Ensure Mapbox is loaded before initializing\n    loadMapbox()\n      .then(() => {\n        initializeMap()\n      })\n      .catch((error) => {\n        console.error(\"Failed to load Mapbox:\", error)\n        setIsLoading(false)\n      })\n\n    return () => {\n      if (map.current) {\n        map.current.remove()\n        map.current = null\n      }\n    }\n  }, [latitude, longitude, zoom, token, markerColor, interactive, actualStyle])\n\n  const handleClick = () => {\n    if (clickForDirections) {\n      const directionsUrl = `https://www.google.com/maps/dir/?api=1&destination=${latitude},${longitude}`\n      window.open(directionsUrl, \"_blank\", \"noopener,noreferrer\")\n    } else if (googleMapsUrl) {\n      window.open(googleMapsUrl, \"_blank\", \"noopener,noreferrer\")\n    }\n  }\n\n  // Show error message if no token\n  if (!token) {\n    return (\n      <div className=\"flex h-96 items-center justify-center rounded-lg border\">\n        <p className=\"text-muted-foreground\">Mapbox token not configured</p>\n      </div>\n    )\n  }\n\n  return (\n    <>\n      <div\n        className={`bg-muted relative overflow-hidden rounded-xl border-none ${\n          clickForDirections || googleMapsUrl ? \"cursor-pointer\" : \"\"\n        } ${className}`}\n        onClick={handleClick}\n      >\n        {isLoading && (\n          <div className=\"absolute inset-0 z-20 flex items-center justify-center\">\n            <div className=\"text-muted-foreground flex items-center gap-2\">\n              <div className=\"h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n              <span className=\"text-sm\">Loading map...</span>\n            </div>\n          </div>\n        )}\n        <div ref={mapContainer} className=\"absolute inset-0 h-full w-full\" />\n        {children ||\n          (label && <MapboxPointerLabel label={label} href={labelHref} />)}\n      </div>\n      <style jsx>{`\n        @keyframes marker {\n          0% {\n            transform: translate(-50%, -50%) scale(1);\n            opacity: 1;\n          }\n          100% {\n            transform: translate(-50%, -50%) scale(6);\n            opacity: 0;\n          }\n        }\n\n        :global(.animate-marker) {\n          animation: marker 4s ease-out infinite;\n        }\n\n        :global(.mapboxgl-ctrl-logo),\n        :global(.mapboxgl-ctrl-attrib),\n        :global(.mapboxgl-ctrl-bottom-left),\n        :global(.mapboxgl-ctrl-bottom-right),\n        :global(.mapboxgl-compact) {\n          display: none !important;\n          visibility: hidden !important;\n          opacity: 0 !important;\n        }\n      `}</style>\n    </>\n  )\n}\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}