{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "map",
  "title": "Map",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-vue/components/map/Map.vue",
      "content": "<script setup lang=\"ts\">\n/**\n * Map — a thin, theme-aware wrapper around @studiometa/vue-mapbox-gl's\n * MapboxMap. Pass an `access-token`; drop `<MapMarker>` / `<MapPopup>` /\n * `<MapLayer>` (re-exported from this package) or any @studiometa child into\n * the default slot to build any kind of map.\n *\n * - The base style follows the app theme (light-v11 / dark-v11) unless you pass\n *   an explicit `map-style`.\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 * - `@created` 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 */\nimport { computed, onMounted, ref } from 'vue'\nimport mapboxgl from 'mapbox-gl'\nimport { MapboxMap } from '@studiometa/vue-mapbox-gl'\nimport 'mapbox-gl/dist/mapbox-gl.css'\n\nconst props = withDefaults(\n  defineProps<{\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  }>(),\n  {\n    mapStyle: undefined,\n    center: () => [0, 20],\n    zoom: 1.4,\n    projection: 'mercator',\n    navigation: true,\n    muted: false,\n  },\n)\n\nconst emit = defineEmits<{ (e: 'created', map: mapboxgl.Map): void }>()\n\nconst { theme } = useTheme()\nconst isDark = () =>\n  theme.value === 'dark' ||\n  (theme.value === 'system' && typeof window !== 'undefined' && window.matchMedia('(prefers-color-scheme: dark)').matches)\n\nconst resolvedStyle = computed(\n  () => props.mapStyle ?? (isDark() ? 'mapbox://styles/mapbox/dark-v11' : 'mapbox://styles/mapbox/light-v11'),\n)\n\n// Render the map only on the client — Mapbox needs the DOM, and rendering it on\n// the server would hydrate-mismatch. The bg-muted box is the SSR placeholder.\nconst mounted = ref(false)\nonMounted(() => {\n  mounted.value = true\n})\n\nlet map: mapboxgl.Map | null = null\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() {\n  if (!map) return\n  let styleLayers: any[]\n  try {\n    styleLayers = map.getStyle()?.layers ?? []\n  } catch {\n    return // style not loaded yet\n  }\n  const P = isDark()\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\nfunction onCreated(instance: mapboxgl.Map) {\n  map = instance\n  try {\n    ;(map as any).setProjection(props.projection)\n  } catch {\n    /* older api */\n  }\n  if (props.navigation) map.addControl(new mapboxgl.NavigationControl({ showCompass: false }), 'bottom-right')\n  // Emit once the style has loaded so the handler can safely add sources/layers.\n  // (A theme restyle wipes custom layers — re-add them in your own\n  // `map.on('style.load')` if you need them to survive a theme switch.)\n  const ready = () => {\n    if (props.muted) applyMuted()\n    emit('created', map!)\n  }\n  if (map.isStyleLoaded()) ready()\n  else map.once('load', ready)\n  map.on('style.load', () => {\n    if (props.muted) applyMuted()\n  })\n}\n</script>\n\n<template>\n  <div class=\"bg-muted size-full overflow-hidden\">\n    <MapboxMap\n      v-if=\"mounted\"\n      class=\"size-full\"\n      :access-token=\"accessToken\"\n      :map-style=\"resolvedStyle\"\n      :center=\"center\"\n      :zoom=\"zoom\"\n      :attribution-control=\"false\"\n      @mb-created=\"onCreated\"\n    >\n      <slot />\n    </MapboxMap>\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/map/Map.vue"
    },
    {
      "path": "packages/registry-vue/components/map/index.ts",
      "content": "export { default as Map } from './Map.vue'\n\n// Re-exported from @studiometa/vue-mapbox-gl under Map* names so consumers get\n// the whole map toolkit from one import. Place these inside <Map>'s slot — they\n// inject the map instance from the wrapping MapboxMap.\nexport {\n  MapboxMarker as MapMarker,\n  MapboxPopup as MapPopup,\n  MapboxLayer as MapLayer,\n  MapboxSource as MapSource,\n  MapboxNavigationControl as MapNavigationControl,\n} from '@studiometa/vue-mapbox-gl'\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/map/index.ts"
    },
    {
      "path": "packages/registry-vue/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": "~/app/components/ui/map/map.css"
    }
  ],
  "dependencies": [
    "mapbox-gl",
    "@studiometa/vue-mapbox-gl"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/vue/use-theme.json"
  ],
  "description": "A thin, theme-aware Mapbox GL JS wrapper (built on @studiometa/vue-mapbox-gl). Pass an access token and drop MapMarker / MapPopup / MapLayer into the slot 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 `@created` hands you the raw map instance for custom layers and fitBounds.",
  "categories": [
    "data-display"
  ]
}