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 ExampleIntegration;
ts
// 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()
}