Skip to content

Commit e749f31

Browse files
authored
docs(vue): vue filters example (#5644)
* examples: vue filters * run through prettier * add missing components * fix typing issue * add docs link to new example
1 parent 7548860 commit e749f31

14 files changed

+485
-0
lines changed

docs/config.json

+4
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,10 @@
724724
{
725725
"to": "framework/vue/examples/sub-components",
726726
"label": "Sub Components"
727+
},
728+
{
729+
"to": "framework/vue/examples/filters",
730+
"label": "Column Filters"
727731
}
728732
]
729733
}

examples/vue/filters/.gitignore

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
pnpm-debug.log*
8+
lerna-debug.log*
9+
10+
node_modules
11+
dist
12+
dist-ssr
13+
*.local
14+
15+
# Editor directories and files
16+
.vscode/*
17+
!.vscode/extensions.json
18+
.idea
19+
.DS_Store
20+
*.suo
21+
*.ntvs*
22+
*.njsproj
23+
*.sln
24+
*.sw?

examples/vue/filters/README.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Example
2+
3+
To run this example:
4+
5+
- `npm install` or `yarn`
6+
- `npm run dev` or `yarn dev`

examples/vue/filters/env.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/// <reference types="vite/client" />

examples/vue/filters/index.html

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" href="/favicon.ico" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Vite App</title>
8+
<script src="https://cdn.tailwindcss.com"></script>
9+
</head>
10+
<body>
11+
<div id="app"></div>
12+
<script type="module" src="/src/main.ts"></script>
13+
</body>
14+
</html>

examples/vue/filters/package.json

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "tanstack-table-example-vue-filters",
3+
"private": true,
4+
"version": "0.0.0",
5+
"scripts": {
6+
"dev": "vite",
7+
"build": "vite build",
8+
"preview": "vite preview",
9+
"test:types": "vue-tsc"
10+
},
11+
"dependencies": {
12+
"@faker-js/faker": "^8.4.1",
13+
"vue": "^3.4.31",
14+
"@tanstack/vue-table": "^8.19.2"
15+
},
16+
"devDependencies": {
17+
"@types/node": "^20.14.9",
18+
"@vitejs/plugin-vue": "^5.0.5",
19+
"typescript": "5.4.5",
20+
"vite": "^5.3.2",
21+
"vue-tsc": "^2.0.22"
22+
}
23+
}
4.19 KB
Binary file not shown.

examples/vue/filters/src/App.vue

+230
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
<script setup lang="ts">
2+
import {
3+
ColumnFiltersState,
4+
FlexRender,
5+
createColumnHelper,
6+
getCoreRowModel,
7+
getFacetedMinMaxValues,
8+
getFacetedRowModel,
9+
getFacetedUniqueValues,
10+
getFilteredRowModel,
11+
useVueTable,
12+
} from '@tanstack/vue-table'
13+
import { ref } from 'vue'
14+
import DebouncedInput from './DebouncedInput.vue'
15+
import Filter from './Filter.vue'
16+
type Person = {
17+
firstName: string
18+
lastName: string
19+
age: number
20+
visits: number
21+
status: string
22+
progress: number
23+
}
24+
const defaultData: Person[] = [
25+
{
26+
firstName: 'tanner',
27+
lastName: 'linsley',
28+
age: 24,
29+
visits: 100,
30+
status: 'In Relationship',
31+
progress: 50,
32+
},
33+
{
34+
firstName: 'tandy',
35+
lastName: 'miller',
36+
age: 40,
37+
visits: 40,
38+
status: 'Single',
39+
progress: 80,
40+
},
41+
{
42+
firstName: 'joe',
43+
lastName: 'dirte',
44+
age: 45,
45+
visits: 20,
46+
status: 'Complicated',
47+
progress: 10,
48+
},
49+
]
50+
const columnHelper = createColumnHelper<Person>()
51+
const columns = [
52+
columnHelper.group({
53+
header: 'Name',
54+
footer: props => props.column.id,
55+
columns: [
56+
columnHelper.accessor('firstName', {
57+
cell: info => info.getValue(),
58+
footer: props => props.column.id,
59+
}),
60+
columnHelper.accessor(row => row.lastName, {
61+
id: 'lastName',
62+
cell: info => info.getValue(),
63+
header: () => 'Last Name',
64+
footer: props => props.column.id,
65+
}),
66+
],
67+
}),
68+
columnHelper.group({
69+
header: 'Info',
70+
footer: props => props.column.id,
71+
columns: [
72+
columnHelper.accessor('age', {
73+
header: () => 'Age',
74+
footer: props => props.column.id,
75+
}),
76+
columnHelper.group({
77+
header: 'More Info',
78+
columns: [
79+
columnHelper.accessor('visits', {
80+
header: () => 'Visits',
81+
footer: props => props.column.id,
82+
}),
83+
columnHelper.accessor('status', {
84+
header: 'Status',
85+
footer: props => props.column.id,
86+
}),
87+
columnHelper.accessor('progress', {
88+
header: 'Profile Progress',
89+
footer: props => props.column.id,
90+
}),
91+
],
92+
}),
93+
],
94+
}),
95+
]
96+
const data = ref(defaultData)
97+
const rerender = () => {
98+
data.value = defaultData
99+
}
100+
const columnFilters = ref<ColumnFiltersState>([])
101+
const globalFilter = ref('')
102+
const table = useVueTable({
103+
get data() {
104+
return data.value
105+
},
106+
columns,
107+
state: {
108+
get columnFilters() {
109+
return columnFilters.value
110+
},
111+
get globalFilter() {
112+
return globalFilter.value
113+
},
114+
},
115+
onColumnFiltersChange: updaterOrValue => {
116+
columnFilters.value =
117+
typeof updaterOrValue === 'function'
118+
? updaterOrValue(columnFilters.value)
119+
: updaterOrValue
120+
},
121+
onGlobalFilterChange: updaterOrValue => {
122+
globalFilter.value =
123+
typeof updaterOrValue === 'function'
124+
? updaterOrValue(globalFilter.value)
125+
: updaterOrValue
126+
},
127+
getCoreRowModel: getCoreRowModel(),
128+
getFilteredRowModel: getFilteredRowModel(),
129+
getFacetedRowModel: getFacetedRowModel(),
130+
getFacetedUniqueValues: getFacetedUniqueValues(),
131+
getFacetedMinMaxValues: getFacetedMinMaxValues(),
132+
})
133+
</script>
134+
135+
<template>
136+
<div class="p-2">
137+
<div>
138+
<DebouncedInput
139+
:modelValue="globalFilter ?? ''"
140+
@update:modelValue="value => (globalFilter = String(value))"
141+
className="p-2 font-lg shadow border border-block"
142+
placeholder="Search all columns..."
143+
/>
144+
</div>
145+
<div className="h-2" />
146+
<table>
147+
<thead>
148+
<tr
149+
v-for="headerGroup in table.getHeaderGroups()"
150+
:key="headerGroup.id"
151+
>
152+
<th
153+
v-for="header in headerGroup.headers"
154+
:key="header.id"
155+
:colSpan="header.colSpan"
156+
>
157+
<FlexRender
158+
v-if="!header.isPlaceholder"
159+
:render="header.column.columnDef.header"
160+
:props="header.getContext()"
161+
/>
162+
<template
163+
v-if="!header.isPlaceholder && header.column.getCanFilter()"
164+
>
165+
<Filter :column="header.column" :table="table" />
166+
</template>
167+
</th>
168+
</tr>
169+
</thead>
170+
<tbody>
171+
<tr v-for="row in table.getRowModel().rows" :key="row.id">
172+
<td v-for="cell in row.getVisibleCells()" :key="cell.id">
173+
<FlexRender
174+
:render="cell.column.columnDef.cell"
175+
:props="cell.getContext()"
176+
/>
177+
</td>
178+
</tr>
179+
</tbody>
180+
<tfoot>
181+
<tr
182+
v-for="footerGroup in table.getFooterGroups()"
183+
:key="footerGroup.id"
184+
>
185+
<th
186+
v-for="header in footerGroup.headers"
187+
:key="header.id"
188+
:colSpan="header.colSpan"
189+
>
190+
<FlexRender
191+
v-if="!header.isPlaceholder"
192+
:render="header.column.columnDef.footer"
193+
:props="header.getContext()"
194+
/>
195+
</th>
196+
</tr>
197+
</tfoot>
198+
</table>
199+
<div class="h-4" />
200+
<button @click="rerender" class="border p-2">Rerender</button>
201+
</div>
202+
</template>
203+
<style>
204+
html {
205+
font-family: sans-serif;
206+
font-size: 14px;
207+
}
208+
209+
table {
210+
border: 1px solid lightgray;
211+
}
212+
213+
tbody {
214+
border-bottom: 1px solid lightgray;
215+
}
216+
217+
th {
218+
border-bottom: 1px solid lightgray;
219+
border-right: 1px solid lightgray;
220+
padding: 2px 4px;
221+
}
222+
223+
tfoot {
224+
color: gray;
225+
}
226+
227+
tfoot th {
228+
font-weight: normal;
229+
}
230+
</style>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<script lang="ts" setup>
2+
import { computed, onBeforeUnmount, ref } from 'vue'
3+
4+
const props = defineProps({
5+
modelValue: {
6+
type: [String, Number],
7+
required: true,
8+
},
9+
debounce: {
10+
type: Number,
11+
default: 500,
12+
},
13+
})
14+
15+
const emit = defineEmits(['update:modelValue'])
16+
17+
const timeout = ref<ReturnType<typeof setTimeout>>()
18+
19+
const localValue = computed({
20+
get() {
21+
return props.modelValue
22+
},
23+
set(newValue) {
24+
if (timeout.value) {
25+
clearTimeout(timeout.value)
26+
}
27+
timeout.value = setTimeout(
28+
() => emit('update:modelValue', newValue),
29+
props.debounce
30+
)
31+
},
32+
})
33+
onBeforeUnmount(() => clearTimeout(timeout.value))
34+
</script>
35+
36+
<template>
37+
<input v-model="localValue" v-bind="$attrs" />
38+
</template>

0 commit comments

Comments
 (0)