feat(lessons): add lessons from client db
This commit is contained in:
227
src/components/lessons/CompositeAreaWidget.tsx
Normal file
227
src/components/lessons/CompositeAreaWidget.tsx
Normal file
@ -0,0 +1,227 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
const CompositeAreaWidget: React.FC = () => {
|
||||
const [mode, setMode] = useState<"add" | "subtract">("add");
|
||||
const [width, setWidth] = useState(10);
|
||||
const [height, setHeight] = useState(6);
|
||||
|
||||
// Scale for display
|
||||
const scale = 20;
|
||||
const displayW = width * scale;
|
||||
const displayH = height * scale;
|
||||
const radius = width / 2; // Semicircle on top (width is diameter)
|
||||
const displayR = radius * scale;
|
||||
|
||||
// Areas
|
||||
const rectArea = width * height;
|
||||
const semiArea = 0.5 * Math.PI * radius * radius;
|
||||
const totalArea = mode === "add" ? rectArea + semiArea : rectArea - semiArea;
|
||||
|
||||
return (
|
||||
<div className="bg-white p-6 rounded-xl shadow-lg border border-slate-200 flex flex-col items-center">
|
||||
<div className="flex gap-4 mb-8">
|
||||
<button
|
||||
onClick={() => setMode("add")}
|
||||
className={`px-4 py-2 rounded-full font-bold text-sm transition-all ${
|
||||
mode === "add"
|
||||
? "bg-orange-600 text-white shadow-md transform scale-105"
|
||||
: "bg-slate-100 text-slate-500 hover:bg-slate-200"
|
||||
}`}
|
||||
>
|
||||
Add Semicircle (Composite)
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setMode("subtract")}
|
||||
className={`px-4 py-2 rounded-full font-bold text-sm transition-all ${
|
||||
mode === "subtract"
|
||||
? "bg-rose-600 text-white shadow-md transform scale-105"
|
||||
: "bg-slate-100 text-slate-500 hover:bg-slate-200"
|
||||
}`}
|
||||
>
|
||||
Subtract Semicircle (Hole)
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="relative mb-8 flex items-end justify-center"
|
||||
style={{ height: "300px", width: "100%" }}
|
||||
>
|
||||
<svg
|
||||
width="400"
|
||||
height="300"
|
||||
className="overflow-visible transition-all duration-500"
|
||||
>
|
||||
<defs>
|
||||
<pattern
|
||||
id="grid"
|
||||
width="20"
|
||||
height="20"
|
||||
patternUnits="userSpaceOnUse"
|
||||
>
|
||||
<path
|
||||
d="M 20 0 L 0 0 0 20"
|
||||
fill="none"
|
||||
stroke="#f1f5f9"
|
||||
strokeWidth="1"
|
||||
/>
|
||||
</pattern>
|
||||
</defs>
|
||||
|
||||
<g transform={`translate(${200 - displayW / 2}, ${250})`}>
|
||||
{/* Rectangle */}
|
||||
<rect
|
||||
x="0"
|
||||
y={-displayH}
|
||||
width={displayW}
|
||||
height={displayH}
|
||||
fill={
|
||||
mode === "add"
|
||||
? "rgba(255,237,213, 1)"
|
||||
: "rgba(254, 226, 226, 1)"
|
||||
}
|
||||
stroke={mode === "add" ? "#f97316" : "#e11d48"}
|
||||
strokeWidth="2"
|
||||
/>
|
||||
|
||||
{mode === "add" && (
|
||||
// Semicircle on TOP
|
||||
<path
|
||||
d={`M 0 ${-displayH} A ${displayR} ${displayR} 0 0 1 ${displayW} ${-displayH} Z`}
|
||||
fill="rgba(255,237,213, 1)"
|
||||
stroke="#f97316"
|
||||
strokeWidth="2"
|
||||
transform={`translate(0,0)`}
|
||||
/>
|
||||
)}
|
||||
|
||||
{mode === "add" && (
|
||||
// Hide the seam line
|
||||
<line
|
||||
x1="2"
|
||||
y1={-displayH}
|
||||
x2={displayW - 2}
|
||||
y2={-displayH}
|
||||
stroke="rgba(255,237,213, 1)"
|
||||
strokeWidth="4"
|
||||
/>
|
||||
)}
|
||||
|
||||
{mode === "subtract" && (
|
||||
// Semicircle Cutting INTO top
|
||||
<path
|
||||
d={`M 0 ${-displayH} A ${displayR} ${displayR} 0 0 0 ${displayW} ${-displayH} Z`}
|
||||
fill="white"
|
||||
stroke="#e11d48"
|
||||
strokeWidth="2"
|
||||
strokeDasharray="4,4"
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Labels */}
|
||||
<text
|
||||
x={displayW / 2}
|
||||
y={-displayH / 2}
|
||||
textAnchor="middle"
|
||||
className="font-bold fill-slate-500 opacity-50 text-xl"
|
||||
>
|
||||
Rect
|
||||
</text>
|
||||
|
||||
{mode === "add" && (
|
||||
<text
|
||||
x={displayW / 2}
|
||||
y={-displayH - displayR / 2}
|
||||
textAnchor="middle"
|
||||
className="font-bold fill-orange-600 text-sm"
|
||||
>
|
||||
Semicircle
|
||||
</text>
|
||||
)}
|
||||
{mode === "subtract" && (
|
||||
<text
|
||||
x={displayW / 2}
|
||||
y={-displayH + displayR / 2}
|
||||
textAnchor="middle"
|
||||
className="font-bold fill-rose-600 text-sm"
|
||||
>
|
||||
Hole
|
||||
</text>
|
||||
)}
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-8 w-full max-w-2xl">
|
||||
<div>
|
||||
<label className="text-xs font-bold text-slate-400 uppercase">
|
||||
Width (Diameter)
|
||||
</label>
|
||||
<input
|
||||
type="range"
|
||||
min="4"
|
||||
max="14"
|
||||
step="2"
|
||||
value={width}
|
||||
onChange={(e) => setWidth(parseInt(e.target.value))}
|
||||
className="w-full h-2 bg-slate-200 rounded-lg appearance-none cursor-pointer accent-slate-600 mt-2"
|
||||
/>
|
||||
<div className="text-right font-mono font-bold text-slate-700">
|
||||
{width}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs font-bold text-slate-400 uppercase">
|
||||
Height
|
||||
</label>
|
||||
<input
|
||||
type="range"
|
||||
min="4"
|
||||
max="12"
|
||||
step="1"
|
||||
value={height}
|
||||
onChange={(e) => setHeight(parseInt(e.target.value))}
|
||||
className="w-full h-2 bg-slate-200 rounded-lg appearance-none cursor-pointer accent-slate-600 mt-2"
|
||||
/>
|
||||
<div className="text-right font-mono font-bold text-slate-700">
|
||||
{height}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 p-4 bg-slate-50 rounded-xl border border-slate-200 w-full max-w-2xl">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<span className="font-bold text-slate-700">Calculation</span>
|
||||
<span
|
||||
className={`px-2 py-1 rounded text-xs font-bold uppercase ${mode === "add" ? "bg-orange-100 text-orange-800" : "bg-rose-100 text-rose-800"}`}
|
||||
>
|
||||
{mode === "add" ? "Sum" : "Difference"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="font-mono text-lg space-y-2">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-slate-500">Rectangle Area (w×h)</span>
|
||||
<span>
|
||||
{width} × {height} = <strong>{rectArea}</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-slate-500">Semicircle Area (½πr²)</span>
|
||||
<span>
|
||||
½ × π × {radius}² ≈ <strong>{semiArea.toFixed(1)}</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div className="border-t border-slate-300 my-2 pt-2 flex justify-between font-bold text-xl">
|
||||
<span>Total Area</span>
|
||||
<span
|
||||
className={mode === "add" ? "text-orange-600" : "text-rose-600"}
|
||||
>
|
||||
{totalArea.toFixed(1)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CompositeAreaWidget;
|
||||
Reference in New Issue
Block a user