Allocation editor

A React component in Beamline's Form controls category.

Parts of a fixed whole edited as transfers: an ordered strip whose boundaries drag or step between neighbours and a roster of exact amounts where typing names the part that gives or takes; locks, limits and your rules refuse with words, nothing is redistributed out of sight, and Save waits for your handler.

Use it for

  • Moving amounts between parts of a fixed whole: minutes of a session, hours of a week, a budget across a few lines.
  • Ordered plans (an agenda, a route, a shift) where moving a boundary between neighbours is the natural edit.
  • When the whole must stay correct after every change and some parts are locked or limited by your rules.

Not for

  • Independent numbers whose sum is an outcome, not a fixed whole (number-field per value, with a computed total).
  • Typing a whole plan from a document, or more than about eight parts (number-field rows with a running total and Save blocked until it matches).
  • Showing a split without editing it (category-bar, usage-meter, pie-chart).
  • Resizing panes of a layout (resizable-panels).
  • One approximate value or range (slider).

Anatomy

  • header (title, fixed total, description)
  • strip: track, segments (part, gap, unallocated, over), boundary handles (slider), fixed seams beside locked parts, saved-boundary marks, scale
  • roster: one row per part (label, exact amount or locked value, change 30 → 35 +5, note: trades-with / lock reason / error, partner choice while an entry needs one)
  • status line and a polite live region
  • refused-save alert
  • footer: Cancel, Save (with onSave)

Variants

  • order: sequence, set

