import type { DsProps } from './props'; import * as React from 'react'; import { ArrowDownRight, ArrowUpRight, Minus } from 'lucide-react'; import { cn } from '@/lib/utils'; /** * MetricStrip — modern executive operational metric card strip. * * `items: [{ label, value, delta?, deltaLabel?, invertTrend?, sub?, icon?, tone? }]` */ const TONE = { default: 'text-ink-1', brand: 'text-blue-600 dark:text-blue-400', success: 'text-emerald-600 dark:text-emerald-400', warning: 'text-amber-600 dark:text-amber-400', risk: 'text-red-600 dark:text-red-400', }; const ICON_BG = { default: 'bg-surface-subtle text-ink-3', brand: 'bg-blue-50 text-blue-600 dark:bg-blue-950/60 dark:text-blue-400', success: 'bg-emerald-50 text-emerald-600 dark:bg-emerald-950/60 dark:text-emerald-400', warning: 'bg-amber-50 text-amber-600 dark:bg-amber-950/60 dark:text-amber-400', risk: 'bg-red-50 text-red-600 dark:bg-red-950/60 dark:text-red-400', }; const ACCENT_BAR = { default: 'from-gray-400 to-gray-600', brand: 'from-blue-500 to-indigo-500', success: 'from-emerald-500 to-teal-500', warning: 'from-amber-500 to-orange-500', risk: 'from-red-500 to-rose-500', }; function Delta({ delta, label, invert }) { if (typeof delta !== 'number') return null; const flat = delta === 0; const good = invert ? delta < 0 : delta > 0; const Icon = flat ? Minus : delta > 0 ? ArrowUpRight : ArrowDownRight; return ( ); } /** @param {any} props */ export function MetricStrip({ items = [], columns, className, loading = false }: DsProps) { const count = columns ?? Math.min(items.length, 6); const gridCols = { 2: 'sm:grid-cols-2', 3: 'sm:grid-cols-3', 4: 'grid-cols-2 sm:grid-cols-4', 5: 'grid-cols-2 sm:grid-cols-3 xl:grid-cols-5', 6: 'grid-cols-2 sm:grid-cols-3 xl:grid-cols-6', }[count] || 'grid-cols-2 sm:grid-cols-4'; return (
{items.map((item) => { const IconComponent = item.icon; const toneKey = item.tone || 'default'; return (
{/* Header: Label & Icon */}
{/* Wraps on a phone, clips on everything wider. Two of these sit side by side at 320px, which leaves about 110px for the label — enough for "OPEN POSITIONS" over two lines and not enough for it on one, so a flat `truncate` turned every metric on the Control Center into "OPEN POSI…". From `sm` the strip is three or six across with room for the label on one line, which is where clipping was the intent. */}

{item.label}

{IconComponent && (
)}
{/* Metric Main Display & Delta */} {loading ? (
) : (
{item.value}
)}
{/* Sub-label & Accent bar */}
{item.sub && ( {item.sub} )}
); })}
); }