├── .babelrc ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── compositor.json ├── package.json ├── rollup.config.js └── src ├── __tests__ ├── __snapshots__ │ └── index.js.snap └── index.js └── index.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "env", 5 | { 6 | "modules": false, 7 | "loose": true 8 | } 9 | ], 10 | "stage-2", 11 | "react" 12 | ], 13 | "env": { 14 | "test": { 15 | "presets": [ 16 | [ 17 | "env", 18 | { 19 | "loose": true 20 | } 21 | ], 22 | "stage-2", 23 | "react" 24 | ] 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /coverage 2 | /demo/dist 3 | /dist 4 | /lib 5 | /es 6 | /node_modules 7 | /umd 8 | /es 9 | npm-debug.log 10 | .idea 11 | yarn.lock 12 | package-lock.json 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "7" 5 | 6 | cache: 7 | directories: 8 | - node_modules 9 | 10 | before_install: 11 | - npm install codecov 12 | 13 | after_success: 14 | - cat ./coverage/lcov.info | ./node_modules/codecov/bin/codecov 15 | 16 | cache: 17 | directories: 18 | - node_modules 19 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | ------------ 3 | 4 | ##### 2.0.0 5 | * Action creators now receive ONE arguement, `store`. 6 | This expands what is possible in an action handler 7 | 8 | * Renamed `addReducer` to `handleActions` (inspired by redux-actions) 9 | The name no longer made sense. Returning a _new_ state from the handler is no longer required. 10 | 11 | * Added `createActions` (inspired by redux-actions) 12 | 13 | Allows developer to create actions and have them available directly from the store 14 | ```js 15 | store.createActions({ 16 | startMediaStream: (constraints) => async store => { 17 | try { 18 | const stream = await navigator.mediaDevices.getUserMedia(constraints) 19 | store.actions.mediaStreamSuccess(stream) 20 | } catch (err) { 21 | store.actions.mediaStreamError(err) 22 | } 23 | }, 24 | addImage: 'camera/ADD_IMAGE' 25 | }) 26 | 27 | // ...later 28 | store.actions.startMediaStream({ audio: true, video: true }) 29 | store.actions.addImage(image) 30 | ``` 31 | 32 | If the property of the action creator is a plain string like `addImage: 'camera/ADD_IMAGE'` you can use the name in your reducer. 33 | 34 | ```js 35 | store.handleActions({ 36 | [store.actions.addImage]: (state, image) => { 37 | state.camera.images.push(image) 38 | } 39 | }) 40 | ``` 41 | 42 | * Reserved event types with the prefix `$$store:` 43 | `$$store:state:change` is fired after an action handler is called 44 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Prerequisites 2 | 3 | [Node.js](http://nodejs.org/) >= v4 must be installed. 4 | 5 | ## Installation 6 | 7 | - Running `npm install` in the module's root directory will install everything you need for development. 8 | 9 | ## Running Tests 10 | 11 | - `npm test` will run the tests once. 12 | 13 | - `npm run test:coverage` will run the tests and produce a coverage report in `coverage/`. 14 | 15 | - `npm run test:watch` will run the tests on every change. 16 | 17 | ## Building 18 | 19 | - `npm run build` will build the module for publishing to npm. 20 | 21 | - `npm run clean` will delete built resources. 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Kye Hohenberger 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # react-geolocation 4 | 🌎🛰 Declarative geolocation in React 5 | 6 | [![npm version](https://badge.fury.io/js/react-geolocation.svg)](https://badge.fury.io/js/react-geolocation) 7 | [![Build Status](https://travis-ci.org/tkh44/react-geolocation.svg?branch=master)](https://travis-ci.org/tkh44/react-geolocation) 8 | [![codecov](https://codecov.io/gh/tkh44/react-geolocation/branch/master/graph/badge.svg)](https://codecov.io/gh/tkh44/react-geolocation) 9 | 10 | - [Install](#install) 11 | - [Basic Usage](#basic-usage) 12 | - [Props](#props) 13 | 14 | ## Install 15 | 16 | ```bash 17 | npm install -S react-geolocation 18 | ``` 19 | 20 | ## Basic Usage 21 | ```javascript 22 | 29 |
30 | 31 | {error && 32 |
33 | {error.message} 34 |
} 35 |
36 |         latitude: {latitude}
37 |         longitude: {longitude}
38 |       
39 |
} 40 | /> 41 | ``` 42 | 43 | ## Props 44 | 45 | ### [enableHighAccuracy `boolean`](https://developer.mozilla.org/en-US/docs/Web/API/PositionOptions/enableHighAccuracy) 46 | 47 | 48 | ### [timeout `number`](https://developer.mozilla.org/en-US/docs/Web/API/PositionOptions/timeout) 49 | 50 | 51 | ### [maximumAge `number`](https://developer.mozilla.org/en-US/docs/Web/API/PositionOptions/maximumAge) 52 | 53 | 54 | ### render `function` 55 | 56 | `render` is a function that receives an object as its only argument. 57 | 58 | The object contains the following keys: 59 | 60 | - fetchingPosition: `bool` 61 | - [position](https://developer.mozilla.org/en-US/docs/Web/API/Position): `object` 62 | - [error](https://developer.mozilla.org/en-US/docs/Web/API/PositionError): `object` 63 | - getCurrentPosition: `function` 64 | 65 | 66 | ### lazy `boolean` 67 | 68 | If true then the component will **not** perform the fetch on mount. 69 | You must use the `getCurrentPosition` named argument in order to initiate the request. 70 | 71 | ```javascript 72 | ( 75 |
76 | 77 |
Fetching Position: {fetchingPosition}
78 |
79 | )} 80 | /> 81 | // renders "Fetching Position: false" until the button is clicked 82 | ``` 83 | 84 | 85 | ### onSuccess `function` 86 | 87 | callback called on success. Its only argument is `position` 88 | 89 | - https://developer.mozilla.org/en-US/docs/Web/API/Position 90 | 91 | ### onError `function` 92 | 93 | callback called on error. Its only argument is `error` 94 | 95 | - https://developer.mozilla.org/en-US/docs/Web/API/PositionError 96 | 97 | -------------------------------------------------------------------------------- /compositor.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tkh44/react-geolocation", 3 | "version": "0.1.4", 4 | "libraries": { 5 | "xv": "^1.1.25" 6 | }, 7 | "title": "react-geolocation", 8 | "branch": "master", 9 | "style": { 10 | "name": "Future", 11 | "componentSet": { 12 | "nav": "nav/BasicNav", 13 | "header": "header/BannerHeader", 14 | "article": "article/BasicArticle", 15 | "footer": "footer/BasicFooter" 16 | }, 17 | "fontFamily": "\"Avenir Next\", Helvetica, sans-serif", 18 | "heading": { 19 | "fontWeight": 500, 20 | "textTransform": "uppercase", 21 | "letterSpacing": "0.2em" 22 | }, 23 | "alternativeText": { 24 | "textTransform": "uppercase", 25 | "fontWeight": 400, 26 | "letterSpacing": "0.2em", 27 | "opacity": 0.75 28 | }, 29 | "colors": { 30 | "text": "#333", 31 | "background": "#fff", 32 | "primary": "#666", 33 | "secondary": "#888", 34 | "highlight": "#1f80ff", 35 | "muted": "#f6f6f6", 36 | "border": "#eee" 37 | }, 38 | "layout": { 39 | "centered": true, 40 | "maxWidth": 1024, 41 | "bannerHeight": "80vh" 42 | } 43 | }, 44 | "content": [ 45 | { 46 | "component": "nav", 47 | "links": [ 48 | { 49 | "href": "https://github.com/tkh44/react-geolocation", 50 | "text": "GitHub" 51 | }, 52 | { 53 | "href": "https://npmjs.com/package/react-geolocation", 54 | "text": "npm" 55 | } 56 | ] 57 | }, 58 | { 59 | "component": "header", 60 | "heading": "react-geolocation", 61 | "subhead": "🌎🛰 Declarative geolocation for React", 62 | "children": [ 63 | { 64 | "component": "ui/TweetButton", 65 | "text": "react-geolocation: 🌎🛰 Declarative geolocation for React", 66 | "url": "" 67 | }, 68 | { 69 | "component": "ui/GithubButton", 70 | "user": "tkh44", 71 | "repo": "react-geolocation" 72 | } 73 | ], 74 | "text": "v1.0.2", 75 | "backgroundImage": "", 76 | "image": "" 77 | }, 78 | { 79 | "component": "article", 80 | "metadata": { 81 | "source": "github.readme" 82 | }, 83 | "html": "\n\n

