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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ es
artifacts
.rpt2_cache
coverage
test-results
*.tgz

# misc
Expand Down
17 changes: 10 additions & 7 deletions _artifacts/domain_map.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ library:

meta:
generated_by: '@tanstack/intent scaffold domain discovery'
date: '2026-07-31'
date: '2026-08-01'
status: reviewed
maintainer_review_pending: false
phase_4_date: '2026-07-10'
Expand All @@ -25,7 +25,7 @@ scope:
- 'Teach headless rendering, feature registration, row-model ownership, framework state, and v8-to-v9 changes as foundations.'
included:
- 'All 18 public workspace packages.'
- 'All 14 stock Table v9 features, custom features, TypeScript helpers, migrations, framework state, composable table hooks, Query, Virtual, Devtools, and fuzzy ranking.'
- 'All 16 stock Table v9 features, custom features, TypeScript helpers, migrations, framework state, composable table hooks, Query, Virtual, Devtools, and fuzzy ranking.'
- 'Renderer-owned CSS edge cases for sticky positioning, column widths, resizing, layout, and virtualization.'
excluded:
- 'Component-library-specific integrations such as shadcn, Material UI, Mantine, and equivalent design systems.'
Expand All @@ -47,7 +47,7 @@ migration_depth_contract:
- 'Framework package and construction entrypoint changes, including framework-version prerequisites.'
- 'Logical start/end column-pinning rename and the full old-to-new API mapping.'
- 'Prototype-bound row, cell, column, and header methods; object-spread/Object.keys/JSON implications.'
- 'Required tableFeatures registration, stockFeatures audit guidance, all 14 feature imports, and feature-gated state/APIs.'
- 'Required tableFeatures registration, stockFeatures audit guidance, all 16 feature imports, and feature-gated state/APIs.'
- 'Core row model removal plus every get*RowModel to create*RowModel feature-slot mapping.'
- 'filterFns, sortFns, aggregationFns, and filterMeta registry-slot migration.'
- 'Adapter state access, selectors/subscriptions, controlled slices, external atoms, precedence, and onStateChange removal.'
Expand Down Expand Up @@ -156,7 +156,7 @@ domains:
description: 'Headless philosophy, feature registration, client/server ownership, TypeScript inference, API discovery, custom features, and v8 migration.'
- slug: feature-plugins
name: 'Feature plugins'
description: 'The 14 stock optional features, their prerequisites, state, row-model participation, and UI responsibilities.'
description: 'The 16 stock optional features, their prerequisites, state, row-model participation, and UI responsibilities.'
- slug: framework-adapters
name: 'Framework adapters'
description: 'Per-framework setup, reactive table state, v8 migration, reusable createTableHook patterns, and supported Query/Virtual composition.'
Expand Down Expand Up @@ -533,15 +533,18 @@ skills:
package: '@tanstack/table-core'
domain: feature-plugins
type: feature
purpose: 'Maintain spreadsheet-style rectangular cell ranges anchored to row and column ids across sorting, filtering, pagination, and column layout changes.'
purpose: 'Maintain spreadsheet-style rectangular selections as ordered include/exclude operations anchored to row and column ids, resolving them into disjoint positive regions across sorting, filtering, pagination, and column layout changes.'
sources:
[
'TanStack/table:docs/framework/react/guide/cell-selection.md',
'TanStack/table:packages/table-core/src/features/cell-selection',
'TanStack/table:examples/react/cell-selection',
]
failure_modes:
- 'Expecting cellSelection to be a per-cell map; it is an array of two-corner rectangles, so a range widens onto new cells when sorting or column order changes what sits between its corners.'
- 'Expecting cellSelection to be a per-cell map or a list of final positive regions; it is an ordered log of two-corner include/exclude operations, and derived bounds may split one stored range into several regions.'
- 'Reordering, deduplicating, or serializing only the corners of controlled cellSelection state and thereby changing or losing subtraction semantics.'
- 'Using additive when the intended operation is explicit subtraction; selectCellRange mode is replace, include, or exclude, while additive is only a deprecated include alias.'
- 'Assuming Ctrl/Cmd always adds a range; a modified interaction excludes when it starts on a selected cell and includes when it starts on an unselected cell.'
- 'Binding only the mousedown handler and expecting drag selection, or reimplementing mouseup even though the start handler owns its own document listener.'
- 'Drawing the selection outline with borders on a border-collapse table, which changes row heights as cells become selected.'
- 'Re-rendering every cell on each drag update instead of subscribing per row to table.atoms.cellSelection.'
Expand Down Expand Up @@ -1656,7 +1659,7 @@ documentation_read:
narrative_docs: 'All 187 narrative Markdown documents were inventoried by title, headings, and admonitions; foundational, feature, state, migration, composable, and virtualization guides were deep-read.'
generated_references: 'All 828 generated reference documents were inventoried for exports and API categories; exact API truth is delegated to installed dist declarations (.d.ts) in skills.'
examples: 'All 277 example directories were inventoried; basic, state, composable, Query, Virtual, feature, and Devtools-relevant examples were sampled or deep-read by skill.'
source: 'All public package entrypoints, tableFeatures prerequisites, core construction/state precedence, all 14 feature implementations/defaults, createTableHook implementations, Devtools registration, and match-sorter-utils source were inspected.'
source: 'All public package entrypoints, tableFeatures prerequisites, core construction/state precedence, all 16 feature implementations/defaults, createTableHook implementations, Devtools registration, and match-sorter-utils source were inspected.'
community: 'Recent v9 issues plus recurring high-signal issues and GitHub discussions were reviewed for failure modes and misconceptions.'

