Skip to content

Commit

Permalink
Add drag and drop support for Marker (#576)
Browse files Browse the repository at this point in the history
  • Loading branch information
schnerd authored Aug 13, 2018
1 parent f28fb6f commit 9c302ae
Show file tree
Hide file tree
Showing 22 changed files with 608 additions and 22 deletions.
26 changes: 25 additions & 1 deletion docs/components/marker.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,35 @@ Offset of the marker from the left in pixels, negative number indicates left.
##### `offsetTop` {Number} - default: `0`
Offset of the marker from the top in pixels, negative number indicates up.

##### `draggable` {Boolean} - default `false`
Allows this marker component to be dragged around the map. (Use `onDragEnd` to capture the final position and update `longitude` and `latitude`).

##### `onDragStart` {Function}
Called when a draggable marker starts being dragged.

Parameters
- `event` - The pointer event.
+ `event.lngLat` - The geo coordinates where the drag started, as `[lng, lat]`.

##### `onDrag` {Function}
Continuously called while a draggable marker is being dragged.

Parameters
- `event` - The pointer event.
+ `event.lngLat` - The geo coordinates of the drag event, as `[lng, lat]`.

##### `onDragEnd` {Function}
Called when a draggable marker is released at its final position. This is usually a good time to capture `event.lngLat` and update the marker's `longitude` and `latitude` props.

Parameters
- `event` - The pointer event.
+ `event.lngLat` - The geo coordinates where the drag ended, as `[lng, lat]`.

##### `captureScroll` {Boolean} - default: `false`
Stop propagation of mouse wheel event to the map component. Can be used to stop map from zooming when this component is scrolled.

##### `captureDrag` {Boolean} - default: `true`
Stop propagation of dragstart event to the map component. Can be used to stop map from panning when this component is dragged.
Stop propagation of dragstart event to the map component. Can be used to stop map from panning when this component is dragged. Automatically true if `draggable` is `true`.

##### `captureClick` {Boolean} - default: `true`
Stop propagation of click event to the map component. Can be used to stop map from calling the `onClick` callback when this component is clicked.
Expand Down
3 changes: 2 additions & 1 deletion examples/controls/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ export default class App extends Component {

_renderCityMarker = (city, index) => {
return (
<Marker key={`marker-${index}`}
<Marker
key={`marker-${index}`}
longitude={city.longitude}
latitude={city.latitude} >
<CityPin size={20} onClick={() => this.setState({popupInfo: city})} />
Expand Down
10 changes: 6 additions & 4 deletions examples/controls/src/city-pin.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ const ICON = `M20.2,15.7L20.2,15.7c1.1-1.6,1.8-3.6,1.8-5.7c0-5.6-4.5-10-10-10S2,
C20.1,15.8,20.2,15.8,20.2,15.7z`;

const pinStyle = {
cursor: 'pointer',
fill: '#d00',
stroke: 'none'
};
Expand All @@ -16,9 +15,12 @@ export default class CityPin extends PureComponent {
const {size = 20, onClick} = this.props;

return (
<svg height={size} viewBox='0 0 24 24'
style={{...pinStyle, transform: `translate(${-size/2}px,${-size}px)`}}
onClick={onClick} >
<svg
height={size}
viewBox="0 0 24 24"
style={{...pinStyle, transform: `translate(${-size / 2}px,${-size}px)`}}
onClick={onClick}
>
<path d={ICON}/>
</svg>
);
Expand Down
2 changes: 1 addition & 1 deletion examples/controls/src/control-panel.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, {PureComponent} from 'react';

const defaultContainer = ({children}) => <div className="control-panel">{children}</div>;
const defaultContainer = ({children}) => <div className="control-panel">{children}</div>;

export default class ControlPanel extends PureComponent {
render() {
Expand Down
12 changes: 12 additions & 0 deletions examples/draggable-markers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<div align="center">
<img src="https://avatars3.githubusercontent.com/u/2105791?v=3&s=200" />
</div>

## Example: Draggable Marker

Demonstrates how Marker component can be dragged with react-map-gl.

```
npm install
npm start
```
20 changes: 20 additions & 0 deletions examples/draggable-markers/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
body {
margin: 0;
background: #000;
}

.control-panel {
position: absolute;
top: 0;
right: 0;
max-width: 320px;
background: #fff;
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
padding: 12px 24px;
margin: 20px;
font-size: 13px;
line-height: 2;
color: #6b6b76;
text-transform: uppercase;
outline: none;
}
12 changes: 12 additions & 0 deletions examples/draggable-markers/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html>
<head>
<meta charset='UTF-8' />
<title>react-map-gl Draggable marker Example</title>
<link rel="stylesheet" type="text/css" href="app.css" />
<link rel="stylesheet" type="text/css" href="node_modules/mapbox-gl/dist/mapbox-gl.css" />
</head>
<body>
<script src='bundle.js'></script>
</body>
</html>
20 changes: 20 additions & 0 deletions examples/draggable-markers/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"scripts": {
"start": "webpack-dev-server --progress --hot --open",
"start-local": "webpack-dev-server --env.local --progress --hot --open"
},
"dependencies": {
"react": "^16.0.0",
"react-dom": "^16.0.0",
"react-map-gl": "^3.2.0"
},
"devDependencies": {
"babel-core": "^6.21.0",
"babel-loader": "^6.2.10",
"babel-preset-es2015": "^6.18.0",
"babel-preset-react": "^6.16.0",
"babel-preset-stage-2": "^6.18.0",
"webpack": "^2.4.0",
"webpack-dev-server": "^2.4.0"
}
}
122 changes: 122 additions & 0 deletions examples/draggable-markers/src/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/* global window */
import React, {Component} from 'react';
import {render} from 'react-dom';
import MapGL, {Marker, Popup, NavigationControl} from 'react-map-gl';

import ControlPanel from './control-panel';
import Pin from './pin';

const TOKEN = ''; // Set your mapbox token here

const navStyle = {
position: 'absolute',
top: 0,
left: 0,
padding: '10px'
};

export default class App extends Component {

constructor(props) {
super(props);
this.state = {
viewport: {
latitude: 37.785164,
longitude: -100,
zoom: 3.5,
bearing: 0,
pitch: 0,
width: 500,
height: 500,
},
marker: {
latitude: 37.785164,
longitude: -100,
},
events: {}
};
}

componentDidMount() {
window.addEventListener('resize', this._resize);
this._resize();
}

componentWillUnmount() {
window.removeEventListener('resize', this._resize);
}

_resize = () => {
this.setState({
viewport: {
...this.state.viewport,
width: this.props.width || window.innerWidth,
height: this.props.height || window.innerHeight
}
});
};

_updateViewport = (viewport) => {
this.setState({viewport});
}

_logDragEvent(name, event) {
this.setState({
events: {
...this.state.events,
[name]: event.lngLat,
}
})
}

_onMarkerDragStart = (event) => {
this._logDragEvent('onDragStart', event);
};

_onMarkerDrag = (event) => {
this._logDragEvent('onDrag', event);
};

_onMarkerDragEnd = (event) => {
this._logDragEvent('onDragEnd', event);
this.setState({
marker: {
longitude: event.lngLat[0],
latitude: event.lngLat[1],
}
});
};

render() {

const {viewport, marker} = this.state;

return (
<MapGL
{...viewport}
mapStyle="mapbox://styles/mapbox/dark-v9"
onViewportChange={this._updateViewport}
mapboxApiAccessToken={TOKEN} >
<Marker
longitude={marker.longitude}
latitude={marker.latitude}
draggable
onDragStart={this._onMarkerDragStart}
onDrag={this._onMarkerDrag}
onDragEnd={this._onMarkerDragEnd} >
<Pin size={20} />
</Marker>

<div className="nav" style={navStyle}>
<NavigationControl onViewportChange={this._updateViewport} />
</div>

<ControlPanel
containerComponent={this.props.containerComponent}
events={this.state.events}
/>
</MapGL>
);
}

}
36 changes: 36 additions & 0 deletions examples/draggable-markers/src/control-panel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React, {PureComponent} from 'react';

const defaultContainer = ({children}) => <div className="control-panel">{children}</div>;

const eventNames = ['onDragStart', 'onDrag', 'onDragEnd'];

function round5(value) {
return (Math.round(value * 1e5) / 1e5).toFixed(5);
}

export default class ControlPanel extends PureComponent {
renderEvent = (eventName) => {
const {events = {}} = this.props;
const lngLat = events[eventName];
return (
<div>
<strong>{eventName}:</strong>{' '}
{lngLat ? lngLat.map(round5).join(', ') : <em>null</em>}
</div>
);
};

render() {
const Container = this.props.containerComponent || defaultContainer;
return (
<Container>
<h3>Draggable Marker</h3>
<p>Try dragging the marker to another location.</p>
<p>
{eventNames.map(this.renderEvent)}
</p>
{/* TODO add a "View Code" link here when we know the release */}
</Container>
);
}
}
27 changes: 27 additions & 0 deletions examples/draggable-markers/src/pin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React, {PureComponent} from 'react';

const ICON = `M20.2,15.7L20.2,15.7c1.1-1.6,1.8-3.6,1.8-5.7c0-5.6-4.5-10-10-10S2,4.5,2,10c0,2,0.6,3.9,1.6,5.4c0,0.1,0.1,0.2,0.2,0.3
c0,0,0.1,0.1,0.1,0.2c0.2,0.3,0.4,0.6,0.7,0.9c2.6,3.1,7.4,7.6,7.4,7.6s4.8-4.5,7.4-7.5c0.2-0.3,0.5-0.6,0.7-0.9
C20.1,15.8,20.2,15.8,20.2,15.7z`;

const pinStyle = {
fill: '#d00',
stroke: 'none'
};

export default class Pin extends PureComponent {

render() {
const {size = 20} = this.props;

return (
<svg
height={size}
viewBox="0 0 24 24"
style={{...pinStyle, transform: `translate(${-size / 2}px,${-size}px)`}}
>
<path d={ICON}/>
</svg>
);
}
}
6 changes: 6 additions & 0 deletions examples/draggable-markers/src/root.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/* global document */
import React from 'react';
import ReactDOM from 'react-dom';
import App from './app';

ReactDOM.render(<App/>, document.body.appendChild(document.createElement('div')));
50 changes: 50 additions & 0 deletions examples/draggable-markers/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// NOTE: To use this example standalone (e.g. outside of repo)
// delete the local development overrides at the bottom of this file

// avoid destructuring for older Node version support
const resolve = require('path').resolve;
const webpack = require('webpack');

// Otherwise modules imported from outside this directory does not compile.
// Also needed if modules from this directory were imported elsewhere
// Seems to be a Babel bug
// https://github.com/babel/babel-loader/issues/149#issuecomment-191991686
const BABEL_CONFIG = {
presets: [
'es2015',
'react',
'stage-2'
].map(function configMap(name) {
return require.resolve(`babel-preset-${name}`);
})
};

const config = {
entry: {
app: resolve('./src/root.js')
},

devtool: 'source-map',

module: {
rules: [{
// Compile ES2015 using bable
test: /\.js$/,
include: [resolve('.')],
exclude: [/node_modules/],
use: [{
loader: 'babel-loader',
options: BABEL_CONFIG
}]
}]
},

// Optional: Enables reading mapbox token from environment variable
plugins: [
new webpack.EnvironmentPlugin(['MapboxAccessToken'])
]
};

// Enables bundling against src in this repo rather than the installed version
module.exports = env => env && env.local ?
require('../webpack.config.local')(config)(env) : config;
Loading

0 comments on commit 9c302ae

Please sign in to comment.