{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "property-boundary-map",
  "title": "Property Boundary Map",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/property-boundary-map/property-boundary-map.component.ts",
      "content": "// Cadastral parcel boundaries with zoning classifications, lot acreage, and tax assessment\n// valuation on a Mapbox satellite map. Port of the React block 1:1 (same lib: map).\nimport { ChangeDetectionStrategy, Component, Input, ViewChild, computed, signal } from '@angular/core'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiCardComponent,\n  UiCardContentComponent,\n  UiCardDescriptionComponent,\n  UiCardHeaderComponent,\n  UiCardTitleComponent,\n} from '@/ui/card/card.component'\nimport { UiMapComponent, UiMapMarkerComponent, type MapLayerInput, type MapSourceInput } from '@/ui/map/map.component'\n\nexport interface Parcel {\n  apn: string\n  address: string\n  zoning: string\n  lotSqFt: string\n  acres: number\n  assessedValue: string\n  taxYear: number\n  coords: [number, number]\n  polygon: [number, number][]\n}\n\nexport const DEFAULT_PARCELS: Parcel[] = [\n  {\n    apn: '0482-104-012',\n    address: '450 Mountain View Rd, Austin, TX',\n    zoning: 'SF-3 (Single Family)',\n    lotSqFt: '14,280 sq ft',\n    acres: 0.33,\n    assessedValue: '$1,280,000',\n    taxYear: 2025,\n    coords: [-97.7431, 30.2672],\n    polygon: [\n      [-97.7438, 30.2678],\n      [-97.7424, 30.2678],\n      [-97.7424, 30.2666],\n      [-97.7438, 30.2666],\n      [-97.7438, 30.2678],\n    ],\n  },\n  {\n    apn: '0482-104-013',\n    address: '460 Mountain View Rd, Austin, TX',\n    zoning: 'SF-3 (Single Family)',\n    lotSqFt: '16,500 sq ft',\n    acres: 0.38,\n    assessedValue: '$1,410,000',\n    taxYear: 2025,\n    coords: [-97.741, 30.2672],\n    polygon: [\n      [-97.7424, 30.2678],\n      [-97.741, 30.2678],\n      [-97.741, 30.2666],\n      [-97.7424, 30.2666],\n      [-97.7424, 30.2678],\n    ],\n  },\n]\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-property-boundary-map, [ui-property-boundary-map]',\n  standalone: true,\n  host: {\n    '[attr.data-slot]': '\"property-boundary-map\"',\n    '[class]': 'hostClass',\n  },\n  imports: [\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiCardDescriptionComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiMapComponent,\n    UiMapMarkerComponent,\n  ],\n  template: `\n    <div class=\"border-border bg-card relative h-[480px] overflow-hidden rounded-xl border\">\n      <div\n        ui-map\n        [accessToken]=\"accessToken ?? ''\"\n        variant=\"satellite-streets\"\n        [center]=\"activeParcel().coords\"\n        [zoom]=\"16.5\"\n        class=\"h-full w-full\"\n        [sources]=\"mapSources()\"\n        [layers]=\"mapLayers()\"\n      >\n        @for (p of parcels; track p.apn) {\n          <ui-map-marker [lngLat]=\"p.coords\" anchor=\"center\">\n            <div class=\"group flex cursor-pointer flex-col items-center\" (click)=\"selectParcel(p)\">\n              <span\n                class=\"border-border bg-background/90 rounded border px-1.5 py-0.5 font-mono text-xs font-bold shadow-md\"\n                [class]=\"activeParcel().apn === p.apn ? 'border-warning text-warning' : 'text-foreground'\"\n              >\n                {{ p.apn }}\n              </span>\n            </div>\n          </ui-map-marker>\n        }\n      </div>\n    </div>\n\n    <!-- Cadastral Parcel Valuation Card -->\n    <div ui-card class=\"flex flex-col justify-between\">\n      <div ui-card-header>\n        <div class=\"flex items-center justify-between\">\n          <span ui-badge variant=\"outline\" class=\"font-mono text-xs\">APN {{ activeParcel().apn }}</span>\n          <span ui-badge class=\"border-warning/20 bg-warning/10 text-warning\">{{ activeParcel().zoning }}</span>\n        </div>\n        <h3 ui-card-title class=\"mt-2 text-base font-medium\">{{ activeParcel().address }}</h3>\n        <p ui-card-description class=\"font-mono text-xs\">\n          {{ activeParcel().lotSqFt }} • {{ activeParcel().acres }} Acres\n        </p>\n      </div>\n      <div ui-card-content class=\"space-y-4\">\n        <div class=\"border-border space-y-2 border-t pt-3 font-mono text-xs\">\n          <div class=\"flex items-center justify-between\">\n            <span class=\"text-muted-foreground\">Assessed Valuation</span>\n            <span class=\"text-foreground text-base font-bold\">{{ activeParcel().assessedValue }}</span>\n          </div>\n          <div class=\"flex items-center justify-between\">\n            <span class=\"text-muted-foreground\">Zoning Code</span>\n            <span class=\"text-foreground\">{{ activeParcel().zoning }}</span>\n          </div>\n          <div class=\"flex items-center justify-between\">\n            <span class=\"text-muted-foreground\">Assessment Roll Year</span>\n            <span class=\"text-foreground\">{{ activeParcel().taxYear }}</span>\n          </div>\n        </div>\n\n        <div class=\"border-border space-y-1 border-t pt-3\">\n          <div class=\"text-muted-foreground mb-1 font-mono text-xs tracking-wider uppercase\">\n            Adjacent Cadastral Lots\n          </div>\n          @for (p of parcels; track p.apn) {\n            <button type=\"button\" [class]=\"rowClass(p)\" (click)=\"selectParcel(p)\">\n              <span class=\"font-mono\">{{ p.apn }}</span>\n              <span class=\"font-mono text-xs opacity-75\">{{ p.lotSqFt }}</span>\n            </button>\n          }\n        </div>\n\n        <button ui-button variant=\"outline\" size=\"sm\" class=\"w-full font-mono text-xs\" (click)=\"recenter()\">\n          Center Cadastral Lot\n        </button>\n      </div>\n    </div>\n  `,\n})\nexport class UiPropertyBoundaryMapComponent {\n  @Input() parcels: Parcel[] = DEFAULT_PARCELS\n  @Input() accessToken?: string\n  @Input('class') className?: string\n\n  @ViewChild(UiMapComponent) map?: UiMapComponent\n\n  readonly activeApn = signal(DEFAULT_PARCELS[0]?.apn ?? '')\n\n  readonly mapSources = computed<MapSourceInput[]>(() => [\n    {\n      id: 'parcel-primary',\n      options: {\n        type: 'geojson',\n        data: {\n          type: 'Feature',\n          properties: {},\n          geometry: { type: 'Polygon', coordinates: [this.activeParcel().polygon] },\n        },\n      },\n    },\n  ])\n\n  readonly mapLayers = computed<MapLayerInput[]>(() => [\n    {\n      id: 'parcel-primary-fill',\n      options: {\n        type: 'fill',\n        source: 'parcel-primary',\n        paint: { 'fill-color': '#eab308', 'fill-opacity': 0.28 },\n      },\n    },\n    {\n      id: 'parcel-primary-line',\n      options: {\n        type: 'line',\n        source: 'parcel-primary',\n        paint: { 'line-color': '#facc15', 'line-width': 2.5 },\n      },\n    },\n  ])\n\n  get hostClass(): string {\n    return cn('grid gap-4 lg:grid-cols-[1fr_320px]', this.className)\n  }\n\n  activeParcel(): Parcel {\n    return this.parcels.find((p) => p.apn === this.activeApn()) ?? this.parcels[0]!\n  }\n\n  selectParcel(p: Parcel): void {\n    this.activeApn.set(p.apn)\n    this.map?.flyTo({ center: p.coords, zoom: 17 })\n  }\n\n  recenter(): void {\n    this.selectParcel(this.activeParcel())\n  }\n\n  rowClass(p: Parcel): string {\n    return cn(\n      'flex w-full items-center justify-between rounded-md px-2 py-1 text-xs transition-colors',\n      this.activeParcel().apn === p.apn\n        ? 'bg-accent text-accent-foreground font-semibold'\n        : 'hover:bg-muted text-muted-foreground',\n    )\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/property-boundary-map/property-boundary-map.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/property-boundary-map/index.ts",
      "content": "export { UiPropertyBoundaryMapComponent, DEFAULT_PARCELS, type Parcel } from './property-boundary-map.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/property-boundary-map/index.ts"
    }
  ],
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/card.json",
    "https://uipkge.dev/r/angular/map.json"
  ],
  "description": "Cadastral parcel boundaries with zoning classifications, lot acreage, and tax assessment valuation.",
  "categories": [
    "real-estate",
    "places",
    "map"
  ]
}