Skip to content
PorabuildHeroGPUI component library
v0.14.0 · Apache-2.0

Pickers

Autocomplete

An autocomplete combines a select with filtering, allowing users to search and select from a list of options.

Rust
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.

Usage

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>.

Rust
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).

Placement

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>.

Rust
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).

Long Selected Value

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>.

Rust
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(),
    ])

Box Customisation

`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>.

Rust
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).

Hover Colours

`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>.

Rust
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).

Virtualization

`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>.

Rust
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).

Variants

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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).

In Surface

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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).

Full Width

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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).

With Description

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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).

Required

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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).

Disabled

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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).

With Disabled Options

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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).

Allows Empty Collection

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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).

With Sections

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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()])

Multiple Select

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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).

Controlled

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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).

Controlled Multiple

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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).

Controlled Open State

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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).

Asynchronous Filtering

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>.

Rust
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).

Custom Indicator

Runs in a view's render: self is the view holding this example's state and cx is its Context<Self>.

Rust
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).

Custom Value

`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>.

Rust
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.

languages
fn languages() -> Vec<SharedString> {
    vec![
        "Rust".into(),
        "TypeScript".into(),
        "Python".into(),
        "Go".into(),
        "Swift".into(),
        "Kotlin".into(),
    ]
}
language_items
/// `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()
}
MONO_FONT
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"
};
virtual_user
/// 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()),
    )
}
virtual_picker_items
/// 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()
}
para
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()
}
CURRENCIES
/// 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

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

Autocomplete styling
MethodValuesDescription
full_width(bool)true | falseFills the available horizontal space.
variant(FieldVariant)FieldVariant::Primary | FieldVariant::SecondarySelects the elevated or lower-emphasis field chrome.
placement(Placement)PopoverPlacementThe 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.
Rust
// 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

Autocomplete builders
BuilderValuesDefaultDescription
placeholder(value)string'Select an item'Temporary trigger text while no item is selected.
selection_mode(SelectionMode)SelectionMode::Single | SelectionMode::MultipleSelectionMode::SingleWhether one or multiple options may be selected.
allows_empty_collection(bool)true | falsefalseAllows the trigger to open when the unfiltered collection has no items.
is_open(bool)true | false—Controlled popover open state.
default_open(bool)true | falsefalseInitial popover state for an uncontrolled Autocomplete.
on_open_change(callback)Fn(isOpen: bool) -> ()—Reports trigger, keyboard, selection, blur, Escape and outside-press open changes.
disabled_keys(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(bool)true | falsefalseRemoves the trigger from interaction and form submission and prevents the popover from opening.
value(keys)Key | Key[] | null—Controlled selection as an iterable of item keys; the trigger renders each selected key's label once in selection insertion order.
default_value(keys)Key | Key[] | null—Uncontrolled selection seed as an iterable of item keys; the held selection survives filtering and reordering by key.
on_selection_change_all(callback)Fn(value: Key | Key[] | null) -> ()—Reports the complete selection as an empty, single-key or multiple-key slice.
is_required(bool)true | falsefalseMarks an empty successful field invalid for native Form submission and labels it required.
is_invalid(bool)true | falsefalseMarks the field invalid, applies invalid chrome and exposes invalid Form state.
name(value)string—Names the live FormField returned by form_field().
full_width(bool)true | falsefalseFills the available horizontal space.
focus_ring(bool)true | falsetrueGPUI extension: hides only the trigger's visual focus ring while retaining focus and popup behavior.
variant(FieldVariant)FieldVariant::Primary | FieldVariant::SecondaryFieldVariant::PrimarySelects the elevated or lower-emphasis field chrome.
new(state, items)PartialAnyElement | render closure—Builds the documented compound anatomy monolithically from state and items.
value_content(render)PartialAnyElement | render closure—Custom value content receives the local SelectionValue projection.
indicator(render)AnyElementAnyElement::DefaultChevronReplaces the trigger chevron; the local closure also receives the open state.
on_clear(callback)PartialFn(event: MouseEvent) -> ()—The local callback runs after selection clears but does not receive a browser MouseEvent; the button renders and clears without it.
placement(Placement)PopoverPlacementPlacement::BottomThe 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(callback)Fn(text: string, input: string) -> booluseFilter containsReplaces the built-in matcher with a caller-supplied predicate.
input_value(value)string—Controls the visible search text and filtering value.
on_input_change(callback)Fn(value: string) -> ()—Reports proposed search edits while a controlled value remains owner-owned.
Not ported8 HeroUI entries not ported
Autocomplete builders not ported
HeroUI v3Why it is not ported
Autocomplete.classNameAdditional classes have no GPUI analogue.
Autocomplete.Trigger.classNameAdditional classes for the trigger have no GPUI analogue.
Autocomplete.Value.classNameAdditional classes for the value have no GPUI analogue.
Autocomplete.Indicator.classNameAdditional classes for the indicator have no GPUI analogue.
Autocomplete.ClearButton.classNameAdditional classes for the clear button have no GPUI analogue.
Autocomplete.ClearButton.refBrowser element refs have no GPUI analogue.
Autocomplete.Popover.classNameAdditional classes for the popover have no GPUI analogue.
Autocomplete.Value Render Props.stateThe browser SelectState object is not exposed; the local closure receives its resolved value projection.

Parts

Autocomplete parts
PartDescription
AutocompleteRoot field stack owning selection, open state, filtering, form state and the floating panel.
AutocompletePartialField-shaped trigger with value, clear button and indicator; it is built in rather than independently composable.
AutocompletePartialSelected labels or placeholder, with a SelectionValue render closure.
AutocompleteDefault chevron or caller content receiving the current open state.
AutocompletePartialBuilt-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.
AutocompletePartialPlacement-aware overlay with pinned entry/exit motion, outside dismissal, SearchField and ListBox content.
AutocompletePartialSearch value and matcher boundary applied to the collection inside the popover.

States

Autocomplete states
StateBuilderDescription
OpenopenThe trigger indicator receives the live open state and the popover is mounted.
HoveredPartialhover_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 visiblering_if_focused(field, handle, true, ...)Keyboard-visible trigger focus draws the offset status ring.
Disabledis_disabledThe trigger leaves the tab order, cannot open and is omitted from FormData.
Read onlyis_read_onlyThe selection remains visible while opening and query edits are inert; gates the clear button on disabled only, so it keeps working.
Invalidis_invalid/error_messageInvalid chrome and error content replace the normal description and block native Form submission.
Requiredis_requiredThe label and FormField carry required state; an empty value blocks native submission.
Placeholderis_placeholderMissing selected items use the placeholder palette and value render state.
Empty selectionclear_emptyThe always-mounted clear affordance hides immediately and becomes pointer-inert while empty; a newly visible affordance fades in over 150ms.
Multiple selectionselection_mode(Multiple)Rows toggle membership, keep the panel open and report the whole selection.
Empty collectiontoggle_allowedAn empty unfiltered collection can open only when explicitly allowed.
EnteringPartialplacement_entry_offsetThe 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.
Exitingoverlay_phaseThe retained panel fades and shrinks to 95% over 100ms with ease-out-quad.