@@ -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 : MaybeSignal < 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,21 @@ 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 = 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 ( ) ) ;
61
70
let menu_ref = create_node_ref :: < html:: Div > ( ) ;
62
71
let is_show_menu = create_rw_signal ( false ) ;
63
72
let auto_complete_ref = create_node_ref :: < html:: Div > ( ) ;
64
73
let options = StoredMaybeSignal :: from ( options) ;
65
74
let open_menu = move || {
66
- select_option_index. set ( 0 ) ;
75
+ select_option_index. set ( default_index ( ) ) ;
67
76
is_show_menu. set ( true ) ;
68
77
} ;
69
78
let allow_value = move |_| {
@@ -82,39 +91,59 @@ pub fn AutoComplete(
82
91
if let Some ( on_select) = on_select {
83
92
on_select. call ( option_value) ;
84
93
}
94
+ if allow_free_input. get_untracked ( ) {
95
+ select_option_index. set ( None ) ;
96
+ }
85
97
is_show_menu. set ( false ) ;
86
98
} ;
87
99
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
+
88
108
let on_keydown = move |event : ev:: KeyboardEvent | {
89
109
if !is_show_menu. get_untracked ( ) {
90
110
return ;
91
111
}
92
112
let key = event. key ( ) ;
93
113
if key == * "ArrowDown" {
94
114
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 ( )
97
117
} else {
98
- * index += 1
118
+ * index = Some ( index . map_or ( 0 , |index| index + 1 ) )
99
119
}
100
120
} ) ;
101
121
} else if key == * "ArrowUp" {
102
122
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
+ }
105
129
} else {
106
- * index -= 1
130
+ * index = index . map ( |index| index - 1 )
107
131
}
108
132
} ) ;
109
133
} else if key == * "Enter" {
110
134
event. prevent_default ( ) ;
111
135
let option_value = options. with_untracked ( |options| {
112
136
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 ,
118
147
}
119
148
} ) ;
120
149
if let Some ( option_value) = option_value {
@@ -189,13 +218,13 @@ pub fn AutoComplete(
189
218
select_value( option_value. clone( ) ) ;
190
219
} ;
191
220
let on_mouseenter = move |_| {
192
- select_option_index. set( index) ;
221
+ select_option_index. set( Some ( index) ) ;
193
222
} ;
194
223
let on_mousedown = move |ev: ev:: MouseEvent | {
195
224
ev. prevent_default( ) ;
196
225
} ;
197
226
create_effect( move |_| {
198
- if index == select_option_index. get( ) {
227
+ if Some ( index) == select_option_index. get( ) {
199
228
if !is_show_menu. get( ) {
200
229
return ;
201
230
}
@@ -218,7 +247,7 @@ pub fn AutoComplete(
218
247
class="thaw-auto-complete__menu-item"
219
248
class=(
220
249
"thaw-auto-complete__menu-item--selected" ,
221
- move || index == select_option_index. get( ) ,
250
+ move || Some ( index) == select_option_index. get( ) ,
222
251
)
223
252
224
253
on: click=on_click
0 commit comments