{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "leaflet-hub-locator-map",
  "title": "Leaflet Hub Locator Map",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/leaflet-hub-locator-map/leaflet-hub-locator-map.component.ts",
      "content": "import { Component, Input, OnInit, ViewChild, signal, ChangeDetectionStrategy } from '@angular/core'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport {\n  UiCardComponent,\n  UiCardContentComponent,\n  UiCardHeaderComponent,\n  UiCardTitleComponent,\n} from '@/ui/card/card.component'\nimport {\n  UiLeafletCircleComponent,\n  UiLeafletMapComponent,\n  UiLeafletMarkerComponent,\n} from '@/ui/leaflet-map/leaflet-map.component'\n\nexport interface DistributionHub {\n  id: string\n  code: string\n  name: string\n  city: string\n  state: string\n  lat: number\n  lng: number\n  capacityPct: number\n  bays: number\n  /** Delivery coverage radius in miles. */\n  coverageMiles: number\n}\n\nexport const DEFAULT_HUBS: DistributionHub[] = [\n  {\n    id: 'atl-01',\n    code: 'ATL-01',\n    name: 'South Atlanta DC',\n    city: 'Atlanta',\n    state: 'GA',\n    lat: 33.629,\n    lng: -84.402,\n    capacityPct: 82,\n    bays: 36,\n    coverageMiles: 18,\n  },\n  {\n    id: 'atl-02',\n    code: 'ATL-02',\n    name: 'Marietta Cross-Dock',\n    city: 'Marietta',\n    state: 'GA',\n    lat: 33.953,\n    lng: -84.55,\n    capacityPct: 64,\n    bays: 22,\n    coverageMiles: 14,\n  },\n  {\n    id: 'atl-03',\n    code: 'ATL-03',\n    name: 'McDonough Fulfillment',\n    city: 'McDonough',\n    state: 'GA',\n    lat: 33.447,\n    lng: -84.147,\n    capacityPct: 91,\n    bays: 48,\n    coverageMiles: 22,\n  },\n  {\n    id: 'atl-04',\n    code: 'ATL-04',\n    name: 'Peachtree City Hub',\n    city: 'Peachtree City',\n    state: 'GA',\n    lat: 33.397,\n    lng: -84.596,\n    capacityPct: 57,\n    bays: 18,\n    coverageMiles: 15,\n  },\n  {\n    id: 'atl-05',\n    code: 'ATL-05',\n    name: 'Douglasville Depot',\n    city: 'Douglasville',\n    state: 'GA',\n    lat: 33.751,\n    lng: -84.747,\n    capacityPct: 73,\n    bays: 26,\n    coverageMiles: 16,\n  },\n  {\n    id: 'atl-06',\n    code: 'ATL-06',\n    name: 'Conyers Annex',\n    city: 'Conyers',\n    state: 'GA',\n    lat: 33.667,\n    lng: -84.018,\n    capacityPct: 88,\n    bays: 30,\n    coverageMiles: 20,\n  },\n]\n\nconst METERS_PER_MILE = 1609.34\n\n// Downtown Atlanta as [lng, lat] — a stable reference so the map's center input never re-fires.\nconst ATLANTA_CENTER: [number, number] = [-84.388, 33.749]\n\nconst PIN_SELECTED =\n  'border-primary bg-primary text-primary-foreground rounded-full border px-2 py-0.5 font-mono text-xs font-semibold shadow-xs'\nconst PIN_IDLE =\n  'border-border bg-card text-foreground rounded-full border px-2 py-0.5 font-mono text-xs font-semibold shadow-xs'\n\n/**\n * Distribution hub locator on free OpenStreetMap tiles — no API key. Coverage\n * rings, capacity badges, and fly-to on select (map pin or location row).\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-leaflet-hub-locator-map, [ui-leaflet-hub-locator-map]',\n  standalone: true,\n  imports: [\n    UiBadgeComponent,\n    UiCardComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiCardContentComponent,\n    UiLeafletMapComponent,\n    UiLeafletMarkerComponent,\n    UiLeafletCircleComponent,\n  ],\n  host: {\n    'data-slot': 'leaflet-hub-locator-map',\n    '[class]': 'hostClass',\n  },\n  template: `\n    <div class=\"flex flex-wrap items-center justify-between gap-3\">\n      <h2 class=\"text-foreground text-lg font-semibold tracking-tight\">Distribution hubs</h2>\n      <p class=\"text-muted-foreground text-xs\">{{ hubs.length }} centers</p>\n    </div>\n\n    <div class=\"grid min-h-0 flex-1 grid-cols-1 gap-4 lg:grid-cols-12\">\n      <ui-card class=\"border-border flex min-h-0 flex-col overflow-hidden shadow-xs lg:col-span-8\">\n        <div class=\"relative min-h-[360px] flex-1 overflow-hidden\">\n          <ui-leaflet-map variant=\"streets\" [center]=\"atlantaCenter\" [zoom]=\"10.5\" class=\"absolute inset-0 size-full\">\n            @for (hub of hubs; track hub.id + '-coverage') {\n              <ui-leaflet-circle\n                [center]=\"[hub.lng, hub.lat]\"\n                [radius]=\"coverageMeters(hub)\"\n                color=\"#3b82f6\"\n                [weight]=\"selectedId() === hub.id ? 2 : 1.5\"\n                [fill]=\"true\"\n                fillColor=\"#3b82f6\"\n                [fillOpacity]=\"selectedId() === hub.id ? 0.16 : 0.07\"\n              />\n            }\n            @for (hub of hubs; track hub.id) {\n              <ui-leaflet-marker [lngLat]=\"[hub.lng, hub.lat]\" anchor=\"center\">\n                <button type=\"button\" [class]=\"pinClass(hub)\" (click)=\"selectHub(hub)\">{{ hub.code }}</button>\n              </ui-leaflet-marker>\n            }\n          </ui-leaflet-map>\n        </div>\n      </ui-card>\n\n      <ui-card class=\"border-border flex min-h-0 flex-col overflow-hidden shadow-xs lg:col-span-4\">\n        <ui-card-header class=\"border-border border-b p-4\">\n          <ui-card-title class=\"text-sm font-semibold\">Locations</ui-card-title>\n        </ui-card-header>\n        <ui-card-content class=\"divide-border min-h-0 flex-1 divide-y overflow-auto p-0\">\n          @for (hub of hubs; track hub.id) {\n            <button type=\"button\" [class]=\"rowClass(hub)\" (click)=\"selectHub(hub)\">\n              <div class=\"min-w-0\">\n                <p class=\"text-foreground truncate text-sm font-medium\">{{ hub.name }}</p>\n                <p class=\"text-muted-foreground truncate text-xs\">\n                  {{ hub.city }}, {{ hub.state }} · {{ hub.bays }} bays · {{ hub.coverageMiles }} mi\n                </p>\n              </div>\n              <ui-badge [variant]=\"capacityVariant(hub.capacityPct)\" class=\"shrink-0 font-mono text-xs tabular-nums\">\n                {{ hub.capacityPct }}%\n              </ui-badge>\n            </button>\n          }\n        </ui-card-content>\n      </ui-card>\n    </div>\n  `,\n})\nexport class UiLeafletHubLocatorMapComponent implements OnInit {\n  @Input() hubs: DistributionHub[] = DEFAULT_HUBS\n  @Input('class') className?: string\n\n  @ViewChild(UiLeafletMapComponent) map?: UiLeafletMapComponent\n\n  protected readonly atlantaCenter = ATLANTA_CENTER\n  readonly selectedId = signal('')\n\n  get hostClass(): string {\n    return cn('flex h-full min-h-0 flex-col gap-4', this.className)\n  }\n\n  ngOnInit(): void {\n    this.selectedId.set(this.hubs[0]?.id ?? '')\n  }\n\n  selectHub(hub: DistributionHub): void {\n    this.selectedId.set(hub.id)\n    this.map?.flyTo({\n      center: [hub.lng, hub.lat],\n      zoom: 11.5,\n      duration: 900,\n    })\n  }\n\n  capacityVariant(capacityPct: number): 'warning' | 'info' {\n    return capacityPct >= 85 ? 'warning' : 'info'\n  }\n\n  coverageMeters(hub: DistributionHub): number {\n    return hub.coverageMiles * METERS_PER_MILE\n  }\n\n  pinClass(hub: DistributionHub): string {\n    return this.selectedId() === hub.id ? PIN_SELECTED : PIN_IDLE\n  }\n\n  rowClass(hub: DistributionHub): string {\n    return cn(\n      'hover:bg-accent/50 flex w-full items-center justify-between gap-3 px-4 py-3 text-left transition-colors',\n      this.selectedId() === hub.id && 'bg-accent/60',\n    )\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/leaflet-hub-locator-map/leaflet-hub-locator-map.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/leaflet-hub-locator-map/index.ts",
      "content": "export {\n  UiLeafletHubLocatorMapComponent,\n  DEFAULT_HUBS,\n  type DistributionHub,\n} from './leaflet-hub-locator-map.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/leaflet-hub-locator-map/index.ts"
    }
  ],
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/card.json",
    "https://uipkge.dev/r/angular/leaflet-map.json"
  ],
  "description": "Distribution hub locator on free OpenStreetMap tiles — no API key. Coverage rings, capacity badges, and fly-to on select.",
  "categories": [
    "dashboard",
    "app",
    "logistics"
  ]
}