{error}
} + {!error && !steps &&Computing…
} + {!error && steps && steps.length <= 2 && ( ++ No adjustments in scope — the bridge shows the walk from baseline to current, + and this selection has only a baseline. +
+ )} + + {!error && steps && steps.length > 2 && !asTable && ( +| Step | +{valueCol} | + {unitsCol &&{unitsCol} | } +Running | +Adjustments | +Rows | +
|---|---|---|---|---|---|
| + {s.label}{!s.tagged && s.kind === 'step' && · untagged} + | ++ {s.kind === 'anchor' ? fmt(s.end) : fmtSigned(s.delta)} + | + {unitsCol && ( ++ {s.kind === 'anchor' ? fmt(s.units) : fmtSigned(s.units)} + | + )} +{fmt(s.end)} | +{s.kind === 'step' ? s.entries : '—'} | +{s.rows} | +
+ Click a pivot row to select a slice.
+ Ctrl/⌘-click to add more.
+
|
+ {multi
+ ? {sliceLabel(s)}
+ : (
+
+ {Object.entries(s).map(([k, v]) => (
+
+ )}
+
+ {k}
+ =
+ {v}
+
+ ))}
+ |
+ {multi && valueCol && (
+ + {fmtNum(perSlice[i]?.total?.value)} + | + )} ++ + | +
| {r.iter} | + {valueCol &&{fmtNum(r.value)} | } + {unitsCol &&{fmtNum(r.units)} | } +
| + {measures.map(m => ( + | + {m.label} + {m.hint && · {m.hint}} + | + ))} +
|---|---|
| + {e.kind === 'tag' && ▪ } + {e.label} + {e.kind === 'tag' && e.count > 1 && ×{e.count}} + | + {measures.map(m => ( ++ {m.key === 'price' ? '' : fmtNum(e[m.key], m.dp)} + | + ))} +
| )} | |
| + {hasExcl ? 'Adjustable' : 'Current'}{perSlice ? ' (all)' : ''} + | + {measures.map(m => ( +{fmtNum(m.current, m.dp)} | + ))} +
| + {exclName} · fixed + | + {measures.map(m => ( ++ {m.key === 'price' ? '' : fmtNum(excl[m.key], m.dp)} + | + ))} +
| Selected total | + {measures.map(m => ( ++ {m.key === 'price' + ? fmtNum(grand.units ? grand.value / grand.units : null, m.dp) + : fmtNum(grand[m.key], m.dp)} + | + ))} +
| )} | |
| + {label}{perSlice && field === 'new' ? ' (each)' : ''} + | + {measures.map(m => { + const active = scaleInputs[m.key]?.field === field + return ( +
+ |
+ )
+ })}
+
+ {onTotal + ? `The ${exclName} rows cannot change, so the adjustable rows absorb the whole difference — the pivot will show your target.` + : `${exclName} rows are ignored. The pivot will show your target plus ${fmtNum(excl.value)}.`} +
++ Applied to each slice separately — the figures above are combined totals, + so each slice's own change will differ. +
+ )} +| dimension | +current | +new value | +
|---|---|---|
| {c.label || c.cname} | +{cur} | ++ setSet(s => ({ ...s, [c.cname]: e.target.value }))} + onBlur={c.is_key && c.dim_group + ? e => lookupDerivedCols(c.cname, e.target.value, setSet) + : undefined} + placeholder="keep" + className={TEXT} /> + | +
+ {verb}{' '} + {valueCol && {fmtNum(t.value)}} + {valueCol && {valueCol}} + {unitsCol && <> + · + {fmtNum(t.units)} + {unitsCol} + >} + {scaled && valueCol && <> + → + {fmtNum(t.value * factor)} + >} +
+ ) +} + +// ── Apply mode ────────────────────────────────────────────────────────────── +function ApplyModeChooser({ op, applyMode, setApplyMode, count }) { + const options = op === 'scale' + ? [ + ['prorate', 'Together', `One pool of ${count} slices — a target is the new combined total, and each slice keeps its share of the mix.`], + ['each', 'Each', `All ${count} slices independently — every slice reaches the target on its own.`], + ] + : [ + ['prorate', 'Together', `One operation over all ${count} slices — a single log entry.`], + ['each', 'Each', `One operation per slice — ${count} log entries, undoable separately.`], + ] + const active = options.find(([v]) => v === applyMode) + return ( +{active[2]}
} +
+ {JSON.stringify(payload, null, 2)}
+
+ )}
+ + {slices.length} cells selected, but they cover {distinctSlices} distinct{' '} + {distinctSlices === 1 ? 'slice' : 'slices'} — some differ only by a column + operations cannot target. The duplicates are applied once. +
+ )} +