Files
edbridge-scholars/src/components/lessons/SimilarityWidget.tsx
2026-03-12 02:39:34 +06:00

202 lines
5.5 KiB
TypeScript

import React, { useState, useRef } from "react";
const SimilarityWidget: React.FC = () => {
const [ratio, setRatio] = useState(0.5); // Position of D along AB (0 to 1)
const isDragging = useRef(false);
const svgRef = useRef<SVGSVGElement>(null);
// Triangle Vertices
const A = { x: 200, y: 50 };
const B = { x: 50, y: 300 };
const C = { x: 350, y: 300 };
// Calculate D and E based on ratio
const D = {
x: A.x + (B.x - A.x) * ratio,
y: A.y + (B.y - A.y) * ratio,
};
const E = {
x: A.x + (C.x - A.x) * ratio,
y: A.y + (C.y - A.y) * ratio,
};
const handleInteraction = (clientY: number) => {
if (!svgRef.current) return;
const rect = svgRef.current.getBoundingClientRect();
const y = clientY - rect.top;
// Clamp y between A.y and B.y
const clampedY = Math.max(A.y, Math.min(B.y, y));
// Calculate new ratio
const newRatio = (clampedY - A.y) / (B.y - A.y);
setRatio(Math.max(0.1, Math.min(0.9, newRatio))); // clamp to avoid degenerate
};
const handleMouseDown = (e: React.MouseEvent) => {
isDragging.current = true;
handleInteraction(e.clientY);
};
const handleMouseMove = (e: React.MouseEvent) => {
if (isDragging.current) {
handleInteraction(e.clientY);
}
};
return (
<div className="bg-white p-6 rounded-xl shadow-lg border border-slate-200 flex flex-col md:flex-row items-center gap-8">
<svg
ref={svgRef}
width="400"
height="350"
className="select-none cursor-ns-resize"
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={() => (isDragging.current = false)}
onMouseLeave={() => (isDragging.current = false)}
>
{/* Main Triangle */}
<path
d={`M ${A.x} ${A.y} L ${B.x} ${B.y} L ${C.x} ${C.y} Z`}
fill="none"
stroke="#e2e8f0"
strokeWidth="2"
/>
{/* Filled Top Triangle (Similar) */}
<path
d={`M ${A.x} ${A.y} L ${D.x} ${D.y} L ${E.x} ${E.y} Z`}
fill="rgba(244, 63, 94, 0.1)"
stroke="none"
/>
{/* Parallel Line DE */}
<line
x1={D.x}
y1={D.y}
x2={E.x}
y2={E.y}
stroke="#e11d48"
strokeWidth="3"
/>
{/* Labels */}
<text
x={A.x}
y={A.y - 10}
textAnchor="middle"
fontWeight="bold"
fill="#64748b"
>
A
</text>
<text
x={B.x - 10}
y={B.y}
textAnchor="end"
fontWeight="bold"
fill="#64748b"
>
B
</text>
<text
x={C.x + 10}
y={C.y}
textAnchor="start"
fontWeight="bold"
fill="#64748b"
>
C
</text>
<text
x={D.x - 10}
y={D.y}
textAnchor="end"
fontWeight="bold"
fill="#e11d48"
>
D
</text>
<text
x={E.x + 10}
y={E.y}
textAnchor="start"
fontWeight="bold"
fill="#e11d48"
>
E
</text>
{/* Drag Handle */}
<circle
cx={D.x}
cy={D.y}
r="6"
fill="#e11d48"
stroke="white"
strokeWidth="2"
/>
<circle
cx={E.x}
cy={E.y}
r="6"
fill="#e11d48"
stroke="white"
strokeWidth="2"
/>
</svg>
<div className="flex-1 w-full">
<h3 className="text-lg font-bold text-slate-800 mb-4">
Triangle Proportionality
</h3>
<p className="text-sm text-slate-500 mb-6">
Drag the red line. Because DE || BC, the small triangle is similar to
the large triangle.
</p>
<div className="space-y-4">
<div className="bg-slate-50 p-4 rounded-lg border-l-4 border-rose-500">
<p className="text-xs font-bold text-slate-400 uppercase mb-1">
Scale Factor
</p>
<p className="font-mono text-xl text-rose-700">
{ratio.toFixed(2)}
</p>
</div>
<div className="bg-white border border-slate-200 p-4 rounded-lg shadow-sm">
<p className="font-mono text-sm mb-2 text-slate-600">
Corresponding Sides Ratio:
</p>
<div className="flex items-center justify-between font-mono font-bold text-lg">
<div className="text-rose-600">AD / AB</div>
<div className="text-slate-400">=</div>
<div className="text-rose-600">AE / AC</div>
<div className="text-slate-400">=</div>
<div className="text-rose-600">{ratio.toFixed(2)}</div>
</div>
</div>
<div className="bg-white border border-slate-200 p-4 rounded-lg shadow-sm">
<p className="font-mono text-sm mb-2 text-slate-600">
Area Ratio (k²):
</p>
<div className="flex items-center justify-between font-mono font-bold text-lg">
<div className="text-rose-600">Area(ADE)</div>
<div className="text-slate-400">/</div>
<div className="text-slate-600">Area(ABC)</div>
<div className="text-slate-400">=</div>
<div className="text-rose-600">{(ratio * ratio).toFixed(2)}</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default SimilarityWidget;