Rewrite library using create-react-library (#900)

* Rewrite library using create-react-library
* Export utils properly
* Remove unnecessary code and fix eslintignore
* Update CHANGELOG
This commit is contained in:
Michael Diego 2020-07-25 03:03:03 -03:00 committed by GitHub
parent e70d0f748d
commit b3bc11c215
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
89 changed files with 20433 additions and 5233 deletions

View File

@ -1,3 +0,0 @@
{
"presets": [["env", {"loose": true}], "react", "stage-0"]
}

6
.eslintignore Normal file
View File

@ -0,0 +1,6 @@
build/
dist/
node_modules/
.snapshots/
*.min.js
*.spec.js

View File

@ -1,65 +1,33 @@
{
"parser": "babel-eslint",
"extends": [
"airbnb",
"prettier",
"prettier/flowtype",
"standard",
"standard-react",
"plugin:prettier/recommended",
"prettier/standard",
"prettier/react"
],
"plugins": [
"babel",
"prettier"
],
"parser": "babel-eslint",
"env": {
"es6": true,
"browser": true,
"mocha": true,
"jest": true,
"node": true
},
"parserOptions": {
"ecmaVersion": 2017,
"sourceType": "module",
"ecmaVersion": 2020,
"ecmaFeatures": {
"jsx": true,
"generators": true,
"experimentalObjectRestSpread": true
"legacyDecorators": true,
"jsx": true
}
},
"settings": {
"react": {
"version": "16"
}
},
"rules": {
"no-bitwise": 0,
"no-nested-ternary": 0,
"space-before-function-paren": 0,
"react/prop-types": 0,
"react/jsx-filename-extension": 0,
"react/require-default-props": 0,
"react/jsx-handler-names": 0,
"react/jsx-fragments": 0,
"react/no-unused-prop-types": 0,
"no-sequences": 1,
"comma-dangle": 0,
"no-mixed-operators": 0,
"no-underscore-dangle": 0,
"no-restricted-properties": 0,
"jsx-a11y/no-static-element-interactions": 0,
"import/no-extraneous-dependencies": 0,
"no-plusplus": 0,
"no-unused-vars": [
"error",
{
"ignoreRestSiblings": true
}
],
"react/sort-comp": 0,
"prettier/prettier": [
"error",
{
"trailingComma": "es5",
"singleQuote": true
}
]
},
"settings": {
"polyfills": [
"fetch__"
]
},
"globals": {}
"import/export": 0
}
}

View File

@ -1,35 +0,0 @@
[ignore]
.*/coverage/.*
.*/scripts/.*
.*/node_modules/flow-bin/.*
.*/node_modules/.*/flow-bin.*/.*
.*/node_modules/.*/broken.json
.*/node_modules/fixed-data-table/*
.*/node_modules/.*/fixtures/package.json
# .*/node_modules/.*/*.json
.*/node_modules/react-motion.*/*.js
.*/build/**/*.json
[libs]
./flowtypes
[include]
[options]
module.system=haste
esproposal.class_static_fields=enable
esproposal.class_instance_fields=enable
esproposal.decorators=ignore
suppress_comment=.*\\$FlowIssue
module.system.node.resolve_dirname=node_modules
module.system.node.resolve_dirname=src
module.system.node.resolve_dirname=app
munge_underscores=true
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(2[0-0]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-0]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy

26
.gitignore vendored
View File

