chayns-components
Version:
A set of beautiful React components for developing chayns® applications.
284 lines (277 loc) • 9.66 kB
JavaScript
"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"));
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