├── .eslintrc
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── assets
└── powered_by_google.png
├── build
├── react-place.js
├── react-place.min.js
└── react-place.min.js.map
├── example-es5-browserify
├── build
│ ├── Location.js
│ └── app.js
├── index.html
└── src
│ └── index.js
├── example-es5
└── index.html
├── example-es6
├── build
│ ├── app.js
│ └── app.js.map
├── index.html
└── src
│ └── index.js
├── index.html
├── karma.conf.js
├── lib
├── Location.js
├── Location.js.map
└── vendor
│ ├── google.js
│ └── google.js.map
├── package.json
├── src
├── Location.jsx
└── vendor
│ └── google.js
├── test
├── fixtures
│ └── predictions.js
└── test.spec.jsx
├── tests.webpack.js
├── webpack.config.js
└── yarn.lock
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "ecmaFeatures": {
3 | "globalReturn": true,
4 | "jsx": true,
5 | "modules": true
6 | },
7 |
8 | "env": {
9 | "browser": true,
10 | "es6": true,
11 | "node": true
12 | },
13 |
14 | "globals": {
15 | "document": false,
16 | "escape": false,
17 | "navigator": false,
18 | "unescape": false,
19 | "window": false,
20 | "describe": true,
21 | "before": true,
22 | "it": true,
23 | "expect": true,
24 | "sinon": true
25 | },
26 |
27 | "parser": "babel-eslint",
28 |
29 | "plugins": [
30 | "react"
31 | ],
32 |
33 | "rules": {
34 | "block-scoped-var": 2,
35 | "brace-style": [2, "1tbs", { "allowSingleLine": true }],
36 | "camelcase": [2, { "properties": "always" }],
37 | "comma-dangle": [2, "never"],
38 | "comma-spacing": [2, { "before": false, "after": true }],
39 | "comma-style": [2, "last"],
40 | "complexity": 0,
41 | "consistent-return": 2,
42 | "consistent-this": 0,
43 | "curly": [2, "multi-line"],
44 | "default-case": 0,
45 | "dot-location": [2, "property"],
46 | "dot-notation": 0,
47 | "eol-last": 2,
48 | "eqeqeq": [2, "allow-null"],
49 | "func-names": 0,
50 | "func-style": 0,
51 | "generator-star-spacing": [2, "both"],
52 | "guard-for-in": 0,
53 | "handle-callback-err": [2, "^(err|error|anySpecificError)$" ],
54 | "indent": [2, 2, { "SwitchCase": 1 }],
55 | "key-spacing": [2, { "beforeColon": false, "afterColon": true }],
56 | "linebreak-style": 0,
57 | "max-depth": 0,
58 | "max-len": [2, 120, 4],
59 | "max-nested-callbacks": 0,
60 | "max-params": 0,
61 | "max-statements": 0,
62 | "new-cap": [2, { "newIsCap": true, "capIsNew": false }],
63 | "newline-after-var": [2, "always"],
64 | "new-parens": 2,
65 | "no-alert": 0,
66 | "no-array-constructor": 2,
67 | "no-bitwise": 0,
68 | "no-caller": 2,
69 | "no-catch-shadow": 0,
70 | "no-cond-assign": 2,
71 | "no-console": 0,
72 | "no-constant-condition": 0,
73 | "no-continue": 0,
74 | "no-control-regex": 2,
75 | "no-debugger": 2,
76 | "no-delete-var": 2,
77 | "no-div-regex": 0,
78 | "no-dupe-args": 2,
79 | "no-dupe-keys": 2,
80 | "no-duplicate-case": 2,
81 | "no-else-return": 2,
82 | "no-empty": 0,
83 | "no-empty-character-class": 2,
84 | "no-empty-label": 2,
85 | "no-eq-null": 0,
86 | "no-eval": 2,
87 | "no-ex-assign": 2,
88 | "no-extend-native": 2,
89 | "no-extra-bind": 2,
90 | "no-extra-boolean-cast": 2,
91 | "no-extra-parens": 0,
92 | "no-extra-semi": 0,
93 | "no-extra-strict": 0,
94 | "no-fallthrough": 2,
95 | "no-floating-decimal": 2,
96 | "no-func-assign": 2,
97 | "no-implied-eval": 2,
98 | "no-inline-comments": 0,
99 | "no-inner-declarations": [2, "functions"],
100 | "no-invalid-regexp": 2,
101 | "no-irregular-whitespace": 2,
102 | "no-iterator": 2,
103 | "no-label-var": 2,
104 | "no-labels": 2,
105 | "no-lone-blocks": 0,
106 | "no-lonely-if": 0,
107 | "no-loop-func": 0,
108 | "no-mixed-requires": 0,
109 | "no-mixed-spaces-and-tabs": [2, false],
110 | "no-multi-spaces": 2,
111 | "no-multi-str": 2,
112 | "no-multiple-empty-lines": [2, { "max": 1 }],
113 | "no-native-reassign": 2,
114 | "no-negated-in-lhs": 2,
115 | "no-nested-ternary": 0,
116 | "no-new": 2,
117 | "no-new-func": 2,
118 | "no-new-object": 2,
119 | "no-new-require": 2,
120 | "no-new-wrappers": 2,
121 | "no-obj-calls": 2,
122 | "no-octal": 2,
123 | "no-octal-escape": 2,
124 | "no-path-concat": 0,
125 | "no-plusplus": 0,
126 | "no-process-env": 0,
127 | "no-process-exit": 0,
128 | "no-proto": 2,
129 | "no-redeclare": 2,
130 | "no-regex-spaces": 2,
131 | "no-reserved-keys": 0,
132 | "no-restricted-modules": 0,
133 | "no-return-assign": 2,
134 | "no-script-url": 0,
135 | "no-self-compare": 2,
136 | "no-sequences": 2,
137 | "no-shadow": 0,
138 | "no-shadow-restricted-names": 2,
139 | "no-spaced-func": 2,
140 | "no-sparse-arrays": 2,
141 | "no-sync": 0,
142 | "no-ternary": 0,
143 | "no-throw-literal": 2,
144 | "no-trailing-spaces": 2,
145 | "no-undef": 2,
146 | "no-undef-init": 2,
147 | "no-undefined": 0,
148 | "no-underscore-dangle": 0,
149 | "no-unneeded-ternary": 2,
150 | "no-unreachable": 2,
151 | "no-unused-expressions": 0,
152 | "no-unused-vars": [2, { "vars": "all", "args": "none" }],
153 | "no-use-before-define": 2,
154 | "no-var": 0,
155 | "no-void": 0,
156 | "no-warning-comments": 0,
157 | "no-with": 2,
158 | "one-var": 0,
159 | "operator-assignment": 0,
160 | "operator-linebreak": [2, "after"],
161 | "padded-blocks": 0,
162 | "quote-props": 0,
163 | "quotes": [2, "single", "avoid-escape"],
164 | "radix": 2,
165 | "jsx-quotes": [2, "prefer-single"],
166 | "react/display-name": 0,
167 | "react/jsx-boolean-value": 2,
168 | "react/jsx-no-undef": 2,
169 | "react/jsx-sort-prop-types": 0,
170 | "react/jsx-sort-props": 0,
171 | "react/jsx-uses-react": 2,
172 | "react/jsx-uses-vars": 2,
173 | "react/no-did-mount-set-state": 2,
174 | "react/no-did-update-set-state": 2,
175 | "react/no-multi-comp": 2,
176 | "react/no-unknown-property": 2,
177 | "react/prop-types": 2,
178 | "react/react-in-jsx-scope": 2,
179 | "react/self-closing-comp": 2,
180 | "react/sort-comp": 0,
181 | "react/wrap-multilines": 2,
182 | "semi": [2, "always"],
183 | "semi-spacing": 0,
184 | "sort-vars": 0,
185 | "space-after-keywords": [2, "always"],
186 | "space-before-blocks": [2, "always"],
187 | "space-before-function-paren": [2, {"anonymous": "always", "named": "never"}],
188 | "space-in-brackets": 0,
189 | "space-in-parens": [2, "never"],
190 | "space-infix-ops": 2,
191 | "space-return-throw-case": 2,
192 | "space-unary-ops": [2, { "words": true, "nonwords": false }],
193 | "spaced-comment": [2, "always"],
194 | "strict": 0,
195 | "use-isnan": 2,
196 | "valid-jsdoc": 0,
197 | "valid-typeof": 2,
198 | "vars-on-top": 2,
199 | "wrap-iife": [2, "any"],
200 | "wrap-regex": 0,
201 | "yoda": [2, "never"]
202 | }
203 | }
204 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build ...
2 | node_modules
3 | npm-debug.log
4 | public
5 | tmp
6 |
7 | # Created by https://www.gitignore.io
8 |
9 | ### OSX ###
10 | .DS_Store
11 | .AppleDouble
12 | .LSOverride
13 |
14 | # Icon must end with two \r
15 | Icon
16 |
17 |
18 | # Thumbnails
19 | ._*
20 |
21 | # Files that might appear in the root of a volume
22 | .DocumentRevisions-V100
23 | .fseventsd
24 | .Spotlight-V100
25 | .TemporaryItems
26 | .Trashes
27 | .VolumeIcon.icns
28 |
29 | # Directories potentially created on remote AFP share
30 | .AppleDB
31 | .AppleDesktop
32 | Network Trash Folder
33 | Temporary Items
34 | .apdisk
35 |
36 |
37 | ### Vim ###
38 | [._]*.s[a-w][a-z]
39 | [._]s[a-w][a-z]
40 | *.un~
41 | Session.vim
42 | .netrwhist
43 | *~
44 |
45 |
46 | ### SublimeText ###
47 | # cache files for sublime text
48 | *.tmlanguage.cache
49 | *.tmPreferences.cache
50 | *.stTheme.cache
51 |
52 | # workspace files are user-specific
53 | *.sublime-workspace
54 |
55 | # project files should be checked into the repository, unless a significant
56 | # proportion of contributors will probably not be using SublimeText
57 | # *.sublime-project
58 |
59 | # sftp configuration file
60 | sftp-config.json
61 |
62 |
63 | ### Node ###
64 | # Logs
65 | logs
66 | *.log
67 |
68 | # Runtime data
69 | pids
70 | *.pid
71 | *.seed
72 |
73 | # Directory for instrumented libs generated by jscoverage/JSCover
74 | lib-cov
75 |
76 | # Coverage directory used by tools like istanbul
77 | coverage
78 |
79 | # node-waf configuration
80 | .lock-wscript
81 |
82 | # Compiled binary addons (http://nodejs.org/api/addons.html)
83 |
84 | # Dependency directory
85 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
86 | node_modules
87 |
88 |
89 | ### Linux ###
90 | *~
91 |
92 | # KDE directory preferences
93 | .directory
94 |
95 | # Linux trash folder which might appear on any partition or disk
96 | .Trash-*
97 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | example
2 | example-es5
3 | example-es5-browserify
4 | src
5 | test
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Krasimir Tsonev
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React geo location component based on Google Maps
2 |
3 | The component uses Google Maps API to fetch the locations. It uses [Awesomplete](http://leaverou.github.io/awesomplete/) as a hard dependency for the dropdown.
4 |
5 | Check out the demo [here](http://krasimir.github.io/react-place).
6 |
7 | 
8 |
9 | ## Installation
10 |
11 | ```
12 | npm install react-place
13 | ```
14 |
15 | ## Dependencies
16 |
17 | * Of course [react](https://www.npmjs.com/package/react) and [react-dom](https://www.npmjs.com/package/react-dom). You need to have these modules installed.
18 | * [Awesomplete](http://leaverou.github.io/awesomplete/) - installed automatically while running `npm install react-place`. It comes with the component so you don't need to have it loaded on the page.
19 | * Google Maps API - you have to add `` to your page to have the component working.
20 |
21 | ## Usage (ES6)
22 |
23 | ```js
24 | import React from 'react';
25 | import ReactDOM from 'react-dom';
26 | import Location from 'react-place';
27 |
28 | var location;
29 | var container = document.querySelector('...');
30 |
31 | var onLocationSet = (data) => {
32 | // data.description
33 | // data.coords.lat
34 | // data.coords.lng
35 | };
36 |
37 | location = ReactDOM.render(
38 | ,
48 | container
49 | );
50 | ```
51 |
52 | ## Usage ES5 (with bundling)
53 |
54 | ```js
55 | var React = require('react');
56 | var ReactDOM = require('react-dom');
57 | var Location = require('react-place');
58 | var createLocation = React.createFactory(Location);
59 |
60 | function onLocationSet (data) {
61 | // data.description
62 | // data.coords.lat
63 | // data.coords.lng
64 | }
65 |
66 | var container = document.querySelector('#container');
67 | var LocationComp = createLocation({
68 | country: country.value,
69 | noMatching: 'Sorry, I can not find {{value}}.',
70 | onLocationSet: onLocationSet,
71 | inputProps={{
72 | style: {color: '#0099FF'},
73 | className:'location',
74 | placeholder: 'Where are your?'
75 | }}
76 | });
77 |
78 | var location = ReactDOM.render(LocationComp, container);
79 | ```
80 |
81 | If you need to update the country dynamically use the following API:
82 |
83 | ```js
84 | location.updateCountry('FR');
85 | ```
86 |
87 | ## Usage ES5 (no bundling)
88 |
89 | Download [react-place.min.js](https://github.com/krasimir/react-place/blob/master/build/react-place.min.js) file and add it to your page.
90 |
91 | ```js
92 |
93 |
94 |
95 |
96 |
122 | ```
123 |
124 | ## Testing
125 |
126 | ```
127 | npm run test
128 | ```
129 |
130 | ## Powered by Google API
131 |
132 | 
133 |
--------------------------------------------------------------------------------
/assets/powered_by_google.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/krasimir/react-place/96ca92cbe1b15023d72441942afceba8e982fe38/assets/powered_by_google.png
--------------------------------------------------------------------------------
/build/react-place.min.js:
--------------------------------------------------------------------------------
1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.ReactPlace=e()}}(function(){return function e(t,n,r){function i(u,a){if(!n[u]){if(!t[u]){var s="function"==typeof require&&require;if(!a&&s)return s(u,!0);if(o)return o(u,!0);var c=new Error("Cannot find module '"+u+"'");throw c.code="MODULE_NOT_FOUND",c}var l=n[u]={exports:{}};t[u][0].call(l.exports,function(e){var n=t[u][1][e];return i(n?n:e)},l,l.exports,e,t,n,r)}return n[u].exports}for(var o="function"==typeof require&&require,u=0;u=0;n--)t=e[n].call(this,t);return t}},T=function(e){function t(){return i(this,t),o(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return u(t,e),s(t,[{key:"render",value:function(){return l["default"].createElement("input",a({type:"text"},this.props.inputProps))}},{key:"componentWillMount",value:function(){this._googlePredictions=[],this._country=this.props.country||E,this._noMatching=this.props.noMatching||_}},{key:"componentDidMount",value:function(){var e,t={minChars:1,keepListItems:!1,sort:function(){return 0},item:function(e,t){return v["default"].$.create("li",{innerHTML:e.replace(RegExp(v["default"].$.regExpEscape(t.trim()),"gi"),"$&"),"aria-selected":"false"})}};e=p["default"].findDOMNode(this),this._autocomplete=new v["default"](e,t),e.addEventListener("awesomplete-selectcomplete",this._handleAutocompleteSelect.bind(this)),e.addEventListener("keyup",this._handleInputChange.bind(this))}},{key:"updateCountry",value:function(e){this._country=e}},{key:"_handleInputChange",value:function(e){var t=this,n=this._getInputValue(),r=O(this._autocomplete.evaluate.bind(this._autocomplete),function(e){return t._autocomplete.list=e},function(e){return e.map(function(e){return e.description})},function(e){return t._googlePredictions=e}),i=O(r,function(e){return[{description:e}]},function(e){return t._noMatching.replace("{{value}}",e)}),o=[38,40,13,27],u=o.indexOf(e.keyCode)>=0;u||this._getPredictions(n).then(r,i)}},{key:"_handleAutocompleteSelect",value:function(){var e=this,t=this._getInputValue(),n=function(e){var n=e.filter(function(e){return e.description===t});return n.length>0?n[0]:!1},r=function(e){return e&&e.place_id?e.place_id:!1},i=O(r,n),o=function(n){e.props.onLocationSet&&e.props.onLocationSet({description:t,coords:{lat:n.lat(),lng:n.lng()}})};this._getCoordinates(i(this._googlePredictions)).then(o)}},{key:"_getInputValue",value:function(){return p["default"].findDOMNode(this).value}},{key:"_getPredictions",value:function(e){var t=this,n=(this.props.google||w["default"]).createAutocompleteService(),r=!!e;return r?new m["default"](function(r,i){n.getPlacePredictions({input:e,componentRestrictions:{country:t._country},types:["(regions)"]},function(t){null!==t?r(t):i(e)})}):new m["default"](function(e,t){})}},{key:"_getCoordinates",value:function(e){var t=(this.props.google||w["default"]).createGeocoder();return new m["default"](function(n,r){t.geocode({placeId:e},function(e,t){"OK"===t&&e&&e.length>0?n(e[0].geometry.location):r(!1)})})}}]),t}(l["default"].Component);n["default"]=T,T.defaultProps={className:"",style:{}},T.propTypes={onLocationSet:h["default"].func,inputProps:h["default"].object,country:h["default"].string,noMatching:h["default"].string,google:h["default"].object},t.exports=n["default"]},{"./vendor/google":2,awesomplete:3,"promise-polyfill":8,"prop-types":12}],2:[function(e,t,n){(function(e){"use strict";function r(){return new e.google.maps.places.AutocompleteService}function i(){return new e.google.maps.Geocoder}Object.defineProperty(n,"__esModule",{value:!0}),n["default"]={createAutocompleteService:r,createGeocoder:i},t.exports=n["default"]}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],3:[function(e,t,n){!function(){function e(e){var t=Array.isArray(e)?{label:e[0],value:e[1]}:"object"==typeof e&&"label"in e&&"value"in e?e:{label:e,value:e};this.label=t.label||t.value,this.value=t.value}function n(e,t,n){for(var r in t){var i=t[r],o=e.input.getAttribute("data-"+r.toLowerCase());"number"==typeof i?e[r]=parseInt(o):i===!1?e[r]=null!==o:i instanceof Function?e[r]=null:e[r]=o,e[r]||0===e[r]||(e[r]=r in n?n[r]:i)}}function r(e,t){return"string"==typeof e?(t||document).querySelector(e):e||null}function i(e,t){return a.call((t||document).querySelectorAll(e))}function o(){i("input.awesomplete").forEach(function(e){new u(e)})}var u=function(e,t){var i=this;this.input=r(e),this.input.setAttribute("autocomplete","off"),this.input.setAttribute("aria-autocomplete","list"),t=t||{},n(this,{minChars:2,maxItems:10,autoFirst:!1,data:u.DATA,filter:u.FILTER_CONTAINS,sort:u.SORT_BYLENGTH,item:u.ITEM,replace:u.REPLACE},t),this.index=-1,this.container=r.create("div",{className:"awesomplete",around:e}),this.ul=r.create("ul",{hidden:"hidden",inside:this.container}),this.status=r.create("span",{className:"visually-hidden",role:"status","aria-live":"assertive","aria-relevant":"additions",inside:this.container}),r.bind(this.input,{input:this.evaluate.bind(this),blur:this.close.bind(this),keydown:function(e){var t=e.keyCode;i.opened&&(13===t&&i.selected?(e.preventDefault(),i.select()):27===t?i.close():(38===t||40===t)&&(e.preventDefault(),i[38===t?"previous":"next"]()))}}),r.bind(this.input.form,{submit:this.close.bind(this)}),r.bind(this.ul,{mousedown:function(e){var t=e.target;if(t!==this){for(;t&&!/li/i.test(t.nodeName);)t=t.parentNode;t&&0===e.button&&(e.preventDefault(),i.select(t,e.target))}}}),this.input.hasAttribute("list")?(this.list="#"+this.input.getAttribute("list"),this.input.removeAttribute("list")):this.list=this.input.getAttribute("data-list")||t.list||[],u.all.push(this)};u.prototype={set list(e){if(Array.isArray(e))this._list=e;else if("string"==typeof e&&e.indexOf(",")>-1)this._list=e.split(/\s*,\s*/);else if(e=r(e),e&&e.children){var t=[];a.apply(e.children).forEach(function(e){if(!e.disabled){var n=e.textContent.trim(),r=e.value||n,i=e.label||n;""!==r&&t.push({label:i,value:r})}}),this._list=t}document.activeElement===this.input&&this.evaluate()},get selected(){return this.index>-1},get opened(){return!this.ul.hasAttribute("hidden")},close:function(){this.ul.setAttribute("hidden",""),this.index=-1,r.fire(this.input,"awesomplete-close")},open:function(){this.ul.removeAttribute("hidden"),this.autoFirst&&-1===this.index&&this["goto"](0),r.fire(this.input,"awesomplete-open")},next:function(){var e=this.ul.children.length;this["goto"](this.index-1&&t.length>0&&(t[e].setAttribute("aria-selected","true"),this.status.textContent=t[e].textContent,r.fire(this.input,"awesomplete-highlight",{text:this.suggestions[this.index]}))},select:function(e,t){if(e?this.index=r.siblingIndex(e):e=this.ul.children[this.index],e){var n=this.suggestions[this.index],i=r.fire(this.input,"awesomplete-select",{text:n,origin:t||e});i&&(this.replace(n),this.close(),r.fire(this.input,"awesomplete-selectcomplete",{text:n}))}},evaluate:function(){var t=this,n=this.input.value;n.length>=this.minChars&&this._list.length>0?(this.index=-1,this.ul.innerHTML="",this.suggestions=this._list.map(function(r){return new e(t.data(r,n))}).filter(function(e){return t.filter(e,n)}).sort(this.sort).slice(0,this.maxItems),this.suggestions.forEach(function(e){t.ul.appendChild(t.item(e,n))}),0===this.ul.children.length?this.close():this.open()):this.close()}},u.all=[],u.FILTER_CONTAINS=function(e,t){return RegExp(r.regExpEscape(t.trim()),"i").test(e)},u.FILTER_STARTSWITH=function(e,t){return RegExp("^"+r.regExpEscape(t.trim()),"i").test(e)},u.SORT_BYLENGTH=function(e,t){return e.length!==t.length?e.length-t.length:t>e?-1:1},u.ITEM=function(e,t){var n=""===t?e:e.replace(RegExp(r.regExpEscape(t.trim()),"gi"),"$&");return r.create("li",{innerHTML:n,"aria-selected":"false"})},u.REPLACE=function(e){this.input.value=e.value},u.DATA=function(e){return e},Object.defineProperty(e.prototype=Object.create(String.prototype),"length",{get:function(){return this.label.length}}),e.prototype.toString=e.prototype.valueOf=function(){return""+this.label};var a=Array.prototype.slice;return r.create=function(e,t){var n=document.createElement(e);for(var i in t){var o=t[i];if("inside"===i)r(o).appendChild(n);else if("around"===i){var u=r(o);u.parentNode.insertBefore(n,u),n.appendChild(u)}else i in n?n[i]=o:n.setAttribute(i,o)}return n},r.bind=function(e,t){if(e)for(var n in t){var r=t[n];n.split(/\s+/).forEach(function(t){e.addEventListener(t,r)})}},r.fire=function(e,t,n){var r=document.createEvent("HTMLEvents");r.initEvent(t,!0,!0);for(var i in n)r[i]=n[i];return e.dispatchEvent(r)},r.regExpEscape=function(e){return e.replace(/[-\\^$*+?.()|[\]{}]/g,"\\$&")},r.siblingIndex=function(e){for(var t=0;e=e.previousElementSibling;t++);return t},"undefined"!=typeof Document&&("loading"!==document.readyState?o():document.addEventListener("DOMContentLoaded",o)),u.$=r,u.$$=i,"undefined"!=typeof self&&(self.Awesomplete=u),"object"==typeof t&&t.exports&&(t.exports=u),u}()},{}],4:[function(e,t,n){"use strict";function r(e){return function(){return e}}var i=function(){};i.thatReturns=r,i.thatReturnsFalse=r(!1),i.thatReturnsTrue=r(!0),i.thatReturnsNull=r(null),i.thatReturnsThis=function(){return this},i.thatReturnsArgument=function(e){return e},t.exports=i},{}],5:[function(e,t,n){(function(e){"use strict";function n(e,t,n,i,o,u,a,s){if(r(t),!e){var c;if(void 0===t)c=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var l=[n,i,o,u,a,s],f=0;c=new Error(t.replace(/%s/g,function(){return l[f++]})),c.name="Invariant Violation"}throw c.framesToPop=1,c}}var r=function(e){};"production"!==e.env.NODE_ENV&&(r=function(e){if(void 0===e)throw new Error("invariant requires an error message argument")}),t.exports=n}).call(this,e("_process"))},{_process:7}],6:[function(e,t,n){(function(n){"use strict";var r=e("./emptyFunction"),i=r;if("production"!==n.env.NODE_ENV){var o=function(e){for(var t=arguments.length,n=Array(t>1?t-1:0),r=1;t>r;r++)n[r-1]=arguments[r];var i=0,o="Warning: "+e.replace(/%s/g,function(){return n[i++]});"undefined"!=typeof console&&console.error(o);try{throw new Error(o)}catch(u){}};i=function(e,t){if(void 0===t)throw new Error("`warning(condition, format, ...args)` requires a warning message argument");if(0!==t.indexOf("Failed Composite propType: ")&&!e){for(var n=arguments.length,r=Array(n>2?n-2:0),i=2;n>i;i++)r[i-2]=arguments[i];o.apply(void 0,[t].concat(r))}}}t.exports=i}).call(this,e("_process"))},{"./emptyFunction":4,_process:7}],7:[function(e,t,n){function r(){throw new Error("setTimeout has not been defined")}function i(){throw new Error("clearTimeout has not been defined")}function o(e){if(f===setTimeout)return setTimeout(e,0);if((f===r||!f)&&setTimeout)return f=setTimeout,setTimeout(e,0);try{return f(e,0)}catch(t){try{return f.call(null,e,0)}catch(t){return f.call(this,e,0)}}}function u(e){if(p===clearTimeout)return clearTimeout(e);if((p===i||!p)&&clearTimeout)return p=clearTimeout,clearTimeout(e);try{return p(e)}catch(t){try{return p.call(null,e)}catch(t){return p.call(this,e)}}}function a(){v&&h&&(v=!1,h.length?y=h.concat(y):g=-1,y.length&&s())}function s(){if(!v){var e=o(a);v=!0;for(var t=y.length;t;){for(h=y,y=[];++g1)for(var n=1;ne;e++)i.call(this,this._deferreds[e]);this._deferreds=null}function s(e,t,n,r){this.onFulfilled="function"==typeof e?e:null,this.onRejected="function"==typeof t?t:null,this.resolve=n,this.reject=r}function c(e,t,n){var r=!1;try{e(function(e){r||(r=!0,t(e))},function(e){r||(r=!0,n(e))})}catch(i){if(r)return;r=!0,n(i)}}var l="function"==typeof setImmediate&&setImmediate||function(e){setTimeout(e,1)},f=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)};r.prototype["catch"]=function(e){return this.then(null,e)},r.prototype.then=function(e,t){var n=this;return new r(function(r,o){i.call(n,new s(e,t,r,o))})},r.all=function(){var e=Array.prototype.slice.call(1===arguments.length&&f(arguments[0])?arguments[0]:arguments);return new r(function(t,n){function r(o,u){try{if(u&&("object"==typeof u||"function"==typeof u)){var a=u.then;if("function"==typeof a)return void a.call(u,function(e){r(o,e)},n)}e[o]=u,0===--i&&t(e)}catch(s){n(s)}}if(0===e.length)return t([]);for(var i=e.length,o=0;or;r++)e[r].then(t,n)})},r._setImmediateFn=function(e){l=e},"undefined"!=typeof t&&t.exports?t.exports=r:e.Promise||(e.Promise=r)}(this)},{}],9:[function(e,t,n){(function(n){"use strict";function r(e,t,r,s,c){if("production"!==n.env.NODE_ENV)for(var l in e)if(e.hasOwnProperty(l)){var f;try{i("function"==typeof e[l],"%s: %s type `%s` is invalid; it must be a function, usually from the `prop-types` package, but received `%s`.",s||"React class",r,l,typeof e[l]),f=e[l](t,l,s,r,null,u)}catch(p){f=p}if(o(!f||f instanceof Error,"%s: type specification of %s `%s` is invalid; the type checker function must return `null` or an `Error` but returned a %s. You may have forgotten to pass an argument to the type checker creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and shape all require an argument).",s||"React class",r,l,typeof f),f instanceof Error&&!(f.message in a)){a[f.message]=!0;var d=c?c():"";o(!1,"Failed %s type: %s%s",r,f.message,null!=d?d:"")}}}if("production"!==n.env.NODE_ENV)var i=e("fbjs/lib/invariant"),o=e("fbjs/lib/warning"),u=e("./lib/ReactPropTypesSecret"),a={};t.exports=r}).call(this,e("_process"))},{"./lib/ReactPropTypesSecret":13,_process:7,"fbjs/lib/invariant":5,"fbjs/lib/warning":6}],10:[function(e,t,n){"use strict";var r=e("fbjs/lib/emptyFunction"),i=e("fbjs/lib/invariant"),o=e("./lib/ReactPropTypesSecret");t.exports=function(){function e(e,t,n,r,u,a){a!==o&&i(!1,"Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types")}function t(){return e}e.isRequired=e;var n={array:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t};return n.checkPropTypes=r,n.PropTypes=n,n}},{"./lib/ReactPropTypesSecret":13,"fbjs/lib/emptyFunction":4,"fbjs/lib/invariant":5}],11:[function(e,t,n){(function(n){"use strict";var r=e("fbjs/lib/emptyFunction"),i=e("fbjs/lib/invariant"),o=e("fbjs/lib/warning"),u=e("object-assign"),a=e("./lib/ReactPropTypesSecret"),s=e("./checkPropTypes");t.exports=function(e,t){function c(e){var t=e&&(R&&e[R]||e[k]);return"function"==typeof t?t:void 0}function l(e,t){return e===t?0!==e||1/e===1/t:e!==e&&t!==t}function f(e){this.message=e,this.stack=""}function p(e){function r(r,c,l,p,d,h,y){if(p=p||N,h=h||l,y!==a)if(t)i(!1,"Calling PropTypes validators directly is not supported by the `prop-types` package. Use `PropTypes.checkPropTypes()` to call them. Read more at http://fb.me/use-check-prop-types");else if("production"!==n.env.NODE_ENV&&"undefined"!=typeof console){var v=p+":"+l;!u[v]&&3>s&&(o(!1,"You are manually calling a React.PropTypes validation function for the `%s` prop on `%s`. This is deprecated and will throw in the standalone `prop-types` package. You may be seeing this warning due to a third-party PropTypes library. See https://fb.me/react-warning-dont-call-proptypes for details.",h,p),u[v]=!0,s++)}return null==c[l]?r?new f(null===c[l]?"The "+d+" `"+h+"` is marked as required "+("in `"+p+"`, but its value is `null`."):"The "+d+" `"+h+"` is marked as required in "+("`"+p+"`, but its value is `undefined`.")):null:e(c,l,p,d,h)}if("production"!==n.env.NODE_ENV)var u={},s=0;var c=r.bind(null,!1);return c.isRequired=r.bind(null,!0),c}function d(e){function t(t,n,r,i,o,u){var a=t[n],s=x(a);if(s!==e){var c=P(a);return new f("Invalid "+i+" `"+o+"` of type "+("`"+c+"` supplied to `"+r+"`, expected ")+("`"+e+"`."))}return null}return p(t)}function h(){return p(r.thatReturnsNull)}function y(e){function t(t,n,r,i,o){if("function"!=typeof e)return new f("Property `"+o+"` of component `"+r+"` has invalid PropType notation inside arrayOf.");var u=t[n];if(!Array.isArray(u)){var s=x(u);return new f("Invalid "+i+" `"+o+"` of type "+("`"+s+"` supplied to `"+r+"`, expected an array."))}for(var c=0;c>",I={array:d("array"),bool:d("boolean"),func:d("function"),number:d("number"),object:d("object"),string:d("string"),symbol:d("symbol"),any:h(),arrayOf:y,element:v(),instanceOf:g,node:_(),objectOf:b,oneOf:m,oneOfType:w,shape:E,exact:O};return f.prototype=Error.prototype,I.checkPropTypes=s,I.PropTypes=I,I}}).call(this,e("_process"))},{"./checkPropTypes":9,"./lib/ReactPropTypesSecret":13,_process:7,"fbjs/lib/emptyFunction":4,"fbjs/lib/invariant":5,"fbjs/lib/warning":6,"object-assign":14}],12:[function(e,t,n){(function(n){if("production"!==n.env.NODE_ENV){var r="function"==typeof Symbol&&Symbol["for"]&&Symbol["for"]("react.element")||60103,i=function(e){return"object"==typeof e&&null!==e&&e.$$typeof===r},o=!0;t.exports=e("./factoryWithTypeCheckers")(i,o)}else t.exports=e("./factoryWithThrowingShims")()}).call(this,e("_process"))},{"./factoryWithThrowingShims":10,"./factoryWithTypeCheckers":11,_process:7}],13:[function(e,t,n){"use strict";var r="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED";t.exports=r},{}],14:[function(e,t,n){"use strict";function r(e){if(null===e||void 0===e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}function i(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;10>n;n++)t["_"+String.fromCharCode(n)]=n;var r=Object.getOwnPropertyNames(t).map(function(e){return t[e]});if("0123456789"!==r.join(""))return!1;var i={};return"abcdefghijklmnopqrst".split("").forEach(function(e){i[e]=e}),"abcdefghijklmnopqrst"!==Object.keys(Object.assign({},i)).join("")?!1:!0}catch(o){return!1}}var o=Object.getOwnPropertySymbols,u=Object.prototype.hasOwnProperty,a=Object.prototype.propertyIsEnumerable;t.exports=i()?Object.assign:function(e,t){for(var n,i,s=r(e),c=1;c
2 |
3 |
4 |
5 | React Location
6 |
154 |
155 |
156 |
157 |
408 |
409 |
410 |

411 |
412 |
413 |
414 |
415 |
--------------------------------------------------------------------------------
/example-es5-browserify/src/index.js:
--------------------------------------------------------------------------------
1 | var React = require('react');
2 | var ReactDOM = require('react-dom');
3 | var Location = require('../../lib/Location');
4 | var createLocation = React.createFactory(Location);
5 |
6 | function onLocationSet(value) {
7 | var pre = document.querySelector('pre');
8 |
9 | pre.innerHTML = JSON.stringify(value, null, 2);
10 | }
11 |
12 | window.onload = function () {
13 | var country = document.querySelector('#country-dropdown');
14 | var container = document.querySelector('#container');
15 | var LocationComp = createLocation({
16 | className: 'location',
17 | placeholder: 'Where are you?',
18 | country: country.value,
19 | noMatching: 'Sorry, I can not find {{value}}.',
20 | onLocationSet: onLocationSet
21 | });
22 | var location = ReactDOM.render(LocationComp, container);
23 |
24 | country.addEventListener('change', function () {
25 | location.updateCountry(country.value);
26 | });
27 |
28 | };
29 |
30 |
--------------------------------------------------------------------------------
/example-es5/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React Location
6 |
154 |
155 |
156 |
157 |
408 |
409 |
410 |

411 |
412 |
413 |
414 |
415 |
416 |
445 |
446 |
--------------------------------------------------------------------------------
/example-es6/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React Location
6 |
154 |
155 |
156 |
157 |
408 |
409 |
410 |

411 |
412 |
413 |
414 |
415 |
--------------------------------------------------------------------------------
/example-es6/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import Location from '../../src/Location.jsx';
4 |
5 | function onLocationSet(value) {
6 | var pre = document.querySelector('pre');
7 |
8 | pre.innerHTML = JSON.stringify(value, null, 2);
9 | }
10 |
11 | window.onload = () => {
12 | var country = document.querySelector('#country-dropdown');
13 | var container = document.querySelector('#container');
14 | var location;
15 |
16 | location = ReactDOM.render(
17 | ,
27 | container
28 | );
29 |
30 | country.addEventListener('change', () => {
31 | location.updateCountry(country.value);
32 | });
33 | };
34 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | var webpackConfig = require('./webpack.config');
2 | webpackConfig.devtool = 'inline-source-map';
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | browsers: [ 'PhantomJS' ],
7 | singleRun: true,
8 | frameworks: [ 'mocha', 'chai', 'sinon', 'sinon-chai' ],
9 | files: [
10 | 'tests.webpack.js'
11 | ],
12 | plugins: [
13 | 'karma-phantomjs-launcher',
14 | 'karma-chai',
15 | 'karma-mocha',
16 | 'karma-sourcemap-loader',
17 | 'karma-webpack',
18 | 'karma-mocha-reporter',
19 | 'karma-sinon',
20 | 'karma-sinon-chai'
21 | ],
22 | preprocessors: {
23 | 'tests.webpack.js': [ 'webpack', 'sourcemap' ]
24 | },
25 | reporters: [ 'mocha' ],
26 | webpack: webpackConfig,
27 | webpackServer: {
28 | noInfo: true
29 | },
30 | autoWatch: true
31 | });
32 | };
--------------------------------------------------------------------------------
/lib/Location.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 _react = require('react');
12 |
13 | var _react2 = _interopRequireDefault(_react);
14 |
15 | var _reactDom = require('react-dom');
16 |
17 | var _reactDom2 = _interopRequireDefault(_reactDom);
18 |
19 | var _propTypes = require('prop-types');
20 |
21 | var _propTypes2 = _interopRequireDefault(_propTypes);
22 |
23 | var _awesomplete = require('awesomplete');
24 |
25 | var _awesomplete2 = _interopRequireDefault(_awesomplete);
26 |
27 | var _promisePolyfill = require('promise-polyfill');
28 |
29 | var _promisePolyfill2 = _interopRequireDefault(_promisePolyfill);
30 |
31 | var _google = require('./vendor/google');
32 |
33 | var _google2 = _interopRequireDefault(_google);
34 |
35 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
36 |
37 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
38 |
39 | 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; }
40 |
41 | 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; }
42 |
43 | var NO_MATCHING = 'Unrecognised {{value}}, please check and re-enter.';
44 | var DEFAULT_COUNTRY = 'US';
45 |
46 | var compose = function compose() {
47 | var fns = arguments;
48 |
49 | return function (result) {
50 | for (var i = fns.length - 1; i >= 0; i--) {
51 | result = fns[i].call(this, result);
52 | }
53 | return result;
54 | };
55 | };
56 |
57 | var Location = function (_React$Component) {
58 | _inherits(Location, _React$Component);
59 |
60 | function Location() {
61 | _classCallCheck(this, Location);
62 |
63 | return _possibleConstructorReturn(this, (Location.__proto__ || Object.getPrototypeOf(Location)).apply(this, arguments));
64 | }
65 |
66 | _createClass(Location, [{
67 | key: 'render',
68 | value: function render() {
69 | return _react2.default.createElement('input', _extends({ type: 'text' }, this.props.inputProps));
70 | }
71 | }, {
72 | key: 'componentWillMount',
73 | value: function componentWillMount() {
74 | this._googlePredictions = [];
75 | this._country = this.props.country || DEFAULT_COUNTRY;
76 | this._noMatching = this.props.noMatching || NO_MATCHING;
77 | }
78 | }, {
79 | key: 'componentDidMount',
80 | value: function componentDidMount() {
81 | var input;
82 | var config = {
83 | minChars: 1,
84 | keepListItems: false,
85 | sort: function sort() {
86 | return 0;
87 | },
88 | item: function item(text, input) {
89 | return _awesomplete2.default.$.create('li', {
90 | innerHTML: text.replace(RegExp(_awesomplete2.default.$.regExpEscape(input.trim()), 'gi'), '$&'),
91 | 'aria-selected': 'false'
92 | });
93 | }
94 | };
95 |
96 | input = _reactDom2.default.findDOMNode(this);
97 | this._autocomplete = new _awesomplete2.default(input, config);
98 |
99 | input.addEventListener('awesomplete-selectcomplete', this._handleAutocompleteSelect.bind(this));
100 | input.addEventListener('keyup', this._handleInputChange.bind(this));
101 | }
102 | }, {
103 | key: 'updateCountry',
104 | value: function updateCountry(country) {
105 | this._country = country;
106 | }
107 | }, {
108 | key: '_handleInputChange',
109 | value: function _handleInputChange(event) {
110 | var _this2 = this;
111 |
112 | var value = this._getInputValue();
113 | var updateAutocomplete = compose(this._autocomplete.evaluate.bind(this._autocomplete), function (list) {
114 | return _this2._autocomplete.list = list;
115 | }, function (list) {
116 | return list.map(function (item) {
117 | return item.description;
118 | });
119 | }, function (results) {
120 | return _this2._googlePredictions = results;
121 | });
122 | var fail = compose(updateAutocomplete, function (text) {
123 | return [{ description: text }];
124 | }, function (text) {
125 | return _this2._noMatching.replace('{{value}}', text);
126 | });
127 | var navKeys = [38, 40, 13, 27];
128 | var isItNavKey = navKeys.indexOf(event.keyCode) >= 0;
129 |
130 | if (!isItNavKey) {
131 | this._getPredictions(value).then(updateAutocomplete, fail);
132 | }
133 | }
134 | }, {
135 | key: '_handleAutocompleteSelect',
136 | value: function _handleAutocompleteSelect() {
137 | var _this3 = this;
138 |
139 | var value = this._getInputValue();
140 | var find = function find(list) {
141 | var l = list.filter(function (item) {
142 | return item.description === value;
143 | });
144 |
145 | return l.length > 0 ? l[0] : false;
146 | };
147 | var validate = function validate(item) {
148 | return item && item.place_id ? item.place_id : false;
149 | };
150 | var getPlaceId = compose(validate, find);
151 | var success = function success(location) {
152 | _this3.props.onLocationSet && _this3.props.onLocationSet({
153 | description: value,
154 | coords: {
155 | lat: location.lat(),
156 | lng: location.lng()
157 | }
158 | });
159 | };
160 |
161 | this._getCoordinates(getPlaceId(this._googlePredictions)).then(success);
162 | }
163 | }, {
164 | key: '_getInputValue',
165 | value: function _getInputValue() {
166 | return _reactDom2.default.findDOMNode(this).value;
167 | }
168 | }, {
169 | key: '_getPredictions',
170 | value: function _getPredictions(text) {
171 | var _this4 = this;
172 |
173 | var service = (this.props.google || _google2.default).createAutocompleteService();
174 | var isThereAnyText = !!text;
175 |
176 | if (isThereAnyText) {
177 | return new _promisePolyfill2.default(function (resolve, reject) {
178 | service.getPlacePredictions({
179 | input: text,
180 | componentRestrictions: { country: _this4._country },
181 | types: ['(regions)']
182 | }, function (result) {
183 | if (result !== null) {
184 | resolve(result);
185 | } else {
186 | reject(text);
187 | }
188 | });
189 | });
190 | }
191 | return new _promisePolyfill2.default(function (resolve, reject) {});
192 | }
193 | }, {
194 | key: '_getCoordinates',
195 | value: function _getCoordinates(placeId) {
196 | var geocoder = (this.props.google || _google2.default).createGeocoder();
197 |
198 | return new _promisePolyfill2.default(function (resolve, reject) {
199 | geocoder.geocode({ placeId: placeId }, function (results, status) {
200 | if (status === 'OK' && results && results.length > 0) {
201 | resolve(results[0].geometry.location);
202 | } else {
203 | reject(false);
204 | }
205 | });
206 | });
207 | }
208 | }]);
209 |
210 | return Location;
211 | }(_react2.default.Component);
212 |
213 | exports.default = Location;
214 | ;
215 |
216 | Location.defaultProps = {
217 | className: '',
218 | style: {}
219 | };
220 |
221 | Location.propTypes = {
222 | onLocationSet: _propTypes2.default.func,
223 | inputProps: _propTypes2.default.object,
224 | country: _propTypes2.default.string,
225 | noMatching: _propTypes2.default.string,
226 | google: _propTypes2.default.object
227 | };
228 | module.exports = exports['default'];
229 | //# sourceMappingURL=Location.js.map
--------------------------------------------------------------------------------
/lib/Location.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../src/Location.jsx"],"names":["NO_MATCHING","DEFAULT_COUNTRY","compose","fns","arguments","result","i","length","call","Location","props","inputProps","_googlePredictions","_country","country","_noMatching","noMatching","input","config","minChars","keepListItems","sort","item","text","$","create","innerHTML","replace","RegExp","regExpEscape","trim","findDOMNode","_autocomplete","addEventListener","_handleAutocompleteSelect","bind","_handleInputChange","event","value","_getInputValue","updateAutocomplete","evaluate","list","map","description","results","fail","navKeys","isItNavKey","indexOf","keyCode","_getPredictions","then","find","l","filter","validate","place_id","getPlaceId","success","location","onLocationSet","coords","lat","lng","_getCoordinates","service","google","createAutocompleteService","isThereAnyText","resolve","reject","getPlacePredictions","componentRestrictions","types","placeId","geocoder","createGeocoder","geocode","status","geometry","Component","defaultProps","className","style","propTypes","func","object","string"],"mappings":"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","file":"Location.js","sourcesContent":["'use strict';\n\nimport React from 'react';\nimport ReactDom from 'react-dom';\nimport PropTypes from 'prop-types';\nimport Awesomplete from 'awesomplete';\nimport Promise from 'promise-polyfill';\nimport google from './vendor/google';\n\nconst NO_MATCHING = 'Unrecognised {{value}}, please check and re-enter.';\nconst DEFAULT_COUNTRY = 'US';\n\nvar compose = function () {\n var fns = arguments;\n\n return function (result) {\n for (let i = fns.length - 1; i >= 0; i--) {\n result = fns[i].call(this, result);\n }\n return result;\n };\n};\n\nexport default class Location extends React.Component {\n\n render() {\n return ;\n }\n\n componentWillMount() {\n this._googlePredictions = [];\n this._country = this.props.country || DEFAULT_COUNTRY;\n this._noMatching = this.props.noMatching || NO_MATCHING;\n }\n\n componentDidMount() {\n var input;\n var config = {\n minChars: 1,\n keepListItems: false,\n sort: function () { return 0; },\n item: function (text, input) {\n return Awesomplete.$.create('li', {\n innerHTML: text.replace(\n RegExp(Awesomplete.$.regExpEscape(input.trim()), 'gi'),\n '$&'\n ),\n 'aria-selected': 'false'\n });\n }\n };\n\n input = ReactDom.findDOMNode(this);\n this._autocomplete = new Awesomplete(input, config);\n\n input.addEventListener(\n 'awesomplete-selectcomplete',\n this._handleAutocompleteSelect.bind(this)\n );\n input.addEventListener(\n 'keyup',\n this._handleInputChange.bind(this)\n );\n }\n\n updateCountry(country) {\n this._country = country;\n }\n\n _handleInputChange(event) {\n var value = this._getInputValue();\n var updateAutocomplete = compose(\n this._autocomplete.evaluate.bind(this._autocomplete),\n (list) => this._autocomplete.list = list,\n (list) => list.map((item) => item.description),\n (results) => this._googlePredictions = results\n );\n var fail = compose(\n updateAutocomplete,\n (text) => [{ description: text }],\n (text) => {\n return this._noMatching.replace('{{value}}', text);\n }\n );\n var navKeys = [38, 40, 13, 27];\n var isItNavKey = navKeys.indexOf(event.keyCode) >= 0;\n\n if (!isItNavKey) {\n this._getPredictions(value).then(updateAutocomplete, fail);\n }\n }\n\n _handleAutocompleteSelect() {\n var value = this._getInputValue();\n var find = (list) => {\n let l = list.filter(item => item.description === value);\n\n return l.length > 0 ? l[0] : false;\n };\n var validate = item => item && item.place_id ? item.place_id : false;\n var getPlaceId = compose(validate, find);\n var success = (location) => {\n this.props.onLocationSet && this.props.onLocationSet({\n description: value,\n coords: {\n lat: location.lat(),\n lng: location.lng()\n }\n });\n };\n\n this._getCoordinates(getPlaceId(this._googlePredictions)).then(success);\n }\n\n _getInputValue() {\n return ReactDom.findDOMNode(this).value;\n }\n\n _getPredictions(text) {\n var service = (this.props.google || google).createAutocompleteService();\n var isThereAnyText = !!text;\n\n if (isThereAnyText) {\n return new Promise((resolve, reject) => {\n service.getPlacePredictions({\n input: text,\n componentRestrictions: { country: this._country },\n types: ['(regions)']\n }, (result) => {\n if (result !== null) {\n resolve(result);\n } else {\n reject(text);\n }\n });\n });\n }\n return new Promise((resolve, reject) => {});\n }\n\n _getCoordinates(placeId) {\n var geocoder = (this.props.google || google).createGeocoder();\n\n return new Promise((resolve, reject) => {\n geocoder.geocode({ placeId: placeId }, (results, status) => {\n if (status === 'OK' && results && results.length > 0) {\n resolve(results[0].geometry.location);\n } else {\n reject(false);\n }\n });\n });\n }\n\n};\n\nLocation.defaultProps = {\n className: '',\n style: {}\n};\n\nLocation.propTypes = {\n onLocationSet: PropTypes.func,\n inputProps: PropTypes.object,\n country: PropTypes.string,\n noMatching: PropTypes.string,\n google: PropTypes.object\n};\n"]}
--------------------------------------------------------------------------------
/lib/vendor/google.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | function createAutocompleteService() {
7 | return new global.google.maps.places.AutocompleteService();
8 | };
9 |
10 | function createGeocoder() {
11 | return new global.google.maps.Geocoder();
12 | };
13 |
14 | exports.default = {
15 | createAutocompleteService: createAutocompleteService,
16 | createGeocoder: createGeocoder
17 | };
18 | module.exports = exports['default'];
19 | //# sourceMappingURL=google.js.map
--------------------------------------------------------------------------------
/lib/vendor/google.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../../src/vendor/google.js"],"names":["createAutocompleteService","global","google","maps","places","AutocompleteService","createGeocoder","Geocoder"],"mappings":";;;;;AAAA,SAASA,yBAAT,GAAqC;AACnC,SAAO,IAAIC,OAAOC,MAAP,CAAcC,IAAd,CAAmBC,MAAnB,CAA0BC,mBAA9B,EAAP;AACD;;AAED,SAASC,cAAT,GAA0B;AACxB,SAAO,IAAIL,OAAOC,MAAP,CAAcC,IAAd,CAAmBI,QAAvB,EAAP;AACD;;kBAEc;AACbP,sDADa;AAEbM;AAFa,C","file":"google.js","sourcesContent":["function createAutocompleteService() {\n return new global.google.maps.places.AutocompleteService();\n};\n\nfunction createGeocoder() {\n return new global.google.maps.Geocoder();\n};\n\nexport default {\n createAutocompleteService,\n createGeocoder\n};\n\n"]}
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-place",
3 | "version": "0.6.2",
4 | "description": "React geo location component based on Google Maps",
5 | "main": "lib/Location.js",
6 | "author": {
7 | "name": "Krasimir Tsonev",
8 | "email": "info@krasimirtsonev.com",
9 | "url": "http://krasimirtsonev.com"
10 | },
11 | "license": "MIT",
12 | "keywords": [
13 | "react",
14 | "location",
15 | "googlemaps",
16 | "geo",
17 | "react-component"
18 | ],
19 | "repository": {
20 | "type": "git",
21 | "url": "git@github.com:krasimir/react-place.git"
22 | },
23 | "scripts": {
24 | "dev": "webpack --watch --inline",
25 | "prepublish": "babel ./src --out-dir ./lib --source-maps --presets es2015,react --plugins babel-plugin-add-module-exports && browserify ./lib/Location.js -o ./build/react-place.js --transform browserify-global-shim --standalone ReactPlace && uglifyjs ./build/react-place.js --compress --mangle --output ./build/react-place.min.js --source-map ./build/react-place.min.js.map",
26 | "test": "karma start",
27 | "test:ci": "watch 'npm run test' src/",
28 | "example-es5-browserify": "browserify ./example-es5-browserify/src/index.js -o ./example-es5-browserify/build/app.js"
29 | },
30 | "dependencies": {
31 | "awesomplete": "1.1.0",
32 | "promise-polyfill": "2.1.0"
33 | },
34 | "devDependencies": {
35 | "babel": "6.5.2",
36 | "babel-cli": "6.1.18",
37 | "babel-core": "6.6.5",
38 | "babel-eslint": "5.0.0",
39 | "babel-loader": "6.2.4",
40 | "babel-plugin-add-module-exports": "0.1.1",
41 | "babel-preset-es2015": "6.6.0",
42 | "babel-preset-react": "6.5.0",
43 | "browserify": "12.0.1",
44 | "browserify-global-shim": "1.0.0",
45 | "chai": "3.3.0",
46 | "core-js": "1.1.4",
47 | "eslint": "1.4.3",
48 | "eslint-loader": "1.0.0",
49 | "eslint-plugin-react": "3.4.1",
50 | "karma": "0.13.19",
51 | "karma-chai": "0.1.0",
52 | "karma-chai-plugins": "0.6.0",
53 | "karma-chai-sinon": "0.1.5",
54 | "karma-chrome-launcher": "0.2.0",
55 | "karma-mocha": "0.2.0",
56 | "karma-mocha-reporter": "1.1.1",
57 | "karma-phantomjs-launcher": "0.2.1",
58 | "karma-sinon": "1.0.4",
59 | "karma-sinon-chai": "1.1.0",
60 | "karma-sourcemap-loader": "0.3.5",
61 | "karma-webpack": "1.7.0",
62 | "phantomjs": "^2.1.7",
63 | "prop-types": "^15.6.0",
64 | "react": "^15.2.1",
65 | "react-addons-test-utils": "^15.2.1",
66 | "react-dom": "^15.2.1",
67 | "uglify-js": "2.6.1",
68 | "watch": "0.16.0",
69 | "webpack": "1.12.2",
70 | "webpack-dev-server": "1.11.0"
71 | },
72 | "browserify-global-shim": {
73 | "react": "React",
74 | "react-dom": "ReactDOM"
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/Location.jsx:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import ReactDom from 'react-dom';
5 | import PropTypes from 'prop-types';
6 | import Awesomplete from 'awesomplete';
7 | import Promise from 'promise-polyfill';
8 | import google from './vendor/google';
9 |
10 | const NO_MATCHING = 'Unrecognised {{value}}, please check and re-enter.';
11 | const DEFAULT_COUNTRY = 'US';
12 |
13 | var compose = function () {
14 | var fns = arguments;
15 |
16 | return function (result) {
17 | for (let i = fns.length - 1; i >= 0; i--) {
18 | result = fns[i].call(this, result);
19 | }
20 | return result;
21 | };
22 | };
23 |
24 | export default class Location extends React.Component {
25 |
26 | render() {
27 | return ;
28 | }
29 |
30 | componentWillMount() {
31 | this._googlePredictions = [];
32 | this._country = this.props.country || DEFAULT_COUNTRY;
33 | this._noMatching = this.props.noMatching || NO_MATCHING;
34 | }
35 |
36 | componentDidMount() {
37 | var input;
38 | var config = {
39 | minChars: 1,
40 | keepListItems: false,
41 | sort: function () { return 0; },
42 | item: function (text, input) {
43 | return Awesomplete.$.create('li', {
44 | innerHTML: text.replace(
45 | RegExp(Awesomplete.$.regExpEscape(input.trim()), 'gi'),
46 | '$&'
47 | ),
48 | 'aria-selected': 'false'
49 | });
50 | }
51 | };
52 |
53 | input = ReactDom.findDOMNode(this);
54 | this._autocomplete = new Awesomplete(input, config);
55 |
56 | input.addEventListener(
57 | 'awesomplete-selectcomplete',
58 | this._handleAutocompleteSelect.bind(this)
59 | );
60 | input.addEventListener(
61 | 'keyup',
62 | this._handleInputChange.bind(this)
63 | );
64 | }
65 |
66 | updateCountry(country) {
67 | this._country = country;
68 | }
69 |
70 | _handleInputChange(event) {
71 | var value = this._getInputValue();
72 | var updateAutocomplete = compose(
73 | this._autocomplete.evaluate.bind(this._autocomplete),
74 | (list) => this._autocomplete.list = list,
75 | (list) => list.map((item) => item.description),
76 | (results) => this._googlePredictions = results
77 | );
78 | var fail = compose(
79 | updateAutocomplete,
80 | (text) => [{ description: text }],
81 | (text) => {
82 | return this._noMatching.replace('{{value}}', text);
83 | }
84 | );
85 | var navKeys = [38, 40, 13, 27];
86 | var isItNavKey = navKeys.indexOf(event.keyCode) >= 0;
87 |
88 | if (!isItNavKey) {
89 | this._getPredictions(value).then(updateAutocomplete, fail);
90 | }
91 | }
92 |
93 | _handleAutocompleteSelect() {
94 | var value = this._getInputValue();
95 | var find = (list) => {
96 | let l = list.filter(item => item.description === value);
97 |
98 | return l.length > 0 ? l[0] : false;
99 | };
100 | var validate = item => item && item.place_id ? item.place_id : false;
101 | var getPlaceId = compose(validate, find);
102 | var success = (location) => {
103 | this.props.onLocationSet && this.props.onLocationSet({
104 | description: value,
105 | coords: {
106 | lat: location.lat(),
107 | lng: location.lng()
108 | }
109 | });
110 | };
111 |
112 | this._getCoordinates(getPlaceId(this._googlePredictions)).then(success);
113 | }
114 |
115 | _getInputValue() {
116 | return ReactDom.findDOMNode(this).value;
117 | }
118 |
119 | _getPredictions(text) {
120 | var service = (this.props.google || google).createAutocompleteService();
121 | var isThereAnyText = !!text;
122 |
123 | if (isThereAnyText) {
124 | return new Promise((resolve, reject) => {
125 | service.getPlacePredictions({
126 | input: text,
127 | componentRestrictions: { country: this._country },
128 | types: ['(regions)']
129 | }, (result) => {
130 | if (result !== null) {
131 | resolve(result);
132 | } else {
133 | reject(text);
134 | }
135 | });
136 | });
137 | }
138 | return new Promise((resolve, reject) => {});
139 | }
140 |
141 | _getCoordinates(placeId) {
142 | var geocoder = (this.props.google || google).createGeocoder();
143 |
144 | return new Promise((resolve, reject) => {
145 | geocoder.geocode({ placeId: placeId }, (results, status) => {
146 | if (status === 'OK' && results && results.length > 0) {
147 | resolve(results[0].geometry.location);
148 | } else {
149 | reject(false);
150 | }
151 | });
152 | });
153 | }
154 |
155 | };
156 |
157 | Location.defaultProps = {
158 | className: '',
159 | style: {}
160 | };
161 |
162 | Location.propTypes = {
163 | onLocationSet: PropTypes.func,
164 | inputProps: PropTypes.object,
165 | country: PropTypes.string,
166 | noMatching: PropTypes.string,
167 | google: PropTypes.object
168 | };
169 |
--------------------------------------------------------------------------------
/src/vendor/google.js:
--------------------------------------------------------------------------------
1 | function createAutocompleteService() {
2 | return new global.google.maps.places.AutocompleteService();
3 | };
4 |
5 | function createGeocoder() {
6 | return new global.google.maps.Geocoder();
7 | };
8 |
9 | export default {
10 | createAutocompleteService,
11 | createGeocoder
12 | };
13 |
14 |
--------------------------------------------------------------------------------
/test/fixtures/predictions.js:
--------------------------------------------------------------------------------
1 | export default function predictions() {
2 | return [
3 | {
4 | 'description': 'New York, NY, United States',
5 | 'id': '7eae6a016a9c6f58e2044573fb8f14227b6e1f96',
6 | 'matched_substrings': [
7 | {
8 | 'length': 2,
9 | 'offset': 0
10 | }
11 | ],
12 | 'place_id': 'ChIJOwg_06VPwokRYv534QaPC8g',
13 | 'reference': '...',
14 | 'terms': [
15 | {
16 | 'offset': 0,
17 | 'value': 'New York'
18 | },
19 | {
20 | 'offset': 10,
21 | 'value': 'NY'
22 | },
23 | {
24 | 'offset': 14,
25 | 'value': 'United States'
26 | }
27 | ],
28 | 'types': [
29 | 'locality',
30 | 'political',
31 | 'geocode'
32 | ]
33 | },
34 | {
35 | 'description': 'New York, IA, United States',
36 | 'id': '329cb7144660f29514f351db26cef864634f748a',
37 | 'matched_substrings': [
38 | {
39 | 'length': 2,
40 | 'offset': 0
41 | }
42 | ],
43 | 'place_id': 'ChIJD_qB3F8X6YcRDraFbXmLUD4',
44 | 'reference': '...',
45 | 'terms': [
46 | {
47 | 'offset': 0,
48 | 'value': 'New York'
49 | },
50 | {
51 | 'offset': 10,
52 | 'value': 'IA'
53 | },
54 | {
55 | 'offset': 14,
56 | 'value': 'United States'
57 | }
58 | ],
59 | 'types': [
60 | 'locality',
61 | 'political',
62 | 'geocode'
63 | ]
64 | },
65 | {
66 | 'description': 'New York, United States',
67 | 'id': '349c7fc49816ce54bb586cf8fa2cd79b255746b3',
68 | 'matched_substrings': [
69 | {
70 | 'length': 2,
71 | 'offset': 0
72 | }
73 | ],
74 | 'place_id': 'ChIJqaUj8fBLzEwRZ5UY3sHGz90',
75 | 'reference': '...',
76 | 'terms': [
77 | {
78 | 'offset': 0,
79 | 'value': 'New York'
80 | },
81 | {
82 | 'offset': 10,
83 | 'value': 'United States'
84 | }
85 | ],
86 | 'types': [
87 | 'administrative_area_level_1',
88 | 'political',
89 | 'geocode'
90 | ]
91 | },
92 | {
93 | 'description': 'New Jersey, United States',
94 | 'id': '10806aba84cf3520ebd83c6a3f749bad23c4e2e6',
95 | 'matched_substrings': [
96 | {
97 | 'length': 2,
98 | 'offset': 0
99 | }
100 | ],
101 | 'place_id': 'ChIJn0AAnpX7wIkRjW0_-Ad70iw',
102 | 'reference': '...',
103 | 'terms': [
104 | {
105 | 'offset': 0,
106 | 'value': 'New Jersey'
107 | },
108 | {
109 | 'offset': 12,
110 | 'value': 'United States'
111 | }
112 | ],
113 | 'types': [
114 | 'administrative_area_level_1',
115 | 'political',
116 | 'geocode'
117 | ]
118 | },
119 | {
120 | 'description': 'Newark, NJ, United States',
121 | 'id': 'c71040d6268e495203b4ca7ca4299893601f63fc',
122 | 'matched_substrings': [
123 | {
124 | 'length': 2,
125 | 'offset': 0
126 | }
127 | ],
128 | 'place_id': 'ChIJHQ6aMnBTwokRc-T-3CrcvOE',
129 | 'reference': '...',
130 | 'terms': [
131 | {
132 | 'offset': 0,
133 | 'value': 'Newark'
134 | },
135 | {
136 | 'offset': 8,
137 | 'value': 'NJ'
138 | },
139 | {
140 | 'offset': 12,
141 | 'value': 'United States'
142 | }
143 | ],
144 | 'types': [
145 | 'locality',
146 | 'political',
147 | 'geocode'
148 | ]
149 | }
150 | ];
151 | };
152 |
--------------------------------------------------------------------------------
/test/test.spec.jsx:
--------------------------------------------------------------------------------
1 | import Location from '../src/Location.jsx';
2 | import TestUtils from 'react-addons-test-utils';
3 | import React from 'react';
4 | import predictions from './fixtures/predictions.js';
5 |
6 | var component;
7 | var onLocationSet = sinon.stub();
8 | var google = {
9 | createAutocompleteService: function () {
10 | return {
11 | getPlacePredictions: sinon.stub().callsArgWith(1, predictions())
12 | };
13 | },
14 | createGeocoder: function () {
15 | return {
16 | geocode: sinon.stub().callsArgWith(1, [
17 | {
18 | geometry: {
19 | location: {
20 | lat: () => 0,
21 | lng: () => 0
22 | }
23 | }
24 | }
25 | ], 'OK')
26 | };
27 | }
28 | };
29 |
30 | function simulateKeyboardEvent(el, keyCode, eventType = 'keyup') {
31 | var evt = document.createEvent('Events');
32 | var code = typeof keyCode === 'string' ? keyCode.charCodeAt(0) : keyCode;
33 |
34 | evt.initEvent(eventType, true, true);
35 | evt.keyCode = evt.which = code;
36 | el.dispatchEvent(evt);
37 | };
38 |
39 | function simulateEvent(el, eventName) {
40 | var evt = document.createEvent('Event');
41 |
42 | evt.initEvent(eventName, true, true);
43 | el.dispatchEvent(evt);
44 | };
45 |
46 | describe('Given an instance of the Component', function () {
47 |
48 | describe('when we render the component', function () {
49 |
50 | before(() => {
51 | component = TestUtils.renderIntoDocument(
52 |
55 | );
56 | });
57 |
58 | it('should have an input field', function () {
59 | var input = TestUtils.scryRenderedDOMComponentsWithTag(component, 'input');
60 |
61 | expect(input).to.have.length.above(0, 'Expected to have element with tag ');
62 | });
63 |
64 | describe('and when we type a city name and choose some of the suggestions', function () {
65 | this.timeout(5000);
66 | it('should call onLocationSet', function (done) {
67 | var input = TestUtils.findRenderedDOMComponentWithTag(component, 'input');
68 |
69 | input.value = 'New';
70 | simulateKeyboardEvent(input, 'n');
71 | setTimeout(function () {
72 | simulateKeyboardEvent(input, 40, 'keydown');
73 | simulateKeyboardEvent(input, 40, 'keydown');
74 | simulateKeyboardEvent(input, 13, 'keydown');
75 | simulateEvent(input, 'awesomplete-selectcomplete');
76 | setTimeout(function () {
77 | expect(onLocationSet).to.be.called;
78 | done();
79 | }, 10);
80 | }, 10);
81 | });
82 | });
83 |
84 | });
85 | });
86 |
--------------------------------------------------------------------------------
/tests.webpack.js:
--------------------------------------------------------------------------------
1 | var context = require.context('./test', true, /.+\.spec\.jsx?$/);
2 |
3 | require('core-js/es5');
4 |
5 | context.keys().forEach(context);
6 | module.exports = context;
7 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var WebpackDevServer = require('webpack-dev-server');
3 | var path = require('path');
4 |
5 | var host = '0.0.0.0';
6 | var port = '9000';
7 |
8 | var config = {
9 | entry: './example-es6/src',
10 | devtool: 'source-map',
11 | output: {
12 | path: __dirname + '/example-es6/build',
13 | filename: 'app.js',
14 | publicPath: __dirname + '/example-es6'
15 | },
16 | module: {
17 | loaders: [
18 | {
19 | test: /(\.jsx|\.js)$/,
20 | loader: 'babel',
21 | exclude: /(node_modules|bower_components)/,
22 | query: {
23 | presets: ['react', 'es2015']
24 | }
25 | },
26 | {
27 | test: /(\.jsx|\.js)$/,
28 | loader: "eslint-loader",
29 | exclude: /node_modules/
30 | }
31 | ]
32 | },
33 | resolve: {
34 | root: path.resolve('./src'),
35 | extensions: ['', '.js', '.jsx']
36 | }
37 | };
38 |
39 | new WebpackDevServer(webpack(config), {
40 | contentBase: './example-es6',
41 | hot: true,
42 | debug: true
43 | }).listen(port, host, function (err, result) {
44 | if (err) {
45 | console.log(err);
46 | }
47 | });
48 | console.log('-------------------------');
49 | console.log('Local web server runs at http://' + host + ':' + port);
50 | console.log('-------------------------');
51 |
52 | module.exports = config;
53 |
--------------------------------------------------------------------------------