{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "map-marker",
  "title": "Map Marker",
  "description": "Selectable map pins, dots, and price labels with tones, captions, and cluster counts.",
  "dependencies": [
    "class-variance-authority"
  ],
  "registryDependencies": [
    "https://uaiblocks.dev/r/uai-theme.json",
    "https://uaiblocks.dev/r/uai-utils.json"
  ],
  "files": [
    {
      "path": "src/registry/uai/components/map-marker.tsx",
      "content": "\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { type ComponentProps, createContext, useContext } from \"react\";\nimport { cn } from \"@/lib/uai-utils\";\n\nexport const MAP_MARKER_VARIANTS = [\"pin\", \"dot\", \"label\"] as const;\nexport type MapMarkerVariant = (typeof MAP_MARKER_VARIANTS)[number];\nexport const MAP_MARKER_TONES = [\n  \"default\",\n  \"primary\",\n  \"success\",\n  \"warning\",\n  \"destructive\",\n] as const;\nexport type MapMarkerTone = (typeof MAP_MARKER_TONES)[number];\nexport type MapMarkerProps = Omit<ComponentProps<\"button\">, \"aria-label\"> & {\n  variant?: MapMarkerVariant;\n  tone?: MapMarkerTone;\n  /** Spoken name of the place, for example \"Ferrow Coffee, open until 6 PM\". */\n  label: string;\n  /** The marker whose details are showing. Raises it and fills it with ink. */\n  selected?: boolean;\n};\n\ntype MarkerContext = { variant: MapMarkerVariant };\nconst Context = createContext<MarkerContext | null>(null);\nfunction useMarker(part: string) {\n  const context = useContext(Context);\n  if (!context) throw new Error(`${part} must be used within MapMarker`);\n  return context;\n}\n\nconst toneClasses =\n  \"data-[tone=default]:[--marker:var(--foreground)] data-[tone=primary]:[--marker:var(--primary)] data-[tone=success]:[--marker:var(--success)] data-[tone=warning]:[--marker:var(--warning)] data-[tone=destructive]:[--marker:var(--destructive)]\";\n\nconst mapMarkerVariants = cva(\n  `group/map-marker relative inline-flex cursor-pointer items-center justify-center border-0 bg-transparent p-0 text-[12px]/4 font-medium text-foreground transition-[scale,translate] duration-180 ease-out-quint outline-none hover:z-10 focus-visible:z-10 active:scale-[0.94] aria-pressed:z-20 motion-reduce:transition-none motion-reduce:active:scale-100 ${toneClasses}`,\n  {\n    variants: {\n      variant: {\n        pin: \"flex-col pb-0.5 hover:-translate-y-0.5 aria-pressed:-translate-y-1 motion-reduce:translate-y-0!\",\n        dot: \"size-6\",\n        label: \"hover:-translate-y-0.5 aria-pressed:-translate-y-0.5 motion-reduce:translate-y-0!\",\n      },\n    },\n  },\n);\n\n/**\n * A selectable place on the map. Your map library positions it; anchor it at the bottom\n * center for the pin and label variants, and at the center for the dot variant.\n */\nexport function MapMarker({\n  variant = \"pin\",\n  tone = \"default\",\n  label,\n  selected = false,\n  type = \"button\",\n  className,\n  children,\n  ...props\n}: MapMarkerProps) {\n  return (\n    <Context.Provider value={{ variant }}>\n      <button\n        data-slot=\"map-marker\"\n        type={type}\n        aria-label={label}\n        aria-pressed={selected}\n        data-tone={tone}\n        className={cn(mapMarkerVariants({ variant }), className)}\n        {...props}\n        data-variant={variant}\n      >\n        {variant === \"pin\" && (\n          <span className=\"relative grid size-8 place-items-center rounded-[50%_50%_50%_6px] bg-card text-(--marker) shadow-[0_0_0_1px_var(--border-strong),0_6px_14px_-6px_oklch(0_0_0/0.4)] [rotate:-45deg] transition-[background-color,color,box-shadow] duration-120 ease-out group-focus-visible/map-marker:shadow-[0_0_0_2px_var(--ring),0_6px_14px_-6px_oklch(0_0_0/0.4)] group-aria-pressed/map-marker:bg-(--marker) group-aria-pressed/map-marker:text-card group-aria-pressed/map-marker:shadow-[0_0_0_2px_var(--card),0_8px_18px_-6px_oklch(0_0_0/0.45)] group-data-[tone=default]/map-marker:group-aria-pressed/map-marker:text-background\">\n            <span className=\"grid [rotate:45deg] place-items-center\">{children}</span>\n          </span>\n        )}\n        {variant === \"dot\" && (\n          <>\n            <span\n              aria-hidden=\"true\"\n              className=\"absolute inset-0 rounded-full bg-(--marker)/24 text-(--marker) opacity-0 transition-opacity duration-180 group-hover/map-marker:opacity-100 group-aria-pressed/map-marker:animate-ring-pulse group-aria-pressed/map-marker:opacity-100 motion-reduce:animate-none!\"\n            />\n            <span className=\"relative size-3 rounded-full bg-(--marker) shadow-[0_0_0_2px_var(--card),0_2px_6px_oklch(0_0_0/0.3)] transition-[scale] duration-180 ease-out-quint group-focus-visible/map-marker:shadow-[0_0_0_2px_var(--card),0_0_0_4px_var(--ring)] group-aria-pressed/map-marker:scale-125 motion-reduce:transition-none\" />\n            {children}\n          </>\n        )}\n        {variant === \"label\" && (\n          <span className=\"relative inline-flex h-7 items-center gap-1.5 rounded-full bg-card px-2.5 text-(--marker) shadow-[0_0_0_1px_var(--border-strong),0_6px_14px_-6px_oklch(0_0_0/0.4)] transition-[background-color,color,box-shadow] duration-120 ease-out group-focus-visible/map-marker:shadow-[0_0_0_2px_var(--ring),0_6px_14px_-6px_oklch(0_0_0/0.4)] group-aria-pressed/map-marker:bg-(--marker) group-aria-pressed/map-marker:text-card group-data-[tone=default]/map-marker:group-aria-pressed/map-marker:text-background after:absolute after:top-full after:left-1/2 after:size-2 after:-translate-x-1/2 after:-translate-y-1 after:rotate-45 after:rounded-[2px] after:bg-inherit after:shadow-[1px_1px_0_0_var(--border-strong)] group-aria-pressed/map-marker:after:shadow-none\">\n            {children}\n          </span>\n        )}\n      </button>\n    </Context.Provider>\n  );\n}\n\n/** A 16px glyph inside the pin or label. Decorative; the marker label names the place. */\nexport function MapMarkerIcon({ className, ...props }: ComponentProps<\"span\">) {\n  const context = useMarker(\"MapMarkerIcon\");\n  return (\n    <span\n      data-slot=\"map-marker-icon\"\n      aria-hidden=\"true\"\n      className={cn(\n        \"grid place-items-center [&_svg]:size-4 [&_svg]:stroke-[1.9]\",\n        context.variant === \"label\" && \"[&_svg]:size-3.5\",\n        context.variant === \"dot\" && \"hidden\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\n/** Visible text: the price, name, or short status for label markers, or a caption below pins and dots. */\nexport function MapMarkerLabel({ className, ...props }: ComponentProps<\"span\">) {\n  const context = useMarker(\"MapMarkerLabel\");\n  return (\n    <span\n      data-slot=\"map-marker-label\"\n      aria-hidden=\"true\"\n      className={cn(\n        \"whitespace-nowrap tabular-nums\",\n        context.variant !== \"label\" &&\n          \"absolute top-full left-1/2 mt-1 -translate-x-1/2 rounded-md bg-card/90 px-1.5 py-px text-[11.5px]/4 text-foreground shadow-[0_0_0_1px_var(--border)] backdrop-blur-sm\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport type MapMarkerClusterProps = Omit<ComponentProps<\"button\">, \"aria-label\"> & {\n  /** Number of places grouped under this marker. */\n  count: number;\n  /** Spoken name. Defaults to \"{count} places, zoom in to expand\". */\n  label?: string;\n};\n\n/** Several nearby places shown as one count. Selecting it usually zooms in. */\nexport function MapMarkerCluster({\n  count,\n  label,\n  type = \"button\",\n  className,\n  ...props\n}: MapMarkerClusterProps) {\n  const size =\n    count >= 100\n      ? \"size-11 text-[12px]\"\n      : count >= 10\n        ? \"size-9 text-[12px]\"\n        : \"size-8 text-[12.5px]\";\n  return (\n    <button\n      data-slot=\"map-marker-cluster\"\n      type={type}\n      aria-label={label ?? `${count} places, zoom in to expand`}\n      className={cn(\n        \"grid cursor-pointer place-items-center rounded-full border-0 bg-foreground p-0 font-medium text-background tabular-nums shadow-[0_0_0_4px_color-mix(in_oklab,var(--foreground)_18%,transparent),0_6px_14px_-6px_oklch(0_0_0/0.4)] transition-[scale,box-shadow] duration-180 ease-out-quint outline-none hover:scale-105 focus-visible:shadow-[0_0_0_4px_var(--ring)] active:scale-[0.96] motion-reduce:transition-none motion-reduce:hover:scale-100 motion-reduce:active:scale-100\",\n        size,\n        className,\n      )}\n      {...props}\n    >\n      <span aria-hidden=\"true\">{count > 999 ? `${Math.floor(count / 1000)}k` : count}</span>\n    </button>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/uai/map-marker.tsx"
    }
  ],
  "categories": [
    "maps"
  ],
  "type": "registry:ui"
}