Skip to content

Commit b7ed057

Browse files
committed
Merge branch 'main' into alpha
2 parents e638890 + 6a4a10d commit b7ed057

File tree

15 files changed

+487
-2
lines changed

15 files changed

+487
-2
lines changed

docs/config.json

Lines changed: 4 additions & 0 deletions
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
}

docs/guide/data.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,12 +239,12 @@ export default function MyComponent() {
239239

240240
### How TanStack Table Transforms Data
241241

242-
Later, in other parts of these docs, you will see how TanStack Table processes the `data` that you pass to the table and generates the row and cell objects that are used to create the table. The `data` the you pass to the table is never mutated by TanStack Table, but the actual values in the rows and cells may be transformed by the accessors in your column definitions, or by other features performed by [row models](../row-models) like grouping or aggregation.
242+
Later, in other parts of these docs, you will see how TanStack Table processes the `data` that you pass to the table and generates the row and cell objects that are used to create the table. The `data` that you pass to the table is never mutated by TanStack Table, but the actual values in the rows and cells may be transformed by the accessors in your column definitions, or by other features performed by [row models](../row-models) like grouping or aggregation.
243243

244244
### How Much Data Can TanStack Table Handle?
245245

246246
Believe it or not, TanStack Table was actually built to scale up to handle potentially hundreds of thousands of rows of data in the client. This is obviously not always possible, depending on the size of each column's data and the number of columns. However, the sorting, filtering, pagination, and grouping features are all built with performance in mind for large datasets.
247247

248248
The default mindset of a developer building a data grid is to implement server-side pagination, sorting, and filtering for large datasets. This is still usually a good idea, but a lot of developers underestimate how much data can actually be handled in the client with modern browsers and the right optimizations. If your table will never have more than a few thousand rows, you can probably take advantage of the client-side features in TanStack Table instead of implementing them yourself on the server. Before committing to letting TanStack Table's client-side features handle your large dataset, you should test it with your actual data to see if it performs well enough for your needs, of course.
249249

250-
This is discussed in more detail in the [Pagination Guide](../pagination#should-you-use-client-side-pagination).
250+
This is discussed in more detail in the [Pagination Guide](../pagination#should-you-use-client-side-pagination).

examples/vue/filters/.gitignore

Lines changed: 24 additions & 0 deletions
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

Lines changed: 6 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/// <reference types="vite/client" />

examples/vue/filters/index.html

Lines changed: 14 additions & 0 deletions
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

Lines changed: 23 additions & 0 deletions
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

Lines changed: 230 additions & 0 deletions
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>
Lines changed: 38 additions & 0 deletions
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)