select, list: Update searchable default to false. (#1504)

Continue #1503 to update the `searchable` default to `false`.
This commit is contained in:
Jason Lee 2025-11-04 20:59:38 +08:00 committed by GitHub
parent 2870bc6e36
commit 087cb70d00
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 37 additions and 31 deletions

View file

@ -304,6 +304,7 @@ impl DrawerStory {
) )
.child( .child(
List::new(&list) List::new(&list)
.searchable(true)
.border_1() .border_1()
.border_color(cx.theme().border) .border_color(cx.theme().border)
.rounded(cx.theme().radius) .rounded(cx.theme().radius)

View file

@ -536,6 +536,7 @@ impl Render for ListStory {
) )
.child( .child(
List::new(&self.company_list) List::new(&self.company_list)
.searchable(true)
.p(px(8.)) .p(px(8.))
.flex_1() .flex_1()
.w_full() .w_full()

View file

@ -191,6 +191,7 @@ impl Render for SelectStory {
.child( .child(
section("Select").max_w_128().child( section("Select").max_w_128().child(
Select::new(&self.country_select) Select::new(&self.country_select)
.searchable(true)
.search_placeholder("Search country by name or code") .search_placeholder("Search country by name or code")
.cleanable() .cleanable()
.disabled(self.disabled), .disabled(self.disabled),
@ -199,6 +200,7 @@ impl Render for SelectStory {
.child( .child(
section("Searchable").max_w_128().child( section("Searchable").max_w_128().child(
Select::new(&self.fruit_select) Select::new(&self.fruit_select)
.searchable(true)
.disabled(self.disabled) .disabled(self.disabled)
.icon(IconName::Search) .icon(IconName::Search)
.w(px(320.)) .w(px(320.))

View file

@ -61,7 +61,7 @@ impl Default for ListOptions {
scrollbar_visible: true, scrollbar_visible: true,
max_height: None, max_height: None,
selectable: true, selectable: true,
searchable: true, searchable: false,
search_placeholder: None, search_placeholder: None,
paddings: EdgesRefinement::default(), paddings: EdgesRefinement::default(),
} }
@ -70,9 +70,9 @@ impl Default for ListOptions {
/// The state for List. /// The state for List.
pub struct ListState<D: ListDelegate> { pub struct ListState<D: ListDelegate> {
focus_handle: FocusHandle, pub(crate) focus_handle: FocusHandle,
pub(crate) query_input: Entity<InputState>,
options: ListOptions, options: ListOptions,
query_input: Option<Entity<InputState>>,
delegate: D, delegate: D,
last_query: Option<String>, last_query: Option<String>,
scroll_handle: VirtualListScrollHandle, scroll_handle: VirtualListScrollHandle,
@ -104,7 +104,7 @@ where
options: ListOptions::default(), options: ListOptions::default(),
delegate, delegate,
rows_cache: RowsCache::default(), rows_cache: RowsCache::default(),
query_input: Some(query_input), query_input,
last_query: None, last_query: None,
selected_index: None, selected_index: None,
item_to_measure_index: IndexPath::default(), item_to_measure_index: IndexPath::default(),
@ -131,6 +131,11 @@ where
self.focus_handle(cx).focus(window); self.focus_handle(cx).focus(window);
} }
/// Return true if either the list or the search input is focused.
pub(crate) fn is_focused(&self, window: &Window, cx: &App) -> bool {
self.focus_handle.is_focused(window) || self.query_input.focus_handle(cx).is_focused(window)
}
/// Set the selected index of the list, /// Set the selected index of the list,
/// this will also scroll to the selected item. /// this will also scroll to the selected item.
pub(crate) fn _set_selected_index( pub(crate) fn _set_selected_index(
@ -254,10 +259,8 @@ where
} }
fn set_searching(&mut self, searching: bool, window: &mut Window, cx: &mut Context<Self>) { fn set_searching(&mut self, searching: bool, window: &mut Window, cx: &mut Context<Self>) {
if let Some(input) = &self.query_input { self.query_input
input.update(cx, |input, cx| input.set_loading(searching, window, cx)) .update(cx, |input, cx| input.set_loading(searching, window, cx));
}
cx.notify();
} }
/// Dispatch delegate's `load_more` method when the /// Dispatch delegate's `load_more` method when the
@ -523,12 +526,8 @@ where
D: ListDelegate, D: ListDelegate,
{ {
fn focus_handle(&self, cx: &App) -> FocusHandle { fn focus_handle(&self, cx: &App) -> FocusHandle {
if !self.options.searchable { if self.options.searchable {
return self.focus_handle.clone(); self.query_input.focus_handle(cx)
}
if let Some(query_input) = &self.query_input {
query_input.focus_handle(cx)
} else { } else {
self.focus_handle.clone() self.focus_handle.clone()
} }
@ -552,16 +551,12 @@ where
let loading = self.delegate().loading(cx); let loading = self.delegate().loading(cx);
let query_input = if self.options.searchable { let query_input = if self.options.searchable {
// sync placeholder // sync placeholder
if let Some(query_input) = &self.query_input { if let Some(placeholder) = &self.options.search_placeholder {
if let Some(placeholder) = &self.options.search_placeholder { self.query_input.update(cx, |input, cx| {
query_input.update(cx, |input, cx| { input.set_placeholder(placeholder.clone(), window, cx);
input.set_placeholder(placeholder.clone(), window, cx); });
});
}
Some(query_input.clone())
} else {
None
} }
Some(self.query_input.clone())
} else { } else {
None None
}; };
@ -591,7 +586,7 @@ where
.size_full() .size_full()
.relative() .relative()
.overflow_hidden() .overflow_hidden()
.when_some(query_input.clone(), |this, input| { .when_some(query_input, |this, input| {
this.child( this.child(
div() div()
.map(|this| match self.options.size { .map(|this| match self.options.size {
@ -670,7 +665,7 @@ where
self self
} }
/// Sets whether the list is searchable, default is `true`. /// Sets whether the list is searchable, default is `false`.
/// ///
/// When `true`, there will be a search input at the top of the list. /// When `true`, there will be a search input at the top of the list.
pub fn searchable(mut self, searchable: bool) -> Self { pub fn searchable(mut self, searchable: bool) -> Self {

View file

@ -324,7 +324,7 @@ impl Default for SelectOptions {
menu_width: Length::Auto, menu_width: Length::Auto,
disabled: false, disabled: false,
appearance: true, appearance: true,
searchable: true, searchable: false,
search_placeholder: None, search_placeholder: None,
} }
} }
@ -559,9 +559,12 @@ where
}; };
let list = cx.new(|cx| ListState::new(delegate, window, cx).reset_on_cancel(false)); let list = cx.new(|cx| ListState::new(delegate, window, cx).reset_on_cancel(false));
let list_focus_handle = list.read(cx).focus_handle.clone();
let list_search_focus_handle = list.read(cx).query_input.focus_handle(cx);
let _subscriptions = vec![ let _subscriptions = vec![
cx.on_blur(&list.focus_handle(cx), window, Self::on_blur), cx.on_blur(&list_focus_handle, window, Self::on_blur),
cx.on_blur(&list_search_focus_handle, window, Self::on_blur),
cx.on_blur(&focus_handle, window, Self::on_blur), cx.on_blur(&focus_handle, window, Self::on_blur),
]; ];
@ -645,7 +648,7 @@ where
fn on_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) { fn on_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
// When the select and dropdown menu are both not focused, close the dropdown menu. // When the select and dropdown menu are both not focused, close the dropdown menu.
if self.list.focus_handle(cx).is_focused(window) || self.focus_handle.is_focused(window) { if self.list.read(cx).is_focused(window, cx) || self.focus_handle.is_focused(window) {
return; return;
} }
@ -954,7 +957,7 @@ where
self self
} }
/// Sets whether the dropdown menu is searchable, default is `true`. /// Sets whether the dropdown menu is searchable, default is `false`.
/// ///
/// When `true`, there will be a search input at the top of the dropdown menu. /// When `true`, there will be a search input at the top of the dropdown menu.
pub fn searchable(mut self, searchable: bool) -> Self { pub fn searchable(mut self, searchable: bool) -> Self {

View file

@ -171,6 +171,8 @@ fn render_item(
The list automatically includes a search input by default. Implement `perform_search` to handle queries: The list automatically includes a search input by default. Implement `perform_search` to handle queries:
And you should use `searchable(true)` when creating the list to show search input.
```rust ```rust
impl ListDelegate for MyListDelegate { impl ListDelegate for MyListDelegate {
fn perform_search( fn perform_search(
@ -190,9 +192,8 @@ impl ListDelegate for MyListDelegate {
} }
} }
// Create list without search input
let state = cx.new(|cx| ListState::new(delegate, window, cx)); let state = cx.new(|cx| ListState::new(delegate, window, cx));
List::new(&state).searchable(false) List::new(&state).searchable(true)
``` ```
### List with Loading State ### List with Loading State

View file

@ -65,6 +65,8 @@ Select::new(&state)
### Searchable ### Searchable
Use `searchable(true)` to enable search functionality within the dropdown.
```rust ```rust
let fruits = SearchableVec::new(vec![ let fruits = SearchableVec::new(vec![
"Apple", "Orange", "Banana", "Grape", "Pineapple", "Apple", "Orange", "Banana", "Grape", "Pineapple",
@ -75,6 +77,7 @@ let state = cx.new(|cx| {
}); });
Select::new(&state) Select::new(&state)
.searchable(true)
.icon(IconName::Search) // Shows search icon .icon(IconName::Search) // Shows search icon
``` ```