Skip to main content
On this pageFunctions

Ui/RadioGroup

Functions

create

functionsource
/**
 * Pairs the radio group's `view` and `update` (and `select`) behind a
 *  single Value-typed entry point. Declaring the radio group once at
 *  module scope ensures the OutMessage's `value` field carries the
 *  consumer's union type without an `as` cast at the call site:
 * 
 *  ```ts
 *  const ToolRadioGroup = Ui.RadioGroup.create<Tool>()
 * 
 *  // In view:
 *  h.submodel({ view: ToolRadioGroup.view, ... })
 * 
 *  // In update:
 *  const [next, commands, maybeOutMessage] = ToolRadioGroup.update(model, message)
 *  // maybeOutMessage: Option<RadioGroup.OutMessage<Tool>>
 *  ```
 * 
 *  The view's `ViewInputs.options` stays typed `ReadonlyArray<string>`;
 *  consumers can pass a `ReadonlyArray<MyUnion>` (assignable) and the
 *  fenced cast inside `update` types the OutMessage's `value` as
 *  `MyUnion`.
 */
<Value extends string = string>(): Readonly<{
  reflectSelectedValue: Reflect<Model, Option.Option<Value>>
  select: (model: Model, value: Value, options: ReadonlyArray<Value>) => readonly [Model, ReadonlyArray<Command.Command<Message>>, Option.Option<OutMessage<Value>>]
  update: (model: Model, message: Message) => readonly [Model, ReadonlyArray<Command.Command<Message>>, Option.Option<OutMessage<Value>>]
  view: SubmodelView<Model, Message, ViewInputs<Value>>
}>

init

functionsource
/** Creates an initial radio group model from a config. Defaults to no selection and vertical orientation. */
(config: InitConfig): RadioGroup.Model

Types

InitConfig

typesource
/** Configuration for creating a radio group model with `init`. */
type InitConfig = Readonly<RadioGroup.Model>

OptionInfo

typesource
/**
 * Per-option render info passed to the consumer's `toView`. The consumer
 *  spreads `option`, `label`, and `description` onto whichever elements
 *  carry that role in their layout. Generic over `Value extends string`:
 *  when `Ui.RadioGroup.create<MyUnion>()` is declared, `option.value` is
 *  typed `MyUnion` so the consumer can switch on it without casting.
 */
type OptionInfo = Readonly<{
  description: ReadonlyArray<ChildAttribute>
  index: number
  isActive: boolean
  isDisabled: boolean
  isSelected: boolean
  label: ReadonlyArray<ChildAttribute>
  option: ReadonlyArray<ChildAttribute>
  value: Value
}>

Orientation

typesource
/** Controls the radio group layout direction and which arrow keys navigate between options. */
type Orientation = Literals<readonly ["Horizontal", "Vertical"]>

OutMessage

typesource
/**
 * Generic over `Value extends string` so consumers who create the radio
 *  group via `Ui.RadioGroup.create<MyUnion>()` receive `value: MyUnion` in
 *  the `Selected` OutMessage from the factory's `update`, instead of
 *  `value: string`. Defaults to `string` for consumers that don't need the
 *  lift.
 */
type OutMessage = Selected<Value>

RenderInfo

typesource
/**
 * Render-time payload published to the consumer's `toView`.
 * 
 *  - `group`: ARIA + role attributes for the wrapping radiogroup element.
 *  - `options`: one entry per option in `viewInputs.options`, in the same
 *    order. Includes the value, derived state, and the attribute bundles
 *    for the option element, its label, and its description.
 *  - `selectedValue`: the currently-selected value, if any. Convenient
 *    for the consumer when rendering selected-state visuals next to the
 *    option attributes.
 *  - `hiddenInput`: when `viewInputs.name` was supplied, attributes for a
 *    hidden form input carrying the selected value. The consumer
 *    renders the `<input>` themselves. Empty array when `name` is
 *    undefined.
 */
type RenderInfo = Readonly<{
  group: ReadonlyArray<ChildAttribute>
  hiddenInput: ReadonlyArray<ChildAttribute>
  options: ReadonlyArray<OptionInfo<Value>>
  selectedValue: Option.Option<Value>
}>

Selected

typesource
/**
 * Sent to the parent when an option is committed. Carries the selected
 *  value and its index. Generic over `Value extends string`: the runtime
 *  schema stores `value: string`, but the type-level OutMessage exposes
 *  `value: Value` so consumers who supply `options: ReadonlyArray<MyUnion>`
 *  receive `value: MyUnion` from the factory's `update` without casting at
 *  the call site. The cast is fenced inside this module's `update` return,
 *  sound because the value was selected from the options array the
 *  consumer supplied.
 */
type Selected = Readonly<{
  _tag: "Selected"
  index: number
  value: Value
}>

ViewInputs

typesource
/**
 * Per-render view inputs passed to `view` via `h.submodel`'s `viewInputs` field.
 *  Generic over `Value extends string` so consumers using
 *  `Ui.RadioGroup.create<MyUnion>()` receive `option.value: MyUnion` in
 *  `toView` and `(value: MyUnion, index) => boolean` in
 *  `isOptionDisabled`, without casting.
 */
type ViewInputs = Readonly<{
  ariaLabel: string
  isDisabled: boolean
  isOptionDisabled: (value: Value, index: number) => boolean
  name: string
  options: ReadonlyArray<Value>
  orientation: Orientation
  toView: (render: RenderInfo<Value>) => Html
}>

Constants

CompletedFocusOption

constsource
/** Sent when the focus-option command completes. */
const CompletedFocusOption: CallableTaggedStruct<"CompletedFocusOption", {}>

FocusOption

constsource
/** Moves focus to the radio option at the given index. */
const FocusOption: CommandDefinitionWithArgs<"FocusOption", {
  id: String
  index: Number
}, Effect<{
  _tag: "CompletedFocusOption"
}, never, never>>

Message

constsource
/** Union of all messages the radio group component can produce. */
const Message: S.Union<[typeof SelectedOption, typeof CompletedFocusOption]>

Model

constsource
/** Schema for the radio group component's state, tracking the selected value and orientation. */
const Model: Struct<{
  id: String
  orientation: Literals<readonly ["Horizontal", "Vertical"]>
  selectedValue: Option<String>
}>

OutMessage

constsource
const OutMessage: Union<readonly [
  CallableTaggedStruct<"Selected", {
    index: Number
    value: String
  }>
]>

Selected

constsource
/**
 * Sent to the parent when an option is committed. Carries the selected
 *  value and its index. Generic over `Value extends string`: the runtime
 *  schema stores `value: string`, but the type-level OutMessage exposes
 *  `value: Value` so consumers who supply `options: ReadonlyArray<MyUnion>`
 *  receive `value: MyUnion` from the factory's `update` without casting at
 *  the call site. The cast is fenced inside this module's `update` return,
 *  sound because the value was selected from the options array the
 *  consumer supplied.
 */
const Selected: CallableTaggedStruct<"Selected", {
  index: Number
  value: String
}>

SelectedOption

constsource
/** Sent when a radio option is selected via click or keyboard navigation. */
const SelectedOption: CallableTaggedStruct<"SelectedOption", {
  index: Number
  value: String
}>

Stay in the update loop.

New releases, patterns, and the occasional deep dive.


Built with Foldkit.

© 2026 Devin Jameson