Skip to content

feat: add vue-pacer, debounce helper, docs, example #29

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions docs/framework/vue/adapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
title: TanStack Pacer Vue Adapter
id: adapter
---

If you are using TanStack Pacer in a Vue application, we recommend using the Vue Adapter. The Vue Adapter provides a set of easy-to-use composables on top of the core Pacer utilities. If you find yourself wanting to use the core Pacer classes/functions directly, the Vue Adapter will also re-export everything from the core package.

## Installation

```sh
npm install @tanstack/vue-pacer
```

## Vue Composables

See the [Vue Functions Reference](./reference/index.md) to see the full list of composables available in the Vue Adapter.

## Basic Usage

Import a Vue specific composable from the Vue Adapter.

```vue
<script setup>
import { ref } from 'vue'
import { useDebouncedValue } from '@tanstack/vue-pacer'

const instantValue = ref(0)
const [debouncedValue, debouncer] = useDebouncedValue(instantValue, {
wait: 1000,
})
</script>
```

Or import a core Pacer class/function that is re-exported from the Vue Adapter.

```ts
import { debounce, Debouncer } from '@tanstack/vue-pacer' // no need to install the core package separately
```
110 changes: 110 additions & 0 deletions docs/framework/vue/reference/functions/usedebouncedvalue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
---
title: useDebouncedValue
id: usedebouncedvalue
---

# useDebouncedValue

A Vue composable that creates a debounced value that updates only after a specified delay. This composable automatically tracks changes to the input value and updates the debounced value accordingly.

## Usage

```vue
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { useDebouncedValue } from '@tanstack/vue-pacer'

// Basic debouncing example
const searchQuery = ref('')
const updateCount = ref(0)
const lastUpdateTime = ref(Date.now())

const { value: debouncedQuery } = useDebouncedValue(searchQuery, {
wait: 500
})

// Compute time since last update
const timeSinceUpdate = computed(() => {
return Date.now() - lastUpdateTime.value
})

// Watch for debounced updates
watch(debouncedQuery, () => {
updateCount.value++
lastUpdateTime.value = Date.now()
})

// Advanced example with controls
const controlledValue = ref('')
const { value: debouncedControlled, ...controlledDebouncer } = useDebouncedValue(
controlledValue,
{
wait: 1000,
leading: false, // Don't execute on first call
trailing: true, // Execute after wait period
}
)
</script>

<template>
<div>
<!-- Basic Example -->
<div>
<input v-model="searchQuery" placeholder="Type here..." />
<p><strong>Instant value:</strong> {{ searchQuery }}</p>
<p><strong>Debounced value:</strong> {{ debouncedQuery }}</p>
<p><strong>Update count:</strong> {{ updateCount }}</p>
<p><strong>Time since last update:</strong> {{ timeSinceUpdate }}ms</p>
</div>

<!-- Advanced Example with Controls -->
<div>
<input v-model="controlledValue" placeholder="Type and use controls..." />
<button
@click="controlledDebouncer.cancel()"
:disabled="!controlledDebouncer.isPending.value"
>
Cancel Update
</button>
<button
@click="controlledDebouncer.flush()"
:disabled="!controlledDebouncer.isPending.value"
>
Update Now
</button>
<p><strong>Status:</strong>
{{ controlledDebouncer.isPending.value ? 'Update Pending...' : 'Up to date' }}
</p>
</div>
</div>
</template>
```

## Type Declaration

```ts
function useDebouncedValue<TValue>(
value: MaybeRefOrGetter<TValue>,
options: DebouncerOptions<(value: TValue) => void>
): UseDebouncedValueReturn<TValue>
```

## Parameters

- `value`: The value to debounce. Can be:
- A raw value
- A Vue ref
- A getter function
- `options`: Configuration options for the debouncer
- `wait`: The number of milliseconds to delay
- `maxWait`: Optional maximum time the debouncer will wait before invoking
- `leading`: Optional, if true the debouncer will invoke on the leading edge
- `trailing`: Optional, if true the debouncer will invoke on the trailing edge

## Returns

