From 46a812dd74182c041f31e15496154bc42508987a Mon Sep 17 00:00:00 2001 From: Paul Trowbridge Date: Mon, 21 Sep 2026 23:05:54 -0400 Subject: [PATCH] Grey the measure the plug holds Absorbed by asks which of price and volume moves, and then said so only in a sentence underneath itself. The column it holds still now says it: the header reads "holds" in place of its hint and the three boxes go flat grey, so what you are editing is legible from the numbers rather than from prose about them. The sentence goes, since the greyed column is the same statement made where it applies. The toggle stays -- it is an input, and with dollars alone nothing in the data answers it. A held box is still typeable, and doing so is how you override the toggle: naming qty or price answers the question directly, which is why the control only appears while dollars are the sole input. Co-Authored-By: Claude Opus 5 (1M context) --- ui/src/components/OperationPanel.jsx | 31 ++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/ui/src/components/OperationPanel.jsx b/ui/src/components/OperationPanel.jsx index 4ae9ce6..110b112 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, onChange, onFocus, suffix }) { +function LedgerInput({ value, active, held, onChange, onFocus, suffix }) { const ref = useRef(null) const caret = useRef(null) const display = groupDigits(value) @@ -328,9 +328,12 @@ function LedgerInput({ value, active, onChange, onFocus, suffix }) { onChange(el.value.replace(/,/g, '')) }} onFocus={onFocus} placeholder="—" - className={`border rounded py-0.5 text-xs w-24 text-right font-mono tabular-nums + 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'} - ${active ? 'border-blue-400 bg-blue-50/40 text-gray-800' : 'border-gray-200 bg-white text-gray-700'}`} /> + ${active ? 'border-blue-400 bg-blue-50/40 text-gray-800' + : held ? 'border-gray-100 bg-gray-50 text-gray-400' + : 'border-gray-200 bg-white text-gray-700'}`} /> {suffix && ( {suffix} @@ -549,6 +552,14 @@ function ScaleLedger({ currentTotals, scaleInputs, setScaleInputs, scalePlug, se })() const showRunning = !!valueCol && (lines.length + exclLines.length) > 1 + // Which measure the plug holds still. Only meaningful while dollars are the + // sole input -- that is the one case where price and volume are both unknown + // and the toggle is answering for them. 'price' means price absorbs the + // change, so qty holds; 'volume' means qty absorbs it, so price holds. + const heldKey = (valueCol && unitsCol && dollarsOnly) + ? (scalePlug === 'price' ? 'units' : 'price') + : null + const numCell = 'text-right font-mono tabular-nums whitespace-nowrap px-2' const rule =
@@ -559,9 +570,13 @@ function ScaleLedger({ currentTotals, scaleInputs, setScaleInputs, scalePlug, se {measures.map(m => ( - + {m.label} - {m.hint && · {m.hint}} + {m.key === heldKey + ? · holds + : m.hint && · {m.hint}} ))} {showRunning && ( @@ -676,6 +691,7 @@ function ScaleLedger({ currentTotals, scaleInputs, setScaleInputs, scalePlug, se setEdit(m.key, field, raw)} onFocus={() => focusRow(m.key, field)} suffix={field === 'pct' ? '%' : null} @@ -716,10 +732,7 @@ function ScaleLedger({ currentTotals, scaleInputs, setScaleInputs, scalePlug, se
{valueCol && unitsCol && dollarsOnly && ( - +