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