Skip to content

Commit 3361352

Browse files
committed
feat(useLocation): Adding useLocation function
1 parent 4a21698 commit 3361352

File tree

8 files changed

+214
-0
lines changed

8 files changed

+214
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ Vue.use(VueCompositionAPI);
6868
- [`useIntersection`](./src/components/useIntersection/stories/useIntersection.md) — tracks intersection of target element with an ancestor element.
6969
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-useintersection--demo)
7070
[![Demo](https://img.shields.io/badge/advanced_demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-useintersection--advanced-demo)
71+
- [`useLocation`](./src/components/useLocation/stories/useLocation.md) — tracks bar navigation location state.
72+
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-uselocation--demo)
7173
- [`useMedia`](./src/components/useMedia/stories/useMedia.md) — tracks state of a CSS media query.
7274
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usemedia--demo)
7375
[![Demo](https://img.shields.io/badge/advanced_demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usemedia--advanced-demo)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './useLocation'
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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>locationState</td>
12+
<td>
13+
<pre>{{ JSON.stringify(locationState, null, 2) }}</pre>
14+
</td>
15+
</tr>
16+
<tr>
17+
<td colspan="2">
18+
<button class="button is-primary" @click="push">
19+
Fire push event
20+
</button>
21+
<button class="button is-danger" @click="replace">
22+
Fire replace event
23+
</button>
24+
</td>
25+
</tr>
26+
<tr>
27+
<td colspan="2">
28+
<button class="button is-primary" @click="start" v-if="!isTracking">
29+
Start tracking location
30+
</button>
31+
<button class="button is-danger" @click="stop" v-else>
32+
Stop tracking location
33+
</button>
34+
</td>
35+
</tr>
36+
</tbody>
37+
</table>
38+
</template>
39+
40+
<script lang="ts">
41+
import Vue from 'vue'
42+
import { useLocation } from '@src/vue-use-kit'
43+
44+
export default Vue.extend({
45+
name: 'UseLocationDemo',
46+
setup() {
47+
let count = 0
48+
const url = location.origin + location.pathname + location.search
49+
const push = () => {
50+
count++
51+
history.pushState({ page: count }, '', `${url}&page=${count}`)
52+
}
53+
const replace = () => {
54+
count--
55+
history.replaceState({ page: count }, '', `${url}&page=${count}`)
56+
}
57+
58+
const { locationState, isTracking, start, stop } = useLocation()
59+
return { locationState, isTracking, start, stop, push, replace }
60+
}
61+
})
62+
</script>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# useLocation
2+
3+
Vue function that tracks bar navigation location state.
4+
5+
## Reference
6+
7+
```typescript
8+
// function useLocation()
9+
```
10+
11+
### Parameters
12+
13+
- `value: string` lorem ipsa
14+
15+
### Returns
16+
17+
- `value: Ref<string>` lorem ipsa
18+
19+
## Usage
20+
21+
```html
22+
<template></template>
23+
```
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 UseLocationDemo from './UseLocationDemo.vue'
5+
6+
const functionName = 'useLocation'
7+
const functionPath = path.resolve(__dirname, '..')
8+
const notes = require(`./${functionName}.md`).default
9+
10+
const basicDemo = () => ({
11+
components: { StoryTitle, demo: UseLocationDemo },
12+
template: `
13+
<div class="container">
14+
<story-title
15+
function-path="${functionPath}"
16+
source-name="${functionName}"
17+
demo-name="UseLocationDemo.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|useLocation', module)
27+
.addParameters({ notes })
28+
.add('Demo', basicDemo)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// import { mount } from '@src/helpers/test'
2+
// import { useLocation } from '@src/vue-use-kit'
3+
4+
afterEach(() => {
5+
jest.clearAllMocks()
6+
})
7+
8+
describe('useLocation', () => {
9+
it('should do something', () => {
10+
// Add test here
11+
})
12+
})
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { ref, onMounted, onUnmounted, Ref } from '@src/api'
2+
3+
// The history methods 'pushState' and 'replaceState' by default do not fire an event
4+
// unless it is coming from user interaction with the browser navigation bar,
5+
// so we are adding a patch to make them detectable
6+
let isPatched = false
7+
const patchHistoryMethodsOnce = () => {
8+
if (isPatched) return
9+
const methods = ['pushState', 'replaceState']
10+
methods.forEach(method => {
11+
const original = (history as any)[method]
12+
;(history as any)[method] = function(state: any) {
13+
// eslint-disable-next-line prefer-rest-params
14+
const result = original.apply(this, arguments)
15+
const event = new Event(method.toLowerCase())
16+
;(event as any).state = state
17+
window.dispatchEvent(event)
18+
return result
19+
}
20+
})
21+
22+
isPatched = true
23+
}
24+
25+
export function useLocation(runOnMount = true) {
26+
const buildState = (trigger: string) => {
27+
const { state, length } = history
28+
29+
const {
30+
hash,
31+
host,
32+
hostname,
33+
href,
34+
origin,
35+
pathname,
36+
port,
37+
protocol,
38+
search
39+
} = location
40+
41+
return {
42+
trigger,
43+
state,
44+
length,
45+
hash,
46+
host,
47+
hostname,
48+
href,
49+
origin,
50+
pathname,
51+
port,
52+
protocol,
53+
search
54+
}
55+
}
56+
const isTracking = ref(false)
57+
const locationState = ref(buildState('load'))
58+
59+
const popState = () => (locationState.value = buildState('popstate'))
60+
const pushState = () => (locationState.value = buildState('pushstate'))
61+
const replaceState = () => (locationState.value = buildState('replacestate'))
62+
63+
const start = () => {
64+
patchHistoryMethodsOnce()
65+
66+
if (isTracking.value) return
67+
isTracking.value = true
68+
window.addEventListener('popstate', popState)
69+
window.addEventListener('pushstate', pushState)
70+
window.addEventListener('replacestate', replaceState)
71+
}
72+
73+
const stop = () => {
74+
if (!isTracking.value) return
75+
isTracking.value = false
76+
window.removeEventListener('popstate', popState)
77+
window.removeEventListener('pushstate', pushState)
78+
window.removeEventListener('replacestate', replaceState)
79+
}
80+
81+
onMounted(() => runOnMount && start())
82+
onUnmounted(stop)
83+
84+
return { locationState, isTracking, start, stop }
85+
}

src/vue-use-kit.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export * from './components/useGeolocation'
77
export * from './components/useHover'
88
export * from './components/useIdle'
99
export * from './components/useIntersection'
10+
export * from './components/useLocation'
1011
export * from './components/useMedia'
1112
export * from './components/useMouse'
1213
export * from './components/useMouseElement'

0 commit comments

Comments
 (0)