@@ -10,9 +10,11 @@ var cx = require('classnames');
10
10
* @param {string|DOMElement } options.container CSS Selector or DOMElement to insert the widget
11
11
* @param {string } [options.placeholder] Input's placeholder
12
12
* @param {Object } [options.cssClasses] CSS classes to add
13
+ * @param {string } [options.cssClasses.root] CSS class to add to the wrapping div (if wrapInput set to `true`)
13
14
* @param {string } [options.cssClasses.input] CSS class to add to the input
14
15
* @param {string } [options.cssClasses.poweredBy] CSS class to add to the poweredBy element
15
16
* @param {boolean } [poweredBy=false] Show a powered by Algolia link below the input
17
+ * @param {boolean } [wrapInput=true] Wrap the input in a div.ais-search-box
16
18
* @param {boolean|string } [autofocus='auto'] autofocus on the input
17
19
* @return {Object }
18
20
*/
@@ -21,10 +23,11 @@ function searchBox({
21
23
placeholder = '' ,
22
24
cssClasses = { } ,
23
25
poweredBy = false ,
26
+ wrapInput = true ,
24
27
autofocus = 'auto'
25
28
} ) {
26
29
if ( ! container ) {
27
- throw new Error ( 'Usage: searchBox({container[, placeholder, cssClasses.{input,poweredBy}, poweredBy, autofocus]})' ) ;
30
+ throw new Error ( 'Usage: searchBox({container[, placeholder, cssClasses.{input,poweredBy}, poweredBy, wrapInput, autofocus]})' ) ;
28
31
}
29
32
30
33
container = utils . getContainerNode ( container ) ;
@@ -35,14 +38,21 @@ function searchBox({
35
38
}
36
39
37
40
return {
38
- // Hook on an existing input, or add one if none targeted
39
41
getInput : function ( ) {
42
+ // Returns reference to targeted input if present, or create a new one
40
43
if ( container . tagName === 'INPUT' ) {
41
44
return container ;
42
45
}
43
- return container . appendChild ( document . createElement ( 'input' ) ) ;
46
+ return document . createElement ( 'input' ) ;
44
47
} ,
45
- init : function ( initialState , helper ) {
48
+ wrapInput : function ( input ) {
49
+ // Wrap input in a .ais-search-box div
50
+ var wrapper = document . createElement ( 'div' ) ;
51
+ wrapper . classList . add ( cx ( bem ( null ) , cssClasses . root ) ) ;
52
+ wrapper . appendChild ( input ) ;
53
+ return wrapper ;
54
+ } ,
55
+ addDefaultAttributesToInput : function ( input , query ) {
46
56
var defaultAttributes = {
47
57
autocapitalize : 'off' ,
48
58
autocomplete : 'off' ,
@@ -51,9 +61,8 @@ function searchBox({
51
61
role : 'textbox' ,
52
62
spellcheck : 'false' ,
53
63
type : 'text' ,
54
- value : initialState . query
64
+ value : query
55
65
} ;
56
- var input = this . getInput ( ) ;
57
66
58
67
// Overrides attributes if not already set
59
68
forEach ( defaultAttributes , ( value , key ) => {
@@ -65,36 +74,57 @@ function searchBox({
65
74
66
75
// Add classes
67
76
input . classList . add ( cx ( bem ( 'input' ) , cssClasses . input ) ) ;
77
+ } ,
78
+ addPoweredBy : function ( input ) {
79
+ var PoweredBy = require ( '../../components/PoweredBy/PoweredBy.js' ) ;
80
+ var poweredByContainer = document . createElement ( 'div' ) ;
81
+ input . parentNode . insertBefore ( poweredByContainer , input . nextSibling ) ;
82
+ var poweredByCssClasses = {
83
+ root : cx ( bem ( 'powered-by' ) , cssClasses . poweredBy ) ,
84
+ link : bem ( 'powered-by-link' )
85
+ } ;
86
+ ReactDOM . render (
87
+ < PoweredBy
88
+ cssClasses = { poweredByCssClasses }
89
+ /> ,
90
+ poweredByContainer
91
+ ) ;
92
+ } ,
93
+ init : function ( initialState , helper ) {
94
+ var isInputTargeted = container . tagName === 'INPUT' ;
95
+ var input = this . getInput ( ) ;
68
96
97
+ // Add all the needed attributes and listeners to the input
98
+ this . addDefaultAttributesToInput ( input , initialState . query ) ;
69
99
input . addEventListener ( 'keyup' , ( ) => {
70
100
helper . setQuery ( input . value ) . search ( ) ;
71
101
} ) ;
72
102
103
+ if ( isInputTargeted ) {
104
+ // To replace the node, we need to create an intermediate node
105
+ var placeholderNode = document . createElement ( 'div' ) ;
106
+ input . parentNode . insertBefore ( placeholderNode , input ) ;
107
+ let parentNode = input . parentNode ;
108
+ let wrappedInput = wrapInput ? this . wrapInput ( input ) : input ;
109
+ parentNode . replaceChild ( wrappedInput , placeholderNode ) ;
110
+ } else {
111
+ let wrappedInput = wrapInput ? this . wrapInput ( input ) : input ;
112
+ container . appendChild ( wrappedInput ) ;
113
+ }
114
+
73
115
// Optional "powered by Algolia" widget
74
116
if ( poweredBy ) {
75
- var PoweredBy = require ( '../../components/PoweredBy/PoweredBy.js' ) ;
76
- var poweredByContainer = document . createElement ( 'div' ) ;
77
- input . parentNode . appendChild ( poweredByContainer ) ;
78
- var poweredByCssClasses = {
79
- root : cx ( bem ( 'powered-by' ) , cssClasses . poweredBy ) ,
80
- link : bem ( 'powered-by-link' )
81
- } ;
82
- ReactDOM . render (
83
- < PoweredBy
84
- cssClasses = { poweredByCssClasses }
85
- /> ,
86
- poweredByContainer
87
- ) ;
117
+ this . addPoweredBy ( input ) ;
88
118
}
89
119
120
+ // Update value when query change outside of the input
90
121
helper . on ( 'change' , function ( state ) {
91
122
if ( input !== document . activeElement && input . value !== state . query ) {
92
123
input . value = state . query ;
93
124
}
94
125
} ) ;
95
126
96
- if ( autofocus === true ||
97
- autofocus === 'auto' && helper . state . query === '' ) {
127
+ if ( autofocus === true || autofocus === 'auto' && helper . state . query === '' ) {
98
128
input . focus ( ) ;
99
129
}
100
130
}
0 commit comments