Skip to content

Commit 6c18ef5

Browse files
authored
feat: support tanstack table features (#2)
* feat: enhance TSTable component with sorting and status styling features * refactor: replace defineColumns with createColumnHelper for improved column definition in TSTable * fix: remove unused import of TableFooter from App.vue to clean up code * refactor: streamline column accessor definitions and improve header/footer slot handling in TSTable * refactor: consolidate table component imports in App.vue for improved clarity
1 parent f1f4104 commit 6c18ef5

File tree

6 files changed

+191
-293
lines changed

6 files changed

+191
-293
lines changed

examples/basic/src/App.vue

Lines changed: 91 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,10 @@
11
<script setup lang="ts">
2-
import { TSTable, defineColumns } from 'tanstack-table-vue'
3-
import {
4-
Table,
5-
TableBody,
6-
TableCell,
7-
TableHead,
8-
TableHeader,
9-
TableRow,
10-
TableFooter
11-
} from '@/components/ui/table'
2+
import { TSTable } from 'tanstack-table-vue'
3+
import { createColumnHelper, FlexRender, getCoreRowModel, getSortedRowModel, type Column } from '@tanstack/vue-table'
4+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from './components/ui/table'
125
13-
type Person = {
6+
7+
interface Person {
148
firstName: string
159
lastName: string
1610
age: number
@@ -46,70 +40,109 @@ const defaultData: Person[] = [
4640
},
4741
]
4842
49-
const columns = defineColumns<Person>([
50-
{
43+
function getSortIcon(column: Column<Person>) {
44+
if (!column.getCanSort?.()) return null
45+
46+
if (column.getIsSorted() === 'desc') {
47+
return ' 🔽'
48+
} else if (column.getIsSorted() === 'asc') {
49+
return ' 🔼'
50+
}
51+
return ' ⏺️'
52+
}
53+
54+
function getStatusClass(status: string) {
55+
switch (status) {
56+
case 'In Relationship':
57+
return 'bg-green-100 text-green-800'
58+
case 'Single':
59+
return 'bg-red-100 text-red-800'
60+
case 'Complicated':
61+
return 'bg-yellow-100 text-yellow-800'
62+
default:
63+
return 'bg-gray-100 text-gray-800'
64+
}
65+
}
66+
67+
const columnHelper = createColumnHelper<Person>()
68+
69+
const columns = [
70+
columnHelper.group({
5171
id: 'name',
5272
header: 'Name',
5373
columns: [
54-
{
55-
id: 'firstName',
56-
header: 'First Name'
57-
},
58-
{
59-
id: 'lastName',
60-
header: 'Last Name'
61-
},
62-
]
63-
},
64-
{
74+
columnHelper.accessor('firstName', {}),
75+
columnHelper.accessor('lastName', {}),
76+
],
77+
}),
78+
columnHelper.group({
6579
id: 'info',
6680
header: 'Info',
6781
columns: [
68-
{
69-
id: 'age',
70-
header: 'Age'
71-
},
72-
{
82+
columnHelper.accessor('age', {}),
83+
columnHelper.group({
7384
id: 'moreInfo',
7485
header: 'More Info',
7586
columns: [
76-
{
77-
id: 'visits',
78-
header: 'Visits'
79-
},
80-
{
81-
id: 'status',
82-
header: 'Status'
83-
},
84-
{
85-
id: 'progress',
86-
header: 'Profile Progress'
87-
}
88-
]
89-
},
90-
]
91-
},
92-
])
87+
columnHelper.accessor('visits', {}),
88+
columnHelper.accessor('status', {}),
89+
columnHelper.accessor('progress', {}),
90+
],
91+
}),
92+
],
93+
}),
94+
]
95+
96+
const tableOptions = {
97+
getSortedRowModel: getSortedRowModel(),
98+
getCoreRowModel: getCoreRowModel(),
99+
}
93100
</script>
94101

