{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "event-shared",
  "version": "1.0.1",
  "category": "events",
  "meta": {
    "version": "1.0.1",
    "changelog": {
      "1.0.0": "Extracted shared map utilities and signal badges from event-list and event-detail",
      "1.0.1": "Replaced useReactLeaflet hook with React.lazy to fix Invalid hook call errors"
    }
  },
  "changelog": {
    "1.0.0": "Extracted shared map utilities and signal badges from event-list and event-detail",
    "1.0.1": "Replaced useReactLeaflet hook with React.lazy to fix Invalid hook call errors"
  },
  "title": "Event Shared Utilities",
  "author": "MNFST, Inc",
  "description": "Shared utilities for event components (Leaflet map helpers, signal badges).",
  "dependencies": [
    "lucide-react",
    "react-leaflet",
    "leaflet"
  ],
  "devDependencies": [
    "@types/leaflet"
  ],
  "registryDependencies": [
    "https://ui.manifest.build/r/manifest-types.json"
  ],
  "files": [
    {
      "path": "registry/events/shared.tsx",
      "content": "'use client'\n\nimport { cn } from '@/lib/utils'\nimport { MapPin } from 'lucide-react'\nimport { lazy } from 'react'\nimport type { ComponentType } from 'react'\nimport type { EventSignal } from './types'\n\n// Internal types for react-leaflet component attributes (not exported component props)\nexport type LeafletMarkerAttrs = {\n  position: [number, number]\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  icon?: any\n  zIndexOffset?: number\n  eventHandlers?: {\n    click?: () => void\n  }\n}\n\n// Props for the lazy-loaded leaflet map component\nexport interface LazyLeafletMapConfig {\n  center: [number, number]\n  zoom: number\n  style?: React.CSSProperties\n  scrollWheelZoom?: boolean\n  tileUrl?: string\n  tileAttribution?: string\n  renderMarkers: (ctx: {\n    Marker: ComponentType<LeafletMarkerAttrs>\n    L: typeof import('leaflet')\n  }) => React.ReactNode\n}\n\n/**\n * Lazy-loaded Leaflet map component using React.lazy.\n * Avoids Invalid hook call errors from dynamic import module boundaries.\n */\nexport const LazyLeafletMap = lazy<ComponentType<LazyLeafletMapConfig>>(async () => {\n  // Guard against SSR - react-leaflet/leaflet require window\n  if (typeof window === 'undefined') {\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    return { default: (() => null) as any }\n  }\n\n  const { MapContainer, TileLayer, Marker } = await import('react-leaflet')\n  const L = (await import('leaflet')).default\n\n  // Inject Leaflet CSS with deduplication\n  const LEAFLET_CSS_ID = 'leaflet-css-1.9.4'\n  if (typeof document !== 'undefined' && !document.getElementById(LEAFLET_CSS_ID)) {\n    const link = document.createElement('link')\n    link.id = LEAFLET_CSS_ID\n    link.rel = 'stylesheet'\n    link.href = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css'\n    document.head.appendChild(link)\n  }\n\n  function LazyLeafletMapComponent(props: LazyLeafletMapConfig) {\n    const tileUrl = props.tileUrl ?? 'https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png'\n    const tileAttribution = props.tileAttribution ?? '&copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> &copy; <a href=\"https://carto.com/attributions\">CARTO</a>'\n\n    return (\n      <MapContainer\n        center={props.center}\n        zoom={props.zoom}\n        style={props.style ?? { height: '100%', width: '100%' }}\n        zoomControl={true}\n        scrollWheelZoom={props.scrollWheelZoom ?? true}\n      >\n        <TileLayer attribution={tileAttribution} url={tileUrl} />\n        {props.renderMarkers({ Marker: Marker as ComponentType<LeafletMarkerAttrs>, L })}\n      </MapContainer>\n    )\n  }\n\n  return { default: LazyLeafletMapComponent }\n})\n\n// Format number with commas (consistent across server/client)\nexport function formatNumber(num: number): string {\n  return num.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',')\n}\n\n// Map placeholder shown during SSR or when Leaflet isn't loaded\nexport function MapPlaceholder() {\n  return (\n    <div className=\"flex h-full items-center justify-center bg-muted/30\">\n      <div className=\"flex flex-col items-center gap-2 text-muted-foreground\">\n        <MapPin className=\"h-8 w-8\" />\n        <span className=\"text-sm\">Loading map...</span>\n      </div>\n    </div>\n  )\n}\n\nexport function EventSignalBadge({ signal }: { signal: EventSignal }) {\n  const config: Record<EventSignal, { label: string; className: string }> = {\n    'going-fast': { label: 'Going Fast', className: 'bg-orange-100 text-orange-700' },\n    'popular': { label: 'Popular', className: 'bg-pink-100 text-pink-700' },\n    'just-added': { label: 'Just Added', className: 'bg-blue-100 text-blue-700' },\n    'sales-end-soon': { label: 'Sales end soon', className: 'bg-red-100 text-red-700' },\n    'few-tickets-left': { label: 'Few Tickets Left', className: 'bg-orange-100 text-orange-700' },\n    'canceled': { label: 'Canceled', className: 'bg-gray-100 text-gray-700' },\n    'ended': { label: 'Ended', className: 'bg-gray-100 text-gray-700' },\n    'postponed': { label: 'Postponed', className: 'bg-yellow-100 text-yellow-700' }\n  }\n\n  const { label, className } = config[signal]\n  return (\n    <span className={cn('inline-flex items-center gap-1 rounded-full px-3 py-1 text-sm font-medium', className)}>\n      {label}\n    </span>\n  )\n}\n",
      "type": "registry:lib",
      "target": "components/ui/shared.tsx"
    }
  ],
  "categories": [
    "events"
  ],
  "type": "registry:lib"
}