feat(ui): complete structural rewrite of content components to strict engineering blueprint aesthetic
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 5s
Build & Deploy / 🏗️ Build (push) Failing after 27s
Build & Deploy / 🧪 QA (push) Failing after 1m15s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🩺 Health Check (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s

This commit is contained in:
2026-02-22 02:35:06 +01:00
parent 3eccff42e4
commit 75c61f1436
6 changed files with 574 additions and 62 deletions

View File

@@ -0,0 +1,91 @@
'use client';
import React from 'react';
interface WaterfallEvent {
name: string;
/** Start time in ms */
start: number;
/** Duration in ms */
duration: number;
/** Optional color class (e.g. bg-blue-500) or hex */
color?: string;
description?: string;
}
interface WaterfallChartProps {
title?: string;
events: WaterfallEvent[];
totalDuration?: number;
showShare?: boolean;
}
export const WaterfallChart: React.FC<WaterfallChartProps> = ({ title = 'Resource Waterfall', events, totalDuration }) => {
const maxTime = totalDuration || Math.max(...events.map(e => e.start + e.duration));
const getDefaultColor = (name: string) => {
const n = name.toLowerCase();
if (n.includes('html') || n.includes('document')) return 'bg-slate-900';
if (n.includes('js') || n.includes('script')) return 'bg-amber-400';
if (n.includes('css') || n.includes('style')) return 'bg-blue-400';
if (n.includes('img') || n.includes('image')) return 'bg-emerald-400';
if (n.includes('font')) return 'bg-pink-400';
return 'bg-slate-300';
};
return (
<section className="my-16 not-prose font-sans">
<header className="mb-6 flex justify-between items-end border-b-2 border-slate-900 pb-2">
<h3 className="text-xl md:text-2xl font-bold text-slate-900 tracking-tight m-0">{title}</h3>
<div className="font-mono text-sm text-slate-500">{maxTime}ms</div>
</header>
<div className="relative">
{/* Raw Grid Lines */}
<div className="absolute inset-x-0 top-0 bottom-0 flex justify-between pointer-events-none z-0">
{[0, 0.25, 0.5, 0.75, 1].map((tick) => (
<div key={tick} className="w-px h-full bg-slate-200 flex flex-col justify-between">
<span className="text-[10px] text-slate-400 font-mono -ml-4 -mt-4 bg-white px-1">
{Math.round(maxTime * tick)}
</span>
</div>
))}
</div>
<div className="relative z-10 pt-8 pb-4">
{events.map((event, i) => {
const left = (event.start / maxTime) * 100;
const width = Math.max((event.duration / maxTime) * 100, 0.5);
return (
<div key={i} className="group relative flex items-center h-8 mb-2">
<div className="w-32 md:w-48 shrink-0 pr-4 flex justify-between items-center bg-white z-20">
<span className="font-bold text-slate-900 text-[11px] md:text-xs truncate uppercase tracking-tight">{event.name}</span>
<span className="font-mono text-slate-400 text-[10px]">{event.duration}ms</span>
</div>
<div className="flex-1 relative h-full flex items-center">
<div
className={`h-[4px] relative ${event.color || getDefaultColor(event.name)}`}
style={{
marginLeft: `${left}%`,
width: `${width}%`,
minWidth: '2px'
}}
>
<div className="absolute top-1/2 left-0 w-2 h-2 -translate-y-1/2 -translate-x-1/2 rounded-full border border-white" style={{ backgroundColor: 'inherit' }} />
</div>
{event.description && (
<div className="absolute left-0 -top-6 opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap bg-slate-900 text-white text-[10px] font-mono px-2 py-1 rounded-sm pointer-events-none z-30 shadow-lg">
{event.description}
</div>
)}
</div>
</div>
);
})}
</div>
</div>
</section>
);
};