open_gaps:
Expand Down
10 changes: 6 additions & 4 deletions _artifacts/skill_spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ This specification is the generation contract for a deliberately smaller, foot-g

## Outcome

Generate 78 short package-local skills across all 18 public packages. A loaded skill should quickly do three things:
Generate 80 short package-local skills across all 18 public packages. A loaded skill should quickly do three things:

1. Correct the user or agent mental model.
2. Show the smallest reliable setup or decision pattern.
Expand Down Expand Up @@ -183,8 +183,10 @@ Skill versions ship with package versions. After release tooling calculates pack
- custom-features — plugin lifecycle after exhausting built-in APIs and meta.
- migrate-v8-to-v9 — shared breaking changes and adapter migration routing.

### Stock feature plugins — @tanstack/table-core (14)
### Stock feature plugins — @tanstack/table-core (16)

- aggregation
- cell-selection
- column-faceting
- column-filtering
- grouping
Expand Down Expand Up @@ -271,7 +273,7 @@ All Devtools skills must emphasize the required non-empty table options.key, lif

| Package | Skills |
| -------------------------------- | -----: |
| @tanstack/table-core | 21 |
| @tanstack/table-core | 23 |
| @tanstack/react-table | 6 |
| @tanstack/preact-table | 6 |
| @tanstack/octane-table | 3 |
Expand All @@ -289,7 +291,7 @@ All Devtools skills must emphasize the required non-empty table options.key, lif
| @tanstack/vue-table-devtools | 1 |
| @tanstack/angular-table-devtools | 1 |
| @tanstack/match-sorter-utils | 1 |
| Total | 78 |
| Total | 80 |

## Framework distinctions that must survive generation

