{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "metric-card", "title": "Metric Card", "description": "Flexible metric card with tone, delta, icon, and loading treatments.", "registryDependencies": [ "askyourpolicy/ss-registry/stitch-base", "askyourpolicy/ss-registry/card", "askyourpolicy/ss-registry/skeleton" ], "files": [ { "path": "src/components/ui/metric-card.tsx", "content": "import type { ComponentProps, ReactNode } from \"react\";\n\nimport { Card, CardDescription, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport { Skeleton } from \"@/components/ui/skeleton\";\nimport { cn } from \"@/lib/utils\";\n\ntype MetricCardTone = \"default\" | \"positive\" | \"warning\" | \"destructive\" | \"info\";\n\ntype MetricCardProps = Omit, \"children\"> & {\n delta?: ReactNode;\n description?: ReactNode;\n icon?: ReactNode;\n label: ReactNode;\n loading?: boolean;\n tone?: MetricCardTone;\n value: ReactNode;\n};\n\nconst toneClasses: Record<\n MetricCardTone,\n {\n accent: string;\n delta: string;\n icon: string;\n }\n> = {\n default: {\n accent: \"text-foreground\",\n delta: \"bg-muted text-muted-foreground\",\n icon: \"bg-muted text-muted-foreground\",\n },\n positive: {\n accent: \"text-status-success\",\n delta: \"bg-status-success-muted text-status-success-muted-foreground\",\n icon: \"bg-status-success-muted text-status-success-muted-foreground\",\n },\n warning: {\n accent: \"text-status-warning\",\n delta: \"bg-status-warning-muted text-status-warning-muted-foreground\",\n icon: \"bg-status-warning-muted text-status-warning-muted-foreground\",\n },\n destructive: {\n accent: \"text-destructive\",\n delta: \"bg-destructive/10 text-destructive\",\n icon: \"bg-destructive/10 text-destructive\",\n },\n info: {\n accent: \"text-status-info\",\n delta: \"bg-status-info-muted text-status-info-muted-foreground\",\n icon: \"bg-status-info-muted text-status-info-muted-foreground\",\n },\n};\n\nfunction MetricCard({\n className,\n delta,\n description,\n icon,\n label,\n loading = false,\n tone = \"default\",\n value,\n ...props\n}: MetricCardProps) {\n const styles = toneClasses[tone];\n\n return (\n \n \n
\n {label}\n {icon != null ? (\n \n {icon}\n \n ) : null}\n
\n {/* The header is a grid, so a row that only appears once the data lands takes the whole page\n below it down with it. Both slots stay mounted while loading and their skeletons are\n measured in line heights, which is the one unit that tracks whatever type the slot holds. */}\n \n {loading ? : value}\n \n {delta != null || description != null ? (\n
\n {delta != null ? (\n \n {delta}\n \n ) : null}\n {description != null ? (\n \n {loading ? (\n \n ) : (\n description\n )}\n \n ) : null}\n
\n ) : null}\n
\n \n );\n}\n\nexport { MetricCard, type MetricCardProps, type MetricCardTone };\n", "type": "registry:ui" } ], "type": "registry:ui" }