chore(build): refactor codebase for production
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState } from "react";
|
||||
|
||||
const BoxPlotComparisonWidget: React.FC = () => {
|
||||
// Box Plot A is fixed
|
||||
@ -9,16 +9,24 @@ const BoxPlotComparisonWidget: React.FC = () => {
|
||||
const [spread, setSpread] = useState(1); // Scale spread
|
||||
|
||||
const statsB = {
|
||||
min: 10 + shift - (5 * (spread - 1)), // Just approximating visual expansion
|
||||
q1: 16 + shift - (2 * (spread - 1)),
|
||||
min: 10 + shift - 5 * (spread - 1), // Just approximating visual expansion
|
||||
q1: 16 + shift - 2 * (spread - 1),
|
||||
med: 26 + shift,
|
||||
q3: 34 + shift + (2 * (spread - 1)),
|
||||
max: 38 + shift + (4 * (spread - 1))
|
||||
q3: 34 + shift + 2 * (spread - 1),
|
||||
max: 38 + shift + 4 * (spread - 1),
|
||||
};
|
||||
|
||||
const scaleX = (val: number) => (val / 60) * 100; // 0 to 60 range mapping to %
|
||||
|
||||
const BoxPlot = ({ stats, color, label }: { stats: any, color: string, label: string }) => {
|
||||
const BoxPlot = ({
|
||||
stats,
|
||||
color,
|
||||
label,
|
||||
}: {
|
||||
stats: any;
|
||||
color: string;
|
||||
label: string;
|
||||
}) => {
|
||||
const leftW = scaleX(stats.min);
|
||||
const rightW = scaleX(stats.max);
|
||||
const boxL = scaleX(stats.q1);
|
||||
@ -27,85 +35,151 @@ const BoxPlotComparisonWidget: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div className="relative h-16 w-full mb-8 group">
|
||||
<div className="absolute left-0 top-0 text-xs font-bold text-slate-400">{label}</div>
|
||||
|
||||
{/* Main Line (Whisker to Whisker) */}
|
||||
<div className="absolute top-1/2 left-0 h-0.5 bg-slate-300 -translate-y-1/2"
|
||||
style={{ left: `${leftW}%`, width: `${rightW - leftW}%` }} />
|
||||
|
||||
{/* Whiskers */}
|
||||
<div className="absolute top-1/2 h-3 w-0.5 bg-slate-400 -translate-y-1/2" style={{ left: `${leftW}%` }} />
|
||||
<div className="absolute top-1/2 h-3 w-0.5 bg-slate-400 -translate-y-1/2" style={{ left: `${rightW}%` }} />
|
||||
<div className="absolute left-0 top-0 text-xs font-bold text-slate-400">
|
||||
{label}
|
||||
</div>
|
||||
|
||||
{/* Box */}
|
||||
<div className={`absolute top-1/2 -translate-y-1/2 h-8 border-2 ${color} bg-white opacity-90`}
|
||||
style={{ left: `${boxL}%`, width: `${boxR - boxL}%`, borderColor: 'currentColor' }}>
|
||||
</div>
|
||||
{/* Main Line (Whisker to Whisker) */}
|
||||
<div
|
||||
className="absolute top-1/2 left-0 h-0.5 bg-slate-300 -translate-y-1/2"
|
||||
style={{ left: `${leftW}%`, width: `${rightW - leftW}%` }}
|
||||
/>
|
||||
|
||||
{/* Median Line */}
|
||||
<div className="absolute top-1/2 h-8 w-1 bg-slate-800 -translate-y-1/2" style={{ left: `${med}%` }} />
|
||||
{/* Whiskers */}
|
||||
<div
|
||||
className="absolute top-1/2 h-3 w-0.5 bg-slate-400 -translate-y-1/2"
|
||||
style={{ left: `${leftW}%` }}
|
||||
/>
|
||||
<div
|
||||
className="absolute top-1/2 h-3 w-0.5 bg-slate-400 -translate-y-1/2"
|
||||
style={{ left: `${rightW}%` }}
|
||||
/>
|
||||
|
||||
{/* Labels on Hover */}
|
||||
<div className="opacity-0 group-hover:opacity-100 transition-opacity absolute top-10 left-0 w-full text-center text-xs font-mono text-slate-500 pointer-events-none">
|
||||
Min:{stats.min.toFixed(0)} Q1:{stats.q1.toFixed(0)} Med:{stats.med.toFixed(0)} Q3:{stats.q3.toFixed(0)} Max:{stats.max.toFixed(0)}
|
||||
</div>
|
||||
{/* Box */}
|
||||
<div
|
||||
className={`absolute top-1/2 -translate-y-1/2 h-8 border-2 ${color} bg-white opacity-90`}
|
||||
style={{
|
||||
left: `${boxL}%`,
|
||||
width: `${boxR - boxL}%`,
|
||||
borderColor: "currentColor",
|
||||
}}
|
||||
></div>
|
||||
|
||||
{/* Median Line */}
|
||||
<div
|
||||
className="absolute top-1/2 h-8 w-1 bg-slate-800 -translate-y-1/2"
|
||||
style={{ left: `${med}%` }}
|
||||
/>
|
||||
|
||||
{/* Labels on Hover */}
|
||||
<div className="opacity-0 group-hover:opacity-100 transition-opacity absolute top-10 left-0 w-full text-center text-xs font-mono text-slate-500 pointer-events-none">
|
||||
Min:{stats.min.toFixed(0)} Q1:{stats.q1.toFixed(0)} Med:
|
||||
{stats.med.toFixed(0)} Q3:{stats.q3.toFixed(0)} Max:
|
||||
{stats.max.toFixed(0)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const iqrA = statsA.q3 - statsA.q1;
|
||||
const iqrB = statsB.q3 - statsB.q1;
|
||||
const rangeA = statsA.max - statsA.min;
|
||||
const rangeB = statsB.max - statsB.min;
|
||||
|
||||
return (
|
||||
<div className="bg-white p-6 rounded-xl shadow-lg border border-slate-200">
|
||||
<div className="mb-6 relative h-48 border-b border-slate-200">
|
||||
<BoxPlot stats={statsA} color="text-indigo-500" label="Dataset A (Fixed)" />
|
||||
<BoxPlot stats={statsB} color="text-rose-500" label="Dataset B (Adjustable)" />
|
||||
|
||||
{/* Axis */}
|
||||
<div className="absolute bottom-0 w-full flex justify-between text-xs text-slate-400 font-mono px-2">
|
||||
<span>0</span><span>10</span><span>20</span><span>30</span><span>40</span><span>50</span><span>60</span>
|
||||
</div>
|
||||
<div className="mb-6 relative h-48 border-b border-slate-200">
|
||||
<BoxPlot
|
||||
stats={statsA}
|
||||
color="text-indigo-500"
|
||||
label="Dataset A (Fixed)"
|
||||
/>
|
||||
<BoxPlot
|
||||
stats={statsB}
|
||||
color="text-rose-500"
|
||||
label="Dataset B (Adjustable)"
|
||||
/>
|
||||
|
||||
{/* Axis */}
|
||||
<div className="absolute bottom-0 w-full flex justify-between text-xs text-slate-400 font-mono px-2">
|
||||
<span>0</span>
|
||||
<span>10</span>
|
||||
<span>20</span>
|
||||
<span>30</span>
|
||||
<span>40</span>
|
||||
<span>50</span>
|
||||
<span>60</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col md:flex-row gap-8">
|
||||
<div className="w-full md:w-1/3 space-y-6">
|
||||
<div>
|
||||
<label className="text-xs font-bold text-slate-500 uppercase">
|
||||
Shift Center (Median B)
|
||||
</label>
|
||||
<input
|
||||
type="range"
|
||||
min="-15"
|
||||
max="15"
|
||||
value={shift}
|
||||
onChange={(e) => setShift(parseInt(e.target.value))}
|
||||
className="w-full h-2 bg-rose-100 rounded-lg appearance-none cursor-pointer accent-rose-600"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs font-bold text-slate-500 uppercase">
|
||||
Adjust Spread (IQR B)
|
||||
</label>
|
||||
<input
|
||||
type="range"
|
||||
min="0.5"
|
||||
max="2"
|
||||
step="0.1"
|
||||
value={spread}
|
||||
onChange={(e) => setSpread(parseFloat(e.target.value))}
|
||||
className="w-full h-2 bg-rose-100 rounded-lg appearance-none cursor-pointer accent-rose-600"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col md:flex-row gap-8">
|
||||
<div className="w-full md:w-1/3 space-y-6">
|
||||
<div>
|
||||
<label className="text-xs font-bold text-slate-500 uppercase">Shift Center (Median B)</label>
|
||||
<input type="range" min="-15" max="15" value={shift} onChange={e => setShift(parseInt(e.target.value))} className="w-full h-2 bg-rose-100 rounded-lg appearance-none cursor-pointer accent-rose-600"/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs font-bold text-slate-500 uppercase">Adjust Spread (IQR B)</label>
|
||||
<input type="range" min="0.5" max="2" step="0.1" value={spread} onChange={e => setSpread(parseFloat(e.target.value))} className="w-full h-2 bg-rose-100 rounded-lg appearance-none cursor-pointer accent-rose-600"/>
|
||||
</div>
|
||||
<div className="flex-1 grid grid-cols-2 gap-4">
|
||||
<div className="bg-slate-50 p-3 rounded border border-slate-200">
|
||||
<div className="text-xs font-bold text-slate-400 uppercase">
|
||||
Median Comparison
|
||||
</div>
|
||||
|
||||
<div className="flex-1 grid grid-cols-2 gap-4">
|
||||
<div className="bg-slate-50 p-3 rounded border border-slate-200">
|
||||
<div className="text-xs font-bold text-slate-400 uppercase">Median Comparison</div>
|
||||
<div className="flex justify-between items-center mt-1">
|
||||
<span className="text-indigo-600 font-bold">{statsA.med}</span>
|
||||
<span className="text-slate-400">{statsA.med > statsB.med ? '>' : statsA.med < statsB.med ? '<' : '='}</span>
|
||||
<span className="text-rose-600 font-bold">{statsB.med}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-slate-50 p-3 rounded border border-slate-200">
|
||||
<div className="text-xs font-bold text-slate-400 uppercase">IQR Comparison</div>
|
||||
<div className="flex justify-between items-center mt-1">
|
||||
<span className="text-indigo-600 font-bold">{iqrA.toFixed(0)}</span>
|
||||
<span className="text-slate-400">{iqrA > iqrB ? '>' : iqrA < iqrB ? '<' : '='}</span>
|
||||
<span className="text-rose-600 font-bold">{iqrB.toFixed(0)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-2 text-xs text-slate-500 text-center">
|
||||
The box length represents the IQR (Middle 50%). The whiskers represent the full Range.
|
||||
</div>
|
||||
<div className="flex justify-between items-center mt-1">
|
||||
<span className="text-indigo-600 font-bold">{statsA.med}</span>
|
||||
<span className="text-slate-400">
|
||||
{statsA.med > statsB.med
|
||||
? ">"
|
||||
: statsA.med < statsB.med
|
||||
? "<"
|
||||
: "="}
|
||||
</span>
|
||||
<span className="text-rose-600 font-bold">{statsB.med}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-slate-50 p-3 rounded border border-slate-200">
|
||||
<div className="text-xs font-bold text-slate-400 uppercase">
|
||||
IQR Comparison
|
||||
</div>
|
||||
<div className="flex justify-between items-center mt-1">
|
||||
<span className="text-indigo-600 font-bold">
|
||||
{iqrA.toFixed(0)}
|
||||
</span>
|
||||
<span className="text-slate-400">
|
||||
{iqrA > iqrB ? ">" : iqrA < iqrB ? "<" : "="}
|
||||
</span>
|
||||
<span className="text-rose-600 font-bold">{iqrB.toFixed(0)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-2 text-xs text-slate-500 text-center">
|
||||
The box length represents the IQR (Middle 50%). The whiskers
|
||||
represent the full Range.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BoxPlotComparisonWidget;
|
||||
export default BoxPlotComparisonWidget;
|
||||
|
||||
Reference in New Issue
Block a user