Expand Down
21 changes: 18 additions & 3 deletions _artifacts/skill_tree.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ library:
generated_from:
domain_map: '_artifacts/domain_map.yaml'
skill_spec: '_artifacts/skill_spec.md'
generated_at: '2026-07-31'
generated_at: '2026-08-01'
status: reviewed
reviewed_at: '2026-07-29'
batch_review: true
Expand Down Expand Up @@ -119,7 +119,7 @@ skills:
domain: foundations
path: 'packages/table-core/skills/migrate-v8-to-v9/SKILL.md'
package: 'packages/table-core'
description: 'Perform a complete TanStack Table v8-to-v9 migration audit: all 14 feature registrations, every row-model and registry slot, state/store changes, prototype methods, full pinning and resizing mappings, sorting and selection semantics, removed internals, helpers, meta typing, and generic changes. useLegacyTable is only a deprecated temporary bridge when already encountered.'
description: 'Perform a complete TanStack Table v8-to-v9 migration audit: all 16 feature registrations, every row-model and registry slot, state/store changes, prototype methods, full pinning and resizing mappings, sorting and selection semantics, removed internals, helpers, meta typing, and generic changes. useLegacyTable is only a deprecated temporary bridge when already encountered.'
requires: ['core', 'table-features', 'typescript']
sources:
- 'TanStack/table:docs/framework/react/guide/migrating.md'
Expand Down Expand Up @@ -174,6 +174,21 @@ skills:
- 'TanStack/table:packages/table-core/src/features/column-grouping'
- 'TanStack/table:examples/react/grouping'

- name: 'Aggregation'
slug: aggregation
type: sub-skill
domain: feature-plugins
path: 'packages/table-core/skills/aggregation/SKILL.md'
package: 'packages/table-core'
description: 'Aggregate columns independently of grouping with rowAggregationFeature, built-in or custom aggregation functions, caller-selected row scopes, keyed results, grouped merges, and manual values.'
requires: ['core', 'table-features']
sources:
- 'TanStack/table:docs/guide/aggregation.md'
- 'TanStack/table:docs/framework/react/guide/aggregation.md'
- 'TanStack/table:packages/table-core/src/features/row-aggregation'
- 'TanStack/table:examples/react/aggregation'
- 'TanStack/table:examples/react/grouped-aggregation'

