File size: 1,724 Bytes
7c5ed63 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | import { useState, type ReactNode } from 'react';
interface SizePreviewProps {
label?: string;
/** Byte/char count for the dim label */
sizeBytes: number;
/** Preview text shown when collapsed (first ~200 chars) */
preview?: string;
/** Full content renderer */
children: ReactNode;
/** Start expanded if true */
defaultOpen?: boolean;
}
export function SizePreview({
label = 'payload',
sizeBytes,
preview,
children,
defaultOpen = false,
}: SizePreviewProps) {
const [open, setOpen] = useState(defaultOpen);
const size = formatBytes(sizeBytes);
return (
<div className="my-1 border border-border bg-surface-0">
<button
onClick={() =>{ setOpen((v) => !v); }}
className="flex w-full items-center justify-between gap-2 px-2 py-1 font-mono text-[11px] text-fg-2 hover:bg-surface-2 hover:text-fg-1"
>
<span className="flex items-center gap-2">
<span className="text-fg-3">{open ? '▾' : '▸'}</span>
<span className="uppercase tracking-[0.08em]">{label}</span>
<span className="text-fg-3 tabular">{size}</span>
</span>
{!open && preview ? (
<span className="truncate text-fg-3 font-mono text-[11px]">
{preview.slice(0, 120)}
{preview.length > 120 ? '…' : ''}
</span>
) : null}
</button>
{open ? (
<div className="border-t border-border px-2 py-1 font-mono text-[12px]">
{children}
</div>
) : null}
</div>
);
}
export function formatBytes(n: number): string {
if (n < 1024) return `${n}B`;
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)}KB`;
return `${(n / 1024 / 1024).toFixed(2)}MB`;
}
|