UNPKG

extpoint-yii2

Version:

JavaScript part for projects on ExtPoint Yii2 Boilerplate and yii2-core

289 lines (260 loc) 10.6 kB
import React from 'react'; import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; import {connect} from 'react-redux'; import {change, getFormInitialValues} from 'redux-form'; import {saveToCache} from 'extpoint-yii2/actions/formList'; import {view, resource, locale} from 'components'; import AddressHelper from './AddressHelper'; class AddressField extends React.Component { static propTypes = { label: PropTypes.oneOfType([ PropTypes.string, PropTypes.element ]), hint: PropTypes.string, input: PropTypes.shape({ name: PropTypes.string, value: PropTypes.any, onChange: PropTypes.func, }), metaItem: PropTypes.shape({ addressType: PropTypes.string, relationName: PropTypes.string, }), parentId: PropTypes.number, addressValues: PropTypes.shape({ country: PropTypes.oneOfType([ PropTypes.number, PropTypes.string, ]), countryLabel: PropTypes.string, region: PropTypes.oneOfType([ PropTypes.number, PropTypes.string, ]), regionLabel: PropTypes.string, city: PropTypes.oneOfType([ PropTypes.number, PropTypes.string, ]), cityLabel: PropTypes.string, metroStation: PropTypes.oneOfType([ PropTypes.number, PropTypes.string, ]), metroStationLabel: PropTypes.string, address: PropTypes.string, longitude: PropTypes.oneOfType([ PropTypes.number, PropTypes.string, ]), latitude: PropTypes.oneOfType([ PropTypes.number, PropTypes.string, ]), }), onChange: PropTypes.func, countryModelClass: PropTypes.string, autoCompleteUrl: PropTypes.string, autoDetect: PropTypes.bool, addressNames: PropTypes.object, }; constructor() { super(...arguments); this._api = null; this._isSentDetect = false; this.state = { isDetecting: this.props.autoDetect, }; this._onChange = this._onChange.bind(this); } componentDidMount() { resource.loadYandexMap() .then(ymaps => { this._api = ymaps; if (this.props.metaItem.addressType === AddressHelper.TYPE_ADDRESS) { this._initSuggest(); } if (this.props.autoDetect && [AddressHelper.TYPE_COUNTRY, AddressHelper.TYPE_REGION, AddressHelper.TYPE_CITY].indexOf(this.props.metaItem.addressType) !== -1) { this._detectAddress(); } }); } componentDidUpdate(prevProps) { // Listen reset for address field if (this.props.metaItem.addressType === AddressHelper.TYPE_ADDRESS && prevProps.input.value) { const input = ReactDOM.findDOMNode(this).querySelector('input[type=text]'); if (input) { input.value = this.props.input.value; // Close ymaps autocomplete on set value setTimeout(() => { const ymapsEl = input.parentNode.querySelector('ymaps'); if (ymapsEl) { ymapsEl.style.display = 'none'; } }); } } } render() { const props = {...this.props}; switch (this.props.metaItem.addressType) { case AddressHelper.TYPE_COUNTRY: case AddressHelper.TYPE_REGION: case AddressHelper.TYPE_CITY: case AddressHelper.TYPE_METRO_STATION: props.dropDownProps = { ...props, onChange: this._onChange, labelProps: null, hintProps: null, errorProps: null, }; if ([AddressHelper.TYPE_COUNTRY, AddressHelper.TYPE_REGION, AddressHelper.TYPE_CITY].indexOf(this.props.metaItem.addressType) !== -1) { props.dropDownProps.placeholder = this.state.isDetecting ? locale.t('Определение...') : undefined; } if (this.props.metaItem.addressType === AddressHelper.TYPE_COUNTRY) { props.dropDownProps.enumClassName = this.props.countryModelClass; } else { props.dropDownProps.autoComplete = { method: this.props.autoCompleteUrl, addressType: this.props.metaItem.addressType, parentId: this.props.parentId || null, }; } break; // Make address field uncontrolled so that API's SuggestView won't reopen it on suggest selection case AddressHelper.TYPE_ADDRESS: props.stringProps = { ...props, inputProps: { defaultValue: props.input.value, value: undefined, onChange: undefined, }, input: { onChange: () => {}, }, onChange: this._onChange, labelProps: null, hintProps: null, errorProps: null, }; break; case AddressHelper.TYPE_LONGITUDE: case AddressHelper.TYPE_LATITUDE: props.stringProps = { ...props, onChange: this._onChange, labelProps: null, hintProps: null, errorProps: null, }; break; } const AddressFieldView = this.props.view || view.getFormView('AddressFieldView'); return ( <AddressFieldView {...props}/> ); } _onChange(value) { const attributes = [ 'country', 'region', 'city', 'metroStation', 'address', 'latitude', 'longitude', ]; let isFined = false; attributes.forEach(key => { const name = this.props.addressNames[key]; if (name) { if (this.props.input.name === name && key !== AddressHelper.TYPE_METRO_STATION) { isFined = true; } else if (isFined) { this.props.dispatch(change(this.props.formId, name, null)); } } }); if (this.props.onChange) { this.props.onChange(value); } } _detectAddress() { if (!this._api.geolocation || this._isSentDetect) { return; } this._isSentDetect = true; this.setState({ isDetecting: true, }); AddressHelper.detectAddress(this._api) .then(data => { const toDispatch = []; Object.keys(data).map(addressType => { const item = data[addressType]; if (this.props.addressNames[addressType]) { toDispatch.push(saveToCache( `${this.props.formId}_${this.props.addressNamesWithoutPrefix[addressType]}`, { [item.id]: { id: item.id, label: item.title, }, } )); toDispatch.push(change( this.props.formId, this.props.addressNames[addressType], item.id )); } }); this.props.dispatch(toDispatch); this.setState({ isDetecting: false, }); }) .catch(e => console.error(e)); // eslint-disable-line no-console } _initSuggest() { const input = ReactDOM.findDOMNode(this).querySelector('input[type=text]'); const suggestView = new this._api.SuggestView(input, { offset: [-1, 5], provider: { suggest: searchQuery => { const areaLabels = [AddressHelper.TYPE_COUNTRY, AddressHelper.TYPE_REGION, AddressHelper.TYPE_CITY] .map(addressType => this.props.addressValues[addressType + 'Label']) .filter(Boolean) .join(', '); return this._api.suggest(areaLabels + ', ' + searchQuery); } } }); suggestView.events.add('select', event => { const address = event.get('item').value; this._api.geocode(address) .then(result => { const coordinates = result.geoObjects.get(0).geometry.getCoordinates(); this.props.dispatch([ //change(this.props.formId, this.props.addressNames.address, address), change(this.props.formId, this.props.addressNames.longitude, coordinates[0]), change(this.props.formId, this.props.addressNames.latitude, coordinates[1]), ]); }); }); } } export default connect( (state, props) => ({ parentId: AddressHelper.getParentId(state, props.formId, props.model, props.prefix, props.attribute, props.attributePrefix), addressNames: AddressHelper.getNames(props.model, props.prefix || ''), addressNamesWithoutPrefix: AddressHelper.getNames(props.model, ''), addressValues: AddressHelper.getValues(state, props.formId, props.model, props.prefix, props.attribute, props.attributePrefix), initialValues: getFormInitialValues(props.formId)(state), }) )(AddressField);