{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "map-toolbar",
  "title": "Map Toolbar",
  "description": "The glass-pill map control toolbar: a prop-driven standard arrangement (zoom, locate, Map/Satellite switcher, tool toggles, reset view) or fully composable groups, buttons and toggles.",
  "dependencies": [
    "@untitledui/icons@^0.0.22",
    "react-map-gl@^8.1.1"
  ],
  "registryDependencies": [
    "@nx-ui/button",
    "@nx-ui/map-view",
    "@nx-ui/separator",
    "@nx-ui/toggle",
    "@nx-ui/tooltip",
    "@nx-ui/utils"
  ],
  "files": [
    {
      "path": "components/blocks/maps/map-toolbar.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n  Globe01,\n  Loading01,\n  Map01,\n  Minus,\n  Plus,\n  RefreshCw01,\n  Target05,\n} from \"@untitledui/icons\"\nimport { useMap, type MapRef } from \"react-map-gl/mapbox\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\nimport { Separator } from \"@/components/ui/separator\"\nimport { Toggle } from \"@/components/ui/toggle\"\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipProvider,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\"\nimport {\n  UK_DEFAULT_VIEW,\n  type MapBasemap,\n} from \"@/components/blocks/maps/map-view\"\n\n/**\n * map-toolbar — the glass-pill map control toolbar for nx-ui.\n *\n * The floating top-centre control bar from the Nexus marketplace map,\n * generalised into a composable kit:\n *\n *   • <MapToolbar> — the positioned glass pill. Given no children it renders\n *     the standard arrangement, entirely prop-driven:\n *       [zoom out][zoom in][locate] | [Map/Satellite] | tool toggles | [reset]\n *     Give it children instead to compose any arrangement you like from the\n *     parts below.\n *   • <MapToolbarGroup> / <MapToolbarSeparator> — grouping and dividers.\n *   • <MapToolbarButton> — a tooltipped icon action button.\n *   • <MapToolbarToggle> — a tooltipped two-state tool button; the active\n *     state fills with the primary colour so the armed tool is unmissable.\n *\n * Camera actions need the Mapbox map: pass `mapRef` (the MapView ref) when the\n * toolbar sits in MapView's `overlay` slot; components rendered *inside* the\n * map pick it up from react-map-gl context automatically.\n */\n\n/** Resolve the map instance from an explicit ref, falling back to context.\n * Returns a getter so handlers read the ref LAZILY — at first render the ref\n * is still null (the map mounts afterwards without re-rendering the parent),\n * so capturing it at render time would leave the controls dead. */\nfunction useToolbarMap(mapRef?: React.RefObject<MapRef | null>) {\n  const { current: contextMap } = useMap()\n  return React.useCallback(\n    () => mapRef?.current ?? contextMap ?? null,\n    [mapRef, contextMap]\n  )\n}\n\nconst PILL_CLASS =\n  \"pointer-events-auto inline-flex items-center gap-1 rounded-lg border border-border/70 bg-background/90 px-2 py-1.5 shadow-lg backdrop-blur-md\"\n\nconst POSITION_CLASSES = {\n  \"top-center\": \"absolute inset-x-0 top-3 z-20 flex justify-center\",\n  \"top-left\": \"absolute top-3 left-3 z-20 flex\",\n  \"top-right\": \"absolute top-3 right-3 z-20 flex\",\n  static: \"flex\",\n} as const\n\nexport type MapToolbarPosition = keyof typeof POSITION_CLASSES\n\n/** A tool toggle rendered in the standard arrangement. */\nexport interface MapToolbarTool {\n  id: string\n  /** Tooltip / accessible label, e.g. \"Travel time\". */\n  label: string\n  icon: React.ReactNode\n  active?: boolean\n  disabled?: boolean\n  onToggle?: (active: boolean) => void\n}\n\nexport function MapToolbarGroup({\n  className,\n  ...props\n}: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"map-toolbar-group\"\n      className={cn(\"flex items-center gap-0.5\", className)}\n      {...props}\n    />\n  )\n}\n\nexport function MapToolbarSeparator({ className }: { className?: string }) {\n  return (\n    <Separator orientation=\"vertical\" className={cn(\"mx-0.5 h-5!\", className)} />\n  )\n}\n\nexport interface MapToolbarButtonProps {\n  /** Tooltip text; also the accessible name. */\n  label: string\n  onClick?: () => void\n  disabled?: boolean\n  children: React.ReactNode\n  className?: string\n}\n\n/** A tooltipped icon button for one-shot map actions (zoom, locate, reset). */\nexport function MapToolbarButton({\n  label,\n  onClick,\n  disabled,\n  children,\n  className,\n}: MapToolbarButtonProps) {\n  return (\n    <Tooltip>\n      <TooltipTrigger\n        render={\n          <Button\n            type=\"button\"\n            variant=\"ghost\"\n            size=\"icon-sm\"\n            aria-label={label}\n            disabled={disabled}\n            onClick={onClick}\n            className={cn(\"text-fg-quaternary hover:text-foreground\", className)}\n          >\n            {children}\n          </Button>\n        }\n      />\n      <TooltipContent side=\"bottom\">{label}</TooltipContent>\n    </Tooltip>\n  )\n}\n\nexport interface MapToolbarToggleProps {\n  /** Tooltip text; also the accessible name. */\n  label: string\n  pressed: boolean\n  onPressedChange?: (pressed: boolean) => void\n  disabled?: boolean\n  children: React.ReactNode\n  className?: string\n}\n\n/** A tooltipped tool toggle. Active = solid primary fill, so the armed tool\n * state is visually obvious at a glance. */\nexport function MapToolbarToggle({\n  label,\n  pressed,\n  onPressedChange,\n  disabled,\n  children,\n  className,\n}: MapToolbarToggleProps) {\n  return (\n    <Tooltip>\n      <TooltipTrigger\n        render={\n          <Toggle\n            size=\"sm\"\n            aria-label={label}\n            pressed={pressed}\n            onPressedChange={onPressedChange}\n            disabled={disabled}\n            className={cn(\n              \"min-w-8 px-2 data-pressed:bg-primary data-pressed:text-primary-foreground data-pressed:shadow-xs-skeuomorphic data-pressed:hover:bg-primary data-pressed:hover:text-primary-foreground\",\n              className\n            )}\n          >\n            {children}\n          </Toggle>\n        }\n      />\n      <TooltipContent side=\"bottom\">{label}</TooltipContent>\n    </Tooltip>\n  )\n}\n\nexport interface MapToolbarProps {\n  /** Where the pill floats within the map frame. Default top-centre. */\n  position?: MapToolbarPosition\n  /** MapView ref, needed for the built-in camera actions when the toolbar is\n   * rendered outside the map (e.g. in MapView's `overlay` slot). */\n  mapRef?: React.RefObject<MapRef | null>\n  /** Show the built-in zoom in/out pair. Default true. */\n  showZoom?: boolean\n  /** Show the built-in browser-geolocation \"locate me\" button. Default true. */\n  showLocate?: boolean\n  /** Fired with the user's coordinates after a successful locate. */\n  onLocate?: (coords: { longitude: number; latitude: number }) => void\n  /** Controlled basemap for the built-in Map/Satellite switcher (wire it to\n   * `resolveMapStyle` + MapView's `mapStyle`). Omit to hide the switcher. */\n  basemap?: MapBasemap\n  onBasemapChange?: (basemap: MapBasemap) => void\n  /** Tool toggles (isochrone, route…) shown between the switcher and reset. */\n  tools?: MapToolbarTool[]\n  /** Show the built-in reset-view button. Default true. */\n  showReset?: boolean\n  /** Reset handler. Defaults to easing back to the UK-wide default view. */\n  onResetView?: () => void\n  /** Custom arrangement — replaces the standard controls entirely. */\n  children?: React.ReactNode\n  className?: string\n  pillClassName?: string\n}\n\nexport function MapToolbar({\n  position = \"top-center\",\n  mapRef,\n  showZoom = true,\n  showLocate = true,\n  onLocate,\n  basemap,\n  onBasemapChange,\n  tools,\n  showReset = true,\n  onResetView,\n  children,\n  className,\n  pillClassName,\n}: MapToolbarProps) {\n  const getMap = useToolbarMap(mapRef)\n  const [locating, setLocating] = React.useState(false)\n\n  const zoomBy = (delta: number) => {\n    const mapbox = getMap()?.getMap()\n    if (!mapbox) return\n    mapbox.easeTo({ zoom: mapbox.getZoom() + delta, duration: 300 })\n  }\n\n  const locate = () => {\n    if (!navigator.geolocation || locating) return\n    setLocating(true)\n    navigator.geolocation.getCurrentPosition(\n      (result) => {\n        setLocating(false)\n        const coords = {\n          longitude: result.coords.longitude,\n          latitude: result.coords.latitude,\n        }\n        const mapbox = getMap()?.getMap()\n        mapbox?.flyTo({\n          center: [coords.longitude, coords.latitude],\n          zoom: Math.max(mapbox.getZoom(), 12),\n          speed: 0.8,\n          curve: 1.4,\n          essential: true,\n        })\n        onLocate?.(coords)\n      },\n      () => {\n        // Denied or unavailable — stay put; the button simply does nothing.\n        setLocating(false)\n      },\n      { enableHighAccuracy: false, timeout: 8_000 }\n    )\n  }\n\n  const resetView = () => {\n    if (onResetView) return onResetView()\n    getMap()?.getMap()?.easeTo({\n      center: [UK_DEFAULT_VIEW.longitude, UK_DEFAULT_VIEW.latitude],\n      zoom: UK_DEFAULT_VIEW.zoom,\n      pitch: 0,\n      bearing: 0,\n      duration: 600,\n    })\n  }\n\n  const showSwitcher = basemap !== undefined && onBasemapChange !== undefined\n  const hasTools = (tools?.length ?? 0) > 0\n\n  return (\n    <div\n      data-slot=\"map-toolbar\"\n      className={cn(\"pointer-events-none\", POSITION_CLASSES[position], className)}\n    >\n      <TooltipProvider delay={300}>\n        <div className={cn(PILL_CLASS, pillClassName)}>\n          {children ?? (\n            <>\n              {showZoom || showLocate ? (\n                <MapToolbarGroup>\n                  {showZoom ? (\n                    <>\n                      <MapToolbarButton label=\"Zoom out\" onClick={() => zoomBy(-1)}>\n                        <Minus />\n                      </MapToolbarButton>\n                      <MapToolbarButton label=\"Zoom in\" onClick={() => zoomBy(1)}>\n                        <Plus />\n                      </MapToolbarButton>\n                    </>\n                  ) : null}\n                  {showLocate ? (\n                    <MapToolbarButton label=\"Current location\" onClick={locate}>\n                      {locating ? (\n                        <Loading01 className=\"animate-spin\" />\n                      ) : (\n                        <Target05 />\n                      )}\n                    </MapToolbarButton>\n                  ) : null}\n                </MapToolbarGroup>\n              ) : null}\n\n              {showSwitcher ? (\n                <>\n                  <MapToolbarSeparator />\n                  <MapToolbarGroup aria-label=\"Basemap style\">\n                    <MapToolbarToggle\n                      label=\"Map\"\n                      pressed={basemap === \"map\"}\n                      onPressedChange={() => onBasemapChange?.(\"map\")}\n                    >\n                      <Map01 />\n                    </MapToolbarToggle>\n                    <MapToolbarToggle\n                      label=\"Satellite\"\n                      pressed={basemap === \"satellite\"}\n                      onPressedChange={() => onBasemapChange?.(\"satellite\")}\n                    >\n                      <Globe01 />\n                    </MapToolbarToggle>\n                  </MapToolbarGroup>\n                </>\n              ) : null}\n\n              {hasTools ? (\n                <>\n                  <MapToolbarSeparator />\n                  <MapToolbarGroup aria-label=\"Map tools\">\n                    {tools?.map((tool) => (\n                      <MapToolbarToggle\n                        key={tool.id}\n                        label={tool.label}\n                        pressed={tool.active ?? false}\n                        onPressedChange={tool.onToggle}\n                        disabled={tool.disabled}\n                      >\n                        {tool.icon}\n                      </MapToolbarToggle>\n                    ))}\n                  </MapToolbarGroup>\n                </>\n              ) : null}\n\n              {showReset ? (\n                <>\n                  <MapToolbarSeparator />\n                  <MapToolbarButton label=\"Reset view\" onClick={resetView}>\n                    <RefreshCw01 />\n                  </MapToolbarButton>\n                </>\n              ) : null}\n            </>\n          )}\n        </div>\n      </TooltipProvider>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/blocks/maps/map-toolbar.tsx"
    }
  ],
  "type": "registry:block"
}