Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
98 lines
2.1 KiB
TypeScript
98 lines
2.1 KiB
TypeScript
import { useState } from "react";
|
|
import type { ReactElement } from "react";
|
|
|
|
export interface MetricCell {
|
|
label: string;
|
|
value: string;
|
|
color: string; // CSS color, e.g., "var(--ms-blue-400)"
|
|
trend?: {
|
|
direction: "up" | "down" | "neutral";
|
|
text: string;
|
|
};
|
|
}
|
|
|
|
export interface MetricsStripProps {
|
|
cells: MetricCell[];
|
|
className?: string;
|
|
}
|
|
|
|
function MetricCellItem({ cell }: { cell: MetricCell }): ReactElement {
|
|
const [hovered, setHovered] = useState(false);
|
|
|
|
const trendColor =
|
|
cell.trend?.direction === "up"
|
|
? "var(--success)"
|
|
: cell.trend?.direction === "down"
|
|
? "var(--danger)"
|
|
: "var(--muted)";
|
|
|
|
return (
|
|
<div
|
|
className="metric-cell"
|
|
onMouseEnter={(): void => {
|
|
setHovered(true);
|
|
}}
|
|
onMouseLeave={(): void => {
|
|
setHovered(false);
|
|
}}
|
|
style={{
|
|
padding: "14px 16px",
|
|
background: hovered ? "var(--surface-2)" : "var(--surface)",
|
|
borderTop: `2px solid ${cell.color}`,
|
|
transition: "background 0.15s ease",
|
|
}}
|
|
>
|
|
<div
|
|
style={{
|
|
fontSize: "1.4rem",
|
|
fontWeight: 800,
|
|
fontFamily: "var(--mono)",
|
|
lineHeight: 1.1,
|
|
color: cell.color,
|
|
}}
|
|
>
|
|
{cell.value}
|
|
</div>
|
|
<div
|
|
style={{
|
|
fontSize: "0.72rem",
|
|
color: "var(--muted)",
|
|
marginTop: 3,
|
|
whiteSpace: "nowrap",
|
|
}}
|
|
>
|
|
{cell.label}
|
|
</div>
|
|
{cell.trend && (
|
|
<div
|
|
style={{
|
|
fontSize: "0.68rem",
|
|
fontFamily: "var(--mono)",
|
|
marginTop: 4,
|
|
color: trendColor,
|
|
}}
|
|
>
|
|
{cell.trend.text}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function MetricsStrip({ cells, className = "" }: MetricsStripProps): ReactElement {
|
|
return (
|
|
<div
|
|
className={`metrics-strip ${className}`.trim()}
|
|
style={
|
|
{
|
|
"--ms-cols": String(cells.length),
|
|
} as React.CSSProperties
|
|
}
|
|
>
|
|
{cells.map((cell) => (
|
|
<MetricCellItem key={cell.label} cell={cell} />
|
|
))}
|
|
</div>
|
|
);
|
|
}
|