Returns an object containing:
- `value`: A Vue ref containing the current debounced value
- `flush()`: Immediately invoke any pending debounced invocations
- `cancel()`: Cancel any pending debounced invocations
- `isPending`: A Vue ref indicating if there are pending updates
113 changes: 113 additions & 0 deletions docs/framework/vue/reference/functions/usedebouncer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
---
title: useDebouncer
id: usedebouncer
---

# useDebouncer

A Vue composable that creates a debouncer instance with Vue reactivity integration. This composable provides a debounced value that updates only after a specified delay has passed since the last update.

## Usage

```vue
<script setup lang="ts">
import { computed, ref } from 'vue'
import { useDebouncer } from '@tanstack/vue-pacer'

// Create a debouncer with initial value and options
const { value, setValue, flush, cancel, isPending, executionCount, setOptions, getOptions } = useDebouncer('', {
wait: 1000,
leading: false, // Don't execute on first call
trailing: true, // Execute after wait period
})

// Track time since last update
const lastUpdateTime = ref(Date.now())
const timeSinceUpdate = computed(() => {
return Date.now() - lastUpdateTime.value
})

// Update handlers
function handleInput(event: Event) {
const input = event.target as HTMLInputElement
setValue(input.value)
}

function updateNow() {
if (value.value !== undefined) {
flush()
lastUpdateTime.value = Date.now()
}
}

function cancelUpdate() {
cancel()
}
</script>

<template>
<div>
<div class="input-group">
<label>Debounced input:</label>
<input
:value="value"
@input="handleInput"
placeholder="Type here..."
/>
</div>

<div class="controls">
<button
@click="cancelUpdate"
:disabled="!isPending.value"
>
Cancel Update
</button>
<button
@click="updateNow"
:disabled="!isPending.value"
>
Update Now
</button>
</div>

<div class="values">
<p><strong>Current value:</strong> {{ value }}</p>
<p><strong>Status:</strong>
{{ isPending.value ? 'Update Pending...' : 'Up to date' }}
</p>
<p><strong>Time since update:</strong> {{ timeSinceUpdate }}ms</p>
</div>
</div>
</template>
```

## Type Declaration

```ts
function useDebouncer<TValue>(
initialValue: MaybeRef<TValue>,
options: DebouncerOptions<(value: TValue) => void>
): UseDebouncerReturn<TValue>
```

## Parameters

- `initialValue`: The initial value for the debouncer. Can be a raw value or a Vue ref.
- `options`: Configuration options for the debouncer
- `wait`: The number of milliseconds to delay
- `maxWait`: Optional maximum time the debouncer will wait before invoking
- `leading`: Optional, if true the debouncer will invoke on the leading edge
- `trailing`: Optional, if true the debouncer will invoke on the trailing edge
- `enabled`: Optional, if false the debouncer will not execute (defaults to true)

## Returns

- `value`: A Vue ref containing the current debounced value
- `setValue`: Function to set a new value (will be debounced)
- `flush`: Function to force immediate update of the value
- `cancel`: Function to cancel any pending updates
- `isPending`: A computed ref indicating if there are any pending updates
- `executionCount`: A computed ref containing the number of times the value has been updated
- `setOptions`: Function to update debouncer options
- `getOptions`: Function to get current debouncer options
13 changes: 13 additions & 0 deletions docs/framework/vue/reference/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
title: Vue Functions Reference
id: reference
---

# Vue Functions Reference

The Vue Adapter provides a set of composables that wrap the core Pacer utilities. These composables are designed to work seamlessly with Vue's reactivity system and provide an idiomatic Vue experience.

## Debouncing

- [useDebouncer](./functions/usedebouncer.md) - Creates a debouncer instance with Vue reactivity integration
- [useDebouncedValue](./functions/usedebouncedvalue.md) - Creates a debounced value that updates after a delay
12 changes: 12 additions & 0 deletions examples/vue/debounce/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue Debouncing Example - TanStack Pacer</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
21 changes: 21 additions & 0 deletions examples/vue/debounce/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "@tanstack/pacer-example-vue-debounce",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"@tanstack/vue-pacer": "workspace:*",
"vue": "^3.3.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.5.0",
"typescript": "5.8.3",
"vite": "^6.3.5",
"vue-tsc": "^1.8.8"
}
}
Loading