\n\n

\n\n

Install

\n
npm install -S react-geolocation

Basic Usage

\n
<Geolocation\n  render={({\n    fetchingPosition,\n    position: { coords: { latitude, longitude } = {} } = {},\n    error,\n    getCurrentPosition\n  }) =>\n    <div>\n      <button onClick={getCurrentPosition}>Get Position</button>\n      {error &&\n        <div>\n          {error.message}\n        </div>}\n      <pre>\n        latitude: {latitude}\n        longitude: {longitude}\n      </pre>\n    </div>}\n/>

Props

\n

enableHighAccuracy boolean

\n

timeout number

\n

maximumAge number

\n

render function

\n

render is a function that receives an object as its only argument.

\n

The object contains the following keys:

\n\n

lazy boolean

\n

If true then the component will not perform the fetch on mount. \nYou must use the getCurrentPosition named argument in order to initiate the request.

\n
<Geolocation \n  lazy \n  render={({getCurrentPosition, fetchingPosition}) => (\n    <div>\n      <button onClick={getCurrentPosition}>Get Current Position</button>\n      <div>Fetching Position: {fetchingPosition}</div>\n    </div>\n  )}\n/> \n// renders "Fetching Position: false" until the button is clicked

onSuccess function

\n

callback called on success. Its only argument is position

