Skip to content

Commit c05d5d6

Browse files
harmyderomanantfupatak-dev
authored
feat(useRefHistory): new option eventFilter, new functions useThrottledRefHistory and useDebouncedRefHistory (vitest-dev#740)
Co-authored-by: Anthony Fu <[email protected]> Co-authored-by: patak <[email protected]>
1 parent fa2a593 commit c05d5d6

File tree

14 files changed

+274
-6
lines changed

14 files changed

+274
-6
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Collection of essential Vue Composition Utilities
77
<a href="https://www.npmjs.com/package/@vueuse/core" target="__blank"><img src="https://img.shields.io/npm/v/@vueuse/core?color=a1b858&label=" alt="NPM version"></a>
88
<a href="https://www.npmjs.com/package/@vueuse/core" target="__blank"><img alt="NPM Downloads" src="https://img.shields.io/npm/dm/@vueuse/core?color=50a36f&label="></a>
99
<a href="https://vueuse.org" target="__blank"><img src="https://img.shields.io/static/v1?label=&message=docs%20%26%20demos&color=1e8a7a" alt="Docs & Demos"></a>
10-
<img alt="Function Count" src="https://img.shields.io/badge/-142%20functions-13708a">
10+
<img alt="Function Count" src="https://img.shields.io/badge/-144%20functions-13708a">
1111
<br>
1212
<a href="https://github.com/vueuse/vueuse" target="__blank"><img alt="GitHub stars" src="https://img.shields.io/github/stars/vueuse/vueuse?style=social"></a>
1313
</p>

indexes.json

+14
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,13 @@
543543
"category": "Browser",
544544
"description": "reactive dark mode with auto data persistence"
545545
},
546+
{
547+
"name": "useDebouncedRefHistory",
548+
"package": "core",
549+
"docs": "https://vueuse.org/core/useDebouncedRefHistory/",
550+
"category": "Utilities",
551+
"description": "same as [useRefHistory](https://vueuse.org/core/useRefHistory/) but with debounse effect"
552+
},
546553
{
547554
"name": "useDeviceMotion",
548555
"package": "core",
@@ -909,6 +916,13 @@
909916
"category": "Component",
910917
"description": "shorthand for binding refs to template elements and components inside `v-for`"
911918
},
919+
{
920+
"name": "useThrottledRefHistory",
921+
"package": "core",
922+
"docs": "https://vueuse.org/core/useThrottledRefHistory/",
923+
"category": "Utilities",
924+
"description": "same as [useRefHistory](https://vueuse.org/core/useRefHistory/) but with throttle effect"
925+
},
912926
{
913927
"name": "useTimeAgo",
914928
"package": "core",

packages/core/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export * from './useBrowserLocation'
1414
export * from './useClipboard'
1515
export * from './useCssVar'
1616
export * from './useDark'
17+
export * from './useDebouncedRefHistory'
1718
export * from './useDeviceMotion'
1819
export * from './useDeviceOrientation'
1920
export * from './useDevicePixelRatio'
@@ -63,6 +64,7 @@ export * from './useSpeechRecognition'
6364
export * from './useStorage'
6465
export * from './useSwipe'
6566
export * from './useTemplateRefsList'
67+
export * from './useThrottledRefHistory'
6668
export * from './useTimeAgo'
6769
export * from './useTimestamp'
6870
export * from './useTitle'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<script lang="ts" setup>
2+
import { ref } from '@vue/reactivity'
3+
import { useCounter } from '@vueuse/shared'
4+
import dayjs from 'dayjs'
5+
import { Ref } from 'vue-demi'
6+
import { useDebouncedRefHistory } from '.'
7+
8+
const format = (ts: number) => dayjs(ts).format()
9+
const delay: Ref<number> = ref(1000)
10+
11+
const { count, inc, dec } = useCounter()
12+
const { history, undo, redo, canUndo, canRedo } = useDebouncedRefHistory(count, { capacity: 10, debounce: delay })
13+
</script>
14+
15+
<template>
16+
<div>Count: {{ count }}</div>
17+
<button @click="inc()">
18+
Increment
19+
</button>
20+
<button @click="dec()">
21+
Decrement
22+
</button>
23+
<span class="ml-2">/</span>
24+
<button :disabled="!canUndo" @click="undo()">
25+
Undo
26+
</button>
27+
<button :disabled="!canRedo" @click="redo()">
28+
Redo
29+
</button>
30+
<span>Delay (in ms):</span>
31+
<input v-model="delay" type="number" />
32+
<br>
33+
<br>
34+
<note>History (limited to 10 records for demo)</note>
35+
<div class="code-block mt-4">
36+
<div v-for="i in history" :key="i.timestamp">
37+
<span class="opacity-50 mr-2 font-mono">{{ format(i.timestamp) }}</span>
38+
<span class="font-mono">{ value: {{ i.snapshot }} }</span>
39+
</div>
40+
</div>
41+
</template>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
category: Utilities
3+
---
4+
5+
# useDebouncedRefHistory
6+
7+
Shorthand for `useRefHistory` with debounced filter.
8+
9+
## Usage
10+
11+
This function takes a snapshot of your counter after 1000ms when the value of it starts to change.
12+
13+
```ts
14+
import { ref } from 'vue'
15+
import { useDebouncedRefHistory } from '@vueuse/core'
16+
17+
const counter = ref(0)
18+
const { history, undo, redo } = useDebouncedRefHistory(counter, { deep: true, debounce: 1000 })
19+
```
20+
21+
## Related Functions
22+
23+
- `useRefHistory`
24+
- `useThrottledRefHistory`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { ref } from 'vue-demi'
2+
import { useSetup } from '../../.test'
3+
import { useDebouncedRefHistory } from '.'
4+
5+
describe('useDebouncedRefHistory - ', () => {
6+
test('take snapshot of data after given time after data was changed', () => {
7+
useSetup(() => {
8+
const ms = 1000
9+
const v = ref(0)
10+
11+
const { history } = useDebouncedRefHistory(v, { debounce: ms })
12+
v.value = 100
13+
expect(history.value.length).toBe(1)
14+
expect(history.value[0].snapshot).toBe(0)
15+
16+
setTimeout(() => {
17+
expect(history.value.length).toBe(2)
18+
expect(history.value[0].snapshot).toBe(100)
19+
}, ms)
20+
})
21+
})
22+
23+
test('undefined debounce don`t break function', () => {
24+
useSetup(() => {
25+
const v = ref(0)
26+
27+
const { history } = useDebouncedRefHistory(v, { deep: false })
28+
v.value = 100
29+
expect(history.value.length).toBe(2)
30+
expect(history.value[0].snapshot).toBe(100)
31+
})
32+
})
33+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { MaybeRef, debounceFilter } from '@vueuse/shared'
2+
import { Ref } from 'vue-demi'
3+
import { UseRefHistoryOptions, UseRefHistoryReturn, useRefHistory } from '../useRefHistory'
4+
5+
/**
6+
* Shorthand for [useRefHistory](https://vueuse.org/useRefHistory) with debounce filter.
7+
*
8+
* @see https://vueuse.org/useDebouncedRefHistory
9+
* @param source
10+
* @param options
11+
*/
12+
export function useDebouncedRefHistory<Raw, Serialized = Raw>(
13+
source: Ref<Raw>,
14+
options: Omit<UseRefHistoryOptions<Raw, Serialized>, 'eventFilter'> & { debounce?: MaybeRef<number> } = {},
15+
): UseRefHistoryReturn<Raw, Serialized> {
16+
const filter = options.debounce ? debounceFilter(options.debounce) : undefined
17+
const history = useRefHistory(source, { ...options, eventFilter: filter })
18+
19+
return {
20+
...history,
21+
}
22+
}

packages/core/useRefHistory/index.ts

+16-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { Fn, pausableFilter, ignorableWatch } from '@vueuse/shared'
1+
import { Fn, pausableFilter, ignorableWatch, ConfigurableEventFilter } from '@vueuse/shared'
22
import { Ref } from 'vue-demi'
33
import { useManualRefHistory, UseRefHistoryRecord, CloneFn } from '../useManualRefHistory'
44

5-
export interface UseRefHistoryOptions<Raw, Serialized = Raw> {
5+
export interface UseRefHistoryOptions<Raw, Serialized = Raw> extends ConfigurableEventFilter {
66
/**
77
* Watch for deep changes, default to false
88
*
@@ -149,13 +149,24 @@ export function useRefHistory<Raw, Serialized = Raw>(
149149
const {
150150
deep = false,
151151
flush = 'pre',
152+
eventFilter,
152153
} = options
153154

154-
const { eventFilter, pause, resume: resumeTracking, isActive: isTracking } = pausableFilter()
155-
const { ignoreUpdates, ignorePrevAsyncUpdates, stop } = ignorableWatch(
155+
const {
156+
eventFilter: composedFilter,
157+
pause,
158+
resume: resumeTracking,
159+
isActive: isTracking,
160+
} = pausableFilter(eventFilter)
161+
162+
const {
163+
ignoreUpdates,
164+
ignorePrevAsyncUpdates,
165+
stop,
166+
} = ignorableWatch(
156167
source,
157168
commit,
158-
{ deep, flush, eventFilter },
169+
{ deep, flush, eventFilter: composedFilter },
159170
)
160171

161172
function setSource(source: Ref<Raw>, value: Raw) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<script setup lang="ts">
2+
import { ref } from '@vue/reactivity'
3+
import { useCounter } from '@vueuse/shared'
4+
import dayjs from 'dayjs'
5+
import { Ref } from 'vue-demi'
6+
import { useThrottledRefHistory } from '.'
7+
8+
const format = (ts: number) => dayjs(ts).format()
9+
const delay: Ref<number> = ref(1000)
10+
11+
const { count, inc, dec } = useCounter()
12+
const { history, undo, redo, canUndo, canRedo } = useThrottledRefHistory(count, { deep: true, throttle: delay })
13+
</script>
14+
15+
<template>
16+
<div>Count: {{ count }}</div>
17+
<button @click="inc()">
18+
Increment
19+
</button>
20+
<button @click="dec()">
21+
Decrement
22+
</button>
23+
<span class="ml-2">/</span>
24+
<button :disabled="!canUndo" @click="undo()">
25+
Undo
26+
</button>
27+
<button :disabled="!canRedo" @click="redo()">
28+
Redo
29+
</button>
30+
<span>Delay (in ms):</span>
31+
<input v-model="delay" type="number" />
32+
<br>
33+
<br>
34+
<note>History (limited to 10 records for demo)</note>
35+
<div class="code-block mt-4">
36+
<div v-for="i in history" :key="i.timestamp">
37+
<span class="opacity-50 mr-2 font-mono">{{ format(i.timestamp) }}</span>
38+
<span class="font-mono">{ value: {{ i.snapshot }} }</span>
39+
</div>
40+
</div>
41+
</template>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
category: Utilities
3+
---
4+
5+
# useThrottledRefHistory
6+
7+
Shorthand for `useRefHistory` with throttled filter.
8+
9+
## Usage
10+
11+
This function takes the first snapshot right after the counter's value was changed and the second with a delay of 1000ms.
12+
13+
```ts
14+
import { ref } from 'vue'
15+
import { useThrottledRefHistory } from '@vueuse/core'
16+
17+
const counter = ref(0)
18+
const { history, undo, redo } = useThrottledRefHistory(counter, { deep: true, throttle: 1000 })
19+
```
20+
21+
## Related Functions
22+
23+
- `useRefHistory`
24+
- `useDebouncedRefHistory`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { ref } from 'vue-demi'
2+
import { useSetup } from '../../.test'
3+
import { useThrottledRefHistory } from '.'
4+
5+
describe('useThrottledRefHistory - sync', () => {
6+
test('take first snapshot right after data was changed and second after given time', () => {
7+
useSetup(() => {
8+
const ms = 1000
9+
const v = ref(0)
10+
11+
const { history } = useThrottledRefHistory(v, { throttle: ms })
12+
v.value = 100
13+
14+
expect(history.value.length).toBe(2)
15+
expect(history.value[0].snapshot).toBe(100)
16+
17+
v.value = 200
18+
v.value = 300
19+
v.value = 400
20+
21+
setTimeout(() => {
22+
expect(history.value.length).toBe(3)
23+
expect(history.value[0].snapshot).toBe(400)
24+
}, ms)
25+
})
26+
})
27+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { MaybeRef, throttleFilter } from '@vueuse/shared'
2+
import { Ref } from 'vue-demi'
3+
import { UseRefHistoryOptions, UseRefHistoryReturn, useRefHistory } from '../useRefHistory'
4+
5+
/**
6+
* Shorthand for [useRefHistory](https://vueuse.org/useRefHistory) with throttled filter.
7+
*
8+
* @see https://vueuse.org/useThrottledRefHistory
9+
* @param source
10+
* @param options
11+
*/
12+
export function useThrottledRefHistory<Raw, Serialized = Raw>(
13+
source: Ref<Raw>,
14+
options: Omit<UseRefHistoryOptions<Raw, Serialized>, 'eventFilter'> & { throttle?: MaybeRef<number> } = {},
15+
): UseRefHistoryReturn<Raw, Serialized> {
16+
const filter = options.throttle ? throttleFilter(options.throttle) : undefined
17+
const history = useRefHistory(source, { ...options, eventFilter: filter })
18+
19+
return {
20+
...history,
21+
}
22+
}

packages/functions.md

+2
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,14 @@
128128
- [`useAsyncState`](https://vueuse.org/core/useAsyncState/) — reactive async state
129129
- [`useCounter`](https://vueuse.org/shared/useCounter/) — basic counter with utility functions
130130
- [`useDebounce`](https://vueuse.org/shared/useDebounce/) — debounce execution of a ref value
131+
- [`useDebouncedRefHistory`](https://vueuse.org/core/useDebouncedRefHistory/) — same as [useRefHistory](https://vueuse.org/core/useRefHistory/) but with debounce effect
131132
- [`useDebounceFn`](https://vueuse.org/shared/useDebounceFn/) — debounce execution of a function
132133
- [`useEventBus`](https://vueuse.org/core/useEventBus/) — a basic event bus
133134
- [`useLastChanged`](https://vueuse.org/shared/useLastChanged/) — records the timestamp of the last change
134135
- [`useManualRefHistory`](https://vueuse.org/core/useManualRefHistory/) — manually track the change history of a ref when the using calls `commit()`
135136
- [`useRefHistory`](https://vueuse.org/core/useRefHistory/) — track the change history of a ref
136137
- [`useThrottle`](https://vueuse.org/shared/useThrottle/) — throttle changing of a ref value
138+
- [`useThrottledRefHistory`](https://vueuse.org/core/useThrottledRefHistory/) — same as [useRefHistory](https://vueuse.org/core/useRefHistory/) but with throttle effect
137139
- [`useThrottleFn`](https://vueuse.org/shared/useThrottleFn/) — throttle execution of a function
138140
- [`useToggle`](https://vueuse.org/shared/useToggle/) — a boolean switcher with utility functions
139141

packages/shared/utils/filters.ts

+5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ export type EventFilter<Args extends any[] = any[], This = any> = (
1515
) => void
1616

1717
export interface ConfigurableEventFilter {
18+
/**
19+
* Filter for if events should to be received.
20+
*
21+
* @see https://vueuse.org/guide/config.html#event-filters
22+
*/
1823
eventFilter?: EventFilter
1924
}
2025

0 commit comments

Comments
 (0)