1
1
import lonlat from '@conveyal/lonlat'
2
2
import React , { Component , PropTypes } from 'react'
3
- import { FormGroup } from 'react-bootstrap'
3
+ import { FormGroup , FormControl , InputGroup , DropdownButton , MenuItem } from 'react-bootstrap'
4
4
import { connect } from 'react-redux'
5
- import Geocoder from 'react-select-geocoder '
5
+ import { autocomplete } from 'isomorphic-mapzen-search '
6
6
7
7
import { setLocation , clearLocation } from '../../actions/map'
8
8
9
9
class LocationField extends Component {
10
10
static propTypes = {
11
+ config : PropTypes . object ,
11
12
location : PropTypes . object ,
12
13
label : PropTypes . string ,
13
- setLocation : PropTypes . func ,
14
- type : PropTypes . string // replace with locationType?
14
+ type : PropTypes . string , // replace with locationType?
15
+
16
+ // dispatch
17
+ clearLocation : PropTypes . func ,
18
+ setLocation : PropTypes . func
15
19
}
20
+
16
21
constructor ( props ) {
17
22
super ( props )
18
- this . state = { }
23
+ this . state = {
24
+ dropdownOpen : false ,
25
+ geocodedFeatures : [ ]
26
+ }
27
+ }
28
+
29
+ componentWillReceiveProps ( nextProps ) {
30
+ if ( this . props . location !== nextProps . location ) {
31
+ this . setState ( {
32
+ value : nextProps . location !== null ? nextProps . location . name : '' ,
33
+ geocodedFeatures : [ ]
34
+ } )
35
+ }
19
36
}
20
- _onChange = ( value , option ) => {
37
+
38
+ /* _onChange = (value, option) => {
21
39
console.log(value, option)
22
40
if (value && value.geometry) {
23
41
const location = lonlat.fromCoordinates(value.geometry.coordinates)
@@ -32,34 +50,147 @@ class LocationField extends Component {
32
50
this.props.clearLocation(this.props.type)
33
51
}
34
52
}
53
+
35
54
_toValue = (location) => {
36
55
return location && {
37
56
value: `${location.lon},${location.lat}`,
38
57
label: location.name
39
58
}
59
+ } */
60
+
61
+ _geocode ( text ) {
62
+ const { config } = this . props
63
+ autocomplete ( {
64
+ apiKey : config . geocoder . MAPZEN_KEY ,
65
+ boundary : config . geocoder . boundary ,
66
+ // TODO: use current location as focus point
67
+ text
68
+ } ) . then ( ( result ) => {
69
+ console . log ( result )
70
+ this . setState ( { geocodedFeatures : result . features } )
71
+ } ) . catch ( ( err ) => {
72
+ console . error ( err )
73
+ } )
40
74
}
75
+
41
76
render ( ) {
42
- const { config, location } = this . props
43
- // TODO: add geolocation (in react-select-geocoder?)
77
+ const currentLocationOption = createOption ( 'location-arrow' , 'Use Current Location' , 'test1' )
78
+
79
+ let geocodedFeatures = this . state . geocodedFeatures
80
+ if ( geocodedFeatures . length > 5 ) geocodedFeatures = geocodedFeatures . splice ( 0 , 5 )
81
+
82
+ let nearbyStops = [ ] /*{
83
+ name: 'Stop X',
84
+ routes: ['10','11']
85
+ }]*/
86
+
87
+ const formControlClassname = this . props . type + '-form-control'
44
88
return (
45
89
< form >
46
- < FormGroup >
47
- < Geocoder
48
- apiKey = { config . geocoder . MAPZEN_KEY }
49
- boundary = { config . geocoder . boundary }
50
- placeholder = { this . props . label || this . props . type }
51
- focusPoint = { [ config . map . initLon , config . map . initLat ] }
52
- value = { this . _toValue ( location ) }
53
- onChange = { this . _onChange }
54
- geolocate
55
- // optionRenderer={location === null ? (option) => <span><Icon type={option.icon} /> {option.label}</span> : undefined }
90
+ < FormGroup className = 'location-field' >
91
+
92
+ < InputGroup >
93
+ { /* location field icon -- also serves as dropdown anchor */ }
94
+ < DropdownButton
95
+ componentClass = { InputGroup . Button }
96
+ open = { this . state . dropdownOpen }
97
+ onToggle = { ( v , e ) => {
98
+ // if clicked on input form control, keep dropdown open; otherwise, toggle
99
+ const targetIsInput = e . target . className . indexOf ( formControlClassname ) !== - 1
100
+ this . setState ( { dropdownOpen : targetIsInput ? true : ! this . state . dropdownOpen } )
101
+ } }
102
+ id = 'location-dropdown'
103
+ title = { < i className = 'fa fa-star' /> }
104
+ noCaret
105
+ >
106
+ { /* current location option */ }
107
+ { createOption ( 'location-arrow' , 'Use Current Location' ) }
108
+
109
+ { /* geocode search result option(s) */ }
110
+ { geocodedFeatures . length > 0 && < MenuItem header > Search Results</ MenuItem > }
111
+ { geocodedFeatures . length > 0 &&
112
+ geocodedFeatures . map ( feature => {
113
+ return createOption ( 'map-pin' , feature . properties . label , ( ) => {
114
+ const location = lonlat . fromCoordinates ( feature . geometry . coordinates )
115
+ location . name = feature . properties . label
116
+ this . props . setLocation ( this . props . type , location )
117
+ } )
118
+ } )
119
+ }
120
+
121
+ { /* nearby transit stop options */ }
122
+ { nearbyStops . length > 0 && < MenuItem header > Nearby Stops</ MenuItem > }
123
+ { nearbyStops . length > 0 &&
124
+ nearbyStops . map ( stop => {
125
+ return createTransitStopOption ( stop . name , stop . routes , ( ) => {
126
+ // set location from stop
127
+ } )
128
+ } )
129
+ }
130
+ { /* recent search history options */ }
131
+
132
+ </ DropdownButton >
133
+ < FormControl ref = 'formControl'
134
+ className = { formControlClassname }
135
+ type = 'text'
136
+ value = { this . state . value }
137
+ placeholder = { this . props . label || this . props . type }
138
+ onChange = { ( e ) => {
139
+ this . setState ( { value : e . target . value } )
140
+ this . _geocode ( e . target . value )
141
+ } }
142
+ onClick = { ( ) => {
143
+ this . setState ( { dropdownOpen : true } )
144
+ } }
56
145
/>
146
+ < InputGroup . Addon onClick = { ( ) => {
147
+ this . props . clearLocation ( this . props . type )
148
+ this . setState ( {
149
+ value : '' ,
150
+ geocodedFeatures : [ ]
151
+ } )
152
+ } } >
153
+ < i className = 'fa fa-times' />
154
+ </ InputGroup . Addon >
155
+ </ InputGroup >
57
156
</ FormGroup >
58
157
</ form >
59
158
)
60
159
}
61
160
}
62
161
162
+ // helper functions for dropdown options
163
+
164
+ function createOption ( icon , title , onSelect ) {
165
+ return < MenuItem onSelect = { onSelect } >
166
+ < div >
167
+ < div style = { { float : 'left' } } > < i className = { `fa fa-${ icon } ` } /> </ div >
168
+ < div style = { { marginLeft : '30px' } } > { title } </ div >
169
+ </ div >
170
+ </ MenuItem >
171
+ }
172
+
173
+ function createTransitStopOption ( name , routes , onSelect ) {
174
+ return < MenuItem onSelect = { onSelect } >
175
+ < div >
176
+ < div style = { { float : 'left' , paddingTop : '3px' } } >
177
+ < i className = 'fa fa-bus' style = { { fontSize : '20px' } } />
178
+ < div style = { { fontSize : '8px' } } > 0.2 mi</ div >
179
+ </ div >
180
+ < div style = { { marginLeft : '30px' } } >
181
+ < div > { name } </ div >
182
+ < div style = { { fontSize : '9px' } } >
183
+ { routes . map ( route => {
184
+ return < span style = { { backgroundColor : 'gray' , color : 'white' , lineHeight : '9px' , padding : '0px 3px' , marginRight : '5px' } } > { route } </ span >
185
+ } ) }
186
+ </ div >
187
+ </ div >
188
+ </ div >
189
+ </ MenuItem >
190
+ }
191
+
192
+ // connect to redux store
193
+
63
194
const mapStateToProps = ( state , ownProps ) => {
64
195
return {
65
196
config : state . otp . config ,
0 commit comments