You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
252 lines
6.1 KiB
252 lines
6.1 KiB
package react.map;
|
|
import react.ReactComponent;
|
|
import react.ReactMacro.jsx;
|
|
import Common;
|
|
import leaflet.L;
|
|
|
|
using Lambda;
|
|
|
|
|
|
/**
|
|
* Externs for react-leaflet
|
|
* @doc https://react-leaflet.js.org/docs/en/intro.html
|
|
*/
|
|
@:jsRequire('react-leaflet', 'Map')
|
|
extern class LeafMap extends ReactComponent {}
|
|
@:jsRequire('react-leaflet', 'TileLayer')
|
|
extern class TileLayer extends ReactComponent {}
|
|
@:jsRequire('react-leaflet', 'Marker')
|
|
extern class Marker extends ReactComponent {}
|
|
@:jsRequire('react-leaflet', 'CircleMarker')
|
|
extern class CircleMarker extends ReactComponent {}
|
|
@:jsRequire('react-leaflet', 'Popup')
|
|
extern class Popup extends ReactComponent {}
|
|
@:jsRequire('react-leaflet', 'FeatureGroup')
|
|
extern class FeatureGroup extends ReactComponent {}
|
|
@:jsRequire('react-leaflet', 'LayerGroup')
|
|
extern class LayerGroup extends ReactComponent {}
|
|
|
|
|
|
/*
|
|
extern class L2 {
|
|
static function icon(a:Dynamic):Dynamic;
|
|
static function latLng(lat:Float, lng:Float):Dynamic;
|
|
}*/
|
|
|
|
/**
|
|
* GroupItem
|
|
* @author rcrestey
|
|
*/
|
|
typedef GroupMapProps = {
|
|
var addressCoord:Dynamic;
|
|
var groups:Array<GroupOnMap>;
|
|
var fetchGroupsInsideBox:Box->Void;
|
|
var groupFocusedId:Int;
|
|
};
|
|
|
|
typedef GroupMapState = {
|
|
var isFitting:Bool;
|
|
var focusedMarker:Dynamic;
|
|
};
|
|
|
|
typedef Box = {
|
|
var minLat:Float;
|
|
var maxLat:Float;
|
|
var minLng:Float;
|
|
var maxLng:Float;
|
|
};
|
|
|
|
class GroupMap extends ReactComponentOfPropsAndState<GroupMapProps, GroupMapState> {
|
|
static inline var DEFAULT_LAT = 46.52863469527167; // center of France
|
|
static inline var DEFAULT_LNG = 2.43896484375; // center of France
|
|
static inline var INIT_ZOOM = 6;
|
|
static inline var DEFAULT_ZOOM = 13;
|
|
|
|
var map:Dynamic;
|
|
var featureGroup:Dynamic;
|
|
var markerMap = new Map<Int,Dynamic>();
|
|
|
|
var groupIcon = L.icon({
|
|
iconUrl: '/img/marker.svg',
|
|
iconSize: [40, 40],
|
|
iconAnchor: [20, 40],
|
|
popupAnchor: [0, -30],
|
|
className: 'icon'
|
|
});
|
|
|
|
var homeIcon = L.icon({
|
|
iconUrl: '/img/home.svg',
|
|
iconSize: [40, 40],
|
|
iconAnchor: [20, 20],
|
|
popupAnchor: [0, -30],
|
|
className: 'icon'
|
|
});
|
|
|
|
function new() {
|
|
super();
|
|
state = {
|
|
isFitting: false,
|
|
focusedMarker: null
|
|
};
|
|
}
|
|
|
|
function getMap(element:Dynamic):Void {
|
|
map = element.leafletElement;
|
|
}
|
|
|
|
function getFeatureGroup(element:Dynamic):Void {
|
|
featureGroup = element.leafletElement;
|
|
setState({
|
|
isFitting: true
|
|
}, fitBounds);
|
|
}
|
|
|
|
function getMarker(element:Dynamic, id:Int):Void {
|
|
if (element != null)
|
|
markerMap.set(id, element.leafletElement);
|
|
}
|
|
|
|
|
|
/**
|
|
* Call API to get groups in the current bounding box
|
|
*/
|
|
function getGroups() {
|
|
var bounds = map.getBounds();
|
|
var southWest = bounds.getSouthWest();
|
|
var northEast = bounds.getNorthEast();
|
|
|
|
props.fetchGroupsInsideBox({
|
|
minLat: southWest.lat,
|
|
maxLat: northEast.lat,
|
|
minLng: southWest.lng,
|
|
maxLng: northEast.lng
|
|
});
|
|
}
|
|
|
|
function fitBounds() {
|
|
map.fitBounds(featureGroup.getBounds(), {
|
|
padding: [30, 30]
|
|
});
|
|
}
|
|
|
|
function handleMoveEnd() {
|
|
if (
|
|
props.addressCoord != null &&
|
|
!Lambda.empty(props.groups) &&
|
|
map.distance(map.getCenter(), props.addressCoord) == 0
|
|
)
|
|
setState({
|
|
isFitting: true
|
|
}, fitBounds);
|
|
else if (state.isFitting)
|
|
setState({
|
|
isFitting: false
|
|
});
|
|
else
|
|
getGroups();
|
|
}
|
|
|
|
override public function componentDidMount() {
|
|
if (props.addressCoord == null)
|
|
getGroups();
|
|
}
|
|
|
|
override public function shouldComponentUpdate(nextProps:GroupMapProps, nextState:GroupMapState) {
|
|
if (nextState.focusedMarker != state.focusedMarker)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
override public function componentDidUpdate(prevProps:GroupMapProps, prevState:GroupMapState) {
|
|
if (props.groupFocusedId != null) {
|
|
if (
|
|
prevProps.groupFocusedId != props.groupFocusedId
|
|
|| state.focusedMarker == null
|
|
) {
|
|
if (state.focusedMarker != null)
|
|
state.focusedMarker.closePopup();
|
|
|
|
var focusedMarker = markerMap.get(props.groupFocusedId);
|
|
focusedMarker.openPopup();
|
|
|
|
setState({
|
|
focusedMarker: focusedMarker
|
|
});
|
|
}
|
|
}
|
|
else if (prevProps.groupFocusedId != null && state.focusedMarker != null) {
|
|
state.focusedMarker.closePopup();
|
|
|
|
setState({
|
|
focusedMarker: null
|
|
});
|
|
}
|
|
}
|
|
|
|
override public function render() {
|
|
var center = props.addressCoord == null
|
|
? L.latLng(DEFAULT_LAT, DEFAULT_LNG)
|
|
: props.addressCoord;
|
|
|
|
var zoom = props.addressCoord == null
|
|
? INIT_ZOOM
|
|
: DEFAULT_ZOOM;
|
|
|
|
return jsx('
|
|
<LeafMap
|
|
center=${center}
|
|
zoom=${zoom}
|
|
ref=${getMap}
|
|
onMoveEnd=${handleMoveEnd}
|
|
>
|
|
<TileLayer
|
|
attribution="&copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors"
|
|
url="https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoiYnViYXIiLCJhIjoiY2loM2lubmZpMDBwcGtxbHlwdmw0bXRkbCJ9.rfgXPakoGnXZ3wIGA3-1kQ"
|
|
id="bubar.cih3inmqd00tjuxm7oc2532l0"
|
|
/>
|
|
<FeatureGroup ref=${getFeatureGroup}>
|
|
${renderGroupMarkers()}
|
|
${renderHomeMarker()}
|
|
</FeatureGroup>
|
|
</LeafMap>
|
|
');
|
|
}
|
|
|
|
function renderGroupMarkers() {
|
|
var markers = props.groups.map(function(group) {
|
|
var coord = [group.place.latitude, group.place.longitude];
|
|
|
|
function markerGetter(e:Dynamic) {
|
|
getMarker(e, group.place.id);
|
|
}
|
|
|
|
var image = group.image==null ? null : jsx('<img className="groupImage img-responsive" src=${group.image}/>');
|
|
|
|
return jsx('
|
|
<Marker
|
|
position=${coord}
|
|
ref=${markerGetter}
|
|
key=${group.place.id}
|
|
icon=${groupIcon}
|
|
>
|
|
<Popup className="popup">
|
|
<div>
|
|
<a href=${"/group/"+group.id} target="_blank">
|
|
$image
|
|
<div className="groupName">${group.name}</div>
|
|
</a>
|
|
</div>
|
|
</Popup>
|
|
</Marker>
|
|
');
|
|
});
|
|
|
|
return jsx('<div>${markers}</div>');
|
|
}
|
|
|
|
function renderHomeMarker() {
|
|
if (props.addressCoord != null)
|
|
return jsx('<Marker position=${props.addressCoord} icon=${homeIcon} />');
|
|
return null;
|
|
}
|
|
}
|