Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/deep-paths-rest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@genseki/react': patch
---

Add sticky column pinning style
46 changes: 39 additions & 7 deletions packages/react/src/react/components/primitives/tanstack-table.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
'use client'

import React, { useRef } from 'react'
import React, { type CSSProperties, useRef } from 'react'

import { CircleNotchIcon, WarningCircleIcon, WarningIcon } from '@phosphor-icons/react'
import { CaretDownIcon, CaretUpDownIcon, CaretUpIcon } from '@phosphor-icons/react/dist/ssr'
import {
type Column,
flexRender,
type Row,
type SortDirection,
type Table as TanstackTableCore,
} from '@tanstack/react-table'
import clsx from 'clsx'

import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from './table'

import { cn } from '../../utils/cn'

type RowClickHandler<T> = (row: Row<T>, e: React.MouseEvent<HTMLTableCellElement>) => void

export interface TanstackTableProps<T> {
Expand Down Expand Up @@ -41,6 +43,25 @@ export interface TanstackTableProps<T> {
}
}

const getCommonPinningClassesAndStyle = (column: Column<any>) => {
const isPinned = column.getIsPinned()
const isLastLeftPinnedColumn = isPinned === 'left' && column.getIsLastColumn('left')
const isFirstRightPinnedColumn = isPinned === 'right' && column.getIsFirstColumn('right')

const className = cn(
isPinned ? 'sticky z-[1]' : 'relative',
isLastLeftPinnedColumn && 'shadow-[inset_-4px_0_4px_-4px_gray]',
isFirstRightPinnedColumn && 'shadow-[inset_4px_0_4px_-4px_gray]'
Comment on lines +53 to +54
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we have shadow token in design token?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only want shadow to be one-sided

)

const style: CSSProperties = {
left: isPinned === 'left' ? `${column.getStart('left')}px` : undefined,
right: isPinned === 'right' ? `${column.getAfter('right')}px` : undefined,
Comment on lines +58 to +59
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check if isPinned. left / right in relative is not working.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

huh

}

return { className, style }
}

export const getSortIcon = (isSorted: false | SortDirection) => {
switch (isSorted) {
case false:
Expand Down Expand Up @@ -92,15 +113,19 @@ export function TanstackTable<T>({
const canSort = configuration?.sortBy?.some(
([columnPath]) => columnPath === normalizeColumnId(header.column.id)
)
const { className: pinnedHeaderClassName, style: pinnedHeaderStyle } =
getCommonPinningClassesAndStyle(header.column)
return (
<TableHead
key={normalizeColumnId(header.id)}
className={clsx(
className={cn(
'focus-visible:ring-focus ring-inset',
header.colSpan > 1 && 'border-bluegray-300 border-b',
classNames?.tableHead,
pinnedHeaderClassName,
header.column.columnDef.meta?.thClassName
)}
style={pinnedHeaderStyle}
onClick={
canSort && children ? header.column.getToggleSortingHandler() : undefined
}
Expand All @@ -109,7 +134,7 @@ export function TanstackTable<T>({
tabIndex={canSort ? 0 : -1}
>
<span
className={clsx(
className={cn(
'inline-flex items-center gap-2 w-full',
header.colSpan > 1 && 'justify-center'
)}
Expand All @@ -123,7 +148,7 @@ export function TanstackTable<T>({
</TableRow>
))}
</TableHeader>
<TableBody className={clsx(classNames?.tableBody)}>
<TableBody className={cn(classNames?.tableBody)}>
{isLoading ? (
<TableLoading table={table} />
) : isError ? (
Expand All @@ -135,13 +160,20 @@ export function TanstackTable<T>({
<TableRow
key={row.id}
data-state={row.getIsSelected() && 'selected'}
className={clsx('border-b border-border last:border-b-0', classNames?.tableBodyRow)}
className={cn('border-b border-border last:border-b-0', classNames?.tableBodyRow)}
>
{row.getVisibleCells().map((cell) => {
const { className: pinnedCellClassName, style: pinnedCellStyle } =
getCommonPinningClassesAndStyle(cell.column)
return (
<TableCell
key={cell.id}
className={clsx(classNames?.tableCell)}
className={cn(
classNames?.tableCell,
pinnedCellClassName,
cell.column.columnDef.meta?.tdClassName
)}
style={pinnedCellStyle}
onClick={(e) => onRowClick?.(row, e)}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/types/tanstack.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ import '@tanstack/react-table'
declare module '@tanstack/react-table' {
interface ColumnMeta<TData, TValue> {
thClassName?: string
tdClassName?: string
}
}
Loading