TanStack Data Grid Demo with RevoGrid
Use this demo to compare how TanStack Table data models can be rendered through RevoGrid in Vue and React applications.
Source code Git
vue
<template>
<VGrid :source="source" :columns="rvColumns" hide-attribution @beforeedit="handleEdit" />
</template>
<script setup lang="ts">
import { VGrid, type ColumnProp } from '@revolist/vue3-datagrid'
import { useVueTable, getCoreRowModel, type ColumnDef, type RowData } from '@tanstack/vue-table'
import { computed, ref } from 'vue'
import { tanstackToRvGridColumns } from '../tanstack/utils'
import { makeData, type Person } from '../makeData'
declare module '@tanstack/vue-table' {
interface TableMeta<_TData extends RowData> {
updateData: (rowIndex: number, columnId: ColumnProp, value: unknown) => void
}
}
// Define columns according tanstack guide
const columns: ColumnDef<Person>[] = [
{
header: 'Name',
columns: [
{
accessorKey: 'firstName',
header: 'First Name',
},
{
accessorKey: 'lastName',
header: 'Last Name',
},
],
},
]
const data = ref<Person[]>(makeData(5))
// define tanstack table
const table = useVueTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
meta: {
updateData: (rowIndex: number, columnId: ColumnProp, value: any) => {
data.value = data.value.map((row, index) => {
if (index === rowIndex) {
return {
...data.value[rowIndex]!,
[columnId]: value,
}
}
return row
})
},
},
})
// get data from tanstack table
const source = computed(() => {
const rows = table.getRowModel().flatRows
return rows
})
const rvColumns = computed(() => {
// transform tanstack to revogrid format
return tanstackToRvGridColumns(table.getAllColumns())
})
function handleEdit(e: CustomEvent<HTMLRevoGridElementEventMap['beforeedit']>) {
e.preventDefault()
table.options.meta?.updateData(e.detail.model.index, e.detail.prop, e.detail.val)
}
</script>tsx
import React, { useState, useMemo, useCallback } from 'react'
import {
RevoGrid,
type BeforeSaveDataDetails,
type RevoGridCustomEvent,
} from '@revolist/react-datagrid'
import { useReactTable, getCoreRowModel, type ColumnDef } from '@tanstack/react-table'
import { tanstackToRvGridColumns } from '../tanstack/utils'
import { makeData, type Person } from '../makeData'
// Define TanStack columns
const columns: ColumnDef<Person>[] = [
{
header: 'Name',
columns: [
{
accessorKey: 'firstName',
header: 'First Name',
},
{
accessorKey: 'lastName',
header: 'Last Name',
},
],
},
]
const ExampleIntegration = () => {
const [data, setData] = useState(makeData(5))
// Set up TanStack table instance
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
meta: {
updateData: (rowIndex, columnId, value) => {
setData(old =>
old.map((row, index) => {
if (index === rowIndex) {
return {
...old[rowIndex],
[columnId]: value,
}
}
return row
}),
)
},
},
})
// Extract data and columns for RevoGrid
const source = useMemo(() => {
const rows = table.getRowModel().flatRows
return rows
}, [table])
const rvColumns = useMemo(() => {
// Convert TanStack column definition to RevoGrid format
return tanstackToRvGridColumns(table.getAllColumns())
}, [table])
const handleEdit = useCallback(
(e: RevoGridCustomEvent<BeforeSaveDataDetails>) => {
e.preventDefault()
const { model, prop, val } = e.detail
table.options.meta?.updateData(model.index, prop, val)
},
[table],
)
return <RevoGrid source={source} columns={rvColumns} onBeforeedit={handleEdit} hideAttribution />
}
export default ExampleIntegrationts
// Helper functions to convert tanstack to revogrid format
import type { ColumnGrouping, ColumnRegular } from '@revolist/vue3-datagrid'
import type { Column } from '@tanstack/vue-table'
export function tanstackToRvGridColumns(tanstackColumns: Column<any>[]) {
return tanstackColumns.map(col => {
// Check if this column has children (group columns)
if (col.columns?.length) {
const rvGroup: ColumnGrouping = {
name: col.columnDef.header,
children: tanstackToRvGridColumns(col.columns),
}
return rvGroup // Recursive call for child columns
}
const rvColumn: ColumnRegular = {
prop: col.id, // Use col.id or accessorKey
name: col.columnDef.header, // Direct mapping for column header
sortable: !!col.getCanSort(), // Check if the column is sortable
filter: col.getCanFilter() ? 'customFilterType' : false, // Handle filters
}
// Handle custom sorting functions via cellCompare
const sortingFn = col.getSortingFn()
if (sortingFn) {
rvColumn.cellCompare = (_, rowA: any, rowB: any) => sortingFn(rowA, rowB, col.id)
rvColumn.sortable = true // Ensure sorting is enabled
}
// Handle filtering logic if provided
const filterFn = col.getFilterFn()
if (filterFn) {
rvColumn.filter = 'customFilterType' // Assume custom type for filtering
}
rvColumn.cellParser = model => {
return model.renderValue(col.id)
}
return rvColumn
})
}ts
import { faker } from '@faker-js/faker'
export type Person = {
avatar: string
firstName: string
lastName: string
name: string
age: number
visits: number
progress: number
status: 'relationship' | 'complicated' | 'single'
subRows?: Person[]
}
const range = (len: number) => {
const arr: number[] = []
for (let i = 0; i < len; i++) {
arr.push(i)
}
return arr
}
const newPerson = (): Person => {
const firstName = faker.person.firstName()
const lastName = faker.person.lastName()
return {
avatar: faker.image.avatar(),
firstName,
lastName,
name: `${firstName} ${lastName}`,
age: faker.number.int(40),
visits: faker.number.int(1000),
progress: faker.number.int(100),
status: faker.helpers.shuffle<Person['status']>(['relationship', 'complicated', 'single'])[0]!,
}
}
export function makeData(...lens: number[]) {
const makeDataLevel = (depth = 0): Person[] => {
const len = lens[depth]!
return range(len).map((): Person => {
return {
...newPerson(),
subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined,
}
})
}
return makeDataLevel()
}