diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..396ff4f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# This file is for unifying the coding style for different editors and IDEs +# editorconfig.org +root = true + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = true +indent_style = tab + +[*.json] +indent_style = space +indent_size = 2 diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..200b8e9 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,5 @@ +.publish/* +dist/* +example/dist/* +lib/* +node_modules/* diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..2bc0140 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,32 @@ +{ + "parser": "babel-eslint", + "env": { + "browser": true, + "node": true + }, + "plugins": [ + "react" + ], + "rules": { + "curly": [2, "multi-line"], + "quotes": [2, "single", "avoid-escape"], + "react/display-name": 0, + "react/jsx-boolean-value": 1, + "react/jsx-quotes": 1, + "react/jsx-no-undef": 1, + "react/jsx-sort-props": 0, + "react/jsx-sort-prop-types": 1, + "react/jsx-uses-react": 1, + "react/jsx-uses-vars": 1, + "react/no-did-mount-set-state": 1, + "react/no-did-update-set-state": 1, + "react/no-multi-comp": 1, + "react/no-unknown-property": 1, + "react/prop-types": 1, + "react/react-in-jsx-scope": 1, + "react/self-closing-comp": 1, + "react/wrap-multilines": 1, + "semi": 2, + "strict": 0 + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..092926f --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Coverage tools +lib-cov +coverage +coverage.html +.cover* + +# Dependency directory +node_modules + +# Example build directory +example/dist +.publish + +# Editor and other tmp files +*.swp +*.un~ +*.iml +*.ipr +*.iws +*.sublime-* +.idea/ +*.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..f3c3b27 --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +# React Select Google Places + +__COMPONENT DESCRIPTION GOES HERE__ + + +## Demo & Examples + +Live demo: [javiercr.github.io/react-select-google-places](http://javiercr.github.io/react-select-google-places/) + +To build the examples locally, run: + +``` +npm install +npm start +``` + +Then open [`localhost:8000`](http://localhost:8000) in a browser. + + +## Installation + +The easiest way to use react-select-google-places is to install it from NPM and include it in your own React build process (using [Browserify](http://browserify.org), [Webpack](http://webpack.github.io/), etc). + +You can also use the standalone build by including `dist/react-select-google-places.js` in your page. If you use this, make sure you have already included React, and it is available as a global variable. + +``` +npm install react-select-google-places --save +``` + + +## Usage + +__EXPLAIN USAGE HERE__ + +``` +var ReactSelectGooglePlaces = require('react-select-google-places'); + +Example +``` + +### Properties + +* __DOCUMENT PROPERTIES HERE__ + +### Notes + +__ADDITIONAL USAGE NOTES__ + + +## Development (`src`, `lib` and the build process) + +**NOTE:** The source code for the component is in `src`. A transpiled CommonJS version (generated with Babel) is available in `lib` for use with node.js, browserify and webpack. A UMD bundle is also built to `dist`, which can be included without the need for any build system. + +To build, watch and serve the examples (which will also watch the component source), run `npm start`. If you just want to watch changes to `src` and rebuild `lib`, run `npm run watch` (this is useful if you are working with `npm link`). + +## License + +__PUT LICENSE HERE__ + +Copyright (c) 2016 Javier Cuevas. + diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..ab14ffc --- /dev/null +++ b/bower.json @@ -0,0 +1,29 @@ +{ + "name": "react-select-google-places", + "version": "0.0.0", + "description": "React Select Google Places", + "main": "dist/react-select-google-places.min.js", + "homepage": "https://github.com/javiercr/react-select-google-places", + "authors": [ + "Javier Cuevas" + ], + "moduleType": [ + "amd", + "globals", + "node" + ], + "keywords": [ + "react", + "react-component" + ], + "license": "MIT", + "ignore": [ + ".editorconfig", + ".gitignore", + "package.json", + "src", + "node_modules", + "example", + "test" + ] +} diff --git a/example/src/.gitignore b/example/src/.gitignore new file mode 100644 index 0000000..c33fa28 --- /dev/null +++ b/example/src/.gitignore @@ -0,0 +1,5 @@ +## This file is here to ensure it is included in the gh-pages branch, +## when `gulp deploy` is used to push updates to the demo site. + +# Dependency directory +node_modules diff --git a/example/src/example.js b/example/src/example.js new file mode 100644 index 0000000..928e852 --- /dev/null +++ b/example/src/example.js @@ -0,0 +1,18 @@ +var React = require('react'); +var ReactDOM = require('react-dom'); +var SelectGooglePlaces = require('react-select-google-places'); + +var App = React.createClass({ + render () { + return ( +
+

