3
3
import { Application , Controller } from "/static/cache/stimulus/3.1.0/stimulus.min.js" // provided by Misk
4
4
window . Stimulus = Application . start ( )
5
5
6
- /*
7
- * Usage
8
- * =====
9
- *
10
- * <div data-controller="autocomplete" data-autocomplete-url-value="/birds/search" role="combobox">
11
- * <input type="text" data-autocomplete-target="input"/>
12
- * <input type="hidden" name="bird_id" data-autocomplete-target="hidden"/>
13
- * <ul class="list-group" data-autocomplete-target="results"></ul>
14
- * </div>
15
- *
16
- * The server is expected to send back snippets as such:
17
- *
18
- * <li class="list-group-item" role="option" data-autocomplete-value="1">Blackbird</li>
19
- * <li class="list-group-item" role="option" data-autocomplete-value="2">Bluebird</li>
20
- * <li class="list-group-item" role="option" data-autocomplete-value="3">Mockingbird</li>
21
- *
22
- */
23
6
class AutocompleteController extends Controller {
24
7
static targets = [ "input" , "results" ]
25
- static values = {
26
- url : String ,
27
- minLength : { type : Number , default : 2 } ,
28
- delay : { type : Number , default : 300 }
29
- }
8
+ static values = { url : String }
30
9
31
10
connect ( ) {
32
- this . inputTarget . addEventListener ( 'input' , this . debounce ( this . onInput . bind ( this ) , this . delayValue ) ) ;
33
- this . inputTarget . addEventListener ( 'blur' , this . onBlur . bind ( this ) ) ;
11
+ this . inputTarget . addEventListener ( 'input' , this . onInput . bind ( this ) ) ;
12
+ this . inputTarget . addEventListener ( 'blur' , ( ) => setTimeout ( ( ) => this . hideResults ( ) , 100 ) ) ;
34
13
this . resultsTarget . addEventListener ( 'click' , this . onClick . bind ( this ) ) ;
35
- this . resultsTarget . addEventListener ( 'mousedown' , this . onMousedown . bind ( this ) ) ;
36
-
37
- // Set autocomplete attributes
38
14
this . inputTarget . setAttribute ( 'autocomplete' , 'off' ) ;
39
- this . inputTarget . setAttribute ( 'spellcheck' , 'false' ) ;
40
-
41
- this . mouseDown = false ;
42
15
}
43
16
44
17
onInput ( event ) {
45
18
const query = event . target . value . trim ( ) ;
46
-
47
- if ( query . length >= this . minLengthValue ) {
48
- this . fetchResults ( query ) ;
49
- } else {
50
- this . hideResults ( ) ;
51
- }
52
- }
53
-
54
- onBlur ( ) {
55
- if ( ! this . mouseDown ) {
56
- this . hideResults ( ) ;
57
- }
58
- }
59
-
60
- onMousedown ( ) {
61
- this . mouseDown = true ;
62
- setTimeout ( ( ) => { this . mouseDown = false ; } , 100 ) ;
19
+ query . length >= 2 ? this . fetchResults ( query ) : this . hideResults ( ) ;
63
20
}
64
21
65
22
onClick ( event ) {
66
23
const option = event . target . closest ( '[role="option"]' ) ;
67
24
if ( option ) {
68
- this . selectOption ( option ) ;
25
+ this . inputTarget . value = option . textContent . trim ( ) ;
26
+ this . hideResults ( ) ;
69
27
}
70
28
}
71
29
72
- selectOption ( option ) {
73
- const value = option . getAttribute ( 'data-autocomplete-value' ) || option . textContent . trim ( ) ;
74
- this . inputTarget . value = value ;
75
- this . hideResults ( ) ;
76
- this . inputTarget . focus ( ) ;
77
- }
78
-
79
30
async fetchResults ( query ) {
80
31
try {
81
- const url = `${ this . urlValue } ?q=${ encodeURIComponent ( query ) } ` ;
82
- const response = await fetch ( url , {
83
- headers : { 'X-Requested-With' : 'XMLHttpRequest' }
84
- } ) ;
32
+ const response = await fetch ( `${ this . urlValue } ?q=${ encodeURIComponent ( query ) } ` ) ;
85
33
const html = await response . text ( ) ;
86
-
87
34
this . resultsTarget . innerHTML = html ;
88
-
89
- if ( this . resultsTarget . children . length > 0 ) {
90
- this . showResults ( ) ;
91
- } else {
92
- this . hideResults ( ) ;
93
- }
94
- } catch ( error ) {
35
+ html . trim ( ) ? this . showResults ( ) : this . hideResults ( ) ;
36
+ } catch {
95
37
this . hideResults ( ) ;
96
38
}
97
39
}
@@ -103,19 +45,6 @@ class AutocompleteController extends Controller {
103
45
hideResults ( ) {
104
46
this . resultsTarget . style . display = 'none' ;
105
47
}
106
-
107
- debounce ( func , wait ) {
108
- let timeout ;
109
- return function executedFunction ( ...args ) {
110
- const later = ( ) => {
111
- clearTimeout ( timeout ) ;
112
- func ( ...args ) ;
113
- } ;
114
- clearTimeout ( timeout ) ;
115
- timeout = setTimeout ( later , wait ) ;
116
- } ;
117
- }
118
48
}
119
49
120
50
Stimulus . register ( "autocomplete" , AutocompleteController )
121
-
0 commit comments