{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "map",
  "title": "Map",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-react/components/map/map.tsx",
      "content": "'use client'\n\n/**\n * Map — a thin, theme-aware wrapper around react-map-gl's Mapbox `Map`. Pass an\n * `accessToken`; drop `<MapMarker>` / `<MapPopup>` / `<MapLayer>` (re-exported\n * from this package) or any react-map-gl child into the children to build any\n * kind of map.\n *\n * - The base style follows the app theme (light-v11 / dark-v11) unless you pass\n *   an explicit `mapStyle`.\n * - `muted` repaints the basemap to a quiet, desaturated canvas (graphite in\n *   dark, light-grey in light) so overlaid data is the only colour on the map —\n *   ideal for dashboards, fleet boards and journey maps.\n * - `onCreated` hands you the raw mapbox-gl Map instance for imperative work\n *   (custom layers, fitBounds, controls).\n *\n * Requires a Mapbox access token (https://account.mapbox.com).\n *\n * IMPORTANT: consumers must import Mapbox GL's stylesheet once (e.g. in their\n * root layout / app entry):  import 'mapbox-gl/dist/mapbox-gl.css'\n * It's intentionally not imported here so bundlers that can't resolve CSS from a\n * library file (some RSC/SSR setups) don't choke — own the import in your app.\n */\nimport * as React from 'react'\nimport type mapboxgl from 'mapbox-gl'\nimport Map, {\n  NavigationControl as MapboxNavigationControl,\n  type MapRef,\n  type MapProps as MapboxMapProps,\n} from 'react-map-gl/mapbox'\nimport { useTheme } from '@/lib/use-theme'\nimport { cn } from '@/lib/utils'\n\nexport interface MapProps extends React.HTMLAttributes<HTMLDivElement> {\n  /** Mapbox access token. */\n  accessToken: string\n  /** Override the base style. Defaults to a theme-aware light/dark style. */\n  mapStyle?: string\n  /** Initial [lng, lat]. */\n  center?: [number, number]\n  zoom?: number\n  /** 'mercator' (flat) or 'globe'. */\n  projection?: string\n  /** Show the zoom/compass control. */\n  navigation?: boolean\n  /** Repaint the basemap to a quiet, desaturated canvas. */\n  muted?: boolean\n  /** Hands you the raw mapbox-gl Map instance once the style has loaded. */\n  onCreated?: (map: mapboxgl.Map) => void\n}\n\n/** Desaturate the basemap to a quiet canvas. Each op is guarded — style layer\n *  ids drift between style versions and the wrong property for a layer type\n *  throws. */\nfunction applyMuted(map: mapboxgl.Map, dark: boolean) {\n  let styleLayers: any[]\n  try {\n    styleLayers = map.getStyle()?.layers ?? []\n  } catch {\n    return // style not loaded yet\n  }\n  const P = dark\n    ? { land: 'rgb(23,24,29)', water: 'rgb(17,18,22)', use: 'rgb(31,32,38)', road: 'rgb(50,52,60)', label: 'rgb(150,152,165)', halo: 'rgb(23,24,29)', admin: 'rgb(50,52,60)' }\n    : { land: 'rgb(246,247,249)', water: 'rgb(226,230,235)', use: 'rgb(238,240,243)', road: 'rgb(221,224,229)', label: 'rgb(120,124,134)', halo: 'rgb(246,247,249)', admin: 'rgb(213,216,221)' }\n  for (const l of styleLayers) {\n    const id = l.id\n    try {\n      if (id === 'background' || id === 'land') map.setPaintProperty(id, 'background-color', P.land)\n      else if (/water/.test(id) && l.type === 'fill') map.setPaintProperty(id, 'fill-color', P.water)\n      else if (/landuse|landcover|national-park/.test(id) && l.type === 'fill') map.setPaintProperty(id, 'fill-color', P.use)\n      else if (/^road-(motorway|trunk|primary)/.test(id) && l.type === 'line') {\n        map.setPaintProperty(id, 'line-color', P.road)\n        map.setPaintProperty(id, 'line-opacity', 0.55)\n      } else if (/^road-(secondary|tertiary|street|minor|service|path|pedestrian)/.test(id)) {\n        map.setLayoutProperty(id, 'visibility', 'none')\n      } else if (/poi|transit|airport|natural-point|water-point|waterway-label|building/.test(id)) {\n        map.setLayoutProperty(id, 'visibility', 'none')\n      } else if (/road-label|settlement-major-label|settlement-minor-label|state-label/.test(id) && l.type === 'symbol') {\n        map.setPaintProperty(id, 'text-color', P.label)\n        map.setPaintProperty(id, 'text-halo-color', P.halo)\n        map.setPaintProperty(id, 'text-opacity', 0.6)\n      } else if (id === 'admin-1-boundary' && l.type === 'line') {\n        map.setPaintProperty(id, 'line-color', P.admin)\n        map.setPaintProperty(id, 'line-opacity', 0.6)\n      } else if (id === 'admin-1-boundary-bg') map.setPaintProperty(id, 'line-opacity', 0)\n    } catch {\n      /* skip */\n    }\n  }\n}\n\nconst MapComponent = React.forwardRef<HTMLDivElement, MapProps>(\n  (\n    {\n      className,\n      accessToken,\n      mapStyle,\n      center = [0, 20],\n      zoom = 1.4,\n      projection = 'mercator',\n      navigation = true,\n      muted = false,\n      onCreated,\n      children,\n      ...props\n    },\n    ref,\n  ) => {\n    const { resolvedTheme } = useTheme()\n    const isDark = resolvedTheme === 'dark'\n\n    const resolvedStyle = mapStyle ?? (isDark ? 'mapbox://styles/mapbox/dark-v11' : 'mapbox://styles/mapbox/light-v11')\n\n    // Render the map only on the client — Mapbox needs the DOM, and rendering it\n    // on the server would hydrate-mismatch. The bg-muted box is the SSR\n    // placeholder.\n    const [mounted, setMounted] = React.useState(false)\n    React.useEffect(() => {\n      setMounted(true)\n    }, [])\n\n    const mapRef = React.useRef<MapRef | null>(null)\n\n    const onLoad = React.useCallback(() => {\n      const map = mapRef.current?.getMap()\n      if (!map) return\n      try {\n        ;(map as any).setProjection(projection)\n      } catch {\n        /* older api */\n      }\n      if (muted) applyMuted(map, isDark)\n      onCreated?.(map)\n    }, [projection, muted, isDark, onCreated])\n\n    const onStyleData = React.useCallback(() => {\n      const map = mapRef.current?.getMap()\n      if (map && muted) applyMuted(map, isDark)\n    }, [muted, isDark])\n\n    return (\n      <div\n        ref={ref}\n        data-uipkge=\"\"\n        data-slot=\"map\"\n        className={cn('bg-muted size-full overflow-hidden', className)}\n        {...props}\n      >\n        {mounted && (\n          <Map\n            ref={mapRef}\n            mapboxAccessToken={accessToken}\n            mapStyle={resolvedStyle}\n            initialViewState={{ longitude: center[0], latitude: center[1], zoom }}\n            attributionControl={false}\n            style={{ width: '100%', height: '100%' }}\n            onLoad={onLoad}\n            onStyleData={onStyleData}\n          >\n            {navigation && <MapboxNavigationControl position=\"bottom-right\" showCompass={false} />}\n            {children}\n          </Map>\n        )}\n      </div>\n    )\n  },\n)\nMapComponent.displayName = 'Map'\n\nexport { MapComponent as Map }\nexport type { MapboxMapProps }\n\n// Re-exported here too (not just index.ts) so the toolkit resolves whether a\n// consumer imports from the package dir or the file (shadcn rewrites to the\n// file). Place these inside <Map>'s children.\nexport {\n  Marker as MapMarker,\n  Popup as MapPopup,\n  Layer as MapLayer,\n  Source as MapSource,\n  NavigationControl as MapNavigationControl,\n} from 'react-map-gl/mapbox'\n",
      "type": "registry:ui",
      "target": "~/components/ui/map/map.tsx"
    },
    {
      "path": "packages/registry-react/components/map/index.ts",
      "content": "export { Map } from './map'\nexport type { MapProps } from './map'\n\n// Re-exported from react-map-gl under Map* names so consumers get the whole map\n// toolkit from one import. Place these inside <Map>'s children — they read the\n// map instance from the wrapping react-map-gl context.\nexport {\n  Marker as MapMarker,\n  Popup as MapPopup,\n  Layer as MapLayer,\n  Source as MapSource,\n  NavigationControl as MapNavigationControl,\n} from 'react-map-gl/mapbox'\n",
      "type": "registry:ui",
      "target": "~/components/ui/map/index.ts"
    },
    {
      "path": "packages/registry-react/components/map/map.css",
      "content": "/**\n * Map chrome — re-skins Mapbox GL's controls + popups to the design-system\n * tokens so they sit naturally on light and dark surfaces. Global (Mapbox\n * renders this chrome outside the component subtree). Import once, e.g. in your\n * root CSS: `@import './components/ui/map/map.css';`\n */\n.mapboxgl-ctrl-group {\n  background: var(--card);\n  border: 1px solid var(--border);\n  border-radius: var(--radius);\n  box-shadow: var(--shadow-sm, 0 1px 2px rgb(0 0 0 / 0.08));\n}\n.mapboxgl-ctrl-group button {\n  background: transparent;\n}\n.mapboxgl-ctrl-group button + button {\n  border-top-color: var(--border);\n}\n.mapboxgl-ctrl-group button .mapboxgl-ctrl-icon {\n  filter: invert(var(--map-ctrl-invert, 0)) opacity(0.7);\n}\n.dark .mapboxgl-ctrl-group button .mapboxgl-ctrl-icon {\n  filter: invert(1) opacity(0.7);\n}\n.mapboxgl-ctrl-attrib {\n  background: color-mix(in oklab, var(--card) 80%, transparent);\n  color: var(--muted-foreground);\n}\n.mapboxgl-ctrl-attrib a {\n  color: var(--muted-foreground);\n}\n.mapboxgl-popup-content {\n  padding: 8px 12px;\n  background: var(--popover);\n  color: var(--popover-foreground);\n  border: 1px solid var(--border);\n  border-radius: var(--radius);\n  box-shadow: var(--shadow-md, 0 4px 12px rgb(0 0 0 / 0.12));\n}\n.mapboxgl-popup-close-button {\n  color: var(--muted-foreground);\n}\n.mapboxgl-popup-tip {\n  border-top-color: var(--popover);\n  border-bottom-color: var(--popover);\n}\n",
      "type": "registry:ui",
      "target": "~/components/ui/map/map.css"
    }
  ],
  "dependencies": [
    "react-map-gl",
    "mapbox-gl",
    "next-themes"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "A thin, theme-aware Mapbox GL JS wrapper (built on react-map-gl). Pass an access token and drop MapMarker / MapPopup / MapLayer into the children to build any map — fleet boards, journey maps, store locators. The base style follows light/dark automatically, an opt-in `muted` prop desaturates the basemap so overlaid data is the only colour, and `onCreated` hands you the raw map instance for custom layers and fitBounds. Import `mapbox-gl/dist/mapbox-gl.css` once in your app.",
  "categories": [
    "data-display"
  ]
}