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) <noreply@anthropic.com>
This commit is contained in:
Paul Trowbridge 2026-09-21 23:08:01 -04:00
parent 46a812dd74
commit fea8d0ac14

View File

@ -289,7 +289,7 @@ function groupDigits(raw) {
return `${neg ? '-' : ''}${grouped}${dot === -1 ? '' : `.${frac}`}` 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 ref = useRef(null)
const caret = useRef(null) const caret = useRef(null)
const display = groupDigits(value) const display = groupDigits(value)
@ -328,6 +328,7 @@ function LedgerInput({ value, active, held, onChange, onFocus, suffix }) {
onChange(el.value.replace(/,/g, '')) onChange(el.value.replace(/,/g, ''))
}} }}
onFocus={onFocus} placeholder="—" 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} 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 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'} ${suffix ? 'pl-2 pr-5' : 'px-2'}
@ -517,6 +518,17 @@ function ScaleLedger({ currentTotals, scaleInputs, setScaleInputs, scalePlug, se
const setEdit = (key, field, raw) => const setEdit = (key, field, raw) =>
setScaleInputs(prev => ({ ...prev, [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, // focusing a different row hands that measure's edit to the focused row,
// carrying across whatever it currently reads // carrying across whatever it currently reads
const focusRow = (key, field) => const focusRow = (key, field) =>
@ -577,6 +589,11 @@ function ScaleLedger({ currentTotals, scaleInputs, setScaleInputs, scalePlug, se
{m.key === heldKey {m.key === heldKey
? <span className="text-gray-400"> · holds</span> ? <span className="text-gray-400"> · holds</span>
: m.hint && <span className="text-gray-500"> · {m.hint}</span>} : m.hint && <span className="text-gray-500"> · {m.hint}</span>}
{scaleInputs[m.key] && (
<button onClick={() => clearEdit(m.key)}
title={`Stop editing ${m.label} — let it follow from the others again`}
className="ml-1 text-gray-300 hover:text-red-500 leading-none">✕</button>
)}
</th> </th>
))} ))}
{showRunning && ( {showRunning && (
@ -694,6 +711,7 @@ function ScaleLedger({ currentTotals, scaleInputs, setScaleInputs, scalePlug, se
held={m.key === heldKey} held={m.key === heldKey}
onChange={(raw) => setEdit(m.key, field, raw)} onChange={(raw) => setEdit(m.key, field, raw)}
onFocus={() => focusRow(m.key, field)} onFocus={() => focusRow(m.key, field)}
onClear={() => clearEdit(m.key)}
suffix={field === 'pct' ? '%' : null} suffix={field === 'pct' ? '%' : null}
/> />
</td> </td>