\n\n

onError function

\n

callback called on error. Its only argument is error

\n\n" 84 | }, 85 | { 86 | "component": "footer", 87 | "links": [ 88 | { 89 | "href": "https://github.com/tkh44/react-geolocation", 90 | "text": "GitHub" 91 | }, 92 | { 93 | "href": "https://github.com/tkh44", 94 | "text": "tkh44" 95 | } 96 | ] 97 | } 98 | ] 99 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-geolocation", 3 | "version": "1.0.4", 4 | "description": "Declarative geolocation in React", 5 | "jsnext:main": "dist/react-geolocation.es.js", 6 | "module": "dist/react-geolocation.es.js", 7 | "main": "dist/react-geolocation.js", 8 | "umd:main": "dist/react-geolocation.umd.js", 9 | "files": [ 10 | "dist" 11 | ], 12 | "scripts": { 13 | "build": "npm-run-all clean -p rollup -p minify:* -s size", 14 | "clean": "rimraf dist", 15 | "test": "standard src test && jest --coverage", 16 | "test:watch": "jest --watch", 17 | "rollup": "rollup -c", 18 | "minify:cjs": "uglifyjs $npm_package_main -cm toplevel -o $npm_package_main -p relative --in-source-map ${npm_package_main}.map --source-map ${npm_package_main}.map", 19 | "minify:umd": "uglifyjs $npm_package_umd_main -cm -o $npm_package_umd_main -p relative --in-source-map ${npm_package_umd_main}.map --source-map ${npm_package_umd_main}.map", 20 | "size": "echo \"Gzipped Size: $(strip-json-comments --no-whitespace $npm_package_main | gzip-size)\"", 21 | "release": "npm run test && npm run build && npm version patch && npm publish && git push --tags" 22 | }, 23 | "dependencies": {}, 24 | "peerDependencies": { 25 | "react": ">=14" 26 | }, 27 | "devDependencies": { 28 | "babel-eslint": "^7.2.3", 29 | "babel-jest": "^20.0.3", 30 | "babel-polyfill": "^6.23.0", 31 | "babel-preset-env": "^1.4.0", 32 | "babel-preset-react": "^6.24.1", 33 | "babel-preset-stage-2": "^6.24.1", 34 | "gzip-size-cli": "^2.0.0", 35 | "hapi": "^16.1.0", 36 | "jest": "^20.0.4", 37 | "npm-run-all": "^4.0.2", 38 | "pretty-bytes-cli": "^2.0.0", 39 | "prop-types": "^15.5.8", 40 | "raw-loader": "^0.5.1", 41 | "react": "^15.5.4", 42 | "react-addons-test-utils": "^15.5.1", 43 | "react-dom": "^15.5.4", 44 | "react-test-renderer": "^15.6.1", 45 | "rimraf": "^2.6.1", 46 | "rollup": "^0.47.2", 47 | "rollup-plugin-babel": "^3.0.1", 48 | "standard": "^10.0.2", 49 | "strip-json-comments-cli": "^1.0.1", 50 | "uglify-js": "^2.8.22" 51 | }, 52 | "author": "Kye Hohenberger", 53 | "homepage": "https://github.com/tkh44/react-geolocation#readme", 54 | "license": "MIT", 55 | "repository": { 56 | "type": "git", 57 | "url": "git+https://github.com/tkh44/react-geolocation.git" 58 | }, 59 | "directories": { 60 | "test": "tests" 61 | }, 62 | "keywords": [ 63 | "react-component", 64 | "react", 65 | "geolocation", 66 | "react-geolocation" 67 | ], 68 | "eslintConfig": { 69 | "extends": "standard", 70 | "parser": "babel-eslint" 71 | }, 72 | "standard": { 73 | "parser": "babel-eslint", 74 | "ignore": [ 75 | "/dist/" 76 | ] 77 | }, 78 | "bugs": { 79 | "url": "https://github.com/tkh44/react-geolocation/issues" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel'; 2 | import fs from 'fs' 3 | 4 | const pkg = JSON.parse(fs.readFileSync('./package.json')) 5 | 6 | export default { 7 | entry: 'src/index.js', 8 | external: ['react'], 9 | exports: 'named', 10 | globals: { react: 'React' }, 11 | useStrict: false, 12 | sourceMap: true, 13 | plugins: [babel({ 14 | exclude: 'node_modules/**' 15 | })], 16 | targets: [ 17 | {dest: pkg.main, format: 'cjs'}, 18 | {dest: pkg.module, format: 'es'}, 19 | {dest: pkg['umd:main'], format: 'umd', moduleName: pkg.name} 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/index.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`react-geolocation renders 1`] = `null`; 4 | 5 | exports[`react-geolocation renders with children 1`] = `null`; 6 | -------------------------------------------------------------------------------- /src/__tests__/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-env jest */ 2 | // eslint-disable-next-line no-unused-vars 3 | import React from 'react' 4 | import Geolocation from '../index' 5 | import renderer from 'react-test-renderer' 6 | const TestUtils = require('react-dom/test-utils') 7 | 8 | describe('react-geolocation', () => { 9 | test('renders', () => { 10 | const tree = renderer.create().toJSON() 11 | expect(tree).toMatchSnapshot() 12 | }) 13 | 14 | test('renders with children', () => { 15 | const renderCallback = result => { 16 | return ( 17 |
18 |           {JSON.stringify(result)}
19 |         
20 | ) 21 | } 22 | const tree = renderer 23 | .create( 24 | 25 | {renderCallback} 26 | 27 | ) 28 | .toJSON() 29 | expect(tree).toMatchSnapshot() 30 | }) 31 | 32 | test('can render with no children', () => { 33 | TestUtils.renderIntoDocument() 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | export default class Geolocation extends React.Component { 5 | constructor (props) { 6 | super(props) 7 | 8 | this.state = { 9 | fetchingPosition: false, 10 | position: undefined, 11 | error: undefined 12 | } 13 | } 14 | 15 | componentWillMount () { 16 | if (typeof window !== 'object') { 17 | return 18 | } 19 | 20 | if (!('geolocation' in window.navigator)) { 21 | return 22 | } 23 | 24 | if (this.props.lazy) { 25 | return 26 | } 27 | 28 | this.getCurrentPosition() 29 | } 30 | 31 | componentWillUnmount () { 32 | this.willUnmount = true 33 | } 34 | 35 | getCurrentPosition = () => { 36 | const { 37 | enableHighAccuracy, 38 | timeout, 39 | maximumAge, 40 | onSuccess, 41 | onError 42 | } = this.props 43 | 44 | this.setState({ fetchingPosition: true }) 45 | 46 | return window.navigator.geolocation.getCurrentPosition( 47 | position => { 48 | if (this.willUnmount) return 49 | 50 | this.setState({ position, fetchingPosition: false }, () => 51 | onSuccess(position) 52 | ) 53 | }, 54 | error => { 55 | if (this.willUnmount) return 56 | 57 | this.setState({ error, fetchingPosition: false }, () => onError(error)) 58 | }, 59 | { enableHighAccuracy, timeout, maximumAge } 60 | ) 61 | } 62 | 63 | render () { 64 | if (!this.props.render) { 65 | return null 66 | } 67 | return ( 68 | this.props.render({ 69 | getCurrentPosition: this.getCurrentPosition, 70 | fetchingPosition: this.state.fetchingPosition, 71 | position: this.state.position, 72 | error: this.state.error 73 | }) || null 74 | ) 75 | } 76 | } 77 | 78 | Geolocation.propTypes = { 79 | // https://developer.mozilla.org/en-US/docs/Web/API/PositionOptions 80 | enableHighAccuracy: PropTypes.bool, 81 | timeout: PropTypes.number, 82 | maximumAge: PropTypes.number, 83 | // https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition 84 | onSuccess: PropTypes.func, 85 | onError: PropTypes.func, 86 | // Do not call getCurrentPosition on mount 87 | lazy: PropTypes.bool 88 | } 89 | 90 | Geolocation.defaultProps = { 91 | enableHighAccuracy: false, 92 | timeout: Infinity, 93 | maximumAge: 0, 94 | onSuccess: pos => {}, 95 | // eslint-disable-next-line handle-callback-err 96 | onError: err => {}, 97 | lazy: false 98 | } 99 | --------------------------------------------------------------------------------