11export default {
22 mounted ( ) {
33 this . mode = this . getMode ( )
4+ this . createOption = this . el . querySelector ( '[data-prima-ref=create-option]' )
5+ this . hasCreateOption = ! ! this . createOption
46 this . el . addEventListener ( 'mouseover' , this . onHover . bind ( this ) )
57 this . el . addEventListener ( 'keydown' , this . onKey . bind ( this ) )
68 this . el . addEventListener ( 'click' , this . onClick . bind ( this ) )
79 this . el . querySelector ( 'input[data-prima-ref=search_input]' ) . addEventListener ( 'focus' , this . showOptions . bind ( this ) )
810
11+ this . initializeCreateOption ( )
12+
913 if ( document . activeElement === this . el . querySelector ( 'input[data-prima-ref=search_input]' ) ) {
1014 this . showOptions ( )
1115 }
@@ -29,8 +33,18 @@ export default {
2933
3034 selectOption ( el ) {
3135 const value = el . getAttribute ( 'data-value' )
32- this . el . querySelector ( 'input[data-prima-ref=submit_input]' ) . value = value
33- this . el . querySelector ( 'input[data-prima-ref=search_input]' ) . value = value
36+ const searchInput = this . el . querySelector ( 'input[data-prima-ref=search_input]' )
37+ const submitInput = this . el . querySelector ( 'input[data-prima-ref=submit_input]' )
38+
39+ if ( value === '__CREATE__' ) {
40+ const searchValue = searchInput . value
41+ submitInput . value = searchValue
42+ searchInput . value = searchValue
43+ } else {
44+ submitInput . value = value
45+ searchInput . value = value
46+ }
47+
3448 this . hideOptions ( )
3549 } ,
3650
@@ -41,24 +55,25 @@ export default {
4155 } ,
4256
4357 onKey ( e ) {
44- const allOptions = Array . from ( this . el . querySelectorAll ( '[role=option]' ) )
45- const firstOption = allOptions [ 0 ]
46- const lastOption = allOptions [ allOptions . length - 1 ]
47- const currentFocusIndex = allOptions . findIndex ( option => option . getAttribute ( 'data-focus' ) === 'true' )
58+ const visibleOptions = Array . from ( this . el . querySelectorAll ( '[role=option]:not([data-hidden])' ) )
59+ const firstOption = visibleOptions [ 0 ]
60+ const lastOption = visibleOptions [ visibleOptions . length - 1 ]
61+ const currentFocusIndex = visibleOptions . findIndex ( option => option . getAttribute ( 'data-focus' ) === 'true' )
62+
4863
4964 if ( e . key === 'ArrowUp' ) {
5065 e . preventDefault ( )
5166 if ( firstOption . getAttribute ( 'data-focus' ) === 'true' ) {
5267 this . setFocus ( lastOption )
5368 } else {
54- this . setFocus ( allOptions [ currentFocusIndex - 1 ] )
69+ this . setFocus ( visibleOptions [ currentFocusIndex - 1 ] )
5570 }
5671 } else if ( e . key === 'ArrowDown' ) {
5772 e . preventDefault ( )
5873 if ( lastOption . getAttribute ( 'data-focus' ) === 'true' ) {
5974 this . setFocus ( firstOption )
6075 } else {
61- this . setFocus ( allOptions [ currentFocusIndex + 1 ] )
76+ this . setFocus ( visibleOptions [ currentFocusIndex + 1 ] )
6277 }
6378 } else if ( e . key === "Enter" || e . key === "Tab" ) {
6479 e . preventDefault ( )
@@ -81,13 +96,21 @@ export default {
8196 } ,
8297
8398 onInput ( e ) {
99+ const searchValue = e . target . value
100+
101+ // Update create option content and visibility
102+ if ( this . hasCreateOption ) {
103+ this . updateCreateOption ( searchValue )
104+ this . updateCreateOptionVisibility ( searchValue )
105+ }
106+
84107 if ( this . mode === 'async' ) {
85108 const options = this . el . querySelector ( '[data-prima-ref=options]' )
86109 this . liveSocket . execJS ( options , options . getAttribute ( 'js-show' ) ) ;
87110 this . focusedOptionBeforeUpdate = this . currentlyFocusedOption ( ) ?. dataset . value
88111 } else {
89- const q = e . target . value . toLowerCase ( )
90- const allOptions = this . el . querySelectorAll ( '[role=option]' )
112+ const q = searchValue . toLowerCase ( )
113+ const allOptions = this . el . querySelectorAll ( '[role=option]:not([data-prima-ref=create-option]) ' )
91114 let previouslyFocusedOptionIsHidden = false
92115
93116 for ( const option of allOptions ) {
@@ -123,6 +146,13 @@ export default {
123146 this . liveSocket . execJS ( options , options . getAttribute ( 'js-show' ) ) ;
124147 this . el . querySelector ( 'input[data-prima-ref=search_input]' ) . select ( )
125148
149+ // Update create option when showing options
150+ if ( this . hasCreateOption ) {
151+ const searchValue = this . el . querySelector ( 'input[data-prima-ref=search_input]' ) . value
152+ this . updateCreateOption ( searchValue )
153+ this . updateCreateOptionVisibility ( searchValue )
154+ }
155+
126156 this . focusFirstOption ( )
127157
128158 const handleClickOutside = ( event ) => {
@@ -167,5 +197,48 @@ export default {
167197
168198 currentlyFocusedOption ( ) {
169199 return this . el . querySelector ( '[role=option][data-focus=true]' )
200+ } ,
201+
202+ updateCreateOption ( searchValue ) {
203+ if ( ! this . createOption ) return
204+ this . createOption . textContent = `Create "${ searchValue } "`
205+ } ,
206+
207+ updateCreateOptionVisibility ( searchValue ) {
208+ if ( ! this . createOption ) return
209+
210+ if ( searchValue . length > 0 && ! this . hasExactMatch ( searchValue ) ) {
211+ this . showOption ( this . createOption )
212+ } else {
213+ // Check if create option is currently focused before hiding it
214+ const createOptionHasFocus = this . createOption . getAttribute ( 'data-focus' ) === 'true'
215+ this . hideOption ( this . createOption )
216+
217+ // If create option was focused, move focus to first visible option
218+ if ( createOptionHasFocus ) {
219+ this . focusFirstOption ( )
220+ }
221+ }
222+ } ,
223+
224+ hasExactMatch ( searchValue ) {
225+ const regularOptions = this . el . querySelectorAll ( '[role=option]:not([data-prima-ref=create-option])' )
226+ const hasStaticMatch = Array . from ( regularOptions ) . some ( option =>
227+ option . getAttribute ( 'data-value' ) === searchValue
228+ )
229+
230+ // Also check if search value matches current selected value (submit input)
231+ const submitInput = this . el . querySelector ( 'input[data-prima-ref=submit_input]' )
232+ const hasSelectedMatch = submitInput . value === searchValue
233+
234+
235+ return hasStaticMatch || hasSelectedMatch
236+ } ,
237+
238+ initializeCreateOption ( ) {
239+ if ( ! this . hasCreateOption ) return
240+
241+ // Hide create option initially since search input starts empty
242+ this . hideOption ( this . createOption )
170243 }
171244}
0 commit comments