Autocomplete
An autocomplete combines a select with filtering, allowing users to search and select from a list of options.
use herogpui::components::autocomplete::Autocomplete;Autocomplete 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.
Values and options use 14px text with 20px lines. Section headers use 12px text with 16px lines and keep their own spacing. The popup anchors to the trigger with an 8px gap, flips when the preferred side cannot fit and the opposite side has more room, keeps the search visible, and scrolls the list within the available height up to 320px; virtual paging follows the visible list height. Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{Autocomplete, PickerItem};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![Autocomplete::new(
self.ac_entity.clone(),
language_items(),
)
.label("Language")
.placeholder("Select a language")
// `radius` is the detached panel's corner; the trigger
// keeps the field chrome's own.
.radius(px(8.))
.into_any_element()])Also calls languages, language_items (included when you copy).
The field panel can enter above its trigger; the four-pixel entry translation follows the resolved physical side after viewport flipping. Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{Autocomplete, PickerItem, Placement};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![Autocomplete::new(
self.demo_text("ac-placement-top", "", cx),
language_items(),
)
.label("Language")
.placeholder("Select a language")
.placement(Placement::Top)
.default_open(true)
.into_any_element()])Also calls languages, language_items (included when you copy).
Selected labels follow wrap-break-word value slot: the trigger grows vertically instead of clipping the label with an ellipsis. Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{Autocomplete, PickerItem};
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(260.))
.child(
Autocomplete::new(
self.demo_text("ac-long-value", "", cx),
vec![
PickerItem::new(
"long",
"A project name that is intentionally long enough to wrap",
),
PickerItem::new("short", "Short option"),
],
)
.label("Project")
.placeholder("Choose one")
.default_value(["long"]),
)
.into_any_element(),
])`height`, `padding_x` and `is_bare` size the trigger; `row_padding_x` / `row_padding_y` size the suggestion rows. 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, Autocomplete, PickerItem};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![Autocomplete::new(
self.demo_text("ac-custom-box", "", cx),
language_items(),
)
.label("Compact")
.placeholder("Pick one")
.height(px(28.))
.padding_x(px(4.))
.is_bare(true)
.row_padding_x(px(16.))
.row_padding_y(px(2.))
.row_hover_bg(cx.colors().accent.soft())
.font_family(MONO_FONT)
.row_font_family(MONO_FONT)
.into_any_element()])Also calls languages, language_items, MONO_FONT (included when you copy).
`trigger_hover_bg` names the fill a hovered trigger fades to and `clear_hover_bg` the clear button's hover fill, in place of `--field-hover` and `--default-hover`; hovering the clear button still suppresses the trigger's own hover. 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, Autocomplete, PickerItem};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![Autocomplete::new(
self.demo_text("ac-hover-colours", "", cx),
language_items(),
)
.label("Language")
.placeholder("Pick one")
.default_value(["rust"])
.trigger_hover_bg(cx.colors().accent.soft())
.clear_hover_bg(cx.colors().danger.soft())
.into_any_element()])Also calls languages, language_items (included when you copy).
`row_height` makes the list geometry computable, so gpui's `uniform_list` builds only the rows in view. A thousand options, forty pixels each. Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{Autocomplete, PickerItem};
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(256.))
.flex()
.flex_col()
.child(Autocomplete::new(
self.demo_text("ac-virtual", "", cx),
virtual_picker_items(),
)
.label("User")
.placeholder("Select a user")
.max_items(1000)
.row_height(px(40.)))
.into_any_element(),
])Also calls virtual_user, virtual_picker_items (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::{Autocomplete, FieldVariant, PickerItem};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![
Autocomplete::new(self.demo_text("ac-primary", "", cx), language_items())
.label("Primary")
.placeholder("Select a language")
.into_any_element(),
Autocomplete::new(self.demo_text("ac-secondary", "", cx), language_items())
.label("Secondary")
.placeholder("Select a language")
.variant(FieldVariant::Secondary)
.into_any_element(),
])Also calls languages, language_items (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::{Autocomplete, FieldVariant, PickerItem, Surface};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![Surface::new()
.padding(px(24.))
.child(
Autocomplete::new(self.demo_text("ac-surface", "", cx), language_items())
.label("Language")
.placeholder("Select a language")
.variant(FieldVariant::Secondary),
)
.into_any_element()])Also calls languages, language_items (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::{Autocomplete, PickerItem};
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(400.))
.child(
Autocomplete::new(
self.demo_text("ac-full", "", cx),
language_items(),
)
.label("Language")
.placeholder("Select a language")
.full_width(true),
)
.into_any_element()])Also calls languages, language_items (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::{Autocomplete, PickerItem};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![Autocomplete::new(
self.demo_text("ac-desc", "", cx),
language_items(),
)
.label("Language")
.placeholder("Select a language")
.description("Type to filter the list")
.into_any_element()])Also calls languages, language_items (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::{Autocomplete, PickerItem};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![Autocomplete::new(
self.demo_text("ac-required", "", cx),
language_items(),
)
.label("Language")
.placeholder("Select a language")
.is_required(true)
.into_any_element()])Also calls languages, language_items (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::{Autocomplete, PickerItem};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![Autocomplete::new(
self.demo_text("ac-disabled", "Rust", cx),
language_items(),
)
.label("Language")
.placeholder("Select a language")
.is_disabled(true)
.into_any_element()])Also calls languages, language_items (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::{Autocomplete, PickerItem};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![Autocomplete::new(
self.demo_text("ac-disabled-opts", "", cx),
language_items(),
)
.label("Language")
.placeholder("Select a language")
.disabled_keys([SharedString::from("go"), SharedString::from("python")])
.default_open(true)
.into_any_element()])Also calls languages, language_items (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::{Autocomplete, PickerItem};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![Autocomplete::new(
self.demo_text("ac-empty", "zzz", cx),
language_items(),
)
.label("Language")
.placeholder("Select a language")
.allows_empty_collection(true)
.default_open(true)
.into_any_element()])Also calls languages, language_items (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::{Autocomplete, PickerItem};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![Autocomplete::new(
self.demo_text("ac-sections", "", cx),
vec![
PickerItem::new("rust", "Rust"),
PickerItem::new("go", "Go"),
PickerItem::new("typescript", "TypeScript"),
PickerItem::new("python", "Python"),
],
)
.label("Language")
.placeholder("Select a language")
.section_before("rust", "Systems")
.section_before("typescript", "Scripting")
.default_open(true)
.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::{Autocomplete, PickerItem, SelectionMode};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![Autocomplete::new(
self.demo_text("ac-multi-select", "", cx),
language_items(),
)
.label("Languages")
.placeholder("Select languages")
.selection_mode(SelectionMode::Multiple)
// `defaultValue` is `Key | Key[]`: the uncontrolled
// selection, seeded once, by key.
.default_value(["rust"])
.default_open(true)
.into_any_element()])Also calls languages, language_items (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::{ActiveTheme, Autocomplete, PickerItem};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{App, SharedString, px};
use herogpui::gpui;
let ac_picked = self.demo_text_value("ac-picked");
let ac_typed = self.demo_text_value("ac-typed");
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![
Autocomplete::new(self.demo_text("ac-controlled", "", cx), language_items())
.label("Language")
.placeholder("Select a language")
.input_value(ac_typed)
.on_input_change(cx.listener(|this, text: &str, _, cx| {
this.set_demo_text_value("ac-typed", text.to_owned());
cx.notify();
}))
.on_change(cx.listener(|this, key: &SharedString, _, cx| {
this.set_demo_text_value("ac-picked", key.to_string());
cx.notify();
}))
.into_any_element(),
para(
&if ac_picked.is_empty() {
"Nothing picked yet".to_owned()
} else {
format!("Picked: {ac_picked}")
},
cx,
),
])Also calls languages, language_items, 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::{Autocomplete, PickerItem, SelectionMode};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
let ac_multi = self.demo_selection("ac-multi");
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![Autocomplete::new(
self.demo_text("ac-ctl-multi", "", cx),
language_items(),
)
.label("Languages")
.placeholder("Select languages")
.selection_mode(SelectionMode::Multiple)
.selected_keys(ac_multi.iter().cloned())
.on_selection_change_all(cx.listener(|this, keys: &[SharedString], _, cx| {
this.set_demo_selection("ac-multi", keys.to_vec());
cx.notify();
}))
.into_any_element()])Also calls languages, language_items (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::{ActiveTheme, Autocomplete, Button, PickerItem, Size, Variant};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{App, SharedString, px};
use herogpui::gpui;
let ac_open = self.demo_flag("ac-open", false);
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("autocomplete-open-btn")
.label(if ac_open { "Close" } else { "Open" })
.size(Size::Sm)
.variant(Variant::Secondary)
.on_press(cx.listener(move |this, _, _, cx| {
this.set_demo_flag("ac-open", !ac_open);
cx.notify();
}))
.into_any_element(),
para(if ac_open { "Open" } else { "Closed" }, cx),
])
.into_any_element(),
Autocomplete::new(self.demo_text("ac-open", "", cx), language_items())
.label("Language")
.placeholder("Select a language")
.is_open(ac_open)
.on_open_change(cx.listener(|this, v: &bool, _, cx| {
this.set_demo_flag("ac-open", *v);
cx.notify();
}))
.into_any_element(),
])Also calls para, languages, language_items (included when you copy).
The matches are fetched as the query changes. `filter` is the hook for that -- it decides what counts as a match -- and a spinner beside the field says a request is in flight. Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.
use herogpui::prelude::{Autocomplete, Filter, PickerItem, Sensitivity, Spinner, SpinnerSize};
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()
.flex()
.flex_wrap()
.items_center()
.justify_center()
.gap(px(12.))
.children(vec![
Autocomplete::new(self.demo_text("ac-async", "", cx), language_items())
.label("Language")
.placeholder("Select a language")
// `useFilter({sensitivity: "base"}).contains`:
// case and accents both ignored, so "cafe"
// finds "Café". The closure receives
// `(item_label, input)`.
.filter(|item, input| {
Filter::new(Sensitivity::Base).contains(item, input)
})
.into_any_element(),
Spinner::new("autocomplete-async-spinner")
.size(SpinnerSize::Sm)
.into_any_element(),
])
.into_any_element(),
])Also calls languages, language_items (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::{Autocomplete, PickerItem};
use herogpui::gpui::prelude::*;
use herogpui::gpui::{SharedString, px};
use herogpui::gpui;
gpui::div()
.flex()
.flex_col()
.w(px(256.))
.gap(px(12.))
.children(vec![Autocomplete::new(
self.demo_text("ac-indicator", "", cx),
language_items(),
)
.label("Languages")
.placeholder("Select languages")
.default_open(true)
.indicator(|is_open| {
gpui::div()
.text_size(px(16.))
.child(if is_open { "−" } else { "+" })
.into_any_element()
})
.into_any_element()])Also calls languages, language_items (included when you copy).
`Autocomplete.Value` takes a render function that is handed the placeholder and selection state — `is_placeholder`, `selected_keys`, `selected_items` and `selected_text`. This one draws the chosen currency as its symbol, code and muted name, and hands the default back while nothing is chosen. The port's rows carry one text run, so each currency row shows its code and name together, the text filters on. 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, Autocomplete, PickerItem, SelectionMode};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;
let ac_muted = cx.colors().muted;
gpui::div()
.flex()
.flex_col()
.items_start()
.justify_center()
.gap(px(12.))
.children(vec![
Autocomplete::new(
self.demo_text("ac-custom", "", cx),
CURRENCIES
.iter()
.map(|(key, code, name, _)| {
PickerItem::new(*key, format!("{code} {name}"))
})
.collect(),
)
.label("Currency")
.placeholder("Select a currency")
.selection_mode(SelectionMode::Single)
.default_value(["usd"])
.value_content(move |value| {
let selected = value
.selected_keys
.and_then(|keys| keys.first())
.and_then(|key| {
CURRENCIES.iter().find(|(id, ..)| *id == key.as_ref())
});
let Some((_, code, name, symbol)) = selected.filter(|_| !value.is_placeholder) else {
return value.default_children;
};
// `flex min-w-0 items-center gap-1.5`: the
// symbol in medium weight, the code, then the
// muted, truncating name.
gpui::div()
.flex()
.min_w_0()
.items_center()
.gap(px(6.))
.child(
gpui::div()
.font_weight(gpui::FontWeight::MEDIUM)
.child(*symbol),
)
.child(*code)
.child(
gpui::div()
.min_w_0()
.truncate()
.text_color(ac_muted)
.child(*name),
)
.into_any_element()
})
.into_any_element(),
])Also calls CURRENCIES (included when you copy).
Helpers used by these examples
Plain functions over the public API, defined once here and copied with each example that calls them.
fn languages() -> Vec<SharedString> {
vec![
"Rust".into(),
"TypeScript".into(),
"Python".into(),
"Go".into(),
"Swift".into(),
"Kotlin".into(),
]
}/// `languages()` as keyed items, for the Autocomplete and ComboBox demos: the
/// slug is the stable key the selection, `disabledKeys` and the form value
/// address, the display name the label the filtering, input text and
/// rendering use.
fn language_items() -> Vec<PickerItem> {
languages()
.into_iter()
.map(|label| PickerItem::new(label.to_lowercase(), label))
.collect()
}const MONO_FONT: &str = if cfg!(target_arch = "wasm32") {
// The web shell bundles JetBrains Mono; the literal family "monospace"
// resolves to nothing there and panics text resolution.
"JetBrains Mono"
} else if cfg!(target_os = "macos") {
"Menlo"
} else {
"Consolas"
};/// 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()),
)
}/// The same thousand names as keyed picker items: the key is the stable
/// `user-N` id, the label the visible name.
fn virtual_picker_items() -> Vec<PickerItem> {
(0..1000)
.map(|i| PickerItem::new(format!("autocomplete-{i}"), virtual_user(i).0))
.collect()
}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()
}/// v3.2.6's "Custom Value" currencies: `(id, code, name, symbol)`.
const CURRENCIES: [(&str, &str, &str, &str); 5] = [
("usd", "USD", "US Dollar", "$"),
("eur", "EUR", "Euro", "€"),
("gbp", "GBP", "British Pound", "£"),
("jpy", "JPY", "Japanese Yen", "¥"),
("chf", "CHF", "Swiss Franc", "₣"),
];Anatomy
Rust types
Autocomplete 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 Autocomplete 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 |
|---|---|---|
full_width(bool) | true | false | Fills the available horizontal space. |
variant(FieldVariant) | FieldVariant::Primary | FieldVariant::Secondary | Selects the elevated or lower-emphasis field chrome. |
placement(Placement) | PopoverPlacement | The full 22-value union renders: physical and aligned spellings plus the logical start/end aliases, which share their left/right side's pixels in this LTR-only port. The panel anchors to the measured trigger with an 8px gap and flips when the preferred side cannot fit and the opposite side has more room. |
// 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: 38 entries ported, 16 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 |
|---|---|---|---|
placeholder( | string | 'Select an item' | Temporary trigger text while no item is selected. |
selection_mode( | SelectionMode:: | SelectionMode:: | Whether one or multiple options may be selected. |
allows_empty_collection( | true | | false | Allows the trigger to open when the unfiltered collection has no items. |
is_open( | true | | — | Controlled popover open state. |
default_open( | true | | false | Initial popover state for an uncontrolled Autocomplete. |
on_open_change( | Fn( | — | Reports trigger, keyboard, selection, blur, Escape and outside-press open changes. |
disabled_keys( | iterable of keys | — | Keys of the options that remain visible but cannot be selected; per key, so one of two same-label items can be disabled alone. |
is_disabled( | true | | false | Removes the trigger from interaction and form submission and prevents the popover from opening. |
value( | Key | | — | Controlled selection as an iterable of item keys; the trigger renders each selected key's label once in selection insertion order. |
default_value( | Key | | — | Uncontrolled selection seed as an iterable of item keys; the held selection survives filtering and reordering by key. |
on_selection_change_all( | Fn( | — | Reports the complete selection as an empty, single-key or multiple-key slice. |
is_required( | true | | false | Marks an empty successful field invalid for native Form submission and labels it required. |
is_invalid( | true | | false | Marks the field invalid, applies invalid chrome and exposes invalid Form state. |
name( | string | — | Names the live FormField returned by form_field(). |
full_width( | true | | false | Fills the available horizontal space. |
focus_ring( | true | | true | GPUI extension: hides only the trigger's visual focus ring while retaining focus and popup behavior. |
variant( | FieldVariant:: | FieldVariant:: | Selects the elevated or lower-emphasis field chrome. |
new(Partial | AnyElement | | — | Builds the documented compound anatomy monolithically from state and items. |
value_content(Partial | AnyElement | | — | Custom value content receives the local SelectionValue projection. |
indicator( | AnyElement | AnyElement:: | Replaces the trigger chevron; the local closure also receives the open state. |
on_clear(Partial | Fn( | — | The local callback runs after selection clears but does not receive a browser MouseEvent; the button renders and clears without it. |
placement( | PopoverPlacement | Placement:: | The full 22-value union renders: physical and aligned spellings plus the logical start/end aliases, which share their left/right side's pixels in this LTR-only port. The panel anchors to the measured trigger with an 8px gap and flips when the preferred side cannot fit and the opposite side has more room. |
filter( | Fn( | useFilter contains | Replaces the built-in matcher with a caller-supplied predicate. |
input_value( | string | — | Controls the visible search text and filtering value. |
on_input_change( | Fn( | — | Reports proposed search edits while a controlled value remains owner-owned. |
Not ported8 HeroUI entries not ported
| HeroUI v3 | Why it is not ported |
|---|---|
Autocomplete.className | Additional classes have no GPUI analogue. |
Autocomplete.Trigger.className | Additional classes for the trigger have no GPUI analogue. |
Autocomplete.Value.className | Additional classes for the value have no GPUI analogue. |
Autocomplete.Indicator.className | Additional classes for the indicator have no GPUI analogue. |
Autocomplete.ClearButton.className | Additional classes for the clear button have no GPUI analogue. |
Autocomplete.ClearButton.ref | Browser element refs have no GPUI analogue. |
Autocomplete.Popover.className | Additional classes for the popover have no GPUI analogue. |
Autocomplete.Value Render Props.state | The browser SelectState object is not exposed; the local closure receives its resolved value projection. |
Parts
| Part | Description |
|---|---|
Autocomplete | Root field stack owning selection, open state, filtering, form state and the floating panel. |
AutocompletePartial | Field-shaped trigger with value, clear button and indicator; it is built in rather than independently composable. |
AutocompletePartial | Selected labels or placeholder, with a SelectionValue render closure. |
Autocomplete | Default chevron or caller content receiving the current open state. |
AutocompletePartial | Built-in clear affordance always mounted with the trigger; invisible and pointer-inert while the selection is empty, inert when disabled, and left working by read-only. |
AutocompletePartial | Placement-aware overlay with pinned entry/exit motion, outside dismissal, SearchField and ListBox content. |
AutocompletePartial | Search value and matcher boundary applied to the collection inside the popover. |
States
| State | Builder | Description |
|---|---|---|
Open | open | The trigger indicator receives the live open state and the popover is mounted. |
HoveredPartial | hover_fade_with_duration_and_easing_suppressed(150ms, EaseSmooth) | Enabled trigger hover interpolates the field background over 150ms ease-smooth while the clear button inside is not itself hovered; the border endpoint remains immediate. |
Focus visible | ring_if_focused(field, handle, true, ...) | Keyboard-visible trigger focus draws the offset status ring. |
Disabled | is_disabled | The trigger leaves the tab order, cannot open and is omitted from FormData. |
Read only | is_read_only | The selection remains visible while opening and query edits are inert; gates the clear button on disabled only, so it keeps working. |
Invalid | is_invalid/error_message | Invalid chrome and error content replace the normal description and block native Form submission. |
Required | is_required | The label and FormField carry required state; an empty value blocks native submission. |
Placeholder | is_placeholder | Missing selected items use the placeholder palette and value render state. |
Empty selection | clear_empty | The always-mounted clear affordance hides immediately and becomes pointer-inert while empty; a newly visible affordance fades in over 150ms. |
Multiple selection | selection_mode(Multiple) | Rows toggle membership, keep the panel open and report the whole selection. |
Empty collection | toggle_allowed | An empty unfiltered collection can open only when explicitly allowed. |
EnteringPartial | placement_entry_offset | The panel fades and grows from 95% over 250ms with ease-out-fluid and uses the resolved four-pixel placement slide; GPUI still has no transform-origin primitive. |
Exiting | overlay_phase | The retained panel fades and shrinks to 95% over 100ms with ease-out-quad. |