chore: Move searchable to ListState and SelectState. (#1507)
This commit is contained in:
parent
087cb70d00
commit
d037f074dc
9 changed files with 50 additions and 54 deletions
|
|
@ -243,7 +243,7 @@ impl DrawerStory {
|
|||
matches: items.clone(),
|
||||
};
|
||||
let list = cx.new(|cx| {
|
||||
let mut list = ListState::new(delegate, window, cx);
|
||||
let mut list = ListState::new(delegate, window, cx).searchable(true);
|
||||
list.focus(window, cx);
|
||||
list
|
||||
});
|
||||
|
|
@ -304,7 +304,6 @@ impl DrawerStory {
|
|||
)
|
||||
.child(
|
||||
List::new(&list)
|
||||
.searchable(true)
|
||||
.border_1()
|
||||
.border_color(cx.theme().border)
|
||||
.rounded(cx.theme().radius)
|
||||
|
|
|
|||
|
|
@ -367,7 +367,7 @@ impl ListStory {
|
|||
};
|
||||
delegate.extend_more(100);
|
||||
|
||||
let company_list = cx.new(|cx| ListState::new(delegate, window, cx));
|
||||
let company_list = cx.new(|cx| ListState::new(delegate, window, cx).searchable(true));
|
||||
|
||||
let _subscriptions =
|
||||
vec![
|
||||
|
|
@ -536,7 +536,6 @@ impl Render for ListStory {
|
|||
)
|
||||
.child(
|
||||
List::new(&self.company_list)
|
||||
.searchable(true)
|
||||
.p(px(8.))
|
||||
.flex_1()
|
||||
.w_full()
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ impl SelectStory {
|
|||
window,
|
||||
cx,
|
||||
)
|
||||
.searchable(true)
|
||||
});
|
||||
let appearance_select = cx.new(|cx| {
|
||||
SelectState::new(
|
||||
|
|
@ -111,7 +112,7 @@ impl SelectStory {
|
|||
"Watermelon & This is a long long long long long long long long long title",
|
||||
"Avocado",
|
||||
]);
|
||||
let fruit_select = cx.new(|cx| SelectState::new(fruits, None, window, cx));
|
||||
let fruit_select = cx.new(|cx| SelectState::new(fruits, None, window, cx).searchable(true));
|
||||
|
||||
cx.new(|cx| {
|
||||
cx.subscribe_in(&country_select, window, Self::on_select_event)
|
||||
|
|
@ -191,7 +192,6 @@ impl Render for SelectStory {
|
|||
.child(
|
||||
section("Select").max_w_128().child(
|
||||
Select::new(&self.country_select)
|
||||
.searchable(true)
|
||||
.search_placeholder("Search country by name or code")
|
||||
.cleanable()
|
||||
.disabled(self.disabled),
|
||||
|
|
@ -200,7 +200,6 @@ impl Render for SelectStory {
|
|||
.child(
|
||||
section("Searchable").max_w_128().child(
|
||||
Select::new(&self.fruit_select)
|
||||
.searchable(true)
|
||||
.disabled(self.disabled)
|
||||
.icon(IconName::Search)
|
||||
.w(px(320.))
|
||||
|
|
|
|||
|
|
@ -327,11 +327,7 @@ impl Render for CodeActionMenu {
|
|||
.top(pos.y)
|
||||
.max_w(max_width)
|
||||
.min_w(px(120.))
|
||||
.child(
|
||||
List::new(&self.list)
|
||||
.searchable(false)
|
||||
.max_h(MAX_MENU_HEIGHT),
|
||||
)
|
||||
.child(List::new(&self.list).max_h(MAX_MENU_HEIGHT))
|
||||
.child(
|
||||
canvas(
|
||||
move |bounds, _, cx| view.update(cx, |r, _| r.bounds = bounds),
|
||||
|
|
|
|||
|
|
@ -428,11 +428,7 @@ impl Render for CompletionMenu {
|
|||
editor_popover("completion-menu", cx)
|
||||
.max_w(max_width)
|
||||
.min_w(px(120.))
|
||||
.child(
|
||||
List::new(&self.list)
|
||||
.searchable(false)
|
||||
.max_h(MAX_MENU_HEIGHT),
|
||||
)
|
||||
.child(List::new(&self.list).max_h(MAX_MENU_HEIGHT))
|
||||
.child(
|
||||
canvas(
|
||||
move |bounds, _, cx| view.update(cx, |r, _| r.bounds = bounds),
|
||||
|
|
|
|||
|
|
@ -47,8 +47,6 @@ pub enum ListEvent {
|
|||
struct ListOptions {
|
||||
size: Size,
|
||||
scrollbar_visible: bool,
|
||||
selectable: bool,
|
||||
searchable: bool,
|
||||
search_placeholder: Option<SharedString>,
|
||||
max_height: Option<Length>,
|
||||
paddings: EdgesRefinement<DefiniteLength>,
|
||||
|
|
@ -60,8 +58,6 @@ impl Default for ListOptions {
|
|||
size: Size::default(),
|
||||
scrollbar_visible: true,
|
||||
max_height: None,
|
||||
selectable: true,
|
||||
searchable: false,
|
||||
search_placeholder: None,
|
||||
paddings: EdgesRefinement::default(),
|
||||
}
|
||||
|
|
@ -83,6 +79,8 @@ pub struct ListState<D: ListDelegate> {
|
|||
deferred_scroll_to_index: Option<(IndexPath, ScrollStrategy)>,
|
||||
mouse_right_clicked_index: Option<IndexPath>,
|
||||
reset_on_cancel: bool,
|
||||
searchable: bool,
|
||||
selectable: bool,
|
||||
_search_task: Task<()>,
|
||||
_load_more_task: Task<()>,
|
||||
_query_input_subscription: Subscription,
|
||||
|
|
@ -107,6 +105,8 @@ where
|
|||
query_input,
|
||||
last_query: None,
|
||||
selected_index: None,
|
||||
selectable: true,
|
||||
searchable: false,
|
||||
item_to_measure_index: IndexPath::default(),
|
||||
deferred_scroll_to_index: None,
|
||||
mouse_right_clicked_index: None,
|
||||
|
|
@ -119,6 +119,25 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Sets whether the list is searchable, default is `false`.
|
||||
///
|
||||
/// When `true`, there will be a search input at the top of the list.
|
||||
pub fn searchable(mut self, searchable: bool) -> Self {
|
||||
self.searchable = searchable;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_searchable(&mut self, searchable: bool, cx: &mut Context<Self>) {
|
||||
self.searchable = searchable;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
/// Sets whether the list is selectable, default is true.
|
||||
pub fn selectable(mut self, selectable: bool) -> Self {
|
||||
self.selectable = selectable;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn delegate(&self) -> &D {
|
||||
&self.delegate
|
||||
}
|
||||
|
|
@ -402,7 +421,7 @@ where
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
let selectable = self.options.selectable;
|
||||
let selectable = self.selectable;
|
||||
let selected = self.selected_index.map(|s| s.eq_row(ix)).unwrap_or(false);
|
||||
let mouse_right_clicked = self
|
||||
.mouse_right_clicked_index
|
||||
|
|
@ -526,7 +545,7 @@ where
|
|||
D: ListDelegate,
|
||||
{
|
||||
fn focus_handle(&self, cx: &App) -> FocusHandle {
|
||||
if self.options.searchable {
|
||||
if self.searchable {
|
||||
self.query_input.focus_handle(cx)
|
||||
} else {
|
||||
self.focus_handle.clone()
|
||||
|
|
@ -549,7 +568,7 @@ where
|
|||
}
|
||||
|
||||
let loading = self.delegate().loading(cx);
|
||||
let query_input = if self.options.searchable {
|
||||
let query_input = if self.searchable {
|
||||
// sync placeholder
|
||||
if let Some(placeholder) = &self.options.search_placeholder {
|
||||
self.query_input.update(cx, |input, cx| {
|
||||
|
|
@ -659,20 +678,6 @@ where
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets whether the list is selectable, default is true.
|
||||
pub fn selectable(mut self, selectable: bool) -> Self {
|
||||
self.options.selectable = selectable;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether the list is searchable, default is `false`.
|
||||
///
|
||||
/// When `true`, there will be a search input at the top of the list.
|
||||
pub fn searchable(mut self, searchable: bool) -> Self {
|
||||
self.options.searchable = searchable;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the placeholder text for the search input.
|
||||
pub fn search_placeholder(mut self, placeholder: impl Into<SharedString>) -> Self {
|
||||
self.options.search_placeholder = Some(placeholder.into());
|
||||
|
|
|
|||
|
|
@ -303,7 +303,6 @@ struct SelectOptions {
|
|||
cleanable: bool,
|
||||
placeholder: Option<SharedString>,
|
||||
title_prefix: Option<SharedString>,
|
||||
searchable: bool,
|
||||
search_placeholder: Option<SharedString>,
|
||||
empty: Option<AnyElement>,
|
||||
menu_width: Length,
|
||||
|
|
@ -324,7 +323,6 @@ impl Default for SelectOptions {
|
|||
menu_width: Length::Auto,
|
||||
disabled: false,
|
||||
appearance: true,
|
||||
searchable: false,
|
||||
search_placeholder: None,
|
||||
}
|
||||
}
|
||||
|
|
@ -334,6 +332,7 @@ impl Default for SelectOptions {
|
|||
pub struct SelectState<D: SelectDelegate + 'static> {
|
||||
focus_handle: FocusHandle,
|
||||
options: SelectOptions,
|
||||
searchable: bool,
|
||||
list: Entity<ListState<SelectListDelegate<D>>>,
|
||||
empty: Option<Box<dyn Fn(&Window, &App) -> AnyElement>>,
|
||||
/// Store the bounds of the input
|
||||
|
|
@ -571,6 +570,7 @@ where
|
|||
let mut this = Self {
|
||||
focus_handle,
|
||||
options: SelectOptions::default(),
|
||||
searchable: false,
|
||||
list,
|
||||
selected_value: None,
|
||||
open: false,
|
||||
|
|
@ -583,6 +583,14 @@ where
|
|||
this
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn searchable(mut self, searchable: bool) -> Self {
|
||||
self.searchable = searchable;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the selected index for the select.
|
||||
pub fn set_selected_index(
|
||||
&mut self,
|
||||
|
|
@ -772,6 +780,7 @@ where
|
|||
D: SelectDelegate + 'static,
|
||||
{
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let searchable = self.searchable;
|
||||
let is_focused = self.focus_handle.is_focused(window);
|
||||
let show_clean = self.options.cleanable && self.selected_index(cx).is_some();
|
||||
let bounds = self.bounds;
|
||||
|
|
@ -779,6 +788,9 @@ where
|
|||
let outline_visible = self.open || is_focused && !self.options.disabled;
|
||||
let popup_radius = cx.theme().radius.min(px(8.));
|
||||
|
||||
self.list
|
||||
.update(cx, |list, cx| list.set_searchable(searchable, cx));
|
||||
|
||||
div()
|
||||
.size_full()
|
||||
.relative()
|
||||
|
|
@ -888,7 +900,6 @@ where
|
|||
.shadow_md()
|
||||
.child(
|
||||
List::new(&self.list)
|
||||
.searchable(self.options.searchable)
|
||||
.when_some(
|
||||
self.options.search_placeholder.clone(),
|
||||
|this, placeholder| {
|
||||
|
|
@ -957,14 +968,6 @@ where
|
|||
self
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn searchable(mut self, searchable: bool) -> Self {
|
||||
self.options.searchable = searchable;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the placeholder text for the search input.
|
||||
pub fn search_placeholder(mut self, placeholder: impl Into<SharedString>) -> Self {
|
||||
self.options.search_placeholder = Some(placeholder.into());
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ fn render_item(
|
|||
|
||||
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.
|
||||
And you should use `searchable(true)` when creating the `ListState` to show search input.
|
||||
|
||||
```rust
|
||||
impl ListDelegate for MyListDelegate {
|
||||
|
|
@ -192,8 +192,8 @@ impl ListDelegate for MyListDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
let state = cx.new(|cx| ListState::new(delegate, window, cx));
|
||||
List::new(&state).searchable(true)
|
||||
let state = cx.new(|cx| ListState::new(delegate, window, cx).searchable(true));
|
||||
List::new(&state)
|
||||
```
|
||||
|
||||
### List with Loading State
|
||||
|
|
|
|||
|
|
@ -73,11 +73,10 @@ let fruits = SearchableVec::new(vec![
|
|||
]);
|
||||
|
||||
let state = cx.new(|cx| {
|
||||
SelectState::new(fruits, None, window, cx)
|
||||
SelectState::new(fruits, None, window, cx).searchable(true)
|
||||
});
|
||||
|
||||
Select::new(&state)
|
||||
.searchable(true)
|
||||
.icon(IconName::Search) // Shows search icon
|
||||
```
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue