From fea8d0ac14e11a4390bad7e549c18a4579e0631c Mon Sep 17 00:00:00 2001 From: Paul Trowbridge Date: Mon, 21 Sep 2026 23:08:01 -0400 Subject: [PATCH] Let a measure be handed back to the others Clicking a held box pins it, and the only way out was to know that emptying the field unpins it -- filled() is false on a blank either way. Clicking into a greyed column to see what is in it and then wanting out again is the ordinary case, not the awkward one. An x on the column header clears that measure's edit, shown only while it has one, and Escape in the box does the same and steps out. The panel's own Escape handler already ignores events from inputs, so the two do not collide. Co-Authored-By: Claude Opus 5 (1M context) --- ui/src/components/OperationPanel.jsx | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/ui/src/components/OperationPanel.jsx b/ui/src/components/OperationPanel.jsx index 110b112..0bfa855 100644 --- a/ui/src/components/OperationPanel.jsx +++ b/ui/src/components/OperationPanel.jsx @@ -289,7 +289,7 @@ function groupDigits(raw) { return `${neg ? '-' : ''}${grouped}${dot === -1 ? '' : `.${frac}`}` } -function LedgerInput({ value, active, held, onChange, onFocus, suffix }) { +function LedgerInput({ value, active, held, onChange, onFocus, onClear, suffix }) { const ref = useRef(null) const caret = useRef(null) const display = groupDigits(value) @@ -328,6 +328,7 @@ function LedgerInput({ value, active, held, onChange, onFocus, suffix }) { onChange(el.value.replace(/,/g, '')) }} onFocus={onFocus} placeholder="—" + onKeyDown={e => { if (e.key === 'Escape' && onClear) { e.preventDefault(); onClear(); e.target.blur() } }} title={held ? 'Held by the Absorbed by setting — type here to set it yourself' : undefined} className={`border rounded py-0.5 text-xs w-24 text-right font-mono tabular-nums transition-colors ${suffix ? 'pl-2 pr-5' : 'px-2'} @@ -517,6 +518,17 @@ function ScaleLedger({ currentTotals, scaleInputs, setScaleInputs, scalePlug, se const setEdit = (key, field, raw) => setScaleInputs(prev => ({ ...prev, [key]: { field, raw } })) + // Hand a measure back to being derived. Emptying the box does the same thing + // -- filled() is false either way -- but that is only obvious once you know + // it, and clicking into a held column to look at it and then wanting out + // again is the ordinary case, not the awkward one. + const clearEdit = (key) => + setScaleInputs(prev => { + if (!(key in prev)) return prev + const { [key]: _drop, ...rest } = prev + return rest + }) + // focusing a different row hands that measure's edit to the focused row, // carrying across whatever it currently reads const focusRow = (key, field) => @@ -577,6 +589,11 @@ function ScaleLedger({ currentTotals, scaleInputs, setScaleInputs, scalePlug, se {m.key === heldKey ? · holds : m.hint && · {m.hint}} + {scaleInputs[m.key] && ( + + )} ))} {showRunning && ( @@ -694,6 +711,7 @@ function ScaleLedger({ currentTotals, scaleInputs, setScaleInputs, scalePlug, se held={m.key === heldKey} onChange={(raw) => setEdit(m.key, field, raw)} onFocus={() => focusRow(m.key, field)} + onClear={() => clearEdit(m.key)} suffix={field === 'pct' ? '%' : null} />