- name: 'Column Ordering'
slug: column-ordering
type: sub-skill
Expand Down Expand Up @@ -297,7 +312,7 @@ skills:
domain: feature-plugins
path: 'packages/table-core/skills/cell-selection/SKILL.md'
package: 'packages/table-core'
description: 'Select rectangular cell ranges with cellSelectionFeature: two-corner range state, drag and Shift and Ctrl handlers, selection edges, and render-order resolution under pinning. Load when ranges widen unexpectedly after sorting or reordering, or when drag re-renders the whole table.'
description: 'Select, add, and subtract rectangular cell ranges with cellSelectionFeature: ordered include/exclude operations, modifier dragging, final positive bounds, selection edges, and render-order resolution under pinning. Load for spreadsheet-style or “select all except” behavior.'
requires: ['core', 'table-features']
sources:
- 'TanStack/table:docs/framework/react/guide/cell-selection.md'
Expand Down
19 changes: 12 additions & 7 deletions docs/framework/alpine/guide/cell-selection.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Alpine.data('table', () => {

## Cell Selection (Alpine) Guide

The cell selection feature keeps track of spreadsheet-style rectangular selections. A user can click a cell, drag across a block of cells, Shift-click to extend, and Ctrl/Cmd-click to add a second rectangle. Let's take a look at some common use cases.
The cell selection feature keeps track of spreadsheet-style rectangular selections. A user can click a cell, drag across a block of cells, Shift-click to extend, and Ctrl/Cmd-drag to add or subtract a rectangle based on whether the starting cell is selected. Let's take a look at some common use cases.

### Access Cell Selection State

Expand All @@ -54,7 +54,7 @@ The table instance already manages the cell selection state for you. You can acc
- `getSelectedCellCount()` - returns how many cells are selected
- `getSelectedCellIds()` - returns the ids of every selected cell
- `getCellSelectionRowIds()` / `getCellSelectionColumnIds()` - returns the rows and columns the selection touches
- `getSelectedCellRangesData()` - returns each selected rectangle's values as a row-major grid
- `getSelectedCellRangesData()` - returns each final positive selection region's values as a row-major grid

```ts
console.log(table.atoms.cellSelection.get()) //get the cell selection state
Expand All @@ -69,22 +69,23 @@ The expansion APIs (`getSelectedCellIds`, `getSelectedCellRangesData`) are memoi

### Cell Selection State Shape

`CellSelectionState` is an array of rectangles, each stored as its two defining corners:
`CellSelectionState` is an ordered array of range operations, each stored as its two defining corners:

```ts
type CellSelectionRange = {
anchorRowId: string
anchorColumnId: string
focusRowId: string
focusColumnId: string
operation?: 'include' | 'exclude'
}

type CellSelectionState = Array<CellSelectionRange>
```

The `anchor` corner is where the selection started and stays put. The `focus` corner is the one that moves while dragging or Shift-extending. Storing both corners, rather than a normalized min/max rectangle, is what makes Shift-extend and "collapse back to the active cell" possible.

Because ranges are only two corners, a drag across thousands of cells updates two strings rather than building a map with one entry per selected cell.
Ranges are applied in order. An omitted `operation` is an inclusion for backward compatibility; an `exclude` range subtracts its rectangle from the selection produced so far. This compact operation log means a “select all except these cells” interaction does not build a map with one entry per selected cell.

### Manage Cell Selection State

Expand Down Expand Up @@ -216,14 +217,18 @@ const table = createTable({

#### Multiple Ranges

Ctrl-clicking or Cmd-clicking pushes an additional rectangle onto the selection instead of replacing it. Set `enableMultiCellRangeSelection: false` to allow only one rectangle at a time, or override `isMultiCellRangeSelectionEvent` to change the modifier.
Ctrl-clicking or Cmd-clicking an unselected cell adds a new inclusive rectangle. Starting the same modified interaction on a selected cell adds an exclusion instead, so clicking removes that cell and dragging subtracts the whole rectangle. Whether the drag includes or excludes is fixed when it starts; shrinking an exclusion drag restores cells that leave its rectangle. Set `enableMultiCellRangeSelection: false` to disable both behaviors, or override `isMultiCellRangeSelectionEvent` to change the modifier.

#### Programmatic Range Operations

`table.selectCellRange(range)` replaces the current selection. Pass `{ mode: 'include' }` to append an inclusion or `{ mode: 'exclude' }` to append an exclusion. The older `{ additive: true }` option remains as a deprecated alias for include mode; `mode` wins if both options are supplied. `table.getCellSelectionBounds()` resolves the operation log into deterministic, disjoint positive rectangles.

### Render Cell Selection UI

TanStack Table does not dictate how you render selected cells. These cell APIs give you everything you need:

- `cell.getIsSelected()` - whether this cell falls inside any range
- `cell.getIsFocused()` - whether this is the active cell
- `cell.getIsFocused()` - whether this is the active cell (an excluded anchor can be focused without being selected)
- `cell.getSelectionEdges()` - which sides sit on the selection boundary
- `cell.getTabIndex()` - `0` for the focused cell and `-1` otherwise, for roving tabindex

Expand Down Expand Up @@ -286,7 +291,7 @@ Scope the hotkeys to the grid element rather than the document, or arrow keys an

### Copying a Selection

`getSelectedCellRangesData()` returns raw values indexed as `[rangeIndex][rowIndex][columnIndex]`. Turning that into clipboard text is left to your application, because the delimiter, the representation of `null`, and any quoting rules are decisions only you can make.
`getSelectedCellRangesData()` returns raw values indexed as `[regionIndex][rowIndex][columnIndex]`. A region is one of the final disjoint positive rectangles after all include and exclude operations are applied, so it does not necessarily correspond one-to-one with stored state. Turning that into clipboard text is left to your application, because the delimiter, the representation of `null`, and any quoting rules are decisions only you can make.

```ts
function escapeTsvValue(value: unknown) {
Expand Down
Loading
Loading