93 lines
3.6 KiB
JavaScript
93 lines
3.6 KiB
JavaScript
import { useDroppable } from '@dnd-kit/core'
|
||
import { SortableContext, rectSortingStrategy } from '@dnd-kit/sortable'
|
||
import { EQUATION_PRESETS } from '../engine/equations.js'
|
||
import { WorkspaceCard } from './VariableCard.jsx'
|
||
|
||
export function Workspace({
|
||
workspaceVarIds,
|
||
userValues,
|
||
solved,
|
||
onRemove,
|
||
onValueChange,
|
||
onAddPreset,
|
||
onClear,
|
||
getUnit,
|
||
setUnit,
|
||
sciNotation,
|
||
areaRatioBranch,
|
||
onToggleAreaRatioBranch,
|
||
}) {
|
||
const { setNodeRef, isOver } = useDroppable({ id: 'workspace' })
|
||
|
||
return (
|
||
<main className="flex flex-col flex-1 min-w-0 h-full overflow-hidden">
|
||
{/* Equation preset bar */}
|
||
<div className="flex flex-wrap gap-2 px-4 py-3 border-b border-slate-700 bg-slate-900 shrink-0">
|
||
<span className="text-xs text-slate-500 self-center mr-1 uppercase tracking-wide">Presets:</span>
|
||
{EQUATION_PRESETS.map(preset => (
|
||
<button
|
||
key={preset.id}
|
||
onClick={() => onAddPreset(preset.id)}
|
||
className="px-3 py-1 rounded-full text-xs font-medium bg-slate-700 text-slate-300 hover:bg-blue-700 hover:text-white border border-slate-600 hover:border-blue-500 transition-colors"
|
||
>
|
||
{preset.label}
|
||
</button>
|
||
))}
|
||
{workspaceVarIds.length > 0 && (
|
||
<button
|
||
onClick={onClear}
|
||
className="ml-auto px-3 py-1 rounded-full text-xs font-medium bg-slate-800 text-red-400 hover:bg-red-900 hover:text-red-200 border border-slate-600 hover:border-red-600 transition-colors"
|
||
>
|
||
Clear all
|
||
</button>
|
||
)}
|
||
</div>
|
||
|
||
{/* Drop zone */}
|
||
<div
|
||
ref={setNodeRef}
|
||
className={`flex-1 overflow-y-auto p-4 transition-colors ${isOver ? 'bg-blue-950/20' : ''}`}
|
||
>
|
||
{workspaceVarIds.length === 0 ? (
|
||
<div className={`flex flex-col items-center justify-center h-full text-center transition-colors ${isOver ? 'text-blue-400' : 'text-slate-600'}`}>
|
||
<div className="text-5xl mb-4">⚗️</div>
|
||
<div className="text-lg font-semibold mb-2">
|
||
{isOver ? 'Drop variable here' : 'Workspace is empty'}
|
||
</div>
|
||
<div className="text-sm max-w-xs">
|
||
Drag variables from the left panel, or click a preset above to get started.
|
||
Enter known values — unknowns solve automatically.
|
||
</div>
|
||
</div>
|
||
) : (
|
||
<SortableContext items={workspaceVarIds} strategy={rectSortingStrategy}>
|
||
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-3 content-start">
|
||
{workspaceVarIds.map(varId => (
|
||
<WorkspaceCard
|
||
key={varId}
|
||
varId={varId}
|
||
userValue={userValues[varId]}
|
||
solvedInfo={solved[varId]}
|
||
onRemove={() => onRemove(varId)}
|
||
onValueChange={val => onValueChange(varId, val)}
|
||
getUnit={getUnit}
|
||
onUnitChange={unitId => setUnit(varId, unitId)}
|
||
sciNotation={sciNotation}
|
||
areaRatioBranch={areaRatioBranch}
|
||
onToggleBranch={onToggleAreaRatioBranch}
|
||
/>
|
||
))}
|
||
{/* Drop indicator card when dragging over a non-empty workspace */}
|
||
{isOver && (
|
||
<div className="rounded-xl border-2 border-dashed border-blue-500 bg-blue-950/30 p-3 flex items-center justify-center text-blue-400 text-sm min-h-[100px]">
|
||
Drop here
|
||
</div>
|
||
)}
|
||
</div>
|
||
</SortableContext>
|
||
)}
|
||
</div>
|
||
</main>
|
||
)
|
||
}
|