Builtin Library
README.md
selectableList
Luau builder for a single-select listbox composed of focusable per-row
canvases inside a panel. Same shape as Z.radioGroup, but rows draw
no glyph — they're highlight-on-selection labels — and the wrapping
panel declares role="listbox" / row role="option" so screen
readers read it as a single-select list.
Exports
selectableList(id: string, options: { any }?, selected: number?, opts: SelectableListOpts?) -> any— build a listbox widget. The module returns this function directly.
Types:
SelectableListOpts = { style: { [string]: any }?, classes: (string | { string })?, rowWidth: number?, rowHeight: number?, onChange: string?, app: any?, ariaLabel: string?, label: string? }
Usage
local selectableList = require("@builtin::modules.zui.widget.selectableList")
selectableList("category", CATEGORIES, state.cat, {
onChange = "ui:cat:change",
})
Notes
- Single-select;
selectedis a 1-based index. The first call seedsui.widgetState(id, "selected")from the caller's arg; subsequent ticks read state back from there. - Keyboard nav (per-row canvas
onKey):ArrowDown/ArrowRightnext,ArrowUp/ArrowLeftprevious,Home-> 1,End->#options. Tab walks the per-row canvases via egui's focus chain. - For sidebar / asset-browser / category-picker patterns where every
option is visible at once. For long lists where only a few items fit,
wrap the result in
Z.scroll(...). opts.onChangeis dispatched as a callback id on the surroundingZ.approuter withdata.valueset to the new 1-based index. Without an app in scope, onlyui.widgetStateupdates.- Handlers are registered once per group id (deduped via
app._selectableListHandlers); option-count andonChangeswaps land without re-registering.