Skip to content

Commit

Permalink
feat(useSearchParams): Adding useSearchParams function
Browse files Browse the repository at this point in the history
  • Loading branch information
microcipcip committed Feb 21, 2020
1 parent ab180cf commit 260b3f8
Show file tree
Hide file tree
Showing 21 changed files with 434 additions and 68 deletions.
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@

> 🛠️ Vue kit of useful [Vue Composition API](https://vue-composition-api-rfc.netlify.com) functions.</em>
Please note that Vue 3.0 has not been released yet, therefore the installation and setup of [@vue/composition-api](https://github.com/vuejs/composition-api) is required for this library to work.

## Install

```shell script
npm install @vue/composition-api vue-use-kit
npm install vue-use-kit
```

Since Vue 3.0 has not yet been released, you must also install [@vue/composition-api](https://github.com/vuejs/composition-api) library, which will enable the composition API in Vue 2.0.

```shell script
npm install @vue/composition-api
```

## Setup
Expand Down Expand Up @@ -78,6 +82,8 @@ Vue.use(VueCompositionAPI);
[![Demo](https://img.shields.io/badge/advanced_demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usemouse--advanced-demo)
- [`useMouseElement`](./src/components/useMouseElement/stories/useMouseElement.md) &mdash; tracks the mouse position relative to given element.
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usemouseelement--demo)
- [`useSearchParams`](./src/components/useSearchParams/stories/useSearchParams.md) &mdash; tracks browser's location search params.
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usesearchparams--demo)
- Animations
- [`useInterval`](./src/components/useInterval/stories/useInterval.md) &mdash; updates `counter` value repeatedly on a fixed time delay.
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/animations-useinterval--demo)
Expand Down
1 change: 0 additions & 1 deletion src/components/useClickAway/useClickAway.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ const testComponent = () => ({
describe('useClickAway', () => {
it('should call document.addEventListener', async () => {
const addEventListenerSpy = jest.spyOn(document, 'addEventListener')
expect(addEventListenerSpy).not.toHaveBeenCalled()
const removeEventListenerSpy = jest.spyOn(document, 'removeEventListener')
const wrapper = mount(testComponent())
await wrapper.vm.$nextTick()
Expand Down
19 changes: 4 additions & 15 deletions src/components/useIdle/stories/UseIdleDemo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
<td colspan="2">
<button
class="button is-primary"
@click="startTracking"
@click="start"
v-if="!isTracking"
>
Start tracking idle status
</button>
<button class="button is-danger" @click="stopTracking" v-else>
<button class="button is-danger" @click="stop" v-else>
Stop tracking idle status
</button>
</td>
Expand All @@ -37,19 +37,8 @@ import { useIdle } from '@src/vue-use-kit'
export default Vue.extend({
name: 'UseIdleDemo',
setup() {
const { isIdle, start, stop } = useIdle(2500)
const isTracking = ref(true)
const startTracking = () => {
isTracking.value = true
start()
}
const stopTracking = () => {
isTracking.value = false
stop()
}
return { isIdle, isTracking, startTracking, stopTracking }
const { isIdle, isTracking, start, stop } = useIdle(2500)
return { isIdle, isTracking, start, stop }
}
})
</script>
Expand Down
21 changes: 6 additions & 15 deletions src/components/useIdle/stories/useIdle.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ function useIdle(
runOnMount?: boolean
): {
isIdle: Ref<boolean>;
isTracking: Ref<boolean>;
start: () => void;
stop: () => void;
}
Expand All @@ -25,6 +26,7 @@ function useIdle(
### Returns

- `isIdle: Ref<boolean>` it is `true` when the user is idle, `false` otherwise
- `isTracking: Ref<boolean>` whether the function is tracking the user idle state or not
- `start: Function` the function used for start tracking the user's idle state
- `stop: Function` the function used for stop tracking the user's idle state

Expand All @@ -34,8 +36,8 @@ function useIdle(
<template>
<div>
<p>isIdle: {{ isIdle }}</p>
<button @click="startTracking" v-if="!isTracking">Start tracking</button>
<button @click="stopTracking" v-else>Stop tracking</button>
<button @click="start" v-if="!isTracking">Start tracking</button>
<button @click="stop" v-else>Stop tracking</button>
</div>
</template>

Expand All @@ -46,19 +48,8 @@ function useIdle(
export default Vue.extend({
name: 'UseIdleDemo',
setup() {
const { isIdle, start, stop } = useIdle(2500)
const isTracking = ref(true)
const startTracking = () => {
isTracking.value = true
start()
}
const stopTracking = () => {
isTracking.value = false
stop()
}
return { isIdle, isTracking, startTracking, stopTracking }
const { isIdle, isTracking, start, stop } = useIdle(2500)
return { isIdle, isTracking, start, stop }
}
})
</script>
Expand Down
1 change: 0 additions & 1 deletion src/components/useIdle/useIdle.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ describe('useIdle', () => {

it('should call document.addEventListener', async () => {
const addEventListenerSpy = jest.spyOn(document, 'addEventListener')
expect(addEventListenerSpy).not.toHaveBeenCalled()
const removeEventListenerSpy = jest.spyOn(document, 'removeEventListener')
const wrapper = mount(testComponent())
await wrapper.vm.$nextTick()
Expand Down
7 changes: 6 additions & 1 deletion src/components/useIdle/useIdle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export function useIdle(
) {
let timeout: any = null
const isIdle = ref(false)
const isTracking = ref(false)

const handleChange = throttle(50, () => {
isIdle.value = false
Expand All @@ -34,14 +35,17 @@ export function useIdle(
}

const start = () => {
if (isTracking.value) return
events.forEach(evtName => document.addEventListener(evtName, handleChange))
document.addEventListener('visibilitychange', handleVisibility)

// Initialize it since the events above may not run immediately
handleChange()
isTracking.value = true
}

const stop = () => {
if (!isTracking.value) return
events.forEach(evtName =>
document.removeEventListener(evtName, handleChange)
)
Expand All @@ -53,10 +57,11 @@ export function useIdle(
// Restore initial status
timeout = null
isIdle.value = false
isTracking.value = false
}

onMounted(() => runOnMount && start())
onUnmounted(stop)

return { isIdle, start, stop }
return { isIdle, isTracking, start, stop }
}
1 change: 0 additions & 1 deletion src/components/useLocation/useLocation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ describe('useLocation', () => {

it('should call popstate, pushstate and replacestate onMounted', async () => {
const addEventListenerSpy = jest.spyOn(window, 'addEventListener')
expect(addEventListenerSpy).not.toHaveBeenCalled()
const removeEventListenerSpy = jest.spyOn(window, 'removeEventListener')
const wrapper = mount(testComponent())
await wrapper.vm.$nextTick()
Expand Down
31 changes: 4 additions & 27 deletions src/components/useLocation/useLocation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ref, onMounted, onUnmounted, Ref } from '@src/api'
import { patchHistoryMethodsOnce } from '@src/utils'

export interface UseLocationState {
trigger: string
Expand All @@ -15,28 +16,6 @@ export interface UseLocationState {
search: string
}

// The history methods 'pushState' and 'replaceState' by default do not fire an event
// unless it is coming from user interaction with the browser navigation bar,
// so we are adding a patch to make them detectable
let isPatched = false
const patchHistoryMethodsOnce = () => {
if (isPatched) return
const methods = ['pushState', 'replaceState']
methods.forEach(method => {
const original = (history as any)[method]
;(history as any)[method] = function(state: any) {
// eslint-disable-next-line prefer-rest-params
const result = original.apply(this, arguments)
const event = new Event(method.toLowerCase())
;(event as any).state = state
window.dispatchEvent(event)
return result
}
})

isPatched = true
}

export function useLocation(runOnMount = true) {
const buildState = (trigger: string) => {
const { state, length } = history
Expand Down Expand Up @@ -76,23 +55,21 @@ export function useLocation(runOnMount = true) {
const replaceState = () => (locationState.value = buildState('replacestate'))

const start = () => {
patchHistoryMethodsOnce()

if (isTracking.value) return
isTracking.value = true

patchHistoryMethodsOnce()
locationState.value = buildState('start')
window.addEventListener('popstate', popState)
window.addEventListener('pushstate', pushState)
window.addEventListener('replacestate', replaceState)
isTracking.value = true
}

const stop = () => {
if (!isTracking.value) return
isTracking.value = false
window.removeEventListener('popstate', popState)
window.removeEventListener('pushstate', pushState)
window.removeEventListener('replacestate', replaceState)
isTracking.value = false
}

onMounted(() => runOnMount && start())
Expand Down
1 change: 0 additions & 1 deletion src/components/useMouse/useMouse.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ const testComponent = () => ({
describe('useMouse', () => {
it('should call document.addEventListener', async () => {
const addEventListenerSpy = jest.spyOn(document, 'addEventListener')
expect(addEventListenerSpy).not.toHaveBeenCalled()
const removeEventListenerSpy = jest.spyOn(document, 'removeEventListener')
const wrapper = mount(testComponent())
await wrapper.vm.$nextTick()
Expand Down
1 change: 0 additions & 1 deletion src/components/useMouseElement/useMouseElement.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ const testComponent = () => ({
describe('useMouseElement', () => {
it('should call document.addEventListener', async () => {
const addEventListenerSpy = jest.spyOn(document, 'addEventListener')
expect(addEventListenerSpy).not.toHaveBeenCalled()
const removeEventListenerSpy = jest.spyOn(document, 'removeEventListener')
const wrapper = mount(testComponent())
await wrapper.vm.$nextTick()
Expand Down
1 change: 1 addition & 0 deletions src/components/useSearchParams/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useSearchParams'
21 changes: 21 additions & 0 deletions src/components/useSearchParams/stories/Field.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<template>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">{{label}}</label>
</div>
<div class="field-body">
<div class="field">
<slot></slot>
</div>
</div>
</div>
</template>

<script>
export default {
name: 'Field',
props: {
label: String,
}
}
</script>
87 changes: 87 additions & 0 deletions src/components/useSearchParams/stories/UseSearchParamsDemo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<template>
<table class="table is-fullwidth">
<thead>
<tr>
<th>Prop</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>searchParams</td>
<td>
<pre>{{ searchParams }}</pre>
<br />
<field label="Search param">
<input class="input" type="text" v-model="searchFld" />
</field>
<field label="Filter param">
<input class="input" type="text" v-model="filterFld" />
</field>
<button class="button is-info" @click="clearFields">Clear</button>
</td>
</tr>
<tr>
<td colspan="2">
<button class="button is-primary" @click="start" v-if="!isTracking">
Start tracking search param
</button>
<button class="button is-danger" @click="stop" v-else>
Stop tracking search param
</button>
</td>
</tr>
</tbody>
</table>
</template>

<script lang="ts">
import Vue from 'vue'
import { ref, watch } from '@src/api'
import { useSearchParams } from '@src/vue-use-kit'
import Field from './Field.vue'
const updateSearchParams = (
url: string,
idVal: string | null,
searchVal: string
) => history.pushState({}, '', `${url}?id=${idVal}${searchVal}`)
export default Vue.extend({
name: 'UseSearchParamsDemo',
components: { Field },
setup() {
const { searchParams, isTracking, start, stop } = useSearchParams([
'id',
'search',
'filter'
])
const searchFld = ref('')
const filterFld = ref('')
// Update location bar params
watch([searchFld, filterFld], ([searchVal, filterVal]) => {
const url = `${location.origin}${location.pathname}`
const idVal = new URLSearchParams(location.search).get('id')
const fieldParams =
searchVal || filterVal ? `&search=${searchVal}&filter=${filterVal}` : ''
updateSearchParams(url, idVal, fieldParams)
})
const clearFields = () => {
searchFld.value = ''
filterFld.value = ''
}
return {
searchParams,
isTracking,
start,
stop,
searchFld,
filterFld,
clearFields
}
}
})
</script>

0 comments on commit 260b3f8

Please sign in to comment.