Skip to content

Commit 3525b3c

Browse files
committed
feat: allow free input in AutoComplete
1 parent 396e654 commit 3525b3c

File tree

1 file changed

+29
-16
lines changed

1 file changed

+29
-16
lines changed

thaw/src/auto_complete/mod.rs

+29-16
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,12 @@ pub fn AutoComplete(
5757
css_vars
5858
});
5959

60-
let select_option_index = create_rw_signal::<usize>(0);
60+
let select_option_index = create_rw_signal::<Option<usize>>(None);
6161
let menu_ref = create_node_ref::<html::Div>();
6262
let is_show_menu = create_rw_signal(false);
6363
let auto_complete_ref = create_node_ref::<html::Div>();
6464
let options = StoredMaybeSignal::from(options);
6565
let open_menu = move || {
66-
select_option_index.set(0);
6766
is_show_menu.set(true);
6867
};
6968
let allow_value = move |_| {
@@ -82,39 +81,53 @@ pub fn AutoComplete(
8281
if let Some(on_select) = on_select {
8382
on_select.call(option_value);
8483
}
84+
select_option_index.set(None);
8585
is_show_menu.set(false);
8686
};
8787

88+
// we unset selection index whenever options get changed
89+
// otherwise e.g. selection could move from one item to
90+
// another staying on the same index
91+
create_effect(move |_ | {
92+
options.get();
93+
select_option_index.update(|index| *index = None);
94+
});
95+
8896
let on_keydown = move |event: ev::KeyboardEvent| {
8997
if !is_show_menu.get_untracked() {
9098
return;
9199
}
92100
let key = event.key();
93101
if key == *"ArrowDown" {
94102
select_option_index.update(|index| {
95-
if *index == options.with_untracked(|options| options.len()) - 1 {
96-
*index = 0
103+
if *index == Some(options.with_untracked(|options| options.len()) - 1) {
104+
*index = None
97105
} else {
98-
*index += 1
106+
*index = Some(index.map_or(0, |index| index + 1))
99107
}
100108
});
101109
} else if key == *"ArrowUp" {
102110
select_option_index.update(|index| {
103-
if *index == 0 {
104-
*index = options.with_untracked(|options| options.len()) - 1;
111+
if *index == Some(0) {
112+
*index = None//options.with_untracked(|options| options.len()) - 1;
105113
} else {
106-
*index -= 1
114+
*index = index.map(|index| index - 1)
107115
}
108116
});
109117
} else if key == *"Enter" {
110118
event.prevent_default();
111119
let option_value = options.with_untracked(|options| {
112120
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
121+
match index {
122+
None => {
123+
let value = value.get_untracked();
124+
(!value.is_empty()).then_some(value)
125+
}
126+
Some(index) if options.len() > index => {
127+
let option = &options[index];
128+
Some(option.value.clone())
129+
}
130+
_ => None
118131
}
119132
});
120133
if let Some(option_value) = option_value {
@@ -189,13 +202,13 @@ pub fn AutoComplete(
189202
select_value(option_value.clone());
190203
};
191204
let on_mouseenter = move |_| {
192-
select_option_index.set(index);
205+
select_option_index.set(Some(index));
193206
};
194207
let on_mousedown = move |ev: ev::MouseEvent| {
195208
ev.prevent_default();
196209
};
197210
create_effect(move |_| {
198-
if index == select_option_index.get() {
211+
if Some(index) == select_option_index.get() {
199212
if !is_show_menu.get() {
200213
return;
201214
}
@@ -218,7 +231,7 @@ pub fn AutoComplete(
218231
class="thaw-auto-complete__menu-item"
219232
class=(
220233
"thaw-auto-complete__menu-item--selected",
221-
move || index == select_option_index.get(),
234+
move || Some(index) == select_option_index.get(),
222235
)
223236

224237
on:click=on_click

0 commit comments

Comments
 (0)