Target table
A React component in Beamline's Form controls category.
Rows of repeated rounds with typed targets (a load × a count, a count range, a time, a load held for a time), each row drawn as a bar beside its numbers that is also its control: drag across for the load, snapped to the unit's step, up or down for the count, tap or arrow keys to set; the fields are the exact twins, kinds are your app's own (warm-up, to failure…), a shown unit never moves a stored value, and a done column logs the rounds as they happen; while logging, a Did column takes the count actually done beside a range.
Use it for
- Planning or logging repeated rounds with typed targets: sets of a strength exercise, rehab rounds, practice drills, interval blocks.
- Wherever each round has a load and a count (or a time) that people adjust by feel and the shape of the rounds matters (a warm-up ramp, a plateau, a drop).
Not for
- A general table of records (table or data-grid).
- One number with bounds (number-field) or a value on a scale with a named limit (scrubber).
- Splitting a fixed whole between parts (allocation-editor).
Anatomy
- column headings: the row's kind, the load with its unit (a menu of units when there are several), the count as a fixed number or a range (a menu), the time; the scale's end said above the bars
- row: the kind's marker (its word where there is room, its letter on a phone, a working row numbered) opening the kind menu with each kind's consequence and Remove; the typed fields; the row's bar; the done mark when logging
- bar: a track that is the scale and a shape inside it, as long as the row's load (or count, or time), cut into one segment per count, a range's extra counts open, a row with no load yet hatched; its value beside its end while held, focused or pointed at
- foot: Add set and your `aside` (rest, a timer)
- TargetShape (also exported): a row's shape alone, to read: length against a max (a load or a time; null hatched), a segment per count with open ones outlined, faint for a warm-up; drawn only, its values said in words beside it (training-progress draws a logged day's sets with it)
- did (with onResultChange, count measures): the count actually done beside the target, a small number the hand pulls up or down; empty, the plan's top shows quietly and a tap or Enter makes it the count; the bar then draws what was done solid and what the range still allowed open
States
- rest (bars in quiet ink, faint kinds fainter)
- pointed at, focused (full ink, the value beside the end, the focus ring)
- held: the bar follows the hand snapped to the step, the fields show the same value, the scale holds
- released: the value commits and every bar glides to the new scale
- cancelled: Escape or a lost pointer puts the row back
- range (low end solid, the rest open)
- as many as possible (an open end)
- not entered (hatched bar, never zero), incomplete after a save attempt (the field marked, its fix said)
- structure locked (kinds, add, remove off; values open)
- done and not done (logging)
- narrow room: the bar on its own line under the fields; the kind's letter instead of its word
- right to left: bars grow from the right, ← adds load
- result: not entered (the plan's count quiet), done within the range, short of it, above it; held while dragged (the bar draws it), Escape puts it back
Keyboard
- Tab reaches the kind menu, each field and the bar of every row
- on a bar: → / ← load by the unit's step (Shift: four steps; mirrored right to left), ↑ / ↓ count by one (Shift: five)
- fields: ↑ ↓ step, typed values ("225 lb" in a kg field converts)
- kind, unit and count headings are menu buttons: Enter opens, arrows move, Escape closes
- Did: ↑/↓ by one (Shift by five), Home the range's low end, End the plan's top, Enter on an empty one the plan's top, Delete or Backspace clears
Motion
A bar's length is a transform from its start, gliding at the fast duration to a typed or released value; while held it follows the hand with no glide. Rows keep their path when they arrive, move and leave (the shared FLIP). The value beside the end arrives at once and leaves over the motion-out duration. Reduced motion places everything at once.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
name (required) | string | — | The item's name, which every field and bar is named by ("Bench press, set 1, weight"). |
measure (required) | "load-count" | "count" | "seconds" | "load-seconds" | — | What each row holds. |
rows / defaultRows / onRowsChange | TargetRow[] | — | { id, kind, load (stored unit), count (a range's top), low (a range's low end), seconds, done? }; every change as the whole list. |
kinds (required) | TargetKind[] | — | The kinds a row can be, in menu order. The first is a new row's kind. |
units / unit / defaultUnit / onUnitChange | TargetUnit[] / string | — | { id, short, long, perStored, step }; the first is the stored unit. Default kg and lb (2.5 / 5 steps). |
countMode / defaultCountMode / onCountModeChange | "fixed" | "range" | — | A fixed count or a range; switching keeps ids and values. |
onDoneChange | (id, done) => void | — | Shows the done column (logging the rounds as they happen). |
structureLocked / showProblems | boolean | — | structureLocked: Rows can't be added, removed or retyped (someone else set them); values stay editable. Say why beside it. showProblems: Mark incomplete rows in place (after a save attempt). |
aside | ReactNode | — | At the end of the row with Add (rest, a timer). |
labels | MessageLabels | — | Replace any built-in words: `{ count: "Breaths", rowNamed: "round {n}" }`. |
rows | TargetRow[] | — | |
defaultRows | TargetRow[] | — | |
onRowsChange | ((rows: TargetRow[]) => void) | — | Every change (a typed value, a released drag, a key, a kind, a row added or removed) as the whole new list. |
units | TargetUnit[] | — | The units the load can be shown in; the first is the stored unit. Default kilograms and pounds. |
unit | string | — | |
defaultUnit | string | — | |
onUnitChange | ((unit: string) => void) | — | The shown unit changed (nothing stored moves). Without it the unit heading is not a menu. |
countMode | TargetCountMode | — | |
defaultCountMode | TargetCountMode | — | |
onCountModeChange | ((mode: TargetCountMode) => void) | — | Fixed count or range. Switching keeps the number as both ends, or a range's top. |
structureLocked | boolean | — | Rows can't be added, removed or retyped (someone else set them); values stay editable. Say why beside it. |
showProblems | boolean | — | Mark incomplete rows in place (after a save attempt). |
className | string | — | Classes on the documented visual root; no override by default. |
classNames | TargetTableClassNames | — | Classes for the named visual parts; no overrides by default. |
style | CSSProperties | — | Inline styles on the documented visual root; no overrides by default. |
ref | Ref<HTMLDivElement> | — | Receives the documented native target or imperative handle; omitted by default. |
TargetShape.length (required) | number | null | — | |
TargetShape.max (required) | number | — | |
TargetShape.count | number | null | — | On TargetShape: Segments; null or 0 draws one whole bar (a time, a load alone). |
TargetShape.open | number | — | |
TargetShape.faint | boolean | — | |
onResultChange | (id: string, result: number | null) => void | — | Shows the Did column (count measures): the count actually done beside the target, scrubbed up or down or stepped with ↑/↓; a tap fills in the plan's top; Delete clears it. |
Built from
Used in
Dependencies
lucide-react
Import
import { TargetTable, TargetShape, KG_PER_LB } from "@beamline/target-table";
Get it
Target table is part of Beamline UI. Install the parts you need in your React app as compiled packages or editable source. Get Beamline · Connect your agent
More in Form controls
- 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.
- 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…
- Allocation editor: 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…
- Radio cards: One choice among rich options as cards with optional media, icon, detail and a figure; one selection ring follows the stable choice.
- 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…
- Availability rule editor: When someone (or something) can be booked: hours for each day of the week, one or more ranges a day, a day turned off, a day's hours copied to the others…
- Recurrence editor: How something repeats, set in plain controls and said back in a sentence: every N days, weeks, months or years; which weekdays; in a month, on a date or…
- Signature pad: A pad for a drawn signature whose ink follows a stylus's pressure (and thins with speed under a mouse or finger); undo, replay, PNG or SVG export, and…
- Image cropper: Frame a picture before it is saved: a fixed window (square, round for avatars, wide for covers, or free with handles) over the picture, which you drag…
- 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.
- Password field: A password field with a reveal control, and a strength meter when a password is being created.
- Card field: A payment card in one field: the number groups as it is typed and names its network, the expiry and security code follow on their own as each part…
- 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…
- Scrubber: A budget, a timeout or a position on a scale where the neighbourhood matters: the readout says how far under or over the named limit the value is, the…
- 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.
- Rating: A score on a small scale: stars that fill as you point (halves too) and light one after another when chosen, with the word for the score beside them…
- 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…
- 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.
- 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.
- Stage field: A record's status as its stages in order, editable in place. Stages before the current one read as passed (gone through, never "complete"); only the…
- Layout picker: Shared record layouts for one page. Each layout says where it's used ("Used on 3 pages: Deals, this page, Forecast") and how many fields it shows, and a…
- Tooth picker: Teeth and their surfaces for one procedure. The arch by Universal number as you face the patient (upper 1–16, lower 32–17; supernumerary teeth on their…
- 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…
- Emoji picker: An emoji chosen from a search and a grid. The trigger shows the chosen emoji and opens a search above the emoji by group, with the recently used first…
- 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”.
- 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…
- 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.
- 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…
- 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…
- Tree select: A field that picks from a tree: departments and teams, categories and subcategories, folders, regions and cities; the list opens as an indented tree…
- Combobox: One value from a long list found by typing (time zones, countries, customers): ranked matches with the match emphasised, keywords, optional group…
- 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…
- 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.
- 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.
- Reminder picker: Snooze or remind, at an exact time. Two named actions on one picker: Snooze hides the item until then, Remind me keeps it in view and reminds at that…
- 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…
- Stock adjust: An on-hand count changed with its reason: received, recounted, damaged, lost or returned; by an amount or to a new count; in the item's unit or in packs…
- Inline edit: Text, a number or an amount of money that becomes a field in the same place: Enter (or ⌘Enter when multiline) saves, Escape cancels, leaving saves…
- Search field: The search entry point: magnifier, clear button, Escape to clear, a shortcut hint and an optional live border beam.
- 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.
- 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…
- Select: One choice from a short known list, in the same frame as every text field: label, help, error, option groups and details.
- Toggle group: Pressable toggles where one or several can be on: alignment, bold and italic, view options; bare for toolbars or framed.
- Input: A single-line text field with its label, help and error wired for assistive tech, three sizes and adornments. With complete or emailDomains it offers…
- Checkbox: A yes/no choice with an optional label and help, including the mixed state for select-all; the check draws itself in.
- Radio group: One choice from a short visible set, each option with an optional description, vertical or in a row.
- Textarea: Multi-line plain text that grows with what is typed up to a limit, with an optional character count.
- Label: The text label tied to a form control, with a required marker that screen readers announce.
- Value grip: A grip on a scale your screen draws (a bar's end, a flag, a markup's edge): drag it, or use the arrows, to a value snapped to its step; the value arrives…
- Count adjust: A count of whole things against its whole (received of ordered, credits of bought), set by hand: what is on record stays solid, the change is drawn as…