Extend develop to support multiple routes

This commit is contained in:
cybice 2016-06-13 11:59:41 +03:00
parent 331c6bfc35
commit d633e78caf
11 changed files with 167 additions and 26 deletions

View File

@ -1,13 +1,15 @@
import React from 'react';
import compose from 'recompose/compose';
import defaultProps from 'recompose/defaultProps';
import withState from 'recompose/withState';
import withStateSelector from './utils/withStateSelector';
import withHandlers from 'recompose/withHandlers';
// import withPropsOnChange from 'recompose/withPropsOnChange';
import withState from 'recompose/withState';
import withPropsOnChange from 'recompose/withPropsOnChange';
import ptInBounds from './utils/ptInBounds';
import GoogleMapReact from '../src';
import SimpleMarker from './markers/SimpleMarker';
import { susolvkaCoords, markersData } from './data/fakeData';
import { susolvkaCoords, generateMarkers } from './data/fakeData';
export const gMap = ({
style, hoverDistance, options,
@ -53,8 +55,14 @@ export const gMapHOC = compose(
flex: 1,
},
}),
// withState so you could change markers if you want
withState('markers', 'setMarkers', markersData),
withStateSelector(
'markers',
'setMarkers',
({ route: { markersCount = 20 } }) => markersCount,
(markersCount) => generateMarkers(markersCount)
),
withState('hoveredMarkerId', 'setHoveredMarkerId', -1),
withState('mapParams', 'setMapParams', { center: susolvkaCoords, zoom: 10 }),
// describe events
@ -69,6 +77,14 @@ export const gMapHOC = compose(
setHoveredMarkerId(-1);
},
}),
withPropsOnChange(
['markers', 'mapParams'],
({ markers, mapParams: { bounds } }) => ({
markers: bounds
? markers.filter(m => ptInBounds(bounds, m))
: [],
})
)
);
export default gMapHOC(gMap);

View File

@ -1,17 +1,18 @@
import React, { Component } from 'react';
import compose from 'recompose/compose';
import { Link } from 'react-router';
import defaultProps from 'recompose/defaultProps';
import layoutStyles from './Layout.sass';
import GMap from './GMap';
// for hmr to work I need the first class to extend Component
export class Layout extends Component { // eslint-disable-line
render() {
const { styles: { layout, header, main, footer, logo } } = this.props;
const { styles: { layout, header, main, footer, logo, links } } = this.props;
return (
<div className={layout}>
<header className={header}>
<div>
Clustering example google-map-react (zoom, move to play with)
<div className={links}>
<Link to="/">Multi Markers</Link>
<Link to="/hoveroptim">Hover optim</Link>
</div>
<div>
<a href="https://github.com/istarkov/google-map-clustering-example">
@ -20,7 +21,7 @@ export class Layout extends Component { // eslint-disable-line
</div>
</header>
<main className={main}>
<GMap />
{this.props.children}
</main>
<footer className={footer}>
<div>

View File

@ -15,6 +15,10 @@
a
color: #fff
.links
a
margin-right: 20px
.logo
width: 1.3em
height: 1.3em

View File

@ -1,11 +1,22 @@
// file: main.jsx
import React from 'react';
import { render } from 'react-dom';
import Layout from './Layout.js';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import Layout from './Layout';
import GMap from './GMap';
import 'normalize.css/normalize.css';
import './Main.sass';
const mountNode = document.getElementById('app');
render(<Layout />, mountNode);
render(
<Router history={browserHistory}>
<Route path="/" component={Layout}>
<Route markersCount={200} path="hoveroptim" component={GMap} />
<IndexRoute markersCount={20} component={GMap} />
</Route>
</Router>
,
mountNode
);

View File

@ -1,17 +1,16 @@
const TOTAL_COUNT = 20;
export const susolvkaCoords = { lat: 60.814305, lng: 47.051773 };
export const markersData = [...Array(TOTAL_COUNT)].fill(0) // fill(0) for loose mode
.map((__, index) => ({
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 generateMarkers = (count) =>
[...Array(count)].fill(0) // fill(0) for loose mode
.map((__, index) => ({
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),
}));

View File

@ -0,0 +1,12 @@
import createEagerElementUtil from './utils/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

@ -0,0 +1,15 @@
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

@ -0,0 +1,13 @@
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

@ -0,0 +1,26 @@
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

@ -0,0 +1,42 @@
import { Component } from 'react';
import createEagerFactory from './createEagerFactory';
import { createSelector } from 'reselect';
const withStateSelector = (stateName, stateUpdaterName, ...selectorArgs) =>
BaseComponent => {
const factory = createEagerFactory(BaseComponent);
return class extends Component {
selector = createSelector(...selectorArgs);
state = {
stateValue: this.selector(this.props),
};
updateStateValue = (updateFn, callback) => (
this.setState(({ stateValue }) => ({
stateValue: typeof updateFn === 'function'
? updateFn(stateValue)
: updateFn,
}), callback)
);
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 withStateSelector;

View File

@ -78,7 +78,9 @@
"react-addons-test-utils": "^15.1.0",
"react-dom": "^15.1.0",
"react-motion": "^0.4.4",
"react-router": "^2.4.1",
"recompose": "^0.19.0",
"reselect": "^2.5.1",
"rimraf": "^2.4.3",
"sass-loader": "^3.2.0",
"style-loader": "^0.13.1",