{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "kpi-card",
  "type": "registry:ui",
  "title": "KPI Card",
  "description": "A metric card with a tinted icon, an actions button, a counting value and a stack of avatars or initials.",
  "dependencies": ["@vueuse/core"],
  "registryDependencies": ["https://nxui.geoql.in/r/chart.json"],
  "files": [
    {
      "path": "registry/new-york/kpi-card/KpiCard.vue",
      "content": "<script setup lang=\"ts\">\n  import { computed, ref } from 'vue';\n  import { useChartTween } from '../chart/use-chart-tween';\n  import type { KpiAccent, KpiAccentStyle, KpiCardProps } from './types';\n\n  const props = withDefaults(defineProps<KpiCardProps>(), {\n    accent: 'violet',\n    people: () => [],\n    maxPeople: 3,\n    showMenu: true,\n    class: '',\n  });\n\n  const emit = defineEmits<{ menu: [event: MouseEvent] }>();\n\n  const shown = useChartTween(() => props.value, 1000);\n  const display = computed(() =>\n    new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(\n      shown.value,\n    ),\n  );\n\n  const ACCENTS: Record<KpiAccent, KpiAccentStyle> = {\n    violet: {\n      icon: 'text-violet-600 dark:text-violet-400',\n      glow: 'from-violet-500/12',\n    },\n    emerald: {\n      icon: 'text-emerald-600 dark:text-emerald-400',\n      glow: 'from-emerald-500/12',\n    },\n    blue: {\n      icon: 'text-blue-600 dark:text-blue-400',\n      glow: 'from-blue-500/12',\n    },\n    rose: {\n      icon: 'text-rose-600 dark:text-rose-400',\n      glow: 'from-rose-500/12',\n    },\n    amber: {\n      icon: 'text-amber-600 dark:text-amber-400',\n      glow: 'from-amber-500/12',\n    },\n  };\n\n  const visible = computed(() => props.people.slice(0, props.maxPeople));\n  const hidden = computed(() =>\n    Math.max(0, props.people.length - props.maxPeople),\n  );\n\n  // Images that fail to load fall back to initials.\n  const failed = ref(new Set<string>());\n\n  function initial(name: string): string {\n    return name.trim().charAt(0).toUpperCase();\n  }\n\n  function onImageError(name: string): void {\n    failed.value = new Set(failed.value).add(name);\n  }\n\n  function onMenu(event: MouseEvent): void {\n    emit('menu', event);\n  }\n</script>\n\n<template>\n  <article\n    :class=\"[\n      'relative flex flex-col overflow-hidden rounded-2xl border bg-card p-4 text-card-foreground shadow-sm',\n      props.class,\n    ]\"\n  >\n    <div\n      aria-hidden=\"true\"\n      :class=\"[\n        'pointer-events-none absolute inset-0 bg-radial-[at_15%_0%] to-transparent to-60%',\n        ACCENTS[props.accent].glow,\n      ]\"\n    />\n\n    <header class=\"relative flex items-start justify-between\">\n      <span\n        :class=\"[\n          'flex size-9 items-center justify-center rounded-lg border bg-background shadow-xs',\n          ACCENTS[props.accent].icon,\n        ]\"\n      >\n        <Icon :name=\"`lucide:${props.icon}`\" class=\"size-4.5\" />\n      </span>\n      <button\n        v-if=\"props.showMenu\"\n        type=\"button\"\n        :aria-label=\"`${props.label} actions`\"\n        class=\"-mr-1 flex size-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring\"\n        @click=\"onMenu\"\n      >\n        <Icon name=\"lucide:ellipsis-vertical\" class=\"size-4\" />\n      </button>\n    </header>\n\n    <p class=\"relative mt-5 text-sm\">{{ props.label }}</p>\n\n    <div class=\"relative mt-1 flex items-end justify-between gap-3\">\n      <span class=\"text-2xl font-semibold tracking-tight tabular-nums\">\n        {{ display }}\n      </span>\n      <div v-if=\"props.people.length\" class=\"flex -space-x-1.5\">\n        <template v-for=\"person in visible\" :key=\"person.name\">\n          <img\n            v-if=\"person.src && !failed.has(person.name)\"\n            :src=\"person.src\"\n            :alt=\"person.name\"\n            width=\"24\"\n            height=\"24\"\n            loading=\"lazy\"\n            class=\"size-6 rounded-full bg-muted object-cover ring-2 ring-card\"\n            @error=\"onImageError(person.name)\"\n          />\n          <span\n            v-else\n            :title=\"person.name\"\n            class=\"flex size-6 items-center justify-center rounded-full bg-muted text-[0.625rem] font-medium text-muted-foreground ring-2 ring-card\"\n          >\n            {{ initial(person.name) }}\n          </span>\n        </template>\n        <span\n          v-if=\"hidden\"\n          class=\"flex size-6 items-center justify-center rounded-full bg-muted text-[0.625rem] font-medium text-muted-foreground ring-2 ring-card\"\n        >\n          +{{ hidden }}\n        </span>\n      </div>\n    </div>\n  </article>\n</template>\n",
      "type": "registry:ui",
      "target": "components/ui/kpi-card/KpiCard.vue"
    },
    {
      "path": "registry/new-york/kpi-card/index.ts",
      "content": "export { default as KpiCard } from './KpiCard.vue';\nexport type { KpiAccent, KpiCardProps, KpiPerson } from './types';\n",
      "type": "registry:ui",
      "target": "components/ui/kpi-card/index.ts"
    },
    {
      "path": "registry/new-york/kpi-card/types.ts",
      "content": "export type KpiAccent = 'violet' | 'emerald' | 'blue' | 'rose' | 'amber';\n\nexport interface KpiPerson {\n  /** Shown as initials when `src` is missing or fails to load. */\n  name: string;\n  src?: string;\n}\n\nexport interface KpiAccentStyle {\n  icon: string;\n  glow: string;\n}\n\nexport interface KpiCardProps {\n  label: string;\n  value: number;\n  /** Lucide icon name without the prefix, e.g. `users`. */\n  icon: string;\n  /** Tints the icon box and the corner glow. */\n  accent?: KpiAccent;\n  /** People behind the metric, shown as an overlapping stack. */\n  people?: KpiPerson[];\n  /** People shown before collapsing the rest into `+N`. */\n  maxPeople?: number;\n  /** Shows the actions button; listen to `menu` to open your own menu. */\n  showMenu?: boolean;\n  class?: string;\n}\n",
      "type": "registry:ui",
      "target": "components/ui/kpi-card/types.ts"
    }
  ]
}
