├── src ├── controllers │ ├── index.js │ ├── MapController.js │ ├── PanoramaController.js │ ├── ImportObjectController.js │ ├── MarkerController.js │ └── layouts.js ├── constants │ └── index.js ├── utils │ ├── .DS_Store │ ├── loaders │ │ ├── fetchScript.js │ │ └── loadApi.js │ ├── decorators.js │ └── eventsHandler.js ├── configs │ └── index.js ├── BalloonLayout.jsx ├── index.js ├── PanoramaElement.jsx ├── MapElement.jsx ├── MarkerLayout.jsx ├── apiEventsLists │ ├── map.js │ └── geoObject.js ├── api.js ├── ConstructorJSONImport.js ├── MapMarker.jsx ├── PanoramaContainer.jsx └── MapContainer.jsx ├── lib ├── controllers │ ├── index.js │ ├── MapController.js │ ├── PanoramaController.js │ ├── ImportObjectController.js │ ├── layouts.js │ └── MarkerController.js ├── constants │ └── index.js ├── configs │ └── index.js ├── apiEventsLists │ ├── map.js │ └── geoObject.js ├── utils │ ├── loaders │ │ ├── fetchScript.js │ │ └── loadApi.js │ ├── decorators.js │ └── eventsHandler.js ├── index.js ├── api.js ├── BalloonLayout.js ├── MapElement.js ├── PanoramaElement.js ├── MarkerLayout.js ├── ConstructorJSONImport.js ├── MapMarker.js ├── MapContainer.js └── PanoramaContainer.js ├── .gitignore ├── .babelrc ├── .editorconfig ├── webpack.config.dev.js ├── .eslintrc ├── webpack.config.js ├── package.json └── README.md /src/controllers/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/controllers/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; -------------------------------------------------------------------------------- /src/constants/index.js: -------------------------------------------------------------------------------- 1 | 2 | export const types = { 3 | MARKER_LAYOUT: 1 4 | }; 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | .DS_Store? 4 | .idea/ 5 | Thumbs.db 6 | *.log 7 | -------------------------------------------------------------------------------- /src/utils/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/effrenus/yandex-map-react/HEAD/src/utils/.DS_Store -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-0", "react"], 3 | "plugins": ["syntax-decorators"] 4 | } 5 | -------------------------------------------------------------------------------- /src/configs/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Yandex API config 3 | */ 4 | export const apiConfig = { 5 | host: 'api-maps.yandex.ru', 6 | version: '2.1' 7 | }; 8 | -------------------------------------------------------------------------------- /lib/constants/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | var types = exports.types = { 7 | MARKER_LAYOUT: 1 8 | }; -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.{json,yml}] 12 | indent_size = 2 -------------------------------------------------------------------------------- /lib/configs/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | /** 7 | * Yandex API config 8 | */ 9 | var apiConfig = exports.apiConfig = { 10 | host: 'api-maps.yandex.ru', 11 | version: '2.1' 12 | }; -------------------------------------------------------------------------------- /src/BalloonLayout.jsx: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React, { Component } from 'react'; 3 | 4 | class BalloonLayout extends Component { 5 | render () { 6 | return ( 7 |
8 | {this.props.children} 9 |
10 | ); 11 | } 12 | } 13 | 14 | export default BalloonLayout; 15 | -------------------------------------------------------------------------------- /webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var baseConfig = require('./webpack.config'); 3 | 4 | module.exports = Object.assign({}, baseConfig, { 5 | plugins: [ 6 | new webpack.optimize.OccurenceOrderPlugin(), 7 | new webpack.DefinePlugin({ 8 | 'process.env.NODE_ENV': JSON.stringify('development') 9 | }) 10 | ] 11 | }); 12 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import Map from './MapContainer'; 2 | import Marker from './MapMarker'; 3 | import MarkerLayout from './MarkerLayout'; 4 | import BalloonLayout from './BalloonLayout'; 5 | import ConstructorJSONImport from './ConstructorJSONImport' 6 | import Panorama from './PanoramaContainer'; 7 | 8 | export { 9 | Map, 10 | Marker, 11 | MarkerLayout, 12 | BalloonLayout, 13 | ConstructorJSONImport, 14 | Panorama 15 | }; 16 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | extends: 'loris/es6', 3 | env: { 4 | es6: true, 5 | browser: true 6 | }, 7 | ecmaFeatures: { 8 | sourceType: 'module' 9 | }, 10 | parserOptions: { 11 | sourceType: 'module', 12 | ecmaFeatures: { 13 | jsx: true, 14 | experimentalObjectRestSpread: true 15 | } 16 | }, 17 | rules: { 18 | space-before-function-paren: 0 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/utils/loaders/fetchScript.js: -------------------------------------------------------------------------------- 1 | export default function fetchScript (url) { 2 | return new Promise((resolve, reject) => { 3 | const script = document.createElement('script'); 4 | script.onload = resolve; 5 | script.onerror = reject; 6 | script.src = url; 7 | 8 | const beforeScript = document.getElementsByTagName('script')[0]; 9 | beforeScript.parentNode.insertBefore(script, beforeScript); 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/decorators.js: -------------------------------------------------------------------------------- 1 | import {register as registerEvents} from './eventsHandler'; 2 | 3 | export function eventsDecorator (Component, {supportEvents}) { 4 | Object.defineProperty(Component.prototype, '_setupEvents', { 5 | enumerable: false, 6 | configurable: true, 7 | writable: true, 8 | value() { 9 | registerEvents(this.getController(), this.props, supportEvents); 10 | } 11 | }); 12 | 13 | return Component; 14 | } 15 | -------------------------------------------------------------------------------- /lib/apiEventsLists/map.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = ['actionbegin', 'actionbreak', 'actionend', 'actiontick', 'actiontickcomplete', 'balloonclose', 'balloonopen', 'boundschange', 'click', 'contextmenu', 'dblclick', 'destroy', 'hintclose', 'hintopen', 'marginchange', 'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'mouseup', 'multitouchend', 'multitouchmove', 'multitouchstart', 'optionschange', 'sizechange', 'typechange', 'wheel']; -------------------------------------------------------------------------------- /src/PanoramaElement.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | const style = { 4 | position: 'absolute', 5 | width: '100%', 6 | height: '100%', 7 | margin: 0, 8 | padding: 0 9 | }; 10 | 11 | class PanoramaElement extends Component { 12 | render () { 13 | if (this.props.show) { 14 | return ( 15 |
16 | ); 17 | } else { 18 | return null; 19 | } 20 | } 21 | } 22 | 23 | export default PanoramaElement; 24 | -------------------------------------------------------------------------------- /lib/apiEventsLists/geoObject.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = ['balloonclose', 'balloonopen', 'beforedrag', 'beforedragstart', 'click', 'contextmenu', 'dblclick', 'geometrychange', 'hintclose', 'hintopen', 'mapchange', 'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'mouseup', 'multitouchend', 'multitouchmove', 'multitouchstart', 'optionschange', 'overlaychange', 'parentchange', 'propertieschange', 'wheel', 'drag', 'dragend', 'dragstart', 'editorstatechange']; -------------------------------------------------------------------------------- /src/MapElement.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | const style = { 4 | position: 'absolute', 5 | width: '100%', 6 | height: '100%', 7 | margin: 0, 8 | padding: 0 9 | }; 10 | 11 | class MapElement extends Component { 12 | constructor (props) { 13 | super(props); 14 | } 15 | 16 | shouldComponentUpdate () { 17 | return false; 18 | } 19 | 20 | render () { 21 | return ( 22 |
23 | ); 24 | } 25 | } 26 | 27 | export default MapElement; 28 | -------------------------------------------------------------------------------- /lib/utils/loaders/fetchScript.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = fetchScript; 7 | function fetchScript(url) { 8 | return new Promise(function (resolve, reject) { 9 | var script = document.createElement('script'); 10 | script.onload = resolve; 11 | script.onerror = reject; 12 | script.src = url; 13 | 14 | var beforeScript = document.getElementsByTagName('script')[0]; 15 | beforeScript.parentNode.insertBefore(script, beforeScript); 16 | }); 17 | } -------------------------------------------------------------------------------- /src/MarkerLayout.jsx: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React, { Component } from 'react'; 3 | import { types } from './constants'; 4 | 5 | /** 6 | * @class MarkerLayout 7 | */ 8 | class MarkerLayout extends Component { 9 | static propTypes = { 10 | marker: PropTypes.object 11 | } 12 | 13 | componentWillUnmount () { 14 | if (this._marker) { 15 | this._marker.destroy(); 16 | } 17 | } 18 | 19 | render () { 20 | return
{this.props.children}
; 21 | } 22 | } 23 | 24 | export default MarkerLayout; 25 | -------------------------------------------------------------------------------- /src/apiEventsLists/map.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | 'actionbegin', 3 | 'actionbreak', 4 | 'actionend', 5 | 'actiontick', 6 | 'actiontickcomplete', 7 | 'balloonclose', 8 | 'balloonopen', 9 | 'boundschange', 10 | 'click', 11 | 'contextmenu', 12 | 'dblclick', 13 | 'destroy', 14 | 'hintclose', 15 | 'hintopen', 16 | 'marginchange', 17 | 'mousedown', 18 | 'mouseenter', 19 | 'mouseleave', 20 | 'mousemove', 21 | 'mouseup', 22 | 'multitouchend', 23 | 'multitouchmove', 24 | 'multitouchstart', 25 | 'optionschange', 26 | 'sizechange', 27 | 'typechange', 28 | 'wheel' 29 | ]; 30 | -------------------------------------------------------------------------------- /lib/utils/decorators.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.eventsDecorator = eventsDecorator; 7 | 8 | var _eventsHandler = require('./eventsHandler'); 9 | 10 | function eventsDecorator(Component, _ref) { 11 | var supportEvents = _ref.supportEvents; 12 | 13 | Object.defineProperty(Component.prototype, '_setupEvents', { 14 | enumerable: false, 15 | configurable: true, 16 | writable: true, 17 | value: function value() { 18 | (0, _eventsHandler.register)(this.getController(), this.props, supportEvents); 19 | } 20 | }); 21 | 22 | return Component; 23 | } -------------------------------------------------------------------------------- /src/apiEventsLists/geoObject.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | 'balloonclose', 3 | 'balloonopen', 4 | 'beforedrag', 5 | 'beforedragstart', 6 | 'click', 7 | 'contextmenu', 8 | 'dblclick', 9 | 'geometrychange', 10 | 'hintclose', 11 | 'hintopen', 12 | 'mapchange', 13 | 'mousedown', 14 | 'mouseenter', 15 | 'mouseleave', 16 | 'mousemove', 17 | 'mouseup', 18 | 'multitouchend', 19 | 'multitouchmove', 20 | 'multitouchstart', 21 | 'optionschange', 22 | 'overlaychange', 23 | 'parentchange', 24 | 'propertieschange', 25 | 'wheel', 26 | 'drag', 27 | 'dragend', 28 | 'dragstart', 29 | 'editorstatechange' 30 | ]; 31 | -------------------------------------------------------------------------------- /src/utils/eventsHandler.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | function toOnEventName (name) { 4 | return `on${name.substr(0, 1).toUpperCase()}${name.substr(1)}`; 5 | } 6 | 7 | /** 8 | * Register event callback on api instance 9 | * @param {Object} controller 10 | * @param {Object} props React component `props` 11 | * @param {Array} eventsList Events supported in API (specific for different objects) 12 | */ 13 | export function register (controller, props, eventsList) { 14 | eventsList.forEach((eventName) => { 15 | const onEventName = toOnEventName(eventName); 16 | if (props.hasOwnProperty(onEventName)) { 17 | controller.events.add(eventName, props[onEventName]); 18 | } 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | 3 | var reactExternal = { 4 | root: 'React', 5 | commonjs2: 'react', 6 | commonjs: 'react', 7 | amd: 'react' 8 | }; 9 | 10 | var reactDomExternal = { 11 | root: 'ReactDOM', 12 | commonjs2: 'react-dom', 13 | commonjs: 'react-dom', 14 | amd: 'react-dom' 15 | }; 16 | 17 | module.exports = { 18 | output: { 19 | library: 'YandexMapReact', 20 | libraryTarget: 'umd' 21 | }, 22 | externals: { 23 | 'react': reactExternal, 24 | 'react-dom': reactDomExternal 25 | }, 26 | module: { 27 | loaders: [ 28 | {test: /\.jsx?/i, exclude: /node_modules/, loader: 'babel'} 29 | ] 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /src/api.js: -------------------------------------------------------------------------------- 1 | import loadApi from './utils/loaders/loadApi'; 2 | 3 | class Api { 4 | constructor () { 5 | this.api = (typeof window != 'undefined' && window.ymaps) ? window.ymaps : null; 6 | } 7 | 8 | setAPI (instance) { 9 | this.api = instance; 10 | 11 | return this.api; 12 | } 13 | 14 | getAPI () { 15 | return this.api; 16 | } 17 | 18 | isAvailible () { 19 | return Boolean(this.api); 20 | } 21 | 22 | /** 23 | * Loading API 24 | * @return {Promise} 25 | */ 26 | load (options={}) { 27 | return loadApi(options).then((instance) => { 28 | this.api = instance; 29 | return instance; 30 | }); 31 | } 32 | } 33 | 34 | export default new Api(); 35 | -------------------------------------------------------------------------------- /lib/utils/eventsHandler.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.register = register; 7 | 8 | 9 | function toOnEventName(name) { 10 | return "on" + name.substr(0, 1).toUpperCase() + name.substr(1); 11 | } 12 | 13 | /** 14 | * Register event callback on api instance 15 | * @param {Object} controller 16 | * @param {Object} props React component `props` 17 | * @param {Array} eventsList Events supported in API (specific for different objects) 18 | */ 19 | function register(controller, props, eventsList) { 20 | eventsList.forEach(function (eventName) { 21 | var onEventName = toOnEventName(eventName); 22 | if (props.hasOwnProperty(onEventName)) { 23 | controller.events.add(eventName, props[onEventName]); 24 | } 25 | }); 26 | } -------------------------------------------------------------------------------- /src/ConstructorJSONImport.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React, { Component } from 'react'; 3 | import ImportObjectController from './controllers/ImportObjectController'; 4 | 5 | class ConstructorJSONImport extends Component { 6 | static propTypes = { 7 | userMapData: PropTypes.object.isRequired 8 | } 9 | 10 | static contextTypes = { 11 | mapController: PropTypes.object 12 | } 13 | 14 | componentDidMount () { 15 | const {map} = this.context.mapController; 16 | this._controller = new ImportObjectController(map, this.props.userMapData); 17 | } 18 | 19 | componentWillUnmount () { 20 | this._controller.destroy(); 21 | } 22 | 23 | shouldComponentUpdate () { 24 | return false; 25 | } 26 | 27 | render () { 28 | return null; 29 | } 30 | } 31 | 32 | export default ConstructorJSONImport; 33 | -------------------------------------------------------------------------------- /src/controllers/MapController.js: -------------------------------------------------------------------------------- 1 | import api from '../api'; 2 | 3 | class MapController { 4 | constructor () { 5 | 6 | } 7 | 8 | createMap (container, state, options) { 9 | this._map = new (api.getAPI()).Map(container, state, options); 10 | this.events = this._map.events.group(); 11 | 12 | this._setupCollection(); 13 | 14 | return this; 15 | } 16 | 17 | appendMarker (marker) { 18 | this._geoCollection.add(marker.getAPIInstance()); 19 | marker.setBalloonState(marker.balloonState); 20 | } 21 | 22 | get map () { 23 | return this._map; 24 | } 25 | 26 | setOptions (name, value) { 27 | this._map.options.set(name, value); 28 | } 29 | 30 | setCenter (coords) { 31 | this._map.setCenter(coords); 32 | } 33 | 34 | setZoom (zoom) { 35 | this._map.setZoom(zoom); 36 | } 37 | setBounds (bounds) { 38 | this._map.setBounds(bounds); 39 | } 40 | 41 | setState (name, value) { 42 | this._map.state.set(name, value); 43 | } 44 | 45 | destroy () { 46 | this.events.removeAll(); 47 | this._map.destroy(); 48 | } 49 | 50 | _setupCollection () { 51 | this._geoCollection = new (api.getAPI()).GeoObjectCollection(); 52 | this._map.geoObjects.add(this._geoCollection); 53 | } 54 | } 55 | 56 | export default MapController; 57 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yandex-map-react", 3 | "version": "1.0.0", 4 | "description": "Yandex map react", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "build:lib": "babel ./src -d lib", 8 | "build:umd": "webpack src/index.js dist/YandexMapReact.js --config webpack.config.dev.js", 9 | "watch": "babel --watch ./src -d lib", 10 | "lint": "eslint src/", 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "files": [ 14 | "lib" 15 | ], 16 | "keywords": [ 17 | "yandex", 18 | "map", 19 | "react" 20 | ], 21 | "author": "Alexey Sazonov", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/effrenus/yandex-map-react/issues" 25 | }, 26 | "devDependencies": { 27 | "babel-cli": "^6.6.5", 28 | "babel-core": "^6.7.4", 29 | "babel-loader": "^6.2.4", 30 | "babel-plugin-syntax-decorators": "^6.8.0", 31 | "babel-preset-es2015": "^6.6.0", 32 | "babel-preset-react": "^6.5.0", 33 | "babel-preset-stage-0": "^6.5.0", 34 | "babel-plugin-syntax-decorators": "^6.8.0", 35 | "eslint": "^2.9.0", 36 | "eslint-config-loris": "^4.0.0", 37 | "webpack": "^1.12.14", 38 | "react": "^15.5.0", 39 | "react-dom": "^15.5.0", 40 | "prop-types": "^15.5.0" 41 | }, 42 | "peerDependencies": { 43 | "prop-types": "^15.5.0", 44 | "react": "^0.14.8 || ^15.5.0 || ^16.0.0", 45 | "react-dom": "^0.14.8 || ^15.5.0 || ^16.0.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.Panorama = exports.ConstructorJSONImport = exports.BalloonLayout = exports.MarkerLayout = exports.Marker = exports.Map = undefined; 7 | 8 | var _MapContainer = require('./MapContainer'); 9 | 10 | var _MapContainer2 = _interopRequireDefault(_MapContainer); 11 | 12 | var _MapMarker = require('./MapMarker'); 13 | 14 | var _MapMarker2 = _interopRequireDefault(_MapMarker); 15 | 16 | var _MarkerLayout = require('./MarkerLayout'); 17 | 18 | var _MarkerLayout2 = _interopRequireDefault(_MarkerLayout); 19 | 20 | var _BalloonLayout = require('./BalloonLayout'); 21 | 22 | var _BalloonLayout2 = _interopRequireDefault(_BalloonLayout); 23 | 24 | var _ConstructorJSONImport = require('./ConstructorJSONImport'); 25 | 26 | var _ConstructorJSONImport2 = _interopRequireDefault(_ConstructorJSONImport); 27 | 28 | var _PanoramaContainer = require('./PanoramaContainer'); 29 | 30 | var _PanoramaContainer2 = _interopRequireDefault(_PanoramaContainer); 31 | 32 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 33 | 34 | exports.Map = _MapContainer2.default; 35 | exports.Marker = _MapMarker2.default; 36 | exports.MarkerLayout = _MarkerLayout2.default; 37 | exports.BalloonLayout = _BalloonLayout2.default; 38 | exports.ConstructorJSONImport = _ConstructorJSONImport2.default; 39 | exports.Panorama = _PanoramaContainer2.default; -------------------------------------------------------------------------------- /src/utils/loaders/loadApi.js: -------------------------------------------------------------------------------- 1 | import fetchScript from './fetchScript'; 2 | import {apiConfig} from '../../configs'; 3 | 4 | let loadPromise; 5 | 6 | const enabledAPIParams = ['lang', 'apikey', 'coordorder', 'load', 'mode']; 7 | const successCallbackName = '_$_api_success'; 8 | const errorCallbackName = '_$_api_error'; 9 | 10 | const defaultOptions = { 11 | lang: 'ru_RU', 12 | coordorder: 'latlong', 13 | load: 'package.full', 14 | mode: 'release', 15 | ns: '', 16 | onload: successCallbackName, 17 | onerror: errorCallbackName 18 | }; 19 | 20 | function generateURL (options) { 21 | const params = Object.assign({}, defaultOptions); 22 | Object.keys(options) 23 | .filter((key) => enabledAPIParams.indexOf(key) !== -1) 24 | .forEach((key) => { 25 | params[key] = options[key]; 26 | }); 27 | 28 | const queryString = Object.keys(params).map((key) => `${key}=${params[key]}`).join('&'); 29 | 30 | return `https://${apiConfig.host}/${options.version || apiConfig.version}/?${queryString}`; 31 | } 32 | 33 | export default function loadApi (options) { 34 | if (loadPromise) { 35 | return loadPromise; 36 | } 37 | 38 | loadPromise = new Promise((resolve, reject) => { 39 | 40 | window[successCallbackName] = (ymaps) => { 41 | resolve(ymaps); 42 | window[successCallbackName] = null; 43 | }; 44 | 45 | window[errorCallbackName] = (error) => { 46 | reject(error); 47 | window[errorCallbackName] = null; 48 | }; 49 | 50 | fetchScript(generateURL(options)); 51 | }); 52 | 53 | return loadPromise; 54 | } 55 | -------------------------------------------------------------------------------- /src/controllers/PanoramaController.js: -------------------------------------------------------------------------------- 1 | import api from '../api'; 2 | 3 | class PanoramaController { 4 | constructor (func) { 5 | this._isPanoramas = func; 6 | } 7 | 8 | createPanorama (container, state, options) { 9 | 10 | this._container = container; 11 | this._coordinates = state.center; 12 | this._options = options; 13 | this._panorama = api.getAPI().panorama; 14 | 15 | return this; 16 | } 17 | 18 | locate (showService) { 19 | 20 | if (this.isSupported()) { 21 | this._panorama.locate(this._coordinates).done( 22 | (panoramas) => { 23 | 24 | this._isPanoramas(Boolean(panoramas.length)); 25 | 26 | if (showService) { 27 | this.show(panoramas); 28 | } 29 | }, 30 | (error) => this.error(error) 31 | ); 32 | } else { 33 | this.error ({ 34 | message: 'Браузер не поддерживается плеером.' 35 | }); 36 | } 37 | } 38 | 39 | show (panoramas) { 40 | if (panoramas.length > 0) { 41 | const player = new this._panorama.Player( 42 | this._container, 43 | panoramas[0], 44 | this._options 45 | ); 46 | 47 | player.events.add('destroy', this.destroy.bind(this)); 48 | } 49 | } 50 | 51 | error (error) { 52 | console.error(error.message); 53 | } 54 | 55 | isSupported () { 56 | return this._panorama.isSupported(); 57 | } 58 | 59 | destroy () { 60 | this._panorama = null; 61 | 62 | if (this._options.parentFunct) { 63 | this._options.parentFunct(); 64 | } 65 | } 66 | } 67 | 68 | export default PanoramaController; 69 | -------------------------------------------------------------------------------- /src/controllers/ImportObjectController.js: -------------------------------------------------------------------------------- 1 | import api from '../api'; 2 | 3 | class ImportObjectController { 4 | constructor (map, data) { 5 | this._map = map; 6 | this._data = data; 7 | 8 | this._setupPresets(); 9 | this._setupGeoObjects(); 10 | } 11 | 12 | destroy () { 13 | this._clearPresets(); 14 | this._geoObject.removeFromMap(this._map); 15 | this._geoObject = null; 16 | this._map = null; 17 | } 18 | 19 | _setupGeoObjects () { 20 | const {geoObjects} = this._data; 21 | const ymaps = api.getAPI(); 22 | 23 | if (!geoObjects) { 24 | return; 25 | } 26 | 27 | this._geoObject = ymaps.geoQuery(this._prepare(geoObjects)).addToMap(this._map); 28 | } 29 | 30 | _prepare (collection) { 31 | const updatedCollection = {...collection}; 32 | 33 | updatedCollection.features.forEach((feature) => { 34 | const props = feature.properties; 35 | if (!props) { 36 | return feature; 37 | } 38 | if (props.name) { 39 | props.balloonContentHeader = props.name; 40 | } 41 | if (props.description) { 42 | props.balloonContentBody = props.description; 43 | } 44 | }); 45 | 46 | return updatedCollection; 47 | } 48 | 49 | _setupPresets () { 50 | const {presetStorage} = this._data; 51 | const ymaps = api.getAPI(); 52 | 53 | if (!presetStorage) { 54 | return; 55 | } 56 | 57 | this._presetKeys = Object.keys(presetStorage); 58 | this._presetKeys.forEach((key) => { 59 | ymaps.option.presetStorage.add(key, presetStorage[key]); 60 | }); 61 | } 62 | 63 | _clearPresets () { 64 | const ymaps = api.getAPI(); 65 | this._presetKeys.forEach((key) => { 66 | ymaps.option.presetStorage.remove(key); 67 | }); 68 | } 69 | } 70 | 71 | export default ImportObjectController; 72 | -------------------------------------------------------------------------------- /lib/utils/loaders/loadApi.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = loadApi; 7 | 8 | var _fetchScript = require('./fetchScript'); 9 | 10 | var _fetchScript2 = _interopRequireDefault(_fetchScript); 11 | 12 | var _configs = require('../../configs'); 13 | 14 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 15 | 16 | var loadPromise = void 0; 17 | 18 | var enabledAPIParams = ['lang', 'apikey', 'coordorder', 'load', 'mode']; 19 | var successCallbackName = '_$_api_success'; 20 | var errorCallbackName = '_$_api_error'; 21 | 22 | var defaultOptions = { 23 | lang: 'ru_RU', 24 | coordorder: 'latlong', 25 | load: 'package.full', 26 | mode: 'release', 27 | ns: '', 28 | onload: successCallbackName, 29 | onerror: errorCallbackName 30 | }; 31 | 32 | function generateURL(options) { 33 | var params = Object.assign({}, defaultOptions); 34 | Object.keys(options).filter(function (key) { 35 | return enabledAPIParams.indexOf(key) !== -1; 36 | }).forEach(function (key) { 37 | params[key] = options[key]; 38 | }); 39 | 40 | var queryString = Object.keys(params).map(function (key) { 41 | return key + '=' + params[key]; 42 | }).join('&'); 43 | 44 | return 'https://' + _configs.apiConfig.host + '/' + (options.version || _configs.apiConfig.version) + '/?' + queryString; 45 | } 46 | 47 | function loadApi(options) { 48 | if (loadPromise) { 49 | return loadPromise; 50 | } 51 | 52 | loadPromise = new Promise(function (resolve, reject) { 53 | 54 | window[successCallbackName] = function (ymaps) { 55 | resolve(ymaps); 56 | window[successCallbackName] = null; 57 | }; 58 | 59 | window[errorCallbackName] = function (error) { 60 | reject(error); 61 | window[errorCallbackName] = null; 62 | }; 63 | 64 | (0, _fetchScript2.default)(generateURL(options)); 65 | }); 66 | 67 | return loadPromise; 68 | } -------------------------------------------------------------------------------- /lib/api.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | 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; }; }(); 8 | 9 | var _loadApi = require('./utils/loaders/loadApi'); 10 | 11 | var _loadApi2 = _interopRequireDefault(_loadApi); 12 | 13 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 14 | 15 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 16 | 17 | var Api = function () { 18 | function Api() { 19 | _classCallCheck(this, Api); 20 | 21 | this.api = typeof window != 'undefined' && window.ymaps ? window.ymaps : null; 22 | } 23 | 24 | _createClass(Api, [{ 25 | key: 'setAPI', 26 | value: function setAPI(instance) { 27 | this.api = instance; 28 | 29 | return this.api; 30 | } 31 | }, { 32 | key: 'getAPI', 33 | value: function getAPI() { 34 | return this.api; 35 | } 36 | }, { 37 | key: 'isAvailible', 38 | value: function isAvailible() { 39 | return Boolean(this.api); 40 | } 41 | 42 | /** 43 | * Loading API 44 | * @return {Promise} 45 | */ 46 | 47 | }, { 48 | key: 'load', 49 | value: function load() { 50 | var _this = this; 51 | 52 | var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 53 | 54 | return (0, _loadApi2.default)(options).then(function (instance) { 55 | _this.api = instance; 56 | return instance; 57 | }); 58 | } 59 | }]); 60 | 61 | return Api; 62 | }(); 63 | 64 | exports.default = new Api(); -------------------------------------------------------------------------------- /lib/BalloonLayout.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | 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; }; }(); 8 | 9 | var _propTypes = require('prop-types'); 10 | 11 | var _propTypes2 = _interopRequireDefault(_propTypes); 12 | 13 | var _react = require('react'); 14 | 15 | var _react2 = _interopRequireDefault(_react); 16 | 17 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 18 | 19 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 20 | 21 | 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; } 22 | 23 | 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; } 24 | 25 | var BalloonLayout = function (_Component) { 26 | _inherits(BalloonLayout, _Component); 27 | 28 | function BalloonLayout() { 29 | _classCallCheck(this, BalloonLayout); 30 | 31 | return _possibleConstructorReturn(this, (BalloonLayout.__proto__ || Object.getPrototypeOf(BalloonLayout)).apply(this, arguments)); 32 | } 33 | 34 | _createClass(BalloonLayout, [{ 35 | key: 'render', 36 | value: function render() { 37 | return _react2.default.createElement( 38 | 'div', 39 | null, 40 | this.props.children 41 | ); 42 | } 43 | }]); 44 | 45 | return BalloonLayout; 46 | }(_react.Component); 47 | 48 | exports.default = BalloonLayout; -------------------------------------------------------------------------------- /lib/MapElement.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | 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; }; }(); 8 | 9 | var _react = require('react'); 10 | 11 | var _react2 = _interopRequireDefault(_react); 12 | 13 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 14 | 15 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 16 | 17 | 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; } 18 | 19 | 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; } 20 | 21 | var style = { 22 | position: 'absolute', 23 | width: '100%', 24 | height: '100%', 25 | margin: 0, 26 | padding: 0 27 | }; 28 | 29 | var MapElement = function (_Component) { 30 | _inherits(MapElement, _Component); 31 | 32 | function MapElement(props) { 33 | _classCallCheck(this, MapElement); 34 | 35 | return _possibleConstructorReturn(this, (MapElement.__proto__ || Object.getPrototypeOf(MapElement)).call(this, props)); 36 | } 37 | 38 | _createClass(MapElement, [{ 39 | key: 'shouldComponentUpdate', 40 | value: function shouldComponentUpdate() { 41 | return false; 42 | } 43 | }, { 44 | key: 'render', 45 | value: function render() { 46 | return _react2.default.createElement('div', { style: style }); 47 | } 48 | }]); 49 | 50 | return MapElement; 51 | }(_react.Component); 52 | 53 | exports.default = MapElement; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # yandex-map-react 2 | 3 | ## Quick start 4 | 5 | ```js 6 | import React from 'react'; 7 | import ReactDOM from 'react-dom'; 8 | import { Map, Marker, MarkerLayout } from 'yandex-map-react'; 9 | 10 | export default function ContactMap (props) { 11 | render () { 12 | return ( 13 | 14 | 15 | 16 | ); 17 | } 18 | } 19 | ``` 20 | 21 | ## Installation 22 | 23 | `yandex-map-react` requires React >= 0.14 24 | 25 | ### npm 26 | 27 | ``` 28 | npm install --save yandex-map-react 29 | ``` 30 | 31 | ## Parameters 32 | 33 | | Parameter | Default value | Type | Decription | 34 | |---------|-----------------------|---------|----------| 35 | | `width` | 600 | Number | container width | 36 | | `height` | 600 | Number | container height | 37 | | `style` | {} | Object | styles that will be applied to container element | 38 | | `loadOptions` | {lang: 'ru_RU', coordorder: 'latlong', load: 'package.full', mode: 'release'} | Object | API loading [params](https://tech.yandex.ru/maps/doc/jsapi/2.1/dg/concepts/load-docpage/). Enabled params: `lang`, `apikey`, `coordorder`, `load`, `mode` | 39 | | [Supported YandexMap API params](https://tech.yandex.com/maps/doc/jsapi/2.1/ref/reference/Map-docpage/) | 40 | | `center` | [55, 45] | Array[Number] | coordinates of map center | 41 | | `zoom` | 10 | Number | zoom level | 42 | | `state` | {controls: []} | Object | describe map state (ex. [controls](https://tech.yandex.com/maps/doc/jsapi/2.1/ref/reference/Map-docpage/#param-state.controls)) | 43 | | Callbacks | 44 | | `onAPIAvailable` | - | Function | callback will be invoked as soon as YandexMAP API available | 45 | 46 | ## Events 47 | 48 | Components support API events, to handle convert first letter of event name to uppercase and add `on` to begin. Example: `mousemove` -> `onMousemove` ([description](https://tech.yandex.com/maps/doc/jsapi/2.1/ref/reference/IDomEventEmitter-docpage/#event-mousemove)). 49 | 50 | ## Features 51 | 52 | Custom Geoobject marker [layout](https://tech.yandex.com/maps/doc/jsapi/2.1/ref/reference/GeoObject-docpage/#param-options.iconLayout). Custom balloon layout - soon. 53 | 54 | ```js 55 | 56 | 57 |
58 | 59 |
60 |
61 |
62 | ``` 63 | 64 | ## Examples 65 | 66 | https://github.com/effrenus/yandex-map-react-examples/ 67 | 68 | ## License 69 | 70 | MIT (http://www.opensource.org/licenses/mit-license.php) 71 | -------------------------------------------------------------------------------- /lib/PanoramaElement.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | 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; }; }(); 8 | 9 | var _react = require('react'); 10 | 11 | var _react2 = _interopRequireDefault(_react); 12 | 13 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 14 | 15 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 16 | 17 | 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; } 18 | 19 | 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; } 20 | 21 | var style = { 22 | position: 'absolute', 23 | width: '100%', 24 | height: '100%', 25 | margin: 0, 26 | padding: 0 27 | }; 28 | 29 | var PanoramaElement = function (_Component) { 30 | _inherits(PanoramaElement, _Component); 31 | 32 | function PanoramaElement() { 33 | _classCallCheck(this, PanoramaElement); 34 | 35 | return _possibleConstructorReturn(this, (PanoramaElement.__proto__ || Object.getPrototypeOf(PanoramaElement)).apply(this, arguments)); 36 | } 37 | 38 | _createClass(PanoramaElement, [{ 39 | key: 'render', 40 | value: function render() { 41 | if (this.props.show) { 42 | return _react2.default.createElement( 43 | 'div', 44 | { style: style }, 45 | ' ' 46 | ); 47 | } else { 48 | return null; 49 | } 50 | } 51 | }]); 52 | 53 | return PanoramaElement; 54 | }(_react.Component); 55 | 56 | exports.default = PanoramaElement; -------------------------------------------------------------------------------- /lib/MarkerLayout.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | 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; }; }(); 8 | 9 | var _propTypes = require('prop-types'); 10 | 11 | var _propTypes2 = _interopRequireDefault(_propTypes); 12 | 13 | var _react = require('react'); 14 | 15 | var _react2 = _interopRequireDefault(_react); 16 | 17 | var _constants = require('./constants'); 18 | 19 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 20 | 21 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 22 | 23 | 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; } 24 | 25 | 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; } 26 | 27 | /** 28 | * @class MarkerLayout 29 | */ 30 | var MarkerLayout = function (_Component) { 31 | _inherits(MarkerLayout, _Component); 32 | 33 | function MarkerLayout() { 34 | _classCallCheck(this, MarkerLayout); 35 | 36 | return _possibleConstructorReturn(this, (MarkerLayout.__proto__ || Object.getPrototypeOf(MarkerLayout)).apply(this, arguments)); 37 | } 38 | 39 | _createClass(MarkerLayout, [{ 40 | key: 'componentWillUnmount', 41 | value: function componentWillUnmount() { 42 | if (this._marker) { 43 | this._marker.destroy(); 44 | } 45 | } 46 | }, { 47 | key: 'render', 48 | value: function render() { 49 | return _react2.default.createElement( 50 | 'div', 51 | null, 52 | this.props.children 53 | ); 54 | } 55 | }]); 56 | 57 | return MarkerLayout; 58 | }(_react.Component); 59 | 60 | MarkerLayout.propTypes = { 61 | marker: _propTypes2.default.object 62 | }; 63 | exports.default = MarkerLayout; -------------------------------------------------------------------------------- /src/controllers/MarkerController.js: -------------------------------------------------------------------------------- 1 | import api from '../api'; 2 | import layouts from './layouts'; 3 | 4 | /** 5 | * @class MarkerController 6 | */ 7 | class MarkerController { 8 | /** 9 | * @constructor 10 | * @param {Number[]} coordinates Marker coordinate 11 | * @param {Object} properties 12 | * @param {Object} options 13 | * @param {HTMLElement} options.markerDOM Marker layout 14 | */ 15 | constructor (coordinates, properties = {}, options = {}, balloonState) { 16 | this.options = options; 17 | this.properties = properties; 18 | this.balloonState = balloonState; 19 | this._coordinates = coordinates; 20 | this._marker = new (api.getAPI()).Placemark(coordinates, null, null); 21 | this._setupMarkerProperties(); 22 | this._setupMarkerOptions(); 23 | this.events = this._marker.events.group(); 24 | } 25 | 26 | /** 27 | * @return {Object} Return marker instance (specific for MAPAPI) 28 | */ 29 | getAPIInstance () { 30 | return this._marker; 31 | } 32 | 33 | /** 34 | * @return {Number[]} Marker coordinates 35 | */ 36 | getCoordinates () { 37 | return this._coordinates; 38 | } 39 | 40 | setPosition (coordinates) { 41 | this._marker.geometry.setCoordinates(coordinates); 42 | } 43 | 44 | setProperty (propName, value) { 45 | this._marker.properties.set(propName, value); 46 | } 47 | 48 | setOption (optName, value) { 49 | this._marker.options.set(optName, value); 50 | } 51 | 52 | setBalloonState(state) { 53 | if (state === 'opened') { 54 | if (!this._marker.balloon.isOpen()) { 55 | this._marker.balloon.open(); 56 | } 57 | } else { 58 | if (this._marker.balloon.isOpen()) { 59 | this._marker.balloon.close(); 60 | } 61 | } 62 | } 63 | 64 | /** 65 | * 66 | * @param {String} name 67 | * @param {HTMLElement} element 68 | */ 69 | setLayout (name, element) { 70 | let layout; 71 | 72 | if (name === 'iconLayout') { 73 | layout = layouts.createIconLayoutClass(element); 74 | } else if (name === 'balloonLayout') { 75 | layout = layouts.createBalloonLayoutClass(element); 76 | } 77 | 78 | this._marker.options.set(name, layout); 79 | } 80 | 81 | /** 82 | * Destroy marker 83 | */ 84 | destroy () { 85 | this.events.removeAll(); 86 | this._marker.setParent(null); 87 | this._marker = null; 88 | } 89 | 90 | _setupMarkerProperties () { 91 | const {properties} = this; 92 | Object.keys(properties).forEach(propName => { 93 | this.setProperty(propName, properties[propName]); 94 | }); 95 | } 96 | 97 | _setupMarkerOptions () { 98 | const {options} = this; 99 | Object.keys(options).forEach(optName => { 100 | this.setOption(optName, options[optName]); 101 | }); 102 | } 103 | } 104 | 105 | export default MarkerController; 106 | -------------------------------------------------------------------------------- /lib/controllers/MapController.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | 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; }; }(); 8 | 9 | var _api = require('../api'); 10 | 11 | var _api2 = _interopRequireDefault(_api); 12 | 13 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 14 | 15 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 16 | 17 | var MapController = function () { 18 | function MapController() { 19 | _classCallCheck(this, MapController); 20 | } 21 | 22 | _createClass(MapController, [{ 23 | key: 'createMap', 24 | value: function createMap(container, state, options) { 25 | this._map = new (_api2.default.getAPI().Map)(container, state, options); 26 | this.events = this._map.events.group(); 27 | 28 | this._setupCollection(); 29 | 30 | return this; 31 | } 32 | }, { 33 | key: 'appendMarker', 34 | value: function appendMarker(marker) { 35 | this._geoCollection.add(marker.getAPIInstance()); 36 | marker.setBalloonState(marker.balloonState); 37 | } 38 | }, { 39 | key: 'setOptions', 40 | value: function setOptions(name, value) { 41 | this._map.options.set(name, value); 42 | } 43 | }, { 44 | key: 'setCenter', 45 | value: function setCenter(coords) { 46 | this._map.setCenter(coords); 47 | } 48 | }, { 49 | key: 'setZoom', 50 | value: function setZoom(zoom) { 51 | this._map.setZoom(zoom); 52 | } 53 | }, { 54 | key: 'setBounds', 55 | value: function setBounds(bounds) { 56 | this._map.setBounds(bounds); 57 | } 58 | }, { 59 | key: 'setState', 60 | value: function setState(name, value) { 61 | this._map.state.set(name, value); 62 | } 63 | }, { 64 | key: 'destroy', 65 | value: function destroy() { 66 | this.events.removeAll(); 67 | this._map.destroy(); 68 | } 69 | }, { 70 | key: '_setupCollection', 71 | value: function _setupCollection() { 72 | this._geoCollection = new (_api2.default.getAPI().GeoObjectCollection)(); 73 | this._map.geoObjects.add(this._geoCollection); 74 | } 75 | }, { 76 | key: 'map', 77 | get: function get() { 78 | return this._map; 79 | } 80 | }]); 81 | 82 | return MapController; 83 | }(); 84 | 85 | exports.default = MapController; -------------------------------------------------------------------------------- /lib/controllers/PanoramaController.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | 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; }; }(); 8 | 9 | var _api = require('../api'); 10 | 11 | var _api2 = _interopRequireDefault(_api); 12 | 13 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 14 | 15 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 16 | 17 | var PanoramaController = function () { 18 | function PanoramaController(func) { 19 | _classCallCheck(this, PanoramaController); 20 | 21 | this._isPanoramas = func; 22 | } 23 | 24 | _createClass(PanoramaController, [{ 25 | key: 'createPanorama', 26 | value: function createPanorama(container, state, options) { 27 | 28 | this._container = container; 29 | this._coordinates = state.center; 30 | this._options = options; 31 | this._panorama = _api2.default.getAPI().panorama; 32 | 33 | return this; 34 | } 35 | }, { 36 | key: 'locate', 37 | value: function locate(showService) { 38 | var _this = this; 39 | 40 | if (this.isSupported()) { 41 | this._panorama.locate(this._coordinates).done(function (panoramas) { 42 | 43 | _this._isPanoramas(Boolean(panoramas.length)); 44 | 45 | if (showService) { 46 | _this.show(panoramas); 47 | } 48 | }, function (error) { 49 | return _this.error(error); 50 | }); 51 | } else { 52 | this.error({ 53 | message: 'Браузер не поддерживается плеером.' 54 | }); 55 | } 56 | } 57 | }, { 58 | key: 'show', 59 | value: function show(panoramas) { 60 | if (panoramas.length > 0) { 61 | var player = new this._panorama.Player(this._container, panoramas[0], this._options); 62 | 63 | player.events.add('destroy', this.destroy.bind(this)); 64 | } 65 | } 66 | }, { 67 | key: 'error', 68 | value: function error(_error) { 69 | console.error(_error.message); 70 | } 71 | }, { 72 | key: 'isSupported', 73 | value: function isSupported() { 74 | return this._panorama.isSupported(); 75 | } 76 | }, { 77 | key: 'destroy', 78 | value: function destroy() { 79 | this._panorama = null; 80 | 81 | if (this._options.parentFunct) { 82 | this._options.parentFunct(); 83 | } 84 | } 85 | }]); 86 | 87 | return PanoramaController; 88 | }(); 89 | 90 | exports.default = PanoramaController; -------------------------------------------------------------------------------- /lib/ConstructorJSONImport.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | 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; }; }(); 8 | 9 | var _propTypes = require('prop-types'); 10 | 11 | var _propTypes2 = _interopRequireDefault(_propTypes); 12 | 13 | var _react = require('react'); 14 | 15 | var _react2 = _interopRequireDefault(_react); 16 | 17 | var _ImportObjectController = require('./controllers/ImportObjectController'); 18 | 19 | var _ImportObjectController2 = _interopRequireDefault(_ImportObjectController); 20 | 21 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 22 | 23 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 24 | 25 | 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; } 26 | 27 | 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; } 28 | 29 | var ConstructorJSONImport = function (_Component) { 30 | _inherits(ConstructorJSONImport, _Component); 31 | 32 | function ConstructorJSONImport() { 33 | _classCallCheck(this, ConstructorJSONImport); 34 | 35 | return _possibleConstructorReturn(this, (ConstructorJSONImport.__proto__ || Object.getPrototypeOf(ConstructorJSONImport)).apply(this, arguments)); 36 | } 37 | 38 | _createClass(ConstructorJSONImport, [{ 39 | key: 'componentDidMount', 40 | value: function componentDidMount() { 41 | var map = this.context.mapController.map; 42 | 43 | this._controller = new _ImportObjectController2.default(map, this.props.userMapData); 44 | } 45 | }, { 46 | key: 'componentWillUnmount', 47 | value: function componentWillUnmount() { 48 | this._controller.destroy(); 49 | } 50 | }, { 51 | key: 'shouldComponentUpdate', 52 | value: function shouldComponentUpdate() { 53 | return false; 54 | } 55 | }, { 56 | key: 'render', 57 | value: function render() { 58 | return null; 59 | } 60 | }]); 61 | 62 | return ConstructorJSONImport; 63 | }(_react.Component); 64 | 65 | ConstructorJSONImport.propTypes = { 66 | userMapData: _propTypes2.default.object.isRequired 67 | }; 68 | ConstructorJSONImport.contextTypes = { 69 | mapController: _propTypes2.default.object 70 | }; 71 | exports.default = ConstructorJSONImport; -------------------------------------------------------------------------------- /src/controllers/layouts.js: -------------------------------------------------------------------------------- 1 | import api from '../api'; 2 | 3 | function detectImagesLoaded (element) { 4 | const images = Array.from(element.querySelectorAll('img') || []); 5 | 6 | if (images.length === 0) { 7 | return Promise.resolve(); 8 | } 9 | 10 | return Promise.all(images.map((image) => { 11 | return new Promise((resolve) => { 12 | if (image.complete) { 13 | resolve(); 14 | return; 15 | } 16 | image.onload = image.onerror = resolve; 17 | }); 18 | })); 19 | } 20 | 21 | function createLayout ({domElement, extendMethods = {}}) { 22 | const LayoutClass = (api.getAPI()).templateLayoutFactory.createClass('', Object.assign({ 23 | build: function () { 24 | LayoutClass.superclass.build.call(this); 25 | 26 | this.options = this.getData().options; 27 | 28 | this._setupContent(domElement); 29 | this._updateSize(); 30 | 31 | detectImagesLoaded(this.getElement()).then(this._updateMarkerShape.bind(this)); 32 | }, 33 | 34 | getShape: function () { 35 | return new (api.getAPI()).shape.Rectangle( 36 | new (api.getAPI()).geometry.pixel.Rectangle( 37 | [ 38 | [0, 0], 39 | [this._size[0], this._size[1]] 40 | ] 41 | ) 42 | ); 43 | }, 44 | 45 | _updateMarkerShape: function () { 46 | this._updateSize(); 47 | this.events.fire('shapechange'); 48 | }, 49 | 50 | _setupContent: function (domElement) { 51 | const element = this.getElement(); 52 | element.appendChild(domElement); 53 | }, 54 | 55 | _updateSize: function () { 56 | this._size = this._getSize(); 57 | }, 58 | 59 | _getSize: function () { 60 | let elementSize = []; 61 | 62 | if (this.getElement()) { 63 | const element = this.getElement().querySelector('.icon-content'); 64 | 65 | if (element) { 66 | elementSize = [element.offsetWidth, element.offsetHeight]; 67 | } 68 | } 69 | 70 | return elementSize; 71 | } 72 | }, extendMethods)); 73 | 74 | return LayoutClass; 75 | } 76 | 77 | export default { 78 | createIconLayoutClass: function (domElement) { 79 | return createLayout({ 80 | domElement, 81 | extendMethods: { 82 | _updateSize: function () { 83 | let geoObject; 84 | const oldSize = this._size; 85 | 86 | this._size = this._getSize(); 87 | 88 | // Update layout offset. 89 | if (this._size.length) { 90 | if (!oldSize || (oldSize[0] !== this._size[0] || oldSize[1] !== this._size[1])) { 91 | geoObject = this.getData().geoObject; 92 | 93 | if (geoObject.getOverlaySync()) { 94 | geoObject.options.set('iconOffset', [-this._size[0] / 2, -this._size[1]]); 95 | } else { 96 | geoObject.getOverlay().then(() => { 97 | geoObject.options.set('iconOffset', [-this._size[0] / 2, -this._size[1]]); 98 | }); 99 | } 100 | } 101 | } 102 | } 103 | } 104 | }); 105 | }, 106 | 107 | createBalloonLayoutClass: function (domElement) { 108 | return createLayout({domElement}); 109 | } 110 | }; 111 | -------------------------------------------------------------------------------- /lib/controllers/ImportObjectController.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | 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; }; 8 | 9 | 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; }; }(); 10 | 11 | var _api = require('../api'); 12 | 13 | var _api2 = _interopRequireDefault(_api); 14 | 15 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 16 | 17 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 18 | 19 | var ImportObjectController = function () { 20 | function ImportObjectController(map, data) { 21 | _classCallCheck(this, ImportObjectController); 22 | 23 | this._map = map; 24 | this._data = data; 25 | 26 | this._setupPresets(); 27 | this._setupGeoObjects(); 28 | } 29 | 30 | _createClass(ImportObjectController, [{ 31 | key: 'destroy', 32 | value: function destroy() { 33 | this._clearPresets(); 34 | this._geoObject.removeFromMap(this._map); 35 | this._geoObject = null; 36 | this._map = null; 37 | } 38 | }, { 39 | key: '_setupGeoObjects', 40 | value: function _setupGeoObjects() { 41 | var geoObjects = this._data.geoObjects; 42 | 43 | var ymaps = _api2.default.getAPI(); 44 | 45 | if (!geoObjects) { 46 | return; 47 | } 48 | 49 | this._geoObject = ymaps.geoQuery(this._prepare(geoObjects)).addToMap(this._map); 50 | } 51 | }, { 52 | key: '_prepare', 53 | value: function _prepare(collection) { 54 | var updatedCollection = _extends({}, collection); 55 | 56 | updatedCollection.features.forEach(function (feature) { 57 | var props = feature.properties; 58 | if (!props) { 59 | return feature; 60 | } 61 | if (props.name) { 62 | props.balloonContentHeader = props.name; 63 | } 64 | if (props.description) { 65 | props.balloonContentBody = props.description; 66 | } 67 | }); 68 | 69 | return updatedCollection; 70 | } 71 | }, { 72 | key: '_setupPresets', 73 | value: function _setupPresets() { 74 | var presetStorage = this._data.presetStorage; 75 | 76 | var ymaps = _api2.default.getAPI(); 77 | 78 | if (!presetStorage) { 79 | return; 80 | } 81 | 82 | this._presetKeys = Object.keys(presetStorage); 83 | this._presetKeys.forEach(function (key) { 84 | ymaps.option.presetStorage.add(key, presetStorage[key]); 85 | }); 86 | } 87 | }, { 88 | key: '_clearPresets', 89 | value: function _clearPresets() { 90 | var ymaps = _api2.default.getAPI(); 91 | this._presetKeys.forEach(function (key) { 92 | ymaps.option.presetStorage.remove(key); 93 | }); 94 | } 95 | }]); 96 | 97 | return ImportObjectController; 98 | }(); 99 | 100 | exports.default = ImportObjectController; -------------------------------------------------------------------------------- /lib/controllers/layouts.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _api = require('../api'); 8 | 9 | var _api2 = _interopRequireDefault(_api); 10 | 11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 12 | 13 | function detectImagesLoaded(element) { 14 | var images = Array.from(element.querySelectorAll('img') || []); 15 | 16 | if (images.length === 0) { 17 | return Promise.resolve(); 18 | } 19 | 20 | return Promise.all(images.map(function (image) { 21 | return new Promise(function (resolve) { 22 | if (image.complete) { 23 | resolve(); 24 | return; 25 | } 26 | image.onload = image.onerror = resolve; 27 | }); 28 | })); 29 | } 30 | 31 | function createLayout(_ref) { 32 | var domElement = _ref.domElement, 33 | _ref$extendMethods = _ref.extendMethods, 34 | extendMethods = _ref$extendMethods === undefined ? {} : _ref$extendMethods; 35 | 36 | var LayoutClass = _api2.default.getAPI().templateLayoutFactory.createClass('', Object.assign({ 37 | build: function build() { 38 | LayoutClass.superclass.build.call(this); 39 | 40 | this.options = this.getData().options; 41 | 42 | this._setupContent(domElement); 43 | this._updateSize(); 44 | 45 | detectImagesLoaded(this.getElement()).then(this._updateMarkerShape.bind(this)); 46 | }, 47 | 48 | getShape: function getShape() { 49 | return new (_api2.default.getAPI().shape.Rectangle)(new (_api2.default.getAPI().geometry.pixel.Rectangle)([[0, 0], [this._size[0], this._size[1]]])); 50 | }, 51 | 52 | _updateMarkerShape: function _updateMarkerShape() { 53 | this._updateSize(); 54 | this.events.fire('shapechange'); 55 | }, 56 | 57 | _setupContent: function _setupContent(domElement) { 58 | var element = this.getElement(); 59 | element.appendChild(domElement); 60 | }, 61 | 62 | _updateSize: function _updateSize() { 63 | this._size = this._getSize(); 64 | }, 65 | 66 | _getSize: function _getSize() { 67 | var elementSize = []; 68 | 69 | if (this.getElement()) { 70 | var element = this.getElement().querySelector('.icon-content'); 71 | 72 | if (element) { 73 | elementSize = [element.offsetWidth, element.offsetHeight]; 74 | } 75 | } 76 | 77 | return elementSize; 78 | } 79 | }, extendMethods)); 80 | 81 | return LayoutClass; 82 | } 83 | 84 | exports.default = { 85 | createIconLayoutClass: function createIconLayoutClass(domElement) { 86 | return createLayout({ 87 | domElement: domElement, 88 | extendMethods: { 89 | _updateSize: function _updateSize() { 90 | var _this = this; 91 | 92 | var geoObject = void 0; 93 | var oldSize = this._size; 94 | 95 | this._size = this._getSize(); 96 | 97 | // Update layout offset. 98 | if (this._size.length) { 99 | if (!oldSize || oldSize[0] !== this._size[0] || oldSize[1] !== this._size[1]) { 100 | geoObject = this.getData().geoObject; 101 | 102 | if (geoObject.getOverlaySync()) { 103 | geoObject.options.set('iconOffset', [-this._size[0] / 2, -this._size[1]]); 104 | } else { 105 | geoObject.getOverlay().then(function () { 106 | geoObject.options.set('iconOffset', [-_this._size[0] / 2, -_this._size[1]]); 107 | }); 108 | } 109 | } 110 | } 111 | } 112 | } 113 | }); 114 | }, 115 | 116 | createBalloonLayoutClass: function createBalloonLayoutClass(domElement) { 117 | return createLayout({ domElement: domElement }); 118 | } 119 | }; -------------------------------------------------------------------------------- /src/MapMarker.jsx: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React, { Component } from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import BalloonLayout from './BalloonLayout' 5 | import MarkerLayout from './MarkerLayout' 6 | import MarkerController from './controllers/MarkerController'; 7 | import supportEvents from './apiEventsLists/geoObject'; 8 | import {eventsDecorator} from './utils/decorators'; 9 | 10 | class MapMarker extends Component { 11 | static propTypes = { 12 | lat: PropTypes.number.isRequired, 13 | lon: PropTypes.number.isRequired, 14 | properties: PropTypes.object, 15 | options: PropTypes.object, 16 | balloonState: PropTypes.oneOf(['opened', 'closed']), 17 | } 18 | 19 | static defaultProps = { 20 | balloonState: 'closed' 21 | } 22 | 23 | static contextTypes = { 24 | mapController: PropTypes.object, 25 | coordorder: PropTypes.oneOf(['latlong', 'longlat']) 26 | } 27 | 28 | constructor (props) { 29 | super(props); 30 | this.options = {}; 31 | } 32 | 33 | componentDidUpdate (prevProps) { 34 | const {lat, lon, children, properties, options, balloonState} = this.props; 35 | 36 | if (lat !== prevProps.lat || lon !== prevProps.lon) { 37 | this._controller.setPosition((this.context.coordorder === 'longlat') ? [lon, lat] : [lat, lon]); 38 | } 39 | 40 | Object.keys(properties || {}).forEach(propName => { 41 | if (!prevProps.properties || properties[propName] !== prevProps.properties[propName]) { 42 | this._controller.setProperty(propName, properties[propName]); 43 | } 44 | }); 45 | 46 | Object.keys(options || {}).forEach(optName => { 47 | if (!prevProps.options || options[optName] !== prevProps.options[optName]) { 48 | this._controller.setOption(optName, options[optName]); 49 | } 50 | }); 51 | 52 | this._controller.setBalloonState(balloonState); 53 | 54 | if (children != prevProps.children) { 55 | this._clearLayouts(); 56 | this._setupLayouts(); 57 | } 58 | } 59 | 60 | componentDidMount () { 61 | const {lat, lon, properties, options, balloonState} = this.props; 62 | const coords = (this.context.coordorder === 'longlat') ? [lon, lat] : [lat, lon]; 63 | 64 | this._controller = new MarkerController(coords, properties, options, balloonState); 65 | 66 | this._setupLayouts(); 67 | this._setupEvents(); 68 | 69 | this.context.mapController.appendMarker(this._controller); 70 | } 71 | 72 | componentWillUnmount () { 73 | this._clearLayouts(); 74 | this._controller.destroy(); 75 | } 76 | 77 | getController () { 78 | return this._controller ? this._controller : null; 79 | } 80 | 81 | _setupLayouts () { 82 | React.Children 83 | .toArray(this.props.children) 84 | .forEach(component => { 85 | if (component.type === BalloonLayout) { 86 | this._setupBalloonLayout(component); 87 | } 88 | if (component.type === MarkerLayout) { 89 | this._setupMarkerLayout(component); 90 | } 91 | }); 92 | } 93 | 94 | _setupMarkerLayout (component) { 95 | this._markerElement = document.createElement('div'); 96 | this._markerElement.className = 'icon-content'; 97 | this._markerElement.style.display = 'inline-block'; 98 | 99 | ReactDOM.render(component, this._markerElement); 100 | this._controller.setLayout('iconLayout', this._markerElement); 101 | } 102 | 103 | _setupBalloonLayout (component) { 104 | this._balloonElement = document.createElement('div'); 105 | 106 | ReactDOM.render(component, this._balloonElement); 107 | this._controller.setLayout('balloonLayout', this._balloonElement); 108 | } 109 | 110 | _clearLayouts () { 111 | if (this._markerElement) { 112 | ReactDOM.unmountComponentAtNode(this._markerElement); 113 | this._markerElement = null; 114 | } 115 | 116 | if (this._balloonElement) { 117 | ReactDOM.unmountComponentAtNode(this._balloonElement); 118 | this._balloonElement = null; 119 | } 120 | } 121 | 122 | render () { 123 | return null; 124 | } 125 | } 126 | 127 | export default eventsDecorator(MapMarker, {supportEvents}); 128 | -------------------------------------------------------------------------------- /src/PanoramaContainer.jsx: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React, { Component } from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import PanoramaElement from './PanoramaElement'; 5 | import PanoramaController from './controllers/PanoramaController'; 6 | import supportEvents from './apiEventsLists/map'; 7 | import {eventsDecorator} from './utils/decorators'; 8 | import api from './api'; 9 | 10 | class YandexPanorama extends Component { 11 | static propTypes = { 12 | apiKey: PropTypes.string, 13 | onAPIAvailable: PropTypes.func, 14 | width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 15 | height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 16 | zoom: PropTypes.number, 17 | state: PropTypes.object, 18 | options: PropTypes.object, 19 | loadOptions: PropTypes.object, 20 | bounds: PropTypes.array 21 | } 22 | 23 | static defaultProps = { 24 | zoom: 10, 25 | center: [55, 45], 26 | width: 600, 27 | height: 600, 28 | bounds: undefined, 29 | state: { 30 | controls: [] 31 | }, 32 | options: {}, 33 | loadOptions: {}, 34 | style: { 35 | position: 'relative' 36 | } 37 | } 38 | 39 | static childContextTypes = { 40 | mapController: PropTypes.object, 41 | coordorder: PropTypes.oneOf(['latlong', 'longlat']) 42 | } 43 | 44 | constructor (props) { 45 | super(props); 46 | this.state = { 47 | isAPILoaded: false, 48 | showService: false, 49 | isPanoramas: false 50 | }; 51 | } 52 | 53 | getChildContext () { 54 | return { 55 | mapController: this._controller, 56 | coordorder: this.props.loadOptions.coordorder || 'latlong' 57 | }; 58 | } 59 | 60 | getController () { 61 | return this._controller ? this._controller : null; 62 | } 63 | 64 | componentWillReceiveProps (nextProps) { 65 | this._controller && Object.keys(nextProps).forEach(key => { 66 | switch (key) { 67 | case 'showService': 68 | if (this.state.showService !== nextProps.showService) { 69 | this.setState({ 70 | showService: nextProps.showService 71 | }, () => this.state.showService && this.init()); 72 | } 73 | break; 74 | default: 75 | break; 76 | } 77 | }); 78 | } 79 | 80 | componentDidMount () { 81 | this.init(); 82 | } 83 | 84 | init () { 85 | if (api.isAvailible()) { 86 | this._onAPILoad(api.getAPI()); 87 | } else { 88 | api.load(this.props.loadOptions) 89 | .then(this._onAPILoad.bind(this)) 90 | .catch((error) => console.log('Error occured: %s', error)); 91 | } 92 | } 93 | 94 | isPanoramas = (isPanoramas) => { 95 | this.setState({ 96 | isPanoramas 97 | }) 98 | } 99 | 100 | render () { 101 | 102 | let style = {}; 103 | if (this.state.showService) { 104 | style = this._getStyle(); 105 | } 106 | 107 | return ( 108 |
109 |
110 | 111 |
112 | 113 | { 114 | !this.state.showService && this.state.isPanoramas && this.props.children 115 | } 116 |
117 | ); 118 | } 119 | 120 | _getStyle () { 121 | return { 122 | ...this.props.style, 123 | width: typeof this.props.width == 'string' ? this.props.width : `${this.props.width}px`, 124 | height: typeof this.props.height == 'string' ? this.props.height : `${this.props.height}px` 125 | }; 126 | } 127 | 128 | _onAPILoad (namespace) { 129 | this.props.onAPIAvailable && this.props.onAPIAvailable(namespace); 130 | 131 | this._controller = new PanoramaController(this.isPanoramas); 132 | this._controller.createPanorama( 133 | ReactDOM.findDOMNode(this.refs.panoramaPlayer), 134 | { 135 | ...this.props.state, 136 | center: this.props.center, 137 | zoom: this.props.zoom, 138 | bounds: this.props.bounds 139 | }, 140 | {...this.props.options} 141 | ); 142 | 143 | this.setState({isAPILoaded: true}, () => this._controller.locate(this.state.showService)); 144 | } 145 | } 146 | 147 | export default eventsDecorator(YandexPanorama, {supportEvents}); 148 | -------------------------------------------------------------------------------- /src/MapContainer.jsx: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React, { Component } from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import MapElement from './MapElement'; 5 | import MapController from './controllers/MapController'; 6 | import supportEvents from './apiEventsLists/map'; 7 | import {eventsDecorator} from './utils/decorators'; 8 | import config from './configs'; 9 | import api from './api'; 10 | 11 | class YandexMap extends Component { 12 | static propTypes = { 13 | apiKey: PropTypes.string, 14 | onAPIAvailable: PropTypes.func, 15 | width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 16 | height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 17 | zoom: PropTypes.number, 18 | state: PropTypes.object, 19 | options: PropTypes.object, 20 | loadOptions: PropTypes.object, 21 | bounds: PropTypes.array 22 | } 23 | 24 | static defaultProps = { 25 | zoom: 10, 26 | center: [55, 45], 27 | width: 600, 28 | height: 600, 29 | bounds: undefined, 30 | state: { 31 | controls: [] 32 | }, 33 | options: {}, 34 | loadOptions: {}, 35 | style: { 36 | position: 'relative' 37 | } 38 | } 39 | 40 | static childContextTypes = { 41 | mapController: PropTypes.object, 42 | coordorder: PropTypes.oneOf(['latlong', 'longlat']) 43 | } 44 | 45 | constructor (props) { 46 | super(props); 47 | this.state = { 48 | isAPILoaded: false 49 | }; 50 | } 51 | 52 | getChildContext () { 53 | return { 54 | mapController: this._controller, 55 | coordorder: this.props.loadOptions.coordorder || 'latlong' 56 | }; 57 | } 58 | 59 | getController () { 60 | return this._controller ? this._controller : null; 61 | } 62 | 63 | componentWillReceiveProps (nextProps) { 64 | this._controller && Object.keys(nextProps).forEach(key => { 65 | switch (key) { 66 | case 'controls': 67 | this._controller.setState(key, nextProps[key]); 68 | break; 69 | case 'center': 70 | if (this.props.center[0] !== nextProps.center[0] 71 | || this.props.center[1] !== nextProps.center[1] ) { 72 | this._controller.setCenter(nextProps.center); 73 | } 74 | 75 | break; 76 | case 'zoom': 77 | if (this.props.zoom !== nextProps.zoom) { 78 | this._controller.setZoom(nextProps.zoom); 79 | } 80 | 81 | break; 82 | case 'bounds': 83 | if (this.props.bounds !== nextProps.bounds) { 84 | this._controller.setBounds(nextProps.bounds); 85 | } 86 | 87 | break; 88 | default: 89 | break; 90 | } 91 | }); 92 | } 93 | 94 | componentDidMount () { 95 | if (api.isAvailible()) { 96 | this._onAPILoad(api.getAPI()); 97 | } else { 98 | api.load(this.props.loadOptions) 99 | .then(this._onAPILoad.bind(this)) 100 | .catch((error) => console.log('Error occured: %s', error)); 101 | } 102 | } 103 | 104 | render () { 105 | return ( 106 |
107 | 108 | {Boolean(this.state.isAPILoaded) ? this.props.children : null} 109 |
110 | ); 111 | } 112 | 113 | _getStyle () { 114 | return { 115 | ...this.props.style, 116 | width: typeof this.props.width == 'string' ? this.props.width : `${this.props.width}px`, 117 | height: typeof this.props.height == 'string' ? this.props.height : `${this.props.height}px` 118 | }; 119 | } 120 | 121 | _onAPILoad (namespace) { 122 | this.props.onAPIAvailable && this.props.onAPIAvailable(namespace); 123 | 124 | this._controller = new MapController(); 125 | this._controller.createMap( 126 | ReactDOM.findDOMNode(this.refs.mapContainer), 127 | { 128 | ...this.props.state, 129 | center: this.props.center, 130 | zoom: this.props.zoom, 131 | bounds: this.props.bounds 132 | }, 133 | {...this.props.options} 134 | ); 135 | 136 | this._setupEvents(); 137 | this.setState({isAPILoaded: true}); 138 | 139 | if (this.props.onMapAvailable) { 140 | this.props.onMapAvailable(this._controller.map); 141 | } 142 | } 143 | } 144 | 145 | export default eventsDecorator(YandexMap, {supportEvents}); 146 | -------------------------------------------------------------------------------- /lib/controllers/MarkerController.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | 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; }; }(); 8 | 9 | var _api = require('../api'); 10 | 11 | var _api2 = _interopRequireDefault(_api); 12 | 13 | var _layouts = require('./layouts'); 14 | 15 | var _layouts2 = _interopRequireDefault(_layouts); 16 | 17 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 18 | 19 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 20 | 21 | /** 22 | * @class MarkerController 23 | */ 24 | var MarkerController = function () { 25 | /** 26 | * @constructor 27 | * @param {Number[]} coordinates Marker coordinate 28 | * @param {Object} properties 29 | * @param {Object} options 30 | * @param {HTMLElement} options.markerDOM Marker layout 31 | */ 32 | function MarkerController(coordinates) { 33 | var properties = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 34 | var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 35 | var balloonState = arguments[3]; 36 | 37 | _classCallCheck(this, MarkerController); 38 | 39 | this.options = options; 40 | this.properties = properties; 41 | this.balloonState = balloonState; 42 | this._coordinates = coordinates; 43 | this._marker = new (_api2.default.getAPI().Placemark)(coordinates, null, null); 44 | this._setupMarkerProperties(); 45 | this._setupMarkerOptions(); 46 | this.events = this._marker.events.group(); 47 | } 48 | 49 | /** 50 | * @return {Object} Return marker instance (specific for MAPAPI) 51 | */ 52 | 53 | 54 | _createClass(MarkerController, [{ 55 | key: 'getAPIInstance', 56 | value: function getAPIInstance() { 57 | return this._marker; 58 | } 59 | 60 | /** 61 | * @return {Number[]} Marker coordinates 62 | */ 63 | 64 | }, { 65 | key: 'getCoordinates', 66 | value: function getCoordinates() { 67 | return this._coordinates; 68 | } 69 | }, { 70 | key: 'setPosition', 71 | value: function setPosition(coordinates) { 72 | this._marker.geometry.setCoordinates(coordinates); 73 | } 74 | }, { 75 | key: 'setProperty', 76 | value: function setProperty(propName, value) { 77 | this._marker.properties.set(propName, value); 78 | } 79 | }, { 80 | key: 'setOption', 81 | value: function setOption(optName, value) { 82 | this._marker.options.set(optName, value); 83 | } 84 | }, { 85 | key: 'setBalloonState', 86 | value: function setBalloonState(state) { 87 | if (state === 'opened') { 88 | if (!this._marker.balloon.isOpen()) { 89 | this._marker.balloon.open(); 90 | } 91 | } else { 92 | if (this._marker.balloon.isOpen()) { 93 | this._marker.balloon.close(); 94 | } 95 | } 96 | } 97 | 98 | /** 99 | * 100 | * @param {String} name 101 | * @param {HTMLElement} element 102 | */ 103 | 104 | }, { 105 | key: 'setLayout', 106 | value: function setLayout(name, element) { 107 | var layout = void 0; 108 | 109 | if (name === 'iconLayout') { 110 | layout = _layouts2.default.createIconLayoutClass(element); 111 | } else if (name === 'balloonLayout') { 112 | layout = _layouts2.default.createBalloonLayoutClass(element); 113 | } 114 | 115 | this._marker.options.set(name, layout); 116 | } 117 | 118 | /** 119 | * Destroy marker 120 | */ 121 | 122 | }, { 123 | key: 'destroy', 124 | value: function destroy() { 125 | this.events.removeAll(); 126 | this._marker.setParent(null); 127 | this._marker = null; 128 | } 129 | }, { 130 | key: '_setupMarkerProperties', 131 | value: function _setupMarkerProperties() { 132 | var _this = this; 133 | 134 | var properties = this.properties; 135 | 136 | Object.keys(properties).forEach(function (propName) { 137 | _this.setProperty(propName, properties[propName]); 138 | }); 139 | } 140 | }, { 141 | key: '_setupMarkerOptions', 142 | value: function _setupMarkerOptions() { 143 | var _this2 = this; 144 | 145 | var options = this.options; 146 | 147 | Object.keys(options).forEach(function (optName) { 148 | _this2.setOption(optName, options[optName]); 149 | }); 150 | } 151 | }]); 152 | 153 | return MarkerController; 154 | }(); 155 | 156 | exports.default = MarkerController; -------------------------------------------------------------------------------- /lib/MapMarker.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | 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; }; }(); 8 | 9 | var _propTypes = require('prop-types'); 10 | 11 | var _propTypes2 = _interopRequireDefault(_propTypes); 12 | 13 | var _react = require('react'); 14 | 15 | var _react2 = _interopRequireDefault(_react); 16 | 17 | var _reactDom = require('react-dom'); 18 | 19 | var _reactDom2 = _interopRequireDefault(_reactDom); 20 | 21 | var _BalloonLayout = require('./BalloonLayout'); 22 | 23 | var _BalloonLayout2 = _interopRequireDefault(_BalloonLayout); 24 | 25 | var _MarkerLayout = require('./MarkerLayout'); 26 | 27 | var _MarkerLayout2 = _interopRequireDefault(_MarkerLayout); 28 | 29 | var _MarkerController = require('./controllers/MarkerController'); 30 | 31 | var _MarkerController2 = _interopRequireDefault(_MarkerController); 32 | 33 | var _geoObject = require('./apiEventsLists/geoObject'); 34 | 35 | var _geoObject2 = _interopRequireDefault(_geoObject); 36 | 37 | var _decorators = require('./utils/decorators'); 38 | 39 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 40 | 41 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 42 | 43 | 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; } 44 | 45 | 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; } 46 | 47 | var MapMarker = function (_Component) { 48 | _inherits(MapMarker, _Component); 49 | 50 | function MapMarker(props) { 51 | _classCallCheck(this, MapMarker); 52 | 53 | var _this = _possibleConstructorReturn(this, (MapMarker.__proto__ || Object.getPrototypeOf(MapMarker)).call(this, props)); 54 | 55 | _this.options = {}; 56 | return _this; 57 | } 58 | 59 | _createClass(MapMarker, [{ 60 | key: 'componentDidUpdate', 61 | value: function componentDidUpdate(prevProps) { 62 | var _this2 = this; 63 | 64 | var _props = this.props, 65 | lat = _props.lat, 66 | lon = _props.lon, 67 | children = _props.children, 68 | properties = _props.properties, 69 | options = _props.options, 70 | balloonState = _props.balloonState; 71 | 72 | 73 | if (lat !== prevProps.lat || lon !== prevProps.lon) { 74 | this._controller.setPosition(this.context.coordorder === 'longlat' ? [lon, lat] : [lat, lon]); 75 | } 76 | 77 | Object.keys(properties || {}).forEach(function (propName) { 78 | if (!prevProps.properties || properties[propName] !== prevProps.properties[propName]) { 79 | _this2._controller.setProperty(propName, properties[propName]); 80 | } 81 | }); 82 | 83 | Object.keys(options || {}).forEach(function (optName) { 84 | if (!prevProps.options || options[optName] !== prevProps.options[optName]) { 85 | _this2._controller.setOption(optName, options[optName]); 86 | } 87 | }); 88 | 89 | this._controller.setBalloonState(balloonState); 90 | 91 | if (children != prevProps.children) { 92 | this._clearLayouts(); 93 | this._setupLayouts(); 94 | } 95 | } 96 | }, { 97 | key: 'componentDidMount', 98 | value: function componentDidMount() { 99 | var _props2 = this.props, 100 | lat = _props2.lat, 101 | lon = _props2.lon, 102 | properties = _props2.properties, 103 | options = _props2.options, 104 | balloonState = _props2.balloonState; 105 | 106 | var coords = this.context.coordorder === 'longlat' ? [lon, lat] : [lat, lon]; 107 | 108 | this._controller = new _MarkerController2.default(coords, properties, options, balloonState); 109 | 110 | this._setupLayouts(); 111 | this._setupEvents(); 112 | 113 | this.context.mapController.appendMarker(this._controller); 114 | } 115 | }, { 116 | key: 'componentWillUnmount', 117 | value: function componentWillUnmount() { 118 | this._clearLayouts(); 119 | this._controller.destroy(); 120 | } 121 | }, { 122 | key: 'getController', 123 | value: function getController() { 124 | return this._controller ? this._controller : null; 125 | } 126 | }, { 127 | key: '_setupLayouts', 128 | value: function _setupLayouts() { 129 | var _this3 = this; 130 | 131 | _react2.default.Children.toArray(this.props.children).forEach(function (component) { 132 | if (component.type === _BalloonLayout2.default) { 133 | _this3._setupBalloonLayout(component); 134 | } 135 | if (component.type === _MarkerLayout2.default) { 136 | _this3._setupMarkerLayout(component); 137 | } 138 | }); 139 | } 140 | }, { 141 | key: '_setupMarkerLayout', 142 | value: function _setupMarkerLayout(component) { 143 | this._markerElement = document.createElement('div'); 144 | this._markerElement.className = 'icon-content'; 145 | this._markerElement.style.display = 'inline-block'; 146 | 147 | _reactDom2.default.render(component, this._markerElement); 148 | this._controller.setLayout('iconLayout', this._markerElement); 149 | } 150 | }, { 151 | key: '_setupBalloonLayout', 152 | value: function _setupBalloonLayout(component) { 153 | this._balloonElement = document.createElement('div'); 154 | 155 | _reactDom2.default.render(component, this._balloonElement); 156 | this._controller.setLayout('balloonLayout', this._balloonElement); 157 | } 158 | }, { 159 | key: '_clearLayouts', 160 | value: function _clearLayouts() { 161 | if (this._markerElement) { 162 | _reactDom2.default.unmountComponentAtNode(this._markerElement); 163 | this._markerElement = null; 164 | } 165 | 166 | if (this._balloonElement) { 167 | _reactDom2.default.unmountComponentAtNode(this._balloonElement); 168 | this._balloonElement = null; 169 | } 170 | } 171 | }, { 172 | key: 'render', 173 | value: function render() { 174 | return null; 175 | } 176 | }]); 177 | 178 | return MapMarker; 179 | }(_react.Component); 180 | 181 | MapMarker.propTypes = { 182 | lat: _propTypes2.default.number.isRequired, 183 | lon: _propTypes2.default.number.isRequired, 184 | properties: _propTypes2.default.object, 185 | options: _propTypes2.default.object, 186 | balloonState: _propTypes2.default.oneOf(['opened', 'closed']) 187 | }; 188 | MapMarker.defaultProps = { 189 | balloonState: 'closed' 190 | }; 191 | MapMarker.contextTypes = { 192 | mapController: _propTypes2.default.object, 193 | coordorder: _propTypes2.default.oneOf(['latlong', 'longlat']) 194 | }; 195 | exports.default = (0, _decorators.eventsDecorator)(MapMarker, { supportEvents: _geoObject2.default }); -------------------------------------------------------------------------------- /lib/MapContainer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | 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; }; 8 | 9 | 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; }; }(); 10 | 11 | var _propTypes = require('prop-types'); 12 | 13 | var _propTypes2 = _interopRequireDefault(_propTypes); 14 | 15 | var _react = require('react'); 16 | 17 | var _react2 = _interopRequireDefault(_react); 18 | 19 | var _reactDom = require('react-dom'); 20 | 21 | var _reactDom2 = _interopRequireDefault(_reactDom); 22 | 23 | var _MapElement = require('./MapElement'); 24 | 25 | var _MapElement2 = _interopRequireDefault(_MapElement); 26 | 27 | var _MapController = require('./controllers/MapController'); 28 | 29 | var _MapController2 = _interopRequireDefault(_MapController); 30 | 31 | var _map = require('./apiEventsLists/map'); 32 | 33 | var _map2 = _interopRequireDefault(_map); 34 | 35 | var _decorators = require('./utils/decorators'); 36 | 37 | var _configs = require('./configs'); 38 | 39 | var _configs2 = _interopRequireDefault(_configs); 40 | 41 | var _api = require('./api'); 42 | 43 | var _api2 = _interopRequireDefault(_api); 44 | 45 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 46 | 47 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 48 | 49 | 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; } 50 | 51 | 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; } 52 | 53 | var YandexMap = function (_Component) { 54 | _inherits(YandexMap, _Component); 55 | 56 | function YandexMap(props) { 57 | _classCallCheck(this, YandexMap); 58 | 59 | var _this = _possibleConstructorReturn(this, (YandexMap.__proto__ || Object.getPrototypeOf(YandexMap)).call(this, props)); 60 | 61 | _this.state = { 62 | isAPILoaded: false 63 | }; 64 | return _this; 65 | } 66 | 67 | _createClass(YandexMap, [{ 68 | key: 'getChildContext', 69 | value: function getChildContext() { 70 | return { 71 | mapController: this._controller, 72 | coordorder: this.props.loadOptions.coordorder || 'latlong' 73 | }; 74 | } 75 | }, { 76 | key: 'getController', 77 | value: function getController() { 78 | return this._controller ? this._controller : null; 79 | } 80 | }, { 81 | key: 'componentWillReceiveProps', 82 | value: function componentWillReceiveProps(nextProps) { 83 | var _this2 = this; 84 | 85 | this._controller && Object.keys(nextProps).forEach(function (key) { 86 | switch (key) { 87 | case 'controls': 88 | _this2._controller.setState(key, nextProps[key]); 89 | break; 90 | case 'center': 91 | if (_this2.props.center[0] !== nextProps.center[0] || _this2.props.center[1] !== nextProps.center[1]) { 92 | _this2._controller.setCenter(nextProps.center); 93 | } 94 | 95 | break; 96 | case 'zoom': 97 | if (_this2.props.zoom !== nextProps.zoom) { 98 | _this2._controller.setZoom(nextProps.zoom); 99 | } 100 | 101 | break; 102 | case 'bounds': 103 | if (_this2.props.bounds !== nextProps.bounds) { 104 | _this2._controller.setBounds(nextProps.bounds); 105 | } 106 | 107 | break; 108 | default: 109 | break; 110 | } 111 | }); 112 | } 113 | }, { 114 | key: 'componentDidMount', 115 | value: function componentDidMount() { 116 | if (_api2.default.isAvailible()) { 117 | this._onAPILoad(_api2.default.getAPI()); 118 | } else { 119 | _api2.default.load(this.props.loadOptions).then(this._onAPILoad.bind(this)).catch(function (error) { 120 | return console.log('Error occured: %s', error); 121 | }); 122 | } 123 | } 124 | }, { 125 | key: 'render', 126 | value: function render() { 127 | return _react2.default.createElement( 128 | 'div', 129 | { style: this._getStyle() }, 130 | _react2.default.createElement(_MapElement2.default, { ref: 'mapContainer' }), 131 | Boolean(this.state.isAPILoaded) ? this.props.children : null 132 | ); 133 | } 134 | }, { 135 | key: '_getStyle', 136 | value: function _getStyle() { 137 | return _extends({}, this.props.style, { 138 | width: typeof this.props.width == 'string' ? this.props.width : this.props.width + 'px', 139 | height: typeof this.props.height == 'string' ? this.props.height : this.props.height + 'px' 140 | }); 141 | } 142 | }, { 143 | key: '_onAPILoad', 144 | value: function _onAPILoad(namespace) { 145 | this.props.onAPIAvailable && this.props.onAPIAvailable(namespace); 146 | 147 | this._controller = new _MapController2.default(); 148 | this._controller.createMap(_reactDom2.default.findDOMNode(this.refs.mapContainer), _extends({}, this.props.state, { 149 | center: this.props.center, 150 | zoom: this.props.zoom, 151 | bounds: this.props.bounds 152 | }), _extends({}, this.props.options)); 153 | 154 | this._setupEvents(); 155 | this.setState({ isAPILoaded: true }); 156 | 157 | if (this.props.onMapAvailable) { 158 | this.props.onMapAvailable(this._controller.map); 159 | } 160 | } 161 | }]); 162 | 163 | return YandexMap; 164 | }(_react.Component); 165 | 166 | YandexMap.propTypes = { 167 | apiKey: _propTypes2.default.string, 168 | onAPIAvailable: _propTypes2.default.func, 169 | width: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.number]), 170 | height: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.number]), 171 | zoom: _propTypes2.default.number, 172 | state: _propTypes2.default.object, 173 | options: _propTypes2.default.object, 174 | loadOptions: _propTypes2.default.object, 175 | bounds: _propTypes2.default.array 176 | }; 177 | YandexMap.defaultProps = { 178 | zoom: 10, 179 | center: [55, 45], 180 | width: 600, 181 | height: 600, 182 | bounds: undefined, 183 | state: { 184 | controls: [] 185 | }, 186 | options: {}, 187 | loadOptions: {}, 188 | style: { 189 | position: 'relative' 190 | } 191 | }; 192 | YandexMap.childContextTypes = { 193 | mapController: _propTypes2.default.object, 194 | coordorder: _propTypes2.default.oneOf(['latlong', 'longlat']) 195 | }; 196 | exports.default = (0, _decorators.eventsDecorator)(YandexMap, { supportEvents: _map2.default }); -------------------------------------------------------------------------------- /lib/PanoramaContainer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | 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; }; 8 | 9 | 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; }; }(); 10 | 11 | var _propTypes = require('prop-types'); 12 | 13 | var _propTypes2 = _interopRequireDefault(_propTypes); 14 | 15 | var _react = require('react'); 16 | 17 | var _react2 = _interopRequireDefault(_react); 18 | 19 | var _reactDom = require('react-dom'); 20 | 21 | var _reactDom2 = _interopRequireDefault(_reactDom); 22 | 23 | var _PanoramaElement = require('./PanoramaElement'); 24 | 25 | var _PanoramaElement2 = _interopRequireDefault(_PanoramaElement); 26 | 27 | var _PanoramaController = require('./controllers/PanoramaController'); 28 | 29 | var _PanoramaController2 = _interopRequireDefault(_PanoramaController); 30 | 31 | var _map = require('./apiEventsLists/map'); 32 | 33 | var _map2 = _interopRequireDefault(_map); 34 | 35 | var _decorators = require('./utils/decorators'); 36 | 37 | var _api = require('./api'); 38 | 39 | var _api2 = _interopRequireDefault(_api); 40 | 41 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 42 | 43 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 44 | 45 | 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; } 46 | 47 | 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; } 48 | 49 | var YandexPanorama = function (_Component) { 50 | _inherits(YandexPanorama, _Component); 51 | 52 | function YandexPanorama(props) { 53 | _classCallCheck(this, YandexPanorama); 54 | 55 | var _this = _possibleConstructorReturn(this, (YandexPanorama.__proto__ || Object.getPrototypeOf(YandexPanorama)).call(this, props)); 56 | 57 | _this.isPanoramas = function (isPanoramas) { 58 | _this.setState({ 59 | isPanoramas: isPanoramas 60 | }); 61 | }; 62 | 63 | _this.state = { 64 | isAPILoaded: false, 65 | showService: false, 66 | isPanoramas: false 67 | }; 68 | return _this; 69 | } 70 | 71 | _createClass(YandexPanorama, [{ 72 | key: 'getChildContext', 73 | value: function getChildContext() { 74 | return { 75 | mapController: this._controller, 76 | coordorder: this.props.loadOptions.coordorder || 'latlong' 77 | }; 78 | } 79 | }, { 80 | key: 'getController', 81 | value: function getController() { 82 | return this._controller ? this._controller : null; 83 | } 84 | }, { 85 | key: 'componentWillReceiveProps', 86 | value: function componentWillReceiveProps(nextProps) { 87 | var _this2 = this; 88 | 89 | this._controller && Object.keys(nextProps).forEach(function (key) { 90 | switch (key) { 91 | case 'showService': 92 | if (_this2.state.showService !== nextProps.showService) { 93 | _this2.setState({ 94 | showService: nextProps.showService 95 | }, function () { 96 | return _this2.state.showService && _this2.init(); 97 | }); 98 | } 99 | break; 100 | default: 101 | break; 102 | } 103 | }); 104 | } 105 | }, { 106 | key: 'componentDidMount', 107 | value: function componentDidMount() { 108 | this.init(); 109 | } 110 | }, { 111 | key: 'init', 112 | value: function init() { 113 | if (_api2.default.isAvailible()) { 114 | this._onAPILoad(_api2.default.getAPI()); 115 | } else { 116 | _api2.default.load(this.props.loadOptions).then(this._onAPILoad.bind(this)).catch(function (error) { 117 | return console.log('Error occured: %s', error); 118 | }); 119 | } 120 | } 121 | }, { 122 | key: 'render', 123 | value: function render() { 124 | 125 | var style = {}; 126 | if (this.state.showService) { 127 | style = this._getStyle(); 128 | } 129 | 130 | return _react2.default.createElement( 131 | 'div', 132 | null, 133 | _react2.default.createElement( 134 | 'div', 135 | { style: style }, 136 | _react2.default.createElement(_PanoramaElement2.default, { ref: 'panoramaPlayer', show: this.state.showService }) 137 | ), 138 | !this.state.showService && this.state.isPanoramas && this.props.children 139 | ); 140 | } 141 | }, { 142 | key: '_getStyle', 143 | value: function _getStyle() { 144 | return _extends({}, this.props.style, { 145 | width: typeof this.props.width == 'string' ? this.props.width : this.props.width + 'px', 146 | height: typeof this.props.height == 'string' ? this.props.height : this.props.height + 'px' 147 | }); 148 | } 149 | }, { 150 | key: '_onAPILoad', 151 | value: function _onAPILoad(namespace) { 152 | var _this3 = this; 153 | 154 | this.props.onAPIAvailable && this.props.onAPIAvailable(namespace); 155 | 156 | this._controller = new _PanoramaController2.default(this.isPanoramas); 157 | this._controller.createPanorama(_reactDom2.default.findDOMNode(this.refs.panoramaPlayer), _extends({}, this.props.state, { 158 | center: this.props.center, 159 | zoom: this.props.zoom, 160 | bounds: this.props.bounds 161 | }), _extends({}, this.props.options)); 162 | 163 | this.setState({ isAPILoaded: true }, function () { 164 | return _this3._controller.locate(_this3.state.showService); 165 | }); 166 | } 167 | }]); 168 | 169 | return YandexPanorama; 170 | }(_react.Component); 171 | 172 | YandexPanorama.propTypes = { 173 | apiKey: _propTypes2.default.string, 174 | onAPIAvailable: _propTypes2.default.func, 175 | width: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.number]), 176 | height: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.number]), 177 | zoom: _propTypes2.default.number, 178 | state: _propTypes2.default.object, 179 | options: _propTypes2.default.object, 180 | loadOptions: _propTypes2.default.object, 181 | bounds: _propTypes2.default.array 182 | }; 183 | YandexPanorama.defaultProps = { 184 | zoom: 10, 185 | center: [55, 45], 186 | width: 600, 187 | height: 600, 188 | bounds: undefined, 189 | state: { 190 | controls: [] 191 | }, 192 | options: {}, 193 | loadOptions: {}, 194 | style: { 195 | position: 'relative' 196 | } 197 | }; 198 | YandexPanorama.childContextTypes = { 199 | mapController: _propTypes2.default.object, 200 | coordorder: _propTypes2.default.oneOf(['latlong', 'longlat']) 201 | }; 202 | exports.default = (0, _decorators.eventsDecorator)(YandexPanorama, { supportEvents: _map2.default }); --------------------------------------------------------------------------------