From 9df9ffa7cf65252bd419b25fdf54040d6fa82afb Mon Sep 17 00:00:00 2001 From: WhizSid Date: Thu, 28 Nov 2019 16:46:51 +0530 Subject: [PATCH 1/2] Search box implemented --- .babelrc | 3 ++ lib/GoogleMap.js | 30 ++++++++------ lib/SearchBox.js | 101 ++++++++++++++++++++++++++++++++++++++++++++++ lib/index.js | 79 ++++++++++++++++++++++++++++-------- package.json | 13 +++--- src/GoogleMap.jsx | 43 +++++++++++--------- src/Search.svg | 4 ++ src/SearchBox.jsx | 63 +++++++++++++++++++++++++++++ src/index.js | 66 +++++++++++++++++++++++------- webpack.config.js | 17 ++++++++ 10 files changed, 352 insertions(+), 67 deletions(-) create mode 100644 lib/SearchBox.js create mode 100644 src/Search.svg create mode 100644 src/SearchBox.jsx diff --git a/.babelrc b/.babelrc index d146300..696db23 100644 --- a/.babelrc +++ b/.babelrc @@ -2,6 +2,9 @@ "presets": [ "react", "env" + ], + "plugins": [ + "inline-react-svg" ] } diff --git a/lib/GoogleMap.js b/lib/GoogleMap.js index dfb127d..105aefd 100644 --- a/lib/GoogleMap.js +++ b/lib/GoogleMap.js @@ -1,4 +1,4 @@ -'use strict'; +"use strict"; Object.defineProperty(exports, "__esModule", { value: true @@ -6,26 +6,30 @@ Object.defineProperty(exports, "__esModule", { var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; -var _react = require('react'); +var _react = require("react"); var _react2 = _interopRequireDefault(_react); -var _withGoogleMap = require('react-google-maps/lib/withGoogleMap'); +var _withGoogleMap = require("react-google-maps/lib/withGoogleMap"); var _withGoogleMap2 = _interopRequireDefault(_withGoogleMap); -var _GoogleMap = require('react-google-maps/lib/components/GoogleMap'); +var _GoogleMap = require("react-google-maps/lib/components/GoogleMap"); var _GoogleMap2 = _interopRequireDefault(_GoogleMap); -var _Marker = require('react-google-maps/lib/components/Marker'); +var _Marker = require("react-google-maps/lib/components/Marker"); var _Marker2 = _interopRequireDefault(_Marker); -var _Circle = require('react-google-maps/lib/components/Circle'); +var _Circle = require("react-google-maps/lib/components/Circle"); var _Circle2 = _interopRequireDefault(_Circle); +var _SearchBox = require("./SearchBox"); + +var _SearchBox2 = _interopRequireDefault(_SearchBox); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /* Create map with withGoogleMap HOC */ @@ -39,14 +43,14 @@ var Map = (0, _withGoogleMap2.default)(function (props) { radius = props.radius, circleOptions = props.circleOptions, shouldRecenterMap = props.shouldRecenterMap, - zoom = props.zoom; + zoom = props.zoom, + onChangeInput = props.onChangeInput, + onChangeSuggestion = props.onChangeSuggestion, + address = props.address, + search = props.search; - var circle = radius !== -1 ? _react2.default.createElement(_Circle2.default, { - center: position, - radius: radius, - options: circleOptions - }) : null; + var circle = radius !== -1 ? _react2.default.createElement(_Circle2.default, { center: position, radius: radius, options: circleOptions }) : null; var mapExtraProps = shouldRecenterMap ? { center: position } : {}; return _react2.default.createElement( _GoogleMap2.default, @@ -54,8 +58,10 @@ var Map = (0, _withGoogleMap2.default)(function (props) { onZoomChanged: onZoomChanged, defaultZoom: defaultZoom, defaultCenter: position, + defaultOptions: { mapTypeControlOptions: { position: 12 }, fullscreenControlOptions: { position: 6 } }, zoom: zoom }, mapExtraProps), + _react2.default.createElement(_SearchBox2.default, { onChangeInput: onChangeInput, onChangeSuggestion: onChangeSuggestion, search: search, address: address }), _react2.default.createElement(_Marker2.default, { draggable: true // Allow marker to drag , position: position, diff --git a/lib/SearchBox.js b/lib/SearchBox.js new file mode 100644 index 0000000..55483b1 --- /dev/null +++ b/lib/SearchBox.js @@ -0,0 +1,101 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _react = require("react"); + +var _react2 = _interopRequireDefault(_react); + +var _reactGooglePlacesSuggest = require("react-google-places-suggest"); + +var _reactGooglePlacesSuggest2 = _interopRequireDefault(_reactGooglePlacesSuggest); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var Icon = function Icon(props) { + return _react2.default.createElement( + "svg", + props, + _react2.default.createElement("path", { + fill: "#757575", + d: "M15.5 14h-.79l-.28-.27A6.471 6.471 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" + }), + _react2.default.createElement("path", { + d: "M0 0h24v24H0z", + fill: "none" + }) + ); +}; + +Icon.defaultProps = { + xmlns: "http://www.w3.org/2000/svg", + width: "24", + height: "24", + viewBox: "0 0 24 24" +}; + + +var searchBoxStyles = { + alignItems: "center", + backgroundColor: "rgb(255, 255, 255)", + borderBottomLeftRadius: "4px", + borderBottomRightRadius: "4px", + borderTopLeftRadius: "4px", + borderTopRightRadius: "4px", + boxShadow: "rgba(0, 0, 0, 0.2) 0px 2px 1px -1px, rgba(0, 0, 0, 0.14) 0px 1px 1px 0px, rgba(0, 0, 0, 0.12) 0px 1px 3px 0px", + boxSizing: "border-box", + color: "rgba(0, 0, 0, 0.87)", + display: "flex", + fontFamily: "Roboto, Helvetica, Arial, sans-serif", + fontSize: "14px", + fontWeight: "400", + height: "48px", + letterSpacing: "0.14994px", + lineHeight: "20.02px", + marginTop: "0px", + outline: "none", + paddingBottom: "2px", + paddingLeft: "16px", + paddingRight: "12px", + paddingTop: "2px", + transitionDelay: "0s", + transitionDuration: "0.3s", + transitionProperty: "box-shadow", + transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)", + maxWidth: 310, + minWidth: 280, + WebkitFontSmoothing: "antialiased", + position: "absolute", + top: 8, + right: 8 +}; + +var SearchBox = function SearchBox(props) { + return _react2.default.createElement( + "label", + { htmlFor: "locationPickerSearch", style: searchBoxStyles }, + _react2.default.createElement( + _reactGooglePlacesSuggest2.default, + { + googleMaps: google.maps, + autocompletionRequest: { input: props.search }, + onSelectSuggest: props.onChangeSuggestion + }, + _react2.default.createElement("input", { + type: "text", + id: "locationPickerSearch", + value: props.address, + placeholder: "Search a location", + style: { border: "none", width: "100%", height: 44 }, + onChange: function onChange(e) { + props.onChangeInput(e.target.value); + } + }) + ), + _react2.default.createElement(Icon, null) + ); +}; + +exports.default = SearchBox; \ No newline at end of file diff --git a/lib/index.js b/lib/index.js index 49c8e6d..4e80cd3 100644 --- a/lib/index.js +++ b/lib/index.js @@ -54,10 +54,14 @@ var LocationPicker = function (_Component) { _this.state = { position: props.defaultPosition, - shouldRecenterMap: false + shouldRecenterMap: false, + address: "", + search: "" }; _this.handleMarkerDragEnd = _this.handleMarkerDragEnd.bind(_this); + _this.handleChangeInput = _this.handleChangeInput.bind(_this); + _this.handleChangeSuggestion = _this.handleChangeSuggestion.bind(_this); return _this; } @@ -85,11 +89,16 @@ var LocationPicker = function (_Component) { value: function notify(position, places) { var onChange = this.props.onChange; + var address = places.length > 0 ? places[0].formatted_address : ''; + var location = { position: position, places: places, - address: places.length > 0 ? places[0].formatted_address : '' + address: address }; + + this.setState({ address: address, position: position }); + onChange && onChange(location); } /** @@ -108,7 +117,7 @@ var LocationPicker = function (_Component) { var lat = mouseEvent.latLng.lat(); var lng = mouseEvent.latLng.lng(); var position = { lat: lat, lng: lng }; - this.setState({ position: position, shouldRecenterMap: false }); + this.setState({ shouldRecenterMap: false, search: "" }); this.geocodePosition(position).then(function (places) { _this3.notify(position, places); }).catch(function (err) { @@ -117,6 +126,32 @@ var LocationPicker = function (_Component) { }); } + /** + * Handle search box input change + * @param { string } value + */ + + }, { + key: 'handleChangeInput', + value: function handleChangeInput(value) { + this.setState({ address: value, search: value }); + } + + /** + * Handle change the position by search box + * @param { google.maps.GeocoderResult } suggestion // https://github.com/cedricdelpoux/react-google-places-suggest#props + */ + + }, { + key: 'handleChangeSuggestion', + value: function handleChangeSuggestion(suggestion) { + var position = suggestion.geometry.location; + + this.setState({ shouldRecenterMap: true, search: "" }); + + this.notify({ lat: position.lat(), lng: position.lng() }, [suggestion]); + } + /** * Geocode position to places * @param { Object } position @@ -150,20 +185,30 @@ var LocationPicker = function (_Component) { mapElement = _props.mapElement; var _state = this.state, position = _state.position, - shouldRecenterMap = _state.shouldRecenterMap; - - - return _react2.default.createElement(_GoogleMap2.default, { - containerElement: containerElement, - mapElement: mapElement, - handleMarkerDragEnd: this.handleMarkerDragEnd, - position: position, - circleOptions: circleOptions, - radius: radius, - defaultZoom: zoom, - zoom: zoom, - shouldRecenterMap: shouldRecenterMap - }); + shouldRecenterMap = _state.shouldRecenterMap, + address = _state.address, + search = _state.search; + + + return _react2.default.createElement( + 'div', + { style: { position: "relative" } }, + _react2.default.createElement(_GoogleMap2.default, { + containerElement: containerElement, + mapElement: mapElement, + handleMarkerDragEnd: this.handleMarkerDragEnd, + position: position, + circleOptions: circleOptions, + radius: radius, + defaultZoom: zoom, + zoom: zoom, + shouldRecenterMap: shouldRecenterMap, + onChangeInput: this.handleChangeInput, + onChangeSuggestion: this.handleChangeSuggestion, + address: address, + search: search + }) + ); } }]); diff --git a/package.json b/package.json index 02c6fb3..c50c4ca 100644 --- a/package.json +++ b/package.json @@ -16,21 +16,24 @@ "babel-cli": "^6.26.0", "babel-core": "^6.26.0", "babel-loader": "^7.1.2", + "babel-plugin-inline-react-svg": "^0.5.0", "babel-preset-env": "^1.6.0", "babel-preset-react": "^6.24.1", + "react": "^16.2.0", "react-dom": "^16.2.0", + "react-svg-loader": "^3.0.3", "webpack": "^3.6.0", - "react": "^16.2.0", "webpack-dev-server": "^2.8.2" }, "dependencies": { "prop-types": "^15.6.1", - "react-google-maps": "^9.4.5" + "react-google-maps": "^9.4.5", + "react-google-places-suggest": "^3.9.0" }, "peerDependencies": { - "react": "^15.0.0 || ^16.0.0", - "react-dom": "^15.0.0 || ^16.0.0", "@types/googlemaps": "^3.37.6", - "@types/react": "^16.9.2" + "@types/react": "^16.9.2", + "react": "^15.0.0 || ^16.0.0", + "react-dom": "^15.0.0 || ^16.0.0" } } diff --git a/src/GoogleMap.jsx b/src/GoogleMap.jsx index 98cdb0d..8ad2daf 100644 --- a/src/GoogleMap.jsx +++ b/src/GoogleMap.jsx @@ -1,13 +1,14 @@ -import React from 'react'; -import withGoogleMap from 'react-google-maps/lib/withGoogleMap'; -import GoogleMap from 'react-google-maps/lib/components/GoogleMap'; -import Marker from 'react-google-maps/lib/components/Marker'; -import Circle from 'react-google-maps/lib/components/Circle'; +import React from "react"; +import withGoogleMap from "react-google-maps/lib/withGoogleMap"; +import GoogleMap from "react-google-maps/lib/components/GoogleMap"; +import Marker from "react-google-maps/lib/components/Marker"; +import Circle from "react-google-maps/lib/components/Circle"; +import SearchBox from "./SearchBox"; /* Create map with withGoogleMap HOC */ /* https://github.com/tomchentw/react-google-maps */ -const Map = withGoogleMap((props) => { +const Map = withGoogleMap(props => { const { position, defaultZoom, @@ -16,25 +17,31 @@ const Map = withGoogleMap((props) => { radius, circleOptions, shouldRecenterMap, - zoom + zoom, + onChangeInput, + onChangeSuggestion, + address, + search } = props; - - const circle = (radius !== -1) ? - : null; - const mapExtraProps = shouldRecenterMap ? { center: position }: {}; + + const circle = + radius !== -1 ? ( + + ) : null; + const mapExtraProps = shouldRecenterMap ? { center: position } : {}; return ( - + {/* Map marker */} { /> {/* Circle */} - { circle } + {circle} - ) + ); }); export default Map; diff --git a/src/Search.svg b/src/Search.svg new file mode 100644 index 0000000..6a414f4 --- /dev/null +++ b/src/Search.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/SearchBox.jsx b/src/SearchBox.jsx new file mode 100644 index 0000000..91286e6 --- /dev/null +++ b/src/SearchBox.jsx @@ -0,0 +1,63 @@ +import React from "react"; +import ReactGooglePlacesSuggest from "react-google-places-suggest"; +import Icon from "./Search.svg"; + +const searchBoxStyles = { + alignItems: "center", + backgroundColor: "rgb(255, 255, 255)", + borderBottomLeftRadius: "4px", + borderBottomRightRadius: "4px", + borderTopLeftRadius: "4px", + borderTopRightRadius: "4px", + boxShadow: + "rgba(0, 0, 0, 0.2) 0px 2px 1px -1px, rgba(0, 0, 0, 0.14) 0px 1px 1px 0px, rgba(0, 0, 0, 0.12) 0px 1px 3px 0px", + boxSizing: "border-box", + color: "rgba(0, 0, 0, 0.87)", + display: "flex", + fontFamily: "Roboto, Helvetica, Arial, sans-serif", + fontSize: "14px", + fontWeight: "400", + height: "48px", + letterSpacing: "0.14994px", + lineHeight: "20.02px", + marginTop: "0px", + outline:"none", + paddingBottom: "2px", + paddingLeft: "16px", + paddingRight: "12px", + paddingTop: "2px", + transitionDelay: "0s", + transitionDuration: "0.3s", + transitionProperty: "box-shadow", + transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)", + maxWidth: 310, + minWidth: 280, + WebkitFontSmoothing: "antialiased", + position: "absolute", + top:8, + right:8 +}; + +const SearchBox = (props) => ( + +) + +export default SearchBox; \ No newline at end of file diff --git a/src/index.js b/src/index.js index e64165b..d5dce16 100644 --- a/src/index.js +++ b/src/index.js @@ -28,10 +28,14 @@ class LocationPicker extends Component { this.state = { position: props.defaultPosition, - shouldRecenterMap: false + shouldRecenterMap: false, + address:"", + search:"" }; this.handleMarkerDragEnd = this.handleMarkerDragEnd.bind(this); + this.handleChangeInput = this.handleChangeInput.bind(this); + this.handleChangeSuggestion = this.handleChangeSuggestion.bind(this); } componentWillReceiveProps(nextProps) { @@ -59,11 +63,16 @@ class LocationPicker extends Component { notify(position, places) { const { onChange } = this.props; + const address = places.length > 0 ? places[0].formatted_address : ''; + const location = { position, places, - address: places.length > 0 ? places[0].formatted_address : '' + address }; + + this.setState({address,position}); + onChange && onChange(location); } /** @@ -76,7 +85,7 @@ class LocationPicker extends Component { const lat = mouseEvent.latLng.lat(); const lng = mouseEvent.latLng.lng(); const position = { lat, lng }; - this.setState({ position, shouldRecenterMap: false }); + this.setState({shouldRecenterMap: false, search:"" }); this.geocodePosition(position) .then(places => { this.notify(position, places); @@ -87,6 +96,26 @@ class LocationPicker extends Component { }); } + /** + * Handle search box input change + * @param { string } value + */ + handleChangeInput(value){ + this.setState({address:value,search:value}); + } + + /** + * Handle change the position by search box + * @param { google.maps.GeocoderResult } suggestion // https://github.com/cedricdelpoux/react-google-places-suggest#props + */ + handleChangeSuggestion(suggestion){ + const position = suggestion.geometry.location; + + this.setState({shouldRecenterMap:true,search:""}); + + this.notify({lat:position.lat(),lng: position.lng()},[suggestion]) + } + /** * Geocode position to places * @param { Object } position @@ -116,20 +145,27 @@ class LocationPicker extends Component { mapElement } = this.props; - const { position, shouldRecenterMap } = this.state; + const { position, shouldRecenterMap, address, search } = this.state; + return ( - +
+ +
); } } diff --git a/webpack.config.js b/webpack.config.js index 13e9abe..1075705 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -12,6 +12,23 @@ const config = { test: /\.jsx?$/, exclude: /node_modules/, use: 'babel-loader' + }, + { + test: /\.svg$/, + use: [ + "babel-loader", + { + loader: "react-svg-loader", + options: { + svgo: { + plugins: [ + { removeTitle: false } + ], + floatPrecision: 2 + } + } + } + ] } ] }, From 08cc79e0f08d5fd25bef0b0906093cad7a066b99 Mon Sep 17 00:00:00 2001 From: WhizSid Date: Thu, 28 Nov 2019 20:47:32 +0530 Subject: [PATCH 2/2] Suggest box as a custom control --- lib/CustomControl.js | 78 +++++++++++++++++++++++++ lib/GoogleMap.js | 131 ++++++++++++++++++++++++++++++------------ lib/SearchBox.js | 16 +++--- lib/index.js | 34 +++++------ src/CustomControl.jsx | 48 ++++++++++++++++ src/GoogleMap.jsx | 125 ++++++++++++++++++++++++---------------- src/SearchBox.jsx | 7 +-- src/index.js | 2 - 8 files changed, 321 insertions(+), 120 deletions(-) create mode 100644 lib/CustomControl.js create mode 100644 src/CustomControl.jsx diff --git a/lib/CustomControl.js b/lib/CustomControl.js new file mode 100644 index 0000000..1de8cfe --- /dev/null +++ b/lib/CustomControl.js @@ -0,0 +1,78 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _react = require("react"); + +var _reactDom = require("react-dom"); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +/** + * This Component for add custom control to map + * (map.controls[position].push(component)) + * NOTE: + * Can ref to map through context in constructor (or this.context expect contructor) + * User constructor to add div and render will createPortal + */ +var MapControl = function (_Component) { + _inherits(MapControl, _Component); + + function MapControl(props, context) { + _classCallCheck(this, MapControl); + + var _this = _possibleConstructorReturn(this, (MapControl.__proto__ || Object.getPrototypeOf(MapControl)).call(this, props)); + + _this.state = { + mounted: false + }; + return _this; + } + + _createClass(MapControl, [{ + key: "componentDidUpdate", + value: function componentDidUpdate() { + if (this.props.map && !this.map) { + this.map = this.props.map; + this.controlDiv = document.createElement("div"); + this.divIndex = this.map.controls[this.props.position].length; + this.map.controls[this.props.position].push(this.controlDiv); + + this.setState({ + mounted: true + }); + } + } + }, { + key: "componentWillUnmount", + value: function componentWillUnmount() { + this.map.controls[this.props.position].removeAt(this.divIndex); + } + }, { + key: "render", + value: function render() { + var className = this.props.className; + + + if (!this.map) { + return null; + } + + className && this.controlDiv.classList.add(className); + + return (0, _reactDom.createPortal)(this.props.children, this.controlDiv); + } + }]); + + return MapControl; +}(_react.Component); + +exports.default = MapControl; \ No newline at end of file diff --git a/lib/GoogleMap.js b/lib/GoogleMap.js index 105aefd..de3f14a 100644 --- a/lib/GoogleMap.js +++ b/lib/GoogleMap.js @@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", { var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + var _react = require("react"); var _react2 = _interopRequireDefault(_react); @@ -30,45 +32,100 @@ var _SearchBox = require("./SearchBox"); var _SearchBox2 = _interopRequireDefault(_SearchBox); +var _CustomControl = require("./CustomControl"); + +var _CustomControl2 = _interopRequireDefault(_CustomControl); + +var _constants = require("react-google-maps/lib/constants"); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + /* Create map with withGoogleMap HOC */ /* https://github.com/tomchentw/react-google-maps */ -var Map = (0, _withGoogleMap2.default)(function (props) { - var position = props.position, - defaultZoom = props.defaultZoom, - handleMarkerDragEnd = props.handleMarkerDragEnd, - onZoomChanged = props.onZoomChanged, - radius = props.radius, - circleOptions = props.circleOptions, - shouldRecenterMap = props.shouldRecenterMap, - zoom = props.zoom, - onChangeInput = props.onChangeInput, - onChangeSuggestion = props.onChangeSuggestion, - address = props.address, - search = props.search; - - - var circle = radius !== -1 ? _react2.default.createElement(_Circle2.default, { center: position, radius: radius, options: circleOptions }) : null; - var mapExtraProps = shouldRecenterMap ? { center: position } : {}; - return _react2.default.createElement( - _GoogleMap2.default, - _extends({ - onZoomChanged: onZoomChanged, - defaultZoom: defaultZoom, - defaultCenter: position, - defaultOptions: { mapTypeControlOptions: { position: 12 }, fullscreenControlOptions: { position: 6 } }, - zoom: zoom - }, mapExtraProps), - _react2.default.createElement(_SearchBox2.default, { onChangeInput: onChangeInput, onChangeSuggestion: onChangeSuggestion, search: search, address: address }), - _react2.default.createElement(_Marker2.default, { - draggable: true // Allow marker to drag - , position: position, - onDragEnd: handleMarkerDragEnd - }), - circle - ); -}); - -exports.default = Map; \ No newline at end of file +var Map = function (_Component) { + _inherits(Map, _Component); + + function Map(props) { + _classCallCheck(this, Map); + + var _this = _possibleConstructorReturn(this, (Map.__proto__ || Object.getPrototypeOf(Map)).call(this, props)); + + _this.state = { + mounted: false + }; + return _this; + } + + _createClass(Map, [{ + key: "componentDidMount", + value: function componentDidMount() { + this.setState({ mounted: true }); + } + }, { + key: "render", + value: function render() { + var _this2 = this; + + var _props = this.props, + position = _props.position, + defaultZoom = _props.defaultZoom, + handleMarkerDragEnd = _props.handleMarkerDragEnd, + onZoomChanged = _props.onZoomChanged, + radius = _props.radius, + circleOptions = _props.circleOptions, + shouldRecenterMap = _props.shouldRecenterMap, + zoom = _props.zoom, + onChangeInput = _props.onChangeInput, + onChangeSuggestion = _props.onChangeSuggestion, + address = _props.address, + search = _props.search; + var mounted = this.state.mounted; + + + var circle = radius !== -1 ? _react2.default.createElement(_Circle2.default, { center: position, radius: radius, options: circleOptions }) : null; + var mapExtraProps = shouldRecenterMap ? { center: position } : {}; + + return _react2.default.createElement( + _GoogleMap2.default, + _extends({ + onZoomChanged: onZoomChanged, + defaultZoom: defaultZoom, + defaultCenter: position, + defaultOptions: { + mapTypeControlOptions: { position: 12 }, + fullscreenControlOptions: { position: 6 } + }, + zoom: zoom, + ref: function ref(map) { + return _this2._map = map; + }, + google: google + }, mapExtraProps), + _react2.default.createElement( + _CustomControl2.default, + { mounted: mounted, position: 3, map: this._map ? this._map.context[_constants.MAP] : undefined }, + _react2.default.createElement(_SearchBox2.default, { onChangeInput: onChangeInput, onChangeSuggestion: onChangeSuggestion, search: search, address: address }) + ), + _react2.default.createElement(_Marker2.default, { + draggable: true // Allow marker to drag + , position: position, + onDragEnd: handleMarkerDragEnd + }), + circle + ); + } + }]); + + return Map; +}(_react.Component); + +; + +exports.default = (0, _withGoogleMap2.default)(Map); \ No newline at end of file diff --git a/lib/SearchBox.js b/lib/SearchBox.js index 55483b1..9b084b1 100644 --- a/lib/SearchBox.js +++ b/lib/SearchBox.js @@ -4,6 +4,8 @@ Object.defineProperty(exports, "__esModule", { value: true }); +var _searchBoxStyles; + var _react = require("react"); var _react2 = _interopRequireDefault(_react); @@ -14,6 +16,8 @@ var _reactGooglePlacesSuggest2 = _interopRequireDefault(_reactGooglePlacesSugges function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + var Icon = function Icon(props) { return _react2.default.createElement( "svg", @@ -37,7 +41,7 @@ Icon.defaultProps = { }; -var searchBoxStyles = { +var searchBoxStyles = (_searchBoxStyles = { alignItems: "center", backgroundColor: "rgb(255, 255, 255)", borderBottomLeftRadius: "4px", @@ -63,14 +67,8 @@ var searchBoxStyles = { transitionDelay: "0s", transitionDuration: "0.3s", transitionProperty: "box-shadow", - transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)", - maxWidth: 310, - minWidth: 280, - WebkitFontSmoothing: "antialiased", - position: "absolute", - top: 8, - right: 8 -}; + transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)" +}, _defineProperty(_searchBoxStyles, "marginTop", 14), _defineProperty(_searchBoxStyles, "marginRight", 14), _defineProperty(_searchBoxStyles, "maxWidth", 310), _defineProperty(_searchBoxStyles, "minWidth", 280), _defineProperty(_searchBoxStyles, "WebkitFontSmoothing", "antialiased"), _searchBoxStyles); var SearchBox = function SearchBox(props) { return _react2.default.createElement( diff --git a/lib/index.js b/lib/index.js index 4e80cd3..9603b59 100644 --- a/lib/index.js +++ b/lib/index.js @@ -190,25 +190,21 @@ var LocationPicker = function (_Component) { search = _state.search; - return _react2.default.createElement( - 'div', - { style: { position: "relative" } }, - _react2.default.createElement(_GoogleMap2.default, { - containerElement: containerElement, - mapElement: mapElement, - handleMarkerDragEnd: this.handleMarkerDragEnd, - position: position, - circleOptions: circleOptions, - radius: radius, - defaultZoom: zoom, - zoom: zoom, - shouldRecenterMap: shouldRecenterMap, - onChangeInput: this.handleChangeInput, - onChangeSuggestion: this.handleChangeSuggestion, - address: address, - search: search - }) - ); + return _react2.default.createElement(_GoogleMap2.default, { + containerElement: containerElement, + mapElement: mapElement, + handleMarkerDragEnd: this.handleMarkerDragEnd, + position: position, + circleOptions: circleOptions, + radius: radius, + defaultZoom: zoom, + zoom: zoom, + shouldRecenterMap: shouldRecenterMap, + onChangeInput: this.handleChangeInput, + onChangeSuggestion: this.handleChangeSuggestion, + address: address, + search: search + }); } }]); diff --git a/src/CustomControl.jsx b/src/CustomControl.jsx new file mode 100644 index 0000000..1600914 --- /dev/null +++ b/src/CustomControl.jsx @@ -0,0 +1,48 @@ +import { Component } from "react"; +import { createPortal } from "react-dom"; + +/** + * This Component for add custom control to map + * (map.controls[position].push(component)) + * NOTE: + * Can ref to map through context in constructor (or this.context expect contructor) + * User constructor to add div and render will createPortal + */ +export default class MapControl extends Component { + constructor(props, context) { + super(props); + + this.state = { + mounted: false + }; + } + + componentDidUpdate() { + if (this.props.map && !this.map) { + this.map = this.props.map; + this.controlDiv = document.createElement("div"); + this.divIndex = this.map.controls[this.props.position].length; + this.map.controls[this.props.position].push(this.controlDiv); + + this.setState({ + mounted: true + }); + } + } + + componentWillUnmount() { + this.map.controls[this.props.position].removeAt(this.divIndex); + } + + render() { + const { className } = this.props; + + if (!this.map) { + return null; + } + + className && this.controlDiv.classList.add(className); + + return createPortal(this.props.children, this.controlDiv); + } +} diff --git a/src/GoogleMap.jsx b/src/GoogleMap.jsx index 8ad2daf..e6ef289 100644 --- a/src/GoogleMap.jsx +++ b/src/GoogleMap.jsx @@ -1,58 +1,85 @@ -import React from "react"; +import React,{Component} from "react"; import withGoogleMap from "react-google-maps/lib/withGoogleMap"; import GoogleMap from "react-google-maps/lib/components/GoogleMap"; import Marker from "react-google-maps/lib/components/Marker"; import Circle from "react-google-maps/lib/components/Circle"; import SearchBox from "./SearchBox"; +import CustomControl from "./CustomControl"; +import { MAP } from "react-google-maps/lib/constants"; /* Create map with withGoogleMap HOC */ /* https://github.com/tomchentw/react-google-maps */ -const Map = withGoogleMap(props => { - const { - position, - defaultZoom, - handleMarkerDragEnd, - onZoomChanged, - radius, - circleOptions, - shouldRecenterMap, - zoom, - onChangeInput, - onChangeSuggestion, - address, - search - } = props; - - const circle = - radius !== -1 ? ( - - ) : null; - const mapExtraProps = shouldRecenterMap ? { center: position } : {}; - return ( - - - {/* Map marker */} - - - {/* Circle */} - {circle} - - ); -}); - -export default Map; +class Map extends Component { + + constructor(props){ + super(props); + + // We are keeping mounted status in states. + // Because we can not use useRef method. We are using react 15 + this.state = { + mounted:false + } + } + + componentDidMount(){ + this.setState({mounted:true}); + } + + render(){ + const { + position, + defaultZoom, + handleMarkerDragEnd, + onZoomChanged, + radius, + circleOptions, + shouldRecenterMap, + zoom, + onChangeInput, + onChangeSuggestion, + address, + search + } = this.props; + + const {mounted} = this.state; + + const circle = + radius !== -1 ? ( + + ) : null; + const mapExtraProps = shouldRecenterMap ? { center: position } : {}; + + return ( + this._map=map} + google={google} + {...mapExtraProps} + > + + + + {/* Map marker */} + + + {/* Circle */} + {circle} + + ); + } +} +; + +export default withGoogleMap(Map); diff --git a/src/SearchBox.jsx b/src/SearchBox.jsx index 91286e6..45d4850 100644 --- a/src/SearchBox.jsx +++ b/src/SearchBox.jsx @@ -30,12 +30,11 @@ const searchBoxStyles = { transitionDuration: "0.3s", transitionProperty: "box-shadow", transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)", + marginTop:14, + marginRight:14, maxWidth: 310, minWidth: 280, - WebkitFontSmoothing: "antialiased", - position: "absolute", - top:8, - right:8 + WebkitFontSmoothing: "antialiased" }; const SearchBox = (props) => ( diff --git a/src/index.js b/src/index.js index d5dce16..22cfd2d 100644 --- a/src/index.js +++ b/src/index.js @@ -149,7 +149,6 @@ class LocationPicker extends Component { return ( -
-
); } }