UNPKG

chayns-components

Version:

A set of beautiful React components for developing chayns® applications.

285 lines (278 loc) 9.69 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); exports.__esModule = true; exports.default = void 0; var _coordinateParser = _interopRequireDefault(require("coordinate-parser")); var _propTypes = _interopRequireDefault(require("prop-types")); var _react = _interopRequireWildcard(require("react")); var _Input = _interopRequireDefault(require("../../react-chayns-input/component/Input")); var _TappPortal = _interopRequireDefault(require("../../react-chayns-tapp_portal/component/TappPortal")); var _debounce = _interopRequireDefault(require("../../utils/debounce")); var _AutocompleteItem = _interopRequireDefault(require("./AutocompleteItem")); var _GoogleMap = _interopRequireDefault(require("./GoogleMap/GoogleMap")); require("./styles.css"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /** * @component */ /* global google */ /** Uses the `toJSON()` method to return a human readable-object */ const toLiteral = value => JSON.parse(JSON.stringify(value)); const ADDRESS = 1; const COORDS = 2; const noop = () => {}; /** * A location input with a map and text input. * * This requires the Google Maps JavaScript API with the places library enabled. * You can find more information about the Maps API * [here](https://developers.google.com/maps/documentation/javascript/overview). */ class PositionInput extends _react.PureComponent { constructor(props) { var _this; super(props); _this = this; this.setAddress = function (position, triggerChange) { if (triggerChange === void 0) { triggerChange = true; } const { onPositionChange } = _this.props; _this.geocoder.geocode({ location: position }, (result, status) => { if (status === google.maps.GeocoderStatus.OK) { _this.setState({ value: result[0].formatted_address }); if (triggerChange) { onPositionChange(position, result[0].formatted_address); } } }); }; this.handleUserPan = map => { const { onPositionChange } = this.props; const { currentInputType } = this.state; const center = toLiteral(map.getCenter()); if (currentInputType === COORDS) { onPositionChange(center); this.setState({ value: `${center.lat.toFixed(4)} ${center.lng.toFixed(4)}` }); } else { this.setAddress(center); } }; this.handleInputChange = value => { this.setState({ value, overlayPosition: this.mapOverlayRef.current.getBoundingClientRect() }); try { const { onPositionChange } = this.props; const { latitude: lat, longitude: lng } = new _coordinateParser.default(value); const position = { lat, lng }; this.mapRef.current.panTo(position); onPositionChange(position, value); this.setState({ currentInputType: COORDS, addresses: [] }); } catch (e) { // Invalid coordinates this.getAddresses(value); this.setState({ currentInputType: ADDRESS }); } }; this.changePosition = value => { const { currentInputType } = this.state; this.mapRef.current.panTo(value); if (currentInputType === COORDS) { this.setState({ value: `${value.lat.toFixed(4)} ${value.lng.toFixed(4)}` }); } else { this.setAddress(value, false); } }; this.getAddresses = value => { if (value) { const { defaultPosition: { lat, lng } } = this.props; this.autocomplete.getPlacePredictions({ location: new google.maps.LatLng(lat, lng), radius: 10000, input: value }, (result, status) => { if (status === google.maps.places.PlacesServiceStatus.OK) { this.setState({ addresses: result.map(a => a.description) }); } }); } }; this.selectAddress = value => { this.setState({ value, addresses: [] }); this.geocoder.geocode({ address: value }, (results, status) => { if (status === google.maps.GeocoderStatus.OK) { const { onPositionChange } = this.props; const position = toLiteral(results[0].geometry.location); this.mapRef.current.panTo(position); onPositionChange(position, value); } }); }; if (!window.google) { throw new Error('The google maps JS API could not be found. Did you forget to include the script? See https://developers.google.com/maps/documentation/javascript/get-api-key for more details.'); } this.autocomplete = new google.maps.places.AutocompleteService(); this.geocoder = new google.maps.Geocoder(); this.state = { value: '', addresses: [], currentInputType: ADDRESS, overlayPosition: { width: 0, left: 0, bottom: 0 } }; /** @type {React.RefObject<google.maps.Map>} */ this.mapRef = /*#__PURE__*/(0, _react.createRef)(); this.mapOverlayRef = /*#__PURE__*/(0, _react.createRef)(); this.getAddresses = (0, _debounce.default)(this.getAddresses, 500); this.setAddress(props.defaultPosition, false); } render() { const { defaultPosition, mapOptions, children, parent } = this.props; const { value, addresses, currentInputType, overlayPosition } = this.state; return /*#__PURE__*/_react.default.createElement("div", { className: "cc__map" }, /*#__PURE__*/_react.default.createElement("div", { className: "map--crosshair" }, "+"), /*#__PURE__*/_react.default.createElement(_GoogleMap.default, { mapRef: this.mapRef, containerStyle: { height: '100%', position: 'relative' }, options: { ...mapOptions, center: defaultPosition }, onDragend: this.handleUserPan }), children && /*#__PURE__*/_react.default.createElement("div", { className: "map--overlay", ref: this.mapOverlayRef }, children(value, this.handleInputChange, this.changePosition), /*#__PURE__*/_react.default.createElement(_TappPortal.default, { parent: parent }, /*#__PURE__*/_react.default.createElement("div", { className: "map--autocomplete_popup", style: { left: `${overlayPosition.left}px`, top: `${overlayPosition.bottom}px`, width: `${overlayPosition.width}px` } }, !!value && currentInputType === ADDRESS && addresses.map((a, index) => /*#__PURE__*/_react.default.createElement(_AutocompleteItem.default, { index: index, address: a, onClick: this.selectAddress, key: a })))))); } } exports.default = PositionInput; PositionInput.propTypes = { /** * The position that will be used as a starting point. */ defaultPosition: _propTypes.default.shape({ lat: _propTypes.default.number.isRequired, lng: _propTypes.default.number.isRequired }).isRequired, /** * This will be called when the position selection changes. */ onPositionChange: _propTypes.default.func, /** * An object with options for the Google Map. These options are documented * [here](https://developers.google.com/maps/documentation/javascript/reference/map#MapOptions). */ mapOptions: _propTypes.default.object, // eslint-disable-line react/forbid-prop-types /** * A render function for creating a custom input overlay. It receives the * currently selected position as its first argument and an onChange-method * as its second argument. */ children: _propTypes.default.func, /** * A DOM element that the overlay should be rendered into. */ parent: _propTypes.default.node }; PositionInput.defaultProps = { onPositionChange: noop, mapOptions: { zoom: 15, gestureHandling: 'greedy', disableDefaultUI: true, styles: [{ featureType: 'poi', elementType: 'labels', stylers: [{ visibility: 'off' }] }] }, children: (value, onChange) => /*#__PURE__*/_react.default.createElement(_Input.default, { placeholder: "Position", value: value, onChange: onChange }), parent: null }; PositionInput.displayName = 'PositionInput'; //# sourceMappingURL=PositionInput.js.map