|
1 | 1 | 'use client' |
2 | 2 |
|
3 | | -import React, { useRef } from 'react' |
| 3 | +import React, { type CSSProperties, useRef } from 'react' |
4 | 4 |
|
5 | 5 | import { CircleNotchIcon, WarningCircleIcon, WarningIcon } from '@phosphor-icons/react' |
6 | 6 | import { CaretDownIcon, CaretUpDownIcon, CaretUpIcon } from '@phosphor-icons/react/dist/ssr' |
7 | 7 | import { |
| 8 | + type Column, |
8 | 9 | flexRender, |
9 | 10 | type Row, |
10 | 11 | type SortDirection, |
@@ -41,6 +42,28 @@ export interface TanstackTableProps<T> { |
41 | 42 | } |
42 | 43 | } |
43 | 44 |
|
| 45 | +const getCommonPinningClassesAndStyle = (column: Column<any>) => { |
| 46 | + const isPinned = column.getIsPinned() |
| 47 | + const isLastLeftPinnedColumn = isPinned === 'left' && column.getIsLastColumn('left') |
| 48 | + const isFirstRightPinnedColumn = isPinned === 'right' && column.getIsFirstColumn('right') |
| 49 | + |
| 50 | + const className = clsx( |
| 51 | + isPinned ? 'sticky z-[1]' : 'relative', |
| 52 | + isLastLeftPinnedColumn && 'shadow-[inset_-4px_0_4px_-4px_gray]', |
| 53 | + isFirstRightPinnedColumn && 'shadow-[inset_4px_0_4px_-4px_gray]' |
| 54 | + ) |
| 55 | + |
| 56 | + const style: CSSProperties = { |
| 57 | + left: isPinned === 'left' ? `${column.getStart('left')}px` : undefined, |
| 58 | + right: isPinned === 'right' ? `${column.getAfter('right')}px` : undefined, |
| 59 | + width: column.getSize(), |
| 60 | + minWidth: column.getSize(), |
| 61 | + maxWidth: column.getSize(), |
| 62 | + } |
| 63 | + |
| 64 | + return { className, style } |
| 65 | +} |
| 66 | + |
44 | 67 | export const getSortIcon = (isSorted: false | SortDirection) => { |
45 | 68 | switch (isSorted) { |
46 | 69 | case false: |
@@ -92,15 +115,19 @@ export function TanstackTable<T>({ |
92 | 115 | const canSort = configuration?.sortBy?.some( |
93 | 116 | ([columnPath]) => columnPath === normalizeColumnId(header.column.id) |
94 | 117 | ) |
| 118 | + const { className: pinnedHeaderClassName, style: pinnedHeaderStyle } = |
| 119 | + getCommonPinningClassesAndStyle(header.column) |
95 | 120 | return ( |
96 | 121 | <TableHead |
97 | 122 | key={normalizeColumnId(header.id)} |
98 | 123 | className={clsx( |
99 | 124 | 'focus-visible:ring-focus ring-inset', |
100 | 125 | header.colSpan > 1 && 'border-bluegray-300 border-b', |
101 | 126 | classNames?.tableHead, |
102 | | - header.column.columnDef.meta?.thClassName |
| 127 | + header.column.columnDef.meta?.thClassName, |
| 128 | + pinnedHeaderClassName |
103 | 129 | )} |
| 130 | + style={pinnedHeaderStyle} |
104 | 131 | onClick={ |
105 | 132 | canSort && children ? header.column.getToggleSortingHandler() : undefined |
106 | 133 | } |
@@ -138,10 +165,13 @@ export function TanstackTable<T>({ |
138 | 165 | className={clsx('border-b border-border last:border-b-0', classNames?.tableBodyRow)} |
139 | 166 | > |
140 | 167 | {row.getVisibleCells().map((cell) => { |
| 168 | + const { className: pinnedCellClassName, style: pinnedCellStyle } = |
| 169 | + getCommonPinningClassesAndStyle(cell.column) |
141 | 170 | return ( |
142 | 171 | <TableCell |
143 | 172 | key={cell.id} |
144 | | - className={clsx(classNames?.tableCell)} |
| 173 | + className={clsx(classNames?.tableCell, pinnedCellClassName)} |
| 174 | + style={pinnedCellStyle} |
145 | 175 | onClick={(e) => onRowClick?.(row, e)} |
146 | 176 | > |
147 | 177 | {flexRender(cell.column.columnDef.cell, cell.getContext())} |
|
0 commit comments