States

  • resting
  • dirty (data-dirty on root, rows and segments)
  • dragging
  • at limit (data-limit)
  • pending partner (data-pending)
  • working: saving (data-loading, aria-busy)
  • resolved: Saved (caller's acknowledged result)
  • error: refused transfer (reason beside the cause)
  • error: refused save (data-error, alert, draft kept)
  • locked part (data-locked, lock reason)
  • unallocated (named segment, Save blocked)
  • over (named hatched band, Save blocked)
  • not set (null: — and Not set, never 0)
  • loading
  • empty
  • read-only
  • disabled

Keyboard

  • Boundary: → / ← move it one step in that direction (mirrored in RTL); ↑ / ↓ toward the end / start
  • Boundary: Shift+arrow or Page Up / Page Down a large step; Home / End as far as limits and rules allow; Esc during a drag reverts
  • Amount field: type, ↑ / ↓ adjust the proposal, Enter applies, Esc reverts; the note names the part that gives or takes before it applies
  • Two eligible partners: Enter moves to the choice; Enter or Space on a choice applies it and returns to the field
  • Tab: boundaries, then fields, then Cancel and Save; locked parts have no stop

Motion

Boundary keys and drags place the boundary at once, in whole steps; an applied entry, Cancel or adopted save moves widths over --pui-duration-standard without overshoot; change marks arrive in 90ms and leave in 180ms; never bounces at a limit. Reduced motion: all instant.

Props

PropTypeDefaultDescription
label (required)string—The whole's name and the group's accessible name.
items (required){ id, label, kind?: "part" | "gap", min?, max?, locked?, lockReason?, description?, color? }[]—
total (required)number—The fixed whole; never changed by the component.
step / largeStepnumber—Every transfer is a whole multiple of step. Defaults 1 / 5 × step.
unit / format / localestring / (amount) => string / string—
scaleboolean | (offset) => stringtrueLabels at boundaries, e.g. clock times.
order"sequence" | "set""sequence"set: order is arbitrary, no handles; an entry asks which part gives whenever more than one could.
value / defaultValue / onValueChangeRecord<id, number | null>, (value, transfer | null) => void—
onValueCommit(value) => void—Once per drag, key press, applied entry, Cancel or adopted save.
savedValueRecord<id, number | null>—The persisted baseline; Cancel returns to it.
canTransfer({ from, to, amount, via }, next) => true | string—Your rule; a string refuses with those words.
onSave({ value, changes, transfers }) => Promise<value | void>—Shows Cancel and Save; throw to refuse and keep the draft.
onCancel() => void—
saveLabel / savedLabel / cancelLabel / messagesstring / Partial<AllocationMessages>—
error / loading / emptyLabel / readOnly / disabled / namemixed—
className / style / classNamesstring / CSSProperties / Partial<Record<slot, string>>—

Dependencies

lucide-react

Import

import { AllocationEditor, UNALLOCATED } from "@/components/allocation-editor/allocation-editor";

Get it

Allocation editor is part of Beamline: 190 React components and screens your coding agent (Claude Code, Codex, Cursor or any MCP client) installs into your app through Beamline's MCP server, as a ready-built package or as plain React source you can change. $49 one payment (regular $200), no subscription, a year of updates, one licence for your whole team. Get Beamline · Connect your agent

More in Form controls

  • Billing toggle: Monthly or yearly: a segmented control whose yearly note turns into what the choice saves.
  • Calendar: Month grids for picking a day or a range: a real grid with arrows by day and week, Page keys by month, closed days, today's dot, and a range span that…
  • Checkbox: A yes/no choice with an optional label and help, including the mixed state for select-all; the check draws itself in.
  • Chip group: Quick filters as chips: a chosen chip fills and grows a check, counts show what each would return, and extra chips fold behind “+N more”.
  • Color Palette: A wheel of preset colours to pick from: twelve hues around the ring, their tints inward, white at the middle; the chosen swatch lifts and carries a check.
  • Colour picker: A swatch that opens a picker: saturation-brightness plane, hue strip, HEX/RGB/HSL/OKLCH value to type or copy, the screen eyedropper where available…
  • Combobox: One value from a long list found by typing (time zones, countries, customers): ranked matches with the match emphasised, keywords, optional group…
  • Date picker: One date in a form: a field that opens the month grid under it with focus inside, picks and closes; Today and Clear below, optional clear button.
  • Date range picker: A reporting range: presets on one side and two months on the other (one on a phone); the trigger names the preset in use, and the footer counts the days.
  • File drop zone: A generous target for files: drop or choose, each checked against type, size and count with the reason when turned away; uploads show progress and can be…
  • Form: Validation wiring for react-hook-form (and zod) on the system's own fields: each field already links its label, help and error, so FormField hands over…
  • Inline edit: Text that becomes a field in the same place: Enter (or ⌘Enter when multiline) saves, Escape cancels, leaving saves; saving shows progress and a refusal…
  • Input: A single-line text field with its label, help and error wired for assistive tech, three sizes and adornments.
  • Input Dissolve: A one-line field whose text breaks into specks and drifts off once it is sent, last letter first; the field is empty and ready again.
  • Label: The text label tied to a form control, with a required marker that screen readers announce.
  • Mention input: A composer where @people and #channels become single tokens with suggestions at the caret; the value keeps the text and where each mention sits.
  • Multi-select: Several values from a known list as removable chips in the field; the list has a search field (for long lists), checkable rows and a count with Clear.
  • Number field: A bounded number with − and +: arrows and Page keys step, typing is read in the locale and clamped on leaving, limits can be explained, and the label can…
  • OTP input: A verification code in separate boxes backed by one real field, so paste, SMS autofill and Backspace work; optional groups like 123–456.
  • Password field: A password field with a reveal control, and a strength meter when a password is being created.
  • Phone input: A phone number with its country: searchable picker with country and dialling codes, “+44…” switches the country, the number groups as typed, and the…
  • Radio cards: One choice among rich options as cards with optional media, icon, detail and a figure; one selection ring follows the stable choice.
  • Radio group: One choice from a short visible set, each option with an optional description, vertical or in a row.
  • Rich text editor: A Notion-grade writing surface: slash menu for every block, a formatting toolbar on selection, drag-to-move blocks, nesting, markdown shortcuts, paste…
  • Search field: The search entry point: magnifier, clear button, Escape to clear, a shortcut hint and an optional live border beam.
  • Segmented control: Two to five views or ranges of the same content (1D / 1W / 1M, List / Board): a radio group whose raised thumb slides to the choice.
  • Select: One choice from a short known list, in the same frame as every text field: label, help, error, option groups and details.
  • Shortcut recorder: Lets people set their own shortcut: press the keys, see them as keycaps; a clash or a browser-kept shortcut asks first. ShortcutCheatsheet lists every…
  • Signature pad: A pad for a drawn signature whose ink thins with speed; undo, replay, PNG or SVG export, and typing instead for keyboards and screen readers.
  • Slider: A value or range on a known scale: the thumb grows under the finger and shows its value while dragged or focused; marks, end icons and a value readout.
  • Switch: A setting that applies at once: a thumb that slides on a spring and stretches under the finger, with an optional label before or after.
  • Tag input: Short free-form values as tags: Enter, comma or Tab adds, a pasted list adds them all, Backspace twice removes the last; duplicates and invalid values…
  • Textarea: Multi-line plain text that grows with what is typed up to a limit, with an optional character count.
  • Time picker: A time of day as hour and minute segments, typed or stepped, 12- or 24-hour; the clock opens a list of times at the step.
  • Toggle group: Pressable toggles where one or several can be on: alignment, bold and italic, view options; bare for toolbars or framed.