@ -1,4 +1,22 @@
lib/*
dist/*
/node_modules
.idea/
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
node_modules
# builds
build
dist
.rpt2_cache
# misc
.DS_Store
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@ -1,8 +0,0 @@
src
scripts
__tests__
examples
.babelrc
.eslintrc
.github
.vscode

10
.prettierrc Normal file
View File

@ -0,0 +1,10 @@
{
"singleQuote": true,
"jsxSingleQuote": true,
"semi": true,
"tabWidth": 2,
"bracketSpacing": true,
"jsxBracketSameLine": false,
"arrowParens": "always",
"trailingComma": "es5"
}

View File

@ -1,11 +1,11 @@
language: node_js
node_js:
- stable
- stable
cache:
directories:
- node_modules
- node_modules
before_script:
- git diff --exit-code # make sure that yarn.lock didn't change
script:
- yarn run lint
- yarn test:lint
- yarn test

View File

@ -1,4 +0,0 @@
// Place your settings in this file to overwrite default and user settings.
{
"editor.formatOnSave": true
}

2
API.md
View File

@ -197,7 +197,7 @@ render() {
Example:
```javascript
import { fitBounds } from 'google-map-react/utils';
import { fitBounds } from 'google-map-react';
const bounds = {
nw: {

View File

@ -1,11 +1,18 @@
## [unreleased(major)]
### Changed
- Rewrite library using [create-react-library](https://www.npmjs.com/package/create-react-library)
- **Breaking** Move `google-map-react/utils` to module import, so instead of doing `import { utilName } from 'google-map-react/utils` you will do `import { utilName } from google-map-react`
## 0.9v
Add prop `onDragEnd` to react on the `dragend` event
Add [google-map-clustering-example](https://github.com/istarkov/google-map-clustering-example)
Add prop `onTilesLoaded` to react on the `tilesloaded` event
###0.9v
Add: `bootstrapURLKeys` (object) instead of `apiKey` prop
(`apiKey` prop is now deprecated)
@ -22,12 +29,11 @@ Example:
>
```
###0.8v
## 0.8v
Draggable markers support, examples comig soon.
###Mon Oct 5 2015
## Mon Oct 5 2015
Add `OnChange({center, zoom, bounds: {nw, se}, size, ...oherMapProps})`
@ -58,10 +64,10 @@ Add `utils` functions, with `fitBounds` and other functions
```
###Sun Oct 4 2015
## Sun Oct 4 2015
Add minZoom calculation, to prevent situations when one map point can have multiple screen coordinates.
###Sun Oct 4 2015
## Sun Oct 4 2015
Add ability to access to internal google api
```javascript
@ -71,14 +77,14 @@ Add ability to access to internal google api
(*to prevent warn message add _yesIWantToUseGoogleMapApiInternals_ property to GoogleMap*)
###Sun Oct 4 2015
## Sun Oct 4 2015
Add: `defaultZoom` `defaultCenter` properties, it closes #9 #10
###Sat Oct 3 2015
## Sat Oct 3 2015
Support `center` prop as `{lat, lng}` object
###Thu Oct 1 2015
## Thu Oct 1 2015
Add `onClick`, `onZoomAnimationStart`, `onZoomAnimationEnd` events.

2
DOC.md
View File

@ -144,7 +144,7 @@ _)
### Helper utilities
```javascript
import { meters2ScreenPixels } from 'google-map-react/utils';
import { meters2ScreenPixels } from 'google-map-react';
const { w, h } = meters2ScreenPixels(sizeInMeters, { lat, lng } /* marker coords*/, zoom /* map zoom*/);
```

View File

@ -1,31 +0,0 @@
{
"name": "google-map-react",
"description": "isomorphic google map react component, allows render react components on the google map",
"main": "lib/umd/GoogleMapReact.js",
"homepage": "https://github.com/google-map-react/google-map-react",
"authors": [
"Ivan Starkov <istarkov@gmail.com>"
],
"keywords": [
"react",
"reactjs",
"google",
"map",
"maps",
"isomorphic",
"render",
"component",
"javascript",
"react-component"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"src",
"scripts",
"package.json",
"webpack.config.js"
]
}

View File

@ -1,109 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
compose,
defaultProps,
withHandlers,
withState,
withContext,
withProps,
withPropsOnChange,
} from 'recompose';
import { createSelector } from 'reselect';
import { susolvkaCoords, generateMarkers } from './data/fakeData';
import SimpleMarker from './markers/SimpleMarker';
import GoogleMapReact from '../src';
import ptInBounds from './utils/ptInBounds';
import withStateSelector from './utils/withStateSelector';
import { GOOGLE_API_KEY } from './config/Google_API_key';
export const gMap = (
{
style,
hoverDistance,
options,
mapParams: { center, zoom },
onChange,
onChildMouseEnter,
onChildMouseLeave,
markers,
draggable, // hoveredMarkerId,
}
) => (
<GoogleMapReact
bootstrapURLKeys={{
key: GOOGLE_API_KEY,
}}
style={style}
options={options}
draggable={draggable}
hoverDistance={hoverDistance}
zoom={zoom}
center={center}
onChange={onChange}
onChildMouseEnter={onChildMouseEnter}
onChildMouseLeave={onChildMouseLeave}
>
{markers}
</GoogleMapReact>
);
export const gMapHOC = compose(
defaultProps({
clusterRadius: 60,
hoverDistance: 30,
options: {
minZoom: 3,
maxZoom: 15,
},
style: {
position: 'relative',
margin: 0,
padding: 0,
flex: 1,
},
}),
withContext({ hello: PropTypes.string }, () => ({ hello: 'world' })),
// withState so you could change markers if you want
withStateSelector('markers', 'setMarkers', () =>
createSelector(
({ route: { markersCount = 20 } }) => markersCount,
markersCount => generateMarkers(markersCount)
)),
withState('hoveredMarkerId', 'setHoveredMarkerId', -1),
withState('mapParams', 'setMapParams', { center: susolvkaCoords, zoom: 6 }),
// describe events
withHandlers({
onChange: ({ setMapParams }) =>
({ center, zoom, bounds }) => {
setMapParams({ center, zoom, bounds });
},
onChildMouseEnter: ({ setHoveredMarkerId }) =>
(hoverKey, { id }) => {
setHoveredMarkerId(id);
},
onChildMouseLeave: ({ setHoveredMarkerId }) =>
() => {
setHoveredMarkerId(-1);
},
}),
withPropsOnChange(['markers', 'mapParams'], ({
markers,
mapParams: { bounds },
}) => ({
markers: bounds ? markers.filter(m => ptInBounds(bounds, m)) : [],
})),
withProps(({ hoveredMarkerId }) => ({
draggable: hoveredMarkerId === -1,
})),
withPropsOnChange(['markers'], ({ markers }) => ({
markers: markers.map(({ ...markerProps, id }) => (
<SimpleMarker key={id} id={id} {...markerProps} />
)),
}))
);
export default gMapHOC(gMap);

View File

@ -1,129 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
compose,
defaultProps,
withHandlers,
withState,
withContext,
withProps,
withPropsOnChange,
} from 'recompose';
import { createSelector } from 'reselect';
import {
susolvkaCoords,
generateMarkers,
heatmapData,
generateHeatmapData,
} from './data/fakeData';
import GoogleMapReact from '../src';
import SimpleMarker from './markers/SimpleMarker';
import ptInBounds from './utils/ptInBounds';
import withStateSelector from './utils/withStateSelector';
import { GOOGLE_API_KEY } from './config/Google_API_key';
import withSafeInterval from './utils/withSafeInterval';
export const gMapHeatmap = (
{
style,
hoverDistance,
options,
heatmap,
mapParams: { center, zoom },
onChange,
onChildMouseEnter,
onChildMouseLeave,
markers,
draggable, // hoveredMarkerId,
}
) => (
<GoogleMapReact
bootstrapURLKeys={{
key: GOOGLE_API_KEY,
}}
style={style}
options={options}
draggable={draggable}
hoverDistance={hoverDistance}
zoom={zoom}
center={center}
onChange={onChange}
onChildMouseEnter={onChildMouseEnter}
onChildMouseLeave={onChildMouseLeave}
heatmap={heatmap}
heatmapLibrary
>
{markers}
</GoogleMapReact>
);
export const gMapHOC = compose(
defaultProps({
clusterRadius: 60,
hoverDistance: 30,
options: {
minZoom: 3,
maxZoom: 15,
},
style: {
position: 'relative',
margin: 10,
padding: 10,
flex: 1,
},
}),
withContext({ hello: PropTypes.string }, () => ({ hello: 'world' })),
// withState so you could change markers if you want
withStateSelector('markers', 'setMarkers', () =>
createSelector(
({ route: { markersCount = 20 } }) => markersCount,
markersCount => generateMarkers(markersCount)
)),
withState('hoveredMarkerId', 'setHoveredMarkerId', -1),
withState('mapParams', 'setMapParams', { center: susolvkaCoords, zoom: 6 }),
withSafeInterval,
withState('heatmap', 'setHeatmap', heatmapData),
// describe events
withHandlers({
onChange: ({ setMapParams, setSafeInterval, setHeatmap, mapParams }) =>
({ center, zoom, bounds }) => {
setMapParams({ center, zoom, bounds });
const boundSetHeatmap = setHeatmap.bind(this);
setSafeInterval(
() => {
boundSetHeatmap(
generateHeatmapData(mapParams.center.lat, mapParams.center.lng)
);
},
3000
);
},
onChildMouseEnter: ({ setHoveredMarkerId }) =>
(hoverKey, { id }) => {
setHoveredMarkerId(id);
},
onChildMouseLeave: ({ setHoveredMarkerId }) =>
() => {
setHoveredMarkerId(-1);
},
}),
withPropsOnChange(['markers', 'mapParams'], ({
markers,
mapParams: { bounds },
}) => ({
markers: bounds ? markers.filter(m => ptInBounds(bounds, m)) : [],
})),
withProps(({ hoveredMarkerId }) => ({
draggable: hoveredMarkerId === -1,
})),
withPropsOnChange(['markers'], ({ markers }) => ({
markers: markers.map(({ ...markerProps, id }) => (
<SimpleMarker key={id} id={id} {...markerProps} />
)),
}))
);
export default gMapHOC(gMapHeatmap);

View File

@ -1,114 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
compose,
defaultProps,
withHandlers,
withState,
withContext,
withProps,
withPropsOnChange,
} from 'recompose';
import { createSelector } from 'reselect';
import { londonCoords, generateMarkers } from './data/fakeData';
import GoogleMapReact from '../src';
import SimpleMarker from './markers/SimpleMarker';
import ptInBounds from './utils/ptInBounds';
import withStateSelector from './utils/withStateSelector';
import { GOOGLE_API_KEY } from './config/Google_API_key';
export const gMap = (
{
style,
hoverDistance,
options,
mapParams: { center, zoom },
onChange,
onChildMouseEnter,
onChildMouseLeave,
markers,
draggable, // hoveredMarkerId,
}
) => (
<GoogleMapReact
bootstrapURLKeys={{
key: GOOGLE_API_KEY,
}}
style={style}
options={options}
draggable={draggable}
hoverDistance={hoverDistance}
zoom={zoom}
center={center}
onChange={onChange}
onChildMouseEnter={onChildMouseEnter}
onChildMouseLeave={onChildMouseLeave}
layerTypes={
zoom > 12
? []
: zoom > 10 ? ['TrafficLayer'] : ['TrafficLayer', 'TransitLayer']
}
>
{markers}
</GoogleMapReact>
);
export const gMapHOC = compose(
defaultProps({
clusterRadius: 60,
hoverDistance: 30,
options: {
minZoom: 3,
maxZoom: 15,
},
style: {
position: 'relative',
margin: 0,
padding: 0,
flex: 1,
},
}),
withContext({ hello: PropTypes.string }, () => ({ hello: 'world' })),
// withState so you could change markers if you want
withStateSelector('markers', 'setMarkers', () =>
createSelector(
({ route: { markersCount = 20 } }) => markersCount,
markersCount => generateMarkers(markersCount)
)),
withState('hoveredMarkerId', 'setHoveredMarkerId', -1),
withState('mapParams', 'setMapParams', { center: londonCoords, zoom: 9 }),
// describe events
withHandlers({
onChange: ({ setMapParams }) =>
({ center, zoom, bounds }) => {
setMapParams({ center, zoom, bounds });
},
onChildMouseEnter: ({ setHoveredMarkerId }) =>
(hoverKey, { id }) => {
setHoveredMarkerId(id);
},
onChildMouseLeave: ({ setHoveredMarkerId }) =>
() => {
setHoveredMarkerId(-1);
},
}),
withPropsOnChange(['markers', 'mapParams'], ({
markers,
mapParams: { bounds },
}) => ({
markers: bounds ? markers.filter(m => ptInBounds(bounds, m)) : [],
})),
withProps(({ hoveredMarkerId }) => ({
draggable: hoveredMarkerId === -1,
})),
withPropsOnChange(['markers'], ({ markers }) => ({
markers: markers.map(({ ...markerProps, id }) => (
<SimpleMarker key={id} id={id} {...markerProps} />
)),
}))
);
export default gMapHOC(gMap);

View File

@ -1,124 +0,0 @@
// Example to test the React Reconciler. This example
// is 100x faster in development mode,
// and 1.5-2x faster with NODE_ENV==='production'
// The idea was to not draw map children on hovers, but subscribe inside children on hover change
// see ./markers/ReactiveMarker source
import React from 'react';
import {
compose,
defaultProps,
withHandlers,
withState,
withProps,
withPropsOnChange,
} from 'recompose';
import { createSelector } from 'reselect';
import { susolvkaCoords, generateMarkers } from './data/fakeData';
import GoogleMapReact from '../src';
// import SimpleMarker from './markers/SimpleMarker';
import ReactiveMarker from './markers/ReactiveMarker';
import ptInBounds from './utils/ptInBounds';
import props2Stream from './utils/props2Stream';
import withStateSelector from './utils/withStateSelector';
import { GOOGLE_API_KEY } from './config/Google_API_key';
export const gMap = (
{
style,
hoverDistance,
options,
mapParams: { center, zoom },
onChange,
onChildMouseEnter,
onChildMouseLeave,
markers,
draggable,
}
) => (
<GoogleMapReact
bootstrapURLKeys={{
key: GOOGLE_API_KEY,
}}
style={style}
options={options}
draggable={draggable}
hoverDistance={hoverDistance}
zoom={zoom}
center={center}
onChange={onChange}
onChildMouseEnter={onChildMouseEnter}
onChildMouseLeave={onChildMouseLeave}
experimental
>
{markers}
</GoogleMapReact>
);
export const gMapHOC = compose(
defaultProps({
clusterRadius: 60,
hoverDistance: 30,
options: {
minZoom: 3,
maxZoom: 15,
},
style: {
position: 'relative',
margin: 0,
padding: 0,
flex: 1,
},
}),
// withState so you could change markers if you want
withStateSelector('markers', 'setMarkers', () =>
createSelector(
({ route: { markersCount = 20 } }) => markersCount,
markersCount => generateMarkers(markersCount)
)),
withState('hoveredMarkerId', 'setHoveredMarkerId', -1),
withState('mapParams', 'setMapParams', { center: susolvkaCoords, zoom: 6 }),
// describe events
withHandlers({
onChange: ({ setMapParams }) =>
({ center, zoom, bounds }) => {
setMapParams({ center, zoom, bounds });
},
onChildMouseEnter: ({ setHoveredMarkerId }) =>
(hoverKey, { id }) => {
setHoveredMarkerId(id);
},
onChildMouseLeave: ({ setHoveredMarkerId }) =>
() => {
setHoveredMarkerId(-1);
},
}),
withPropsOnChange(['markers', 'mapParams'], ({
markers,
mapParams: { bounds },
}) => ({
markers: bounds ? markers.filter(m => ptInBounds(bounds, m)) : [],
})),
withProps(({ hoveredMarkerId }) => ({
draggable: hoveredMarkerId === -1,
})),
props2Stream('hoveredMarkerId'),
withPropsOnChange(['markers', 'hoveredMarkerId$'], ({
markers,
hoveredMarkerId$,
}) => ({
markers: markers.map(({ ...markerProps, id }) => (
<ReactiveMarker
key={id}
id={id}
hoveredMarkerId$={hoveredMarkerId$}
{...markerProps}
/>
)),
}))
);
export default gMapHOC(gMap);

View File

@ -1,111 +0,0 @@
/* eslint-disable */
import React from 'react';
import PropTypes from 'prop-types';
import {
compose,
defaultProps,
withHandlers,
withState,
withContext,
withProps,
withPropsOnChange,
} from 'recompose';
import { createSelector } from 'reselect';
import { susolvkaCoords, generateMarkers } from './data/fakeData';
import GoogleMapReact from '../src';
import SimpleMarker from './markers/SimpleMarker';
import ptInBounds from './utils/ptInBounds';
import withStateSelector from './utils/withStateSelector';
import { GOOGLE_API_KEY } from './config/Google_API_key';
export const gMapResizable = (
{
style,
hoverDistance,
options,
mapParams: { center, zoom },
onChange,
onChildMouseEnter,
onChildMouseLeave,
markers,
draggable, // hoveredMarkerId,
}
) => (
<GoogleMapReact
bootstrapURLKeys={{
key: GOOGLE_API_KEY,
}}
style={style}
options={options}
draggable={draggable}
hoverDistance={hoverDistance}
zoom={zoom}
center={center}
onChange={onChange}
onChildMouseEnter={onChildMouseEnter}
onChildMouseLeave={onChildMouseLeave}
resetBoundsOnResize
>
{markers}
</GoogleMapReact>
);
export const gMapHOC = compose(
defaultProps({
clusterRadius: 60,
hoverDistance: 30,
options: {
minZoom: 3,
maxZoom: 15,
},
style: {
position: 'relative',
margin: 10,
padding: 10,
flex: 1,
},
}),
withContext({ hello: PropTypes.string }, () => ({ hello: 'world' })),
// withState so you could change markers if you want
withStateSelector('markers', 'setMarkers', () =>
createSelector(
({ route: { markersCount = 20 } }) => markersCount,
markersCount => generateMarkers(markersCount)
)),
withState('hoveredMarkerId', 'setHoveredMarkerId', -1),
withState('mapParams', 'setMapParams', { center: susolvkaCoords, zoom: 6 }),
// describe events
withHandlers({
onChange: ({ setMapParams }) =>
({ center, zoom, bounds }) => {
setMapParams({ center, zoom, bounds });
},
onChildMouseEnter: ({ setHoveredMarkerId }) =>
(hoverKey, { id }) => {
setHoveredMarkerId(id);
},
onChildMouseLeave: ({ setHoveredMarkerId }) =>
() => {
setHoveredMarkerId(-1);
},
}),
withPropsOnChange(['markers', 'mapParams'], ({
markers,
mapParams: { bounds },
}) => ({
markers: bounds ? markers.filter(m => ptInBounds(bounds, m)) : [],
})),
withProps(({ hoveredMarkerId }) => ({
draggable: hoveredMarkerId === -1,
})),
withPropsOnChange(['markers'], ({ markers }) => ({
markers: markers.map(({ ...markerProps, id }) => (
<SimpleMarker key={id} id={id} {...markerProps} />
)),
}))
);
export default gMapHOC(gMapResizable);

View File

@ -1,58 +0,0 @@
import { Link } from 'react-router';
import React, { Component } from 'react';
import { compose, defaultProps } from 'recompose';
import layoutStyles from './Layout.sass';
// for hmr to work I need the first class to extend Component
/* eslint-disable react/prefer-stateless-function */
export class Layout extends Component {
render() {
const {
styles: { layout, header, main, footer, logo, links },
} = this.props;
return (
<div className={layout}>
<header className={header}>
<div className={links}>
<Link to="/">Multi Markers</Link>
<Link to="/layers">With layers</Link>
<Link to="/hoverunoptim">Hover unoptim</Link>
<Link to="/hoveroptim">Hover optim</Link>
<Link to="/resizable">Resizable Map</Link>
<Link to="/heatmap">Heatmap</Link>
</div>
<div>
<a href="https://github.com/istarkov/google-map-clustering-example">
Star at github.com
</a>
</div>
</header>
<main className={main}>
{this.props.children}
</main>
<footer className={footer}>
<div>
<a href="https://github.com/istarkov">
Ivan Starkov
</a>
</div>
<div className={logo} />
<div>
<a href="https://twitter.com/icelabaratory">
@icelabaratory
</a>
</div>
</footer>
</div>
);
}
}
export const layoutHOC = compose(
defaultProps({
styles: layoutStyles,
})
);
export default layoutHOC(Layout);

View File

@ -1,44 +0,0 @@
.layout
display: flex
min-height: 90vh
flex-direction: column
margin: 0 1px 0 1px
width: 100vw
.header
height: 2em
background-color: #004336
color: #fff
display: flex
align-items: center
justify-content: space-between
padding: 0 10px 0 10px
a
color: #fff
.links
a
margin-right: 20px
.logo
width: 1.3em
height: 1.3em
margin: 0.3em
background-size: contain
background-repeat: no-repeat
background-image: url('https://avatars2.githubusercontent.com/u/5077042?v=3&s=40')
.main
flex: 1
resize: both
overflow: auto
display: flex
.footer
height: 2em
background-color: #004336
color: #fff
display: flex
align-items: center
justify-content: center
a
color: #fff

View File

@ -1,30 +0,0 @@
/* eslint-disable import/no-named-as-default */
import React from 'react';
import { render } from 'react-dom';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import 'normalize.css/normalize.css';
import GMap from './GMap';
import Layout from './Layout';
import GMapOptim from './GMapOptim';
import GMapLayers from './GMapLayers';
import GMapHeatmap from './GMapHeatmap';
import GMapResizable from './GMapResizable';
import './Main.sass';
const mountNode = document.getElementById('app');
render(
<Router history={browserHistory}>
<Route path="/" component={Layout}>
<Route markersCount={50} path="hoverunoptim" component={GMap} />
<Route markersCount={50} path="layers" component={GMapLayers} />
<Route markersCount={50} path="hoveroptim" component={GMapOptim} />
<Route markersCount={20} path="resizable" component={GMapResizable} />
<Route markersCount={20} path="heatmap" component={GMapHeatmap} />
<IndexRoute markersCount={20} component={GMap} />
</Route>
</Router>,
mountNode
);

View File

@ -1,3 +0,0 @@
html, body
height: 100%
font-size: 14px

View File

@ -1,2 +0,0 @@
// use your own google maps API key with localhost permissions below
export const GOOGLE_API_KEY = '';

View File

@ -1,50 +0,0 @@
import path from 'path'; // eslint-disable-line no-var
import autoprefixer from 'autoprefixer'; // eslint-disable-line no-var
import webpack from 'webpack';
export default {
devtool: 'cheap-module-eval-source-map',
postcss: [autoprefixer({ browsers: ['last 2 versions'] })],
plugins: [
new webpack.DefinePlugin(
process.env.NODE_ENV
? {
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
}
: {}
),
],
module: {
loaders: [
{
test: /\.sass$/,
loaders: [
'style-loader',
'css-loader?modules&importLoaders=2&localIdentName=[name]__[local]',
'postcss-loader',
'sass-loader?precision=10&indentedSyntax=sass',
],
include: [
path.join(__dirname, '../../src'),
path.join(__dirname, '..'),
],
},
{
test: /\.css$/,
loaders: [
'style-loader',
'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]',
'postcss-loader',
],
include: [
path.join(__dirname, '..'),
path.join(__dirname, '../../node_modules'),
],
},
{
test: /\.svg$/,
loaders: ['url-loader?limit=7000'],
},
],
},
};

View File

@ -1,73 +0,0 @@
export const susolvkaCoords = { lat: 60.814305, lng: 47.051773 };
export const londonCoords = { lat: 51.508411, lng: -0.125364 };
export const generateMarkers = count =>
[...Array(count)].fill(0).map((__, index) => ({
// fill(0) for loose mode
id: index,
lat: susolvkaCoords.lat +
0.01 *
index *
Math.sin(30 * Math.PI * index / 180) *
Math.cos(50 * Math.PI * index / 180) +
Math.sin(5 * index / 180),
lng: susolvkaCoords.lng +
0.01 *
index *
Math.cos(70 + 23 * Math.PI * index / 180) *
Math.cos(50 * Math.PI * index / 180) +
Math.sin(5 * index / 180),
}));
export const heatmapData = {
positions: [
{
lat: 60.714305,
lng: 47.051773,
},
{
lat: 60.734305,
lng: 47.061773,
},
{
lat: 60.754305,
lng: 47.081773,
},
{
lat: 60.774305,
lng: 47.101773,
},
{
lat: 60.804305,
lng: 47.111773,
},
],
options: {
radius: 20,
opacity: 0.7,
},
};
function getRandomNumberBetween(min, max) {
return Math.random() * (max - min) + min;
}
export const generateHeatmapData = (lat, lng) => {
const newFakeReadings = x => {
const newReadings = [];
for (let i = 0; i <= x; i++) {
newReadings.push({
weight: getRandomNumberBetween(0.1, 4),
lat: getRandomNumberBetween(lat - 1, lat + 1),
lng: getRandomNumberBetween(lng - 1, lng + 1),
});
}
return newReadings;
};
return {
positions: newFakeReadings(10),
options: heatmapData.options,
};
};

View File

@ -1,78 +0,0 @@
import React from 'react';
import { Motion, spring } from 'react-motion';
import { compose, defaultProps, withPropsOnChange, pure } from 'recompose';
import clusterMarkerStyles from './ClusterMarker.sass';
export const clusterMarker = (
{
styles,
text,
hovered,
$hover,
defaultMotionStyle,
motionStyle,
}
) => (
<Motion defaultStyle={defaultMotionStyle} style={motionStyle}>
{({ scale }) => (
<div
className={styles.marker}
style={{
transform: `translate3D(0,0,0) scale(${scale}, ${scale})`,
zIndex: hovered || $hover ? 1 : 0,
}}
>
<div className={styles.text}>
{text}
</div>
</div>
)}
</Motion>
);
export const clusterMarkerHOC = compose(
defaultProps({
text: '0',
styles: clusterMarkerStyles,
initialScale: 0.6,
defaultScale: 1,
hoveredScale: 1.15,
hovered: false,
stiffness: 320,
damping: 7,
precision: 0.001,
}),
// pure optimization can cause some effects you don't want,
// don't use it in development for markers
pure,
withPropsOnChange(['initialScale'], ({
initialScale,
defaultScale,
$prerender,
}) => ({
initialScale,
defaultMotionStyle: { scale: $prerender ? defaultScale : initialScale },
})),
withPropsOnChange(['hovered', '$hover'], ({
hovered,
$hover,
hoveredScale,
defaultScale,
stiffness,
damping,
precision,
}) => ({
$hover,
hovered,
motionStyle: {
scale: spring(hovered || $hover ? hoveredScale : defaultScale, {
stiffness,
damping,
precision,
}),
},
}))
);
export default clusterMarkerHOC(clusterMarker);

View File

@ -1,28 +0,0 @@
@function stripUnits($number)
@return $number / ($number * 0 + 1)
$marker-width: 40px !default
$marker-height: 40px !default
$marker-border-width: 5px !default
$marker-font-size: 14px !default
.marker
position: absolute
cursor: pointer
width: $marker-width
height: $marker-height
left: -$marker-width / 2
top: -$marker-height / 2
border: $marker-border-width solid #004336
border-radius: 50%
background-color: white
text-align: center
color: #333
font-size: $marker-font-size
font-weight: bold
display: flex
align-items: center
justify-content: center

View File

@ -1,23 +0,0 @@
import React from 'react';
import { compose } from 'recompose';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/scan';
import 'rxjs/add/operator/distinctUntilChanged';
import SimpleMarker from './SimpleMarker';
import stream2Props from '../utils/stream2Props';
export const reactiveMarker = props => <SimpleMarker {...props} />;
export const reactiveMarkerHOC = compose(
stream2Props(({ id, hoveredMarkerId$ }) =>
hoveredMarkerId$
.map(hoveredMarkerId => hoveredMarkerId === id)
.distinctUntilChanged()
.map(v => ({ hovered: v })))
);
export default reactiveMarkerHOC(reactiveMarker);

View File

@ -1,47 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Motion } from 'react-motion';
import { compose, defaultProps, getContext } from 'recompose';
import { clusterMarkerHOC } from './ClusterMarker';
import simpleMarkerStyles from './SimpleMarker.sass';
export const simpleMarker = (
{
styles,
hovered,
$hover,
defaultMotionStyle,
motionStyle,
// hello,
} // console.log('hello', hello),
) => (
<Motion defaultStyle={defaultMotionStyle} style={motionStyle}>
{({ scale }) => (
<div
className={styles.marker}
style={{
transform: `translate3D(0,0,0) scale(${scale}, ${scale})`,
zIndex: hovered || $hover ? 1 : 0,
}}
/>
)}
</Motion>
);
export const simpleMarkerHOC = compose(
defaultProps({
styles: simpleMarkerStyles,
initialScale: 0.6,
defaultScale: 0.6,
hoveredScale: 0.7,
}),
getContext({
hello: PropTypes.string,
}),
// resuse HOC
clusterMarkerHOC
);
export default simpleMarkerHOC(simpleMarker);

View File

@ -1,18 +0,0 @@
$markerWidth: 49px
$markerHeight: 64px
$originX: $markerWidth * .5
$originY: $markerHeight
.marker
background-image: url('./mapIcon.svg')
position: absolute
cursor: pointer
width: $markerWidth
height: $markerHeight
top: -$originY
left: -$originX
transform-origin: $originX $originY
margin: 0
padding: 0

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="49px" height="64px" viewBox="0 0 49 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: Sketch 3.0.4 (8054) - http://www.bohemiancoding.com/sketch -->
<title>icons 2</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g id="map58-(1)" sketch:type="MSLayerGroup" transform="translate(1.000000, 1.000000)">
<path d="M-0.0450631868,23.2602268 C-0.0450631868,28.4922598 1.69858516,33.3156041 4.64331593,37.2038351 L23.4492555,62.0272289 L42.2550659,37.2038351 C45.2000549,33.3156041 46.9434451,28.4923876 46.9434451,23.2602268 C46.9434451,10.4151052 36.4235797,0 23.4491264,0 C10.4748022,0 -0.0450631868,10.4151052 -0.0450631868,23.2602268 Z" id="Path" stroke="#FFFFFF" stroke-width="2" fill="#4990E2" sketch:type="MSShapeGroup"></path>
<path d="M23.4491264,38.7668742 C14.7994478,38.7668742 7.78650549,31.8240247 7.78650549,23.2602268 C7.78650549,14.6966845 14.7994478,7.75332371 23.4491264,7.75332371 C32.0990632,7.75332371 39.1117473,14.6966845 39.1117473,23.2602268 C39.1117473,31.8241526 32.0991923,38.7668742 23.4491264,38.7668742 L23.4491264,38.7668742 Z" id="Shape" fill="#FFFFFF" sketch:type="MSShapeGroup"></path>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1,17 +0,0 @@
/* eslint-disable max-len */
// `npm bin`/mocha --compilers js:babel-register --require babel-polyfill --reporter min --watch './develop/**/*.spec.js'
/* eslint-enable max-len */
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import 'rxjs/add/operator/distinctUntilChanged';
describe('Playground', () => {
it('Play', async () => {
const comparator = (a, b) => a === b;
const props$ = new BehaviorSubject(1).distinctUntilChanged(comparator);
props$.subscribe(v => console.log(v)); // eslint-disable-line
props$.next(1);
props$.next(2);
props$.next(1);
});
});

View File

@ -1,26 +0,0 @@
import React from 'react';
const createEagerElementUtil = (
hasKey,
isReferentiallyTransparent,
type,
props,
children
) => {
if (!hasKey && isReferentiallyTransparent) {
if (children) {
return type({ ...props, children });
}
return type(props);
}
const Component = type;
if (children) {
return <Component {...props}>{children}</Component>;
}
return <Component {...props} />;
};
export default createEagerElementUtil;

View File

@ -1,13 +0,0 @@
import createEagerElementUtil from './createEagerElementUtil';
import isReferentiallyTransparentFunctionComponent
from './isReferentiallyTransparentFunctionComponent';
const createFactory = type => {
const isReferentiallyTransparent = isReferentiallyTransparentFunctionComponent(
type
);
return (p, c) =>
createEagerElementUtil(false, isReferentiallyTransparent, type, p, c);
};
export default createFactory;

View File

@ -1,29 +0,0 @@
import { wrapDisplayName } from 'recompose';
const createHelper = (
func,
helperName,
setDisplayName = true,
noArgs = false
) => {
if (process.env.NODE_ENV !== 'production' && setDisplayName) {
if (noArgs) {
return BaseComponent => {
const Component = func(BaseComponent);
Component.displayName = wrapDisplayName(BaseComponent, helperName);
return Component;
};
}
return (...args) =>
BaseComponent => {
const Component = func(...args)(BaseComponent);
Component.displayName = wrapDisplayName(BaseComponent, helperName);
return Component;
};
}
return func;
};
export default createHelper;

View File

@ -1,17 +0,0 @@
const isClassComponent = Component =>
Boolean(
Component &&
Component.prototype &&
typeof Component.prototype.isReactComponent === 'object'
);
const isReferentiallyTransparentFunctionComponent = Component =>
Boolean(
typeof Component === 'function' &&
!isClassComponent(Component) &&
!Component.defaultProps &&
!Component.contextTypes &&
!Component.propTypes
);
export default isReferentiallyTransparentFunctionComponent;

View File

@ -1,13 +0,0 @@
// https://github.com/acdlite/recompose/blob/master/src/packages/recompose/utils/omit.js
const omit = (obj, keys) => {
const { ...rest } = obj;
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (key in rest) {
delete rest[key];
}
}
return rest;
};
export default omit;

View File

@ -1,32 +0,0 @@
import { Component } from 'react';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import 'rxjs/add/operator/distinctUntilChanged';
import omit from './omit';
import createHelper from './createHelper';
import createEagerFactory from './createEagerFactory';
const prop2Stream = (propName, comparator = (a, b) => a === b) =>
BaseComponent => {
const factory = createEagerFactory(BaseComponent);
return class extends Component {
props$ = new BehaviorSubject(this.props[propName]).distinctUntilChanged(
comparator
);
// eslint-disable-next-line camelcase
UNSAFE_componentWillReceiveProps(nextProps) {
this.props$.next(nextProps[propName]);
}
render() {
return factory({
...omit(this.props, [propName]),
[`${propName}$`]: this.props$,
});
}
};
};
export default createHelper(prop2Stream, 'prop2Stream');

View File

@ -1,10 +0,0 @@
const ptInSect = (x, a, b) => (x - a) * (x - b) <= 0;
export default ({ nw, se }, pt) => {
const lngs = nw.lng < se.lng
? [[nw.lng, se.lng]]
: [[nw.lng, 180], [-180, se.lng]];
return ptInSect(pt.lat, se.lat, nw.lat) &&
lngs.some(([lngFrom, lngTo]) => ptInSect(pt.lng, lngFrom, lngTo));
};

View File

@ -1,31 +0,0 @@
import { Component } from 'react';
import createHelper from './createHelper';
import createEagerFactory from './createEagerFactory';
// if stream prop will change this will fail,
// this is expected behavior
const stream2Props = props2Stream =>
BaseComponent => {
const factory = createEagerFactory(BaseComponent);
return class extends Component {
state = {};
UNSAFE_componentWillMount() {
this.subscription = props2Stream(this.props).subscribe(value =>
this.setState({ value }));
}
componentWillUnmount() {
this.subscription.unsubscribe();
}
render() {
return factory({
...this.props,
...this.state.value,
});
}
};
};
export default createHelper(stream2Props, 'stream2Props');

View File

@ -1,48 +0,0 @@
import { createElement, Component } from 'react';
const safeTimerFactory = (setFn, clearFn, propName, hocName) =>
Target => {
class SafeTimer extends Component {
constructor(props, context) {
super(props, context);
this.unsubscribers = [];
this[propName] = this[propName].bind(this);
}
componentWillUnmount() {
this.unsubscribers.forEach(unsubscribe => unsubscribe());
this.unsubscribers = [];
}
[propName](...args) {
const id = setFn(...args);
const unsubscriber = () => clearFn(id);
this.unsubscribers.push(unsubscriber);
return unsubscriber;
}
render() {
return createElement(Target, {
...this.props,
[propName]: this[propName],
});
}
}
if (process.env.NODE_ENV !== 'production') {
SafeTimer.displayName = `${hocName}`;
}
return SafeTimer;
};
export default safeTimerFactory(
global.setInterval,
global.clearInterval,
'setSafeInterval',
'withSafeInterval'
);

View File

@ -1,45 +0,0 @@
import { Component } from 'react';
import createHelper from './createHelper';
import createEagerFactory from './createEagerFactory';
const withStateSelector = (stateName, stateUpdaterName, selectorFactory) =>
BaseComponent => {
const factory = createEagerFactory(BaseComponent);
return class extends Component {
selector = selectorFactory();
state = {
stateValue: this.selector(this.props),
};
updateStateValue = (updateFn, callback) =>
this.setState(
({ stateValue }) => ({
stateValue: typeof updateFn === 'function'
? updateFn(stateValue)
: updateFn,
}),
callback
);
// eslint-disable-next-line camelcase
UNSAFE_componentWillReceiveProps(nextProps) {
// reselect memoize result
const nextStateValue = this.selector(nextProps);
if (nextStateValue !== this.state.stateValue) {
this.setState({
stateValue: nextStateValue,
});
}
}
render() {
return factory({
...this.props,
[stateName]: this.state.stateValue,
[stateUpdaterName]: this.updateStateValue,
});
}
};
};
export default createHelper(withStateSelector, 'withStateSelector');

5
example/README.md Normal file
View File

@ -0,0 +1,5 @@
This example was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
It is linked to the google-map-react package in the parent directory for development purposes.
You can run `yarn install` and then `yarn start` to test your package.

31
example/package.json Normal file
View File

@ -0,0 +1,31 @@
{
"name": "google-map-react-example",
"homepage": ".",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ../node_modules/react-scripts/bin/react-scripts.js start",
"build": "node ../node_modules/react-scripts/bin/react-scripts.js build",
"test": "node ../node_modules/react-scripts/bin/react-scripts.js test",
"eject": "node ../node_modules/react-scripts/bin/react-scripts.js eject"
},
"dependencies": {
"google-map-react": "link:..",
"react": "link:../node_modules/react",
"react-dom": "link:../node_modules/react-dom",
"react-scripts": "link:../node_modules/react-scripts",
"styled-components": "^5.1.1"
},
"devDependencies": {
"@babel/plugin-syntax-object-rest-spread": "^7.8.3"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}

BIN
example/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

48
example/public/index.html Normal file
View File

@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#000000" />
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>google-map-react</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

View File

@ -0,0 +1,15 @@
{
"short_name": "google-map-react",
"name": "google-map-react",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

886
example/public/places.json Normal file
View File

@ -0,0 +1,886 @@
{
"html_attributions": [],
"next_page_token": "CpQCDwEAAJwdqLU_q4CMC9j-DOFIiwmwpP8ICAF_lHHKpWrNLfYsLS0hubKFTbau5sp7Tc_vC2bftXqZSRfq4uMk34-nE1f3weBRv77ZvsUhQn1MvJCfW6pNFA6zrDP_lPLBQZVrGVePIGhW8g485WnZdAHUyKpr4jjJ6hiOh2R6MxVBTYHyRuy5mDa-NGx4zQXDsHGSejOPH6BmPUoyfb-2MmRo1kcYABkBZRAaXpDmTtoXLmX8GxELbMOOAjEJ8Vzxdk-4GQkzoDYB1YwHBR1JXzxlawX0DdUyaJKjLWGjvt8GOwF8d9KVTF0r528jDCheFjvRgxOGMeYq1IrUx9lBofHH4sAX2plD6BGumN40mdBkDKVMEhC8pyLxL6t51mflBEc_WkF6GhTsH0kHwldrZYDnyUZ78TU9zeiiqg",
"results": [{
"formatted_address": "3818 Sunset Blvd, Los Angeles, CA 90026, USA",
"geometry": {
"location": {
"lat": 34.091158,
"lng": -118.2795188
},
"viewport": {
"northeast": {
"lat": 34.09258172989272,
"lng": -118.2780556701073
},
"southwest": {
"lat": 34.08988207010728,
"lng": -118.2807553298927
}
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
"id": "1113d3dd7339f965caae39387dd808a9e877bc2e",
"name": "Flore Vegan",
"opening_hours": {
"open_now": false,
"weekday_text": []
},
"photos": [{
"height": 2336,
"html_attributions": [
"<a href=\"https://maps.google.com/maps/contrib/100540448898812651487/photos\">Roman Roze</a>"
],
"photo_reference": "CmRaAAAA69UVwaJnUQXQUSSX9IfB3b29opNIohkexsGAGoHTD5Lyg24lhpBtaiNlrgihstR-k7Su9Vgbc8-eE5qHEdeLVY1QTfiuyS9TPp3e2GMM_grW2FtrgrFQGtMJSeJ336cPEhCVHYfFzoOgrrKdXlk34rJiGhSXSv_XG1q1CtOrWJjWQrxJmLvIPg",
"width": 3504
}],
"place_id": "ChIJj62e80jHwoARusJT4mjohWw",
"price_level": 2,
"rating": 4.6,
"reference": "CmRbAAAAM9_YQ6Dt9T69zucczidzOd6HU2vmzaXvTG-lJ89KyBlJVBJ0aEfar2Exre4iDWKHjExUshYSpAXEzA-YqotVnOt4XznKY_vkD520XK5nzFz5v5IefUe6FDBqZPzYlxRDEhAgQvwzNjwC49WWlyoMKza5GhS6r-VIl1lXdMl_JEW67yL7fPkZdg",
"types": [
"restaurant",
"food",
"point_of_interest",
"establishment"
]
},
{
"formatted_address": "1700 Sunset Blvd, Los Angeles, CA 90026, USA",
"geometry": {
"location": {
"lat": 34.0771192,
"lng": -118.2587199
},
"viewport": {
"northeast": {
"lat": 34.07856197989273,
"lng": -118.2573112201073
},
"southwest": {
"lat": 34.07586232010728,
"lng": -118.2600108798928
}
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
"id": "d8bc8d867ccf72cbc552e100238a0820628963ee",
"name": "Sage Plant Based Bistro and Brewery Echo Park",
"opening_hours": {
"open_now": false,
"weekday_text": []
},
"photos": [{
"height": 3024,
"html_attributions": [
"<a href=\"https://maps.google.com/maps/contrib/105756638182226336287/photos\">Anurag Singhai</a>"
],
"photo_reference": "CmRaAAAAW6v0EyZa77vrG5Aq8zbnU1sR7pljqfoDpSxXhISFgWoYWLkFY5hh7YCFbzYLj1XzflTJOrCXJa-q5jPT4L0vMY8cjXrhCzB5y7Z--qJTWOO_NxaRUBbB5QhpxyUT-R6tEhBru_ZZ_xcCxFueYrRxI6pFGhRgiExnWWOhU2Ii_NnW6M8R4xs4IQ",
"width": 4032
}],
"place_id": "ChIJ6T9ggBvHwoARc3aegK3PBe0",
"price_level": 2,
"rating": 4.6,
"reference": "CmRbAAAAy5CT3sE8bRlADeIeMYtC7NRdE58vKOCjOvhZNUs0QwBD7kSS6WIfo3wmxvt4EGZm5TJ6WdqOCRSnnnFoXAVpz9F3EyZPosUDCN2LJIvtYxwS3BYkwh6uVQRSj-OPLak6EhBRf8xOpcuc5WWWBgGNbGGbGhSYiomy4iNV-g_KzzqNrpymm3MxQw",
"types": [
"restaurant",
"food",
"point_of_interest",
"establishment"
]
},
{
"formatted_address": "8284 Melrose Ave, Los Angeles, CA 90046, USA",
"geometry": {
"location": {
"lat": 34.083527,
"lng": -118.370157
},
"viewport": {
"northeast": {
"lat": 34.08495787989272,
"lng": -118.3688093201073
},
"southwest": {
"lat": 34.08225822010727,
"lng": -118.3715089798927
}
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
"id": "8529e7504ec45e2f380310882a3bd05fe050d701",
"name": "Crossroads Kitchen",
"opening_hours": {
"open_now": false,
"weekday_text": []
},
"photos": [{
"height": 446,
"html_attributions": [
"<a href=\"https://maps.google.com/maps/contrib/105056983516844525128/photos\">Crossroads Vegan Restaurant</a>"
],
"photo_reference": "CmRaAAAAeNvmtrTLNo95XHaHxur4Lc3CSCbtl8zUk1cbjlFdDSpMupFJ0LXRP7iaqHQeP0N8srb3P9NzwI0jrASIvj7dn-8b0KkmjvgeAmETi9sDAEwQVOui2yIJd5yzzJN3LD_bEhCCaBWZ_g1fJHyzVzz9c18eGhTSCsqJIwmOF_xRWfhPBoFUO86y1A",
"width": 720
}],
"place_id": "ChIJhZy9Gba-woARQLEEpEN46uw",
"price_level": 3,
"rating": 4.5,
"reference": "CmRbAAAAU-AZsWP9B3I6f7bIB2lho68meak73rt_fxdoJug-tXoqRil2guPjWxQmycp2dPFKXZNG5azOwT-rEy9vJtXQf44uVj45j_ohS6ietOXClyyAppqZt_ngGVISdtcNvrFaEhDYSDnUEsrd7GxTol1wHfJ4GhRsQAnur588F9jnXy0S4IYaiaEAQA",
"types": [
"bar",
"restaurant",
"food",
"point_of_interest",
"establishment"
]
},
{
"formatted_address": "4319 Sunset Blvd, Los Angeles, CA 90029, USA",
"geometry": {
"location": {
"lat": 34.0951843,
"lng": -118.283107
},
"viewport": {
"northeast": {
"lat": 34.09646662989272,
"lng": -118.2818451701072
},
"southwest": {
"lat": 34.09376697010727,
"lng": -118.2845448298927
}
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
"id": "287049135d7d1ab1bfd2de8d7654afcc97cfee77",
"name": "My Vegan Gold",
"opening_hours": {
"open_now": false,
"weekday_text": []
},
"photos": [{
"height": 667,
"html_attributions": [
"<a href=\"https://maps.google.com/maps/contrib/117593961088704552381/photos\">Sayamon Riddang</a>"
],
"photo_reference": "CmRaAAAABpfiLbCymEfhpZDl0vsV5ZjC7UDVT-GQAryHJkQ1rZHakstWk4Cugb0HrxU3dsdqbjxvKU9gxVHsZjxToVuMwRw_dHWdwIPFNl-VGYtipa8Z6Uzf6RzmS2tDje45jzTREhD1E_zkXr3Z6v8wWLzDDxwlGhS8VB8VsaT96ycvEwdkjhzmNKSfDg",
"width": 1000
}],
"place_id": "ChIJh6ttFUzHwoAR9p8Wi_B8AbI",
"price_level": 2,
"rating": 4.6,
"reference": "CmRbAAAAUR_MLHOu0gPb-77cvoygt4blzXX2PtoI7PRfh8mqqOgx8MPgpclf8PcfCXkEvAfDxu4wafz5l1jBDTPTntDz3Epf0dJxilKvshezCkwMXdlIoZjBZwW8n6BMU5WKGCjXEhBd8OVC5aJ7Qc_S3skTq1KnGhQNTUFaUZAnY5r37iUSLCT59zfhrw",
"types": [
"restaurant",
"food",
"point_of_interest",
"establishment"
]
},
{
"formatted_address": "4442, 1769 Hillhurst Ave, Los Angeles, CA 90027, United States",
"geometry": {
"location": {
"lat": 34.1033401,
"lng": -118.2875469
},
"viewport": {
"northeast": {
"lat": 34.10468952989272,
"lng": -118.2861405701073
},
"southwest": {
"lat": 34.10198987010727,
"lng": -118.2888402298927
}
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
"id": "1401d50571af0bd7fc0267e6a3635bfb3d3dbda2",
"name": "Green Leaves Vegan",
"opening_hours": {
"open_now": false,
"weekday_text": []
},
"photos": [{
"height": 3168,
"html_attributions": [
"<a href=\"https://maps.google.com/maps/contrib/118194982700766373715/photos\">Carl Afable</a>"
],
"photo_reference": "CmRaAAAAz4NOaoeJczV-BFF7HYE-unBeYl8bud0vmkVtYxCv-lKLYo1YnAgcf-Cisr4BQypE9WbaUwm157PYyxLkWkQ1CwVcP7bXtV6QAdNegOXB8QlWAEPR95Q6lm4NQ71B3Vm2EhD2nTXqDhitEm0BrjyRh3JxGhRYCJXLJCF4AWJiBXk5fcdcjylX_g",
"width": 4752
}],
"place_id": "ChIJ2RKN_rHAwoARIGWYxAgzzyQ",
"price_level": 1,
"rating": 4.5,
"reference": "CmRbAAAAtM6n50gnrmJYk2qR_1uslm5ILIFgla8_RVtkm5pLzg9pMV-UcNQ3LzYSc6iM2vm6rbIzSvmjYMvWZ0Qy0AnVyIhb0kYHi1HpNFbYF3aD5cxH2hyHuB9qbn_OY_7vTpWEEhDTEJ002c2Pt-QTBNerb9iOGhRnv28Ti7KlI3gYVtBKG5XKOju9Zg",
"types": [
"restaurant",
"food",
"point_of_interest",
"establishment"
]
},
{
"formatted_address": "707 E 10th St, Los Angeles, CA 90021, USA",
"geometry": {
"location": {
"lat": 34.035798,
"lng": -118.251288
},
"viewport": {
"northeast": {
"lat": 34.03709272989273,
"lng": -118.2499997201073
},
"southwest": {
"lat": 34.03439307010728,
"lng": -118.2526993798928
}
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
"id": "01c75569ea2c5ee7ae29e91990ff4d872bc1ca5b",
"name": "The Vegan Joint",
"opening_hours": {
"open_now": false,
"weekday_text": []
},
"photos": [{
"height": 680,
"html_attributions": [
"<a href=\"https://maps.google.com/maps/contrib/106918371964521288041/photos\">The Vegan Joint (Downtown)</a>"
],
"photo_reference": "CmRaAAAAGdHRzCGTwQ0_tc-fWo9wfezr51jbxPQ7IH1Gsc9OCRxoIijxg110YoNAaCTQSefXtCVdiH2xYcifKokVtnwxp01tE6J888NxT_MA1zpzxiM5ZM3MgJ9OxeJERDyE8jT0EhBSS5kv0YX7tmAxiUFHPOEXGhT2ZyiB6uRAfzpKchXvfOxyvA7S2Q",
"width": 1906
}],
"place_id": "ChIJL8eKojLGwoAR-inFlT4uF8Q",
"rating": 4.5,
"reference": "CmRbAAAAPj4xP0HMJoDhx-P7C2JZTRSxu44XKoALQqupq2WaiqDjqiqi7g1ua7imgENWEH_fXu8XS5XxAmpkYV4XcDWfbLeZFuOKbgNGIvxQhXBhL-215hJQvoHFodeO9_lCGliNEhC6QZwCTKktRp33eibHgQJyGhRZf9Z_PHxTO-dxhjCFmIY8KQUYlQ",
"types": [
"restaurant",
"food",
"point_of_interest",
"establishment"
]
},
{
"formatted_address": "2135 Sunset Blvd, Los Angeles, CA 90026, USA",
"geometry": {
"location": {
"lat": 34.0776068,
"lng": -118.2646526
},
"viewport": {
"northeast": {
"lat": 34.07886997989272,
"lng": -118.2632818701073
},
"southwest": {
"lat": 34.07617032010727,
"lng": -118.2659815298927
}
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
"id": "946525c8b38a73842b88f67b1786746dce63fc27",
"name": "Elf Cafe",
"opening_hours": {
"open_now": false,
"weekday_text": []
},
"photos": [{
"height": 1365,
"html_attributions": [
"<a href=\"https://maps.google.com/maps/contrib/104111246635874032234/photos\">ZAGAT</a>"
],
"photo_reference": "CmRaAAAAXeyh-4q2GinyRWob20cRuMTPB9uwOMr4jceLbiGh1sUI3I3dU2tqPBg0fIALpeBD4h5cfCTyULBTj6vSqNEcDUcPGFnjOaDO3BYQALO94_WCtbjkY83fQkdM_BPFlm2oEhDshmrB_41OPbyEVGJ4fPUVGhSYvebsAZyOFjSgUoJm_QQAhDFhzA",
"width": 2048
}],
"place_id": "ChIJEy4mphDHwoARejZ9zUN_d4U",
"price_level": 2,
"rating": 4.7,
"reference": "CmRbAAAAwxF-jK7VtlDfSJigYrgWtGrgNXRvw-4pU62YULylvTajhxW9lwzvmYKmKKx4ZO0SBMR1TOqVE-rPlhlh7aZI5NfcL8RItn_IX1BxS7X9X2ode26MDbVwICFFC_qziC-MEhA4i-3OikQ9eHbTeu-L27G1GhRLgk421tk5hnWDbhrEbtK-S3Ap4A",
"types": [
"restaurant",
"food",
"point_of_interest",
"establishment"
]
},
{
"formatted_address": "4114 Santa Monica Blvd, Los Angeles, CA 90029, USA",
"geometry": {
"location": {
"lat": 34.0919263,
"lng": -118.2820544
},
"viewport": {
"northeast": {
"lat": 34.09333132989273,
"lng": -118.2807559701073
},
"southwest": {
"lat": 34.09063167010728,
"lng": -118.2834556298927
}
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
"id": "d358acb483e9bacaf445de016f78473908253696",
"name": "Bulan Thai Vegetarian Kitchen",
"opening_hours": {
"open_now": false,
"weekday_text": []
},
"photos": [{
"height": 3024,
"html_attributions": [
"<a href=\"https://maps.google.com/maps/contrib/107987703028923546874/photos\">jeromy robert</a>"
],
"photo_reference": "CmRaAAAAbjH5vQk8Ese5ppONFAIwbunnNnhcEEkKtRrn5yttTYvxHxjbgbGDXIv7cNSfQMU7wICcox5NT__xnaKwJi-e3zr5230Z4t8TcReOcVoV8Ewjfn5g7AL4AK0gksctR-KqEhD6iw1PbCByU1zD7iD3lxPXGhT5AoSLQ7BHIzymU94-0xSvRMo9yA",
"width": 4032
}],
"place_id": "ChIJAaqI1E7HwoARRCxBLPw048Y",
"price_level": 2,
"rating": 4.6,
"reference": "CmRbAAAAbLDZZu-oOjRmRi7hsQpZELuusRQZEk6lE1YJL5r7pnP3RtWaSLlA_p9uYp0QIalfzXhS0605iRnNsEr_WDhos1HxubW4h0sPKz96GPUKeULOux319b2pYHqQCYwYUmOMEhCEXh-YmfKFafd1EtkAYkB2GhSbxBpYLCWz45RTP0YaWm6eeJecwQ",
"types": [
"restaurant",
"food",
"point_of_interest",
"establishment"
]
},
{
"formatted_address": "1047 S Fairfax Ave, Los Angeles, CA 90019, USA",
"geometry": {
"location": {
"lat": 34.0568525,
"lng": -118.3646369
},
"viewport": {
"northeast": {
"lat": 34.05817752989272,
"lng": -118.3632100701073
},
"southwest": {
"lat": 34.05547787010728,
"lng": -118.3659097298927
}
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
"id": "6f73d8dfd2f3dc8b08d127ea3d0691b33030973b",
"name": "Rahel Ethiopian Vegan Cuisine",
"opening_hours": {
"open_now": false,
"weekday_text": []
},
"photos": [{
"height": 2988,
"html_attributions": [
"<a href=\"https://maps.google.com/maps/contrib/100435880280656818067/photos\">Edna Banayat</a>"
],
"photo_reference": "CmRaAAAAsltyy0_FJzHaxUaRfy3ks0qgLs8N0x2Y7dZqKDw21NUi7D9STyFscfcUKiL9z9E2LXC3jrIqq6vikR6j0hb8_L7kCDr1WjgVUamyzOw3GdXSmaDVEQrOWDkBSGWGCvSAEhA9zXJDgrZrtdC90Jah1VWnGhRr8wcVDTal8O1R7S6WOQ0482phCw",
"width": 5312
}],
"place_id": "ChIJqeeNwhW5woARdmN2K2Q107A",
"price_level": 2,
"rating": 4.6,
"reference": "CmRbAAAAoNTyxGrtvDexCku_OpmL47Akpq5T587ncWV1Rp8xWIc7YQlqk75_0h2_nB0bduMRadfyHsW6i6SXFUNicU0BWvaGGy3Mtta6Omd-9lePm-ZN2EQBbuT05kFBNHkWbs9zEhCfXjvnfKzALRwRyDKEeQr-GhRIXuOSJHoM7g6L-V83WDXl3mxUgQ",
"types": [
"restaurant",
"food",
"point_of_interest",
"establishment"
]
},
{
"formatted_address": "4664, 10438 National Blvd, Los Angeles, CA 90034, United States",
"geometry": {
"location": {
"lat": 34.0285781,
"lng": -118.4115541
},
"viewport": {
"northeast": {
"lat": 34.02999047989272,
"lng": -118.4102540701073
},
"southwest": {
"lat": 34.02729082010728,
"lng": -118.4129537298927
}
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
"id": "92cc1f89e314ba9e518f1d7aa345a57441affe39",
"name": "The Vegan Joint (West LA)",
"opening_hours": {
"open_now": false,
"weekday_text": []
},
"photos": [{
"height": 421,
"html_attributions": [
"<a href=\"https://maps.google.com/maps/contrib/106873412906570774753/photos\">The Vegan Joint (West LA)</a>"
],
"photo_reference": "CmRaAAAAN3v7CUQbGdsnPNSZ26qvp11iqa78bJjwe_P9pr0rA0meuKU916gHo0OktysWHWB-ZhFRtiVGYQeTm6mQUVChAxglotv9Tgv4VTRAIFdy_-WeLSvkQz-KhOf-mtvmfObfEhCOC0m29jY1Dvm0Xz_l2s1sGhT-9vWMge8CoQrb5wpFAtLrUB8phg",
"width": 750
}],
"place_id": "ChIJ74X8ncu7woARzxiCmkqeeXc",
"price_level": 2,
"rating": 4.4,
"reference": "CmRbAAAAh4e7HwMd83IQ1TW182b2n_qdhUDULWHH33HQjJOT_DGzaFV2HyLLEnN4z07DGn_-S0GsA2mwy3hvI-GFIKW1hM-VO8MOaJkiFMfWW347qlVy6iYmltY0DzU_-eA02rHPEhC0AGDxyCgGEH-2PrMsG0_ZGhTFDKatE5VBRZkTdWAaYfzSrs3QDg",
"types": [
"restaurant",
"food",
"point_of_interest",
"establishment"
]
},
{
"formatted_address": "3655 S Grand Ave, Los Angeles, CA 90007, USA",
"geometry": {
"location": {
"lat": 34.017339,
"lng": -118.278469
},
"viewport": {
"northeast": {
"lat": 34.01859817989273,
"lng": -118.2769120701073
},
"southwest": {
"lat": 34.01589852010728,
"lng": -118.2796117298927
}
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
"id": "da772e983e3e6dbc0cd042042311ab79839d1f28",
"name": "Azla Ethiopian Vegan Cuisine",
"opening_hours": {
"open_now": false,
"weekday_text": []
},
"photos": [{
"height": 3024,
"html_attributions": [
"<a href=\"https://maps.google.com/maps/contrib/110952242659405432450/photos\">Benjamin Rubin</a>"
],
"photo_reference": "CmRaAAAARwfVFSLVUmk_dVkBaY43M8sPng1XdXX7Is882vzG7KcFewp2dMMlYqwKQSGyfAOSWZ3vJrgBUEypUwJESuqoxRneTLjp9W-XcQ4pQByDVabpYbT5r5y006esU3aTeHvIEhDXtTfa4Kh1z_-u4D_BFJT2GhSQDwL9zFfvKbwRuGmmcui08SdiLg",
"width": 4032
}],
"place_id": "ChIJmb4BkgrIwoAR-PS33wrjk_M",
"rating": 4.8,
"reference": "CmRbAAAA5lMrGEVyK5VhjDMMJZ2tUR0Poo9zQt4wZZBcW46e3CfsYUN0INPZKeeKERZJwkhcogLK-FsKAZI92jncCYN4RZQYqtyiJONx3I6VWIrTXGI9rOt01dSTtBCZEKtag0AcEhBlaMFWX2bnU8gMjCv3Fs1AGhSs8gJhrAa-jQNLRX7pxzIqBJ_HQg",
"types": [
"restaurant",
"food",
"point_of_interest",
"establishment"
]
},
{
"formatted_address": "8101½ Beverly Blvd, Los Angeles, CA 90048, USA",
"geometry": {
"location": {
"lat": 34.0764288,
"lng": -118.3661624
},
"viewport": {
"northeast": {
"lat": 34.07760962989272,
"lng": -118.3648135201073
},
"southwest": {
"lat": 34.07490997010728,
"lng": -118.3675131798927
}
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
"id": "1924e9b97b3101ea1d18ac33191e8c2d8fffcbc2",
"name": "Araya's Vegetarian Place",
"opening_hours": {
"open_now": false,
"weekday_text": []
},
"photos": [{
"height": 3088,
"html_attributions": [
"<a href=\"https://maps.google.com/maps/contrib/103485549458724391583/photos\">Simon Catford</a>"
],
"photo_reference": "CmRaAAAAyNe-nZSiUqUrfU3RKs7KqCG2-yoYpRixjGySI1LQgkDKkmRycyaz8RNyaVTYD8bqIf8FpmH7MOvC84sF6gVTsOBKI3IUGeyx54Uxntf8IkZOx4MjDr-Gdh_e40hXYKePEhApDNZUIBhEwnQHrWJqcBA1GhSN3DbanyozQB0pl8wVQucBZbsg1g",
"width": 4160
}],
"place_id": "ChIJV-aT-jS5woAROYkaRCGf2s8",
"price_level": 2,
"rating": 4.6,
"reference": "CmRbAAAAeSSwdHRB2eK9ro7pBWjJZeiDbH12E8AhhMyUsosVZeCMtzmNgPNfRgbS96NRPgdbWrudWjLJrKcomMqCyH8PWpA8rhSKHf7Kj7u7rgBv0r3b4kowdzJ4VUe8WyNoGQ_VEhBVcix6m4C5QlEf-G3y7AveGhTqHZ55gdeHglCukJFsCIpkzMR9Cw",
"types": [
"restaurant",
"food",
"point_of_interest",
"establishment"
]
},
{
"formatted_address": "4507 S Centinela Ave, Los Angeles, CA 90066, USA",
"geometry": {
"location": {
"lat": 33.9925942,
"lng": -118.4232475
},
"viewport": {
"northeast": {
"lat": 33.99401072989273,
"lng": -118.4218230701073
},
"southwest": {
"lat": 33.99131107010729,
"lng": -118.4245227298927
}
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
"id": "8f5ac5552a0e53f7ae627916c3d1d48b45030f54",
"name": "LA Vegan",
"opening_hours": {
"open_now": false,
"weekday_text": []
},
"photos": [{
"height": 3644,
"html_attributions": [
"<a href=\"https://maps.google.com/maps/contrib/108088361466169015751/photos\">Eric Estenzo</a>"
],
"photo_reference": "CmRaAAAAdcatw_mFed_48beFNIsyCrjLRenujvkj0YkhN5PX8oJQAr013xQEGat49VQ1GmrvEMRTV8CIeWtBOYSwzIWUrD7kyY1c3sgmx-0y_ecKUIuSlxhlbDtjQeCa3U8SV5yGEhAGwkRlZ2kuFsZ5uBKLh79CGhRKQDVIo1Z_ijdaHPLZqjuRU8mcew",
"width": 7012
}],
"place_id": "ChIJZUzGG266woARXsRdJc2silw",
"price_level": 2,
"rating": 4.6,
"reference": "CmRbAAAAXBx1MbFAy5X5HJqg1yMWtv10pIpp5Qeq2lCD7y7dhJM5B8iHXgysRGXgjsD5eTeKnxuaM26srTPC3Ib7g1TfBAnaIcVVw7K0-JEiuRzs-dBnuj_1ey5A0r3zvXOQpcN0EhB18uIS3_Rj_T61b2Sfo0ecGhSmROJ3Of8T44ckuMzOqKUJsZng8Q",
"types": [
"restaurant",
"food",
"point_of_interest",
"establishment"
]
},
{
"formatted_address": "8393 Beverly Blvd, Los Angeles, CA 90048, USA",
"geometry": {
"location": {
"lat": 34.0764345,
"lng": -118.3730332
},
"viewport": {
"northeast": {
"lat": 34.07760002989272,
"lng": -118.3716821701073
},
"southwest": {
"lat": 34.07490037010728,
"lng": -118.3743818298927
}
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
"id": "1d6756ade69dac8cf5ce42aeeeb793535b4504e8",
"name": "Vegan Glory",
"opening_hours": {
"open_now": false,
"weekday_text": []
},
"photos": [{
"height": 570,
"html_attributions": [
"<a href=\"https://maps.google.com/maps/contrib/104981305421921650092/photos\">Thom Uber</a>"
],
"photo_reference": "CmRaAAAA1YSeQpoJrDP6swpc1vw2b364pPMsjw2s2DVZYV4AWfENCwY02zHen8VpfI27Vnh8762bVFs4kda28R2sH354xKhju0vBIEM4yRlQr_G1-NvcywFtMQE7mBVtgegTo6ADEhBBLf_S0InHdmZ-SVC1KX8zGhQS9hpDvsrqVNDPNuWKLSPHy34XoA",
"width": 760
}],
"place_id": "ChIJyYMRL0u5woARId-DVapUFNk",
"rating": 4.4,
"reference": "CmRbAAAActbey6zjHroEmUk5ZVCyD6LR0-RHIuWbMAJW5v7PaA-V-pijZFT9MgDjuoCOYAy7asUfIXfLiPxli2mdJtUGTCsUW6qw91uHaZR6XQv62bUFwmM9qM6IB6gOcv4T5BKbEhD9Jo87T4pNpyFSgCZWcyMOGhT2Au7pVgKuofn6_B7s4vSFUNhXPA",
"types": [
"restaurant",
"food",
"point_of_interest",
"establishment"
]
},
{
"formatted_address": "1253 Vine St # 9, Los Angeles, CA 90038, USA",
"geometry": {
"location": {
"lat": 34.093981,
"lng": -118.327638
},
"viewport": {
"northeast": {
"lat": 34.09533472989273,
"lng": -118.3257989701073
},
"southwest": {
"lat": 34.09263507010728,
"lng": -118.3284986298927
}
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
"id": "a869aba570e8103a6ae09ae71335a38e99b11e01",
"name": "Doomie's Home Cookin'",
"opening_hours": {
"open_now": false,
"weekday_text": []
},
"photos": [{
"height": 2400,
"html_attributions": [
"<a href=\"https://maps.google.com/maps/contrib/114793967685216290949/photos\">Joe Gebis</a>"
],
"photo_reference": "CmRaAAAA20MBConiFPlO2-6BuoTBSGSAUBJaYoX0_m0xj_MNE2BPaqhDDSNtUXMTcmiKKZw0umwGkTaBt-Y3kNfKwKfRAdNnW9byHMuvElqBWXZTPSPuTIN_4xJKt1tKsosLuIfBEhB_ZCYjBhFWDue7-Z-UcfFjGhRyD45yAH2035Ifu15OklnylBIfhA",
"width": 3200
}],
"place_id": "ChIJ56wYlja_woAR7f2m0YbS8hk",
"price_level": 2,
"rating": 4.4,
"reference": "CmRbAAAAObVl81DX-3CYhyQv0itdfPojjR7bwG0eAv2JpZb9ZYoVBNg9F0znMXC72bBsDctx7ogiPKPvagqMNtEFkHYG7aLMk5VO-xtR8sfDAgeEiHHZV24oStUWs4aY-xqrU8W4EhAok2aSJPg0eRFD7nXeJpzzGhRPGtkJYZeCZHvy9UctDGItmFdbKg",
"types": [
"restaurant",
"food",
"point_of_interest",
"establishment"
]
},
{
"formatted_address": "710 W 1st St, Los Angeles, CA 90012, USA",
"geometry": {
"location": {
"lat": 34.056385,
"lng": -118.2508724
},
"viewport": {
"northeast": {
"lat": 34.05794337989273,
"lng": -118.2493326201073
},
"southwest": {
"lat": 34.05524372010728,
"lng": -118.2520322798927
}
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
"id": "53b4341b80e3596c05431abaa9b42b3fb0adf66d",
"name": "Âu Lạc LA",
"opening_hours": {
"open_now": false,
"weekday_text": []
},
"photos": [{
"height": 3036,
"html_attributions": [
"<a href=\"https://maps.google.com/maps/contrib/103902774314509444130/photos\">Patrick Donnelly</a>"
],
"photo_reference": "CmRaAAAAmofVPSjKMklO3J2mpcRl_kHAwl710EqByuQjaP8rjOtmj08yJKKr1xcJICtaZ-quKiiG2aYUhFru5JF1eYeDHx-pILIPOmKspUN07oGjhMwKnUa73wikXvM-FVJuJcRMEhDLVub-Rx6D4N3iiN0JAtHhGhQQ2Un8dIsylPeLN9IcRcerUif-1w",
"width": 4048
}],
"place_id": "ChIJe0sPnFLGwoARxFDUYFEjhuQ",
"price_level": 2,
"rating": 4.5,
"reference": "CmRbAAAAHb0M5CcLv12TZJiEq0QRs5nexZ-fBdKvzQmTE8w3j_-YHX15hSy7WYb3EuweeyxhuIBirsVr4uBMtSUP4KoClwz1l6uE0KgO5jHySb6a9Qi5_Hoju3iN55xLA38dCGPTEhBqVAmmstFHL-wzx6LKFLkWGhTdLVJpc_s2vuSY-OL3rJzZStmuFg",
"types": [
"bar",
"restaurant",
"food",
"point_of_interest",
"establishment"
]
},
{
"formatted_address": "2870 Rowena Ave, Los Angeles, CA 90039, USA",
"geometry": {
"location": {
"lat": 34.107701,
"lng": -118.2667943
},
"viewport": {
"northeast": {
"lat": 34.10916667989272,
"lng": -118.2653993701072
},
"southwest": {
"lat": 34.10646702010727,
"lng": -118.2680990298927
}
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
"id": "199ee04e9dbcb33b16d3aed792b6c0cc0e441d8d",
"name": "Little Pine",
"opening_hours": {
"open_now": false,
"weekday_text": []
},
"photos": [{
"height": 563,
"html_attributions": [
"<a href=\"https://maps.google.com/maps/contrib/113035519513505430750/photos\">Karen TenEyck</a>"
],
"photo_reference": "CmRaAAAAoYVVT_rhyzFXzPVGgWSkRNXU5Fqm7AC5BcX7obilkGQX_cqVfu74Jb4Dm77gWDtvF3uov5Oeu06JutufKGQwbWlBJmdH0O3xCDoNA-t5uPOHjvZu26A2WYA3JtaMR4TzEhAbM1Fsz-8UUaZOh37fbEs3GhRdnpshI2u0QbwbL0KbAa_IiNPPdg",
"width": 1000
}],
"place_id": "ChIJjc_P0c3AwoARBWVMPN_YVRU",
"price_level": 2,
"rating": 4.7,
"reference": "CmRbAAAAugyZhEPl2BK5sW7_AnhEZv-d-21pR6GzooTVD3RJFsDF9hecZMusog36IO34t7wuFczZWyD8kEiVKilj4Sp9buBdtO077raxcOkVC6--BXKuhGrh2Lkq5dhR2pLXNLXZEhBKcQzGE58Xr82Fz7Oy--MaGhTT7qBjkenQ3jVinJpgqASlYR5c0g",
"types": [
"restaurant",
"food",
"point_of_interest",
"establishment"
]
},
{
"formatted_address": "Little Tokyo Shopping Center, 333 South Alameda St #310, Los Angeles, CA 90013, USA",
"geometry": {
"location": {
"lat": 34.0450139,
"lng": -118.2388682
},
"viewport": {
"northeast": {
"lat": 34.04636372989273,
"lng": -118.2375183701073
},
"southwest": {
"lat": 34.04366407010728,
"lng": -118.2402180298928
}
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
"id": "bdc0c9353ceb91395214f3f84f0016059271acd7",
"name": "Shojin",
"opening_hours": {
"open_now": false,
"weekday_text": []
},
"photos": [{
"height": 2336,
"html_attributions": [
"<a href=\"https://maps.google.com/maps/contrib/100540448898812651487/photos\">Roman Roze</a>"
],
"photo_reference": "CmRaAAAALLVT3JKv4U1gRmbItfHhzOIdYRIibKmLMWThQa0gYU-umKbevliJUIogQL_Hm1_6oWGlutmR4kuzd6ikrpM4_eIeLSprpxAlHiEmi3FVNN5XvnBxfV8xWj6ggQLNM8DxEhDscXYyU5r1aKlYTCVLG61tGhQsd9XLF9OR7UL9XZo_eOm1Y98hCw",
"width": 3504
}],
"place_id": "ChIJyy_0pjnGwoARi47oqNbdedU",
"price_level": 3,
"rating": 4.6,
"reference": "CmRbAAAAORx-wW_TP5eWz0P93UBThqK-FdOLxos-ptjGRGlmiFaOf6ySc43rGF1ENCNilSFQL0Q-7RtFawCJMdvApBCTEyHZnRIIx4Jp4cRIcvSxRBJzB-S3aWfAglqv4L_sO7zsEhAjm-hxEpM5a-8fqVVcqJAbGhQDjg_IeMxUnePapODPTZPxOis_Lw",
"types": [
"restaurant",
"food",
"point_of_interest",
"establishment"
]
},
{
"formatted_address": "2520 Glendale Blvd, Los Angeles, CA 90039, USA",
"geometry": {
"location": {
"lat": 34.1031997,
"lng": -118.2586152
},
"viewport": {
"northeast": {
"lat": 34.10455672989272,
"lng": -118.2575728201073
},
"southwest": {
"lat": 34.10185707010727,
"lng": -118.2602724798928
}
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
"id": "52be338abc98361f3c8664a7d4e853a11332afec",
"name": "by CHLOE. Silver Lake",
"opening_hours": {
"open_now": false,
"weekday_text": []
},
"photos": [{
"height": 2988,
"html_attributions": [
"<a href=\"https://maps.google.com/maps/contrib/104636687373132435944/photos\">Jeannette G.</a>"
],
"photo_reference": "CmRaAAAA4Yb-teP-cLR0wHYMwtNvoo-wPlgVsj444v-VTgdwi63Gshd-PV5ZMhVPQSN2v5yPM-lxvZQW7Redm4wNKPLmGku5MNs0yhMuBysvXO6gpJ6kF4PGCepMIcZQ409LpBD_EhD8ZeAMspzsx_RnDHSQkuDlGhQ3-VOGj2K85VfvxsOCAyIS4HdVnw",
"width": 5312
}],
"place_id": "ChIJ4fYRfivHwoAR_2DlpZxO5_8",
"price_level": 2,
"rating": 4.5,
"reference": "CmRbAAAALsJqmhwH-aLZPy6SNAbLNzQRQ3qfT92yX3k6t13Ynq9K5hQatkTEBPhTr-pqcF3RznWRRYDNUoyPSoT8irbse6j2VZUHsgvo7wvPPVSTeYaeB3gZYXNaFnJmcqyUUU4CEhAhjByzVT-NNxUGH3pcyuS-GhTSUc8pvZUzjDie_3LXHrkObrtDzA",
"types": [
"restaurant",
"food",
"point_of_interest",
"establishment"
]
},
{
"formatted_address": "639 N Larchmont Blvd, Los Angeles, CA 90004, USA",
"geometry": {
"location": {
"lat": 34.0828183,
"lng": -118.3241586
},
"viewport": {
"northeast": {
"lat": 34.08422687989272,
"lng": -118.3225729201073
},
"southwest": {
"lat": 34.08152722010728,
"lng": -118.3252725798928
}
}
},
"icon": "https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png",
"id": "21f5bb524cea1c8698187b817298300df290ad1b",
"name": "Cafe Gratitude",
"opening_hours": {
"open_now": false,
"weekday_text": []
},
"photos": [{
"height": 1365,
"html_attributions": [
"<a href=\"https://maps.google.com/maps/contrib/115181038312887901832/photos\">Cafe Gratitude</a>"
],
"photo_reference": "CmRaAAAAX_eBNo4lUv8qGy5th3upd5Qp_Gx9NEQ55wfy0Yb7l1L_0M_gIwQqPb55RiJivmK_Wu3_CiyjBwEVGeeQCGRPHanVPYIG7_fQGV3_o2QgPSgQOAEfeKvuvdeUFGLX2TMzEhANtSJNyyzGbezwvsEmgvklGhSNKWwiEgOHaw28SqiWmqm1aAWDJQ",
"width": 2048
}],
"place_id": "ChIJjRPL7sm4woARFcIwlfejAGA",
"price_level": 2,
"rating": 4.5,
"reference": "CmRbAAAANXEcxEtfEYz2V5OIwdFEuyw35xZZ7SpF9AVuYFUqha3tf8CiZbuYrXLCG_y-twfXpcRiYyN0kXDfwmjA-uPZQaPbfpriBY397myHuiFF1A1ESI-5Hcb1y6UiRger5g3tEhCRq3Ml_vdNqA8AGua9iLXrGhTz1KC9902YirqAu7KLW4Bq7FYOEQ",
"types": [
"cafe",
"restaurant",
"food",
"point_of_interest",
"establishment"
]
}
],
"status": "OK"
}

48
example/src/App.js Normal file
View File

@ -0,0 +1,48 @@
import React, { useState, useEffect } from 'react';
import styled from 'styled-components';
import GoogleMapReact from 'google-map-react'
// import 'google-map-react/dist/index.css'
import LOS_ANGELES_CENTER from './const/la_center';
import Marker from './components/Marker';
const Wrapper = styled.main`
width: 100%;
height: 100%;
`;
const App = () => {
const [places, setPlaces] = useState([])
useEffect(() => {
fetch('places.json')
.then((response) => response.json())
.then((data) => setPlaces(data.results))
})
if (!places || places.length === 0) {
return null;
}
return (
<Wrapper>
<GoogleMapReact
defaultZoom={10}
defaultCenter={LOS_ANGELES_CENTER}
>
{places.map((place) => (
<Marker
key={place.id}
text={place.name}
lat={place.geometry.location.lat}
lng={place.geometry.location.lng}
/>
))}
</GoogleMapReact>
</Wrapper>
)
}
export default App

9
example/src/App.test.js Normal file
View File

@ -0,0 +1,9 @@
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
it('renders without crashing', () => {
const div = document.createElement('div')
ReactDOM.render(<App />, div)
ReactDOM.unmountComponentAtNode(div)
})

View File

@ -0,0 +1,38 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
const Wrapper = styled.div`
position: absolute;
top: 50%;
left: 50%;
width: 18px;
height: 18px;
background-color: #000;
border: 2px solid #fff;
border-radius: 100%;
user-select: none;
transform: translate(-50%, -50%);
cursor: ${(props) => (props.onClick ? 'pointer' : 'default')};
&:hover {
z-index: 1;
}
`;
const Marker = ({ text, onClick }) => (
<Wrapper
alt={text}
onClick={onClick}
/>
);
Marker.defaultProps = {
onClick: null,
};
Marker.propTypes = {
onClick: PropTypes.func,
text: PropTypes.string.isRequired,
};
export default Marker;

View File

@ -0,0 +1 @@
export default [34.0522, -118.2437];

19
example/src/index.css Normal file
View File

@ -0,0 +1,19 @@
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
/* this allows map to extend through the height of the dom */
html, body, #root {
height: 100%;
}

7
example/src/index.js Normal file
View File

@ -0,0 +1,7 @@
import './index.css'
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
ReactDOM.render(<App />, document.getElementById('root'))

10805
example/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +1,67 @@
{
"name": "google-map-react",
"version": "1.1.7",
"description": "isomorphic google map react component, allows render react components on the google map",
"main": "lib/index",
"scripts": {
"build:lib": "babel ./src -d lib",
"build:umd": "webpack src/index_umd.js dist/GoogleMapReact.js --config webpack.config.dev.js",
"build:umd:min": "webpack src/index_umd.js dist/GoogleMapReact.min.js --config webpack.config.prod.js",
"build": "yarn run build:lib && yarn run build:umd && yarn run build:umd:min",
"clean": "rimraf lib dist",
"prepublish": "yarn run clean && yarn run build",
"format": "prettier --trailing-comma es5 --single-quote --write 'src/**/*.js' 'src/**/*.js'",
"format:dev": "prettier --trailing-comma es5 --single-quote --write 'develop/**/*.js' 'develop/**/*.js'",
"lint": "eslint src",
"lint:dev": "eslint develop",
"test": "NODE_ENV=eee mocha --compilers js:babel-register --recursive --require babel-polyfill",
"test:watch": "NODE_ENV=eee mocha --compilers js:babel-register --recursive --require babel-polyfill --watch",
"start": "kotatsu serve --port 4000 --config ./develop/config/index.babel.js --presets es2015,stage-0,react,react-hmre ./develop/Main.js",
"precommit": "lint-staged"
},
"description": "Isomorphic component that allows rendering react components on a google map",
"author": "istarkov https://github.com/istarkov",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/google-map-react/google-map-react.git"
},
"main": "dist/index.js",
"module": "dist/index.modern.js",
"source": "src/index.js",
"engines": {
"node": ">=10"
},
"scripts": {
"build": "microbundle-crl --no-compress --format modern,cjs",
"start": "microbundle-crl watch --no-compress --format modern,cjs",
"prepare": "run-s build",
"test": "run-s test:unit test:lint test:build",
"test:build": "run-s build",
"test:lint": "eslint .",
"test:unit": "cross-env CI=1 react-scripts test --env=jsdom",
"test:watch": "react-scripts test --env=jsdom",
"predeploy": "cd example && yarn install && yarn run build",
"deploy": "gh-pages -d example/build"
},
"dependencies": {
"@mapbox/point-geometry": "^0.1.0",
"eventemitter3": "^4.0.4",
"prop-types": "^15.7.2",
"scriptjs": "^2.5.9"
},
"peerDependencies": {
"react": "^16.0.0",
"react-dom": "^16.0.0"
},
"devDependencies": {
"babel-eslint": "^10.0.3",
"cross-env": "^7.0.2",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.7.0",
"eslint-config-standard": "^14.1.0",
"eslint-config-standard-react": "^9.2.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-node": "^11.0.0",
"eslint-plugin-prettier": "^3.1.1",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-react": "^7.17.0",
"eslint-plugin-standard": "^4.0.1",
"expect": "1.20.2",
"jsdom": "^6.5.1",
"gh-pages": "^2.2.0",
"microbundle-crl": "^0.13.10",
"npm-run-all": "^4.1.5",
"prettier": "^2.0.4",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-scripts": "^3.4.1"
},
"files": [
"dist"
],
"keywords": [
"react",
"reactjs",
@ -34,73 +73,5 @@
"component",
"javascript",
"react-component"
],
"author": "istarkov https://github.com/istarkov",
"license": "MIT",
"bugs": {
"url": "https://github.com/google-map-react/google-map-react/issues"
},
"homepage": "https://github.com/google-map-react/google-map-react#readme",
"peerDependencies": {
"react": "^0.14.0 || ^15.0.0 || ^16.0.0",
"react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0"
},
"dependencies": {
"@mapbox/point-geometry": "^0.1.0",
"eventemitter3": "^4.0.4",
"prop-types": "^15.5.6",
"scriptjs": "^2.5.9"
},
"devDependencies": {
"autoprefixer": "^6.3.6",
"babel-cli": "^6.14.0",
"babel-core": "^6.14.0",
"babel-eslint": "^7.2.2",
"babel-loader": "^6.2.5",
"babel-plugin-webpack-loaders": "^0.5.0",
"babel-polyfill": "^6.13.0",
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.11.1",
"babel-preset-react-hmre": "^1.1.1",
"babel-preset-stage-0": "^6.5.0",
"babel-register": "^6.14.0",
"css-loader": "^0.23.1",
"eslint": "^4.18.2",
"eslint-config-airbnb": "^14.0.0",
"eslint-config-prettier": "^1.5.0",
"eslint-plugin-babel": "^4.1.1",
"eslint-plugin-import": "^2.2.0",
"eslint-plugin-jsx-a11y": "^4.0.0",
"eslint-plugin-prettier": "^2.0.1",
"eslint-plugin-react": "^6.8.0",
"expect": "^1.11.1",
"file-loader": "^0.8.5",
"husky": "^0.13.3",
"jsdom": "^6.5.1",
"kotatsu": "^0.14.0",
"lint-staged": "^3.4.0",
"mocha": "^5.2.0",
"node-sass": "^4.14.1",
"normalize.css": "^4.1.1",
"postcss-loader": "^0.9.1",
"prettier": "^0.22.0",
"react": "^16.0.0",
"react-dom": "^16.4.1",
"react-motion": "^0.4.4",
"react-router": "^3.2.0",
"recompose": "^0.26.0",
"reselect": "^2.5.1",
"rimraf": "^2.4.3",
"rxjs": "^5.0.0-beta.8",
"sass-loader": "^4.1.1",
"style-loader": "^0.13.1",
"url-loader": "^0.5.7",
"webpack": "^1.12.2"
},
"lint-staged": {
"*.js": [
"prettier --trailing-comma es5 --single-quote --write",
"git add"
]
}
}
]
}

View File

@ -1,16 +1,13 @@
export const generateHeatmap = (instance, { positions }) =>
new instance.visualization.HeatmapLayer({
data: positions.reduce(
(acc, { lat, lng, weight = 1 }) => {
acc.push({
location: new instance.LatLng(lat, lng),
weight,
});
return acc;
},
[]
),
data: positions.reduce((acc, { lat, lng, weight = 1 }) => {
acc.push({
location: new instance.LatLng(lat, lng),
weight,
});
return acc;
}, []),
});
export const optionsHeatmap = (instance, { options = {} }) =>
Object.keys(options).map(option => instance.set(option, options[option]));
Object.keys(options).map((option) => instance.set(option, options[option]));

View File

@ -1,4 +1,4 @@
/* eslint-disable import/no-extraneous-dependencies, react/forbid-prop-types, react/no-find-dom-node, no-console */
/* eslint-disable import/no-extraneous-dependencies, react/forbid-prop-types, react/no-find-dom-node, no-console, no-undef */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
@ -13,19 +13,24 @@ import { generateHeatmap, optionsHeatmap } from './google_heatmap';
// loaders
import googleMapLoader from './loaders/google_map_loader';
// utils
import Geo from './utils/geo';
// lib
import Geo from './lib/geo';
// tools
import raf from './utils/raf';
import pick from './utils/pick';
import log2 from './utils/log2';
import omit from './utils/omit';
import log2 from './utils/math/log2';
import pick from './utils/pick';
import isEmpty from './utils/isEmpty';
import isNumber from './utils/isNumber';
import detectBrowser from './utils/detect';
import shallowEqual from './utils/shallowEqual';
import isPlainObject from './utils/isPlainObject';
import isArraysEqualEps from './utils/isArraysEqualEps';
import detectElementResize from './utils/detectElementResize';
import {
addResizeListener,
removeResizeListener,
} from './utils/detectElementResize';
import addPassiveEventListener from './utils/passiveEvents';
// consts
@ -62,7 +67,7 @@ function defaultOptions_(/* maps */) {
};
}
const latLng2Obj = latLng =>
const latLng2Obj = (latLng) =>
isPlainObject(latLng) ? latLng : { lat: latLng[0], lng: latLng[1] };
const _checkMinZoom = (zoom, minZoom) => {
@ -292,7 +297,7 @@ export default class GoogleMap extends Component {
);
if (this.props.resetBoundsOnResize) {
const that = this;
detectElementResize.addResizeListener(mapDom, that._mapDomResizeCallback);
addResizeListener(mapDom, that._mapDomResizeCallback);
}
}
@ -367,9 +372,10 @@ export default class GoogleMap extends Component {
!shallowEqual(this.props.options, nextProps.options)
) {
const mapPlainObjects = pick(this.maps_, isPlainObject);
let options = typeof nextProps.options === 'function'
? nextProps.options(mapPlainObjects)
: nextProps.options;
let options =
typeof nextProps.options === 'function'
? nextProps.options(mapPlainObjects)
: nextProps.options;
// remove zoom, center and draggable options as these are managed by google-maps-react
options = omit(options, ['zoom', 'center', 'draggable']);
@ -382,7 +388,7 @@ export default class GoogleMap extends Component {
}
if (!shallowEqual(nextProps.layerTypes, this.props.layerTypes)) {
Object.keys(this.layers_).forEach(layerKey => {
Object.keys(this.layers_).forEach((layerKey) => {
this.layers_[layerKey].setMap(null);
delete this.layers_[layerKey];
});
@ -394,7 +400,7 @@ export default class GoogleMap extends Component {
!shallowEqual(nextProps.heatmap.positions, this.props.heatmap.positions)
) {
this.heatmap.setData(
nextProps.heatmap.positions.map(p => ({
nextProps.heatmap.positions.map((p) => ({
location: new this.maps_.LatLng(p.lat, p.lng),
weight: p.weight,
}))
@ -405,10 +411,12 @@ export default class GoogleMap extends Component {
shouldComponentUpdate(nextProps, nextState) {
// draggable does not affect inner components
return !shallowEqual(
omit(this.props, ['draggable']),
omit(nextProps, ['draggable'])
) || !shallowEqual(this.state, nextState);
return (
!shallowEqual(
omit(this.props, ['draggable']),
omit(nextProps, ['draggable'])
) || !shallowEqual(this.state, nextState)
);
}
componentDidUpdate(prevProps) {
@ -429,10 +437,7 @@ export default class GoogleMap extends Component {
window.removeEventListener('keydown', this._onKeyDownCapture);
window.removeEventListener('mouseup', this._onChildMouseUp, false);
if (this.props.resetBoundsOnResize) {
detectElementResize.removeResizeListener(
mapDom,
this._mapDomResizeCallback
);
removeResizeListener(mapDom, this._mapDomResizeCallback);
}
if (this.overlay_) {
@ -459,25 +464,24 @@ export default class GoogleMap extends Component {
delete this.markersDispatcher_;
}
}
// calc minZoom if map size available
// it's better to not set minZoom less than this calculation gives
// otherwise there is no homeomorphism between screen coordinates and map
// (one map coordinate can have different screen coordinates)
_getMinZoom = () => {
if (this.geoService_.getWidth() > 0 || this.geoService_.getHeight() > 0) {
const tilesPerWidth = Math.ceil(
this.geoService_.getWidth() / K_GOOGLE_TILE_SIZE
) + 2;
const tilesPerHeight = Math.ceil(
this.geoService_.getHeight() / K_GOOGLE_TILE_SIZE
) + 2;
const tilesPerWidth =
Math.ceil(this.geoService_.getWidth() / K_GOOGLE_TILE_SIZE) + 2;
const tilesPerHeight =
Math.ceil(this.geoService_.getHeight() / K_GOOGLE_TILE_SIZE) + 2;
const maxTilesPerDim = Math.max(tilesPerWidth, tilesPerHeight);
return Math.ceil(log2(maxTilesPerDim));
}
return DEFAULT_MIN_ZOOM;
};
_computeMinZoom = minZoom => {
_computeMinZoom = (minZoom) => {
if (!isEmpty(minZoom)) {
return minZoom;
}
@ -496,8 +500,8 @@ export default class GoogleMap extends Component {
}
};
_setLayers = layerTypes => {
layerTypes.forEach(layerType => {
_setLayers = (layerTypes) => {
layerTypes.forEach((layerType) => {
this.layers_[layerType] = new this.maps_[layerType]();
this.layers_[layerType].setMap(this.map_);
});
@ -543,7 +547,7 @@ export default class GoogleMap extends Component {
this.props
.googleMapLoader(bootstrapURLKeys, this.props.heatmapLibrary)
.then(maps => {
.then((maps) => {
if (!this.mounted_) {
return;
}
@ -576,9 +580,10 @@ export default class GoogleMap extends Component {
// "MaxZoomStatus", "StreetViewStatus", "TransitMode", "TransitRoutePreference",
// "TravelMode", "UnitSystem"
const mapPlainObjects = pick(maps, isPlainObject);
const options = typeof this.props.options === 'function'
? this.props.options(mapPlainObjects)
: this.props.options;
const options =
typeof this.props.options === 'function'
? this.props.options(mapPlainObjects)
: this.props.options;
const defaultOptions = defaultOptions_(mapPlainObjects);
const draggableOptions = !isEmpty(this.props.draggable) && {
@ -625,12 +630,10 @@ export default class GoogleMap extends Component {
const this_ = this;
const overlay = Object.assign(new maps.OverlayView(), {
onAdd() {
const K_MAX_WIDTH = typeof screen !== 'undefined'
? `${screen.width}px`
: '2000px';
const K_MAX_HEIGHT = typeof screen !== 'undefined'
? `${screen.height}px`
: '2000px';
const K_MAX_WIDTH =
typeof screen !== 'undefined' ? `${screen.width}px` : '2000px';
const K_MAX_HEIGHT =
typeof screen !== 'undefined' ? `${screen.height}px` : '2000px';
const div = document.createElement('div');
div.style.backgroundColor = 'transparent';
@ -643,7 +646,7 @@ export default class GoogleMap extends Component {
if (this_.props.overlayViewDivStyle) {
const { overlayViewDivStyle } = this_.props;
if (typeof overlayViewDivStyle === 'object') {
Object.keys(overlayViewDivStyle).forEach(property => {
Object.keys(overlayViewDivStyle).forEach((property) => {
div.style[property] = overlayViewDivStyle[property];
});
}
@ -732,7 +735,8 @@ export default class GoogleMap extends Component {
const TIMEOUT_ZOOM = 300;
if (
new Date().getTime() - this.zoomControlClickTime_ < TIMEOUT_ZOOM
new Date().getTime() - this.zoomControlClickTime_ <
TIMEOUT_ZOOM
) {
// there is strange Google Map Api behavior in chrome when zoom animation of map
// is started only on second raf call, if was click on zoom control
@ -744,7 +748,8 @@ export default class GoogleMap extends Component {
raf(() => {
this_.updateCounter_++;
this_._onBoundsChanged(map, maps);
}));
})
);
} else {
this_.updateCounter_++;
this_._onBoundsChanged(map, maps);
@ -821,7 +826,7 @@ export default class GoogleMap extends Component {
this_._onMapTypeIdChange(map.getMapTypeId());
});
})
.catch(e => {
.catch((e) => {
// notify callback of load failure
this._onGoogleApiLoaded({
map: null,
@ -937,7 +942,7 @@ export default class GoogleMap extends Component {
this.resetSizeOnIdle_ = true;
};
_onMapMouseMove = e => {
_onMapMouseMove = (e) => {
if (!this.mouseInMap_) return;
const currTime = new Date().getTime();
@ -980,7 +985,7 @@ export default class GoogleMap extends Component {
this.dragTime_ === 0 &&
this.props.onClick(...args);
_onMapClick = event => {
_onMapClick = (event) => {
if (this.markersDispatcher_) {
// support touch events and recalculate mouse position on click
this._onMapMouseMove(event);
@ -1000,13 +1005,13 @@ export default class GoogleMap extends Component {
// gmap can't prevent map drag if mousedown event already occured
// the only workaround I find is prevent mousedown native browser event
_onMapMouseDownNative = event => {
_onMapMouseDownNative = (event) => {
if (!this.mouseInMap_) return;
this._onMapMouseDown(event);
};
_onMapMouseDown = event => {
_onMapMouseDown = (event) => {
if (this.markersDispatcher_) {
const currTime = new Date().getTime();
if (currTime - this.dragTime_ > K_IDLE_TIMEOUT) {
@ -1031,7 +1036,7 @@ export default class GoogleMap extends Component {
}
};
_isCenterDefined = center =>
_isCenterDefined = (center) =>
center &&
((isPlainObject(center) && isNumber(center.lat) && isNumber(center.lng)) ||
(center.length === 2 && isNumber(center[0]) && isNumber(center[1])));
@ -1123,26 +1128,26 @@ export default class GoogleMap extends Component {
}
};
_registerChild = ref => {
_registerChild = (ref) => {
this.googleMapDom_ = ref;
};
render() {
const overlay = this.state.overlay;
const mapMarkerPrerender = !overlay
? <GoogleMapMarkersPrerender
experimental={this.props.experimental}
onChildClick={this._onChildClick}
onChildMouseDown={this._onChildMouseDown}
onChildMouseEnter={this._onChildMouseEnter}
onChildMouseLeave={this._onChildMouseLeave}
geoService={this.geoService_}
insideMapPanes={false}
distanceToMouse={this.props.distanceToMouse}
getHoverDistance={this._getHoverDistance}
dispatcher={this.markersDispatcher_}
/>
: null;
const mapMarkerPrerender = !overlay ? (
<GoogleMapMarkersPrerender
experimental={this.props.experimental}
onChildClick={this._onChildClick}
onChildMouseDown={this._onChildMouseDown}
onChildMouseEnter={this._onChildMouseEnter}
onChildMouseLeave={this._onChildMouseLeave}
geoService={this.geoService_}
insideMapPanes={false}
distanceToMouse={this.props.distanceToMouse}
getHoverDistance={this._getHoverDistance}
dispatcher={this.markersDispatcher_}
/>
) : null;
return (
<div

View File

@ -66,15 +66,19 @@ export default class GoogleMapMarkers extends Component {
shouldComponentUpdate(nextProps, nextState) {
if (this.props.experimental === true) {
return !shallowEqual(this.props, nextProps) ||
return (
!shallowEqual(this.props, nextProps) ||
!shallowEqual(
omit(this.state, ['hoverKey']),
omit(nextState, ['hoverKey'])
);
)
);
}
return !shallowEqual(this.props, nextProps) ||
!shallowEqual(this.state, nextState);
return (
!shallowEqual(this.props, nextProps) ||
!shallowEqual(this.state, nextState)
);
}
componentWillUnmount() {
@ -165,7 +169,7 @@ export default class GoogleMapMarkers extends Component {
}
};
_onMouseAllow = value => {
_onMouseAllow = (value) => {
if (!value) {
this._onChildMouseLeave();
}
@ -201,9 +205,10 @@ export default class GoogleMapMarkers extends Component {
return;
}
const childKey = child.key !== undefined && child.key !== null
? child.key
: childIndex;
const childKey =
child.key !== undefined && child.key !== null
? child.key
: childIndex;
const dist = this.props.distanceToMouse(
this.dimensionsCache_[childKey],
mp,
@ -236,7 +241,7 @@ export default class GoogleMapMarkers extends Component {
}
};
_getDimensions = key => {
_getDimensions = (key) => {
const childKey = key;
return this.dimensionsCache_[childKey];
};
@ -261,9 +266,10 @@ export default class GoogleMapMarkers extends Component {
});
}
const latLng = child.props.latLng !== undefined
? child.props.latLng
: { lat: child.props.lat, lng: child.props.lng };
const latLng =
child.props.latLng !== undefined
? child.props.latLng
: { lat: child.props.lat, lng: child.props.lng };
const pt = this.props.insideMapPanes
? this.props.geoService.fromLatLngToDivPixel(latLng)
@ -281,9 +287,10 @@ export default class GoogleMapMarkers extends Component {
child.props.seLatLng !== undefined ||
(child.props.seLat !== undefined && child.props.seLng !== undefined)
) {
const seLatLng = child.props.seLatLng !== undefined
? child.props.seLatLng
: { lat: child.props.seLat, lng: child.props.seLng };
const seLatLng =
child.props.seLatLng !== undefined
? child.props.seLatLng
: { lat: child.props.seLat, lng: child.props.seLng };
const sePt = this.props.insideMapPanes
? this.props.geoService.fromLatLngToDivPixel(seLatLng)
@ -299,9 +306,10 @@ export default class GoogleMapMarkers extends Component {
// to prevent rerender on child element i need to pass
// const params $getDimensions and $dimensionKey instead of dimension object
const childKey = child.key !== undefined && child.key !== null
? child.key
: childIndex;
const childKey =
child.key !== undefined && child.key !== null
? child.key
: childIndex;
this.dimensionsCache_[childKey] = {
x: containerPt.x,
@ -328,10 +336,6 @@ export default class GoogleMapMarkers extends Component {
}
);
return (
<div style={mainElementStyle}>
{markers}
</div>
);
return <div style={mainElementStyle}>{markers}</div>;
}
}

View File

@ -13,7 +13,7 @@ const style = {
// opacity: 0.3
};
export default function(props) {
export default function (props) {
return (
<div style={style}>
<GoogleMapMarkers {...props} prerender />

View File

@ -1 +1,5 @@
export default from './google_map';
import GoogleMap from './google_map';
export * from './lib';
export default GoogleMap;

View File

@ -1,3 +0,0 @@
import GoogleMap from './google_map';
module.exports = GoogleMap;

1
src/lib/geo/README.md Normal file
View File

@ -0,0 +1 @@
### The code uses mapboxgl so that coordinate transformations do not depend on the library

View File

@ -1,6 +1,7 @@
import Point from '@mapbox/point-geometry';
import LatLng from './lib_geo/lat_lng';
import Transform from './lib_geo/transform';
import LatLng from './lat_lng';
import Transform from './transform';
export default class Geo {
constructor(tileSize) {
@ -62,8 +63,8 @@ export default class Geo {
}
const pt = this.fromLatLngToCenterPixel(ptLatLng);
pt.x -= this.transform_.worldSize *
Math.round(pt.x / this.transform_.worldSize);
pt.x -=
this.transform_.worldSize * Math.round(pt.x / this.transform_.worldSize);
pt.x += this.transform_.width / 2;
pt.y += this.transform_.height / 2;
@ -112,7 +113,8 @@ export default class Geo {
const bndL = (margins && margins[3]) || 0;
if (
this.getWidth() - bndR - bndL > 0 && this.getHeight() - bndT - bndB > 0
this.getWidth() - bndR - bndL > 0 &&
this.getHeight() - bndT - bndB > 0
) {
const topLeftCorner = this.transform_.pointLocation(
Point.convert({
@ -139,7 +141,7 @@ export default class Geo {
];
if (roundFactor) {
res = res.map(r => Math.round(r * roundFactor) / roundFactor);
res = res.map((r) => Math.round(r * roundFactor) / roundFactor);
}
return res;
}

View File

@ -1,7 +1,7 @@
import { wrap } from './wrap';
export default class LatLng {
static convert = a => {
static convert = (a) => {
if (a instanceof LatLng) {
return a;
}

View File

@ -1,7 +1,7 @@
import LatLng from './lat_lng';
export default class LatLngBounds {
static convert = a => {
static convert = (a) => {
if (!a || a instanceof LatLngBounds) return a;
return new LatLngBounds(a);
};

View File

@ -51,11 +51,11 @@ export default class Transform {
}
get bearing() {
return -this.angle / Math.PI * 180;
return (-this.angle / Math.PI) * 180;
}
set bearing(bearing) {
this.angle = -wrap(bearing, -180, 180) * Math.PI / 180;
this.angle = (-wrap(bearing, -180, 180) * Math.PI) / 180;
}
get zoom() {
@ -106,24 +106,23 @@ export default class Transform {
// lat/lon <-> absolute pixel coords convertion
lngX(lon, worldSize) {
return (180 + lon) * (worldSize || this.worldSize) / 360;
return ((180 + lon) * (worldSize || this.worldSize)) / 360;
}
// latitude to absolute y coord
latY(lat, worldSize) {
const y = 180 /
Math.PI *
Math.log(Math.tan(Math.PI / 4 + lat * Math.PI / 360));
return (180 - y) * (worldSize || this.worldSize) / 360;
const y =
(180 / Math.PI) * Math.log(Math.tan(Math.PI / 4 + (lat * Math.PI) / 360));
return ((180 - y) * (worldSize || this.worldSize)) / 360;
}
xLng(x, worldSize) {
return x * 360 / (worldSize || this.worldSize) - 180;
return (x * 360) / (worldSize || this.worldSize) - 180;
}
yLat(y, worldSize) {
const y2 = 180 - y * 360 / (worldSize || this.worldSize);
return 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90;
const y2 = 180 - (y * 360) / (worldSize || this.worldSize);
return (360 / Math.PI) * Math.atan(Math.exp((y2 * Math.PI) / 180)) - 90;
}
locationPoint(latlng) {

View File

@ -2,5 +2,5 @@
export function wrap(n, min, max) {
const d = max - min;
return n === max ? n : ((n - min) % d + d) % d + min;
return n === max ? n : ((((n - min) % d) + d) % d) + min;
}

View File

@ -1,15 +1,17 @@
import log2 from './math/log2';
import log2 from '../utils/log2';
const GOOGLE_TILE_SIZE = 256;
function latLng2World({ lat, lng }) {
const sin = Math.sin(lat * Math.PI / 180);
const sin = Math.sin((lat * Math.PI) / 180);
const x = lng / 360 + 0.5;
let y = 0.5 - 0.25 * Math.log((1 + sin) / (1 - sin)) / Math.PI;
let y = 0.5 - (0.25 * Math.log((1 + sin) / (1 - sin))) / Math.PI;
y = y < 0 // eslint-disable-line
? 0
: y > 1 ? 1 : y;
? 0
: y > 1
? 1
: y;
return { x, y };
}
@ -19,19 +21,21 @@ function world2LatLng({ x, y }) {
// TODO test that this is faster
// 360 * Math.atan(Math.exp((180 - y * 360) * Math.PI / 180)) / Math.PI - 90;
return {
lat: 180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))),
lat: (180 / Math.PI) * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))),
lng: x * 360 - 180,
};
}
// Thank you wiki https://en.wikipedia.org/wiki/Geographic_coordinate_system
function latLng2MetersPerDegree({ lat }) {
const phi = lat * Math.PI / 180;
const metersPerLatDegree = 111132.92 -
const phi = (lat * Math.PI) / 180;
const metersPerLatDegree =
111132.92 -
559.82 * Math.cos(2 * phi) +
1.175 * Math.cos(4 * phi) -
0.0023 * Math.cos(6 * phi);
const metersPerLngDegree = 111412.84 * Math.cos(phi) -
const metersPerLngDegree =
111412.84 * Math.cos(phi) -
93.5 * Math.cos(3 * phi) +
0.118 * Math.cos(5 * phi);
return { metersPerLatDegree, metersPerLngDegree };
@ -42,8 +46,8 @@ function meters2LatLngBounds(meters, { lat, lng }) {
lat,
});
const latDelta = 0.5 * meters / metersPerLatDegree;
const lngDelta = 0.5 * meters / metersPerLngDegree;
const latDelta = (0.5 * meters) / metersPerLatDegree;
const lngDelta = (0.5 * meters) / metersPerLngDegree;
return {
nw: {
@ -71,9 +75,8 @@ function fitNwSe(nw, se, width, height) {
const EPS = 0.000000001;
const nwWorld = latLng2World(nw);
const seWorld = latLng2World(se);
const dx = nwWorld.x < seWorld.x
? seWorld.x - nwWorld.x
: 1 - nwWorld.x + seWorld.x;
const dx =
nwWorld.x < seWorld.x ? seWorld.x - nwWorld.x : 1 - nwWorld.x + seWorld.x;
const dy = seWorld.y - nwWorld.y;
if (dx <= 0 && dy <= 0) {
@ -87,10 +90,10 @@ function fitNwSe(nw, se, width, height) {
// TODO find center just unproject middle world point
const middle = {
x: nwWorld.x < seWorld.x // eslint-disable-line
? 0.5 * (nwWorld.x + seWorld.x)
: nwWorld.x + seWorld.x - 1 > 0
? 0.5 * (nwWorld.x + seWorld.x - 1)
: 0.5 * (1 + nwWorld.x + seWorld.x),
? 0.5 * (nwWorld.x + seWorld.x)
: nwWorld.x + seWorld.x - 1 > 0
? 0.5 * (nwWorld.x + seWorld.x - 1)
: 0.5 * (1 + nwWorld.x + seWorld.x),
y: 0.5 * (nwWorld.y + seWorld.y),
};
@ -181,11 +184,11 @@ export function meters2ScreenPixels(meters, { lat, lng }, zoom) {
// Helper functions for working with svg tiles, (examples coming soon)
export function tile2LatLng({ x, y }, zoom) {
const n = Math.PI - 2 * Math.PI * y / Math.pow(2, zoom);
const n = Math.PI - (2 * Math.PI * y) / Math.pow(2, zoom);
return {
lat: 180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))),
lng: x / Math.pow(2, zoom) * 360 - 180,
lat: (180 / Math.PI) * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))),
lng: (x / Math.pow(2, zoom)) * 360 - 180,
};
}

View File

@ -8,7 +8,7 @@ let loadPromise_;
let resolveCustomPromise_;
const _customPromise = new Promise(resolve => {
const _customPromise = new Promise((resolve) => {
resolveCustomPromise_ = resolve;
});

View File

@ -1,12 +1,10 @@
/* eslint-disable */
import './utils/jsdomInit.js';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import TestUtils from 'react-dom/test-utils';
import expect from 'expect';
import TestUtils from 'react-dom/test-utils';
import GoogleMap from '../../src/google_map';
import GoogleMap from '../../google_map';
describe('Components', () => {
it('Should work', () => {
@ -149,7 +147,7 @@ describe('Components', () => {
it('Should call custom loader', () => {
const API_KEY = 'API_KEY';
const spy = expect.createSpy(() => {});
const spy = expect.createSpy(() => { });
const asyncSpy = async a => spy(a);
TestUtils.renderIntoDocument(

View File

@ -30,29 +30,29 @@ var attachEvent = typeof document !== 'undefined' && document.attachEvent;
var stylesCreated = false;
if (canUseDOM && !attachEvent) {
var requestFrame = (function() {
var requestFrame = (function () {
var raf = _window.requestAnimationFrame ||
_window.mozRequestAnimationFrame ||
_window.webkitRequestAnimationFrame ||
function(fn) {
function (fn) {
return _window.setTimeout(fn, 20);
};
return function(fn) {
return function (fn) {
return raf(fn);
};
})();
var cancelFrame = (function() {
var cancelFrame = (function () {
var cancel = _window.cancelAnimationFrame ||
_window.mozCancelAnimationFrame ||
_window.webkitCancelAnimationFrame ||
_window.clearTimeout;
return function(id) {
return function (id) {
return cancel(id);
};
})();
var resetTriggers = function(element) {
var resetTriggers = function (element) {
var triggers = element.__resizeTriggers__,
expand = triggers.firstElementChild,
contract = triggers.lastElementChild,
@ -65,20 +65,20 @@ if (canUseDOM && !attachEvent) {
expand.scrollTop = expand.scrollHeight;
};
var checkTriggers = function(element) {
var checkTriggers = function (element) {
return element.offsetWidth != element.__resizeLast__.width ||
element.offsetHeight != element.__resizeLast__.height;
};
var scrollListener = function(e) {
var scrollListener = function (e) {
var element = this;
resetTriggers(this);
if (this.__resizeRAF__) cancelFrame(this.__resizeRAF__);
this.__resizeRAF__ = requestFrame(function() {
this.__resizeRAF__ = requestFrame(function () {
if (checkTriggers(element)) {
element.__resizeLast__.width = element.offsetWidth;
element.__resizeLast__.height = element.offsetHeight;
element.__resizeListeners__.forEach(function(fn) {
element.__resizeListeners__.forEach(function (fn) {
fn.call(element, e);
});
}
@ -128,7 +128,7 @@ if (canUseDOM && !attachEvent) {
'; ';
}
var createStyles = function() {
var createStyles = function () {
if (!stylesCreated) {
//opacity:0 works around a chrome bug https://code.google.com/p/chromium/issues/detail?id=286360
var css = (animationKeyframes ? animationKeyframes : '') +
@ -151,7 +151,7 @@ var createStyles = function() {
}
};
var addResizeListener = function(element, fn) {
var addResizeListener = function (element, fn) {
if (element.parentNode === undefined) {
var tempParentDiv = document.createElement('div');
element.parentNode = tempParentDiv;
@ -180,7 +180,7 @@ var addResizeListener = function(element, fn) {
animationstartevent &&
element.__resizeTriggers__.addEventListener(
animationstartevent,
function(e) {
function (e) {
if (e.animationName == animationName) resetTriggers(element);
}
);
@ -189,7 +189,7 @@ var addResizeListener = function(element, fn) {
}
};
var removeResizeListener = function(element, fn) {
var removeResizeListener = function (element, fn) {
element = element.parentNode;
if (attachEvent)
element.detachEvent('onresize', fn);
@ -207,7 +207,7 @@ var removeResizeListener = function(element, fn) {
}
};
module.exports = {
addResizeListener: addResizeListener,
removeResizeListener: removeResizeListener,
export {
addResizeListener,
removeResizeListener,
};

View File

@ -1 +0,0 @@
export * from './utils.js';

View File

@ -1,4 +1,4 @@
const isEmpty = val => {
const isEmpty = (val) => {
// check for empty object {}, array []
if (val !== null && typeof val === 'object') {
if (Object.keys(val).length === 0) {

View File

@ -6,6 +6,8 @@ const objectToString = Object.prototype.toString;
export default function isNumber(value) {
const numberTag = '[object Number]';
return typeof value === 'number' ||
(isObjectLike(value) && objectToString.call(value) === numberTag);
return (
typeof value === 'number' ||
(isObjectLike(value) && objectToString.call(value) === numberTag)
);
}

View File

@ -1,5 +1,5 @@
// source taken from https://github.com/rackt/redux/blob/master/src/utils/isPlainObject.js
const fnToString = fn => Function.prototype.toString.call(fn);
const fnToString = (fn) => Function.prototype.toString.call(fn);
/**
* @param {any} obj The object to inspect.
@ -10,9 +10,10 @@ export default function isPlainObject(obj) {
return false;
}
const proto = typeof obj.constructor === 'function'
? Object.getPrototypeOf(obj)
: Object.prototype;
const proto =
typeof obj.constructor === 'function'
? Object.getPrototypeOf(obj)
: Object.prototype;
if (proto === null) {
return true;
@ -20,7 +21,9 @@ export default function isPlainObject(obj) {
const constructor = proto.constructor;
return typeof constructor === 'function' &&
return (
typeof constructor === 'function' &&
constructor instanceof constructor &&
fnToString(constructor) === fnToString(Object);
fnToString(constructor) === fnToString(Object)
);
}

View File

@ -1,2 +0,0 @@
###Код выдран из mapboxgl чтобы преобразования координат не зависели от библиотеки

3
src/utils/log2.js Normal file
View File

@ -0,0 +1,3 @@
const log2 = Math.log2 ? Math.log2 : (x) => Math.log(x) / Math.LN2;
export default log2;

View File

@ -1,3 +0,0 @@
const log2 = Math.log2 ? Math.log2 : x => Math.log(x) / Math.LN2;
export default log2;

View File

@ -1,13 +1,10 @@
// source taken from https://github.com/rackt/redux/blob/master/src/utils/pick.js
export default function pick(obj, fn) {
return Object.keys(obj).reduce(
(result, key) => {
if (fn(obj[key])) {
return Object.keys(obj).reduce((result, key) => {
if (fn(obj[key])) {
result[key] = obj[key]; // eslint-disable-line
}
return result;
},
{}
);
}
return result;
}, {});
}

View File

@ -3,8 +3,8 @@ export default function raf(callback) {
return window.requestAnimationFrame(callback);
}
const nativeRaf = window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame;
const nativeRaf =
window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame;
return nativeRaf
? nativeRaf(callback)

View File

@ -15,7 +15,7 @@ const hasOwnProperty = Object.prototype.hasOwnProperty;
* inlined Object.is polyfill to avoid requiring consumers ship their own
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
*/
function is(x: mixed, y: mixed): boolean {
function is(x, y) {
// SameValue algorithm
if (x === y) {
// Steps 1-5, 7-10
@ -33,7 +33,7 @@ function is(x: mixed, y: mixed): boolean {
* when any key has values which are not strictly equal between the arguments.
* Returns true when the values of all keys are strictly equal.
*/
function shallowEqual(objA: mixed, objB: mixed): boolean {
function shallowEqual(objA, objB) {
if (is(objA, objB)) {
return true;
}
@ -67,5 +67,5 @@ function shallowEqual(objA: mixed, objB: mixed): boolean {
return true;
}
module.exports = shallowEqual;
export default shallowEqual;
/* src: https://github.com/facebook/fbjs/blob/master/packages/fbjs/src/core/shallowEqual.js */

View File

@ -1,18 +0,0 @@
/* eslint-disable */
import jsdom from 'jsdom';
const doc = jsdom.jsdom('<!doctype html><html><body></body></html>');
const win = doc.defaultView;
global.document = doc;
global.window = win;
function propagateToGlobal(window) {
for (const key in window) {
if (!window.hasOwnProperty(key)) continue;
if (key in global) continue;
global[key] = window[key];
}
}
propagateToGlobal(win);

View File

@ -1,2 +0,0 @@
var utils = require('../lib/utils'); // eslint-disable-line
module.exports = utils;

View File

@ -1,27 +0,0 @@
const reactExternal = {
root: 'React',
commonjs2: 'react',
commonjs: 'react',
amd: 'react',
};
const reactDomExternal = {
root: 'ReactDOM',
commonjs2: 'react-dom',
commonjs: 'react-dom',
amd: 'react-dom',
};
module.exports = {
output: {
library: 'GoogleMapReact',
libraryTarget: 'umd',
},
externals: {
react: reactExternal,
'react-dom': reactDomExternal,
},
module: {
loaders: [{ test: /\.js$/, exclude: /node_modules/, loader: 'babel' }],
},
};

View File

@ -1,12 +0,0 @@
const webpack = require('webpack');
const baseConfig = require('./webpack.config.base');
const config = Object.create(baseConfig);
config.plugins = [
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development'),
}),
];
module.exports = config;

View File

@ -1,18 +0,0 @@
const webpack = require('webpack');
const baseConfig = require('./webpack.config.base');
const config = Object.create(baseConfig);
config.plugins = [
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
new webpack.optimize.UglifyJsPlugin({
compressor: {
screw_ie8: true,
warnings: false,
},
}),
];
module.exports = config;

11569
yarn.lock

File diff suppressed because it is too large Load Diff