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
1 change: 1 addition & 0 deletions examples/alpine/aggregation/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ Alpine.data('table', () => {
return { rowSource: local.rowSource }
},
initialState: { pagination: { pageIndex: 0, pageSize: 10 } },
// manualAggregation: true, // supply aggregate values yourself instead of calculating them locally
debugTable: true,
debugColumns: true,
})
Expand Down
2 changes: 1 addition & 1 deletion examples/alpine/basic-create-table/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<title>TanStack Alpine Table - Basic Create Table</title>
</head>
<body>
<!-- 7. Render your table markup from the table instance APIs -->
<!-- 6. Render your table markup from the table instance APIs -->
<div id="root" class="demo-root" x-data="table">
<div class="button-row">
<button @click="refreshData()">Regenerate Data</button>
Expand Down
52 changes: 26 additions & 26 deletions examples/alpine/basic-create-table/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,55 @@
import Alpine from 'alpinejs'
import { FlexRender, createTable, tableFeatures } from '@tanstack/alpine-table'
import {
FlexRender,
createColumnHelper,
createTable,
tableFeatures,
} from '@tanstack/alpine-table'
import { makeData } from './makeData'
import './index.css'
import type { ColumnDef } from '@tanstack/alpine-table'
import type { Person } from './makeData'

// This example uses the standalone `createTable` function to create a table without the `createTableHook` util.

// 1. New in V9! Tell the table which features and row models we want to use. In this case, this will be a basic table with no additional features
const features = tableFeatures({}) // util method to create sharable TFeatures object/type

// 4. Define the columns for your table. This uses the new `ColumnDef` type to define columns.
// Alternatively, check out the createTableHook/createAppColumnHelper util for an even more type-safe way to define columns.
const columns: Array<ColumnDef<typeof features, Person>> = [
{
accessorKey: 'firstName', // accessorKey method (most common for simple use-cases)
// 2. Create a column helper with the table features and row type
const columnHelper = createColumnHelper<typeof features, Person>()

// 3. Define the columns for your table with the column helper
const columns = columnHelper.columns([
columnHelper.accessor('firstName', {
header: 'First Name',
cell: (info) => info.getValue(),
},
{
accessorFn: (row) => row.lastName, // accessorFn used (alternative) along with a custom id
}),
columnHelper.accessor((row) => row.lastName, {
id: 'lastName',
header: () => 'Last Name',
cell: (info) => info.getValue(),
},
{
accessorFn: (row) => Number(row.age), // accessorFn used to transform the data
}),
columnHelper.accessor((row) => Number(row.age), {
id: 'age',
header: () => 'Age',
cell: (info) => info.renderValue(),
},
{
accessorKey: 'visits',
}),
columnHelper.accessor('visits', {
header: () => 'Visits',
},
{
accessorKey: 'status',
}),
columnHelper.accessor('status', {
header: 'Status',
},
{
accessorKey: 'progress',
}),
columnHelper.accessor('progress', {
header: 'Profile Progress',
},
]
}),
])

