diff options
Diffstat (limited to 'app/javascript/routes/components')
| -rw-r--r-- | app/javascript/routes/components/App.js | 25 | ||||
| -rw-r--r-- | app/javascript/routes/components/BSelect2.js | 125 | ||||
| -rw-r--r-- | app/javascript/routes/components/OlMap.js | 169 | ||||
| -rw-r--r-- | app/javascript/routes/components/StopPoint.js | 94 | ||||
| -rw-r--r-- | app/javascript/routes/components/StopPointList.js | 69 |
5 files changed, 482 insertions, 0 deletions
diff --git a/app/javascript/routes/components/App.js b/app/javascript/routes/components/App.js new file mode 100644 index 000000000..0f5786407 --- /dev/null +++ b/app/javascript/routes/components/App.js @@ -0,0 +1,25 @@ +import React, { Component, PropTypes } from 'react' +import AddStopPoint from '../containers/AddStopPoint' +import VisibleStopPoints from'../containers/VisibleStopPoints' +import clone from '../../helpers/clone' +const I18n = clone(window , "I18n", true) + +export default class App extends Component { + + getChildContext() { + return { I18n } + } + + render() { + return ( + <div> + <VisibleStopPoints /> + <AddStopPoint /> + </div> + ) + } +} + +App.childContextTypes = { + I18n: PropTypes.object +} diff --git a/app/javascript/routes/components/BSelect2.js b/app/javascript/routes/components/BSelect2.js new file mode 100644 index 000000000..340d9df95 --- /dev/null +++ b/app/javascript/routes/components/BSelect2.js @@ -0,0 +1,125 @@ +import _ from'lodash' +import React, { Component, PropTypes } from 'react' +import Select2 from 'react-select2' + + +// get JSON full path +var origin = window.location.origin +var path = window.location.pathname.split('/', 3).join('/') + + +export default class BSelect3 extends Component { + constructor(props, context) { + super(props, context) + } + onChange(e) { + this.props.onChange(this.props.index, { + text: e.currentTarget.textContent, + stoparea_id: e.currentTarget.value, + user_objectid: e.params.data.user_objectid, + longitude: e.params.data.longitude, + latitude: e.params.data.latitude, + name: e.params.data.name, + short_name: e.params.data.short_name, + city_name: e.params.data.city_name, + area_type: e.params.data.area_type, + zip_code: e.params.data.zip_code, + comment: e.params.data.comment + }) + } + + parsedText(data) { + let a = data.replace('</em></small>', '') + let b = a.split('<small><em>') + if (b.length > 1) { + return ( + <span> + {b[0]} + <small><em>{b[1]}</em></small> + </span> + ) + } else { + return ( + <span>{data}</span> + ) + } + } + + render() { + if(this.props.value.edit) + return ( + <div className='select2-bootstrap-append'> + <BSelect2 {...this.props} onSelect={ this.onChange.bind(this) }/> + </div> + ) + else + if(!this.props.value.stoparea_id) + return ( + <div> + <BSelect2 {...this.props} onSelect={ this.onChange.bind(this) }/> + </div> + ) + else + return ( + <a + className='navlink' + href={origin + path + '/stop_areas/' + this.props.value.stoparea_id} + title="Voir l'arrĂȘt" + > + {this.parsedText(this.props.value.text)} + </a> + ) + } +} + +class BSelect2 extends Component{ + componentDidMount() { + this.refs.newSelect.el.select2('open') + } + + render() { + return ( + <Select2 + value={ this.props.value.stoparea_id } + onSelect={ this.props.onSelect } + ref='newSelect' + options={{ + placeholder: this.context.I18n.routes.edit.select2.placeholder, + allowClear: true, + language: 'fr', /* Doesn't seem to work... :( */ + theme: 'bootstrap', + width: '100%', + ajax: { + url: origin + path + '/autocomplete_stop_areas.json', + dataType: 'json', + delay: '500', + data: function(params) { + return { + q: params.term, + target_type: 'zdep' + }; + }, + processResults: function(data, params) { + return { + results: data.map( + item => _.assign( + {}, + item, + { text: item.name + ", " + item.zip_code + " " + item.short_city_name + " <small><em>(" + item.user_objectid + ")</em></small>" } + ) + ) + }; + }, + cache: true + }, + escapeMarkup: function (markup) { return markup; }, + minimumInputLength: 3 + }} + /> + ) + } +} + +BSelect2.contextTypes = { + I18n: PropTypes.object +} diff --git a/app/javascript/routes/components/OlMap.js b/app/javascript/routes/components/OlMap.js new file mode 100644 index 000000000..2c01dfa7f --- /dev/null +++ b/app/javascript/routes/components/OlMap.js @@ -0,0 +1,169 @@ +import _ from 'lodash' +import React, { Component, PropTypes } from 'react' + +export default class OlMap extends Component{ + constructor(props, context){ + super(props, context) + } + + fetchApiURL(id){ + const origin = window.location.origin + const path = window.location.pathname.split('/', 3).join('/') + return origin + path + "/autocomplete_stop_areas/" + id + "/around?target_type=zdep" + } + + componentDidUpdate(prevProps, prevState){ + if(prevProps.value.olMap.isOpened == false && this.props.value.olMap.isOpened == true){ + var source = new ol.source.Vector({ + format: new ol.format.GeoJSON(), + url: this.fetchApiURL(this.props.value.stoparea_id) + }) + var feature = new ol.Feature({ + geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(this.props.value.longitude), parseFloat(this.props.value.latitude)])) + }) + + var defaultStyles = new ol.style.Style({ + image: new ol.style.Circle(({ + radius: 4, + fill: new ol.style.Fill({ + color: '#004d87' + }) + })) + }) + var selectedStyles = new ol.style.Style({ + image: new ol.style.Circle(({ + radius: 6, + fill: new ol.style.Fill({ + color: '#da2f36' + }) + })) + }) + + var centerLayer = new ol.layer.Vector({ + source: new ol.source.Vector({ + features: [feature] + }), + style: selectedStyles, + zIndex: 2 + }) + var vectorLayer = new ol.layer.Vector({ + source: source, + style: defaultStyles, + zIndex: 1 + }); + + var map = new ol.Map({ + target: 'stoppoint_map' + this.props.index, + layers: [ + new ol.layer.Tile({ + source: new ol.source.OSM() + }), + vectorLayer, + centerLayer + ], + controls: [ new ol.control.ScaleLine() ], + interactions: ol.interaction.defaults({ + dragPan: false, + doubleClickZoom: false, + shiftDragZoom: false, + mouseWheelZoom: false + }), + view: new ol.View({ + center: ol.proj.fromLonLat([parseFloat(this.props.value.longitude), parseFloat(this.props.value.latitude)]), + zoom: 18 + }) + }); + + // Selectable marker + var select = new ol.interaction.Select({ + style: selectedStyles + }); + + map.addInteraction(select); + + select.on('select', function(e) { + feature.setStyle(defaultStyles); + centerLayer.setZIndex(0); + + if(e.selected.length != 0) { + + if(e.selected[0].getGeometry() == feature.getGeometry()) { + if(e.selected[0].style_.image_.fill_.color_ != '#da2f36'){ + feature.setStyle(selectedStyles); + centerLayer.setZIndex(2); + e.preventDefault() + return false + } + } + let data = _.assign({}, e.selected[0].getProperties(), {geometry: undefined}); + + this.props.onSelectMarker(this.props.index, data) + } else { + this.props.onUnselectMarker(this.props.index) + } + }, this); + } + } + + render() { + if (this.props.value.olMap.isOpened) { + return ( + <div className='map_container'> + <div className='map_metas'> + <p> + <strong>{this.props.value.olMap.json.name}</strong> + </p> + <p> + <strong>{this.context.I18n.routes.edit.stop_point_type} : </strong> + {this.props.value.olMap.json.area_type} + </p> + <p> + <strong>{this.context.I18n.routes.edit.short_name} : </strong> + {this.props.value.olMap.json.short_name} + </p> + <p> + <strong>{this.context.I18n.id_reflex} : </strong> + {this.props.value.olMap.json.user_objectid} + </p> + + <p><strong>{this.context.I18n.routes.edit.map.coordinates} : </strong></p> + <p style={{paddingLeft: 10, marginTop: 0}}> + <em>{this.context.I18n.routes.edit.map.proj}.: </em>WSG84<br/> + <em>{this.context.I18n.routes.edit.map.lat}.: </em>{this.props.value.olMap.json.latitude} <br/> + <em>{this.context.I18n.routes.edit.map.lon}.: </em>{this.props.value.olMap.json.longitude} + </p> + <p> + <strong>{this.context.I18n.routes.edit.map.postal_code} : </strong> + {this.props.value.olMap.json.zip_code} + </p> + <p> + <strong>{this.context.I18n.routes.edit.map.city} : </strong> + {this.props.value.olMap.json.city_name} + </p> + <p> + <strong>{this.context.I18n.routes.edit.map.comment} : </strong> + {this.props.value.olMap.json.comment} + </p> + {(this.props.value.stoparea_id != this.props.value.olMap.json.stoparea_id) &&( + <div className='btn btn-outline-primary btn-sm' + onClick= {() => {this.props.onUpdateViaOlMap(this.props.index, this.props.value.olMap.json)}} + >{this.context.I18n.actions.select}</div> + )} + </div> + <div className='map_content'> + <div id={"stoppoint_map" + this.props.index} className='map'></div> + </div> + </div> + ) + } else { + return false + } + } +} + +OlMap.PropTypes = { +} + +OlMap.contextTypes = { + I18n: PropTypes.object +} diff --git a/app/javascript/routes/components/StopPoint.js b/app/javascript/routes/components/StopPoint.js new file mode 100644 index 000000000..606121f99 --- /dev/null +++ b/app/javascript/routes/components/StopPoint.js @@ -0,0 +1,94 @@ +import React, { PropTypes } from 'react' +import BSelect2 from './BSelect2' +import OlMap from './OlMap' + +export default function StopPoint(props, {I18n}) { + return ( + <div className='nested-fields'> + <div className='wrapper'> + <div style={{width: 90}}> + <span>{props.value.user_objectid}</span> + </div> + + <div> + <BSelect2 id={'route_stop_points_' + props.id} value={props.value} onChange={props.onChange} index={props.index} /> + </div> + + <div> + <select className='form-control' value={props.value.for_boarding} id="for_boarding" onChange={props.onSelectChange}> + <option value="normal">{I18n.routes.edit.stop_point.boarding.normal}</option> + <option value="forbidden">{I18n.routes.edit.stop_point.boarding.forbidden}</option> + </select> + </div> + + <div> + <select className='form-control' value={props.value.for_alighting} id="for_alighting" onChange={props.onSelectChange}> + <option value="normal">{I18n.routes.edit.stop_point.alighting.normal}</option> + <option value="forbidden">{I18n.routes.edit.stop_point.alighting.forbidden}</option> + </select> + </div> + + <div className='actions-5'> + <div + className={'btn btn-link' + (props.value.stoparea_id ? '' : ' disabled')} + onClick={props.onToggleMap} + > + <span className='fa fa-map-marker'></span> + </div> + + <div + className={'btn btn-link' + (props.first ? ' disabled' : '')} + onClick={props.onMoveUpClick} + > + <span className='fa fa-arrow-up'></span> + </div> + <div + className={'btn btn-link' + (props.last ? ' disabled' : '')} + onClick={props.onMoveDownClick} + > + <span className='fa fa-arrow-down'></span> + </div> + + <div + className='btn btn-link' + onClick={props.onToggleEdit} + > + <span className={'fa' + (props.value.edit ? ' fa-check' : ' fa-pencil')}></span> + </div> + <div + className='btn btn-link' + onClick={props.onDeleteClick} + > + <span className='fa fa-trash text-danger'></span> + </div> + </div> + </div> + + <OlMap + value = {props.value} + index = {props.index} + onSelectMarker = {props.onSelectMarker} + onUnselectMarker = {props.onUnselectMarker} + onUpdateViaOlMap = {props.onUpdateViaOlMap} + /> + </div> + ) +} + +StopPoint.PropTypes = { + onToggleMap: PropTypes.func.isRequired, + onToggleEdit: PropTypes.func.isRequired, + onDeleteClick: PropTypes.func.isRequired, + onMoveUpClick: PropTypes.func.isRequired, + onMoveDownClick: PropTypes.func.isRequired, + onChange: PropTypes.func.isRequired, + onSelectChange: PropTypes.func.isRequired, + first: PropTypes.bool, + last: PropTypes.bool, + index: PropTypes.number, + value: PropTypes.object +} + +StopPoint.contextTypes = { + I18n: PropTypes.object +}
\ No newline at end of file diff --git a/app/javascript/routes/components/StopPointList.js b/app/javascript/routes/components/StopPointList.js new file mode 100644 index 000000000..68af16f57 --- /dev/null +++ b/app/javascript/routes/components/StopPointList.js @@ -0,0 +1,69 @@ +import React, { PropTypes } from 'react' +import StopPoint from './StopPoint' + +export default function StopPointList({ stopPoints, onDeleteClick, onMoveUpClick, onMoveDownClick, onChange, onSelectChange, onToggleMap, onToggleEdit, onSelectMarker, onUnselectMarker, onUpdateViaOlMap }, {I18n}) { + return ( + <div className='subform'> + <div className='nested-head'> + <div className="wrapper"> + <div style={{width: 100}}> + <div className="form-group"> + <label className="control-label">{I18n.reflex_id}</label> + </div> + </div> + <div> + <div className="form-group"> + <label className="control-label">{I18n.simple_form.labels.stop_point.name}</label> + </div> + </div> + <div> + <div className="form-group"> + <label className="control-label">{I18n.simple_form.labels.stop_point.for_boarding}</label> + </div> + </div> + <div> + <div className="form-group"> + <label className="control-label">{I18n.simple_form.labels.stop_point.for_alighting}</label> + </div> + </div> + <div className='actions-5'></div> + </div> + </div> + {stopPoints.map((stopPoint, index) => + <StopPoint + key={'item-' + index} + onDeleteClick={() => onDeleteClick(index)} + onMoveUpClick={() => { + onMoveUpClick(index) + }} + onMoveDownClick={() => onMoveDownClick(index)} + onChange={ onChange } + onSelectChange={ (e) => onSelectChange(e, index) } + onToggleMap={() => onToggleMap(index)} + onToggleEdit={() => onToggleEdit(index)} + onSelectMarker={onSelectMarker} + onUnselectMarker={onUnselectMarker} + onUpdateViaOlMap={onUpdateViaOlMap} + first={ index === 0 } + last={ index === (stopPoints.length - 1) } + index={ index } + value={ stopPoint } + /> + )} + </div> + ) +} + +StopPointList.PropTypes = { + stopPoints: PropTypes.array.isRequired, + onDeleteClick: PropTypes.func.isRequired, + onMoveUpClick: PropTypes.func.isRequired, + onMoveDownClick: PropTypes.func.isRequired, + onSelectChange: PropTypes.func.isRequired, + onSelectMarker: PropTypes.func.isRequired, + onUnselectMarker : PropTypes.func.isRequired +} + +StopPointList.contextTypes = { + I18n: PropTypes.object +}
\ No newline at end of file |
