List Box
A list of options, nonselecting until a selection mode is set.
use herogpui::components::list_box::{ListBox, ListBoxItem};ListBox on docs.rsSource at v0.14.0
Usage
Showing Usage, live: HeroGPUI compiled to WebAssembly. The canvas has no accessibility tree; each example's description and code below carry the same information.
Labels use 14px text with 20px lines; descriptions and section headers use 12px text with 16px lines. An element expression with window: &mut Window and cx: &mut App in scope.
use herogpui::prelude::{ListBox, ListBoxItem, SelectionMode};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![gpui::div()
.w(px(220.))
.child(ListBox::new(
"list-box",
vec![
ListBoxItem::new("inbox", "Inbox"),
ListBoxItem::new("sent", "Sent"),
ListBoxItem::new("drafts", "Drafts"),
],
)
.selection_mode(SelectionMode::Single))
.into_any_element()])`row_padding_x` / `row_padding_y` resize the option rows; enabled rows use the 98% ease-out-quart press scale inside a stable footprint, and section headings keep their own inset. An element expression with window: &mut Window and cx: &mut App in scope.
use herogpui::prelude::{ActiveTheme, ListBox, ListBoxItem};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![gpui::div()
.w(px(220.))
.child(
ListBox::new(
"list-box-row-padding",
vec![
ListBoxItem::new("a", "Alpha"),
ListBoxItem::new("b", "Beta"),
],
)
.row_padding_x(px(16.))
.row_padding_y(px(2.))
.row_hover_bg(cx.colors().accent.soft()),
)
.into_any_element()])An element expression with window: &mut Window and cx: &mut App in scope.
use herogpui::prelude::{ListBox, ListBoxItem, SelectionMode};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![gpui::div()
.w(px(280.))
.child(
ListBox::new(
"list-box-disabled",
vec![
ListBoxItem::new("inbox", "Inbox"),
ListBoxItem::new("sent", "Sent"),
ListBoxItem::new("drafts", "Drafts"),
],
)
.selection_mode(SelectionMode::None)
.disabled_keys([SharedString::from("drafts")]),
)
.into_any_element()])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{ActiveTheme, ListBox, ListBoxItem};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{App, SharedString, px};
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![gpui::div()
.w(px(256.))
.child(ListBox::new(
"list-box-sections",
vec![
ListBoxItem::section("Actions"),
ListBoxItem::new("new-file", "New file")
.description("Create a new file")
.shortcut("⌘ N"),
ListBoxItem::new("edit-file", "Edit file")
.description("Make changes")
.shortcut("⌘ E"),
ListBoxItem::separator(),
ListBoxItem::section("Danger zone"),
ListBoxItem::new("delete-file", "Delete file")
.description("Move to trash")
.shortcut("⌘ ⇧ D")
.danger(),
],
).on_action(cx.listener(|this, key: &SharedString, _, cx| {
this.set_demo_text_value("lb-section-action", key.to_string());
cx.notify();
})))
.into_any_element(), para(&format!("Selected item: {}", self.demo_text_value("lb-section-action")), cx)])Also calls para (included when you copy).
Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{ListBox, ListBoxItem, SelectionMode};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
use std::collections::HashSet;
let selection = self.list_selection.clone();
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![gpui::div()
.w(px(280.))
.child(
ListBox::new(
"list-box-multi-select",
vec![
ListBoxItem::new("inbox", "Inbox"),
ListBoxItem::new("sent", "Sent"),
ListBoxItem::new("spam", "Spam"),
],
)
.selection_mode(SelectionMode::Multiple)
.selected_keys(selection.iter().cloned())
.on_selection_change(cx.listener(
|this, keys: &HashSet<SharedString>, _, cx| {
this.list_selection = keys.clone();
cx.notify();
},
)),
)
.into_any_element()])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{ActiveTheme, ListBox, ListBoxItem, SelectionMode};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{App, SharedString, px};
use herogpui::gpui;
use std::collections::HashSet;
let selection = self.list_selection.clone();
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![
gpui::div()
.w(px(280.))
.child(
ListBox::new(
"list-box-controlled",
vec![
ListBoxItem::new("inbox", "Inbox"),
ListBoxItem::new("sent", "Sent"),
ListBoxItem::new("spam", "Spam"),
],
)
.selection_mode(SelectionMode::Multiple)
.selected_keys(selection.iter().cloned())
// `onAction` fires on a press, selection or not.
.on_action(cx.listener(|this, key: &SharedString, _, cx| {
this.set_demo_text_value("lb-action", key.to_string());
cx.notify();
}))
.on_selection_change(cx.listener(
|this, keys: &HashSet<SharedString>, _, cx| {
this.list_selection = keys.clone();
cx.notify();
},
)),
)
.into_any_element(),
para(&format!("{} selected", selection.len()), cx),
])Also calls para (included when you copy).
The inherited selection policy keeps the final selected row selected, including when Escape would otherwise clear the collection. An element expression with window: &mut Window and cx: &mut App in scope.
use herogpui::prelude::{ListBox, ListBoxItem, SelectionMode};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![
gpui::div()
.w(px(280.))
.child(
ListBox::new(
"list-box-keep-selection",
vec![
ListBoxItem::new("inbox", "Inbox"),
ListBoxItem::new("sent", "Sent"),
],
)
.selection_mode(SelectionMode::Single)
.default_selected_keys([SharedString::from("inbox")])
.disallow_empty_selection(true),
)
.into_any_element(),
])Press Escape while this list is focused. The `None` policy preserves the selection and leaves Escape available to an enclosing surface. An element expression with window: &mut Window and cx: &mut App in scope.
use herogpui::prelude::{EscapeKeyBehavior, ListBox, ListBoxItem, SelectionMode};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![
gpui::div()
.w(px(280.))
.child(
ListBox::new(
"list-box-escape-none",
vec![
ListBoxItem::new("inbox", "Inbox"),
ListBoxItem::new("sent", "Sent"),
],
)
.selection_mode(SelectionMode::Single)
.default_selected_keys([SharedString::from("inbox")])
.escape_key_behavior(EscapeKeyBehavior::None),
)
.into_any_element(),
])`row_height` makes the list geometry computable instead of laid out, so a uniform `VirtualList` builds only the rows in view — one thousand users, fifty pixels each. The fixed-row list caps at `max_h`, shrinks below it in a bounded parent, and PageUp/PageDown move by the visible viewport, including after resize, while skipping disabled stops. An element expression with window: &mut Window and cx: &mut App in scope.
use herogpui::prelude::{ActiveTheme, ListBox, ListBoxItem, SelectionMode};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{App, px};
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![
gpui::div()
.w(px(300.))
.child(
ListBox::new("list-box-virtual", virtual_users())
.selection_mode(SelectionMode::None)
.row_height(px(50.))
.max_h(px(400.)),
)
.into_any_element(),
gpui::div()
.w(px(300.))
.child(
ListBox::new("list-box-virtual-var", virtual_users_described())
.selection_mode(SelectionMode::None)
.estimated_row_height(px(44.))
.heading_height(px(28.))
.max_h(px(400.)),
)
.into_any_element(),
para(
"`estimated_row_height` is the other half: rows that are \
*not* all one height, measured as they are built. Every \
third row here carries a description, so it is taller.",
cx,
),
])Also calls virtual_user, virtual_users, virtual_users_described, para (included when you copy).
HeroGPUI extension, not a example: `VirtualList` is the standalone variable-height virtual list. Rows are measured as they are built (every fifth row here is taller) and only rows near the viewport exist; its `VirtualListHandle` scrolls to a row from outside. Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{ActiveTheme, Button, ListBox, ListBoxItem, Variant, VirtualList, VirtualListScroll};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{AnyElement, px};
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![
gpui::div()
.flex()
.flex_wrap()
.items_center()
.justify_center()
.gap(px(12.))
.children(vec![
Button::new("list-box-jump")
.label("Row 500 to top")
.variant(Variant::Secondary)
.on_press(cx.listener(|this, _, _, cx| {
this.virtual_list.scroll_to_item(500, VirtualListScroll::Top);
cx.notify();
}))
.into_any_element(),
Button::new("list-box-first")
.label("Back to first")
.variant(Variant::Secondary)
.on_press(cx.listener(|this, _, _, cx| {
this.virtual_list.scroll_to_item(0, VirtualListScroll::Top);
cx.notify();
}))
.into_any_element(),
])
.into_any_element(),
gpui::div()
.w(px(320.))
.rounded(px(12.))
.border_1()
.border_color(cx.colors().border)
.child(
VirtualList::new("list-box-virtual-list-rows", &self.virtual_list, |ix, _, cx| {
let (name, email) = virtual_user(ix);
let tall = ix.is_multiple_of(5);
gpui::div()
.px(px(12.))
.py(px(if tall { 14. } else { 6. }))
.flex()
.flex_col()
.child(format!("{ix}. {name}"))
.when(tall, |row| {
row.child(
gpui::div()
.text_color(cx.colors().muted)
.child(email),
)
})
.into_any_element()
})
.height(px(260.)),
)
.into_any_element(),
])Also calls virtual_user, row (included when you copy).
HeroGPUI extension, not a example: `TreeView` is a hierarchy of `TreeItem`s with one tab stop. Up and Down move over the visible rows (skipping the disabled Secrets), Right expands a folder and then enters it, Left collapses it and then returns to the parent, and the chevron toggles a folder under the pointer. Selection follows the `ListBox` model. Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{ActiveTheme, ListBox, ListBoxItem, SelectionMode, TreeItem, TreeView};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{App, SharedString, px};
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![
gpui::div()
.w(px(280.))
.child(
TreeView::new(
"list-box-tree",
vec![
TreeItem::new("src", "src").children(vec![
TreeItem::new("components", "components").children(vec![
TreeItem::new("button", "button.rs"),
TreeItem::new("list-box-view", "tree_view.rs"),
]),
TreeItem::new("lib", "lib.rs"),
]),
TreeItem::new("docs", "docs").children(vec![
TreeItem::new("guide", "guide.md"),
]),
TreeItem::new("secrets", "secrets").is_disabled(true),
TreeItem::new("readme", "README.md"),
],
)
.selection_mode(SelectionMode::Single)
.default_expanded_keys([SharedString::from("src")])
.on_selection_change({
let view = cx.entity().downgrade();
move |keys, _, cx| {
let mut keys: Vec<String> = keys.iter().map(|k| k.to_string()).collect();
keys.sort();
view.update(cx, |this, cx| {
this.tree_selected = if keys.is_empty() {
SharedString::from("nothing")
} else {
SharedString::from(keys.join(", "))
};
cx.notify();
})
.ok();
}
}),
)
.into_any_element(),
para(&format!("Selected: {}", self.tree_selected), cx),
])Also calls para (included when you copy).
HeroGPUI extension: two thousand open rows under `max_h`. Only the rows in view are built, and the keyboard cursor scrolls into view. With `SelectionMode::Multiple`, a press or Space seats the anchor and Shift+Up/Down or a Shift press selects the visible rows between it and the target. Each row reports its level and its position among its siblings to assistive technology. An element expression with window: &mut Window and cx: &mut App in scope.
use herogpui::prelude::{ListBox, ListBoxItem, SelectionMode, TreeItem, TreeView};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![
gpui::div()
.w(px(280.))
.child(
TreeView::new(
"list-box-tree-large",
(0..50)
.map(|group| {
TreeItem::new(format!("list-box-{group}"), format!("Folder {group}"))
.children((0..40).map(|file| {
TreeItem::new(
format!("list-box-{group}-{file}"),
format!("file-{file}.rs"),
)
}))
})
.collect(),
)
.selection_mode(SelectionMode::Multiple)
.default_expanded_keys((0..50).map(|group| SharedString::from(format!("group-{group}"))))
.max_h(px(260.)),
)
.into_any_element(),
])There is no separate indicator part; a row's `variant` carries the indicator style, so the danger row below shows the same tick in its own colour. An element expression with window: &mut Window and cx: &mut App in scope.
use herogpui::prelude::{ListBox, ListBoxItem, SelectionMode};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![
gpui::div()
.w(px(280.))
.child(
ListBox::new(
"list-box-check",
vec![
ListBoxItem::new("keep", "Keep"),
ListBoxItem::new("delete", "Delete").danger(),
],
)
.selection_mode(SelectionMode::Multiple)
.default_selected_keys([SharedString::from("keep")]),
)
.into_any_element(),
])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{ListBox, ListBoxItem, SelectionMode};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
use std::collections::HashSet;
let selection = self.list_selection.clone();
let items = vec![
ListBoxItem::section("Mail"),
ListBoxItem::new("inbox", "Inbox").description("24 unread"),
ListBoxItem::new("sent", "Sent"),
ListBoxItem::new("drafts", "Drafts").is_disabled(true),
ListBoxItem::separator(),
ListBoxItem::new("trash", "Move to trash").danger(),
];
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![gpui::div()
.w(px(280.))
.child(
ListBox::new("list-box-single", items.clone())
.selection_mode(SelectionMode::Single)
.selected_keys(selection.iter().cloned())
.on_selection_change(cx.listener(
|this, keys: &HashSet<SharedString>, _, cx| {
this.list_selection = keys.clone();
cx.notify();
},
)),
)
.into_any_element()])Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{ListBox, ListBoxItem, SelectionMode};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
use std::collections::HashSet;
let selection = self.list_selection.clone();
let items = vec![
ListBoxItem::section("Mail"),
ListBoxItem::new("inbox", "Inbox").description("24 unread"),
ListBoxItem::new("sent", "Sent"),
ListBoxItem::new("drafts", "Drafts").is_disabled(true),
ListBoxItem::separator(),
ListBoxItem::new("trash", "Move to trash").danger(),
];
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![gpui::div()
.w(px(280.))
.child(
ListBox::new("list-box-multi", items)
.selection_mode(SelectionMode::Multiple)
.selected_keys(selection.iter().cloned())
.on_selection_change(cx.listener(
|this, keys: &HashSet<SharedString>, _, cx| {
this.list_selection = keys.clone();
cx.notify();
},
)),
)
.into_any_element()])The plainest list: single selection over rows that carry a label and a description, uncontrolled. An element expression with window: &mut Window and cx: &mut App in scope.
use herogpui::prelude::{ListBox, ListBoxItem, SelectionMode};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![gpui::div()
.w(px(280.))
.child(
ListBox::new(
"list-box-basic",
vec![
ListBoxItem::new("bob", "Bob")
.description("bob@example.com"),
ListBoxItem::new("alice", "Alice")
.description("alice@example.com"),
],
)
.selection_mode(SelectionMode::Single),
)
.into_any_element()])The selected keys live in the caller and are fed back through `selected_keys`; the caption reads the same set the list renders. Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{ActiveTheme, ListBox, ListBoxItem, SelectionMode};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{App, SharedString, px};
use herogpui::gpui;
use std::collections::HashSet;
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![
gpui::div()
.w(px(280.))
.child(
ListBox::new(
"list-box-controlled-selection",
vec![
ListBoxItem::new("opt-1", "Option 1"),
ListBoxItem::new("opt-2", "Option 2"),
ListBoxItem::new("opt-3", "Option 3"),
],
)
.selection_mode(SelectionMode::Multiple)
.selected_keys(self.lb_pair_selection.iter().cloned())
.on_selection_change(cx.listener(
|this, keys: &HashSet<SharedString>, _, cx| {
this.lb_pair_selection = keys.clone();
cx.notify();
},
)),
)
.into_any_element(),
para(&format!("{} selected", self.lb_pair_selection.len()), cx),
])Also calls para (included when you copy).
The pair-page twin of the check-icon example: a row's `variant` restyles its selection indicator, here a danger tick on the delete row. An element expression with window: &mut Window and cx: &mut App in scope.
use herogpui::prelude::{ListBox, ListBoxItem, SelectionMode};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![gpui::div()
.w(px(280.))
.child(
ListBox::new(
"list-box-indicator",
vec![
ListBoxItem::new("archive", "Archive"),
ListBoxItem::new("discard", "Discard").danger(),
],
)
.selection_mode(SelectionMode::Multiple)
.default_selected_keys([SharedString::from("archive")]),
)
.into_any_element()])Helpers used by these examples
Plain functions over the public API, defined once here and copied with each example that calls them.
fn para(text: &str, cx: &App) -> gpui::AnyElement {
let colors = cx.colors();
gpui::div()
.text_size(px(14.5))
.line_height(px(24.))
.text_color(colors.foreground)
.max_w(px(720.))
.child(text.to_owned())
.into_any_element()
}/// The nth of the thousand users v3's virtualization examples list, as
/// `(name, email)`. Same two twenty-name lists, so the rows read the same.
fn virtual_user(i: usize) -> (String, String) {
pub(super) const FIRST: [&str; 20] = [
"Emma",
"Liam",
"Olivia",
"Noah",
"Ava",
"James",
"Sophia",
"Oliver",
"Isabella",
"Lucas",
"Mia",
"Ethan",
"Charlotte",
"Mason",
"Amelia",
"Logan",
"Harper",
"Alexander",
"Ella",
"Benjamin",
];
pub(super) const LAST: [&str; 20] = [
"Smith",
"Johnson",
"Williams",
"Brown",
"Jones",
"Garcia",
"Miller",
"Davis",
"Rodriguez",
"Martinez",
"Anderson",
"Taylor",
"Thomas",
"Jackson",
"White",
"Harris",
"Clark",
"Lewis",
"Robinson",
"Walker",
];
let first = FIRST[i % FIRST.len()];
let last = LAST[(i / FIRST.len()) % LAST.len()];
(
format!("{first} {last}"),
format!("{}.{}@acme.com", first.to_lowercase(), last.to_lowercase()),
)
}/// A thousand list rows, for the virtualization demos.
fn virtual_users() -> Vec<ListBoxItem> {
(0..1000)
.map(|i| {
let (name, email) = virtual_user(i);
ListBoxItem::new(format!("user-{i}"), name).description(email)
})
.collect()
}/// The same thousand users, but every third row carries a description and a
/// section header lands every hundred -- rows of three different heights, which
/// is what `estimated_row_height` virtualizes.
fn virtual_users_described() -> Vec<ListBoxItem> {
let mut items = Vec::with_capacity(1010);
for i in 0..1000 {
if i % 100 == 0 {
items.push(ListBoxItem::section(format!("Batch {}", i / 100 + 1)));
}
let (name, email) = virtual_user(i);
let item = ListBoxItem::new(format!("user-{i}"), name);
items.push(if i % 3 == 0 {
item.description(email)
} else {
item
});
}
items
}fn row(children: Vec<AnyElement>) -> AnyElement {
gpui::div()
.flex()
.flex_wrap()
.items_center()
.justify_center()
.gap(px(12.))
.children(children)
.into_any_element()
}Anatomy
Rust types
List Box is assembled from these builders. The API reference lists each one.
Customization
Shared appearance belongs on ThemeBuilder::components and .recipe("name"). Typed builders change how List Box looks at one call site. Every builder also carries sx for placement, width and flex: it takes GPUI's own styling methods and refines them over the component's root after the theme, so an instance override wins. Behaviour builders live in the API reference below.
Styling
| Method | Values | Description |
|---|---|---|
variant(ListBoxItemVariant) | ListBoxItemVariant::Default | ListBoxItemVariant::Danger | Default visual variant inherited by options unless an item overrides it. |
// The one slot for caller-owned low-level styling: GPUI's
// styling methods, refined over the root element last.
.sx(|el| {
el.bg(gpui::rgba(0xffa500ff)) // background
.text_color(gpui::rgba(0x000000ff)) // text
.w(gpui::px(13.)) // size
.h(gpui::px(12.))
})API reference
Against HeroUI v3.2.6: 32 entries ported, 2 partial, 8 not ported. Partial rows are marked in the tables; entries not ported are listed under each table with the reason.
Builders
| Builder | Values | Default | Description |
|---|---|---|---|
selection_mode( | SelectionMode:: | SelectionMode:: | Chooses inert, single, or multiple selection semantics; the wrapper sets no default, so the one applies. |
selected_keys( | Selection | — | Controlled selected keys; the owner must accept reported changes. |
default_selected_keys( | Selection | — | Seeds the list's uncontrolled selected-key set once. |
on_selection_change( | Fn( | — | Reports the complete selected-key set after pointer and keyboard changes. |
disabled_keys( | iterable of keys | — | Removes matching options from pointer and keyboard selection stops. |
on_action( | Fn( | — | Reports item activation independently from selection where gives action priority. |
variant( | ListBoxItemVariant:: | ListBoxItemVariant:: | Default visual variant inherited by options unless an item overrides it. |
disallow_empty_selection( | true | | false | Inherited policy blocks the final-key toggle and Escape clearing. |
should_focus_wrap( | true | | false | Inherited policy joins the arrow-key ends. |
entry focus onlyPartial | bool | | false | Entry focus follows the selected key or first enabled option, but automatic mount focus is not exposed. |
escape_key_behavior( | EscapeKeyBehavior:: | EscapeKeyBehavior:: | Controls whether unmodified Escape clears a nonempty selection or remains available to an enclosing interaction. |
new( | AnyElement | — | Items, sections, and separators are supplied as the constructor collection. |
new( | Key | — | Stable option identity used for selection and callbacks. |
is_disabled( | true | | false | Disables one option independently from disabledKeys. |
item_content( | AnyElement | | — | Replacement row content receives selected, focused, focus-visible, pressed, and disabled state. |
indicator( | AnyElement | | checkmark | Replaces the selected indicator and receives isSelected. |
section( | AnyElement | — | Section headers and separators are explicit collection records. |
row_height( | number | | 48 | Fixed row geometry enables uniform-list virtualization; the virtual list caps at max_h, shrinks below it in a bounded parent, and pages by the visible viewport while skipping disabled stops. |
estimated_row_height( | number | | — | Estimated variable row geometry enables measured list virtualization. |
heading_height( | number | | 48 | Sets virtual section-row height. |
gap( | number | | 0 | Overrides the direct-child spacing. |
padding( | number | | 0 | Overrides the list inset. |
Not ported8 HeroUI entries not ported
| HeroUI v3 | Why it is not ported |
|---|---|
ListBox.selectionBehavior | Implements pinned toggle behavior only; replace-on-focus selection is unavailable. |
ListBox.renderEmptyState | An empty collection renders no synthetic option and has no empty-state slot. |
ListBox.layout / orientation | This port exposes the vertical stack layout only. |
ListBox.aria-label / aria-labelledby | GPUI has no accessibility tree attributes. |
ListBox.className / render | Browser styles and root-element substitution are unavailable. |
ListBox.Item.render | Root substitution and link attributes are unavailable. |
ListLayout.estimatedHeadingHeight | Section headings are single-line records rather than independently estimated layouts. |
ListLayout.loaderHeight / dropIndicatorThickness | This collection has neither a loader row nor drag-and-drop indicators. |
Parts
| Part | Description |
|---|---|
ListBox | Selection, focus, keyboard, scrolling, and virtualization owner. |
ListBoxItem | Selectable option with label, description, icon, shortcut, variant, and render state. |
ListBox | Built-in check or caller-rendered selected marker. |
ListBoxItemPartial | Non-selectable heading record grouping following options; nested section ownership is flattened. |
States
| State | Builder | Description |
|---|---|---|
Selected | selected_keys | Selection is shown by the trailing indicator without adding a row fill. |
Focused | cursor keyed state | Roving keyboard cursor tracks one enabled option. |
Focus visible | with_focus_ring | Keyboard focus draws the status ring and reaches item render state. |
Pressed | interaction | Interactive options shrink to 98% and expose pressed render state. |
Hovered | row hover chrome | Enabled options use the default hover fill; does not include hover in its documented item render-prop table. |
Disabled | is_disabled | Disabled options dim and leave pointer, arrow, typeahead, and selection stops. |