Add success state to playground
Update the Rules of Inference Playground to display a light green background and freeze interactions upon successful completion of a proof. This state allows users to select a new puzzle.
This commit is contained in:
parent
8f5eeb77a9
commit
1bb24d2620
1 changed files with 21 additions and 14 deletions
|
|
@ -88,13 +88,17 @@ function parseDragData(e: React.DragEvent): { type: DragType; value: string } |
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function DraggableChip({ label, onDragStart: handle }: { label: string; onDragStart: (e: React.DragEvent) => void }) {
|
function DraggableChip({ label, onDragStart: handle, disabled = false }: { label: string; onDragStart: (e: React.DragEvent) => void; disabled?: boolean }) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
role="button"
|
role="button"
|
||||||
draggable
|
draggable={!disabled}
|
||||||
onDragStart={handle}
|
onDragStart={disabled ? undefined : handle}
|
||||||
className="select-none cursor-grab active:cursor-grabbing rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground shadow-sm hover:bg-accent hover:text-accent-foreground transition-colors"
|
className={`select-none rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground shadow-sm transition-colors ${
|
||||||
|
disabled
|
||||||
|
? 'opacity-50 cursor-not-allowed'
|
||||||
|
: 'cursor-grab active:cursor-grabbing hover:bg-accent hover:text-accent-foreground'
|
||||||
|
}`}
|
||||||
aria-label={label}
|
aria-label={label}
|
||||||
title={label}
|
title={label}
|
||||||
>
|
>
|
||||||
|
|
@ -222,6 +226,7 @@ export default function RulesOfInferencePlayground() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePremiseDrop(e: React.DragEvent) {
|
function handlePremiseDrop(e: React.DragEvent) {
|
||||||
|
if (reachedTarget) return; // Freeze interactions when solved
|
||||||
const data = parseDragData(e);
|
const data = parseDragData(e);
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
if (data.type === "premise" || data.type === "statement") {
|
if (data.type === "premise" || data.type === "statement") {
|
||||||
|
|
@ -233,6 +238,7 @@ export default function RulesOfInferencePlayground() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRuleDrop(e: React.DragEvent) {
|
function handleRuleDrop(e: React.DragEvent) {
|
||||||
|
if (reachedTarget) return; // Freeze interactions when solved
|
||||||
const data = parseDragData(e);
|
const data = parseDragData(e);
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
if (data.type === "rule") setActiveRule(data.value);
|
if (data.type === "rule") setActiveRule(data.value);
|
||||||
|
|
@ -248,7 +254,7 @@ export default function RulesOfInferencePlayground() {
|
||||||
setActivePremises((prev) => prev.filter((x) => x !== p));
|
setActivePremises((prev) => prev.filter((x) => x !== p));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-[70vh] space-y-4">
|
<div className={`min-h-[70vh] space-y-4 transition-colors duration-500 ${reachedTarget ? 'bg-green-50/50 dark:bg-green-950/20' : ''}`}>
|
||||||
{/* Top controls */}
|
{/* Top controls */}
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Select value={exampleId} onValueChange={setExampleId}>
|
<Select value={exampleId} onValueChange={setExampleId}>
|
||||||
|
|
@ -282,7 +288,7 @@ export default function RulesOfInferencePlayground() {
|
||||||
<p className="text-xs text-muted-foreground">Initial premises</p>
|
<p className="text-xs text-muted-foreground">Initial premises</p>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{example.premises.map((p) => (
|
{example.premises.map((p) => (
|
||||||
<DraggableChip key={p} label={p} onDragStart={(e) => onDragStart(e, "premise", p)} />
|
<DraggableChip key={p} label={p} onDragStart={(e) => onDragStart(e, "premise", p)} disabled={reachedTarget} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -290,13 +296,14 @@ export default function RulesOfInferencePlayground() {
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<p className="text-xs text-muted-foreground">Derived so far</p>
|
<p className="text-xs text-muted-foreground">Derived so far</p>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{steps.map((s, idx) => (
|
{steps.map((s, idx) => (
|
||||||
<DraggableChip
|
<DraggableChip
|
||||||
key={`${s.result}-${idx}`}
|
key={`${s.result}-${idx}`}
|
||||||
label={s.result}
|
label={s.result}
|
||||||
onDragStart={(e) => onDragStart(e, "statement", s.result)}
|
onDragStart={(e) => onDragStart(e, "statement", s.result)}
|
||||||
/>
|
disabled={reachedTarget}
|
||||||
))}
|
/>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -308,7 +315,7 @@ export default function RulesOfInferencePlayground() {
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{example.rules.map((r) => (
|
{example.rules.map((r) => (
|
||||||
<div key={r.id} className="space-y-1">
|
<div key={r.id} className="space-y-1">
|
||||||
<DraggableChip label={r.id} onDragStart={(e) => onDragStart(e, "rule", r.id)} />
|
<DraggableChip label={r.id} onDragStart={(e) => onDragStart(e, "rule", r.id)} disabled={reachedTarget} />
|
||||||
{r.help && <p className="text-xs text-muted-foreground pl-1">{r.help}</p>}
|
{r.help && <p className="text-xs text-muted-foreground pl-1">{r.help}</p>}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue