import {
columnResizingFeature,
columnSizingFeature,
createColumnHelper,
createTable,
tableFeatures,
} from '@tanstack/solid-table'
import { For, createMemo, createSignal } from 'solid-js'
import { makeData } from './makeData'
import type { Table as SolidTableType } from '@tanstack/solid-table'
import type { Person } from './makeData'
const features = tableFeatures({ columnSizingFeature, columnResizingFeature })
const columnHelper = createColumnHelper<typeof features, Person>()
const columns = columnHelper.columns([
columnHelper.group({
header: 'Name',
footer: (props) => props.column.id,
columns: columnHelper.columns([
columnHelper.accessor('firstName', {
cell: (info) => info.getValue(),
footer: (props) => props.column.id,
}),
columnHelper.accessor((row) => row.lastName, {
id: 'lastName',
cell: (info) => info.getValue(),
header: () => <span>Last Name</span>,
footer: (props) => props.column.id,
}),
]),
}),
columnHelper.group({
header: 'Info',
footer: (props) => props.column.id,
columns: columnHelper.columns([
columnHelper.accessor('age', {
header: () => 'Age',
footer: (props) => props.column.id,
}),
columnHelper.accessor('visits', {
header: () => <span>Visits</span>,
footer: (props) => props.column.id,
}),
columnHelper.accessor('status', {
header: 'Status',
footer: (props) => props.column.id,
}),
columnHelper.accessor('progress', {
header: 'Profile Progress',
footer: (props) => props.column.id,
}),
]),
}),
])
function App() {
const [data, setData] = createSignal(makeData(200))
const refreshData = () => setData(makeData(200))
const stressTest = () => setData(makeData(5_000))
const table = createTable({
features,
columns,
get data() {
return data()
},
defaultColumn: { minSize: 60, maxSize: 800 },
columnResizeMode: 'onChange',
debugTable: true,
debugHeaders: true,
debugColumns: true,
})
/**
* All column widths flow through CSS variables computed in ONE memo and
* bound to the <table> element's style. `header.getSize()` reads the
* signal-backed columnSizing atom, so a resize tick re-runs only this memo
* and its single style binding; header and data cells reference the
* variables and never re-render.
*/
const tableStyle = createMemo(() => {
const styles: Record<string, string> = { display: 'grid' }
for (const header of table.getFlatHeaders()) {
styles[`--header-${header.id}-size`] = `${header.getSize()}`
styles[`--col-${header.column.id}-size`] = `${header.column.getSize()}`
}
styles.width = `${table.getTotalSize()}px`
return styles
})
return (
<div class="demo-root">
<div>
<button onClick={() => refreshData()} class="demo-button">
Regenerate Data
</button>
<button onClick={() => stressTest()} class="demo-button">
Stress Test (5k rows)
</button>
</div>
<div class="spacer-md" />
{}
<pre style={{ height: '10rem', overflow: 'auto' }}>
{JSON.stringify(table.store.get(), null, 2)}
</pre>
<div class="spacer-md" />({data().length.toLocaleString()} rows)
<div class="scroll-container">
{}
<table style={tableStyle()}>
<thead style={{ display: 'grid' }}>
<For each={table.getHeaderGroups()}>
{(headerGroup) => (
<tr
style={{
display: 'flex',
width: '100%',
height: '30px',
}}
>
<For each={headerGroup.headers}>
{(header) => (
<th
colspan={header.colSpan}
style={{
display: 'flex',
'flex-shrink': '0',
width: `calc(var(--header-${header.id}-size) * 1px)`,
}}
>
{header.isPlaceholder ? null : (
<table.FlexRender header={header} />
)}
{}
<div
onDblClick={() => header.column.resetSize()}
onMouseDown={header.getResizeHandler()}
onTouchStart={header.getResizeHandler()}
class={`resizer ${header.column.getIsResizing() ? 'isResizing' : ''}`}
/>
</th>
)}
</For>
</tr>
)}
</For>
</thead>
{}
<TableBody table={table} />
</table>
</div>
</div>
)
}
function TableBody({
table,
}: {
table: SolidTableType<typeof features, Person>
}) {
return (
<tbody style={{ display: 'grid' }}>
<For each={table.getRowModel().rows}>
{(row) => (
<tr
style={{
display: 'flex',
width: '100%',
height: '30px',
'content-visibility': 'auto',
'contain-intrinsic-height': 'auto 30px',
}}
>
<For each={row.getAllCells()}>
{(cell) => (
<td
style={{
display: 'flex',
'flex-shrink': '0',
width: `calc(var(--col-${cell.column.id}-size) * 1px)`,
}}
>
{cell.renderValue<any>()}
</td>
)}
</For>
</tr>
)}
</For>
</tbody>
)
}
export default App