Dropdown
A versatile dropdown/select component that allows users to choose from a list of options. Supports search functionality, grouped items, custom rendering, and various states. Built with keyboard navigation and accessibility in mind.
Import
rust
use gpui_component::dropdown::{
Dropdown, DropdownState, DropdownItem, DropdownDelegate,
DropdownEvent, SearchableVec, DropdownItemGroup
};
Usage
Basic Dropdown
rust
let dropdown = cx.new(|cx| {
DropdownState::new(
vec!["Apple".into(), "Orange".into(), "Banana".into()],
Some(IndexPath::default()), // Select first item
window,
cx,
)
});
Dropdown::new(&dropdown)
With Placeholder
rust
let dropdown = cx.new(|cx| {
DropdownState::new(
vec!["Rust".into(), "Go".into(), "JavaScript".into()],
None, // No initial selection
window,
cx,
)
});
Dropdown::new(&dropdown)
.placeholder("Select a language...")
Searchable Dropdown
rust
let fruits = SearchableVec::new(vec![
"Apple".into(),
"Orange".into(),
"Banana".into(),
"Grape".into(),
"Pineapple".into(),
]);
let dropdown = cx.new(|cx| {
DropdownState::new(fruits, None, window, cx)
});
Dropdown::new(&dropdown)
.icon(IconName::Search) // Shows search icon
Custom Item Implementation
rust
#[derive(Debug, Clone)]
struct Country {
name: SharedString,
code: SharedString,
}
impl DropdownItem for Country {
type Value = SharedString;
fn title(&self) -> SharedString {
self.name.clone()
}
fn display_title(&self) -> Option<gpui::AnyElement> {
// Custom display for selected item
Some(format!("{} ({})", self.name, self.code).into_any_element())
}
fn value(&self) -> &Self::Value {
&self.code
}
fn matches(&self, query: &str) -> bool {
// Custom search logic
self.name.to_lowercase().contains(&query.to_lowercase()) ||
self.code.to_lowercase().contains(&query.to_lowercase())
}
}
Grouped Items
rust
let mut grouped_items = SearchableVec::new(vec![]);
// Group countries by first letter
grouped_items.push(
DropdownItemGroup::new("A")
.items(vec![
Country { name: "Australia".into(), code: "AU".into() },
Country { name: "Austria".into(), code: "AT".into() },
])
);
grouped_items.push(
DropdownItemGroup::new("B")
.items(vec![
Country { name: "Brazil".into(), code: "BR".into() },
Country { name: "Belgium".into(), code: "BE".into() },
])
);
let dropdown = cx.new(|cx| {
DropdownState::new(grouped_items, None, window, cx)
});
Dropdown::new(&dropdown)
Dropdown Sizes
rust
Dropdown::new(&dropdown).large()
Dropdown::new(&dropdown) // medium (default)
Dropdown::new(&dropdown).small()
Disabled State
rust
Dropdown::new(&dropdown).disabled(true)
Cleanable Dropdown
rust
Dropdown::new(&dropdown)
.cleanable() // Shows clear button when item is selected
Custom Width and Appearance
rust
Dropdown::new(&dropdown)
.w(px(320.)) // Set dropdown width
.menu_width(px(400.)) // Set menu popup width
.appearance(false) // Remove default styling
.title_prefix("Country: ") // Add prefix to selected title
Empty State
rust
let dropdown = cx.new(|cx| {
DropdownState::new(Vec::<SharedString>::new(), None, window, cx)
});
Dropdown::new(&dropdown)
.empty(
h_flex()
.h_24()
.justify_center()
.text_color(cx.theme().muted_foreground)
.child("No options available")
)
Handle Selection Events
rust
cx.subscribe_in(&dropdown, window, |view, state, event, window, cx| {
match event {
DropdownEvent::Confirm(value) => {
if let Some(selected_value) = value {
println!("Selected: {:?}", selected_value);
} else {
println!("Selection cleared");
}
}
}
});
Programmatic Selection
rust
// Set by index
dropdown.update(cx, |state, cx| {
state.set_selected_index(Some(IndexPath::default().row(2)), window, cx);
});
// Set by value (requires PartialEq on Value type)
dropdown.update(cx, |state, cx| {
state.set_selected_value(&"US".into(), window, cx);
});
// Get current selection
let current_value = dropdown.read(cx).selected_value();
Dynamic Items Update
rust
dropdown.update(cx, |state, cx| {
let new_items = vec!["New Option 1".into(), "New Option 2".into()];
state.set_items(new_items, window, cx);
});
API Reference
DropdownState
Method | Description |
---|---|
new(items, selected_index, window, cx) | Create new dropdown state |
set_selected_index(index, window, cx) | Set selected item by index |
set_selected_value(value, window, cx) | Set selected item by value |
selected_index(cx) | Get currently selected index |
selected_value() | Get currently selected value |
set_items(items, window, cx) | Update dropdown items |
focus(window, cx) | Focus the dropdown |
Dropdown
Method | Description |
---|---|
new(state) | Create dropdown with state entity |
placeholder(str) | Set placeholder text |
icon(icon) | Set custom icon (replaces arrow) |
title_prefix(str) | Add prefix to selected title |
cleanable() | Show clear button when selected |
disabled(bool) | Set disabled state |
appearance(bool) | Enable/disable default styling |
menu_width(width) | Set popup menu width |
empty(element) | Custom empty state element |
large() | Large size |
small() | Small size |
DropdownItem Trait
Method | Description |
---|---|
title() | Display text for the item |
display_title() | Custom element for selected item display |
value() | Value returned when selected |
matches(query) | Custom search matching logic |
DropdownDelegate Trait
Method | Description |
---|---|
sections_count(cx) | Number of sections (for grouping) |
section(index) | Section header element |
items_count(section) | Number of items in section |
item(index) | Get item at index path |
position(value) | Find index of item with value |
searchable() | Whether delegate supports search |
perform_search(query, window, cx) | Perform search operation |
SearchableVec
Method | Description |
---|---|
new(items) | Create searchable vector |
push(item) | Add item to vector |
DropdownItemGroup
Method | Description |
---|---|
new(title) | Create new group with title |
items(vec) | Set items for the group |
DropdownEvent
Event | Description |
---|---|
Confirm(Option<Value>) | Item selected or cleared |
Examples
Language Selector
rust
let languages = SearchableVec::new(vec![
"Rust".into(),
"TypeScript".into(),
"Go".into(),
"Python".into(),
"JavaScript".into(),
]);
let dropdown = cx.new(|cx| {
DropdownState::new(languages, None, window, cx)
});
Dropdown::new(&dropdown)
.placeholder("Select language...")
.title_prefix("Language: ")
.cleanable()
Country/Region Selector
rust
#[derive(Debug, Clone)]
struct Region {
name: SharedString,
code: SharedString,
flag: SharedString,
}
impl DropdownItem for Region {
type Value = SharedString;
fn title(&self) -> SharedString {
self.name.clone()
}
fn display_title(&self) -> Option<gpui::AnyElement> {
Some(
h_flex()
.items_center()
.gap_2()
.child(self.flag.clone())
.child(format!("{} ({})", self.name, self.code))
.into_any_element()
)
}
fn value(&self) -> &Self::Value {
&self.code
}
}
let regions = vec![
Region {
name: "United States".into(),
code: "US".into(),
flag: "🇺🇸".into()
},
Region {
name: "Canada".into(),
code: "CA".into(),
flag: "🇨🇦".into()
},
];
let dropdown = cx.new(|cx| {
DropdownState::new(regions, None, window, cx)
});
Dropdown::new(&dropdown)
.placeholder("Select country...")
Integrated with Input Field
rust
// Combined country code + phone input
h_flex()
.border_1()
.border_color(cx.theme().input)
.rounded_lg()
.w_full()
.gap_1()
.child(
div().w(px(140.)).child(
Dropdown::new(&country_dropdown)
.appearance(false) // No border/background
.py_2()
.pl_3()
)
)
.child(Divider::vertical())
.child(
div().flex_1().child(
TextInput::new(&phone_input)
.appearance(false)
.placeholder("Phone number")
.pr_3()
.py_2()
)
)
Multi-level Grouped Dropdown
rust
let mut grouped_countries = SearchableVec::new(vec![]);
for (continent, countries) in countries_by_continent {
grouped_countries.push(
DropdownItemGroup::new(continent)
.items(countries)
);
}
let dropdown = cx.new(|cx| {
DropdownState::new(grouped_countries, None, window, cx)
});
Dropdown::new(&dropdown)
.menu_width(px(350.))
.placeholder("Select country...")
Accessibility
- Full keyboard navigation (Arrow keys, Enter, Escape)
- Tab to focus dropdown
- Arrow keys to navigate options
- Enter to select current option
- Escape to close menu
- Screen reader support for selected values
- Focus management between dropdown and menu
- Clear focus indicators
- Proper ARIA attributes for dropdown state
Keyboard Shortcuts
Key | Action |
---|---|
Tab | Focus dropdown |
Enter | Open menu or select current item |
Up/Down | Navigate options (opens menu if closed) |
Escape | Close menu |
Space | Open menu |
Theming
The dropdown respects the current theme and uses the following theme tokens:
background
- Dropdown input backgroundinput
- Border colorforeground
- Text colormuted_foreground
- Placeholder and disabled textaccent
- Selected item backgroundaccent_foreground
- Placeholder text colorborder
- Menu borderradius
- Border radius