4.3k

Select

select is a trigger-only primitive — it takes no options attribute. Its content is the current value (placeholder shows while empty) and on-press opens. The options are composed as an anchored dropdown-menu of menu-items beside the trigger in a stack, rendered under an if — the model owns the open flag, and on-dismiss clears it when Escape, Tab, or a click outside closes the surface. In the open menu the selected option wears a trailing checkmark, and the row under the keyboard or pointer carries a full-row highlight — the two are independent, so arrowing away from the committed option never moves its mark. For a trigger the user types into, see combobox.

select
Select triggers rendered by the engine (light theme)
a select trigger with a value, and a disabled trigger — press to open the anchored menu

Markup

The full pattern: trigger and menu are siblings in a stack, the menu floats with anchor="below" (anchor-alignment="stretch" widens it to the trigger, the select-menu look), and every path back to closed goes through the model — picking dispatches pick_env and on-dismiss dispatches close_env_picker, both of which clear env_picker_open in update.

The keyboard rides the same composition with no extra wiring: on the focused trigger, Enter, Space, or an arrow key presses it (the model-owned open); with the menu mounted, Down/Up move into and walk the options (entering at the selected one) and Home/End jump to the edges — walking only moves the highlight, it never dispatches a pick. Enter or a click commits the highlighted option (one on-press, then the model closes); Escape, Tab, or a click outside dismisses through on-dismiss with the committed value untouched — focus returns to the trigger either way.

<stack width="240">
  <select placeholder="Choose environment" on-press="toggle_env_picker">{envName}</select>
  <if test="{env_picker_open}">
    <dropdown-menu anchor="below" anchor-alignment="stretch" on-dismiss="close_env_picker">
      <for each="environments" key="id" as="e">
        <menu-item selected="{e.id == env_id}" on-press="pick_env:{e.id}">{e.name}</menu-item>
      </for>
    </dropdown-menu>
  </if>
</stack>

Zig builder

ui.stack(.{ .width = 240 }, .{
    ui.el(.select, .{
        .text = model.envName(),
        .placeholder = "Choose environment",
        .on_press = .toggle_env_picker,
    }, .{}),
    if (model.env_picker_open) ui.el(.dropdown_menu, .{
        .anchor = .below,
        .anchor_alignment = .stretch,
        .on_dismiss = .close_env_picker,
    }, .{
        ui.el(.menu_item, .{ .text = "Production", .selected = model.env == .production, .on_press = .{ .pick_env = .production } }, .{}),
        ui.el(.menu_item, .{ .text = "Staging", .selected = model.env == .staging, .on_press = .{ .pick_env = .staging } }, .{}),
        ui.el(.menu_item, .{ .text = "Development", .selected = model.env == .development, .on_press = .{ .pick_env = .development } }, .{}),
    }) else ui.spacer(0),
})

Attributes

AttributeDescription
textText value for text-bearing elements; a literal or one {binding}.
placeholderHint text shown while a text entry is empty.
disabledDisables the control; true/false or a {binding}.
on-pressDispatch a Msg on press: tag or tag:{payload}. Legal on any element — a bound press handler makes it pressable, and presses on plain text/icons inside it fall through to it (dragging still selects text).
on-dismissDismissible surfaces only (dialog, drawer, sheet, dropdown-menu): Msg dispatched when Escape or a click outside dismisses the surface, so the MODEL owns the close (clear the open flag in update). The engine hides the surface immediately as an optimistic echo; the source tree wins on the next rebuild.