Skip to content

Commit 0ad47e8

Browse files
committed
feat: allow free input in AutoComplete
1 parent 396e654 commit 0ad47e8

File tree

1 file changed

+45
-16
lines changed

1 file changed

+45
-16
lines changed

thaw/src/auto_complete/mod.rs

+45-16
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pub fn AutoComplete(
3333
#[prop(optional, into)] clear_after_select: MaybeSignal<bool>,
3434
#[prop(optional, into)] on_select: Option<Callback<String>>,
3535
#[prop(optional, into)] disabled: MaybeSignal<bool>,
36+
#[prop(optional, into)] allow_free_input: MaybeSignal<bool>,
3637
#[prop(optional, into)] invalid: MaybeSignal<bool>,
3738
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
3839
#[prop(optional)] auto_complete_prefix: Option<AutoCompletePrefix>,
@@ -57,13 +58,21 @@ pub fn AutoComplete(
5758
css_vars
5859
});
5960

60-
let select_option_index = create_rw_signal::<usize>(0);
61+
let default_index = move || {
62+
if allow_free_input.get_untracked() {
63+
None
64+
} else {
65+
Some(0)
66+
}
67+
};
68+
69+
let select_option_index = create_rw_signal::<Option<usize>>(default_index());
6170
let menu_ref = create_node_ref::<html::Div>();
6271
let is_show_menu = create_rw_signal(false);
6372
let auto_complete_ref = create_node_ref::<html::Div>();
6473
let options = StoredMaybeSignal::from(options);
6574
let open_menu = move || {
66-
select_option_index.set(0);
75+
select_option_index.set(default_index());
6776
is_show_menu.set(true);
6877
};
6978
let allow_value = move |_| {
@@ -82,39 +91,59 @@ pub fn AutoComplete(
8291
if let Some(on_select) = on_select {
8392
on_select.call(option_value);
8493
}
94+
if allow_free_input.get_untracked() {
95+
select_option_index.set(None);
96+
}
8597
is_show_menu.set(false);
8698
};
8799

100+
// we unset selection index whenever options get changed
101+
// otherwise e.g. selection could move from one item to
102+
// another staying on the same index
103+
create_effect(move |_| {
104+
options.get();
105+
select_option_index.set(default_index());
106+
});
107+
88108
let on_keydown = move |event: ev::KeyboardEvent| {
89109
if !is_show_menu.get_untracked() {
90110
return;
91111
}
92112
let key = event.key();
93113
if key == *"ArrowDown" {
94114
select_option_index.update(|index| {
95-
if *index == options.with_untracked(|options| options.len()) - 1 {
96-
*index = 0
115+
if *index == Some(options.with_untracked(|options| options.len()) - 1) {
116+
*index = default_index()
97117
} else {
98-
*index += 1
118+
*index = Some(index.map_or(0, |index| index + 1))
99119
}
100120
});
101121
} else if key == *"ArrowUp" {
102122
select_option_index.update(|index| {
103-
if *index == 0 {
104-
*index = options.with_untracked(|options| options.len()) - 1;
123+
if *index == Some(0) {
124+
if allow_free_input.get_untracked() {
125+
*index = None
126+
} else {
127+
*index = Some(options.with_untracked(|options| options.len()) - 1);
128+
}
105129
} else {
106-
*index -= 1
130+
*index = index.map(|index| index - 1)
107131
}
108132
});
109133
} else if key == *"Enter" {
110134
event.prevent_default();
111135
let option_value = options.with_untracked(|options| {
112136
let index = select_option_index.get_untracked();
113-
if options.len() > index {
114-
let option = &options[index];
115-
Some(option.value.clone())
116-
} else {
117-
None
137+
match index {
138+
None if allow_free_input.get_untracked() => {
139+
let value = value.get_untracked();
140+
(!value.is_empty()).then_some(value)
141+
}
142+
Some(index) if options.len() > index => {
143+
let option = &options[index];
144+
Some(option.value.clone())
145+
}
146+
_ => None,
118147
}
119148
});
120149
if let Some(option_value) = option_value {
@@ -189,13 +218,13 @@ pub fn AutoComplete(
189218
select_value(option_value.clone());
190219
};
191220
let on_mouseenter = move |_| {
192-
select_option_index.set(index);
221+
select_option_index.set(Some(index));
193222
};
194223
let on_mousedown = move |ev: ev::MouseEvent| {
195224
ev.prevent_default();
196225
};
197226
create_effect(move |_| {
198-
if index == select_option_index.get() {
227+
if Some(index) == select_option_index.get() {
199228
if !is_show_menu.get() {
200229
return;
201230
}
@@ -218,7 +247,7 @@ pub fn AutoComplete(
218247
class="thaw-auto-complete__menu-item"
219248
class=(
220249
"thaw-auto-complete__menu-item--selected",
221-
move || index == select_option_index.get(),
250+
move || Some(index) == select_option_index.get(),
222251
)
223252

224253
on:click=on_click

0 commit comments

Comments
 (0)