Demo Single selection

+ +

Demo Multiple selection

+ +
+ ); + } +}); + +ReactDOM.render(, document.getElementById('app')); diff --git a/example/src/example.less b/example/src/example.less new file mode 100644 index 0000000..73e35b5 --- /dev/null +++ b/example/src/example.less @@ -0,0 +1,63 @@ +@import "../../node_modules/react-select/less/select.less"; + +/* +// Examples Stylesheet +// ------------------- +*/ + +body { + font-family: Helvetica Neue, Helvetica, Arial, sans-serif; + font-size: 14px; + color: #333; + margin: 0; + padding: 0; +} + +a { + color: #08c; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +.container { + margin-left: auto; + margin-right: auto; + max-width: 720px; + padding: 1em; +} + +.footer { + margin-top: 50px; + border-top: 1px solid #eee; + padding: 20px 0; + font-size: 12px; + color: #999; +} + +h1, h2, h3, h4, h5, h6 { + color: #222; + font-weight: 100; + margin: 0.5em 0; +} + +label { + color: #999; + display: inline-block; + font-size: 0.85em; + font-weight: bold; + margin: 1em 0; + text-transform: uppercase; +} + +.hint { + margin: 15px 0; + font-style: italic; + color: #999; +} + +.Select, ul { + margin-bottom: 2em; +} \ No newline at end of file diff --git a/example/src/index.html b/example/src/index.html new file mode 100644 index 0000000..b916ce8 --- /dev/null +++ b/example/src/index.html @@ -0,0 +1,32 @@ + + + React Select Google Places + + + +
+

React Select Google Places

+

View project on GitHub

+

+ This is a Google Places Autocomplete select component based on React Select. +

+

Features

+ + + +
+
+ +
+ +
+ + + + diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..907acac --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,42 @@ +var gulp = require('gulp'); +var initGulpTasks = require('react-component-gulp-tasks'); + +/** + * Tasks are added by the react-component-gulp-tasks package + * + * See https://github.com/JedWatson/react-component-gulp-tasks + * for documentation. + * + * You can also add your own additional gulp tasks if you like. + */ + +var taskConfig = { + + component: { + name: 'SelectGooglePlaces', + dependencies: [ + 'classnames', + 'react', + 'react-dom' + ], + lib: 'lib' + }, + + example: { + src: 'example/src', + dist: 'example/dist', + files: [ + 'index.html', + '.gitignore' + ], + scripts: [ + 'example.js' + ], + less: [ + 'example.less' + ] + } + +}; + +initGulpTasks(gulp, taskConfig); diff --git a/package.json b/package.json new file mode 100644 index 0000000..6846d3e --- /dev/null +++ b/package.json @@ -0,0 +1,49 @@ +{ + "name": "react-select-google-places", + "version": "1.0.0", + "description": "React Select Google Places", + "main": "lib/ReactSelectGooglePlaces.js", + "author": "Javier Cuevas", + "homepage": "https://github.com/javiercr/react-select-google-places", + "repository": { + "type": "git", + "url": "https://github.com/javiercr/react-select-google-places.git" + }, + "bugs": { + "url": "https://github.com/javiercr/react-select-google-places/issues" + }, + "dependencies": { + "classnames": "^2.1.2", + "react-select": "^1.0.0-beta11", + "scriptjs": "^2.5.8" + }, + "devDependencies": { + "babel-eslint": "^4.1.3", + "eslint": "^1.6.0", + "eslint-plugin-react": "^3.5.1", + "gulp": "^3.9.0", + "react": "^0.14.0", + "react-component-gulp-tasks": "^0.7.6", + "react-dom": "^0.14.0" + }, + "peerDependencies": { + "react": "^0.14.0" + }, + "browserify-shim": { + "react": "global:React" + }, + "scripts": { + "build": "gulp clean && NODE_ENV=production gulp build", + "examples": "gulp dev:server", + "lint": "eslint ./; true", + "publish:site": "NODE_ENV=production gulp publish:examples", + "release": "NODE_ENV=production gulp release", + "start": "gulp dev", + "test": "echo \"no tests yet\" && exit 0", + "watch": "gulp watch:lib" + }, + "keywords": [ + "react", + "react-component" + ] +} diff --git a/src/SelectGooglePlaces.js b/src/SelectGooglePlaces.js new file mode 100644 index 0000000..db41809 --- /dev/null +++ b/src/SelectGooglePlaces.js @@ -0,0 +1,117 @@ +var React = require('react'); + +import Select from 'react-select'; +import scriptjs from 'scriptjs'; + +const SelectGooglePlaces = React.createClass({ + displayName: 'SelectGooglePlaces', + + propTypes: { + multi: React.PropTypes.bool, // Single or multiple places selection + language: React.PropTypes.string, // Language code for loading Google Maps API + country: React.PropTypes.string, // ISO 3166-1 Alpha-2 country code for limiting results + types: React.PropTypes.array, // Types of results to be displayed + // See https://developers.google.com/places/supported_types#table3 + formatName: React.PropTypes.func, // Receives the result placesService.getDetails() and returns a formatted name + // See https://developers.google.com/maps/documentation/javascript/3.exp/reference#PlaceResult + optionsForSelect: React.PropTypes.object // See https://github.com/JedWatson/react-select#further-options + }, + + getDefaultProps () { + return { + language: 'en', + country: 'USA', + types: ["(cities)"], + formatName(placeResult) { + return placeResult.address_components[0].long_name; + }, + optionsForSelect: { + multi: true, + cache: false, + name: 'places' + } + } + }, + + getInitialState () { + return { + value: null, + autocompleteService: null, + placesService: null + }; + }, + + componentDidMount() { + if (typeof window.google === 'undefined') { + window.googleMapsLoaded = () => { + scriptjs.done('google-maps-places') + } + + scriptjs(`https://maps.googleapis.com/maps/api/js?libraries=places&language=${this.props.language}`, 'google-maps-places'); + scriptjs.ready('google-maps-places', () => { + this.handleLoaded(google.maps) + }) + } else { + this.handleLoaded(google.maps) + } + }, + + handleLoaded(googleMaps) { + this.setState({ + autocompleteService: new googleMaps.places.AutocompleteService(), + placesService: new googleMaps.places.PlacesService(this.refs.attributions) + }); + }, + + onChange (value) { + let selectedPlace; + if (value.constructor === Array) { + selectedPlace = value[value.length - 1]; + } else { + selectedPlace = value; + } + + // Once a place is selected, fetch more data for it (long_name, lat, lng, etc.) + if (selectedPlace) { + this.processPlace(selectedPlace, () => this.setState({ value: value })); + } else { + this.setState({ value: [] }); + } + }, + + processPlace (autocompletePrediction, callback) { + this.state.placesService.getDetails({placeId: autocompletePrediction.place_id}, (placeResult) => { + autocompletePrediction = Object.assign(autocompletePrediction, placeResult); + autocompletePrediction.description = this.props.formatName(placeResult); + callback(); + }); + }, + + getPredictions (input, callback) { + if (this.state.autocompleteService && input) { + const geocoderRequest = { + input: input, + types: this.props.types, + componentRestrictions: { + country: this.props.country + } + }; + this.state.autocompleteService.getPlacePredictions(geocoderRequest, function(data){ + callback(null, {options: data, complete: false}); + }); + } + callback(null, {options: [], complete: false}); + }, + + render () { + return ( +
+ +
+
+ ); + } +}); + + +export default SelectGooglePlaces; \ No newline at end of file