// 5. Register the Alpine component. Store data in Alpine-reactive state so the
// 4. Register the Alpine component. Store data in Alpine-reactive state so the
// buttons can swap it out and the table re-renders.
Alpine.data('table', () => {
const local = Alpine.reactive({ data: makeData(20) })

// 6. Create the table instance with required features, columns, and data
// 5. Create the table instance with required features, columns, and data
const table = createTable({
debugTable: true, // optionally, enable console logging debug messages
features, // new required option in V9. Tell the table which features you are importing and using (better tree-shaking)
Expand Down
1 change: 1 addition & 0 deletions examples/alpine/column-sizing/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Alpine.data('table', () => {
},
columnResizeMode: 'onChange',
columnResizeDirection: 'ltr',
// defaultColumn: { size: 150, minSize: 50, maxSize: 500 }, // set sizing defaults for every column
// initialState: { columnSizing: { firstName: 200 } }, // set column sizes on first render
// atoms: { columnSizing: columnSizingAtom }, // preferred: own sizing state with an external atom
// state: { columnSizing }, // classic controlled state; pair with onColumnSizingChange
Expand Down
9 changes: 8 additions & 1 deletion examples/alpine/expanding/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,11 @@ Alpine.data('table', () => {
get data() {
return local.data
},
getSubRows: (row) => row.subRows,
getSubRows: (row) => row.subRows, // tell the table where nested rows live
// enableRowSelection: row => row.original.age > 18, // enable selection conditionally; default true
// enableMultiRowSelection: false, // allow only one selected row at a time; default true
// enableSubRowSelection: false, // disable sub-row selection; default true
// enableRowRangeSelection: false, // disable shift-click range selection; default true
// initialState: { expanded: { '0': true } }, // expand rows on first render
// atoms: { expanded: expandedAtom }, // preferred: own expanded state with an external atom
// state: { expanded }, // classic controlled state; pair with onExpandedChange
Expand All @@ -110,8 +114,11 @@ Alpine.data('table', () => {
// paginateExpandedRows: false, // keep expanded children on their parent page; default true
// autoResetExpanded: false, // keep expanded rows after page-altering changes; default true
// autoResetAll: false, // turn off every feature's automatic reset, including expansion
// enableFilters: false, // disable all column and global filtering; default true
// enableColumnFilters: false, // disable per-column filters; default true
// filterFromLeafRows: true, // with filtering, keep parents whose descendants match
// maxLeafRowFilterDepth: 0, // with filtering, only filter root rows
// manualFiltering: true, // pass data that is already filtered, for example from a server
debugTable: true,
})

Expand Down
1 change: 1 addition & 0 deletions examples/alpine/row-pinning/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ Alpine.data('table', () => {
},
initialState: {
pagination: { pageSize: 20, pageIndex: 0 },
// rowPinning: { top: ['0'], bottom: ['1'] }, // pin rows on first render
},
getSubRows: (row) => row.subRows,
keepPinnedRows: true,
Expand Down
1 change: 1 addition & 0 deletions examples/alpine/row-selection/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Alpine.data('table', () => {
return local.data
},
enableRowSelection: true,
// enableRowSelection: row => row.original.age > 18, // or enable selection conditionally
// initialState: { rowSelection: { '0': true } }, // select rows on first render
// atoms: { rowSelection: rowSelectionAtom }, // preferred: own selection state with an external atom
// state: { rowSelection }, // classic controlled state; pair with onRowSelectionChange
Expand Down
1 change: 1 addition & 0 deletions examples/angular/aggregation/src/app/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export class App {
data: this.data(),
meta: { rowSource: this.rowSource() },
initialState: { pagination: { pageIndex: 0, pageSize: 10 } },
// manualAggregation: true, // supply aggregate values yourself instead of calculating them locally
debugTable: true,
debugColumns: true,
}))
Expand Down
54 changes: 27 additions & 27 deletions examples/angular/basic-inject-table/src/app/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ import {
inject,
signal,
} from '@angular/core'
import { FlexRender, injectTable, tableFeatures } from '@tanstack/angular-table'
import {
FlexRender,
createColumnHelper,
injectTable,
tableFeatures,
} from '@tanstack/angular-table'
import { injectTanStackTableDevtools } from '@tanstack/angular-table-devtools'
import type { ColumnDef } from '@tanstack/angular-table'

// This example uses the Angular standalone `injectTable` helper to create a table without the `createTableHook` util.

Expand Down Expand Up @@ -61,39 +65,35 @@ const defaultData: Array<Person> = [
// In this case, this will be a basic table with no additional features
const features = tableFeatures({})

// 4. Define the columns for your table. This uses the new `ColumnDef` type to define columns.
// Alternatively, check out the createTableHook/createColumnHelper util for an even more type-safe way to define columns.
const columns: Array<ColumnDef<typeof features, Person>> = [
{
accessorKey: 'firstName',
// 4. Create a column helper with the table features and row type
const columnHelper = createColumnHelper<typeof features, Person>()

// 5. Define the columns for your table with the column helper
const columns = columnHelper.columns([
columnHelper.accessor('firstName', {
header: 'First Name',
cell: (info) => info.getValue(),
},
{
accessorFn: (row) => row.lastName,
}),
columnHelper.accessor((row) => row.lastName, {
id: 'lastName',
header: () => 'Last Name',
cell: (info) => info.getValue<string>(),
},
{
accessorFn: (row) => Number(row.age),
cell: (info) => info.getValue(),
}),
columnHelper.accessor((row) => Number(row.age), {
id: 'age',
header: () => 'Age',
cell: (info) => info.renderValue(),
},
{
accessorKey: 'visits',
}),
columnHelper.accessor('visits', {
header: () => 'Visits',
},
{
accessorKey: 'status',
}),
columnHelper.accessor('status', {
header: 'Status',
},
{
accessorKey: 'progress',
}),
columnHelper.accessor('progress', {
header: 'Profile Progress',
},
]
}),
])

@Component({
selector: 'app-root',
Expand All @@ -104,10 +104,10 @@ const columns: Array<ColumnDef<typeof features, Person>> = [
export class App {
private readonly injector = inject(Injector)

// 5. Store data with a stable reference
// 6. Store data with a stable reference
readonly data = signal<Array<Person>>([...defaultData])

// 6. Create the table instance with required features, columns, and data
// 7. Create the table instance with required features, columns, and data
readonly table = injectTable(() => ({
key: 'basic-inject-table', // needed for devtools
debugTable: true,
Expand Down
1 change: 1 addition & 0 deletions examples/angular/column-sizing/src/app/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export class App {
features,
columns,
data: this.data(),
// defaultColumn: { size: 150, minSize: 50, maxSize: 500 }, // set sizing defaults for every column
// initialState: { columnSizing: { firstName: 200 } }, // set column sizes on first render
// atoms: { columnSizing: columnSizingAtom }, // preferred: own sizing state with an external atom
// state: { columnSizing }, // classic controlled state; pair with onColumnSizingChange
Expand Down
26 changes: 26 additions & 0 deletions examples/angular/expanding/src/app/app.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,32 @@
>
<div [innerHTML]="header"></div>
</ng-container>

@if (header.column.getCanFilter()) {
<div>
@if (isNumberColumn(header.column)) {
<input
type="number"
placeholder="Min"
[value]="getNumberFilterValue(header.column, 0)"
(input)="setNumberFilter(header.column, $event, 0)"
/>
<input
type="number"
placeholder="Max"
[value]="getNumberFilterValue(header.column, 1)"
(input)="setNumberFilter(header.column, $event, 1)"
/>
} @else {
<input
type="text"
placeholder="Search..."
[value]="header.column.getFilterValue() ?? ''"
(input)="setTextFilter(header.column, $event)"
/>
}
</div>
}
}
</th>
}
Expand Down
53 changes: 51 additions & 2 deletions examples/angular/expanding/src/app/app.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { ChangeDetectionStrategy, Component, signal } from '@angular/core'
import {
FlexRender,
columnFilteringFeature,
createExpandedRowModel,
createFilteredRowModel,
createPaginatedRowModel,
filterFn_inNumberRange,
filterFn_includesString,
flexRenderComponent,
injectTable,
rowExpandingFeature,
Expand All @@ -17,14 +21,20 @@ import {
ExpandableHeaderCell,
} from './expandable-cell/expandable-cell'
import type { Person } from './makeData'
import type { ColumnDef, ExpandedState } from '@tanstack/angular-table'
import type { Column, ColumnDef, ExpandedState } from '@tanstack/angular-table'

export const features = tableFeatures({
columnFilteringFeature,
rowExpandingFeature: rowExpandingFeature,
rowPaginationFeature: rowPaginationFeature,
rowSelectionFeature: rowSelectionFeature,
filteredRowModel: createFilteredRowModel(),
paginatedRowModel: createPaginatedRowModel(),
expandedRowModel: createExpandedRowModel(),
filterFns: {
includesString: filterFn_includesString,
inNumberRange: filterFn_inNumberRange,
},
})

const defaultColumns: Array<ColumnDef<typeof features, Person>> = [
Expand Down Expand Up @@ -93,7 +103,11 @@ export class App {
typeof updater === 'function'
? this.expanded.update(updater)
: this.expanded.set(updater),
getSubRows: (row) => row.subRows,
getSubRows: (row) => row.subRows, // tell the table where nested rows live
// enableRowSelection: row => row.original.age > 18, // enable selection conditionally; default true
// enableMultiRowSelection: false, // allow only one selected row at a time; default true
// enableSubRowSelection: false, // disable sub-row selection; default true
// enableRowRangeSelection: false, // disable shift-click range selection; default true
// initialState: { expanded: { '0': true } }, // expand rows on first render
// atoms: { expanded: expandedAtom }, // preferred: own expanded state with an external atom
// enableExpanding: false, // disable expanding for every row; default true
Expand All @@ -103,8 +117,11 @@ export class App {
// paginateExpandedRows: false, // keep expanded children on their parent page; default true
// autoResetExpanded: false, // keep expanded rows after page-altering changes; default true
// autoResetAll: false, // turn off every feature's automatic reset, including expansion
// enableFilters: false, // disable all column and global filtering; default true
// enableColumnFilters: false, // disable per-column filters; default true
// filterFromLeafRows: true, // with filtering, keep parents whose descendants match
// maxLeafRowFilterDepth: 0, // with filtering, only filter root rows
// manualFiltering: true, // pass data that is already filtered, for example from a server
debugTable: true,
}))

Expand All @@ -122,6 +139,38 @@ export class App {
this.table.setPageSize(Number(event.target.value))
}

isNumberColumn(column: Column<typeof features, Person>): boolean {
const firstValue = this.table
.getPreFilteredRowModel()
.flatRows[0]?.getValue(column.id)
return typeof firstValue === 'number'
}

setTextFilter(column: Column<typeof features, Person>, event: Event): void {
column.setFilterValue((event.target as HTMLInputElement).value)
}

getNumberFilterValue(
column: Column<typeof features, Person>,
index: 0 | 1,
): number | '' {
const value = column.getFilterValue() as [number?, number?] | undefined
return value?.[index] ?? ''
}

setNumberFilter(
column: Column<typeof features, Person>,
event: Event,
index: 0 | 1,
): void {
const value = (event.target as HTMLInputElement).value
column.setFilterValue((old: [number?, number?] | undefined) => {
const next: [number?, number?] = [...(old ?? [])]
next[index] = value === '' ? undefined : Number(value)
return next
})
}

refreshData = () => this.data.set(makeData(100, 5, 3))
stressTest = () => this.data.set(makeData(10_000, 5, 3))
}
5 changes: 4 additions & 1 deletion examples/angular/row-pinning/src/app/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ export class App {
features,
columns,
data: this.data(),
initialState: { pagination: { pageSize: 20, pageIndex: 0 } },
initialState: {
pagination: { pageSize: 20, pageIndex: 0 },
// rowPinning: { top: ['0'], bottom: ['1'] }, // pin rows on first render
},
Comment on lines +76 to +79

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file info =="
fd -a 'app\.ts$' examples/angular/row-pinning/src/app 2>/dev/null || true

echo "== git status/stat =="
git diff --stat HEAD~1..HEAD 2>/dev/null || git diff --stat || true

echo "== relevant lines =="
for f in $(fd 'app\.ts$' examples/angular/row-pinning/src/app 2>/dev/null); do
  echo "--- $f"
  wc -l "$f"
  sed -n '1,140p' "$f" | nl -ba | sed -n '50,110p'
done

echo "== rowPinning occurrences =="
rg -n "rowPinning|row-pinning|pin rows|initialState|state:" examples/angular/row-pinning/src/app examples/angular/row-pinning 2>/dev/null || true

Repository: TanStack/table

Length of output: 5005


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file info =="
fd -a 'app\.ts$' examples/angular/row-pinning/src/app 2>/dev/null || true

echo "== git status/stat =="
git diff --stat HEAD~1.. is-available 2>/dev/null || git diff --stat || true

echo "== relevant lines =="
for f in $(fd 'app\.ts$' examples/angular/row-pinning/src/app 2>/dev/null); do
  echo "--- $f"
  wc -l "$f"
  sed -n '1,140p' "$f" | nl -ba | sed -n '50,110p'
done

echo "== rowPinning occurrences =="
rg -n "rowPinning|row-pinning|pin rows|initialState|state:" examples/angular/row-pinning/src/app examples/angular/row-pinning 2>/dev/null || true

Repository: TanStack/table

Length of output: 411


🏁 Script executed:

#!/bin/bash
set -euo pipefail

f="examples/angular/row-pinning/src/app/app.ts"

echo "== app.ts lines 50-110 =="
awk 'NR>=50 && NR<=110 { printf "%4d  %s\n", NR, $0 }' "$f"

echo "== all rowPinning/initialState state occurrences =="
rg -n "rowPinning|row-pinning|pin rows|initialState|state:" "$f" . 2>/dev/null || true

echo "== package/source references =="
rg -n "rowPinning|initialState|rowModels|pin" examples/angular packages angular 2>/dev/null | head -200 || true

Repository: TanStack/table

Length of output: 50371


🏁 Script executed:

#!/bin/bash
set -euo pipefail

f="examples/angular/row-pinning/src/app/app.ts"

echo "== app.ts relevant sections =="
awk 'NR>=1 && NR<=125 { printf "%4d  %s\n", NR, $0 }' "$f"

echo "== focused rowPinning occurrences only in target example =="
rg -n "rowPinning|row-pinning|pin rows|initialState|state:" "$f" || true

echo "== source control precedence definitions =="
rg -n "initialState|external atom|state|atoms|rowPinningFeature|rowPinning:" packages/framework docs/framework/angular/guide/table-state.md docs/framework/angular/quick-start.md | head -120 || true

Repository: TanStack/table

Length of output: 17447


Make the row-pinning initial state match the controlled row-pinning source.

state.rowPinning controls row pinning from this.rowPinning(), which starts as { top: [], bottom: [] }. Uncommenting initialState.rowPinning will not pin rows because controlled state takes precedence. Remove controlled rowPinning for this example, or initialize this.rowPinning with the documented row IDs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/angular/row-pinning/src/app/app.ts` around lines 76 - 79, Align the
row-pinning example’s initial state with its controlled state: update the
row-pinning source used by this.rowPinning() to initialize with the documented
row IDs, or remove the controlled rowPinning state so initialState.rowPinning
takes effect. Preserve the intended first-render pinning behavior and avoid
leaving conflicting controlled and initial row-pinning configurations.

state: { expanded: this.expanded(), rowPinning: this.rowPinning() },
onExpandedChange: (updater: Updater<ExpandedState>) =>
isFunction(updater)
Expand Down
Loading
Loading