@@ -33,6 +33,7 @@ pub fn AutoComplete(
33
33
#[ prop( optional, into) ] clear_after_select : MaybeSignal < bool > ,
34
34
#[ prop( optional, into) ] on_select : Option < Callback < String > > ,
35
35
#[ prop( optional, into) ] disabled : MaybeSignal < bool > ,
36
+ #[ prop( optional, into) ] allow_free_input : bool ,
36
37
#[ prop( optional, into) ] invalid : MaybeSignal < bool > ,
37
38
#[ prop( optional, into) ] class : OptionalProp < MaybeSignal < String > > ,
38
39
#[ prop( optional) ] auto_complete_prefix : Option < AutoCompletePrefix > ,
@@ -57,13 +58,15 @@ pub fn AutoComplete(
57
58
css_vars
58
59
} ) ;
59
60
60
- let select_option_index = create_rw_signal :: < usize > ( 0 ) ;
61
+ let default_index = if allow_free_input { None } else { Some ( 0 ) } ;
62
+
63
+ let select_option_index = create_rw_signal :: < Option < usize > > ( default_index) ;
61
64
let menu_ref = create_node_ref :: < html:: Div > ( ) ;
62
65
let is_show_menu = create_rw_signal ( false ) ;
63
66
let auto_complete_ref = create_node_ref :: < html:: Div > ( ) ;
64
67
let options = StoredMaybeSignal :: from ( options) ;
65
68
let open_menu = move || {
66
- select_option_index. set ( 0 ) ;
69
+ select_option_index. set ( default_index ) ;
67
70
is_show_menu. set ( true ) ;
68
71
} ;
69
72
let allow_value = move |_| {
@@ -82,39 +85,59 @@ pub fn AutoComplete(
82
85
if let Some ( on_select) = on_select {
83
86
on_select. call ( option_value) ;
84
87
}
88
+ if allow_free_input {
89
+ select_option_index. set ( None ) ;
90
+ }
85
91
is_show_menu. set ( false ) ;
86
92
} ;
87
93
94
+ // we unset selection index whenever options get changed
95
+ // otherwise e.g. selection could move from one item to
96
+ // another staying on the same index
97
+ create_effect ( move |_| {
98
+ options. get ( ) ;
99
+ select_option_index. set ( default_index) ;
100
+ } ) ;
101
+
88
102
let on_keydown = move |event : ev:: KeyboardEvent | {
89
103
if !is_show_menu. get_untracked ( ) {
90
104
return ;
91
105
}
92
106
let key = event. key ( ) ;
93
107
if key == * "ArrowDown" {
94
108
select_option_index. update ( |index| {
95
- if * index == options. with_untracked ( |options| options. len ( ) ) - 1 {
96
- * index = 0
109
+ if * index == Some ( options. with_untracked ( |options| options. len ( ) ) - 1 ) {
110
+ * index = default_index
97
111
} else {
98
- * index += 1
112
+ * index = Some ( index . map_or ( 0 , |index| index + 1 ) )
99
113
}
100
114
} ) ;
101
115
} else if key == * "ArrowUp" {
102
116
select_option_index. update ( |index| {
103
- if * index == 0 {
104
- * index = options. with_untracked ( |options| options. len ( ) ) - 1 ;
117
+ if * index == Some ( 0 ) {
118
+ if allow_free_input {
119
+ * index = None
120
+ } else {
121
+ * index = Some ( options. with_untracked ( |options| options. len ( ) ) - 1 ) ;
122
+ }
105
123
} else {
106
- * index -= 1
124
+ * index = index . map ( |index| index - 1 )
107
125
}
108
126
} ) ;
109
127
} else if key == * "Enter" {
110
128
event. prevent_default ( ) ;
111
129
let option_value = options. with_untracked ( |options| {
112
130
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
131
+ match index {
132
+ None if allow_free_input => {
133
+ let value = value. get_untracked ( ) ;
134
+ ( !value. is_empty ( ) ) . then_some ( value)
135
+ }
136
+ Some ( index) if options. len ( ) > index => {
137
+ let option = & options[ index] ;
138
+ Some ( option. value . clone ( ) )
139
+ }
140
+ _ => None ,
118
141
}
119
142
} ) ;
120
143
if let Some ( option_value) = option_value {
@@ -189,13 +212,13 @@ pub fn AutoComplete(
189
212
select_value( option_value. clone( ) ) ;
190
213
} ;
191
214
let on_mouseenter = move |_| {
192
- select_option_index. set( index) ;
215
+ select_option_index. set( Some ( index) ) ;
193
216
} ;
194
217
let on_mousedown = move |ev: ev:: MouseEvent | {
195
218
ev. prevent_default( ) ;
196
219
} ;
197
220
create_effect( move |_| {
198
- if index == select_option_index. get( ) {
221
+ if Some ( index) == select_option_index. get( ) {
199
222
if !is_show_menu. get( ) {
200
223
return ;
201
224
}
@@ -218,7 +241,7 @@ pub fn AutoComplete(
218
241
class="thaw-auto-complete__menu-item"
219
242
class=(
220
243
"thaw-auto-complete__menu-item--selected" ,
221
- move || index == select_option_index. get( ) ,
244
+ move || Some ( index) == select_option_index. get( ) ,
222
245
)
223
246
224
247
on: click=on_click
0 commit comments