spb/datacenterindex
Public
HTML 53.9%
TypeScript 44.5%
JavaScript 0.6%
SQL 0.5%
1import type { ReactNode } from "react";2import { cn } from "./cn";34export interface MetricProps {5 value: ReactNode;6 unit?: ReactNode;7 label: ReactNode;8 delta?: number | null;9 deltaLabel?: string;10 note?: ReactNode;11 size?: "sm" | "md" | "lg";12 className?: string;13 /** render as skeleton */14 loading?: boolean;15 href?: string;16}1718/** Big figure in mono/tabular, tight tracking; unit in small caps beside; label under. */19export function Metric({ value, unit, label, delta, deltaLabel, note, size = "md", className, loading }: MetricProps) {20 const vs = size === "lg" ? "text-[30px] sm:text-[36px]" : size === "sm" ? "text-[18px]" : "text-[24px] sm:text-[28px]";21 return (22 <div className={cn("flex min-w-0 flex-col gap-0.5", className)}>23 <div className={cn("flex items-baseline gap-1 leading-none", vs)}>24 {loading ? <span className="inline-block h-[0.9em] w-[3.2em] animate-pulse-soft rounded-xs bg-surface-3" /> : <span className="figure font-medium tracking-[-0.03em] text-ink">{value}</span>}25 {unit && !loading && <span className="text-[0.5em] font-medium text-ink-3">{unit}</span>}26 {delta !== undefined && delta !== null && !loading && (27 <span className={cn("figure ml-1 text-[0.45em] font-medium", delta > 0 ? "text-ok" : delta < 0 ? "text-danger" : "text-ink-3")} title={deltaLabel}>28 {delta > 0 ? "+" : ""}29 {delta}30 </span>31 )}32 </div>33 <div className="label-caps truncate">{label}</div>34 {note && <div className="text-[11px] leading-tight text-ink-3">{note}</div>}35 </div>36 );37}3839/**40 * Metric strip. Under 640 px: a 2-column grid so every figure is visible without scrolling (an odd41 * last cell spans both columns); ≥ 640 px: one row of `cols` columns. Hairline separators, no cards.42 */43export function MetricRow({ children, className, cols }: { children: ReactNode; className?: string; cols?: number }) {44 return (45 <div46 className={cn(47 "grid grid-cols-2 border-y border-hair [&>*]:min-w-0",48 // phones: vertical hairline between the two columns, horizontal hairline between rows, odd last cell full width49 "max-sm:[&>*:nth-child(even)]:border-l max-sm:[&>*:nth-child(n+3)]:border-t max-sm:[&>*:nth-child(odd)]:pl-0 max-sm:[&>*:last-child:nth-child(odd)]:col-span-2 max-sm:[&>*]:border-hair",50 // ≥ sm: one row, separators between cells51 "sm:[grid-template-columns:var(--metric-cols)] sm:[&>*+*]:border-l sm:[&>*]:border-hair",52 className,53 )}54 style={{ ["--metric-cols" as string]: `repeat(${cols ?? 4}, minmax(0, 1fr))` }}55 >56 {children}57 </div>58 );59}6061/** Wrapper for one metric cell inside MetricRow. */62export function MetricCell({ children, className }: { children: ReactNode; className?: string }) {63 return <div className={cn("min-w-0 px-3 py-3 sm:px-4 sm:first:pl-0", className)}>{children}</div>;64}65