Platform roster
A list of platforms with quick-glance link, autonomy, and battery per row. The active platform is unmistakable — Ember-marked left edge and leading dot. Essential for group / swarm command surfaces.
Live
Two roster postures. Click a row to make it active — the Ember marker shifts. Each row carries signal bars, identifier, class tag, autonomy rung, battery percent, and link label. Battery and link colours use semantic tokens; identity stays on the Ember edge.
Anatomy
Vertical list. Each row is a single mono line of state. The active row carries the Ember marker on the left edge; everything else stays neutral so the active context reads unambiguously.
Left-edge Ember bar + Ember leading dot mark the active platform. Honours invariant 3 — the active context is unmistakable at a glance.
Four-bar reading coloured by link status. Greyed bars indicate empty slots. Matches the sibling comms / health strip's bar treatment.
Mono identifier with optional class tag (UGV / UAV / USV / UUV). Class reads in fg-subtle so it doesn't compete with the ID.
Right-aligned cluster: current autonomy rung (mono caps), battery percentage with semantic colour, and link label (LINK / DEGRADED / LOST) in semantic tone.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| platforms | RosterEntry[] | — | Platforms to render, in display order. |
| activeId | string | — | The id of the currently active platform. Marked with Ember left-edge + dot. |
| onSelect | (id: string) => void | — | Fires when the operator selects a row. When omitted, rows are non-interactive (status only) and render without focus / hover affordances. |
| label | string | — | Optional roster label rendered above the list — e.g. "ECHELON BRAVO". |
| fillHeight | boolean | false | Opt into fit-and-scroll. Root takes parent's full height; the label header stays anchored; the list scrolls within the roster frame. Use when the fleet might be longer than the column can show. |
| className | string | — | Forwarded to the root container. |
Types
RosterEntryPer-platform row state. Drives the visual cells. Leave lost platforms in the array (with `link: "lost"`) rather than removing them — the operator's spatial map stays stable.
interface RosterEntry {
id: string; // operator-visible identifier, e.g. "UGV-04"
link: "good" | "degraded" | "lost";
signal?: 0 | 1 | 2 | 3 | 4; // bars; defaults from link status when omitted
battery?: number; // 0-100, colour-coded
autonomy?: string; // e.g. "MANUAL", "SUPERVISED"
klass?: string; // class tag, e.g. "UGV", "UAV"
}Wiring
The roster is read-only state plus a single selection callback. The consumer holds the active-platform state machine; the roster renders it.
<PlatformRoster
label="ECHELON BRAVO"
platforms={fleet.map(toRosterEntry)}
activeId={activePlatformId}
onSelect={(id) => commandContext.focus(id)}
/>Behavioural rule
Active context unambiguous
The same command word means different things at different platforms. The roster's Ember-marked active row anchors the operator's spatial sense of who am I addressing right now — no row hover state, no near-miss colour, no ambiguity.
Accessibility
| Listbox role | The root carries `role="listbox"` with an `aria-label` (defaults to `Platform roster`). Each row is `role="option"` with `aria-selected` reflecting the active state. |
|---|---|
| Interactive vs status | When `onSelect` is passed, rows render as buttons with hover / focus affordances and keyboard activation. When omitted, rows are static status-only items — no focus ring, no hover. |
| Decorative visuals | Signal bars, the Ember left-edge, and the leading dot are `aria-hidden`. Row text — ID, autonomy, battery percentage, link label — carries the meaning. |
| Colour and meaning | Link state (LINK / DEGRADED / LOST) and battery percentage are paired with explicit text. Operators with colour-vision differences still read state. |
JavaFX
Ships in the PRIZM JavaFX library for thick-client C3 apps as Rc3PlatformRoster (extends VBox). Run the gallery to see it natively.
import design.prizm.fx.rc3.Rc3PlatformRoster;
Rc3PlatformRoster()| Member | Type | Default | Description |
|---|---|---|---|
| LinkStatus | enum | — | GOOD / DEGRADED / LOST — signal-bar colour + link label. |
| RosterEntry | record(String id, LinkStatus link, Integer signal, Integer battery, String autonomy, String klass) | — | Optional fields may be null; signal falls back to a status default. |
| setPlatforms | (List<RosterEntry>) → void | — | Display order. |
| setActiveId | (String) → void | — | Ember-marked active row (left bar + dot). |
| setOnSelect | (Consumer<String>) → void | — | Makes rows interactive. |
| setLabel | (String) → void | — | Optional roster header. |
| setFillHeight | (boolean) → void | false | Fit-and-scroll — needs a height-constrained parent. |
Honours invariant 3 (active context unambiguous). Mirrors components/rc3/platform-roster.tsx.
Usage
Use whenever the operator commands more than one platform. The roster's first responsibility is to make the active context unambiguous — if no platform is active, pass an explicit activeId that the consumer's state machine can verify. Leave lost platforms in the list with link: "lost" rather than removing them — the operator's spatial map of the fleet stays stable.