Skip to content

Commit c237580

Browse files
committed
feat(useGeolocation): Adding useGeolocation function
1 parent dc49c36 commit c237580

File tree

8 files changed

+257
-1
lines changed

8 files changed

+257
-1
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,11 @@ Vue.use(VueCompositionAPI);
5959
## APIs
6060

6161
- Sensors
62+
- [`useGeolocation`](./src/components/useGeolocation/stories/useGeolocation.md) — tracks geolocation state of user's device.
63+
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usegeolocation--demo)
6264
- [`useHover`](./src/components/useHover/stories/useHover.md) — tracks mouse hover state of a given element.
6365
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usehover--demo)
64-
- [`useIntersection`](./src/components/useIntersection/stories/useIntersection.md) — tracks intersection of target element with an ancestor element.
66+
- [`useIntersection`](./src/components/useIntersection/stories/useIntersection.md) — tracks intersection of target element with an ancestor element.
6567
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-useintersection--demo)
6668
[![Demo](https://img.shields.io/badge/advanced_demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-useintersection--advanced-demo)
6769
- [`useMedia`](./src/components/useMedia/stories/useMedia.md) — tracks state of a CSS media query.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './useGeolocation'
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<template>
2+
<table class="table is-fullwidth">
3+
<thead>
4+
<tr>
5+
<th>Prop</th>
6+
<th>Value</th>
7+
</tr>
8+
</thead>
9+
<tbody>
10+
<tr>
11+
<td>geo</td>
12+
<td>
13+
<pre>{{ JSON.stringify(geo, null, 2) }}</pre>
14+
</td>
15+
</tr>
16+
<tr>
17+
<td colspan="2">
18+
<button class="button is-primary" @click="start" v-if="!isTracking">
19+
Enable geolocation tracking
20+
</button>
21+
<button class="button is-danger" @click="stop" v-else>
22+
Disable geolocation tracking
23+
</button>
24+
</td>
25+
</tr>
26+
</tbody>
27+
</table>
28+
</template>
29+
30+
<script lang="ts">
31+
import Vue from 'vue'
32+
import { useGeolocation } from '@src/vue-use-kit'
33+
34+
export default Vue.extend({
35+
name: 'UseGeolocationDemo',
36+
setup() {
37+
const { isTracking, geo, start, stop } = useGeolocation({}, false)
38+
return { isTracking, geo, start, stop }
39+
}
40+
})
41+
</script>
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# useGeolocation
2+
3+
Vue function that tracks geolocation state of user's device, based on the [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API).
4+
5+
## Reference
6+
7+
```typescript
8+
interface UseGeolocation {
9+
loading: boolean
10+
accuracy: number | null
11+
altitude: number | null
12+
altitudeAccuracy: number | null
13+
heading: number | null
14+
latitude: number | null
15+
longitude: number | null
16+
speed: number | null
17+
timestamp: number | null
18+
error?: Error | PositionError
19+
}
20+
```
21+
22+
```typescript
23+
useGeolocation(
24+
options?: PositionOptions,
25+
runOnMount?: boolean
26+
): {
27+
isTracking: Ref<boolean>;
28+
geo: Ref<UseGeolocation>;
29+
start: () => void;
30+
stop: () => void;
31+
}
32+
```
33+
34+
### Parameters
35+
36+
- `options: PositionOptions` the [geolocation position options](https://developer.mozilla.org/en-US/docs/Web/API/PositionOptions)
37+
- `runOnMount: boolean` whether to run the geolocation tracking on mount, `true` by default
38+
39+
### Returns
40+
41+
- `isTracking: Ref<boolean>` whether the function is tracking the user's location or not
42+
- `geo: Ref<UseGeolocation>` the geolocation object
43+
- `start: Function` the function used for starting the geolocation tracking
44+
- `stop: Function` the function used for stopping the geolocation tracking
45+
46+
## Usage
47+
48+
```html
49+
<template>
50+
<div>
51+
<div>
52+
Geo:
53+
<pre>{{ JSON.stringify(geo, null, 2) }}</pre>
54+
</div>
55+
<button @click="start" v-if="!isTracking">
56+
Enable geolocation tracking
57+
</button>
58+
<button @click="stop" v-else>Disable geolocation tracking</button>
59+
</div>
60+
</template>
61+
62+
<script lang="ts">
63+
import Vue from 'vue'
64+
import { useGeolocation } from 'vue-use-kit'
65+
66+
export default Vue.extend({
67+
name: 'UseGeolocationDemo',
68+
setup() {
69+
const { isTracking, geo, start, stop } = useGeolocation()
70+
return { isTracking, geo, start, stop }
71+
}
72+
})
73+
</script>
74+
```
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { storiesOf } from '@storybook/vue'
2+
import path from 'path'
3+
import StoryTitle from '@src/helpers/StoryTitle.vue'
4+
import UseGeolocationDemo from './UseGeolocationDemo.vue'
5+
6+
const functionName = 'useGeolocation'
7+
const functionPath = path.resolve(__dirname, '..')
8+
const notes = require(`./${functionName}.md`).default
9+
10+
const basicDemo = () => ({
11+
components: { StoryTitle, demo: UseGeolocationDemo },
12+
template: `
13+
<div class="container">
14+
<story-title
15+
function-path="${functionPath}"
16+
source-name="${functionName}"
17+
demo-name="UseGeolocationDemo.vue"
18+
>
19+
<template v-slot:title></template>
20+
<template v-slot:intro></template>
21+
</story-title>
22+
<demo />
23+
</div>`
24+
})
25+
26+
storiesOf('sensors|useGeolocation', module)
27+
.addParameters({ notes })
28+
.add('Demo', basicDemo)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// import { mount } from '@src/helpers/test'
2+
// import { useGeolocation } from '@src/vue-use-kit'
3+
4+
describe('useGeolocation', () => {
5+
it('should do something', () => {
6+
// Add test here
7+
})
8+
})
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { ref, onMounted, onUnmounted, Ref } from '@src/api'
2+
3+
export interface UseGeolocation {
4+
loading: boolean
5+
accuracy: number | null
6+
altitude: number | null
7+
altitudeAccuracy: number | null
8+
heading: number | null
9+
latitude: number | null
10+
longitude: number | null
11+
speed: number | null
12+
timestamp: number | null
13+
error?: Error | PositionError
14+
}
15+
16+
const defaultOpts = {
17+
enableHighAccuracy: false,
18+
timeout: Infinity,
19+
maximumAge: 0
20+
}
21+
22+
export function useGeolocation(
23+
options: PositionOptions = {},
24+
runOnMount = true
25+
) {
26+
options = Object.assign(defaultOpts, options)
27+
28+
// Note: surprisingly the watchId can be 0 (not positive) so
29+
// we have to check if watchId !== null every time
30+
let watchId: number | null = null
31+
32+
const geoInitData = {
33+
loading: false,
34+
accuracy: null,
35+
altitude: null,
36+
altitudeAccuracy: null,
37+
heading: null,
38+
latitude: null,
39+
longitude: null,
40+
speed: null,
41+
timestamp: null
42+
}
43+
44+
const isTracking = ref(false)
45+
const geo: Ref<UseGeolocation> = ref({ ...geoInitData })
46+
47+
const onEventError = (error: PositionError) => {
48+
if (watchId === null) return
49+
geo.value.loading = false
50+
geo.value.error = {
51+
code: error.code,
52+
message: error.message
53+
} as PositionError
54+
isTracking.value = false
55+
}
56+
57+
const handleGeolocation = ({ coords, timestamp }: Position) => {
58+
geo.value = {
59+
loading: false,
60+
accuracy: coords.accuracy,
61+
altitude: coords.altitude,
62+
altitudeAccuracy: coords.altitudeAccuracy,
63+
heading: coords.heading,
64+
latitude: coords.latitude,
65+
longitude: coords.longitude,
66+
speed: coords.speed,
67+
timestamp
68+
}
69+
isTracking.value = true
70+
}
71+
72+
const start = () => {
73+
if (watchId !== null) return
74+
geo.value.loading = true
75+
geo.value.timestamp = Date.now()
76+
77+
navigator.geolocation.getCurrentPosition(
78+
handleGeolocation,
79+
onEventError,
80+
options
81+
)
82+
83+
watchId = navigator.geolocation.watchPosition(
84+
handleGeolocation,
85+
onEventError,
86+
options
87+
)
88+
}
89+
90+
const stop = () => {
91+
if (watchId === null) return
92+
navigator.geolocation.clearWatch(watchId)
93+
watchId = null
94+
isTracking.value = false
95+
}
96+
97+
onMounted(() => runOnMount && start())
98+
onUnmounted(stop)
99+
100+
return { isTracking, geo, start, stop }
101+
}

src/vue-use-kit.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export * from './components/getQuery'
33
export * from './components/useClickAway'
44
export * from './components/useFullscreen'
55

6+
export * from './components/useGeolocation'
67
export * from './components/useHover'
78
export * from './components/useIntersection'
89
export * from './components/useMedia'

0 commit comments

Comments
 (0)