optimization / frontends /react /src /ComparePanel.tsx
nanye's picture
hide/show algorithms
8071c8f
Raw History Blame Contribute Delete
3.92 kB
import { useState } from "react";
import AlgorithmControls, { DEFAULT_HYPERPARAMETERS } from "./AlgorithmControls";
import type { ComparisonEntry, ComparisonTrajectory, SettingsUi } from "./types";
export default function ComparePanel({ settings, entries, trajectories, onAdd, onRemove, onToggleVisibility, iteration, busy, ready, animating, onAction }: {
settings: SettingsUi;
entries: ComparisonEntry[];
trajectories: ComparisonTrajectory[];
onAdd: (settings: SettingsUi) => void;
onRemove: (id: number) => void;
onToggleVisibility: (id: number) => void;
iteration: number;
busy: boolean;
ready: boolean;
animating: boolean;
onAction: (action: string) => void;
}) {
const [draft, setDraft] = useState(settings);
const configured: SettingsUi = { algorithm: draft.algorithm, x0: draft.x0, y0: draft.y0,
mode: settings.mode, functionExpr: settings.functionExpr,
...Object.fromEntries(Object.entries(DEFAULT_HYPERPARAMETERS[draft.algorithm]).map(([key, fallback]) => [key, draft[key as keyof SettingsUi] ?? fallback])) };
const valid = [configured.x0, ...(configured.mode === "Bivariate" ? [configured.y0] : []),
...Object.entries(configured).filter(([key]) => ["learningRate", "momentum", "beta", "beta1", "beta2", "epsilon"].includes(key)).map(([, value]) => value)
].every(value => value !== undefined && value.trim() !== "" && Number.isFinite(Number(value)));
return <>
<AlgorithmControls settings={configured} setSettings={setDraft} onRandomInitialPoint={() => setDraft({ ...configured, x0: (Math.random() * 2 - 1).toFixed(2), y0: (Math.random() * 2 - 1).toFixed(2) })} />
<button className="rounded border border-gray-300 bg-orange-200 px-3 py-1 disabled:opacity-50" disabled={!valid || !ready || busy || animating} onClick={() => onAdd(configured)}>Add</button>
<div className="text-sm font-semibold">Selected algorithms</div>
{entries.length === 0 && <p className="text-sm text-stone-500">Add algorithms to compare their trajectories on the same objective.</p>}
<ul className="flex flex-col gap-2">
{entries.map((entry, index) => <li key={entry.id} className="rounded border border-stone-300 p-2 text-sm">
<div className="flex items-center gap-2">
<span className="h-3 w-3 rounded-full shrink-0" style={{ background: entry.color }} />
<span className="flex-1 font-semibold">{index + 1}. {entry.settings.algorithm}</span>
<button
aria-label={`${entry.visible ? "Hide" : "Show"} ${index + 1}. ${entry.settings.algorithm}`}
aria-pressed={!entry.visible}
onClick={() => onToggleVisibility(entry.id)}
>
{entry.visible ? "Hide" : "Show"}
</button>
<button aria-label={`Remove ${index + 1}. ${entry.settings.algorithm}`} onClick={() => onRemove(entry.id)}>Remove</button>
</div>
<div className="text-xs text-stone-600">{Object.entries(entry.settings).filter(([key]) => !["mode", "functionExpr", "algorithm"].includes(key) && (key !== "y0" || settings.mode === "Bivariate")).map(([key, value]) => `${key}: ${value}`).join(", ")}</div>
{trajectories.find(item => item.id === entry.id)?.error && <p role="alert" className="text-red-700">Unable to compute this trajectory. Check its settings.</p>}
</li>)}
</ul>
<div className="text-sm" role="status">Iteration {iteration}{busy ? " · Computing…" : ""}</div>
<div className="flex flex-wrap gap-2">
{["Animate", "Previous", "Next", "Reset", "Pause"].map(action => <button key={action}
className="rounded border border-gray-300 bg-orange-200 px-3 py-1 text-sm disabled:opacity-50"
disabled={action === "Pause" ? !animating : !ready || !entries.length || busy || animating || ((action === "Previous" || action === "Reset") && iteration === 0)}
onClick={() => onAction(action)}>{action}</button>)}
</div>
</>;
}