Skip to content

Multi-check Group ​

MultiCheckGroup extends the checkbox-group interaction model to an ordered set of states per item. Instead of a boolean, every row stores a numeric state index in a two-way Signal<number[]>. Pressing a row advances its index and wraps from the final state back to the first.

This is useful for compact inheritance, coverage, or tri-state settings such as Off, Partial, and Full. It is not a substitute for a menu when state order has no clear meaning.

Usage ​

ts
import { MultiCheckGroup, signal } from '@jsvision/ui';

const coverage = signal([0, 2]);
const group = new MultiCheckGroup({
  items: ['~S~ync', '~B~ackup'],
  states: ' xX',
  value: coverage,
});

Live example ​

Cycle Off, Partial, and Full states independently; watch the last state wrap to the first.

The laboratory translates numeric state indexes into readable words beside the glyphs. It also includes a disabled row and a reset action so state boundaries remain easy to inspect.

Props ​

MultiCheckGroup accepts MultiCheckGroupOptions:

PropTypePurpose
itemsreadonly string[]One caption per row; each may mark a ~X~ item accelerator.
statesstringOrdered one-code-unit, one-cell marker glyphs; each defines one state.
valueSignal<number[]>Two-way state index per item, in the same positional order.

Provide at least one state glyph. Missing array entries read as state 0; an out-of-range value also renders as the first marker. The next press normalizes the written array and advances with wrapping, including from a negative external value.

Rows inherit setItemEnabled(index, enabled) from the shared cluster control.

Size and Layout ​

The group paints one row per item, using five cells for the bracketed marker before the caption. Allocate items.length rows and enough width for the longest display caption. A smaller height clips rows and does not introduce scrolling.

Use one UTF-16 code unit and one terminal display cell per marker, such as a space, x, or X. Astral emoji, wide glyphs, and combining sequences are not valid state markers because marker lookup is positional within the JavaScript string and the drawing column is exactly one cell.

State cycles ​

The glyph order is the interaction order. With states: ' xX', index 0 draws a blank marker, index 1 draws x, and index 2 draws X; another press returns to 0. Each row advances independently and writes a full-length number array.

External signal writes repaint all markers. Keep a separate semantic label table in application code when the numeric indexes need to be shown to users or persisted as domain values.

Keyboard & mouse ​

InputResult
Up / DownMove and wrap the highlight, skipping disabled rows.
SpaceAdvance the highlighted row by one state.
Click an enabled rowFocus, highlight, and advance that row.
Alt + item hotkeyFocus, highlight, and advance the matching row dialog-wide.

Disabled rows remain visible but cannot advance. Navigation is a handled no-op when every item is disabled.

Best Practices ​

  • Use a stable, documented state order. Users should be able to predict what the next press means.
  • Pair unfamiliar marker glyphs with a visible legend or reactive text, as the live example does.
  • Keep items, persisted indexes, and state labels aligned when reordering data.
  • Prefer CheckGroup for ordinary booleans and RadioGroup for one selection across rows.

Theming ​

Theme roleRegion
clusterNormalEnabled rows outside the current keyboard highlight.
clusterSelectedHighlighted row while the group owns focus.
clusterShortcutMarked accelerator glyph on enabled rows.
clusterDisabledDisabled marker, caption, and accelerator.

Custom state glyphs inherit the row's role rather than receiving state-specific colors. Choose glyphs that remain distinguishable in every cluster role and under limited terminal capabilities.