{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "leaflet-property-boundary-map",
  "title": "Leaflet Property Boundary Map",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-vue/blocks/leaflet-property-boundary-map/LeafletPropertyBoundaryMap.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { computed, ref } from 'vue'\nimport { cn } from '@/lib/utils'\nimport { LeafletMap, LeafletMarker, LeafletPolygon, type LeafletMapRef } from '@/components/ui/leaflet-map'\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\n\nexport interface Parcel {\n  apn: string\n  address: string\n  zoning: string\n  /** Plain-hex zone fill — Leaflet renders paths to SVG/canvas, so CSS vars do not resolve there. */\n  color: string\n  lotSqFt: string\n  acres: number\n  assessedValue: string\n  taxYear: number\n  coords: [number, number]\n  polygon: [number, number][]\n}\n\nexport interface LeafletPropertyBoundaryMapProps {\n  class?: HTMLAttributes['class']\n  parcels?: Parcel[]\n}\n\nconst DEFAULT_PARCELS: Parcel[] = [\n  {\n    apn: '093-142-018',\n    address: '418 Kingsley Ave, Palo Alto, CA',\n    zoning: 'R-1 (Single Family)',\n    color: '#facc15',\n    lotSqFt: '8,712 sq ft',\n    acres: 0.2,\n    assessedValue: '$3,940,000',\n    taxYear: 2025,\n    coords: [-122.1709, 37.4281],\n    polygon: [\n      [-122.1715, 37.4285],\n      [-122.1703, 37.4285],\n      [-122.1703, 37.4277],\n      [-122.1715, 37.4277],\n      [-122.1715, 37.4285],\n    ],\n  },\n  {\n    apn: '093-142-019',\n    address: '424 Kingsley Ave, Palo Alto, CA',\n    zoning: 'R-1 (Single Family)',\n    color: '#facc15',\n    lotSqFt: '9,580 sq ft',\n    acres: 0.22,\n    assessedValue: '$4,120,000',\n    taxYear: 2025,\n    coords: [-122.1693, 37.4281],\n    polygon: [\n      [-122.1699, 37.4285],\n      [-122.1687, 37.4285],\n      [-122.1687, 37.4277],\n      [-122.1699, 37.4277],\n      [-122.1699, 37.4285],\n    ],\n  },\n  {\n    apn: '093-142-021',\n    address: '510 Lytton Ave, Palo Alto, CA',\n    zoning: 'CS (Service Commercial)',\n    color: '#c084fc',\n    lotSqFt: '21,780 sq ft',\n    acres: 0.5,\n    assessedValue: '$6,850,000',\n    taxYear: 2025,\n    coords: [-122.1699, 37.4268],\n    polygon: [\n      [-122.1708, 37.4273],\n      [-122.169, 37.4273],\n      [-122.169, 37.4263],\n      [-122.1708, 37.4263],\n      [-122.1708, 37.4273],\n    ],\n  },\n  {\n    apn: '093-142-024',\n    address: '777 Bryant Ct, Palo Alto, CA',\n    zoning: 'RM-20 (Multi-Family)',\n    color: '#38bdf8',\n    lotSqFt: '13,068 sq ft',\n    acres: 0.3,\n    assessedValue: '$5,230,000',\n    taxYear: 2025,\n    coords: [-122.1723, 37.4268],\n    polygon: [\n      [-122.173, 37.4273],\n      [-122.1716, 37.4273],\n      [-122.1716, 37.4263],\n      [-122.173, 37.4263],\n      [-122.173, 37.4273],\n    ],\n  },\n  {\n    apn: '093-142-030',\n    address: '250 Hamilton Ave, Palo Alto, CA',\n    zoning: 'PF (Public Facility)',\n    color: '#34d399',\n    lotSqFt: '30,492 sq ft',\n    acres: 0.7,\n    assessedValue: '$8,900,000',\n    taxYear: 2025,\n    coords: [-122.1679, 37.4261],\n    polygon: [\n      [-122.1687, 37.4265],\n      [-122.1671, 37.4265],\n      [-122.1671, 37.4256],\n      [-122.1687, 37.4256],\n      [-122.1687, 37.4265],\n    ],\n  },\n]\n\n// Downtown Palo Alto — stable reference so the map's center watcher never re-fires.\nconst MAP_CENTER: [number, number] = [-122.1697, 37.4275]\n\nconst props = defineProps<LeafletPropertyBoundaryMapProps>()\n\nconst parcels = computed(() => props.parcels ?? DEFAULT_PARCELS)\nconst mapRef = ref<LeafletMapRef | null>(null)\nconst activeApn = ref(parcels.value[0]?.apn ?? '')\nconst activeParcel = computed(() => parcels.value.find((p) => p.apn === activeApn.value) ?? parcels.value[0])\n\nfunction selectParcel(p: Parcel) {\n  activeApn.value = p.apn\n  mapRef.value?.flyTo({ center: p.coords, zoom: 17 })\n}\n</script>\n\n<template>\n  <div data-slot=\"leaflet-property-boundary-map\" :class=\"cn('grid gap-4 lg:grid-cols-[1fr_320px]', props.class)\">\n    <div class=\"border-border bg-card relative h-[480px] overflow-hidden rounded-xl border\">\n      <LeafletMap ref=\"mapRef\" variant=\"satellite-streets\" :center=\"MAP_CENTER\" :zoom=\"16\" class=\"h-full w-full\">\n        <!-- Cadastral lot polygons, filled by zoning class -->\n        <LeafletPolygon\n          v-for=\"p in parcels\"\n          :key=\"`lot-${p.apn}`\"\n          :lng-lat-path=\"p.polygon\"\n          :color=\"p.color\"\n          :weight=\"activeParcel?.apn === p.apn ? 3 : 1.5\"\n          :opacity=\"0.95\"\n          :fill=\"true\"\n          :fill-color=\"p.color\"\n          :fill-opacity=\"activeParcel?.apn === p.apn ? 0.35 : 0.15\"\n          @click=\"selectParcel(p)\"\n        />\n\n        <LeafletMarker v-for=\"p in parcels\" :key=\"p.apn\" :lng-lat=\"p.coords\" anchor=\"center\" @click=\"selectParcel(p)\">\n          <div class=\"group flex cursor-pointer flex-col items-center\">\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 ? 'text-foreground' : 'text-muted-foreground'\"\n              :style=\"activeParcel?.apn === p.apn ? { color: p.color, borderColor: p.color } : undefined\"\n            >\n              {{ p.apn }}\n            </span>\n          </div>\n        </LeafletMarker>\n      </LeafletMap>\n    </div>\n\n    <!-- Cadastral Parcel Valuation Card -->\n    <Card v-if=\"activeParcel\" class=\"flex flex-col justify-between\">\n      <CardHeader>\n        <div class=\"flex items-center justify-between\">\n          <Badge variant=\"outline\" class=\"font-mono text-xs\">APN {{ activeParcel.apn }}</Badge>\n          <Badge variant=\"secondary\" class=\"gap-1.5 text-xs\">\n            <span class=\"size-2 rounded-full\" :style=\"{ backgroundColor: activeParcel.color }\" />\n            {{ activeParcel.zoning }}\n          </Badge>\n        </div>\n        <CardTitle class=\"mt-2 text-base font-medium\">{{ activeParcel.address }}</CardTitle>\n        <CardDescription class=\"font-mono text-xs\"\n          >{{ activeParcel.lotSqFt }} • {{ activeParcel.acres }} Acres</CardDescription\n        >\n      </CardHeader>\n      <CardContent 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          <button\n            v-for=\"p in parcels\"\n            :key=\"p.apn\"\n            type=\"button\"\n            class=\"flex w-full items-center justify-between rounded-md px-2 py-1 text-xs transition-colors\"\n            :class=\"\n              activeParcel.apn === p.apn\n                ? 'bg-accent text-accent-foreground font-semibold'\n                : 'hover:bg-muted text-muted-foreground'\n            \"\n            @click=\"selectParcel(p)\"\n          >\n            <span class=\"font-mono\">{{ p.apn }}</span>\n            <span class=\"font-mono text-xs opacity-75\">{{ p.lotSqFt }}</span>\n          </button>\n        </div>\n\n        <Button variant=\"outline\" size=\"sm\" class=\"w-full font-mono text-xs\" @click=\"selectParcel(activeParcel)\">\n          Center Cadastral Lot\n        </Button>\n      </CardContent>\n    </Card>\n  </div>\n</template>\n",
      "type": "registry:block",
      "target": "~/app/components/blocks/LeafletPropertyBoundaryMap.vue"
    }
  ],
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/vue/badge.json",
    "https://uipkge.dev/r/vue/button.json",
    "https://uipkge.dev/r/vue/card.json",
    "https://uipkge.dev/r/vue/leaflet-map.json"
  ],
  "description": "Cadastral parcel boundary polygons with zoning classes, lot acreage, and assessed valuation on free Esri satellite tiles — no API key.",
  "categories": [
    "real-estate",
    "places",
    "map"
  ]
}