Skip to content

Commit e885b0e

Browse files
committed
fix(Table): properly position pinned columns based on size
Resolves #4721, resolves #3927
1 parent d4ce4e8 commit e885b0e

File tree

4 files changed

+46
-18
lines changed

4 files changed

+46
-18
lines changed

docs/app/components/content/examples/table/TableColumnPinningExample.vue

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,31 @@ type Payment = {
1515
}
1616
1717
const data = ref<Payment[]>([{
18-
id: '46000000000000000000000000000000000000000',
18+
id: '4600000000000000000000000000000000000000',
1919
date: '2024-03-11T15:30:00',
2020
status: 'paid',
2121
2222
amount: 594000
2323
}, {
24-
id: '45990000000000000000000000000000000000000',
24+
id: '4599000000000000000000000000000000000000',
2525
date: '2024-03-11T10:10:00',
2626
status: 'failed',
2727
2828
amount: 276000
2929
}, {
30-
id: '45980000000000000000000000000000000000000',
30+
id: '4598000000000000000000000000000000000000',
3131
date: '2024-03-11T08:50:00',
3232
status: 'refunded',
3333
3434
amount: 315000
3535
}, {
36-
id: '45970000000000000000000000000000000000000',
36+
id: '4597000000000000000000000000000000000000',
3737
date: '2024-03-10T19:45:00',
3838
status: 'paid',
3939
4040
amount: 5290000
4141
}, {
42-
id: '45960000000000000000000000000000000000000',
42+
id: '4596000000000000000000000000000000000000',
4343
date: '2024-03-10T15:55:00',
4444
status: 'paid',
4545
@@ -49,10 +49,12 @@ const data = ref<Payment[]>([{
4949
const columns: TableColumn<Payment>[] = [{
5050
accessorKey: 'id',
5151
header: ({ column }) => getHeader(column, 'ID', 'left'),
52-
cell: ({ row }) => `#${row.getValue('id')}`
52+
cell: ({ row }) => `#${row.getValue('id')}`,
53+
size: 381
5354
}, {
5455
accessorKey: 'date',
55-
header: ({ column }) => getHeader(column, 'Date', 'left')
56+
header: ({ column }) => getHeader(column, 'Date', 'left'),
57+
size: 172
5658
}, {
5759
accessorKey: 'status',
5860
header: ({ column }) => getHeader(column, 'Status', 'left'),
@@ -64,10 +66,12 @@ const columns: TableColumn<Payment>[] = [{
6466
})[row.getValue('status') as string]
6567
6668
return h(UBadge, { class: 'capitalize', variant: 'subtle', color }, () => row.getValue('status'))
67-
}
69+
},
70+
size: 103
6871
}, {
6972
accessorKey: 'email',
70-
header: ({ column }) => getHeader(column, 'Email', 'left')
73+
header: ({ column }) => getHeader(column, 'Email', 'left'),
74+
size: 232
7175
}, {
7276
accessorKey: 'amount',
7377
header: ({ column }) => h('div', { class: 'text-right' }, getHeader(column, 'Amount', 'right')),
@@ -80,7 +84,8 @@ const columns: TableColumn<Payment>[] = [{
8084
}).format(amount)
8185
8286
return h('div', { class: 'text-right font-medium' }, formatted)
83-
}
87+
},
88+
size: 130
8489
}]
8590
8691
function getHeader(column: Column<Payment>, label: string, position: 'left' | 'right') {
@@ -99,7 +104,7 @@ function getHeader(column: Column<Payment>, label: string, position: 'left' | 'r
99104
}
100105
101106
const columnPinning = ref({
102-
left: [],
107+
left: ['id'],
103108
right: ['amount']
104109
})
105110
</script>

docs/content/docs/2.components/table.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,13 +450,14 @@ In this example, we use a function to define the column header but you can also
450450
You can update a column `header` to render a [Button](/docs/components/button) component inside the `header` to toggle the pinning state using the TanStack Table [Pinning APIs](https://tanstack.com/table/latest/docs/api/features/row-pinning).
451451

452452
::note
453-
A pinned column will become sticky on the left or right side of the table.
453+
A pinned column will become sticky on the left or right side of the table. When using column pinning, you should define explicit `size` values for your columns to ensure proper column width handling, especially with multiple pinned columns.
454454
::
455455

456456
::component-example
457457
---
458458
prettier: true
459459
collapse: true
460+
overflowHidden: true
460461
name: 'table-column-pinning-example'
461462
highlights:
462463
- 100

src/runtime/components/Table.vue

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<script lang="ts">
33
import type { Ref, WatchOptions, ComponentPublicInstance } from 'vue'
44
import type { AppConfig } from '@nuxt/schema'
5-
import type { Cell, Header, RowData, TableMeta } from '@tanstack/table-core'
5+
import type { Cell, Column, Header, RowData, TableMeta } from '@tanstack/table-core'
66
import type {
77
CellContext,
88
ColumnDef,
@@ -486,6 +486,19 @@ function resolveValue<T, A = undefined>(prop: T | ((arg: A) => T), arg?: A): T |
486486
return prop
487487
}
488488
489+
function getColumnStyles(column: Column<T>): Record<string, string> {
490+
const styles: Record<string, string> = {}
491+
492+
const pinned = column.getIsPinned()
493+
if (pinned === 'left') {
494+
styles.left = `${column.getStart('left')}px`
495+
} else if (pinned === 'right') {
496+
styles.right = `${column.getAfter('right')}px`
497+
}
498+
499+
return styles
500+
}
501+
489502
watch(() => props.data, () => {
490503
data.value = props.data ? [...props.data] : []
491504
}, props.watchOptions)
@@ -534,7 +547,10 @@ defineExpose({
534547
],
535548
pinned: !!cell.column.getIsPinned()
536549
})"
537-
:style="resolveValue(cell.column.columnDef.meta?.style?.td, cell)"
550+
:style="[
551+
getColumnStyles(cell.column),
552+
resolveValue(cell.column.columnDef.meta?.style?.td, cell)
553+
]"
538554
>
539555
<slot :name="`${cell.column.id}-cell`" v-bind="cell.getContext()">
540556
<FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
@@ -574,7 +590,10 @@ defineExpose({
574590
],
575591
pinned: !!header.column.getIsPinned()
576592
})"
577-
:style="resolveValue(header.column.columnDef.meta?.style?.th, header)"
593+
:style="[
594+
getColumnStyles(header.column),
595+
resolveValue(header.column.columnDef.meta?.style?.th, header)
596+
]"
578597
>
579598
<slot :name="`${header.id}-header`" v-bind="header.getContext()">
580599
<FlexRender v-if="!header.isPlaceholder" :render="header.column.columnDef.header" :props="header.getContext()" />
@@ -648,7 +667,10 @@ defineExpose({
648667
],
649668
pinned: !!header.column.getIsPinned()
650669
})"
651-
:style="resolveValue(header.column.columnDef.meta?.style?.th, header)"
670+
:style="[
671+
getColumnStyles(header.column),
672+
resolveValue(header.column.columnDef.meta?.style?.th, header)
673+
]"
652674
>
653675
<slot :name="`${header.id}-footer`" v-bind="header.getContext()">
654676
<FlexRender v-if="!header.isPlaceholder" :render="header.column.columnDef.footer" :props="header.getContext()" />

src/theme/table.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ export default (options: Required<ModuleOptions>) => ({
2424
},
2525
pinned: {
2626
true: {
27-
th: 'sticky bg-default/75 data-[pinned=left]:left-0 data-[pinned=right]:right-0',
28-
td: 'sticky bg-default/75 data-[pinned=left]:left-0 data-[pinned=right]:right-0'
27+
th: 'sticky bg-default/75 z-1',
28+
td: 'sticky bg-default/75 z-1'
2929
}
3030
},
3131
sticky: {

0 commit comments

Comments
 (0)