Skip to content

Commit

Permalink
feat: new column pinning, sizing, ordering APIs for easier sticky pin…
Browse files Browse the repository at this point in the history
…ning (#5344)

* improved memo method to accept depArgs
getMemoOptions method to reduce memo boilerplate
new ColumnSizing getAfter API (similar to getStart)
added memoization to column.getStart method
new Ordering column.getIndex API
new Ordering column.getIsFirstColumn API
new Ordering column.getIsLastColumn API
improved table._getPinnedRows memo performance

* update column pinning example and docs

* update lock file

* even better column sizing performance

* smaller code
  • Loading branch information
KevinVandy authored Feb 14, 2024
1 parent 3aa9da9 commit de1a715
Show file tree
Hide file tree
Showing 38 changed files with 873 additions and 360 deletions.
26 changes: 26 additions & 0 deletions docs/api/features/column-ordering.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,29 @@ resetColumnOrder: (defaultState?: boolean) => void
```
Resets the **columnOrder** state to `initialState.columnOrder`, or `true` can be passed to force a default blank state reset to `[]`.
## Column API
### `getIndex`
```tsx
getIndex: (position?: ColumnPinningPosition) => number
```
Returns the index of the column in the order of the visible columns. Optionally pass a `position` parameter to get the index of the column in a sub-section of the table.
### `getIsFirstColumn`
```tsx
getIsFirstColumn: (position?: ColumnPinningPosition) => boolean
```
Returns `true` if the column is the first column in the order of the visible columns. Optionally pass a `position` parameter to check if the column is the first in a sub-section of the table.
### `getIsLastColumn`
```tsx
getIsLastColumn: (position?: ColumnPinningPosition) => boolean
```
Returns `true` if the column is the last column in the order of the visible columns. Optionally pass a `position` parameter to check if the column is the last in a sub-section of the table.
18 changes: 14 additions & 4 deletions docs/api/features/column-sizing.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@ The maximum allowed size for the column
## Column API
## Table Options
### `getSize`
```tsx
Expand All @@ -77,7 +75,19 @@ Returns the current size of the column
getStart: (position?: ColumnPinningPosition) => number
```
Returns the offset measurement along the row-axis (usually the x-axis for standard tables) for the column.
Returns the offset measurement along the row-axis (usually the x-axis for standard tables) for the column, measuring the size of all preceding columns.
Useful for sticky or absolute positioning of columns. (e.g. `left` or `transform`)
### `getAfter`
```tsx
getAfter: (position?: ColumnPinningPosition) => number
```
Returns the offset measurement along the row-axis (usually the x-axis for standard tables) for the column, measuring the size of all succeeding columns.
Useful for sticky or absolute positioning of columns. (e.g. `right` or `transform`)
### `getCanResize`
Expand Down Expand Up @@ -134,7 +144,7 @@ Returns an event handler function that can be used to resize the header. It can
The dragging and release events are automatically handled for you.
## Table API Options
## Table Options
### `enableColumnResizing`
Expand Down
4 changes: 4 additions & 0 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@
"to": "framework/react/examples/column-pinning",
"label": "Column Pinning"
},
{
"to": "framework/react/examples/column-pinning-sticky",
"label": "Sticky Column Pinning"
},
{
"to": "framework/react/examples/column-sizing",
"label": "Column Sizing"
Expand Down
71 changes: 71 additions & 0 deletions docs/guide/column-pinning.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,87 @@ title: Column Pinning
Want to skip to the implementation? Check out these examples:

- [column-pinning](../framework/react/examples/column-pinning)
- [sticky-column-pinning](../framework/react/examples/column-pinning/sticky)
- [row-pinning](../framework/react/examples/row-pinning)

### Other Examples

- [Svelte column-pinning](../framework/svelte/examples/column-pinning)
- [Vue column-pinning](../framework/vue/examples/column-pinning)

## API

[Pinning API](../api/features/pinning)

## Column Pinning Guide

TanStack Table offers state and APIs helpful for implementing column pinning features in your table UI. You can implement column pinning in multiple ways. You can either split pinned columns into their own separate tables, or you can keep all columns in the same table, but use the pinning state to order the columns correctly and use sticky CSS to pin the columns to the left or right.

### How Column Pinning Affects Column Order

There are 3 table features that can reorder columns, which happen in the following order:

1. **Column Pinning** - If pinning, columns are split into left, center (unpinned), and right pinned columns.
2. Manual [Column Ordering](../guide/column-ordering) - A manually specified column order is applied.
3. [Grouping](../guide/grouping) - If grouping is enabled, a grouping state is active, and `tableOptions.columnGroupingMode` is set to `'reorder' | 'remove'`, then the grouped columns are reordered to the start of the column flow.

The only way to change the order of the pinned columns is in the `columnPinning.left` and `columnPinning.right` state itself. `columnOrder` state will only affect the order of the unpinned ("center") columns.

### Column Pinning State

Managing the `columnPinning` state is optional, and usually not necessary unless you are adding persistent state features. TanStack Table will already keep track of the column pinning state for you. Manage the `columnPinning` state just like any other table state if you need to.

```jsx
const [columnPinning, setColumnPinning] = useState<ColumnPinningState>({
left: [],
right: [],
});
//...
const table = useReactTable({
//...
state: {
columnPinning,
//...
}
onColumnPinningChange: setColumnPinning,
//...
});
```

### Pin Columns by Default

A very common use case is to pin some columns by default. You can do this by either initializing the `columnPinning` state with the pinned columnIds, or by using the `initialState` table option

```jsx
const table = useReactTable({
//...
initialState: {
columnPinning: {
left: ['expand-column'],
right: ['actions-column'],
},
//...
}
//...
});
```

### Useful Column Pinning APIs

> Note: Some of these APIs are new in v8.12.0
There are a handful of useful Column API methods to help you implement column pinning features:

- [`column.getCanPin`](../api/features/pinning#getcanpin): Use to determine if a column can be pinned.
- [`column.pin`](../api/features/pinning#pin): Use to pin a column to the left or right. Or use to unpin a column.
- [`column.getIsPinned`](../api/features/pinning#getispinned): Use to determine where a column is pinned.
- [`column.getStart`](../api/features/pinning#getstart): Use to provide the correct `left` CSS value for a pinned column.
- [`column.getAfter`](../api/features/pinning#getafter): Use to provide the correct `right` CSS value for a pinned column.
- [`column.getIsLastColumn`](../api/features/pinning#getislastcolumn): Use to determine if a column is the last column in its pinned group. Useful for adding a box-shadow
- [`column.getIsFirstColumn`](../api/features/pinning#getisfirstcolumn): Use to determine if a column is the first column in its pinned group. Useful for adding a box-shadow

### Split Table Column Pinning

If you are just using sticky CSS to pin columns, you can for the most part, just render the table as you normally would with the `table.getHeaderGroups` and `row.getVisibleCells` methods.

However, if you are splitting up pinned columns into their own separate tables, you can make use of the `table.getLeftHeaderGroups`, `table.getCenterHeaderGroups`, `table.getRightHeaderGroups`, `row.getLeftVisibleCells`, `row.getCenterVisibleCells`, and `row.getRightVisibleCells` methods to only render the columns that are relevant to the current table.
2 changes: 1 addition & 1 deletion docs/guide/virtualization.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Want to skip to the implementation? Check out these examples:

- [virtualized-columns](../framework/react/examples/virtualized-columns)
- [virtualized-rows (dynamic row height)](../framework/react/examples/virtualized-rows)
- [virtualized-rows (fixed row height)](../../../../virtual/v3/docs/examples/react/table)
- [virtualized-rows (fixed row height)](../../../../virtual/v3/docs/framework/react/examples/table)
- [virtualized-infinite-scrolling](../framework/react/examples/virtualized-infinite-scrolling)

## API
Expand Down
1 change: 0 additions & 1 deletion examples/react/column-dnd/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ const defaultColumns: ColumnDef<Person>[] = [
header: 'Age',
footer: props => props.column.id,
},

{
accessorKey: 'visits',
id: 'visits',
Expand Down
5 changes: 5 additions & 0 deletions examples/react/column-pinning-sticky/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
.DS_Store
dist
dist-ssr
*.local
6 changes: 6 additions & 0 deletions examples/react/column-pinning-sticky/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Example

To run this example:

- `npm install` or `yarn`
- `npm run start` or `yarn start`
13 changes: 13 additions & 0 deletions examples/react/column-pinning-sticky/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
<script type="module" src="https://cdn.skypack.dev/twind/shim"></script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
24 changes: 24 additions & 0 deletions examples/react/column-pinning-sticky/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "tanstack-table-example-column-pinning-sticky",
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview --port 3000",
"start": "vite"
},
"dependencies": {
"@faker-js/faker": "^8.3.1",
"@tanstack/react-table": "^8.11.8",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@rollup/plugin-replace": "^5.0.5",
"@types/react": "^18.2.48",
"@types/react-dom": "^18.2.18",
"@vitejs/plugin-react": "^4.2.1",
"vite": "^5.0.11"
}
}
50 changes: 50 additions & 0 deletions examples/react/column-pinning-sticky/src/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
html {
font-family: sans-serif;
font-size: 14px;
}

.table-container {
border: 1px solid lightgray;
overflow-x: scroll;
width: 100%;
max-width: 960px;
position: relative;
}

table {
/* box-shadow and borders will not work with positon: sticky otherwise */
border-collapse: separate !important;
border-spacing: 0;
}

th {
background-color: lightgray;
border-bottom: 1px solid lightgray;
font-weight: bold;
height: 30px;
padding: 2px 4px;
position: relative;
text-align: center;
}

td {
background-color: white;
padding: 2px 4px;
}

.resizer {
background: rgba(0, 0, 0, 0.5);
cursor: col-resize;
height: 100%;
position: absolute;
right: 0;
top: 0;
touch-action: none;
user-select: none;
width: 5px;
}

.resizer.isResizing {
background: blue;
opacity: 1;
}
Loading

0 comments on commit de1a715

Please sign in to comment.