From c776dcf0e1580ec3c31347950c00a57e33d061a8 Mon Sep 17 00:00:00 2001 From: Javid Date: Fri, 9 Feb 2018 16:46:14 -0700 Subject: [PATCH] Reuse map function: reparenting child nodes when reusing the saved map (#457) * Update readme * Remove unused code * Add comments * Import data from examples/data --- examples/reuse-map/README.md | 7 +++ examples/reuse-map/app.css | 30 ++++++++++++ examples/reuse-map/index.html | 11 +++++ examples/reuse-map/package.json | 20 ++++++++ examples/reuse-map/src/app.js | 27 +++++++++++ examples/reuse-map/src/bart-map.js | 65 ++++++++++++++++++++++++++ examples/reuse-map/src/marker-style.js | 30 ++++++++++++ examples/reuse-map/src/root.js | 6 +++ examples/reuse-map/webpack.config.js | 57 ++++++++++++++++++++++ src/mapbox/mapbox.js | 14 +++++- 10 files changed, 265 insertions(+), 2 deletions(-) create mode 100644 examples/reuse-map/README.md create mode 100644 examples/reuse-map/app.css create mode 100644 examples/reuse-map/index.html create mode 100644 examples/reuse-map/package.json create mode 100644 examples/reuse-map/src/app.js create mode 100644 examples/reuse-map/src/bart-map.js create mode 100644 examples/reuse-map/src/marker-style.js create mode 100644 examples/reuse-map/src/root.js create mode 100644 examples/reuse-map/webpack.config.js diff --git a/examples/reuse-map/README.md b/examples/reuse-map/README.md new file mode 100644 index 00000000..71a79268 --- /dev/null +++ b/examples/reuse-map/README.md @@ -0,0 +1,7 @@ +
+ +
+ +## Example: Reuse Map + +This example showcases how to reuse the same map without destroying it. diff --git a/examples/reuse-map/app.css b/examples/reuse-map/app.css new file mode 100644 index 00000000..22c2f75d --- /dev/null +++ b/examples/reuse-map/app.css @@ -0,0 +1,30 @@ +body { + margin: 0; + font-family: Helvetica, Arial, sans-serif; +} + +.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; +} + +label { + display: inline-block; + width: 160px; +} + +input { + margin-left: 20px; + max-width: 60px; +} diff --git a/examples/reuse-map/index.html b/examples/reuse-map/index.html new file mode 100644 index 00000000..3e542913 --- /dev/null +++ b/examples/reuse-map/index.html @@ -0,0 +1,11 @@ + + + + + react-map-gl Interaction Example + + + + + + diff --git a/examples/reuse-map/package.json b/examples/reuse-map/package.json new file mode 100644 index 00000000..8a52efc0 --- /dev/null +++ b/examples/reuse-map/package.json @@ -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" + } +} diff --git a/examples/reuse-map/src/app.js b/examples/reuse-map/src/app.js new file mode 100644 index 00000000..d920e4da --- /dev/null +++ b/examples/reuse-map/src/app.js @@ -0,0 +1,27 @@ +import React, {Component} from 'react'; +import {render} from 'react-dom'; +import BartMap from './bart-map'; + +export default class App extends Component { + + state = { + showMap: true + }; + + _toggleMap() { + this.setState({showMap: !this.state.showMap}); + } + + render() { + const {showMap} = this.state; + return ( +
+
+ Toggle Map +
+ {showMap && } +
+ ); + } + +} diff --git a/examples/reuse-map/src/bart-map.js b/examples/reuse-map/src/bart-map.js new file mode 100644 index 00000000..4ba9db4d --- /dev/null +++ b/examples/reuse-map/src/bart-map.js @@ -0,0 +1,65 @@ +import React, {Component} from 'react'; +import MapGL, {Marker} from '../../../src'; + +import bartStations from '../../data/bart-station.json'; + +const MAPBOX_TOKEN = ''; // Set your mapbox token here + +import MARKER_STYLE from './marker-style'; + +export default class BartMap extends Component { + + state = { + viewport: { + latitude: 37.729, + longitude: -122.36, + zoom: 11, + bearing: 0, + pitch: 50, + width: 500, + height: 500 + }, + settings: { + dragPan: true, + dragRotate: true, + scrollZoom: true, + touchZoom: true, + touchRotate: true, + keyboard: true, + doubleClickZoom: true, + minZoom: 0, + maxZoom: 20, + minPitch: 0, + maxPitch: 85 + } + }; + + _onViewportChange = viewport => this.setState({viewport}); + + _renderMarker(station, i) { + const {name, coordinates} = station; + return ( + +
{name}
+
+ ); + } + + render() { + const {viewport, settings} = this.state; + return ( + + + { bartStations.map(this._renderMarker) } + + ); + } + +} diff --git a/examples/reuse-map/src/marker-style.js b/examples/reuse-map/src/marker-style.js new file mode 100644 index 00000000..ab6546eb --- /dev/null +++ b/examples/reuse-map/src/marker-style.js @@ -0,0 +1,30 @@ +export default ` +.station:before { + content: ' '; + display: inline-block; + width: 8px; + height: 8px; + background: red; + border-radius: 8px; + margin: 0 8px; +} +.station { + border-radius: 20px; + padding-right: 12px; + margin: -12px; + color: transparent; + line-height: 24px; + font-size: 13px; + white-space: nowrap; +} +.station span { + display: none; +} +.station:hover { + background: rgba(0,0,0,0.8); + color: #fff; +} +.station:hover span { + display: inline-block; +} +`; diff --git a/examples/reuse-map/src/root.js b/examples/reuse-map/src/root.js new file mode 100644 index 00000000..715cac95 --- /dev/null +++ b/examples/reuse-map/src/root.js @@ -0,0 +1,6 @@ +/* global document */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import App from './app'; + +ReactDOM.render(, document.body.appendChild(document.createElement('div'))); diff --git a/examples/reuse-map/webpack.config.js b/examples/reuse-map/webpack.config.js new file mode 100644 index 00000000..1a53c5fd --- /dev/null +++ b/examples/reuse-map/webpack.config.js @@ -0,0 +1,57 @@ +// NOTE: To use this example standalone (e.g. outside of deck.gl 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 + }] + }] + }, + + resolve: { + alias: { + // From mapbox-gl-js README. Required for non-browserify bundlers (e.g. webpack): + 'mapbox-gl$': resolve('./node_modules/mapbox-gl/dist/mapbox-gl.js') + } + }, + + // 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; diff --git a/src/mapbox/mapbox.js b/src/mapbox/mapbox.js index c4537866..b31c7cc6 100644 --- a/src/mapbox/mapbox.js +++ b/src/mapbox/mapbox.js @@ -33,8 +33,7 @@ function noop() {} const propTypes = { // Creation parameters - // container: PropTypes.DOMElement || String - + container: PropTypes.object, /** The container to have the map. */ mapboxApiAccessToken: PropTypes.string, /** Mapbox API access token for Mapbox tiles/styles. */ attributionControl: PropTypes.bool, /** Show attribution control or not. */ preserveDrawingBuffer: PropTypes.bool, /** Useful when you want to export the canvas as a PNG. */ @@ -163,6 +162,17 @@ export default class Mapbox { // Reuse a saved map, if available if (props.reuseMaps && Mapbox.savedMap) { this._map = this.map = Mapbox.savedMap; + // When reusing the saved map, we need to reparent the map(canvas) and other child nodes + // intoto the new container from the props. + // Step1: reparenting child nodes from old container to new container + const oldContainer = this._map.getContainer(); + const newContainer = props.container; + newContainer.classList.add('mapboxgl-map'); + while (oldContainer.childNodes.length > 0) { + newContainer.appendChild(oldContainer.childNodes[0]); + } + // Step2: replace the internal container with new container from the react component + this._map._container = newContainer; Mapbox.savedMap = null; // TODO - need to call onload again, need to track with Promise? props.onLoad();