From 4d0168a386206ba89578d745a2e0d05c53ce61de Mon Sep 17 00:00:00 2001 From: Tom Chen Date: Sun, 26 Apr 2015 15:09:52 +0800 Subject: [PATCH 1/7] chore(package.json): upgrade react and hot-loader version --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f404c759..c699ccc1 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "jquery": "^2.1.1", "jsx-loader": "^0.12.0", "raw-loader": "^0.5.1", - "react-hot-loader": "^0.5.0", + "react-hot-loader": "^1.2.5", "react-prism": "^1.0.0", "react-toastr": "^1.1.2", "react-tools": "^0.12.0", @@ -71,7 +71,7 @@ "webpack-dev-server": "^1.6.5" }, "dependencies": { - "react": "^0.12.0", + "react": "^0.13.0", "deep-equal": "^0.2.1" }, "jest": { From 1516699617da603dd9dc7e51528a3dd827470617 Mon Sep 17 00:00:00 2001 From: Tom Chen Date: Sun, 26 Apr 2015 15:10:23 +0800 Subject: [PATCH 2/7] fix(package.json): add peerDependencies --- package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/package.json b/package.json index c699ccc1..395ff6db 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,10 @@ "react": "^0.13.0", "deep-equal": "^0.2.1" }, + "peerDependencies": { + "react": "^0.13.0", + "deep-equal": "^0.2.1" + }, "jest": { "scriptPreprocessor": "/test/__preprocessor__.js", "testPathDirs": [ From 56a5d4ccb754262fdbd82f9f37cbd7a00fa3ca43 Mon Sep 17 00:00:00 2001 From: Tom Chen Date: Sun, 26 Apr 2015 15:15:58 +0800 Subject: [PATCH 3/7] chore(.editorConfig): add EditorConfig --- .editorConfig | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .editorConfig diff --git a/.editorConfig b/.editorConfig new file mode 100644 index 00000000..f683d7fa --- /dev/null +++ b/.editorConfig @@ -0,0 +1,33 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true + +# Matches multiple files with brace expansion notation +# Set default charset +[*.{js,py}] +charset = utf-8 + +# 4 space indentation +[*.py] +indent_style = space +indent_size = 4 + +# Tab indentation (no size specified) +[Makefile] +indent_style = tab + +# Indentation override for all JS under lib directory +[*.js] +indent_style = space +indent_size = 2 + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 From 336389931fec7ebc3c4da2f8e9b311a86e8e0f9c Mon Sep 17 00:00:00 2001 From: Tom Chen Date: Sun, 26 Apr 2015 15:21:57 +0800 Subject: [PATCH 4/7] chore(package.json): use babel to build --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 395ff6db..20c039f9 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "description": "React.js Google Maps integration component", "main": "lib/index.js", "scripts": { - "build": "jsx --harmony ./src ./lib", + "build": "rm -rf ./lib && babel ./src --out-dir ./lib", "build_client": "webpack -p --config ./client/webpack.config.js", "test": "jest", "dev_install": "npm install && npm dedupe && bower install", @@ -51,6 +51,7 @@ }, "homepage": "https://github.com/tomchentw/react-google-maps", "devDependencies": { + "babel": "^5.1.13", "bower": "^1.3.9", "css-loader": "^0.9.0", "expose-loader": "^0.5.3", @@ -63,7 +64,6 @@ "react-hot-loader": "^1.2.5", "react-prism": "^1.0.0", "react-toastr": "^1.1.2", - "react-tools": "^0.12.0", "sass-loader": "^0.3.0", "style-loader": "^0.8.1", "tomchentw-npm-dev": "^1.1.0", From 01df22c2d0f19455426ec9d459014b24f0160c49 Mon Sep 17 00:00:00 2001 From: Tom Chen Date: Mon, 27 Apr 2015 09:28:53 +0800 Subject: [PATCH 5/7] chore(package.json): switch build tool to babel --- package.json | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 20c039f9..cde10990 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "description": "React.js Google Maps integration component", "main": "lib/index.js", "scripts": { - "build": "rm -rf ./lib && babel ./src --out-dir ./lib", + "build": "rm -rf ./lib && babel --stage=1 ./src --out-dir ./lib", "build_client": "webpack -p --config ./client/webpack.config.js", "test": "jest", "dev_install": "npm install && npm dedupe && bower install", @@ -52,6 +52,9 @@ "homepage": "https://github.com/tomchentw/react-google-maps", "devDependencies": { "babel": "^5.1.13", + "babel-core": "^5.1.13", + "babel-jest": "^5.0.1", + "babel-loader": "^5.0.0", "bower": "^1.3.9", "css-loader": "^0.9.0", "expose-loader": "^0.5.3", @@ -71,15 +74,13 @@ "webpack-dev-server": "^1.6.5" }, "dependencies": { - "react": "^0.13.0", - "deep-equal": "^0.2.1" + "react": "^0.13.0" }, "peerDependencies": { - "react": "^0.13.0", - "deep-equal": "^0.2.1" + "react": "^0.13.0" }, "jest": { - "scriptPreprocessor": "/test/__preprocessor__.js", + "scriptPreprocessor": "/node_modules/babel-jest", "testPathDirs": [ "/src" ], From a525d5f2a074506ad0a9a50fd58d77ecfc4318f9 Mon Sep 17 00:00:00 2001 From: Tom Chen Date: Mon, 27 Apr 2015 12:35:29 +0800 Subject: [PATCH 6/7] feat(src): rewrite with new API * remove the usage of React undocumented context API * embrace es6 with babel --- src/Circle.js | 13 ++ src/DirectionsRenderer.js | 14 ++ src/GoogleMaps.js | 108 ++++++++++++++ src/InfoWindow.js | 34 +++++ src/Map.js | 137 ------------------ src/Marker.js | 43 ++++++ src/Polygon.js | 14 ++ src/Polyline.js | 14 ++ src/__tests__/Map-test.js | 35 ----- src/__tests__/index-test.js | 14 -- ...assign_event_map_to_prop_types_and_spec.js | 14 -- src/helpers/create_child_component.js | 107 -------------- src/helpers/expose_getters_from.js | 9 -- src/helpers/to_event_map.js | 22 --- src/index.js | 59 +++----- src/internals/BASIC_EVENT_NAMES.js | 5 + src/internals/EventComponent.js | 59 ++++++++ src/internals/SimpleChildComponent.js | 64 ++++++++ src/internals/createRegisterEvents.js | 48 ++++++ src/internals/exposeGetters.js | 9 ++ src/mixins/EventBindingMixin.js | 27 ---- src/mixins/GoogleMapsMixin.js | 59 -------- 22 files changed, 443 insertions(+), 465 deletions(-) create mode 100644 src/Circle.js create mode 100644 src/DirectionsRenderer.js create mode 100644 src/GoogleMaps.js create mode 100644 src/InfoWindow.js delete mode 100644 src/Map.js create mode 100644 src/Marker.js create mode 100644 src/Polygon.js create mode 100644 src/Polyline.js delete mode 100644 src/__tests__/Map-test.js delete mode 100644 src/__tests__/index-test.js delete mode 100644 src/helpers/assign_event_map_to_prop_types_and_spec.js delete mode 100644 src/helpers/create_child_component.js delete mode 100644 src/helpers/expose_getters_from.js delete mode 100644 src/helpers/to_event_map.js create mode 100644 src/internals/BASIC_EVENT_NAMES.js create mode 100644 src/internals/EventComponent.js create mode 100644 src/internals/SimpleChildComponent.js create mode 100644 src/internals/createRegisterEvents.js create mode 100644 src/internals/exposeGetters.js delete mode 100644 src/mixins/EventBindingMixin.js delete mode 100644 src/mixins/GoogleMapsMixin.js diff --git a/src/Circle.js b/src/Circle.js new file mode 100644 index 00000000..62575c19 --- /dev/null +++ b/src/Circle.js @@ -0,0 +1,13 @@ +import SimpleChildComponent from "./internals/SimpleChildComponent"; +import createRegisterEvents from "./internals/createRegisterEvents"; + +class Circle extends SimpleChildComponent { +} + +Circle._GoogleMapsClassName = "Circle"; + +Circle._registerEvents = createRegisterEvents( + "center_changed click dblclick drag dragend dragstart mousedown mousemove mouseout mouseover mouseup radius_changed rightclick" +); + +export default Circle; diff --git a/src/DirectionsRenderer.js b/src/DirectionsRenderer.js new file mode 100644 index 00000000..ae558098 --- /dev/null +++ b/src/DirectionsRenderer.js @@ -0,0 +1,14 @@ +import SimpleChildComponent from "./internals/SimpleChildComponent"; +import createRegisterEvents from "./internals/createRegisterEvents"; +import BASIC_EVENT_NAMES from "./internals/BASIC_EVENT_NAMES"; + +class DirectionsRenderer extends SimpleChildComponent { +} + +DirectionsRenderer._GoogleMapsClassName = "DirectionsRenderer"; + +DirectionsRenderer._registerEvents = createRegisterEvents( + BASIC_EVENT_NAMES +); + +export default DirectionsRenderer; diff --git a/src/GoogleMaps.js b/src/GoogleMaps.js new file mode 100644 index 00000000..224de701 --- /dev/null +++ b/src/GoogleMaps.js @@ -0,0 +1,108 @@ +import React from "react"; + +import EventComponent from "./internals/EventComponent"; +import exposeGetters from "./internals/exposeGetters"; +import createRegisterEvents from "./internals/createRegisterEvents"; + +const {PropTypes} = React; + +class GoogleMaps extends EventComponent { + /* + * Some public API we'd like to expose + */ + panBy (x, y) { + const {instance} = this.state; + if (instance) { + instance.panBy(x, y); + } + } + + panTo (latLng) { + const {instance} = this.state; + if (instance) { + instance.panTo(latLng); + } + } + + panToBounds (latLngBounds) { + const {instance} = this.state; + if (instance) { + instance.panToBounds(latLngBounds); + } + } + + fitBounds (latLngBounds) { + const {instance} = this.state; + if (instance) { + instance.fitBounds(latLngBounds); + } + } + /* + * Internals + */ + constructor (props) { + super(props); + this.state = {}; + } + + _createOrUpdateInstance () { + const {props} = this; + if (!props.googleMapsApi) { + return; + } + // googleMapsApi can be async loaded + const {containerProps, googleMapsApi, key, ref, ...googleMapsConfig} = props; + var {instance} = this.state; + + if (instance) { + instance.setOptions(googleMapsConfig); + } else { + const GoogleMapsClass = googleMapsApi.Map; + instance = new GoogleMapsClass( + React.findDOMNode(this.refs.googleMaps), + googleMapsConfig + ); + exposeGetters(this, GoogleMapsClass.prototype, instance); + + this.setState({instance}); + } + return instance; + } + + render () { + const {props} = this; + + return ( +
+
+ {this._render_children_()} +
+ ); + } + + _render_children_ () { + const extraProps = { + googleMapsApi: this.props.googleMapsApi, + map: this.state.instance, + }; + + return React.Children.map(this.props.children, (child) => { + if (child && child.type) { + child = React.cloneElement(child, extraProps); + } + return child; + }); + } +} + +GoogleMaps.propTypes = { + ...EventComponent.propTypes, + containerProps: PropTypes.object.isRequired, + mapProps: PropTypes.object.isRequired, +}; + +GoogleMaps._registerEvents = createRegisterEvents( + "bounds_changed center_changed click dblclick drag dragend dragstart heading_changed idle maptypeid_changed mousemove mouseout mouseover projection_changed resize rightclick tilesloaded tilt_changed zoom_changed" +); + +export default GoogleMaps; diff --git a/src/InfoWindow.js b/src/InfoWindow.js new file mode 100644 index 00000000..8e5c4b3c --- /dev/null +++ b/src/InfoWindow.js @@ -0,0 +1,34 @@ +import React from "react"; + +import SimpleChildComponent from "./internals/SimpleChildComponent"; +import createRegisterEvents from "./internals/createRegisterEvents"; + +const {PropTypes} = React; + +class InfoWindow extends SimpleChildComponent { + + _createOrUpdateInstance () { + const instance = super._createOrUpdateInstance(); + if (instance) { + instance.open( + this.props.map, + this.props.anchor + ); + } + return instance; + } + +} + +InfoWindow.propTypes = { + ...SimpleChildComponent.propTypes, + anchor: PropTypes.object, +}; + +InfoWindow._GoogleMapsClassName = "InfoWindow"; + +InfoWindow._registerEvents = createRegisterEvents( + "closeclick content_changed domready position_changed zindex_changed" +); + +export default InfoWindow; diff --git a/src/Map.js b/src/Map.js deleted file mode 100644 index 26cf9b33..00000000 --- a/src/Map.js +++ /dev/null @@ -1,137 +0,0 @@ -"use strict"; -var React = require("react/addons"), - deepEqual = require("deep-equal"), - - expose_getters_from = require("./helpers/expose_getters_from"), - to_event_map = require("./helpers/to_event_map"), - assign_event_map_to_prop_types_and_spec = require("./helpers/assign_event_map_to_prop_types_and_spec"), - EventBindingMixin = require("./mixins/EventBindingMixin"), - - EVENT_NAMES = "bounds_changed center_changed click dblclick drag dragend dragstart heading_changed idle maptypeid_changed mousemove mouseout mouseover projection_changed resize rightclick tilesloaded tilt_changed zoom_changed", - EVENT_MAP = to_event_map(EVENT_NAMES), - - MapSpec, - MapPropTypes; - -function ensure_map_created (component, createdCallback, createFactory) { - var {context} = component, - map = context.getMap(), - noMap = !map; - - if (noMap && context.getApi() && createFactory) { - map = createFactory(component, context); - } - if (map) { - createdCallback(map); - if (noMap) { - component.setState({_initialized: true}); - } - } -} - -function create_map (component, context) { - var {Map} = context.getApi(), - map = new Map( - component.refs.mapCanvas.getDOMNode(), - component.props - ); - expose_getters_from(component, Map.prototype, map); - return context._set_map(map); -} - -MapPropTypes = { - -}; - -MapSpec = { - displayName: "Map", - - mixins: [EventBindingMixin(EVENT_MAP)], - - propTypes: MapPropTypes, - - contextTypes: { - getMap: React.PropTypes.func, - getApi: React.PropTypes.func, - hasMap: React.PropTypes.func, - _set_map: React.PropTypes.func - }, - - /* - * Some public API we'd like to expose - */ - panBy (x, y) { - ensure_map_created(this, (map) => { - map.panBy(x, y); - }); - }, - - panTo (latLng) { - ensure_map_created(this, (map) => { - map.panTo(latLng); - }); - }, - - panToBounds (latLngBounds) { - ensure_map_created(this, (map) => { - map.panToBounds(latLngBounds); - }); - }, - - fitBounds (latLngBounds) { - ensure_map_created(this, (map) => { - map.fitBounds(latLngBounds); - }); - }, - - getInitialState () { - return { - /* [null, false, true] => ["init", "api loaded", "done"] */ - _initialized: null, - }; - }, - - shouldComponentUpdate(nextProps, nextState) { - return !deepEqual(nextProps, this.props) || nextState._initialized !== this.state._initialized; - }, - - componentDidMount () { - ensure_map_created(this, this.add_listeners, create_map); - }, - - componentWillReceiveProps (nextProps, nextContext) { - if (null == this.state._initialized && nextContext.getApi()) { - this.setState({ - _initialized: false, - }); - } - }, - - componentDidUpdate () { - ensure_map_created(this, (map) => { - map.setOptions(this.props); - this.add_listeners(map); - }, create_map); - }, - - componentWillUnmount () { - ensure_map_created(this, (map) => { - this.clear_listeners(map); - this.context._set_map(null); - }); - }, - - render () { - return this._render(this.props, this.state); - }, - - _render (props, state) { - return
-
; - } -}; - -assign_event_map_to_prop_types_and_spec(EVENT_MAP, MapPropTypes, MapSpec); - -module.exports = React.createClass(MapSpec); diff --git a/src/Marker.js b/src/Marker.js new file mode 100644 index 00000000..865edd15 --- /dev/null +++ b/src/Marker.js @@ -0,0 +1,43 @@ +import React from "react"; + +import SimpleChildComponent from "./internals/SimpleChildComponent"; +import createRegisterEvents from "./internals/createRegisterEvents"; + +import InfoWindow from "./InfoWindow"; + +class Marker extends SimpleChildComponent { + + render () { + const {props} = this; + + return ( + + ); + } + + _render_potential_info_windows_ () { + const extraProps = { + googleMapsApi: this.props.googleMapsApi, + map: this.props.map, + anchor: this.state.instance, + }; + + return React.Children.map(this.props.children, (child) => { + if (React.isValidElement(child) && child.type === InfoWindow) { + child = React.cloneElement(child, extraProps); + } + return child; + }, this); + } + +} + +Marker._GoogleMapsClassName = "Marker"; + +Marker._registerEvents = createRegisterEvents( + "animation_changed click clickable_changed cursor_changed dblclick drag dragend draggable_changed dragstart flat_changed icon_changed mousedown mouseout mouseover mouseup position_changed rightclick shape_changed title_changed visible_changed zindex_changed" +); + +export default Marker; diff --git a/src/Polygon.js b/src/Polygon.js new file mode 100644 index 00000000..9ec59ced --- /dev/null +++ b/src/Polygon.js @@ -0,0 +1,14 @@ +import SimpleChildComponent from "./internals/SimpleChildComponent"; +import createRegisterEvents from "./internals/createRegisterEvents"; +import BASIC_EVENT_NAMES from "./internals/BASIC_EVENT_NAMES"; + +class Polygon extends SimpleChildComponent { +} + +Polygon._GoogleMapsClassName = "Polygon"; + +Polygon._registerEvents = createRegisterEvents( + BASIC_EVENT_NAMES +); + +export default Polygon; diff --git a/src/Polyline.js b/src/Polyline.js new file mode 100644 index 00000000..587fcd24 --- /dev/null +++ b/src/Polyline.js @@ -0,0 +1,14 @@ +import SimpleChildComponent from "./internals/SimpleChildComponent"; +import createRegisterEvents from "./internals/createRegisterEvents"; +import BASIC_EVENT_NAMES from "./internals/BASIC_EVENT_NAMES"; + +class Polyline extends SimpleChildComponent { +} + +Polyline._GoogleMapsClassName = "Polyline"; + +Polyline._registerEvents = createRegisterEvents( + BASIC_EVENT_NAMES +); + +export default Polyline; diff --git a/src/__tests__/Map-test.js b/src/__tests__/Map-test.js deleted file mode 100644 index 85643832..00000000 --- a/src/__tests__/Map-test.js +++ /dev/null @@ -1,35 +0,0 @@ -"use strict"; - -jest.dontMock("../Map.js"); -jest.dontMock("../mixins/GoogleMapsMixin.js"); -jest.dontMock("../mixins/EventBindingMixin.js"); - -describe("Map", function() { - it("should render a canvas", function() { - var React = require("react/addons"), - Map = require("../Map.js"), - {TestUtils} = React.addons, - MockContext, - map, - divCanvas; - - MockContext = React.createClass({ - mixins: [require("../mixins/GoogleMapsMixin")], - - render () { - return ; - } - }); - - map = TestUtils.findRenderedComponentWithType( - TestUtils.renderIntoDocument(), - Map - ); - - divCanvas = TestUtils.findRenderedDOMComponentWithTag( - map, "div" - ); - - expect(divCanvas.getDOMNode().getAttribute("style")).toEqual(null); - }); -}); diff --git a/src/__tests__/index-test.js b/src/__tests__/index-test.js deleted file mode 100644 index 72c0b3bd..00000000 --- a/src/__tests__/index-test.js +++ /dev/null @@ -1,14 +0,0 @@ -"use strict"; - -jest.dontMock("../index.js"); -jest.dontMock("../helpers/create_child_component"); -describe("index", function() { - it("should export components", function() { - var index = require("../index.js"); - - expect(index.GoogleMapsMixin).toBeDefined(); - expect(index.Map).toBeDefined(); - expect(index.Marker).toBeDefined(); - expect(index.Circle).toBeDefined(); - }); -}); diff --git a/src/helpers/assign_event_map_to_prop_types_and_spec.js b/src/helpers/assign_event_map_to_prop_types_and_spec.js deleted file mode 100644 index 4259b4ed..00000000 --- a/src/helpers/assign_event_map_to_prop_types_and_spec.js +++ /dev/null @@ -1,14 +0,0 @@ -"use strict"; -var React = require("react/addons"), - {PropTypes} = React; - -function noop () {} - -module.exports = function (eventMap, propTypes, spec) { - eventMap.__keys__.forEach(function (eventName) { - propTypes[eventName] = PropTypes.func; - spec[eventName] = function (...args) { - (this.props[eventName] || noop).apply(null, args); - }; - }); -}; diff --git a/src/helpers/create_child_component.js b/src/helpers/create_child_component.js deleted file mode 100644 index cbb780bd..00000000 --- a/src/helpers/create_child_component.js +++ /dev/null @@ -1,107 +0,0 @@ -"use strict"; -var React = require("react/addons"), - - expose_getters_from = require("./expose_getters_from"), - to_event_map = require("../helpers/to_event_map"), - assign_event_map_to_prop_types_and_spec = require("../helpers/assign_event_map_to_prop_types_and_spec"), - EventBindingMixin = require("../mixins/EventBindingMixin"); - -function ensure_instance_created (component, createdCallback, createFactory) { - var {context} = component, - {_instance} = component.state, - noInstance = !_instance; - - if (noInstance && context.getApi() && context.hasMap() && createFactory) { - _instance = createFactory(component, context); - } - if (_instance) { - createdCallback(_instance); - if (noInstance) { - component.setState({_instance}); - } - } -} - -function setMapToInstance (component, instance) { - instance.setMap(component.context.getMap()); -} - -module.exports = (childName, eventNames, _created_callback) => { - var createdCallback = _created_callback || setMapToInstance, - - EVENT_MAP = to_event_map(eventNames), - - ChildPropTypes, - ChildSpec; - - function create_instance (component, context) { - var ChildClass = context.getApi()[childName], - instance = new ChildClass(component.props); - - expose_getters_from(component, ChildClass.prototype, instance); - return instance; - } - - ChildPropTypes = { - - }; - - /* - * shouldComponentUpdate: true. Always rerender for child - */ - ChildSpec = { - displayName: childName, - - mixins: [EventBindingMixin(EVENT_MAP)], - - contextTypes: { - getMap: React.PropTypes.func, - getApi: React.PropTypes.func, - hasMap: React.PropTypes.func, - getInstanceByRef: React.PropTypes.func - }, - - propTypes: ChildPropTypes, - - getInitialState () { - return { - _instance: null - }; - }, - - componentDidMount () { - ensure_instance_created(this, (instance) => { - this.add_listeners(instance); - createdCallback(this, instance); - }, create_instance); - }, - - componentDidUpdate () { - ensure_instance_created(this, (instance) => { - instance.setOptions(this.props); - this.add_listeners(instance); - createdCallback(this, instance); - }, create_instance); - }, - - componentWillUnmount () { - ensure_instance_created(this, (instance) => { - this.clear_listeners(instance); - instance.setMap(null); - }); - }, - - render () { - return this._render(this.props, this.state); - }, - - _render (props, state) { - return null; - } - }; - - assign_event_map_to_prop_types_and_spec(EVENT_MAP, ChildPropTypes, ChildSpec); - - return React.createClass(ChildSpec); -}; - diff --git a/src/helpers/expose_getters_from.js b/src/helpers/expose_getters_from.js deleted file mode 100644 index 2d67b7a3..00000000 --- a/src/helpers/expose_getters_from.js +++ /dev/null @@ -1,9 +0,0 @@ -"use strict"; - -module.exports = function (component, prototype, instance) { - for (var key in prototype) { - if (key.match(/^get/) && !key.match(/Map$/)) { - component[key] = instance[key].bind(instance); - } - } -}; diff --git a/src/helpers/to_event_map.js b/src/helpers/to_event_map.js deleted file mode 100644 index a91a1fb9..00000000 --- a/src/helpers/to_event_map.js +++ /dev/null @@ -1,22 +0,0 @@ -"use strict"; - -module.exports = (event_names) => { - return event_names.split(" ").reduce(listToMap, {__keys__: []}); -}; - -function listToMap (map, event_name, index, list) { - var eventName = toEventName(event_name); - map.__keys__.push(eventName); - map[eventName] = event_name; - return map; -} - -function toEventName(name) { - return `on${ name - .replace(/^(.)/, groupToUpperCase) - .replace(/_(.)/g, groupToUpperCase) }`; -} - -function groupToUpperCase (match, group) { - return group.toUpperCase(); -} diff --git a/src/index.js b/src/index.js index 44e2bad5..ea48a017 100644 --- a/src/index.js +++ b/src/index.js @@ -1,44 +1,21 @@ -"use strict"; -var create_child_component = require("./helpers/create_child_component"), +import GoogleMaps from "./GoogleMaps"; - BASIC_EVENT_NAMES = "click dblclick drag dragend dragstart mousedown mousemove mouseout mouseover mouseup rightclick"; +import Circle from "./Circle"; +import DirectionsRenderer from "./DirectionsRenderer"; +import InfoWindow from "./InfoWindow"; +import Marker from "./Marker"; +import Polygon from "./Polygon"; +import Polyline from "./Polyline"; -exports.GoogleMapsMixin = require("./mixins/GoogleMapsMixin"); -exports.Map = require("./Map"); +export { + GoogleMaps, +}; -[ - [ - "Circle", - "center_changed click dblclick drag dragend dragstart mousedown mousemove mouseout mouseover mouseup radius_changed rightclick" - ], - [ - "Marker", - "animation_changed click clickable_changed cursor_changed dblclick drag dragend draggable_changed dragstart flat_changed icon_changed mousedown mouseout mouseover mouseup position_changed rightclick shape_changed title_changed visible_changed zindex_changed", - ], - [ - "Polyline", - BASIC_EVENT_NAMES, - ], - [ - "Polygon", - BASIC_EVENT_NAMES, - ], - [ - "InfoWindow", - "closeclick content_changed domready position_changed zindex_changed", - (component, infoWindow) => { - var {context} = component, - {owner} = component.props; - infoWindow.open( - context.getMap(), - owner ? context.getInstanceByRef(owner) : undefined - ); - } - ], - [ - "DirectionsRenderer", - BASIC_EVENT_NAMES, - ], -].forEach((args) => { - exports[args[0]] = create_child_component.apply(null, args); -}); +export { + Circle, + DirectionsRenderer, + InfoWindow, + Marker, + Polygon, + Polyline, +}; diff --git a/src/internals/BASIC_EVENT_NAMES.js b/src/internals/BASIC_EVENT_NAMES.js new file mode 100644 index 00000000..02f5f796 --- /dev/null +++ b/src/internals/BASIC_EVENT_NAMES.js @@ -0,0 +1,5 @@ +const BASIC_EVENT_NAMES = ( + "click dblclick drag dragend dragstart mousedown mousemove mouseout mouseover mouseup rightclick" +); + +export default BASIC_EVENT_NAMES; diff --git a/src/internals/EventComponent.js b/src/internals/EventComponent.js new file mode 100644 index 00000000..6f06fcbc --- /dev/null +++ b/src/internals/EventComponent.js @@ -0,0 +1,59 @@ +import React from "react"; + +const {PropTypes} = React; + +function noop () { +} + +class EventComponent extends React.Component { + /* Contract + * statics: + * _registerEvents: + * member: + * _createOrUpdateInstance + */ + constructor (props) { + super(props); + + this._unregisterEvents = noop; + } + + componentDidMount () { + const instance = this._createOrUpdateInstance(); + if (!instance) { + return; + } + this._add_listeners_(instance); + } + + componentDidUpdate () { + const instance = this._createOrUpdateInstance(); + if (!instance) { + return; + } + this._unregisterEvents(); + this._add_listeners_(instance); + } + + componentWillUnmount () { + const {instance} = this.state; + if (!instance) { + return; + } + this._unregisterEvents(); + } + + _add_listeners_ (instance) { + const {event} = this.props.googleMapsApi; + const {_registerEvents} = this.constructor; + + this._unregisterEvents = _registerEvents(event, instance, this.props); + } + +} + +EventComponent.propTypes = { + googleMapsApi: PropTypes.object, +}; + +export default EventComponent; diff --git a/src/internals/SimpleChildComponent.js b/src/internals/SimpleChildComponent.js new file mode 100644 index 00000000..559238f2 --- /dev/null +++ b/src/internals/SimpleChildComponent.js @@ -0,0 +1,64 @@ +import React from "react"; + +import EventComponent from "./EventComponent"; +import exposeGetters from "./exposeGetters"; + +const {PropTypes} = React; + +class SimpleChildComponent extends EventComponent { + /* Contract + * statics: + * _GoogleMapsClassName: + * state: + * instance + */ + constructor (props) { + super(props); + this.state = {}; + } + + _createOrUpdateInstance () { + const {props} = this; + if (!props.googleMapsApi || !props.map) { + return; + } + const {googleMapsApi, key, ref, ...googleMapsConfig} = props; + var {instance} = this.state; + + if (instance) { + if (googleMapsConfig.map !== instance.getMap()) { + instance.setMap(googleMapsConfig.map); + } + instance.setOptions(googleMapsConfig); + } else { + const GoogleMapsClass = googleMapsApi[this.constructor._GoogleMapsClassName]; + instance = new GoogleMapsClass(googleMapsConfig); + + exposeGetters(this, GoogleMapsClass.prototype, instance); + this.setState({instance}); + } + return instance; + } + + componentWillUnmount () { + super.componentWillUnmount(); + const {instance} = this.state; + if (instance) { + instance.setMap(null); + } + } + + render () { + const {props} = this; + + return
; } }); -bodyComponent = React.render(, document.body); +const bodyComponent = React.render(, document.body); diff --git a/client/styles/index.scss b/client/styles/index.scss index 72e5cd02..312dc8cf 100644 --- a/client/styles/index.scss +++ b/client/styles/index.scss @@ -1,4 +1,9 @@ @import url(//fonts.googleapis.com/css?family=Lato:400,900); + +@import url(~github-fork-ribbon-css/gh-fork-ribbon.css); +@import url(~prism/themes/prism.css); +@import url(~toastr/toastr.min.css); + @import "./bootstrap_variables"; @import "./bootstrap_override"; diff --git a/client/webpack.config.js b/client/webpack.config.js index 2924a588..ce1dad24 100644 --- a/client/webpack.config.js +++ b/client/webpack.config.js @@ -8,7 +8,7 @@ var Path = require("path"), webpackConfig, IS_PRODUCTION = "production" === process.env.NODE_ENV, - JSX_WITH_HOT_LOEADERS = ["react-hot-loader", "jsx-loader?harmony"], + JSX_WITH_HOT_LOEADERS = ["react-hot-loader", "babel-loader?stage=1"], CSS_LOADER = "style-loader!css-loader?root=../", SCSS_LOADER = CSS_LOADER + "!sass-loader?" + JSON.stringify({ includePaths: [ @@ -33,8 +33,7 @@ webpackConfig = module.exports = { }, module: { loaders: [ - { test: require.resolve("react/addons"), loader: "expose-loader?React" }, - { test: /\.js(x?)$/, loaders: JSX_WITH_HOT_LOEADERS }, + { test: /\.js(x?)$/, exclude: /node_modules/, loaders: JSX_WITH_HOT_LOEADERS }, { test: /\.jpg$/, loader: "file-loader" }, { test: /\.css$/, loader: CSS_LOADER }, { test: /\.scss$/, loader: SCSS_LOADER }, diff --git a/package.json b/package.json index cde10990..0a12be65 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "babel-jest": "^5.0.1", "babel-loader": "^5.0.0", "bower": "^1.3.9", + "classnames": "^1.2.1", "css-loader": "^0.9.0", "expose-loader": "^0.5.3", "file-loader": "^0.8.1", @@ -65,8 +66,8 @@ "jsx-loader": "^0.12.0", "raw-loader": "^0.5.1", "react-hot-loader": "^1.2.5", - "react-prism": "^1.0.0", - "react-toastr": "^1.1.2", + "react-prism": "^1.3.0", + "react-toastr": "^1.2.0", "sass-loader": "^0.3.0", "style-loader": "^0.8.1", "tomchentw-npm-dev": "^1.1.0",