Skip to content

Commit f7d0500

Browse files
Merge pull request #1241 from vtex/feat/table-virtualization
feat(simple-table): add simple table virtualization
2 parents dee4d9f + 534e4ee commit f7d0500

22 files changed

+6190
-15636
lines changed

packages/components/package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@
6363
"@tanstack/react-table": "8.10.7",
6464
"@vtex/shoreline-icons": "^0.2.1",
6565
"@vtex/shoreline-utils": "^0.4.0",
66-
"react-hot-toast": "2.4.1"
66+
"react-hot-toast": "2.4.1",
67+
"react-virtual": "^2.10.4",
68+
"@tanstack/react-virtual": "3.0.0-beta.68"
6769
}
6870
}

packages/components/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export * from './skeleton'
44
export * from './tab'
55
export * from './tooltip'
66
export * from './filter'
7+
export * from './virtual'
78
export * from './scroll-area'
89
export * from './combobox'
910
export * from './select'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import type { Ref } from 'react'
2+
import React from 'react'
3+
import { TableHeader, TableHeaderCell, TableRow } from '../table'
4+
import type { TableHeaderProps } from '../table'
5+
import { flexRender } from '@tanstack/react-table'
6+
import type { HeaderGroup } from '@tanstack/react-table'
7+
import { IconArrowDown, IconArrowUp } from '@vtex/shoreline-icons'
8+
import { forwardRef } from '@vtex/shoreline-utils'
9+
10+
export const SimpleTableHeader = forwardRef(function SimpleTableHeader<T>(
11+
props: SimpleTableRowProps<T>,
12+
ref: Ref<HTMLDivElement>
13+
) {
14+
const { headers, children, ...otherProps } = props
15+
16+
return (
17+
<TableHeader {...otherProps} ref={ref}>
18+
{headers.map((headerGroup) => (
19+
<TableRow key={headerGroup.id}>
20+
{headerGroup.headers.map((header) => (
21+
<TableHeaderCell
22+
key={header.id}
23+
onClick={header.column.getToggleSortingHandler()}
24+
sortable={header.column.getCanSort()}
25+
>
26+
{header.isPlaceholder
27+
? null
28+
: flexRender(
29+
header.column.columnDef.header,
30+
header.getContext()
31+
)}
32+
{header.column.getIsSorted() === 'asc' ? (
33+
<IconArrowUp />
34+
) : header.column.getIsSorted() === 'desc' ? (
35+
<IconArrowDown />
36+
) : null}
37+
</TableHeaderCell>
38+
))}
39+
</TableRow>
40+
))}
41+
</TableHeader>
42+
)
43+
})
44+
45+
export interface SimpleTableRowProps<T> extends TableHeaderProps {
46+
headers: Array<HeaderGroup<T>>
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import type { ReactNode, Ref } from 'react'
2+
import React, { Fragment } from 'react'
3+
import { TableCell, TableRow, type TableRowProps } from '../table'
4+
import { flexRender, type Row } from '@tanstack/react-table'
5+
import type { NavigationTarget } from '../link-box/link-box-utils'
6+
import { Clickable } from '../clickable'
7+
import { LinkBox } from '../link-box'
8+
import { forwardRef } from '@vtex/shoreline-utils'
9+
10+
export const SimpleTableRow = forwardRef(function SimpleTableRow<T>(
11+
props: SimpleTableRowProps<T>,
12+
ref: Ref<HTMLDivElement>
13+
) {
14+
const { row, id, rowClick, renderDetail, children, ...otherProps } = props
15+
16+
return (
17+
<Fragment key={id}>
18+
<TableRow
19+
ref={ref}
20+
selected={row.getIsSelected()}
21+
expanded={row.getIsExpanded()}
22+
{...otherProps}
23+
>
24+
{row.getVisibleCells().map((cell) => {
25+
if (rowClick) {
26+
if (rowClick.type === 'action') {
27+
const { onClick } = rowClick
28+
29+
return (
30+
<Clickable onClick={() => onClick(row)} key={cell.id} asChild>
31+
<TableCell>
32+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
33+
</TableCell>
34+
</Clickable>
35+
)
36+
}
37+
38+
if (rowClick.type === 'link') {
39+
const { getHref, target } = rowClick
40+
41+
return (
42+
<LinkBox
43+
href={getHref(row)}
44+
target={target}
45+
key={cell.id}
46+
asChild
47+
>
48+
<TableCell>
49+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
50+
</TableCell>
51+
</LinkBox>
52+
)
53+
}
54+
}
55+
56+
return (
57+
<TableCell key={cell.id}>
58+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
59+
</TableCell>
60+
)
61+
})}
62+
</TableRow>
63+
{row.getIsExpanded() && (
64+
<TableRow data-sl-detail-row selected={row.getIsSelected()}>
65+
<TableCell
66+
style={{
67+
gridColumn: `1 / span ${row.getVisibleCells().length}`,
68+
}}
69+
>
70+
{renderDetail?.(row)}
71+
</TableCell>
72+
</TableRow>
73+
)}
74+
</Fragment>
75+
)
76+
})
77+
78+
export interface SimpleTableRowProps<T> extends TableRowProps {
79+
row: Row<T>
80+
rowClick?: RowClick<T>
81+
renderDetail?: (row: Row<T>) => ReactNode
82+
}
83+
84+
export type RowClick<T> =
85+
| {
86+
type: 'link'
87+
getHref: (row: Row<T>) => string
88+
target?: NavigationTarget | undefined
89+
}
90+
| {
91+
type: 'action'
92+
onClick: (row: Row<T>) => void
93+
}
94+
| undefined

packages/components/src/simple-table/simple-table.css

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
@layer sl-components {
22
[data-sl-simple-table] {
33
width: 100%;
4+
5+
&[data-virtualize='true'] {
6+
height: 500px;
7+
overflow-y: auto;
8+
}
49
}
510

611
[data-sl-detail-row] {

0 commit comments

Comments
 (0)