95102
<template>
96103
<div class="container mx-auto p-4">
97-
<TSTable :columns="columns" :data="defaultData" :tableComponent="Table" :theadComponent="TableHeader"
98-
:tbodyComponent="TableBody" :trComponent="TableRow" :thComponent="TableHead" :tdComponent="TableCell"
99-
:tfootComponent="TableFooter">
104+
<h1 class="text-2xl font-bold mb-4">TSTable with Slots Example</h1>
105+
<TSTable :columns="columns" :data="defaultData" :tableOptions="tableOptions">
106+
107+
<template #header-firstName="{ column }">
108+
<div class="flex items-center cursor-pointer" @click="column.toggleSorting()">
109+
<span class="font-bold">First Name</span>
110+
<span>{{ getSortIcon(column) }}</span>
111+
</div>
112+
</template>
113+
114+
<template #header-lastName="{ column }">
115+
<div class="flex items-center cursor-pointer" @click="column.toggleSorting()">
116+
<span class="font-bold">Last Name</span>
117+
<span>{{ getSortIcon(column) }}</span>
118+
</div>
119+
</template>
120+
100121
<template #cell-status="{ value }">
101-
<span class="px-2 py-1 rounded" :class="{
102-
'bg-green-100 text-green-700': value === 'In Relationship',
103-
'bg-yellow-100 text-yellow-700': value === 'Complicated',
104-
'bg-red-100 text-red-700': value === 'Single'
105-
}">
122+
<span class="px-2 py-1 rounded text-xs font-medium inline-block" :class="getStatusClass(value)">
106123
{{ value }}
107124
</span>
108125
</template>
109-
<template #cell-progress="{ value }">
110-
<div class="w-full bg-gray-200 rounded-full h-2.5">
111-
<div class="bg-blue-600 h-2.5 rounded-full" :style="{ width: `${value}%` }"></div>
112-
</div>
126+
127+
<template #default="{ table }">
128+
129+
<Table>
130+
<TableHeader>
131+
<TableRow v-for="headerGroup in table.getHeaderGroups()" :key="headerGroup.id">
132+
<TableHead v-for="header in headerGroup.headers" :key="header.id" :colSpan="header.colSpan">
133+
<FlexRender v-if="!header.isPlaceholder" :render="header.column.columnDef.header"
134+
:props="header.getContext()" />
135+
</TableHead>
136+
</TableRow>
137+
</TableHeader>
138+
<TableBody>
139+
<TableRow v-for="row in table.getRowModel().rows" :key="row.id">
140+
<TableCell v-for="cell in row.getVisibleCells()" :key="cell.id">
141+
<FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
142+
</TableCell>
143+
</TableRow>
144+
</TableBody>
145+
</Table>
113146
</template>
114147
</TSTable>
115148
</div>
Lines changed: 34 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,53 @@
11
<script lang="ts">
2-
export interface TSTableProps<TData extends object> {
3-
data: TData[]
4-
columns: ColumnDef<TData>[]
2+
import type { ColumnDef, RowData, Table } from '@tanstack/vue-table'
53
6-
/**
7-
* Custom components for the table
8-
*/
9-
tableComponent?: string | object
10-
theadComponent?: string | object
11-
tbodyComponent?: string | object
12-
tfootComponent?: string | object
13-
trComponent?: string | object
14-
thComponent?: string | object
15-
tdComponent?: string | object
4+
export interface TSTableProps<TData extends RowData & object> {
5+
columns: ColumnDef<TData>[]
6+
data: TData[]
7+
tableOptions?: Record<string, any>
168
}
179
</script>
1810

19-
<script setup lang="ts" generic="TData extends object">
20-
import type { ColumnDef, HeaderSlotProps, CellSlotProps, FooterSlotProps } from '../shared/types'
11+
<script setup lang="ts" generic="TData extends RowData & object">
2112
import { computed, useSlots } from 'vue'
22-
import { FlexRender, type HeaderGroup } from '@tanstack/vue-table'
23-
import {
24-
createColumnHelper,
25-
useVueTable,
26-
getCoreRowModel,
27-
} from '@tanstack/vue-table'
28-
import { processColumns } from '../shared/utils';
13+
import { useVueTable, createColumnHelper, type ColumnDef as TStackColumnDef, getCoreRowModel } from '@tanstack/vue-table'
14+
import { processColumns } from '../shared'
2915
30-
const props = withDefaults(defineProps<TSTableProps<TData>>(), {
31-
tableComponent: 'table',
32-
theadComponent: 'thead',
33-
tbodyComponent: 'tbody',
34-
tfootComponent: 'tfoot',
35-
trComponent: 'tr',
36-
thComponent: 'th',
37-
tdComponent: 'td'
38-
})
39-
40-
defineSlots<{
41-
[key: `cell-${string}`]: (props: CellSlotProps<TData>) => any;
42-
[key: `header-${string}`]: (props: HeaderSlotProps<TData>) => any;
43-
[key: `footer-${string}`]: (props: FooterSlotProps<TData>) => any;
44-
}>()
16+
const props = defineProps<TSTableProps<TData>>()
4517
4618
const slots = useSlots()
47-
4819
const columnHelper = createColumnHelper<TData>()
4920
50-
const tableColumns = computed(() => {
51-
return processColumns(columnHelper, props.columns, slots)
52-
})
53-
54-
const table = useVueTable<TData>({
55-
get data() {
56-
return props.data
57-
},
58-
get columns() {
59-
return tableColumns.value
60-
},
61-
getCoreRowModel: getCoreRowModel()
21+
const processedColumns = computed(() => {
22+
const initialTable = useVueTable<TData>({
23+
columns: props.columns as unknown as TStackColumnDef<TData>[],
24+
data: props.data,
25+
getCoreRowModel: getCoreRowModel(),
26+
...props.tableOptions
27+
})
28+
return processColumns(columnHelper, props.columns, slots, initialTable)
6229
})
6330
64-
const hasFooterGroupRows = (footerGroup: HeaderGroup<TData>) => {
65-
return footerGroup.headers.some((h) =>
66-
!h.isPlaceholder &&
67-
h.column.columnDef.footer &&
68-
typeof h.column.columnDef.footer === 'function' &&
69-
h.column.columnDef.footer(h.getContext())
70-
)
31+
const tableOptions = {
32+
columns: processedColumns.value,
33+
data: props.data,
34+
getCoreRowModel: getCoreRowModel(),
35+
...props.tableOptions
7136
}
7237
73-
const hasFooterRows = computed(() => {
74-
return table.getFooterGroups().some(footerGroup => hasFooterGroupRows(footerGroup))
75-
})
38+
const table = useVueTable<TData>(tableOptions)
39+
40+
defineSlots<{
41+
default: (props: {
42+
table: Table<TData>;
43+
}) => any;
44+
45+
[key: `header-${string}`]: (props: any) => any;
46+
[key: `cell-${string}`]: (props: any) => any;
47+
[key: `footer-${string}`]: (props: any) => any;
48+
}>()
7649
</script>
7750

7851
<template>
79-
<component :is="props.tableComponent">
80-
<component :is="props.theadComponent">
81-
<component :is="props.trComponent" v-for="headerGroup in table.getHeaderGroups()" :key="headerGroup.id">
82-
<component :is="props.thComponent" v-for="header in headerGroup.headers" :key="header.id"
83-
:colSpan="header.colSpan">
84-
<FlexRender v-if="!header.isPlaceholder" :render="header.column.columnDef.header"
85-
:props="header.getContext()" />
86-
</component>
87-
</component>
88-
</component>
89-
<component :is="props.tbodyComponent">
90-
<component :is="props.trComponent" v-for="row in table.getRowModel().rows" :key="row.id">
91-
<component :is="props.tdComponent" v-for="cell in row.getVisibleCells()" :key="cell.id">
92-
<FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
93-
</component>
94-
</component>
95-
</component>
96-
<component :is="props.tfootComponent" v-if="hasFooterRows">
97-
<template v-for="footerGroup in table.getFooterGroups()" :key="footerGroup.id">
98-
<component :is="props.trComponent" v-if="hasFooterGroupRows(footerGroup)">
99-
<component :is="props.thComponent" v-for="header in footerGroup.headers" :key="header.id"
100-
:colSpan="header.colSpan">
101-
<FlexRender v-if="!header.isPlaceholder" :render="header.column.columnDef.footer"
102-
:props="header.getContext()" />
103-
</component>
104-
</component>
105-
</template>
106-
</component>
107-
</component>
52+
<slot :table="table" />
10853
</template>

packages/core/src/shared/defineColumns.ts

Lines changed: 0 additions & 37 deletions
This file was deleted.

packages/core/src/shared/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
export * from './defineColumns'
2-
export * from './types'
31
export * from './utils'

0 commit comments

Comments
 (0)