├── .gitignore
├── examples
├── example1.html
└── src
│ └── example1.js
├── README.md
├── LICENSE
├── src
├── info-window.js
├── marker.js
└── index.js
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | lib
2 | node_modules
3 | examples/*.js
4 | npm-debug.log
5 |
--------------------------------------------------------------------------------
/examples/example1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-maps
2 |
3 | [](https://www.npmjs.org/package/react-maps)
4 |
5 | Read the code for an understanding
6 |
7 |
8 | ## Example
9 |
10 | ``` javascript
11 | // ...
12 |
13 | render: function() {
14 | // Melbourne
15 | var center = {
16 | lat: -37.8602828,
17 | lng: 145.079616
18 | }
19 |
20 | return (
21 |
22 | Melbourne
23 |
24 |
25 | )
26 | }
27 | ```
28 |
29 | ## LICENSE [MIT](LICENSE)
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Daniel Cousens
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/info-window.js:
--------------------------------------------------------------------------------
1 | /* global google */
2 |
3 | let React = require('react')
4 | let ReactDOMServer = require('react-dom/server')
5 | let blacklist = require('blacklist')
6 |
7 | module.exports = React.createClass({
8 | getDefaultProps () {
9 | return {
10 | // WARNING: if autoPan is enabled, the parent maps props will be overridden
11 | disableAutoPan: true
12 | }
13 | },
14 |
15 | componentWillUnmount () {
16 | if (!this.iw) return
17 |
18 | this.iw.close()
19 | },
20 |
21 | render () {
22 | let props = this.props
23 | let options = blacklist(this.props, 'anchor', 'children', 'map', 'open')
24 |
25 | if (props.children) {
26 | options.content = ReactDOMServer.renderToStaticMarkup(React.createElement('span', {}, props.children))
27 | }
28 |
29 | // new
30 | if (!this.iw) {
31 | this.iw = new google.maps.InfoWindow(options)
32 |
33 | // existing
34 | } else {
35 | this.iw.setOptions(options)
36 | }
37 |
38 | if (props.open) {
39 | if (!props.map) return null
40 |
41 | this.iw.open(props.map, props.anchor)
42 |
43 | } else {
44 | this.iw.close()
45 | }
46 |
47 | return null
48 | }
49 | })
50 |
--------------------------------------------------------------------------------
/src/marker.js:
--------------------------------------------------------------------------------
1 | /* global google */
2 |
3 | let React = require('react')
4 | let blacklist = require('blacklist')
5 |
6 | module.exports = React.createClass({
7 | propTypes: {
8 | onClick: React.PropTypes.func
9 | },
10 |
11 | componentWillUnmount () {
12 | if (!this.marker) return
13 |
14 | this.marker.setMap(null)
15 | google.maps.event.clearInstanceListeners(this.marker)
16 | },
17 |
18 | getMarker () {
19 | return this.marker
20 | },
21 |
22 | onClick () {
23 | if (!this.props.onClick) return
24 |
25 | this.props.onClick()
26 | },
27 |
28 | render () {
29 | let options = blacklist(this.props, {})
30 | if (!options.map) return null
31 |
32 | // avoid superfluous map reset
33 | if (options.map === this.map) {
34 | options = blacklist(options, 'map')
35 |
36 | } else {
37 | this.map = options.map
38 | }
39 |
40 | // new
41 | if (!this.marker) {
42 | this.marker = new google.maps.Marker(options)
43 | google.maps.event.addListener(this.marker, 'click', this.onClick)
44 |
45 | // existing
46 | } else {
47 | this.marker.setOptions(options)
48 | }
49 |
50 | return null
51 | }
52 | })
53 |
54 | module.exports.Animation = google.maps.Animation
55 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-maps",
3 | "version": "0.4.0",
4 | "description": "A Google map API wrapper for React",
5 | "author": "Daniel Cousens",
6 | "license": "MIT",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/dcousens/react-maps.git"
10 | },
11 | "bugs": {
12 | "url": "https://github.com/dcousens/react-maps/issues"
13 | },
14 | "homepage": "https://github.com/dcousens/react-maps",
15 | "keywords": [
16 | "react-maps"
17 | ],
18 | "main": "lib/index.js",
19 | "files": [
20 | "lib/"
21 | ],
22 | "scripts": {
23 | "build": "babel --out-dir lib/ src/",
24 | "build-examples": "find examples/src -name '*.js' -exec sh -c 'browserify -t babelify {} --outfile examples/`basename {}`' \\;",
25 | "prepublish": "npm run build",
26 | "standard": "standard",
27 | "start": "npm run watch-examples",
28 | "test": "npm run standard",
29 | "watch": "babel --watch --out-dir lib/ src/",
30 | "watch-examples": "find examples/src -name '*.js' -exec sh -c 'watchify -v -t babelify {} --outfile examples/`basename {}`' \\;"
31 | },
32 | "standard": {
33 | "ignore": [
34 | "lib/"
35 | ]
36 | },
37 | "dependencies": {
38 | "blacklist": "^1.1.2"
39 | },
40 | "devDependencies": {
41 | "babel": "*",
42 | "babelify": "*",
43 | "browserify": "*",
44 | "react": "^0.14.0",
45 | "react-dom": "^0.14.0",
46 | "standard": "*",
47 | "watchify": "*"
48 | },
49 | "peerDependencies": {
50 | "react": "^0.14.0",
51 | "react-dom": "^0.14.0"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/examples/src/example1.js:
--------------------------------------------------------------------------------
1 | let React = require('react')
2 | let ReactDOM = require('react-dom')
3 | let GoogleMap = require('../../src/index')
4 |
5 | let App = React.createClass({
6 | getInitialState () {
7 | return {
8 | t: 0,
9 | center: {
10 | lat: -37.9,
11 | lng: 145.08
12 | },
13 | marker: {
14 | lat: -37.9,
15 | lng: 145.08
16 | }
17 | }
18 | },
19 |
20 | componentDidMount () {
21 | setInterval(() => {
22 | let dx = 0.6 * Math.cos(this.state.t)
23 | let dy = 0.6 * Math.sin(this.state.t)
24 |
25 | this.setState({
26 | t: this.state.t + 0.01,
27 | marker: {
28 | lng: this.state.center.lng + dx,
29 | lat: this.state.center.lat + dy
30 | }
31 | })
32 | }, 10)
33 | },
34 |
35 | onClickMarker () {
36 | this.setState({ markerInfoOpen: !this.state.markerInfoOpen })
37 | },
38 |
39 | render () {
40 | let { center, marker, markerInfoOpen } = this.state
41 | let marker1 = this.refs.marker1
42 |
43 | return (
44 |
45 |
46 |
47 | Marker
48 |
49 |
50 |
51 | Center
52 |
53 |
54 | )
55 | }
56 | })
57 |
58 | ReactDOM.render(React.createElement(App), document.getElementById('app'))
59 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /* global google */
2 |
3 | let React = require('react')
4 | let ReactDOM = require('react-dom')
5 | let blacklist = require('blacklist')
6 |
7 | module.exports = React.createClass({
8 | getDefaultProps () {
9 | return {
10 | // WARNING: even if draggable/panControl is enable, props.center will still be authoritative
11 | draggable: false,
12 | panControl: false,
13 |
14 | // WARNING: even if rotateControl is enabled, props.heading will still be authoritative
15 | rotateControl: false,
16 |
17 | // WARNING: if scaleControl is enabled, ... something
18 | scaleControl: false,
19 |
20 | // WARNING: even if these are enabled, props.zoom will still be authoritative
21 | disableDoubleClickZoom: true,
22 | scrollwheel: false,
23 | zoomControl: false,
24 |
25 | // WARNING: even if enabled, props.mapTypeId will still be authoritative
26 | mapTypeControl: false,
27 |
28 | // WARNING: be careful with this
29 | keyboardShortcuts: false,
30 | streetViewControl: false,
31 |
32 | noClear: true
33 | }
34 | },
35 |
36 | getInitialState () {
37 | return {}
38 | },
39 |
40 | componentDidMount () { this.updateMap(this.props) },
41 | componentWillReceiveProps: function (newProps) { this.updateMap(newProps) },
42 |
43 | updateMap: function (newProps) {
44 | let domNode = ReactDOM.findDOMNode(this)
45 | let options = blacklist(newProps, 'children', 'pan', 'autofit')
46 | let map = this.state.map
47 |
48 | // create new map
49 | if (!map) {
50 | map = new google.maps.Map(domNode, options)
51 |
52 | // update existing map
53 | } else {
54 | map.setOptions(options)
55 | }
56 |
57 | // autofit the bounds to the children?
58 | if (newProps.autofit && newProps.children.length) {
59 | let bounds = new google.maps.LatLngBounds()
60 |
61 | React.Children.forEach(newProps.children, function (child) {
62 | let position = child.props.position
63 | if (!position) return
64 |
65 | bounds.extend(new google.maps.LatLng(position.lat, position.lng))
66 | })
67 |
68 | map.fitBounds(bounds)
69 | }
70 |
71 | this.setState({ map: map }, () => {
72 | if (!this.props.onUpdate) return
73 |
74 | this.props.onUpdate(map)
75 | })
76 | },
77 |
78 | getMap () { return this.state.map },
79 | getBounds () { return this.state.map.getBounds() },
80 | getCenter () { return this.state.map.getCenter() },
81 | getHeading () { return this.state.map.getHeading() },
82 | getProjection () { return this.state.map.getProjection() },
83 | getTilt () { return this.state.map.getTilt() },
84 | getZoom () { return this.state.map.getZoom() },
85 |
86 | render () {
87 | let map = this.state.map
88 | let children = React.Children.map(this.props.children, function (child) {
89 | return React.cloneElement(child, { map: map })
90 | })
91 |
92 | return (
93 |
94 | {children}
95 |
96 | )
97 | }
98 | })
99 |
100 | // expose the marker on the top level export
101 | module.exports.InfoWindow = require('./info-window')
102 | module.exports.Marker = require('./marker')
103 |
--------------------------------------------------------------------------------